eyelang 1.3.8 → 1.4.0

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 (219) hide show
  1. package/README.md +19 -14
  2. package/SPEC.md +4 -4
  3. package/conformance/expected/core/001_fact_query.out +0 -8
  4. package/conformance/expected/core/002_rule_recursion.out +0 -41
  5. package/conformance/expected/core/003_terms_and_readback.out +0 -104
  6. package/conformance/expected/core/004_conjunction_and_parentheses.out +0 -25
  7. package/conformance/expected/core/005_list_deconstruction.out +0 -32
  8. package/conformance/expected/core/006_comma_formula_data.out +0 -15
  9. package/conformance/expected/core/007_anonymous_variables.out +0 -19
  10. package/conformance/expected/core/008_graphic_atoms.out +0 -24
  11. package/conformance/expected/core/009_comments_and_whitespace.out +0 -30
  12. package/conformance/expected/core/010_variable_scope_and_reuse.out +0 -52
  13. package/conformance/expected/core/011_predicate_arity.out +0 -30
  14. package/conformance/expected/core/012_nested_compound_unification.out +0 -30
  15. package/conformance/expected/core/013_multiple_clauses_order.out +0 -44
  16. package/conformance/expected/core/014_failure_filters_answers.out +0 -19
  17. package/conformance/expected/core/015_improper_list_unification.out +0 -45
  18. package/conformance/expected/core/016_zero_arity_compound.out +0 -15
  19. package/conformance/expected/core/017_three_step_recursion.out +0 -99
  20. package/conformance/expected/core/018_quoted_atom_readback.out +0 -45
  21. package/conformance/expected/core/019_parenthesized_three_conjuncts.out +0 -36
  22. package/conformance/expected/core/020_nested_list_terms.out +0 -30
  23. package/conformance/expected/extension/001_default_derived_output.out +0 -56
  24. package/conformance/expected/extension/002_materialize_focus.out +0 -22
  25. package/conformance/expected/extension/003_arithmetic_and_comparison.out +0 -148
  26. package/conformance/expected/extension/004_strings_and_atoms.out +0 -58
  27. package/conformance/expected/extension/005_lists_aggregation_ordering.out +0 -135
  28. package/conformance/expected/extension/006_formula_terms.out +0 -76
  29. package/conformance/expected/extension/007_negation_once_generators.out +0 -40
  30. package/conformance/expected/extension/008_equality_and_inequality.out +0 -58
  31. package/conformance/expected/extension/009_list_relations.out +0 -73
  32. package/conformance/expected/extension/010_append_splits.out +0 -45
  33. package/conformance/expected/extension/011_matching_and_comparison.out +0 -70
  34. package/conformance/expected/extension/012_memoize_declaration.out +0 -41
  35. package/conformance/expected/extension/013_numeric_functions.out +0 -105
  36. package/conformance/expected/extension/014_between_enumeration.out +0 -45
  37. package/conformance/expected/extension/015_smallest_divisor.out +0 -15
  38. package/conformance/expected/extension/016_negation_filter.out +0 -38
  39. package/conformance/expected/extension/017_once_user_predicate.out +0 -21
  40. package/conformance/expected/extension/018_findall_user_goal.out +0 -15
  41. package/conformance/expected/extension/019_sort_deduplicates_atoms.out +0 -15
  42. package/conformance/expected/extension/020_append_bound_prefix_suffix.out +0 -30
  43. package/conformance/expected/extension/021_nth0_index_generation.out +0 -15
  44. package/conformance/expected/extension/022_set_nth0_edges.out +0 -30
  45. package/conformance/expected/extension/023_select_duplicate_occurrences.out +0 -30
  46. package/conformance/expected/extension/024_not_member_filter.out +0 -19
  47. package/conformance/expected/extension/025_is_list_filter.out +0 -19
  48. package/conformance/expected/extension/026_nested_formula_terms.out +0 -114
  49. package/conformance/expected/extension/027_materialize_excludes_source_fact.out +0 -14
  50. package/conformance/expected/extension/028_numeric_and_lexical_comparison.out +0 -42
  51. package/conformance/expected/extension/029_string_matching_filters.out +0 -38
  52. package/conformance/expected/extension/030_string_and_atom_concat.out +0 -30
  53. package/conformance/expected/extension/031_countall_empty_and_nonempty.out +0 -18
  54. package/conformance/expected/extension/032_sumall_numeric_template.out +0 -8
  55. package/conformance/expected/extension/033_aggregate_min_template.out +0 -8
  56. package/conformance/expected/extension/034_aggregate_max_compound_key.out +0 -8
  57. package/examples/output/access-control-policy.pl +0 -156
  58. package/examples/output/ackermann.pl +0 -1240
  59. package/examples/output/age.pl +0 -69
  60. package/examples/output/aliases-and-namespaces.pl +0 -73
  61. package/examples/output/alignment-demo.pl +0 -862
  62. package/examples/output/allen-interval-calculus.pl +0 -9169
  63. package/examples/output/ancestor.pl +0 -134
  64. package/examples/output/animal.pl +0 -64
  65. package/examples/output/annotation.pl +0 -76
  66. package/examples/output/backward.pl +0 -21
  67. package/examples/output/basic-monadic.pl +0 -22770
  68. package/examples/output/bayes-diagnosis.pl +0 -195
  69. package/examples/output/bayes-therapy.pl +0 -34873
  70. package/examples/output/beam-deflection.pl +0 -222
  71. package/examples/output/blocks-world-planning.pl +0 -1339
  72. package/examples/output/bmi.pl +0 -1903
  73. package/examples/output/braking-safety-worlds.pl +0 -2452
  74. package/examples/output/buck-converter-design.pl +0 -663
  75. package/examples/output/cache-performance.pl +0 -306
  76. package/examples/output/canary-release.pl +0 -167
  77. package/examples/output/cat-koko.pl +0 -83
  78. package/examples/output/clinical-trial-screening.pl +0 -380
  79. package/examples/output/collatz-1000.pl +0 -19000
  80. package/examples/output/combinatorics-findall-sort.pl +0 -56
  81. package/examples/output/competitive-enzyme-kinetics.pl +0 -563
  82. package/examples/output/complex-matrix-stability.pl +0 -250
  83. package/examples/output/complex.pl +0 -653
  84. package/examples/output/composition-of-injective-functions-is-injective.pl +0 -256
  85. package/examples/output/context-association.pl +0 -75
  86. package/examples/output/control-system.pl +0 -328
  87. package/examples/output/cryptarithmetic-send-more-money.pl +0 -168
  88. package/examples/output/cyclic-path.pl +0 -504
  89. package/examples/output/d3-group.pl +0 -56
  90. package/examples/output/dairy-energy-balance.pl +0 -1034
  91. package/examples/output/data-negotiation.pl +0 -75
  92. package/examples/output/deep-taxonomy-10.pl +0 -1235
  93. package/examples/output/deep-taxonomy-100.pl +0 -1235
  94. package/examples/output/deep-taxonomy-1000.pl +0 -1235
  95. package/examples/output/deep-taxonomy-10000.pl +0 -2915
  96. package/examples/output/deep-taxonomy-100000.pl +0 -20240
  97. package/examples/output/delfour.pl +0 -2829
  98. package/examples/output/dense-hamiltonian-cycle.pl +0 -207
  99. package/examples/output/deontic-logic.pl +0 -105
  100. package/examples/output/derived-rule.pl +0 -41
  101. package/examples/output/diamond-property.pl +0 -303
  102. package/examples/output/dijkstra-findall-sort.pl +0 -484
  103. package/examples/output/dijkstra-risk-path.pl +0 -4841
  104. package/examples/output/dijkstra.pl +0 -1336
  105. package/examples/output/dining-philosophers.pl +0 -21646
  106. package/examples/output/dog.pl +0 -30
  107. package/examples/output/drone-corridor-planner.pl +0 -2466
  108. package/examples/output/easter-computus.pl +0 -5690
  109. package/examples/output/electrical-rc-filter.pl +0 -102
  110. package/examples/output/epidemic-policy.pl +0 -760
  111. package/examples/output/equivalence-classes-overlap-implies-same-class.pl +0 -1080
  112. package/examples/output/eulerian-path.pl +0 -383
  113. package/examples/output/ev-range-worlds.pl +0 -2294
  114. package/examples/output/exact-cover-sudoku.pl +0 -396
  115. package/examples/output/existential-rule.pl +0 -38
  116. package/examples/output/exoplanet-validation-worlds.pl +0 -1904
  117. package/examples/output/expression-eval.pl +0 -104
  118. package/examples/output/family-cousins.pl +0 -3852
  119. package/examples/output/fastpow.pl +0 -1064
  120. package/examples/output/fft8-numeric.pl +0 -2556
  121. package/examples/output/fibonacci.pl +0 -2060
  122. package/examples/output/field-nitrogen-balance.pl +0 -1452
  123. package/examples/output/floating-point.pl +0 -151
  124. package/examples/output/four-color-map.pl +0 -191
  125. package/examples/output/fundamental-theorem-arithmetic.pl +0 -1610
  126. package/examples/output/gcd-bezout-identity.pl +0 -1842
  127. package/examples/output/gd-step-certified.pl +0 -32354
  128. package/examples/output/gdpr-compliance.pl +0 -193
  129. package/examples/output/goldbach-1000.pl +0 -17490
  130. package/examples/output/good-cobbler.pl +0 -15
  131. package/examples/output/gps.pl +0 -4166
  132. package/examples/output/graph-reachability.pl +0 -148
  133. package/examples/output/gray-code-counter.pl +0 -835
  134. package/examples/output/greatest-lower-bound-uniqueness.pl +0 -148
  135. package/examples/output/group-inverse-uniqueness.pl +0 -82
  136. package/examples/output/hamiltonian-cycle.pl +0 -102
  137. package/examples/output/hamiltonian-path.pl +0 -14157
  138. package/examples/output/hamming-code.pl +0 -2665
  139. package/examples/output/hanoi.pl +0 -184
  140. package/examples/output/heat-loss.pl +0 -223
  141. package/examples/output/heron-theorem.pl +0 -253
  142. package/examples/output/ideal-gas-law.pl +0 -148
  143. package/examples/output/illegitimate-reasoning.pl +0 -530
  144. package/examples/output/kaprekar.pl +0 -208
  145. package/examples/output/law-of-cosines.pl +0 -165
  146. package/examples/output/least-squares-regression.pl +0 -1447
  147. package/examples/output/list-collection.pl +0 -49
  148. package/examples/output/lldm.pl +0 -17298
  149. package/examples/output/manufacturing-quality-control.pl +0 -562
  150. package/examples/output/matrix.pl +0 -206
  151. package/examples/output/microgrid-dispatch.pl +0 -787
  152. package/examples/output/monkey-bananas.pl +0 -575
  153. package/examples/output/n-queens.pl +0 -2046
  154. package/examples/output/network-sla.pl +0 -282
  155. package/examples/output/newton-raphson.pl +0 -756
  156. package/examples/output/nixon-diamond.pl +0 -176
  157. package/examples/output/odrl-dpv-healthcare-risk-ranked.pl +0 -6148
  158. package/examples/output/odrl-dpv-risk-ranked.pl +0 -12961
  159. package/examples/output/orbital-transfer-design.pl +0 -961
  160. package/examples/output/path-discovery.pl +0 -333
  161. package/examples/output/peano-arithmetic.pl +0 -1432
  162. package/examples/output/peasant.pl +0 -190
  163. package/examples/output/pendulum-period.pl +0 -271
  164. package/examples/output/polynomial.pl +0 -7952
  165. package/examples/output/project-portfolio-optimization.pl +0 -93
  166. package/examples/output/proof-contrapositive.pl +0 -75
  167. package/examples/output/quadratic-formula.pl +0 -454
  168. package/examples/output/quine-mccluskey.pl +0 -188
  169. package/examples/output/radioactive-decay.pl +0 -299
  170. package/examples/output/sat-dpll.pl +0 -121
  171. package/examples/output/security-incident-correlation.pl +0 -267
  172. package/examples/output/service-impact.pl +0 -457
  173. package/examples/output/sieve.pl +0 -19
  174. package/examples/output/skolem-functions.pl +0 -264
  175. package/examples/output/socket-age.pl +0 -31
  176. package/examples/output/socket-family.pl +0 -56
  177. package/examples/output/socrates.pl +0 -36
  178. package/examples/output/statistics-summary.pl +0 -741
  179. package/examples/output/sudoku.pl +0 -38
  180. package/examples/output/superdense-coding.pl +0 -476
  181. package/examples/output/traveling-salesman.pl +0 -284
  182. package/examples/output/turing.pl +0 -2144
  183. package/examples/output/vector-similarity.pl +0 -421
  184. package/examples/output/witch.pl +0 -209
  185. package/examples/output/wolf-goat-cabbage.pl +0 -900
  186. package/examples/output/zebra.pl +0 -401
  187. package/examples/proof/age.pl +71 -0
  188. package/examples/proof/aliases-and-namespaces.pl +78 -0
  189. package/examples/proof/ancestor.pl +140 -0
  190. package/examples/proof/animal.pl +68 -0
  191. package/examples/proof/annotation.pl +80 -0
  192. package/examples/proof/backward.pl +22 -0
  193. package/examples/proof/cat-koko.pl +86 -0
  194. package/examples/proof/data-negotiation.pl +76 -0
  195. package/examples/proof/derived-rule.pl +43 -0
  196. package/examples/proof/dog.pl +31 -0
  197. package/examples/proof/electrical-rc-filter.pl +105 -0
  198. package/examples/proof/existential-rule.pl +40 -0
  199. package/examples/proof/floating-point.pl +160 -0
  200. package/examples/proof/good-cobbler.pl +16 -0
  201. package/examples/proof/group-inverse-uniqueness.pl +84 -0
  202. package/examples/proof/list-collection.pl +52 -0
  203. package/examples/proof/proof-contrapositive.pl +78 -0
  204. package/examples/proof/socket-age.pl +32 -0
  205. package/examples/proof/socket-family.pl +59 -0
  206. package/examples/proof/socrates.pl +38 -0
  207. package/examples/socket-age.pl +4 -4
  208. package/examples/socket-family.pl +4 -4
  209. package/package.json +1 -1
  210. package/playground-worker.mjs +4 -4
  211. package/playground.html +15 -15
  212. package/src/cli.js +10 -10
  213. package/src/index.js +3 -2
  214. package/src/parser.js +130 -1
  215. package/src/program.js +33 -27
  216. package/test/run-all.js +0 -0
  217. package/test/run-conformance.js +0 -0
  218. package/test/run-examples.js +38 -8
  219. package/test/run-regression.js +29 -28
