/*----------------------------------------------------------------------
		 Partial Execution of Prolog Programs
----------------------------------------------------------------------*/

compile :-
    program_clause((Head :- Body)),
    partially_execute(Body, ExpandedBody),
    add_rule((Head :- ExpandedBody)),
    fail.



partially_execute((Head:-Body), (Head:-ExpandedBody)) :-
    partially_execute(Body, ExpandedBody).

partially_execute((Literal, Rest), Expansion) :- !,
    partially_execute(Literal, ExpandedLiteral),
    partially_execute(Rest, ExpandedRest),
    conjoin(ExpandedLiteral, ExpandedRest, Expansion).

partially_execute(Literal, Expansion) :-
    % if the literal should be partially evaluated
    part_evaluate(Literal),
    % and at least one rule matches
    setof(Some, Literal^aclause(Literal, Some), [_Clause|_Others]), !,
    % then pick up any rule 
    aclause(Literal, Body),
    % and expand its body
    partially_execute(Body, Expansion).

partially_execute(Literal, true) :-
    % if the literal should be fully executed
    full_evaluate(Literal), !, 
    % just execute it, and add no literals to expanded body
    call(Literal).

partially_execute(Literal, Literal) :- !.


add_rule((Head :- Body)) :-
    rewrite(Head, NewHead),
    rewrite(Body, NewBody),
    write('Asserting "'), write((NewHead :- NewBody)), write('."'), nl,
    assert((NewHead :- NewBody)).

rewrite((A,B), (C,D)) :- !,
    rewrite(A, C), rewrite(B, D).
rewrite(parse(Term, P1, P2), NewLiteral) :- !,
    Term =.. [Function|Args],
    conc(Args, [P1, P2], AllArgs),
    NewLiteral =.. [Function|AllArgs].
rewrite(Term,Term).


/*----------------------------------------------------------------------
			       Utilities
----------------------------------------------------------------------*/


conjoin((A,B), C, ABC) :- !,
    conjoin(B, C, BC),
    conjoin(A, BC, ABC).

conjoin(true, A, A) :- !.
conjoin(A, true, A) :- !.

conjoin(A, C, (A,C)).

conc([], List, List).
conc([Element|Rest], List, [Element|LongRest]) :-
    conc(Rest, List, LongRest).

member(X, [X|_List]).
member(X, [_Y|List]) :- member(X, List).

aclause(Head, Body) :-
     clause((Head:-Body)) ; (clause(Head), Body = true).

/*----------------------------------------------------------------------
		      Program to Partially Execute
----------------------------------------------------------------------*/

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
		Control Information for Partial Executor
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 

:- op(1200,xfx,'--->').

:- dynamic (--->)/2, parse/3, connect/3, parse_body/3, compiled/1.

part_evaluate( (_ ---> _) ).
part_evaluate(parse(_, _, _)).
full_evaluate(X) :- fail.


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
			   The Program Itself
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 


program_clause((        parse(NT, P_0, P) :-
                           (NT ---> Body), 
                           parse(Body, P_0, P)
              )).

program_clause((        connect(W, [W|R], R) :- true
              )).


clause((                parse((Body1, Body2), P_0, P) :-
                            parse(Body1, P_0, P_1), 
                            parse(Body2, P_1, P)
      )).

clause((                parse([], P, P)
      )).

clause((                parse([Word|Rest], P_0, P) :-
                            connect(Word, P_0, P_1), 
                            parse(Rest, P_1, P)
      )).

clause((                parse({Goals}, P, P) :- call(Goals)
      )).



/*----------------------------------------------------------------------
                 Data for Program to Partially Execute
----------------------------------------------------------------------*/

clause((                 s ---> np, vp				)).
clause((                 np ---> [john], {print((john))}	)).
clause((                 vp ---> v,  np				)).
clause((                 v ---> [loves]				)).
clause((                 np ---> [mary]				)).
