eyeling 1.24.0 → 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 (165) hide show
  1. package/HANDBOOK.md +98 -0
  2. package/README.md +4 -4
  3. package/dist/browser/eyeling.browser.js +103 -0
  4. package/eyeling.js +103 -0
  5. package/lib/lexer.js +103 -0
  6. package/package.json +1 -1
  7. package/see/README.md +13 -11
  8. package/see/examples/_see.js +33 -2
  9. package/see/examples/age.js +37 -11
  10. package/see/examples/annotation.js +37 -11
  11. package/see/examples/backward.js +37 -11
  12. package/see/examples/backward_recursion.js +37 -11
  13. package/see/examples/bayes_diagnosis.js +37 -11
  14. package/see/examples/bayes_therapy.js +37 -11
  15. package/see/examples/bmi.js +37 -11
  16. package/see/examples/builtin_coverage.js +37 -11
  17. package/see/examples/collection.js +37 -11
  18. package/see/examples/complex.js +37 -11
  19. package/see/examples/complex_matrix_stability.js +37 -11
  20. package/see/examples/composition_of_injective_functions_is_injective.js +37 -11
  21. package/see/examples/control_system.js +37 -11
  22. package/see/examples/crypto_builtins_tests.js +37 -11
  23. package/see/examples/delfour.js +37 -11
  24. package/see/examples/digital_product_passport.js +37 -11
  25. package/see/examples/dijkstra.js +37 -11
  26. package/see/examples/dijkstra_risk_path.js +37 -11
  27. package/see/examples/doc/age.md +1 -1
  28. package/see/examples/doc/annotation.md +1 -1
  29. package/see/examples/doc/backward.md +1 -1
  30. package/see/examples/doc/backward_recursion.md +1 -1
  31. package/see/examples/doc/bayes_diagnosis.md +1 -1
  32. package/see/examples/doc/bayes_therapy.md +1 -1
  33. package/see/examples/doc/bmi.md +1 -1
  34. package/see/examples/doc/builtin_coverage.md +1 -1
  35. package/see/examples/doc/collection.md +1 -1
  36. package/see/examples/doc/complex.md +1 -1
  37. package/see/examples/doc/complex_matrix_stability.md +1 -1
  38. package/see/examples/doc/composition_of_injective_functions_is_injective.md +1 -1
  39. package/see/examples/doc/control_system.md +1 -1
  40. package/see/examples/doc/crypto_builtins_tests.md +1 -1
  41. package/see/examples/doc/delfour.md +1 -1
  42. package/see/examples/doc/digital_product_passport.md +1 -1
  43. package/see/examples/doc/dijkstra.md +1 -1
  44. package/see/examples/doc/dijkstra_risk_path.md +1 -1
  45. package/see/examples/doc/dog.md +1 -1
  46. package/see/examples/doc/eco_route_insight.md +1 -1
  47. package/see/examples/doc/equals.md +1 -1
  48. package/see/examples/doc/equivalence_classes_overlap_implies_same_class.md +1 -1
  49. package/see/examples/doc/euler_identity.md +1 -1
  50. package/see/examples/doc/ev_roundtrip_planner.md +1 -1
  51. package/see/examples/doc/existential_rule.md +1 -1
  52. package/see/examples/doc/expression_eval.md +1 -1
  53. package/see/examples/doc/family_cousins.md +1 -1
  54. package/see/examples/doc/fastpow.md +1 -1
  55. package/see/examples/doc/fibonacci.md +1 -1
  56. package/see/examples/doc/french_cities.md +1 -1
  57. package/see/examples/doc/fundamental_theorem_arithmetic.md +1 -1
  58. package/see/examples/doc/genetic_knapsack_selection.md +1 -1
  59. package/see/examples/doc/goldbach_1000.md +1 -1
  60. package/see/examples/doc/good_cobbler.md +1 -1
  61. package/see/examples/doc/gps.md +1 -1
  62. package/see/examples/doc/gray_code_counter.md +1 -1
  63. package/see/examples/doc/greatest_lower_bound_uniqueness.md +1 -1
  64. package/see/examples/doc/group_inverse_uniqueness.md +1 -1
  65. package/see/examples/doc/hadamard_approx.md +1 -1
  66. package/see/examples/doc/hanoi.md +1 -1
  67. package/see/examples/doc/odrl_dpv_risk_ranked.md +1 -1
  68. package/see/examples/doc/path_discovery.md +1 -1
  69. package/see/examples/doc/rc_discharge_envelope.md +1 -1
  70. package/see/examples/doc/rdf_message_flow.md +1 -1
  71. package/see/examples/doc/rdf_messages.md +1 -1
  72. package/see/examples/doc/school_placement_audit.md +1 -1
  73. package/see/examples/doc/smoke_arithmetic.md +1 -1
  74. package/see/examples/doc/socrates.md +1 -1
  75. package/see/examples/doc/triple_terms.md +26 -0
  76. package/see/examples/doc/wind_turbine.md +1 -1
  77. package/see/examples/doc/witch.md +1 -1
  78. package/see/examples/dog.js +37 -11
  79. package/see/examples/eco_route_insight.js +37 -11
  80. package/see/examples/equals.js +37 -11
  81. package/see/examples/equivalence_classes_overlap_implies_same_class.js +37 -11
  82. package/see/examples/euler_identity.js +37 -11
  83. package/see/examples/ev_roundtrip_planner.js +37 -11
  84. package/see/examples/existential_rule.js +37 -11
  85. package/see/examples/expression_eval.js +37 -11
  86. package/see/examples/family_cousins.js +37 -11
  87. package/see/examples/fastpow.js +37 -11
  88. package/see/examples/fibonacci.js +37 -11
  89. package/see/examples/french_cities.js +37 -11
  90. package/see/examples/fundamental_theorem_arithmetic.js +37 -11
  91. package/see/examples/genetic_knapsack_selection.js +37 -11
  92. package/see/examples/goldbach_1000.js +37 -11
  93. package/see/examples/good_cobbler.js +37 -11
  94. package/see/examples/gps.js +37 -11
  95. package/see/examples/gray_code_counter.js +37 -11
  96. package/see/examples/greatest_lower_bound_uniqueness.js +37 -11
  97. package/see/examples/group_inverse_uniqueness.js +37 -11
  98. package/see/examples/hadamard_approx.js +37 -11
  99. package/see/examples/hanoi.js +37 -11
  100. package/see/examples/input/triple_terms.trig +28 -0
  101. package/see/examples/n3/triple_terms.n3 +23 -0
  102. package/see/examples/odrl_dpv_risk_ranked.js +37 -11
  103. package/see/examples/output/age.md +3 -3
  104. package/see/examples/output/annotation.md +4 -4
  105. package/see/examples/output/backward.md +3 -3
  106. package/see/examples/output/backward_recursion.md +3 -3
  107. package/see/examples/output/bayes_diagnosis.md +1 -1
  108. package/see/examples/output/bayes_therapy.md +1 -1
  109. package/see/examples/output/bmi.md +1 -1
  110. package/see/examples/output/builtin_coverage.md +3 -3
  111. package/see/examples/output/collection.md +3 -3
  112. package/see/examples/output/complex.md +4 -4
  113. package/see/examples/output/complex_matrix_stability.md +1 -1
  114. package/see/examples/output/composition_of_injective_functions_is_injective.md +3 -3
  115. package/see/examples/output/control_system.md +3 -3
  116. package/see/examples/output/crypto_builtins_tests.md +3 -3
  117. package/see/examples/output/delfour.md +1 -1
  118. package/see/examples/output/digital_product_passport.md +1 -1
  119. package/see/examples/output/dijkstra.md +3 -3
  120. package/see/examples/output/dijkstra_risk_path.md +1 -1
  121. package/see/examples/output/dog.md +3 -3
  122. package/see/examples/output/eco_route_insight.md +1 -1
  123. package/see/examples/output/equals.md +3 -3
  124. package/see/examples/output/equivalence_classes_overlap_implies_same_class.md +3 -3
  125. package/see/examples/output/euler_identity.md +3 -3
  126. package/see/examples/output/ev_roundtrip_planner.md +1 -1
  127. package/see/examples/output/existential_rule.md +3 -3
  128. package/see/examples/output/expression_eval.md +3 -3
  129. package/see/examples/output/family_cousins.md +3 -3
  130. package/see/examples/output/fastpow.md +1 -1
  131. package/see/examples/output/fibonacci.md +1 -1
  132. package/see/examples/output/french_cities.md +3 -3
  133. package/see/examples/output/fundamental_theorem_arithmetic.md +1 -1
  134. package/see/examples/output/genetic_knapsack_selection.md +1 -1
  135. package/see/examples/output/goldbach_1000.md +1 -1
  136. package/see/examples/output/good_cobbler.md +4 -4
  137. package/see/examples/output/gps.md +1 -1
  138. package/see/examples/output/gray_code_counter.md +1 -1
  139. package/see/examples/output/greatest_lower_bound_uniqueness.md +3 -3
  140. package/see/examples/output/group_inverse_uniqueness.md +3 -3
  141. package/see/examples/output/hadamard_approx.md +3 -3
  142. package/see/examples/output/hanoi.md +3 -3
  143. package/see/examples/output/odrl_dpv_risk_ranked.md +3 -3
  144. package/see/examples/output/path_discovery.md +3 -3
  145. package/see/examples/output/rc_discharge_envelope.md +1 -1
  146. package/see/examples/output/rdf_message_flow.md +1 -1
  147. package/see/examples/output/rdf_messages.md +1 -1
  148. package/see/examples/output/school_placement_audit.md +1 -1
  149. package/see/examples/output/smoke_arithmetic.md +1 -1
  150. package/see/examples/output/socrates.md +3 -3
  151. package/see/examples/output/triple_terms.md +53 -0
  152. package/see/examples/output/wind_turbine.md +1 -1
  153. package/see/examples/output/witch.md +3 -3
  154. package/see/examples/path_discovery.js +37 -11
  155. package/see/examples/rc_discharge_envelope.js +37 -11
  156. package/see/examples/rdf_message_flow.js +37 -11
  157. package/see/examples/rdf_messages.js +37 -11
  158. package/see/examples/school_placement_audit.js +37 -11
  159. package/see/examples/smoke_arithmetic.js +37 -11
  160. package/see/examples/socrates.js +37 -11
  161. package/see/examples/triple_terms.js +1442 -0
  162. package/see/examples/wind_turbine.js +37 -11
  163. package/see/examples/witch.js +37 -11
  164. package/see/see.js +455 -94
  165. package/test/api.test.js +20 -0
