eyeling 1.24.4 → 1.24.6

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 (301) hide show
  1. package/HANDBOOK.md +2 -101
  2. package/README.md +2 -2
  3. package/dist/browser/eyeling.browser.js +256 -4
  4. package/examples/annotation.n3 +3 -4
  5. package/{see/examples/n3/backward_recursion.n3 → examples/backward-recursion.n3} +0 -2
  6. package/examples/collection.n3 +1 -2
  7. package/examples/context-association.n3 +56 -30
  8. package/{see/examples/n3/dijkstra_risk_path.n3 → examples/dijkstra-risk-path.n3} +3 -18
  9. package/{see/examples/n3/eco_route_insight.n3 → examples/eco-route-insight.n3} +2 -47
  10. package/{see/examples/n3/genetic_knapsack_selection.n3 → examples/genetic-knapsack-selection.n3} +2 -24
  11. package/{see/examples → examples}/input/annotation.trig +3 -3
  12. package/{see/examples/input/backward_recursion.trig → examples/input/backward-recursion.trig} +2 -2
  13. package/{see/examples/input/builtin_coverage.trig → examples/input/builtin-coverage.trig} +2 -2
  14. package/{see/examples → examples}/input/collection.trig +3 -3
  15. package/{see/examples/input/dijkstra_risk_path.trig → examples/input/dijkstra-risk-path.trig} +4 -4
  16. package/{see/examples/input/eco_route_insight.trig → examples/input/eco-route-insight.trig} +4 -4
  17. package/{see/examples/input/genetic_knapsack_selection.trig → examples/input/genetic-knapsack-selection.trig} +4 -4
  18. package/{see/examples/input/rc_discharge_envelope.trig → examples/input/rc-discharge-envelope.trig} +4 -4
  19. package/{see/examples/input/rdf_dataset.trig → examples/input/rdf-dataset.trig} +4 -4
  20. package/{see/examples/input/rdf_message_flow.trig → examples/input/rdf-message-flow.trig} +3 -3
  21. package/{see/examples/input/rdf_messages.trig → examples/input/rdf-messages.trig} +4 -4
  22. package/{see/examples/input/school_placement_audit.trig → examples/input/school-placement-audit.trig} +4 -4
  23. package/{see/examples/input/smoke_arithmetic.trig → examples/input/smoke-arithmetic.trig} +3 -3
  24. package/{see/examples/input/triple_terms.trig → examples/input/triple-terms.trig} +3 -3
  25. package/examples/output/backward-recursion.n3 +4 -0
  26. package/examples/output/context-association.n3 +9 -0
  27. package/examples/output/dijkstra-risk-path.n3 +3 -0
  28. package/examples/output/eco-route-insight.n3 +3 -0
  29. package/examples/output/genetic-knapsack-selection.n3 +3 -0
  30. package/examples/output/rc-discharge-envelope.n3 +9 -0
  31. package/examples/output/rc-discharge-envelope.txt +9 -0
  32. package/examples/output/rdf-dataset.n3 +5 -0
  33. package/examples/output/rdf-message-flow.n3 +7 -0
  34. package/examples/output/rdf-messages.n3 +7 -0
  35. package/examples/output/school-placement-audit.n3 +3 -0
  36. package/examples/output/smoke-arithmetic.n3 +5 -0
  37. package/examples/output/smoke-arithmetic.txt +5 -0
  38. package/examples/output/triple-terms.n3 +5 -0
  39. package/{see/examples/n3/rc_discharge_envelope.n3 → examples/rc-discharge-envelope.n3} +2 -15
  40. package/{see/examples/n3/rdf_dataset.n3 → examples/rdf-dataset.n3} +2 -11
  41. package/{see/examples/n3/rdf_message_flow.n3 → examples/rdf-message-flow.n3} +9 -75
  42. package/{see/examples/n3/rdf_messages.n3 → examples/rdf-messages.n3} +6 -43
  43. package/{see/examples/n3/school_placement_audit.n3 → examples/school-placement-audit.n3} +2 -14
  44. package/{see/examples/n3/smoke_arithmetic.n3 → examples/smoke-arithmetic.n3} +3 -5
  45. package/{see/examples/n3/triple_terms.n3 → examples/triple-terms.n3} +1 -4
  46. package/eyeling.js +256 -4
  47. package/lib/builtins.js +6 -0
  48. package/lib/lexer.js +250 -4
  49. package/package.json +4 -7
  50. package/test/api.test.js +65 -8
  51. package/test/examples.test.js +22 -2
  52. package/test/package.test.js +16 -2
  53. package/examples/arcling/README.md +0 -11
  54. package/examples/input/annotation.ttl +0 -6
  55. package/examples/input/collection.ttl +0 -13
  56. package/examples/input/reifies.ttl +0 -10
  57. package/examples/input/triple-term.ttl +0 -8
  58. package/examples/output/triple-term.n3 +0 -0
  59. package/examples/reifies.n3 +0 -8
  60. package/examples/triple-term.n3 +0 -7
  61. package/see/README.md +0 -149
  62. package/see/examples/_see.js +0 -249
  63. package/see/examples/age.js +0 -1459
  64. package/see/examples/annotation.js +0 -1320
  65. package/see/examples/backward.js +0 -1405
  66. package/see/examples/backward_recursion.js +0 -1504
  67. package/see/examples/bayes_diagnosis.js +0 -2883
  68. package/see/examples/bayes_therapy.js +0 -4152
  69. package/see/examples/bmi.js +0 -3038
  70. package/see/examples/builtin_coverage.js +0 -2524
  71. package/see/examples/collection.js +0 -1320
  72. package/see/examples/complex.js +0 -3762
  73. package/see/examples/complex_matrix_stability.js +0 -2973
  74. package/see/examples/composition_of_injective_functions_is_injective.js +0 -2170
  75. package/see/examples/control_system.js +0 -1918
  76. package/see/examples/crypto_builtins_tests.js +0 -1489
  77. package/see/examples/delfour.js +0 -3174
  78. package/see/examples/digital_product_passport.js +0 -2856
  79. package/see/examples/dijkstra.js +0 -2070
  80. package/see/examples/dijkstra_risk_path.js +0 -1874
  81. package/see/examples/doc/age.md +0 -27
  82. package/see/examples/doc/annotation.md +0 -24
  83. package/see/examples/doc/backward.md +0 -26
  84. package/see/examples/doc/backward_recursion.md +0 -26
  85. package/see/examples/doc/bayes_diagnosis.md +0 -41
  86. package/see/examples/doc/bayes_therapy.md +0 -40
  87. package/see/examples/doc/bmi.md +0 -38
  88. package/see/examples/doc/builtin_coverage.md +0 -53
  89. package/see/examples/doc/collection.md +0 -24
  90. package/see/examples/doc/complex.md +0 -38
  91. package/see/examples/doc/complex_matrix_stability.md +0 -35
  92. package/see/examples/doc/composition_of_injective_functions_is_injective.md +0 -24
  93. package/see/examples/doc/control_system.md +0 -32
  94. package/see/examples/doc/crypto_builtins_tests.md +0 -27
  95. package/see/examples/doc/delfour.md +0 -37
  96. package/see/examples/doc/digital_product_passport.md +0 -36
  97. package/see/examples/doc/dijkstra.md +0 -28
  98. package/see/examples/doc/dijkstra_risk_path.md +0 -30
  99. package/see/examples/doc/dog.md +0 -28
  100. package/see/examples/doc/eco_route_insight.md +0 -33
  101. package/see/examples/doc/equals.md +0 -26
  102. package/see/examples/doc/equivalence_classes_overlap_implies_same_class.md +0 -24
  103. package/see/examples/doc/euler_identity.md +0 -39
  104. package/see/examples/doc/ev_roundtrip_planner.md +0 -32
  105. package/see/examples/doc/existential_rule.md +0 -24
  106. package/see/examples/doc/expression_eval.md +0 -26
  107. package/see/examples/doc/family_cousins.md +0 -24
  108. package/see/examples/doc/fastpow.md +0 -29
  109. package/see/examples/doc/fibonacci.md +0 -28
  110. package/see/examples/doc/french_cities.md +0 -28
  111. package/see/examples/doc/fundamental_theorem_arithmetic.md +0 -36
  112. package/see/examples/doc/genetic_knapsack_selection.md +0 -29
  113. package/see/examples/doc/goldbach_1000.md +0 -31
  114. package/see/examples/doc/good_cobbler.md +0 -27
  115. package/see/examples/doc/gps.md +0 -35
  116. package/see/examples/doc/gray_code_counter.md +0 -31
  117. package/see/examples/doc/greatest_lower_bound_uniqueness.md +0 -24
  118. package/see/examples/doc/group_inverse_uniqueness.md +0 -24
  119. package/see/examples/doc/hadamard_approx.md +0 -32
  120. package/see/examples/doc/hanoi.md +0 -26
  121. package/see/examples/doc/odrl_dpv_risk_ranked.md +0 -57
  122. package/see/examples/doc/path_discovery.md +0 -33
  123. package/see/examples/doc/rc_discharge_envelope.md +0 -33
  124. package/see/examples/doc/rdf_dataset.md +0 -26
  125. package/see/examples/doc/rdf_message_flow.md +0 -35
  126. package/see/examples/doc/rdf_messages.md +0 -37
  127. package/see/examples/doc/school_placement_audit.md +0 -31
  128. package/see/examples/doc/smoke_arithmetic.md +0 -31
  129. package/see/examples/doc/socrates.md +0 -24
  130. package/see/examples/doc/triple_terms.md +0 -26
  131. package/see/examples/doc/wind_turbine.md +0 -37
  132. package/see/examples/doc/witch.md +0 -28
  133. package/see/examples/dog.js +0 -1436
  134. package/see/examples/eco_route_insight.js +0 -2110
  135. package/see/examples/equals.js +0 -1363
  136. package/see/examples/equivalence_classes_overlap_implies_same_class.js +0 -1792
  137. package/see/examples/euler_identity.js +0 -2038
  138. package/see/examples/ev_roundtrip_planner.js +0 -2562
  139. package/see/examples/existential_rule.js +0 -1363
  140. package/see/examples/expression_eval.js +0 -1798
  141. package/see/examples/family_cousins.js +0 -1586
  142. package/see/examples/fastpow.js +0 -2207
  143. package/see/examples/fibonacci.js +0 -1594
  144. package/see/examples/french_cities.js +0 -1492
  145. package/see/examples/fundamental_theorem_arithmetic.js +0 -2106
  146. package/see/examples/genetic_knapsack_selection.js +0 -1743
  147. package/see/examples/goldbach_1000.js +0 -1798
  148. package/see/examples/good_cobbler.js +0 -1396
  149. package/see/examples/gps.js +0 -2813
  150. package/see/examples/gray_code_counter.js +0 -1641
  151. package/see/examples/greatest_lower_bound_uniqueness.js +0 -1918
  152. package/see/examples/group_inverse_uniqueness.js +0 -1897
  153. package/see/examples/hadamard_approx.js +0 -4417
  154. package/see/examples/hanoi.js +0 -1625
  155. package/see/examples/input/age.trig +0 -27
  156. package/see/examples/input/backward.trig +0 -25
  157. package/see/examples/input/bayes_diagnosis.trig +0 -111
  158. package/see/examples/input/bayes_therapy.trig +0 -130
  159. package/see/examples/input/bmi.trig +0 -28
  160. package/see/examples/input/complex.trig +0 -26
  161. package/see/examples/input/complex_matrix_stability.trig +0 -65
  162. package/see/examples/input/composition_of_injective_functions_is_injective.trig +0 -35
  163. package/see/examples/input/control_system.trig +0 -31
  164. package/see/examples/input/crypto_builtins_tests.trig +0 -25
  165. package/see/examples/input/delfour.trig +0 -90
  166. package/see/examples/input/digital_product_passport.trig +0 -116
  167. package/see/examples/input/dijkstra.trig +0 -34
  168. package/see/examples/input/dog.trig +0 -31
  169. package/see/examples/input/equals.trig +0 -25
  170. package/see/examples/input/equivalence_classes_overlap_implies_same_class.trig +0 -28
  171. package/see/examples/input/euler_identity.trig +0 -34
  172. package/see/examples/input/ev_roundtrip_planner.trig +0 -90
  173. package/see/examples/input/existential_rule.trig +0 -26
  174. package/see/examples/input/expression_eval.trig +0 -41
  175. package/see/examples/input/family_cousins.trig +0 -39
  176. package/see/examples/input/fastpow.trig +0 -25
  177. package/see/examples/input/fibonacci.trig +0 -51
  178. package/see/examples/input/french_cities.trig +0 -38
  179. package/see/examples/input/fundamental_theorem_arithmetic.trig +0 -42
  180. package/see/examples/input/goldbach_1000.trig +0 -53
  181. package/see/examples/input/good_cobbler.trig +0 -24
  182. package/see/examples/input/gps.trig +0 -35
  183. package/see/examples/input/gray_code_counter.trig +0 -33
  184. package/see/examples/input/greatest_lower_bound_uniqueness.trig +0 -29
  185. package/see/examples/input/group_inverse_uniqueness.trig +0 -29
  186. package/see/examples/input/hadamard_approx.trig +0 -32
  187. package/see/examples/input/hanoi.trig +0 -26
  188. package/see/examples/input/odrl_dpv_risk_ranked.trig +0 -107
  189. package/see/examples/input/path-discovery.trig +0 -96448
  190. package/see/examples/input/path_discovery.trig +0 -29
  191. package/see/examples/input/socrates.trig +0 -26
  192. package/see/examples/input/wind_turbine.trig +0 -48
  193. package/see/examples/input/witch.trig +0 -26
  194. package/see/examples/n3/age.n3 +0 -28
  195. package/see/examples/n3/annotation.n3 +0 -7
  196. package/see/examples/n3/backward.n3 +0 -22
  197. package/see/examples/n3/bayes_diagnosis.n3 +0 -122
  198. package/see/examples/n3/bayes_therapy.n3 +0 -149
  199. package/see/examples/n3/bmi.n3 +0 -145
  200. package/see/examples/n3/collection.n3 +0 -3
  201. package/see/examples/n3/complex.n3 +0 -140
  202. package/see/examples/n3/complex_matrix_stability.n3 +0 -113
  203. package/see/examples/n3/composition_of_injective_functions_is_injective.n3 +0 -27
  204. package/see/examples/n3/control_system.n3 +0 -59
  205. package/see/examples/n3/crypto_builtins_tests.n3 +0 -18
  206. package/see/examples/n3/delfour.n3 +0 -167
  207. package/see/examples/n3/digital_product_passport.n3 +0 -156
  208. package/see/examples/n3/dijkstra.n3 +0 -46
  209. package/see/examples/n3/dog.n3 +0 -20
  210. package/see/examples/n3/equals.n3 +0 -11
  211. package/see/examples/n3/equivalence_classes_overlap_implies_same_class.n3 +0 -19
  212. package/see/examples/n3/euler_identity.n3 +0 -41
  213. package/see/examples/n3/ev_roundtrip_planner.n3 +0 -82
  214. package/see/examples/n3/existential_rule.n3 +0 -10
  215. package/see/examples/n3/expression_eval.n3 +0 -21
  216. package/see/examples/n3/family_cousins.n3 +0 -62
  217. package/see/examples/n3/fastpow.n3 +0 -56
  218. package/see/examples/n3/fibonacci.n3 +0 -44
  219. package/see/examples/n3/french_cities.n3 +0 -28
  220. package/see/examples/n3/fundamental_theorem_arithmetic.n3 +0 -84
  221. package/see/examples/n3/goldbach_1000.n3 +0 -66
  222. package/see/examples/n3/good_cobbler.n3 +0 -10
  223. package/see/examples/n3/gps.n3 +0 -70
  224. package/see/examples/n3/gray_code_counter.n3 +0 -53
  225. package/see/examples/n3/greatest_lower_bound_uniqueness.n3 +0 -20
  226. package/see/examples/n3/group_inverse_uniqueness.n3 +0 -19
  227. package/see/examples/n3/hadamard_approx.n3 +0 -43
  228. package/see/examples/n3/hanoi.n3 +0 -16
  229. package/see/examples/n3/odrl_dpv_risk_ranked.n3 +0 -460
  230. package/see/examples/n3/path_discovery.n3 +0 -43
  231. package/see/examples/n3/socrates.n3 +0 -21
  232. package/see/examples/n3/wind_turbine.n3 +0 -85
  233. package/see/examples/n3/witch.n3 +0 -30
  234. package/see/examples/odrl_dpv_risk_ranked.js +0 -5128
  235. package/see/examples/output/age.md +0 -48
  236. package/see/examples/output/annotation.md +0 -43
  237. package/see/examples/output/backward.md +0 -50
  238. package/see/examples/output/backward_recursion.md +0 -54
  239. package/see/examples/output/bayes_diagnosis.md +0 -103
  240. package/see/examples/output/bayes_therapy.md +0 -84
  241. package/see/examples/output/bmi.md +0 -164
  242. package/see/examples/output/builtin_coverage.md +0 -99
  243. package/see/examples/output/collection.md +0 -44
  244. package/see/examples/output/complex.md +0 -61
  245. package/see/examples/output/complex_matrix_stability.md +0 -55
  246. package/see/examples/output/composition_of_injective_functions_is_injective.md +0 -62
  247. package/see/examples/output/control_system.md +0 -61
  248. package/see/examples/output/crypto_builtins_tests.md +0 -68
  249. package/see/examples/output/delfour.md +0 -100
  250. package/see/examples/output/digital_product_passport.md +0 -100
  251. package/see/examples/output/dijkstra.md +0 -74
  252. package/see/examples/output/dijkstra_risk_path.md +0 -76
  253. package/see/examples/output/dog.md +0 -50
  254. package/see/examples/output/eco_route_insight.md +0 -88
  255. package/see/examples/output/equals.md +0 -50
  256. package/see/examples/output/equivalence_classes_overlap_implies_same_class.md +0 -86
  257. package/see/examples/output/euler_identity.md +0 -73
  258. package/see/examples/output/ev_roundtrip_planner.md +0 -79
  259. package/see/examples/output/existential_rule.md +0 -54
  260. package/see/examples/output/expression_eval.md +0 -50
  261. package/see/examples/output/family_cousins.md +0 -187
  262. package/see/examples/output/fastpow.md +0 -36
  263. package/see/examples/output/fibonacci.md +0 -53
  264. package/see/examples/output/french_cities.md +0 -70
  265. package/see/examples/output/fundamental_theorem_arithmetic.md +0 -101
  266. package/see/examples/output/genetic_knapsack_selection.md +0 -66
  267. package/see/examples/output/goldbach_1000.md +0 -58
  268. package/see/examples/output/good_cobbler.md +0 -54
  269. package/see/examples/output/gps.md +0 -102
  270. package/see/examples/output/gray_code_counter.md +0 -68
  271. package/see/examples/output/greatest_lower_bound_uniqueness.md +0 -60
  272. package/see/examples/output/group_inverse_uniqueness.md +0 -60
  273. package/see/examples/output/hadamard_approx.md +0 -510
  274. package/see/examples/output/hanoi.md +0 -51
  275. package/see/examples/output/odrl_dpv_risk_ranked.md +0 -139
  276. package/see/examples/output/path_discovery.md +0 -65
  277. package/see/examples/output/rc_discharge_envelope.md +0 -102
  278. package/see/examples/output/rdf_dataset.md +0 -54
  279. package/see/examples/output/rdf_message_flow.md +0 -198
  280. package/see/examples/output/rdf_messages.md +0 -134
  281. package/see/examples/output/school_placement_audit.md +0 -99
  282. package/see/examples/output/smoke_arithmetic.md +0 -54
  283. package/see/examples/output/socrates.md +0 -55
  284. package/see/examples/output/triple_terms.md +0 -53
  285. package/see/examples/output/wind_turbine.md +0 -108
  286. package/see/examples/output/witch.md +0 -87
  287. package/see/examples/path_discovery.js +0 -1774
  288. package/see/examples/rc_discharge_envelope.js +0 -1993
  289. package/see/examples/rdf_dataset.js +0 -1512
  290. package/see/examples/rdf_message_flow.js +0 -2580
  291. package/see/examples/rdf_messages.js +0 -2176
  292. package/see/examples/school_placement_audit.js +0 -1867
  293. package/see/examples/smoke_arithmetic.js +0 -1483
  294. package/see/examples/socrates.js +0 -1420
  295. package/see/examples/triple_terms.js +0 -1442
  296. package/see/examples/wind_turbine.js +0 -2853
  297. package/see/examples/witch.js +0 -1519
  298. package/see/see.js +0 -2179
  299. package/test/see.test.js +0 -159
  300. /package/{see/examples/n3/builtin_coverage.n3 → examples/builtin-coverage.n3} +0 -0
  301. /package/examples/output/{reifies.n3 → builtin-coverage.n3} +0 -0