package/README.md CHANGED
@@ -5,10 +5,10 @@
5
5
 
6
6
  eyelang is a small rule engine for Prolog-style Horn clauses over ordinary terms, lists, arithmetic, strings, and finite search. The command-line executable is `eyelang`.
7
7
 
8
- Programs write relations directly, for example `ancestor(pat, emma)` or `status(case1, accepted)`. eyelang output is ordinary eyelang syntax: by default, each answer fact is followed by a `why/2` explanation that records the proof. Pass `--no-why` when you want answer facts only. When no `--query` is supplied, the CLI materializes distinct new binary derivations of the form `p(S, O)`, suppresses repeated source facts, and prints each derived answer. Programs may add `materialize(Name, Arity).` declarations to focus default output on selected predicates.
8
+ Programs write relations directly, for example `ancestor(pat, emma)` or `status(case1, accepted)`. eyelang output is ordinary eyelang syntax: by default, it prints answer facts only. Pass `--proof` (or `-p`) when you also want each answer followed by a `why/2` explanation fact that records the proof. When no `--query` is supplied, the CLI materializes distinct new binary derivations of the form `p(S, O)`, suppresses repeated source facts, and prints each derived answer. Programs may add `materialize(Name, Arity).` declarations to focus default output on selected predicates.
9
9
 
