prologprolog-metainterpreter

What is wrong with this version of trace?


I have this tracing meta interpreter. It is written for swi-prolog.

trace(Goal):-
        trace(Goal, 0).

trace(true, _Depth):-!, true.
trace(fail, _Depth):-!, fail.
trace(A > B, _Depth):-!, A > B.
trace(A < B, _Depth):-!, A < B.
trace(A <= B, _Depth):-!, A <= B.
trace(A >= B, _Depth):-!, A >= B.
trace(A = B, _Depth):-!, A = B.
trace(A is B, _Depth):-!, A is B.
trace(!, _Depth):-!, fail.
trace(not(A), _Depth):-!, not(A).
trace((Goal1, Goal2), Depth):-!,
        trace(Goal1, Depth),
        trace(Goal2, Depth).
trace(Goal, Depth):-
        display('Call: ', Goal, Depth),
        clause(Goal, Body),
        Depth1 is Depth + 1,
        trace(Body, Depth1),
        display('Exit: ', Goal, Depth),
        display_redo(Goal, Depth).
trace(Goal, Depth):-
        display('Fail: ', Goal, Depth),
        fail.

display(Message, Goal, Depth):-
        tab(Depth), write(Depth), write(': '), write(Message),
        write(Goal), nl.

display_redo(Goal, Depth):-
        true
        ;
        display('Redo: ', Goal, Depth),
        fail.

just_do_it(In, Out, Query):-
        consult(In),
        tell(Out),
        call_with_depth_limit(findall(Query, trace(Query), _Solutions), 30, _XMessage),
        writeln(_XMessage),
        writeln(_Solutions),
        told,
        unload_file(In),
        true.

It is working fine and how it should except one thing. When I have variable in Query in function just_do_it(In, Out, Query), the output is with binded variable. Is there some way to unbind the variable in further steps of tracing so I could see when it is binded and when not?

Example output :

0: Call: a(_G1085,_G1085,_G1087)
0: Exit: a(3,3,7)
0: Redo: a(3,3,7)
  1: Call: b(_G1087,_G1085)
  1: Exit: b(7,3)
  1: Redo: b(7,3)
  1: Exit: b(5,1)
  1: Redo: b(5,1)
  1: Fail: b(_G1087,_G1085)
0: Fail: a(_G1085,_G1085,_G1087)

Example output I wish to have:

0: Call: a(_G1085,_G1085,_G1087)
0: Exit: a(3,3,7)
0: Redo: a(_G1085,_G1085,_G1087)
 1: Call: b(_G1087,_G1085)
 1: Exit: b(7,3)
 1: Redo: b(_G1087,_G1085)
 1: Exit: b(5,1)
 1: Redo: b(_G1087,_G1085)
 1: Fail: b(_G1087,_G1085)
0: Fail: a(_G1085,_G1085,_G1087)

Solution

  • There are several issues with your interpreter:

    You are using an operator <= which does not exist. So you must get a syntax error on this. In Prolog less-then-or-equal is =< to avoid confusion with an arrow ⇐ / <= .

    You are defining a predicate trace/1, but SWI as many other Prologs have this predicate already predefined. So better use another name.

    The cut is not correctly implemented.

    The rule for not/1 should rather define (\+)/1, and could read:

    trace(\+A, Depth):-!, \+trace(A, Depth).
    

    Proving variables should result in an instantiation_error. Like in call(_). So you need as first rule

    trace(V, _) :- var(V), !, throw(error(instantiation_error, _)).
    

    To display redo correctly, you need to replace the goal clause(Goal, Body), by a more explicit way. After all, there is no way to unbind variables, except via backtracking. That is, we have to find the right moment where the variables are still uninstantiated. Replace clause/2 by redo_clause/3.

    redo_clause(Depth, Goal, Body) :-
       findall(Goal-Body, clause(Goal, Body), [First|Rest]),
       ( Goal-Body = First
       ; length(Rest, RL), length(RestC, RL),
         member(Goal-Body,RestC),
         display('Redo: ', Goal, Depth),
         Rest = RestC
       ).