eyeprolog 1.5.56 → 1.5.58

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.56",
6
+ "version": "1.5.58",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/cli.js CHANGED
@@ -335,6 +335,7 @@ Options:
335
335
  --proof-detail mode Use abstract or expanded proof detail (implies --proof).
336
336
  --verify-proof file Verify why/2 proof certificates against the input program.
337
337
  -q, --quads Run embedded quad tests and fail if any do not hold.
338
+ Note: -q is quads, not quiet; --quiet has no short form.
338
339
  --quiet Suppress answer terms while preserving Prolog output.
339
340
  -s, --stats Print solver and memory statistics to stderr after execution.
340
341
  --iso-strict Use ISO/IEC 13211-1 core + Corrigenda 1-3 only;
package/src/errors.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // Keep these independent of the ISO builtin registry so syntax, DCG, program,
3
3
  // and solver layers can report Prolog errors without importing the whole ISO
4
4
  // implementation (and without creating semantic-layer import cycles).
5
- import { termToString } from './term.js';
5
+ import { atom, compound, numberTerm, termToString } from './term.js';
6
6
 
7
7
  export class PrologError extends Error {
8
8
  constructor(formal, culprit = null) {
@@ -21,3 +21,28 @@ export class HaltSignal extends Error {
21
21
  this.code = code;
22
22
  }
23
23
  }
24
+
25
+ // ISO 7.12.2 leaves the second argument of error/2 implementation defined.
26
+ // Reporting which built-in raised the error is far more useful than a constant,
27
+ // so built-in call sites attach the predicate indicator when the raising code
28
+ // did not supply a context of its own. The indicator term is built once per
29
+ // registry entry and reused, so the error path allocates nothing extra.
30
+ function builtinErrorContext(def, goal) {
31
+ let context = def._errorContextTerm;
32
+ if (context === undefined) {
33
+ context = compound('/', [atom(goal.name), numberTerm(goal.arity)]);
34
+ def._errorContextTerm = context;
35
+ }
36
+ return context;
37
+ }
38
+
39
+ // Shared, pre-built error instances are thrown many times from different
40
+ // built-ins, so tagging one with a context would leak the first thrower's
41
+ // indicator into every later report. They keep the default context, which also
42
+ // preserves the ground-error-term cache in formalErrorTerm.
43
+ export function attachBuiltinErrorContext(error, def, goal) {
44
+ if (!(error instanceof PrologError)) return error;
45
+ if (error.contextTerm != null || error._sharedInstance === true) return error;
46
+ error.contextTerm = builtinErrorContext(def, goal);
47
+ return error;
48
+ }
package/src/iso.js CHANGED
@@ -22,7 +22,7 @@ import {
22
22
  let isoFresh = 0;
23
23
 
24
24
  export { PrologError, HaltSignal } from './errors.js';
25
- import { PrologError, HaltSignal } from './errors.js';
25
+ import { HaltSignal, PrologError, attachBuiltinErrorContext } from './errors.js';
26
26
 
27
27
  class ThrownTerm extends Error {
28
28
  constructor(term) {
@@ -2452,6 +2452,7 @@ function numberListText(list, env, kind, valueIsBound, solver = null) {
2452
2452
  }
2453
2453
 
2454
2454
  const numberSyntaxError = new PrologError('syntax_error(number)');
2455
+ numberSyntaxError._sharedInstance = true;
2455
2456
 
2456
2457
  function numberListBuiltin(kind) {
2457
2458
  return function* ({ solver, goal, env }) {
@@ -3161,8 +3162,17 @@ function* catchSolutions({ solver, goal, env }, state) {
3161
3162
  // isolated in a child solver. Running it directly avoids constructing a
3162
3163
  // complete Solver for hot caught failures such as number_chars/2 syntax
3163
3164
  // probes, while the cloned environment keeps catch/3's rollback boundary.
3164
- const iterator = direct.handler({ solver, goal: invoked, env: env.clone() });
3165
- const result = iterator.next();
3165
+ let iterator;
3166
+ let result;
3167
+ try {
3168
+ iterator = direct.handler({ solver, goal: invoked, env: env.clone() });
3169
+ result = iterator.next();
3170
+ } catch (caught) {
3171
+ // This fast path bypasses the solver's builtin frame, so it has to
3172
+ // attach the raising predicate's indicator itself (see
3173
+ // attachBuiltinErrorContext in solver.js).
3174
+ throw attachBuiltinErrorContext(caught, direct, invoked);
3175
+ }
3166
3176
  if (result.done) solver.stats.deterministic_builtin_failures++;
3167
3177
  else {
3168
3178
  solver.stats.deterministic_builtin_successes++;
package/src/parser.js CHANGED
@@ -1138,7 +1138,7 @@ class Parser {
1138
1138
  }
1139
1139
  const operator = this.applyOperatorDirective(directive, line);
1140
1140
  if (!coreDirective && !extensionDirective && !operator) {
1141
- throw new Error(`parse line ${line}: bad term`);
1141
+ throw new Error(`parse line ${line}: ${unsupportedDirectiveMessage(directive, this.strictIso)}`);
1142
1142
  }
1143
1143
  this.expect(TOK.DOT, '.');
1144
1144
  this.applyParserFlagDirective(directive, line);
@@ -1911,3 +1911,76 @@ export function parseGoalText(text, options = {}) {
1911
1911
  }
1912
1912
  return head.args[0];
1913
1913
  }
1914
+
1915
+ // Directives are accepted from a fixed list, so an unrecognized one is a
1916
+ // different mistake from unparseable input and deserves a different message.
1917
+ // The three cases below cover what people actually write: a bare goal that
1918
+ // belongs in initialization/1, a misspelt directive name, and a term that is
1919
+ // not a directive at all.
1920
+ const CORE_DIRECTIVE_INDICATORS = [
1921
+ 'dynamic/1', 'multifile/1', 'discontiguous/1', 'initialization/1', 'include/1',
1922
+ 'ensure_loaded/1', 'char_conversion/2', 'set_prolog_flag/2', 'op/3',
1923
+ ];
1924
+ const EXTENSION_DIRECTIVE_INDICATORS = [
1925
+ 'use_module/1', 'use_module/2', 'meta_predicate/1', 'attribute/1', 'table/1',
1926
+ 'public/1', 'module/2',
1927
+ ];
1928
+
1929
+ function directiveNameDistance(a, b) {
1930
+ // Small Levenshtein distance, used only to suggest a near-miss spelling.
1931
+ const rows = a.length + 1;
1932
+ const cols = b.length + 1;
1933
+ let previous = Array.from({ length: cols }, (_, i) => i);
1934
+ for (let i = 1; i < rows; i++) {
1935
+ const current = [i];
1936
+ for (let j = 1; j < cols; j++) {
1937
+ current[j] = Math.min(
1938
+ previous[j] + 1,
1939
+ current[j - 1] + 1,
1940
+ previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1),
1941
+ );
1942
+ }
1943
+ previous = current;
1944
+ }
1945
+ return previous[cols - 1];
1946
+ }
1947
+
1948
+ function nearestDirectiveName(name, available) {
1949
+ let best = null;
1950
+ let bestDistance = Infinity;
1951
+ for (const indicator of available) {
1952
+ const candidate = indicator.slice(0, indicator.lastIndexOf('/'));
1953
+ const distance = directiveNameDistance(name, candidate);
1954
+ if (distance < bestDistance) {
1955
+ bestDistance = distance;
1956
+ best = candidate;
1957
+ }
1958
+ }
1959
+ // Only suggest a genuinely close spelling, not the alphabetically nearest.
1960
+ return bestDistance <= Math.max(1, Math.floor(name.length / 3)) ? best : null;
1961
+ }
1962
+
1963
+ function unsupportedDirectiveMessage(directive, strictIso) {
1964
+ const available = strictIso
1965
+ ? CORE_DIRECTIVE_INDICATORS
1966
+ : [...CORE_DIRECTIVE_INDICATORS, ...EXTENSION_DIRECTIVE_INDICATORS];
1967
+ if (directive.type !== COMPOUND && directive.type !== ATOM) {
1968
+ return 'a directive must be a callable term';
1969
+ }
1970
+ const name = directive.name;
1971
+ const arity = directive.type === COMPOUND ? directive.arity : 0;
1972
+ const indicator = `${name}/${arity}`;
1973
+ const known = available.filter((entry) => entry.slice(0, entry.lastIndexOf('/')) === name);
1974
+ if (known.length > 0) {
1975
+ return `directive ${indicator} has the wrong arity; expected ${known.join(' or ')}`;
1976
+ }
1977
+ const suggestion = nearestDirectiveName(name, available);
1978
+ if (suggestion != null) {
1979
+ return `unknown directive ${indicator}; did you mean ${suggestion}?`;
1980
+ }
1981
+ // A goal such as `:- write(hello), nl.` is well-formed but is not one of the
1982
+ // recognized declarations, so point at initialization/1 rather than reporting
1983
+ // a syntax error.
1984
+ return `unknown directive ${indicator}; to run a goal at load time use ` +
1985
+ ':- initialization(Goal).';
1986
+ }
package/src/program.js CHANGED
@@ -1439,8 +1439,13 @@ function analyzeInteropPortability(program, extraGoals = []) {
1439
1439
  // this only affects user code, but within user code it acts at a distance
1440
1440
  // across included files.
1441
1441
  //
1442
- // How strictly this is treated depends on how explicit the program was, which
1443
- // follows the graded scheme SWI-Prolog uses for the same situation:
1442
+ // How strictly this is treated depends on how explicit the program was. The
1443
+ // first two tiers match what Trealla and Scryer do for the same program
1444
+ // (`:- use_module(library(lists)).` followed by clauses for append/3): both
1445
+ // warn and continue, Trealla with `overwriting user:'append'/3` and Scryer with
1446
+ // `overwriting append/3 because the clauses are discontiguous`. SWI-Prolog
1447
+ // treats imported predicates as weak symbols and warns likewise. No system
1448
+ // raises an error there, so neither does EyeProlog:
1444
1449
  //
1445
1450
  // implicit autoload -> warning
1446
1451
  // use_module(library(L)) -> warning
@@ -1448,9 +1453,9 @@ function analyzeInteropPortability(program, extraGoals = []) {
1448
1453
  //
1449
1454
  // Only the last case is an outright contradiction: the program asked for that
1450
1455
  // exact predicate to be imported and then supplied clauses for it. The first
1451
- // two stay warnings so that flat, module-free programs -- which load unchanged
1452
- // on Scryer and Trealla, where the library is simply never imported -- keep
1453
- // working. Note that ISO reserves permission_error(modify, static_procedure)
1456
+ // two stay warnings so that flat, module-free programs keep loading here
1457
+ // exactly as they do on Scryer and Trealla, which was checked directly rather
1458
+ // than inferred. Note that ISO reserves permission_error(modify, static_procedure)
1454
1459
  // for built-in predicates (7.5.3); library predicates are not built-ins, so
1455
1460
  // this error is raised only where the program's own import list demands it.
1456
1461
  function analyzeLibraryShadowing(program) {
package/src/solver.js CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  numberTerm, numberTextFromDouble, properListItems, termIsGround, termToString, unify, variable, variantTerms,
7
7
  } from './term.js';
8
8
  import { numberValueKey, sameNumberValue } from './number-value.js';
9
+ import { attachBuiltinErrorContext } from './errors.js';
9
10
  import { PrologError, getStrictIsoRegistry } from './iso.js';
10
11
  import { getEyePrologRegistry } from './standard-library.js';
11
12
  import { selectClauseCandidates, selectClauseCandidatesForValues, selectGroundClauseCandidates } from './program-indexing.js';
@@ -757,8 +758,14 @@ export class Solver {
757
758
  if (builtinReady) {
758
759
  const deterministic = def.deterministic ||
759
760
  def.deterministicWhen?.({ solver: this, goal, env }) === true;
760
- const iterator = def.handler({ solver: this, goal, env });
761
- const firstResult = iterator.next();
761
+ let iterator;
762
+ let firstResult;
763
+ try {
764
+ iterator = def.handler({ solver: this, goal, env });
765
+ firstResult = iterator.next();
766
+ } catch (caught) {
767
+ throw attachBuiltinErrorContext(caught, def, goal);
768
+ }
762
769
  if (deterministic) {
763
770
  if (!firstResult.done) this.stats.deterministic_builtin_successes++;
764
771
  else this.stats.deterministic_builtin_failures++;
@@ -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` | The default context term is the atom `eyeprolog`. A few implementation-specific diagnostics may deliberately supply a more specific context term. | **defined** — `formalErrorTerm()` in `src/iso.js`. |
70
+ | 7.12.1 | Second argument of `error/2` | Built-in predicates report the predicate indicator of the predicate that raised the error, for example `error(type_error(atom, 1.0), atom_length/2)`. Errors raised outside a built-in frame, and errors thrown from shared pre-built instances that several built-ins reuse, fall back to the atom `eyeprolog`. A few implementation-specific diagnostics deliberately supply their own context term. | **defined** — `attachBuiltinErrorContext()` in `src/errors.js`, `formalErrorTerm()` in `src/iso.js`. |
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. |
@@ -23,7 +23,7 @@ chosen behavior is also indexed by `ISO-IMPLEMENTATION-DEFINED.md`.
23
23
  | 7.4.2.7 `include/1` | covered | The included text is prepared at the directive position and shares operator/character/flag preparation state with its parent. |
24
24
  | 7.4.2.8 `ensure_loaded/1` | covered | A source is prepared at most once in the current load graph, including repeated references and self/top-level references. |
25
25
  | 7.4.2.9 `set_prolog_flag/2` | covered | Preparation-time flag changes affect subsequent text and are replayed into execution state; strict flag names/values/changeability remain governed by the closed 7.11 review. |
26
- | 7.4.3 source clauses | covered | Source heads/bodies are validated like program clauses, standardized static/control procedures are protected, declarations can create empty procedures, and body conversion follows 7.6.2 while preserving head/body variable identity. The built-in restriction applies in every execution mode, so consulting a clause for a built-in predicate or control construct reports the same `permission_error(modify, static_procedure)` as `assert`ing one; EyeProlog's own library and extension predicates are not standard built-ins, so ISO does not protect them. They stay redefinable when they reach the program implicitly, through autoloading or a whole-module `use_module(library(L))`, but the redefinition is reported as a shadowing warning because it replaces the library predicate for all user code in the program. Naming a predicate in an explicit import list, `use_module(library(L), [Name/Arity])`, and then supplying clauses for it is a contradiction and reports `permission_error(modify, static_procedure)`. Bundled library modules keep resolving their own internal calls, so a user redefinition never changes library behaviour. Corpus: `error/iso/consult_redefines_builtin`. |
26
+ | 7.4.3 source clauses | covered | Source heads/bodies are validated like program clauses, standardized static/control procedures are protected, declarations can create empty procedures, and body conversion follows 7.6.2 while preserving head/body variable identity. The built-in restriction applies in every execution mode, so consulting a clause for a built-in predicate or control construct reports the same `permission_error(modify, static_procedure)` as `assert`ing one; EyeProlog's own library and extension predicates are not standard built-ins, so ISO does not protect them. They stay redefinable when they reach the program implicitly, through autoloading or a whole-module `use_module(library(L))`, but the redefinition is reported as a shadowing warning because it replaces the library predicate for all user code in the program. Naming a predicate in an explicit import list, `use_module(library(L), [Name/Arity])`, and then supplying clauses for it is a contradiction and reports `permission_error(modify, static_procedure)`. Bundled library modules keep resolving their own internal calls, so a user redefinition never changes library behaviour. Trealla and Scryer also warn and continue for the whole-module case rather than raising an error, so the warning tiers match those systems. Corpus: `error/iso/consult_redefines_builtin`. |
27
27
 
28
28
  The strict release test `closes ISO 7.4 Prolog-text preparation and directive
29
29
  rows` exercises the cross-text operator/character/flag state, include and
@@ -1,3 +1,3 @@
1
- answer(7, red, caught(eyeprolog), first, caught(eyeprolog)).
2
- answer(7, red, caught(eyeprolog), second, caught(eyeprolog)).
1
+ answer(7, red, caught(atom_length / 2), first, caught(eyeprolog)).
2
+ answer(7, red, caught(atom_length / 2), second, caught(eyeprolog)).
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)]).
@@ -1 +1 @@
1
- parse line 1: bad term
1
+ parse line 1: unknown directive q/1; to run a goal at load time use :- initialization(Goal).
@@ -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), eyeprolog)', 'REPL syntax error');
1894
+ assertIncludes(repl.stdout, 'error(syntax_error(read_term), 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]), eyeprolog).\n' +
2723
- '?- error(type_error(list, [1, [], _A | 2]), eyeprolog).\n' +
2722
+ '?- error(type_error(list, [1, [], _A | 2]), number_chars / 2).\n' +
2723
+ '?- error(type_error(list, [1, [], _A | 2]), 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, eyeprolog).\n' +
2738
- '?- Error = instantiation_error, Imp_def = eyeprolog.\n' +
2737
+ '?- error(instantiation_error, (is) / 2).\n' +
2738
+ '?- Error = instantiation_error, Imp_def = (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), eyeprolog).',
4047
+ 'error(domain_error(statistics_key, nonsense), statistics / 2).',
4048
4048
  'statistics key error');
4049
4049
  assertEqual(result.stderr, '', 'stderr');
4050
4050
  },
@@ -4427,6 +4427,42 @@ answer(Result) :- countdown(2048, Result), Result = 2048.
4427
4427
  assertIncludes(result.stdout, 'answer([1, 2, 3])', 'library append/2 still uses the library append/3');
4428
4428
  },
4429
4429
  },
4430
+ {
4431
+ // Directives come from a fixed list, so an unrecognized one is a
4432
+ // different mistake from unparseable input and gets its own message
4433
+ // instead of the generic parser 'bad term'.
4434
+ name: 'unsupported directives report why they were rejected',
4435
+ run: () => {
4436
+ const goalDirective = runCli(['-'], { input: ':- write(hello), nl.\n' });
4437
+ assertEqual(goalDirective.status, 1, 'goal directive status');
4438
+ assertIncludes(goalDirective.stderr, 'unknown directive write/1', 'goal directive indicator');
4439
+ assertIncludes(goalDirective.stderr, 'initialization(Goal)', 'goal directive remedy');
4440
+ assertNotIncludes(goalDirective.stderr, 'bad term', 'no generic parser message');
4441
+
4442
+ const misspelt = runCli(['-'], { input: ':- dynamc(foo/1).\n' });
4443
+ assertEqual(misspelt.status, 1, 'misspelt status');
4444
+ assertIncludes(misspelt.stderr, 'did you mean dynamic?', 'near-miss suggestion');
4445
+
4446
+ const wrongArity = runCli(['-'], { input: ':- dynamic(foo/1, bar).\n' });
4447
+ assertEqual(wrongArity.status, 1, 'wrong arity status');
4448
+ assertIncludes(wrongArity.stderr, 'expected dynamic/1', 'arity guidance');
4449
+
4450
+ const notCallable = runCli(['-'], { input: ':- 42.\n' });
4451
+ assertEqual(notCallable.status, 1, 'non-callable status');
4452
+ assertIncludes(notCallable.stderr, 'must be a callable term', 'callable guidance');
4453
+ },
4454
+ },
4455
+ {
4456
+ // Genuinely malformed input must keep reporting a syntax error rather
4457
+ // than being misreported as a directive problem.
4458
+ name: 'malformed terms still report a parse error',
4459
+ run: () => {
4460
+ const result = runCli(['-'], { input: 'foo(1). bar(\n' });
4461
+ assertEqual(result.status, 1, 'exit status');
4462
+ assertIncludes(result.stderr, 'parse line', 'parse error');
4463
+ assertNotIncludes(result.stderr, 'unknown directive', 'not a directive message');
4464
+ },
4465
+ },
4430
4466
  {
4431
4467
  name: 'double dash permits option-shaped file names',
4432
4468
  run: () => {
@@ -5046,7 +5082,7 @@ answer(Result) :- countdown(2048, Result), Result = 2048.
5046
5082
  goal: 'answer(T)',
5047
5083
  ioOptions: { input: invalidOctal },
5048
5084
  }).stdout,
5049
- 'answer(error(syntax_error(read_term), eyeprolog)).\n',
5085
+ 'answer(error(syntax_error(read_term), read / 1)).\n',
5050
5086
  'read/1 rejects non-octal numeric escape',
5051
5087
  );
5052
5088
  },
@@ -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),eyeprolog),true)',
652
+ 'catch(set_input(tmp_in),error(existence_error(stream,tmp_in),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,eyeprolog),true)",
860
- "catch(atom_length(1,N),error(type_error(atom,1),eyeprolog),true)",
861
- "catch(op(1300,xfx,foo),error(domain_error(operator_priority,1300),eyeprolog),true)",
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)",
862
862
  "catch(call(no_such_predicate),error(existence_error(procedure,no_such_predicate/0),eyeprolog),true)",
863
- "catch(abolish(atom/1),error(permission_error(modify,static_procedure,atom/1),eyeprolog),true)",
864
- "catch(char_code(C,1114112),error(representation_error(character_code),eyeprolog),true)",
865
- "catch(X is 1/0,error(evaluation_error(zero_divisor),eyeprolog),true)",
866
- "catch(X is 1<<4294967296,error(resource_error(memory),eyeprolog),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)",
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),eyeprolog),true)",
874
+ goal: "catch(read_term(T,[]),error(syntax_error(read_term),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,eyeprolog),true)', { isoStrict: true }),
881
+ parseGoalText('catch(flush_output(user_output),error(system_error,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