eyeling 1.24.1 → 1.24.3

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 (77) hide show
  1. package/HANDBOOK.md +134 -0
  2. package/dist/browser/eyeling.browser.js +454 -22
  3. package/eyeling.js +454 -22
  4. package/index.js +2 -0
  5. package/lib/cli.js +27 -10
  6. package/lib/engine.js +17 -7
  7. package/lib/lexer.js +295 -2
  8. package/lib/multisource.js +9 -2
  9. package/lib/printing.js +106 -1
  10. package/package.json +1 -1
  11. package/see/README.md +4 -0
  12. package/see/examples/_see.js +33 -2
  13. package/see/examples/age.js +27 -1
  14. package/see/examples/annotation.js +27 -1
  15. package/see/examples/backward.js +27 -1
  16. package/see/examples/backward_recursion.js +27 -1
  17. package/see/examples/bayes_diagnosis.js +27 -1
  18. package/see/examples/bayes_therapy.js +27 -1
  19. package/see/examples/bmi.js +27 -1
  20. package/see/examples/builtin_coverage.js +27 -1
  21. package/see/examples/collection.js +27 -1
  22. package/see/examples/complex.js +27 -1
  23. package/see/examples/complex_matrix_stability.js +27 -1
  24. package/see/examples/composition_of_injective_functions_is_injective.js +27 -1
  25. package/see/examples/control_system.js +27 -1
  26. package/see/examples/crypto_builtins_tests.js +27 -1
  27. package/see/examples/delfour.js +27 -1
  28. package/see/examples/digital_product_passport.js +27 -1
  29. package/see/examples/dijkstra.js +27 -1
  30. package/see/examples/dijkstra_risk_path.js +27 -1
  31. package/see/examples/doc/rdf_dataset.md +26 -0
  32. package/see/examples/doc/triple_terms.md +26 -0
  33. package/see/examples/dog.js +27 -1
  34. package/see/examples/eco_route_insight.js +27 -1
  35. package/see/examples/equals.js +27 -1
  36. package/see/examples/equivalence_classes_overlap_implies_same_class.js +27 -1
  37. package/see/examples/euler_identity.js +27 -1
  38. package/see/examples/ev_roundtrip_planner.js +27 -1
  39. package/see/examples/existential_rule.js +27 -1
  40. package/see/examples/expression_eval.js +27 -1
  41. package/see/examples/family_cousins.js +27 -1
  42. package/see/examples/fastpow.js +27 -1
  43. package/see/examples/fibonacci.js +27 -1
  44. package/see/examples/french_cities.js +27 -1
  45. package/see/examples/fundamental_theorem_arithmetic.js +27 -1
  46. package/see/examples/genetic_knapsack_selection.js +27 -1
  47. package/see/examples/goldbach_1000.js +27 -1
  48. package/see/examples/good_cobbler.js +27 -1
  49. package/see/examples/gps.js +27 -1
  50. package/see/examples/gray_code_counter.js +27 -1
  51. package/see/examples/greatest_lower_bound_uniqueness.js +27 -1
  52. package/see/examples/group_inverse_uniqueness.js +27 -1
  53. package/see/examples/hadamard_approx.js +27 -1
  54. package/see/examples/hanoi.js +27 -1
  55. package/see/examples/input/rdf_dataset.trig +34 -0
  56. package/see/examples/input/triple_terms.trig +28 -0
  57. package/see/examples/n3/rdf_dataset.n3 +34 -0
  58. package/see/examples/n3/triple_terms.n3 +23 -0
  59. package/see/examples/odrl_dpv_risk_ranked.js +27 -1
  60. package/see/examples/output/rdf_dataset.md +54 -0
  61. package/see/examples/output/triple_terms.md +53 -0
  62. package/see/examples/path_discovery.js +27 -1
  63. package/see/examples/rc_discharge_envelope.js +27 -1
  64. package/see/examples/rdf_dataset.js +1512 -0
  65. package/see/examples/rdf_message_flow.js +27 -1
  66. package/see/examples/rdf_messages.js +27 -1
  67. package/see/examples/school_placement_audit.js +27 -1
  68. package/see/examples/smoke_arithmetic.js +27 -1
  69. package/see/examples/socrates.js +27 -1
  70. package/see/examples/triple_terms.js +1442 -0
  71. package/see/examples/wind_turbine.js +27 -1
  72. package/see/examples/witch.js +27 -1
  73. package/see/see.js +101 -4
  74. package/test/api.test.js +86 -0
  75. package/test/see.test.js +0 -0
  76. package/tools/bundle.js +0 -0
  77. package/tools/n3gen.js +0 -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
  }
@@ -1827,6 +1842,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1827
1842
  }
1828
1843
  return out;
1829
1844
  }
1845
+ function termHasTripleTerm(term) {
1846
+ if (!term) return false;
1847
+ if (term.kind === 'triple') return true;
1848
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1849
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1850
+ return false;
1851
+ }
1852
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1853
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1854
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1830
1855
  function trigGraphBlock(label, atoms) {
1831
1856
  const lines = [label + ' {'];
1832
1857
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1874,7 +1899,8 @@ function formalOutputToTrig(facts, trig) {
1874
1899
  const prefixes = prefixLinesFromTrig(trig);
1875
1900
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1876
1901
  const nl = String.fromCharCode(10);
1877
- return prefixes.join(nl) + nl + nl + body.join(nl);
1902
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
1903
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1878
1904
  }
1879
1905
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
1880
1906
  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
  }
