eyeprolog 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +6 -0
  2. package/examples/book/README.md +45 -19
  3. package/examples/book/chapter-01/02-child.pl +1 -3
  4. package/examples/book/chapter-02/01-same_shape.pl +2 -0
  5. package/examples/book/chapter-04/01-ancestor.pl +3 -0
  6. package/examples/book/chapter-10/01-color.pl +14 -0
  7. package/examples/book/chapter-21/01-parent.pl +7 -0
  8. package/examples/book/chapter-22/04-sentence.pl +19 -0
  9. package/examples/book/chapter-24/01-worker.pl +17 -0
  10. package/examples/book/chapter-25/04-permit.pl +9 -0
  11. package/examples/book/chapter-26/02-triple.pl +9 -2
  12. package/examples/book/chapter-26/03-triple-2.pl +2 -0
  13. package/examples/book/chapter-28/04-integer_rectangle.pl +5 -0
  14. package/examples/book/chapter-29/01-counterexample_to_odd_square.pl +7 -0
  15. package/examples/book/chapter-31/01-edge.pl +6 -0
  16. package/examples/book/chapter-31/02-program.pl +6 -0
  17. package/examples/book/chapter-31/03-double.pl +13 -0
  18. package/examples/book/chapter-32/03-candidate_debug.pl +7 -0
  19. package/examples/book/chapter-32/04-reference_square.pl +12 -0
  20. package/examples/book/chapter-39/01-answer.pl +5 -0
  21. package/examples/book/chapter-39/02-answer-2.pl +6 -0
  22. package/examples/book/chapter-39/03-answer-3.pl +10 -0
  23. package/examples/book/chapter-39/04-cost.pl +10 -0
  24. package/examples/book/chapter-39/05-message.pl +16 -0
  25. package/package.json +1 -1
  26. package/test/run-regression.mjs +5 -0
  27. package/the-art-of-eyeprolog.md +139 -33
  28. /package/examples/book/chapter-02/{01-measurement.pl → 02-measurement.pl} +0 -0
  29. /package/examples/book/chapter-02/{02-label.pl → 03-label.pl} +0 -0
  30. /package/examples/book/chapter-04/{01-path.pl → 02-path.pl} +0 -0
  31. /package/examples/book/chapter-10/{01-plan.pl → 02-plan.pl} +0 -0
  32. /package/examples/book/chapter-21/{01-grandparent.pl → 02-grandparent.pl} +0 -0
  33. /package/examples/book/chapter-21/{02-loop_edge.pl → 03-loop_edge.pl} +0 -0
  34. /package/examples/book/chapter-21/{03-eligible.pl → 04-eligible.pl} +0 -0
  35. /package/examples/book/chapter-21/{04-grows.pl → 05-grows.pl} +0 -0
  36. /package/examples/book/chapter-22/{04-evaluate.pl → 05-evaluate.pl} +0 -0
  37. /package/examples/book/chapter-22/{05-lookup.pl → 06-lookup.pl} +0 -0
  38. /package/examples/book/chapter-22/{06-simplify.pl → 07-simplify.pl} +0 -0
  39. /package/examples/book/chapter-24/{01-simple_path.pl → 02-simple_path.pl} +0 -0
  40. /package/examples/book/chapter-24/{02-reachable.pl → 03-reachable.pl} +0 -0
  41. /package/examples/book/chapter-24/{03-best_plan.pl → 04-best_plan.pl} +0 -0
  42. /package/examples/book/chapter-25/{04-incompatible_status.pl → 05-incompatible_status.pl} +0 -0
  43. /package/examples/book/chapter-26/{03-edge.pl → 04-edge.pl} +0 -0
  44. /package/examples/book/chapter-28/{04-preserves_combine.pl → 05-preserves_combine.pl} +0 -0
  45. /package/examples/book/chapter-29/{01-noncommuting_pair.pl → 02-noncommuting_pair.pl} +0 -0
package/README.md CHANGED
@@ -5,6 +5,12 @@
5
5
 
6
6
  EyeProlog turns portable ISO Prolog programs into answers and inspectable proofs.
7
7
 
