eyelang 1.7.16 → 1.7.17

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.
@@ -1,11 +1,8 @@
1
1
  % =============================================================================================================================
2
- % Deep Taxonomy - depth 10 - expanded N3-style eyelang with checkpoint proof accelerators
2
+ % Deep Taxonomy - depth 10 - expanded N3-style eyelang
3
3
  %
4
- % Adjacent rules mirror the Eyeling N3 deep-taxonomy chain. The checkpoint
5
- % rules are redundant consequences of that chain and are placed first so eyelang
6
- % can prove terminal/report goals without overflowing the JavaScript call stack.
7
- % Report/check rules use once/1 so successful checks do not backtrack into a
8
- % long adjacent-only proof after a shorter checkpoint proof has succeeded.
4
+ % Adjacent rules mirror the Eyeling N3 deep-taxonomy chain. Each step derives
5
+ % the next taxonomy class together with two side labels.
9
6
  % =============================================================================================================================
10
7
 
11
8
  % Output declarations: materialize/2 selects the relations written to this example's golden output.
@@ -27,12 +24,6 @@ a(ind, n0).
27
24
  is(test, true) :- once(a(ind, a2)).
28
25
  a(?x, a2) :- a(?x, n10).
29
26
 
30
- % Redundant checkpoint accelerators.
31
-
32
- a(?x, n10) :- a(?x, n0).
33
- a(?x, n4) :- a(?x, n0).
34
- a(?x, n9) :- a(?x, n0).
35
-
36
27
  % Adjacent N3-style taxonomy rules.
37
28
 
38
29
  a(?x, n1) :- a(?x, n0).
