eyeling 1.24.1 → 1.24.2
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/HANDBOOK.md +98 -0
- package/dist/browser/eyeling.browser.js +103 -0
- package/eyeling.js +103 -0
- package/lib/lexer.js +103 -0
- package/package.json +1 -1
- package/see/README.md +3 -0
- package/see/examples/_see.js +33 -2
- package/see/examples/age.js +27 -1
- package/see/examples/annotation.js +27 -1
- package/see/examples/backward.js +27 -1
- package/see/examples/backward_recursion.js +27 -1
- package/see/examples/bayes_diagnosis.js +27 -1
- package/see/examples/bayes_therapy.js +27 -1
- package/see/examples/bmi.js +27 -1
- package/see/examples/builtin_coverage.js +27 -1
- package/see/examples/collection.js +27 -1
- package/see/examples/complex.js +27 -1
- package/see/examples/complex_matrix_stability.js +27 -1
- package/see/examples/composition_of_injective_functions_is_injective.js +27 -1
- package/see/examples/control_system.js +27 -1
- package/see/examples/crypto_builtins_tests.js +27 -1
- package/see/examples/delfour.js +27 -1
- package/see/examples/digital_product_passport.js +27 -1
- package/see/examples/dijkstra.js +27 -1
- package/see/examples/dijkstra_risk_path.js +27 -1
- package/see/examples/doc/triple_terms.md +26 -0
- package/see/examples/dog.js +27 -1
- package/see/examples/eco_route_insight.js +27 -1
- package/see/examples/equals.js +27 -1
- package/see/examples/equivalence_classes_overlap_implies_same_class.js +27 -1
- package/see/examples/euler_identity.js +27 -1
- package/see/examples/ev_roundtrip_planner.js +27 -1
- package/see/examples/existential_rule.js +27 -1
- package/see/examples/expression_eval.js +27 -1
- package/see/examples/family_cousins.js +27 -1
- package/see/examples/fastpow.js +27 -1
- package/see/examples/fibonacci.js +27 -1
- package/see/examples/french_cities.js +27 -1
- package/see/examples/fundamental_theorem_arithmetic.js +27 -1
- package/see/examples/genetic_knapsack_selection.js +27 -1
- package/see/examples/goldbach_1000.js +27 -1
- package/see/examples/good_cobbler.js +27 -1
- package/see/examples/gps.js +27 -1
- package/see/examples/gray_code_counter.js +27 -1
- package/see/examples/greatest_lower_bound_uniqueness.js +27 -1
- package/see/examples/group_inverse_uniqueness.js +27 -1
- package/see/examples/hadamard_approx.js +27 -1
- package/see/examples/hanoi.js +27 -1
- package/see/examples/input/triple_terms.trig +28 -0
- package/see/examples/n3/triple_terms.n3 +23 -0
- package/see/examples/odrl_dpv_risk_ranked.js +27 -1
- package/see/examples/output/triple_terms.md +53 -0
- package/see/examples/path_discovery.js +27 -1
- package/see/examples/rc_discharge_envelope.js +27 -1
- package/see/examples/rdf_message_flow.js +27 -1
- package/see/examples/rdf_messages.js +27 -1
- package/see/examples/school_placement_audit.js +27 -1
- package/see/examples/smoke_arithmetic.js +27 -1
- package/see/examples/socrates.js +27 -1
- package/see/examples/triple_terms.js +1442 -0
- package/see/examples/wind_turbine.js +27 -1
- package/see/examples/witch.js +27 -1
- package/see/see.js +75 -2
- package/test/api.test.js +20 -0
|
@@ -8,6 +8,7 @@ const crypto = require('crypto');
|
|
|
8
8
|
|
|
9
9
|
function canonical(term) {
|
|
10
10
|
if (term.kind === 'list') return ['list', term.items.map(canonical)];
|
|
11
|
+
if (term.kind === 'triple') return ['triple', canonical(term.s), canonical(term.p), canonical(term.o)];
|
|
11
12
|
if (term.kind === 'formula') return ['formula', term.atoms.map((a) => [canonical(a.s), canonical(a.p), canonical(a.o)])];
|
|
12
13
|
return [term.kind, term.value];
|
|
13
14
|
}
|
|
@@ -17,6 +18,7 @@ function compoundIndexKey() { return Array.from(arguments).map(termIndexKey).joi
|
|
|
17
18
|
function termIsConcrete(t) {
|
|
18
19
|
if (!t || t.kind === 'var') return false;
|
|
19
20
|
if (t.kind === 'list') return t.items.every(termIsConcrete);
|
|
21
|
+
if (t.kind === 'triple') return termIsConcrete(t.s) && termIsConcrete(t.p) && termIsConcrete(t.o);
|
|
20
22
|
if (t.kind === 'formula') return t.atoms.every((a) => termIsConcrete(a.s) && termIsConcrete(a.p) && termIsConcrete(a.o));
|
|
21
23
|
return true;
|
|
22
24
|
}
|
|
@@ -32,6 +34,7 @@ function primitive(t) {
|
|
|
32
34
|
if (t.kind === 'iri') return t.value.replace(/^:/, '');
|
|
33
35
|
if (t.kind === 'blank') return t.value;
|
|
34
36
|
if (t.kind === 'list') return t.items.map(primitive);
|
|
37
|
+
if (t.kind === 'triple') return termToN3(t);
|
|
35
38
|
if (t.kind === 'formula') return termToN3(t);
|
|
36
39
|
return undefined;
|
|
37
40
|
}
|
|
@@ -52,6 +55,7 @@ function termToN3(t) {
|
|
|
52
55
|
if (t.kind === 'var') return '?' + t.value;
|
|
53
56
|
if (t.kind === 'blank') return t.value.startsWith('_:') ? t.value : '_:' + t.value.replace(/^_+/, '');
|
|
54
57
|
if (t.kind === 'list') return '(' + t.items.map(termToN3).join(' ') + ')';
|
|
58
|
+
if (t.kind === 'triple') return '<<( ' + termToN3(t.s) + ' ' + termToN3(t.p) + ' ' + termToN3(t.o) + ' )>>';
|
|
55
59
|
if (t.kind === 'formula') return '{ ' + t.atoms.map(atomToN3).join(' . ') + ' }';
|
|
56
60
|
return String(t.value ?? t);
|
|
57
61
|
}
|
|
@@ -74,6 +78,7 @@ function resolve(term, env, seen = new Set()) {
|
|
|
74
78
|
return resolve(env[term.value], env, seen);
|
|
75
79
|
}
|
|
76
80
|
if (term.kind === 'list') return list(term.items.map((item) => resolve(item, env, seen)));
|
|
81
|
+
if (term.kind === 'triple') return { kind: 'triple', s: resolve(term.s, env), p: resolve(term.p, env), o: resolve(term.o, env) };
|
|
77
82
|
if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: resolve(a.s, env), p: resolve(a.p, env), o: resolve(a.o, env) })) };
|
|
78
83
|
return term;
|
|
79
84
|
}
|
|
@@ -91,6 +96,14 @@ function unify(a, b, env) {
|
|
|
91
96
|
}
|
|
92
97
|
return out;
|
|
93
98
|
}
|
|
99
|
+
if (a.kind === 'triple' || b.kind === 'triple') {
|
|
100
|
+
if (a.kind !== 'triple' || b.kind !== 'triple') return null;
|
|
101
|
+
let out = unify(a.s, b.s, env);
|
|
102
|
+
if (!out) return null;
|
|
103
|
+
out = unify(a.p, b.p, out);
|
|
104
|
+
if (!out) return null;
|
|
105
|
+
return unify(a.o, b.o, out);
|
|
106
|
+
}
|
|
94
107
|
return deepEqual(a, b) ? env : null;
|
|
95
108
|
}
|
|
96
109
|
function bind(pattern, value, env) { return unify(pattern, value, env); }
|
|
@@ -106,6 +119,7 @@ function termIsGround(t, env) {
|
|
|
106
119
|
const r = resolve(t, env);
|
|
107
120
|
if (r.kind === 'var') return false;
|
|
108
121
|
if (r.kind === 'list') return r.items.every((item) => termIsGround(item, env));
|
|
122
|
+
if (r.kind === 'triple') return termIsGround(r.s, env) && termIsGround(r.p, env) && termIsGround(r.o, env);
|
|
109
123
|
if (r.kind === 'formula') return r.atoms.every((atom) => atomIsGround(atom, env));
|
|
110
124
|
return true;
|
|
111
125
|
}
|
|
@@ -665,6 +679,7 @@ function instantiate(term, env, ruleId) {
|
|
|
665
679
|
}
|
|
666
680
|
if (term.kind === 'blank') return blank('_:r' + ruleId + '_' + envSignature(env) + '_' + term.value.replace(/^_/, ''));
|
|
667
681
|
if (term.kind === 'list') return list(term.items.map((item) => instantiate(item, env, ruleId)));
|
|
682
|
+
if (term.kind === 'triple') return { kind: 'triple', s: instantiate(term.s, env, ruleId), p: instantiate(term.p, env, ruleId), o: instantiate(term.o, env, ruleId) };
|
|
668
683
|
if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: instantiate(a.s, env, ruleId), p: instantiate(a.p, env, ruleId), o: instantiate(a.o, env, ruleId) })) };
|
|
669
684
|
return cloneTerm(term);
|
|
670
685
|
}
|
|
@@ -2762,6 +2777,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
|
|
|
2762
2777
|
}
|
|
2763
2778
|
return out;
|
|
2764
2779
|
}
|
|
2780
|
+
function termHasTripleTerm(term) {
|
|
2781
|
+
if (!term) return false;
|
|
2782
|
+
if (term.kind === 'triple') return true;
|
|
2783
|
+
if (term.kind === 'list') return term.items.some(termHasTripleTerm);
|
|
2784
|
+
if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
|
|
2785
|
+
return false;
|
|
2786
|
+
}
|
|
2787
|
+
function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
|
|
2788
|
+
function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
|
|
2789
|
+
function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
|
|
2765
2790
|
function trigGraphBlock(label, atoms) {
|
|
2766
2791
|
const lines = [label + ' {'];
|
|
2767
2792
|
for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
|
|
@@ -2809,7 +2834,8 @@ function formalOutputToTrig(facts, trig) {
|
|
|
2809
2834
|
const prefixes = prefixLinesFromTrig(trig);
|
|
2810
2835
|
if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
|
|
2811
2836
|
const nl = String.fromCharCode(10);
|
|
2812
|
-
|
|
2837
|
+
const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
|
|
2838
|
+
return version + prefixes.join(nl) + nl + nl + body.join(nl);
|
|
2813
2839
|
}
|
|
2814
2840
|
function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
|
|
2815
2841
|
const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);
|
package/see/examples/witch.js
CHANGED
|
@@ -8,6 +8,7 @@ const crypto = require('crypto');
|
|
|
8
8
|
|
|
9
9
|
function canonical(term) {
|
|
10
10
|
if (term.kind === 'list') return ['list', term.items.map(canonical)];
|
|
11
|
+
if (term.kind === 'triple') return ['triple', canonical(term.s), canonical(term.p), canonical(term.o)];
|
|
11
12
|
if (term.kind === 'formula') return ['formula', term.atoms.map((a) => [canonical(a.s), canonical(a.p), canonical(a.o)])];
|
|
12
13
|
return [term.kind, term.value];
|
|
13
14
|
}
|
|
@@ -17,6 +18,7 @@ function compoundIndexKey() { return Array.from(arguments).map(termIndexKey).joi
|
|
|
17
18
|
function termIsConcrete(t) {
|
|
18
19
|
if (!t || t.kind === 'var') return false;
|
|
19
20
|
if (t.kind === 'list') return t.items.every(termIsConcrete);
|
|
21
|
+
if (t.kind === 'triple') return termIsConcrete(t.s) && termIsConcrete(t.p) && termIsConcrete(t.o);
|
|
20
22
|
if (t.kind === 'formula') return t.atoms.every((a) => termIsConcrete(a.s) && termIsConcrete(a.p) && termIsConcrete(a.o));
|
|
21
23
|
return true;
|
|
22
24
|
}
|
|
@@ -32,6 +34,7 @@ function primitive(t) {
|
|
|
32
34
|
if (t.kind === 'iri') return t.value.replace(/^:/, '');
|
|
33
35
|
if (t.kind === 'blank') return t.value;
|
|
34
36
|
if (t.kind === 'list') return t.items.map(primitive);
|
|
37
|
+
if (t.kind === 'triple') return termToN3(t);
|
|
35
38
|
if (t.kind === 'formula') return termToN3(t);
|
|
36
39
|
return undefined;
|
|
37
40
|
}
|
|
@@ -52,6 +55,7 @@ function termToN3(t) {
|
|
|
52
55
|
if (t.kind === 'var') return '?' + t.value;
|
|
53
56
|
if (t.kind === 'blank') return t.value.startsWith('_:') ? t.value : '_:' + t.value.replace(/^_+/, '');
|
|
54
57
|
if (t.kind === 'list') return '(' + t.items.map(termToN3).join(' ') + ')';
|
|
58
|
+
if (t.kind === 'triple') return '<<( ' + termToN3(t.s) + ' ' + termToN3(t.p) + ' ' + termToN3(t.o) + ' )>>';
|
|
55
59
|
if (t.kind === 'formula') return '{ ' + t.atoms.map(atomToN3).join(' . ') + ' }';
|
|
56
60
|
return String(t.value ?? t);
|
|
57
61
|
}
|
|
@@ -74,6 +78,7 @@ function resolve(term, env, seen = new Set()) {
|
|
|
74
78
|
return resolve(env[term.value], env, seen);
|
|
75
79
|
}
|
|
76
80
|
if (term.kind === 'list') return list(term.items.map((item) => resolve(item, env, seen)));
|
|
81
|
+
if (term.kind === 'triple') return { kind: 'triple', s: resolve(term.s, env), p: resolve(term.p, env), o: resolve(term.o, env) };
|
|
77
82
|
if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: resolve(a.s, env), p: resolve(a.p, env), o: resolve(a.o, env) })) };
|
|
78
83
|
return term;
|
|
79
84
|
}
|
|
@@ -91,6 +96,14 @@ function unify(a, b, env) {
|
|
|
91
96
|
}
|
|
92
97
|
return out;
|
|
93
98
|
}
|
|
99
|
+
if (a.kind === 'triple' || b.kind === 'triple') {
|
|
100
|
+
if (a.kind !== 'triple' || b.kind !== 'triple') return null;
|
|
101
|
+
let out = unify(a.s, b.s, env);
|
|
102
|
+
if (!out) return null;
|
|
103
|
+
out = unify(a.p, b.p, out);
|
|
104
|
+
if (!out) return null;
|
|
105
|
+
return unify(a.o, b.o, out);
|
|
106
|
+
}
|
|
94
107
|
return deepEqual(a, b) ? env : null;
|
|
95
108
|
}
|
|
96
109
|
function bind(pattern, value, env) { return unify(pattern, value, env); }
|
|
@@ -106,6 +119,7 @@ function termIsGround(t, env) {
|
|
|
106
119
|
const r = resolve(t, env);
|
|
107
120
|
if (r.kind === 'var') return false;
|
|
108
121
|
if (r.kind === 'list') return r.items.every((item) => termIsGround(item, env));
|
|
122
|
+
if (r.kind === 'triple') return termIsGround(r.s, env) && termIsGround(r.p, env) && termIsGround(r.o, env);
|
|
109
123
|
if (r.kind === 'formula') return r.atoms.every((atom) => atomIsGround(atom, env));
|
|
110
124
|
return true;
|
|
111
125
|
}
|
|
@@ -665,6 +679,7 @@ function instantiate(term, env, ruleId) {
|
|
|
665
679
|
}
|
|
666
680
|
if (term.kind === 'blank') return blank('_:r' + ruleId + '_' + envSignature(env) + '_' + term.value.replace(/^_/, ''));
|
|
667
681
|
if (term.kind === 'list') return list(term.items.map((item) => instantiate(item, env, ruleId)));
|
|
682
|
+
if (term.kind === 'triple') return { kind: 'triple', s: instantiate(term.s, env, ruleId), p: instantiate(term.p, env, ruleId), o: instantiate(term.o, env, ruleId) };
|
|
668
683
|
if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: instantiate(a.s, env, ruleId), p: instantiate(a.p, env, ruleId), o: instantiate(a.o, env, ruleId) })) };
|
|
669
684
|
return cloneTerm(term);
|
|
670
685
|
}
|
|
@@ -1428,6 +1443,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
|
|
|
1428
1443
|
}
|
|
1429
1444
|
return out;
|
|
1430
1445
|
}
|
|
1446
|
+
function termHasTripleTerm(term) {
|
|
1447
|
+
if (!term) return false;
|
|
1448
|
+
if (term.kind === 'triple') return true;
|
|
1449
|
+
if (term.kind === 'list') return term.items.some(termHasTripleTerm);
|
|
1450
|
+
if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
|
|
1451
|
+
return false;
|
|
1452
|
+
}
|
|
1453
|
+
function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
|
|
1454
|
+
function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
|
|
1455
|
+
function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
|
|
1431
1456
|
function trigGraphBlock(label, atoms) {
|
|
1432
1457
|
const lines = [label + ' {'];
|
|
1433
1458
|
for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
|
|
@@ -1475,7 +1500,8 @@ function formalOutputToTrig(facts, trig) {
|
|
|
1475
1500
|
const prefixes = prefixLinesFromTrig(trig);
|
|
1476
1501
|
if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
|
|
1477
1502
|
const nl = String.fromCharCode(10);
|
|
1478
|
-
|
|
1503
|
+
const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
|
|
1504
|
+
return version + prefixes.join(nl) + nl + nl + body.join(nl);
|
|
1479
1505
|
}
|
|
1480
1506
|
function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
|
|
1481
1507
|
const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);
|
package/see/see.js
CHANGED
|
@@ -183,7 +183,7 @@ function Blank(id) {
|
|
|
183
183
|
|
|
184
184
|
// This tokenizer/parser is deliberately smaller than a complete N3 parser. It
|
|
185
185
|
// accepts the SEE example subset: triples, lists, blank-node property lists,
|
|
186
|
-
// quoted formulas, implication arrows, variables, literals, and prefix lines.
|
|
186
|
+
// quoted formulas, RDF 1.2 triple terms, implication arrows, variables, literals, and prefix/version lines.
|
|
187
187
|
function tokenize(source) {
|
|
188
188
|
const s = removeComments(source);
|
|
189
189
|
const tokens = [];
|
|
@@ -196,6 +196,16 @@ function tokenize(source) {
|
|
|
196
196
|
i += 1;
|
|
197
197
|
continue;
|
|
198
198
|
}
|
|
199
|
+
if (s.startsWith('<<(', i)) {
|
|
200
|
+
tokens.push({ type: '<<(', value: '<<(' });
|
|
201
|
+
i += 3;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (s.startsWith(')>>', i)) {
|
|
205
|
+
tokens.push({ type: ')>>', value: ')>>' });
|
|
206
|
+
i += 3;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
199
209
|
if (s.startsWith('=>', i) || s.startsWith('<=', i) || s.startsWith('^^', i)) {
|
|
200
210
|
tokens.push({ type: s.slice(i, i + 2), value: s.slice(i, i + 2) });
|
|
201
211
|
i += 2;
|
|
@@ -255,6 +265,7 @@ function tokenize(source) {
|
|
|
255
265
|
|
|
256
266
|
function classifyToken(raw) {
|
|
257
267
|
if (raw === '@prefix') return { type: '@prefix', value: raw };
|
|
268
|
+
if (/^VERSION$/i.test(raw)) return { type: 'VERSION', value: raw };
|
|
258
269
|
if (raw === 'a') return { type: 'qname', value: 'rdf:type' };
|
|
259
270
|
if (raw.startsWith('?')) return { type: 'var', value: raw.slice(1) };
|
|
260
271
|
if (/^(true|false)$/i.test(raw)) return { type: 'boolean', value: /^true$/i.test(raw) };
|
|
@@ -300,6 +311,11 @@ class Parser {
|
|
|
300
311
|
// Prefix declaration is irrelevant after QName compaction; skip until final dot.
|
|
301
312
|
while (!this.eof() && !this.accept('.')) this.next();
|
|
302
313
|
}
|
|
314
|
+
skipVersion() {
|
|
315
|
+
this.expect('VERSION');
|
|
316
|
+
if (this.peek('string')) this.next();
|
|
317
|
+
this.accept('.');
|
|
318
|
+
}
|
|
303
319
|
parseProgram() {
|
|
304
320
|
const facts = [],
|
|
305
321
|
rules = [],
|
|
@@ -318,6 +334,10 @@ class Parser {
|
|
|
318
334
|
if (m) prefixes[(m[1] || ':').replace(/:$/, '')] = m[2];
|
|
319
335
|
continue;
|
|
320
336
|
}
|
|
337
|
+
if (this.peek('VERSION')) {
|
|
338
|
+
this.skipVersion();
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
321
341
|
if (this.peek('{')) {
|
|
322
342
|
const lhs = this.parseFormula('body');
|
|
323
343
|
if (this.accept('=>')) {
|
|
@@ -417,6 +437,13 @@ class Parser {
|
|
|
417
437
|
}
|
|
418
438
|
if (tok.type === 'number' || tok.type === 'boolean') return L(tok.value);
|
|
419
439
|
if (tok.type === 'iri' || tok.type === 'qname') return I(tok.value);
|
|
440
|
+
if (tok.type === '<<(') {
|
|
441
|
+
const subject = this.parseTerm(mode, sink);
|
|
442
|
+
const predicate = this.parsePredicate();
|
|
443
|
+
const object = this.parseTerm(mode, sink);
|
|
444
|
+
this.expect(')>>');
|
|
445
|
+
return { kind: 'triple', s: subject, p: predicate, o: object };
|
|
446
|
+
}
|
|
420
447
|
if (tok.type === '(') {
|
|
421
448
|
const items = [];
|
|
422
449
|
while (!this.accept(')')) items.push(this.parseTerm(mode, sink));
|
|
@@ -470,6 +497,7 @@ function termToJsComment(term) {
|
|
|
470
497
|
if (term.kind === 'var') return `?${term.value}`;
|
|
471
498
|
if (term.kind === 'blank') return term.value;
|
|
472
499
|
if (term.kind === 'list') return `(${term.items.map(termToJsComment).join(' ')})`;
|
|
500
|
+
if (term.kind === 'triple') return `<<( ${termToJsComment(term.s)} ${termToJsComment(term.p)} ${termToJsComment(term.o)} )>>`;
|
|
473
501
|
if (term.kind === 'formula') return `{ ${term.atoms.map(atomToComment).join(' . ')} }`;
|
|
474
502
|
return String(term.value ?? term);
|
|
475
503
|
}
|
|
@@ -524,12 +552,30 @@ function inputTermToN3(term) {
|
|
|
524
552
|
if (term.kind === 'var') return '?' + term.value;
|
|
525
553
|
if (term.kind === 'blank') return term.value.startsWith('_:') ? term.value : '_:' + term.value.replace(/^_+/, '');
|
|
526
554
|
if (term.kind === 'list') return '(' + term.items.map(inputTermToN3).join(' ') + ')';
|
|
555
|
+
if (term.kind === 'triple') return '<<( ' + inputTermToN3(term.s) + ' ' + inputTermToN3(term.p) + ' ' + inputTermToN3(term.o) + ' )>>';
|
|
527
556
|
if (term.kind === 'formula') return '{ ' + term.atoms.map(inputAtomToN3).join(' . ') + ' }';
|
|
528
557
|
return String(term.value ?? term);
|
|
529
558
|
}
|
|
530
559
|
function inputAtomToN3(atom) {
|
|
531
560
|
return inputTermToN3(atom.s) + ' ' + inputTermToN3(atom.p) + ' ' + inputTermToN3(atom.o);
|
|
532
561
|
}
|
|
562
|
+
function inputTermHasTripleTerm(term) {
|
|
563
|
+
if (!term) return false;
|
|
564
|
+
if (term.kind === 'triple') return true;
|
|
565
|
+
if (term.kind === 'list') return term.items.some(inputTermHasTripleTerm);
|
|
566
|
+
if (term.kind === 'formula') return term.atoms.some(inputAtomHasTripleTerm);
|
|
567
|
+
return false;
|
|
568
|
+
}
|
|
569
|
+
function inputAtomHasTripleTerm(atom) {
|
|
570
|
+
return inputTermHasTripleTerm(atom.s) || inputTermHasTripleTerm(atom.p) || inputTermHasTripleTerm(atom.o);
|
|
571
|
+
}
|
|
572
|
+
function programHasTripleTerms(program) {
|
|
573
|
+
return [
|
|
574
|
+
...(program.facts || []),
|
|
575
|
+
...(program.rules || []).flatMap((r) => [...(r.body || []), ...(r.head || [])]),
|
|
576
|
+
...(program.queries || []).flatMap((q) => [...(q.premise || []), ...(q.conclusion || [])]),
|
|
577
|
+
].some(inputAtomHasTripleTerm);
|
|
578
|
+
}
|
|
533
579
|
function formulaBlock(label, atoms) {
|
|
534
580
|
const lines = [label + ' {'];
|
|
535
581
|
for (const atom of atoms) lines.push(' ' + inputAtomToN3(atom) + ' .');
|
|
@@ -584,6 +630,7 @@ function generateInputTrig(n3Path, name, title, header, stats, program) {
|
|
|
584
630
|
'}',
|
|
585
631
|
].join('\n');
|
|
586
632
|
const sections = [
|
|
633
|
+
...(programHasTripleTerms(program) ? ['VERSION "1.2"', ''] : []),
|
|
587
634
|
...prefixLines(program.prefixes),
|
|
588
635
|
'',
|
|
589
636
|
'# Formal SEE input evidence in RDF 1.2 TriG.',
|
|
@@ -604,6 +651,7 @@ const crypto = require('crypto');
|
|
|
604
651
|
|
|
605
652
|
function canonical(term) {
|
|
606
653
|
if (term.kind === 'list') return ['list', term.items.map(canonical)];
|
|
654
|
+
if (term.kind === 'triple') return ['triple', canonical(term.s), canonical(term.p), canonical(term.o)];
|
|
607
655
|
if (term.kind === 'formula') return ['formula', term.atoms.map((a) => [canonical(a.s), canonical(a.p), canonical(a.o)])];
|
|
608
656
|
return [term.kind, term.value];
|
|
609
657
|
}
|
|
@@ -613,6 +661,7 @@ function compoundIndexKey() { return Array.from(arguments).map(termIndexKey).joi
|
|
|
613
661
|
function termIsConcrete(t) {
|
|
614
662
|
if (!t || t.kind === 'var') return false;
|
|
615
663
|
if (t.kind === 'list') return t.items.every(termIsConcrete);
|
|
664
|
+
if (t.kind === 'triple') return termIsConcrete(t.s) && termIsConcrete(t.p) && termIsConcrete(t.o);
|
|
616
665
|
if (t.kind === 'formula') return t.atoms.every((a) => termIsConcrete(a.s) && termIsConcrete(a.p) && termIsConcrete(a.o));
|
|
617
666
|
return true;
|
|
618
667
|
}
|
|
@@ -628,6 +677,7 @@ function primitive(t) {
|
|
|
628
677
|
if (t.kind === 'iri') return t.value.replace(/^:/, '');
|
|
629
678
|
if (t.kind === 'blank') return t.value;
|
|
630
679
|
if (t.kind === 'list') return t.items.map(primitive);
|
|
680
|
+
if (t.kind === 'triple') return termToN3(t);
|
|
631
681
|
if (t.kind === 'formula') return termToN3(t);
|
|
632
682
|
return undefined;
|
|
633
683
|
}
|
|
@@ -648,6 +698,7 @@ function termToN3(t) {
|
|
|
648
698
|
if (t.kind === 'var') return '?' + t.value;
|
|
649
699
|
if (t.kind === 'blank') return t.value.startsWith('_:') ? t.value : '_:' + t.value.replace(/^_+/, '');
|
|
650
700
|
if (t.kind === 'list') return '(' + t.items.map(termToN3).join(' ') + ')';
|
|
701
|
+
if (t.kind === 'triple') return '<<( ' + termToN3(t.s) + ' ' + termToN3(t.p) + ' ' + termToN3(t.o) + ' )>>';
|
|
651
702
|
if (t.kind === 'formula') return '{ ' + t.atoms.map(atomToN3).join(' . ') + ' }';
|
|
652
703
|
return String(t.value ?? t);
|
|
653
704
|
}
|
|
@@ -670,6 +721,7 @@ function resolve(term, env, seen = new Set()) {
|
|
|
670
721
|
return resolve(env[term.value], env, seen);
|
|
671
722
|
}
|
|
672
723
|
if (term.kind === 'list') return list(term.items.map((item) => resolve(item, env, seen)));
|
|
724
|
+
if (term.kind === 'triple') return { kind: 'triple', s: resolve(term.s, env), p: resolve(term.p, env), o: resolve(term.o, env) };
|
|
673
725
|
if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: resolve(a.s, env), p: resolve(a.p, env), o: resolve(a.o, env) })) };
|
|
674
726
|
return term;
|
|
675
727
|
}
|
|
@@ -687,6 +739,14 @@ function unify(a, b, env) {
|
|
|
687
739
|
}
|
|
688
740
|
return out;
|
|
689
741
|
}
|
|
742
|
+
if (a.kind === 'triple' || b.kind === 'triple') {
|
|
743
|
+
if (a.kind !== 'triple' || b.kind !== 'triple') return null;
|
|
744
|
+
let out = unify(a.s, b.s, env);
|
|
745
|
+
if (!out) return null;
|
|
746
|
+
out = unify(a.p, b.p, out);
|
|
747
|
+
if (!out) return null;
|
|
748
|
+
return unify(a.o, b.o, out);
|
|
749
|
+
}
|
|
690
750
|
return deepEqual(a, b) ? env : null;
|
|
691
751
|
}
|
|
692
752
|
function bind(pattern, value, env) { return unify(pattern, value, env); }
|
|
@@ -702,6 +762,7 @@ function termIsGround(t, env) {
|
|
|
702
762
|
const r = resolve(t, env);
|
|
703
763
|
if (r.kind === 'var') return false;
|
|
704
764
|
if (r.kind === 'list') return r.items.every((item) => termIsGround(item, env));
|
|
765
|
+
if (r.kind === 'triple') return termIsGround(r.s, env) && termIsGround(r.p, env) && termIsGround(r.o, env);
|
|
705
766
|
if (r.kind === 'formula') return r.atoms.every((atom) => atomIsGround(atom, env));
|
|
706
767
|
return true;
|
|
707
768
|
}
|
|
@@ -1261,6 +1322,7 @@ function instantiate(term, env, ruleId) {
|
|
|
1261
1322
|
}
|
|
1262
1323
|
if (term.kind === 'blank') return blank('_:r' + ruleId + '_' + envSignature(env) + '_' + term.value.replace(/^_/, ''));
|
|
1263
1324
|
if (term.kind === 'list') return list(term.items.map((item) => instantiate(item, env, ruleId)));
|
|
1325
|
+
if (term.kind === 'triple') return { kind: 'triple', s: instantiate(term.s, env, ruleId), p: instantiate(term.p, env, ruleId), o: instantiate(term.o, env, ruleId) };
|
|
1264
1326
|
if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: instantiate(a.s, env, ruleId), p: instantiate(a.p, env, ruleId), o: instantiate(a.o, env, ruleId) })) };
|
|
1265
1327
|
return cloneTerm(term);
|
|
1266
1328
|
}
|
|
@@ -1844,6 +1906,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
|
|
|
1844
1906
|
}
|
|
1845
1907
|
return out;
|
|
1846
1908
|
}
|
|
1909
|
+
function termHasTripleTerm(term) {
|
|
1910
|
+
if (!term) return false;
|
|
1911
|
+
if (term.kind === 'triple') return true;
|
|
1912
|
+
if (term.kind === 'list') return term.items.some(termHasTripleTerm);
|
|
1913
|
+
if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
|
|
1914
|
+
return false;
|
|
1915
|
+
}
|
|
1916
|
+
function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
|
|
1917
|
+
function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
|
|
1918
|
+
function trigHasVersion12(trig) { return /^\s*(?:@version|VERSION)\s+["']1\.2["']/mi.test(String(trig || '')); }
|
|
1847
1919
|
function trigGraphBlock(label, atoms) {
|
|
1848
1920
|
const lines = [label + ' {'];
|
|
1849
1921
|
for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
|
|
@@ -1891,7 +1963,8 @@ function formalOutputToTrig(facts, trig) {
|
|
|
1891
1963
|
const prefixes = prefixLinesFromTrig(trig);
|
|
1892
1964
|
if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
|
|
1893
1965
|
const nl = String.fromCharCode(10);
|
|
1894
|
-
|
|
1966
|
+
const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
|
|
1967
|
+
return version + prefixes.join(nl) + nl + nl + body.join(nl);
|
|
1895
1968
|
}
|
|
1896
1969
|
function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
|
|
1897
1970
|
const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);
|
package/test/api.test.js
CHANGED
|
@@ -2539,6 +2539,26 @@ _:b a ex:Person ; ex:name "B" .
|
|
|
2539
2539
|
`,
|
|
2540
2540
|
expect: [/^:test\s+:is\s+true\s*\./m],
|
|
2541
2541
|
},
|
|
2542
|
+
|
|
2543
|
+
{
|
|
2544
|
+
name: 'RDF 1.2 triple terms are accepted as N3 singleton graph terms',
|
|
2545
|
+
opt: { proofComments: false },
|
|
2546
|
+
input: `VERSION "1.2"
|
|
2547
|
+
@prefix : <http://example.org/triple-terms#> .
|
|
2548
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
|
2549
|
+
|
|
2550
|
+
:observation rdf:reifies <<( :sensor :reports :overheating )>> .
|
|
2551
|
+
:overheating :requires :inspection .
|
|
2552
|
+
|
|
2553
|
+
{
|
|
2554
|
+
?observation rdf:reifies <<( ?device :reports ?condition )>> .
|
|
2555
|
+
?condition :requires ?action .
|
|
2556
|
+
} => {
|
|
2557
|
+
?observation :entails <<( ?device :needs ?action )>> .
|
|
2558
|
+
} .
|
|
2559
|
+
`,
|
|
2560
|
+
expect: [/:observation\s+:entails\s+\{\s+:sensor\s+:needs\s+:inspection\s*\.\s*\}\s*\./m],
|
|
2561
|
+
},
|
|
2542
2562
|
];
|
|
2543
2563
|
|
|
2544
2564
|
let passed = 0;
|