diff --git a/src/Codeable_Eval_Parser.pl b/src/Codeable_Eval_Parser.pl new file mode 100644 index 0000000..524e5c2 --- /dev/null +++ b/src/Codeable_Eval_Parser.pl @@ -0,0 +1,227 @@ +% :- use_rendering(svgtree). % Only works in SWISH.swi-prolog.org + + +%%%%% Token Parser %%%%%% + +% v3 (Heavily inspired by M2.1 - slide 59) +factor(factor_digit(F)) --> digit(F). +factor(factor_identifier(F)) --> identifier(F). +factor(factor_expression(F)) --> ['('], expr(F), [')']. + +term(term_factor(T)) --> factor(T). +term(term_times(T1, T2)) --> factor(T1), [*], term(T2). +term(term_divide(T1, T2)) --> factor(T1), [/], term(T2). + +expr(expr_term(E)) --> term(E). +expr(expr_plus(E1, E2)) --> term(E1), [+], expr(E2). +expr(expr_minus(E1, E2)) --> term(E1), [-], expr(E2). +expr(expr_assign(A)) --> assignment(A). + +boolean(true) --> [true]. +boolean(false) --> [false]. +boolean(not(B)) --> [not], boolean(B). +boolean(equals(E1, E2)) --> expr(E1), [=], expr(E2). + +assignment(assign(I, E)) --> identifier(I), [:=], expr(E). +ternary(if(B, T, F)) --> [if], boolean(B), [then], command(T), [else], command(F), [endif]. +loop(while(B, C)) --> [while], boolean(B), [do], command(C), [endwhile]. + +command(C) --> assignment(C). +command(C) --> ternary(C). +command(C) --> loop(C). +command(C) --> block(C). +command(cmd(C1, C2)) --> assignment(C1), [;], command(C2). +command(cmd(C1, C2)) --> ternary(C1), [;], command(C2). +command(cmd(C1, C2)) --> loop(C1), [;], command(C2). +command(cmd(C1, C2)) --> block(C1), [;], command(C2). + +constant(const(I, N)) --> [const], identifier(I), [=], digit(N). +variable(var(I)) --> [var], identifier(I). + +declaration(D) --> constant(D). +declaration(D) --> variable(D). +declaration(decl(D1, D2)) --> constant(D1), [;], declaration(D2). +declaration(decl(D1, D2)) --> variable(D1), [;], declaration(D2). + +block(blk(D, C)) --> [begin], declaration(D), [;], command(C), [end]. + +program(prog(P)) --> block(P), ['.']. + + +/** + * eval(+Node, +EnvIn, -EnvOut, -ValueOut) + * + * Evaluates the parse tree rooted at Node, with the environment EnvIn, + * and returns the environment EnvOut and the value ValueOut. + * + * The environment is a list of pairs (Identifier, Value) where Identifier + * is a string and Value is an integer. + * + */ +eval(prog(P), EnvIn, EnvOut, ValueOut) :- + eval(P, EnvIn, EnvOut, ValueOut). + +eval(blk(D, C), EnvIn, EnvOut, ValueOut) :- + eval(D, EnvIn, EnvDecl, _ValueOut), + eval(C, EnvDecl, EnvOut, ValueOut). + +eval(var(I), EnvIn, EnvOut, _) :- + declare(I, EnvIn, EnvOut). +eval(const(I, N), EnvIn, EnvOut, N) :- + update(I, N, EnvIn, EnvOut). +eval(decl(D1, D2), EnvIn, EnvOut, ValueOut) :- + eval(D1, EnvIn, EnvTemp, _ValueOut), + eval(D2, EnvTemp, EnvOut, ValueOut). + +eval(cmd(C1, C2), EnvIn, EnvOut, ValueOut) :- + eval(C1, EnvIn, EnvTemp, _ValueOut), + eval(C2, EnvTemp, EnvOut, ValueOut). + +eval(assign(I, E), EnvIn, EnvOut, ValueOut) :- + eval(E, EnvIn, EnvTemp, ValueOut), + update(I, ValueOut, EnvTemp, EnvOut). + +eval(if(B, T, _), EnvIn, EnvOut, ValueOut) :- + eval(B, EnvIn, EnvTemp, true), + eval(T, EnvTemp, EnvOut, ValueOut). +eval(if(B, _, F), EnvIn, EnvOut, ValueOut) :- + eval(B, EnvIn, EnvTemp, false), + eval(F, EnvTemp, EnvOut, ValueOut). + +eval(while(B, C), EnvIn, EnvOut, ValueOut) :- + eval(B, EnvIn, EnvTemp, true), + eval(C, EnvTemp, EnvTemp2, _ValueOut), + eval(while(B, C), EnvTemp2, EnvOut, ValueOut). +eval(while(B, _), EnvIn, EnvOut, _ValueOut) :- + eval(B, EnvIn, EnvOut, false). + +eval(expr_plus(Term, Expression), EnvIn, EnvOut, ValueOut) :- + eval(Term, EnvIn, EnvTemp, ValTerm), + eval(Expression, EnvTemp, EnvOut, ValExpr), + ValueOut is ValTerm + ValExpr. +eval(expr_minus(Term, Expression), EnvIn, EnvOut, ValueOut) :- + eval(Term, EnvIn, EnvTemp, ValTerm), + eval(Expression, EnvTemp, EnvOut, ValExpr), + ValueOut is ValTerm + (0 - ValExpr). +eval(expr_term(T), EnvIn, EnvOut, ValueOut) :- + eval(T, EnvIn, EnvOut, ValueOut). +eval(expr_assign(A), EnvIn, EnvOut, ValueOut) :- + eval(A, EnvIn, EnvOut, ValueOut). + +eval(term_times(Factor, Term), EnvIn, Env2Out, ValueOut) :- + eval(Factor, EnvIn, Env1Out, Val1Out), + eval(Term, Env1Out, Env2Out, Val2Out), + ValueOut is Val1Out * Val2Out. +eval(term_divide(Factor, Term), EnvIn, Env2Out, ValueOut) :- + eval(Factor, EnvIn, Env1Out, Val1Out), + eval(Term, Env1Out, Env2Out, Val2Out), + ValueOut is Val1Out * (1 / Val2Out). +eval(term_factor(F), EnvIn, EnvOut, ValueOut) :- + eval(F, EnvIn, EnvOut, ValueOut). + +eval(factor_digit(D), EnvIn, EnvIn, D). +eval(factor_identifier(I), EnvIn, EnvIn, Value) :- + lookup(I, EnvIn, Value). +eval(factor_expression(E), EnvIn, EnvOut, Value) :- + eval(E, EnvIn, EnvOut, Value). + +eval(true, EnvIn, EnvIn, true). +eval(false, EnvIn, EnvIn, false). +eval(not(B), EnvIn, EnvOut, false) :- + eval(B, EnvIn, EnvOut, true). +eval(not(B), EnvIn, EnvOut, true) :- + eval(B, EnvIn, EnvOut, false). +eval(equals(E1, E2), EnvIn, EnvOut, true) :- + eval(E1, EnvIn, EnvTemp, ValueOut), + eval(E2, EnvTemp, EnvOut, ValueOut). +eval(equals(E1, E2), EnvIn, EnvOut, false) :- + eval(E1, EnvIn, EnvTemp, ValueOut1), + eval(E2, EnvTemp, EnvOut, ValueOut2), + ValueOut1 =\= ValueOut2. + +/** + * lookup(+Identifier, +Env, -Value) + * + * Looks up the Value of Identifier in Env. + */ +lookup(Identifier, [(Identifier, Value) | _], Value). +lookup(Identifier, [(OtherIdentifier, _) | RemainingEnvironment], Value) :- + Identifier \= OtherIdentifier, + lookup(Identifier, RemainingEnvironment, Value). + +/** + * declare(+Identifier, +EnvIn, -EnvOut) + * + * Adds a new binding (Identifier, _) to EnvIn and returns EnvOut. + * + * If Identifier is already bound in EnvIn, it is not rebound. + */ +declare(Identifier, [], [(Identifier, _)]). +declare( + Identifier, + [(Identifier, Value) | RemainingEnvironment], + [(Identifier, Value) | RemainingEnvironment] +). +declare(Identifier, [H | RemainingEnvironment], [H | NewEnvironment]) :- + declare(Identifier, RemainingEnvironment, NewEnvironment). + +/** + * update(+Identifier, +Value, +EnvIn, -EnvOut) + * + * Updates the binding of Identifier in EnvIn to Value and returns EnvOut. + */ +update(Identifier, Value, [], [(Identifier, Value)]). +update( + Identifier, + Value, + [(Identifier, _) | RemainingEnvironment], + [(Identifier, Value) | RemainingEnvironment] +). +update( + Identifier, + Value, + [(OtherIdentifier, OtherValue) | RemainingEnvironment], + [(OtherIdentifier, OtherValue) | NewEnvironment] +) :- + Identifier \= OtherIdentifier, + update(Identifier, Value, RemainingEnvironment, NewEnvironment). + + +/** + * program_eval(+Program, +InitialX, +InitialY, -FinalZ) + * + * Evaluates the Program with the initial value of X and Y + * and returns the final value of Z. + */ +program_eval(P, X, Y, Z) :- + update(x, X, [], EnvX), + update(y, Y, EnvX, EnvY), + eval(P, EnvY, EnvOut, _ValueOut), + lookup(z, EnvOut, Z). + + +digit(0) --> [0]. +digit(1) --> [1]. +digit(2) --> [2]. +digit(3) --> [3]. +digit(4) --> [4]. +digit(5) --> [5]. +digit(6) --> [6]. +digit(7) --> [7]. +digit(8) --> [8]. +digit(9) --> [9]. + +identifier(WORD) --> { var(WORD), ! }, + chars(CHARS), { atom_codes(WORD, CHARS) }. +identifier(WORD) --> { nonvar(WORD) }, + { atom_codes(WORD, CHARS) }, chars(CHARS). + +chars([X|Y]) --> char(X), chars(Y). +chars([]) --> []. + +char(X) --> [X], { is_char(X) }. + +is_char(X) :- X >= 0'a, X =< 0'z, !. +is_char(X) :- X >= 0'A, X =< 0'Z, !. +is_char(X) :- X >= 0'0, X =< 0'9, !. +is_char(0'_). \ No newline at end of file