@@ -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,34 @@
1
+ VERSION "1.2"
2
+
3
+ @prefix : <https://eyereasoner.github.io/see/examples/rdf-dataset#> .
4
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
5
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
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
+ :sensor :reports :overheating .
13
+ :overheating :requires :inspection .
14
+ :maintenanceSystem :trusted true .
15
+
16
+ :factoryDataset {
17
+ :observation rdf:reifies <<( :sensor :reports :overheating )>> .
18
+ :observation :recordedBy :maintenanceSystem .
19
+ }
20
+
21
+ in:metadata {
22
+ in:run a see:InputDataset .
23
+ in:run see:name "rdf_dataset" .
24
+ in:run see:title "RDF dataset compatibility" .
25
+ in:run see:sourceFile "examples/n3/rdf_dataset.n3" .
26
+ in:run see:sourceSHA256 "76035a24eb72fc1d28a5e1f3021baefee0dde667aa8fc9f2b0a111f9c773b744" .
27
+ in:run see:description "RDF 1.1 named graph data and RDF 1.2 triple terms are normalized to ordinary N3 graph terms before SEE compiles the derivation." .
28
+ in:run see:compiler "see.js N3-to-JS compiler" .
29
+ in:run see:inputFacts 4 .
30
+ in:run see:compiledRules 1 .
31
+ in:run see:compiledBackwardRules 0 .
32
+ in:run see:compiledFuses 0 .
33
+ in:run see:compiledQueries 1 .
34
+ }
@@ -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,34 @@
1
+ # RDF dataset compatibility
2
+ # RDF 1.1 named graph data and RDF 1.2 triple terms are normalized to ordinary N3 graph terms before SEE compiles the derivation.
3
+
4
+ VERSION "1.2"
5
+ @prefix : <https://eyereasoner.github.io/see/examples/rdf-dataset#> .
6
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
7
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
8
+
9
+ :sensor :reports :overheating .
10
+ :overheating :requires :inspection .
11
+ :maintenanceSystem :trusted true .
12
+
13
+ :factoryDataset {
14
+ :observation rdf:reifies <<( :sensor :reports :overheating )>> .
15
+ :observation :recordedBy :maintenanceSystem .
16
+ }
17
+
18
+ {
19
+ :sensor :reports ?condition .
20
+ ?condition :requires ?action .
21
+ :maintenanceSystem :trusted true .
22
+ } => {
23
+ :workOrder :entails <<( :sensor :needs ?action )>> .
24
+ :audit log:nameOf {
25
+ :workOrder :basedOn :factoryDataset .
26
+ :workOrder :checkedBy :maintenanceSystem .
27
+ } .
28
+ } .
29
+
30
+ {
31
+ :workOrder :entails <<( ?device :needs ?action )>> .
32
+ } log:query {
33
+ :workOrder :entails <<( ?device :needs ?action )>> .
34
+ } .
@@ -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,54 @@
1
+ # RDF dataset compatibility
2
+
3
+ ## Entailment
4
+ The compiled query selected 1 fact(s) after the rule closure was computed.
5
+ Main entailment: **:workOrder :entails <<( :sensor :needs :inspection )>>.**
6
+
7
+ Selected entailments:
8
+ - :workOrder :entails <<( :sensor :needs :inspection )>> .
9
+
10
+ ## Explanation
11
+ Starts with 4 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 (3 premise pattern(s) => 2 conclusion pattern(s)) derives :workOrder :entails <<( :sensor :needs :inspection )>> ., :audit log:nameOf { :workOrder :basedOn :factoryDataset . :workOrder :checkedBy :maintenanceSystem } .
16
+ - Uses: :sensor :reports :overheating . _(source)_; :overheating :requires :inspection . _(source)_; :maintenanceSystem :trusted true . _(source)_
17
+
18
+ Selected explanation support:
19
+ - :workOrder :entails <<( :sensor :needs :inspection )>> . _(derived by Rule 1)_
20
+ - :sensor :reports :overheating . _(source)_
21
+ - :overheating :requires :inspection . _(source)_
22
+ - :maintenanceSystem :trusted true . _(source)_
23
+
24
+ The query-selected facts are serialized in the Formal TriG Output section.
25
+
26
+ ## Formal TriG Output
27
+
28
+ ```trig
29
+ VERSION "1.2"
30
+
31
+ @prefix : <https://eyereasoner.github.io/see/examples/rdf-dataset#> .
32
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
33
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
34
+ @prefix see: <https://example.org/see#> .
35
+ @prefix in: <https://example.org/see/input#> .
36
+
37
+ :workOrder :entails <<( :sensor :needs :inspection )>> .
38
+
39
+ in:metadata {
40
+ in:run a see:InputDataset .
41
+ in:run see:name "rdf_dataset" .
42
+ in:run see:title "RDF dataset compatibility" .
43
+ in:run see:sourceFile "examples/n3/rdf_dataset.n3" .
44
+ in:run see:sourceSHA256 "76035a24eb72fc1d28a5e1f3021baefee0dde667aa8fc9f2b0a111f9c773b744" .
45
+ in:run see:description "RDF 1.1 named graph data and RDF 1.2 triple terms are normalized to ordinary N3 graph terms before SEE compiles the derivation." .
46
+ in:run see:compiler "see.js N3-to-JS compiler" .
47
+ in:run see:inputFacts 4 .
48
+ in:run see:compiledRules 1 .
49
+ in:run see:compiledBackwardRules 0 .
50
+ in:run see:compiledFuses 0 .
51
+ in:run see:compiledQueries 1 .
52
+ }
53
+ ```
54
+