8
+ <p align="center">
9
+ <a href="https://eyereasoner.github.io/eyeprolog/the-art-of-eyeprolog">
10
+ <img src="book-assets/title-page.svg" alt="Read The Art of EyeProlog" width="180">
11
+ </a>
12
+ </p>
13
+
8
14
  **[Book — *The Art of EyeProlog*](https://eyereasoner.github.io/eyeprolog/the-art-of-eyeprolog)** ·
9
15
  **[Why EyeProlog?](https://eyereasoner.github.io/eyeprolog/why-eyeprolog)** ·
10
16
  **[Playground](https://eyereasoner.github.io/eyeprolog/playground)**
@@ -18,8 +18,9 @@ npm run generate
18
18
 
19
19
  ## Chapter 2: Terms, variables, and substitution
20
20
 
21
- - [01-measurement.pl](chapter-02/01-measurement.pl)
22
- - [02-label.pl](chapter-02/02-label.pl)
21
+ - [01-same_shape.pl](chapter-02/01-same_shape.pl) — Unification
22
+ - [02-measurement.pl](chapter-02/02-measurement.pl)
23
+ - [03-label.pl](chapter-02/03-label.pl)
23
24
 
24
25
  ## Chapter 3: Rules and their two readings
25
26
 
@@ -37,7 +38,8 @@ npm run generate
37
38
 
38
39
  ## Chapter 4: Recursion: describing reachability
39
40
 
40
- - [01-path.pl](chapter-04/01-path.pl) — Constructing the recursive argument
41
+ - [01-ancestor.pl](chapter-04/01-ancestor.pl)
42
+ - [02-path.pl](chapter-04/02-path.pl) — Constructing the recursive argument
41
43
 
42
44
  ## Chapter 5: Lists as relations
43
45
 
@@ -70,7 +72,8 @@ npm run generate
70
72
 
71
73
  ## Chapter 10: From puzzles to models
72
74
 
73
- - [01-plan.pl](chapter-10/01-plan.pl)
75
+ - [01-color.pl](chapter-10/01-color.pl)
76
+ - [02-plan.pl](chapter-10/02-plan.pl)
74
77
 
75
78
  ## Chapter 11: Queries, answers, and proofs
76
79
 
@@ -113,19 +116,21 @@ npm run generate
113
116
 
114
117
  ## Chapter 21: Reading the computation
115
118
 
116
- - [01-grandparent.pl](chapter-21/01-grandparent.pl) — Substitutions accumulate
117
- - [02-loop_edge.pl](chapter-21/02-loop_edge.pl)
118
- - [03-eligible.pl](chapter-21/03-eligible.pl) — Failure rewinds choices, not facts
119
- - [04-grows.pl](chapter-21/04-grows.pl) — Variants, cycles, and tables
119
+ - [01-parent.pl](chapter-21/01-parent.pl)
120
+ - [02-grandparent.pl](chapter-21/02-grandparent.pl) — Substitutions accumulate
121
+ - [03-loop_edge.pl](chapter-21/03-loop_edge.pl)
122
+ - [04-eligible.pl](chapter-21/04-eligible.pl) — Failure rewinds choices, not facts
123
+ - [05-grows.pl](chapter-21/05-grows.pl) — Variants, cycles, and tables
120
124
 
121
125
  ## Chapter 22: Trees, languages, and symbolic evaluation
122
126
 
123
127
  - [01-tree.pl](chapter-22/01-tree.pl)
124
128
  - [02-tree_member.pl](chapter-22/02-tree_member.pl)
125
129
  - [03-mirror.pl](chapter-22/03-mirror.pl) — Transforming a tree
126
- - [04-evaluate.pl](chapter-22/04-evaluate.pl) — Interpreting an expression
127
- - [05-lookup.pl](chapter-22/05-lookup.pl)
128
- - [06-simplify.pl](chapter-22/06-simplify.pl) — Rewriting symbolic expressions
130
+ - [04-sentence.pl](chapter-22/04-sentence.pl) — A grammar without special syntax
131
+ - [05-evaluate.pl](chapter-22/05-evaluate.pl) — Interpreting an expression
132
+ - [06-lookup.pl](chapter-22/06-lookup.pl)
133
+ - [07-simplify.pl](chapter-22/07-simplify.pl) — Rewriting symbolic expressions
129
134
 
130
135
  ## Chapter 23: Transforming programs
131
136
 
@@ -140,22 +145,25 @@ npm run generate
140
145
 
141
146
  ## Chapter 24: Designing finite search
142
147
 
143
- - [01-simple_path.pl](chapter-24/01-simple_path.pl) — Search over states
144
- - [02-reachable.pl](chapter-24/02-reachable.pl) — Existence, one witness, and all witnesses
145
- - [03-best_plan.pl](chapter-24/03-best_plan.pl) — Optimization is search plus an order
148
+ - [01-worker.pl](chapter-24/01-worker.pl) — Generate, constrain, describe
149
+ - [02-simple_path.pl](chapter-24/02-simple_path.pl) — Search over states
150
+ - [03-reachable.pl](chapter-24/03-reachable.pl) — Existence, one witness, and all witnesses
151
+ - [04-best_plan.pl](chapter-24/04-best_plan.pl) — Optimization is search plus an order
146
152
 
147
153
  ## Chapter 25: Case study: an auditable decision service
148
154
 
149
155
  - [01-person.pl](chapter-25/01-person.pl) — Source and concept layers
150
156
  - [02-active_badge.pl](chapter-25/02-active_badge.pl)
151
157
  - [03-in_good_standing.pl](chapter-25/03-in_good_standing.pl) — Closed-world choice
152
- - [04-incompatible_status.pl](chapter-25/04-incompatible_status.pl) — Integrity before decisions
158
+ - [04-permit.pl](chapter-25/04-permit.pl) — Decision, reasons, and proof
159
+ - [05-incompatible_status.pl](chapter-25/05-incompatible_status.pl) — Integrity before decisions
153
160
 
154
161
  ## Chapter 26: A proof can be a computation
155
162
 
156
163
  - [01-mortal.pl](chapter-26/01-mortal.pl)
157
- - [02-triple.pl](chapter-26/02-triple.pl) — Proof objects and proof checking
158
- - [03-edge.pl](chapter-26/03-edge.pl) — The least model as mathematical closure
164
+ - [02-triple.pl](chapter-26/02-triple.pl) — Answers are existential witnesses
165
+ - [03-triple-2.pl](chapter-26/03-triple-2.pl) — Proof objects and proof checking
166
+ - [04-edge.pl](chapter-26/04-edge.pl) — The least model as mathematical closure
159
167
 
160
168
  ## Chapter 27: Recursion is induction in motion
161
169
 
@@ -168,16 +176,26 @@ npm run generate
168
176
  - [01-program.pl](chapter-28/01-program.pl) — Unification is structural equation solving
169
177
  - [02-triangle.pl](chapter-28/02-triangle.pl) — Symmetry reduces search
170
178
  - [03-rectangle.pl](chapter-28/03-rectangle.pl) — Relations reveal inverse problems
171
- - [04-preserves_combine.pl](chapter-28/04-preserves_combine.pl) — Composition, homomorphism, and reusable laws
179
+ - [04-integer_rectangle.pl](chapter-28/04-integer_rectangle.pl)
180
+ - [05-preserves_combine.pl](chapter-28/05-preserves_combine.pl) — Composition, homomorphism, and reusable laws
172
181
 
173
182
  ## Chapter 29: Search as experimental mathematics
174
183
 
175
- - [01-noncommuting_pair.pl](chapter-29/01-noncommuting_pair.pl) — One counterexample has asymmetric power
184
+ - [01-counterexample_to_odd_square.pl](chapter-29/01-counterexample_to_odd_square.pl) — Examples suggest; proofs compel
185
+ - [02-noncommuting_pair.pl](chapter-29/02-noncommuting_pair.pl) — One counterexample has asymmetric power
186
+
187
+ ## Chapter 31: Testing a theory
188
+
189
+ - [01-edge.pl](chapter-31/01-edge.pl) — Positive and negative observers
190
+ - [02-program.pl](chapter-31/02-program.pl)
191
+ - [03-double.pl](chapter-31/03-double.pl) — Properties over finite domains
176
192
 
177
193
  ## Chapter 32: Debugging by meaning, search, and proof
178
194
 
179
195
  - [01-eligible.pl](chapter-32/01-eligible.pl) — Follow bindings from left to right
180
196
  - [02-eligible-2.pl](chapter-32/02-eligible-2.pl)
197
+ - [03-candidate_debug.pl](chapter-32/03-candidate_debug.pl) — Create diagnostic relations
198
+ - [04-reference_square.pl](chapter-32/04-reference_square.pl) — Compare specification and implementation
181
199
 
182
200
  ## Chapter 33: A pattern catalog for reasoning
183
201
 
@@ -215,3 +233,11 @@ npm run generate
215
233
  ## Chapter 38: Language and ISO profile
216
234
 
217
235
  - [01-city.pl](chapter-38/01-city.pl)
236
+
237
+ ## Chapter 39: Built-in predicates by programming role
238
+
239
+ - [01-answer.pl](chapter-39/01-answer.pl) — The EyeProlog library
240
+ - [02-answer-2.pl](chapter-39/02-answer-2.pl)
241
+ - [03-answer-3.pl](chapter-39/03-answer-3.pl)
242
+ - [04-cost.pl](chapter-39/04-cost.pl)
243
+ - [05-message.pl](chapter-39/05-message.pl)
@@ -1,4 +1,2 @@
1
1
  % From The Art of EyeProlog, Chapter 1.
2
- child(byron, ada).
3
- child(clara, byron).
4
- child(diego, clara).
2
+ child(Child, Parent) :- parent(Parent, Child).
@@ -0,0 +1,2 @@
1
+ % From The Art of EyeProlog, Chapter 2 — Unification.
2
+ same_shape(Pair) :- (Pair = pair(X, X)).
@@ -0,0 +1,3 @@
1
+ % From The Art of EyeProlog, Chapter 4.
2
+ ancestor(X, Y) :- parent(X, Y).
3
+ ancestor(X, Z) :- parent(X, Y), ancestor(Y, Z).
@@ -0,0 +1,14 @@
1
+ % From The Art of EyeProlog, Chapter 10.
2
+ color(red).
3
+ color(green).
4
+ color(blue).
5
+
6
+ coloring(A, B, C) :-
7
+ color(A),
8
+ color(B),
9
+ (A \= B),
10
+ color(C),
11
+ (B \= C),
12
+ (A \= C).
13
+
14
+ answer(colors(A, B, C)) :- coloring(A, B, C).
@@ -0,0 +1,7 @@
1
+ % From The Art of EyeProlog, Chapter 21.
2
+ parent(ada, byron).
3
+ parent(byron, clara).
4
+ parent(clara, diego).
5
+
6
+ ancestor(X, Y) :- parent(X, Y).
7
+ ancestor(X, Z) :- parent(X, Y), ancestor(Y, Z).
@@ -0,0 +1,19 @@
1
+ % From The Art of EyeProlog, Chapter 22 — A grammar without special syntax.
2
+ sentence(Input, Rest) :-
3
+ noun_phrase(Input, AfterNoun),
4
+ verb_phrase(AfterNoun, Rest).
5
+
6
+ noun_phrase([the | Input], Rest) :- noun(Input, Rest).
7
+ noun_phrase([a | Input], Rest) :- noun(Input, Rest).
8
+
9
+ noun([robot | Rest], Rest).
10
+ noun([scientist | Rest], Rest).
11
+
12
+ verb_phrase(Input, Rest) :-
13
+ verb(Input, AfterVerb),
14
+ noun_phrase(AfterVerb, Rest).
15
+
16
+ verb([helps | Rest], Rest).
17
+ verb([observes | Rest], Rest).
18
+
19
+ complete_sentence(Words) :- sentence(Words, []).
@@ -0,0 +1,17 @@
1
+ % From The Art of EyeProlog, Chapter 24 — Generate, constrain, describe.
2
+ worker(ada).
3
+ worker(byron).
4
+ worker(clara).
5
+
6
+ task(inspect).
7
+ task(repair).
8
+
9
+ qualified(ada, inspect).
10
+ qualified(byron, repair).
11
+ qualified(clara, inspect).
12
+ qualified(clara, repair).
13
+
14
+ assignment(Worker, Task) :-
15
+ worker(Worker),
16
+ task(Task),
17
+ qualified(Worker, Task).
@@ -0,0 +1,9 @@
1
+ % From The Art of EyeProlog, Chapter 25 — Decision, reasons, and proof.
2
+ permit(Person, Zone) :-
3
+ active_badge(Person, Badge),
4
+ cleared_for(Badge, Zone),
5
+ prepared_for(Person, Zone),
6
+ in_good_standing(Person).
7
+
8
+ reason(Person, Zone, badge_and_training_verified) :-
9
+ permit(Person, Zone).
@@ -1,2 +1,9 @@
1
- % From The Art of EyeProlog, Chapter 26 — Proof objects and proof checking.
2
- triple(3, 4, 5).
1
+ % From The Art of EyeProlog, Chapter 26 — Answers are existential witnesses.
2
+ triple(A, B, C) :-
3
+ between(1, 20, A),
4
+ between(A, 20, B),
5
+ between(B, 20, C),
6
+ (AA is A * A),
7
+ (BB is B * B),
8
+ (Sum is AA + BB),
9
+ (Sum is C * C).
@@ -0,0 +1,2 @@
1
+ % From The Art of EyeProlog, Chapter 26 — Proof objects and proof checking.
2
+ triple(3, 4, 5).
@@ -0,0 +1,5 @@
1
+ % From The Art of EyeProlog, Chapter 28.
2
+ integer_rectangle(Area, W, H) :-
3
+ between(1, Area, W),
4
+ between(W, Area, H),
5
+ (Area is W * H).
@@ -0,0 +1,7 @@
1
+ % From The Art of EyeProlog, Chapter 29 — Examples suggest; proofs compel.
2
+ counterexample_to_odd_square(N) :-
3
+ between(1, 100, N),
4
+ (1 is N mod 2),
5
+ (Square is N * N),
6
+ (Remainder is Square mod 2),
7
+ (Remainder \= 1).
@@ -0,0 +1,6 @@
1
+ % From The Art of EyeProlog, Chapter 31 — Positive and negative observers.
2
+ edge(a, b).
3
+ edge(b, c).
4
+
5
+ path(X, Y) :- edge(X, Y).
6
+ path(X, Z) :- edge(X, Y), path(Y, Z).
@@ -0,0 +1,6 @@
1
+ % From The Art of EyeProlog, Chapter 31.
2
+ unexpected_path :-
3
+ path(a, d).
4
+
5
+ expected_absence :-
6
+ \+ unexpected_path.
@@ -0,0 +1,13 @@
1
+ % From The Art of EyeProlog, Chapter 31 — Properties over finite domains.
2
+ double(N, D) :- (D is N + N).
3
+
4
+ double_is_even(N) :-
5
+ double(N, D),
6
+ (0 is D mod 2).
7
+
8
+ bounded_double_law :-
9
+ \+ bounded_double_counterexample.
10
+
11
+ bounded_double_counterexample :-
12
+ between(-100, 100, N),
13
+ \+ double_is_even(N).
@@ -0,0 +1,7 @@
1
+ % From The Art of EyeProlog, Chapter 32 — Create diagnostic relations.
2
+ candidate_debug(Person, Age) :-
3
+ age(Person, Age).
4
+
5
+ adult_debug(Person, Age) :-
6
+ candidate_debug(Person, Age),
7
+ (Age >= 18).
@@ -0,0 +1,12 @@
1
+ % From The Art of EyeProlog, Chapter 32 — Compare specification and implementation.
2
+ reference_square(N, S) :-
3
+ between(0, 20, N),
4
+ (S is N * N).
5
+
6
+ optimized_square(N, S) :-
7
+ between(0, 20, N),
8
+ (S is N * N).
9
+
10
+ disagreement(N, S) :-
11
+ reference_square(N, S),
12
+ \+ optimized_square(N, S).
@@ -0,0 +1,5 @@
1
+ % From The Art of EyeProlog, Chapter 39 — The EyeProlog library.
2
+ answer(square, S) :- (S is 12 * 12).
3
+ answer(day_count, N) :- between(3, 5, N).
4
+ answer(age, D) :- difference('2026-07-28', '2020-05-20', D).
5
+ answer(random_pair, [A,B]) :- random(42, A, S), random(S, B, _).
@@ -0,0 +1,6 @@
1
+ % From The Art of EyeProlog, Chapter 39.
2
+ answer(split, pair(Prefix, Suffix)) :-
3
+ append(Prefix, Suffix, [a, b]).
4
+
5
+ answer(second, Item) :-
6
+ nth0(1, [a, b, c], Item).
@@ -0,0 +1,10 @@
1
+ % From The Art of EyeProlog, Chapter 39.
2
+ answer(words, Words) :-
3
+ trim(' Logic Made Visible ', Clean),
4
+ lowercase(Clean, Lower),
5
+ split(Lower, ' ', Words).
6
+
7
+ answer(captures, Context) :-
8
+ matches('Ada Lovelace',
9
+ '^(?<first>[A-Za-z]+) (?<last>[A-Za-z]+)$',
10
+ Context).
@@ -0,0 +1,10 @@
1
+ % From The Art of EyeProlog, Chapter 39.
2
+ cost(a, 8).
3
+ cost(b, 3).
4
+ cost(c, 3).
5
+
6
+ answer(count, N) :- countall(cost(_, _), N).
7
+ answer(best(Name), Cost) :-
8
+ aggregate_min(CandidateCost, CandidateName,
9
+ cost(CandidateName, CandidateCost),
10
+ Cost, Name).
@@ -0,0 +1,16 @@
1
+ % From The Art of EyeProlog, Chapter 39.
2
+ message(event_17,
3
+ (severity(high), source(sensor_3), reading(temp, 91))).
4
+
5
+ context_member((Left, _right), Member) :- context_member(Left, Member).
6
+ context_member((_left, Right), Member) :- context_member(Right, Member).
7
+ context_member(Member, Member) :- Member \= (_left, _right).
8
+
9
+ context_parts(Context, Name, Args) :-
10
+ context_member(Context, Member),
11
+ (Member =.. [Name | Args]),
12
+ atom(Name).
13
+
14
+ answer(field(Name, Args)) :-
15
+ message(event_17, Context),
16
+ context_parts(Context, Name, Args).
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.0.0",
6
+ "version": "1.0.2",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
@@ -1960,6 +1960,11 @@ function documentationSourceStyleIssues() {
1960
1960
  if (/\bv\d+\.\d+(?:\.\d+)?\b/i.test(text)) {
1961
1961
  issues.push('the-art-of-eyeprolog.md: describe the current system instead of release chronology');
1962
1962
  }
1963
+ for (const block of text.matchAll(/^```eyeprolog\s*\n([\s\S]*?)^```\s*$/gm)) {
1964
+ if (/^\s*(?:eyeprolog|node|npm)\b/m.test(block[1])) {
1965
+ issues.push('the-art-of-eyeprolog.md: keep host commands outside eyeprolog code fences');
1966
+ }
1967
+ }
1963
1968
  return issues;
1964
1969
  }
1965
1970
 
@@ -2,8 +2,7 @@
2
2
  <img src="book-assets/title-page.svg" alt="Front page for The Art of EyeProlog, presenting ISO Prolog rules and inspectable proofs." width="720">
3
3
  </p>
4
4
 
5
- **Copyright © 2021–2026 Jos De Roo, KNoWS office of IDLab, Ghent University –
6
- imec.** This book is licensed under [Creative Commons Attribution 4.0
5
+ This book is licensed under [Creative Commons Attribution 4.0
7
6
  International](https://creativecommons.org/licenses/by/4.0/). You may copy,
8
7
  share, and adapt it for any purpose, including commercially; please give
9
8
  appropriate credit, link to the licence, and indicate changes.
@@ -60,11 +59,10 @@ Robert Kowalski's phrase “algorithm = logic + control” names this separation
60
59
  EyeProlog's focused surface makes it unusually easy to see in running examples.
61
60
 
62
61
  Complete EyeProlog code displays from the book are also available as files under
63
- [`examples/book/`](https://github.com/eyereasoner/eyeprolog/tree/main/examples/book/), grouped by chapter. From a source checkout,
64
- use Node.js 18 or newer, install the dependencies, and run the CLI:
62
+ [`examples/book/`](https://github.com/eyereasoner/eyeprolog/tree/main/examples/book/), grouped by chapter. From a source checkout
63
+ with Node.js 18 or newer, run the CLI directly:
65
64
 
66
65
  ```sh
67
- npm install
68
66
  node bin/eyeprolog.js examples/socrates.pl
69
67
  ```
70
68
 
@@ -357,16 +355,20 @@ parent(clara, diego).
357
355
  Each line is a **fact**. `parent/2` is a relation: the name is `parent` and the
358
356
  arity is two. Arity matters. `parent/2` and `parent/3` are different predicates.
359
357
 
360
- A **query declaration** selects the relation whose ground answers EyeProlog prints:
358
+ A host-supplied **query** selects the relation whose ground answers EyeProlog
359
+ prints:
361
360
 
362
361
  ```eyeprolog
363
362
  child(Child, Parent) :- parent(Parent, Child).
363
+ ```
364
+
365
+ ```sh
364
366
  eyeprolog --goal 'child(X, Y)' program.pl
365
367
  ```
366
368
 
367
369
  The answers are:
368
370
 
369
- ```eyeprolog
371
+ ```text
370
372
  child(byron, ada).
371
373
  child(clara, byron).
372
374
  child(diego, clara).
@@ -479,7 +481,9 @@ EyeProlog exposes unification as `=/2`:
479
481
 
480
482
  ```eyeprolog
481
483
  same_shape(Pair) :- (Pair = pair(X, X)).
484
+ ```
482
485
 
486
+ ```sh
483
487
  eyeprolog --goal 'same_shape(pair(red, red))' program.pl
484
488
  eyeprolog --goal 'same_shape(pair(red, blue))' program.pl
485
489
  ```
@@ -797,6 +801,9 @@ parent, or a parent of an ancestor:
797
801
  ```eyeprolog
798
802
  ancestor(X, Y) :- parent(X, Y).
799
803
  ancestor(X, Z) :- parent(X, Y), ancestor(Y, Z).
804
+ ```
805
+
806
+ ```sh
800
807
  eyeprolog --goal 'ancestor(X, Y)' program.pl
801
808
  ```
802
809
 
@@ -1222,6 +1229,9 @@ coloring(A, B, C) :-
1222
1229
  (A \= C).
1223
1230
 
1224
1231
  answer(colors(A, B, C)) :- coloring(A, B, C).
1232
+ ```
1233
+
1234
+ ```sh
1225
1235
  eyeprolog --goal 'answer(X)' program.pl
1226
1236
  ```
1227
1237
 
@@ -1531,7 +1541,7 @@ A maintainable theory separates:
1531
1541
  - source facts: measurements, records, and asserted relationships;
1532
1542
  - helpers: normalization, classifications, and reachability;
1533
1543
  - decisions: `status/2`, `action/2`, `risk/2`, and `reason/2`;
1534
- - integrity constraints: rules headed by `false`;
1544
+ - integrity relations: predicates that return diagnostic invalid-state witnesses;
1535
1545
  - outputs: focused host-supplied goals.
1536
1546
 
1537
1547
  <figure>
@@ -1589,6 +1599,62 @@ A boundary should make four decisions visible:
1589
1599
  - which predicates the imported clauses may define; and
1590
1600
  - which resource limits apply to the resulting query.
1591
1601
 
1602
+ ### A boundary in four steps
1603
+
1604
+ Suppose a host receives one JSON temperature record. The host, not the logic
1605
+ program, owns the JSON syntax and the decision to trust that record. A narrow
1606
+ adapter can validate the record, map its values into a deliberately small
1607
+ Prolog vocabulary, construct the theory, and ask one bounded question:
1608
+
1609
+ ```js
1610
+ import { run } from 'eyeprolog';
1611
+
1612
+ const inputText = '{"sensor":"sensor_1","celsius":91}';
1613
+ const allowedSensors = new Set(['sensor_1', 'sensor_2']);
1614
+
1615
+ function reasoningSource(record) {
1616
+ if (!record || typeof record !== 'object') throw new TypeError('record');
1617
+ if (!allowedSensors.has(record.sensor)) throw new TypeError('sensor');
1618
+ if (!Number.isFinite(record.celsius)) throw new TypeError('celsius');
1619
+ if (record.celsius < -100 || record.celsius > 200) {
1620
+ throw new RangeError('celsius');
1621
+ }
1622
+
1623
+ return `
1624
+ reading(${record.sensor}, ${record.celsius}).
1625
+ thermal_alert(Sensor) :-
1626
+ reading(Sensor, Celsius),
1627
+ (Celsius >= 80).
1628
+ `;
1629
+ }
1630
+
1631
+ const record = JSON.parse(inputText);
1632
+ const result = run(reasoningSource(record), {
1633
+ goal: `thermal_alert(${record.sensor})`,
1634
+ proof: true,
1635
+ maxDepth: 10_000,
1636
+ solutionLimit: 10
1637
+ });
1638
+
1639
+ console.log(result.stdout);
1640
+ ```
1641
+
1642
+ The allow-list makes interpolation safe here: the external sensor identifier
1643
+ can become only one of two known Prolog atoms, and the temperature must be a
1644
+ finite number in an accepted range. General text must be encoded with a
1645
+ well-tested term constructor or serializer rather than inserted into source.
1646
+ The generated program defines only `reading/2` and the fixed domain rule; the
1647
+ host supplies the goal and ceilings explicitly.
1648
+
1649
+ This small example exposes four different claims:
1650
+
1651
+ | Stage | Claim and owner |
1652
+ | --- | --- |
1653
+ | Parse | the bytes are valid JSON — host parser |
1654
+ | Validate | the record has an accepted sensor and temperature — adapter |
1655
+ | Convert | the accepted values denote these exact Prolog terms — adapter |
1656
+ | Derive | the supplied reading satisfies `thermal_alert/1` — EyeProlog proof |
1657
+
1592
1658
  The proof procedure can explain how supplied clauses support an answer. It
1593
1659
  cannot prove that a file, database, sensor, or remote service was trustworthy.
1594
1660
  That responsibility stays with the host application.
@@ -1602,12 +1668,11 @@ resource limit that prevents an untrusted input from consuming unbounded work.
1602
1668
  The JavaScript API exposes a convenience runner and lower-level types:
1603
1669
 
1604
1670
  ```js
1605
- import { run, Program, Solver } from 'eyeprolog';
1671
+ import { run, Program, Solver, parseGoalText } from 'eyeprolog';
1606
1672
 
1607
1673
  const result = run(`
1608
- eyeprolog --goal 'answer(X)' program.pl
1609
1674
  answer(ok) :- ok = ok.
1610
- `);
1675
+ `, { goal: 'answer(X)' });
1611
1676
  console.log(result.stdout);
1612
1677
  console.log(result.stats);
1613
1678
  ```
@@ -1627,7 +1692,6 @@ For applications that inspect or prepare a theory before running it, use
1627
1692
 
1628
1693
  ```js
1629
1694
  const source = `
1630
- eyeprolog --goal 'path(a, X)' program.pl
1631
1695
  edge(a, b).
1632
1696
  edge(b, c).
1633
1697
  path(X, Y) :- edge(X, Y).
@@ -1635,9 +1699,10 @@ path(X, Z) :- edge(X, Y), path(Y, Z).
1635
1699
  `;
1636
1700
 
1637
1701
  const program = Program.parse(source, { analyzeNegation: true });
1702
+ const goal = parseGoalText('path(a, X)');
1638
1703
  const path = program.findGroup('path', 2);
1639
1704
 
1640
- console.log(program.queries);
1705
+ console.log(goal);
1641
1706
  console.log(program.stratifiedNegation);
1642
1707
  console.log(path?.recursive, path?.tabled, path?.tableInputPositions);
1643
1708
 
@@ -1700,9 +1765,8 @@ registry.add(
1700
1765
  );
1701
1766
 
1702
1767
  const result = run(`
1703
- eyeprolog --goal 'answer(X)' program.pl
1704
1768
  answer(X) :- host_status(service, X).
1705
- `, { registry });
1769
+ `, { registry, goal: 'answer(X)' });
1706
1770
  ```
1707
1771
 
1708
1772
  Only mark a built-in deterministic when it can produce at most one environment
@@ -2088,8 +2152,8 @@ constructs `s(s(s(...)))` without bound.
2088
2152
 
2089
2153
  ### Negation and aggregation require bounded subsearch
2090
2154
 
2091
- `\+ Goal`, `forall/2`, and aggregates ask the engine to settle a nested
2092
- search. Their meaning is usable only when that search can finish. Before
2155
+ `\+ Goal` and aggregates ask the engine to settle a nested search. Their
2156
+ meaning is usable only when that search can finish. Before
2093
2157
  writing:
2094
2158
 
2095
2159
  ```eyeprolog
@@ -2282,7 +2346,9 @@ parent(clara, diego).
2282
2346
 
2283
2347
  ancestor(X, Y) :- parent(X, Y).
2284
2348
  ancestor(X, Z) :- parent(X, Y), ancestor(Y, Z).
2349
+ ```
2285
2350
 
2351
+ ```sh
2286
2352
  eyeprolog --goal 'ancestor(ada, Who)' program.pl
2287
2353
  ```
2288
2354
 
@@ -2488,7 +2554,9 @@ verb([helps | Rest], Rest).
2488
2554
  verb([observes | Rest], Rest).
2489
2555
 
2490
2556
  complete_sentence(Words) :- sentence(Words, []).
2557
+ ```
2491
2558
 
2559
+ ```sh
2492
2560
  eyeprolog --goal 'complete_sentence([the, robot, helps, a, scientist])' program.pl
2493
2561
  ```
2494
2562
 
@@ -2521,7 +2589,7 @@ evaluate(multiply(Left, Right), Value) :-
2521
2589
  (Value is L * R).
2522
2590
  ```
2523
2591
 
2524
- ```eyeprolog
2592
+ ```sh
2525
2593
  eyeprolog --goal 'evaluate(
2526
2594
  add(number(2), multiply(number(3), number(4))),
2527
2595
  Value
@@ -2777,7 +2845,9 @@ assignment(Worker, Task) :-
2777
2845
  worker(Worker),
2778
2846
  task(Task),
2779
2847
  qualified(Worker, Task).
2848
+ ```
2780
2849
 
2850
+ ```sh
2781
2851
  eyeprolog --goal 'assignment(Worker, Task)' program.pl
2782
2852
  ```
2783
2853
 
@@ -2960,7 +3030,9 @@ permit(Person, Zone) :-
2960
3030
 
2961
3031
  reason(Person, Zone, badge_and_training_verified) :-
2962
3032
  permit(Person, Zone).
3033
+ ```
2963
3034
 
3035
+ ```sh
2964
3036
  eyeprolog --goal 'permit(Person, Zone)' program.pl
2965
3037
  eyeprolog --goal 'reason(Person, Zone, Reason)' program.pl
2966
3038
  ```
@@ -3097,9 +3169,9 @@ inherits the twentieth century's harder questions. What counts as a formal
3097
3169
  proof? What is an effective procedure? Which truths follow from a finite set
3098
3170
  of axioms? Which questions cannot be decided by any uniform mechanical method?
3099
3171
 
3100
- EyeProlog is a very small descendant of those questions. It is not a foundation
3101
- for all mathematics, a computer algebra system, or an interactive theorem
3102
- prover. Its definite clauses cover only a disciplined fragment of logic.
3172
+ EyeProlog inherits a small, practical fragment of that tradition. It is not a
3173
+ foundation for all mathematics, a computer algebra system, or an interactive
3174
+ theorem prover. Its definite clauses cover only a disciplined fragment of logic.
3103
3175
  Precisely because the fragment is small, however, one can see the ancient
3104
3176
  mathematical acts inside the running machine:
3105
3177
 
@@ -3196,7 +3268,9 @@ triple(A, B, C) :-
3196
3268
  (BB is B * B),
3197
3269
  (Sum is AA + BB),
3198
3270
  (Sum is C * C).
3271
+ ```
3199
3272
 
3273
+ ```sh
3200
3274
  eyeprolog --goal 'triple(A, B, C)' program.pl
3201
3275
  ```
3202
3276
 
@@ -3503,7 +3577,9 @@ integer_rectangle(Area, W, H) :-
3503
3577
  between(1, Area, W),
3504
3578
  between(W, Area, H),
3505
3579
  (Area is W * H).
3580
+ ```
3506
3581
 
3582
+ ```sh
3507
3583
  eyeprolog --goal 'integer_rectangle(24, W, H)' program.pl
3508
3584
  ```
3509
3585
 
@@ -3534,9 +3610,10 @@ preserves_combine(X, Y) :-
3534
3610
  (IXY = CombinedImages).
3535
3611
  ```
3536
3612
 
3537
- Over a finite carrier, `forall/2` can test the law for every generated pair.
3538
- Over an infinite carrier, finite testing is evidence, not proof. The algebraic
3539
- law must instead follow from definitions or a stronger proof system.
3613
+ Over a finite carrier, define a relation for a violating pair and use ISO
3614
+ `\+/1` to ask whether that counterexample relation has any answer. Over an
3615
+ infinite carrier, finite testing is evidence, not proof. The algebraic law must
3616
+ instead follow from definitions or a stronger proof system.
3540
3617
 
3541
3618
  The examples `d3-group.pl`, `matrix-noncommutativity.pl`,
3542
3619
  `group-inverse-uniqueness.pl`, and
@@ -3609,7 +3686,9 @@ counterexample_to_odd_square(N) :-
3609
3686
  (Square is N * N),
3610
3687
  (Remainder is Square mod 2),
3611
3688
  (Remainder \= 1).
3689
+ ```
3612
3690
 
3691
+ ```sh
3613
3692
  eyeprolog --goal 'counterexample_to_odd_square(N)' program.pl
3614
3693
  ```
3615
3694
 
@@ -3974,7 +4053,9 @@ edge(b, c).
3974
4053
 
3975
4054
  path(X, Y) :- edge(X, Y).
3976
4055
  path(X, Z) :- edge(X, Y), path(Y, Z).
4056
+ ```
3977
4057
 
4058
+ ```sh
3978
4059
  eyeprolog --goal 'path(a, b)' program.pl
3979
4060
  eyeprolog --goal 'path(a, c)' program.pl
3980
4061
  ```
@@ -3987,7 +4068,9 @@ unexpected_path :-
3987
4068
 
3988
4069
  expected_absence :-
3989
4070
  \+ unexpected_path.
4071
+ ```
3990
4072
 
4073
+ ```sh
3991
4074
  eyeprolog --goal 'expected_absence' program.pl
3992
4075
  ```
3993
4076
 
@@ -4003,7 +4086,7 @@ golden answer file is an executable specification of the expected answer set.
4003
4086
 
4004
4087
  Suppose `append/3` is intended both to concatenate and to split:
4005
4088
 
4006
- ```eyeprolog
4089
+ ```sh
4007
4090
  eyeprolog --goal 'append([a, b], [c], Whole)' program.pl
4008
4091
  eyeprolog --goal 'append(Prefix, Suffix, [a, b])' program.pl
4009
4092
  ```
@@ -4042,7 +4125,9 @@ bounded_double_law :-
4042
4125
  bounded_double_counterexample :-
4043
4126
  between(-100, 100, N),
4044
4127
  \+ double_is_even(N).
4128
+ ```
4045
4129
 
4130
+ ```sh
4046
4131
  eyeprolog --goal 'bounded_double_law' program.pl
4047
4132
  ```
4048
4133
 
@@ -4157,7 +4242,7 @@ Use four views in a fixed order:
4157
4242
  Do not begin with an open query that prints hundreds of answers. Name one
4158
4243
  conclusion that is missing or surprising:
4159
4244
 
4160
- ```eyeprolog
4245
+ ```sh
4161
4246
  eyeprolog --goal 'eligible(alex)' program.pl
4162
4247
  ```
4163
4248
 
@@ -4252,7 +4337,9 @@ candidate_debug(Person, Age) :-
4252
4337
  adult_debug(Person, Age) :-
4253
4338
  candidate_debug(Person, Age),
4254
4339
  (Age >= 18).
4340
+ ```
4255
4341
 
4342
+ ```sh
4256
4343
  eyeprolog --goal 'candidate_debug(Person, Age)' program.pl
4257
4344
  eyeprolog --goal 'adult_debug(Person, Age)' program.pl
4258
4345
  ```
@@ -4279,7 +4366,9 @@ optimized_square(N, S) :-
4279
4366
  disagreement(N, S) :-
4280
4367
  reference_square(N, S),
4281
4368
  \+ optimized_square(N, S).
4369
+ ```
4282
4370
 
4371
+ ```sh
4283
4372
  eyeprolog --goal 'disagreement(N, S)' program.pl
4284
4373
  ```
4285
4374
 
@@ -5048,7 +5137,7 @@ Supported output syntax is designed to be readable as Prolog input accepted by E
5048
5137
  #### Automatic hybrid reasoning
5049
5138
 
5050
5139
  The program loader detects predicate-dependency cycles, including dependencies
5051
- inside conjunction, `\+/1`, `once/1`, `forall/2`, and aggregate goals.
5140
+ inside conjunction, `\+/1`, `once/1`, and aggregate goals.
5052
5141
  Positive recursive components—including directly queried recursive
5053
5142
  relations—are tabled to an answer fixed point before answers are replayed.
5054
5143
  Components with a negative dependency retain guarded ordinary resolution,
@@ -5451,7 +5540,12 @@ JavaScript uses the same registry by default:
5451
5540
  ```js
5452
5541
  import { run } from 'eyeprolog';
5453
5542
 
5454
- const result = run(source);
5543
+ const source = `
5544
+ answer(Whole) :- append([red, green], [blue], Whole).
5545
+ `;
5546
+
5547
+ const result = run(source, { goal: 'answer(X)' });
5548
+ console.log(result.stdout);
5455
5549
  ```
5456
5550
 
5457
5551
  The mode notation below is descriptive:
@@ -5480,6 +5574,9 @@ answer(square, S) :- (S is 12 * 12).
5480
5574
  answer(day_count, N) :- between(3, 5, N).
5481
5575
  answer(age, D) :- difference('2026-07-28', '2020-05-20', D).
5482
5576
  answer(random_pair, [A,B]) :- random(42, A, S), random(S, B, _).
5577
+ ```
5578
+
5579
+ ```sh
5483
5580
  eyeprolog --goal 'answer(Kind, Value)' program.pl
5484
5581
  ```
5485
5582
 
@@ -5530,7 +5627,9 @@ answer(split, pair(Prefix, Suffix)) :-
5530
5627
 
5531
5628
  answer(second, Item) :-
5532
5629
  nth0(1, [a, b, c], Item).
5630
+ ```
5533
5631
 
5632
+ ```sh
5534
5633
  eyeprolog --goal 'answer(Kind, Value)' program.pl
5535
5634
  ```
5536
5635
 
@@ -5572,7 +5671,9 @@ answer(captures, Context) :-
5572
5671
  matches('Ada Lovelace',
5573
5672
  '^(?<first>[A-Za-z]+) (?<last>[A-Za-z]+)$',
5574
5673
  Context).
5674
+ ```
5575
5675
 
5676
+ ```sh
5576
5677
  eyeprolog --goal 'answer(Kind, Value)' program.pl
5577
5678
  ```
5578
5679
 
@@ -5608,7 +5709,9 @@ answer(best(Name), Cost) :-
5608
5709
  aggregate_min(CandidateCost, CandidateName,
5609
5710
  cost(CandidateName, CandidateCost),
5610
5711
  Cost, Name).
5712
+ ```
5611
5713
 
5714
+ ```sh
5612
5715
  eyeprolog --goal 'answer(Kind, Value)' program.pl
5613
5716
  ```
5614
5717
 
@@ -5633,7 +5736,9 @@ context_parts(Context, Name, Args) :-
5633
5736
  answer(field(Name, Args)) :-
5634
5737
  message(event_17, Context),
5635
5738
  context_parts(Context, Name, Args).
5739
+ ```
5636
5740
 
5741
+ ```sh
5637
5742
  eyeprolog --goal 'answer(X)' program.pl
5638
5743
  ```
5639
5744
 
@@ -6230,8 +6335,8 @@ sentence, mode, finite domain, answer, proof, and revision.
6230
6335
  This book is the single reference for the EyeProlog implementation. Chapters 38–40
6231
6336
  describe its supported ISO Prolog syntax, directives, execution model,
6232
6337
  built-in predicates, and command-line interface. The earlier chapters explain the reasoner, automatic tabling,
6233
- proof terms, warnings, answer formatting, embedding, and external data
6234
- adapters.
6338
+ proof terms, warnings, answer formatting, embedding, and explicit host data
6339
+ boundaries.
6235
6340
 
6236
6341
  The executable corpus under `test/conformance/` tests the JavaScript
6237
6342
  implementation. Positive programs and exact output cover arithmetic, text relations,
@@ -6247,7 +6352,8 @@ The complete suite must pass before release. The file-based conformance corpus
6247
6352
  contains 710 cases, including 304 focused ISO
6248
6353
  cases derived from the success, failure, mode, and error behavior in
6249
6354
  ISO/IEC 13211-1 clauses 7 and 8. Separate exact-output suites check 188 normal
6250
- examples, 60 proof examples, and extracted book displays. The seven-case
6355
+ examples and 60 proof examples; all extracted book programs are parsed and
6356
+ their declared goals are executed. The seven-case
6251
6357
  playground contract suite imports the production worker, sends real reasoning
6252
6358
  requests through its message protocol, and crawls the served module graph for
6253
6359
  missing assets, bad MIME types, and static Node-only imports. The generated
@@ -6696,7 +6802,7 @@ deadlines.
6696
6802
  | Laboratories 3–4 | Chapters 6–10 and 13 | 4–8 hours each |
6697
6803
  | Laboratories 5–7 | Chapters 19 and 26–29 | 4–8 hours each |
6698
6804
  | Laboratories 8–10 | Chapters 14, 25, and 31–33 | 6–12 hours each |
6699
- | Laboratory 11 | Chapter 15 and `tools/README.md` | 4–8 hours |
6805
+ | Laboratory 11 | Chapters 15–16 | 4–8 hours |
6700
6806
  | Laboratory 12 | Chapters 16, 25, and 31–33 | multi-session capstone |
6701
6807
 
6702
6808
  ### Laboratory 1. A family theory
@@ -6990,7 +7096,7 @@ list bound, the prefix/suffix splits are:
6990
7096
  `[a | Tail]` is not yet known to be proper because `Tail` might never resolve
6991
7097
  to a finite chain ending in `[]`.
6992
7098
 
6993
- **Chapter 6.** `is/2`, `is/2`, `is/2`, comparisons, and the recursive
7099
+ **Chapter 6.** `is/2`, numeric comparisons, and the recursive
6994
7100
  arithmetic steps require their documented numeric inputs. In
6995
7101
  `between(1, 10, N)`, an unbound `N` is generated from a finite interval; a
6996
7102
  bound `N` is checked for membership in that interval.