36
37:- module(apply_macros,
38 [ expand_phrase/2, 39 expand_phrase/4, 40 apply_macros_sentinel/0
41 ]). 43:- use_module(library(apply), [maplist/2, maplist/3, maplist/4]). 44:- use_module(library(yall), [is_lambda/1, lambda_calls/3]). 46:- autoload(library(lists),[append/3]). 47:- autoload(library(prolog_code), [mkconj/3, extend_goal/3]). 48
73
74:- create_prolog_flag(optimise_apply, default,
75 [ keep(true),
76 type(oneof([default,false,true]))
77 ]). 78:- create_prolog_flag(apply_macros_scope, global,
79 [ keep(true),
80 type(oneof([global,imported]))
81 ]). 82
83:- dynamic
84 user:goal_expansion/2. 85:- multifile
86 user:goal_expansion/2. 87
96
97expand_maplist(Callable, Lists, Goal) :-
98 maplist(is_list, Lists),
99 maplist(length, Lists, Lens),
100 ( sort(Lens, [Len])
101 -> Len < 10,
102 unfold_maplist(Lists, Callable, Goal),
103 !
104 ; Maplist =.. [maplist,Callable|Lists],
105 print_message(warning, maplist(inconsistent_length(Maplist, Lens))),
106 fail
107 ).
108expand_maplist(Callable0, Lists, Goal) :-
109 length(Lists, N),
110 expand_closure_no_fail(Callable0, N, Callable1),
111 ( Callable1 = _:_
112 -> strip_module(Callable1, M, Callable),
113 NextGoal = M:NextCall,
114 QPred = M:Pred
115 ; Callable = Callable1,
116 NextGoal = NextCall,
117 QPred = Pred
118 ),
119 Callable =.. [Pred|Args],
120 length(Args, Argc),
121 length(Argv, Argc),
122 length(Vars, N),
123 MapArity is N + 1,
124 format(atom(AuxName), '__aux_maplist/~d_~w+~d', [MapArity, QPred, Argc]),
125 append(Lists, Args, AuxArgs),
126 Goal =.. [AuxName|AuxArgs],
127
128 AuxArity is N+Argc,
129 prolog_load_context(module, Module),
130 functor(NextCall, Pred, AuxArity),
131 \+ predicate_property(Module:NextGoal, transparent),
132 ( predicate_property(Module:Goal, defined)
133 -> true
134 ; empty_lists(N, BaseLists),
135 length(Anon, Argc),
136 append(BaseLists, Anon, BaseArgs),
137 BaseClause =.. [AuxName|BaseArgs],
138
139 heads_and_tails(N, NextArgs, Vars, Tails),
140 append(NextArgs, Argv, AllNextArgs),
141 NextHead =.. [AuxName|AllNextArgs],
142 append(Argv, Vars, PredArgs),
143 NextCall =.. [Pred|PredArgs],
144 append(Tails, Argv, IttArgs),
145 NextIterate =.. [AuxName|IttArgs],
146 NextClause = (NextHead :- NextGoal, NextIterate),
147 compile_aux_clauses([BaseClause, NextClause])
148 ).
149
150unfold_maplist(Lists, Callable, Goal) :-
151 maplist(cons, Lists, Heads, Tails),
152 !,
153 maplist_extend_goal(Callable, Heads, G1),
154 unfold_maplist(Tails, Callable, G2),
155 mkconj(G1, G2, Goal).
156unfold_maplist(_, _, true).
157
158cons([H|T], H, T).
159
167
168maplist_extend_goal(Closure, Args, Goal) :-
169 is_lambda(Closure),
170 !,
171 lambda_calls(Closure, Args, Goal1),
172 expand_goal_no_instantiate(Goal1, Goal).
173maplist_extend_goal(Closure, Args, Goal) :-
174 extend_goal(Closure, Args, Goal1),
175 expand_goal_no_instantiate(Goal1, Goal).
176
179
180expand_goal_no_instantiate(Goal0, Goal) :-
181 term_variables(Goal0, Vars0),
182 expand_goal(Goal0, Goal),
183 term_variables(Goal0, Vars1),
184 Vars0 == Vars1.
185
189
190expand_closure_no_fail(Callable0, N, Callable1) :-
191 '$expand_closure'(Callable0, N, Callable1),
192 !.
193expand_closure_no_fail(Callable, _, Callable).
194
195empty_lists(0, []) :- !.
196empty_lists(N, [[]|T]) :-
197 N2 is N - 1,
198 empty_lists(N2, T).
199
200heads_and_tails(0, [], [], []).
201heads_and_tails(N, [[H|T]|L1], [H|L2], [T|L3]) :-
202 N2 is N - 1,
203 heads_and_tails(N2, L1, L2, L3).
204
205
209
210expand_apply(Maplist, Goal) :-
211 compound(Maplist),
212 compound_name_arity(Maplist, maplist, N),
213 N >= 2,
214 Maplist =.. [maplist, Callable|Lists],
215 qcall_instantiated(Callable),
216 !,
217 expand_maplist(Callable, Lists, Goal).
218
228
229expand_apply(forall(Cond, Action), Pos0, Goal, Pos) :-
230 Goal = \+((Cond, \+(Action))),
231 ( nonvar(Pos0),
232 Pos0 = term_position(_,_,_,_,[PosCond,PosAct])
233 -> Pos = term_position(0,0,0,0, 234 [ term_position(0,0,0,0, 235 [ PosCond,
236 term_position(0,0,0,0, 237 [PosAct])
238 ])
239 ])
240 ; true
241 ).
242expand_apply(once(Once), Pos0, Goal, Pos) :-
243 Goal = (Once->true),
244 ( nonvar(Pos0),
245 Pos0 = term_position(_,_,_,_,[OncePos]),
246 compound(OncePos)
247 -> Pos = term_position(0,0,0,0, 248 [ OncePos,
249 F-T 250 ]),
251 arg(2, OncePos, F), 252 T is F+1
253 ; true
254 ).
255expand_apply(ignore(Ignore), Pos0, Goal, Pos) :-
256 Goal = (Ignore->true;true),
257 ( nonvar(Pos0),
258 Pos0 = term_position(_,_,_,_,[IgnorePos]),
259 compound(IgnorePos)
260 -> Pos = term_position(0,0,0,0, 261 [ term_position(0,0,0,0, 262 [ IgnorePos,
263 F-T 264 ]),
265 F-T 266 ]),
267 arg(2, IgnorePos, F), 268 T is F+1
269 ; true
270 ).
271expand_apply(Phrase, Pos0, Expanded, Pos) :-
272 expand_phrase(Phrase, Pos0, Expanded, Pos),
273 !.
274
275
292
293expand_phrase(Phrase, Goal) :-
294 expand_phrase(Phrase, _, Goal, _).
295
296expand_phrase(phrase(NT,Xs), Pos0, NTXsNil, Pos) :-
297 !,
298 extend_pos(Pos0, 1, Pos1),
299 expand_phrase(phrase(NT,Xs,[]), Pos1, NTXsNil, Pos).
300expand_phrase(Goal, Pos0, NewGoal, Pos) :-
301 dcg_goal(Goal, NT, Xs0, Xs),
302 nonvar(NT),
303 nt_pos(Pos0, NTPos),
304 dcg_extend(NT, NTPos, NewGoal, Pos, Xs0, Xs).
305
306dcg_goal(phrase(NT,Xs0,Xs), NT, Xs0, Xs).
307dcg_goal(call_dcg(NT,Xs0,Xs), NT, Xs0, Xs).
308
310
311dcg_extend(Terminal, Pos0, Xs0 = DList, Pos, Xs0, Xs) :-
312 terminal(Terminal, DList, Xs),
313 !,
314 t_pos(Pos0, Pos).
315dcg_extend(Q0, Pos0, M:Q, Pos, Xs0, Xs) :-
316 nonvar(Q0), Q0 = M:Q1,
317 !,
318 '$expand':f2_pos(Pos0, MPos, APos0, Pos, MPos, APos),
319 dcg_extend(Q1, APos0, Q, APos, Xs0, Xs).
320dcg_extend(Control, _, _, _, _, _) :-
321 dcg_control(Control),
322 !,
323 fail.
324dcg_extend(Compound0, Pos0, Compound, Pos, Xs0, Xs) :-
325 compound(Compound0),
326 !,
327 extend_pos(Pos0, 2, Pos),
328 compound_name_arguments(Compound0, Name, Args0),
329 append(Args0, [Xs0,Xs], Args),
330 compound_name_arguments(Compound, Name, Args).
331dcg_extend(Name, Pos0, Compound, Pos, Xs0, Xs) :-
332 atom(Name),
333 !,
334 extend_pos(Pos0, 2, Pos),
335 compound_name_arguments(Compound, Name, [Xs0,Xs]).
336
337dcg_control(!).
338dcg_control([]).
339dcg_control([_|_]).
340dcg_control({_}).
341dcg_control((_,_)).
342dcg_control((_;_)).
343dcg_control((_->_)).
344dcg_control((_*->_)).
345
346terminal([], DList, Tail) =>
347 DList = Tail.
348terminal(String, DList, Tail), string(String) =>
349 string(String),
350 string_codes(String, List),
351 append(List, Tail, DList).
352terminal(List, DList, Tail), is_list(List) =>
353 append(List, Tail, DList).
354terminal(_, _, _) =>
355 fail.
356
357extend_pos(Var, _, Var) :-
358 var(Var),
359 !.
360extend_pos(term_position(F,T,FF,FT,ArgPos0), Extra,
361 term_position(F,T,FF,FT,ArgPos)) :-
362 !,
363 extra_pos(Extra, T, ExtraPos),
364 append(ArgPos0, ExtraPos, ArgPos).
365extend_pos(FF-FT, Extra,
366 term_position(FF,FT,FF,FT,ArgPos)) :-
367 !,
368 extra_pos(Extra, FT, ArgPos).
369
(1, T, [T-T]).
371extra_pos(2, T, [T-T,T-T]).
372
373nt_pos(PhrasePos, _NTPos) :-
374 var(PhrasePos),
375 !.
376nt_pos(term_position(_,_,_,_,[NTPos|_]), NTPos).
377
378t_pos(Pos0, term_position(F,T,F,T,[F-T,F-T])) :-
379 compound(Pos0),
380 !,
381 arg(1, Pos0, F),
382 arg(2, Pos0, T).
383t_pos(_, _).
384
385
391
392qcall_instantiated(Var) :-
393 var(Var),
394 !,
395 fail.
396qcall_instantiated(M:C) :-
397 !,
398 atom(M),
399 callable(C).
400qcall_instantiated(C) :-
401 callable(C).
402
403
404 407
408:- multifile
409 prolog_clause:unify_goal/5. 410
411prolog_clause:unify_goal(Maplist, Expanded, _Module, Pos0, Pos) :-
412 is_maplist(Maplist),
413 maplist_expansion(Expanded),
414 Pos0 = term_position(F,T,FF,FT,[_MapPos|ArgsPos]),
415 Pos = term_position(F,T,FF,FT,ArgsPos).
416
417is_maplist(Goal) :-
418 compound(Goal),
419 compound_name_arity(Goal, maplist, A),
420 A >= 2.
421
422maplist_expansion(Expanded) :-
423 compound(Expanded),
424 compound_name_arity(Expanded, Name, _),
425 sub_atom(Name, 0, _, _, '__aux_maplist/').
426
427
428 431
432:- multifile
433 prolog_colour:vararg_goal_classification/3. 434
435prolog_colour:vararg_goal_classification(maplist, Arity, expanded) :-
436 Arity >= 2.
437
438
439 442
443:- multifile
444 system:goal_expansion/2,
445 system:goal_expansion/4. 446
452
453apply_macros_sentinel.
454
455optimise_apply :-
456 ( current_prolog_flag(optimise_apply, true)
457 -> true
458 ; current_prolog_flag(optimise_apply, default),
459 current_prolog_flag(optimise, true)
460 -> true
461 ).
462
463apply_macros :-
464 current_prolog_flag(xref, true),
465 !,
466 fail.
467apply_macros :-
468 optimise_apply,
469 current_prolog_flag(apply_macros_scope, Scope),
470 apply_macros(Scope).
471
472apply_macros(global) =>
473 true.
474apply_macros(imported) =>
475 prolog_load_context(module, M),
476 predicate_property(M:apply_macros_sentinel, imported_from(apply_macros)),
477 !.
478
479system:goal_expansion(GoalIn, GoalOut) :-
480 apply_macros,
481 expand_apply(GoalIn, GoalOut).
482system:goal_expansion(GoalIn, PosIn, GoalOut, PosOut) :-
483 apply_macros,
484 expand_apply(GoalIn, PosIn, GoalOut, PosOut).
485
486
487 490
491:- multifile
492 prolog:message//1. 493
494prolog:message(maplist(inconsistent_length(Maplist, Lens))) -->
495 { functor(Maplist, _, N) },
496 [ 'maplist/~d called with proper lists of different lengths (~p) always fails'
497 -[N, Lens] ]