eyeprolog 1.3.55 → 1.3.56

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.3.55",
6
+ "version": "1.3.56",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/iso.js CHANGED
@@ -590,12 +590,22 @@ function requireClauseHead(head) {
590
590
  if (head.type !== ATOM && head.type !== COMPOUND) throw new PrologError('type_error(callable)', head);
591
591
  }
592
592
 
593
- function convertAssertedBody(term) {
593
+ function convertAssertedBody(term, culprit = term) {
594
594
  if (term.type === VAR) return compound('call', [term]);
595
- if (term.type === COMPOUND && term.name === ',' && term.arity === 2) {
596
- return compound(',', [convertAssertedBody(term.args[0]), convertAssertedBody(term.args[1])]);
595
+ // ISO 7.6.2 converts each argument of conjunction, disjunction, and if-then
596
+ // recursively when a term is converted to a clause body. This matters for
597
+ // variables (which become call(Var)) and for invalid nested terms, which
598
+ // must be rejected while assert[az]/1 performs the conversion rather than
599
+ // being stored and failing only if that branch is later executed.
600
+ if (term.type === COMPOUND && [',', ';', '->'].includes(term.name) && term.arity === 2) {
601
+ return compound(term.name, [
602
+ convertAssertedBody(term.args[0], culprit),
603
+ convertAssertedBody(term.args[1], culprit),
604
+ ]);
605
+ }
606
+ if (term.type !== ATOM && term.type !== COMPOUND) {
607
+ throw new PrologError('type_error(callable)', culprit);
597
608
  }
598
- if (term.type !== ATOM && term.type !== COMPOUND) throw new PrologError('type_error(callable)', term);
599
609
  return term;
600
610
  }
601
611
 
package/src/program.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Program representation and clause indexing.
2
2
  // Indexes are deliberately conservative: they speed up common scalar arguments but never replace unification as the final check.
3
- import { ATOM, COMPOUND, VAR, Env, atom, compound, deref, flattenConjunction, isScalar, numberTerm, properListItems, termToString, variable } from './term.js';
3
+ import { ATOM, COMPOUND, STRING, VAR, Env, atom, compound, deref, flattenConjunction, isScalar, numberTerm, properListItems, termToString, variable } from './term.js';
4
4
  import { formatTermForWrite } from './write.js';
5
5
  import {
6
6
  ISO_OPERATOR_DEFINITIONS,
@@ -33,6 +33,21 @@ import {
33
33
  const DEFER_PROGRAM_BUILD = Symbol('deferProgramBuild');
34
34
  const FAST_PARSE_ABORT = Symbol('fastParseAbort');
35
35
  const PROGRAM_BUILD_BATCH_SIZE = 16384;
36
+
37
+ function termContainsStringExtension(term, seen = new Set()) {
38
+ if (term == null || typeof term !== 'object' || seen.has(term)) return false;
39
+ seen.add(term);
40
+ if (term.type === STRING) return true;
41
+ if (term.type === COMPOUND) return term.args.some((arg) => termContainsStringExtension(arg, seen));
42
+ return false;
43
+ }
44
+
45
+ function clauseContainsStringExtension(clause) {
46
+ if (clause == null) return false;
47
+ if (clause.head0Type === STRING || clause.head1Type === STRING) return true;
48
+ if (termContainsStringExtension(clause.head) || termContainsStringExtension(clause.query)) return true;
49
+ return Array.isArray(clause.body) && clause.body.some((goal) => termContainsStringExtension(goal));
50
+ }
36
51
  export class Program {
37
52
  constructor(clauses = [], options = {}) {
38
53
  this.clauses = [];
@@ -574,6 +589,12 @@ class ProgramBuilder {
574
589
  let lastGroup = this.lastGroup;
575
590
 
576
591
  for (const clause of clauses) {
592
+ if (program.strictIso && clauseContainsStringExtension(clause)) {
593
+ // Programmatic clauses are part of the public embedding surface. The
594
+ // normal profile permits its host string term extension, but the Part 1
595
+ // strict profile must reject that implementation-specific sixth type.
596
+ throw new PrologError('representation_error(term)');
597
+ }
577
598
  if (clause?.kind === 'quad') {
578
599
  const module = clause.module ?? 'user';
579
600
  annotateGoalModule(clause.query, module);
package/src/solver.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // Depth-first EyeProlog solver with builtin dispatch, memoization, and guarded recursion handling.
2
2
  // Most semantic decisions still flow through unification; optimizations only select candidates earlier.
3
3
  import {
4
- ATOM, COMPOUND, NUMBER, VAR, Env, compactListLength, compactVariableList, compound, cons, copyResolved, deref, emptyList,
4
+ ATOM, COMPOUND, NUMBER, STRING, VAR, Env, compactListLength, compactVariableList, compound, cons, copyResolved, deref, emptyList,
5
5
  flattenConjunction, freshTerm, isCons, isDecimalInteger, isEmptyList,
6
6
  numberTerm, numberTextFromDouble, termIsGround, termToString, unify, variable, variantTerms,
7
7
  } from './term.js';
@@ -45,6 +45,26 @@ function raiseOccursCheckError(left, right, env) {
45
45
  throw error;
46
46
  }
47
47
 
48
+
49
+ function rejectStrictIsoStringTerms(terms, env) {
50
+ const seen = new Set();
51
+ const visit = (term) => {
52
+ const resolved = deref(term, env);
53
+ if (resolved == null || seen.has(resolved)) return;
54
+ seen.add(resolved);
55
+ if (resolved.type === STRING) {
56
+ // `stringTerm()` is a normal-profile host/API term extension. It has no
57
+ // Part 1 abstract/token syntax, so strict execution must reject it rather
58
+ // than silently introducing a sixth term type through the embedding API.
59
+ throw new PrologError('representation_error(term)');
60
+ }
61
+ if (resolved.type === COMPOUND) {
62
+ for (const arg of resolved.args) visit(arg);
63
+ }
64
+ };
65
+ for (const term of terms) visit(term);
66
+ }
67
+
48
68
  export class Solver {
49
69
  constructor(program, options = {}) {
50
70
  this.program = program;
@@ -301,6 +321,7 @@ export class Solver {
301
321
  *solve(goals, env = new Env(), depth = 0) {
302
322
  if (!Array.isArray(goals)) goals = [goals];
303
323
  env.setOccursCheckHandler(this.occursCheckHandler);
324
+ if (this.isoStrict) rejectStrictIsoStringTerms(goals, env);
304
325
 
305
326
  const writeVariableState = this.writeVariableState;
306
327
  if (writeVariableState.depth === 0) {
@@ -0,0 +1,205 @@
1
+ # ISO Part 1 built-in mode/error audit
2
+
3
+ This matrix turns the broad built-in audit in `ISO-COMPLIANCE.md` into smaller,
4
+ release-gated units. The normative baseline is ISO/IEC 13211-1:1995 plus
5
+ Technical Corrigenda 1:2007, 2:2012, and 3:2017.
6
+
7
+ A `covered` row has an executable assertion for the prescribed successful mode,
8
+ failure behavior, or error condition. `not applicable` records a conditional
9
+ standard branch that cannot occur under EyeProlog's documented processor
10
+ choices. The table is intentionally explicit about what has and has not been
11
+ closed: this tranche row-audits 8.2-8.5 and 8.15-8.17; 8.6-8.14 remain under
12
+ the broader audit and will be expanded in later passes.
13
+
14
+ The focused assertions added for this matrix live in `test/run-iso-strict.mjs`;
15
+ existing file-based cases remain additional independent evidence.
16
+
17
+ ## 8.2 — term unification
18
+
19
+ | Clause / predicate | Prescribed row | Status | Executable evidence |
20
+ | --- | --- | --- | --- |
21
+ | 8.2.1 `(=)/2` | `?term, ?term`; succeeds/fails according to Prolog unification; no prescribed errors | covered | existing unification corpus and `term_modes_and_ordering` |
22
+ | 8.2.2 `unify_with_occurs_check/2` | `?term, ?term`; succeeds/fails and never reports a prescribed error | covered | strict row-audit success assertion plus unification corpus |
23
+ | 8.2.3 `(\=)/2` | `@term, @term`; succeeds/fails for the defined NSTO cases; no prescribed errors | covered | unification/control corpus |
24
+ | Cor.2 8.2.4 `subsumes_term/2` | `@term, @term`; succeeds/fails without binding its arguments; no prescribed errors | covered | strict row-audit success assertion and Corrigendum 2 term-predicate cases |
25
+
26
+ ## 8.3 — type testing
27
+
28
+ All Part 1 type tests have a single input-term mode and no prescribed errors.
29
+ The Corrigendum 2 additions have the same shape.
30
+
31
+ | Predicate | Prescribed row | Status | Executable evidence |
32
+ | --- | --- | --- | --- |
33
+ | `var/1` | input term; semidet; no errors | covered | `type_var_success` and term-mode cases |
34
+ | `atom/1` | input term; semidet; no errors | covered | `type_atom_success` |
35
+ | `integer/1` | input term; semidet; no errors | covered | `type_integer_success` |
36
+ | `float/1` | input term; semidet; no errors | covered | `type_float_success` |
37
+ | `atomic/1` | input term; semidet; no errors | covered | `type_atomic_atom_success`, `type_atomic_number_success` |
38
+ | `compound/1` | input term; semidet; no errors | covered | `type_compound_success` |
39
+ | `nonvar/1` | input term; semidet; no errors | covered | `type_nonvar_success` |
40
+ | `number/1` | input term; semidet; no errors | covered | `type_number_integer_success`, `type_number_float_success` |
41
+ | Cor.2 `callable/1` | input term; semidet; no errors | covered | `type_callable_atom_success`, `type_callable_compound_success` |
42
+ | Cor.2 `ground/1` | input term; semidet; no errors | covered | `type_ground_success` |
43
+ | Cor.2 `acyclic_term/1` | input term; semidet; no errors | covered | Corrigendum 2 term-predicate coverage |
44
+
45
+ ## 8.4 — term comparison and sorting
46
+
47
+ | Clause / predicate | Prescribed row | Status | Executable evidence |
48
+ | --- | --- | --- | --- |
49
+ | 8.4.1 `(@<)/2` | two input terms; semidet; no prescribed errors | covered | standard-order corpus and strict type/collation ordering assertions |
50
+ | 8.4.1 `(@=<)/2` | two input terms; semidet; no prescribed errors | covered | standard-order corpus |
51
+ | 8.4.1 `(==)/2` | two input terms; identity test; no prescribed errors | covered | identity/term-mode corpus |
52
+ | 8.4.1 `(\==)/2` | two input terms; non-identity test; no prescribed errors | covered | identity/term-mode corpus |
53
+ | 8.4.1 `(@>)/2` | two input terms; semidet; no prescribed errors | covered | standard-order corpus |
54
+ | 8.4.1 `(@>=)/2` | two input terms; semidet; no prescribed errors | covered | standard-order corpus |
55
+ | Cor.2 8.4.2 `compare/3` | output-order mode | covered | strict `compare(<,a,b)` / comparison cases |
56
+ | Cor.2 8.4.2 `compare/3` | input-order mode | covered | comparison cases |
57
+ | Cor.2 8.4.2 error (a) | non-variable, non-atom `Order` -> atom type error | covered | strict `compare(1,3,3.0)` |
58
+ | Cor.2 8.4.2 error (b) | atom outside `<`, `=`, `>` -> order domain error | covered | strict `compare(>=,3,3.0)` |
59
+ | Cor.2 8.4.3 `sort/2` | input list -> output list | covered | strict `sort([1,1],[1])` and sorting corpus |
60
+ | Cor.2 8.4.3 `sort/2` | input list + input result list | covered | sorting corpus |
61
+ | Cor.2 8.4.3 error (a) | partial input list -> instantiation error | covered | strict `sort([1|T],_)` |
62
+ | Cor.2 8.4.3 error (b) | non-list input -> list type error | covered | strict `sort(foo,_)` |
63
+ | Cor.2 8.4.3 error (c) | non-list result -> list type error | covered | strict `sort([],foo)` |
64
+ | Cor.2 8.4.4 `keysort/2` | input pairs -> output list | covered | strict `keysort([2-b,1-a],[1-a,2-b])` and Corrigendum cases |
65
+ | Cor.2 8.4.4 `keysort/2` | input pairs + input result list | covered | Corrigendum cases |
66
+ | Cor.2 8.4.4 error (a) | partial `Pairs` -> instantiation error | covered | strict `keysort([1-a|T],_)` |
67
+ | Cor.2 8.4.4 error (b) | non-list `Pairs` -> list type error | covered | strict `keysort(foo,_)` |
68
+ | Cor.2 8.4.4 error (c) | non-list `Sorted` -> list type error | covered | strict `keysort([],foo)` |
69
+ | Cor.2 8.4.4 error (d) | variable element in `Pairs` prefix -> instantiation error | covered | strict `keysort([X],_)` |
70
+ | Cor.2 8.4.4 error (e) | non-pair element in `Pairs` prefix -> pair type error | covered | strict `keysort([foo],_)` |
71
+ | Cor.2 8.4.4 error (f) | non-pair element in `Sorted` prefix -> pair type error | covered | strict `keysort([],[foo])` |
72
+
73
+ ## 8.5 — term creation and decomposition
74
+
75
+ | Clause / predicate | Prescribed row | Status | Executable evidence |
76
+ | --- | --- | --- | --- |
77
+ | 8.5.1 `functor/3` | decompose a non-variable term | covered | term-construction corpus |
78
+ | 8.5.1 `functor/3` | construct from `Name` and `Arity` | covered | term-construction corpus |
79
+ | 8.5.1 errors (a-b) | missing required `Name` or `Arity` when constructing -> instantiation error | covered | strict `functor(X,Y,3)` and `functor(X,foo,N)` |
80
+ | 8.5.1 error (c) | non-atomic `Name` -> atomic type error | covered | strict `functor(X,foo(a),a)` precedence case |
81
+ | 8.5.1 error (d) | non-integer `Arity` -> integer type error | covered | strict `functor(X,foo,a)` |
82
+ | 8.5.1 error (e) | positive arity with non-atom atomic name -> atom type error | covered | strict `functor(X,1.5,1)` |
83
+ | 8.5.1 error (f) | requested arity above finite `max_arity` | not applicable | EyeProlog selects `max_arity=unbounded`; finite host exhaustion is a resource condition |
84
+ | 8.5.1 error (g) | negative `Arity` -> non-negative domain error | covered | strict `functor(X,foo,-1)` |
85
+ | 8.5.2 `arg/3` | positive integer index + compound term | covered | `logtalk_arg_unification` and strict cases |
86
+ | 8.5.2 error (a) | variable index -> instantiation error | covered | strict `arg(X,foo(a),_)` |
87
+ | 8.5.2 error (b) | variable term -> instantiation error | covered | strict `arg(1,X,_)` |
88
+ | 8.5.2 error (c) | non-integer index -> integer type error | covered | strict `arg(a,foo(a),_)` |
89
+ | 8.5.2 error (d) | non-compound term -> compound type error | covered | strict `arg(0,atom,_)` precedence case |
90
+ | 8.5.2 error (e) | negative index -> non-negative domain error | covered | strict `arg(-1,foo(a),_)` |
91
+ | 8.5.3 `(=..)/2` | term -> list decomposition | covered | `logtalk_univ` and term-construction cases |
92
+ | 8.5.3 `(=..)/2` | list -> term construction | covered | `logtalk_univ` and term-construction cases |
93
+ | 8.5.3 error (a) | both arguments insufficiently instantiated | covered | strict `X=..Y` |
94
+ | 8.5.3 error (b) | partial list in construction direction | covered | strict `X=..[foo|T]` |
95
+ | 8.5.3 error (c) | non-list second argument | covered | strict `X=..foo` |
96
+ | 8.5.3 error (d) | variable list head | covered | strict `X=..[F,a]` |
97
+ | 8.5.3 error (e) | invalid compound functor/list head | covered | strict `X=..[3,a]` and singleton compound-head case |
98
+ | 8.5.3 error (f) | empty construction list | covered | strict `X=..[]` |
99
+ | 8.5.3 finite-arity representation branch | constructed list exceeds finite `max_arity` | not applicable | selected `max_arity=unbounded` |
100
+ | 8.5.4 `copy_term/2` | copy with fresh variables | covered | strict success assertion, `copy_term_fresh_variables`, `logtalk_copy_term_semantics` |
101
+ | Cor.2 8.5.5 `term_variables/2` | output witness-variable list | covered | strict `term_variables(A+B+B,[A,B])`, Logtalk-derived cases |
102
+ | Cor.2 8.5.5 `term_variables/2` | supplied partial/list result | covered | Corrigendum cases |
103
+ | Cor.2 8.5.5 error (a) | second argument neither partial list nor list -> list type error | covered | strict `term_variables(t,[X|foo])` |
104
+
105
+ ## 8.15 — logic and control
106
+
107
+ | Clause / predicate | Prescribed row | Status | Executable evidence |
108
+ | --- | --- | --- | --- |
109
+ | 8.15.1 `(\+)/1` | callable goal; semidet | covered | negation corpus |
110
+ | 8.15.1 error (a) | variable goal -> instantiation error | covered | strict `\+(X)` |
111
+ | 8.15.1 error (b) | non-callable goal -> callable type error | covered | strict `\+(3)` |
112
+ | 8.15.2 `once/1` | callable goal; only first solution survives | covered | direct strict solver assertion, `once_commits_first`, `logtalk_once` |
113
+ | 8.15.2 error (a) | variable goal -> instantiation error | covered | strict `once(X)` |
114
+ | 8.15.2 error (b) | non-callable goal -> callable type error | covered | strict `once(3)` |
115
+ | 8.15.3 `repeat/0` | repeatedly succeeds; no prescribed errors | covered | `logtalk_repeat` |
116
+ | Cor.2 8.15.4 `call/2..8` | closure expansion through the standardized maximum arity | covered | direct strict closure assertion and `corrigenda_call_closure` |
117
+ | Cor.2 8.15.4 error (a) | variable closure -> instantiation error | covered | strict `call(X,a)` |
118
+ | Cor.2 8.15.4 error (b) | non-callable closure -> callable type error | covered | strict `call(3,a)` |
119
+ | Cor.2 8.15.4 finite-`max_arity` branch | resulting goal exceeds finite `max_arity` | not applicable | selected `max_arity=unbounded`; branch remains implementation-tested conditionally |
120
+ | Cor.2 8.15.4 implementation-dependent `call/N`, N>=9 | EyeProlog supplies no additional standardized closure arities | covered | strict `call(foo,a,b,c,d,e,f,g,h)` reaches procedure-existence handling rather than an extra call/N built-in |
121
+ | Cor.2 8.15.4 resulting-goal conversion error | expanded goal cannot be converted to a callable goal | covered | strict callability/error tests |
122
+ | Cor.2 8.15.5 `false/0` | always fails; no prescribed errors | covered | strict assertion and `false_builtin` |
123
+
124
+ ## 8.16 — atomic term processing
125
+
126
+ | Clause / predicate | Prescribed row | Status | Executable evidence |
127
+ | --- | --- | --- | --- |
128
+ | 8.16.1 `atom_length/2` | atom -> length / check supplied length | covered | `logtalk_atom_length`, strict row audit |
129
+ | 8.16.1 error: atom variable | required atom unavailable -> instantiation error | covered | strict `atom_length(X,4)` |
130
+ | 8.16.1 error: atom type | first argument non-atom -> atom type error | covered | strict `atom_length(1.2,4)` |
131
+ | 8.16.1 error: length type | non-integer length -> integer type error | covered | strict `atom_length(atom,'4')` |
132
+ | 8.16.1 error: length domain | negative length -> non-negative domain error | covered | strict `atom_length(atom,-1)` |
133
+ | 8.16.2 `atom_concat/3` | concatenate known pieces or enumerate splits of known whole | covered | direct strict enumeration assertion and `logtalk_atom_concat` |
134
+ | 8.16.2 errors: insufficient first/whole or second/whole instantiation | instantiation error | covered | strict `atom_concat(X,small,Y)` and `atom_concat(small,X,Y)` |
135
+ | 8.16.2 errors: atom types | non-atom in each fixed position -> atom type error | covered | three strict type assertions |
136
+ | 8.16.3 `sub_atom/5` | enumerate/check source slices | covered | direct strict six-slice assertion and `logtalk_sub_atom` |
137
+ | 8.16.3 error: source variable | instantiation error | covered | strict `sub_atom(X,0,1,0,a)` |
138
+ | 8.16.3 errors: atom types | source/result non-atom -> atom type error | covered | strict source/result assertions |
139
+ | 8.16.3 errors: integer types | fixed Before/Length/After non-integer -> integer type error | covered | three strict assertions |
140
+ | 8.16.3 errors: non-negative domains | fixed Before/Length/After negative -> domain error | covered | three strict assertions |
141
+ | 8.16.4 `atom_chars/2` | atom <-> list of characters | covered | forward/reverse and Logtalk-derived cases |
142
+ | Cor.2 `atom_chars/2`: both insufficiently instantiated | instantiation error | covered | strict `atom_chars(X,Y)` |
143
+ | Cor.2 `atom_chars/2`: atom type | atom side non-atom | covered | strict `atom_chars(1,[])` |
144
+ | Cor.2 `atom_chars/2`: improper list | list type error on complete list culprit | covered | strict `atom_chars(X,[a|foo])` and focused culprit regressions |
145
+ | Cor.2 `atom_chars/2`: variable list-prefix element | instantiation error | covered | strict `atom_chars(X,[Y,a])` |
146
+ | Cor.2 `atom_chars/2`: non-character element | character type error | covered | strict `atom_chars(X,[a,1])` |
147
+ | 8.16.5 `atom_codes/2` | atom <-> list of character codes | covered | forward/reverse and Logtalk-derived cases |
148
+ | Cor.2 `atom_codes/2`: both insufficiently instantiated | instantiation error | covered | strict `atom_codes(X,Y)` |
149
+ | Cor.2 `atom_codes/2`: atom type | atom side non-atom | covered | strict `atom_codes(1,[])` |
150
+ | Cor.2 `atom_codes/2`: improper list | list type error on complete list culprit | covered | strict `atom_codes(X,[97|foo])` |
151
+ | Cor.2 `atom_codes/2`: variable prefix element | instantiation error | covered | strict `atom_codes(X,[Y,97])` |
152
+ | Cor.2 `atom_codes/2`: code type | non-integer code -> integer type error | covered | strict `atom_codes(X,[97,foo])` |
153
+ | Cor.2 `atom_codes/2`: code representation | integer outside processor character-code set -> representation error | covered | strict `atom_codes(X,[97,-1])` |
154
+ | 8.16.6 `char_code/2` | character <-> character code | covered | forward/reverse cases |
155
+ | 8.16.6 under-instantiation | both sides variable -> instantiation error | covered | strict `char_code(X,Y)` |
156
+ | 8.16.6 character type | non-character first argument takes precedence | covered | strict `char_code(ab,foo)` |
157
+ | 8.16.6 code type | non-integer code -> integer type error | covered | strict `char_code(a,foo)` |
158
+ | 8.16.6 code representation | integer outside processor character-code set -> representation error | covered | strict `char_code(a,-1)` |
159
+ | 8.16.7 `number_chars/2` | number <-> character-list number syntax | covered | number conversion corpus |
160
+ | Cor.2 `number_chars/2`: both insufficiently instantiated | instantiation error | covered | strict `number_chars(X,Y)` |
161
+ | Cor.2 `number_chars/2`: number type | fixed non-number -> number type error | covered | strict `number_chars(foo,[])` |
162
+ | Cor.2 `number_chars/2`: improper list | list type error | covered | strict `number_chars(X,[a|foo])` |
163
+ | Cor.2 `number_chars/2`: variable prefix element | instantiation error | covered | strict `number_chars(X,[Y,a])` |
164
+ | Cor.2 `number_chars/2`: non-character element | character type error | covered | strict `number_chars(X,[a,1])` |
165
+ | `number_chars/2`: invalid number syntax | syntax error | covered | strict `number_chars(X,[a])` and parenthesized-number rejection |
166
+ | 8.16.8 `number_codes/2` | number <-> code-list number syntax | covered | number conversion corpus |
167
+ | Cor.2 `number_codes/2`: both insufficiently instantiated | instantiation error | covered | strict `number_codes(X,Y)` |
168
+ | Cor.2 `number_codes/2`: number type | fixed non-number -> number type error | covered | strict `number_codes(foo,[])` |
169
+ | Cor.2 `number_codes/2`: improper list | list type error | covered | strict `number_codes(X,[49|foo])` |
170
+ | Cor.2 `number_codes/2`: variable prefix element | instantiation error | covered | strict `number_codes(X,[Y,49])` |
171
+ | Cor.2 `number_codes/2`: code type | non-integer code -> integer type error | covered | strict `number_codes(X,[49,foo])` |
172
+ | Cor.2 `number_codes/2`: code representation | integer outside processor character-code set -> representation error | covered | strict `number_codes(X,[49,-1])` |
173
+ | `number_codes/2`: invalid number syntax | syntax error | covered | strict `number_codes(X,[97])` |
174
+
175
+ ## 8.17 — flags and hooks
176
+
177
+ The complete Part 1 flag family had already been closed before this tranche;
178
+ these rows make the corresponding built-in modes/errors explicit here so the
179
+ built-in matrix does not have a gap at 8.17.
180
+
181
+ | Clause / predicate | Prescribed row | Status | Executable evidence |
182
+ | --- | --- | --- | --- |
183
+ | 8.17.1 `set_prolog_flag/2` | set a supported, changeable flag value | covered | strict flag audit |
184
+ | 8.17.1 errors | variable/name type/domain, value type/domain, and non-changeable-flag permission distinctions | covered | `covers the Part 1 flag defaults, value domains, and changeability` plus file-based flag errors |
185
+ | 8.17.2 `current_prolog_flag/2` | enumerate/query current standard flags | covered | strict complete flag audit |
186
+ | 8.17.2 flag-name type error | fixed non-atom name -> atom type error | covered | strict `current_prolog_flag(1,_)` |
187
+ | 8.17.2 unknown-flag domain error | fixed unsupported flag name -> Prolog-flag domain error | covered | strict `current_prolog_flag(no_such_iso_flag,_)` |
188
+ | 8.17.3 `halt/0` | terminate with implementation-defined successful host status | covered | `halt` conformance case / host runner tests |
189
+ | 8.17.4 `halt/1` | integer host status | covered | halt conformance/runner tests |
190
+ | 8.17.4 variable status | instantiation error | covered | strict `halt(X)` |
191
+ | 8.17.4 non-integer status | integer type error | covered | strict `halt(a)` |
192
+
193
+ ## Remaining row-audit work
194
+
195
+ This file deliberately does not relabel the whole 8.2-8.17 family as complete.
196
+ The next row-by-row passes are:
197
+
198
+ - 8.6-8.7 arithmetic predicates and expression-evaluation interactions;
199
+ - 8.8-8.10 database and all-solutions predicates, including all conditional
200
+ static/private/dynamic branches;
201
+ - 8.11-8.14 streams, character/byte I/O, options, and term I/O, including the
202
+ remaining option/mode cross-product.
203
+
204
+ Clause 9 exceptional-value rows and Clause 6/7 processor semantics remain
205
+ tracked separately in `ISO-COMPLIANCE.md` and `ISO-PROCESSOR-REQUIREMENTS.md`.
@@ -4,8 +4,11 @@ This ledger is the release-facing audit for EyeProlog's ISO/IEC 13211-1 core.
4
4
  The normative baseline is ISO/IEC 13211-1:1995 together with Technical
5
5
  Corrigenda 1:2007, 2:2012, and 3:2017. It complements
6
6
  [ISO-MATRIX.md](ISO-MATRIX.md), which maps standard families to representative
7
- executable tests, and the generated [`conformance-report.md`](../../conformance-report.md),
8
- which gives current case totals.
7
+ executable tests, [ISO-BUILTIN-MODE-ERROR-MATRIX.md](ISO-BUILTIN-MODE-ERROR-MATRIX.md),
8
+ which records the row-by-row built-in audit, and
9
+ [ISO-PROCESSOR-REQUIREMENTS.md](ISO-PROCESSOR-REQUIREMENTS.md), which decomposes
10
+ Clause 5 processor obligations. The generated [`conformance-report.md`](../../conformance-report.md)
11
+ gives current case totals.
9
12
 
10
13
  The ledger deliberately does **not** claim independent certification. A row
11
14
  marked `covered` means EyeProlog has implementation and executable tests for
@@ -22,25 +25,25 @@ error-ordering alternative to an individual executable assertion.
22
25
  | 5.1(b) execute conforming Prolog goals | audit | Clause 7-9 conformance corpus plus regression/API/example gates. A normative goal-semantics ledger is still being expanded. |
23
26
  | 5.1(c) reject nonconforming text/read-terms | audit | Dedicated syntax-error cases and strict-core extension rejection. Exhaustive lexical rejection coverage remains open. |
24
27
  | 5.1(d) document permitted variations | covered | The clause-by-clause [ISO 5.4 decision index](ISO-IMPLEMENTATION-DEFINED.md) records every explicit implementation-defined decision found in the Part 1 + Corrigenda baseline and separately inventories implementation-specific extension families. Rows marked `audit gap` remain conformance work, but the variation is no longer undocumented. |
25
- | 5.1(e) offer a strictly conforming mode | covered | `--iso-strict` and API option `isoStrict: true` restrict the processor to the Part 1 + Corrigenda 1-3 core language surface, remove EyeProlog-only registry/flag/operator features, and disable automatic tabling/recursion guards. |
28
+ | 5.1(e) offer a strictly conforming mode | covered | `--iso-strict` and API option `isoStrict: true` restrict the processor to the Part 1 + Corrigenda 1-3 core language surface, remove EyeProlog-only registry/flag/operator features, disable automatic tabling/recursion guards, and reject the normal-profile host `stringTerm/1` term type at strict program/goal entry. |
26
29
  | 5.4 accompanying documentation | covered | *The Art of EyeProlog* remains the implementation reference; [ISO-IMPLEMENTATION-DEFINED.md](ISO-IMPLEMENTATION-DEFINED.md) is the closed clause-by-clause 5.4 decision index and points each decision to implementation evidence or an explicit audit gap. |
27
- | 5.5 extensions preserve standard text | covered | Default mode retains EyeProlog extensions; strict core mode removes their language/runtime interpretation. Regression tests ensure the default profile remains unchanged. |
30
+ | 5.5 extension boundaries | audit | [ISO-PROCESSOR-REQUIREMENTS.md](ISO-PROCESSOR-REQUIREMENTS.md) now splits 5.5 into its individual extension hooks. Operators, character conversion, the normal-profile host string type, directives, control constructs, flags, and reserved atoms have explicit decisions; exhaustive syntax-preservation, side-effect, built-in, and evaluable-functor dependency mapping remains open. |
28
31
 
29
32
  ## Normative language families
30
33
 
31
34
  | Standard area | Status | Current evidence |
32
35
  | --- | --- | --- |
33
36
  | Clause 6 — tokens, terms, lists, operators, quoted text | audit | Complete vendored WG17 syntax matrix, `lexical_and_curly_terms`, `scryer_lexical_terms`, operator suites, syntax-error cases, quoted-layout/escape error cases, writer/read-back regressions, and Unicode-scalar PCS/collation boundary tests. The implementation-defined 6.5/6.6 character-model decisions are now closed; wider shall-by-shall lexical mapping remains open. |
34
- | 7.1-7.3 — term types, term order, unification | audit | Standard-order, identity, finite-tree and occurs-check suites, Corrigendum 2 term predicates, plus strict checks for the required `variable < float < integer < atom < compound` type order, PCS-based atom collation, and the `max_arity=unbounded` compound-term model. |
37
+ | 7.1-7.3 — term types, term order, unification | audit | Standard-order, identity, finite-tree and occurs-check suites, Corrigendum 2 term predicates, plus strict checks for the required `variable < float < integer < atom < compound` type order, PCS-based atom collation, and the `max_arity=unbounded` compound-term model. The normal JavaScript API string term is now explicitly documented as a 5.5.4 extension and rejected at strict program/goal entry so it cannot add a sixth strict-core type. |
35
38
  | 7.4 — Prolog text and directives | audit | All Part 1 directive indicators are parsed; include/ensure-loaded/operator/flag/character-conversion behavior has executable coverage. Preparation-time `char_conversion/2` affects later unquoted source text and respects `char_conversion=off`. Strict preparation now enforces declaration-before-clause ordering for `dynamic/1`, `multifile/1`, and `discontiguous/1`, cross-text `multifile/1`, discontiguous clause grouping, empty declared procedures, include textual-replacement behavior, and one-time initialization per prepared program. The remaining shall-by-shall mapping is still open. |
36
- | 7.5-7.6 — database and term/clause conversion | audit | Dynamic database and logical-update-view suites. Strict mode restores Part 1 private-static/public-dynamic `clause/2` access; focused strict checks now cover `current_predicate/1`, `clause/2`, `asserta/1`, `assertz/1`, `retract/1`, Corrigendum 2 `retractall/1`, and `abolish/1` errors plus empty-procedure lifetime. Runtime protection includes the solver-native conjunction control construct `','/2` and, following STC #56's accepted-direction action item, the source syntax functors `(:-)/1-2` for database modification/private access while leaving their call behavior distinct. The remaining clause-conversion shall-by-shall mapping is still open. |
39
+ | 7.5-7.6 — database and term/clause conversion | audit | Dynamic database and logical-update-view suites. Strict mode restores Part 1 private-static/public-dynamic `clause/2` access; focused strict checks cover the database built-ins and empty-procedure lifetime. Runtime assertion conversion now applies 7.6.2 recursively through conjunction, disjunction, and if-then: nested variables become `call/1`, and an invalid nested branch is rejected while `asserta/1` or `assertz/1` converts the term instead of being stored for a later execution-time failure. Runtime protection includes the solver-native conjunction control construct `','/2` and, following STC #56's accepted-direction action item, `(:-)/1-2` for database modification/private access while leaving their call behavior distinct. The remaining clause-conversion shall-by-shall mapping is still open. |
37
40
  | 7.7 — execution and backtracking | audit | Control/search suites. Strict mode disables EyeProlog automatic tabling, cycle guards, and recursive numeric shortcuts so core execution uses ordinary clause selection/backtracking. |
38
- | 7.8 — control constructs and exceptions | audit | call, cut, conjunction, disjunction (including failed branches after callee-local cuts), if-then, catch/throw, renamed-copy tests. |
41
+ | 7.8 — control constructs and exceptions | audit | call, cut, conjunction, disjunction (including failed branches after callee-local cuts), if-then, catch/throw, renamed-copy tests. The 8.15 built-in slice (`\+/1`, `once/1`, `repeat/0`, Corrigendum 2 `call/2..8`, `false/0`) is now row-audited in [ISO-BUILTIN-MODE-ERROR-MATRIX.md](ISO-BUILTIN-MODE-ERROR-MATRIX.md); the remaining general control-construct semantics ledger stays open. |
39
42
  | 7.9 — expression evaluation | audit | Arithmetic/evaluation/error suites, including Corrigenda. Strict mode now pins direct-variable precedence, 7.9.2 non-evaluable `F/N` errors (including STC #69), arithmetic numeric/integer type errors, float-only rounding conversions, the Part 1 mixed integer/float comparison conversion rule, I->F overflow before floating evaluable functors (STC #42), explicit `exp/1`/power underflow, and prescribed negative/zero power errors before later conversion overflow. Unbounded integer powers/shifts no longer leak host `RangeError`; finite-host exhaustion is normalized to `resource_error(memory)` in line with the Part 1 resource-error note and STC #21. The remaining exceptional-value/error-precedence rows are still being exhaustively enumerated. |
40
43
  | 7.10 — input/output concepts | audit | Stream, character/byte I/O, read/write options, operator-sensitive write-back, and Corrigendum 3 writer cases. The audit now also covers write-mode creation/truncation, append creation, repositioned overwrite, flushing, EOF actions, close/current-stream handling, strict stream-position terms, stream-property validation, stream-term requirements, text-vs-binary permission errors, and the prescribed `alias(A)` culprit for `open/4` alias collisions. The latest 8.11/8.12 pass also makes `close/2`'s `force(true)` semantics presence-based and defers `get_code/1-2` / `peek_code/1-2` invalid in-character-code representation errors until after the earlier stream existence/mode/type/EOF/entity conditions. The full option/mode cross-product is still being mapped. |
41
44
  | 7.11 — flags | covered | The complete Part 1 flag set, selected defaults, standard value domains, changeability, `current_prolog_flag/2`, and `set_prolog_flag/2` error behavior have dedicated strict tests. EyeProlog selects `bounded=false` and `integer_rounding_function=toward_zero`; valid alternative values of those fixed flags reach `permission_error(modify,flag,...)`, while `max_integer` and `min_integer` have no current value. STC #70 is recorded explicitly: EyeProlog has no separate finite procedure-arity ceiling, so the optional `max_procedure_arity` flag is absent while `max_arity` remains `unbounded`. Strict mode excludes the EyeProlog `occurs_check` extension. |
42
- | 7.12 — errors | audit | ISO `error(Error, Context)` envelope, type/domain/permission/representation/evaluation/syntax/resource families and focused error cases. Exact ISO 8.14.1-8.14.4 error precedence is covered for `read_term/3`, `write_term/3`, `op/3`, and `current_op/3`; the continuing audit has corrected additional 8.11-8.13 stream/character/byte errors (including the complete `alias(A)` culprit for `open/4` alias collisions), Corrigendum 2 `keysort/2` errors, atomic-conversion list-shape precedence, `arg/3`, `atom_concat/3`, `sub_atom/5`, `char_conversion/2`, runtime control-construct/database protection, arithmetic culprit/precedence reporting, input-code representation ordering, presence-based close forcing, and host-resource normalization for unbounded term/integer operations. A complete one-row-per-prescribed-error ordering map remains open. |
43
- | 8.2-8.17 — built-in predicates | audit | Predicate-family coverage is mapped in ISO-MATRIX.md; Corrigendum 2 additions (`subsumes_term/2`, `term_variables/2`, `call/2..8`, `false/0`) are in the strict registry. The audit now includes corrected `keysort/2` variable/non-pair errors, stricter 8.11-8.13 stream and character/byte modes/errors, the 8.14 option/error-order subfamily, closed 8.17 flags, additional prescribed precedence for `arg/3`, `atom_concat/3`, `sub_atom/5`, number/atom list conversions, and `char_conversion/2`, plus finite-host resource handling for unbounded `functor/3` construction and the 8.11/8.12 close/input-code overlap cases. The remaining mode/error matrix is not yet one-row-per-standard-row. |
45
+ | 7.12 — errors | audit | ISO `error(Error, Context)` envelope, type/domain/permission/representation/evaluation/syntax/resource families and focused error cases. [ISO-BUILTIN-MODE-ERROR-MATRIX.md](ISO-BUILTIN-MODE-ERROR-MATRIX.md) now gives explicit prescribed mode/error rows for 8.2-8.5 and 8.15-8.17, including Corrigendum additions and selected not-applicable finite-`max_arity` branches. Exact ISO 8.14.1-8.14.4 precedence and the recent 8.11/8.12 overlaps are also covered. The one-row map remains open for 8.6-8.14 and for the remaining Clause 7/9 exceptional-order interactions. |
46
+ | 8.2-8.17 — built-in predicates | audit | Predicate-family coverage is mapped in ISO-MATRIX.md. The new [ISO-BUILTIN-MODE-ERROR-MATRIX.md](ISO-BUILTIN-MODE-ERROR-MATRIX.md) closes 8.2-8.5 and 8.15-8.17 at one row per prescribed mode/error condition (with finite-`max_arity` branches explicitly marked not applicable under EyeProlog's `unbounded` choice). The still-open row audit is therefore narrowed to 8.6-8.14, especially database/all-solutions and the remaining stream/term-I/O option cross-product. |
44
47
  | Clause 9 — evaluable functors | audit | Integer/float/rounding/transcendental/bitwise suites and corrigendum cases. Strict mode excludes the EyeProlog-only evaluable atom `e`, retains Corrigendum 2 arithmetic additions, reports unknown zero-arity evaluables with the required `F/0` culprit shape, distinguishes non-evaluable `F/N` errors from numeric/integer operand errors per 7.9.2 and STC #69, and enforces float-only rounding modes, performs I->F conversion before floating functors (including overflow), reports the explicit `exp/1`, `**/2`, and Corrigendum 2 `^/2` underflow conditions, preserves the power-specific undefined conditions ahead of conversion overflow, and uses the Part 1 integer-to-float rule for mixed arithmetic comparisons. Normal mode retains EyeProlog's exact mixed-type comparison and round-to-zero arithmetic behavior as extensions. Unbounded BigInt resource exhaustion is translated into the Prolog error model rather than leaking host exceptions. Host floating-point representation choices remain documented implementation-defined behavior; the remaining exceptional-value rows are still under audit. |
45
48
  | Corrigendum 1 | covered | Double-quoted atom/operator-priority corrections have dedicated cases. |
46
49
  | Corrigendum 2 | covered | Added predicates/functors, catch corrections, bar/operator and uninstantiation corrections have dedicated cases. |
@@ -162,6 +165,28 @@ describes: a later `force(false)` does not cancel an earlier `force(true)` when
162
165
  a Resource Error or System Error occurs during closure. Focused strict tests
163
166
  cover both overlap families.
164
167
 
168
+ The next row-audit tranche makes the remaining exit criterion concrete rather
169
+ than leaving it as a broad test-count claim.
170
+ [ISO-BUILTIN-MODE-ERROR-MATRIX.md](ISO-BUILTIN-MODE-ERROR-MATRIX.md) now records
171
+ each prescribed mode/error row for the 8.2-8.5 and 8.15-8.17 slices, and
172
+ [ISO-PROCESSOR-REQUIREMENTS.md](ISO-PROCESSOR-REQUIREMENTS.md) decomposes the
173
+ Clause 5 obligations into covered, audit, and not-applicable decisions. While
174
+ doing that mapping, the 7.6.2 term-to-clause conversion audit exposed a runtime
175
+ assertion defect: variables and invalid terms nested under `;/2` or `->/2` were
176
+ not being recursively converted. `asserta/1` and `assertz/1` now convert all
177
+ three standardized binary control forms `,/2`, `;/2`, and `->/2` recursively,
178
+ so nested variables become `call/1` and a non-callable nested branch is rejected
179
+ at assertion time with the complete clause-body culprit.
180
+
181
+ The Clause 5 decomposition also exposed an embedding-only type boundary that
182
+ source-text tests could not see. EyeProlog's normal JavaScript API deliberately
183
+ exports `stringTerm(Text)` as an implementation-specific additional term type.
184
+ Its normal-profile ordering, conversion, evaluation, and writing behavior is
185
+ now documented under 5.5.4, while strict `Program` construction and strict
186
+ `Solver` goal entry reject such terms with `representation_error(term)`. This
187
+ keeps the host extension available in normal mode without allowing it to become
188
+ a sixth term type in the Part 1 strict execution domain.
189
+
165
190
  ## Post-Corrigendum STC cross-check
166
191
 
167
192
  The public WG17 STC draft is tracked as useful defect-discovery material, but it
@@ -197,7 +222,8 @@ not interpret the following as core-language features:
197
222
  - Part 2 module directives (`module/2`, `use_module/1-2`, `meta_predicate/1`);
198
223
  - Part 3 grammar-rule expansion and `phrase/2-3`;
199
224
  - automatic tabling, cycle guards, and recursive numeric execution shortcuts;
200
- - EyeProlog well-founded negation via `tnot/1` and its WFS runtime statistics.
225
+ - EyeProlog well-founded negation via `tnot/1` and its WFS runtime statistics;
226
+ - the JavaScript API `stringTerm(Text)` additional term type (normal mode only).
201
227
 
202
228
  The predefined Part 1 `1200 fx` `?-` operator and `1200 xfx` `-->` operator
203
229
  remain ordinary operator syntax in strict core mode. A conforming `op/3`
@@ -113,7 +113,7 @@ families; `--iso-strict` is intended to remove their Part 1 interpretation.
113
113
  | 5.5.1 Syntax | Part 2 modules, Part 3 grammar-rule expansion, and embedded quad syntax | Module directives are rejected; grammar rules remain ordinary `-->/2` terms rather than being expanded; quad syntax is rejected. Unicode PCS membership/classification is implementation defined and therefore shared with normal mode rather than treated as an extension. |
114
114
  | 5.5.2 Predefined operators | Part 3 `|` and EyeProlog's labelable infix `(?-)/2`; CLP(Z) operators when that library is imported | Only the Part 1 operator table is predefined; a conforming `op/3` may still add permitted operators. |
115
115
  | 5.5.3 Character-conversion mapping | No non-identity initial `Convc` extension | Identity initial mapping. |
116
- | 5.5.4 Types | No additional runtime Prolog term type is exposed by the core solver | Only variable, integer, float, atom, and compound term ordering participates in strict mode. |
116
+ | 5.5.4 Types | The normal JavaScript API exposes an implementation-specific string term `stringTerm(Text)`. It is disjoint from the five Part 1 term types; normal term order places it after atoms and before compound terms, and normal `atomic/1` treats it as atomic. It has no Prolog source token syntax (double-quoted source still follows `double_quotes`), is non-callable for term-to-clause conversion, is not evaluable as an arithmetic expression, and writes as a double-quoted host string. | Strict mode rejects a programmatic string term at program/goal entry with `representation_error(term)`, so the additional type cannot enter the Part 1 execution domain. **defined** — `src/term.js`, `src/program.js`, `src/solver.js`; strict API-boundary regression. |
117
117
  | 5.5.5 Directives | `module/2`, `use_module/1-2`, `meta_predicate/1` and normal-profile library behavior | Rejected as implementation-specific Part 1 directives. |
118
118
  | 5.5.6 Side effects | Host output/statistics, library-specific state, proof/statistics collection | The strict registry excludes library adapters; ordinary Part 1 I/O/database/flag/operator side effects remain. |
119
119
  | 5.5.7 Control constructs | `tnot/1` and normal-profile execution optimizations | `tnot/1` is absent; automatic tabling/recursion guards are disabled. |
@@ -11,13 +11,13 @@ compliance audit and the remaining work before a full conformance claim.
11
11
  | Clause 6 lexical and term syntax | tokenizer, operator parser, lists, curly terms, quotes, numeric syntax, comments, Unicode-scalar PCS/collation | `scryer_lexical_terms`, `lexical_and_curly_terms`, `double_quoted_lists`, `corrigendum1_double_quote_operator`, `wg17_syntax_high_risk`, `wg17_invalid_octal_escape`, `wg17_unterminated_quoted_token`, `wg17_literal_newline_in_quote`, `wg17_non_iso_escape`, strict PCS/collation tests in `run-iso-strict.mjs`, syntax error cases |
12
12
  | Clause 7 term order and unification | finite-tree unification, identity, standard order, errors | `unification_control_information`, `swipl_occurs_check`, `term_modes_and_ordering`, `logtalk_compare_standard_order` |
13
13
  | Clause 7 control and exceptions | call, cut, conjunction, disjunction, if-then-else, catch and throw | `cut_control`, `control_and_terms`, `exceptions_and_flags`, `corrigenda_catch_callability`, `throw_copies_ball` |
14
- | 8.2-8.5 term predicates | unification, Corrigendum 2 tests, comparison, sorting, creation and decomposition | `corrigenda_term_predicates`, `corrigenda_sort_keysort`, `logtalk_arg_unification`, `logtalk_univ`, associated error cases, Corrigendum 2 `keysort/2` variable/non-pair errors, and strict `arg/3`, `functor/3` / `=../2` error-order plus unbounded-arity/resource checks |
14
+ | 8.2-8.5 term predicates | unification, Corrigendum 2 tests, comparison, sorting, creation and decomposition | `corrigenda_term_predicates`, `corrigenda_sort_keysort`, `logtalk_arg_unification`, `logtalk_univ`, associated error cases, and the one-row-per-mode/error 8.2-8.5 slice in `ISO-BUILTIN-MODE-ERROR-MATRIX.md`, including conditional finite-`max_arity` branches |
15
15
  | 8.6-8.7 arithmetic predicates | `is/2` and all six arithmetic comparisons | `arithmetic`, comparison cases, isolated evaluation errors |
16
- | 8.8-8.10 database and solutions | logical update view, dynamic mutation, all-solutions grouping | `dynamic_database`, `trealla_logical_update_view`, `corrigenda_retractall`, `grouped_solutions_and_clauses`, plus strict clause/database error and all-solutions precedence checks, including runtime static/private protection for the solver-native conjunction control construct and STC #56 `(:-)/1-2` database protection |
16
+ | 8.8-8.10 database and solutions | logical update view, dynamic mutation, all-solutions grouping | `dynamic_database`, `trealla_logical_update_view`, `corrigenda_retractall`, `grouped_solutions_and_clauses`, plus strict clause/database error and all-solutions precedence checks, recursive 7.6.2 assertion-body conversion through `,/2`, `;/2`, and `->/2`, runtime static/private protection for conjunction, and STC #56 `(:-)/1-2` database protection |
17
17
  | 8.11-8.14 streams and term I/O | text/binary streams, properties, units, read/write options and operators | `streams_and_term_io`, `operators`, Corrigendum 3 option cases, stream error cases, strict 8.14.1-8.14.4 option/error-order tests, Corrigendum 3 variable metadata/write naming checks, and issue #65 checks for stream-term/property validation, text-vs-binary permissions, EOF/flush/position, open/close semantics, presence-based `force(true)` close handling, late `get_code/peek_code` in-character-code representation errors, and the exact `alias(A)` collision culprit |
18
- | 8.15 logic and control | negation, once, repeat, `call/2` through `call/8`, `false/0` | `logtalk_once`, `corrigenda_call_closure`, `false_builtin`, plus strict Corrigendum 2 resulting-goal `max_arity` error coverage |
19
- | 8.16 atomic processing | atoms, characters, codes and number conversion with prescribed errors | `atomic_term_processing`, focused forward/reverse cases, parenthesized-number rejection, Logtalk-derived error cases, strict Corrigendum 2 partial/improper-list precedence for atom/number conversions, and prescribed `atom_concat/3` / `sub_atom/5` ordering checks |
20
- | 8.17 flags and hooks | complete required Part 1 flag set, selected defaults, standard value domains/changeability, halt and character conversion | `exceptions_and_flags`, `remaining_builtins_and_directives`, flag error cases, strict `char_conversion/2` precedence, and the complete strict flag audit in `run-iso-strict.mjs` |
18
+ | 8.15 logic and control | negation, once, repeat, `call/2` through `call/8`, `false/0` | `logtalk_once`, `corrigenda_call_closure`, `false_builtin`, plus the complete 8.15 row-audit in `ISO-BUILTIN-MODE-ERROR-MATRIX.md` |
19
+ | 8.16 atomic processing | atoms, characters, codes and number conversion with prescribed errors | `atomic_term_processing`, focused forward/reverse cases, parenthesized-number rejection, Logtalk-derived cases, and the complete 8.16 mode/error slice in `ISO-BUILTIN-MODE-ERROR-MATRIX.md` |
20
+ | 8.17 flags and halt | complete required Part 1 flag set, selected defaults, standard value domains/changeability, and halt status handling | `exceptions_and_flags`, `remaining_builtins_and_directives`, the complete strict flag audit, halt cases, and explicit 8.17 rows in `ISO-BUILTIN-MODE-ERROR-MATRIX.md` |
21
21
  | Clause 9 evaluable functors | integer, float, rounding, transcendental and bitwise operations | `arithmetic`, `corrigenda_arithmetic`, `corrigenda_atan2_zero`, `corrigenda_integer_negative_power`, strict exclusion of the normal-mode `e` evaluable extension, zero-arity unknown-evaluable checks, prescribed operand/type and power-error precedence, float-only rounding modes, I->F overflow before floating functors, explicit `exp/1`/`**/2`/`^/2` underflow, Part 1 mixed integer/float comparison conversion, and resource-error normalization for finite-host exhaustion of unbounded integer operations |
22
22
  | Module compatibility profile (related to ISO/IEC 13211-2 and later WG17 amendment work) | module declarations, exports, imports, qualification, meta-predicate context | `modules/qualified_call`, `modules/selective_library_import`, `dcg_module_nonterminal_indicator` |
23
23
  | Part 3-oriented DCG compatibility profile | `-->`, terminal and partial sequences, grammar control constructs, semicontexts, nonterminal indicators, modules, `phrase/2-3`, steadfastness and errors | `dcg_terminals_and_remainder`, `dcg_control_constructs`, `dcg_partial_sequences`, `dcg_phrase_steadfastness`, `dcg_dynamic_nonterminal_indicator`, `logtalk_dcg_phrase_identity`, `logtalk_dcg_semicontexts`, DCG error and precedence cases |
@@ -0,0 +1,55 @@
1
+ # ISO Part 1 processor-requirements audit
2
+
3
+ This is the clause-level checklist for the processor requirements that sit
4
+ above the individual built-ins. It complements `ISO-COMPLIANCE.md`: the latter
5
+ is release-facing prose, while this file keeps each Clause 5 requirement or
6
+ extension boundary visible as its own auditable row.
7
+
8
+ The normative baseline is ISO/IEC 13211-1:1995 plus Technical Corrigenda 1-3.
9
+ `covered` means the requirement has an implementation/documentation decision
10
+ and executable evidence. `audit` means the implementation has relevant tests
11
+ but the project has not yet reduced every dependent Clause 6-9 requirement to
12
+ an explicit pass/not-applicable decision. No row here is an independent
13
+ certification claim.
14
+
15
+ ## 5.1 — conforming processor obligations
16
+
17
+ | Requirement | Status | Current evidence / remaining work |
18
+ | --- | --- | --- |
19
+ | 5.1(a) prepare conforming Prolog text | audit | parser/preparation tests, directives, include behavior, preparation-time operators/flags/character conversion, and the complete vendored WG17 syntax matrix; the remaining Clause 6/7.4 shall-by-shall map is still open |
20
+ | 5.1(b) execute conforming Prolog goals | audit | Clause 7-9 suites plus the row-audited built-in slices in `ISO-BUILTIN-MODE-ERROR-MATRIX.md`; 8.6-8.14 and remaining Clause 7/9 semantic rows are still being enumerated |
21
+ | 5.1(c) reject nonconforming text/read terms | audit | syntax-error corpus, WG17 negative syntax cases, strict extension rejection; exhaustive Clause 6 rejection mapping remains open |
22
+ | 5.1(d) specify permitted variations | covered | `ISO-IMPLEMENTATION-DEFINED.md` records the Part 1 implementation-defined choices and implementation-specific extension families |
23
+ | 5.1(e) offer a strictly conforming mode | covered | CLI `--iso-strict` and API `isoStrict: true`; registry/directive/operator/flag extension filtering plus disabled implementation-specific execution shortcuts |
24
+
25
+ ## 5.2-5.4 — text, goals, and documentation
26
+
27
+ | Requirement | Status | Current evidence / remaining work |
28
+ | --- | --- | --- |
29
+ | 5.2 conforming and strictly conforming Prolog text boundary | audit | strict parser accepts Part 1 + Corrigenda syntax plus implementation-defined PCS choices and rejects implementation-specific language facilities; full Clause 6/7.4 dependency audit remains open |
30
+ | 5.3 conforming and strictly conforming Prolog goal boundary | audit | strict registry/control/evaluable filtering and Clause 7-9 regression suites; remaining prescribed mode/error and goal-semantics rows remain open |
31
+ | 5.4 accompanying documentation for implementation-defined and implementation-specific features | covered | `ISO-IMPLEMENTATION-DEFINED.md`, *The Art of EyeProlog*, strict-boundary documentation, and release-facing conformance ledgers |
32
+
33
+ ## 5.5 — extension boundaries
34
+
35
+ | Requirement | Status | EyeProlog decision / evidence |
36
+ | --- | --- | --- |
37
+ | 5.5 general extension rule | covered | normal mode may provide documented extensions; strict mode removes their Part 1 interpretation rather than changing implementation-defined choices |
38
+ | 5.5.1 syntax extensions preserve standard token/text meaning | audit | WG17 syntax is a release gate and strict mode removes module/DCG/quad interpretation; a complete one-row preservation map for every Clause 6 production remains open |
39
+ | 5.5.2 additional predefined operators | covered | strict mode starts from the Part 1 predefined operator table; normal-profile extra operators are documented and filtered |
40
+ | 5.5.3 initial character-conversion mapping | covered | identity initial mapping; user changes are exercised through preparation/execution `char_conversion/2` behavior |
41
+ | 5.5.4 additional term types | covered | the normal JavaScript API's `stringTerm(Text)` is documented as an implementation-specific sixth term type, including disjointness, ordering, clause conversion, lack of source token syntax, expression behavior, and writing; strict program/goal entry rejects that type with `representation_error(term)` |
42
+ | 5.5.5 additional directives | covered | normal module/library directives are documented implementation-specific features and are rejected by strict mode |
43
+ | 5.5.6 additional side effects | audit | host/proof/statistics/library effects are outside the strict registry or documented normal-mode behavior; exhaustive side-effect preservation mapping remains part of the Clause 7.7/7.10 audit |
44
+ | 5.5.7 additional control constructs | covered | `tnot/1` and implementation-specific execution optimizations are absent/disabled in strict mode; standard control constructs remain separately audited |
45
+ | 5.5.8 additional flags | covered | normal `occurs_check` extension is absent from strict mode; the Part 1 flag family is fully audited |
46
+ | 5.5.9 additional built-in predicates and error forms | audit | strict registry excludes normal-profile library/native additions; `ISO-BUILTIN-MODE-ERROR-MATRIX.md` now closes 8.2-8.5 and 8.15-8.17 row-by-row, while 8.6-8.14 remain open |
47
+ | 5.5.10 additional evaluable functors/types | audit | strict mode excludes normal-profile arithmetic extensions such as evaluable `e`; Clause 9 exceptional-value and error-precedence rows remain under audit |
48
+ | 5.5.11 reserved atoms | not applicable | EyeProlog declares no reserved-atom extension; extension names remain ordinary atoms unless used in a documented syntactic/predicate/directive role |
49
+
50
+ ## Exit-use rule
51
+
52
+ A top-level row stays `audit` until all of the normative clauses it depends on
53
+ have explicit pass, not-applicable, or documented implementation-defined
54
+ outcomes. This prevents a large green regression suite from being mistaken for
55
+ a completed processor-requirement audit.
@@ -9,7 +9,10 @@ The suite is intentionally file-based. Exact standard output, errors, warnings,
9
9
  and proof output test the behavior of the JavaScript implementation.
10
10
  [ISO-MATRIX.md](ISO-MATRIX.md) maps Part 1 normative clause families, all three
11
11
  corrigenda, plus EyeProlog's module and DCG compatibility profiles, to representative
12
- executable cases. [WG17-SYNTAX-STATUS.md](WG17-SYNTAX-STATUS.md) records the
12
+ executable cases. [ISO-BUILTIN-MODE-ERROR-MATRIX.md](ISO-BUILTIN-MODE-ERROR-MATRIX.md)
13
+ tracks the row-by-row built-in audit, while
14
+ [ISO-PROCESSOR-REQUIREMENTS.md](ISO-PROCESSOR-REQUIREMENTS.md) decomposes the
15
+ Clause 5 processor obligations. [WG17-SYNTAX-STATUS.md](WG17-SYNTAX-STATUS.md) records the
13
16
  complete one-to-one trace for the vendored active upstream WG17 syntax cases.
14
17
  [STC-DRAFT-STATUS.md](STC-DRAFT-STATUS.md) separately tracks executable
15
18
  implementation questions from the post-N289 working draft; those cases are
@@ -19,12 +22,11 @@ review evidence, not normative ISO claims.
19
22
  profile and implementation extensions. The default registry covers the exact
20
23
  predicate indicators listed in Appendix B of the book across the Part 1 strict-core
21
24
  target and the normal-mode module/DCG compatibility families. [ISO-COMPLIANCE.md](ISO-COMPLIANCE.md) is the explicit
22
- processor-requirement ledger for the Part 1 strict-core audit. This suite is
25
+ release-facing ledger for the Part 1 strict-core audit. This suite is
23
26
  still not an independent certification of every processor requirement, lexical
24
- edge, or option combination. Selected prescribed-error sequences are now
25
- checked exactly (including term construction, database/all-solutions predicates,
26
- and ISO 8.14.1-8.14.4), while the remaining built-in error-order matrix is
27
- tracked in the compliance ledger. Cases under `iso/`
27
+ edge, or option combination. The 8.2-8.5 and 8.15-8.17 built-in slices are now
28
+ tracked one prescribed mode/error row at a time; 8.6-8.14 and the remaining
29
+ processor semantics stay explicitly open in the dedicated audit files. Cases under `iso/`
28
30
  identify standards-derived behavior; other directories cover EyeProlog host
29
31
  contracts and extensions. EyeProlog-only execution features such as automatic
30
32
  tabling and `tnot/1` well-founded negation are outside the Part 1 strict-core
@@ -6,10 +6,12 @@ import {
6
6
  Program,
7
7
  Solver,
8
8
  Env,
9
+ compound,
9
10
  createStrictIsoRegistry,
10
11
  parseGoalText,
11
12
  parseProgramText,
12
13
  run,
14
+ stringTerm,
13
15
  } from '../src/index.js';
14
16
  import { TestReporter, isMainModule, runStandalone } from './test-style.mjs';
15
17
 
@@ -177,6 +179,23 @@ export function runIsoStrict(reporter = new TestReporter()) {
177
179
  equal(strict.stats.completed_goal_lists, 1, 'strict Unicode char_code/2');
178
180
  });
179
181
 
182
+ reporter.test('rejects the normal-profile string term extension at strict API boundaries', () => {
183
+ const normalSolver = new Solver(Program.parse(''), {});
184
+ const normalGoal = compound('=', [stringTerm('x'), stringTerm('x')]);
185
+ equal([...normalSolver.solve([normalGoal], new Env(), 0)].length, 1,
186
+ 'normal API string terms remain usable');
187
+
188
+ const strictSolver = new Solver(Program.parse('', { isoStrict: true }), { isoStrict: true });
189
+ equal(capture(() => [...strictSolver.solve([normalGoal], new Env(), 0)]).formal,
190
+ 'representation_error(term)', 'strict execution rejects API string terms');
191
+
192
+ equal(capture(() => new Program([{
193
+ head: compound('p', [stringTerm('x')]),
194
+ body: [],
195
+ }], { isoStrict: true })).formal,
196
+ 'representation_error(term)', 'strict program construction rejects API string terms');
197
+ });
198
+
180
199
  reporter.test('keeps the strict write-option surface to Part 1 plus Corrigendum 3', () => {
181
200
  const strictError = capture(() => run('', {
182
201
  isoStrict: true,
@@ -608,6 +627,157 @@ export function runIsoStrict(reporter = new TestReporter()) {
608
627
  1, 'resource handling does not reintroduce a finite max_arity');
609
628
  });
610
629
 
630
+ reporter.test('converts asserted control bodies recursively per ISO 7.6.2', () => {
631
+ const source = ':- dynamic(p/0).\n:- dynamic(q/0).\n:- dynamic(r/0).\n';
632
+ const disjunction = run(source, {
633
+ isoStrict: true,
634
+ goal: 'assertz((p :- (X;true))), clause(p,B), arg(1,B,L), nonvar(L), functor(L,call,1)',
635
+ });
636
+ equal(disjunction.stats.completed_goal_lists, 1, 'variable in asserted disjunction becomes call/1');
637
+
638
+ const ifThenElse = run(source, {
639
+ isoStrict: true,
640
+ goal: 'assertz((q :- (X->Y;Z))), clause(q,B), arg(1,B,I), arg(1,I,XG), arg(2,I,YG), arg(2,B,ZG), nonvar(XG), nonvar(YG), nonvar(ZG), functor(XG,call,1), functor(YG,call,1), functor(ZG,call,1)',
641
+ });
642
+ equal(ifThenElse.stats.completed_goal_lists, 1, 'variables in asserted if-then-else become call/1');
643
+
644
+ const disjunctionError = capture(() => run(source, { isoStrict: true, goal: 'assertz((r :- (true;4)))' }));
645
+ equal(disjunctionError.formal, 'type_error(callable)', 'invalid asserted disjunction rejected during conversion');
646
+ includes(disjunctionError.message, "';'(true, 4)", 'asserted disjunction error identifies body');
647
+
648
+ const conjunctionError = capture(() => run(source, { isoStrict: true, goal: 'asserta((r :- (true,4)))' }));
649
+ equal(conjunctionError.formal, 'type_error(callable)', 'invalid asserted conjunction rejected during conversion');
650
+ includes(conjunctionError.message, '(true, 4)', 'asserted conjunction error identifies body');
651
+ });
652
+
653
+ reporter.test('closes the ISO 8.2-8.5 prescribed mode/error rows', () => {
654
+ const cases = [
655
+ ['compare(1,3,3.0)', 'type_error(atom)', 'compare/3 order type'],
656
+ ['compare(>=,3,3.0)', 'domain_error(order)', 'compare/3 order domain'],
657
+ ['sort([1|T],_)', 'instantiation_error', 'sort/2 partial input'],
658
+ ['sort(foo,_)', 'type_error(list)', 'sort/2 input list type'],
659
+ ['sort([],foo)', 'type_error(list)', 'sort/2 output list type'],
660
+ ['keysort([1-a|T],_)', 'instantiation_error', 'keysort/2 partial input'],
661
+ ['keysort(foo,_)', 'type_error(list)', 'keysort/2 input list type'],
662
+ ['keysort([],foo)', 'type_error(list)', 'keysort/2 output list type'],
663
+ ['keysort([X],_)', 'instantiation_error', 'keysort/2 variable pair'],
664
+ ['keysort([foo],_)', 'type_error(pair)', 'keysort/2 input pair type'],
665
+ ['keysort([],[foo])', 'type_error(pair)', 'keysort/2 output pair type'],
666
+ ['functor(X,Y,3)', 'instantiation_error', 'functor/3 Name instantiation'],
667
+ ['functor(X,foo,N)', 'instantiation_error', 'functor/3 Arity instantiation'],
668
+ ['functor(X,foo(a),a)', 'type_error(atomic)', 'functor/3 Name atomic type before Arity type'],
669
+ ['functor(X,foo,a)', 'type_error(integer)', 'functor/3 Arity integer type'],
670
+ ['functor(X,1.5,1)', 'type_error(atom)', 'functor/3 positive-arity atom name'],
671
+ ['functor(X,foo,-1)', 'domain_error(not_less_than_zero)', 'functor/3 non-negative arity'],
672
+ ['arg(X,foo(a),_)', 'instantiation_error', 'arg/3 index instantiation'],
673
+ ['arg(1,X,_)', 'instantiation_error', 'arg/3 term instantiation'],
674
+ ['arg(a,foo(a),_)', 'type_error(integer)', 'arg/3 index type'],
675
+ ['arg(0,atom,_)', 'type_error(compound)', 'arg/3 term type before index domain'],
676
+ ['arg(-1,foo(a),_)', 'domain_error(not_less_than_zero)', 'arg/3 index domain'],
677
+ ['X=..Y', 'instantiation_error', '=../2 both variables'],
678
+ ['X=..[foo|T]', 'instantiation_error', '=../2 partial list'],
679
+ ['X=..foo', 'type_error(list)', '=../2 list type'],
680
+ ['X=..[F,a]', 'instantiation_error', '=../2 head instantiation'],
681
+ ['X=..[3,a]', 'type_error(atom)', '=../2 compound functor atom type'],
682
+ ['X=..[foo(a)]', 'type_error(atomic)', '=../2 singleton atomic type'],
683
+ ['X=..[]', 'domain_error(non_empty_list)', '=../2 non-empty list'],
684
+ ['term_variables(t,[X|foo])', 'type_error(list)', 'term_variables/2 output list type'],
685
+ ];
686
+ for (const [goal, formal, label] of cases) {
687
+ equal(capture(() => run('', { isoStrict: true, goal })).formal, formal, label);
688
+ }
689
+
690
+ for (const goal of [
691
+ 'unify_with_occurs_check(X,1)',
692
+ 'subsumes_term(f(X,Y),f(Z,Z))',
693
+ 'compare(<,a,b)',
694
+ 'sort([1,1],[1])',
695
+ 'keysort([2-b,1-a],[1-a,2-b])',
696
+ 'copy_term(f(X,X),f(Y,Y))',
697
+ 'term_variables(A+B+B,[A,B])',
698
+ ]) {
699
+ equal(run('', { isoStrict: true, goal }).stats.completed_goal_lists, 1, `${goal} mode`);
700
+ }
701
+ });
702
+
703
+ reporter.test('closes the ISO 8.15-8.17 prescribed mode/error rows', () => {
704
+ const cases = [
705
+ ['\\+(X)', 'instantiation_error', '\+/1 variable goal'],
706
+ ['\\+(3)', 'type_error(callable)', '\+/1 callable type'],
707
+ ['once(X)', 'instantiation_error', 'once/1 variable goal'],
708
+ ['once(3)', 'type_error(callable)', 'once/1 callable type'],
709
+ ['call(X,a)', 'instantiation_error', 'call/2 variable closure'],
710
+ ['call(3,a)', 'type_error(callable)', 'call/2 callable closure'],
711
+ ['call(foo,a,b,c,d,e,f,g,h)', 'existence_error(procedure)', 'selected call/N ceiling at call/8'],
712
+ ['atom_length(X,4)', 'instantiation_error', 'atom_length/2 atom instantiation'],
713
+ ['atom_length(1.2,4)', 'type_error(atom)', 'atom_length/2 atom type'],
714
+ ["atom_length(atom,'4')", 'type_error(integer)', 'atom_length/2 length type'],
715
+ ['atom_length(atom,-1)', 'domain_error(not_less_than_zero)', 'atom_length/2 length domain'],
716
+ ['atom_concat(X,small,Y)', 'instantiation_error', 'atom_concat/3 first/whole under-instantiation'],
717
+ ['atom_concat(small,X,Y)', 'instantiation_error', 'atom_concat/3 second/whole under-instantiation'],
718
+ ['atom_concat(1,a,b)', 'type_error(atom)', 'atom_concat/3 first type'],
719
+ ['atom_concat(a,1,b)', 'type_error(atom)', 'atom_concat/3 second type'],
720
+ ['atom_concat(a,b,1)', 'type_error(atom)', 'atom_concat/3 whole type'],
721
+ ['sub_atom(X,0,1,0,a)', 'instantiation_error', 'sub_atom/5 source instantiation'],
722
+ ['sub_atom(1,0,1,0,a)', 'type_error(atom)', 'sub_atom/5 source type'],
723
+ ['sub_atom(a,0,1,0,1)', 'type_error(atom)', 'sub_atom/5 result atom type'],
724
+ ['sub_atom(a,x,1,0,_)', 'type_error(integer)', 'sub_atom/5 Before type'],
725
+ ['sub_atom(a,0,x,0,_)', 'type_error(integer)', 'sub_atom/5 Length type'],
726
+ ['sub_atom(a,0,1,x,_)', 'type_error(integer)', 'sub_atom/5 After type'],
727
+ ['sub_atom(a,-1,1,0,_)', 'domain_error(not_less_than_zero)', 'sub_atom/5 Before domain'],
728
+ ['sub_atom(a,0,-1,0,_)', 'domain_error(not_less_than_zero)', 'sub_atom/5 Length domain'],
729
+ ['sub_atom(a,0,1,-1,_)', 'domain_error(not_less_than_zero)', 'sub_atom/5 After domain'],
730
+ ['atom_chars(X,Y)', 'instantiation_error', 'atom_chars/2 under-instantiation'],
731
+ ['atom_chars(1,[])', 'type_error(atom)', 'atom_chars/2 atom type'],
732
+ ['atom_chars(X,[a|foo])', 'type_error(list)', 'atom_chars/2 improper list'],
733
+ ['atom_chars(X,[Y,a])', 'instantiation_error', 'atom_chars/2 variable prefix element'],
734
+ ['atom_chars(X,[a,1])', 'type_error(character)', 'atom_chars/2 character element type'],
735
+ ['atom_codes(X,Y)', 'instantiation_error', 'atom_codes/2 under-instantiation'],
736
+ ['atom_codes(1,[])', 'type_error(atom)', 'atom_codes/2 atom type'],
737
+ ['atom_codes(X,[97|foo])', 'type_error(list)', 'atom_codes/2 improper list'],
738
+ ['atom_codes(X,[Y,97])', 'instantiation_error', 'atom_codes/2 variable prefix element'],
739
+ ['atom_codes(X,[97,foo])', 'type_error(integer)', 'atom_codes/2 code integer type'],
740
+ ['atom_codes(X,[97,-1])', 'representation_error(character_code)', 'atom_codes/2 code representation'],
741
+ ['char_code(X,Y)', 'instantiation_error', 'char_code/2 under-instantiation'],
742
+ ['char_code(ab,foo)', 'type_error(character)', 'char_code/2 character type before code type'],
743
+ ['char_code(a,foo)', 'type_error(integer)', 'char_code/2 code type'],
744
+ ['char_code(a,-1)', 'representation_error(character_code)', 'char_code/2 code representation'],
745
+ ['number_chars(X,Y)', 'instantiation_error', 'number_chars/2 under-instantiation'],
746
+ ['number_chars(foo,[])', 'type_error(number)', 'number_chars/2 number type'],
747
+ ['number_chars(X,[a|foo])', 'type_error(list)', 'number_chars/2 improper list'],
748
+ ['number_chars(X,[Y,a])', 'instantiation_error', 'number_chars/2 variable prefix element'],
749
+ ['number_chars(X,[a,1])', 'type_error(character)', 'number_chars/2 character element type'],
750
+ ['number_chars(X,[a])', 'syntax_error(number)', 'number_chars/2 number syntax'],
751
+ ['number_codes(X,Y)', 'instantiation_error', 'number_codes/2 under-instantiation'],
752
+ ['number_codes(foo,[])', 'type_error(number)', 'number_codes/2 number type'],
753
+ ['number_codes(X,[49|foo])', 'type_error(list)', 'number_codes/2 improper list'],
754
+ ['number_codes(X,[Y,49])', 'instantiation_error', 'number_codes/2 variable prefix element'],
755
+ ['number_codes(X,[49,foo])', 'type_error(integer)', 'number_codes/2 code integer type'],
756
+ ['number_codes(X,[49,-1])', 'representation_error(character_code)', 'number_codes/2 code representation'],
757
+ ['number_codes(X,[97])', 'syntax_error(number)', 'number_codes/2 number syntax'],
758
+ ['current_prolog_flag(1,_)', 'type_error(atom)', 'current_prolog_flag/2 flag type'],
759
+ ['current_prolog_flag(no_such_iso_flag,_)', 'domain_error(prolog_flag)', 'current_prolog_flag/2 flag domain'],
760
+ ['halt(X)', 'instantiation_error', 'halt/1 instantiation'],
761
+ ['halt(a)', 'type_error(integer)', 'halt/1 integer type'],
762
+ ];
763
+ for (const [goal, formal, label] of cases) {
764
+ equal(capture(() => run('', { isoStrict: true, goal })).formal, formal, label);
765
+ }
766
+
767
+ equal(run('', { isoStrict: true, goal: 'false' }).stats.completed_goal_lists, 0, 'false/0 fails');
768
+ const onceSolver = new Solver(Program.parse('', { isoStrict: true }), { isoStrict: true });
769
+ equal([...onceSolver.solve([parseGoalText('once((X=1;X=2))', { isoStrict: true })], new Env(), 0)].length,
770
+ 1, 'once/1 first solution');
771
+ const onceCommittedSolver = new Solver(Program.parse('', { isoStrict: true }), { isoStrict: true });
772
+ equal([...onceCommittedSolver.solve([parseGoalText('once((X=1;X=2)), X=2', { isoStrict: true })], new Env(), 0)].length,
773
+ 0, 'once/1 commits to its first solution');
774
+ const callSolver = new Solver(Program.parse('', { isoStrict: true }), { isoStrict: true });
775
+ equal([...callSolver.solve([parseGoalText('call(atom_concat,pro,log,prolog)', { isoStrict: true })], new Env(), 0)].length,
776
+ 1, 'call/4 closure expansion');
777
+ equal(run('', { isoStrict: true, goal: 'atom_concat(A,B,ab)' }).stats.completed_goal_lists, 3, 'atom_concat/3 re-executable splits');
778
+ equal(run('', { isoStrict: true, goal: 'sub_atom(ab,_,_,_,_)' }).stats.completed_goal_lists, 6, 'sub_atom/5 re-executable slices');
779
+ });
780
+
611
781
  reporter.test('covers ISO database predicate errors and empty-procedure lifetime', () => {
612
782
  const errors = [
613
783
  ['current_predicate(4)', '', 'type_error(predicate_indicator)'],