% ---AST Definition---
%
% There are four kinds of expressions:
%
% 1.) Numbers, which represent a numeric literal.  These are
%     represented with a structure named number with arity 1,
%     containing the following:
%     1.1.) A Prolog number, e.g., 42
%
% 2.) Arithmetic addition, which represents the idea of adding
%     two subexpressions.  This is represented with a structure
%     named plus with arity 2, containing the following:
%     2.1.) An expression, according to this AST definition
%     2.2.) Another expression according to this AST definition
%
% 3.) Arithmetic subtraction, which represents the idea of subtracting
%     one subexpression from another.  This is represented with a
%     structure named minus with arity 2, containing the following:
%     3.1.) An expression, according to this AST definition
%     3.2.) Another expression according to this AST definition
%     The second expression is intended to be subtracted from the first
%     expression.
%
% 4.) Arithmetic multiplication, which represents the idea of multiplying
%     two subexpressions together.  This is represented with a structure
%     named mult of arity 2, containing the following:
%     4.1.) An expression, according to this AST definition
%     4.2.) Another expression according to this AST definition
%
% 5.) Arithmetic negation, which represents the idea of negating a
%     subexpression.  This is represented with a structure named
%     negate of arity 1, containing the following:
%     5.1.) An expression, according to this AST definition
%
% A more compact representation of all the above information is shown
% below in a variant of a BNF grammar:
%
% n ∈ PrologNumber
% e ∈ Expression ::= number(n) | plus(e1, e2) | minus(e1, e2) |
%                    mult(e1, e2) | negate(e)

% ---Code---
%

% eval
%
% Takes the following:
% 1.) An expression, according to the above AST definition
% 2.) The number that the expression evaluates to.
%
eval(number(N), N).
eval(plus(E1, E2), Result) :-
    eval(E1, E1Num),
    eval(E2, E2Num),
    Result is E1Num + E2Num.
eval(minus(E1, E2), Result) :-
    eval(E1, E1Num),
    eval(E2, E2Num),
    Result is E1Num - E2Num.
eval(mult(E1, E2), Result) :-
    eval(E1, E1Num),
    eval(E2, E2Num),
    Result is E1Num * E2Num.
eval(negate(E), Result) :-
    eval(E, ENum),
    Result is -ENum.

tests([pair(number(1), 1),
       pair(plus(number(1), number(2)), 3),
       pair(minus(number(3), number(1)), 2),
       pair(mult(number(3), number(4)), 12),
       pair(negate(number(1)), -1),
       pair(negate(plus(number(2),
                        minus(number(3),
                              number(4)))), -1)]).

testPassed(pair(E, N)) :-
    eval(E, N).

runTests([]).
runTests([Head|Tail]) :-
    testPassed(Head),
    runTests(Tail).

runTests :-
    tests(Tests),
    runTests(Tests).
