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.
Files changed (64) hide show
  1. package/HANDBOOK.md +98 -0
  2. package/dist/browser/eyeling.browser.js +103 -0
  3. package/eyeling.js +103 -0
  4. package/lib/lexer.js +103 -0
  5. package/package.json +1 -1
  6. package/see/README.md +3 -0
  7. package/see/examples/_see.js +33 -2
  8. package/see/examples/age.js +27 -1
  9. package/see/examples/annotation.js +27 -1
  10. package/see/examples/backward.js +27 -1
  11. package/see/examples/backward_recursion.js +27 -1
  12. package/see/examples/bayes_diagnosis.js +27 -1
  13. package/see/examples/bayes_therapy.js +27 -1
  14. package/see/examples/bmi.js +27 -1
  15. package/see/examples/builtin_coverage.js +27 -1
  16. package/see/examples/collection.js +27 -1
  17. package/see/examples/complex.js +27 -1
  18. package/see/examples/complex_matrix_stability.js +27 -1
  19. package/see/examples/composition_of_injective_functions_is_injective.js +27 -1
  20. package/see/examples/control_system.js +27 -1
  21. package/see/examples/crypto_builtins_tests.js +27 -1
  22. package/see/examples/delfour.js +27 -1
  23. package/see/examples/digital_product_passport.js +27 -1
  24. package/see/examples/dijkstra.js +27 -1
  25. package/see/examples/dijkstra_risk_path.js +27 -1
  26. package/see/examples/doc/triple_terms.md +26 -0
  27. package/see/examples/dog.js +27 -1
  28. package/see/examples/eco_route_insight.js +27 -1
  29. package/see/examples/equals.js +27 -1
  30. package/see/examples/equivalence_classes_overlap_implies_same_class.js +27 -1
  31. package/see/examples/euler_identity.js +27 -1
  32. package/see/examples/ev_roundtrip_planner.js +27 -1
  33. package/see/examples/existential_rule.js +27 -1
  34. package/see/examples/expression_eval.js +27 -1
  35. package/see/examples/family_cousins.js +27 -1
  36. package/see/examples/fastpow.js +27 -1
  37. package/see/examples/fibonacci.js +27 -1
  38. package/see/examples/french_cities.js +27 -1
  39. package/see/examples/fundamental_theorem_arithmetic.js +27 -1
  40. package/see/examples/genetic_knapsack_selection.js +27 -1
  41. package/see/examples/goldbach_1000.js +27 -1
  42. package/see/examples/good_cobbler.js +27 -1
  43. package/see/examples/gps.js +27 -1
  44. package/see/examples/gray_code_counter.js +27 -1
  45. package/see/examples/greatest_lower_bound_uniqueness.js +27 -1
  46. package/see/examples/group_inverse_uniqueness.js +27 -1
  47. package/see/examples/hadamard_approx.js +27 -1
  48. package/see/examples/hanoi.js +27 -1
  49. package/see/examples/input/triple_terms.trig +28 -0
  50. package/see/examples/n3/triple_terms.n3 +23 -0
  51. package/see/examples/odrl_dpv_risk_ranked.js +27 -1
  52. package/see/examples/output/triple_terms.md +53 -0
  53. package/see/examples/path_discovery.js +27 -1
  54. package/see/examples/rc_discharge_envelope.js +27 -1
  55. package/see/examples/rdf_message_flow.js +27 -1
  56. package/see/examples/rdf_messages.js +27 -1
  57. package/see/examples/school_placement_audit.js +27 -1
  58. package/see/examples/smoke_arithmetic.js +27 -1
  59. package/see/examples/socrates.js +27 -1
  60. package/see/examples/triple_terms.js +1442 -0
  61. package/see/examples/wind_turbine.js +27 -1
  62. package/see/examples/witch.js +27 -1
  63. package/see/see.js +75 -2
  64. 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
  }
@@ -1806,6 +1821,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1806
1821
  }
1807
1822
  return out;
1808
1823
  }
1824
+ function termHasTripleTerm(term) {
1825
+ if (!term) return false;
1826
+ if (term.kind === 'triple') return true;
1827
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1828
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1829
+ return false;
1830
+ }
1831
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1832
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1833
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1809
1834
  function trigGraphBlock(label, atoms) {
1810
1835
  const lines = [label + ' {'];
1811
1836
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1853,7 +1878,8 @@ function formalOutputToTrig(facts, trig) {
1853
1878
  const prefixes = prefixLinesFromTrig(trig);
1854
1879
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1855
1880
  const nl = String.fromCharCode(10);
1856
- return prefixes.join(nl) + nl + nl + body.join(nl);
1881
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
1882
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1857
1883
  }
1858
1884
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
1859
1885
  const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);
