pereira@sri-unix.UUCP (08/15/83)
% File : ADVICE.PL % Author : R.A.O'Keefe % Updated: 30 May 1983 % Purpose: Interlisp-like advice package. %> There isn't much point in compiling this. %> NEEDS : concat/3 and flag/3 from UTIL % This module defines two commands and a predicate: % advise(Predicate, Port, Action) % unadvise(Predicate, Port) % advised(Prediate) % which cause the Action to be performed at that Port (advise), % cause no action to be performed at that Port (unadvise), or % test whether a predicate is advised. % The Predicate argument must be a Prolog term. a/2 will be taken % as referring to the predicate (/)/2, which is probably not what % you want. Write a(_,_). This is the same convention as that % used by current_predicate. For advise/3 the arguments will be % retained and may be used as a further condition; the other two % predicates ignore the arguments except to note how many they are. % The Port argument may be call, exit, redo, fail, or *. * is % for unadvise only, and means to remove all advice. % Advice is only heeded when the 'advice' flag is on, use the % utility flag/3 to switch it on and off % flag(advice, Val, Val) -- returns the current setting % flag(advice, _, on) -- turns it on % flag(advice, _, off) -- turns it off % Unlike the InterLISP facility on which this is based, it is % not possible to advise built-in predicates. Also, advice is % only heeded in interpreted calls, public compiled predicates % may be advised, but calls to them from within compiled code % will not see the advice. This is due to implementation facts % beyond my power to change, and may perhaps be different in % Prolog-X if/when that gets off the ground. %----------------------------------------------------------------------------% %...advised(Goal) % is true when Goal is a Prolog goal whose predicate is under advice. % The Goal may have some of its arguments filled in, that doesn't % matter. This predicate may be used to enumerate the predicates % being advised, in the spirit of current_predicate. advised(Goal) :- current_predicate(_, Goal), clause(Goal, 'a$call'(Goal,_)). %...advised(Goal, Skel, Call) % is a version of advised/1 which returns the general skeleton for % goal, and if the goal is ill-formed or not being advised, prints % an error message. It may NOT be used to enumerate goals, but as % you are not supposed to know about it, that is acceptable. As a % result of picking up the mapped call from the clause, only advise % needs to know how the new name is generated. advised(Goal, Skel, Call) :- functor(Goal, Functor, Arity), functor(Skel, Functor, Arity), clause(Skel, 'a$call'(Skel,Call)), !. advised(Goal, _, _) :- display('! You are not advising '), display(Goal), ttynl, fail. %----------------------------------------------------------------------------% %...advise(Goal, Port, Action) % makes sure that the Goal is being advised, and then adds an % entry to its advice table. On the Dec-10 and in C-Prolog, we % are able to put the advice "on the Goal's property list" by % using recorda/z. advise(Goal, Port, Action) :- 'a$fail'(Port), functor(Goal, Functor, Arity), functor(Skel, Functor, Arity), advise(Goal, Skel), recordz(Skel, advice(Port,Goal,Action), _). %...a$fail(Port) % checks that the Port is a valid port name. 'a$fail'(Var) :- var(Var), !, display('! Variable as port name in advise/unadvise'), ttynl, fail. 'a$fail'(call) :- !. 'a$fail'(exit) :- !. 'a$fail'(redo) :- !. 'a$fail'(fail) :- !. 'a$fail'(Port) :- display('! unknown port name '), display(Port), ttynl. %...advise(Goal) % takes a skeletal Goal, e.g. f(A,B,C), and makes sure that calls % to that goal will be routed to e.g. a$f(A,B,C) via a$call. If % the goal is advised already, nothing is done. If there are no % clauses for this goal, an error is announced (as this usually % indicates a typing mistake or a system predicate. Otherwise % all the current clauses are renamed, and a new clause % f(A, B, C) :- a$call(f(A,B,C), a$f(A,B,C)). % is added. BEWARE: this will not work if you change the predicate % using assert/retract. It would have to be built in to the system % at a much lower level for that to work. advise(_, Skel) :- clause(Skel, 'a$call'(Skel,_)), !. advise(_, Skel) :- current_predicate(Functor, Skel), !, concat('a$', Functor, Afunctor), Skel =.. [Functor |Args], Call =.. [Afunctor|Args], ( clause(Skel, Body, Ref), assertz((Call :- Body)), erase(Ref), fail ; true ), !, assert((Skel :- 'a$call'(Skel,Call))). advise(Goal, _) :- display('! You have no clauses for '), display(Goal), ttynl, fail. %----------------------------------------------------------------------------% %...unadvise(Goal, Port) % wipes out some or all (Port=*) of the advice for Goal. unadvise(Goal, Star) :- Star == '*', !, advised(Goal, Skel, Call), retract((Skel :- 'a$call'(Skel,Call))), ( clause(Call, Body, Ref), assertz((Skel :- Body)), erase(Ref), fail ; true ), !, 'a$abolish'(Skel, _). unadvise(Goal, Port) :- 'a$fail'(Port), advised(Goal, Skel, Call), 'a$abolish'(Skel, Port). %...a$abolish(Goal, Port) % wipes out all the advice for this port of the goal, given % that the goal and port have been validated. 'a$abolish'(Goal, Port) :- recorded(Goal, advice(Port,_,_), Ref), erase(Ref), fail. 'a$abolish'(_, _). %...a$call(SourceGoal, MappedGoal) % routes a call on SourceGoal to call MappedGoal instead, but obeys % any advice that may be lying around. 'a$call'(Goal, Call) :- flag(advice, off, off), !, call(Call). 'a$call'(Goal, Call) :- ( recorded(Goal, advice(call,Goal,Action), _), call(Action), fail ; true ; recorded(Goal, advice(fail,Goal,Action), _), call(Action), fail ), call(Call), ( recorded(Goal, advice(exit,Goal,Action), _), call(Action), fail ; true ; recorded(Goal, advice(redo,Goal,Action), _), call(Action), fail ).