kibi-core 0.24.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/schema/validation.pl +3 -1
- package/src/certificate_check.pl +38 -4
- package/src/certificates.pl +10 -2
- package/src/checks.pl +178 -17
- package/src/claim_obligations.pl +371 -28
- package/src/code_certificates.pl +243 -4
- package/src/discovery.pl +65 -23
- package/src/intervals.pl +35 -14
- package/src/kb.pl +337 -38
- package/src/kb_consistency.pl +15 -0
- package/src/logic_ir.pl +130 -161
- package/src/model_check.pl +776 -0
- package/src/requirement_proof.pl +101 -13
- package/src/rule_registry.pl +1 -1
- package/src/status.pl +16 -0
- package/src/units.pl +1 -3
package/src/logic_ir.pl
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
logic_rule_safety/2,
|
|
9
9
|
logic_rule_render/2,
|
|
10
10
|
logic_rule_semantic_key/2,
|
|
11
|
-
logic_derive/2,
|
|
12
11
|
logic_rule_conflict/3,
|
|
13
12
|
logic_rule_conflict/4,
|
|
14
13
|
logic_rule_conflict_witness/3,
|
|
@@ -22,23 +21,59 @@
|
|
|
22
21
|
]).
|
|
23
22
|
|
|
24
23
|
:- use_module(library(http/json)).
|
|
25
|
-
:- use_module(library(aggregate)).
|
|
26
24
|
:- use_module(library(clpfd)).
|
|
27
25
|
:- use_module('intervals.pl', [numeric_constraints_satisfiable/2, numeric_constraint_entailed/3, numeric_constraint_holds/3]).
|
|
28
|
-
:- use_module('kb.pl', [
|
|
26
|
+
:- use_module('kb.pl', [predicate_schema_keys/4]).
|
|
29
27
|
|
|
30
28
|
%% logic_rule_from_props(+Props, -Rule)
|
|
31
29
|
% Props is an entity property list from a fact_kind=rule fact.
|
|
32
30
|
logic_rule_from_props(Props, Rule) :-
|
|
33
31
|
memberchk(rule_ir=Raw, Props),
|
|
34
32
|
raw_json_atom(Raw, Json),
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
logic_rule_from_json(Json, Rule).
|
|
34
|
+
|
|
35
|
+
% A rule body is decoded once per text: the feasibility analysis of every
|
|
36
|
+
% scenario and every requirement's proof ladder read the same rule facts.
|
|
37
|
+
% Decoding is a pure function of the text, so the memo never goes stale; it
|
|
38
|
+
% is bounded so a long-lived engine cannot grow it without limit.
|
|
39
|
+
:- dynamic logic_rule_memo/2.
|
|
40
|
+
:- dynamic logic_rule_memo_size/1.
|
|
41
|
+
|
|
42
|
+
% mutalog-disable-next-line literal: the bound is a tuning value; any size gives the same answers
|
|
43
|
+
logic_rule_memo_limit(16384).
|
|
44
|
+
|
|
45
|
+
logic_rule_from_json(Json, Rule) :-
|
|
46
|
+
( logic_rule_memo(Json, Stored)
|
|
47
|
+
-> Stored = rule(Rule)
|
|
48
|
+
; ( catch(atom_json_dict(Json, Dict, [value_string_as(atom)]), _, fail),
|
|
49
|
+
json_rule(Dict, Rule0)
|
|
50
|
+
-> Stored = rule(Rule0)
|
|
51
|
+
; Stored = invalid
|
|
52
|
+
),
|
|
53
|
+
logic_rule_remember(Json, Stored),
|
|
54
|
+
Stored = rule(Rule)
|
|
55
|
+
).
|
|
56
|
+
|
|
57
|
+
logic_rule_remember(Json, Stored) :-
|
|
58
|
+
( logic_rule_memo_size(Size)
|
|
59
|
+
-> true
|
|
60
|
+
; Size = 0
|
|
61
|
+
),
|
|
62
|
+
logic_rule_memo_limit(Limit),
|
|
63
|
+
( Size >= Limit
|
|
64
|
+
-> retractall(logic_rule_memo(_, _)),
|
|
65
|
+
retractall(logic_rule_memo_size(_)),
|
|
66
|
+
Next = 1
|
|
67
|
+
; retractall(logic_rule_memo_size(_)),
|
|
68
|
+
Next is Size + 1
|
|
69
|
+
),
|
|
70
|
+
assertz(logic_rule_memo(Json, Stored)),
|
|
71
|
+
assertz(logic_rule_memo_size(Next)).
|
|
37
72
|
|
|
38
|
-
|
|
39
|
-
raw_json_atom(
|
|
40
|
-
raw_json_atom(Value,
|
|
41
|
-
raw_json_atom(
|
|
73
|
+
% Anything that is not JSON text is rejected by atom_json_dict/3.
|
|
74
|
+
raw_json_atom(^^(Value, _), Text) :- !, raw_json_atom(Value, Text).
|
|
75
|
+
raw_json_atom(literal(type(_, Value)), Text) :- !, raw_json_atom(Value, Text).
|
|
76
|
+
raw_json_atom(Text, Text).
|
|
42
77
|
|
|
43
78
|
json_rule(Dict, rule(Kind, Modality, Head, Body, Exceptions, Scope, From, To, SchemaId, Variables)) :-
|
|
44
79
|
json_rule_keys(Dict),
|
|
@@ -71,7 +106,7 @@ json_quantifier(Value, Value) :- memberchk(Value, [forall, exists]), !.
|
|
|
71
106
|
json_quantifier(Value, Quantifier) :- string(Value), atom_string(Atom, Value), json_quantifier(Atom, Quantifier).
|
|
72
107
|
|
|
73
108
|
json_modality(Value, Value) :- memberchk(Value, [assert, deny, oblige, permit, forbid]), !.
|
|
74
|
-
json_modality(Value,
|
|
109
|
+
json_modality(Value, Modality) :- string(Value), atom_string(Atom, Value), json_modality(Atom, Modality).
|
|
75
110
|
|
|
76
111
|
json_atom(Dict, atom(Namespace, Name, Args, Polarity, ClosedWorld)) :-
|
|
77
112
|
is_dict(Dict),
|
|
@@ -89,7 +124,7 @@ json_atom(Dict, atom(Namespace, Name, Args, Polarity, ClosedWorld)) :-
|
|
|
89
124
|
( get_dict(closedWorld, Dict, RawClosedWorld) -> memberchk(RawClosedWorld, [true, false]), ClosedWorld = RawClosedWorld ; ClosedWorld = false ).
|
|
90
125
|
|
|
91
126
|
json_atom_name(Value, Atom) :- atom(Value), !, Atom = Value.
|
|
92
|
-
json_atom_name(Value, Atom) :- string(Value),
|
|
127
|
+
json_atom_name(Value, Atom) :- string(Value), atom_string(Atom, Value).
|
|
93
128
|
|
|
94
129
|
valid_variable_name(Name) :-
|
|
95
130
|
atom(Name),
|
|
@@ -119,11 +154,7 @@ valid_predicate_name(Name) :-
|
|
|
119
154
|
char_type(First, lower),
|
|
120
155
|
forall(member(Char, Rest), allowed_predicate_char(Char)).
|
|
121
156
|
|
|
122
|
-
allowed_predicate_char(Char) :- char_type(Char, alnum),
|
|
123
|
-
allowed_predicate_char('_').
|
|
124
|
-
allowed_predicate_char('-').
|
|
125
|
-
allowed_predicate_char('/').
|
|
126
|
-
allowed_predicate_char('.').
|
|
157
|
+
allowed_predicate_char(Char) :- char_type(Char, alnum) -> true ; memberchk(Char, ['_', '-', '/', '.']).
|
|
127
158
|
|
|
128
159
|
json_text(Value, Expected) :- atom(Value), !, Value = Expected.
|
|
129
160
|
json_text(Value, Expected) :- string(Value), atom_string(Expected, Value).
|
|
@@ -145,7 +176,7 @@ json_term(Dict, number(Value, Unit)) :-
|
|
|
145
176
|
( get_dict(unit, Dict, RawUnit) -> json_atom_name(RawUnit, Unit), valid_type_name(Unit) ; Unit = none ), !.
|
|
146
177
|
json_term(Dict, duration(Value, Unit)) :-
|
|
147
178
|
is_dict(Dict), dict_keys_allowed(Dict, [kind, value, unit]), get_dict(kind, Dict, Kind), json_text(Kind, duration),
|
|
148
|
-
get_dict(value, Dict, Value), number(Value), Value
|
|
179
|
+
get_dict(value, Dict, Value), number(Value), Value >= 0,
|
|
149
180
|
get_dict(unit, Dict, RawUnit), json_atom_name(RawUnit, Unit), memberchk(Unit, [ms, s, m, h, d, w]), !.
|
|
150
181
|
json_term(Dict, timestamp(Value)) :-
|
|
151
182
|
is_dict(Dict), dict_keys_allowed(Dict, [kind, value]), get_dict(kind, Dict, Kind), json_text(Kind, timestamp),
|
|
@@ -153,15 +184,15 @@ json_term(Dict, timestamp(Value)) :-
|
|
|
153
184
|
json_term(Dict, interval(Start, End)) :-
|
|
154
185
|
is_dict(Dict), dict_keys_allowed(Dict, [kind, start, end]), get_dict(kind, Dict, Kind), json_text(Kind, interval),
|
|
155
186
|
get_dict(start, Dict, RawStart), json_timestamp(RawStart, Start),
|
|
156
|
-
get_dict(end, Dict, RawEnd), json_timestamp(RawEnd, End), Start @=< End
|
|
187
|
+
get_dict(end, Dict, RawEnd), json_timestamp(RawEnd, End), Start @=< End.
|
|
157
188
|
|
|
158
|
-
json_expression(Dict, Atom) :- is_dict(Dict),
|
|
189
|
+
json_expression(Dict, Atom) :- is_dict(Dict), get_dict(kind, Dict, Kind), json_text(Kind, atom), !, json_atom(Dict, Atom).
|
|
159
190
|
json_expression(Dict, all(Items)) :- is_dict(Dict), dict_keys_allowed(Dict, [kind, items]), get_dict(kind, Dict, Kind), json_text(Kind, all), !, get_dict(items, Dict, Raw), is_list(Raw), Raw \= [], length(Raw, Count), Count =< 32, maplist(json_expression, Raw, Items).
|
|
160
191
|
json_expression(Dict, any(Items)) :- is_dict(Dict), dict_keys_allowed(Dict, [kind, items]), get_dict(kind, Dict, Kind), json_text(Kind, any), !, get_dict(items, Dict, Raw), is_list(Raw), Raw \= [], length(Raw, Count), Count =< 32, maplist(json_expression, Raw, Items).
|
|
161
192
|
json_expression(Dict, not(Atom)) :- is_dict(Dict), dict_keys_allowed(Dict, [kind, item]), get_dict(kind, Dict, Kind), json_text(Kind, not), !, get_dict(item, Dict, Raw), json_atom(Raw, Atom).
|
|
162
193
|
json_expression(Dict, compare(Op, Left, Right)) :- is_dict(Dict), dict_keys_allowed(Dict, [kind, operator, left, right]), get_dict(kind, Dict, Kind), json_text(Kind, compare), !, get_dict(operator, Dict, OpRaw), json_atom_name(OpRaw, Op), valid_compare_operator(Op), get_dict(left, Dict, LeftRaw), get_dict(right, Dict, RightRaw), json_term(LeftRaw, Left), json_term(RightRaw, Right).
|
|
163
194
|
json_expression(Dict, count(Atom, Op, Value)) :- is_dict(Dict), dict_keys_allowed(Dict, [kind, atom, operator, value]), get_dict(kind, Dict, Kind), json_text(Kind, count), !, get_dict(atom, Dict, AtomRaw), json_atom(AtomRaw, Atom), get_dict(operator, Dict, OpRaw), json_atom_name(OpRaw, Op), valid_compare_operator(Op), get_dict(value, Dict, Value), integer(Value), Value >= 0.
|
|
164
|
-
json_expression(Dict, temporal(Relation, Left, Right)) :- is_dict(Dict), dict_keys_allowed(Dict, [kind, relation, left, right]), get_dict(kind, Dict, Kind), json_text(Kind, temporal),
|
|
195
|
+
json_expression(Dict, temporal(Relation, Left, Right)) :- is_dict(Dict), dict_keys_allowed(Dict, [kind, relation, left, right]), get_dict(kind, Dict, Kind), json_text(Kind, temporal), get_dict(relation, Dict, RelationRaw), json_atom_name(RelationRaw, Relation), valid_temporal_relation(Relation), get_dict(left, Dict, LeftRaw), get_dict(right, Dict, RightRaw), json_term(LeftRaw, Left), json_term(RightRaw, Right).
|
|
165
196
|
|
|
166
197
|
valid_compare_operator(Op) :- memberchk(Op, [eq, neq, lt, lte, gt, gte]).
|
|
167
198
|
valid_temporal_relation(Relation) :- memberchk(Relation, [before, after, during, overlaps, starts, finishes]).
|
|
@@ -215,19 +246,27 @@ rule_limit_error(Head, Body, Exceptions, expression_depth_exceeded) :-
|
|
|
215
246
|
|
|
216
247
|
expression_atom_count(none, 0).
|
|
217
248
|
expression_atom_count(atom(_, _, _, _, _), 1).
|
|
249
|
+
% mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
|
|
218
250
|
expression_atom_count(all(Items), Count) :- maplist(expression_atom_count, Items, Counts), sum_list(Counts, ChildCount), Count is ChildCount.
|
|
251
|
+
% mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
|
|
219
252
|
expression_atom_count(any(Items), Count) :- maplist(expression_atom_count, Items, Counts), sum_list(Counts, ChildCount), Count is ChildCount.
|
|
253
|
+
% mutalog-disable-next-line cut_insert: a det call on a subterm
|
|
220
254
|
expression_atom_count(not(Atom), Count) :- expression_atom_count(Atom, ChildCount), Count is ChildCount.
|
|
221
255
|
expression_atom_count(compare(_, _, _), 0).
|
|
256
|
+
% mutalog-disable-next-line cut_insert: a det call on a subterm
|
|
222
257
|
expression_atom_count(count(Atom, _, _), Count) :- expression_atom_count(Atom, Count).
|
|
223
258
|
expression_atom_count(temporal(_, _, _), 0).
|
|
224
259
|
|
|
225
260
|
expression_depth(none, 0).
|
|
226
261
|
expression_depth(atom(_, _, _, _, _), 1).
|
|
262
|
+
% mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
|
|
227
263
|
expression_depth(all(Items), Depth) :- maplist(expression_depth, Items, Depths), max_list([0|Depths], ChildDepth), Depth is ChildDepth + 1.
|
|
264
|
+
% mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
|
|
228
265
|
expression_depth(any(Items), Depth) :- maplist(expression_depth, Items, Depths), max_list([0|Depths], ChildDepth), Depth is ChildDepth + 1.
|
|
266
|
+
% mutalog-disable-next-line cut_insert: a det call on a subterm
|
|
229
267
|
expression_depth(not(Atom), Depth) :- expression_depth(Atom, ChildDepth), Depth is ChildDepth + 1.
|
|
230
268
|
expression_depth(compare(_, _, _), 1).
|
|
269
|
+
% mutalog-disable-next-line cut_insert: a det call on a subterm
|
|
231
270
|
expression_depth(count(Atom, _, _), Depth) :- expression_depth(Atom, ChildDepth), Depth is ChildDepth + 1.
|
|
232
271
|
expression_depth(temporal(_, _, _), 1).
|
|
233
272
|
|
|
@@ -238,7 +277,6 @@ expression_term(not(Atom), Term) :- expression_term(Atom, Term).
|
|
|
238
277
|
expression_term(compare(_, Left, Right), Term) :- member(Term, [Left, Right]).
|
|
239
278
|
expression_term(count(Atom, _, _), Term) :- expression_term(Atom, Term).
|
|
240
279
|
expression_term(temporal(_, Left, Right), Term) :- member(Term, [Left, Right]).
|
|
241
|
-
expression_term(Term, Term) :- Term = interval(_, _).
|
|
242
280
|
|
|
243
281
|
variable_safety_error(Head, Body, _Variables, head_variable_not_range_restricted(Name)) :-
|
|
244
282
|
head_variable(Name, Head),
|
|
@@ -269,7 +307,6 @@ positive_variable(Name, atom(_, _, Args, positive, _)) :- member(Arg, Args), ter
|
|
|
269
307
|
positive_variable(Name, all(Items)) :- member(Item, Items), positive_variable(Name, Item).
|
|
270
308
|
positive_variable(Name, any(Items)) :- member(Item, Items), positive_variable(Name, Item).
|
|
271
309
|
positive_variable(Name, count(Atom, _, _)) :- positive_variable(Name, Atom).
|
|
272
|
-
positive_variable(_, _) :- fail.
|
|
273
310
|
|
|
274
311
|
term_variable(var(Name, _), Name).
|
|
275
312
|
expression_variable(atom(_, _, Args, _, _), Name) :- member(Arg, Args), term_variable(Arg, Name).
|
|
@@ -281,99 +318,78 @@ expression_variable(count(Atom, _, _), Name) :- expression_variable(Atom, Name).
|
|
|
281
318
|
expression_variable(temporal(_, Left, Right), Name) :- (term_variable(Left, Name) ; term_variable(Right, Name)).
|
|
282
319
|
|
|
283
320
|
rule_shape_error(atom, none, _Body, missing_head).
|
|
321
|
+
% mutalog-disable-next-line cut_insert: a test, and no later clause matches the same term
|
|
284
322
|
rule_shape_error(atom, _Head, Body, body_for_atom) :- Body \= none.
|
|
285
323
|
rule_shape_error(rule, none, _Body, missing_head).
|
|
286
324
|
rule_shape_error(rule, _Head, none, missing_body).
|
|
287
325
|
rule_shape_error(constraint, _Head, none, missing_body).
|
|
288
326
|
rule_shape_error(constraint, Head, _Body, head_for_constraint) :- Head \= none.
|
|
289
|
-
rule_shape_error(_, _, _, _) :- fail.
|
|
290
327
|
|
|
291
|
-
expression_safety_error(none, _) :- fail.
|
|
292
328
|
expression_safety_error(not(atom(_, _, _, _, false)), negation_requires_closed_world).
|
|
329
|
+
% mutalog-disable-next-line cut_insert: a test, and no later clause matches the same term
|
|
293
330
|
expression_safety_error(compare(Op, _Left, _Right), invalid_compare_operator) :- \+ valid_compare_operator(Op).
|
|
331
|
+
% mutalog-disable-next-line cut_insert: a test, and no later clause matches the same term
|
|
294
332
|
expression_safety_error(count(_Atom, Op, _Value), invalid_compare_operator) :- \+ valid_compare_operator(Op).
|
|
295
333
|
expression_safety_error(count(_Atom, _Op, Value), invalid_count_value) :- \+ integer(Value) ; Value < 0.
|
|
334
|
+
% mutalog-disable-next-line cut_insert: a test, and no later clause matches the same term
|
|
296
335
|
expression_safety_error(temporal(Relation, _Left, _Right), invalid_temporal_relation) :- \+ valid_temporal_relation(Relation).
|
|
297
336
|
expression_safety_error(all(Items), Error) :- member(Item, Items), expression_safety_error(Item, Error).
|
|
298
337
|
expression_safety_error(any(Items), Error) :- member(Item, Items), expression_safety_error(Item, Error).
|
|
299
338
|
expression_safety_error(not(Atom), Error) :- expression_safety_error(Atom, Error).
|
|
300
|
-
expression_safety_error(_, _) :- fail.
|
|
301
339
|
|
|
302
340
|
%% logic_rule_render(+Props, -Text)
|
|
303
341
|
logic_rule_render(Props, Text) :-
|
|
304
342
|
logic_rule_from_props(Props, Rule),
|
|
305
343
|
render_rule(Rule, Text).
|
|
306
344
|
|
|
307
|
-
render_rule(rule(_Kind, Modality, Head, none, _Exceptions, _Scope, _From, _To, _Schema, _Variables), Text) :-
|
|
308
|
-
render_head(Modality, Head, HeadText), format(string(Text), '~w.', [HeadText]).
|
|
309
345
|
render_rule(rule(_Kind, Modality, Head, Body, _Exceptions, _Scope, _From, _To, _Schema, _Variables), Text) :-
|
|
310
|
-
render_head(Modality, Head, HeadText),
|
|
346
|
+
render_head(Modality, Head, HeadText),
|
|
347
|
+
( Body == none
|
|
348
|
+
-> format(string(Text), '~w.', [HeadText])
|
|
349
|
+
; render_expression(Body, BodyText), format(string(Text), '~w :- ~w.', [HeadText, BodyText])
|
|
350
|
+
).
|
|
311
351
|
|
|
312
|
-
render_head(Modality,
|
|
313
|
-
|
|
352
|
+
render_head(Modality, Head, Text) :-
|
|
353
|
+
( Head == none
|
|
354
|
+
-> format(string(Text), '~w', [Modality])
|
|
355
|
+
; render_atom(Head, AtomText), format(string(Text), '~w(~w)', [Modality, AtomText])
|
|
356
|
+
).
|
|
314
357
|
render_atom(atom(Namespace, Name, Args, Polarity, _), Text) :-
|
|
315
358
|
( Namespace == default -> Full = Name ; format(atom(Full), '~w:~w', [Namespace, Name]) ),
|
|
316
359
|
maplist(render_term, Args, Rendered), atomic_list_concat(Rendered, ',', ArgsText),
|
|
317
360
|
( Polarity == negative -> format(string(Text), 'negative(~w(~w))', [Full, ArgsText]) ; format(string(Text), '~w(~w)', [Full, ArgsText]) ).
|
|
318
361
|
render_term(var(Name, _), Name).
|
|
319
362
|
render_term(const(Value, _), Value).
|
|
320
|
-
render_term(number(Value,
|
|
321
|
-
|
|
363
|
+
render_term(number(Value, Unit), Text) :-
|
|
364
|
+
( Unit == none -> format(string(Text), '~w', [Value]) ; format(string(Text), '~w~w', [Value, Unit]) ).
|
|
365
|
+
% mutalog-disable-next-line cut_insert: format/3 is det
|
|
322
366
|
render_term(duration(Value, Unit), Text) :- format(string(Text), 'duration(~w,~w)', [Value, Unit]).
|
|
323
367
|
render_term(timestamp(Value), Value).
|
|
324
368
|
render_term(interval(Start, End), Text) :- format(string(Text), 'interval(~w,~w)', [Start, End]).
|
|
325
|
-
|
|
369
|
+
% mutalog-disable-next-line cut_insert: rendering is det
|
|
370
|
+
render_expression(atom(Namespace, Name, Args, Polarity, ClosedWorld), Text) :- render_atom(atom(Namespace, Name, Args, Polarity, ClosedWorld), Text).
|
|
371
|
+
% mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
|
|
326
372
|
render_expression(all(Items), Text) :- maplist(render_expression, Items, Parts), atomic_list_concat(Parts, ', ', Text).
|
|
373
|
+
% mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
|
|
327
374
|
render_expression(any(Items), Text) :- maplist(render_expression, Items, Parts), atomic_list_concat(Parts, ' ; ', Inner), format(string(Text), '(~w)', [Inner]).
|
|
375
|
+
% mutalog-disable-next-line cut_insert: rendering is det
|
|
328
376
|
render_expression(not(Atom), Text) :- render_atom(Atom, AtomText), format(string(Text), 'not(~w)', [AtomText]).
|
|
377
|
+
% mutalog-disable-next-line cut_insert: rendering is det
|
|
329
378
|
render_expression(compare(Op, Left, Right), Text) :- render_term(Left, L), render_term(Right, R), format(string(Text), '~w ~w ~w', [L, Op, R]).
|
|
379
|
+
% mutalog-disable-next-line cut_insert: rendering is det
|
|
330
380
|
render_expression(count(Atom, Op, Value), Text) :- render_atom(Atom, AtomText), format(string(Text), 'count(~w,~w,~w)', [AtomText, Op, Value]).
|
|
331
381
|
render_expression(temporal(Relation, Left, Right), Text) :- render_term(Left, L), render_term(Right, R), format(string(Text), 'temporal(~w,~w,~w)', [Relation, L, R]).
|
|
332
382
|
|
|
333
383
|
logic_rule_semantic_key(Props, Key) :-
|
|
334
384
|
memberchk(semantic_key=Key, Props), !.
|
|
335
385
|
logic_rule_semantic_key(Props, Key) :-
|
|
336
|
-
memberchk(rule_hash=
|
|
386
|
+
memberchk(rule_hash=Key, Props).
|
|
337
387
|
|
|
338
388
|
logic_atom_signature(atom(Namespace, Name, Args, Polarity, _), signature(Namespace, Name, Args, Polarity)).
|
|
339
389
|
|
|
340
|
-
%% logic_derive(+Goal, -Proof)
|
|
341
|
-
% Goal is atom(Namespace, Name, Args, Polarity), with ground arguments. Rules
|
|
342
|
-
% are interpreted by a finite, data-driven evaluator; no stored source is
|
|
343
|
-
% consulted and recursive rule chains are bounded.
|
|
344
|
-
logic_derive(Goal, Proof) :-
|
|
345
|
-
ground(Goal),
|
|
346
|
-
derive_goal(Goal, [], 0, Proof).
|
|
347
|
-
|
|
348
|
-
logic_max_depth(16).
|
|
349
|
-
|
|
350
|
-
derive_goal(Goal, _Seen, _Depth, direct(FactId)) :-
|
|
351
|
-
Goal = atom(Namespace, Name, Args, Polarity, _),
|
|
352
|
-
predicate_fact(FactId, Namespace, Name, Args, Polarity), !.
|
|
353
|
-
derive_goal(Goal, Seen, Depth, rule(FactId, BodyProof)) :-
|
|
354
|
-
logic_max_depth(MaxDepth),
|
|
355
|
-
Depth < MaxDepth,
|
|
356
|
-
logic_rule_fact(FactId, StoredRule),
|
|
357
|
-
materialize_rule(StoredRule, Rule),
|
|
358
|
-
Rule = rule(rule, _Modality, Head, Body, Exceptions, _Scope, _From, _To, _Schema, _Variables),
|
|
359
|
-
Goal = Head,
|
|
360
|
-
atom_signature_for_seen(Head, Signature),
|
|
361
|
-
\+ memberchk(Signature, Seen),
|
|
362
|
-
NextDepth is Depth + 1,
|
|
363
|
-
prove_expression(Body, [Signature|Seen], NextDepth, BodyProof),
|
|
364
|
-
exceptions_do_not_hold(Exceptions, [Signature|Seen], NextDepth).
|
|
365
|
-
|
|
366
|
-
logic_rule_fact(FactId, Rule) :-
|
|
367
|
-
kb_entity(FactId, fact, Props),
|
|
368
|
-
memberchk(fact_kind=Kind, Props), normalize_atom(Kind, rule),
|
|
369
|
-
logic_rule_from_props(Props, Rule).
|
|
370
|
-
|
|
371
390
|
% The JSON decoder keeps variable names as data terms for validation and
|
|
372
391
|
% rendering. Evaluation reifies those names into fresh Prolog variables so
|
|
373
392
|
% ordinary unification can be used without ever evaluating caller source.
|
|
374
|
-
materialize_rule(Rule, Materialized) :-
|
|
375
|
-
materialize_rule_environment(Rule, Materialized, _Environment).
|
|
376
|
-
|
|
377
393
|
% Environment maps each declared variable name to its fresh Prolog variable.
|
|
378
394
|
materialize_rule_environment(
|
|
379
395
|
rule(Kind, Modality, Head, Body, Exceptions, Scope, From, To, Schema, Variables),
|
|
@@ -389,18 +405,23 @@ variable_environment([], []).
|
|
|
389
405
|
variable_environment([variable(Name, _Type, _Quantifier)|Rest], [Name-_Variable|Environment]) :-
|
|
390
406
|
variable_environment(Rest, Environment).
|
|
391
407
|
|
|
392
|
-
materialize_head(
|
|
393
|
-
|
|
408
|
+
materialize_head(Head, Variables, MaterialHead) :-
|
|
409
|
+
( Head == none -> MaterialHead = none ; materialize_atom(Head, Variables, MaterialHead) ).
|
|
394
410
|
|
|
395
411
|
materialize_expression(none, _Variables, none).
|
|
396
|
-
materialize_expression(
|
|
397
|
-
|
|
398
|
-
materialize_atom(
|
|
412
|
+
materialize_expression(atom(Namespace, Name, Args, Polarity, ClosedWorld), Variables, Materialized) :-
|
|
413
|
+
% mutalog-disable-next-line cut_insert: materializing is det
|
|
414
|
+
materialize_atom(atom(Namespace, Name, Args, Polarity, ClosedWorld), Variables, Materialized).
|
|
415
|
+
% mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
|
|
399
416
|
materialize_expression(all(Items), Variables, all(MaterialItems)) :- maplist(materialize_expression_with(Variables), Items, MaterialItems).
|
|
417
|
+
% mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
|
|
400
418
|
materialize_expression(any(Items), Variables, any(MaterialItems)) :- maplist(materialize_expression_with(Variables), Items, MaterialItems).
|
|
419
|
+
% mutalog-disable-next-line cut_insert: materializing is det
|
|
401
420
|
materialize_expression(not(Atom), Variables, not(MaterialAtom)) :- materialize_atom(Atom, Variables, MaterialAtom).
|
|
402
421
|
materialize_expression(compare(Op, Left, Right), Variables, compare(Op, MaterialLeft, MaterialRight)) :-
|
|
422
|
+
% mutalog-disable-next-line cut_insert: materializing is det
|
|
403
423
|
materialize_term(Left, Variables, MaterialLeft), materialize_term(Right, Variables, MaterialRight).
|
|
424
|
+
% mutalog-disable-next-line cut_insert: materializing is det
|
|
404
425
|
materialize_expression(count(Atom, Op, Value), Variables, count(MaterialAtom, Op, Value)) :- materialize_atom(Atom, Variables, MaterialAtom).
|
|
405
426
|
materialize_expression(temporal(Relation, Left, Right), Variables, temporal(Relation, MaterialLeft, MaterialRight)) :-
|
|
406
427
|
materialize_term(Left, Variables, MaterialLeft), materialize_term(Right, Variables, MaterialRight).
|
|
@@ -408,7 +429,8 @@ materialize_expression_with(Variables, Expression, Materialized) :- materialize_
|
|
|
408
429
|
|
|
409
430
|
materialize_atom(atom(Namespace, Name, Args, Polarity, ClosedWorld), Variables, atom(Namespace, Name, MaterialArgs, Polarity, ClosedWorld)) :-
|
|
410
431
|
maplist(materialize_term_with(Variables), Args, MaterialArgs).
|
|
411
|
-
|
|
432
|
+
% mutalog-disable-next-line cut_insert: memberchk/2 is det
|
|
433
|
+
materialize_term(var(Name, _Type), Variables, Variable) :- memberchk(Name-Variable, Variables).
|
|
412
434
|
materialize_term(const(Value, Type), _Variables, const(Value, Type)).
|
|
413
435
|
materialize_term(number(Value, Unit), _Variables, number(Value, Unit)).
|
|
414
436
|
materialize_term(duration(Value, Unit), _Variables, duration(Value, Unit)).
|
|
@@ -416,47 +438,27 @@ materialize_term(timestamp(Value), _Variables, timestamp(Value)).
|
|
|
416
438
|
materialize_term(interval(Start, End), _Variables, interval(Start, End)).
|
|
417
439
|
materialize_term_with(Variables, Term, Materialized) :- materialize_term(Term, Variables, Materialized).
|
|
418
440
|
|
|
419
|
-
|
|
420
|
-
Atom = atom(_, _, _, _, _),
|
|
421
|
-
derive_goal(Atom, Seen, Depth, Proof).
|
|
422
|
-
prove_expression(all(Items), Seen, Depth, all(Proofs)) :- maplist(prove_expression_seen(Seen, Depth), Items, Proofs).
|
|
423
|
-
prove_expression(any(Items), Seen, Depth, Proof) :- member(Item, Items), prove_expression(Item, Seen, Depth, Proof), !.
|
|
424
|
-
prove_expression(not(Atom), Seen, Depth, negated(Atom)) :-
|
|
425
|
-
Atom = atom(Namespace, Name, Args, Polarity, _),
|
|
426
|
-
\+ derive_goal(atom(Namespace, Name, Args, Polarity, _), Seen, Depth, _).
|
|
427
|
-
prove_expression(compare(Op, Left, Right), _Seen, _Depth, comparison(Op, Left, Right)) :- compare_terms(Op, Left, Right).
|
|
428
|
-
prove_expression(count(Atom, Op, Value), _Seen, _Depth, count(Atom, Op, Value)) :-
|
|
429
|
-
Atom = atom(Namespace, Name, Args, Polarity, _),
|
|
430
|
-
aggregate_all(count, predicate_fact(_, Namespace, Name, Args, Polarity), Count),
|
|
431
|
-
compare_numbers(Op, Count, Value).
|
|
432
|
-
prove_expression(temporal(Relation, Left, Right), _Seen, _Depth, temporal(Relation, Left, Right)) :- temporal_holds(Relation, Left, Right).
|
|
433
|
-
prove_expression_seen(Seen, Depth, Item, Proof) :- prove_expression(Item, Seen, Depth, Proof).
|
|
434
|
-
|
|
435
|
-
exceptions_do_not_hold([], _Seen, _Depth).
|
|
436
|
-
exceptions_do_not_hold([Exception|Rest], Seen, Depth) :-
|
|
437
|
-
\+ prove_expression(Exception, Seen, Depth, _),
|
|
438
|
-
exceptions_do_not_hold(Rest, Seen, Depth).
|
|
439
|
-
|
|
440
|
-
atom_signature_for_seen(atom(Namespace, Name, Args, Polarity, _), signature(Namespace, Name, Args, Polarity)).
|
|
441
|
-
|
|
441
|
+
% mutalog-disable-next-line cut_insert: term_value/2 is det
|
|
442
442
|
compare_terms(eq, Left, Right) :- term_value(Left, L), term_value(Right, R), L = R.
|
|
443
|
+
% mutalog-disable-next-line cut_insert: term_value/2 is det
|
|
443
444
|
compare_terms(neq, Left, Right) :- term_value(Left, L), term_value(Right, R), L \= R.
|
|
445
|
+
% mutalog-disable-next-line cut_insert: term_value/2 is det
|
|
444
446
|
compare_terms(lt, Left, Right) :- term_value(Left, L), term_value(Right, R), L < R.
|
|
447
|
+
% mutalog-disable-next-line cut_insert: term_value/2 is det
|
|
445
448
|
compare_terms(lte, Left, Right) :- term_value(Left, L), term_value(Right, R), L =< R.
|
|
449
|
+
% mutalog-disable-next-line cut_insert: term_value/2 is det
|
|
446
450
|
compare_terms(gt, Left, Right) :- term_value(Left, L), term_value(Right, R), L > R.
|
|
447
451
|
compare_terms(gte, Left, Right) :- term_value(Left, L), term_value(Right, R), L >= R.
|
|
448
452
|
|
|
449
|
-
|
|
453
|
+
% mutalog-disable-next-line cut_insert: duration_milliseconds/3 is det
|
|
454
|
+
term_value(number(Value, Unit), Normalized) :- duration_milliseconds(Value, Unit, Normalized).
|
|
455
|
+
% mutalog-disable-next-line cut_insert: duration_milliseconds/3 is det
|
|
450
456
|
term_value(duration(Value, Unit), Normalized) :- duration_milliseconds(Value, Unit, Normalized).
|
|
451
457
|
term_value(const(Value, _), Number) :- atom_number(Value, Number), !.
|
|
452
458
|
term_value(const(Value, _), Value).
|
|
453
459
|
term_value(Value, Value) :- number(Value).
|
|
454
460
|
|
|
455
|
-
|
|
456
|
-
normalize_numeric_unit(Value, Unit, Normalized) :-
|
|
457
|
-
duration_milliseconds(Value, Unit, Normalized).
|
|
458
|
-
|
|
459
|
-
duration_milliseconds(Value, ms, Value) :- !.
|
|
461
|
+
% Other units, none and ms among them, keep their value.
|
|
460
462
|
duration_milliseconds(Value, s, Normalized) :- Normalized is Value * 1000, !.
|
|
461
463
|
duration_milliseconds(Value, m, Normalized) :- Normalized is Value * 60000, !.
|
|
462
464
|
duration_milliseconds(Value, h, Normalized) :- Normalized is Value * 3600000, !.
|
|
@@ -464,40 +466,6 @@ duration_milliseconds(Value, d, Normalized) :- Normalized is Value * 86400000, !
|
|
|
464
466
|
duration_milliseconds(Value, w, Normalized) :- Normalized is Value * 604800000, !.
|
|
465
467
|
duration_milliseconds(Value, _Unit, Value).
|
|
466
468
|
|
|
467
|
-
compare_numbers(eq, L, R) :- L =:= R.
|
|
468
|
-
compare_numbers(neq, L, R) :- L =\= R.
|
|
469
|
-
compare_numbers(lt, L, R) :- L < R.
|
|
470
|
-
compare_numbers(lte, L, R) :- L =< R.
|
|
471
|
-
compare_numbers(gt, L, R) :- L > R.
|
|
472
|
-
compare_numbers(gte, L, R) :- L >= R.
|
|
473
|
-
|
|
474
|
-
temporal_holds(before, Left, Right) :-
|
|
475
|
-
temporal_end(Left, LeftEnd),
|
|
476
|
-
temporal_start(Right, RightStart),
|
|
477
|
-
LeftEnd @< RightStart.
|
|
478
|
-
temporal_holds(after, Left, Right) :-
|
|
479
|
-
temporal_start(Left, LeftStart),
|
|
480
|
-
temporal_end(Right, RightEnd),
|
|
481
|
-
LeftStart @> RightEnd.
|
|
482
|
-
temporal_holds(during, timestamp(Point), interval(Start, End)) :-
|
|
483
|
-
Point @>= Start,
|
|
484
|
-
Point @=< End.
|
|
485
|
-
temporal_holds(during, interval(InnerStart, InnerEnd), interval(Start, End)) :-
|
|
486
|
-
InnerStart @>= Start,
|
|
487
|
-
InnerEnd @=< End.
|
|
488
|
-
temporal_holds(overlaps, interval(LeftStart, LeftEnd), interval(RightStart, RightEnd)) :-
|
|
489
|
-
LeftStart @< RightEnd,
|
|
490
|
-
RightStart @< LeftEnd.
|
|
491
|
-
temporal_holds(starts, interval(LeftStart, _), interval(RightStart, _)) :-
|
|
492
|
-
LeftStart == RightStart.
|
|
493
|
-
temporal_holds(finishes, interval(_, LeftEnd), interval(_, RightEnd)) :-
|
|
494
|
-
LeftEnd == RightEnd.
|
|
495
|
-
|
|
496
|
-
temporal_start(timestamp(Value), Value).
|
|
497
|
-
temporal_start(interval(Start, _), Start).
|
|
498
|
-
temporal_end(timestamp(Value), Value).
|
|
499
|
-
temporal_end(interval(_, End), End).
|
|
500
|
-
|
|
501
469
|
%% logic_rule_conflict(+RuleA, +RuleB, -Status)
|
|
502
470
|
% implements REQ-kibi-truthful-consistency
|
|
503
471
|
% Three-valued comparison of two opposing rules.
|
|
@@ -577,7 +545,6 @@ logic_rule_missing_keys(RuleA, RuleB, Functional, Missing) :-
|
|
|
577
545
|
( Single \== []
|
|
578
546
|
-> Missing = Single
|
|
579
547
|
; value_last_declarations(Undeclared, Declarations),
|
|
580
|
-
Declarations \== [],
|
|
581
548
|
append(Declarations, Functional, Assumed),
|
|
582
549
|
logic_rule_conflict(RuleA, RuleB, Assumed, AssumedStatus),
|
|
583
550
|
AssumedStatus \== unresolved
|
|
@@ -705,8 +672,9 @@ body_pair_status(PartsA, PartsB, ExceptionsA, ExceptionsB, Functional, Declared,
|
|
|
705
672
|
rule_context_compatible(scope(AuthA, ScopeA, _), FromA, ToA, scope(AuthB, ScopeB, _), FromB, ToB) :-
|
|
706
673
|
(AuthA == '' ; AuthB == '' ; AuthA == AuthB),
|
|
707
674
|
(ScopeA == '' ; ScopeB == '' ; ScopeA == ScopeB),
|
|
708
|
-
|
|
709
|
-
(
|
|
675
|
+
% An open start ('') sorts before every timestamp.
|
|
676
|
+
(ToB == '' ; FromA @=< ToB),
|
|
677
|
+
(ToA == '' ; FromB @=< ToA).
|
|
710
678
|
|
|
711
679
|
conflicting_modality(assert, deny).
|
|
712
680
|
conflicting_modality(deny, assert).
|
|
@@ -761,8 +729,7 @@ every_declaration_integer(Declared, Variable) :-
|
|
|
761
729
|
some_declaration_integer(Declared, Variable) :-
|
|
762
730
|
member(Other-Type, Declared),
|
|
763
731
|
Other == Variable,
|
|
764
|
-
integer_type(Type)
|
|
765
|
-
!.
|
|
732
|
+
integer_type(Type).
|
|
766
733
|
|
|
767
734
|
integer_type(int).
|
|
768
735
|
integer_type(integer).
|
|
@@ -942,7 +909,6 @@ comparison_entailed(Premises, Integers, compare(Op, Left, Right)) :-
|
|
|
942
909
|
exception_entailed(Exceptions, OtherParts, Declared) :-
|
|
943
910
|
member(Exception, Exceptions),
|
|
944
911
|
expression_parts(Exception, ExceptionParts),
|
|
945
|
-
ExceptionParts = parts(_, _, []),
|
|
946
912
|
\+ \+ parts_subsume(ExceptionParts, OtherParts, Declared), !.
|
|
947
913
|
|
|
948
914
|
%% translate_comparisons(+Comparisons, -Constraints, -Status)
|
|
@@ -987,13 +953,14 @@ reflexive_operator(gte).
|
|
|
987
953
|
comparison_operand(Term, var(Term)) :- var(Term), !.
|
|
988
954
|
comparison_operand(Term, num(Value)) :-
|
|
989
955
|
ir_ground(Term),
|
|
990
|
-
catch(term_value(Term, Value), _, fail),
|
|
991
|
-
number(Value), !.
|
|
956
|
+
catch((term_value(Term, Value), number(Value)), _, fail), !.
|
|
992
957
|
comparison_operand(Term, ground(Term)) :- ir_ground(Term), !.
|
|
993
958
|
comparison_operand(Term, other(Term)).
|
|
994
959
|
|
|
995
960
|
% IR terms keep variables as var(Name, Type) data until materialized.
|
|
996
|
-
|
|
961
|
+
% An unbound Prolog variable unifies with var(_, _), so this also rejects
|
|
962
|
+
% terms that are not ground.
|
|
963
|
+
ir_ground(Term) :- \+ sub_term(var(_, _), Term).
|
|
997
964
|
|
|
998
965
|
flipped_operator(eq, eq).
|
|
999
966
|
flipped_operator(neq, neq).
|
|
@@ -1087,6 +1054,7 @@ logic_rule_property_form(Rule0, property_form(Modality, Head, ScopeName, From, T
|
|
|
1087
1054
|
).
|
|
1088
1055
|
|
|
1089
1056
|
expression_property_key(atom(Namespace, Name, Args, _, _), Namespace-Name) :-
|
|
1057
|
+
% mutalog-disable-next-line cut_insert: a test, and no later clause matches the same term
|
|
1090
1058
|
Namespace \== default,
|
|
1091
1059
|
Args \== [].
|
|
1092
1060
|
expression_property_key(all(Items), Key) :-
|
|
@@ -1096,13 +1064,13 @@ expression_property_key(any(Items), Key) :-
|
|
|
1096
1064
|
member(Item, Items),
|
|
1097
1065
|
expression_property_key(Item, Key).
|
|
1098
1066
|
expression_property_key(not(Atom), Key) :-
|
|
1067
|
+
% mutalog-disable-next-line cut_insert: an atom has at most one property key
|
|
1099
1068
|
expression_property_key(Atom, Key).
|
|
1100
1069
|
expression_property_key(count(Atom, _, _), Key) :-
|
|
1101
1070
|
expression_property_key(Atom, Key).
|
|
1102
1071
|
|
|
1103
1072
|
property_read(atom(Namespace, Name, Args, positive, _), read(Namespace-Name, Value, Instance)) :-
|
|
1104
1073
|
Namespace \== default,
|
|
1105
|
-
Args \== [],
|
|
1106
1074
|
append(Instance, [Value], Args).
|
|
1107
1075
|
|
|
1108
1076
|
% Bindings maps each value variable name to its property and declared type.
|
|
@@ -1123,16 +1091,19 @@ property_value_bindings(Reads, Head, Bindings, ConstantConditions) :-
|
|
|
1123
1091
|
ValueA == ValueB
|
|
1124
1092
|
).
|
|
1125
1093
|
|
|
1126
|
-
read_binding(IdentityNames, read(Key,
|
|
1127
|
-
|
|
1128
|
-
\+ memberchk(Name, IdentityNames),
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1094
|
+
read_binding(IdentityNames, read(Key, Value, _), acc(Bindings0, Conditions0), acc(Bindings, Conditions)) :-
|
|
1095
|
+
( Value = var(Name, Type)
|
|
1096
|
+
-> \+ memberchk(Name, IdentityNames),
|
|
1097
|
+
Conditions = Conditions0,
|
|
1098
|
+
( memberchk(Name-OtherKey-_, Bindings0)
|
|
1099
|
+
-> OtherKey == Key,
|
|
1100
|
+
Bindings = Bindings0
|
|
1101
|
+
; Bindings = [Name-Key-Type|Bindings0]
|
|
1102
|
+
)
|
|
1103
|
+
; constant_term(Value),
|
|
1104
|
+
Bindings = Bindings0,
|
|
1105
|
+
Conditions = [cond(Key, eq, Value, value)|Conditions0]
|
|
1133
1106
|
).
|
|
1134
|
-
read_binding(_, read(Key, Value, _), acc(Bindings, Conditions), acc(Bindings, [cond(Key, eq, Value, value)|Conditions])) :-
|
|
1135
|
-
constant_term(Value).
|
|
1136
1107
|
|
|
1137
1108
|
constant_term(number(_, _)).
|
|
1138
1109
|
constant_term(duration(_, _)).
|
|
@@ -1196,6 +1167,7 @@ logic_predicate_signature(atom(Namespace, Name, Args, _Polarity, _ClosedWorld),
|
|
|
1196
1167
|
length(Args, Arity).
|
|
1197
1168
|
|
|
1198
1169
|
expression_dependency(Atom, Atom, positive) :-
|
|
1170
|
+
% mutalog-disable-next-line cut_insert: a test, and no later clause matches the same term
|
|
1199
1171
|
Atom = atom(_, _, _, _, _).
|
|
1200
1172
|
expression_dependency(not(Atom), Atom, negative).
|
|
1201
1173
|
expression_dependency(all(Items), Atom, Sign) :- member(Item, Items), expression_dependency(Item, Atom, Sign).
|
|
@@ -1203,9 +1175,9 @@ expression_dependency(any(Items), Atom, Sign) :- member(Item, Items), expression
|
|
|
1203
1175
|
expression_dependency(count(Atom, _Operator, _Value), Atom, positive).
|
|
1204
1176
|
|
|
1205
1177
|
dependency_path(Current, Target, _Edges, Seen) :-
|
|
1178
|
+
% mutalog-disable-next-line cut_insert: a test, and no later clause matches the same term
|
|
1206
1179
|
Current == Target,
|
|
1207
|
-
\+ memberchk(Current, Seen)
|
|
1208
|
-
!.
|
|
1180
|
+
\+ memberchk(Current, Seen).
|
|
1209
1181
|
dependency_path(Current, Target, Edges, Seen) :-
|
|
1210
1182
|
\+ memberchk(Current, Seen),
|
|
1211
1183
|
member(edge(Current, Next, _Sign), Edges),
|
|
@@ -1247,6 +1219,3 @@ logic_rule_conflict_witness(RuleA, RuleB, Functional, Witness) :-
|
|
|
1247
1219
|
rule_schema_b: SchemaB
|
|
1248
1220
|
}.
|
|
1249
1221
|
|
|
1250
|
-
|
|
1251
|
-
normalize_atom(Value, Atom) :- atom(Value), !, Atom = Value.
|
|
1252
|
-
normalize_atom(Value, Atom) :- string(Value), atom_string(Atom, Value).
|