/* prolog tutorial 2.18 Clauses as data  */

:- dynamic calltree/1,analyze_all/2,analyze/2,process/3,
          uses/2,record/3,calls/1,display_dependencies/1,forget/0 .

calltree(Program) :-
     analyze_all(Program,Program),
     display_dependencies(Program),
     forget.

analyze_all([Pred|Rest],Program) :-
     analyze(Pred,Program),
     analyze_all(Rest,Program).
analyze_all([],_).

analyze(P/N,Program) :-
     functor(PE,P,N),            /* generate predicate expression */
     clause(PE,Body),            /* fetch definition */
     process(P/N,Body,Program),  /* Pred calls Body literals */
     fail.
analyze(_,_).                    /* Force bactracking then succeed */

process(P,(Q,Rest),Program) :-
     record(P,Q,Program),
     process(P,Rest,Program).
process(P,(Q),Program) :- 
     record(P,Q,Program).

record(P,Q,Program) :-
     functor(Q,QF,N),
     member(QF/N,Program),
     \+uses(P,QF/N),!,
     assertz(uses(P,QF/N)).
record(_,_,_).                   /* already recorded */

display_dependencies([P|R]) :-
     calls(P),
     nl,
     display_dependencies(R).
display_dependencies([]).

calls(P) :-
     write(P),
     write(' calls: '),
     uses(P,Q),
     write(Q),
     write(' '),
     fail.
calls(_).

forget :-
     retract(uses(_,_)),
     fail.
forget.
 
/* use this program on itself ... */
 go :- calltree([calltree/1,analyze_all/2,analyze/2,process/3,
                 record/3,calls/1,display_dependencies/1,forget/0,uses/2]).