% This is partly based on O'Keefe's Book Section 9.4 and partly  on
% the expand.pl utilities of O'Keefe's free libraries

:- dynamic macro/2.

% Rule
expand_clause((Head :- OldBody)) :-!, 
	expand_body(OldBody, NewBody).
% Directive
expand_clause((:- OldBody), (:- NewBody)) :-!,
	expand_body(OldBody, NewBody).
% Query
expand_clause((?- OldBody), (?- NewBody)) :-!,
	expand_body(OldBody, NewBody).
% Fact
expand_clause(OldBody,NewBody) :-
	expand_body(OldBody,NewBody).

% Variable
expand_body(Var, call(Var)) :-
	var(Var), !.
% Conjunction
expand_body((OldA,OldB), Answer) :- !,
	expand_body(OldA, NewA),
	expand_body(OldB, NewB),
	get_rid_of_extra_true(NewA, NewB, Answer).
% Disjunction
expand_body((OldA;OldB), (NewA;NewB)) :- !,
	expand_body(OldA, NewA),
	expand_body(OldB, NewB).
% Implication
expand_body((OldA->OldB), (NewA->NewB)) :- !,
	expand_body(OldA, NewA),
	expand_body(OldB, NewB).
% Forall
expand_body(forall(OldA,OldB), forall(NewA,NewB)) :- !,
	expand_body(OldA, NewA),
	expand_body(OldB, NewB).
% Negation
expand_body(\+(Old), \+(New)) :- !,
	expand_body(Old, New).
% And finally:  macro application
expand_body(Old, New) :-
	macro(Old, New),
	!.			% FORCE a unique expansion.
expand_body(Old, Old).	% Not a macro.


%   Macros very often turn into 'true', and clauses that come out
%   looking like ... :- true,true,true,true,true. would be silly.
%   So if one member of a conjunction is 'true' we discard it.  A
%   final 'true' as in "p :- true" does no harm as Prolog will do
%   any removal necessary.  We **can't** remove 'true' disjuncts,
%   though we could in logic, as that would change the behaviour
%   of the program by backtracking a different number of times.

get_rid_of_extra_true(true, X, X) :- !.
get_rid_of_extra_true(X, true, X) :- !.


/* -------------------- EXAMPLE MACROS

macro(cons(H, T, [H|T]),	true).
macro(head([H|T], H),		true).
macro(tail([H|T], T),		true).
macro(empty([]),		true).
macro(positive(X),		X>0).

/* --------------------	EXAMPLE OF THEIR USE

append(Prefix, Suffix, Answer) :-
	head(Prefix, Head),
	tail(Prefix, Tail),
	cons(Head, Rest, Answer),
	append(Tail, Suffix, Rest).
append(Prefix, Answer, Answer) :-
	empty(Prefix).


member(Element, List) :-
	head(List, Element).
member(Element, List) :-
	tail(List, Rest),
	member(Element, Rest).

bug(Var) :-			% bug([2|_]) is meant to succeed
	nonvar(Var), !,		% bug(X) is meant to bind X to 'var'
	head(Var, 2).		% bug(any other non-variable) is
bug(var).			% meant to fail


greater(X, Y) :-
	Z is Y-Z,
	positive(Z).
