1:- module(macros,
2 [ macro_position/1, 3 4 expand_macros/5, 5 include_macros/3, 6 op(10, fx, #)
7 ]). 8:- use_module(library(terms)). 9:- use_module(library(error)). 10:- use_module(library(lists)). 11
106
107define_macro((#define(From, To)), Clauses) =>
108 valid_macro(From),
109 Clause0 = ('$macro'(From, Expansion) => Expansion = To),
110 prepare_module(Clause0, Clauses).
111define_macro((#define(From, To) :- Cond), Clauses) =>
112 valid_macro(From),
113 Clause0 = ('$macro'(From, Expansion), Cond => Expansion = To),
114 prepare_module(Clause0, Clauses).
115define_macro((#import(File)), Clauses) =>
116 use_module(File, []),
117 source_file_property(File, module(M)),
118 Clause0 = ('$macro'(Macro, Expansion), include_macros(M, Macro, Expansion)
119 => true),
120 prepare_module(Clause0, Clauses).
121
122define_macro(_, _) =>
123 fail.
124
125valid_macro(Macro), reserved_macro(Macro) =>
126 domain_error(macro, Macro).
127valid_macro(Macro), callable(Macro) =>
128 true.
129valid_macro(Macro), is_dict(Macro) =>
130 true.
131valid_macro(_Macro) =>
132 fail.
133
134reserved_macro(define(_,_)) => true.
135reserved_macro(import(_)) => true.
136reserved_macro(_) => fail.
137
138:- multifile
139 error:has_type/2. 140
141error:has_type(macro, Term) :-
142 ( callable(Term)
143 -> true
144 ; is_dict(Term)
145 ),
146 \+ reserved_macro(Term).
147
148prepare_module(Clause0, Clauses) :-
149 prolog_load_context(module, M),
150 ( is_prepared_module(M)
151 -> Clauses = Clause0
152 ; Clauses = [ (:- multifile(('$macro'/2,term_expansion/4))),
153 (term_expansion(In, PIn, Out, Pout) :-
154 expand_macros(M, In, Out, PIn, Pout)),
155 expand_macros,
156 Clause0
157 ]
158 ).
159
160is_prepared_module(M) :-
161 current_predicate(M:expand_macros/0),
162 \+ predicate_property(M:expand_macros, imported_from(_)).
163
170
171include_macros(M, Macro, Expanded) :-
172 catch(M:'$macro'(Macro, Expanded),
173 error(existence_error(matching_rule,
174 M:'$macro'(Macro,_)),_),
175 fail).
176
184
185expand_macros(M, T0, T, P0, P) :-
186 prolog_load_context(module, M),
187 \+ is_define(T0),
188 expand_macros(M, T0, T, P0, P, _State0, _State),
189 T \== T0.
190
191is_define(#Macro), reserved_macro(Macro) => true.
192is_define((#Macro :- _)), reserved_macro(Macro) => true.
193is_define(_) => fail.
194
195:- meta_predicate
196 foldsubterms_pos(6, +, -, +, -, +, -). 197
198expand_macros(M, T0, T, P0, P, State0, State) :-
199 foldsubterms_pos(expand_macro(M), T0, T, P0, P, State0, State).
200
201expand_macro(M, #Macro, T, P0, P, State0, State) =>
202 valid_macro(Macro),
203 arg_pos(1, P0, P1),
204 call_macro(M, Macro, Expanded, P1, P2),
205 expand_macros(M, Expanded, T, P2, P, State0, State).
206expand_macro(_, \#(T0), T, P0, P, State0, State) =>
207 arg_pos(1, P0, P),
208 T = T0, State = State0.
209expand_macro(_, _, _, _, _, _, _) =>
210 fail.
211
212call_macro(M, Macro, Expanded, P0, P) :-
213 b_setval('$macro_position', P0),
214 catch(M:'$macro'(Macro, Expanded),
215 error(existence_error(matching_rule, _), _),
216 macro_failed(Macro, P0)),
217 fix_pos_shape(Macro, Expanded, P0, P),
218 b_setval('$macro_position', 0).
219
220macro_failed(Macro, TermPos) :-
221 macro_error_position(TermPos, Pos),
222 throw(error(existence_error(macro, Macro), Pos)).
223
224macro_error_position(TermPos, Position) :-
225 macro_position(TermPos, AtMacro),
226 !,
227 prolog_load_context(stream, Input),
228 stream_position_to_position_term(Input, AtMacro, Position).
229macro_error_position(_, _).
230
231stream_position_to_position_term(Stream, StreamPos,
232 stream(Stream, Line, LinePos, CharNo)) :-
233 stream_position_data(line_count, StreamPos, Line),
234 stream_position_data(line_position, StreamPos, LinePos),
235 stream_position_data(char_count, StreamPos, CharNo).
236
261
262macro_position(File:Line:LinePos) :-
263 prolog_load_context(file, File),
264 !,
265 ( b_getval('$macro_position', TermPos),
266 macro_position(TermPos, StreamPos)
267 -> stream_position_data(line_count, StreamPos, Line),
268 stream_position_data(line_position, StreamPos, LinePos)
269 ; Line = 0,
270 LinePos = 0
271 ).
272macro_position((-):0:0).
273
274macro_position(TermPos, AtMacro) :-
275 compound(TermPos),
276 arg(1, TermPos, MacroStartCharCount),
277 integer(MacroStartCharCount),
278 prolog_load_context(stream, Input),
279 stream_property(Input, reposition(true)),
280 stream_property(Input, position(Here)),
281 prolog_load_context(term_position, ClauseStart),
282 stream_position_data(char_count, ClauseStart, ClauseStartCharCount),
283 MacroStartCharCount >= ClauseStartCharCount,
284 $,
285 set_stream_position(Input, ClauseStart),
286 Skip is MacroStartCharCount - ClauseStartCharCount,
287 forall(between(1, Skip, _), get_char(Input, _)),
288 stream_property(Input, position(AtMacro)),
289 set_stream_position(Input, Here).
290
296
297fix_pos_shape(_, _, P0, _), var(P0) =>
298 true.
299fix_pos_shape(_, V, P0, P),
300 atomic(V),
301 compound(P0), compound_name_arity(P0, _, Arity), Arity >= 2 =>
302 P = F-T,
303 arg(1, P0, F),
304 arg(2, P0, T).
305fix_pos_shape(_, _, P0, P) =>
306 P = P0.
307
314
315foldsubterms_pos(Goal, Term1, Term2, P1, P2, State0, State) :-
316 call(Goal, Term1, Term2, P1, P2, State0, State),
317 !.
318foldsubterms_pos(Goal, Term1, Term2, P1, P2, State0, State) :-
319 is_dict(Term1),
320 !,
321 pos_parts(dict, P1, P2, VPos1, VPos2),
322 dict_pairs(Term1, Tag, Pairs1),
323 fold_dict_pairs(Pairs1, Pairs2, VPos1, VPos2, Goal, State0, State),
324 dict_pairs(Term2, Tag, Pairs2).
325foldsubterms_pos(Goal, Term1, Term2, P1, P2, State0, State) :-
326 nonvar(Term1), Term1 = [_|_], 327 !,
328 pos_parts(list, P1, P2, list(Elms1,Tail1), list(Elms2,Tail2)),
329 fold_list(Term1, Term2, Elms1, Elms2, Tail1, Tail2, Goal, State0, State).
330foldsubterms_pos(Goal, Term1, Term2, P1, P2, State0, State) :-
331 compound(Term1),
332 !,
333 pos_parts(compound, P1, P2, ArgPos1, ArgPos2),
334 same_functor(Term1, Term2, Arity),
335 foldsubterms_(1, Arity, Goal, Term1, Term2, ArgPos1, ArgPos2, State0, State).
336foldsubterms_pos(_, Term, Term, P, P, State, State).
337
338:- det(fold_dict_pairs/7). 339fold_dict_pairs([], [], KVPos, KVPos, _, State, State).
340fold_dict_pairs([K0-V0|T0], [K-V|T1], KVPos0, KVPos, Goal, State0, State) :-
341 ( nonvar(KVPos0),
342 selectchk(key_value_position(F,T,SF,ST,K0,KP0,VP0), KVPos0,
343 key_value_position(F,T,SF,ST,K, KP, VP), KVPos1)
344 -> true
345 ; true
346 ),
347 foldsubterms_pos(Goal, K0, K, KP0, KP, State0, State1),
348 foldsubterms_pos(Goal, V0, V, VP0, VP, State1, State2),
349 fold_dict_pairs(T0, T1, KVPos1, KVPos, Goal, State2, State).
350
351:- det(fold_list/9). 352fold_list(Var0, Var, EP, EP, TP0, TP, Goal, State0, State) :-
353 var(Var0),
354 !,
355 foldsubterms_pos(Goal, Var0, Var, TP0, TP, State0, State).
356fold_list([], [], [], [], TP, TP, _, State, State) :-
357 !.
358fold_list([H0|T0], [H|T], [EP0|EPT0], [EP1|EPT1], TP1, TP2, Goal, State0, State) :-
359 !,
360 foldsubterms_pos(Goal, H0, H, EP0, EP1, State0, State1),
361 fold_list(T0, T, EPT0, EPT1, TP1, TP2, Goal, State1, State).
362fold_list(T0, T, EP, EP, TP0, TP, Goal, State0, State) :-
363 foldsubterms_pos(Goal, T0, T, TP0, TP, State0, State).
364
365:- det(foldsubterms_/9). 366foldsubterms_(I, Arity, Goal, Term1, Term2, PosIn, PosOut, State0, State) :-
367 I =< Arity,
368 !,
369 ( PosIn = [AP1|APT1]
370 -> PosOut = [AP2|APT2]
371 ; true
372 ),
373 arg(I, Term1, A1),
374 arg(I, Term2, A2),
375 foldsubterms_pos(Goal, A1, A2, AP1, AP2, State0, State1),
376 I2 is I+1,
377 foldsubterms_(I2, Arity, Goal, Term1, Term2, APT1, APT2, State1, State).
378foldsubterms_(_, _, _, _, _, _, [], State, State).
379
380:- det(pos_parts/5). 381pos_parts(_, Var, _, _, _), var(Var) => true.
382pos_parts(Type, parentheses_term_position(F,T,In), PosOut, SubIn, SubOut) =>
383 PosOut = parentheses_term_position(F,T,Out),
384 pos_parts(Type, In, Out, SubIn, SubOut).
385pos_parts(compound, term_position(From, To, FFrom, FTo, SubPos),
386 PosOut, SubIn, SubOut) =>
387 PosOut = term_position(From, To, FFrom, FTo, SubOut),
388 SubIn = SubPos.
389pos_parts(compound, brace_term_position(From, To, ArgPos0),
390 PosOut, SubIn, SubOut) =>
391 PosOut = brace_term_position(From, To, ArgPos),
392 SubIn = [ArgPos0],
393 SubOut = [ArgPos].
394pos_parts(list, list_position(From, To, Elms, Tail),
395 PosOut, SubIn, SubOut) =>
396 PosOut = list_position(From, To, Elms1, Tail1),
397 SubIn = list(Elms, Tail),
398 SubOut = list(Elms1, Tail1).
399pos_parts(dict, dict_position(From, To, TagFrom, TagTo, KVPosIn),
400 PosOut, SubIn, SubOut) =>
401 PosOut = dict_position(From, To, TagFrom, TagTo, SubOut),
402 SubIn = KVPosIn.
403pos_parts(_, _, _, _, _) =>
404 true. 405
406arg_pos(_, TermPos, _), var(TermPos) => true.
407arg_pos(I, parentheses_term_position(_,_,TP), AP) =>
408 arg_pos(I, TP, AP).
409arg_pos(I, term_position(_,_,_,_,APL), AP) =>
410 ignore(nth1(I, APL, AP)).
411arg_pos(1, brace_term_position(_,_,TPA), AP) =>
412 AP = TPA.
413arg_pos(_,_,_) =>
414 true.
415
416 419
422
423system:term_expansion(In, Out) :-
424 is_define(In),
425 prolog_load_context(module, M),
426 predicate_property(M:expand_macros(_,_,_,_,_), imported_from(macros)),
427 $,
428 define_macro(In, Out).
429
430
431 434
435:- multifile prolog:error_message//1. 436
437prolog:error_message(domain_error(macro, Macro)) -->
438 [ 'Invalid macro: ~p'-[Macro] ].
439prolog:error_message(existence_error(macro, Macro)) -->
440 [ 'Failed to expand macro: ~p'-[Macro] ].
441
442
443 446
447:- multifile prolog_colour:term_colours/2. 448
449prolog_colour:term_colours(#define(_Macro, _Replacement),
450 expanded - [ expanded - [ classify, classify ]]).
451prolog_colour:term_colours((#define(_Macro, _Replacement) :- _Body),
452 neck(:-) - [ expanded - [ expanded - [ classify, classify ]],
453 body
454 ]).
455prolog_colour:term_colours(#import(_File),
456 expanded - [ expanded - [ file ]])