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
  }
@@ -2015,6 +2030,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
2015
2030
  }
2016
2031
  return out;
2017
2032
  }
2033
+ function termHasTripleTerm(term) {
2034
+ if (!term) return false;
2035
+ if (term.kind === 'triple') return true;
2036
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
2037
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
2038
+ return false;
2039
+ }
2040
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
2041
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
2042
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
2018
2043
  function trigGraphBlock(label, atoms) {
2019
2044
  const lines = [label + ' {'];
2020
2045
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -2062,7 +2087,8 @@ function formalOutputToTrig(facts, trig) {
2062
2087
  const prefixes = prefixLinesFromTrig(trig);
2063
2088
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
2064
2089
  const nl = String.fromCharCode(10);
2065
- return prefixes.join(nl) + nl + nl + body.join(nl);
2090
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
2091
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
2066
2092
  }
2067
2093
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
2068
2094
  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
  }
@@ -1652,6 +1667,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1652
1667
  }
1653
1668
  return out;
1654
1669
  }
1670
+ function termHasTripleTerm(term) {
1671
+ if (!term) return false;
1672
+ if (term.kind === 'triple') return true;
1673
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1674
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1675
+ return false;
1676
+ }
1677
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1678
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1679
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1655
1680
  function trigGraphBlock(label, atoms) {
1656
1681
  const lines = [label + ' {'];
1657
1682
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1699,7 +1724,8 @@ function formalOutputToTrig(facts, trig) {
1699
1724
  const prefixes = prefixLinesFromTrig(trig);
1700
1725
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1701
1726
  const nl = String.fromCharCode(10);
1702
- return prefixes.join(nl) + nl + nl + body.join(nl);
1727
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
1728
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1703
1729
  }
1704
1730
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
1705
1731
  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
  }
@@ -1707,6 +1722,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1707
1722
  }
1708
1723
  return out;
1709
1724
  }
1725
+ function termHasTripleTerm(term) {
1726
+ if (!term) return false;
1727
+ if (term.kind === 'triple') return true;
1728
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1729
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1730
+ return false;
1731
+ }
1732
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1733
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1734
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1710
1735
  function trigGraphBlock(label, atoms) {
1711
1736
  const lines = [label + ' {'];
1712
1737
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1754,7 +1779,8 @@ function formalOutputToTrig(facts, trig) {
1754
1779
  const prefixes = prefixLinesFromTrig(trig);
1755
1780
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1756
1781
  const nl = String.fromCharCode(10);
1757
- return prefixes.join(nl) + nl + nl + body.join(nl);
1782
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
1783
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1758
1784
  }
1759
1785
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
1760
1786
  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
  }
@@ -1305,6 +1320,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1305
1320
  }
1306
1321
  return out;
1307
1322
  }
1323
+ function termHasTripleTerm(term) {
1324
+ if (!term) return false;
1325
+ if (term.kind === 'triple') return true;
1326
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1327
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1328
+ return false;
1329
+ }
1330
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1331
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1332
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1308
1333
  function trigGraphBlock(label, atoms) {
1309
1334
  const lines = [label + ' {'];
1310
1335
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1352,7 +1377,8 @@ function formalOutputToTrig(facts, trig) {
1352
1377
  const prefixes = prefixLinesFromTrig(trig);
1353
1378
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1354
1379
  const nl = String.fromCharCode(10);
1355
- return prefixes.join(nl) + nl + nl + body.join(nl);
1380
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
1381
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1356
1382
  }
1357
1383
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
1358
1384
  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
  }
@@ -2722,6 +2737,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
2722
2737
  }
2723
2738
  return out;
2724
2739
  }
2740
+ function termHasTripleTerm(term) {
2741
+ if (!term) return false;
2742
+ if (term.kind === 'triple') return true;
2743
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
2744
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
2745
+ return false;
2746
+ }
2747
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
2748
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
2749
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
2725
2750
  function trigGraphBlock(label, atoms) {
2726
2751
  const lines = [label + ' {'];
2727
2752
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -2769,7 +2794,8 @@ function formalOutputToTrig(facts, trig) {
2769
2794
  const prefixes = prefixLinesFromTrig(trig);
2770
2795
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
2771
2796
  const nl = String.fromCharCode(10);
2772
- return prefixes.join(nl) + nl + nl + body.join(nl);
2797
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
2798
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
2773
2799
  }
2774
2800
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
2775
2801
  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
  }
@@ -1550,6 +1565,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1550
1565
  }
1551
1566
  return out;
1552
1567
  }
1568
+ function termHasTripleTerm(term) {
1569
+ if (!term) return false;
1570
+ if (term.kind === 'triple') return true;
1571
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1572
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1573
+ return false;
1574
+ }
1575
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1576
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1577
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1553
1578
  function trigGraphBlock(label, atoms) {
1554
1579
  const lines = [label + ' {'];
1555
1580
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1597,7 +1622,8 @@ function formalOutputToTrig(facts, trig) {
1597
1622
  const prefixes = prefixLinesFromTrig(trig);
1598
1623
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1599
1624
  const nl = String.fromCharCode(10);
1600
- return prefixes.join(nl) + nl + nl + body.join(nl);
1625
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
1626
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1601
1627
  }
1602
1628
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
1603
1629
  const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);