package/HANDBOOK.md CHANGED
@@ -36,6 +36,7 @@
36
36
  - [Appendix I — The Eyeling Playground](#app-i)
37
37
  - [Appendix J — Formalism Is Fine](#app-j)
38
38
  - [Appendix K — Whitehead-inspired becoming examples](#app-k)
39
+ - [Appendix L — SEE: Specialized Eyeling Executables](#app-l)
39
40
 
40
41
  ---
41
42
 
@@ -236,6 +237,20 @@ The lexer turns the input into tokens like:
236
237
 
237
238
  Parsing becomes dramatically simpler because tokenization already decided where strings end, where numbers are, and so on.
238
239
 
240
+ One compatibility wrinkle is handled in the lexer before normal parsing: RDF 1.2 triple terms written as `<<( s p o )>>` are accepted and normalized to Eyeling's existing singleton quoted-formula term `{ s p o }`. A leading `VERSION "1.2"` or `@version "1.2"` directive is ignored for the same reason. This keeps Eyeling's N3 model stable while allowing small RDF 1.2 triple-term inputs to run through the existing `GraphTerm` machinery. For example:
241
+
242
+ ```n3
243
+ :observation rdf:reifies <<( :sensor :reports :overheating )>> .
244
+ ```
245
+
246
+ is treated internally like:
247
+
248
+ ```n3
249
+ :observation rdf:reifies { :sensor :reports :overheating } .
250
+ ```
251
+
252
+ This is intentionally a compatibility translation, not a new full RDF 1.2 parser layer. Nested or more exotic future triple-term forms should be added only if they can be mapped cleanly onto Eyeling's quoted-formula term model.
253
+
239
254
  ### 4.2 Parsing triples, with Turtle-style convenience
240
255
 
241
256
  The parser supports:
@@ -2561,6 +2576,8 @@ Quoted graphs/formulas use `{ ... }`. Inside a quoted formula, directive scope m
2561
2576
 
2562
2577
  - `@prefix/@base` and `PREFIX/BASE` directives may appear at top level **or inside `{ ... }`**, and apply to the formula they occur in (formula-local scoping).
2563
2578
 
2579
+ Eyeling also accepts the RDF 1.2 triple-term surface form `<<( s p o )>>` as a compatibility spelling for a singleton quoted formula `{ s p o }`. This is useful for inputs that use `rdf:reifies` or other predicates whose objects are RDF 1.2 triple terms, while keeping the rest of Eyeling on its N3 formula-term model.
2580
+
2564
2581
  For the formal grammar, see the N3 spec grammar:
2565
2582
 
2566
2583
  - [https://w3c.github.io/N3/spec/#grammar](https://w3c.github.io/N3/spec/#grammar)
@@ -3794,3 +3811,84 @@ The becoming examples should therefore be read as **executable schemata** rather
3794
3811
  For the handbook, these examples matter for two reasons. First, they provide a concrete demonstration that Eyeling can handle a style of reasoning that feels closer to **becoming, development, and transformation** than to static classification. Second, they show how expressive gains can come from modeling choices rather than from adding new machinery to the engine. The same forward-chaining core that proves `:Socrates a :Mortal` can also prove that a lineage becomes evolvable, that a controller becomes approved, or that a wake switch becomes serviceable under a low-bias regime.
3795
3812
 
3796
3813
  That is why this appendix belongs after Appendix J. “Formalism is fine” not only because it supports rigor, but because it can remain flexible enough to describe worlds in motion. The becoming examples are small demonstrations of that claim. They show that a compact N3 reasoner can host process-oriented models without ceasing to be simple, readable, and executable.
3814
+
3815
+ ---
3816
+
3817
+ <a id="app-l"></a>
3818
+
3819
+ ## Appendix L — SEE: Specialized Eyeling Executables
3820
+
3821
+ SEE, **Specialized Eyeling Executables**, is the companion example system under `see/`. It compiles selected Eyeling-style N3 programs into small, standalone JavaScript runners. The goal is not to replace the main reasoner. The goal is to turn a particular reasoning task into an executable artifact that carries its input evidence, derivation logic, entailment, explanation, and reference output together.
3822
+
3823
+ The short mental model is:
3824
+
3825
+ ```text
3826
+ N3 source + TriG evidence -> specialized JavaScript -> entailment + explanation
3827
+ ```
3828
+
3829
+ ### L.1 Why SEE exists
3830
+
3831
+ The normal Eyeling CLI is a general reasoner: it parses an input file, runs the engine, and prints the resulting closure, selected query output, or rendered `log:outputString` report.
3832
+
3833
+ SEE is narrower and more packageable. For each committed example, `see.js` generates:
3834
+
3835
+ - `see/examples/<name>.js` — a runnable JavaScript derivation program,
3836
+ - `see/examples/input/<name>.trig` — the RDF/TriG evidence read by the runner,
3837
+ - `see/examples/output/<name>.md` — the expected Markdown result,
3838
+ - `see/examples/doc/<name>.md` — a short generated explanation of the compiled example.
3839
+
3840
+ That makes each example easier to audit. A reviewer can inspect the source N3, the generated runner, the input evidence, and the expected entailment/explanation without reconstructing the whole test setup.
3841
+
3842
+ ### L.2 Entailment plus explanation
3843
+
3844
+ SEE reports use an **Entailment** section and an **Explanation** section. The entailment is the selected result of the reasoning task. The explanation records how the compiled runner reached it: source facts, rules, selected support trees, and the formal TriG output when relevant.
3845
+
3846
+ This vocabulary is deliberately more precise than a generic “result”. It says: the program is not merely presenting a suggestion; it is presenting something that follows from the encoded facts, rules, gates, and query projection.
3847
+
3848
+ ### L.3 Direct TriG input
3849
+
3850
+ Generated SEE runners read their committed `.trig` input directly. Earlier versions used an intermediate `.n3` conversion step, but the current design keeps the evidence in TriG and lets the runner parse that committed input. This matters for examples that are closer to RDF data exchange than to hand-written N3 rule files.
3851
+
3852
+ SEE also includes a triple-term example. Its input can contain RDF 1.2 triple terms such as:
3853
+
3854
+ ```trig
3855
+ VERSION "1.2"
3856
+
3857
+ :observation rdf:reifies <<( :sensor :reports :overheating )>> .
3858
+ ```
3859
+
3860
+ Eyeling accepts this surface form by translating the triple term to a singleton quoted formula internally:
3861
+
3862
+ ```n3
3863
+ :observation rdf:reifies { :sensor :reports :overheating } .
3864
+ ```
3865
+
3866
+ That is enough for the SEE example to demonstrate RDF 1.2-style triple terms as input and output without forcing Eyeling to implement a separate full RDF 1.2 syntax model.
3867
+
3868
+ ### L.4 Generation and tests
3869
+
3870
+ The root package scripts treat SEE as part of the normal repository workflow:
3871
+
3872
+ ```bash
3873
+ npm run generate
3874
+ npm run test:see
3875
+ ```
3876
+
3877
+ `npm run generate` refreshes the generated SEE artifacts from `see/examples/n3/*.n3`. `npm run test:see` runs every generated SEE example and compares its Markdown output with the committed reference output.
3878
+
3879
+ In the full test suite, this means SEE is not just documentation. It is executable regression coverage for a broad range of Eyeling behavior: forward rules, backward rules, builtins, fuses, queries, RDF lists, formula-valued terms, generated explanations, and RDF 1.2 triple-term compatibility.
3880
+
3881
+ ### L.5 When to add a SEE example
3882
+
3883
+ A normal Eyeling example is best when you want to demonstrate a language feature or a compact reasoning pattern directly in N3.
3884
+
3885
+ A SEE example is best when you want a more self-contained artifact:
3886
+
3887
+ - the input evidence matters,
3888
+ - the final entailment should be explained in Markdown,
3889
+ - the example should be runnable without an external reasoner at runtime,
3890
+ - the generated JavaScript itself is worth inspecting,
3891
+ - or the example should become stable regression coverage for a specific reasoning workflow.
3892
+
3893
+ In that sense, SEE is an executable publication format for selected Eyeling examples: small enough to read, deterministic enough to test, and explicit enough to audit.
3894
+
package/README.md CHANGED
@@ -14,7 +14,7 @@ echo '@prefix : <http://example.org/> .
14
14
 
15
15
  ## Read more
16
16
 
17
- - **Handbook:** [eyereasoner.github.io/eyeling/HANDBOOK](https://eyereasoner.github.io/eyeling/HANDBOOK)
18
- - **Playground:** [eyereasoner.github.io/eyeling/demo](https://eyereasoner.github.io/eyeling/demo)
19
- - **SEE:** [Specialized Eyeling Executables](see/README.md) — compiled Notation3 examples as standalone JavaScript runners.
20
- - **Conformance report:** [codeberg.org/phochste/notation3tests/.../report.md](https://codeberg.org/phochste/notation3tests/src/branch/main/reports/report.md)
17
+ - [Handbook](https://eyereasoner.github.io/eyeling/HANDBOOK)
18
+ - [Playground](https://eyereasoner.github.io/eyeling/demo)
19
+ - [SEE: Specialized Eyeling Executables](https://github.com/eyereasoner/eyeling/tree/main/see)
20
+ - [Conformance report](https://codeberg.org/phochste/notation3tests/src/branch/main/reports/report.md)
@@ -9555,7 +9555,110 @@ function stripQuotes(lex) {
9555
9555
  return lex;
9556
9556
  }
9557
9557
 
9558
+
9559
+ // RDF 1.2 triple terms use <<( s p o )>>. Eyeling's N3 engine does not
9560
+ // implement a new RDF 1.2 term kind; instead, it accepts this syntax as a
9561
+ // compatibility surface and normalizes it to the existing N3 singleton quoted
9562
+ // graph term { s p o }. This keeps ordinary N3 reasoning unchanged while making
9563
+ // RDF 1.2 examples parseable by the current engine.
9564
+ function normalizeRdf12TripleTerms(inputText) {
9565
+ const text = String(inputText ?? '');
9566
+ let i = 0;
9567
+
9568
+ function startsAt(needle, at = i) {
9569
+ return text.startsWith(needle, at);
9570
+ }
9571
+
9572
+ function readString() {
9573
+ const quote = text[i];
9574
+ let out = quote;
9575
+ const long = text.startsWith(quote.repeat(3), i);
9576
+ if (long) {
9577
+ out = quote.repeat(3);
9578
+ i += 3;
9579
+ while (i < text.length) {
9580
+ if (text.startsWith(quote.repeat(3), i)) {
9581
+ out += quote.repeat(3);
9582
+ i += 3;
9583
+ return out;
9584
+ }
9585
+ if (text[i] === '\\' && i + 1 < text.length) {
9586
+ out += text.slice(i, i + 2);
9587
+ i += 2;
9588
+ } else {
9589
+ out += text[i++];
9590
+ }
9591
+ }
9592
+ return out;
9593
+ }
9594
+ i += 1;
9595
+ let escaped = false;
9596
+ while (i < text.length) {
9597
+ const ch = text[i++];
9598
+ out += ch;
9599
+ if (escaped) {
9600
+ escaped = false;
9601
+ } else if (ch === '\\') {
9602
+ escaped = true;
9603
+ } else if (ch === quote) {
9604
+ break;
9605
+ }
9606
+ }
9607
+ return out;
9608
+ }
9609
+
9610
+ function readIri() {
9611
+ let out = text[i++];
9612
+ while (i < text.length) {
9613
+ const ch = text[i++];
9614
+ out += ch;
9615
+ if (ch === '>') break;
9616
+ }
9617
+ return out;
9618
+ }
9619
+
9620
+ function convertUntil(stopToken) {
9621
+ let out = '';
9622
+ while (i < text.length) {
9623
+ if (stopToken && startsAt(stopToken)) {
9624
+ i += stopToken.length;
9625
+ return out;
9626
+ }
9627
+ if (startsAt('<<(')) {
9628
+ i += 3;
9629
+ out += '{ ' + convertUntil(')>>').trim() + ' }';
9630
+ continue;
9631
+ }
9632
+ const ch = text[i];
9633
+ if (ch === '"' || ch === "'") {
9634
+ out += readString();
9635
+ continue;
9636
+ }
9637
+ if (ch === '<') {
9638
+ out += readIri();
9639
+ continue;
9640
+ }
9641
+ if (ch === '#') {
9642
+ while (i < text.length) {
9643
+ const c = text[i++];
9644
+ out += c;
9645
+ if (c === '\n' || c === '\r') break;
9646
+ }
9647
+ continue;
9648
+ }
9649
+ out += ch;
9650
+ i += 1;
9651
+ }
9652
+ if (stopToken) throw new N3SyntaxError(`Unterminated RDF 1.2 triple term, expected ${stopToken}`);
9653
+ return out;
9654
+ }
9655
+
9656
+ const converted = convertUntil(null);
9657
+ return converted.replace(/^\s*(?:@version|VERSION)\s+(["'])1\.2\1\s*\.?\s*(?:#.*)?$/gim, '');
9658
+ }
9659
+
9558
9660
  function lex(inputText) {
9661
+ inputText = normalizeRdf12TripleTerms(inputText);
9559
9662
  const chars = Array.from(inputText);
9560
9663
  const n = chars.length;
9561
9664
  let i = 0;
package/eyeling.js CHANGED
@@ -9555,7 +9555,110 @@ function stripQuotes(lex) {
9555
9555
  return lex;
9556
9556
  }
9557
9557
 
9558
+
9559
+ // RDF 1.2 triple terms use <<( s p o )>>. Eyeling's N3 engine does not
9560
+ // implement a new RDF 1.2 term kind; instead, it accepts this syntax as a
9561
+ // compatibility surface and normalizes it to the existing N3 singleton quoted
9562
+ // graph term { s p o }. This keeps ordinary N3 reasoning unchanged while making
9563
+ // RDF 1.2 examples parseable by the current engine.
9564
+ function normalizeRdf12TripleTerms(inputText) {
9565
+ const text = String(inputText ?? '');
9566
+ let i = 0;
9567
+
9568
+ function startsAt(needle, at = i) {
9569
+ return text.startsWith(needle, at);
9570
+ }
9571
+
9572
+ function readString() {
9573
+ const quote = text[i];
9574
+ let out = quote;
9575
+ const long = text.startsWith(quote.repeat(3), i);
9576
+ if (long) {
9577
+ out = quote.repeat(3);
9578
+ i += 3;
9579
+ while (i < text.length) {
9580
+ if (text.startsWith(quote.repeat(3), i)) {
9581
+ out += quote.repeat(3);
9582
+ i += 3;
9583
+ return out;
9584
+ }
9585
+ if (text[i] === '\\' && i + 1 < text.length) {
9586
+ out += text.slice(i, i + 2);
9587
+ i += 2;
9588
+ } else {
9589
+ out += text[i++];
9590
+ }
9591
+ }
9592
+ return out;
9593
+ }
9594
+ i += 1;
9595
+ let escaped = false;
9596
+ while (i < text.length) {
9597
+ const ch = text[i++];
9598
+ out += ch;
9599
+ if (escaped) {
9600
+ escaped = false;
9601
+ } else if (ch === '\\') {
9602
+ escaped = true;
9603
+ } else if (ch === quote) {
9604
+ break;
9605
+ }
9606
+ }
9607
+ return out;
9608
+ }
9609
+
9610
+ function readIri() {
9611
+ let out = text[i++];
9612
+ while (i < text.length) {
9613
+ const ch = text[i++];
9614
+ out += ch;
9615
+ if (ch === '>') break;
9616
+ }
9617
+ return out;
9618
+ }
9619
+
9620
+ function convertUntil(stopToken) {
9621
+ let out = '';
9622
+ while (i < text.length) {
9623
+ if (stopToken && startsAt(stopToken)) {
9624
+ i += stopToken.length;
9625
+ return out;
9626
+ }
9627
+ if (startsAt('<<(')) {
9628
+ i += 3;
9629
+ out += '{ ' + convertUntil(')>>').trim() + ' }';
9630
+ continue;
9631
+ }
9632
+ const ch = text[i];
9633
+ if (ch === '"' || ch === "'") {
9634
+ out += readString();
9635
+ continue;
9636
+ }
9637
+ if (ch === '<') {
9638
+ out += readIri();
9639
+ continue;
9640
+ }
9641
+ if (ch === '#') {
9642
+ while (i < text.length) {
9643
+ const c = text[i++];
9644
+ out += c;
9645
+ if (c === '\n' || c === '\r') break;
9646
+ }
9647
+ continue;
9648
+ }
9649
+ out += ch;
9650
+ i += 1;
9651
+ }
9652
+ if (stopToken) throw new N3SyntaxError(`Unterminated RDF 1.2 triple term, expected ${stopToken}`);
9653
+ return out;
9654
+ }
9655
+
9656
+ const converted = convertUntil(null);
9657
+ return converted.replace(/^\s*(?:@version|VERSION)\s+(["'])1\.2\1\s*\.?\s*(?:#.*)?$/gim, '');
9658
+ }
9659
+
9558
9660
  function lex(inputText) {
9661
+ inputText = normalizeRdf12TripleTerms(inputText);
9559
9662
  const chars = Array.from(inputText);
9560
9663
  const n = chars.length;
9561
9664
  let i = 0;
package/lib/lexer.js CHANGED
@@ -347,7 +347,110 @@ function stripQuotes(lex) {
347
347
  return lex;
348
348
  }
349
349
 
350
+
351
+ // RDF 1.2 triple terms use <<( s p o )>>. Eyeling's N3 engine does not
352
+ // implement a new RDF 1.2 term kind; instead, it accepts this syntax as a
353
+ // compatibility surface and normalizes it to the existing N3 singleton quoted
354
+ // graph term { s p o }. This keeps ordinary N3 reasoning unchanged while making
355
+ // RDF 1.2 examples parseable by the current engine.
356
+ function normalizeRdf12TripleTerms(inputText) {
357
+ const text = String(inputText ?? '');
358
+ let i = 0;
359
+
360
+ function startsAt(needle, at = i) {
361
+ return text.startsWith(needle, at);
362
+ }
363
+
364
+ function readString() {
365
+ const quote = text[i];
366
+ let out = quote;
367
+ const long = text.startsWith(quote.repeat(3), i);
368
+ if (long) {
369
+ out = quote.repeat(3);
370
+ i += 3;
371
+ while (i < text.length) {
372
+ if (text.startsWith(quote.repeat(3), i)) {
373
+ out += quote.repeat(3);
374
+ i += 3;
375
+ return out;
376
+ }
377
+ if (text[i] === '\\' && i + 1 < text.length) {
378
+ out += text.slice(i, i + 2);
379
+ i += 2;
380
+ } else {
381
+ out += text[i++];
382
+ }
383
+ }
384
+ return out;
385
+ }
386
+ i += 1;
387
+ let escaped = false;
388
+ while (i < text.length) {
389
+ const ch = text[i++];
390
+ out += ch;
391
+ if (escaped) {
392
+ escaped = false;
393
+ } else if (ch === '\\') {
394
+ escaped = true;
395
+ } else if (ch === quote) {
396
+ break;
397
+ }
398
+ }
399
+ return out;
400
+ }
401
+
402
+ function readIri() {
403
+ let out = text[i++];
404
+ while (i < text.length) {
405
+ const ch = text[i++];
406
+ out += ch;
407
+ if (ch === '>') break;
408
+ }
409
+ return out;
410
+ }
411
+
412
+ function convertUntil(stopToken) {
413
+ let out = '';
414
+ while (i < text.length) {
415
+ if (stopToken && startsAt(stopToken)) {
416
+ i += stopToken.length;
417
+ return out;
418
+ }
419
+ if (startsAt('<<(')) {
420
+ i += 3;
421
+ out += '{ ' + convertUntil(')>>').trim() + ' }';
422
+ continue;
423
+ }
424
+ const ch = text[i];
425
+ if (ch === '"' || ch === "'") {
426
+ out += readString();
427
+ continue;
428
+ }
429
+ if (ch === '<') {
430
+ out += readIri();
431
+ continue;
432
+ }
433
+ if (ch === '#') {
434
+ while (i < text.length) {
435
+ const c = text[i++];
436
+ out += c;
437
+ if (c === '\n' || c === '\r') break;
438
+ }
439
+ continue;
440
+ }
441
+ out += ch;
442
+ i += 1;
443
+ }
444
+ if (stopToken) throw new N3SyntaxError(`Unterminated RDF 1.2 triple term, expected ${stopToken}`);
445
+ return out;
446
+ }
447
+
448
+ const converted = convertUntil(null);
449
+ return converted.replace(/^\s*(?:@version|VERSION)\s+(["'])1\.2\1\s*\.?\s*(?:#.*)?$/gim, '');
450
+ }
451
+
350
452
  function lex(inputText) {
453
+ inputText = normalizeRdf12TripleTerms(inputText);
351
454
  const chars = Array.from(inputText);
352
455
  const n = chars.length;
353
456
  let i = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.24.0",
3
+ "version": "1.24.2",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
package/see/README.md CHANGED
@@ -5,20 +5,21 @@
5
5
  SEE (**Specialized Eyeling Executables**) is a small compiler and example suite for turning Eyeling-style Notation3 programs into specialized JavaScript executables with human-readable explanations.
6
6
 
7
7
  ```text
8
- input facts -> rules -> trust gate -> insight + explanation
8
+ input facts -> rules -> trust gate -> entailment + explanation
9
9
  ```
10
10
 
11
11
  Each example starts from a Notation3 source and is compiled by `see.js` into a small Node.js program:
12
12
 
13
13
  - `examples/n3/<name>.n3` contains the N3 facts, rules, gates, and query.
14
14
  - `examples/input/<name>.trig` contains the formal RDF 1.2 TriG input evidence read by the runner.
15
- - `examples/<name>.js` is generated by `see.js` and derives the insight.
15
+ - `examples/<name>.js` is generated by `see.js` and derives the entailment.
16
16
  - `examples/output/<name>.md` is the reference Markdown output used by `npm run test:see`.
17
17
  - `examples/doc/<name>.md` explains the compiled example.
18
18
 
19
- The trust gate is executable verification. If a required fact is missing, the program fails instead of emitting an unsupported insight.
19
+ The trust gate is executable verification. If a required fact is missing, the program fails instead of emitting an unsupported entailment.
20
+
21
+ The `triple_terms` example uses RDF 1.2 `<<( ... )>>` triple-term syntax. Eyeling accepts that syntax by normalizing it to the existing N3 singleton graph term `{ ... }`; SEE keeps the committed `.trig` input and formal output in RDF 1.2 triple-term form.
20
22
 
21
- SEE was largely inspired by Prof. Ruben Verborgh's essay [Inside the Insight Economy](https://ruben.verborgh.org/blog/2025/08/12/inside-the-insight-economy/).
22
23
 
23
24
  ## Run
24
25
 
@@ -72,7 +73,7 @@ Validate that the source is inside the supported compiler subset:
72
73
  node see/see.js inspect path/to/example.n3
73
74
  ```
74
75
 
75
- The compiler currently supports ordinary triples, semicolon/comma property lists, RDF lists, blank nodes, variable predicates, formula-valued conclusions, forward rules, recursive backward rules (`<=`), top-level `log:query`, `=> false` fuses, and a broad native builtin registry for Eyeling-style `math:`, `string:`, `list:`, `crypto:`, `time:`, and `log:` predicates. The compiled JavaScript covers multi-solution predicates such as `list:member`, `list:in`, `list:iterate`, `list:memberAt`, and `list:map`, plus relation-like reverse modes such as `math:exponentiation`, unary trig/degree relations, `list:reverse`, and `list:append` splitting. Offline dereferencing/parsing builtins (`log:content`, `log:semantics`, `log:semanticsOrError`, `log:parsedAsN3`) fail explicitly in compiled SEE examples instead of doing network or parser work at runtime. Unsupported constructs fail rather than jumping directly to a prewritten `log:outputString`. When no authored `log:outputString` report is derived, the compiled runner still emits a SEE-style report with just **Insight** and **Explanation** sections instead of dumping the whole graph; supporting facts are folded into the explanation, including concise support trees that trace selected conclusions back to source facts when the compiler has rule-premise evidence available.
76
+ The compiler currently supports ordinary triples, semicolon/comma property lists, RDF lists, blank nodes, variable predicates, formula-valued conclusions, forward rules, recursive backward rules (`<=`), top-level `log:query`, `=> false` fuses, and a broad native builtin registry for Eyeling-style `math:`, `string:`, `list:`, `crypto:`, `time:`, and `log:` predicates. The compiled JavaScript covers multi-solution predicates such as `list:member`, `list:in`, `list:iterate`, `list:memberAt`, and `list:map`, plus relation-like reverse modes such as `math:exponentiation`, unary trig/degree relations, `list:reverse`, and `list:append` splitting. Offline dereferencing/parsing builtins (`log:content`, `log:semantics`, `log:semanticsOrError`, `log:parsedAsN3`) fail explicitly in compiled SEE examples instead of doing network or parser work at runtime. Unsupported constructs fail rather than jumping directly to a prewritten `log:outputString`. When no authored `log:outputString` report is derived, the compiled runner still emits a SEE-style report with just **Entailment** and **Explanation** sections instead of dumping the whole graph; supporting facts are folded into the explanation, including concise support trees that trace selected entailments back to source facts when the compiler has rule-premise evidence available.
76
77
 
77
78
  ## Files
78
79
 
@@ -81,7 +82,7 @@ examples/
81
82
  <name>.js executable example
82
83
  _see.js shared helper
83
84
  input/<name>.trig formal RDF 1.2 TriG evidence
84
- output/<name>.md reference insight and explanation
85
+ output/<name>.md reference entailment and explanation
85
86
  doc/<name>.md guide
86
87
  n3/<name>.n3 Notation3 source
87
88
  ```
@@ -103,7 +104,7 @@ The repository currently contains **50 N3-compiled SEE examples**. Each example
103
104
  | <a id="example-bmi"></a>[BMI](#example-bmi) | compiled from Notation3 by `see.js`; unit normalization, BMI category selection, and healthy-weight range validations are derived from N3 rules. | [doc](examples/doc/bmi.md), [js](examples/bmi.js), [input](examples/input/bmi.trig), [output](examples/output/bmi.md), [n3](examples/n3/bmi.n3) |
104
105
  | <a id="example-builtin-coverage-smoke"></a>[Builtin coverage smoke](#example-builtin-coverage-smoke) | compiled from Notation3 by `see.js`; smoke test for compiled math, string, list, crypto, time, and log builtins. | [doc](examples/doc/builtin_coverage.md), [js](examples/builtin_coverage.js), [input](examples/input/builtin_coverage.trig), [output](examples/output/builtin_coverage.md), [n3](examples/n3/builtin_coverage.n3) |
105
106
  | <a id="example-collection"></a>[Collection](#example-collection) | compiled from Notation3 by `see.js`; RDF collection/list data with a blank node inside the list. | [doc](examples/doc/collection.md), [js](examples/collection.js), [input](examples/input/collection.trig), [output](examples/output/collection.md), [n3](examples/n3/collection.n3) |
106
- | <a id="example-complex-numbers"></a>[Complex numbers](#example-complex-numbers) | compiled from Notation3 by `see.js`; complex arithmetic rules and formula-valued conclusion. | [doc](examples/doc/complex.md), [js](examples/complex.js), [input](examples/input/complex.trig), [output](examples/output/complex.md), [n3](examples/n3/complex.n3) |
107
+ | <a id="example-complex-numbers"></a>[Complex numbers](#example-complex-numbers) | compiled from Notation3 by `see.js`; complex arithmetic rules and formula-valued entailment. | [doc](examples/doc/complex.md), [js](examples/complex.js), [input](examples/input/complex.trig), [output](examples/output/complex.md), [n3](examples/n3/complex.n3) |
107
108
  | <a id="example-complex-matrix-stability"></a>[Complex matrix stability](#example-complex-matrix-stability) | compiled from Notation3 by `see.js`; diagonal 2x2 complex matrices are classified from squared eigenvalue moduli, with complex product and scaling validations. | [doc](examples/doc/complex_matrix_stability.md), [js](examples/complex_matrix_stability.js), [input](examples/input/complex_matrix_stability.trig), [output](examples/output/complex_matrix_stability.md), [n3](examples/n3/complex_matrix_stability.n3) |
108
109
  | <a id="example-composition-of-injective-functions"></a>[Composition of injective functions](#example-composition-of-injective-functions) | compiled from Notation3 by `see.js`; proof-style rules show that the composition of injective functions is injective. | [doc](examples/doc/composition_of_injective_functions_is_injective.md), [js](examples/composition_of_injective_functions_is_injective.js), [input](examples/input/composition_of_injective_functions_is_injective.trig), [output](examples/output/composition_of_injective_functions_is_injective.md), [n3](examples/n3/composition_of_injective_functions_is_injective.n3) |
109
110
  | <a id="example-control-system"></a>[Control system](#example-control-system) | compiled from Notation3 by `see.js`; compact EYE-style sensor, disturbance, and target facts derive two actuator commands. | [doc](examples/doc/control_system.md), [js](examples/control_system.js), [input](examples/input/control_system.trig), [output](examples/output/control_system.md), [n3](examples/n3/control_system.n3) |
@@ -111,8 +112,8 @@ The repository currently contains **50 N3-compiled SEE examples**. Each example
111
112
  | <a id="example-dijkstra-shortest-path"></a>[Dijkstra shortest path](#example-dijkstra-shortest-path) | compiled from Notation3 by `see.js`; list, collection, sorting, and recursive backward rules derive the shortest path from `:a` to `:f`. | [doc](examples/doc/dijkstra.md), [js](examples/dijkstra.js), [input](examples/input/dijkstra.trig), [output](examples/output/dijkstra.md), [n3](examples/n3/dijkstra.n3) |
112
113
  | <a id="example-dijkstra-risk-path"></a>[Dijkstra risk path](#example-dijkstra-risk-path) | compiled from Notation3 by `see.js`; risk-adjusted route selection derives the best path from edge costs, route risk, and threshold rules. | [doc](examples/doc/dijkstra_risk_path.md), [js](examples/dijkstra_risk_path.js), [input](examples/input/dijkstra_risk_path.trig), [output](examples/output/dijkstra_risk_path.md), [n3](examples/n3/dijkstra_risk_path.n3) |
113
114
  | <a id="example-dog-license-example"></a>[Dog license example](#example-dog-license-example) | compiled from Notation3 by `see.js`; `log:collectAllIn` and `math:sum` derive that a multi-dog household needs a license. | [doc](examples/doc/dog.md), [js](examples/dog.js), [input](examples/input/dog.trig), [output](examples/output/dog.md), [n3](examples/n3/dog.n3) |
114
- | <a id="example-eco-route-insight"></a>[Eco route insight](#example-eco-route-insight) | compiled from Notation3 by `see.js`; privacy-preserving route evidence derives an eco-route recommendation with signed-envelope explanation data. | [doc](examples/doc/eco_route_insight.md), [js](examples/eco_route_insight.js), [input](examples/input/eco_route_insight.trig), [output](examples/output/eco_route_insight.md), [n3](examples/n3/eco_route_insight.n3) |
115
- | <a id="example-delfour"></a>[Delfour](#example-delfour) | compiled from Notation3 by `see.js`; scoped insight-policy facts, cryptographic hash validation, and product rules authorize a lower-sugar scanner recommendation. | [doc](examples/doc/delfour.md), [js](examples/delfour.js), [input](examples/input/delfour.trig), [output](examples/output/delfour.md), [n3](examples/n3/delfour.n3) |
115
+ | <a id="example-eco-route-insight"></a>[Eco route entailment](#example-eco-route-insight) | compiled from Notation3 by `see.js`; privacy-preserving route evidence derives an eco-route recommendation with signed-envelope explanation data. | [doc](examples/doc/eco_route_insight.md), [js](examples/eco_route_insight.js), [input](examples/input/eco_route_insight.trig), [output](examples/output/eco_route_insight.md), [n3](examples/n3/eco_route_insight.n3) |
116
+ | <a id="example-delfour"></a>[Delfour](#example-delfour) | compiled from Notation3 by `see.js`; scoped policy facts, cryptographic hash validation, and product rules authorize a lower-sugar scanner recommendation. | [doc](examples/doc/delfour.md), [js](examples/delfour.js), [input](examples/input/delfour.trig), [output](examples/output/delfour.md), [n3](examples/n3/delfour.n3) |
116
117
  | <a id="example-digital-product-passport"></a>[Digital product passport](#example-digital-product-passport) | compiled from Notation3 by `see.js`; component, document, access-policy, and footprint facts fold into a public passport PASS decision. | [doc](examples/doc/digital_product_passport.md), [js](examples/digital_product_passport.js), [input](examples/input/digital_product_passport.trig), [output](examples/output/digital_product_passport.md), [n3](examples/n3/digital_product_passport.n3) |
117
118
  | <a id="example-equals-test"></a>[Equals test](#example-equals-test) | compiled from Notation3 by `see.js`; N3 `=` / `owl:sameAs` shorthand compiled into a normal derivation. | [doc](examples/doc/equals.md), [js](examples/equals.js), [input](examples/input/equals.trig), [output](examples/output/equals.md), [n3](examples/n3/equals.n3) |
118
119
  | <a id="example-equivalence-class-overlap"></a>[Equivalence class overlap](#example-equivalence-class-overlap) | compiled from Notation3 by `see.js`; equivalence-relation closure proves overlapping equivalence classes are the same class. | [doc](examples/doc/equivalence_classes_overlap_implies_same_class.md), [js](examples/equivalence_classes_overlap_implies_same_class.js), [input](examples/input/equivalence_classes_overlap_implies_same_class.trig), [output](examples/output/equivalence_classes_overlap_implies_same_class.md), [n3](examples/n3/equivalence_classes_overlap_implies_same_class.n3) |
@@ -127,7 +128,7 @@ The repository currently contains **50 N3-compiled SEE examples**. Each example
127
128
  | <a id="example-fundamental-theorem-arithmetic"></a>[Fundamental theorem arithmetic](#example-fundamental-theorem-arithmetic) | compiled from Notation3 by `see.js`; primary factorization is validated by product reconstruction, reverse-order sorting, and prime witnesses, with sample summaries retained as data. | [doc](examples/doc/fundamental_theorem_arithmetic.md), [js](examples/fundamental_theorem_arithmetic.js), [input](examples/input/fundamental_theorem_arithmetic.trig), [output](examples/output/fundamental_theorem_arithmetic.md), [n3](examples/n3/fundamental_theorem_arithmetic.n3) |
128
129
  | <a id="example-genetic-knapsack-selection"></a>[Genetic knapsack selection](#example-genetic-knapsack-selection) | compiled from Notation3 by `see.js`; deterministic one-bit mutation candidates are scored against knapsack capacity and value constraints. | [doc](examples/doc/genetic_knapsack_selection.md), [js](examples/genetic_knapsack_selection.js), [input](examples/input/genetic_knapsack_selection.trig), [output](examples/output/genetic_knapsack_selection.md), [n3](examples/n3/genetic_knapsack_selection.n3) |
129
130
  | <a id="example-goldbach-1000"></a>[Goldbach 1000](#example-goldbach-1000) | compiled from Notation3 by `see.js`; bounded Goldbach run data is validated through representative prime-sum witnesses and a zero-counterexample condition. | [doc](examples/doc/goldbach_1000.md), [js](examples/goldbach_1000.js), [input](examples/input/goldbach_1000.trig), [output](examples/output/goldbach_1000.md), [n3](examples/n3/goldbach_1000.n3) |
130
- | <a id="example-good-cobbler"></a>[Good cobbler](#example-good-cobbler) | compiled from Notation3 by `see.js`; term-logic-style list pattern and formula-valued conclusion. | [doc](examples/doc/good_cobbler.md), [js](examples/good_cobbler.js), [input](examples/input/good_cobbler.trig), [output](examples/output/good_cobbler.md), [n3](examples/n3/good_cobbler.n3) |
131
+ | <a id="example-good-cobbler"></a>[Good cobbler](#example-good-cobbler) | compiled from Notation3 by `see.js`; term-logic-style list pattern and formula-valued entailment. | [doc](examples/doc/good_cobbler.md), [js](examples/good_cobbler.js), [input](examples/input/good_cobbler.trig), [output](examples/output/good_cobbler.md), [n3](examples/n3/good_cobbler.n3) |
131
132
  | <a id="example-gps-route-planning"></a>[GPS route planning](#example-gps-route-planning) | compiled from Notation3 by `see.js`; recursive path rules compare western-Belgium routes by duration, cost, belief, and comfort. | [doc](examples/doc/gps.md), [js](examples/gps.js), [input](examples/input/gps.trig), [output](examples/output/gps.md), [n3](examples/n3/gps.n3) |
132
133
  | <a id="example-gray-code-counter"></a>[Gray code counter](#example-gray-code-counter) | compiled from Notation3 by `see.js`; a 4-bit reflected Gray-code cycle is validated as a complete one-bit-transition sequence. | [doc](examples/doc/gray_code_counter.md), [js](examples/gray_code_counter.js), [input](examples/input/gray_code_counter.trig), [output](examples/output/gray_code_counter.md), [n3](examples/n3/gray_code_counter.n3) |
133
134
  | <a id="example-greatest-lower-bound-uniqueness"></a>[Greatest lower bound uniqueness](#example-greatest-lower-bound-uniqueness) | compiled from Notation3 by `see.js`; partial-order rules prove two greatest lower bounds of the same pair coincide. | [doc](examples/doc/greatest_lower_bound_uniqueness.md), [js](examples/greatest_lower_bound_uniqueness.js), [input](examples/input/greatest_lower_bound_uniqueness.trig), [output](examples/output/greatest_lower_bound_uniqueness.md), [n3](examples/n3/greatest_lower_bound_uniqueness.n3) |
@@ -142,5 +143,6 @@ The repository currently contains **50 N3-compiled SEE examples**. Each example
142
143
  | <a id="example-school-placement-route-audit"></a>[School placement route audit](#example-school-placement-route-audit) | compiled from Notation3 by `see.js`; student, school, distance, and policy facts derive an auditable school-placement route decision. | [doc](examples/doc/school_placement_audit.md), [js](examples/school_placement_audit.js), [input](examples/input/school_placement_audit.trig), [output](examples/output/school_placement_audit.md), [n3](examples/n3/school_placement_audit.n3) |
143
144
  | <a id="example-smoke-arithmetic"></a>[Smoke Arithmetic](#example-smoke-arithmetic) | compiled from Notation3 by `see.js`; arithmetic compiler smoke test deriving 6 × 7 through rules and builtins. | [doc](examples/doc/smoke_arithmetic.md), [js](examples/smoke_arithmetic.js), [input](examples/input/smoke_arithmetic.trig), [output](examples/output/smoke_arithmetic.md), [n3](examples/n3/smoke_arithmetic.n3) |
144
145
  | <a id="example-socrates-inference"></a>[Socrates inference](#example-socrates-inference) | compiled from Notation3 by `see.js`; subclass closure with top-level `log:query` projection. | [doc](examples/doc/socrates.md), [js](examples/socrates.js), [input](examples/input/socrates.trig), [output](examples/output/socrates.md), [n3](examples/n3/socrates.n3) |
146
+ | <a id="example-triple-terms"></a>[Triple terms](#example-triple-terms) | compiled from Notation3 by `see.js`; RDF 1.2 TriG triple terms are used as input evidence and as a derived entailment. | [doc](examples/doc/triple_terms.md), [js](examples/triple_terms.js), [input](examples/input/triple_terms.trig), [output](examples/output/triple_terms.md), [n3](examples/n3/triple_terms.n3) |
145
147
  | <a id="example-wind-turbine-envelope"></a>[Wind turbine envelope](#example-wind-turbine-envelope) | compiled from Notation3 by `see.js`; wind-speed samples are classified against cut-in, rated, and cut-out thresholds and accumulated into energy. | [doc](examples/doc/wind_turbine.md), [js](examples/wind_turbine.js), [input](examples/input/wind_turbine.trig), [output](examples/output/wind_turbine.md), [n3](examples/n3/wind_turbine.n3) |
146
- | <a id="example-burn-the-witch"></a>[Burn the witch](#example-burn-the-witch) | compiled from Notation3 by `see.js`; rule-chain explanation for the witch conclusion. | [doc](examples/doc/witch.md), [js](examples/witch.js), [input](examples/input/witch.trig), [output](examples/output/witch.md), [n3](examples/n3/witch.n3) |
148
+ | <a id="example-burn-the-witch"></a>[Burn the witch](#example-burn-the-witch) | compiled from Notation3 by `see.js`; rule-chain explanation for the witch entailment. | [doc](examples/doc/witch.md), [js](examples/witch.js), [input](examples/input/witch.trig), [output](examples/output/witch.md), [n3](examples/n3/witch.n3) |
@@ -7,11 +7,31 @@ function lit(value) { return { kind: 'lit', value }; }
7
7
  function blank(value) { return { kind: 'blank', value }; }
8
8
  function list(items) { return { kind: 'list', items }; }
9
9
  function formula(atoms) { return { kind: 'formula', atoms }; }
10
+ function triple(s, p, o) { return { kind: 'triple', s, p, o }; }
10
11
 
11
12
  function readTermToken(text, start = 0) {
12
13
  let i = start;
13
14
  while (/\s/.test(text[i])) i += 1;
14
15
  const begin = i;
16
+ if (text.startsWith('<<(', i)) {
17
+ let depth = 0;
18
+ while (i < text.length) {
19
+ if (text[i] === '"') {
20
+ const [, next] = readTermToken(text, i);
21
+ i = next;
22
+ continue;
23
+ }
24
+ if (text.startsWith('<<(', i)) { depth += 1; i += 3; continue; }
25
+ if (text.startsWith(')>>', i)) {
26
+ depth -= 1;
27
+ i += 3;
28
+ if (depth === 0) break;
29
+ continue;
30
+ }
31
+ i += 1;
32
+ }
33
+ return [text.slice(begin, i), i];
34
+ }
15
35
  if (text[i] === '"') {
16
36
  i += 1;
17
37
  let escaped = false;
@@ -60,7 +80,10 @@ function splitListItems(text) {
60
80
  while (/\s/.test(text[i])) i += 1;
61
81
  if (i >= text.length) break;
62
82
  const start = i;
63
- if (text[i] === '"') {
83
+ if (text.startsWith('<<(', i)) {
84
+ const [, next] = readTermToken(text, i);
85
+ i = next;
86
+ } else if (text[i] === '"') {
64
87
  i += 1;
65
88
  let escaped = false;
66
89
  while (i < text.length) {
@@ -83,9 +106,17 @@ function splitListItems(text) {
83
106
  }
84
107
  return out;
85
108
  }
109
+ function parseTripleTermBody(text) {
110
+ const [s, i1] = readTermToken(text, 0);
111
+ const [p, i2] = readTermToken(text, i1);
112
+ const [o, i3] = readTermToken(text, i2);
113
+ if (!s || !p || !o || text.slice(i3).trim()) throw new Error('bad triple term: ' + text);
114
+ return triple(parseTerm(s), parseTerm(p), parseTerm(o));
115
+ }
86
116
  function parseTerm(text) {
87
117
  const t = String(text || '').trim();
88
118
  if (!t) throw new Error('empty term');
119
+ if (t.startsWith('<<(') && t.endsWith(')>>')) return parseTripleTermBody(t.slice(3, -3).trim());
89
120
  const first = t[0];
90
121
  if (first === '"') return lit(JSON.parse(t));
91
122
  if (first === '(' && t[t.length - 1] === ')') return list(splitListItems(t.slice(1, -1)).map(parseTerm));
@@ -112,7 +143,7 @@ function parseInputTrigFast(trig) {
112
143
  const lines = String(trig || '').split(/\r?\n/);
113
144
  for (let i = 0; i < lines.length; i += 1) {
114
145
  const line = lines[i].trim();
115
- if (!line || line.startsWith('#') || line.toLowerCase().startsWith('@prefix ')) continue;
146
+ if (!line || line.startsWith('#') || line.toLowerCase().startsWith('@prefix ') || /^(@version|version)\s+/i.test(line)) continue;
116
147
  const graphStart = line.match(/^(\S+)\s*\{\s*$/);
117
148
  if (graphStart) {
118
149
  const atoms = [];