% wrapper

% prefix T with one get call for each ?X in T.
% suffix T with one set call for each !Y in T.
:- op(1,fx, ?),op(1,fx, !).

/* --------------------------
e.g.
demo1 :- wrap((  !x is ?x + 1, % 1
	         ?a=L,         % 2
	         member(!b,L), % 3
	         !c = [a|?c],  % 4
	         ?X=Y,         % 5
	         print(X=Y)    % 6
   ),Z),numbervars(Z,0,_),format('~p\n',[Z]).

prints:
    ?(A=B), ?(a=C), ?(c=D), ?(x=E),
    ( F is E+1,      % 1
      C=G,           % 2
      member(H, G),  % 3
      I=[a|D],       % 4
      B=J,           % 5
      print(A=J)     % 6
    ),   
    !(b=H), !(c=I), !(x=F)

--------------------------- */

% uses a naughty sorting trick to place the 
% goal (marked with a "2") after the gets
% (marked with a "1") and before the sets
% (marked with a "3"). this is perhaps
% over-tricky- but the option is to carry six
% I/O variables around- yucko
wrap(Goals0,Out) :-
	wrap1(Goals0,Goals,Do,[]),
	% fails if no wrapping needed
	Do \= [], 
	sort(['2'(Goals)|Do],Ordered),
	% remove the ordering markers
	maplist(arg(1),Ordered,Temp),                  
	l2c(Temp,Out).

l2c([H],(H)) :- !.
l2c([H|T],(H,Rest)) :- l2c(T,Rest).

wrap1(Goals0,Goals,Do,Done) :-
	% standard top-down parser.
	% once = sugar for cut
	once(wrap2(Goals0,Goals,Do,Done)).

% standard base case1: dodge vars
wrap2(X,X,Do, Do) :-
	var(X).
% standard base case2: leaf of term
wrap2(X, X,Do, Do) :-
	atomic(X).

% replace get/set symbols with a variable;
% store that symbol and var.
wrap2(?X,Y,['1'(?(X=Y))|Do],Do). 
wrap2(!X,Y,['3'(!(X=Y))|Do],Do).

% termination case
wrap2([], [],Do, Do).
% recursive over list
wrap2([H0|T0],[H|T]) -->                  
	wrap1(H0,H), wrap1(T0,T).

% recurse into term
wrap2(Term0, Term,Do0,Do) :-              
	% standard recustive univ trick.
	% ONE: bust up the term into a list
	Term0 =.. L0,
        % TWO: run over the list to build a new list
        wrap2(L0,L,Do0,Do),
	% THREE: piece back together a new list
        Term  =.. L.