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
  }
@@ -1345,6 +1360,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1345
1360
  }
1346
1361
  return out;
1347
1362
  }
1363
+ function termHasTripleTerm(term) {
1364
+ if (!term) return false;
1365
+ if (term.kind === 'triple') return true;
1366
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1367
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1368
+ return false;
1369
+ }
1370
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1371
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1372
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1348
1373
  function trigGraphBlock(label, atoms) {
1349
1374
  const lines = [label + ' {'];
1350
1375
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1392,7 +1417,8 @@ function formalOutputToTrig(facts, trig) {
1392
1417
  const prefixes = prefixLinesFromTrig(trig);
1393
1418
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1394
1419
  const nl = String.fromCharCode(10);
1395
- return prefixes.join(nl) + nl + nl + body.join(nl);
1420
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
1421
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1396
1422
  }
1397
1423
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
1398
1424
  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
  }
@@ -2019,6 +2034,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
2019
2034
  }
2020
2035
  return out;
2021
2036
  }
2037
+ function termHasTripleTerm(term) {
2038
+ if (!term) return false;
2039
+ if (term.kind === 'triple') return true;
2040
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
2041
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
2042
+ return false;
2043
+ }
2044
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
2045
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
2046
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
2022
2047
  function trigGraphBlock(label, atoms) {
2023
2048
  const lines = [label + ' {'];
2024
2049
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -2066,7 +2091,8 @@ function formalOutputToTrig(facts, trig) {
2066
2091
  const prefixes = prefixLinesFromTrig(trig);
2067
2092
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
2068
2093
  const nl = String.fromCharCode(10);
2069
- return prefixes.join(nl) + nl + nl + body.join(nl);
2094
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
2095
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
2070
2096
  }
2071
2097
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
2072
2098
  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
  }
@@ -1272,6 +1287,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1272
1287
  }
1273
1288
  return out;
1274
1289
  }
1290
+ function termHasTripleTerm(term) {
1291
+ if (!term) return false;
1292
+ if (term.kind === 'triple') return true;
1293
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1294
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1295
+ return false;
1296
+ }
1297
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1298
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1299
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1275
1300
  function trigGraphBlock(label, atoms) {
1276
1301
  const lines = [label + ' {'];
1277
1302
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1319,7 +1344,8 @@ function formalOutputToTrig(facts, trig) {
1319
1344
  const prefixes = prefixLinesFromTrig(trig);
1320
1345
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1321
1346
  const nl = String.fromCharCode(10);
1322
- return prefixes.join(nl) + nl + nl + body.join(nl);
1347
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
1348
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1323
1349
  }
1324
1350
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
1325
1351
  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
  }
@@ -1701,6 +1716,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1701
1716
  }
1702
1717
  return out;
1703
1718
  }
1719
+ function termHasTripleTerm(term) {
1720
+ if (!term) return false;
1721
+ if (term.kind === 'triple') return true;
1722
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1723
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1724
+ return false;
1725
+ }
1726
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1727
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1728
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1704
1729
  function trigGraphBlock(label, atoms) {
1705
1730
  const lines = [label + ' {'];
1706
1731
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1748,7 +1773,8 @@ function formalOutputToTrig(facts, trig) {
1748
1773
  const prefixes = prefixLinesFromTrig(trig);
1749
1774
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1750
1775
  const nl = String.fromCharCode(10);
1751
- return prefixes.join(nl) + nl + nl + body.join(nl);
1776
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
1777
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1752
1778
  }
1753
1779
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
1754
1780
  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
  }
@@ -1947,6 +1962,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1947
1962
  }
1948
1963
  return out;
1949
1964
  }
1965
+ function termHasTripleTerm(term) {
1966
+ if (!term) return false;
1967
+ if (term.kind === 'triple') return true;
1968
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1969
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1970
+ return false;
1971
+ }
1972
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1973
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1974
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1950
1975
  function trigGraphBlock(label, atoms) {
1951
1976
  const lines = [label + ' {'];
1952
1977
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1994,7 +2019,8 @@ function formalOutputToTrig(facts, trig) {
1994
2019
  const prefixes = prefixLinesFromTrig(trig);
1995
2020
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1996
2021
  const nl = String.fromCharCode(10);
1997
- return prefixes.join(nl) + nl + nl + body.join(nl);
2022
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
2023
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1998
2024
  }
1999
2025
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
2000
2026
  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
  }
@@ -2471,6 +2486,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
2471
2486
  }
2472
2487
  return out;
2473
2488
  }
2489
+ function termHasTripleTerm(term) {
2490
+ if (!term) return false;
2491
+ if (term.kind === 'triple') return true;
2492
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
2493
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
2494
+ return false;
2495
+ }
2496
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
2497
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
2498
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
2474
2499
  function trigGraphBlock(label, atoms) {
2475
2500
  const lines = [label + ' {'];
2476
2501
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -2518,7 +2543,8 @@ function formalOutputToTrig(facts, trig) {
2518
2543
  const prefixes = prefixLinesFromTrig(trig);
2519
2544
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
2520
2545
  const nl = String.fromCharCode(10);
2521
- return prefixes.join(nl) + nl + nl + body.join(nl);
2546
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
2547
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
2522
2548
  }
2523
2549
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
2524
2550
  const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);