eyeprolog 1.5.60 → 1.5.62
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 +1 -1
- package/src/errors.js +7 -1
- package/src/lib/error.pl +35 -19
- package/src/lib/random.pl +14 -11
- package/test/conformance/ISO-IMPLEMENTATION-DEFINED.md +1 -1
- package/test/conformance/expected/iso/exceptions_and_flags.pl +2 -2
- package/test/regression/cases-regression.mjs +53 -13
- package/test/run-iso-strict.mjs +10 -10
package/package.json
CHANGED
package/src/errors.js
CHANGED
|
@@ -33,8 +33,14 @@ function builtinErrorContext(def, goal) {
|
|
|
33
33
|
// A one-element list, not a bare indicator: error contexts are lists so
|
|
34
34
|
// that library(error)'s call_with_error_context/2 can prepend its own
|
|
35
35
|
// elements and still yield a proper list (issue #98).
|
|
36
|
+
// A one-element list holding a predicate-F/A pair: contexts are lists so
|
|
37
|
+
// call_with_error_context/2 can prepend, and elements are pairs so the
|
|
38
|
+
// convention is uniform with library-supplied elements (issues #98, #99).
|
|
36
39
|
context = compound('.', [
|
|
37
|
-
compound('
|
|
40
|
+
compound('-', [
|
|
41
|
+
atom('predicate'),
|
|
42
|
+
compound('/', [atom(goal.name), numberTerm(goal.arity)]),
|
|
43
|
+
]),
|
|
38
44
|
emptyList(),
|
|
39
45
|
]);
|
|
40
46
|
def._errorContextTerm = context;
|
package/src/lib/error.pl
CHANGED
|
@@ -21,34 +21,39 @@
|
|
|
21
21
|
% raise site: the raise sites throw with [] and this wrapper prepends its
|
|
22
22
|
% element, so contexts stay proper lists and compose with any enclosing
|
|
23
23
|
% call_with_error_context/2 (issue #98).
|
|
24
|
+
% The context is named in exactly one place -- error__must_be_throw/1 -- so no
|
|
25
|
+
% raise site hands one over (issue #98). It is a throw helper rather than a
|
|
26
|
+
% call_with_error_context/2 wrapper because wrapping a Prolog goal in catch/3
|
|
27
|
+
% costs a child Solver on every call, and must_be/2 succeeds almost always.
|
|
24
28
|
must_be(Type, Term) :-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
( var(Type) -> error__must_be_throw(instantiation_error)
|
|
30
|
+
; error__must_be(Type, Term)
|
|
31
|
+
).
|
|
32
|
+
|
|
33
|
+
error__must_be_throw(Formal) :-
|
|
34
|
+
throw(error(Formal, [predicate-must_be/2])).
|
|
30
35
|
|
|
31
36
|
error__must_be(integer, Term) :- !,
|
|
32
|
-
( var(Term) -> instantiation_error
|
|
37
|
+
( var(Term) -> error__must_be_throw(instantiation_error)
|
|
33
38
|
; integer(Term) -> true
|
|
34
|
-
; type_error(integer, Term)
|
|
39
|
+
; error__must_be_throw(type_error(integer, Term))
|
|
35
40
|
).
|
|
36
41
|
error__must_be(atom, Term) :- !,
|
|
37
|
-
( var(Term) -> instantiation_error
|
|
42
|
+
( var(Term) -> error__must_be_throw(instantiation_error)
|
|
38
43
|
; atom(Term) -> true
|
|
39
|
-
; type_error(atom, Term)
|
|
44
|
+
; error__must_be_throw(type_error(atom, Term))
|
|
40
45
|
).
|
|
41
46
|
error__must_be(number, Term) :- !,
|
|
42
|
-
( var(Term) -> instantiation_error
|
|
47
|
+
( var(Term) -> error__must_be_throw(instantiation_error)
|
|
43
48
|
; number(Term) -> true
|
|
44
|
-
; type_error(number, Term)
|
|
49
|
+
; error__must_be_throw(type_error(number, Term))
|
|
45
50
|
).
|
|
46
51
|
error__must_be(var, Term) :- !,
|
|
47
52
|
( var(Term) -> true
|
|
48
53
|
; throw(error(uninstantiation_error(Term), []))
|
|
49
54
|
).
|
|
50
55
|
error__must_be(ground, Term) :- !,
|
|
51
|
-
( ground(Term) -> true ; instantiation_error ).
|
|
56
|
+
( ground(Term) -> true ; error__must_be_throw(instantiation_error) ).
|
|
52
57
|
error__must_be(acyclic, Term) :- !,
|
|
53
58
|
( acyclic_term(Term) -> true ; type_error(acyclic_term, Term) ).
|
|
54
59
|
error__must_be(list, Term) :- !,
|
|
@@ -56,23 +61,23 @@ error__must_be(list, Term) :- !,
|
|
|
56
61
|
error__must_be(list(Type), Term) :- !,
|
|
57
62
|
error__proper_list_of(Term, Type).
|
|
58
63
|
error__must_be(pair, Term) :- !,
|
|
59
|
-
( var(Term) -> instantiation_error
|
|
64
|
+
( var(Term) -> error__must_be_throw(instantiation_error)
|
|
60
65
|
; Term = _-_ -> true
|
|
61
|
-
; type_error(pair, Term)
|
|
66
|
+
; error__must_be_throw(type_error(pair, Term))
|
|
62
67
|
).
|
|
63
68
|
error__must_be(not_less_than_zero, Term) :- !,
|
|
64
69
|
must_be(integer, Term),
|
|
65
70
|
( Term >= 0 -> true ; domain_error(not_less_than_zero, Term) ).
|
|
66
71
|
error__must_be(Type, Term) :-
|
|
67
|
-
( var(Term) -> instantiation_error
|
|
68
|
-
; type_error(Type, Term)
|
|
72
|
+
( var(Term) -> error__must_be_throw(instantiation_error)
|
|
73
|
+
; error__must_be_throw(type_error(Type, Term))
|
|
69
74
|
).
|
|
70
75
|
|
|
71
76
|
error__proper_list([]) :- !.
|
|
72
77
|
error__proper_list([_|Tail]) :- !, error__proper_list(Tail).
|
|
73
78
|
error__proper_list(Term) :-
|
|
74
|
-
( var(Term) -> instantiation_error
|
|
75
|
-
; type_error(list, Term)
|
|
79
|
+
( var(Term) -> error__must_be_throw(instantiation_error)
|
|
80
|
+
; error__must_be_throw(type_error(list, Term))
|
|
76
81
|
).
|
|
77
82
|
|
|
78
83
|
error__proper_list_of([], _) :- !.
|
|
@@ -80,7 +85,7 @@ error__proper_list_of([Head|Tail], Type) :- !,
|
|
|
80
85
|
must_be(Type, Head),
|
|
81
86
|
error__proper_list_of(Tail, Type).
|
|
82
87
|
error__proper_list_of(Term, _) :-
|
|
83
|
-
( var(Term) -> instantiation_error
|
|
88
|
+
( var(Term) -> error__must_be_throw(instantiation_error)
|
|
84
89
|
; type_error(list, Term)
|
|
85
90
|
).
|
|
86
91
|
|
|
@@ -132,7 +137,18 @@ resource_error(Resource, Context) :- throw(error(resource_error(Resource), Conte
|
|
|
132
137
|
% free, though: see issue #98 for measurements and the primitive design that
|
|
133
138
|
% would remove it.
|
|
134
139
|
call_with_error_context(Goal, Pair) :-
|
|
140
|
+
error__require_pair(Pair),
|
|
135
141
|
catch(Goal,
|
|
136
142
|
error(Error, Context),
|
|
137
143
|
( copy_term(Pair, Element),
|
|
138
144
|
throw(error(Error, [Element|Context])) )).
|
|
145
|
+
|
|
146
|
+
% The element must be a pair (issue #99). This cannot go through must_be/2:
|
|
147
|
+
% must_be/2 declares its own context with call_with_error_context/2, so
|
|
148
|
+
% checking with must_be/2 here would recurse. The reported context matches
|
|
149
|
+
% what must_be(pair, _) would have produced.
|
|
150
|
+
error__require_pair(Pair) :-
|
|
151
|
+
( var(Pair) -> throw(error(instantiation_error, [predicate-must_be/2]))
|
|
152
|
+
; Pair = _-_ -> true
|
|
153
|
+
; throw(error(type_error(pair, Pair), [predicate-must_be/2]))
|
|
154
|
+
).
|
package/src/lib/random.pl
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
:- module(random, [maybe/0, maybe/1, maybe/2, random/1, random/3, random_integer/3, set_random/1]).
|
|
11
11
|
|
|
12
12
|
:- use_module(library(iso_ext), [bb_get/2, bb_put/2]).
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
|
|
15
15
|
maybe :-
|
|
16
16
|
random_integer(0, 2, 0).
|
|
@@ -31,7 +31,7 @@ random(Value) :-
|
|
|
31
31
|
% Context declared once per predicate instead of at each raise site, so the
|
|
32
32
|
% raise sites throw with [] and contexts stay proper composable lists.
|
|
33
33
|
random_integer(Lower, Upper, R) :-
|
|
34
|
-
|
|
34
|
+
random__check_integer_range(Lower, Upper),
|
|
35
35
|
Lower < Upper,
|
|
36
36
|
random__current_seed(Seed0),
|
|
37
37
|
random(Seed0, _, Seed),
|
|
@@ -39,28 +39,31 @@ random_integer(Lower, Upper, R) :-
|
|
|
39
39
|
R is Lower + Seed mod (Upper - Lower).
|
|
40
40
|
|
|
41
41
|
set_random(Seed) :-
|
|
42
|
-
|
|
42
|
+
random__set_seed(Seed).
|
|
43
|
+
|
|
44
|
+
random__throw(Formal, Predicate) :-
|
|
45
|
+
throw(error(Formal, [predicate-Predicate])).
|
|
43
46
|
|
|
44
47
|
random__check_integer_range(Lower, Upper) :-
|
|
45
|
-
( var(Lower) -> instantiation_error
|
|
46
|
-
; var(Upper) -> instantiation_error
|
|
48
|
+
( var(Lower) -> random__throw(instantiation_error, random_integer/3)
|
|
49
|
+
; var(Upper) -> random__throw(instantiation_error, random_integer/3)
|
|
47
50
|
; integer(Lower) -> true
|
|
48
|
-
; type_error(integer, Lower)
|
|
51
|
+
; random__throw(type_error(integer, Lower), random_integer/3)
|
|
49
52
|
),
|
|
50
53
|
( integer(Upper) -> true
|
|
51
|
-
; type_error(integer, Upper)
|
|
54
|
+
; random__throw(type_error(integer, Upper), random_integer/3)
|
|
52
55
|
).
|
|
53
56
|
|
|
54
57
|
random__set_seed(Seed) :-
|
|
55
|
-
( var(Seed) -> instantiation_error
|
|
58
|
+
( var(Seed) -> random__throw(instantiation_error, set_random/1)
|
|
56
59
|
; Seed = seed(S) ->
|
|
57
|
-
( var(S) -> instantiation_error
|
|
60
|
+
( var(S) -> random__throw(instantiation_error, set_random/1)
|
|
58
61
|
; integer(S) ->
|
|
59
62
|
random__random_normalize_seed(S, Normalized),
|
|
60
63
|
bb_put('$random_seed', Normalized)
|
|
61
|
-
; type_error(integer, S)
|
|
64
|
+
; random__throw(type_error(integer, S), set_random/1)
|
|
62
65
|
)
|
|
63
|
-
; type_error(random_state, Seed)
|
|
66
|
+
; random__throw(type_error(random_state, Seed), set_random/1)
|
|
64
67
|
).
|
|
65
68
|
|
|
66
69
|
random__current_seed(Seed) :- bb_get('$random_seed', Seed), !.
|
|
@@ -67,7 +67,7 @@ Status values are:
|
|
|
67
67
|
| 7.11.2.2 | Effect when `debug=on` | The flag is accepted and stored; it does not change goal semantics or enable a debugger. | **defined** — `src/solver.js`; no semantic branch depends on `debug`. |
|
|
68
68
|
| 7.11.2.3 | Default `max_arity` | `unbounded`: EyeProlog imposes no fixed semantic ceiling on compound-term arity. Practical host allocation exhaustion is a resource condition. This flag is distinct from any potential implementation-specific procedure-arity limit; EyeProlog currently declares no separate finite procedure limit. | **defined** — `src/iso-limits.js`, `src/solver.js`, `src/parser.js`, `src/iso.js`; strict regression coverage. |
|
|
69
69
|
| 7.11.2.5 | Default `double_quotes` | `chars`. | **defined** — `src/solver.js`, parser flag state. |
|
|
70
|
-
| 7.12.1 | Second argument of `error/2` | A list of context elements, innermost last.
|
|
70
|
+
| 7.12.1 | Second argument of `error/2` | A list of context elements, innermost last. Elements are pairs; a built-in contributes `predicate-F/A`, so `atom_length(1.0, _)` raises `error(type_error(atom, 1.0), [predicate-atom_length/2])`. Errors raised outside a built-in frame, and errors thrown from shared pre-built instances reused by several built-ins, use `[]`. Each enclosing `call_with_error_context/2` prepends a copy of its own element, so contexts compose into proper lists. | **defined** — `attachBuiltinErrorContext()` in `src/errors.js`, `formalErrorTerm()` in `src/iso.js`, `call_with_error_context/2` in `src/lib/error.pl`. |
|
|
71
71
|
| 7.12.2(f) | Implementation-defined representation limits | Character and character-code operations are limited to Unicode scalar values; surrogates and values above U+10FFFF are representation errors. Arity/integer values are modeled as unbounded but may hit host/resource limits. Float input overflow uses the implementation-specific `max_float`/`min_float` representation names documented by the STC-oriented tests. | **defined** — parser/ISO numeric and character guards. |
|
|
72
72
|
| 8.17.1 | Implementation-defined flag value ranges | Strict mode exposes only Part 1 core flags and their standard value sets. Normal mode additionally exposes EyeProlog's `occurs_check` and `default_procedure_access` flags. With `bounded=false`, `max_integer` and `min_integer` have no current or selectable value and their `current_prolog_flag/2` queries fail. Valid alternative values of fixed standard flags are distinguished from invalid values so `set_prolog_flag/2` reports permission versus domain errors as prescribed. | **defined** — strict registry/flag filtering in `src/solver.js`; strict flag tests. |
|
|
73
73
|
| 8.17.3 | Other effects of `halt/0` | Terminates EyeProlog execution and returns host/process status `0`; it produces no Prolog solution. | **defined** — `HaltSignal`, `haltBuiltin()`, CLI/runner handling. |
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
answer(7, red, caught([atom_length / 2]), first, caught([])).
|
|
2
|
-
answer(7, red, caught([atom_length / 2]), second, caught([])).
|
|
1
|
+
answer(7, red, caught([predicate - atom_length / 2]), first, caught([])).
|
|
2
|
+
answer(7, red, caught([predicate - atom_length / 2]), second, caught([])).
|
|
3
3
|
flags(off, on, [pair(bounded, false), pair(integer_rounding_function, toward_zero), pair(char_conversion, on), pair(debug, off), pair(max_arity, unbounded), pair(unknown, fail), pair(double_quotes, chars), pair(occurs_check, true), pair(default_procedure_access, private)]).
|
|
@@ -1891,7 +1891,7 @@ c4 ?- call((!;1)).
|
|
|
1891
1891
|
assertIncludes(repl.stdout, 'T = ./*. .', 'REPL dotted graphic atom answer');
|
|
1892
1892
|
assertNotIncludes(repl.stdout, "T = './*.'", 'REPL dotted graphic atom has no spurious quotes');
|
|
1893
1893
|
assertIncludes(repl.stdout, 'T = ok.', 'REPL following read answer');
|
|
1894
|
-
assertIncludes(repl.stdout, 'error(syntax_error(read_term), [read/1])', 'REPL syntax error');
|
|
1894
|
+
assertIncludes(repl.stdout, 'error(syntax_error(read_term), [predicate-read/1])', 'REPL syntax error');
|
|
1895
1895
|
assertEqual(repl.stderr, '', 'REPL stderr');
|
|
1896
1896
|
|
|
1897
1897
|
const continuedGraphic = runCli([], {
|
|
@@ -2719,8 +2719,8 @@ c4 ?- call((!;1)).
|
|
|
2719
2719
|
});
|
|
2720
2720
|
assertEqual(result.status, 0, 'exit status');
|
|
2721
2721
|
assertEqual(result.stdout,
|
|
2722
|
-
'?- error(type_error(list, [1, [], _A | 2]), [number_chars/2]).\n' +
|
|
2723
|
-
'?- error(type_error(list, [1, [], _A | 2]), [number_chars/2]).\n' +
|
|
2722
|
+
'?- error(type_error(list, [1, [], _A | 2]), [predicate-number_chars/2]).\n' +
|
|
2723
|
+
'?- error(type_error(list, [1, [], _A | 2]), [predicate-number_chars/2]).\n' +
|
|
2724
2724
|
'?- ',
|
|
2725
2725
|
'stdout');
|
|
2726
2726
|
assertEqual(result.stderr, '', 'stderr');
|
|
@@ -2734,8 +2734,8 @@ c4 ?- call((!;1)).
|
|
|
2734
2734
|
});
|
|
2735
2735
|
assertEqual(result.status, 0, 'exit status');
|
|
2736
2736
|
assertEqual(result.stdout,
|
|
2737
|
-
'?- error(instantiation_error, [(is)/2]).\n' +
|
|
2738
|
-
'?- Error = instantiation_error, Imp_def = [(is)/2].\n' +
|
|
2737
|
+
'?- error(instantiation_error, [predicate-(is)/2]).\n' +
|
|
2738
|
+
'?- Error = instantiation_error, Imp_def = [predicate-(is)/2].\n' +
|
|
2739
2739
|
'?- ',
|
|
2740
2740
|
'stdout');
|
|
2741
2741
|
assertEqual(result.stderr, '', 'stderr');
|
|
@@ -4044,7 +4044,7 @@ child.stdin.write(\`consult(${consultedAtom}).\\n\`);
|
|
|
4044
4044
|
const result = runCli([], { input: 'statistics(nonsense, Value).\nhalt.\n' });
|
|
4045
4045
|
assertEqual(result.status, 0, 'exit status');
|
|
4046
4046
|
assertIncludes(result.stdout,
|
|
4047
|
-
'error(domain_error(statistics_key, nonsense), [statistics/2]).',
|
|
4047
|
+
'error(domain_error(statistics_key, nonsense), [predicate-statistics/2]).',
|
|
4048
4048
|
'statistics key error');
|
|
4049
4049
|
assertEqual(result.stderr, '', 'stderr');
|
|
4050
4050
|
},
|
|
@@ -4473,7 +4473,7 @@ answer(Result) :- countdown(2048, Result), Result = 2048.
|
|
|
4473
4473
|
'answer(C) :- catch(call_with_error_context(atom_length(1.0,_), outer-1), error(_,C), true).\n';
|
|
4474
4474
|
const result = runCli(['-'], { input });
|
|
4475
4475
|
assertEqual(result.status, 0, `exit status; stderr=${result.stderr}`);
|
|
4476
|
-
assertIncludes(result.stdout, 'answer([outer - 1, atom_length / 2])', 'composed context');
|
|
4476
|
+
assertIncludes(result.stdout, 'answer([outer - 1, predicate - atom_length / 2])', 'composed context');
|
|
4477
4477
|
},
|
|
4478
4478
|
},
|
|
4479
4479
|
{
|
|
@@ -4487,13 +4487,13 @@ answer(Result) :- countdown(2048, Result), Result = 2048.
|
|
|
4487
4487
|
'answer(C) :- catch(must_be(integer, a), error(_,C), true).\n';
|
|
4488
4488
|
const result = runCli(['-'], { input });
|
|
4489
4489
|
assertEqual(result.status, 0, `exit status; stderr=${result.stderr}`);
|
|
4490
|
-
assertIncludes(result.stdout, 'answer([must_be / 2])', 'must_be context is a list');
|
|
4490
|
+
assertIncludes(result.stdout, 'answer([predicate - must_be / 2])', 'must_be context is a list');
|
|
4491
4491
|
|
|
4492
4492
|
const composed = ':- use_module(library(error)).\n%% goal: answer(X)\n' +
|
|
4493
4493
|
'answer(C) :- catch(call_with_error_context(must_be(integer, a), outer-1), error(_,C), true).\n';
|
|
4494
4494
|
const nested = runCli(['-'], { input: composed });
|
|
4495
4495
|
assertEqual(nested.status, 0, `nested exit status; stderr=${nested.stderr}`);
|
|
4496
|
-
assertIncludes(nested.stdout, 'answer([outer - 1, must_be / 2])', 'composes as a proper list');
|
|
4496
|
+
assertIncludes(nested.stdout, 'answer([outer - 1, predicate - must_be / 2])', 'composes as a proper list');
|
|
4497
4497
|
},
|
|
4498
4498
|
},
|
|
4499
4499
|
{
|
|
@@ -4505,11 +4505,51 @@ answer(Result) :- countdown(2048, Result), Result = 2048.
|
|
|
4505
4505
|
const repl = runCli([], {
|
|
4506
4506
|
input: 'must_be(integer, a).\nthrow(foo).\nhalt.\n',
|
|
4507
4507
|
});
|
|
4508
|
-
assertIncludes(repl.stdout, 'error(type_error(integer, a), [must_be/2])', 'thrown ISO error');
|
|
4508
|
+
assertIncludes(repl.stdout, 'error(type_error(integer, a), [predicate-must_be/2])', 'thrown ISO error');
|
|
4509
4509
|
assertNotIncludes(repl.stdout, 'throw(error(type_error', 'no throw/1 wrapper on error/2');
|
|
4510
4510
|
assertIncludes(repl.stdout, 'throw(foo)', 'non-error ball keeps the wrapper');
|
|
4511
4511
|
},
|
|
4512
4512
|
},
|
|
4513
|
+
{
|
|
4514
|
+
// must_be/2 is called constantly by library code, so its context must be
|
|
4515
|
+
// supplied without a catch/3 frame: wrapping a Prolog goal in catch/3
|
|
4516
|
+
// costs a child Solver per call and made the suite ~35% slower.
|
|
4517
|
+
name: 'must_be/2 does not pay for a catch frame on success',
|
|
4518
|
+
run: () => {
|
|
4519
|
+
const input = '%% goal: answer(X)\n' +
|
|
4520
|
+
'loop(0) :- !.\nloop(N) :- must_be(integer, N), M is N - 1, loop(M).\n' +
|
|
4521
|
+
'answer(ok) :- loop(20000).\n';
|
|
4522
|
+
const started = Date.now();
|
|
4523
|
+
const result = runCli(['-'], { input });
|
|
4524
|
+
const elapsed = Date.now() - started;
|
|
4525
|
+
assertEqual(result.status, 0, `exit status; stderr=${result.stderr}`);
|
|
4526
|
+
assertIncludes(result.stdout, 'answer(ok)', 'loop completes');
|
|
4527
|
+
// Generous bound: the catch/3 wrapper made this roughly 2x slower.
|
|
4528
|
+
if (elapsed >= 20000) throw new Error(`20k must_be/2 calls took ${elapsed}ms`);
|
|
4529
|
+
},
|
|
4530
|
+
},
|
|
4531
|
+
{
|
|
4532
|
+
// Issue #99: the context element must be a pair, as in Scryer and
|
|
4533
|
+
// Trealla, and predicate contexts use the predicate-F/A convention.
|
|
4534
|
+
name: 'call_with_error_context/2 requires a pair as its context element',
|
|
4535
|
+
run: () => {
|
|
4536
|
+
const bad = ':- use_module(library(error)).\n%% goal: answer(X)\n' +
|
|
4537
|
+
'answer(C) :- catch(call_with_error_context(true, x), error(E,_), C = E).\n';
|
|
4538
|
+
const result = runCli(['-'], { input: bad });
|
|
4539
|
+
assertEqual(result.status, 0, `exit status; stderr=${result.stderr}`);
|
|
4540
|
+
assertIncludes(result.stdout, 'answer(type_error(pair, x))', 'non-pair element rejected');
|
|
4541
|
+
|
|
4542
|
+
const unbound = ':- use_module(library(error)).\n%% goal: answer(X)\n' +
|
|
4543
|
+
'answer(C) :- catch(call_with_error_context(true, _), error(E,_), C = E).\n';
|
|
4544
|
+
const varResult = runCli(['-'], { input: unbound });
|
|
4545
|
+
assertIncludes(varResult.stdout, 'answer(instantiation_error)', 'unbound element rejected');
|
|
4546
|
+
|
|
4547
|
+
const good = ':- use_module(library(error)).\n%% goal: answer(X)\n' +
|
|
4548
|
+
'answer(ok) :- call_with_error_context(true, a-b).\n';
|
|
4549
|
+
const okResult = runCli(['-'], { input: good });
|
|
4550
|
+
assertIncludes(okResult.stdout, 'answer(ok)', 'pair element accepted');
|
|
4551
|
+
},
|
|
4552
|
+
},
|
|
4513
4553
|
{
|
|
4514
4554
|
// Nesting prepends outermost-first and keeps the raising predicate last.
|
|
4515
4555
|
name: 'nested call_with_error_context/2 accumulates outermost first',
|
|
@@ -4518,7 +4558,7 @@ answer(Result) :- countdown(2048, Result), Result = 2048.
|
|
|
4518
4558
|
'answer(C) :- catch(call_with_error_context(call_with_error_context(atom_length(1.0,_), inner-1), outer-2), error(_,C), true).\n';
|
|
4519
4559
|
const result = runCli(['-'], { input });
|
|
4520
4560
|
assertEqual(result.status, 0, `exit status; stderr=${result.stderr}`);
|
|
4521
|
-
assertIncludes(result.stdout, 'answer([outer - 2, inner - 1, atom_length / 2])', 'nesting order');
|
|
4561
|
+
assertIncludes(result.stdout, 'answer([outer - 2, inner - 1, predicate - atom_length / 2])', 'nesting order');
|
|
4522
4562
|
},
|
|
4523
4563
|
},
|
|
4524
4564
|
{
|
|
@@ -4527,7 +4567,7 @@ answer(Result) :- countdown(2048, Result), Result = 2048.
|
|
|
4527
4567
|
name: 'call_with_error_context/2 copies the context element',
|
|
4528
4568
|
run: () => {
|
|
4529
4569
|
const input = ':- use_module(library(error)).\n%% goal: answer(X)\n' +
|
|
4530
|
-
'answer(ok) :- catch(call_with_error_context(atom_length(1.0,_), ctx
|
|
4570
|
+
'answer(ok) :- catch(call_with_error_context(atom_length(1.0,_), ctx-V), error(_,[ctx-W|_]), (V == W -> fail ; true)).\n';
|
|
4531
4571
|
const result = runCli(['-'], { input });
|
|
4532
4572
|
assertEqual(result.status, 0, `exit status; stderr=${result.stderr}`);
|
|
4533
4573
|
assertIncludes(result.stdout, 'answer(ok)', 'context element is a fresh copy');
|
|
@@ -5152,7 +5192,7 @@ answer(Result) :- countdown(2048, Result), Result = 2048.
|
|
|
5152
5192
|
goal: 'answer(T)',
|
|
5153
5193
|
ioOptions: { input: invalidOctal },
|
|
5154
5194
|
}).stdout,
|
|
5155
|
-
'answer(error(syntax_error(read_term), [read / 1])).\n',
|
|
5195
|
+
'answer(error(syntax_error(read_term), [predicate - read / 1])).\n',
|
|
5156
5196
|
'read/1 rejects non-octal numeric escape',
|
|
5157
5197
|
);
|
|
5158
5198
|
},
|
package/test/run-iso-strict.mjs
CHANGED
|
@@ -649,7 +649,7 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
649
649
|
'close(tmp_in)',
|
|
650
650
|
'current_input(C)',
|
|
651
651
|
'stream_property(C,alias(user_input))',
|
|
652
|
-
'catch(set_input(tmp_in),error(existence_error(stream,tmp_in),[set_input/1]),true)',
|
|
652
|
+
'catch(set_input(tmp_in),error(existence_error(stream,tmp_in),[predicate-set_input/1]),true)',
|
|
653
653
|
].join(','),
|
|
654
654
|
}).stats.completed_goal_lists, 1, 'stream-term, alias lifetime, target/current input, and close fallback');
|
|
655
655
|
|
|
@@ -856,14 +856,14 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
856
856
|
|
|
857
857
|
reporter.test('closes the ISO 7.12 processor error envelope and classification rows', () => {
|
|
858
858
|
const caught = [
|
|
859
|
-
"catch(atom_length(X,N),error(instantiation_error,[atom_length/2]),true)",
|
|
860
|
-
"catch(atom_length(1,N),error(type_error(atom,1),[atom_length/2]),true)",
|
|
861
|
-
"catch(op(1300,xfx,foo),error(domain_error(operator_priority,1300),[op/3]),true)",
|
|
859
|
+
"catch(atom_length(X,N),error(instantiation_error,[predicate-atom_length/2]),true)",
|
|
860
|
+
"catch(atom_length(1,N),error(type_error(atom,1),[predicate-atom_length/2]),true)",
|
|
861
|
+
"catch(op(1300,xfx,foo),error(domain_error(operator_priority,1300),[predicate-op/3]),true)",
|
|
862
862
|
"catch(call(no_such_predicate),error(existence_error(procedure,no_such_predicate/0),[]),true)",
|
|
863
|
-
"catch(abolish(atom/1),error(permission_error(modify,static_procedure,atom/1),[abolish/1]),true)",
|
|
864
|
-
"catch(char_code(C,1114112),error(representation_error(character_code),[char_code/2]),true)",
|
|
865
|
-
"catch(X is 1/0,error(evaluation_error(zero_divisor),[(is)/2]),true)",
|
|
866
|
-
"catch(X is 1<<4294967296,error(resource_error(memory),[(is)/2]),true)",
|
|
863
|
+
"catch(abolish(atom/1),error(permission_error(modify,static_procedure,atom/1),[predicate-abolish/1]),true)",
|
|
864
|
+
"catch(char_code(C,1114112),error(representation_error(character_code),[predicate-char_code/2]),true)",
|
|
865
|
+
"catch(X is 1/0,error(evaluation_error(zero_divisor),[predicate-(is)/2]),true)",
|
|
866
|
+
"catch(X is 1<<4294967296,error(resource_error(memory),[predicate-(is)/2]),true)",
|
|
867
867
|
];
|
|
868
868
|
for (const goal of caught) {
|
|
869
869
|
equal(run('', { isoStrict: true, goal }).stats.completed_goal_lists, 1, goal);
|
|
@@ -871,14 +871,14 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
871
871
|
|
|
872
872
|
equal(run('', {
|
|
873
873
|
isoStrict: true,
|
|
874
|
-
goal: "catch(read_term(T,[]),error(syntax_error(read_term),[read_term/2]),true)",
|
|
874
|
+
goal: "catch(read_term(T,[]),error(syntax_error(read_term),[predicate-read_term/2]),true)",
|
|
875
875
|
ioOptions: { input: "'unterminated." },
|
|
876
876
|
}).stats.completed_goal_lists, 1, 'syntax error uses error/2 envelope and implementation-defined context');
|
|
877
877
|
|
|
878
878
|
const systemSolver = new Solver(Program.parse('', { isoStrict: true }), { isoStrict: true });
|
|
879
879
|
systemSolver.io.flush = () => { throw new Error('simulated host I/O failure'); };
|
|
880
880
|
equal([...systemSolver.solve([
|
|
881
|
-
parseGoalText('catch(flush_output(user_output),error(system_error,[flush_output/1]),true)', { isoStrict: true }),
|
|
881
|
+
parseGoalText('catch(flush_output(user_output),error(system_error,[predicate-flush_output/1]),true)', { isoStrict: true }),
|
|
882
882
|
], new Env(), 0)].length, 1, 'system error uses error/2 envelope and implementation-defined context');
|
|
883
883
|
|
|
884
884
|
// ISO 7.12 deliberately leaves the choice implementation-dependent when
|