eyeling 1.24.1 → 1.24.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/HANDBOOK.md +98 -0
  2. package/dist/browser/eyeling.browser.js +103 -0
  3. package/eyeling.js +103 -0
  4. package/lib/lexer.js +103 -0
  5. package/package.json +1 -1
  6. package/see/README.md +3 -0
  7. package/see/examples/_see.js +33 -2
  8. package/see/examples/age.js +27 -1
  9. package/see/examples/annotation.js +27 -1
  10. package/see/examples/backward.js +27 -1
  11. package/see/examples/backward_recursion.js +27 -1
  12. package/see/examples/bayes_diagnosis.js +27 -1
  13. package/see/examples/bayes_therapy.js +27 -1
  14. package/see/examples/bmi.js +27 -1
  15. package/see/examples/builtin_coverage.js +27 -1
  16. package/see/examples/collection.js +27 -1
  17. package/see/examples/complex.js +27 -1
  18. package/see/examples/complex_matrix_stability.js +27 -1
  19. package/see/examples/composition_of_injective_functions_is_injective.js +27 -1
  20. package/see/examples/control_system.js +27 -1
  21. package/see/examples/crypto_builtins_tests.js +27 -1
  22. package/see/examples/delfour.js +27 -1
  23. package/see/examples/digital_product_passport.js +27 -1
  24. package/see/examples/dijkstra.js +27 -1
  25. package/see/examples/dijkstra_risk_path.js +27 -1
  26. package/see/examples/doc/triple_terms.md +26 -0
  27. package/see/examples/dog.js +27 -1
  28. package/see/examples/eco_route_insight.js +27 -1
  29. package/see/examples/equals.js +27 -1
  30. package/see/examples/equivalence_classes_overlap_implies_same_class.js +27 -1
  31. package/see/examples/euler_identity.js +27 -1
  32. package/see/examples/ev_roundtrip_planner.js +27 -1
  33. package/see/examples/existential_rule.js +27 -1
  34. package/see/examples/expression_eval.js +27 -1
  35. package/see/examples/family_cousins.js +27 -1
  36. package/see/examples/fastpow.js +27 -1
  37. package/see/examples/fibonacci.js +27 -1
  38. package/see/examples/french_cities.js +27 -1
  39. package/see/examples/fundamental_theorem_arithmetic.js +27 -1
  40. package/see/examples/genetic_knapsack_selection.js +27 -1
  41. package/see/examples/goldbach_1000.js +27 -1
  42. package/see/examples/good_cobbler.js +27 -1
  43. package/see/examples/gps.js +27 -1
  44. package/see/examples/gray_code_counter.js +27 -1
  45. package/see/examples/greatest_lower_bound_uniqueness.js +27 -1
  46. package/see/examples/group_inverse_uniqueness.js +27 -1
  47. package/see/examples/hadamard_approx.js +27 -1
  48. package/see/examples/hanoi.js +27 -1
  49. package/see/examples/input/triple_terms.trig +28 -0
  50. package/see/examples/n3/triple_terms.n3 +23 -0
  51. package/see/examples/odrl_dpv_risk_ranked.js +27 -1
  52. package/see/examples/output/triple_terms.md +53 -0
  53. package/see/examples/path_discovery.js +27 -1
  54. package/see/examples/rc_discharge_envelope.js +27 -1
  55. package/see/examples/rdf_message_flow.js +27 -1
  56. package/see/examples/rdf_messages.js +27 -1
  57. package/see/examples/school_placement_audit.js +27 -1
  58. package/see/examples/smoke_arithmetic.js +27 -1
  59. package/see/examples/socrates.js +27 -1
  60. package/see/examples/triple_terms.js +1442 -0
  61. package/see/examples/wind_turbine.js +27 -1
  62. package/see/examples/witch.js +27 -1
  63. package/see/see.js +75 -2
  64. package/test/api.test.js +20 -0
@@ -8,6 +8,7 @@ const crypto = require('crypto');
8
8
 
9
9
  function canonical(term) {
10
10
  if (term.kind === 'list') return ['list', term.items.map(canonical)];
11
+ if (term.kind === 'triple') return ['triple', canonical(term.s), canonical(term.p), canonical(term.o)];
11
12
  if (term.kind === 'formula') return ['formula', term.atoms.map((a) => [canonical(a.s), canonical(a.p), canonical(a.o)])];
12
13
  return [term.kind, term.value];
13
14
  }
