eyelang 4.0.5 → 4.0.7

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 (73) hide show
  1. package/README.md +5 -1
  2. package/bin/eyelang.js +1 -1
  3. package/conformance-report.md +3 -3
  4. package/docs/guide.md +15 -14
  5. package/docs/language-reference.md +16 -14
  6. package/examples/abstract-interpretation.pl +0 -1
  7. package/examples/bayes-therapy.pl +1 -4
  8. package/examples/binomial-vandermonde.pl +1 -2
  9. package/examples/catalan-convolution.pl +1 -2
  10. package/examples/chart-parser.pl +1 -2
  11. package/examples/collatz-1000.pl +1 -2
  12. package/examples/continued-fraction-sqrt2.pl +1 -2
  13. package/examples/critical-path-schedule.pl +0 -2
  14. package/examples/dijkstra-risk-path.pl +0 -1
  15. package/examples/drone-corridor-planner.pl +0 -1
  16. package/examples/equality-saturation.pl +0 -2
  17. package/examples/family-cousins.pl +0 -3
  18. package/examples/fibonacci.pl +0 -1
  19. package/examples/gd-step-certified.pl +0 -7
  20. package/examples/gps.pl +0 -2
  21. package/examples/hamiltonian-path.pl +0 -1
  22. package/examples/hanoi.pl +0 -1
  23. package/examples/integer-partitions.pl +0 -1
  24. package/examples/intuitionistic-logic-kripke.pl +0 -2
  25. package/examples/knuth-bendix-completion.pl +0 -1
  26. package/examples/language.pl +1 -2
  27. package/examples/matrix-chain-order.pl +1 -2
  28. package/examples/modular-exponentiation.pl +0 -1
  29. package/examples/monkey-bananas.pl +0 -1
  30. package/examples/n-queens-8.pl +0 -1
  31. package/examples/partial-evaluator.pl +0 -1
  32. package/examples/peano-calculus.pl +0 -3
  33. package/examples/pell-equation.pl +1 -2
  34. package/examples/pointer-analysis.pl +0 -2
  35. package/examples/prime-range.pl +0 -1
  36. package/examples/proof/chart-parser.pl +71 -71
  37. package/examples/proof/hanoi.pl +16 -16
  38. package/examples/proof/intuitionistic-logic-kripke.pl +16 -16
  39. package/examples/route-planning.pl +0 -1
  40. package/examples/stirling-bell-numbers.pl +0 -4
  41. package/examples/totient-summatory.pl +1 -3
  42. package/examples/truth-maintenance-system.pl +0 -1
  43. package/examples/type-inference.pl +0 -1
  44. package/examples/web-names.pl +0 -1
  45. package/examples/weighted-interval-scheduling.pl +2 -3
  46. package/examples/wolf-goat-cabbage.pl +0 -1
  47. package/package.json +3 -4
  48. package/src/explain.js +3 -1
  49. package/src/program.js +24 -9
  50. package/test/conformance/README.md +2 -2
  51. package/test/conformance/cases/{table/038_table_declaration.pl → automatic-tabling/038_automatic_tabling.pl} +1 -2
  52. package/test/conformance/cases/{table/102_table_declaration.pl → automatic-tabling/102_recursive_inference.pl} +1 -2
  53. package/test/conformance/cases/{table/tabled_left_recursion_path.pl → automatic-tabling/automatic_left_recursion_path.pl} +0 -1
  54. package/test/conformance/cases/{table/table_does_not_change_answers.pl → automatic-tabling/automatic_tabling_preserves_answers.pl} +1 -2
  55. package/test/conformance/cases/{table/extra_table_bound_cycle.pl → automatic-tabling/extra_automatic_bound_cycle.pl} +0 -1
  56. package/test/conformance/cases/{table/extra_table_open_call_fallback.pl → automatic-tabling/extra_automatic_open_call_fallback.pl} +0 -1
  57. package/test/conformance/cases/rules/transitive_rule_three_edges.pl +0 -1
  58. package/test/conformance/expected-proofs/proofs/{table_recursive.pl → automatic_recursive.pl} +8 -8
  59. package/test/conformance/expected-proofs/proofs/{extra_table_path_bound.pl → extra_automatic_path_bound.pl} +8 -8
  60. package/test/conformance/proofs/proofs/{table_recursive.pl → automatic_recursive.pl} +1 -2
  61. package/test/conformance/proofs/proofs/{extra_table_path_bound.pl → extra_automatic_path_bound.pl} +0 -1
  62. package/test/conformance/warnings/negation/{extra_stratified_negation_with_table_quiet.pl → extra_stratified_negation_with_recursion_quiet.pl} +0 -1
  63. package/test/run-regression.mjs +11 -11
  64. package/test/conformance/cases/declarations/table_is_also_fact.pl +0 -4
  65. package/test/conformance/expected/declarations/table_is_also_fact.pl +0 -1
  66. /package/test/conformance/expected/{table/038_table_declaration.pl → automatic-tabling/038_automatic_tabling.pl} +0 -0
  67. /package/test/conformance/expected/{table/102_table_declaration.pl → automatic-tabling/102_recursive_inference.pl} +0 -0
  68. /package/test/conformance/expected/{table/tabled_left_recursion_path.pl → automatic-tabling/automatic_left_recursion_path.pl} +0 -0
  69. /package/test/conformance/expected/{table/table_does_not_change_answers.pl → automatic-tabling/automatic_tabling_preserves_answers.pl} +0 -0
  70. /package/test/conformance/expected/{table/extra_table_bound_cycle.pl → automatic-tabling/extra_automatic_bound_cycle.pl} +0 -0
  71. /package/test/conformance/expected/{table/extra_table_open_call_fallback.pl → automatic-tabling/extra_automatic_open_call_fallback.pl} +0 -0
  72. /package/test/conformance/expected-warnings/negation/{extra_stratified_negation_with_table_quiet.pl → extra_stratified_negation_with_recursion_quiet.pl} +0 -0
  73. /package/test/conformance/expected-warnings/negation/{extra_stratified_negation_with_table_quiet.txt → extra_stratified_negation_with_recursion_quiet.txt} +0 -0
