
/* the small language with assignment */

:- op(1150, xfx, run).

% :- op(1100, xfy, ;).

:- op(800, xfy, then).
:- op(800, xfy, else).
:- op(790, fx,  if).

:- op(750, fx, not).

% :- op(700, xfx, =).

:- op(600, xfx, :=).

% :- op(500, fx, +).

A run P :-
	progeval(P, A, Z),
	print(Z), nl.

progeval(CMD, A, Z) :-
	add_value(a, A, [], ENV),
	comdeval(CMD,ENV,NEWENV),
	give_value(z, Z, NEWENV).

/* a command change the state of the memory */
comdeval(ID := EXP,ENV,NEWENV)
   :- sexpeval(EXP,VAL,ENV),
      add_value(ID,VAL,ENV,NEWENV).

comdeval((Cmd1 ; Cmd2), ENV,NEWENV)
   :- comdeval(Cmd1,ENV,ENV2), comdeval(Cmd2,ENV2,NEWENV).

comdeval(if B1 then Cmd1 else _, ENV, NEWENV)
  :- booleval(B1, ENV),
     comdeval(Cmd1, ENV, NEWENV).

comdeval(if _ then _ else Cmd2, ENV, NEWENV)
  :- comdeval(Cmd2, ENV, NEWENV).

/* evaluate an boolean */
booleval((EXP1 = EXP2), ENV)
   :- sexpeval(EXP1,VAL1,ENV), sexpeval(EXP2,VAL2,ENV), VAL1 = VAL2.

booleval(not E, ENV) :- not(booleval(E,ENV)).

/* evaluate an expression */
sexpeval(N,N,_) :- number(N), !.

sexpeval(ID,V,ENV) :- atom(ID),!, give_value(ID,V,ENV).

sexpeval(E1+E2, Val,ENV)
  :- sexpeval(E1,V1,ENV), sexpeval(E2,V2,ENV), Val is V1 + V2.


add_value(ID, VAL, ENV, [[ID,VAL]|ENV]).
give_value(ID, VAL, [[ID,VAL]|_]) :- !.
give_value(ID, VAL, [_|ENV]) :-
	give_value(ID, VAL, ENV).
give_value(_, 0, []).

test1(A) :-
	A run z := 1; if a = 0 then z := 3 else z := z + a.

