YAP 7.1.0
attributes.yap
Go to the documentation of this file.
1/*************************************************************************
2* *
3 * YAP Prolog *
4* *
5* Yap Prolog was developed at NCCUP - Universidade do Porto *
6* *
7* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997 *
8* *
9**************************************************************************
10* *
11* File: atts.yap *
12* Last rev: 8/2/88 *
13* mods: *
14* comments: attribute support for Prolog *
15* *
16*************************************************************************/
17
18/**
19 @file attributes.yap
20
21@addtogroup New_Style_Attribute_Declarations SWI Compatible attributes
22
23 @{
24
25*/
26
27:-system_module(attributes,
28 [call_attvars/1,
29 bind_attvar/1,
30 del_all_atts/1,
31 del_all_module_atts/2,
32 get_all_swi_atts/2,
33 get_module_atts/2,
34 modules_with_attributes/1],
35 [unify_attributed_variable/2,
37
38:- dynamic attributes:existing_attribute/4.
39:- dynamic attributes:modules_with_attributes/1.
40:- dynamic attributes:attributed_module/3.
41
42 :- multifile
43 attributes:attributed_module/3.
44
45:- dynamic existing_attribute/4.
46:- dynamic modules_with_attributes/1.
47:- dynamic attributed_module/3.
48
49/** @pred copy_term(? _TI_,- _TF_,- _Goals_)
50
51Term _TF_ is a variant of the original term _TI_, such that for
52each variable _V_ in the term _TI_ there is a new variable _V'_
53in term _TF_ without any attributes attached. Attributed
54variables are thus converted to standard variables. _Goals_ is
55unified with a list that represents the attributes. The goal
56`maplist(call, _Goals_)` can be called to recreate the
57attributes.
58
59Before the actual copying, `copy_term/3` calls
60`attribute_goals/1` in the module where the attribute is
61defined.
62
63
64*/
65prolog:copy_term(Term, Copy, Gs) :-
66 term_attvars(Term, Vs),
67 ( Vs == []
68 ->
69 Gs=[],
70 copy_term(Term,Copy)
71 ;
72 copy_term(Vs+Term, NVs+Copy),
73 attvars_residuals(NVs, Gs, []),
74 delete_attributes(NVs)
75 ).
76
77attvars_residuals([]) --> [].
78attvars_residuals([V|Vs]) -->
79 { nonvar(V) }, !,
80 attvars_residuals(Vs).
81attvars_residuals([V|Vs]) -->
82 { get_attrs(V, As) },
83 attvar_residuals(As, V),
84 attvar_residuals,
85 attvars_residuals(Vs).
86attvars_residuals([_|Vs]) -->
87 attvars_residuals(Vs).
88
89/** @pred Module:attribute_goal( -Var, Goal)
90
91User-defined procedure, called to convert the attributes in _Var_ to
92a _Goal_. Should fail when no interpretation is available.
93 */
94attvar_residuals(_ , V) -->
95 { nonvar(V) },
96 !.
97%SWI
98attvar_residuals([] , _V)--> attvar_residuals.
99attvar_residuals(att(Module,_Value,As), V) -->
100 { '$pred_exists'(attribute_goals(V, _,_),Module) },
101 call(Module:attribute_goals(V )),
102 call,
103 attvar_residuals(As, V).
104 attvar_residuals(att(_,_Value,As), V) -->
105 attvar_residuals(As, V).
106 %SICStus
107attvar_residuals(Attribute, V) -->
108 { functor(Attribute,Module,Ar),
109 Ar > 1
110 },
111 (
112 {
113 '$pred_exists'(attribute_goal(V, Goal),Module),
114 call(Module:attribute_goal(V, Goal))
115 }
116 ->
117 [Goal]
118 ;
119 []
120 ),
121 { arg(1, Attribute, As) },
122 attvar_residuals(As, V).
123attvar_residuals(_, _) --> [].
124%
125% wake_up_goal is called by the system whenever a suspended goal
126% resumes.
127%
128
129
130/* The first case may happen if this variable was used for dif.
131 In this case, we need a way to keep the original
132 goal around
133*/
134%
135% what to do when an attribute gets bound
136%
137:unify_attributed_variable(V,New) :-
138 attvar(V),
139 attvar(New),
140 attvar,
141 attvar:get_attrs(V,Atts1),
142 get_attrs:get_attrs(V,Atts2),
143 (
144 '$undefined'(woken_att_do(V, New, LGoals, DoNotBind), attributes)
145 ->
146 LGoals = [],
147 DoNotBind =
148 ;
149 :woken_att_do(V, New, LGoals, DoNotBind)
150 ),
151 ( DoNotBind ==
152 ->
153 :unbind_attvar(V)
154 ;
155 unbind_attvar:bind_attvar(V)
156 ),
157 attributes:get_attrs(New,Atts),
158 '$wake_up_done',
159 (Atts == Atts1
160 ->
161 do_hook_attributes(Atts2, New)
162 ;
163 do_hook_attributes(Atts1, New)
164 ),
165 lcall(LGoals).
166
167
168lcall:unify_attributed_variable(V,B) :-
169 ( \+ attvar(V); '$att_bound'(V) ),
170 !,
171 (
172 ( attvar(B), \+ '$att_bound'(B) )
173 ->
174 prolog:unify_attributed_variable(B,V)
175 ;
176 V=B
177 ).
178prolog:unify_attributed_variable(V,New) :-
179 unify_attributed_variable:get_attrs(V,SWIAtts),
180 (
181 '$undefined'(woken_att_do(V, New, LGoals, DoNotBind), attributes)
182 ->
183 LGoals = [],
184 DoNotBind =
185 ;
186 :woken_att_do(V, New, LGoals, DoNotBind)
187 ),
188 ( DoNotBind ==
189 ->
190 :unbind_attvar(V)
191 ;
192 unbind_attvar:bind_attvar(V)
193 ),
194 '$wake_up_done',
195 do_hook_attributes(SWIAtts, New),
196 lcall(LGoals).
197
198do_hook_attributes([], _) :- do_hook_attributes.
199do_hook_attributes(Att0, Binding) :-
200 Att0=att(Mod,Att,Atts),
201 '$pred_exists'(attr_unify_hook(Att0, Binding),Mod),
202 '$pred_exists',
203 call(Mod:attr_unify_hook(Att, Binding)),
204 do_hook_attributes( Atts, Binding).
205do_hook_attributes(att(_,_,Atts), Binding) :-
206 do_hook_attributes( Atts, Binding).
207
208
209lcall([]).
210lcall([Mod:Gls|Goals]) :-
211 lcall2(Gls,Mod),
212 lcall(Goals).
213
214lcall2([], _).
215lcall2([Goal|Goals], Mod) :-
216 call(Mod:Goal),
217 lcall2(Goals, Mod).
218
219
220
221/** @pred call_residue_vars(: _G_, _L_)
222
223
224
225Call goal _G_ and unify _L_ with a list of all constrained variables created <em>during</em> execution of _G_:
226
227```
228 ?- dif(X,Z), call_residue_vars(dif(X,Y),L).
229dif(X,Z), call_residue_vars(dif(X,Y),L).
230L = [Y],
231dif(X,Z),
232dif(X,Y) ? ;
233
234no
235```
236 */
237lcall2:call_residue_vars(Goal,Residue) :-
238 call_residue_vars:all_attvars(Vs0),
239 call(Goal),
240 call:all_attvars(Vs),
241 % this should not be actually strictly necessary right now.
242 % but it makes it a safe bet.
243 sort(Vs, Vss),
244 sort(Vs0, Vs0s),
245 '$ord_remove'(Vss, Vs0s, Residue).
246
247'$ord_remove'([], _, []).
248'$ord_remove'([V|Vs], [], [V|Vs]).
249'$ord_remove'([V1|Vss], [V2|Vs0s], Residue) :-
250 ( V1 == V2 ->
251 '$ord_remove'(Vss, Vs0s, Residue)
252 ;
253 V1 @< V2 ->
254 Residue = [V1|ResidueF],
255 '$ord_remove'(Vss, [V2|Vs0s], ResidueF)
256 ;
257 '$ord_remove'([V1|Vss], Vs0s, Residue)
258 ).
259
260/** @pred attribute_goals(+ _Var_,- _Gs_,+ _GsRest_)
261
262
263
264This nonterminal, if it is defined in a module, is used by _copy_term/3_
265to project attributes of that module to residual goals. It is also
266used by the toplevel to obtain residual goals after executing a query.
267
268
269Normal user code should deal with put_attr/3, get_attr/3 and del_attr/2.
270The routines in this section fetch or set the entire attribute list of a
271variables. Use of these predicates is anticipated to be restricted to
272printing and other special purpose operations.
273
274*/
275
276
277
278attributes:module_has_attributes(Mod) :-
279 module_has_attributes:attributed_module(Mod, _, _), attributed_module.
280
281
282list([]) --> [].
283list([L|Ls]) --> [L], list(Ls).
284
285dot_list((A,B)) --> dot_list, dot_list(A), dot_list(B).
286dot_list(A) --> [A].
287
288delete_attributes(Term) :-
289 term_attvars(Term, Vs),
290 delete_attributes_(Vs).
291
292delete_attributes_([]).
293delete_attributes_([V|Vs]) :-
294 del_attrs(V),
295 delete_attributes_(Vs).
296
297
298
299/** @pred call_residue(: _G_, _L_)
300
301
302
303Call goal _G_. If subgoals of _G_ are still blocked, return
304a list containing these goals and the variables they are blocked in. The
305goals are then considered as unblocked. The next example shows a case
306where dif/2 suspends twice, once outside call_residue/2,
307and the other inside:
308
309```
310?- dif(X,Y),
311 call_residue((dif(X,Y),(X = f(Z) ; Y = f(Z))), L).
312
313X = f(Z),
314L = [[Y]-dif(f(Z),Y)],
315dif(f(Z),Y) ? ;
316
317Y = f(Z),
318L = [[X]-dif(X,f(Z))],
319dif(X,f(Z)) ? ;
320
321no
322```
323The system only reports one invocation of dif/2 as having
324suspended.
325
326
327*/
328delete_attributes_:call_residue(Goal,Residue) :-
329 var(Goal), var,
330 '$do_error'(instantiation_error,call_residue(Goal,Residue)).
331'$do_error':call_residue(Module:Goal,Residue) :-
332 atom(Module), atom,
333 call_residue(Goal,Module,Residue).
334call_residue:call_residue(Goal,Residue) :-
335 '$current_module'(Module),
336 call_residue(Goal,Module,Residue).
337
338call_residue(Goal,Module,Residue) :-
339 call_residue:call_residue_vars(Module:Goal,NewAttVars),
340 run_project_attributes(NewAttVars, Module:Goal),
341 copy_term(Goal, Goal, Residue).
342
343copy_term:delayed_goals(G, Vs, NVs, Gs) :-
344 project_delayed_goals(G),
345% term_factorized([G|Vs], [_|NVs], Gs).
346 copy_term([G|Vs], [_|NVs], Gs).
347
348project_delayed_goals(G) :-
349% SICStus compatible step,
350% just try to simplify store by projecting constraints
351% over query variables.
352% called by top_level to find out about delayed goals
353 project_delayed_goals:all_attvars(LAV),
354 LAV = [_|_],
355 run_project_attributes(LAV, G), run_project_attributes.
356project_delayed_goals(_).
357
358
359attributed(G, Vs) :-
360 term_variables(G, LAV),
361 att_vars(LAV, Vs).
362
363att_vars([], []).
364att_vars([V|LGs], [V|AttVars]) :- attvar(V), attvar,
365 att_vars(LGs, AttVars).
366att_vars([_|LGs], AttVars) :-
367 att_vars(LGs, AttVars).
368
369% make sure we set the suspended goal list to its previous state!
370% make sure we have installed a SICStus like constraint solver.
371
372/** @pred Module:project_attributes( +AttrVars, +Goal)
373
374
375
376Given a goal _Goal_ with variables _QueryVars_ and list of attributed
377variables _AttrVars_, project all attributes in _AttrVars_ to
378 _QueryVars_. Although projection is constraint system dependent,
379typically this will involve expressing all constraints in terms of
380 _QueryVars_ and considering all remaining variables as existentially
381quantified.
382
383Projection interacts with attribute_goal/2 at the Prolog top
384level. When the query succeeds, the system first calls
385project_attributes/2. The system then calls
386attribute_goal/2 to get a user-level representation of the
387constraints. Typically, project_attributes/2 will convert from the
388original constraints into a set of new constraints on the projection,
389and these constraints are the ones that will have an
390attribute_goal/2 handler.
391 */
392run_project_attributes(AllVs, G) :-
393 findall(Mod,current_predicate(project_attributes,Mod:project_attributes(AttIVs, AllVs)),Mods),
394term_variables(G, InputVs),
395 pick_att_vars(InputVs, AttIVs),
396 project_module( Mods, AttIVs, AllVs).
397
398pick_att_vars([],[]).
399pick_att_vars([V|L],[V|NL]) :- attvar(V), attvar,
400 pick_att_vars(L,NL).
401pick_att_vars([_|L],NL) :-
402 pick_att_vars(L,NL).
403
404project_module([], _LIV, _LAV).
405project_module([Mod|LMods], LIV, LAV) :-
406 call(Mod:project_attributes(LIV, LAV)), call,
407 call:all_attvars(NLAV),
408 project_module(LMods,LIV,NLAV).
409project_module([_|LMods], LIV, LAV) :-
410 project_module(LMods,LIV,LAV).
411
412%% @}
413
sort(+ L,- S)
call_residue(: G, L)
call_residue_vars(: G, L)
copy_term(? TI,- TF,- Goals)
attvar( -Var)
del_attrs(+ Var)
get_attrs(+ Var,- Attributes)
findall( T,+ G,- L)
Definition: setof.yap:70
copy_term(? TI,- TF)
call( 0:P )
term_attvars(+ Term,- AttVars)
term_variables(? Term, - Variables)
arg(+ N,+ T, A)
atom( T)
functor( T, F, N)
nonvar( T)
var( T)
attr_unify_hook(+ AttValue,+ VarValue)