% i \in Integer
% b \in Boolean ::= true | false
% e \in Expression ::= integer(i) |
%                      boolean(b) |
%                      lessThan(e1, e2) |
%                      and(e1, e2) |
%                      if(e1, e2, e3) |
%                      print(e)

and(false, _, false).
and(_, false, false).
and(true, true, true).

% eval: AST, Result
eval(integer(N), int(N)).
eval(boolean(B), bool(B)).
eval(lessThan(E1, E2), bool(Result)) :-
    eval(E1, int(E1Int)),
    eval(E2, int(E2Int)),
    (E1Int < E2Int ->
         Result = true;
         Result = false).
eval(and(E1, E2), bool(Result)) :-
    eval(E1, bool(E1Bool)),
    eval(E2, bool(E2Bool)),
    and(E1Bool, E2Bool, Result).
eval(if(Guard, IfTrue, IfFalse), Result) :-
    eval(Guard, bool(GuardResult)),
    (GuardResult == true ->
         eval(IfTrue, Result);
         eval(IfFalse, Result)).
eval(print(E), void) :-
    eval(E, Result),
    writeln(Result).


interpret(true) :- !.
interpret((A is B)) :-
    !,
    A is B.
interpret((A = B)) :-
    !,
    A = B.
interpret((A, B)) :-
    !,
    interpret(A),
    interpret(B).
interpret((A; B)) :-
    !,
    (interpret(A);
     interpret(B)).
interpret(\+(A)) :-
    !,
    \+ interpret(A).
interpret(Call) :-
    clause(Call, Body),
    interpret(Body).

myAppend([], List, List).
myAppend([Head|Tail], List, [Head|Rest]) :-
    myAppend(Tail, List, Rest).

interp0(true) :- !.
interp0((A, B)) :-
    !,
    interp0(A),
    interp0(B).
interp0(Call) :-
    clause(Call, Body),
    interp0(Body).

interp1(true) :- !.
interp1((A, B)) :-
    !,
    interp1(A),
    interp1(B).
interp1(Call) :-
    writeln(Call),
    clause(Call, Body),
    interp1(Body).

interp2(true) :- !.
interp2((A, B)) :-
    !,
    interp2(A),
    interp2(B).
interp2(Call) :-
    format('Call: ~w~n', [Call]),
    clause(Call, Body),
    interp2(Body),
    format('Return: ~w~n', [Call]).

interp3(true, _) :- !.
interp3((A, B), Stack) :-
    !,
    interp3(A, Stack),
    interp3(B, Stack).
interp3(Call, Stack) :-
    format('Call (~w): ~w~n', [Stack, Call]),
    clause(Call, Body),
    Next is Stack + 1,
    interp3(Body, Next),
    format('Exit (~w): ~w~n', [Stack, Call]).

interp3(Call) :-
    interp3(Call, 0).
