eyeprolog 1.5.66 → 1.5.67

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.5.66",
6
+ "version": "1.5.67",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/lib/si.pl CHANGED
@@ -70,12 +70,7 @@ si__condition((A;B)) :- si__condition(A), si__condition(B).
70
70
  % f(a) vs f(Y) and X vs 1 are genuinely undecided.
71
71
  compare_si(Order, A, B) :-
72
72
  si__require_order(Order),
73
- ( A == B
74
- -> Order = (=)
75
- ; si__order_decided(A, B)
76
- -> compare(Order, A, B)
77
- ; throw(error(instantiation_error, [predicate-compare_si/3]))
78
- ).
73
+ si__compare(Order, [A-B]).
79
74
 
80
75
  si__require_order(Order) :- var(Order), !.
81
76
  si__require_order(Order) :-
@@ -86,25 +81,38 @@ si__require_order(Order) :-
86
81
  ; throw(error(type_error(atom, Order), [predicate-compare_si/3]))
87
82
  ).
88
83
 
89
- % Both arguments are known to be non-identical here.
90
- si__order_decided(A, B) :- ( var(A) ; var(B) ), !, fail.
91
- si__order_decided(A, B) :-
84
+ % Keep pending argument pairs in a work list. Recursing inside an if-then-else
85
+ % condition retains a child Solver per list cell; testing whole-tail identity
86
+ % at each step also repeatedly scans the same suffix (issue #105).
87
+ si__compare(=, []).
88
+ si__compare(Order, [A-B|Pairs]) :-
89
+ si__compare_pair(A, B, Comparison, Pairs, Next),
90
+ si__compare_next(Comparison, Order, Next).
91
+
92
+ si__compare_next(=, Order, Pairs) :- si__compare(Order, Pairs).
93
+ si__compare_next(<, <, _).
94
+ si__compare_next(>, >, _).
95
+
96
+ si__compare_pair(A, B, =, Pairs, Pairs) :-
97
+ ( var(A) ; var(B) ), !,
98
+ ( A == B -> true
99
+ ; throw(error(instantiation_error, [predicate-compare_si/3])) ).
100
+ si__compare_pair([A|As], [B|Bs], =, Pairs, [A-B,As-Bs|Pairs]) :- !.
101
+ si__compare_pair(A, B, Comparison, Pairs, Next) :-
92
102
  compound(A), compound(B), !,
93
103
  functor(A, NameA, ArityA),
94
104
  functor(B, NameB, ArityB),
95
- ( ArityA =\= ArityB -> true
96
- ; NameA \== NameB -> true
105
+ ( ArityA =\= ArityB -> compare(Comparison, ArityA, ArityB), Next = Pairs
106
+ ; NameA \== NameB -> compare(Comparison, NameA, NameB), Next = Pairs
97
107
  ; A =.. [_|ArgsA],
98
108
  B =.. [_|ArgsB],
99
- si__args_decided(ArgsA, ArgsB)
100
- ).
101
- % Otherwise both are non-variables and at least one is atomic, so either the
102
- % type order or the values themselves settle it and no instantiation can
103
- % change the outcome.
104
- si__order_decided(_, _).
105
-
106
- si__args_decided([A|As], [B|Bs]) :-
107
- ( A == B
108
- -> si__args_decided(As, Bs)
109
- ; si__order_decided(A, B)
109
+ Comparison = (=),
110
+ si__prepend_pairs(ArgsA, ArgsB, Next, Pairs)
110
111
  ).
112
+ si__compare_pair(A, _, >, Pairs, Pairs) :- compound(A), !.
113
+ si__compare_pair(_, B, <, Pairs, Pairs) :- compound(B), !.
114
+ si__compare_pair(A, B, Comparison, Pairs, Pairs) :- compare(Comparison, A, B).
115
+
116
+ si__prepend_pairs([], [], Pairs, Pairs).
117
+ si__prepend_pairs([A|As], [B|Bs], [A-B|Pairs], Tail) :-
118
+ si__prepend_pairs(As, Bs, Pairs, Tail).
@@ -28,6 +28,44 @@ import {
28
28
 
29
29
  export function regressionCases() {
30
30
  return [
31
+ {
32
+ name: 'timed compare_si/3 backtracks over growing prefixes without exhausting the host stack (issue #105)',
33
+ run: () => {
34
+ // Bound the reported generator so the regression exhausts every
35
+ // answer, including sizes beyond the original 256-cell failure.
36
+ const result = runCli(['-'], { input:
37
+ '%% goal: answer(I,R,S)\n' +
38
+ 'answer(I,R,S) :- length(_,I), (I =< 9 -> true ; !, fail), ' +
39
+ 'N is 2^I, length(P,N), append(P,[1],L1), append(P,[2],L2), ' +
40
+ 'time(compare(R,L1,L2)), time(compare_si(S,L1,L2)).\n',
41
+ timeout: 20000,
42
+ });
43
+ assertEqual(result.status, 0, `exit status; error=${result.error}; stderr=${result.stderr}`);
44
+ for (let i = 0; i <= 9; i++) {
45
+ assertIncludes(result.stdout, `answer(${i}, <, <).`, 'comparison result');
46
+ }
47
+ assertNotIncludes(result.stderr + result.stdout, 'Maximum call stack', 'host stack');
48
+ },
49
+ },
50
+ {
51
+ name: 'compare_si/3 work list preserves argument priority and instantiation errors',
52
+ run: () => {
53
+ const goals = [
54
+ 'compare_si(=,f(X,g(Y)),f(X,g(Y))),var(X),var(Y)',
55
+ 'compare_si(<,f(g(X),a),f(g(X),b)),var(X)',
56
+ 'compare_si(>,f(g(b),a),f(g(a),z))',
57
+ 'compare_si(<,[a|X],[b|Y]),var(X),var(Y)',
58
+ 'compare_si(>,f(X),a),var(X)',
59
+ 'compare_si(<,a,f(X)),var(X)',
60
+ '\\+ compare_si(>,f(g(X),a),f(g(X),b))',
61
+ 'catch((compare_si(_,f(g(X),a),f(g(Y),b)),fail),error(instantiation_error,[predicate-compare_si/3]),true),var(X),var(Y)',
62
+ ];
63
+ for (const goal of goals) {
64
+ const result = runEyeProlog(`answer(ok) :- ${goal}.`, { goals: ['answer(X)'] });
65
+ assertIncludes(result.stdout, 'answer(ok)', goal);
66
+ }
67
+ },
68
+ },
31
69
  {
32
70
  name: 'compare_si/3 validates Order before term instantiation (issue #100)',
33
71
  run: () => {