@@ -1,2070 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
- const fs = require('fs');
4
- const path = require('path');
5
- const { fail, loadInput } = require('./_see');
6
-
7
- const crypto = require('crypto');
8
-
9
- function canonical(term) {
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)];
12
- if (term.kind === 'formula') return ['formula', term.atoms.map((a) => [canonical(a.s), canonical(a.p), canonical(a.o)])];
13
- return [term.kind, term.value];
14
- }
15
- function factKey(f) { return JSON.stringify([canonical(f.s), canonical(f.p), canonical(f.o)]); }
16
- function termIndexKey(t) { return JSON.stringify(canonical(t)); }
17
- function compoundIndexKey() { return Array.from(arguments).map(termIndexKey).join('\u001f'); }
18
- function termIsConcrete(t) {
19
- if (!t || t.kind === 'var') return false;
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);
22
- if (t.kind === 'formula') return t.atoms.every((a) => termIsConcrete(a.s) && termIsConcrete(a.p) && termIsConcrete(a.o));
23
- return true;
24
- }
25
- function isVar(t) { return t && t.kind === 'var'; }
26
- function isIri(t, iri) { return t && t.kind === 'iri' && t.value === iri; }
27
- function lit(value) { return { kind: 'lit', value }; }
28
- function blank(value) { return { kind: 'blank', value }; }
29
- function list(items) { return { kind: 'list', items }; }
30
- function cloneTerm(t) { return JSON.parse(JSON.stringify(t)); }
31
- function primitive(t) {
32
- if (!t) return undefined;
33
- if (t.kind === 'lit') return t.value;
34
- if (t.kind === 'iri') return t.value.replace(/^:/, '');
35
- if (t.kind === 'blank') return t.value;
36
- if (t.kind === 'list') return t.items.map(primitive);
37
- if (t.kind === 'triple') return termToN3(t);
38
- if (t.kind === 'formula') return termToN3(t);
39
- return undefined;
40
- }
41
- function literalToN3(value) {
42
- if (typeof value === 'string') return JSON.stringify(value);
43
- if (typeof value === 'number') {
44
- if (Object.is(value, -0)) return '0';
45
- if (Number.isInteger(value)) return String(value);
46
- return Number(value.toPrecision(15)).toString();
47
- }
48
- if (typeof value === 'boolean') return value ? 'true' : 'false';
49
- return JSON.stringify(value);
50
- }
51
- function termToN3(t) {
52
- if (!t) return 'undefined';
53
- if (t.kind === 'iri') return t.value;
54
- if (t.kind === 'lit') return literalToN3(t.value);
55
- if (t.kind === 'var') return '?' + t.value;
56
- if (t.kind === 'blank') return t.value.startsWith('_:') ? t.value : '_:' + t.value.replace(/^_+/, '');
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) + ' )>>';
59
- if (t.kind === 'formula') return '{ ' + t.atoms.map(atomToN3).join(' . ') + ' }';
60
- return String(t.value ?? t);
61
- }
62
- function atomToN3(f) { return termToN3(f.s) + ' ' + termToN3(f.p) + ' ' + termToN3(f.o); }
63
- function display(t) {
64
- const value = primitive(t);
65
- if (Array.isArray(value)) return value.map(String).join(' ');
66
- return String(value);
67
- }
68
- function deepEqual(a, b) {
69
- if (a?.kind === 'lit' && b?.kind === 'lit' && typeof a.value === 'number' && typeof b.value === 'number') {
70
- return Math.abs(a.value - b.value) < 1e-12;
71
- }
72
- return JSON.stringify(canonical(a)) === JSON.stringify(canonical(b));
73
- }
74
- function resolve(term, env, seen = new Set()) {
75
- if (term.kind === 'var' && Object.hasOwn(env, term.value)) {
76
- if (seen.has(term.value)) return term;
77
- seen.add(term.value);
78
- return resolve(env[term.value], env, seen);
79
- }
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) };
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) })) };
83
- return term;
84
- }
85
- function unify(a, b, env) {
86
- a = resolve(a, env);
87
- b = resolve(b, env);
88
- if (a.kind === 'var') return { ...env, [a.value]: b };
89
- if (b.kind === 'var') return { ...env, [b.value]: a };
90
- if (a.kind === 'list' || b.kind === 'list') {
91
- if (a.kind !== 'list' || b.kind !== 'list' || a.items.length !== b.items.length) return null;
92
- let out = env;
93
- for (let i = 0; i < a.items.length; i += 1) {
94
- out = unify(a.items[i], b.items[i], out);
95
- if (!out) return null;
96
- }
97
- return out;
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
- }
107
- return deepEqual(a, b) ? env : null;
108
- }
109
- function bind(pattern, value, env) { return unify(pattern, value, env); }
110
- function matchFact(atom, fact, env) {
111
- let out = unify(atom.p, fact.p, env); if (!out) return null;
112
- out = unify(atom.s, fact.s, out); if (!out) return null;
113
- return unify(atom.o, fact.o, out);
114
- }
115
- function atomIsGround(atom, env) {
116
- return termIsGround(atom.s, env) && termIsGround(atom.p, env) && termIsGround(atom.o, env);
117
- }
118
- function termIsGround(t, env) {
119
- const r = resolve(t, env);
120
- if (r.kind === 'var') return false;
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);
123
- if (r.kind === 'formula') return r.atoms.every((atom) => atomIsGround(atom, env));
124
- return true;
125
- }
126
- function stringValue(t) {
127
- const r = t;
128
- if (!r) return null;
129
- if (r.kind === 'lit') return String(r.value);
130
- if (r.kind === 'iri') return r.value.replace(/[<>]/g, '');
131
- if (r.kind === 'blank' || r.kind === 'var') return null;
132
- return termToN3(r);
133
- }
134
- function xsdDurationSeconds(value) {
135
- const m = String(value || '').match(/^(-)?P(?:(\d+(?:\.\d+)?)Y)?(?:(\d+(?:\.\d+)?)M)?(?:(\d+(?:\.\d+)?)W)?(?:(\d+(?:\.\d+)?)D)?(?:T(?:(\d+(?:\.\d+)?)H)?(?:(\d+(?:\.\d+)?)M)?(?:(\d+(?:\.\d+)?)S)?)?$/i);
136
- if (!m) return null;
137
- const sign = m[1] ? -1 : 1;
138
- const years = Number(m[2] || 0);
139
- const months = Number(m[3] || 0);
140
- const weeks = Number(m[4] || 0);
141
- const days = Number(m[5] || 0);
142
- const hours = Number(m[6] || 0);
143
- const minutes = Number(m[7] || 0);
144
- const seconds = Number(m[8] || 0);
145
-
146
- // xsd:duration years/months are calendar-relative. SEE uses Gregorian averages
147
- // for standalone numeric comparisons such as "age above P80Y".
148
- return sign * (
149
- years * 365.2425 * 86400 +
150
- months * (365.2425 / 12) * 86400 +
151
- weeks * 7 * 86400 +
152
- days * 86400 +
153
- hours * 3600 +
154
- minutes * 60 +
155
- seconds
156
- );
157
- }
158
-
159
- function toNumberMaybe(t) {
160
- const v = primitive(t);
161
- if (typeof v === 'number') return Number.isFinite(v) || Number.isNaN(v) ? v : null;
162
- if (typeof v === 'boolean') return v ? 1 : 0;
163
- if (typeof v !== 'string') return null;
164
- const dur = xsdDurationSeconds(v);
165
- if (dur !== null) return dur;
166
- const d = Date.parse(v);
167
- if (/^\d{4}-\d{2}-\d{2}(?:T|$)/.test(v) && Number.isFinite(d)) return d / 1000;
168
- const n = Number(v);
169
- return Number.isNaN(n) ? null : n;
170
- }
171
- function toNumber(t) {
172
- const n = toNumberMaybe(t);
173
- if (n === null || !Number.isFinite(n)) throw new Error('Expected numeric value, got ' + JSON.stringify(primitive(t)));
174
- return n;
175
- }
176
- function toIntegerMaybe(t) {
177
- const n = toNumberMaybe(t);
178
- if (n === null || !Number.isFinite(n) || !Number.isInteger(n)) return null;
179
- return n;
180
- }
181
- function parseDate(t) {
182
- const v = primitive(t);
183
- if (typeof v !== 'string') return null;
184
- const ms = Date.parse(v);
185
- return Number.isFinite(ms) ? new Date(ms) : null;
186
- }
187
- function dateTimeLiteral(date) { return lit(date.toISOString().replace(/\.000Z$/, 'Z')); }
188
- function durationLiteral(seconds) { return lit('PT' + Number(seconds.toPrecision(15)).toString() + 'S'); }
189
- function termFrom(value) {
190
- if (Array.isArray(value)) return list(value.map(termFrom));
191
- if (value && typeof value === 'object' && value.kind) return value;
192
- return lit(value);
193
- }
194
- function allBoundList(term, env) {
195
- const r = resolve(term, env);
196
- if (r.kind !== 'list') throw new Error('Expected N3 list');
197
- if (r.items.some((item) => resolve(item, env).kind === 'var')) return null;
198
- return r.items.map((item) => resolve(item, env));
199
- }
200
- function bindResult(pattern, value, env) {
201
- return bind(pattern, termFrom(value), env);
202
- }
203
- function bindTermResult(pattern, term, env) {
204
- const out = bind(pattern, term, env);
205
- return out ? [out] : [];
206
- }
207
- function succeedIf(ok, env) { return ok ? [env] : []; }
208
- function compareNumericTerms(a, b) {
209
- const na = toNumberMaybe(a), nb = toNumberMaybe(b);
210
- if (na === null || nb === null) return null;
211
- if (Number.isNaN(na) || Number.isNaN(nb)) return NaN;
212
- return na < nb ? -1 : na > nb ? 1 : 0;
213
- }
214
- function comparisonOperands(s, o, env) {
215
- const left = resolve(s, env), right = resolve(o, env);
216
- if (left.kind === 'list' && left.items.length === 2 && (right.kind === 'lit' ? right.value === true : right.kind === 'iri' && right.value === 'true')) {
217
- return [resolve(left.items[0], env), resolve(left.items[1], env)];
218
- }
219
- return [left, right];
220
- }
221
- function bindNumericOutput(pattern, value, env) {
222
- if (!Number.isFinite(value)) return [];
223
- const normalized = Number.isInteger(value) ? value : Number(value.toPrecision(15));
224
- return bindTermResult(pattern, lit(normalized), env);
225
- }
226
- function unaryMath(name, s, o, env, fwd, inv) {
227
- const left = resolve(s, env), right = resolve(o, env);
228
- if (left.kind !== 'var') return bindNumericOutput(o, fwd(toNumber(left)), env);
229
- if (right.kind !== 'var' && inv) return bindNumericOutput(s, inv(toNumber(right)), env);
230
- if (left.kind === 'var' && right.kind === 'var') return [env];
231
- return [];
232
- }
233
- function builtinMath(name, s, o, env) {
234
- const left = resolve(s, env);
235
- const right = resolve(o, env);
236
- const testCmp = (pred) => {
237
- const [a, b] = comparisonOperands(s, o, env);
238
- if (a.kind === 'var' || b.kind === 'var') return [];
239
- const cmp = compareNumericTerms(a, b);
240
- if (cmp === null || Number.isNaN(cmp)) return [];
241
- return succeedIf(pred(cmp), env);
242
- };
243
- if (name === 'math:lessThan') return testCmp((c) => c < 0);
244
- if (name === 'math:notLessThan') return testCmp((c) => c >= 0);
245
- if (name === 'math:greaterThan') return testCmp((c) => c > 0);
246
- if (name === 'math:notGreaterThan') return testCmp((c) => c <= 0);
247
- if (name === 'math:equalTo') return testCmp((c) => c === 0);
248
- if (name === 'math:notEqualTo') {
249
- const [a, b] = comparisonOperands(s, o, env);
250
- if (a.kind === 'var' || b.kind === 'var') return [];
251
- const cmp = compareNumericTerms(a, b);
252
- if (cmp !== null) return succeedIf(Number.isNaN(cmp) || cmp !== 0, env);
253
- return succeedIf(!deepEqual(a, b), env);
254
- }
255
- if (name === 'math:negation') return unaryMath(name, s, o, env, (x) => -x, (x) => -x);
256
- if (name === 'math:absoluteValue') {
257
- if (left.kind === 'var' && right.kind === 'var') return [env];
258
- if (left.kind !== 'var') return bindNumericOutput(o, Math.abs(toNumber(left)), env);
259
- const n = toNumber(right);
260
- const outs = [];
261
- for (const v of n === 0 ? [0] : [n, -n]) {
262
- const out = bind(s, lit(v), env); if (out) outs.push(out);
263
- }
264
- return outs;
265
- }
266
- if (name === 'math:degrees') return unaryMath(name, s, o, env, (x) => x * 180 / Math.PI, (x) => x * Math.PI / 180);
267
- const unary = {
268
- 'math:sin': [Math.sin, Math.asin], 'math:cos': [Math.cos, Math.acos], 'math:tan': [Math.tan, Math.atan],
269
- 'math:asin': [Math.asin, Math.sin], 'math:acos': [Math.acos, Math.cos], 'math:atan': [Math.atan, Math.tan],
270
- 'math:sinh': [Math.sinh, Math.asinh], 'math:cosh': [Math.cosh, Math.acosh], 'math:tanh': [Math.tanh, Math.atanh],
271
- 'math:rounded': [Math.round, null], 'math:floor': [Math.floor, null]
272
- };
273
- if (Object.hasOwn(unary, name)) {
274
- const [fwd, inv] = unary[name];
275
- if (typeof fwd !== 'function') return [];
276
- return unaryMath(name, s, o, env, fwd, typeof inv === 'function' ? inv : null);
277
- }
278
- if (name === 'math:exponentiation') {
279
- const l = resolve(s, env);
280
- if (l.kind !== 'list' || l.items.length !== 2) return [];
281
- const base = resolve(l.items[0], env);
282
- const exponent = resolve(l.items[1], env);
283
- const result = resolve(o, env);
284
- if (base.kind !== 'var' && exponent.kind !== 'var') return bindNumericOutput(o, Math.pow(toNumber(base), toNumber(exponent)), env);
285
- if (base.kind !== 'var' && result.kind !== 'var') {
286
- const b = toNumber(base), r = toNumber(result);
287
- if (b > 0 && b !== 1 && r > 0) return bindNumericOutput(l.items[1], Math.log(r) / Math.log(b), env);
288
- return [];
289
- }
290
- if (exponent.kind !== 'var' && result.kind !== 'var') {
291
- const e = toNumber(exponent), r = toNumber(result);
292
- if (e !== 0) return bindNumericOutput(l.items[0], Math.pow(r, 1 / e), env);
293
- return [];
294
- }
295
- return [];
296
- }
297
- if (name === 'math:product' || name === 'math:sum') {
298
- const vals = allBoundList(s, env); if (!vals) return [];
299
- if (vals.length === 2) {
300
- const [aT, bT] = vals;
301
- const aD = parseDate(aT);
302
- if (name === 'math:sum' && aD) {
303
- const secs = toNumberMaybe(bT);
304
- if (secs !== null && Number.isFinite(secs)) return bindTermResult(o, dateTimeLiteral(new Date(aD.getTime() + secs * 1000)), env);
305
- }
306
- }
307
- const value = name === 'math:product' ? vals.reduce((a, x) => a * toNumber(x), 1) : vals.reduce((a, x) => a + toNumber(x), 0);
308
- return bindNumericOutput(o, value, env);
309
- }
310
- if (name === 'math:difference' || name === 'math:quotient' || name === 'math:integerQuotient' || name === 'math:remainder') {
311
- const vals = allBoundList(s, env); if (!vals || vals.length !== 2) return [];
312
- const aD = parseDate(vals[0]), bD = parseDate(vals[1]);
313
- if (name === 'math:difference' && aD && bD) return bindTermResult(o, durationLiteral((aD.getTime() - bD.getTime()) / 1000), env);
314
- if (name === 'math:difference' && aD && !bD) {
315
- const secs = toNumberMaybe(vals[1]);
316
- if (secs !== null && Number.isFinite(secs)) return bindTermResult(o, dateTimeLiteral(new Date(aD.getTime() - secs * 1000)), env);
317
- }
318
- const a = toNumber(vals[0]), b = toNumber(vals[1]);
319
- if ((name === 'math:quotient' || name === 'math:integerQuotient' || name === 'math:remainder') && b === 0) return [];
320
- if (name === 'math:difference') return bindNumericOutput(o, a - b, env);
321
- if (name === 'math:quotient') return bindNumericOutput(o, a / b, env);
322
- if (!Number.isInteger(a) || !Number.isInteger(b)) return [];
323
- if (name === 'math:integerQuotient') return bindNumericOutput(o, Math.trunc(a / b), env);
324
- return bindNumericOutput(o, a % b, env);
325
- }
326
- return null;
327
- }
328
- function applyFormat(fmt, args) {
329
- let i = 0;
330
- return String(fmt).replace(/%([-0]?\d+)?(?:\.(\d+))?([%sdiufFeEgGc])/g, (m, width, precision, conv) => {
331
- if (conv === '%') return '%';
332
- const arg = args[i++];
333
- if (conv === 's') return String(arg ?? '');
334
- if (conv === 'c') return String.fromCodePoint(Number(arg));
335
- if ('diu'.includes(conv)) return String(Math.trunc(Number(arg)));
336
- const n = Number(arg);
337
- if (conv === 'f' || conv === 'F') return n.toFixed(precision == null ? 6 : Number(precision));
338
- if (conv === 'e' || conv === 'E') return n.toExponential(precision == null ? 6 : Number(precision));
339
- if (conv === 'g' || conv === 'G') return n.toPrecision(precision == null ? 6 : Number(precision)).replace(/\.0+(e|$)/i, '$1');
340
- return m;
341
- });
342
- }
343
- function compileRegex(pattern, flags = '') {
344
- try { return new RegExp(String(pattern), flags); }
345
- catch { return null; }
346
- }
347
- function builtinString(name, s, o, env) {
348
- const left = resolve(s, env), right = resolve(o, env);
349
- function output(value) { return bindTermResult(o, lit(String(value)), env); }
350
- const str = (term) => stringValue(resolve(term, env));
351
- if (name === 'string:concatenation') {
352
- const vals = allBoundList(s, env); if (!vals) return [];
353
- return output(vals.map(stringValue).join(''));
354
- }
355
- if (name === 'string:format') {
356
- const vals = allBoundList(s, env); if (!vals || vals.length === 0) return [];
357
- return output(applyFormat(stringValue(vals[0]), vals.slice(1).map(stringValue)));
358
- }
359
- if (name === 'string:length') { const a = str(s); return a === null ? [] : bindTermResult(o, lit(a.length), env); }
360
- if (name === 'string:charAt') {
361
- const vals = allBoundList(s, env); if (!vals || vals.length !== 2) return [];
362
- const a = stringValue(vals[0]); const idx = Math.trunc(toNumber(vals[1]));
363
- return output(idx < 0 || idx >= a.length ? '' : a.charAt(idx));
364
- }
365
- if (name === 'string:setCharAt') {
366
- const vals = allBoundList(s, env); if (!vals || vals.length !== 3) return [];
367
- const a = stringValue(vals[0]); const idx = Math.trunc(toNumber(vals[1])); const ch = String(stringValue(vals[2]) || '').charAt(0);
368
- if (idx < 0 || idx >= a.length) return output(a);
369
- return output(a.slice(0, idx) + ch + a.slice(idx + 1));
370
- }
371
- const a = str(s), b = str(o);
372
- if (name === 'string:contains') return a === null || b === null ? [] : succeedIf(a.includes(b), env);
373
- if (name === 'string:containsIgnoringCase') return a === null || b === null ? [] : succeedIf(a.toLowerCase().includes(b.toLowerCase()), env);
374
- if (name === 'string:startsWith') return a === null || b === null ? [] : succeedIf(a.startsWith(b), env);
375
- if (name === 'string:endsWith') return a === null || b === null ? [] : succeedIf(a.endsWith(b), env);
376
- if (name === 'string:equalIgnoringCase') return a === null || b === null ? [] : succeedIf(a.toLowerCase() === b.toLowerCase(), env);
377
- if (name === 'string:notEqualIgnoringCase') return a === null || b === null ? [] : succeedIf(a.toLowerCase() !== b.toLowerCase(), env);
378
- if (name === 'string:greaterThan') return a === null || b === null ? [] : succeedIf(a > b, env);
379
- if (name === 'string:lessThan') return a === null || b === null ? [] : succeedIf(a < b, env);
380
- if (name === 'string:notGreaterThan') return a === null || b === null ? [] : succeedIf(a <= b, env);
381
- if (name === 'string:notLessThan') return a === null || b === null ? [] : succeedIf(a >= b, env);
382
- if (name === 'string:matches' || name === 'string:notMatches') {
383
- if (a === null || b === null) return [];
384
- const re = compileRegex(b); if (!re) return [];
385
- const ok = re.test(a);
386
- return succeedIf(name === 'string:matches' ? ok : !ok, env);
387
- }
388
- if (name === 'string:replace') {
389
- const vals = allBoundList(s, env); if (!vals || vals.length !== 3) return [];
390
- const data = stringValue(vals[0]), pattern = stringValue(vals[1]), replacement = stringValue(vals[2]);
391
- const re = compileRegex(pattern, 'g'); if (!re) return [];
392
- return output(data.replace(re, replacement));
393
- }
394
- if (name === 'string:scrape') {
395
- const vals = allBoundList(s, env); if (!vals || vals.length !== 2) return [];
396
- const data = stringValue(vals[0]), pattern = stringValue(vals[1]);
397
- const re = compileRegex(pattern); if (!re) return [];
398
- const m = data.match(re); if (!m || m.length < 2) return [];
399
- return output(m[1]);
400
- }
401
- return null;
402
- }
403
- function builtinCrypto(name, s, o, env) {
404
- const algos = { 'crypto:sha': 'sha1', 'crypto:md5': 'md5', 'crypto:sha256': 'sha256', 'crypto:sha512': 'sha512' };
405
- if (!Object.hasOwn(algos, name)) return null;
406
- if (!termIsGround(s, env)) return [];
407
- const digest = crypto.createHash(algos[name]).update(String(primitive(resolve(s, env))), 'utf8').digest('hex');
408
- return bindTermResult(o, lit(digest), env);
409
- }
410
- function formulaContains(graph, patternAtoms, env, rules, depth) {
411
- const localGraph = graph && graph.facts ? graph : makeGraph(graph?.atoms || graph?.facts || []);
412
- return evalBody(patternAtoms, [env], localGraph, rules, depth + 1).length > 0;
413
- }
414
- function builtinLog(name, s, o, env, graph, rules, depth) {
415
- if (name === 'log:equalTo') return bindTermResult(s, resolve(o, env), env);
416
- if (name === 'log:notEqualTo') {
417
- const out = unify(resolve(s, env), resolve(o, env), env);
418
- return out ? [] : [env];
419
- }
420
- if (name === 'log:conjunction') {
421
- const vals = allBoundList(s, env); if (!vals) return [];
422
- const merged = [], seen = new Set();
423
- for (const part of vals) {
424
- if (part.kind === 'lit' && part.value === true) continue;
425
- if (part.kind !== 'formula') return [];
426
- for (const atom of part.atoms) { const k = factKey(atom); if (!seen.has(k)) { seen.add(k); merged.push(atom); } }
427
- }
428
- return bindTermResult(o, { kind: 'formula', atoms: merged }, env);
429
- }
430
- if (name === 'log:includes' || name === 'log:notIncludes') {
431
- const scope = resolve(s, env), pattern = resolve(o, env);
432
- if (pattern.kind !== 'formula') return [];
433
- const local = scope.kind === 'formula' ? makeGraph(scope.atoms) : graph;
434
- const ok = evalBody(pattern.atoms, [env], local, rules, depth + 1).length > 0;
435
- return succeedIf(name === 'log:includes' ? ok : !ok, env);
436
- }
437
- if (name === 'log:rawType') {
438
- const rt = resolve(s, env);
439
- if (rt.kind === 'var') return [];
440
- const ty = rt.kind === 'formula' ? 'log:Formula' : rt.kind === 'lit' ? 'log:Literal' : rt.kind === 'list' ? 'rdf:List' : 'log:Other';
441
- return bindTermResult(o, { kind: 'iri', value: ty }, env);
442
- }
443
- if (name === 'log:dtlit') {
444
- const l = resolve(s, env), obj = resolve(o, env);
445
- if (l.kind === 'list' && l.items.length === 2) {
446
- const lex = stringValue(resolve(l.items[0], env));
447
- const dt = resolve(l.items[1], env);
448
- if (lex === null || dt.kind !== 'iri') return [];
449
- return bindTermResult(o, lit(lex), env);
450
- }
451
- if (obj.kind === 'var') return [];
452
- return stringValue(obj) === null ? [] : [env];
453
- }
454
- if (name === 'log:outputString') return [env];
455
- if (name === 'log:collectAllIn') {
456
- const l = resolve(s, env); if (l.kind !== 'list' || l.items.length !== 3) return [];
457
- const [valueTemplate, clause, outList] = l.items;
458
- const scope = resolve(o, env);
459
- const local = scope.kind === 'formula' ? makeGraph(scope.atoms) : graph;
460
- if (clause.kind !== 'formula') return [];
461
- const sols = evalBody(clause.atoms, [env], local, rules, depth + 1);
462
- return bindTermResult(outList, list(sols.map((sol) => resolve(valueTemplate, sol))), env);
463
- }
464
- if (name === 'log:forAllIn') {
465
- const l = resolve(s, env); if (l.kind !== 'list' || l.items.length !== 2) return [];
466
- const [where, then] = l; if (where.kind !== 'formula' || then.kind !== 'formula') return [];
467
- const scope = resolve(o, env);
468
- const local = scope.kind === 'formula' ? makeGraph(scope.atoms) : graph;
469
- const sols = evalBody(where.atoms, [{}], local, rules, depth + 1);
470
- for (const sol of sols) if (!evalBody(then.atoms, [sol], local, rules, depth + 1).length) return [];
471
- return [env];
472
- }
473
- if (['log:content', 'log:semantics', 'log:semanticsOrError', 'log:parsedAsN3'].includes(name)) {
474
- throw new Error(name + ' requires dereferencing or parsing at runtime and is intentionally not available in offline specialized SEE output');
475
- }
476
- return null;
477
- }
478
- function splitList(items, parts, env, start = 0, idx = 0) {
479
- if (idx === parts.length) return start === items.length ? [env] : [];
480
- const outs = [];
481
- const last = idx === parts.length - 1;
482
- for (let end = start; end <= items.length; end += 1) {
483
- if (last && end !== items.length) continue;
484
- const out = bind(parts[idx], list(items.slice(start, end)), env);
485
- if (out) outs.push(...splitList(items, parts, out, end, idx + 1));
486
- }
487
- return outs;
488
- }
489
- function termSortKey(t) {
490
- if (t.kind === 'lit') { const n = Number(t.value); return Number.isNaN(n) ? '1:' + String(t.value) : '0:' + n.toString().padStart(20, '0'); }
491
- if (t.kind === 'iri') return '2:' + t.value;
492
- if (t.kind === 'list') return '3:' + t.items.map(termSortKey).join('|');
493
- return '9:' + JSON.stringify(canonical(t));
494
- }
495
- function builtinList(name, s, o, env, graph, rules, depth) {
496
- const predName = name === 'rdf:first' ? 'list:first' : name === 'rdf:rest' ? 'list:rest' : name;
497
- const left = resolve(s, env);
498
- const right = resolve(o, env);
499
- const listItems = (term) => term.kind === 'list' ? term.items.map((x) => resolve(x, env)) : null;
500
- if (predName === 'list:first') {
501
- const xs = listItems(left); if (!xs || !xs.length) return [];
502
- return bindTermResult(o, xs[0], env);
503
- }
504
- if (predName === 'list:rest') {
505
- const xs = listItems(left); if (!xs || !xs.length) return [];
506
- return bindTermResult(o, list(xs.slice(1)), env);
507
- }
508
- if (predName === 'list:firstRest') {
509
- const outs = [];
510
- if (left.kind === 'list' && left.items.length > 0 && !left.items.some((x) => resolve(x, env).kind === 'var')) {
511
- const items = left.items.map((x) => resolve(x, env));
512
- const out = bind(o, list([items[0], list(items.slice(1))]), env); if (out) outs.push(out);
513
- }
514
- if (right.kind === 'list' && right.items.length === 2) {
515
- const rest = resolve(right.items[1], env);
516
- const first = resolve(right.items[0], env);
517
- if (rest.kind === 'list') { const out = bind(s, list([first, ...rest.items]), env); if (out) outs.push(out); }
518
- }
519
- return outs;
520
- }
521
- if (predName === 'list:append') {
522
- if (left.kind !== 'list') return [];
523
- if (right.kind === 'list') return splitList(right.items, left.items, env);
524
- const outElems = [];
525
- for (const part of left.items) { const p = resolve(part, env); if (p.kind !== 'list') return []; outElems.push(...p.items); }
526
- return bindTermResult(o, list(outElems), env);
527
- }
528
- if (predName === 'list:iterate') {
529
- const xs = listItems(left); if (!xs) return [];
530
- const outs = [];
531
- for (let i = 0; i < xs.length; i += 1) { const out = bind(o, list([lit(i), xs[i]]), env); if (out) outs.push(out); }
532
- return outs;
533
- }
534
- if (predName === 'list:last') {
535
- const xs = listItems(left); if (!xs || !xs.length) return [];
536
- return bindTermResult(o, xs[xs.length - 1], env);
537
- }
538
- if (predName === 'list:memberAt') {
539
- if (left.kind !== 'list' || left.items.length !== 2) return [];
540
- const arr = resolve(left.items[0], env), idx = resolve(left.items[1], env);
541
- if (arr.kind !== 'list') return [];
542
- const outs = [];
543
- for (let i = 0; i < arr.items.length; i += 1) {
544
- let out = idx.kind === 'var' ? bind(left.items[1], lit(i), env) : (Number(idx.value) === i ? env : null);
545
- if (out) out = bind(o, arr.items[i], out);
546
- if (out) outs.push(out);
547
- }
548
- return outs;
549
- }
550
- if (predName === 'list:remove') {
551
- if (left.kind !== 'list' || left.items.length !== 2) return [];
552
- const arr = resolve(left.items[0], env), item = resolve(left.items[1], env);
553
- if (arr.kind !== 'list' || item.kind === 'var') return [];
554
- return bindTermResult(o, list(arr.items.filter((x) => !deepEqual(resolve(x, env), item))), env);
555
- }
556
- if (predName === 'list:member') {
557
- const xs = listItems(left); if (!xs) return [];
558
- return xs.map((x) => bind(o, x, env)).filter(Boolean);
559
- }
560
- if (predName === 'list:in') {
561
- const xs = listItems(right); if (!xs) return [];
562
- return xs.map((x) => bind(s, x, env)).filter(Boolean);
563
- }
564
- if (predName === 'list:length') {
565
- const xs = listItems(left); if (!xs) return [];
566
- return bindTermResult(o, lit(xs.length), env);
567
- }
568
- if (predName === 'list:notMember') {
569
- const xs = listItems(left); if (!xs) return [];
570
- return xs.some((x) => unify(o, x, env)) ? [] : [env];
571
- }
572
- if (predName === 'list:reverse') {
573
- if (left.kind === 'list') return bindTermResult(o, list([...left.items].reverse()), env);
574
- if (right.kind === 'list') return bindTermResult(s, list([...right.items].reverse()), env);
575
- return [];
576
- }
577
- if (predName === 'list:sort') {
578
- const source = left.kind === 'list' ? left : right.kind === 'list' ? right : null;
579
- if (!source || source.items.some((x) => resolve(x, env).kind === 'var')) return [];
580
- const sorted = [...source.items].sort((a, b) => termSortKey(a).localeCompare(termSortKey(b)));
581
- return left.kind === 'list' ? bindTermResult(o, list(sorted), env) : bindTermResult(s, list(sorted), env);
582
- }
583
- if (predName === 'list:map') {
584
- if (left.kind !== 'list' || left.items.length !== 2) return [];
585
- const arr = resolve(left.items[0], env), pred = resolve(left.items[1], env);
586
- if (arr.kind !== 'list' || pred.kind !== 'iri') return [];
587
- const values = [];
588
- for (let i = 0; i < arr.items.length; i += 1) {
589
- const valueVar = { kind: 'var', value: '__map_' + depth + '_' + i };
590
- const matches = evalAtom({ s: arr.items[i], p: pred, o: valueVar }, env, graph, rules, depth + 1);
591
- for (const m of matches) { const v = resolve(valueVar, m); if (v.kind !== 'var') values.push(v); }
592
- }
593
- return bindTermResult(o, list(values), env);
594
- }
595
- return null;
596
- }
597
- function parseDateTimeParts(t) {
598
- const v = primitive(t);
599
- if (typeof v !== 'string') return null;
600
- const m = v.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|[+-]\d{2}:\d{2})?$/);
601
- if (!m) return null;
602
- return { year: Number(m[1]), month: Number(m[2]), day: Number(m[3]), hour: Number(m[4]), minute: Number(m[5]), second: Number(m[6]), timeZone: m[7] || '' };
603
- }
604
- function builtinTime(name, s, o, env) {
605
- if (name === 'time:localTime') return bindTermResult(o, lit(new Date().toISOString().replace(/\.000Z$/, 'Z')), env);
606
- const parts = parseDateTimeParts(resolve(s, env)); if (!parts) return [];
607
- const key = name.slice('time:'.length);
608
- if (['year','month','day','hour','minute','second'].includes(key)) return bindTermResult(o, lit(parts[key]), env);
609
- if (key === 'timeZone') return bindTermResult(o, lit(parts.timeZone || 'Z'), env);
610
- return null;
611
- }
612
- function evalBuiltin(atom, env, graph, rules, depth) {
613
- const pred = resolve(atom.p, env);
614
- if (pred.kind !== 'iri') return null;
615
- const name = pred.value;
616
- if (name.startsWith('math:')) return builtinMath(name, atom.s, atom.o, env);
617
- if (name.startsWith('string:')) return builtinString(name, atom.s, atom.o, env);
618
- if (name.startsWith('crypto:')) return builtinCrypto(name, atom.s, atom.o, env);
619
- if (name.startsWith('list:') || name === 'rdf:first' || name === 'rdf:rest') return builtinList(name, atom.s, atom.o, env, graph, rules, depth);
620
- if (name.startsWith('time:')) return builtinTime(name, atom.s, atom.o, env);
621
- if (name.startsWith('log:')) return builtinLog(name, atom.s, atom.o, env, graph, rules, depth);
622
- return null;
623
- }
624
- function renameTerm(term, suffix) {
625
- if (term.kind === 'var') return { kind: 'var', value: term.value + suffix };
626
- if (term.kind === 'list') return list(term.items.map((item) => renameTerm(item, suffix)));
627
- if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => renameAtom(a, suffix)) };
628
- return cloneTerm(term);
629
- }
630
- function renameAtom(atom, suffix) {
631
- return { s: renameTerm(atom.s, suffix), p: renameTerm(atom.p, suffix), o: renameTerm(atom.o, suffix) };
632
- }
633
- let renameCounter = 0;
634
- function evalBackward(atom, env, graph, rules, depth) {
635
- if (depth > 200) throw new Error('Backward rule recursion limit exceeded');
636
- const outs = [];
637
- for (const rule of rules) {
638
- if (rule.kind !== 'backward') continue;
639
- for (const headAtom of rule.head) {
640
- const suffix = '__rule' + rule.id + '_' + depth + '_' + (++renameCounter);
641
- const renamedHead = renameAtom(headAtom, suffix);
642
- let out = unify(atom.p, renamedHead.p, env); if (!out) continue;
643
- out = unify(atom.s, renamedHead.s, out); if (!out) continue;
644
- out = unify(atom.o, renamedHead.o, out); if (!out) continue;
645
- const renamedBody = rule.body.map((bodyAtom) => renameAtom(bodyAtom, suffix));
646
- outs.push(...evalBody(renamedBody, [out], graph, rules, depth + 1));
647
- }
648
- }
649
- return outs;
650
- }
651
- function evalAtom(atom, env, graph, rules, depth = 0) {
652
- const builtin = evalBuiltin(atom, env, graph, rules, depth);
653
- if (builtin !== null) return builtin;
654
- const outs = [];
655
- for (const fact of candidateFactsForAtom(atom, env, graph)) {
656
- const out = matchFact(atom, fact, env);
657
- if (out) outs.push(out);
658
- }
659
- outs.push(...evalBackward(atom, env, graph, rules, depth + 1));
660
- return outs;
661
- }
662
- function evalBody(atoms, envs, graph, rules, depth = 0) {
663
- let out = envs;
664
- for (const atom of atoms) {
665
- const next = [];
666
- for (const env of out) next.push(...evalAtom(atom, env, graph, rules, depth));
667
- out = next;
668
- if (!out.length) break;
669
- }
670
- return out;
671
- }
672
- function envSignature(env) {
673
- return crypto.createHash('sha1').update(JSON.stringify(Object.keys(env).sort().map((k) => [k, canonical(env[k])]))).digest('hex').slice(0, 12);
674
- }
675
- function instantiate(term, env, ruleId) {
676
- if (term.kind === 'var') {
677
- if (!Object.hasOwn(env, term.value)) throw new Error('Unbound variable in rule head: ?' + term.value);
678
- return cloneTerm(resolve(env[term.value], env));
679
- }
680
- if (term.kind === 'blank') return blank('_:r' + ruleId + '_' + envSignature(env) + '_' + term.value.replace(/^_/, ''));
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) };
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) })) };
684
- return cloneTerm(term);
685
- }
686
- function supportFactsForBody(body, env, graph) {
687
- const seen = new Set();
688
- const out = [];
689
- for (const atom of body || []) {
690
- const pred = resolve(atom.p, env);
691
- if (pred.kind === 'iri') {
692
- const builtin = pred.value.startsWith('math:') || pred.value.startsWith('string:') || pred.value.startsWith('crypto:') ||
693
- pred.value.startsWith('list:') || pred.value.startsWith('time:') || pred.value.startsWith('log:') ||
694
- pred.value === 'rdf:first' || pred.value === 'rdf:rest';
695
- if (builtin) continue;
696
- }
697
- for (const fact of candidateFactsForAtom(atom, env, graph)) {
698
- if (!matchFact(atom, fact, env)) continue;
699
- const key = factKey(fact);
700
- if (!seen.has(key)) { seen.add(key); out.push(fact); }
701
- }
702
- }
703
- return out;
704
- }
705
- function makeIndex() { return { p: new Map(), spByP: new Map(), poByP: new Map(), s: null, o: null }; }
706
- function pushIndex(map, key, fact) {
707
- let bucket = map.get(key);
708
- if (!bucket) { bucket = []; map.set(key, bucket); }
709
- bucket.push(fact);
710
- }
711
- function makeGraph(facts) {
712
- const graph = { facts: [], keys: new Set(), index: makeIndex() };
713
- for (const fact of facts) addFact(graph, fact);
714
- return graph;
715
- }
716
- function ensureFlatIndex(graph, kind, termSelector) {
717
- if (!graph.index[kind]) {
718
- const map = new Map();
719
- for (const fact of graph.facts) pushIndex(map, termIndexKey(termSelector(fact)), fact);
720
- graph.index[kind] = map;
721
- }
722
- return graph.index[kind];
723
- }
724
- function ensurePredicateTermIndex(graph, byPredicate, pKey, pFacts, termSelector) {
725
- let map = graph.index[byPredicate].get(pKey);
726
- if (!map) {
727
- map = new Map();
728
- for (const fact of pFacts) pushIndex(map, termIndexKey(termSelector(fact)), fact);
729
- graph.index[byPredicate].set(pKey, map);
730
- }
731
- return map;
732
- }
733
- function addFact(graph, fact) {
734
- const key = factKey(fact);
735
- if (graph.keys.has(key)) return false;
736
- graph.keys.add(key);
737
- graph.facts.push(fact);
738
- const pKey = termIndexKey(fact.p);
739
- pushIndex(graph.index.p, pKey, fact);
740
- if (graph.index.s) pushIndex(graph.index.s, termIndexKey(fact.s), fact);
741
- if (graph.index.o) pushIndex(graph.index.o, termIndexKey(fact.o), fact);
742
- const sp = graph.index.spByP.get(pKey);
743
- if (sp) pushIndex(sp, termIndexKey(fact.s), fact);
744
- const po = graph.index.poByP.get(pKey);
745
- if (po) pushIndex(po, termIndexKey(fact.o), fact);
746
- return true;
747
- }
748
- function candidateFactsForAtom(atom, env, graph) {
749
- if (!graph || !graph.index) return graph && graph.facts ? graph.facts : [];
750
- const s = resolve(atom.s, env), p = resolve(atom.p, env), o = resolve(atom.o, env);
751
- const sg = termIsConcrete(s), pg = termIsConcrete(p), og = termIsConcrete(o);
752
- if (pg) {
753
- const pKey = termIndexKey(p);
754
- const pFacts = graph.index.p.get(pKey) || [];
755
- if (sg) return ensurePredicateTermIndex(graph, 'spByP', pKey, pFacts, (f) => f.s).get(termIndexKey(s)) || [];
756
- if (og) return ensurePredicateTermIndex(graph, 'poByP', pKey, pFacts, (f) => f.o).get(termIndexKey(o)) || [];
757
- return pFacts;
758
- }
759
- if (sg) return ensureFlatIndex(graph, 's', (f) => f.s).get(termIndexKey(s)) || [];
760
- if (og) return ensureFlatIndex(graph, 'o', (f) => f.o).get(termIndexKey(o)) || [];
761
- return graph.facts;
762
- }
763
-
764
- function isBuiltinPredicateValue(name) {
765
- return typeof name === 'string' && (name.startsWith('math:') || name.startsWith('string:') || name.startsWith('crypto:') ||
766
- name.startsWith('list:') || name.startsWith('time:') || name.startsWith('log:') || name === 'rdf:first' || name === 'rdf:rest');
767
- }
768
- function collectFormulaDependencyKeys(term, keys) {
769
- if (!term) return true;
770
- if (term.kind === 'formula') {
771
- for (const atom of term.atoms || []) {
772
- if (!collectAtomDependencyKeys(atom, keys)) return false;
773
- }
774
- return true;
775
- }
776
- if (term.kind === 'list') {
777
- for (const item of term.items || []) if (!collectFormulaDependencyKeys(item, keys)) return false;
778
- }
779
- return true;
780
- }
781
- function collectAtomDependencyKeys(atom, keys) {
782
- const pred = atom && atom.p;
783
- if (!pred || pred.kind === 'var') return false;
784
- if (pred.kind === 'iri') {
785
- if (!isBuiltinPredicateValue(pred.value)) keys.add(termIndexKey(pred));
786
- else if (pred.value.startsWith('log:')) {
787
- if (!collectFormulaDependencyKeys(atom.s, keys)) return false;
788
- if (!collectFormulaDependencyKeys(atom.o, keys)) return false;
789
- }
790
- }
791
- return true;
792
- }
793
- function ruleDependencyPredicateKeys(rule) {
794
- const keys = new Set();
795
- for (const atom of rule.body || []) {
796
- if (!collectAtomDependencyKeys(atom, keys)) return null;
797
- }
798
- return keys;
799
- }
800
- function producedFactsCanAffectRules(producedPredicates, ruleDependencies) {
801
- if (!producedPredicates || !producedPredicates.size) return false;
802
- for (const deps of ruleDependencies) {
803
- if (deps === null) return true;
804
- for (const key of producedPredicates) if (deps.has(key)) return true;
805
- }
806
- return false;
807
- }
808
- function saturate(initialFacts, rules) {
809
- const graph = makeGraph(initialFacts);
810
- const trace = [];
811
- const maxIterations = 10000;
812
- const activeRules = (rules || []).filter((rule) => rule.kind !== 'backward');
813
- const ruleDependencies = activeRules.map(ruleDependencyPredicateKeys);
814
- for (let iter = 0; iter < maxIterations; iter += 1) {
815
- let changed = false;
816
- const producedPredicates = new Set();
817
- for (const rule of activeRules) {
818
- const matches = evalBody(rule.body, [{}], graph, rules);
819
- if (rule.kind === 'fuse') {
820
- if (matches.length) throw new Error('N3 fuse failed in compiled rule #' + rule.id);
821
- continue;
822
- }
823
- for (const env of matches) {
824
- const supportFacts = supportFactsForBody(rule.body, env, graph);
825
- const produced = [];
826
- for (const atom of rule.head) {
827
- const fact = { s: instantiate(atom.s, env, rule.id), p: instantiate(atom.p, env, rule.id), o: instantiate(atom.o, env, rule.id) };
828
- if (addFact(graph, fact)) {
829
- changed = true;
830
- produced.push(fact);
831
- producedPredicates.add(termIndexKey(fact.p));
832
- }
833
- }
834
- if (produced.length) {
835
- trace.push({
836
- rule: rule.id,
837
- produced: produced.length,
838
- producedFacts: produced.map(codeFact).map((x) => x.replace(/ \.$/, '')),
839
- supportFacts: supportFacts.map(codeFact).map((x) => x.replace(/ \.$/, ''))
840
- });
841
- }
842
- }
843
- }
844
- if (!changed) break;
845
- if (!producedFactsCanAffectRules(producedPredicates, ruleDependencies)) break;
846
- if (iter === maxIterations - 1) throw new Error('Compiled derivation did not reach a fixpoint');
847
- }
848
- return { graph, trace };
849
- }
850
- function queryFacts(graph, queries, rules) {
851
- const out = [];
852
- const seen = new Set();
853
- for (const query of queries || []) {
854
- const matches = evalBody(query.premise || [], [{}], graph, rules);
855
- for (const env of matches) {
856
- for (const atom of query.conclusion || []) {
857
- const fact = { s: instantiate(atom.s, env, 'q' + query.id), p: instantiate(atom.p, env, 'q' + query.id), o: instantiate(atom.o, env, 'q' + query.id) };
858
- if (!atomIsGround(fact, {})) throw new Error('Unbound variable in log:query projection');
859
- const key = factKey(fact);
860
- if (!seen.has(key)) { seen.add(key); out.push(fact); }
861
- }
862
- }
863
- }
864
- return out;
865
- }
866
- function outputStrings(graph) {
867
- return graph.facts
868
- .filter((f) => isIri(f.p, 'log:outputString'))
869
- .sort((a, b) => display(a.s).localeCompare(display(b.s)))
870
- .map((f) => String(primitive(f.o)));
871
- }
872
- function initialKeys(initialFacts) {
873
- if (!initialFacts) return new Set();
874
- if (initialFacts.__seeKeySet) return initialFacts.__seeKeySet;
875
- const keys = new Set((initialFacts || []).map(factKey));
876
- try { Object.defineProperty(initialFacts, '__seeKeySet', { value: keys, enumerable: false }); } catch (_) {}
877
- return keys;
878
- }
879
- function derivedFacts(graph, initialFacts) {
880
- const base = initialKeys(initialFacts);
881
- return graph.facts.filter((f) => !base.has(factKey(f)));
882
- }
883
- function codeFact(f) { return atomToN3(f) + ' .'; }
884
- function unquoteLiteral(text) {
885
- const m = String(text || '').match(/^\"([\s\S]*)\"$/);
886
- if (!m) return String(text || '');
887
- return m[1].replace(/\\"/g, '"').replace(/\\n/g, '\n').replace(/\\\\/g, '\\');
888
- }
889
- function readablePredicateName(term) {
890
- return String(term || '')
891
- .replace(/^[:<#]*/, '')
892
- .replace(/[>#]*$/g, '')
893
- .replace(/^./, (ch) => ch.toLowerCase());
894
- }
895
- function compactValidationValue(value) {
896
- return String(value || '').replace(/^OK\s*-\s*/i, '').trim();
897
- }
898
- function compactSupportFactText(fact) {
899
- const text = String(fact || '')
900
- .replace(/===\s*Reason\s+Why\s*===/gi, '=== Explanation ===')
901
- .replace(/===\s*Review\s*===/gi, '=== Explanation ===');
902
- const report = text.match(/^(.*?\blog:outputString)\s+/);
903
- if (report) return report[1] + ' "[authored report]"';
904
-
905
- return text.replace(/\s{2,}/g, ' ').trim();
906
- }
907
- function compactRuleComment(comment) {
908
- return compactSupportFactText(comment);
909
- }
910
- function shortTerm(t) { return termToN3(t); }
911
- function factSentence(f) {
912
- const s = shortTerm(f.s), p = shortTerm(f.p), o = shortTerm(f.o);
913
- if (isIri(f.p, 'rdf:type')) return s + ' is a ' + o + '.';
914
- if (isIri(f.p, 'rdfs:subClassOf')) return s + ' is a subclass of ' + o + '.';
915
- if (isIri(f.p, ':is') || /(^|:)is$/.test(p)) return s + ' is ' + o + '.';
916
- return s + ' ' + p + ' ' + o + '.';
917
- }
918
- function previewItems(items, limit = 2) {
919
- const list = Array.isArray(items) ? items.filter(Boolean).map(compactRuleComment) : [];
920
- const shown = list.slice(0, limit).join('; ');
921
- const more = list.length > limit ? '; … +' + (list.length - limit) + ' more' : '';
922
- return shown + more;
923
- }
924
- function describeRule(rule) {
925
- if (!rule) return 'compiled rule';
926
- const body = rule.bodyComment || [];
927
- const head = rule.headComment || [];
928
- if (rule.kind === 'fuse') {
929
- const preview = previewItems(body, 2) || 'forbidden pattern';
930
- return 'Fuse ' + rule.id + ' guards against ' + preview;
931
- }
932
- const bodyCount = body.length;
933
- const headCount = head.length;
934
- if (bodyCount <= 2 && headCount <= 2) {
935
- return 'Rule ' + rule.id + ': ' + (previewItems(body, 2) || 'true') + ' => ' + (previewItems(head, 2) || 'false');
936
- }
937
- return 'Rule ' + rule.id + ' (' + bodyCount + ' premise pattern(s) => ' + headCount + ' conclusion pattern(s))';
938
- }
939
- function traceApplications(trace, rules, limit = 6) {
940
- const byId = new Map((rules || []).map((r) => [r.id, r]));
941
- const grouped = new Map();
942
- for (const step of trace || []) {
943
- const facts = Array.isArray(step.producedFacts) ? step.producedFacts : [];
944
- if (!facts.length) continue;
945
- const key = String(step.rule);
946
- if (!grouped.has(key)) grouped.set(key, { rule: byId.get(step.rule), facts: [], supportFacts: [] });
947
- const entry = grouped.get(key);
948
- for (const fact of facts) if (!entry.facts.includes(fact)) entry.facts.push(fact);
949
- for (const fact of Array.isArray(step.supportFacts) ? step.supportFacts : []) {
950
- if (!entry.supportFacts.includes(fact)) entry.supportFacts.push(fact);
951
- }
952
- }
953
- return Array.from(grouped.values()).slice(0, limit);
954
- }
955
- function supportIndex(trace) {
956
- const index = new Map();
957
- for (const step of trace || []) {
958
- const facts = Array.isArray(step.producedFacts) ? step.producedFacts : [];
959
- for (const fact of facts) {
960
- if (!index.has(fact)) {
961
- index.set(fact, { rule: step.rule, supportFacts: Array.isArray(step.supportFacts) ? step.supportFacts : [] });
962
- }
963
- }
964
- }
965
- return index;
966
- }
967
- function supportTreeLines(fact, supportMap, sourceKeys, ruleMap, opts = {}) {
968
- const lines = [];
969
- const maxDepth = opts.maxDepth ?? 4;
970
- const maxChildren = opts.maxChildren ?? 4;
971
- const maxLines = opts.maxLines ?? 40;
972
- function walk(item, depth, seen) {
973
- if (lines.length >= maxLines) return;
974
- const indent = ' '.repeat(depth);
975
- if (/\blog:outputString\b/.test(String(item || ''))) {
976
- const support = supportMap.get(item);
977
- const rule = support ? ruleMap.get(support.rule) : null;
978
- const label = support ? (rule ? 'Rule ' + rule.id : 'compiled rule #' + support.rule) : 'no recorded rule support';
979
- lines.push(indent + '- ' + compactSupportFactText(item) + ' . _(authored report, ' + label + ')_');
980
- return;
981
- }
982
- if (sourceKeys.has(item)) {
983
- lines.push(indent + '- ' + compactSupportFactText(item) + ' . _(source)_');
984
- return;
985
- }
986
- const support = supportMap.get(item);
987
- if (!support) {
988
- lines.push(indent + '- ' + compactSupportFactText(item) + ' . _(no recorded rule support)_');
989
- return;
990
- }
991
- const rule = ruleMap.get(support.rule);
992
- const label = rule ? 'Rule ' + rule.id : 'compiled rule #' + support.rule;
993
- lines.push(indent + '- ' + compactSupportFactText(item) + ' . _(derived by ' + label + ')_');
994
- if (depth >= maxDepth) {
995
- if (support.supportFacts && support.supportFacts.length) lines.push(indent + ' - support omitted beyond depth ' + maxDepth);
996
- return;
997
- }
998
- const children = (support.supportFacts || []).slice(0, maxChildren);
999
- const childIndent = indent + ' ';
1000
- if (!children.length) {
1001
- lines.push(childIndent + '- no graph premises; built-ins/constants satisfied the rule.');
1002
- return;
1003
- }
1004
- for (const child of children) {
1005
- if (seen.has(child)) {
1006
- lines.push(childIndent + '- ' + compactSupportFactText(child) + ' . _(already shown)_');
1007
- continue;
1008
- }
1009
- const nextSeen = new Set(seen);
1010
- nextSeen.add(child);
1011
- walk(child, depth + 1, nextSeen);
1012
- if (lines.length >= maxLines) break;
1013
- }
1014
- if ((support.supportFacts || []).length > children.length) {
1015
- lines.push(childIndent + '- ... ' + ((support.supportFacts || []).length - children.length) + ' more premise fact(s)');
1016
- }
1017
- }
1018
- walk(fact, 0, new Set([fact]));
1019
- if (lines.length >= maxLines) lines.push('- … support tree truncated after ' + maxLines + ' line(s)');
1020
- return lines;
1021
- }
1022
- function conclusionSupportSection(selected, trace, rules, initialFacts, limit = 6) {
1023
- const supportMap = supportIndex(trace);
1024
- const sourceKeys = new Set((initialFacts || []).map((f) => codeFact(f).replace(/ \.$/, '')));
1025
- const ruleMap = new Map((rules || []).map((r) => [r.id, r]));
1026
- const facts = [];
1027
- const seen = new Set();
1028
- for (const f of selected || []) {
1029
- const key = codeFact(f).replace(/ \.$/, '');
1030
- if (!seen.has(key)) { seen.add(key); facts.push(key); }
1031
- }
1032
- const focus = facts.slice(-limit).reverse();
1033
- if (!focus.length) return [];
1034
- const lines = [];
1035
- lines.push('Selected explanation support:');
1036
- for (const fact of focus) {
1037
- lines.push(...supportTreeLines(fact, supportMap, sourceKeys, ruleMap).map((line) => ' ' + line));
1038
- }
1039
- return lines;
1040
- }
1041
- function evidenceSummaryLine(mode) {
1042
- if (mode === 'query') return 'The query-selected facts are serialized in the Formal TriG Output section.';
1043
- if (mode === 'formula') return 'The formula-valued facts are serialized in the Formal TriG Output section.';
1044
- return 'The selected facts are serialized in the Formal TriG Output section.';
1045
- }
1046
- function renderStructuredOutput({ title, graph, queries = [], rules = [], initialFacts = [], trace = [], mode = 'derived' }) {
1047
- let selected = [];
1048
- if (mode === 'query') selected = queryFacts(graph, queries, rules);
1049
- else if (mode === 'formula') selected = graph.facts.filter((f) => f.o && f.o.kind === 'formula');
1050
- else selected = derivedFacts(graph, initialFacts);
1051
- if (!selected.length) selected = graph.facts.slice(0, 30);
1052
-
1053
- const derived = derivedFacts(graph, initialFacts);
1054
- const keyFact = selected[selected.length - 1];
1055
- const lines = [];
1056
- lines.push('# ' + title);
1057
- lines.push('');
1058
- lines.push('## Entailment');
1059
- if (mode === 'query') {
1060
- lines.push('The compiled query selected ' + selected.length + ' fact(s) after the rule closure was computed.');
1061
- } else if (mode === 'formula') {
1062
- lines.push('The derivation produced ' + selected.length + ' formula-valued entailment(s).');
1063
- } else {
1064
- lines.push('The derivation produced ' + derived.length + ' new fact(s) from ' + initialFacts.length + ' stated fact(s).');
1065
- }
1066
- if (keyFact) lines.push('Main entailment: **' + factSentence(keyFact) + '**');
1067
- const bullets = selected.slice(-6).reverse();
1068
- if (bullets.length) {
1069
- lines.push('');
1070
- lines.push('Selected entailments:');
1071
- for (const fact of bullets) lines.push('- ' + codeFact(fact));
1072
- }
1073
- lines.push('');
1074
- lines.push('## Explanation');
1075
- const ordinaryRules = (rules || []).filter((r) => r.kind !== 'fuse').length;
1076
- const fuses = (rules || []).filter((r) => r.kind === 'fuse').length;
1077
- lines.push('Starts with ' + initialFacts.length + ' source fact(s), applies ' + ordinaryRules + ' rule(s), and reaches a fixpoint.');
1078
- if (queries.length) lines.push('The log:query projection then keeps only the matching fact(s) shown above.');
1079
- if (fuses) lines.push('The run also validates ' + fuses + ' fuse(s) for forbidden patterns.');
1080
- const apps = traceApplications(trace, rules);
1081
- if (apps.length) {
1082
- lines.push('');
1083
- lines.push('Derivation steps:');
1084
- const sourceKeys = new Set((initialFacts || []).map((f) => codeFact(f).replace(/ \.$/, '')));
1085
- for (const app of apps) {
1086
- const produced = app.facts.slice(0, 4).map((f) => compactSupportFactText(f) + ' .').join(', ');
1087
- const more = app.facts.length > 4 ? ', … +' + (app.facts.length - 4) + ' more' : '';
1088
- lines.push('- ' + describeRule(app.rule) + ' derives ' + produced + more);
1089
- if (app.supportFacts && app.supportFacts.length) {
1090
- const used = app.supportFacts.slice(0, 4).map((fact) => compactSupportFactText(fact) + ' . ' + (sourceKeys.has(fact) ? '_(source)_' : '_(derived)_')).join('; ');
1091
- const omitted = app.supportFacts.length > 4 ? '; … +' + (app.supportFacts.length - 4) + ' more premise fact(s)' : '';
1092
- lines.push(' - Uses: ' + used + omitted);
1093
- } else {
1094
- lines.push(' - Uses: no graph premises; built-ins/constants satisfied the rule.');
1095
- }
1096
- }
1097
- }
1098
- const supportLines = conclusionSupportSection(selected, trace, rules, initialFacts);
1099
- if (supportLines.length) {
1100
- lines.push('');
1101
- lines.push(...supportLines);
1102
- }
1103
- lines.push('');
1104
- lines.push(evidenceSummaryLine(mode));
1105
- return lines.join('\n') + '\n';
1106
- }
1107
- function renderRawOutput(graph, queries = [], rules = [], initialFacts = []) {
1108
- const outs = outputStrings(graph);
1109
- if (outs.length) return outs.join('');
1110
- if (queries && queries.length) {
1111
- const selected = queryFacts(graph, queries, rules);
1112
- if (selected.length) return selected.map((f) => codeFact(f)).join('\n') + '\n';
1113
- return '';
1114
- }
1115
- const formulaFacts = graph.facts.filter((f) => f.o && f.o.kind === 'formula');
1116
- if (formulaFacts.length) return formulaFacts.map((f) => codeFact(f)).join('\n') + '\n';
1117
- const derived = derivedFacts(graph, initialFacts);
1118
- const selected = derived.length ? derived : graph.facts.slice(0, 30);
1119
- return selected.map((f) => codeFact(f)).join('\n') + '\n';
1120
- }
1121
- function dedupeExplanationHeadings(text) {
1122
- let seen = false;
1123
- return String(text || '').replace(/^##\s+Explanation\s*$/gmi, () => {
1124
- if (seen) return '';
1125
- seen = true;
1126
- return '## Explanation';
1127
- });
1128
- }
1129
- function normalizePublicReport(markdown, title) {
1130
- let text = String(markdown || '').trimEnd();
1131
- if (!/^\s*#\s+/m.test(text)) text = '# ' + title + '\n\n' + text;
1132
- if (!/^##\s+Entailment\s*$/mi.test(text)) {
1133
- text = text.replace(/^(#\s+[^\n]+\n*)/, '$1\n## Entailment\n');
1134
- }
1135
- if (!/^##\s+Explanation\s*$/mi.test(text)) {
1136
- text += '\n\n## Explanation\nNo additional explanation was provided by the generated output.';
1137
- }
1138
- text = text.replace(/^##\s+([^\n]+?)\s*$/gm, (line, heading) => {
1139
- const normalized = heading.trim().toLowerCase();
1140
- if (normalized === 'insight' || normalized === 'conclusion' || normalized === 'entailment' || normalized === 'explanation') return '## ' + (normalized === 'explanation' ? 'Explanation' : 'Entailment');
1141
- return '**' + heading.trim() + '**';
1142
- });
1143
- text = dedupeExplanationHeadings(text);
1144
- return text.trimEnd() + '\n';
1145
- }
1146
- function markdownize(raw, title) {
1147
- let text = String(raw || '');
1148
- text = text
1149
- .replace(/===\s*Answer\s*===/g, '## Entailment')
1150
- .replace(/===\s*Reason\s+Why\s*===/gi, '## Explanation')
1151
- .replace(/===\s*Explanation\s*===/gi, '## Explanation')
1152
- .replace(/===\s*([^=]+?)\s*===/g, (_, h) => '**' + h.trim() + '**');
1153
- text = text.replace(/^C(\d+)\s+OK\s*-\s*/gm, 'C$1: ');
1154
- text = dedupeExplanationHeadings(text);
1155
- if (!text.trim()) text = '## Entailment\nNo log:outputString facts were derived.\n\n## Explanation\nThe compiled derivation did not produce authored report text.';
1156
- return normalizePublicReport(text, title);
1157
- }
1158
- function authoredSupportAppendix(graph, queries, rules, initialFacts, trace) {
1159
- const derived = derivedFacts(graph, initialFacts);
1160
- let selected = [];
1161
- if (queries && queries.length) selected = queryFacts(graph, queries, rules);
1162
- else if (graph.facts.some((f) => f.o && f.o.kind === 'formula')) selected = graph.facts.filter((f) => f.o && f.o.kind === 'formula');
1163
- else selected = derived;
1164
- const lines = [];
1165
- const ordinaryRules = (rules || []).filter((r) => r.kind !== 'fuse').length;
1166
- const apps = traceApplications(trace, rules, 6);
1167
- const supportLines = conclusionSupportSection(selected, trace, rules, initialFacts, 4);
1168
- if (!apps.length && !supportLines.length) return '';
1169
- lines.push('**Generated derivation support**');
1170
- lines.push('');
1171
- lines.push('Compiled support: ' + initialFacts.length + ' source fact(s), ' + ordinaryRules + ' rule(s), fixpoint reached before rendering.');
1172
- if (apps.length) {
1173
- lines.push('');
1174
- lines.push('Derivation steps:');
1175
- const sourceKeys = new Set((initialFacts || []).map((f) => codeFact(f).replace(/ \.$/, '')));
1176
- for (const app of apps) {
1177
- const produced = app.facts.slice(0, 4).map((f) => compactSupportFactText(f) + ' .').join(', ');
1178
- const more = app.facts.length > 4 ? ', … +' + (app.facts.length - 4) + ' more' : '';
1179
- lines.push('- ' + describeRule(app.rule) + ' derives ' + produced + more);
1180
- if (app.supportFacts && app.supportFacts.length) {
1181
- const used = app.supportFacts.slice(0, 4).map((fact) => compactSupportFactText(fact) + ' . ' + (sourceKeys.has(fact) ? '_(source)_' : '_(derived)_')).join('; ');
1182
- const omitted = app.supportFacts.length > 4 ? '; … +' + (app.supportFacts.length - 4) + ' more premise fact(s)' : '';
1183
- lines.push(' - Uses: ' + used + omitted);
1184
- } else {
1185
- lines.push(' - Uses: no graph premises; built-ins/constants satisfied the rule.');
1186
- }
1187
- }
1188
- }
1189
- if (supportLines.length) {
1190
- lines.push('');
1191
- lines.push(...supportLines);
1192
- }
1193
- return lines.join('\n');
1194
- }
1195
- function appendAuthoredExplanation(markdown, graph, queries, rules, initialFacts, trace) {
1196
- const appendix = authoredSupportAppendix(graph, queries, rules, initialFacts, trace);
1197
- if (!appendix) return markdown;
1198
- return markdown.trimEnd() + '\n\n' + appendix + '\n';
1199
- }
1200
- function renderPresentation(graph, queries, rules, initialFacts, title, trace) {
1201
- const outs = outputStrings(graph);
1202
- if (outs.length) return appendAuthoredExplanation(markdownize(outs.join(''), title), graph, queries, rules, initialFacts, trace);
1203
- if (queries && queries.length) return renderStructuredOutput({ title, graph, queries, rules, initialFacts, trace, mode: 'query' });
1204
- if (graph.facts.some((f) => f.o && f.o.kind === 'formula')) return renderStructuredOutput({ title, graph, queries, rules, initialFacts, trace, mode: 'formula' });
1205
- return renderStructuredOutput({ title, graph, queries, rules, initialFacts, trace, mode: 'derived' });
1206
- }
1207
-
1208
- const NAME = "dijkstra";
1209
- const TITLE = "Dijkstra's algorithm to find the shortest path";
1210
- const EXPECTED_INPUT_FACTS = 9;
1211
- const RULES = [
1212
- {
1213
- "kind": "rule",
1214
- "id": 1,
1215
- "body": [
1216
- {
1217
- "s": {
1218
- "kind": "list",
1219
- "items": [
1220
- {
1221
- "kind": "var",
1222
- "value": "A"
1223
- },
1224
- {
1225
- "kind": "var",
1226
- "value": "B"
1227
- }
1228
- ]
1229
- },
1230
- "p": {
1231
- "kind": "iri",
1232
- "value": ":edge"
1233
- },
1234
- "o": {
1235
- "kind": "var",
1236
- "value": "C"
1237
- }
1238
- }
1239
- ],
1240
- "head": [
1241
- {
1242
- "s": {
1243
- "kind": "list",
1244
- "items": [
1245
- {
1246
- "kind": "var",
1247
- "value": "B"
1248
- },
1249
- {
1250
- "kind": "var",
1251
- "value": "A"
1252
- }
1253
- ]
1254
- },
1255
- "p": {
1256
- "kind": "iri",
1257
- "value": ":edge"
1258
- },
1259
- "o": {
1260
- "kind": "var",
1261
- "value": "C"
1262
- }
1263
- }
1264
- ],
1265
- "bodyComment": [
1266
- "(?A ?B) :edge ?C"
1267
- ],
1268
- "headComment": [
1269
- "(?B ?A) :edge ?C"
1270
- ]
1271
- },
1272
- {
1273
- "kind": "backward",
1274
- "id": 2,
1275
- "body": [
1276
- {
1277
- "s": {
1278
- "kind": "list",
1279
- "items": [
1280
- {
1281
- "kind": "list",
1282
- "items": [
1283
- {
1284
- "kind": "list",
1285
- "items": [
1286
- {
1287
- "kind": "lit",
1288
- "value": 0
1289
- },
1290
- {
1291
- "kind": "var",
1292
- "value": "Start"
1293
- }
1294
- ]
1295
- }
1296
- ]
1297
- },
1298
- {
1299
- "kind": "var",
1300
- "value": "Goal"
1301
- },
1302
- {
1303
- "kind": "list",
1304
- "items": []
1305
- }
1306
- ]
1307
- },
1308
- "p": {
1309
- "kind": "iri",
1310
- "value": ":dijkstra2"
1311
- },
1312
- "o": {
1313
- "kind": "list",
1314
- "items": [
1315
- {
1316
- "kind": "var",
1317
- "value": "RevPath"
1318
- },
1319
- {
1320
- "kind": "var",
1321
- "value": "Cost"
1322
- }
1323
- ]
1324
- }
1325
- },
1326
- {
1327
- "s": {
1328
- "kind": "var",
1329
- "value": "RevPath"
1330
- },
1331
- "p": {
1332
- "kind": "iri",
1333
- "value": "list:reverse"
1334
- },
1335
- "o": {
1336
- "kind": "var",
1337
- "value": "Path"
1338
- }
1339
- }
1340
- ],
1341
- "head": [
1342
- {
1343
- "s": {
1344
- "kind": "list",
1345
- "items": [
1346
- {
1347
- "kind": "var",
1348
- "value": "Start"
1349
- },
1350
- {
1351
- "kind": "var",
1352
- "value": "Goal"
1353
- }
1354
- ]
1355
- },
1356
- "p": {
1357
- "kind": "iri",
1358
- "value": ":dijkstra"
1359
- },
1360
- "o": {
1361
- "kind": "list",
1362
- "items": [
1363
- {
1364
- "kind": "var",
1365
- "value": "Path"
1366
- },
1367
- {
1368
- "kind": "var",
1369
- "value": "Cost"
1370
- }
1371
- ]
1372
- }
1373
- }
1374
- ],
1375
- "bodyComment": [
1376
- "(((0 ?Start)) ?Goal ()) :dijkstra2 (?RevPath ?Cost)",
1377
- "?RevPath list:reverse ?Path"
1378
- ],
1379
- "headComment": [
1380
- "(?Start ?Goal) :dijkstra (?Path ?Cost)"
1381
- ]
1382
- },
1383
- {
1384
- "kind": "backward",
1385
- "id": 3,
1386
- "body": [
1387
- {
1388
- "s": {
1389
- "kind": "var",
1390
- "value": "L1"
1391
- },
1392
- "p": {
1393
- "kind": "iri",
1394
- "value": "list:firstRest"
1395
- },
1396
- "o": {
1397
- "kind": "list",
1398
- "items": [
1399
- {
1400
- "kind": "var",
1401
- "value": "L4"
1402
- },
1403
- {
1404
- "kind": "var",
1405
- "value": "L5"
1406
- }
1407
- ]
1408
- }
1409
- },
1410
- {
1411
- "s": {
1412
- "kind": "var",
1413
- "value": "L4"
1414
- },
1415
- "p": {
1416
- "kind": "iri",
1417
- "value": "list:firstRest"
1418
- },
1419
- "o": {
1420
- "kind": "list",
1421
- "items": [
1422
- {
1423
- "kind": "var",
1424
- "value": "Cost"
1425
- },
1426
- {
1427
- "kind": "var",
1428
- "value": "L3"
1429
- }
1430
- ]
1431
- }
1432
- },
1433
- {
1434
- "s": {
1435
- "kind": "var",
1436
- "value": "L3"
1437
- },
1438
- "p": {
1439
- "kind": "iri",
1440
- "value": "list:firstRest"
1441
- },
1442
- "o": {
1443
- "kind": "list",
1444
- "items": [
1445
- {
1446
- "kind": "var",
1447
- "value": "Goal"
1448
- },
1449
- {
1450
- "kind": "var",
1451
- "value": "Path"
1452
- }
1453
- ]
1454
- }
1455
- }
1456
- ],
1457
- "head": [
1458
- {
1459
- "s": {
1460
- "kind": "list",
1461
- "items": [
1462
- {
1463
- "kind": "var",
1464
- "value": "L1"
1465
- },
1466
- {
1467
- "kind": "var",
1468
- "value": "Goal"
1469
- },
1470
- {
1471
- "kind": "var",
1472
- "value": "L2"
1473
- }
1474
- ]
1475
- },
1476
- "p": {
1477
- "kind": "iri",
1478
- "value": ":dijkstra2"
1479
- },
1480
- "o": {
1481
- "kind": "list",
1482
- "items": [
1483
- {
1484
- "kind": "var",
1485
- "value": "L3"
1486
- },
1487
- {
1488
- "kind": "var",
1489
- "value": "Cost"
1490
- }
1491
- ]
1492
- }
1493
- }
1494
- ],
1495
- "bodyComment": [
1496
- "?L1 list:firstRest (?L4 ?L5)",
1497
- "?L4 list:firstRest (?Cost ?L3)",
1498
- "?L3 list:firstRest (?Goal ?Path)"
1499
- ],
1500
- "headComment": [
1501
- "(?L1 ?Goal ?L2) :dijkstra2 (?L3 ?Cost)"
1502
- ]
1503
- },
1504
- {
1505
- "kind": "backward",
1506
- "id": 4,
1507
- "body": [
1508
- {
1509
- "s": {
1510
- "kind": "var",
1511
- "value": "L1"
1512
- },
1513
- "p": {
1514
- "kind": "iri",
1515
- "value": "list:firstRest"
1516
- },
1517
- "o": {
1518
- "kind": "list",
1519
- "items": [
1520
- {
1521
- "kind": "var",
1522
- "value": "L2"
1523
- },
1524
- {
1525
- "kind": "var",
1526
- "value": "Queue"
1527
- }
1528
- ]
1529
- }
1530
- },
1531
- {
1532
- "s": {
1533
- "kind": "var",
1534
- "value": "L2"
1535
- },
1536
- "p": {
1537
- "kind": "iri",
1538
- "value": "list:firstRest"
1539
- },
1540
- "o": {
1541
- "kind": "list",
1542
- "items": [
1543
- {
1544
- "kind": "var",
1545
- "value": "Cost"
1546
- },
1547
- {
1548
- "kind": "var",
1549
- "value": "L3"
1550
- }
1551
- ]
1552
- }
1553
- },
1554
- {
1555
- "s": {
1556
- "kind": "var",
1557
- "value": "L3"
1558
- },
1559
- "p": {
1560
- "kind": "iri",
1561
- "value": "list:firstRest"
1562
- },
1563
- "o": {
1564
- "kind": "list",
1565
- "items": [
1566
- {
1567
- "kind": "var",
1568
- "value": "Node"
1569
- },
1570
- {
1571
- "kind": "var",
1572
- "value": "Path"
1573
- }
1574
- ]
1575
- }
1576
- },
1577
- {
1578
- "s": {
1579
- "kind": "list",
1580
- "items": [
1581
- {
1582
- "kind": "var",
1583
- "value": "L4"
1584
- },
1585
- {
1586
- "kind": "formula",
1587
- "atoms": [
1588
- {
1589
- "s": {
1590
- "kind": "var",
1591
- "value": "L4"
1592
- },
1593
- "p": {
1594
- "kind": "iri",
1595
- "value": "list:firstRest"
1596
- },
1597
- "o": {
1598
- "kind": "list",
1599
- "items": [
1600
- {
1601
- "kind": "var",
1602
- "value": "NewCost"
1603
- },
1604
- {
1605
- "kind": "var",
1606
- "value": "L5"
1607
- }
1608
- ]
1609
- }
1610
- },
1611
- {
1612
- "s": {
1613
- "kind": "var",
1614
- "value": "L5"
1615
- },
1616
- "p": {
1617
- "kind": "iri",
1618
- "value": "list:firstRest"
1619
- },
1620
- "o": {
1621
- "kind": "list",
1622
- "items": [
1623
- {
1624
- "kind": "var",
1625
- "value": "Neighbor"
1626
- },
1627
- {
1628
- "kind": "var",
1629
- "value": "L6"
1630
- }
1631
- ]
1632
- }
1633
- },
1634
- {
1635
- "s": {
1636
- "kind": "var",
1637
- "value": "L6"
1638
- },
1639
- "p": {
1640
- "kind": "iri",
1641
- "value": "list:firstRest"
1642
- },
1643
- "o": {
1644
- "kind": "list",
1645
- "items": [
1646
- {
1647
- "kind": "var",
1648
- "value": "Node"
1649
- },
1650
- {
1651
- "kind": "var",
1652
- "value": "Path"
1653
- }
1654
- ]
1655
- }
1656
- },
1657
- {
1658
- "s": {
1659
- "kind": "list",
1660
- "items": [
1661
- {
1662
- "kind": "var",
1663
- "value": "Node"
1664
- },
1665
- {
1666
- "kind": "var",
1667
- "value": "Neighbor"
1668
- }
1669
- ]
1670
- },
1671
- "p": {
1672
- "kind": "iri",
1673
- "value": ":edge"
1674
- },
1675
- "o": {
1676
- "kind": "var",
1677
- "value": "Weight"
1678
- }
1679
- },
1680
- {
1681
- "s": {
1682
- "kind": "var",
1683
- "value": "Visited"
1684
- },
1685
- "p": {
1686
- "kind": "iri",
1687
- "value": "list:notMember"
1688
- },
1689
- "o": {
1690
- "kind": "var",
1691
- "value": "Neighbor"
1692
- }
1693
- },
1694
- {
1695
- "s": {
1696
- "kind": "list",
1697
- "items": [
1698
- {
1699
- "kind": "var",
1700
- "value": "Cost"
1701
- },
1702
- {
1703
- "kind": "var",
1704
- "value": "Weight"
1705
- }
1706
- ]
1707
- },
1708
- "p": {
1709
- "kind": "iri",
1710
- "value": "math:sum"
1711
- },
1712
- "o": {
1713
- "kind": "var",
1714
- "value": "NewCost"
1715
- }
1716
- }
1717
- ]
1718
- },
1719
- {
1720
- "kind": "var",
1721
- "value": "Neighbors"
1722
- }
1723
- ]
1724
- },
1725
- "p": {
1726
- "kind": "iri",
1727
- "value": "log:collectAllIn"
1728
- },
1729
- "o": {
1730
- "kind": "var",
1731
- "value": "SCOPE"
1732
- }
1733
- },
1734
- {
1735
- "s": {
1736
- "kind": "list",
1737
- "items": [
1738
- {
1739
- "kind": "var",
1740
- "value": "Queue"
1741
- },
1742
- {
1743
- "kind": "var",
1744
- "value": "Neighbors"
1745
- }
1746
- ]
1747
- },
1748
- "p": {
1749
- "kind": "iri",
1750
- "value": "list:append"
1751
- },
1752
- "o": {
1753
- "kind": "var",
1754
- "value": "NewQueue"
1755
- }
1756
- },
1757
- {
1758
- "s": {
1759
- "kind": "var",
1760
- "value": "NewQueue"
1761
- },
1762
- "p": {
1763
- "kind": "iri",
1764
- "value": "list:sort"
1765
- },
1766
- "o": {
1767
- "kind": "var",
1768
- "value": "SortedQueue"
1769
- }
1770
- },
1771
- {
1772
- "s": {
1773
- "kind": "var",
1774
- "value": "L7"
1775
- },
1776
- "p": {
1777
- "kind": "iri",
1778
- "value": "list:firstRest"
1779
- },
1780
- "o": {
1781
- "kind": "list",
1782
- "items": [
1783
- {
1784
- "kind": "var",
1785
- "value": "Node"
1786
- },
1787
- {
1788
- "kind": "var",
1789
- "value": "Visited"
1790
- }
1791
- ]
1792
- }
1793
- },
1794
- {
1795
- "s": {
1796
- "kind": "list",
1797
- "items": [
1798
- {
1799
- "kind": "var",
1800
- "value": "SortedQueue"
1801
- },
1802
- {
1803
- "kind": "var",
1804
- "value": "Goal"
1805
- },
1806
- {
1807
- "kind": "var",
1808
- "value": "L7"
1809
- }
1810
- ]
1811
- },
1812
- "p": {
1813
- "kind": "iri",
1814
- "value": ":dijkstra2"
1815
- },
1816
- "o": {
1817
- "kind": "list",
1818
- "items": [
1819
- {
1820
- "kind": "var",
1821
- "value": "ResultPath"
1822
- },
1823
- {
1824
- "kind": "var",
1825
- "value": "ResultCost"
1826
- }
1827
- ]
1828
- }
1829
- }
1830
- ],
1831
- "head": [
1832
- {
1833
- "s": {
1834
- "kind": "list",
1835
- "items": [
1836
- {
1837
- "kind": "var",
1838
- "value": "L1"
1839
- },
1840
- {
1841
- "kind": "var",
1842
- "value": "Goal"
1843
- },
1844
- {
1845
- "kind": "var",
1846
- "value": "Visited"
1847
- }
1848
- ]
1849
- },
1850
- "p": {
1851
- "kind": "iri",
1852
- "value": ":dijkstra2"
1853
- },
1854
- "o": {
1855
- "kind": "list",
1856
- "items": [
1857
- {
1858
- "kind": "var",
1859
- "value": "ResultPath"
1860
- },
1861
- {
1862
- "kind": "var",
1863
- "value": "ResultCost"
1864
- }
1865
- ]
1866
- }
1867
- }
1868
- ],
1869
- "bodyComment": [
1870
- "?L1 list:firstRest (?L2 ?Queue)",
1871
- "?L2 list:firstRest (?Cost ?L3)",
1872
- "?L3 list:firstRest (?Node ?Path)",
1873
- "(?L4 { ?L4 list:firstRest (?NewCost ?L5) . ?L5 list:firstRest (?Neighbor ?L6) . ?L6 list:firstRest (?Node ?Path) . (?Node ?Neighbor) :edge ?Weight . ?Visited list:notMember ?Neighbor . (?Cost ?Weight) math:sum ?NewCost } ?Neighbors) log:collectAllIn ?SCOPE",
1874
- "(?Queue ?Neighbors) list:append ?NewQueue",
1875
- "?NewQueue list:sort ?SortedQueue",
1876
- "?L7 list:firstRest (?Node ?Visited)",
1877
- "(?SortedQueue ?Goal ?L7) :dijkstra2 (?ResultPath ?ResultCost)"
1878
- ],
1879
- "headComment": [
1880
- "(?L1 ?Goal ?Visited) :dijkstra2 (?ResultPath ?ResultCost)"
1881
- ]
1882
- },
1883
- {
1884
- "kind": "rule",
1885
- "id": 5,
1886
- "body": [
1887
- {
1888
- "s": {
1889
- "kind": "list",
1890
- "items": [
1891
- {
1892
- "kind": "iri",
1893
- "value": ":a"
1894
- },
1895
- {
1896
- "kind": "iri",
1897
- "value": ":f"
1898
- }
1899
- ]
1900
- },
1901
- "p": {
1902
- "kind": "iri",
1903
- "value": ":dijkstra"
1904
- },
1905
- "o": {
1906
- "kind": "list",
1907
- "items": [
1908
- {
1909
- "kind": "var",
1910
- "value": "Path"
1911
- },
1912
- {
1913
- "kind": "var",
1914
- "value": "Cost"
1915
- }
1916
- ]
1917
- }
1918
- }
1919
- ],
1920
- "head": [
1921
- {
1922
- "s": {
1923
- "kind": "list",
1924
- "items": [
1925
- {
1926
- "kind": "iri",
1927
- "value": ":a"
1928
- },
1929
- {
1930
- "kind": "iri",
1931
- "value": ":f"
1932
- }
1933
- ]
1934
- },
1935
- "p": {
1936
- "kind": "iri",
1937
- "value": ":path"
1938
- },
1939
- "o": {
1940
- "kind": "list",
1941
- "items": [
1942
- {
1943
- "kind": "var",
1944
- "value": "Path"
1945
- },
1946
- {
1947
- "kind": "var",
1948
- "value": "Cost"
1949
- }
1950
- ]
1951
- }
1952
- }
1953
- ],
1954
- "bodyComment": [
1955
- "(:a :f) :dijkstra (?Path ?Cost)"
1956
- ],
1957
- "headComment": [
1958
- "(:a :f) :path (?Path ?Cost)"
1959
- ]
1960
- }
1961
- ];
1962
- const QUERIES = [];
1963
- const DOC_MARKDOWN = "# Dijkstra's algorithm to find the shortest path\n\nGenerated by `see.js` from a Notation3 source file.\n\n## Compilation summary\n\n- Example name: `dijkstra`\n- Input facts emitted: 9\n- Forward rules compiled: 2\n- Backward predicate rules compiled: 3\n- Fuses compiled: 0\n- Predicate count: 9\n\n## Built-ins used\n\n- `list:append`\n- `list:firstRest`\n- `list:reverse`\n- `list:sort`\n- `log:collectAllIn`\n\n## Runtime model\n\nThe generated `examples/dijkstra.js` is a specialized JavaScript derivation program. For ordinary sources, `see.js` emits the source facts as `examples/input/dijkstra.trig`. For rules-only sources, generation can reuse an existing external evidence file such as `examples/input/dijkstra.trig` or `examples/input/dijkstra.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.\n\n## Output model\n\nRunning `node examples/dijkstra.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.\n";
1964
- function seeMetadata(data) { return (data && data.__see) || {}; }
1965
- function trustedDerivation(data) { const meta = seeMetadata(data); const facts = data && Array.isArray(data.facts) ? data.facts : []; const expectedFacts = EXPECTED_INPUT_FACTS || Number(meta.InputFacts || 0); if (meta.SourceSHA256 && meta.SourceSHA256 !== "e877c1cfea99078228bd424f6e55761e56cc54bc1e64b21540231620cc3620ab") throw new Error('input evidence does not match the N3 source compiled into this example'); const result = saturate(facts, RULES); const rawOutput = renderRawOutput(result.graph, QUERIES, RULES, facts); fail('Compiled N3 derivation failed', { 'input evidence metadata is present and matches compiled source': meta.SourceSHA256 === "e877c1cfea99078228bd424f6e55761e56cc54bc1e64b21540231620cc3620ab", 'input evidence facts were loaded': expectedFacts > 0 ? facts.length === expectedFacts : facts.length >= 0, 'compiled rules were loaded': RULES.length === 5, 'compiled query directives were loaded': QUERIES.length === 0, 'a derivation fixpoint was reached': result.graph.facts.length >= facts.length, 'query or output facts were produced': rawOutput.length > 0 }); return { ...result, rawOutput, inputFacts: facts }; }
1966
- function snapshotMarkdown(markdown) { return markdown.split(/\n/).map((line) => line ? line + ' \n' : '\n').join(''); }
1967
- function prefixLinesFromTrig(trig) {
1968
- const out = [];
1969
- const seen = new Set();
1970
- for (const rawLine of String(trig || '').split(String.fromCharCode(10))) {
1971
- const trimmed = rawLine.replace(String.fromCharCode(13), '').trim();
1972
- if (!trimmed.toLowerCase().startsWith('@prefix ')) continue;
1973
- if (!seen.has(trimmed)) { seen.add(trimmed); out.push(trimmed); }
1974
- }
1975
- if (!out.some((line) => line.toLowerCase().startsWith('@prefix rdf:'))) out.push('@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .');
1976
- return out;
1977
- }
1978
- function formalOutputFacts(graph, queries, rules, initialFacts) {
1979
- const base = initialKeys(initialFacts);
1980
- const out = [];
1981
- const seen = new Set();
1982
- const add = (fact) => {
1983
- if (!fact) return;
1984
- const key = factKey(fact);
1985
- if (!seen.has(key)) { seen.add(key); out.push(fact); }
1986
- };
1987
- if (queries && queries.length) {
1988
- for (const fact of queryFacts(graph, queries, rules)) add(fact);
1989
- return out;
1990
- }
1991
- for (const fact of derivedFacts(graph, initialFacts)) add(fact);
1992
- if (!out.length) {
1993
- for (const fact of graph.facts) if (fact.o && fact.o.kind === 'formula' && !base.has(factKey(fact))) add(fact);
1994
- }
1995
- return out;
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 || '')); }
2007
- function trigGraphBlock(label, atoms) {
2008
- const lines = [label + ' {'];
2009
- for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
2010
- lines.push('}');
2011
- return lines;
2012
- }
2013
- function formalFactToTrigLines(fact, state) {
2014
- if (fact.o && fact.o.kind === 'formula') {
2015
- if (isIri(fact.p, 'log:nameOf')) return trigGraphBlock(termToN3(fact.s), fact.o.atoms);
2016
- state.formulaCounter += 1;
2017
- state.needOutPrefix = true;
2018
- const label = 'out:formula' + state.formulaCounter;
2019
- return [termToN3(fact.s) + ' ' + termToN3(fact.p) + ' ' + label + ' .', '', ...trigGraphBlock(label, fact.o.atoms)];
2020
- }
2021
- return [codeFact(fact)];
2022
- }
2023
- function trigMetadataBlock(trig) {
2024
- const lines = String(trig || '').split(String.fromCharCode(10));
2025
- const out = [];
2026
- let depth = 0;
2027
- let active = false;
2028
- for (const line of lines) {
2029
- const trimmed = line.trim();
2030
- if (!active && !trimmed.startsWith('in:metadata')) continue;
2031
- active = true;
2032
- out.push(line.replace(String.fromCharCode(13), ''));
2033
- depth += (line.match(/{/g) || []).length;
2034
- depth -= (line.match(/}/g) || []).length;
2035
- if (active && depth <= 0) break;
2036
- }
2037
- return out.length ? out.join(String.fromCharCode(10)).trimEnd() : '';
2038
- }
2039
- function formalOutputToTrig(facts, trig) {
2040
- const state = { formulaCounter: 0, needOutPrefix: false };
2041
- const body = [];
2042
- for (const fact of facts || []) {
2043
- body.push(...formalFactToTrigLines(fact, state));
2044
- }
2045
- const metadata = trigMetadataBlock(trig);
2046
- if (metadata) {
2047
- if (body.length) body.push('');
2048
- body.push(metadata);
2049
- }
2050
- if (!body.length) return '';
2051
- const prefixes = prefixLinesFromTrig(trig);
2052
- if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
2053
- const nl = String.fromCharCode(10);
2054
- const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
2055
- return version + prefixes.join(nl) + nl + nl + body.join(nl);
2056
- }
2057
- function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
2058
- const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);
2059
- if (!trig) return markdown;
2060
- const nl = String.fromCharCode(10);
2061
- const fence = String.fromCharCode(96).repeat(3);
2062
- const fenced = trig.trimEnd().replace(new RegExp(fence, 'g'), '` ` `');
2063
- return markdown.trimEnd() + nl + nl + '## Formal TriG Output' + nl + nl + fence + 'trig' + nl + fenced + nl + fence + nl;
2064
- }
2065
- function outputMarkdown() { const data = loadInput(NAME); const result = trustedDerivation(data); const markdown = renderPresentation(result.graph, QUERIES, RULES, result.inputFacts, TITLE, result.trace); return snapshotMarkdown(appendFormalTrigOutput(markdown, result.graph, QUERIES, RULES, result.inputFacts, data)); }
2066
- function documentationMarkdown() { return DOC_MARKDOWN; }
2067
- function writeArtefacts() { const outputDir = path.join(__dirname, 'output'); const docDir = path.join(__dirname, 'doc'); fs.mkdirSync(outputDir, { recursive: true }); fs.mkdirSync(docDir, { recursive: true }); fs.writeFileSync(path.join(outputDir, NAME + '.md'), outputMarkdown(), 'utf8'); fs.writeFileSync(path.join(docDir, NAME + '.md'), documentationMarkdown(), 'utf8'); }
2068
- function main(argv = process.argv.slice(2)) { if (argv.includes('--write') || argv.includes('--write-files') || argv.includes('--snapshot')) { writeArtefacts(); return; } if (argv.includes('--doc')) { process.stdout.write(documentationMarkdown()); return; } process.stdout.write(outputMarkdown()); }
2069
- if (require.main === module) main();
2070
- module.exports = { trustedDerivation, outputMarkdown, documentationMarkdown, writeArtefacts };