@@ -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
  }
@@ -4326,6 +4341,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
4326
4341
  }
4327
4342
  return out;
4328
4343
  }
4344
+ function termHasTripleTerm(term) {
4345
+ if (!term) return false;
4346
+ if (term.kind === 'triple') return true;
4347
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
4348
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
4349
+ return false;
4350
+ }
4351
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
4352
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
4353
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
4329
4354
  function trigGraphBlock(label, atoms) {
4330
4355
  const lines = [label + ' {'];
4331
4356
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -4373,7 +4398,8 @@ function formalOutputToTrig(facts, trig) {
4373
4398
  const prefixes = prefixLinesFromTrig(trig);
4374
4399
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
4375
4400
  const nl = String.fromCharCode(10);
4376
- return prefixes.join(nl) + nl + nl + body.join(nl);
4401
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
4402
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
4377
4403
  }
4378
4404
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
4379
4405
  const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);
@@ -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
  }
@@ -1534,6 +1549,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1534
1549
  }
1535
1550
  return out;
1536
1551
  }
1552
+ function termHasTripleTerm(term) {
1553
+ if (!term) return false;
1554
+ if (term.kind === 'triple') return true;
1555
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1556
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1557
+ return false;
1558
+ }
1559
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1560
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1561
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1537
1562
  function trigGraphBlock(label, atoms) {
1538
1563
  const lines = [label + ' {'];
1539
1564
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1581,7 +1606,8 @@ function formalOutputToTrig(facts, trig) {
1581
1606
  const prefixes = prefixLinesFromTrig(trig);
1582
1607
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1583
1608
  const nl = String.fromCharCode(10);
1584
- return prefixes.join(nl) + nl + nl + body.join(nl);
1609
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
1610
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1585
1611
  }
1586
1612
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
1587
1613
  const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);
@@ -0,0 +1,28 @@
1
+ VERSION "1.2"
2
+
3
+ @prefix : <https://eyereasoner.github.io/eyeling/see/examples/triple_terms#> .
4
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
5
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
6
+ @prefix see: <https://example.org/see#> .
7
+ @prefix in: <https://example.org/see/input#> .
8
+
9
+ # Formal SEE input evidence in RDF 1.2 TriG.
10
+ # The generated runner reads this TriG evidence directly.
11
+
12
+ :observation rdf:reifies <<( :sensor :reports :overheating )>> .
13
+ :overheating :requires :inspection .
14
+
15
+ in:metadata {
16
+ in:run a see:InputDataset .
17
+ in:run see:name "triple_terms" .
18
+ in:run see:title "Triple terms" .
19
+ in:run see:sourceFile "examples/n3/triple_terms.n3" .
20
+ in:run see:sourceSHA256 "f94fae1b4087f790c085c4dd1570875694238a882213951b457480ee26c06944" .
21
+ in:run see:description "Demonstrates RDF 1.2 TriG triple terms as input evidence and as a derived entailment." .
22
+ in:run see:compiler "see.js N3-to-JS compiler" .
23
+ in:run see:inputFacts 2 .
24
+ in:run see:compiledRules 1 .
25
+ in:run see:compiledBackwardRules 0 .
26
+ in:run see:compiledFuses 0 .
27
+ in:run see:compiledQueries 1 .
28
+ }
@@ -0,0 +1,23 @@
1
+ # Triple terms
2
+ #
3
+ # Demonstrates RDF 1.2 TriG triple terms as input evidence and as a derived entailment.
4
+
5
+ VERSION "1.2"
6
+
7
+ @prefix : <https://eyereasoner.github.io/eyeling/see/examples/triple_terms#> .
8
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
9
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
10
+
11
+ :observation rdf:reifies <<( :sensor :reports :overheating )>> .
12
+ :overheating :requires :inspection .
13
+
14
+ {
15
+ ?observation rdf:reifies <<( ?device :reports ?condition )>> .
16
+ ?condition :requires ?action .
17
+ } => {
18
+ ?observation :entails <<( ?device :needs ?action )>> .
19
+ } .
20
+
21
+ { ?observation :entails ?entailment }
22
+ log:query
23
+ { ?observation :entails ?entailment } .
@@ -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
  }
@@ -5037,6 +5052,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
5037
5052
  }