@@ -17,6 +18,7 @@ function compoundIndexKey() { return Array.from(arguments).map(termIndexKey).joi
17
18
  function termIsConcrete(t) {
18
19
  if (!t || t.kind === 'var') return false;
19
20
  if (t.kind === 'list') return t.items.every(termIsConcrete);
21
+ if (t.kind === 'triple') return termIsConcrete(t.s) && termIsConcrete(t.p) && termIsConcrete(t.o);
20
22
  if (t.kind === 'formula') return t.atoms.every((a) => termIsConcrete(a.s) && termIsConcrete(a.p) && termIsConcrete(a.o));
21
23
  return true;
22
24
  }
@@ -32,6 +34,7 @@ function primitive(t) {
32
34
  if (t.kind === 'iri') return t.value.replace(/^:/, '');
33
35
  if (t.kind === 'blank') return t.value;
34
36
  if (t.kind === 'list') return t.items.map(primitive);
37
+ if (t.kind === 'triple') return termToN3(t);
35
38
  if (t.kind === 'formula') return termToN3(t);
36
39
  return undefined;
37
40
  }
@@ -52,6 +55,7 @@ function termToN3(t) {
52
55
  if (t.kind === 'var') return '?' + t.value;
53
56
  if (t.kind === 'blank') return t.value.startsWith('_:') ? t.value : '_:' + t.value.replace(/^_+/, '');
54
57
  if (t.kind === 'list') return '(' + t.items.map(termToN3).join(' ') + ')';
58
+ if (t.kind === 'triple') return '<<( ' + termToN3(t.s) + ' ' + termToN3(t.p) + ' ' + termToN3(t.o) + ' )>>';
55
59
  if (t.kind === 'formula') return '{ ' + t.atoms.map(atomToN3).join(' . ') + ' }';
56
60
  return String(t.value ?? t);
57
61
  }
@@ -74,6 +78,7 @@ function resolve(term, env, seen = new Set()) {
74
78
  return resolve(env[term.value], env, seen);
75
79
  }
76
80
  if (term.kind === 'list') return list(term.items.map((item) => resolve(item, env, seen)));
81
+ if (term.kind === 'triple') return { kind: 'triple', s: resolve(term.s, env), p: resolve(term.p, env), o: resolve(term.o, env) };
77
82
  if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: resolve(a.s, env), p: resolve(a.p, env), o: resolve(a.o, env) })) };
78
83
  return term;
79
84
  }
@@ -91,6 +96,14 @@ function unify(a, b, env) {
91
96
  }
92
97
  return out;
93
98
  }
99
+ if (a.kind === 'triple' || b.kind === 'triple') {
100
+ if (a.kind !== 'triple' || b.kind !== 'triple') return null;
101
+ let out = unify(a.s, b.s, env);
102
+ if (!out) return null;
103
+ out = unify(a.p, b.p, out);
104
+ if (!out) return null;
105
+ return unify(a.o, b.o, out);
106
+ }
94
107
  return deepEqual(a, b) ? env : null;
95
108
  }
96
109
  function bind(pattern, value, env) { return unify(pattern, value, env); }
@@ -106,6 +119,7 @@ function termIsGround(t, env) {
106
119
  const r = resolve(t, env);
107
120
  if (r.kind === 'var') return false;
108
121
  if (r.kind === 'list') return r.items.every((item) => termIsGround(item, env));
122
+ if (r.kind === 'triple') return termIsGround(r.s, env) && termIsGround(r.p, env) && termIsGround(r.o, env);
109
123
  if (r.kind === 'formula') return r.atoms.every((atom) => atomIsGround(atom, env));
110
124
  return true;
111
125
  }
@@ -665,6 +679,7 @@ function instantiate(term, env, ruleId) {
665
679
  }
666
680
  if (term.kind === 'blank') return blank('_:r' + ruleId + '_' + envSignature(env) + '_' + term.value.replace(/^_/, ''));
667
681
  if (term.kind === 'list') return list(term.items.map((item) => instantiate(item, env, ruleId)));
682
+ if (term.kind === 'triple') return { kind: 'triple', s: instantiate(term.s, env, ruleId), p: instantiate(term.p, env, ruleId), o: instantiate(term.o, env, ruleId) };
668
683
  if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: instantiate(a.s, env, ruleId), p: instantiate(a.p, env, ruleId), o: instantiate(a.o, env, ruleId) })) };
669
684
  return cloneTerm(term);
670
685
  }
@@ -1398,6 +1413,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1398
1413
  }
1399
1414
  return out;
1400
1415
  }
1416
+ function termHasTripleTerm(term) {
1417
+ if (!term) return false;
1418
+ if (term.kind === 'triple') return true;
1419
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1420
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1421
+ return false;
1422
+ }
1423
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1424
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1425
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1401
1426
  function trigGraphBlock(label, atoms) {
1402
1427
  const lines = [label + ' {'];
1403
1428
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1445,7 +1470,8 @@ function formalOutputToTrig(facts, trig) {
1445
1470
  const prefixes = prefixLinesFromTrig(trig);
1446
1471
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1447
1472
  const nl = String.fromCharCode(10);
1448
- return prefixes.join(nl) + nl + nl + body.join(nl);
1473
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
1474
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1449
1475
  }
1450
1476
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
1451
1477
  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
  }