10
10
 
11
- Try it in the [browser playground](https://eyereasoner.github.io/eyelang/playground). The playground includes run options equivalent to CLI `--query`, `--stats`, and `--no-why`.
11
+ Try it in the [browser playground](https://eyereasoner.github.io/eyelang/playground). The playground includes run options equivalent to CLI `--query`, `--stats`, and `--proof`.
12
12
 
13
13
  For the normative language definition, including lexical syntax, terms, clauses, goals, built-ins, `memoize/2`, `materialize/2`, and conformance boundaries, read the [eyelang specification](SPEC.md).
14
14
 
@@ -63,7 +63,7 @@ bin/eyelang --version
63
63
  bin/eyelang -v
64
64
  ```
65
65
 
66
- Run a program and let eyelang print derived binary facts with explanations:
66
+ Run a program and let eyelang print derived binary facts:
67
67
 
68
68
  ```sh
69
69
  bin/eyelang examples/ancestor.pl
@@ -75,14 +75,14 @@ Run an explicit query:
75
75
  bin/eyelang --query 'ancestor(pat, X)' examples/ancestor.pl
76
76
  ```
77
77
 
78
- Suppress proof explanations when you want only answer facts:
78
+ Enable proof explanations when you want machine-readable provenance:
79
79
 
80
80
  ```sh
81
- bin/eyelang --no-why --query 'ancestor(pat, X)' examples/ancestor.pl
82
- bin/eyelang --no-why examples/ancestor.pl
81
+ bin/eyelang --proof --query 'ancestor(pat, X)' examples/ancestor.pl
82
+ bin/eyelang -p examples/ancestor.pl
83
83
  ```
84
84
 
85
- eyelang-readable explanations are part of the default output. Each `why/2` fact contains a nested abstract proof term, and a blank line separates consecutive explanations. Using eyelang syntax for explanations keeps them in the same language as the answers themselves: they are readable by humans, parseable by eyelang, easy to test, and can be queried, transformed, or explained further like any other eyelang data. For example:
85
+ eyelang-readable explanations are opt-in proof output. Each `why/2` fact contains a nested abstract proof term, and a blank line separates consecutive explanations. Using eyelang syntax for explanations keeps them in the same language as the answers themselves: they are readable by humans, parseable by eyelang, easy to test, and can be queried, transformed, or explained further like any other eyelang data. For example:
86
86
 
87
87
  ```prolog
88
88
  type(socrates, mortal).
@@ -103,16 +103,16 @@ why(
103
103
 
104
104
  ```
105
105
 
106
- The explanation output can itself be read as eyelang input and queried, for example `why(type(socrates, mortal), Proof)`. `--no-why` suppresses only these explanation facts; it does not change the answers found by the solver.
106
+ The explanation output can itself be read as eyelang input and queried, for example `why(type(socrates, mortal), Proof)`. `--proof` adds only these explanation facts; it does not change the answers found by the solver.
107
107
 
108
108
  ### Explanation cookbook
109
109
 
110
- eyelang answers carry their own provenance by default.
110
+ eyelang answers can carry their own provenance when proof output is enabled.
111
111
 
112
112
  Explain one derived fact:
113
113
 
114
114
  ```sh
115
- bin/eyelang --query 'type(socrates, mortal)' examples/socrates.pl
115
+ bin/eyelang --proof --query 'type(socrates, mortal)' examples/socrates.pl
116
116
  ```
117
117
 
118
118
  The output contains the answer and a `why/2` fact. The proof term shows the source rule that produced the answer and the source fact used below it. Source references use `rule("file.pl", clause(N))` and `fact("file.pl", clause(N))`, where `N` is the 1-based clause number in that file.
@@ -153,7 +153,7 @@ Use the `uses([...])` list to follow the proof tree. In the policy example it co
153
153
  Reuse explanations as data:
154
154
 
155
155
  ```sh
156
- bin/eyelang --query 'type(socrates, mortal)' examples/socrates.pl > socrates.why.pl
156
+ bin/eyelang --proof --query 'type(socrates, mortal)' examples/socrates.pl > socrates.why.pl
157
157
  bin/eyelang --query 'why(type(socrates, mortal), Proof)' socrates.why.pl
158
158
  ```
159
159
 
@@ -266,7 +266,7 @@ Add `--stats` when you want lightweight solver counters on stderr without changi
266
266
  bin/eyelang --stats --query 'once(solution(classic, S))' examples/sudoku.pl
267
267
  ```
268
268
 
269
- The playground has matching `--stats` and `--no-why` checkboxes, so browser runs can show the same counters or suppress explanations like the CLI.
269
+ The playground has matching `--stats` and `--proof` checkboxes, so browser runs can show the same counters or explanations like the CLI.
270
270
 
271
271
 
272
272
  ### Builtins
@@ -474,13 +474,18 @@ The repository includes examples for recursion, graph reachability, finite searc
474
474
 
475
475
  ## Golden outputs, tests, and conformance
476
476
 
477
- Golden outputs live in [`examples/output`](examples/output). They include both answer facts and their `why/2` explanations. Example tests pin `local_time/1` to `2026-05-30` so date-dependent examples stay deterministic. Regenerate them after an intentional output or explanation change:
477
+ Golden answer outputs live in [`examples/output`](examples/output). A curated proof-output suite lives in [`examples/proof`](examples/proof). Example tests pin `local_time/1` to `2026-05-30` so date-dependent examples stay deterministic. Regenerate them after an intentional output or explanation change:
478
478
 
479
479
  ```sh
480
480
  for f in examples/*.pl; do
481
481
  b=$(basename "$f")
482
482
  EYELANG_LOCAL_TIME=2026-05-30 bin/eyelang "$f" > "examples/output/$b"
483
483
  done
484
+
485
+ for f in examples/proof/*.pl; do
486
+ b=$(basename "$f")
487
+ EYELANG_LOCAL_TIME=2026-05-30 bin/eyelang --proof "examples/$b" > "examples/proof/$b"
488
+ done
484
489
  ```
485
490
 
486
491
  Run the full test suite:
@@ -525,7 +530,7 @@ For a release:
525
530
  2. update `README.md` and `SPEC.md`;
526
531
  3. regenerate golden outputs if behavior changed;
527
532
  4. run `npm test`;
528
- 5. publish the repository with `playground.html` and `playground-worker.mjs` if publishing the playground. The playground includes controls equivalent to CLI `--query GOAL`, `--stats`, and `--no-why`.
533
+ 5. publish the repository with `playground.html` and `playground-worker.mjs` if publishing the playground. The playground includes controls equivalent to CLI `--query GOAL`, `--stats`, and `--proof`.
529
534
 
530
535
  ## Performance notes
531
536
 
package/SPEC.md CHANGED
@@ -570,13 +570,13 @@ When eyelang derives `ancestor(pat, emma)`, the answer explanation can still ref
570
570
 
571
571
  ### 12.3 Sockets and AI agents
572
572
 
573
- eyelang Sockets are especially useful for AI-facing systems. An AI agent can extract or propose candidate claims, but those claims should enter a reasoning program as explicit eyelang facts or rules through a declared socket rather than as opaque text. eyelang can then check the claims against other facts and rules, derive consequences, and return ordinary `why/2` explanations.
573
+ eyelang Sockets are especially useful for AI-facing systems. An AI agent can extract or propose candidate claims, but those claims should enter a reasoning program as explicit eyelang facts or rules through a declared socket rather than as opaque text. eyelang can then check the claims against other facts and rules, derive consequences, and optionally return ordinary `why/2` explanations.
574
574
 
575
575
  This gives a clear division of labor: AI can help generate, translate, and connect knowledge; eyelang can represent, check, and explain the reasoning; sockets define the boundary between them.
576
576
 
577
577
  ## 13. Output and read-back profile
578
578
 
579
- Normal answer output prints one resolved answer term followed by a period, and by default a `why/2` explanation fact for that answer. Strings are double-quoted; atom constants are quoted when needed; lists use list syntax; compound terms use functor notation. Host interfaces MAY provide an option such as `--no-why` to suppress explanation facts; this option MUST NOT change the answers found.
579
+ Normal answer output prints one resolved answer term followed by a period. Strings are double-quoted; atom constants are quoted when needed; lists use list syntax; compound terms use functor notation. Host interfaces MAY provide an option such as `--proof` to add `why/2` explanation facts; this option MUST NOT change the answers found.
580
580
 
581
581
  Output SHOULD be accepted as eyelang input when it contains only supported term syntax. Explanations are ordinary eyelang facts, so answer output can be read back and queried by eyelang.
582
582
 
@@ -588,11 +588,11 @@ Without `--query`, the host behavior is:
588
588
  4. keep only ground answers;
589
589
  5. remove answers identical to source facts;
590
590
  6. suppress duplicates;
591
- 7. print each answer followed by its `why/2` explanation, unless the host interface was explicitly asked to suppress explanations.
591
+ 7. print each answer, followed by its `why/2` explanation only if the host interface was explicitly asked to emit proof output.
592
592
 
593
593
  ### 13.1 Explanation output
594
594
 
595
- Each answer SHOULD be followed by a machine-readable `why/2` fact unless explanation output is explicitly suppressed by the host interface. Explanation output is ordinary eyelang syntax whose second argument is a nested abstract proof term such as `proof(goal(G), by(Method), bindings(Bindings), uses(Proofs))`; implementations SHOULD print `goal(...)` and `by(...)` on separate lines for readability. A proof term preserves the answer goal, derivation method, relevant bindings, and nested uses while omitting proof IDs. User clauses SHOULD be referenced explicitly as `fact(Filename, clause(N))` or `rule(Filename, clause(N))`, where `N` is the 1-based clause number within that source. Built-ins SHOULD be referenced as `builtin(Name, Arity)` because they do not come from source clauses. Explanation output is outside the logical semantics of the input program and MUST NOT change the set of answers.
595
+ When proof output is enabled, each answer SHOULD be followed by a machine-readable `why/2` fact. Explanation output is ordinary eyelang syntax whose second argument is a nested abstract proof term such as `proof(goal(G), by(Method), bindings(Bindings), uses(Proofs))`; implementations SHOULD print `goal(...)` and `by(...)` on separate lines for readability. A proof term preserves the answer goal, derivation method, relevant bindings, and nested uses while omitting proof IDs. User clauses SHOULD be referenced explicitly as `fact(Filename, clause(N))` or `rule(Filename, clause(N))`, where `N` is the 1-based clause number within that source. Built-ins SHOULD be referenced as `builtin(Name, Arity)` because they do not come from source clauses. Explanation output is outside the logical semantics of the input program and MUST NOT change the set of answers.
596
596
 
597
597
  ## 14. Conformance profiles
598
598
 
@@ -1,9 +1 @@
1
1
  parent(pat, jan).
2
- why(
3
- parent(pat, jan),
4
- proof(
5
- goal(parent(pat, jan)),
6
- by(fact("001_fact_query.pl", clause(1)))
7
- )
8
- ).
9
-
@@ -1,43 +1,2 @@
1
1
  ancestor(pat, jan).
2
- why(
3
- ancestor(pat, jan),
4
- proof(
5
- goal(ancestor(pat, jan)),
6
- by(rule("002_rule_recursion.pl", clause(3))),
7
- bindings([binding("X", pat), binding("Y", jan)]),
8
- uses([
9
- proof(
10
- goal(parent(pat, jan)),
11
- by(fact("002_rule_recursion.pl", clause(1)))
12
- )
13
- ])
14
- )
15
- ).
16
-
17
2
  ancestor(pat, emma).
18
- why(
19
- ancestor(pat, emma),
20
- proof(
21
- goal(ancestor(pat, emma)),
22
- by(rule("002_rule_recursion.pl", clause(4))),
23
- bindings([binding("X", pat), binding("Z", emma), binding("Y", jan)]),
24
- uses([
25
- proof(
26
- goal(parent(pat, jan)),
27
- by(fact("002_rule_recursion.pl", clause(1)))
28
- ),
29
- proof(
30
- goal(ancestor(jan, emma)),
31
- by(rule("002_rule_recursion.pl", clause(3))),
32
- bindings([binding("X", jan), binding("Y", emma)]),
33
- uses([
34
- proof(
35
- goal(parent(jan, emma)),
36
- by(fact("002_rule_recursion.pl", clause(2)))
37
- )
38
- ])
39
- )
40
- ])
41
- )
42
- ).
43
-
@@ -1,117 +1,13 @@
1
1
  value(atom, pat).
2
- why(
3
- value(atom, pat),
4
- proof(
5
- goal(value(atom, pat)),
6
- by(fact("003_terms_and_readback.pl", clause(1)))
7
- )
8
- ).
9
-
10
2
  value(quoted_atom, 'atom with spaces').
11
- why(
12
- value(quoted_atom, 'atom with spaces'),
13
- proof(
14
- goal(value(quoted_atom, 'atom with spaces')),
15
- by(fact("003_terms_and_readback.pl", clause(2)))
16
- )
17
- ).
18
-
19
3
  value(quoted_quote, 'needs''quote').
20
- why(
21
- value(quoted_quote, 'needs''quote'),
22
- proof(
23
- goal(value(quoted_quote, 'needs''quote')),
24
- by(fact("003_terms_and_readback.pl", clause(3)))
25
- )
26
- ).
27
-
28
4
  value(empty_atom, '').
29
- why(
30
- value(empty_atom, ''),
31
- proof(
32
- goal(value(empty_atom, '')),
33
- by(fact("003_terms_and_readback.pl", clause(4)))
34
- )
35
- ).
36
-
37
5
  value(string, "line\nquote: \"ok\"").
38
- why(
39
- value(string, "line\nquote: \"ok\""),
40
- proof(
41
- goal(value(string, "line\nquote: \"ok\"")),
42
- by(fact("003_terms_and_readback.pl", clause(5)))
43
- )
44
- ).
45
-
46
6
  value(integer, -42).
47
- why(
48
- value(integer, -42),
49
- proof(
50
- goal(value(integer, -42)),
51
- by(fact("003_terms_and_readback.pl", clause(6)))
52
- )
53
- ).
54
-
55
7
  value(decimal, 0.25).
56
- why(
57
- value(decimal, 0.25),
58
- proof(
59
- goal(value(decimal, 0.25)),
60
- by(fact("003_terms_and_readback.pl", clause(7)))
61
- )
62
- ).
63
-
64
8
  value(scientific, 1.25e-3).
65
- why(
66
- value(scientific, 1.25e-3),
67
- proof(
68
- goal(value(scientific, 1.25e-3)),
69
- by(fact("003_terms_and_readback.pl", clause(8)))
70
- )
71
- ).
72
-
73
9
  value(compound, pair(3, nested(atom, [x, y]))).
74
- why(
75
- value(compound, pair(3, nested(atom, [x, y]))),
76
- proof(
77
- goal(value(compound, pair(3, nested(atom, [x, y])))),
78
- by(fact("003_terms_and_readback.pl", clause(9)))
79
- )
80
- ).
81
-
82
10
  value(zero_arity, nil()).
83
- why(
84
- value(zero_arity, nil()),
85
- proof(
86
- goal(value(zero_arity, nil())),
87
- by(fact("003_terms_and_readback.pl", clause(10)))
88
- )
89
- ).
90
-
91
11
  value(empty_list, []).
92
- why(
93
- value(empty_list, []),
94
- proof(
95
- goal(value(empty_list, [])),
96
- by(fact("003_terms_and_readback.pl", clause(11)))
97
- )
98
- ).
99
-
100
12
  value(proper_list, [a, b, c]).
101
- why(
102
- value(proper_list, [a, b, c]),
103
- proof(
104
- goal(value(proper_list, [a, b, c])),
105
- by(fact("003_terms_and_readback.pl", clause(12)))
106
- )
107
- ).
108
-
109
13
  value(improper_list, [a, b | tail]).
110
- why(
111
- value(improper_list, [a, b | tail]),
112
- proof(
113
- goal(value(improper_list, [a, b | tail])),
114
- by(fact("003_terms_and_readback.pl", clause(13)))
115
- )
116
- ).
117
-
@@ -1,26 +1 @@
1
1
  ok(a, yes).
2
- why(
3
- ok(a, yes),
4
- proof(
5
- goal(ok(a, yes)),
6
- by(rule("004_conjunction_and_parentheses.pl", clause(3))),
7
- bindings([binding("X", a)]),
8
- uses([
9
- proof(
10
- goal((p(a), q(a))),
11
- by(conjunction),
12
- uses([
13
- proof(
14
- goal(p(a)),
15
- by(fact("004_conjunction_and_parentheses.pl", clause(1)))
16
- ),
17
- proof(
18
- goal(q(a)),
19
- by(fact("004_conjunction_and_parentheses.pl", clause(2)))
20
- )
21
- ])
22
- )
23
- ])
24
- )
25
- ).
26
-
@@ -1,34 +1,2 @@
1
1
  answer(first, a).
2
- why(
3
- answer(first, a),
4
- proof(
5
- goal(answer(first, a)),
6
- by(rule("005_list_deconstruction.pl", clause(3))),
7
- bindings([binding("X", a)]),
8
- uses([
9
- proof(
10
- goal(first([a, b, c], a)),
11
- by(fact("005_list_deconstruction.pl", clause(1))),
12
- bindings([binding("X", a), binding("_Rest", [b, c])])
13
- )
14
- ])
15
- )
16
- ).
17
-
18
2
  answer(tail, [b, c]).
19
- why(
20
- answer(tail, [b, c]),
21
- proof(
22
- goal(answer(tail, [b, c])),
23
- by(rule("005_list_deconstruction.pl", clause(4))),
24
- bindings([binding("Tail", [b, c])]),
25
- uses([
26
- proof(
27
- goal(tail([a, b, c], [b, c])),
28
- by(fact("005_list_deconstruction.pl", clause(2))),
29
- bindings([binding("_Head", a), binding("Tail", [b, c])])
30
- )
31
- ])
32
- )
33
- ).
34
-
@@ -1,16 +1 @@
1
1
  answer(formula, (name(alice, "Alice"), knows(alice, bob))).
2
- why(
3
- answer(formula, (name(alice, "Alice"), knows(alice, bob))),
4
- proof(
5
- goal(answer(formula, (name(alice, "Alice"), knows(alice, bob)))),
6
- by(rule("006_comma_formula_data.pl", clause(2))),
7
- bindings([binding("F", (name(alice, "Alice"), knows(alice, bob)))]),
8
- uses([
9
- proof(
10
- goal(record((name(alice, "Alice"), knows(alice, bob)))),
11
- by(fact("006_comma_formula_data.pl", clause(1)))
12
- )
13
- ])
14
- )
15
- ).
16
-
@@ -1,20 +1 @@
1
1
  answer(fresh, yes).
2
- why(
3
- answer(fresh, yes),
4
- proof(
5
- goal(answer(fresh, yes)),
6
- by(rule("007_anonymous_variables.pl", clause(3))),
7
- bindings([binding("__anon0", one), binding("__anon1", two)]),
8
- uses([
9
- proof(
10
- goal(pair(a, one)),
11
- by(fact("007_anonymous_variables.pl", clause(1)))
12
- ),
13
- proof(
14
- goal(pair(b, two)),
15
- by(fact("007_anonymous_variables.pl", clause(2)))
16
- )
17
- ])
18
- )
19
- ).
20
-
@@ -1,27 +1,3 @@
1
1
  value(hash, #).
2
- why(
3
- value(hash, #),
4
- proof(
5
- goal(value(hash, #)),
6
- by(fact("008_graphic_atoms.pl", clause(1)))
7
- )
8
- ).
9
-
10
2
  value(arrow, =>).
11
- why(
12
- value(arrow, =>),
13
- proof(
14
- goal(value(arrow, =>)),
15
- by(fact("008_graphic_atoms.pl", clause(2)))
16
- )
17
- ).
18
-
19
3
  value(comparison, =<).
20
- why(
21
- value(comparison, =<),
22
- proof(
23
- goal(value(comparison, =<)),
24
- by(fact("008_graphic_atoms.pl", clause(3)))
25
- )
26
- ).
27
-
@@ -1,32 +1,2 @@
1
1
  answer(quoted_percent, "% not a comment").
2
- why(
3
- answer(quoted_percent, "% not a comment"),
4
- proof(
5
- goal(answer(quoted_percent, "% not a comment")),
6
- by(rule("009_comments_and_whitespace.pl", clause(3))),
7
- bindings([binding("K", quoted_percent), binding("V", "% not a comment")]),
8
- uses([
9
- proof(
10
- goal(item(quoted_percent, "% not a comment")),
11
- by(fact("009_comments_and_whitespace.pl", clause(1)))
12
- )
13
- ])
14
- )
15
- ).
16
-
17
2
  answer(quoted_atom, 'has % sign').
18
- why(
19
- answer(quoted_atom, 'has % sign'),
20
- proof(
21
- goal(answer(quoted_atom, 'has % sign')),
22
- by(rule("009_comments_and_whitespace.pl", clause(3))),
23
- bindings([binding("K", quoted_atom), binding("V", 'has % sign')]),
24
- uses([
25
- proof(
26
- goal(item(quoted_atom, 'has % sign')),
27
- by(fact("009_comments_and_whitespace.pl", clause(2)))
28
- )
29
- ])
30
- )
31
- ).
32
-
@@ -1,54 +1,2 @@
1
1
  answer(from_a, c).
2
- why(
3
- answer(from_a, c),
4
- proof(
5
- goal(answer(from_a, c)),
6
- by(rule("010_variable_scope_and_reuse.pl", clause(5))),
7
- bindings([binding("Z", c)]),
8
- uses([
9
- proof(
10
- goal(two_step(a, c)),
11
- by(rule("010_variable_scope_and_reuse.pl", clause(4))),
12
- bindings([binding("X", a), binding("Z", c), binding("Y", b)]),
13
- uses([
14
- proof(
15
- goal(edge(a, b)),
16
- by(fact("010_variable_scope_and_reuse.pl", clause(1)))
17
- ),
18
- proof(
19
- goal(edge(b, c)),
20
- by(fact("010_variable_scope_and_reuse.pl", clause(2)))
21
- )
22
- ])
23
- )
24
- ])
25
- )
26
- ).
27
-
28
2
  answer(from_b, d).
29
- why(
30
- answer(from_b, d),
31
- proof(
32
- goal(answer(from_b, d)),
33
- by(rule("010_variable_scope_and_reuse.pl", clause(6))),
34
- bindings([binding("Z", d)]),
35
- uses([
36
- proof(
37
- goal(two_step(b, d)),
38
- by(rule("010_variable_scope_and_reuse.pl", clause(4))),
39
- bindings([binding("X", b), binding("Z", d), binding("Y", c)]),
40
- uses([
41
- proof(
42
- goal(edge(b, c)),
43
- by(fact("010_variable_scope_and_reuse.pl", clause(2)))
44
- ),
45
- proof(
46
- goal(edge(c, d)),
47
- by(fact("010_variable_scope_and_reuse.pl", clause(3)))
48
- )
49
- ])
50
- )
51
- ])
52
- )
53
- ).
54
-
@@ -1,32 +1,2 @@
1
1
  answer(unary, a).
2
- why(
3
- answer(unary, a),
4
- proof(
5
- goal(answer(unary, a)),
6
- by(rule("011_predicate_arity.pl", clause(3))),
7
- bindings([binding("X", a)]),
8
- uses([
9
- proof(
10
- goal(p(a)),
11
- by(fact("011_predicate_arity.pl", clause(1)))
12
- )
13
- ])
14
- )
15
- ).
16
-
17
2
  answer(binary, pair(a, b)).
18
- why(
19
- answer(binary, pair(a, b)),
20
- proof(
21
- goal(answer(binary, pair(a, b))),
22
- by(rule("011_predicate_arity.pl", clause(4))),
23
- bindings([binding("X", a), binding("Y", b)]),
24
- uses([
25
- proof(
26
- goal(p(a, b)),
27
- by(fact("011_predicate_arity.pl", clause(2)))
28
- )
29
- ])
30
- )
31
- ).
32
-
@@ -1,32 +1,2 @@
1
1
  answer(middle, b).
2
- why(
3
- answer(middle, b),
4
- proof(
5
- goal(answer(middle, b)),
6
- by(rule("012_nested_compound_unification.pl", clause(2))),
7
- bindings([binding("X", b)]),
8
- uses([
9
- proof(
10
- goal(fact(pair(a, nested(b, [c, d])))),
11
- by(fact("012_nested_compound_unification.pl", clause(1)))
12
- )
13
- ])
14
- )
15
- ).
16
-
17
2
  answer(list_tail, [d]).
18
- why(
19
- answer(list_tail, [d]),
20
- proof(
21
- goal(answer(list_tail, [d])),
22
- by(rule("012_nested_compound_unification.pl", clause(3))),
23
- bindings([binding("T", [d])]),
24
- uses([
25
- proof(
26
- goal(fact(pair(a, nested(b, [c, d])))),
27
- by(fact("012_nested_compound_unification.pl", clause(1)))
28
- )
29
- ])
30
- )
31
- ).
32
-
@@ -1,46 +1,2 @@
1
1
  answer(color, red).
2
- why(
3
- answer(color, red),
4
- proof(
5
- goal(answer(color, red)),
6
- by(rule("013_multiple_clauses_order.pl", clause(4))),
7
- bindings([binding("X", red)]),
8
- uses([
9
- proof(
10
- goal(paint(red)),
11
- by(rule("013_multiple_clauses_order.pl", clause(3))),
12
- bindings([binding("X", red)]),
13
- uses([
14
- proof(
15
- goal(color(red)),
16
- by(fact("013_multiple_clauses_order.pl", clause(1)))
17
- )
18
- ])
19
- )
20
- ])
21
- )
22
- ).
23
-
24
2
  answer(color, blue).
25
- why(
26
- answer(color, blue),
27
- proof(
28
- goal(answer(color, blue)),
29
- by(rule("013_multiple_clauses_order.pl", clause(4))),
30
- bindings([binding("X", blue)]),
31
- uses([
32
- proof(
33
- goal(paint(blue)),
34
- by(rule("013_multiple_clauses_order.pl", clause(3))),
35
- bindings([binding("X", blue)]),
36
- uses([
37
- proof(
38
- goal(color(blue)),
39
- by(fact("013_multiple_clauses_order.pl", clause(2)))
40
- )
41
- ])
42
- )
43
- ])
44
- )
45
- ).
46
-
@@ -1,20 +1 @@
1
1
  answer(a, ok).
2
- why(
3
- answer(a, ok),
4
- proof(
5
- goal(answer(a, ok)),
6
- by(rule("014_failure_filters_answers.pl", clause(4))),
7
- bindings([binding("X", a)]),
8
- uses([
9
- proof(
10
- goal(candidate(a)),
11
- by(fact("014_failure_filters_answers.pl", clause(1)))
12
- ),
13
- proof(
14
- goal(allowed(a)),
15
- by(fact("014_failure_filters_answers.pl", clause(3)))
16
- )
17
- ])
18
- )
19
- ).
20
-