eyeprolog 1.5.59 → 1.5.61

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.59",
6
+ "version": "1.5.61",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
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('/', [atom(goal.name), numberTerm(goal.arity)]),
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/iso.js CHANGED
@@ -3219,6 +3219,11 @@ function* catchSolutions({ solver, goal, env }, state) {
3219
3219
  if (child != null) solver.absorbStatsFrom(child);
3220
3220
  }
3221
3221
  }
3222
+ // library(error) call_with_error_context/2, as a primitive rather than a
3223
+ // Prolog catch/3 wrapper (issue #98). Context elements are assembled only while
3224
+ // an error unwinds, so the success path pays for neither a clause resolution
3225
+ // nor a Prolog-level catch frame. The element is copied as it is added, so
3226
+ // variables in it cannot alias bindings that the unwinding undoes.
3222
3227
  function* throwBuiltin({ goal, env }) {
3223
3228
  const ball = deref(goal.args[0], env);
3224
3229
  if (ball.type === VAR) throw new PrologError('instantiation_error');
package/src/lib/error.pl CHANGED
@@ -17,56 +17,62 @@
17
17
 
18
18
  :- meta_predicate(call_with_error_context(0, +)).
19
19
 
20
+ % The context is supplied once here rather than handed over manually at every
21
+ % raise site: the raise sites throw with [] and this wrapper prepends its
22
+ % element, so contexts stay proper lists and compose with any enclosing
23
+ % call_with_error_context/2 (issue #98).
20
24
  must_be(Type, Term) :-
21
- ( var(Type) -> instantiation_error(must_be/2)
22
- ; error__must_be(Type, Term)
23
- ).
25
+ call_with_error_context(
26
+ ( var(Type) -> instantiation_error
27
+ ; error__must_be(Type, Term)
28
+ ),
29
+ predicate-must_be/2).
24
30
 
25
31
  error__must_be(integer, Term) :- !,
26
- ( var(Term) -> instantiation_error(must_be/2)
32
+ ( var(Term) -> instantiation_error
27
33
  ; integer(Term) -> true
28
- ; type_error(integer, Term, must_be/2)
34
+ ; type_error(integer, Term)
29
35
  ).
30
36
  error__must_be(atom, Term) :- !,
31
- ( var(Term) -> instantiation_error(must_be/2)
37
+ ( var(Term) -> instantiation_error
32
38
  ; atom(Term) -> true
33
- ; type_error(atom, Term, must_be/2)
39
+ ; type_error(atom, Term)
34
40
  ).
35
41
  error__must_be(number, Term) :- !,
36
- ( var(Term) -> instantiation_error(must_be/2)
42
+ ( var(Term) -> instantiation_error
37
43
  ; number(Term) -> true
38
- ; type_error(number, Term, must_be/2)
44
+ ; type_error(number, Term)
39
45
  ).
40
46
  error__must_be(var, Term) :- !,
41
47
  ( var(Term) -> true
42
- ; throw(error(uninstantiation_error(Term), must_be/2))
48
+ ; throw(error(uninstantiation_error(Term), []))
43
49
  ).
44
50
  error__must_be(ground, Term) :- !,
45
- ( ground(Term) -> true ; instantiation_error(must_be/2) ).
51
+ ( ground(Term) -> true ; instantiation_error ).
46
52
  error__must_be(acyclic, Term) :- !,
47
- ( acyclic_term(Term) -> true ; type_error(acyclic_term, Term, must_be/2) ).
53
+ ( acyclic_term(Term) -> true ; type_error(acyclic_term, Term) ).
48
54
  error__must_be(list, Term) :- !,
49
55
  error__proper_list(Term).
50
56
  error__must_be(list(Type), Term) :- !,
51
57
  error__proper_list_of(Term, Type).
52
58
  error__must_be(pair, Term) :- !,
53
- ( var(Term) -> instantiation_error(must_be/2)
59
+ ( var(Term) -> instantiation_error
54
60
  ; Term = _-_ -> true
55
- ; type_error(pair, Term, must_be/2)
61
+ ; type_error(pair, Term)
56
62
  ).
57
63
  error__must_be(not_less_than_zero, Term) :- !,
58
64
  must_be(integer, Term),
59
- ( Term >= 0 -> true ; domain_error(not_less_than_zero, Term, must_be/2) ).
65
+ ( Term >= 0 -> true ; domain_error(not_less_than_zero, Term) ).
60
66
  error__must_be(Type, Term) :-
61
- ( var(Term) -> instantiation_error(must_be/2)
62
- ; type_error(Type, Term, must_be/2)
67
+ ( var(Term) -> instantiation_error
68
+ ; type_error(Type, Term)
63
69
  ).
64
70
 
65
71
  error__proper_list([]) :- !.
66
72
  error__proper_list([_|Tail]) :- !, error__proper_list(Tail).
67
73
  error__proper_list(Term) :-
68
- ( var(Term) -> instantiation_error(must_be/2)
69
- ; type_error(list, Term, must_be/2)
74
+ ( var(Term) -> instantiation_error
75
+ ; type_error(list, Term)
70
76
  ).
71
77
 
72
78
  error__proper_list_of([], _) :- !.
@@ -74,8 +80,8 @@ error__proper_list_of([Head|Tail], Type) :- !,
74
80
  must_be(Type, Head),
75
81
  error__proper_list_of(Tail, Type).
76
82
  error__proper_list_of(Term, _) :-
77
- ( var(Term) -> instantiation_error(must_be/2)
78
- ; type_error(list, Term, must_be/2)
83
+ ( var(Term) -> instantiation_error
84
+ ; type_error(list, Term)
79
85
  ).
80
86
 
81
87
  can_be(Type, Term) :-
@@ -122,11 +128,22 @@ resource_error(Resource) :- throw(error(resource_error(Resource), [])).
122
128
  resource_error(Resource, Context) :- throw(error(resource_error(Resource), Context)).
123
129
 
124
130
  % Context elements are assembled only when an error actually propagates, so
125
- % the success path costs nothing. The added element is copied so that
126
- % variables in it are not shared with the goal's bindings, which would
127
- % otherwise be undone as the error unwinds.
131
+ % the prepend costs nothing on success. The enclosing catch/3 frame is not
132
+ % free, though: see issue #98 for measurements and the primitive design that
133
+ % would remove it.
128
134
  call_with_error_context(Goal, Pair) :-
135
+ error__require_pair(Pair),
129
136
  catch(Goal,
130
137
  error(Error, Context),
131
138
  ( copy_term(Pair, Element),
132
139
  throw(error(Error, [Element|Context])) )).
140
+
141
+ % The element must be a pair (issue #99). This cannot go through must_be/2:
142
+ % must_be/2 declares its own context with call_with_error_context/2, so
143
+ % checking with must_be/2 here would recurse. The reported context matches
144
+ % what must_be(pair, _) would have produced.
145
+ error__require_pair(Pair) :-
146
+ ( var(Pair) -> throw(error(instantiation_error, [predicate-must_be/2]))
147
+ ; Pair = _-_ -> true
148
+ ; throw(error(type_error(pair, Pair), [predicate-must_be/2]))
149
+ ).
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
- :- use_module(library(error), [instantiation_error/1, type_error/3]).
13
+ :- use_module(library(error), [call_with_error_context/2, instantiation_error/0, type_error/2]).
14
14
 
15
15
  maybe :-
16
16
  random_integer(0, 2, 0).
@@ -28,15 +28,10 @@ maybe(K, N) :-
28
28
  random(Value) :-
29
29
  eyeprolog__random_value(Value).
30
30
 
31
+ % Context declared once per predicate instead of at each raise site, so the
32
+ % raise sites throw with [] and contexts stay proper composable lists.
31
33
  random_integer(Lower, Upper, R) :-
32
- ( var(Lower) -> instantiation_error(random_integer/3)
33
- ; var(Upper) -> instantiation_error(random_integer/3)
34
- ; integer(Lower) -> true
35
- ; type_error(integer, Lower, random_integer/3)
36
- ),
37
- ( integer(Upper) -> true
38
- ; type_error(integer, Upper, random_integer/3)
39
- ),
34
+ call_with_error_context(random__check_integer_range(Lower, Upper), predicate-random_integer/3),
40
35
  Lower < Upper,
41
36
  random__current_seed(Seed0),
42
37
  random(Seed0, _, Seed),
@@ -44,15 +39,28 @@ random_integer(Lower, Upper, R) :-
44
39
  R is Lower + Seed mod (Upper - Lower).
45
40
 
46
41
  set_random(Seed) :-
47
- ( var(Seed) -> instantiation_error(set_random/1)
42
+ call_with_error_context(random__set_seed(Seed), predicate-set_random/1).
43
+
44
+ random__check_integer_range(Lower, Upper) :-
45
+ ( var(Lower) -> instantiation_error
46
+ ; var(Upper) -> instantiation_error
47
+ ; integer(Lower) -> true
48
+ ; type_error(integer, Lower)
49
+ ),
50
+ ( integer(Upper) -> true
51
+ ; type_error(integer, Upper)
52
+ ).
53
+
54
+ random__set_seed(Seed) :-
55
+ ( var(Seed) -> instantiation_error
48
56
  ; Seed = seed(S) ->
49
- ( var(S) -> instantiation_error(set_random/1)
57
+ ( var(S) -> instantiation_error
50
58
  ; integer(S) ->
51
59
  random__random_normalize_seed(S, Normalized),
52
60
  bb_put('$random_seed', Normalized)
53
- ; type_error(integer, S, set_random/1)
61
+ ; type_error(integer, S)
54
62
  )
55
- ; type_error(random_state, Seed, set_random/1)
63
+ ; type_error(random_state, Seed)
56
64
  ).
57
65
 
58
66
  random__current_seed(Seed) :- bb_get('$random_seed', Seed), !.
package/src/repl.js CHANGED
@@ -861,8 +861,16 @@ function formatError(engine, state, error) {
861
861
  // Reuse the same conversion as catch/3 so uncaught errors at the top
862
862
  // level cannot lose the implementation-defined context or misplace a
863
863
  // culprit as the second argument of error/2.
864
+ // A thrown ball that is already an error/2 envelope is the same thing a
865
+ // built-in raises, so display it the same way. Wrapping only that case in
866
+ // throw/1 made one error print two different ways depending on whether the
867
+ // engine or Prolog code raised it. Other balls keep the wrapper, because
868
+ // throw(foo) has no error/2 envelope to show.
869
+ const thrownBall = error.name === 'ThrownTerm' ? engine.deref(error.term, env) : null;
870
+ const thrownIsErrorEnvelope = thrownBall != null
871
+ && thrownBall.type === 'compound' && thrownBall.name === 'error' && thrownBall.arity === 2;
864
872
  const term = error.name === 'ThrownTerm'
865
- ? engine.compound('throw', [error.term])
873
+ ? (thrownIsErrorEnvelope ? thrownBall : engine.compound('throw', [error.term]))
866
874
  : formalErrorTerm(error);
867
875
  collectUnboundVariables(engine, term, env, variableNames, () => `_${letterName(generated++)}`);
868
876
  return `${engine.formatTermForWrite(term, env, {
@@ -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. A built-in contributes its own predicate indicator, so `atom_length(1.0, _)` raises `error(type_error(atom, 1.0), [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`. |
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,63 @@ 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
+ },
4478
+ },
4479
+ {
4480
+ // Issue #98: library predicates declare their context once with
4481
+ // call_with_error_context/2 rather than handing it over manually at each
4482
+ // raise site. Manual handover produced a bare, non-list context, so it
4483
+ // composed into an improper list.
4484
+ name: 'library predicates yield composable contexts, not manual ones',
4485
+ run: () => {
4486
+ const input = ':- use_module(library(error)).\n:- use_module(library(random)).\n%% goal: answer(X)\n' +
4487
+ 'answer(C) :- catch(must_be(integer, a), error(_,C), true).\n';
4488
+ const result = runCli(['-'], { input });
4489
+ assertEqual(result.status, 0, `exit status; stderr=${result.stderr}`);
4490
+ assertIncludes(result.stdout, 'answer([predicate - must_be / 2])', 'must_be context is a list');
4491
+
4492
+ const composed = ':- use_module(library(error)).\n%% goal: answer(X)\n' +
4493
+ 'answer(C) :- catch(call_with_error_context(must_be(integer, a), outer-1), error(_,C), true).\n';
4494
+ const nested = runCli(['-'], { input: composed });
4495
+ assertEqual(nested.status, 0, `nested exit status; stderr=${nested.stderr}`);
4496
+ assertIncludes(nested.stdout, 'answer([outer - 1, predicate - must_be / 2])', 'composes as a proper list');
4497
+ },
4498
+ },
4499
+ {
4500
+ // An error/2 ball raised by Prolog throw/1 is the same thing a built-in
4501
+ // raises, so the top level must display it the same way. Only balls
4502
+ // without an error/2 envelope keep the throw/1 wrapper.
4503
+ name: 'uncaught error/2 balls display without a throw/1 wrapper',
4504
+ run: () => {
4505
+ const repl = runCli([], {
4506
+ input: 'must_be(integer, a).\nthrow(foo).\nhalt.\n',
4507
+ });
4508
+ assertIncludes(repl.stdout, 'error(type_error(integer, a), [predicate-must_be/2])', 'thrown ISO error');
4509
+ assertNotIncludes(repl.stdout, 'throw(error(type_error', 'no throw/1 wrapper on error/2');
4510
+ assertIncludes(repl.stdout, 'throw(foo)', 'non-error ball keeps the wrapper');
4511
+ },
4512
+ },
4513
+ {
4514
+ // Issue #99: the context element must be a pair, as in Scryer and
4515
+ // Trealla, and predicate contexts use the predicate-F/A convention.
4516
+ name: 'call_with_error_context/2 requires a pair as its context element',
4517
+ run: () => {
4518
+ const bad = ':- use_module(library(error)).\n%% goal: answer(X)\n' +
4519
+ 'answer(C) :- catch(call_with_error_context(true, x), error(E,_), C = E).\n';
4520
+ const result = runCli(['-'], { input: bad });
4521
+ assertEqual(result.status, 0, `exit status; stderr=${result.stderr}`);
4522
+ assertIncludes(result.stdout, 'answer(type_error(pair, x))', 'non-pair element rejected');
4523
+
4524
+ const unbound = ':- use_module(library(error)).\n%% goal: answer(X)\n' +
4525
+ 'answer(C) :- catch(call_with_error_context(true, _), error(E,_), C = E).\n';
4526
+ const varResult = runCli(['-'], { input: unbound });
4527
+ assertIncludes(varResult.stdout, 'answer(instantiation_error)', 'unbound element rejected');
4528
+
4529
+ const good = ':- use_module(library(error)).\n%% goal: answer(X)\n' +
4530
+ 'answer(ok) :- call_with_error_context(true, a-b).\n';
4531
+ const okResult = runCli(['-'], { input: good });
4532
+ assertIncludes(okResult.stdout, 'answer(ok)', 'pair element accepted');
4477
4533
  },
4478
4534
  },
4479
4535
  {
@@ -4484,7 +4540,7 @@ answer(Result) :- countdown(2048, Result), Result = 2048.
4484
4540
  'answer(C) :- catch(call_with_error_context(call_with_error_context(atom_length(1.0,_), inner-1), outer-2), error(_,C), true).\n';
4485
4541
  const result = runCli(['-'], { input });
4486
4542
  assertEqual(result.status, 0, `exit status; stderr=${result.stderr}`);
4487
- assertIncludes(result.stdout, 'answer([outer - 2, inner - 1, atom_length / 2])', 'nesting order');
4543
+ assertIncludes(result.stdout, 'answer([outer - 2, inner - 1, predicate - atom_length / 2])', 'nesting order');
4488
4544
  },
4489
4545
  },
4490
4546
  {
@@ -4493,7 +4549,7 @@ answer(Result) :- countdown(2048, Result), Result = 2048.
4493
4549
  name: 'call_with_error_context/2 copies the context element',
4494
4550
  run: () => {
4495
4551
  const input = ':- use_module(library(error)).\n%% goal: answer(X)\n' +
4496
- 'answer(ok) :- catch(call_with_error_context(atom_length(1.0,_), ctx(V)), error(_,[ctx(W)|_]), (V == W -> fail ; true)).\n';
4552
+ 'answer(ok) :- catch(call_with_error_context(atom_length(1.0,_), ctx-V), error(_,[ctx-W|_]), (V == W -> fail ; true)).\n';
4497
4553
  const result = runCli(['-'], { input });
4498
4554
  assertEqual(result.status, 0, `exit status; stderr=${result.stderr}`);
4499
4555
  assertIncludes(result.stdout, 'answer(ok)', 'context element is a fresh copy');
@@ -5118,7 +5174,7 @@ answer(Result) :- countdown(2048, Result), Result = 2048.
5118
5174
  goal: 'answer(T)',
5119
5175
  ioOptions: { input: invalidOctal },
5120
5176
  }).stdout,
5121
- 'answer(error(syntax_error(read_term), [read / 1])).\n',
5177
+ 'answer(error(syntax_error(read_term), [predicate - read / 1])).\n',
5122
5178
  'read/1 rejects non-octal numeric escape',
5123
5179
  );
5124
5180
  },
@@ -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