@@ -3083,6 +3098,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
3083
3098
  }
3084
3099
  return out;
3085
3100
  }
3101
+ function termHasTripleTerm(term) {
3102
+ if (!term) return false;
3103
+ if (term.kind === 'triple') return true;
3104
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
3105
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
3106
+ return false;
3107
+ }
3108
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
3109
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
3110
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
3086
3111
  function trigGraphBlock(label, atoms) {
3087
3112
  const lines = [label + ' {'];
3088
3113
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -3130,7 +3155,8 @@ function formalOutputToTrig(facts, trig) {
3130
3155
  const prefixes = prefixLinesFromTrig(trig);
3131
3156
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
3132
3157
  const nl = String.fromCharCode(10);
3133
- return prefixes.join(nl) + nl + nl + body.join(nl);
3158
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
3159
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
3134
3160
  }
3135
3161
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
3136
3162
  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
  }
@@ -2765,6 +2780,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
2765
2780
  }
2766
2781
  return out;
2767
2782
  }
2783
+ function termHasTripleTerm(term) {
2784
+ if (!term) return false;
2785
+ if (term.kind === 'triple') return true;
2786
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
2787
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
2788
+ return false;
2789
+ }
2790
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
2791
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
2792
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
2768
2793
  function trigGraphBlock(label, atoms) {
2769
2794
  const lines = [label + ' {'];
2770
2795
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -2812,7 +2837,8 @@ function formalOutputToTrig(facts, trig) {
2812
2837
  const prefixes = prefixLinesFromTrig(trig);
2813
2838
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
2814
2839
  const nl = String.fromCharCode(10);
2815
- return prefixes.join(nl) + nl + nl + body.join(nl);
2840
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
2841
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
2816
2842
  }
2817
2843
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
2818
2844
  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
  }
@@ -1979,6 +1994,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1979
1994
  }
1980
1995
  return out;
1981
1996
  }
1997
+ function termHasTripleTerm(term) {
1998
+ if (!term) return false;
1999
+ if (term.kind === 'triple') return true;
2000
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
2001
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
2002
+ return false;
2003
+ }
2004
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
2005
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
2006
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1982
2007
  function trigGraphBlock(label, atoms) {
1983
2008
  const lines = [label + ' {'];
1984
2009
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -2026,7 +2051,8 @@ function formalOutputToTrig(facts, trig) {
2026
2051
  const prefixes = prefixLinesFromTrig(trig);
2027
2052
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
2028
2053
  const nl = String.fromCharCode(10);
2029
- return prefixes.join(nl) + nl + nl + body.join(nl);
2054
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
2055
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
2030
2056
  }
2031
2057
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
2032
2058
  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
  }
@@ -1783,6 +1798,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1783
1798
  }
1784
1799
  return out;
1785
1800
  }
1801
+ function termHasTripleTerm(term) {
1802
+ if (!term) return false;
1803
+ if (term.kind === 'triple') return true;
1804
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1805
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1806
+ return false;
1807
+ }
1808
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1809
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1810
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1786
1811
  function trigGraphBlock(label, atoms) {
1787
1812
  const lines = [label + ' {'];
1788
1813
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1830,7 +1855,8 @@ function formalOutputToTrig(facts, trig) {
1830
1855
  const prefixes = prefixLinesFromTrig(trig);
1831
1856
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1832
1857
  const nl = String.fromCharCode(10);
1833
- return prefixes.join(nl) + nl + nl + body.join(nl);
1858
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
1859
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1834
1860
  }
1835
1861
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
1836
1862
  const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);
@@ -0,0 +1,26 @@
1
+ # Triple terms
2
+
3
+ Generated by `see.js` from a Notation3 source file.
4
+
5
+ Demonstrates RDF 1.2 TriG triple terms as input evidence and as a derived entailment.
6
+
7
+ ## Compilation summary
8
+
9
+ - Example name: `triple_terms`
10
+ - Input facts emitted: 2
11
+ - Forward rules compiled: 1
12
+ - Backward predicate rules compiled: 0
13
+ - Fuses compiled: 0
14
+ - Predicate count: 3
15
+
16
+ ## Built-ins used
17
+
18
+ - none
19
+
20
+ ## Runtime model
21
+
22
+ The generated `examples/triple_terms.js` is a specialized JavaScript derivation program. For ordinary sources, `see.js` emits the source facts as `examples/input/triple_terms.trig`. For rules-only sources, generation can reuse an existing external evidence file such as `examples/input/triple-terms.trig` or `examples/input/triple_terms.trig`. The runner reads that TriG evidence directly and performs a local fixpoint derivation; it does not parse the program source or call an external reasoner.
23
+
24
+ ## Output model
25
+
26
+ Running `node examples/triple_terms.js` produces a SEE-style Markdown report with an **Entailment** section, an **Explanation** section, and a **Formal TriG Output** section containing the selected derived/query facts.
@@ -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);