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.
- package/README.md +5 -1
- package/bin/eyelang.js +1 -1
- package/conformance-report.md +3 -3
- package/docs/guide.md +15 -14
- package/docs/language-reference.md +16 -14
- package/examples/abstract-interpretation.pl +0 -1
- package/examples/bayes-therapy.pl +1 -4
- package/examples/binomial-vandermonde.pl +1 -2
- package/examples/catalan-convolution.pl +1 -2
- package/examples/chart-parser.pl +1 -2
- package/examples/collatz-1000.pl +1 -2
- package/examples/continued-fraction-sqrt2.pl +1 -2
- package/examples/critical-path-schedule.pl +0 -2
- package/examples/dijkstra-risk-path.pl +0 -1
- package/examples/drone-corridor-planner.pl +0 -1
- package/examples/equality-saturation.pl +0 -2
- package/examples/family-cousins.pl +0 -3
- package/examples/fibonacci.pl +0 -1
- package/examples/gd-step-certified.pl +0 -7
- package/examples/gps.pl +0 -2
- package/examples/hamiltonian-path.pl +0 -1
- package/examples/hanoi.pl +0 -1
- package/examples/integer-partitions.pl +0 -1
- package/examples/intuitionistic-logic-kripke.pl +0 -2
- package/examples/knuth-bendix-completion.pl +0 -1
- package/examples/language.pl +1 -2
- package/examples/matrix-chain-order.pl +1 -2
- package/examples/modular-exponentiation.pl +0 -1
- package/examples/monkey-bananas.pl +0 -1
- package/examples/n-queens-8.pl +0 -1
- package/examples/partial-evaluator.pl +0 -1
- package/examples/peano-calculus.pl +0 -3
- package/examples/pell-equation.pl +1 -2
- package/examples/pointer-analysis.pl +0 -2
- package/examples/prime-range.pl +0 -1
- package/examples/proof/chart-parser.pl +71 -71
- package/examples/proof/hanoi.pl +16 -16
- package/examples/proof/intuitionistic-logic-kripke.pl +16 -16
- package/examples/route-planning.pl +0 -1
- package/examples/stirling-bell-numbers.pl +0 -4
- package/examples/totient-summatory.pl +1 -3
- package/examples/truth-maintenance-system.pl +0 -1
- package/examples/type-inference.pl +0 -1
- package/examples/web-names.pl +0 -1
- package/examples/weighted-interval-scheduling.pl +2 -3
- package/examples/wolf-goat-cabbage.pl +0 -1
- package/package.json +3 -4
- package/src/explain.js +3 -1
- package/src/program.js +24 -9
- package/test/conformance/README.md +2 -2
- package/test/conformance/cases/{table/038_table_declaration.pl → automatic-tabling/038_automatic_tabling.pl} +1 -2
- package/test/conformance/cases/{table/102_table_declaration.pl → automatic-tabling/102_recursive_inference.pl} +1 -2
- package/test/conformance/cases/{table/tabled_left_recursion_path.pl → automatic-tabling/automatic_left_recursion_path.pl} +0 -1
- package/test/conformance/cases/{table/table_does_not_change_answers.pl → automatic-tabling/automatic_tabling_preserves_answers.pl} +1 -2
- package/test/conformance/cases/{table/extra_table_bound_cycle.pl → automatic-tabling/extra_automatic_bound_cycle.pl} +0 -1
- package/test/conformance/cases/{table/extra_table_open_call_fallback.pl → automatic-tabling/extra_automatic_open_call_fallback.pl} +0 -1
- package/test/conformance/cases/rules/transitive_rule_three_edges.pl +0 -1
- package/test/conformance/expected-proofs/proofs/{table_recursive.pl → automatic_recursive.pl} +8 -8
- package/test/conformance/expected-proofs/proofs/{extra_table_path_bound.pl → extra_automatic_path_bound.pl} +8 -8
- package/test/conformance/proofs/proofs/{table_recursive.pl → automatic_recursive.pl} +1 -2
- package/test/conformance/proofs/proofs/{extra_table_path_bound.pl → extra_automatic_path_bound.pl} +0 -1
- package/test/conformance/warnings/negation/{extra_stratified_negation_with_table_quiet.pl → extra_stratified_negation_with_recursion_quiet.pl} +0 -1
- package/test/run-regression.mjs +11 -11
- package/test/conformance/cases/declarations/table_is_also_fact.pl +0 -4
- package/test/conformance/expected/declarations/table_is_also_fact.pl +0 -1
- /package/test/conformance/expected/{table/038_table_declaration.pl → automatic-tabling/038_automatic_tabling.pl} +0 -0
- /package/test/conformance/expected/{table/102_table_declaration.pl → automatic-tabling/102_recursive_inference.pl} +0 -0
- /package/test/conformance/expected/{table/tabled_left_recursion_path.pl → automatic-tabling/automatic_left_recursion_path.pl} +0 -0
- /package/test/conformance/expected/{table/table_does_not_change_answers.pl → automatic-tabling/automatic_tabling_preserves_answers.pl} +0 -0
- /package/test/conformance/expected/{table/extra_table_bound_cycle.pl → automatic-tabling/extra_automatic_bound_cycle.pl} +0 -0
- /package/test/conformance/expected/{table/extra_table_open_call_fallback.pl → automatic-tabling/extra_automatic_open_call_fallback.pl} +0 -0
- /package/test/conformance/expected-warnings/negation/{extra_stratified_negation_with_table_quiet.pl → extra_stratified_negation_with_recursion_quiet.pl} +0 -0
- /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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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)))))),
|
|
@@ -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).
|
package/examples/web-names.pl
CHANGED
|
@@ -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
|
|
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.
|
|
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 &&
|
|
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
|
-
|
|
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 === '
|
|
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
|
-
|
|
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
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
|
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).
|
package/test/conformance/expected-proofs/proofs/{table_recursive.pl → automatic_recursive.pl}
RENAMED
|
@@ -3,12 +3,12 @@ why(
|
|
|
3
3
|
path(a, b),
|
|
4
4
|
proof(
|
|
5
5
|
goal(path(a, b)),
|
|
6
|
-
by(rule("<stdin>", clause(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
43
|
+
by(fact("<stdin>", clause(2)))
|
|
44
44
|
),
|
|
45
45
|
proof(
|
|
46
46
|
goal(path(b, c)),
|
|
47
|
-
by(rule("<stdin>", clause(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
39
|
+
by(fact("<stdin>", clause(2)))
|
|
40
40
|
),
|
|
41
41
|
proof(
|
|
42
42
|
goal(path(b, c)),
|
|
43
|
-
by(rule("<stdin>", clause(
|
|
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(
|
|
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).
|
package/test/run-regression.mjs
CHANGED
|
@@ -749,21 +749,21 @@ function whiteBoxCases() {
|
|
|
749
749
|
},
|
|
750
750
|
},
|
|
751
751
|
{
|
|
752
|
-
name: '
|
|
752
|
+
name: 'recursive predicate groups are tabled automatically',
|
|
753
753
|
run: () => {
|
|
754
|
-
const program = Program.parse('
|
|
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
|
|
757
|
+
assertEqual(group.tabled, true, 'path/2 tabled automatically');
|
|
758
758
|
},
|
|
759
759
|
},
|
|
760
760
|
{
|
|
761
|
-
name: '
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
797
|
-
['critical-path-schedule.pl', 'finish_time', 2,
|
|
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,
|
|
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,
|
|
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 +0,0 @@
|
|
|
1
|
-
answer(path, 2).
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|