%% 15-317 S'23
%% Metainterpreter for (pure) Prolog
%% using goal stacks to fix subgoal ordering

mem(Dcopy, [D | Gamma]) :- copy_term(D, Dcopy).
mem(Dcopy, [_ | Gamma]) :- mem(Dcopy, Gamma).

unify(P,Q) :- unify_with_occurs_check(P,Q).
% unify(P,P).  % Prolog, but logically unsound

chooseR(Gamma, []).
chooseR(Gamma, [G|Omega]) :- focusR(Gamma, G, Omega).

focusR(Gamma, and(G1,G2), Omega) :- focusR(Gamma, G1, [G2|Omega]).
focusR(Gamma, atom(P), Omega) :- chooseL(Gamma, atom(P), Omega).

chooseL(Gamma, atom(P), Omega) :-
    mem(D, Gamma),
    focusL(Gamma, D, atom(P), Omega).

focusL(Gamma, atom(Q), atom(P), Omega) :-
    unify(Q,P),
    chooseR(Gamma, Omega).
focusL(Gamma, imp(G,atom(Q)), atom(P), Omega) :-
    unify(Q,P),
    focusR(Gamma, G, Omega).

%% examples

ex1([atom(p), imp(atom(p),atom(q)), atom(q),
     imp(atom(q), atom(r))]).
query1 :- ex1(Gamma), focusR(Gamma, atom(r), []).

ex2([imp(atom(p),atom(p)), atom(p)]).
query2 :- ex2(Gamma), focusR(Gamma, atom(p), []).

ex3([atom(plus(0, Y, Y)),
     imp(atom(plus(X, Y, Z)), atom(plus(s(X), Y, s(Z)))),
     atom(times(0, Y, 0)),
     imp(and(atom(times(X, Y, W)), atom(plus(W, Y, Z))),
         atom(times(s(X), Y, Z)))]).

query3(Z) :- ex3(Gamma),
             focusR(Gamma, atom(plus(s(0), s(s(0)), Z)), []).
query4(X,Y) :- ex3(Gamma),
               focusR(Gamma, atom(plus(X, Y, s(s(s(s(0)))))), []).
query5(Z) :- ex3(Gamma),
             focusR(Gamma, atom(times(s(s(s(0))), s(s(0)), Z)), []).

% next doesn't terminate after a few solutions
query6(X,Y) :- ex3(Gamma),
               focusR(Gamma, atom(times(X, Y, s(s(s(s(s(s(0)))))))), []).
