:- dynamic used/1, % refraction memory
           val/2.  % working memory

say(X)         :- print(X),nl. 
and(A,B)       :- A,B. 
or(A,B)        :- A;B. 
in(A, to(B,C)) :- val(A,X), X >= B, X < C. 
upto(A,B)      :- val(A,X), X =< B. 
below(A,B)     :- val(A,X), X <  B. 
equals(A,B)    :- val(A,B). 
over(A,B)      :- val(A,X), X > B. 
downto(A,B)    :- val(A,X), X >= B.

ugly(if(r1,
     then(or(and(below(age, 30), 
	         equals(name, ying)), 
	     not(and(equals(sex, male), 
		     downto(age, 40)))), 
	  say(hello(ying))))).

yuck :- 
  ugly(if(Id,then(Condition,Action))), 
  not used(Id),
  Condition, 
  print(firing(Id)),
  nl,
  assert(used(Id)),
  Action.

 
:- op(999, xfx, if).  
:- op(998, xfx, then). 
:- op(997, xfy, or).
:- op(996, xfy, and). 
:- op(995,  fy, not).  
:- op(700,  fx, say).
:- op(700, xfx, [upto,below,equals,over,downto,in]). 
:- op(1,   xfx, to ).


r1 if age below 30
      and name equals 'ying'
      or not (sex equals male
              and age downto 40
              )
   then say hello(ying).

% better forward chaining interpreter
fChain :- reset, steps.

reset :- retractall(used(_)).

steps  :- step, !, steps.
steps.  % terminate if no satisfied rule can be found

step :-  X if Y then Z, % find a rule
         step1(X,Y,Z).  % try to use it.

% what we have before in yuck/0: but buried away
% beneath several layers of convenience.
step1(X,Y,Z) :- 
  not used(X),Y, 
  print(firing(X)),
  nl,
  assert(used(X)), 
  !,
  Z.

fastChain :- reset, run.

runs :- run,!, runs.
runs.  % terminate if no satisfied rule can be found

term_expansion(X if Y then Z,(run :- Body)) :-
    clause(step1(X,Y,Z),Body).
goal_expansion(X, Y) :-
    not predicate_property(X,built_in), clause(X,Y).


r2 if age below 30
      and name equals 'ying'
      or not (sex equals male
              and age downto 40
              )
   then say hello(ying).

