35
36:- module((record),
37 [ (record)/1, 38 current_record/2, 39 current_record_predicate/2, 40 op(1150, fx, record)
41 ]). 42:- autoload(library(error),
43 [ instantiation_error/1,
44 current_type/3,
45 domain_error/2,
46 must_be/2
47 ]). 48:- autoload(library(lists),[member/2]). 49
50
72
73:- multifile
74 error:has_type/2,
75 prolog:generated_predicate/1. 76
77error:has_type(record(M:Name), X) :-
78 is_record(Name, M, X).
79
80is_record(Name, M, X) :-
81 current_record(Name, M, _, X, IsX),
82 !,
83 call(M:IsX).
84
119
120record(Record) :-
121 Record == '<compiled>',
122 !.
123record(Record) :-
124 throw(error(context_error(nodirective, record(Record)), _)).
125
126
130
131compile_records(Spec,
132 [ (:- record('<compiled>')) 133 | Clauses 134 ]) :-
135 phrase(compile_records(Spec), Clauses).
136
137compile_records(Var) -->
138 { var(Var),
139 !,
140 instantiation_error(Var)
141 }.
142compile_records((A,B)) -->
143 compile_record(A),
144 compile_records(B).
145compile_records(A) -->
146 compile_record(A).
147
151
152compile_record(RecordDef) -->
153 { RecordDef =.. [Constructor|Args],
154 defaults(Args, Defs, TypedArgs),
155 types(TypedArgs, Names, Types),
156 atom_concat(default_, Constructor, DefName),
157 atom_concat(Constructor, '_data', DataName),
158 DefRecord =.. [Constructor|Defs],
159 DefClause =.. [DefName,DefRecord],
160 length(Names, Arity)
161 },
162 [ DefClause ],
163 access_predicates(Names, 1, Arity, Constructor),
164 data_predicate(Names, 1, Arity, Constructor, DataName),
165 set_predicates(Names, 1, Arity, Types, Constructor),
166 set_field_predicates(Names, 1, Arity, Types, Constructor),
167 make_predicate(Constructor),
168 is_predicate(Constructor, Types),
169 current_clause(RecordDef).
170
171:- meta_predicate
172 current_record(?, :),
173 current_record_predicate(?, :). 174:- multifile
175 current_record/5. 176
182
183current_record(Name, M:Term) :-
184 current_record(Name, M, Term, _, _).
185
186current_clause(RecordDef) -->
187 { prolog_load_context(module, M),
188 functor(RecordDef, Name, _),
189 atom_concat(is_, Name, IsName),
190 IsX =.. [IsName, X]
191 },
192 [ (record):current_record(Name, M, RecordDef, X, IsX)
193 ].
194
195
201
202current_record_predicate(Record, M:PI) :-
203 ( ground(PI)
204 -> Det = true
205 ; Det = false
206 ),
207 current_record(Record, M:RecordDef),
208 ( general_record_pred(Record, M:PI)
209 ; RecordDef =.. [_|Args],
210 defaults(Args, _Defs, TypedArgs),
211 types(TypedArgs, Names, _Types),
212 member(Field, Names),
213 field_record_pred(Record, Field, M:PI)
214 ),
215 ( Det == true
216 -> !
217 ; true
218 ).
219
220general_record_pred(Record, _:Name/1) :-
221 atom_concat(is_, Record, Name).
222general_record_pred(Record, _:Name/1) :-
223 atom_concat(default_, Record, Name).
224general_record_pred(Record, _:Name/A) :-
225 member(A, [2,3]),
226 atom_concat(make_, Record, Name).
227general_record_pred(Record, _:Name/3) :-
228 atom_concat(Record, '_data', Name).
229general_record_pred(Record, _:Name/A) :-
230 member(A, [3,4]),
231 atomic_list_concat([set_, Record, '_fields'], Name).
232general_record_pred(Record, _:Name/3) :-
233 atomic_list_concat([set_, Record, '_field'], Name).
234
235field_record_pred(Record, Field, _:Name/2) :-
236 atomic_list_concat([Record, '_', Field], Name).
237field_record_pred(Record, Field, _:Name/A) :-
238 member(A, [2,3]),
239 atomic_list_concat([set_, Field, '_of_', Record], Name).
240field_record_pred(Record, Field, _:Name/2) :-
241 atomic_list_concat([nb_set_, Field, '_of_', Record], Name).
242
243prolog:generated_predicate(P) :-
244 current_record_predicate(_, P).
245
273
274make_predicate(Constructor) -->
275 { atomic_list_concat([make_, Constructor], MakePredName),
276 atomic_list_concat([default_, Constructor], DefPredName),
277 atomic_list_concat([set_, Constructor, '_fields'], SetFieldsName),
278 atomic_list_concat([set_, Constructor, '_field'], SetFieldName),
279 MakeHead3 =.. [MakePredName, Fields, Record],
280 MakeHead4 =.. [MakePredName, Fields, Record, []],
281 MakeClause3 = (MakeHead3 :- MakeHead4),
282 MakeHead =.. [MakePredName, Fields, Record, RestFields],
283 DefGoal =.. [DefPredName, Record0],
284 SetGoal =.. [SetFieldsName, Fields, Record0, Record, RestFields],
285 MakeClause = (MakeHead :- DefGoal, SetGoal),
286 SetHead3 =.. [SetFieldsName, Fields, R0, R],
287 SetHead4 =.. [SetFieldsName, Fields, R0, R, []],
288 SetClause0 = (SetHead3 :- SetHead4),
289 SetClause1 =.. [SetFieldsName, [], R, R, []],
290 SetHead2 =.. [SetFieldsName, [H|T], R0, R, RF],
291 SetGoal2a =.. [SetFieldName, H, R0, R1],
292 SetGoal2b =.. [SetFieldsName, T, R1, R, RF],
293 SetGoal2c =.. [SetFieldsName, T, R0, R, RF1],
294 SetClause2 = (SetHead2 :- (SetGoal2a -> SetGoal2b ; RF=[H|RF1], SetGoal2c))
295 },
296 [ MakeClause3, MakeClause, SetClause0, SetClause1, SetClause2 ].
297
301
302is_predicate(Constructor, Types) -->
303 { type_checks(Types, Vars, Body0),
304 clean_body(Body0, Body),
305 Term =.. [Constructor|Vars],
306 atom_concat(is_, Constructor, Name),
307 Head =.. [Name,VarOrTerm]
308 },
309 ( { Body == true }
310 -> [ (Head :- nonvar(VarOrTerm), VarOrTerm = Term) ]
311 ; [ (Head :- nonvar(VarOrTerm), VarOrTerm = Term, Body) ]
312 ).
313
314type_checks([], [], true).
315type_checks([any|T], [_|Vars], Body) :-
316 type_checks(T, Vars, Body).
317type_checks([Type|T], [V|Vars], (Goal, Body)) :-
318 type_goal(Type, V, Goal),
319 type_checks(T, Vars, Body).
320
324
325type_goal(Type, Var, Body) :-
326 current_type(Type, Var, Body),
327 !.
328type_goal(record(Record), Var, Body) :-
329 !,
330 atom_concat(is_, Record, Pred),
331 Body =.. [Pred,Var].
332type_goal(Record, Var, Body) :-
333 atom(Record),
334 !,
335 atom_concat(is_, Record, Pred),
336 Body =.. [Pred,Var].
337type_goal(Type, _, _) :-
338 domain_error(type, Type).
339
340
341clean_body(Var, G) :-
342 var(Var),
343 !,
344 G = Var.
345clean_body(M:C0, G) :-
346 nonvar(C0),
347 control(C0),
348 !,
349 C0 =.. [Name|Args0],
350 clean_args(Args0, M, Args),
351 G =.. [Name|Args].
352clean_body((A0,true), A) :-
353 !,
354 clean_body(A0, A).
355clean_body((true,A0), A) :-
356 !,
357 clean_body(A0, A).
358clean_body(C0, G) :-
359 control(C0),
360 !,
361 C0 =.. [Name|Args0],
362 clean_args(Args0, Args),
363 G =.. [Name|Args].
364clean_body(_:A, A) :-
365 predicate_property(system:A, built_in),
366 \+ predicate_property(system:A, meta_predicate(_)),
367 !.
368clean_body(A, A).
369
370clean_args([], []).
371clean_args([H0|T0], [H|T]) :-
372 clean_body(H0, H),
373 clean_args(T0, T).
374
375clean_args([], _, []).
376clean_args([H0|T0], M, [H|T]) :-
377 clean_body(M:H0, H),
378 clean_args(T0, M, T).
379
380control((_,_)).
381control((_;_)).
382control((_->_)).
383control((_*->_)).
384control(\+(_)).
385
386
390
391access_predicates([], _, _, _) -->
392 [].
393access_predicates([Name|NT], I, Arity, Constructor) -->
394 { atomic_list_concat([Constructor, '_', Name], PredName),
395 functor(Record, Constructor, Arity),
396 arg(I, Record, Value),
397 Clause =.. [PredName, Record, Value],
398 I2 is I + 1
399 },
400 [Clause],
401 access_predicates(NT, I2, Arity, Constructor).
402
403
407
408data_predicate([], _, _, _, _) -->
409 [].
410data_predicate([Name|NT], I, Arity, Constructor, DataName) -->
411 { functor(Record, Constructor, Arity),
412 arg(I, Record, Value),
413 Clause =.. [DataName, Name, Record, Value],
414 I2 is I + 1
415 },
416 [Clause],
417 data_predicate(NT, I2, Arity, Constructor, DataName).
418
419
426
427set_predicates([], _, _, _, _) -->
428 [].
429set_predicates([Name|NT], I, Arity, [Type|TT], Constructor) -->
430 { atomic_list_concat(['set_', Name, '_of_', Constructor], PredName),
431 atomic_list_concat(['nb_set_', Name, '_of_', Constructor], NBPredName),
432 length(Args, Arity),
433 replace_nth(I, Args, Value, NewArgs),
434 Old =.. [Constructor|Args],
435 New =.. [Constructor|NewArgs],
436 Head =.. [PredName, Value, Old, New],
437 SetHead =.. [PredName, Value, Term],
438 NBSetHead =.. [NBPredName, Value, Term],
439 ( Type == any
440 -> Clause = Head,
441 SetClause = (SetHead :- setarg(I, Term, Value)),
442 NBSetClause = (NBSetHead :- nb_setarg(I, Term, Value))
443 ; type_check(Type, Value, MustBe),
444 Clause = (Head :- MustBe),
445 SetClause = (SetHead :- MustBe,
446 setarg(I, Term, Value)),
447 NBSetClause = (NBSetHead :- MustBe,
448 nb_setarg(I, Term, Value))
449 ),
450 I2 is I + 1
451 },
452 [ Clause, SetClause, NBSetClause ],
453 set_predicates(NT, I2, Arity, TT, Constructor).
454
455type_check(Type, Value, must_be(Type, Value)) :-
456 current_type(Type, Value, _),
457 !.
458type_check(record(Spec), Value, must_be(record(M:Name), Value)) :-
459 !,
460 prolog_load_context(module, C),
461 strip_module(C:Spec, M, Name).
462type_check(Atom, Value, Check) :-
463 atom(Atom),
464 !,
465 type_check(record(Atom), Value, Check).
466
467
473
474set_field_predicates([], _, _, _, _) -->
475 [].
476set_field_predicates([Name|NT], I, Arity, [Type|TT], Constructor) -->
477 { atomic_list_concat(['set_', Constructor, '_field'], FieldPredName),
478 length(Args, Arity),
479 replace_nth(I, Args, Value, NewArgs),
480 Old =.. [Constructor|Args],
481 New =.. [Constructor|NewArgs],
482 NameTerm =.. [Name, Value],
483 SetFieldHead =.. [FieldPredName, NameTerm, Old, New],
484 ( Type == any
485 -> SetField = SetFieldHead
486 ; type_check(Type, Value, MustBe),
487 SetField = (SetFieldHead :- MustBe)
488 ),
489 I2 is I + 1
490 },
491 [ SetField ],
492 set_field_predicates(NT, I2, Arity, TT, Constructor).
493
494
498
499replace_nth(1, [_|T], V, [V|T]) :- !.
500replace_nth(I, [H|T0], V, [H|T]) :-
501 I2 is I - 1,
502 replace_nth(I2, T0, V, T).
503
504
508
509defaults([], [], []).
510defaults([Arg=Default|T0], [Default|TD], [Arg|TA]) :-
511 !,
512 defaults(T0, TD, TA).
513defaults([Arg|T0], [_|TD], [Arg|TA]) :-
514 defaults(T0, TD, TA).
515
516
520
521types([], [], []).
522types([Name:Type|T0], [Name|TN], [Type|TT]) :-
523 !,
524 must_be(atom, Name),
525 types(T0, TN, TT).
526types([Name|T0], [Name|TN], [any|TT]) :-
527 must_be(atom, Name),
528 types(T0, TN, TT).
529
530
531 534
535:- multifile
536 system:term_expansion/2,
537 sandbox:safe_primitive/1. 538:- dynamic
539 system:term_expansion/2. 540
541system:term_expansion((:- record(Record)), Clauses) :-
542 compile_records(Record, Clauses).
543
544sandbox:safe_primitive((record):is_record(_,_,_))