5038
5053
  return out;
5039
5054
  }
5055
+ function termHasTripleTerm(term) {
5056
+ if (!term) return false;
5057
+ if (term.kind === 'triple') return true;
5058
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
5059
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
5060
+ return false;
5061
+ }
5062
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
5063
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
5064
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
5040
5065
  function trigGraphBlock(label, atoms) {
5041
5066
  const lines = [label + ' {'];
5042
5067
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -5084,7 +5109,8 @@ function formalOutputToTrig(facts, trig) {
5084
5109
  const prefixes = prefixLinesFromTrig(trig);
5085
5110
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
5086
5111
  const nl = String.fromCharCode(10);
5087
- return prefixes.join(nl) + nl + nl + body.join(nl);
5112
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
5113
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
5088
5114
  }
5089
5115
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
5090
5116
  const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);
@@ -0,0 +1,53 @@
1
+ # Triple terms
2
+
3
+ ## Entailment
4
+ The compiled query selected 1 fact(s) after the rule closure was computed.
5
+ Main entailment: **:observation :entails <<( :sensor :needs :inspection )>>.**
6
+
7
+ Selected entailments:
8
+ - :observation :entails <<( :sensor :needs :inspection )>> .
9
+
10
+ ## Explanation
11
+ Starts with 2 source fact(s), applies 1 rule(s), and reaches a fixpoint.
12
+ The log:query projection then keeps only the matching fact(s) shown above.
13
+
14
+ Derivation steps:
15
+ - Rule 1: ?observation rdf:reifies <<( ?device :reports ?condition )>>; ?condition :requires ?action => ?observation :entails <<( ?device :needs ?action )>> derives :observation :entails <<( :sensor :needs :inspection )>> .
16
+ - Uses: :observation rdf:reifies <<( :sensor :reports :overheating )>> . _(source)_; :overheating :requires :inspection . _(source)_
17
+
18
+ Selected explanation support:
19
+ - :observation :entails <<( :sensor :needs :inspection )>> . _(derived by Rule 1)_
20
+ - :observation rdf:reifies <<( :sensor :reports :overheating )>> . _(source)_
21
+ - :overheating :requires :inspection . _(source)_
22
+
23
+ The query-selected facts are serialized in the Formal TriG Output section.
24
+
25
+ ## Formal TriG Output
26
+
27
+ ```trig
28
+ VERSION "1.2"
29
+
30
+ @prefix : <https://eyereasoner.github.io/eyeling/see/examples/triple_terms#> .
31
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
32
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
33
+ @prefix see: <https://example.org/see#> .
34
+ @prefix in: <https://example.org/see/input#> .
35
+
36
+ :observation :entails <<( :sensor :needs :inspection )>> .
37
+
38
+ in:metadata {
39
+ in:run a see:InputDataset .
40
+ in:run see:name "triple_terms" .
41
+ in:run see:title "Triple terms" .
42
+ in:run see:sourceFile "examples/n3/triple_terms.n3" .
43
+ in:run see:sourceSHA256 "f94fae1b4087f790c085c4dd1570875694238a882213951b457480ee26c06944" .
44
+ in:run see:description "Demonstrates RDF 1.2 TriG triple terms as input evidence and as a derived entailment." .
45
+ in:run see:compiler "see.js N3-to-JS compiler" .
46
+ in:run see:inputFacts 2 .
47
+ in:run see:compiledRules 1 .
48
+ in:run see:compiledBackwardRules 0 .
49
+ in:run see:compiledFuses 0 .
50
+ in:run see:compiledQueries 1 .
51
+ }
52
+ ```
53
+
@@ -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
  }
@@ -1683,6 +1698,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1683
1698
  }
1684
1699
  return out;
1685
1700
  }
1701
+ function termHasTripleTerm(term) {
1702
+ if (!term) return false;
1703
+ if (term.kind === 'triple') return true;
1704
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1705
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1706
+ return false;
1707
+ }
1708
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1709
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1710
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1686
1711
  function trigGraphBlock(label, atoms) {
1687
1712
  const lines = [label + ' {'];
1688
1713
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1730,7 +1755,8 @@ function formalOutputToTrig(facts, trig) {
1730
1755
  const prefixes = prefixLinesFromTrig(trig);
1731
1756
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1732
1757
  const nl = String.fromCharCode(10);
1733
- return prefixes.join(nl) + nl + nl + body.join(nl);
1758
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
1759
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1734
1760
  }
1735
1761
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
1736
1762
  const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);