@@ -3,30 +3,30 @@ why(
3
3
  intuitionistic_truth(monotone_p_reaches_both, both, atom(p)),
4
4
  proof(
5
5
  goal(intuitionistic_truth(monotone_p_reaches_both, both, atom(p))),
6
- by(rule("intuitionistic-logic-kripke.pl", clause(24))),
6
+ by(rule("intuitionistic-logic-kripke.pl", clause(22))),
7
7
  uses([
8
8
  proof(
9
9
  goal(forces(both, atom(p))),
10
- by(rule("intuitionistic-logic-kripke.pl", clause(17))),
10
+ by(rule("intuitionistic-logic-kripke.pl", clause(15))),
11
11
  bindings([binding("World", both), binding("Prop", p), binding("Someworld", left)]),
12
12
  uses([
13
13
  proof(
14
14
  goal(leq(left, both)),
15
- by(rule("intuitionistic-logic-kripke.pl", clause(16))),
15
+ by(rule("intuitionistic-logic-kripke.pl", clause(14))),
16
16
  bindings([binding("From", left), binding("To", both), binding("Mid", both)]),
17
17
  uses([
18
18
  proof(
19
19
  goal(step(left, both)),
20
- by(fact("intuitionistic-logic-kripke.pl", clause(11)))
20
+ by(fact("intuitionistic-logic-kripke.pl", clause(9)))
21
21
  ),
22
22
  proof(
23
23
  goal(leq(both, both)),
24
- by(rule("intuitionistic-logic-kripke.pl", clause(15))),
24
+ by(rule("intuitionistic-logic-kripke.pl", clause(13))),
25
25
  bindings([binding("World", both)]),
26
26
  uses([
27
27
  proof(
28
28
  goal(world(both)),
29
- by(fact("intuitionistic-logic-kripke.pl", clause(8)))
29
+ by(fact("intuitionistic-logic-kripke.pl", clause(6)))
30
30
  )
31
31
  ])
32
32
  )
@@ -34,7 +34,7 @@ why(
34
34
  ),
35
35
  proof(
36
36
  goal(base(left, p)),
37
- by(fact("intuitionistic-logic-kripke.pl", clause(13)))
37
+ by(fact("intuitionistic-logic-kripke.pl", clause(11)))
38
38
  )
39
39
  ])
40
40
  )
@@ -47,16 +47,16 @@ why(
47
47
  intuitionistic_truth(constructive_case_analysis, root, implies(atom(p), or(atom(p), atom(q)))),
48
48
  proof(
49
49
  goal(intuitionistic_truth(constructive_case_analysis, root, implies(atom(p), or(atom(p), atom(q))))),
50
- by(rule("intuitionistic-logic-kripke.pl", clause(25))),
50
+ by(rule("intuitionistic-logic-kripke.pl", clause(23))),
51
51
  uses([
52
52
  proof(
53
53
  goal(forces(root, implies(atom(p), or(atom(p), atom(q))))),
54
- by(rule("intuitionistic-logic-kripke.pl", clause(21))),
54
+ by(rule("intuitionistic-logic-kripke.pl", clause(19))),
55
55
  bindings([binding("World", root), binding("Left", atom(p)), binding("Right", or(atom(p), atom(q)))]),
56
56
  uses([
57
57
  proof(
58
58
  goal(world(root)),
59
- by(fact("intuitionistic-logic-kripke.pl", clause(5)))
59
+ by(fact("intuitionistic-logic-kripke.pl", clause(3)))
60
60
  ),
61
61
  proof(
62
62
  goal(not(bad_implication(root, atom(p), or(atom(p), atom(q))))),
@@ -73,21 +73,21 @@ why(
73
73
  intuitionistic_truth(double_negated_branch_information, root, neg(neg(or(atom(p), atom(q))))),
74
74
  proof(
75
75
  goal(intuitionistic_truth(double_negated_branch_information, root, neg(neg(or(atom(p), atom(q)))))),
76
- by(rule("intuitionistic-logic-kripke.pl", clause(26))),
76
+ by(rule("intuitionistic-logic-kripke.pl", clause(24))),
77
77
  uses([
78
78
  proof(
79
79
  goal(forces(root, neg(neg(or(atom(p), atom(q)))))),
80
- by(rule("intuitionistic-logic-kripke.pl", clause(22))),
80
+ by(rule("intuitionistic-logic-kripke.pl", clause(20))),
81
81
  bindings([binding("World", root), binding("Formula", neg(or(atom(p), atom(q))))]),
82
82
  uses([
83
83
  proof(
84
84
  goal(forces(root, implies(neg(or(atom(p), atom(q))), bottom))),
85
- by(rule("intuitionistic-logic-kripke.pl", clause(21))),
85
+ by(rule("intuitionistic-logic-kripke.pl", clause(19))),
86
86
  bindings([binding("World", root), binding("Left", neg(or(atom(p), atom(q)))), binding("Right", bottom)]),
87
87
  uses([
88
88
  proof(
89
89
  goal(world(root)),
90
- by(fact("intuitionistic-logic-kripke.pl", clause(5)))
90
+ by(fact("intuitionistic-logic-kripke.pl", clause(3)))
91
91
  ),
92
92
  proof(
93
93
  goal(not(bad_implication(root, neg(or(atom(p), atom(q))), bottom))),
@@ -106,7 +106,7 @@ why(
106
106
  intuitionistic_countermodel(root_does_not_decide_branch, root, or(atom(p), atom(q))),
107
107
  proof(
108
108
  goal(intuitionistic_countermodel(root_does_not_decide_branch, root, or(atom(p), atom(q)))),
109
- by(rule("intuitionistic-logic-kripke.pl", clause(27))),
109
+ by(rule("intuitionistic-logic-kripke.pl", clause(25))),
110
110
  uses([
111
111
  proof(
112
112
  goal(not(forces(root, or(atom(p), atom(q))))),
@@ -121,7 +121,7 @@ why(
121
121
  intuitionistic_countermodel(excluded_middle_not_forced, root, or(atom(p), neg(atom(p)))),
122
122
  proof(
123
123
  goal(intuitionistic_countermodel(excluded_middle_not_forced, root, or(atom(p), neg(atom(p))))),
124
- by(rule("intuitionistic-logic-kripke.pl", clause(28))),
124
+ by(rule("intuitionistic-logic-kripke.pl", clause(26))),
125
125
  uses([
126
126
  proof(
127
127
  goal(not(forces(root, or(atom(p), neg(atom(p)))))),
@@ -6,7 +6,6 @@
6
6
  % route can be inspected, stored, or used by later rules.
7
7
 
8
8
  materialize(route_to_nantes, 2).
9
- table(path, 2).
10
9
  mode(path, 2, [in, out]).
11
10
 
12
11
  oneway(paris, orleans).
@@ -7,10 +7,6 @@
7
7
  % The table declarations memoize the smaller helper relations used by both formulas.
8
8
  materialize(stirling_bell_answer, 2).
9
9
 
10
- table(factorial, 2).
11
- table(binomial, 3).
12
- table(stirling2, 3).
13
- table(bell, 2).
14
10
 
15
11
  factorial(0, 1).
16
12
  factorial(N, Value) :-
@@ -1,12 +1,10 @@
1
- % Euler totients and coprimality by tabled Euclidean gcd.
1
+ % Euler totients and coprimality by automatically tabled Euclidean gcd.
2
2
  %
3
3
  % phi(N) is modeled directly as the count of integers K in 1..N with gcd(N,K)=1.
4
4
  % The summatory query reuses many gcd/totient subgoals, so memoization keeps the
5
5
  % example responsive while preserving the relational presentation.
6
6
  materialize(totient_answer, 2).
7
7
 
8
- table(gcd, 3).
9
- table(totient, 2).
10
8
 
11
9
  % Euclid's algorithm is expressed recursively over remainders.
12
10
  gcd(A, 0, A) :- ge(A, 0).
@@ -10,7 +10,6 @@ materialize(tmsJustification, 3).
10
10
  materialize(tmsInconsistent, 1).
11
11
  materialize(tmsConclusion, 2).
12
12
 
13
- table(supported, 2).
14
13
 
15
14
  % Candidate environments.
16
15
  environment(clear_only, [sensor_clear]).
@@ -9,7 +9,6 @@
9
9
  materialize(type_answer, 2).
10
10
  materialize(type_reason, 2).
11
11
 
12
- table(type_expr, 3).
13
12
 
14
13
  % Tiny expression language: int_lit/1, bool_lit/1, var/1, lam/3, app/2,
15
14
  % add/2, if/3, pair/2, fst/1, and snd/1.
@@ -7,7 +7,6 @@
7
7
  materialize(affiliated_with, 2).
8
8
  materialize(project_contact, 3).
9
9
 
10
- table(parent_organization, 2).
11
10
 
12
11
  % A tiny graph using absolute IRI atoms directly.
13
12
  triple('<https://data.ugent.be/id/josd>', '<https://schema.org/name>', "Jos De Roo").
@@ -1,11 +1,10 @@
1
- % Weighted interval scheduling via tabled dynamic programming.
1
+ % Weighted interval scheduling via automatically tabled dynamic programming.
2
2
  %
3
3
  % Intervals are ordered by finish time. best_from(I,Best) compares the two
4
4
  % choices at position I: skip it, or take it and jump to the next compatible
5
- % interval. chosen_from/2 then walks the tabled decisions to report a schedule.
5
+ % interval. chosen_from/2 then walks the cached decisions to report a schedule.
6
6
  materialize(weighted_interval_answer, 2).
7
7
 
8
- table(best_from, 2).
9
8
 
10
9
  last_interval(8).
11
10
  sentinel(9).
@@ -10,7 +10,6 @@ materialize(solution, 2).
10
10
  materialize(solved, 2).
11
11
 
12
12
  % Program structure: facts set up the scenario, and rules derive the materialized conclusions.
13
- table(solve, 4).
14
13
 
15
14
  % Derivation rules: each rule below contributes one logical step toward the displayed results.
16
15
  solution(Moves) :-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyelang",
3
- "version": "4.0.5",
3
+ "version": "4.0.7",
4
4
  "description": "A small reasoning language with ISO Prolog-compatible surface syntax.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -44,8 +44,7 @@
44
44
  "test:conformance": "node test/run-conformance.mjs",
45
45
  "test:examples": "node test/run-examples.mjs",
46
46
  "test:regression": "node test/run-regression.mjs",
47
- "preversion": "npm test && npm run conformance:report -- conformance-report.md",
48
- "postversion": "git push origin HEAD --follow-tags",
49
- "conformance:report": "node test/run-conformance-report.mjs"
47
+ "preversion": "npm test && node test/run-conformance-report.mjs conformance-report.md",
48
+ "postversion": "git push origin HEAD --follow-tags"
50
49
  }
51
50
  }
package/src/explain.js CHANGED
@@ -69,7 +69,9 @@ function* proveGoalAll(program, goal, env, depth, maxDepth, registry, active) {
69
69
 
70
70
  const group = program.findGroup(goal.name, goal.arity);
71
71
  if (!group) return;
72
- if (!group.tabled && activeVariant(goal, env, active)) return;
72
+ // Explanation replay does not use the solver's answer tables, so its cycle
73
+ // guard applies even when normal execution tables this predicate.
74
+ if (activeVariant(goal, env, active)) return;
73
75
 
74
76
  const candidates = selectClauseCandidates(group, goal, env);
75
77
  for (const pass of [candidates.primary, candidates.fallback]) {
package/src/program.js CHANGED
@@ -84,9 +84,7 @@ export class Program {
84
84
  const indicator = declarationIndicator(h.args[0], h.args[1]);
85
85
  if (!indicator) continue;
86
86
  const group = this.groups.get(indicator.key);
87
- if (h.name === 'table') {
88
- if (group) group.tabled = true;
89
- } else if (h.name === 'materialize') {
87
+ if (h.name === 'materialize') {
90
88
  this.hasMaterialize = true;
91
89
  this.materializedGroups.add(indicator.key);
92
90
  } else if ((h.name === 'det' || h.name === 'semidet') && group) {
@@ -105,7 +103,10 @@ export class Program {
105
103
  }
106
104
  }
107
105
  }
108
- if (options.markRecursive !== false) this.markRecursivePredicates();
106
+ // Hybrid planning is part of normal execution, so dependency analysis is
107
+ // always performed. `markRecursive` is retained as a compatible parse
108
+ // option but no longer disables the engine's automatic table decisions.
109
+ this.markRecursivePredicates();
109
110
  if (options.analyzeNegation === true || options.strictNegation === true) this.analyzeNegationStratification();
110
111
  if (options.strictNegation === true) this.assertStratifiedNegation();
111
112
  }
@@ -119,11 +120,11 @@ export class Program {
119
120
  for (const group of groups) {
120
121
  const groupIndex = indexByGroup.get(group);
121
122
  for (const clause of group.clauses) {
122
- const bodyGoals = clause.body.flatMap((goal) => goal.type === COMPOUND && goal.name === ',' && goal.arity === 2 ? flattenConjunction(goal) : [goal]);
123
- for (const goal of bodyGoals) {
124
- if (goal.type !== COMPOUND) continue;
125
- const dep = this.findGroup(goal.name, goal.arity);
126
- if (dep) deps[groupIndex].add(indexByGroup.get(dep));
123
+ for (const goal of clause.body) {
124
+ for (const dependency of collectGoalDependencies(goal, false)) {
125
+ const dep = this.groups.get(dependency.key);
126
+ if (dep) deps[groupIndex].add(indexByGroup.get(dep));
127
+ }
127
128
  }
128
129
  }
129
130
  }
@@ -142,6 +143,13 @@ export class Program {
142
143
  }
143
144
  }
144
145
  group.recursive = recursive;
146
+ // Recursive predicates are proved with tabling automatically, keeping
147
+ // search control inside the engine.
148
+ // A directly materialized recursive relation is normally entered as a
149
+ // fully open call. Keep that relation on ordinary resolution: tabling its
150
+ // later bound subcalls can prematurely complete answers on cyclic data.
151
+ // Recursive helpers demanded by an output predicate are safe candidates.
152
+ group.tabled = recursive && !this.materializedGroups.has(`${group.name}/${group.arity}`);
145
153
  }
146
154
  }
147
155
 
@@ -295,6 +303,13 @@ function collectGoalDependencies(goal, negated) {
295
303
  ...collectGoalDependencies(goal.args[1], negated),
296
304
  ];
297
305
  }
306
+ if ((goal.name === 'findall' || goal.name === 'sumall') && goal.arity === 3) {
307
+ return collectGoalDependencies(goal.args[1], negated);
308
+ }
309
+ if (goal.name === 'countall' && goal.arity === 2) return collectGoalDependencies(goal.args[0], negated);
310
+ if ((goal.name === 'aggregate_min' || goal.name === 'aggregate_max') && goal.arity === 5) {
311
+ return collectGoalDependencies(goal.args[2], negated);
312
+ }
298
313
  return [{ key: `${goal.name}/${goal.arity}`, negative: negated }];
299
314
  }
300
315
 
@@ -46,8 +46,8 @@ node test/run-conformance.mjs
46
46
  Summarize conformance coverage by category:
47
47
 
48
48
  ```sh
49
- npm run conformance:report
50
- npm run conformance:report -- conformance-report.md
49
+ node test/run-conformance-report.mjs
50
+ node test/run-conformance-report.mjs conformance-report.md
51
51
  ```
52
52
 
53
53
  Run matching conformance cases by passing a filename or directory fragment:
@@ -1,6 +1,5 @@
1
- % Reference 10.1: table/2 is a declaration and does not change answers.
1
+ % Reference 11.1: recursive predicates are tabled automatically without changing answers.
2
2
  materialize(reach, 2).
3
- table(reach_any, 2).
4
3
  edge(a, b).
5
4
  edge(b, c).
6
5
  reach_any(X, Y) :- edge(X, Y).
@@ -1,6 +1,5 @@
1
- % Reference 11.1: table/2 is the preferred spelling for tabled predicates.
1
+ % Reference 11.1: recursive predicate groups are detected automatically.
2
2
  materialize(reach, 2).
3
- table(reach_any, 2).
4
3
 
5
4
  edge(a, b).
6
5
  edge(b, c).
@@ -1,5 +1,4 @@
1
1
  materialize(answer, 1).
2
- table(reachable, 2).
3
2
  edge(a, b).
4
3
  edge(b, c).
5
4
  reachable(X, Z) :- reachable(X, Y), edge(Y, Z).
@@ -1,6 +1,5 @@
1
- % `table/2` is a search-control declaration and does not change answers.
1
+ % Automatic tabling is a search-control strategy and does not change answers.
2
2
  materialize(path, 2).
3
- table(path, 2).
4
3
  edge(a, b).
5
4
  edge(b, c).
6
5
  path(X, Y) :- edge(X, Y).
@@ -1,5 +1,4 @@
1
1
  materialize(reach, 2).
2
- table(path, 2).
3
2
  edge(a, b).
4
3
  edge(b, a).
5
4
  path(X, Y) :- edge(X, Y).
@@ -1,5 +1,4 @@
1
1
  materialize(answer, 2).
2
- table(edge, 2).
3
2
  edge(a, b).
4
3
  edge(b, c).
5
4
  answer(table_open_call_fallback, pair(X, Y)) :- edge(X, Y).
@@ -1,5 +1,4 @@
1
1
  materialize(path, 2).
2
- table(path, 2).
3
2
  edge(a, b).
4
3
  edge(b, c).
5
4
  edge(c, d).
@@ -3,12 +3,12 @@ why(
3
3
  path(a, b),
4
4
  proof(
5
5
  goal(path(a, b)),
6
- by(rule("<stdin>", clause(5))),
6
+ by(rule("<stdin>", clause(4))),
7
7
  bindings([binding("X", a), binding("Y", b)]),
8
8
  uses([
9
9
  proof(
10
10
  goal(edge(a, b)),
11
- by(fact("<stdin>", clause(3)))
11
+ by(fact("<stdin>", clause(2)))
12
12
  )
13
13
  ])
14
14
  )
@@ -19,12 +19,12 @@ why(
19
19
  path(b, c),
20
20
  proof(
21
21
  goal(path(b, c)),
22
- by(rule("<stdin>", clause(5))),
22
+ by(rule("<stdin>", clause(4))),
23
23
  bindings([binding("X", b), binding("Y", c)]),
24
24
  uses([
25
25
  proof(
26
26
  goal(edge(b, c)),
27
- by(fact("<stdin>", clause(4)))
27
+ by(fact("<stdin>", clause(3)))
28
28
  )
29
29
  ])
30
30
  )
@@ -35,21 +35,21 @@ why(
35
35
  path(a, c),
36
36
  proof(
37
37
  goal(path(a, c)),
38
- by(rule("<stdin>", clause(6))),
38
+ by(rule("<stdin>", clause(5))),
39
39
  bindings([binding("X", a), binding("Z", c), binding("Y", b)]),
40
40
  uses([
41
41
  proof(
42
42
  goal(edge(a, b)),
43
- by(fact("<stdin>", clause(3)))
43
+ by(fact("<stdin>", clause(2)))
44
44
  ),
45
45
  proof(
46
46
  goal(path(b, c)),
47
- by(rule("<stdin>", clause(5))),
47
+ by(rule("<stdin>", clause(4))),
48
48
  bindings([binding("X", b), binding("Y", c)]),
49
49
  uses([
50
50
  proof(
51
51
  goal(edge(b, c)),
52
- by(fact("<stdin>", clause(4)))
52
+ by(fact("<stdin>", clause(3)))
53
53
  )
54
54
  ])
55
55
  )
@@ -3,17 +3,17 @@ why(
3
3
  answer(table_path_bound, b),
4
4
  proof(
5
5
  goal(answer(table_path_bound, b)),
6
- by(rule("<stdin>", clause(7))),
6
+ by(rule("<stdin>", clause(6))),
7
7
  bindings([binding("X", b)]),
8
8
  uses([
9
9
  proof(
10
10
  goal(path(a, b)),
11
- by(rule("<stdin>", clause(5))),
11
+ by(rule("<stdin>", clause(4))),
12
12
  bindings([binding("X", a), binding("Y", b)]),
13
13
  uses([
14
14
  proof(
15
15
  goal(edge(a, b)),
16
- by(fact("<stdin>", clause(3)))
16
+ by(fact("<stdin>", clause(2)))
17
17
  )
18
18
  ])
19
19
  )
@@ -26,26 +26,26 @@ why(
26
26
  answer(table_path_bound, c),
27
27
  proof(
28
28
  goal(answer(table_path_bound, c)),
29
- by(rule("<stdin>", clause(7))),
29
+ by(rule("<stdin>", clause(6))),
30
30
  bindings([binding("X", c)]),
31
31
  uses([
32
32
  proof(
33
33
  goal(path(a, c)),
34
- by(rule("<stdin>", clause(6))),
34
+ by(rule("<stdin>", clause(5))),
35
35
  bindings([binding("X", a), binding("Z", c), binding("Y", b)]),
36
36
  uses([
37
37
  proof(
38
38
  goal(edge(a, b)),
39
- by(fact("<stdin>", clause(3)))
39
+ by(fact("<stdin>", clause(2)))
40
40
  ),
41
41
  proof(
42
42
  goal(path(b, c)),
43
- by(rule("<stdin>", clause(5))),
43
+ by(rule("<stdin>", clause(4))),
44
44
  bindings([binding("X", b), binding("Y", c)]),
45
45
  uses([
46
46
  proof(
47
47
  goal(edge(b, c)),
48
- by(fact("<stdin>", clause(4)))
48
+ by(fact("<stdin>", clause(3)))
49
49
  )
50
50
  ])
51
51
  )
@@ -1,6 +1,5 @@
1
- % Reference 12: tabled recursive proofs still explain the successful derivation path.
1
+ % Reference 12: automatically tabled recursive proofs still explain the successful derivation path.
2
2
  materialize(path, 2).
3
- table(path, 2).
4
3
  edge(a, b).
5
4
  edge(b, c).
6
5
  path(X, Y) :- edge(X, Y).
@@ -1,5 +1,4 @@
1
1
  materialize(answer, 2).
2
- table(path, 2).
3
2
  edge(a, b).
4
3
  edge(b, c).
5
4
  path(X, Y) :- edge(X, Y).
@@ -1,5 +1,4 @@
1
1
  materialize(open, 1).
2
- table(path, 2).
3
2
  edge(a, b).
4
3
  path(X, Y) :- edge(X, Y).
5
4
  closed(c).
@@ -749,21 +749,21 @@ function whiteBoxCases() {
749
749
  },
750
750
  },
751
751
  {
752
- name: 'table declaration marks predicate group tabled',
752
+ name: 'recursive predicate groups are tabled automatically',
753
753
  run: () => {
754
- const program = Program.parse('table(path, 2).\nedge(a, b).\npath(X, Y) :- edge(X, Y).\n');
754
+ const program = Program.parse('edge(a, b).\npath(X, Y) :- edge(X, Y).\npath(X, Z) :- path(X, Y), edge(Y, Z).\n');
755
755
  const group = program.findGroup('path', 2);
756
756
  assertEqual(Boolean(group), true, 'path/2 group exists');
757
- assertEqual(group.tabled, true, 'path/2 tabled through table/2');
757
+ assertEqual(group.tabled, true, 'path/2 tabled automatically');
758
758
  },
759
759
  },
760
760
  {
761
- name: 'memoize fact does not table predicates',
761
+ name: 'ordinary declaration-like facts do not table predicates',
762
762
  run: () => {
763
763
  const program = Program.parse('memoize(path, 2).\nedge(a, b).\npath(X, Y) :- edge(X, Y).\n');
764
764
  const group = program.findGroup('path', 2);
765
765
  assertEqual(Boolean(group), true, 'path/2 group exists');
766
- assertEqual(group.tabled, false, 'memoize/2 is not a table declaration');
766
+ assertEqual(group.tabled, false, 'memoize/2 has no search-control meaning');
767
767
  },
768
768
  },
769
769
  {
@@ -786,17 +786,17 @@ function whiteBoxCases() {
786
786
  },
787
787
  },
788
788
  {
789
- name: 'challenging examples keep dynamic-programming predicates tabled',
789
+ name: 'challenging examples infer dynamic-programming predicates automatically',
790
790
  run: () => {
791
791
  const checks = [
792
792
  ['binomial-vandermonde.pl', 'choose_step', 5, true],
793
- ['catalan-convolution.pl', 'catalan', 2, false],
793
+ ['catalan-convolution.pl', 'catalan', 2, true],
794
794
  ['chart-parser.pl', 'span', 4, true],
795
795
  ['continued-fraction-sqrt2.pl', 'conv', 3, true],
796
- ['critical-path-schedule.pl', 'earliest_start', 2, false],
797
- ['critical-path-schedule.pl', 'finish_time', 2, false],
796
+ ['critical-path-schedule.pl', 'earliest_start', 2, true],
797
+ ['critical-path-schedule.pl', 'finish_time', 2, true],
798
798
  ['integer-partitions.pl', 'partitions', 3, true],
799
- ['matrix-chain-order.pl', 'cost', 3, false],
799
+ ['matrix-chain-order.pl', 'cost', 3, true],
800
800
  ['modular-exponentiation.pl', 'pow_mod', 4, true],
801
801
  ['pell-equation.pl', 'pell', 3, true],
802
802
  ['stirling-bell-numbers.pl', 'stirling2', 3, false],
@@ -809,7 +809,7 @@ function whiteBoxCases() {
809
809
  const program = Program.parseSources([{ text, filename }]);
810
810
  const group = program.findGroup(name, arity);
811
811
  assertEqual(Boolean(group), true, `${filename} ${name}/${arity} group exists`);
812
- assertEqual(group.tabled, true, `${filename} ${name}/${arity} tabled`);
812
+ assertEqual(group.tabled, recursive, `${filename} ${name}/${arity} automatic table decision`);
813
813
  assertEqual(group.recursive, recursive, `${filename} ${name}/${arity} recursive`);
814
814
  }
815
815
  },
@@ -1,4 +0,0 @@
1
- % Declarations remain ordinary facts unless materialized output excludes them.
2
- materialize(answer, 2).
3
- table(path, 2).
4
- answer(Name, Arity) :- table(Name, Arity).
@@ -1 +0,0 @@
1
- answer(path, 2).