@@ -98,7 +89,7 @@ arc(check6, "C6 OK - the success flag is raised only after the terminal class a2
98
89
  answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n10 and then as a2.") :-
99
90
  once(is(test, true)).
100
91
 
101
- reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10 chain. Redundant checkpoint rules are included only as proof accelerators: each shortcut is entailed by the adjacent chain, and once/1 prevents backtracking into the long adjacent-only proof after a check has succeeded.") :-
92
+ reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10 chain: each rule advances one taxonomy level and adds the matching side labels.") :-
102
93
  once(a(ind, a2)),
103
94
  once(is(test, true)).
104
95
 
@@ -1,11 +1,8 @@
1
1
  % =============================================================================================================================
2
- % Deep Taxonomy - depth 100 - expanded N3-style eyelang with checkpoint proof accelerators
2
+ % Deep Taxonomy - depth 100 - expanded N3-style eyelang
3
3
  %
4
- % Adjacent rules mirror the Eyeling N3 deep-taxonomy chain. The checkpoint
5
- % rules are redundant consequences of that chain and are placed first so eyelang
6
- % can prove terminal/report goals without overflowing the JavaScript call stack.
7
- % Report/check rules use once/1 so successful checks do not backtrack into a
8
- % long adjacent-only proof after a shorter checkpoint proof has succeeded.
4
+ % Adjacent rules mirror the Eyeling N3 deep-taxonomy chain. Each step derives
5
+ % the next taxonomy class together with two side labels.
9
6
  % =============================================================================================================================
10
7
 
11
8
  % Output declarations: materialize/2 selects the relations written to this example's golden output.
@@ -27,12 +24,6 @@ a(ind, n0).
27
24
  is(test, true) :- once(a(ind, a2)).
28
25
  a(?x, a2) :- a(?x, n100).
29
26
 
30
- % Redundant checkpoint accelerators.
31
-
32
- a(?x, n100) :- a(?x, n0).
33
- a(?x, n49) :- a(?x, n0).
34
- a(?x, n99) :- a(?x, n0).
35
-
36
27
  % Adjacent N3-style taxonomy rules.
37
28
 
38
29
  a(?x, n1) :- a(?x, n0).
@@ -368,7 +359,7 @@ arc(check6, "C6 OK - the success flag is raised only after the terminal class a2
368
359
  answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n100 and then as a2.") :-
369
360
  once(is(test, true)).
370
361
 
371
- reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100 chain. Redundant checkpoint rules are included only as proof accelerators: each shortcut is entailed by the adjacent chain, and once/1 prevents backtracking into the long adjacent-only proof after a check has succeeded.") :-
362
+ reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100 chain: each rule advances one taxonomy level and adds the matching side labels.") :-
372
363
  once(a(ind, a2)),
373
364
  once(is(test, true)).
374
365
 
@@ -1,11 +1,8 @@
1
1
  % =============================================================================================================================
2
- % Deep Taxonomy - depth 1000 - expanded N3-style eyelang with checkpoint proof accelerators
2
+ % Deep Taxonomy - depth 1000 - expanded N3-style eyelang
3
3
  %
4
- % Adjacent rules mirror the Eyeling N3 deep-taxonomy chain. The checkpoint
5
- % rules are redundant consequences of that chain and are placed first so eyelang
6
- % can prove terminal/report goals without overflowing the JavaScript call stack.
7
- % Report/check rules use once/1 so successful checks do not backtrack into a
8
- % long adjacent-only proof after a shorter checkpoint proof has succeeded.
4
+ % Adjacent rules mirror the Eyeling N3 deep-taxonomy chain. Each step derives
5
+ % the next taxonomy class together with two side labels.
9
6
  % =============================================================================================================================
10
7
 
11
8
  % Output declarations: materialize/2 selects the relations written to this example's golden output.
@@ -27,12 +24,6 @@ a(ind, n0).
27
24
  is(test, true) :- once(a(ind, a2)).
28
25
  a(?x, a2) :- a(?x, n1000).
29
26
 
30
- % Redundant checkpoint accelerators.
31
-
32
- a(?x, n1000) :- a(?x, n0).
33
- a(?x, n499) :- a(?x, n0).
34
- a(?x, n999) :- a(?x, n0).
35
-
36
27
  % Adjacent N3-style taxonomy rules.
37
28
 
38
29
  a(?x, n1) :- a(?x, n0).
@@ -3068,7 +3059,7 @@ arc(check6, "C6 OK - the success flag is raised only after the terminal class a2
3068
3059
  answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n1000 and then as a2.") :-
3069
3060
  once(is(test, true)).
3070
3061
 
3071
- reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-1000 chain. Redundant checkpoint rules are included only as proof accelerators: each shortcut is entailed by the adjacent chain, and once/1 prevents backtracking into the long adjacent-only proof after a check has succeeded.") :-
3062
+ reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-1000 chain: each rule advances one taxonomy level and adds the matching side labels.") :-
3072
3063
  once(a(ind, a2)),
3073
3064
  once(is(test, true)).
3074
3065
 
@@ -1,11 +1,8 @@
1
1
  % =============================================================================================================================
2
- % Deep Taxonomy - depth 10000 - expanded N3-style eyelang with checkpoint proof accelerators
2
+ % Deep Taxonomy - depth 10000 - expanded N3-style eyelang
3
3
  %
4
- % Adjacent rules mirror the Eyeling N3 deep-taxonomy chain. The checkpoint
5
- % rules are redundant consequences of that chain and are placed first so eyelang
6
- % can prove terminal/report goals without overflowing the JavaScript call stack.
7
- % Report/check rules use once/1 so successful checks do not backtrack into a
8
- % long adjacent-only proof after a shorter checkpoint proof has succeeded.
4
+ % Adjacent rules mirror the Eyeling N3 deep-taxonomy chain. Each step derives
5
+ % the next taxonomy class together with two side labels.
9
6
  % =============================================================================================================================
10
7
 
11
8
  % Output declarations: materialize/2 selects the relations written to this example's golden output.
@@ -27,21 +24,6 @@ a(ind, n0).
27
24
  is(test, true) :- once(a(ind, a2)).
28
25
  a(?x, a2) :- a(?x, n10000).
29
26
 
30
- % Redundant checkpoint accelerators.
31
-
32
- a(?x, n1000) :- a(?x, n0).
33
- a(?x, n2000) :- a(?x, n1000).
34
- a(?x, n3000) :- a(?x, n2000).
35
- a(?x, n4000) :- a(?x, n3000).
36
- a(?x, n5000) :- a(?x, n4000).
37
- a(?x, n6000) :- a(?x, n5000).
38
- a(?x, n7000) :- a(?x, n6000).
39
- a(?x, n8000) :- a(?x, n7000).
40
- a(?x, n9000) :- a(?x, n8000).
41
- a(?x, n10000) :- a(?x, n9000).
42
- a(?x, n4999) :- a(?x, n4000).
43
- a(?x, n9999) :- a(?x, n9000).
44
-
45
27
  % Adjacent N3-style taxonomy rules.
46
28
 
47
29
  a(?x, n1) :- a(?x, n0).
@@ -30077,7 +30059,7 @@ arc(check6, "C6 OK - the success flag is raised only after the terminal class a2
30077
30059
  answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n10000 and then as a2.") :-
30078
30060
  once(is(test, true)).
30079
30061
 
30080
- reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10000 chain. Redundant checkpoint rules are included only as proof accelerators: each shortcut is entailed by the adjacent chain, and once/1 prevents backtracking into the long adjacent-only proof after a check has succeeded.") :-
30062
+ reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10000 chain: each rule advances one taxonomy level and adds the matching side labels.") :-
30081
30063
  once(a(ind, a2)),
30082
30064
  once(is(test, true)).
30083
30065
 
@@ -1,11 +1,8 @@
1
1
  % =============================================================================================================================
2
- % Deep Taxonomy - depth 100000 - expanded N3-style eyelang with checkpoint proof accelerators
2
+ % Deep Taxonomy - depth 100000 - expanded N3-style eyelang
3
3
  %
4
- % Adjacent rules mirror the Eyeling N3 deep-taxonomy chain. The checkpoint
5
- % rules are redundant consequences of that chain and are placed first so eyelang
6
- % can prove terminal/report goals without overflowing the JavaScript call stack.
7
- % Report/check rules use once/1 so successful checks do not backtrack into a
8
- % long adjacent-only proof after a shorter checkpoint proof has succeeded.
4
+ % Adjacent rules mirror the Eyeling N3 deep-taxonomy chain. Each step derives
5
+ % the next taxonomy class together with two side labels.
9
6
  % =============================================================================================================================
10
7
 
11
8
  % Output declarations: materialize/2 selects the relations written to this example's golden output.
@@ -27,111 +24,6 @@ a(ind, n0).
27
24
  is(test, true) :- once(a(ind, a2)).
28
25
  a(?x, a2) :- a(?x, n100000).
29
26
 
30
- % Redundant checkpoint accelerators.
31
-
32
- a(?x, n1000) :- a(?x, n0).
33
- a(?x, n2000) :- a(?x, n1000).
34
- a(?x, n3000) :- a(?x, n2000).
35
- a(?x, n4000) :- a(?x, n3000).
36
- a(?x, n5000) :- a(?x, n4000).
37
- a(?x, n6000) :- a(?x, n5000).
38
- a(?x, n7000) :- a(?x, n6000).
39
- a(?x, n8000) :- a(?x, n7000).
40
- a(?x, n9000) :- a(?x, n8000).
41
- a(?x, n10000) :- a(?x, n9000).
42
- a(?x, n11000) :- a(?x, n10000).
43
- a(?x, n12000) :- a(?x, n11000).
44
- a(?x, n13000) :- a(?x, n12000).
45
- a(?x, n14000) :- a(?x, n13000).
46
- a(?x, n15000) :- a(?x, n14000).
47
- a(?x, n16000) :- a(?x, n15000).
48
- a(?x, n17000) :- a(?x, n16000).
49
- a(?x, n18000) :- a(?x, n17000).
50
- a(?x, n19000) :- a(?x, n18000).
51
- a(?x, n20000) :- a(?x, n19000).
52
- a(?x, n21000) :- a(?x, n20000).
53
- a(?x, n22000) :- a(?x, n21000).
54
- a(?x, n23000) :- a(?x, n22000).
55
- a(?x, n24000) :- a(?x, n23000).
56
- a(?x, n25000) :- a(?x, n24000).
57
- a(?x, n26000) :- a(?x, n25000).
58
- a(?x, n27000) :- a(?x, n26000).
59
- a(?x, n28000) :- a(?x, n27000).
60
- a(?x, n29000) :- a(?x, n28000).
61
- a(?x, n30000) :- a(?x, n29000).
62
- a(?x, n31000) :- a(?x, n30000).
63
- a(?x, n32000) :- a(?x, n31000).
64
- a(?x, n33000) :- a(?x, n32000).
65
- a(?x, n34000) :- a(?x, n33000).
66
- a(?x, n35000) :- a(?x, n34000).
67
- a(?x, n36000) :- a(?x, n35000).
68
- a(?x, n37000) :- a(?x, n36000).
69
- a(?x, n38000) :- a(?x, n37000).
70
- a(?x, n39000) :- a(?x, n38000).
71
- a(?x, n40000) :- a(?x, n39000).
72
- a(?x, n41000) :- a(?x, n40000).
73
- a(?x, n42000) :- a(?x, n41000).
74
- a(?x, n43000) :- a(?x, n42000).
75
- a(?x, n44000) :- a(?x, n43000).
76
- a(?x, n45000) :- a(?x, n44000).
77
- a(?x, n46000) :- a(?x, n45000).
78
- a(?x, n47000) :- a(?x, n46000).
79
- a(?x, n48000) :- a(?x, n47000).
80
- a(?x, n49000) :- a(?x, n48000).
81
- a(?x, n50000) :- a(?x, n49000).
82
- a(?x, n51000) :- a(?x, n50000).
83
- a(?x, n52000) :- a(?x, n51000).
84
- a(?x, n53000) :- a(?x, n52000).
85
- a(?x, n54000) :- a(?x, n53000).
86
- a(?x, n55000) :- a(?x, n54000).
87
- a(?x, n56000) :- a(?x, n55000).
88
- a(?x, n57000) :- a(?x, n56000).
89
- a(?x, n58000) :- a(?x, n57000).
90
- a(?x, n59000) :- a(?x, n58000).
91
- a(?x, n60000) :- a(?x, n59000).
92
- a(?x, n61000) :- a(?x, n60000).
93
- a(?x, n62000) :- a(?x, n61000).
94
- a(?x, n63000) :- a(?x, n62000).
95
- a(?x, n64000) :- a(?x, n63000).
96
- a(?x, n65000) :- a(?x, n64000).
97
- a(?x, n66000) :- a(?x, n65000).
98
- a(?x, n67000) :- a(?x, n66000).
99
- a(?x, n68000) :- a(?x, n67000).
100
- a(?x, n69000) :- a(?x, n68000).
101
- a(?x, n70000) :- a(?x, n69000).
102
- a(?x, n71000) :- a(?x, n70000).
103
- a(?x, n72000) :- a(?x, n71000).
104
- a(?x, n73000) :- a(?x, n72000).
105
- a(?x, n74000) :- a(?x, n73000).
106
- a(?x, n75000) :- a(?x, n74000).
107
- a(?x, n76000) :- a(?x, n75000).
108
- a(?x, n77000) :- a(?x, n76000).
109
- a(?x, n78000) :- a(?x, n77000).
110
- a(?x, n79000) :- a(?x, n78000).
111
- a(?x, n80000) :- a(?x, n79000).
112
- a(?x, n81000) :- a(?x, n80000).
113
- a(?x, n82000) :- a(?x, n81000).
114
- a(?x, n83000) :- a(?x, n82000).
115
- a(?x, n84000) :- a(?x, n83000).
116
- a(?x, n85000) :- a(?x, n84000).
117
- a(?x, n86000) :- a(?x, n85000).
118
- a(?x, n87000) :- a(?x, n86000).
119
- a(?x, n88000) :- a(?x, n87000).
120
- a(?x, n89000) :- a(?x, n88000).
121
- a(?x, n90000) :- a(?x, n89000).
122
- a(?x, n91000) :- a(?x, n90000).
123
- a(?x, n92000) :- a(?x, n91000).
124
- a(?x, n93000) :- a(?x, n92000).
125
- a(?x, n94000) :- a(?x, n93000).
126
- a(?x, n95000) :- a(?x, n94000).
127
- a(?x, n96000) :- a(?x, n95000).
128
- a(?x, n97000) :- a(?x, n96000).
129
- a(?x, n98000) :- a(?x, n97000).
130
- a(?x, n99000) :- a(?x, n98000).
131
- a(?x, n100000) :- a(?x, n99000).
132
- a(?x, n49999) :- a(?x, n49000).
133
- a(?x, n99999) :- a(?x, n99000).
134
-
135
27
  % Adjacent N3-style taxonomy rules.
136
28
 
137
29
  a(?x, n1) :- a(?x, n0).
@@ -300167,7 +300059,7 @@ arc(check6, "C6 OK - the success flag is raised only after the terminal class a2
300167
300059
  answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n100000 and then as a2.") :-
300168
300060
  once(is(test, true)).
300169
300061
 
300170
- reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100000 chain. Redundant checkpoint rules are included only as proof accelerators: each shortcut is entailed by the adjacent chain, and once/1 prevents backtracking into the long adjacent-only proof after a check has succeeded.") :-
300062
+ reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100000 chain: each rule advances one taxonomy level and adds the matching side labels.") :-
300171
300063
  once(a(ind, a2)),
300172
300064
  once(is(test, true)).
300173
300065
 
@@ -6,7 +6,7 @@ arc(check4, "C4 OK - the final taxonomy step from n9 to n10 was completed.").
6
6
  arc(check5, "C5 OK - once n10 is reached, the terminal class a2 is derived.").
7
7
  arc(check6, "C6 OK - the success flag is raised only after the terminal class a2 is present.").
8
8
  answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n10 and then as a2.").
9
- reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10 chain. Redundant checkpoint rules are included only as proof accelerators: each shortcut is entailed by the adjacent chain, and once/1 prevents backtracking into the long adjacent-only proof after a check has succeeded.").
9
+ reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10 chain: each rule advances one taxonomy level and adds the matching side labels.").
10
10
  checkPassed(report, check1).
11
11
  checkPassed(report, check2).
12
12
  checkPassed(report, check3).
@@ -6,7 +6,7 @@ arc(check4, "C4 OK - the final taxonomy step from n99 to n100 was completed.").
6
6
  arc(check5, "C5 OK - once n100 is reached, the terminal class a2 is derived.").
7
7
  arc(check6, "C6 OK - the success flag is raised only after the terminal class a2 is present.").
8
8
  answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n100 and then as a2.").
9
- reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100 chain. Redundant checkpoint rules are included only as proof accelerators: each shortcut is entailed by the adjacent chain, and once/1 prevents backtracking into the long adjacent-only proof after a check has succeeded.").
9
+ reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100 chain: each rule advances one taxonomy level and adds the matching side labels.").
10
10
  checkPassed(report, check1).
11
11
  checkPassed(report, check2).
12
12
  checkPassed(report, check3).
@@ -6,7 +6,7 @@ arc(check4, "C4 OK - the final taxonomy step from n999 to n1000 was completed.")
6
6
  arc(check5, "C5 OK - once n1000 is reached, the terminal class a2 is derived.").
7
7
  arc(check6, "C6 OK - the success flag is raised only after the terminal class a2 is present.").
8
8
  answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n1000 and then as a2.").
9
- reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-1000 chain. Redundant checkpoint rules are included only as proof accelerators: each shortcut is entailed by the adjacent chain, and once/1 prevents backtracking into the long adjacent-only proof after a check has succeeded.").
9
+ reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-1000 chain: each rule advances one taxonomy level and adds the matching side labels.").
10
10
  checkPassed(report, check1).
11
11
  checkPassed(report, check2).
12
12
  checkPassed(report, check3).
@@ -6,7 +6,7 @@ arc(check4, "C4 OK - the final taxonomy step from n9999 to n10000 was completed.
6
6
  arc(check5, "C5 OK - once n10000 is reached, the terminal class a2 is derived.").
7
7
  arc(check6, "C6 OK - the success flag is raised only after the terminal class a2 is present.").
8
8
  answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n10000 and then as a2.").
9
- reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10000 chain. Redundant checkpoint rules are included only as proof accelerators: each shortcut is entailed by the adjacent chain, and once/1 prevents backtracking into the long adjacent-only proof after a check has succeeded.").
9
+ reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10000 chain: each rule advances one taxonomy level and adds the matching side labels.").
10
10
  checkPassed(report, check1).
11
11
  checkPassed(report, check2).
12
12
  checkPassed(report, check3).
@@ -6,7 +6,7 @@ arc(check4, "C4 OK - the final taxonomy step from n99999 to n100000 was complete
6
6
  arc(check5, "C5 OK - once n100000 is reached, the terminal class a2 is derived.").
7
7
  arc(check6, "C6 OK - the success flag is raised only after the terminal class a2 is present.").
8
8
  answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n100000 and then as a2.").
9
- reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100000 chain. Redundant checkpoint rules are included only as proof accelerators: each shortcut is entailed by the adjacent chain, and once/1 prevents backtracking into the long adjacent-only proof after a check has succeeded.").
9
+ reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100000 chain: each rule advances one taxonomy level and adds the matching side labels.").
10
10
  checkPassed(report, check1).
11
11
  checkPassed(report, check2).
12
12
  checkPassed(report, check3).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyelang",
3
- "version": "1.7.16",
3
+ "version": "1.7.17",
4
4
  "description": "A small Prolog-like logic programming language for facts, rules, goals, answers, and proofs.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -11,15 +11,19 @@ function* notBuiltin({ solver, goal, env }) {
11
11
  const limited = solver.cloneForInnerGoal(1);
12
12
  let found = false;
13
13
  for (const _ of limited.solve([goal.args[0]], env.clone(), 0)) { found = true; break; }
14
+ solver.absorbStatsFrom(limited);
14
15
  if (!found) yield env;
15
16
  }
16
17
 
17
18
  function* onceBuiltin({ solver, goal, env }) {
18
19
  const limited = solver.cloneForInnerGoal(1);
20
+ let first = null;
19
21
  for (const answerEnv of limited.solve([goal.args[0]], env.clone(), 0)) {
20
- yield answerEnv;
22
+ first = answerEnv;
21
23
  break;
22
24
  }
25
+ solver.absorbStatsFrom(limited);
26
+ if (first) yield first;
23
27
  }
24
28
 
25
29
  function* forallBuiltin({ solver, goal, env }) {
@@ -28,7 +32,12 @@ function* forallBuiltin({ solver, goal, env }) {
28
32
  const checker = solver.cloneForInnerGoal(1);
29
33
  let ok = false;
30
34
  for (const _ of checker.solve([goal.args[1]], answerEnv.clone(), 0)) { ok = true; break; }
31
- if (!ok) return;
35
+ solver.absorbStatsFrom(checker);
36
+ if (!ok) {
37
+ solver.absorbStatsFrom(generator);
38
+ return;
39
+ }
32
40
  }
41
+ solver.absorbStatsFrom(generator);
33
42
  yield env;
34
43
  }
package/src/cli.js CHANGED
@@ -103,13 +103,12 @@ async function runDefault(engine, program, options) {
103
103
  const materializedKeys = new Set(goals.map((goal) => `${goal.name}/${goal.arity}`));
104
104
  const facts = program.sourceFactLines(materializedKeys);
105
105
  const lines = new Set();
106
- let lastStats = null;
107
106
  const registry = engine.getDefaultRegistry();
108
107
  const explanation = options.proof ? await loadExplanation() : null;
108
+ const solver = new engine.Solver(program, { registry });
109
109
 
110
110
  for (const goal of goals) {
111
- const solver = new engine.Solver(program, { registry });
112
-
111
+ solver.solutionsSeen = 0;
113
112
  for (const env of solver.solve([goal], new engine.Env(), 0)) {
114
113
  if (!engine.termIsGround(goal, env)) continue;
115
114
 
@@ -122,10 +121,9 @@ async function runDefault(engine, program, options) {
122
121
  if (options.proof) writeExplanation(explanation, program, engine.copyResolved(goal, env), registry);
123
122
  }
124
123
 
125
- lastStats = solver.stats;
126
124
  }
127
125
 
128
- if (options.stats && lastStats) printStats(lastStats);
126
+ if (options.stats) printStats(solver.stats);
129
127
  }
130
128
 
131
129
  function writeExplanation(explanation, program, resolved, registry) {
package/src/solver.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Depth-first eyelang solver with builtin dispatch, memoization, and guarded recursion handling.
2
2
  // Most semantic decisions still flow through unification; optimizations only select candidates earlier.
3
- import { COMPOUND, Env, flattenConjunction, freshTerm, termToString, unify, variantTerms } from './term.js';
3
+ import { COMPOUND, Env, copyResolved, flattenConjunction, freshTerm, termIsGround, termToString, unify, variantTerms } from './term.js';
4
4
  import { createDefaultRegistry } from './builtins/registry.js';
5
5
  import { selectClauseCandidates } from './program.js';
6
6
 
@@ -19,6 +19,7 @@ export class Solver {
19
19
  this.solutionsSeen = 0;
20
20
  this.active = [];
21
21
  this.memo = new Map();
22
+ this.groundChainSuccess = new Set();
22
23
  this.stats = {
23
24
  completed_goal_lists: 0,
24
25
  solve_goals_calls: 0,
@@ -34,9 +35,21 @@ export class Solver {
34
35
  cloneForInnerGoal(solutionLimit = this.solutionLimit) {
35
36
  const solver = new Solver(this.program, { registry: this.registry, maxDepth: this.maxDepth, solutionLimit });
36
37
  solver.memo = this.memo;
38
+ solver.groundChainSuccess = this.groundChainSuccess;
37
39
  return solver;
38
40
  }
39
41
 
42
+ absorbStatsFrom(child) {
43
+ if (!child || child === this || !child.stats) return;
44
+ for (const [key, value] of Object.entries(child.stats)) {
45
+ if (key === 'max_depth' || key === 'max_goal_count') {
46
+ this.stats[key] = Math.max(this.stats[key] ?? 0, value ?? 0);
47
+ } else {
48
+ this.stats[key] = (this.stats[key] ?? 0) + (value ?? 0);
49
+ }
50
+ }
51
+ }
52
+
40
53
  *solve(goals, env = new Env(), depth = 0) {
41
54
  if (!Array.isArray(goals)) goals = [goals];
42
55
 
@@ -267,6 +280,7 @@ function pushMemoAnswerFrames(stack, entry, goal, rest, env, depth, active, solv
267
280
 
268
281
  function pushUserGoalUncachedFrames(stack, solver, group, goal, rest, env, depth, active) {
269
282
  if (activeVariantIn(goal, env, active)) return;
283
+ if (tryPushGroundChainFrames(stack, solver, group, goal, rest, env, depth, active)) return;
270
284
  const candidates = selectClauseCandidates(group, goal, env);
271
285
  const frames = [];
272
286
  for (const pass of [candidates.primary, candidates.fallback]) {
@@ -294,6 +308,80 @@ function pushUserGoalUncachedFrames(stack, solver, group, goal, rest, env, depth
294
308
  for (let i = frames.length - 1; i >= 0; i--) stack.push(frames[i]);
295
309
  }
296
310
 
311
+
312
+ function tryPushGroundChainFrames(stack, solver, group, goal, rest, env, depth, active) {
313
+ // Compress deterministic ground single-goal chains such as deep taxonomy
314
+ // proofs: a(ind, n100000) -> a(ind, n99999) -> ... -> a(ind, n0).
315
+ // This is a search-control optimization only. It fires only while each step
316
+ // has exactly one matching clause and a single ground body goal; otherwise the
317
+ // normal clause path below remains authoritative.
318
+ if (!termIsGround(goal, env)) return false;
319
+
320
+ const baseEnv = env;
321
+ let currentGroup = group;
322
+ let currentGoal = copyResolved(goal, env);
323
+ let currentDepth = depth;
324
+ const currentEnv = new Env();
325
+ const seen = new Set();
326
+
327
+ while (true) {
328
+ // The compressed path is iterative and protected by `seen`, so it does not
329
+ // consume JavaScript recursion depth the way the ordinary solver path does.
330
+ // Keep recording the logical depth for diagnostics, but do not cut off long
331
+ // finite taxonomy chains with the recursive maxDepth guard.
332
+ if (solver.solutionsSeen >= solver.solutionLimit) return true;
333
+ solver.stats.max_depth = Math.max(solver.stats.max_depth, currentDepth);
334
+ const key = `${currentGoal.name}/${currentGoal.arity}:${termToString(currentGoal, currentEnv, true)}`;
335
+ if (seen.has(key)) return true;
336
+ if (activeVariantIn(currentGoal, currentEnv, active)) return true;
337
+ if (solver.groundChainSuccess.has(key)) {
338
+ rememberGroundChainSuccess(solver, seen);
339
+ stack.push({ kind: 'goals', goals: rest, env: baseEnv, depth: depth + 1, active });
340
+ return true;
341
+ }
342
+ seen.add(key);
343
+
344
+ const candidates = selectClauseCandidates(currentGroup, currentGoal, currentEnv);
345
+ const matches = [];
346
+ for (const pass of [candidates.primary, candidates.fallback]) {
347
+ for (const clause of pass) {
348
+ if (headCannotMatch(currentGoal, clause.head, currentEnv)) continue;
349
+ const id = nextFreshId();
350
+ const freshHead = freshTerm(clause.head, id);
351
+ const freshBody = clause.body.map((term) => freshTerm(term, id));
352
+ const next = new Env();
353
+ solver.stats.unify_calls++;
354
+ if (!unify(currentGoal, freshHead, next)) continue;
355
+ matches.push({ body: freshBody, env: next });
356
+ if (matches.length > 1) return false;
357
+ }
358
+ }
359
+
360
+ if (matches.length !== 1) return false;
361
+ const match = matches[0];
362
+ if (match.body.length === 0) {
363
+ rememberGroundChainSuccess(solver, seen);
364
+ stack.push({ kind: 'goals', goals: rest, env: baseEnv, depth: depth + 1, active });
365
+ return true;
366
+ }
367
+ if (match.body.length !== 1) return false;
368
+ const nextGoal = match.body[0];
369
+ if (nextGoal.type !== COMPOUND || !termIsGround(nextGoal, match.env)) return false;
370
+ const resolvedNextGoal = copyResolved(nextGoal, match.env);
371
+ const nextGroup = solver.program.findGroup(resolvedNextGoal.name, resolvedNextGoal.arity);
372
+ if (!nextGroup) return false;
373
+
374
+ currentGoal = resolvedNextGoal;
375
+ currentGroup = nextGroup;
376
+ currentDepth++;
377
+ }
378
+ }
379
+
380
+
381
+ function rememberGroundChainSuccess(solver, seen) {
382
+ for (const key of seen) solver.groundChainSuccess.add(key);
383
+ }
384
+
297
385
  function rememberMemoAnswer(entry, goal, env) {
298
386
  const answerArgs = goal.args.map((arg) => importResolved(arg, env));
299
387
  const key = answerArgs.map((arg) => termToString(arg, new Env(), true)).join('\x1f');