34
35:- module((record),
36 [ (record)/1, 37 current_record/2, 38 current_record_predicate/2, 39 op(1150, fx, record)
40 ]). 41:- autoload(library(error),
42 [ instantiation_error/1,
43 current_type/3,
44 domain_error/2,
45 must_be/2
46 ]). 47:- autoload(library(lists),[member/2]). 48
49
71
72:- multifile
73 error:has_type/2,
74 prolog:generated_predicate/1. 75
76error:has_type(record(M:Name), X) :-
77 is_record(Name, M, X).
78
79is_record(Name, M, X) :-
80 current_record(Name, M, _, X, IsX),
81 !,
82 call(M:IsX).
83
108
109record(Record) :-
110 Record == '<compiled>',
111 !.
112record(Record) :-
113 throw(error(context_error(nodirective, record(Record)), _)).
114
115
119
120compile_records(Spec,
121 [ (:- record('<compiled>')) 122 | Clauses 123 ]) :-
124 phrase(compile_records(Spec), Clauses).
126
127compile_records(Var) -->
128 { var(Var),
129 !,
130 instantiation_error(Var)
131 }.
132compile_records((A,B)) -->
133 compile_record(A),
134 compile_records(B).
135compile_records(A) -->
136 compile_record(A).
137
141
142compile_record(RecordDef) -->
143 { RecordDef =.. [Constructor|Args],
144 defaults(Args, Defs, TypedArgs),
145 types(TypedArgs, Names, Types),
146 atom_concat(default_, Constructor, DefName),
147 atom_concat(Constructor, '_data', DataName),
148 DefRecord =.. [Constructor|Defs],
149 DefClause =.. [DefName,DefRecord],
150 length(Names, Arity)
151 },
152 [ DefClause ],
153 access_predicates(Names, 1, Arity, Constructor),
154 data_predicate(Names, 1, Arity, Constructor, DataName),
155 set_predicates(Names, 1, Arity, Types, Constructor),
156 set_field_predicates(Names, 1, Arity, Types, Constructor),
157 make_predicate(Constructor),
158 is_predicate(Constructor, Types),
159 current_clause(RecordDef).
160
161:- meta_predicate
162 current_record(?, :),
163 current_record_predicate(?, :). 164:- multifile
165 current_record/5. 166
172
173current_record(Name, M:Term) :-
174 current_record(Name, M, Term, _, _).
175
176current_clause(RecordDef) -->
177 { prolog_load_context(module, M),
178 functor(RecordDef, Name, _),
179 atom_concat(is_, Name, IsName),
180 IsX =.. [IsName, X]
181 },
182 [ (record):current_record(Name, M, RecordDef, X, IsX)
183 ].
184
185
191
192current_record_predicate(Record, M:PI) :-
193 ( ground(PI)
194 -> Det = true
195 ; Det = false
196 ),
197 current_record(Record, M:RecordDef),
198 ( general_record_pred(Record, M:PI)
199 ; RecordDef =.. [_|Args],
200 defaults(Args, _Defs, TypedArgs),
201 types(TypedArgs, Names, _Types),
202 member(Field, Names),
203 field_record_pred(Record, Field, M:PI)
204 ),
205 ( Det == true
206 -> !
207 ; true
208 ).
209
210general_record_pred(Record, _:Name/1) :-
211 atom_concat(is_, Record, Name).
212general_record_pred(Record, _:Name/1) :-
213 atom_concat(default_, Record, Name).
214general_record_pred(Record, _:Name/A) :-
215 member(A, [2,3]),
216 atom_concat(make_, Record, Name).
217general_record_pred(Record, _:Name/3) :-
218 atom_concat(Record, '_data', Name).
219general_record_pred(Record, _:Name/A) :-
220 member(A, [3,4]),
221 atomic_list_concat([set_, Record, '_fields'], Name).
222general_record_pred(Record, _:Name/3) :-
223 atomic_list_concat([set_, Record, '_field'], Name).
224
225field_record_pred(Record, Field, _:Name/2) :-
226 atomic_list_concat([Record, '_', Field], Name).
227field_record_pred(Record, Field, _:Name/A) :-
228 member(A, [2,3]),
229 atomic_list_concat([set_, Field, '_of_', Record], Name).
230field_record_pred(Record, Field, _:Name/2) :-
231 atomic_list_concat([nb_set_, Field, '_of_', Record], Name).
232
233prolog:generated_predicate(P) :-
234 current_record_predicate(_, P).
235
263
264make_predicate(Constructor) -->
265 { atomic_list_concat([make_, Constructor], MakePredName),
266 atomic_list_concat([default_, Constructor], DefPredName),
267 atomic_list_concat([set_, Constructor, '_fields'], SetFieldsName),
268 atomic_list_concat([set_, Constructor, '_field'], SetFieldName),
269 MakeHead3 =.. [MakePredName, Fields, Record],
270 MakeHead4 =.. [MakePredName, Fields, Record, []],
271 MakeClause3 = (MakeHead3 :- MakeHead4),
272 MakeHead =.. [MakePredName, Fields, Record, RestFields],
273 DefGoal =.. [DefPredName, Record0],
274 SetGoal =.. [SetFieldsName, Fields, Record0, Record, RestFields],
275 MakeClause = (MakeHead :- DefGoal, SetGoal),
276 SetHead3 =.. [SetFieldsName, Fields, R0, R],
277 SetHead4 =.. [SetFieldsName, Fields, R0, R, []],
278 SetClause0 = (SetHead3 :- SetHead4),
279 SetClause1 =.. [SetFieldsName, [], R, R, []],
280 SetHead2 =.. [SetFieldsName, [H|T], R0, R, RF],
281 SetGoal2a =.. [SetFieldName, H, R0, R1],
282 SetGoal2b =.. [SetFieldsName, T, R1, R, RF],
283 SetGoal2c =.. [SetFieldsName, T, R0, R, RF1],
284 SetClause2 = (SetHead2 :- (SetGoal2a -> SetGoal2b ; RF=[H|RF1], SetGoal2c))
285 },
286 [ MakeClause3, MakeClause, SetClause0, SetClause1, SetClause2 ].
287
291
292is_predicate(Constructor, Types) -->
293 { type_checks(Types, Vars, Body0),
294 clean_body(Body0, Body),
295 Term =.. [Constructor|Vars],
296 atom_concat(is_, Constructor, Name),
297 Head1 =.. [Name,Var],
298 Head2 =.. [Name,Term]
299 },
300 [ (Head1 :- var(Var), !, fail) ],
301 ( { Body == true }
302 -> [ Head2 ]
303 ; [ (Head2 :- Body) ]
304 ).
305
306type_checks([], [], true).
307type_checks([any|T], [_|Vars], Body) :-
308 type_checks(T, Vars, Body).
309type_checks([Type|T], [V|Vars], (Goal, Body)) :-
310 type_goal(Type, V, Goal),
311 type_checks(T, Vars, Body).
312
316
317type_goal(Type, Var, Body) :-
318 current_type(Type, Var, Body),
319 !.
320type_goal(record(Record), Var, Body) :-
321 !,
322 atom_concat(is_, Record, Pred),
323 Body =.. [Pred,Var].
324type_goal(Record, Var, Body) :-
325 atom(Record),
326 !,
327 atom_concat(is_, Record, Pred),
328 Body =.. [Pred,Var].
329type_goal(Type, _, _) :-
330 domain_error(type, Type).
331
332
333clean_body(Var, G) :-
334 var(Var),
335 !,
336 G = Var.
337clean_body(M:C0, G) :-
338 nonvar(C0),
339 control(C0),
340 !,
341 C0 =.. [Name|Args0],
342 clean_args(Args0, M, Args),
343 G =.. [Name|Args].
344clean_body((A0,true), A) :-
345 !,
346 clean_body(A0, A).
347clean_body((true,A0), A) :-
348 !,
349 clean_body(A0, A).
350clean_body(C0, G) :-
351 control(C0),
352 !,
353 C0 =.. [Name|Args0],
354 clean_args(Args0, Args),
355 G =.. [Name|Args].
356clean_body(_:A, A) :-
357 predicate_property(system:A, built_in),
358 \+ predicate_property(system:A, meta_predicate(_)),
359 !.
360clean_body(A, A).
361
362clean_args([], []).
363clean_args([H0|T0], [H|T]) :-
364 clean_body(H0, H),
365 clean_args(T0, T).
366
367clean_args([], _, []).
368clean_args([H0|T0], M, [H|T]) :-
369 clean_body(M:H0, H),
370 clean_args(T0, M, T).
371
372control((_,_)).
373control((_;_)).
374control((_->_)).
375control((_*->_)).
376control(\+(_)).
377
378
382
383access_predicates([], _, _, _) -->
384 [].
385access_predicates([Name|NT], I, Arity, Constructor) -->
386 { atomic_list_concat([Constructor, '_', Name], PredName),
387 functor(Record, Constructor, Arity),
388 arg(I, Record, Value),
389 Clause =.. [PredName, Record, Value],
390 I2 is I + 1
391 },
392 [Clause],
393 access_predicates(NT, I2, Arity, Constructor).
394
395
399
400data_predicate([], _, _, _, _) -->
401 [].
402data_predicate([Name|NT], I, Arity, Constructor, DataName) -->
403 { functor(Record, Constructor, Arity),
404 arg(I, Record, Value),
405 Clause =.. [DataName, Name, Record, Value],
406 I2 is I + 1
407 },
408 [Clause],
409 data_predicate(NT, I2, Arity, Constructor, DataName).
410
411
418
419set_predicates([], _, _, _, _) -->
420 [].
421set_predicates([Name|NT], I, Arity, [Type|TT], Constructor) -->
422 { atomic_list_concat(['set_', Name, '_of_', Constructor], PredName),
423 atomic_list_concat(['nb_set_', Name, '_of_', Constructor], NBPredName),
424 length(Args, Arity),
425 replace_nth(I, Args, Value, NewArgs),
426 Old =.. [Constructor|Args],
427 New =.. [Constructor|NewArgs],
428 Head =.. [PredName, Value, Old, New],
429 SetHead =.. [PredName, Value, Term],
430 NBSetHead =.. [NBPredName, Value, Term],
431 ( Type == any
432 -> Clause = Head,
433 SetClause = (SetHead :- setarg(I, Term, Value)),
434 NBSetClause = (NBSetHead :- nb_setarg(I, Term, Value))
435 ; type_check(Type, Value, MustBe),
436 Clause = (Head :- MustBe),
437 SetClause = (SetHead :- MustBe,
438 setarg(I, Term, Value)),
439 NBSetClause = (NBSetHead :- MustBe,
440 nb_setarg(I, Term, Value))
441 ),
442 I2 is I + 1
443 },
444 [ Clause, SetClause, NBSetClause ],
445 set_predicates(NT, I2, Arity, TT, Constructor).
446
447type_check(Type, Value, must_be(Type, Value)) :-
448 current_type(Type, Value, _),
449 !.
450type_check(record(Spec), Value, must_be(record(M:Name), Value)) :-
451 !,
452 prolog_load_context(module, C),
453 strip_module(C:Spec, M, Name).
454type_check(Atom, Value, Check) :-
455 atom(Atom),
456 !,
457 type_check(record(Atom), Value, Check).
458
459
465
466set_field_predicates([], _, _, _, _) -->
467 [].
468set_field_predicates([Name|NT], I, Arity, [Type|TT], Constructor) -->
469 { atomic_list_concat(['set_', Constructor, '_field'], FieldPredName),
470 length(Args, Arity),
471 replace_nth(I, Args, Value, NewArgs),
472 Old =.. [Constructor|Args],
473 New =.. [Constructor|NewArgs],
474 NameTerm =.. [Name, Value],
475 SetFieldHead =.. [FieldPredName, NameTerm, Old, New],
476 ( Type == any
477 -> SetField = SetFieldHead
478 ; type_check(Type, Value, MustBe),
479 SetField = (SetFieldHead :- MustBe)
480 ),
481 I2 is I + 1
482 },
483 [ SetField ],
484 set_field_predicates(NT, I2, Arity, TT, Constructor).
485
486
490
491replace_nth(1, [_|T], V, [V|T]) :- !.
492replace_nth(I, [H|T0], V, [H|T]) :-
493 I2 is I - 1,
494 replace_nth(I2, T0, V, T).
495
496
500
501defaults([], [], []).
502defaults([Arg=Default|T0], [Default|TD], [Arg|TA]) :-
503 !,
504 defaults(T0, TD, TA).
505defaults([Arg|T0], [_|TD], [Arg|TA]) :-
506 defaults(T0, TD, TA).
507
508
512
513types([], [], []).
514types([Name:Type|T0], [Name|TN], [Type|TT]) :-
515 !,
516 must_be(atom, Name),
517 types(T0, TN, TT).
518types([Name|T0], [Name|TN], [any|TT]) :-
519 must_be(atom, Name),
520 types(T0, TN, TT).
521
522
523 526
527:- multifile
528 system:term_expansion/2,
529 sandbox:safe_primitive/1. 530:- dynamic
531 system:term_expansion/2. 532
533system:term_expansion((:- record(Record)), Clauses) :-
534 compile_records(Record, Clauses).
535
536sandbox:safe_primitive((record):is_record(_,_,_))