eyeprolog 1.3.56 → 1.3.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/README.md CHANGED
@@ -228,7 +228,13 @@ ongoing issue #65 audit additionally tightens Prolog-text declaration ordering,
228
228
  initialization lifetime, stream/open/close behavior, character/byte stream
229
229
  errors, `keysort/2` error cases, and strict arithmetic-extension boundaries.
230
230
  Those additions improve the Part 1 surface without turning the remaining audit
231
- rows into a blanket conformance claim.
231
+ rows into a blanket conformance claim. The post-N289 WG17/STC working draft is
232
+ reviewed separately rather than treated as a fourth Corrigendum. The
233
+ 2026-08-23 draft items #73-#76 are now pinned explicitly: float input limits for
234
+ `read*` and `number_*` conversions, invalid-character term input, and the draft
235
+ proposal that would make the two power-underflow rows depend on the processor's
236
+ 9.1.4.2 `resultF` choice. Strict mode retains the published Part 1 +
237
+ Corrigenda 1-3 behavior where the draft has not been standardized.
232
238
 
233
239
  The auditable processor-requirement checklist lives in
234
240
  [`test/conformance/ISO-COMPLIANCE.md`](test/conformance/ISO-COMPLIANCE.md).
@@ -18,10 +18,10 @@ This report summarizes the file-based conformance corpus under `test/conformance
18
18
  | proofs | 0 | 0 | 0 | 21 | 21 |
19
19
  | query | 8 | 2 | 0 | 0 | 10 |
20
20
  | rules | 13 | 3 | 0 | 0 | 16 |
21
- | stc | 5 | 4 | 0 | 0 | 9 |
21
+ | stc | 5 | 6 | 0 | 0 | 11 |
22
22
  | strings | 40 | 0 | 0 | 0 | 40 |
23
23
  | syntax | 12 | 23 | 0 | 0 | 35 |
24
24
  | terms | 26 | 3 | 0 | 0 | 29 |
25
25
  | unification | 18 | 0 | 0 | 0 | 18 |
26
26
  | variables | 16 | 7 | 0 | 0 | 23 |
27
- | **Total** | **490** | **270** | **19** | **21** | **800** |
27
+ | **Total** | **490** | **272** | **19** | **21** | **802** |
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.3.56",
6
+ "version": "1.3.58",
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
@@ -393,9 +393,9 @@ function* functorBuiltin({ goal, env }) {
393
393
 
394
394
  const name = deref(goal.args[1], env);
395
395
  const arityTerm = deref(goal.args[2], env);
396
- // ISO 8.5.1.3 orders construction-mode diagnostics. A compound Name is an
397
- // atomic type error (before an Arity type error); only an atomic non-atom
398
- // used for positive arity receives type_error(atom,...).
396
+ // Keep EyeProlog's deterministic construction-mode diagnostics aligned with
397
+ // the individual 8.5.1.3 conditions. Under 7.12, a call satisfying several
398
+ // error conditions at once does not acquire a globally mandated table order.
399
399
  if (name.type === VAR) throw new PrologError('instantiation_error');
400
400
  if (arityTerm.type === VAR) throw new PrologError('instantiation_error');
401
401
  if (name.type === COMPOUND) throw new PrologError('type_error(atomic)', name);
@@ -562,7 +562,9 @@ function* clauseBuiltin({ solver, goal, env }) {
562
562
  if (head.type !== ATOM && head.type !== COMPOUND) throw new PrologError('type_error(callable)', head);
563
563
  const indicator = compound('/', [atom(head.name), numberTerm(head.arity)]);
564
564
  const group = solver.program.findGroup(head.name, head.arity, head.module ?? goal.module ?? 'user');
565
- // ISO 8.8.1.3 places private-procedure access before Body callability.
565
+ // EyeProlog selects private-procedure access before Body callability when both
566
+ // conditions hold. ISO 7.12 permits an implementation-dependent choice when
567
+ // more than one error condition is simultaneously satisfied.
566
568
  if (isProcessorStaticProcedure(solver, head) ||
567
569
  (solver.isoStrict && group && !group.dynamic)) {
568
570
  throw new PrologError('permission_error(access, private_procedure)', indicator);
@@ -590,7 +592,7 @@ function requireClauseHead(head) {
590
592
  if (head.type !== ATOM && head.type !== COMPOUND) throw new PrologError('type_error(callable)', head);
591
593
  }
592
594
 
593
- function convertAssertedBody(term, culprit = term) {
595
+ export function convertClauseBodyTerm(term, culprit = term) {
594
596
  if (term.type === VAR) return compound('call', [term]);
595
597
  // ISO 7.6.2 converts each argument of conjunction, disjunction, and if-then
596
598
  // recursively when a term is converted to a clause body. This matters for
@@ -599,8 +601,8 @@ function convertAssertedBody(term, culprit = term) {
599
601
  // being stored and failing only if that branch is later executed.
600
602
  if (term.type === COMPOUND && [',', ';', '->'].includes(term.name) && term.arity === 2) {
601
603
  return compound(term.name, [
602
- convertAssertedBody(term.args[0], culprit),
603
- convertAssertedBody(term.args[1], culprit),
604
+ convertClauseBodyTerm(term.args[0], culprit),
605
+ convertClauseBodyTerm(term.args[1], culprit),
604
606
  ]);
605
607
  }
606
608
  if (term.type !== ATOM && term.type !== COMPOUND) {
@@ -643,7 +645,7 @@ function assertBuiltin(atStart) {
643
645
  return function* ({ solver, goal, env }) {
644
646
  const parts = clauseParts(goal.args[0], env);
645
647
  requireClauseHead(parts.head);
646
- const body = convertAssertedBody(parts.body);
648
+ const body = convertClauseBodyTerm(parts.body);
647
649
  assertModifiable(solver, parts.head, goal.module ?? 'user');
648
650
  const copied = freshCopy(compound('$clause', [parts.head, body]), env);
649
651
  solver.program.insertDynamicClause({
@@ -782,9 +784,9 @@ function* opBuiltin({ solver, goal, env }) {
782
784
  const priorityTerm = deref(goal.args[0], env);
783
785
  const specifierTerm = deref(goal.args[1], env);
784
786
 
785
- // ISO 8.14.3.3 prescribes the ordering below. In particular, all three
786
- // instantiation cases precede type/domain errors, and Operator list-shape
787
- // errors precede priority/specifier domain errors.
787
+ // Keep EyeProlog's selected 8.14.3.3 overlap order deterministic. Each
788
+ // individual condition remains observable; ISO 7.12 leaves the choice
789
+ // implementation dependent when several error conditions hold at once.
788
790
  if (priorityTerm.type === VAR) throw new PrologError('instantiation_error');
789
791
  if (specifierTerm.type === VAR) throw new PrologError('instantiation_error');
790
792
  const operator = opListPreflight(goal.args[2], env);
@@ -943,12 +945,11 @@ function optionList(term, env) {
943
945
  return items.map((item) => deref(item, env));
944
946
  }
945
947
 
946
- // read_term/3 and write_term/3 have prescribed error ordering in ISO 8.14.
947
- // In particular, a partial option list (or a variable option element) is
948
- // diagnosed before several stream errors, while the relative order of a
949
- // non-list Options term and an invalid stream-or-alias differs between the
950
- // read and write predicates. Split list-shape inspection from full option
951
- // validation so those predicates can follow the standard order exactly.
948
+ // Keep read_term/3 and write_term/3 diagnostics deterministic and make every
949
+ // 8.14 error condition independently testable. ISO 7.12 leaves the choice
950
+ // implementation dependent when several error conditions hold simultaneously,
951
+ // so this preflight order is an EyeProlog processor choice rather than a claim
952
+ // that the textual order of an error table is globally mandatory.
952
953
  function preflightOptionList(term, env) {
953
954
  const value = deref(term, env);
954
955
  if (value.type === VAR) throw new PrologError('instantiation_error');
@@ -1017,9 +1018,9 @@ function* openBuiltin({ solver, goal, env }) {
1017
1018
  const streamTarget = deref(goal.args[2], env);
1018
1019
  const optionTerm = goal.arity === 3 ? emptyList() : goal.args[3];
1019
1020
 
1020
- // ISO 8.11.5.3 + Corrigenda 2/3. Keep each prescribed condition
1021
- // observable on its own and apply the published ordering for deterministic
1022
- // diagnostics when several arguments are erroneous.
1021
+ // ISO 8.11.5.3 + Corrigenda 2/3. Keep each required condition observable
1022
+ // on its own. When several conditions hold, the order below is EyeProlog's
1023
+ // deterministic 7.12 processor choice unless a procedural rule constrains it.
1023
1024
  if (source.type === VAR) throw new PrologError('instantiation_error');
1024
1025
  if (mode.type === VAR) throw new PrologError('instantiation_error');
1025
1026
  const preflight = preflightOptionList(optionTerm, env);
@@ -1183,16 +1184,21 @@ function isStreamPropertyPattern(value) {
1183
1184
  function* streamPropertyBuiltin({ solver, goal, env }) {
1184
1185
  const reference = deref(goal.args[0], env);
1185
1186
  const propertyPattern = deref(goal.args[1], env);
1187
+ let fixedStreamId = null;
1186
1188
  if (reference.type !== VAR) {
1187
- const id = streamTermReference(goal.args[0], env);
1188
- if (!solver.io.resolve(id)) throw new PrologError('domain_error(stream)', reference);
1189
+ // 8.11.8.1 enumerates (Stream, Property) pairs for currently open
1190
+ // streams. A ground term with the implementation's stream-term shape is
1191
+ // still a stream-term even when its stream is no longer open; in that
1192
+ // case the enumeration simply has no matching pair. domain_error(stream)
1193
+ // is reserved for a term which is not a stream-term at all.
1194
+ fixedStreamId = streamTermReference(goal.args[0], env);
1189
1195
  }
1190
1196
  if (!isStreamPropertyPattern(propertyPattern)) {
1191
1197
  throw new PrologError('domain_error(stream_property)', propertyPattern);
1192
1198
  }
1193
1199
  const streams = reference.type === VAR
1194
1200
  ? [...solver.io.streams.values()]
1195
- : [solver.io.resolve(streamTermReference(goal.args[0], env))];
1201
+ : [solver.io.resolve(fixedStreamId)].filter(Boolean);
1196
1202
  for (const stream of streams) {
1197
1203
  for (const property of streamProperties(stream)) {
1198
1204
  const next = env.clone();
@@ -1232,11 +1238,10 @@ function preflightInputUnitTarget(name, target, env, solver) {
1232
1238
  throw new PrologError('type_error(in_character)', value);
1233
1239
  }
1234
1240
  } else if (name.endsWith('code')) {
1235
- // ISO 8.12.1.3/8.12.2.3 puts the integer type check before stream
1236
- // diagnostics, but the integer-is-not-an-in-character-code
1237
- // representation error comes after the stream existence/mode/type/EOF
1238
- // conditions. Defer that range/repertoire check until the input entity
1239
- // itself has been inspected below.
1241
+ // Keep the selected 8.12 overlap behavior stable: integer type validation
1242
+ // is done here, while the in-character-code repertoire check is deferred
1243
+ // until after stream/entity diagnostics. ISO 7.12 permits this processor
1244
+ // choice when those error conditions overlap.
1240
1245
  if (value.type !== VAR && (value.type !== NUMBER || !isDecimalInteger(value.name))) {
1241
1246
  throw new PrologError('type_error(integer)', value);
1242
1247
  }
@@ -1290,10 +1295,9 @@ function inputUnitBuiltin(name) {
1290
1295
  if (!binary && unit != null && solver.isoStrict && !isStrictIsoPcsCharacter(unit)) {
1291
1296
  throw new PrologError('representation_error(character)');
1292
1297
  }
1293
- // For get_code/peek_code the published error table places an invalid
1294
- // in-character-code target after stream and input-entity errors. Doing
1295
- // this here prevents a fixed bad Code from masking (for example) an
1296
- // output-stream, binary-stream, past-EOF, or invalid-character error.
1298
+ // EyeProlog deliberately defers this representation check until after
1299
+ // stream and input-entity diagnostics. ISO 7.12 permits that deterministic
1300
+ // choice when several 8.12 error conditions hold simultaneously.
1297
1301
  const targetValue = deref(target, env);
1298
1302
  if (name.endsWith('code') && targetValue.type !== VAR &&
1299
1303
  !validStrictInputCharacterCode(targetValue, solver)) {
@@ -1596,9 +1600,9 @@ function* readTermBuiltin({ solver, goal, env }) {
1596
1600
  const optionTerm = goal.args[goal.arity - 1];
1597
1601
  const streamTerm = goal.arity === 3 ? deref(goal.args[0], env) : null;
1598
1602
 
1599
- // ISO 8.14.1.3 error order:
1600
- // a) variable stream; b) partial/options-variable; c) bad stream-or-alias;
1601
- // d) non-list options; e) invalid read option; then stream existence/mode.
1603
+ // EyeProlog's selected 8.14.1.3 overlap order. Each listed error condition
1604
+ // is covered independently; 7.12 makes simultaneous-error selection
1605
+ // implementation dependent unless another procedural requirement constrains it.
1602
1606
  if (streamTerm?.type === VAR) throw new PrologError('instantiation_error');
1603
1607
  const preflight = preflightOptionList(optionTerm, env);
1604
1608
  if (goal.arity === 3) streamReference(goal.args[0], env);
@@ -1712,9 +1716,9 @@ function* writeTermBuiltin({ solver, goal, env }) {
1712
1716
  const optionTerm = goal.args[goal.arity - 1];
1713
1717
  const streamTerm = goal.arity === 3 ? deref(goal.args[0], env) : null;
1714
1718
 
1715
- // ISO 8.14.2.3 error order:
1716
- // a) variable stream; b) partial/options-variable; c) non-list options;
1717
- // d) bad stream-or-alias; e) invalid write option; then stream errors.
1719
+ // EyeProlog's selected 8.14.2.3 overlap order. Each listed error condition
1720
+ // is covered independently; 7.12 makes simultaneous-error selection
1721
+ // implementation dependent unless another procedural requirement constrains it.
1718
1722
  if (streamTerm?.type === VAR) throw new PrologError('instantiation_error');
1719
1723
  const preflight = preflightOptionList(optionTerm, env);
1720
1724
  const optionItems = requireProperOptionList(preflight);
@@ -2187,7 +2191,9 @@ function flatFindallTemplate(template) {
2187
2191
 
2188
2192
  function* findallBuiltin({ solver, goal, env }) {
2189
2193
  const [template, innerGoal, bag] = goal.args;
2190
- // ISO 8.10.1.3 diagnoses Goal before Instances when both are erroneous.
2194
+ // EyeProlog validates Goal before Instances when both are erroneous. ISO 7.12
2195
+ // makes simultaneous-error selection implementation dependent; this order is
2196
+ // a documented/tested processor choice, not a blanket table-order rule.
2191
2197
  const invoked = callable(innerGoal, env);
2192
2198
  assertListOrPartial(bag, env);
2193
2199
  const collector = solver.cloneForInnerGoal(10000000);
@@ -2252,7 +2258,9 @@ function sortedUnique(items) {
2252
2258
 
2253
2259
  function allSolutionsBuiltin(asSet) {
2254
2260
  return function* ({ solver, goal, env }) {
2255
- // ISO 8.10.2.3/8.10.3.3 diagnose the iterated goal before Instances.
2261
+ // EyeProlog validates the iterated goal before Instances. ISO 7.12 makes
2262
+ // simultaneous-error selection implementation dependent; keep this stable as
2263
+ // a processor choice rather than describing it as mandatory table ordering.
2256
2264
  const { iterated, quantified } = bagGoalParts(goal.args[1], env);
2257
2265
  assertListOrPartial(goal.args[2], env);
2258
2266
  const free = freeVariables(iterated, goal.args[0], quantified, env);
package/src/program.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  parseGoalText,
13
13
  tryParseClausesFastInto,
14
14
  } from './parser.js';
15
- import { PrologError, getStrictIsoRegistry } from './iso.js';
15
+ import { PrologError, convertClauseBodyTerm, getStrictIsoRegistry } from './iso.js';
16
16
  import { currentWorkingDirectory, fs, path } from './platform.js';
17
17
  import {
18
18
  standardLibrarySources,
@@ -627,6 +627,14 @@ class ProgramBuilder {
627
627
  }
628
628
 
629
629
  if (!isDirectiveClause(clause)) {
630
+ if (program.strictIso && Array.isArray(clause.body) && clause.body.length > 0) {
631
+ // ISO 7.6.2 applies when source text is prepared as well as when a
632
+ // clause term is asserted dynamically. Keep the original variable
633
+ // objects while wrapping variable goals in call/1 so sharing with
634
+ // the head (and recursively through ;/2 and ->/2) is preserved for
635
+ // later clause/2 and retract/1 conversion back to terms.
636
+ clause.body = clause.body.map((goal) => convertClauseBodyTerm(goal));
637
+ }
630
638
  assertHeadIsDefinable(clause.head, program.strictIso);
631
639
  const head = clause.head;
632
640
  if (head.type !== ATOM && head.type !== COMPOUND) continue;
@@ -4,12 +4,21 @@ This matrix turns the broad built-in audit in `ISO-COMPLIANCE.md` into smaller,
4
4
  release-gated units. The normative baseline is ISO/IEC 13211-1:1995 plus
5
5
  Technical Corrigenda 1:2007, 2:2012, and 3:2017.
6
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.
7
+ A `covered` row has executable evidence for the required successful mode,
8
+ failure behavior, or individual error condition. `not applicable` records a
9
+ conditional standard branch that cannot occur under EyeProlog's documented
10
+ processor choices. ISO 7.12 makes the reported error implementation dependent
11
+ when more than one error condition is simultaneously satisfied. Accordingly,
12
+ this matrix treats overlap tests as stable EyeProlog processor choices unless a
13
+ more specific procedural requirement or Corrigendum constrains the result; it
14
+ does not infer a universal priority merely from the textual order of an error
15
+ table.
16
+
17
+ The table is intentionally explicit about what has and has not been closed.
18
+ The current audit now covers the complete 8.2-8.17 built-in family at the
19
+ level of prescribed modes, success/failure behavior, individual error
20
+ conditions, and explicit not-applicable processor branches. Higher-level
21
+ Clause 7 and Clause 9 semantics remain tracked separately.
13
22
 
14
23
  The focused assertions added for this matrix live in `test/run-iso-strict.mjs`;
15
24
  existing file-based cases remain additional independent evidence.
@@ -102,6 +111,171 @@ The Corrigendum 2 additions have the same shape.
102
111
  | Cor.2 8.5.5 `term_variables/2` | supplied partial/list result | covered | Corrigendum cases |
103
112
  | 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
113
 
114
+ ## 8.6 — arithmetic evaluation
115
+
116
+ | Clause / predicate | Prescribed row | Status | Executable evidence |
117
+ | --- | --- | --- | --- |
118
+ | 8.6.1 `is/2` | evaluate the second argument and unify/check the result | covered | strict `X is 1+2, X=3`, arithmetic corpus |
119
+ | 8.6.1 failed result unification | a successfully evaluated value need not unify with the first argument | covered | strict `4 is 1+2` fails |
120
+ | 8.6.1 error (a) | variable expression -> instantiation error | covered | strict `X is Y` |
121
+ | 8.6.1 delegated expression errors | evaluation may report any applicable 7.9.2 error | covered | strict `X is foo` plus Clause 9 arithmetic/error suites |
122
+
123
+ ## 8.7 — arithmetic comparison
124
+
125
+ | Clause / predicate | Prescribed row | Status | Executable evidence |
126
+ | --- | --- | --- | --- |
127
+ | 8.7.1 `(=:=)/2` | evaluate two expressions and test arithmetic equality | covered | strict `1 =:= 1`, arithmetic comparison corpus |
128
+ | 8.7.1 `(=\=)/2` | evaluate two expressions and test arithmetic inequality | covered | strict `1 =\= 2` |
129
+ | 8.7.1 `(<)/2` | arithmetic less-than | covered | strict `1 < 2` |
130
+ | 8.7.1 `(=<)/2` | arithmetic less-than-or-equal | covered | strict `1 =< 1` |
131
+ | 8.7.1 `(>)/2` | arithmetic greater-than | covered | strict `2 > 1` |
132
+ | 8.7.1 `(>=)/2` | arithmetic greater-than-or-equal | covered | strict `2 >= 2` |
133
+ | 8.7.1 error (a) | first expression variable -> instantiation error | covered | strict variable-first assertions across the comparison family |
134
+ | 8.7.1 error (b) | second expression variable -> instantiation error | covered | strict variable-second assertions across the comparison family |
135
+ | 8.7.1 delegated expression errors | either expression may report an applicable 7.9.2 error | covered | strict `foo =:= 1` plus Clause 9 error suites |
136
+ | 8.7 mixed integer/float operation | selected Part 1 I-to-F comparison operations, including conversion overflow | covered | dedicated strict mixed-comparison tests |
137
+
138
+ ## 8.8 — clause inspection
139
+
140
+ | Clause / predicate | Prescribed row | Status | Executable evidence |
141
+ | --- | --- | --- | --- |
142
+ | 8.8.1 `clause/2` | enumerate clauses of a public user-defined procedure | covered | dynamic-clause tests and grouped-solutions/clause corpus |
143
+ | 8.8.1 error (a) | variable head -> instantiation error | covered | strict `clause(X,_)` |
144
+ | 8.8.1 error (b) | non-callable head -> callable type error | covered | strict `clause(4,_)` |
145
+ | 8.8.1 error (c) | private procedure -> access/private-procedure permission error | covered | strict static/private tests |
146
+ | 8.8.1 error (d) | fixed non-callable Body -> callable type error | covered | strict dynamic `clause(p(_),4)` coverage |
147
+ | 8.8.1 clause-to-term conversion | source variable goals become `call/1` while preserving sharing with the head | covered | strict `foo(X):-X` -> `clause(foo(C),call(C))`; nested `;/2` and `->/2` regressions |
148
+ | 8.8.2 `current_predicate/1` | enumerate/query user-defined predicate indicators, including empty declared procedures | covered | strict row audit and empty-procedure lifetime tests |
149
+ | 8.8.2 error (a) | fixed non-predicate-indicator -> predicate-indicator type error | covered | strict `current_predicate(4)` |
150
+
151
+ ## 8.9 — database modification
152
+
153
+ | Clause / predicate | Prescribed row | Status | Executable evidence |
154
+ | --- | --- | --- | --- |
155
+ | 8.9.1 `asserta/1` | convert term to clause and insert before existing clauses | covered | dynamic-database corpus and strict body-conversion tests |
156
+ | 8.9.1 errors (a-b) | variable/non-callable Head -> instantiation/callable type error | covered | strict `asserta(_)`, `asserta(4)` |
157
+ | 8.9.1 error (c) | Body cannot be converted to a goal -> callable type error | covered | strict `asserta((p:-4))` and nested-control conversion tests |
158
+ | 8.9.1 error (d) | static procedure -> modify/static-procedure permission error | covered | strict static-procedure tests |
159
+ | 8.9.2 `assertz/1` | convert term to clause and insert after existing clauses | covered | dynamic-database corpus and strict body-conversion tests |
160
+ | 8.9.2 errors (a-d) | same Head/Body/static families as `asserta/1` | covered | strict `assertz(_)`, `assertz(4)`, `assertz((p:-4))`, static-procedure tests |
161
+ | 8.9.3 `retract/1` | retract matching dynamic clauses re-executably using the logical update view | covered | dynamic-database and logical-update-view suites |
162
+ | 8.9.3 errors (a-b) | variable/non-callable Head -> instantiation/callable type error | covered | strict `retract(_)`, `retract(4)` |
163
+ | Cor.2 8.9.3 error (c) | static procedure -> **modify**/static-procedure permission error | covered | strict `retract(atom(_))` and Corrigendum coverage |
164
+ | 8.9.3 clause-term matching | converted source body retains head/body variable identity when matched by `retract/1` | covered | strict `retract((foo(C):-call(C)))` regression |
165
+ | 8.9.4 `abolish/1` | remove a dynamic procedure | covered | strict lifetime tests |
166
+ | 8.9.4 errors (a-b) | variable indicator or variable Name/Arity component -> instantiation error | covered | strict `abolish(X)`, `abolish(foo/X)` |
167
+ | 8.9.4 error (c) | non-predicate-indicator -> predicate-indicator type error | covered | strict `abolish(foo)` |
168
+ | 8.9.4 error (d) | non-integer Arity -> integer type error | covered | strict `abolish(foo/a)` |
169
+ | 8.9.4 error (e) | non-atom Name -> atom type error | covered | strict `abolish(4/1)` |
170
+ | 8.9.4 error (f) | negative Arity -> non-negative domain error | covered | strict `abolish(foo/(-1))` |
171
+ | 8.9.4 finite-`max_arity` error (g) | Arity exceeds finite `max_arity` | not applicable | selected `max_arity=unbounded` |
172
+ | 8.9.4 error (h) | static procedure -> modify/static-procedure permission error | covered | strict `abolish(atom/1)` |
173
+ | Cor.2 8.9.5 `retractall/1` | remove all matching dynamic heads while retaining the procedure | covered | strict empty-procedure lifetime test and Corrigendum cases |
174
+ | Cor.2 8.9.5 errors (a-c) | variable/non-callable Head or static procedure | covered | strict `retractall(_)`, `retractall(4)`, static-procedure test |
175
+
176
+ ## 8.10 — all solutions
177
+
178
+ | Clause / predicate | Prescribed row | Status | Executable evidence |
179
+ | --- | --- | --- | --- |
180
+ | 8.10.1 `findall/3` | collect renamed template instances in solution order; empty result succeeds with `[]` | covered | strict `[1,2]` assertion plus all-solutions corpus |
181
+ | 8.10.1 error (a) | variable Goal -> instantiation error | covered | strict `findall(X,Y,L)` |
182
+ | 8.10.1 error (b) | non-callable Goal -> callable type error | covered | strict `findall(X,4,L)` |
183
+ | 8.10.1 error (c) | Instances neither partial list nor list -> list type error | covered | strict `findall(X,true,foo)` |
184
+ | 8.10.2 `bagof/3` | non-empty grouped solution lists, re-executable over free-variable witnesses | covered | grouped-solutions corpus and strict `[1,2]` assertion |
185
+ | 8.10.2 empty solution set | goal fails rather than returning `[]` | covered | strict `bagof(X,fail,L)` |
186
+ | 8.10.2 errors (a-b) | variable/non-callable iterated-goal term -> instantiation/callable type error | covered | strict `bagof(X,Y,L)`, `bagof(X,4,L)` |
187
+ | 8.10.2 error (c) | Instances neither partial list nor list -> list type error | covered | strict `bagof(X,true,foo)` |
188
+ | 8.10.3 `setof/3` | grouped solutions sorted with duplicates removed | covered | strict duplicate/sort assertion plus grouped-solutions corpus |
189
+ | 8.10.3 empty solution set | goal fails rather than returning `[]` | covered | strict `setof(X,fail,L)` |
190
+ | 8.10.3 errors (a-b) | variable/non-callable iterated-goal term -> instantiation/callable type error | covered | strict `setof(X,Y,L)`, `setof(X,4,L)` |
191
+ | 8.10.3 error (c) | Instances neither partial list nor list -> list type error | covered | strict `setof(X,true,foo)` |
192
+ | 8.10.2-3 witness-group order where the standard leaves a choice | implementation dependent | covered | existing grouping tests document EyeProlog's stable selection without turning it into a cross-processor requirement |
193
+
194
+
195
+ ## 8.11 — stream selection and control
196
+
197
+ | Clause / predicate | Prescribed row | Status | Executable evidence |
198
+ | --- | --- | --- | --- |
199
+ | 8.11.1 `current_input/1` | unify/query the current input stream-term | covered | strict current-input success assertion |
200
+ | 8.11.1 error | fixed argument is neither variable nor stream-term -> stream domain error | covered | strict `current_input(foo)` |
201
+ | 8.11.2 `current_output/1` | unify/query the current output stream-term | covered | strict current-output success assertion |
202
+ | 8.11.2 error | fixed argument is neither variable nor stream-term -> stream domain error | covered | strict `current_output(foo)` |
203
+ | 8.11.3 `set_input/1` | select an open input stream by stream-term or alias | covered | stream-selection corpus |
204
+ | 8.11.3 errors | variable; invalid stream-or-alias; missing stream; output stream | covered | dedicated strict assertions for all four conditions |
205
+ | 8.11.4 `set_output/1` | select an open output stream by stream-term or alias | covered | stream-selection corpus |
206
+ | 8.11.4 errors | variable; invalid stream-or-alias; missing stream; input stream | covered | dedicated strict assertions for all four conditions |
207
+ | 8.11.5 `open/4`, `open/3` | open a source/sink with standardized mode/options and return a fresh stream | covered | stream corpus plus strict successful/error probes |
208
+ | 8.11.5 errors: instantiation | variable source/mode or variable option element | covered | strict `open/4` assertions |
209
+ | 8.11.5 errors: argument shapes | non-atom mode; non-list options; non-variable stream output (Cor.2 uninstantiation) | covered | strict `open/4` assertions |
210
+ | 8.11.5 errors: domains | invalid source/sink, I/O mode, or stream option | covered | strict `open/4` assertions |
211
+ | 8.11.5 source/sink errors | missing read source or source/sink that cannot be opened | covered | strict host-I/O probes for existence/permission conditions |
212
+ | 8.11.5 alias collision | `alias(A)` already names an open stream -> open permission error with complete `alias(A)` culprit | covered | focused issue #65 alias-collision regression |
213
+ | 8.11.5 `reposition(true)` impossible | permission error when the requested stream cannot be repositioned | covered | stream option corpus and implementation-defined stream capability checks |
214
+ | 8.11.6 `close/2`, `close/1` | close an open non-standard stream and maintain current-stream fallbacks | covered | stream lifecycle corpus |
215
+ | 8.11.6 errors | variable stream/option element; non-list options; invalid stream-or-alias; invalid close option; missing stream | covered | strict close assertions |
216
+ | 8.11.6 `force(true)` | presence of `force(true)` suppresses resource/system close failure regardless of contradictory later `force(false)` | covered | dedicated two-order strict regression |
217
+ | 8.11.7 `flush_output/1`, `flush_output/0` | flush an output stream/current output | covered | strict success assertion and stream corpus |
218
+ | 8.11.7 errors | variable; invalid stream-or-alias; missing stream; input stream | covered | dedicated strict assertions |
219
+ | 8.11.8 `stream_property/2` | enumerate properties of currently open streams; order is implementation dependent | covered | stream-property corpus and strict checks |
220
+ | 8.11.8 stream-term error | fixed non-stream-term -> stream domain error | covered | strict `stream_property(foo,_)` |
221
+ | 8.11.8 property error | fixed non-stream-property -> stream-property domain error | covered | strict invalid-property assertion |
222
+ | 8.11.8 closed valid stream-term | no pair exists for a stream that is no longer open -> fail | covered | dedicated closed-stream regression |
223
+ | 8.11.8 `at_end_of_stream/0-1` | test current/named stream end or past-end state | covered | strict success and EOF corpus |
224
+ | 8.11.8 `at_end_of_stream/1` errors | variable; invalid stream-or-alias; missing stream | covered | dedicated strict assertions |
225
+ | 8.11.9 `set_stream_position/2` | set a repositionable open stream to a valid stream position | covered | dedicated positioned-stream success assertion |
226
+ | 8.11.9 errors | variable stream/position; invalid stream-or-alias/position; missing stream; non-repositionable stream | covered | dedicated strict assertions |
227
+
228
+ ## 8.12 — character input/output
229
+
230
+ | Clause / predicate | Prescribed row | Status | Executable evidence |
231
+ | --- | --- | --- | --- |
232
+ | 8.12.1 `get_char/1-2` | input next text character or EOF indicator | covered | stream/character corpus and strict probes |
233
+ | 8.12.1 `get_code/1-2` | input next text character code or `-1` at EOF | covered | stream/code corpus and strict probes |
234
+ | 8.12.1 argument errors | variable stream; invalid fixed in-character; non-integer fixed code | covered | dedicated strict assertions |
235
+ | 8.12.1 stream errors | invalid stream-or-alias; missing stream; output stream; binary stream; past-end with `eof_action(error)` | covered | dedicated strict and constructed-stream assertions |
236
+ | 8.12.1 input entity/code representation | non-character input entity or fixed integer not an in-character code | covered | strict entity/code representation regressions; bad-code check is deferred until earlier stream/entity conditions are resolved |
237
+ | 8.12.2 `peek_char/1-2`, `peek_code/1-2` | inspect next character/code without changing stream position | covered | strict probes and stream corpus |
238
+ | 8.12.2 errors | corresponding 8.12.1 argument, stream, EOF, entity and code-representation conditions | covered | dedicated strict assertions including overlap regression |
239
+ | 8.12.3 `put_char/1-2` | output one text character | covered | output corpus |
240
+ | 8.12.3 `put_code/1-2` | output character selected by character code | covered | output corpus |
241
+ | 8.12.3 `nl/0-1` | output implementation-defined new-line character | covered | output corpus |
242
+ | 8.12.3 argument errors | variable character/code or wrong character/integer type | covered | dedicated strict assertions |
243
+ | 8.12.3 stream errors | variable/invalid/missing stream; input stream; binary stream | covered | dedicated strict and constructed-stream assertions |
244
+ | 8.12.3 character-code representation | integer outside processor character-code set | covered | strict `put_code/1` boundary assertion |
245
+
246
+ ## 8.13 — byte input/output
247
+
248
+ | Clause / predicate | Prescribed row | Status | Executable evidence |
249
+ | --- | --- | --- | --- |
250
+ | 8.13.1 `get_byte/1-2` | input next byte or `-1` at EOF | covered | strict binary-stream success and byte corpus |
251
+ | 8.13.1 errors | variable stream; invalid fixed in-byte; invalid/missing/output stream; text stream; EOF-action conditions | covered | strict assertions and EOF stream corpus |
252
+ | 8.13.2 `peek_byte/1-2` | inspect next byte without changing position | covered | strict binary-stream success and byte corpus |
253
+ | 8.13.2 errors | corresponding fixed-byte, stream, text-stream, and EOF conditions | covered | strict assertions / shared byte-I/O implementation tests |
254
+ | 8.13.3 `put_byte/1-2` | output one byte to a binary stream | covered | strict binary-output success and byte corpus |
255
+ | 8.13.3 errors | variable byte/stream; non-byte value; invalid/missing/input stream; text stream | covered | dedicated strict assertions |
256
+
257
+ ## 8.14 — term input/output, operators, and character conversion
258
+
259
+ | Clause / predicate | Prescribed row | Status | Executable evidence |
260
+ | --- | --- | --- | --- |
261
+ | 8.14.1 `read_term/3`, `read_term/2`, `read/1-2` | read a term using the standardized read-option surface | covered | term-I/O corpus, Corrigendum 3 cases, strict success/error suite |
262
+ | 8.14.1 option/argument errors | stream/option instantiation, non-list options, invalid read option | covered | strict 8.14.1 individual-condition assertions |
263
+ | 8.14.1 stream/input errors | invalid/missing/output/binary stream, past-end state, invalid input character, syntax error | covered | strict and constructed-stream assertions; post-N289 STC #76 is pinned by invalid-character read regressions |
264
+ | 8.14.1 conditional finite-number/arity representation branches | limits tied to finite selected processor bounds | not applicable where bound is unbounded | EyeProlog selects unbounded integers/`max_arity`; the implementation-defined finite binary64 input limits are pinned separately, including post-N289 STC #73 `max_float` / `min_float` coverage |
265
+ | 8.14.1 Corrigendum options | `variables/1`, `variable_names/1`, `singletons/1` list traversal/unification behavior | covered | Corrigendum 3 metadata tests and STC #48 regression |
266
+ | 8.14.2 `write_term/3`, `write_term/2`, `write/1-2`, `writeq/1-2` | write terms under standardized write options | covered | term-I/O/write-back corpus and strict writer suite |
267
+ | 8.14.2 option/argument errors | stream/option instantiation, non-list options, invalid write option | covered | strict 8.14.2 assertions |
268
+ | 8.14.2 stream/output errors | invalid/missing/input/binary stream and character representation conditions | covered | strict and constructed-stream assertions |
269
+ | 8.14.2 contradictory write options | rightmost option applies | covered | writer option corpus |
270
+ | 8.14.3 `op/3` | add/remove operators subject to Part 1 restrictions | covered | operator corpus and preparation-time ordering tests |
271
+ | 8.14.3 errors | priority/specifier/name instantiation/type/domain plus protected-operator permission conditions | covered | strict `op/3` individual-condition suite |
272
+ | 8.14.4 `current_op/3` | enumerate/query current operator definitions | covered | operator corpus and strict success assertion |
273
+ | 8.14.4 errors | invalid priority/specifier/operator filters | covered | strict `current_op/3` individual-condition suite |
274
+ | 8.14.5 `char_conversion/2` | update character-conversion mapping | covered | preparation/runtime conversion tests and strict success assertion |
275
+ | 8.14.5 errors | input/output instantiation and processor-character representation conditions | covered | dedicated strict assertions |
276
+ | 8.14.6 `current_char_conversion/2` | enumerate/query current conversion mapping | covered | strict success assertion and conversion corpus |
277
+ | 8.14.6 errors | fixed input/output is not a character | covered | dedicated strict assertions |
278
+
105
279
  ## 8.15 — logic and control
106
280
 
107
281
  | Clause / predicate | Prescribed row | Status | Executable evidence |
@@ -190,16 +364,15 @@ built-in matrix does not have a gap at 8.17.
190
364
  | 8.17.4 variable status | instantiation error | covered | strict `halt(X)` |
191
365
  | 8.17.4 non-integer status | integer type error | covered | strict `halt(a)` |
192
366
 
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:
367
+ ## Remaining audit work
197
368
 
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.
369
+ The built-in **8.2-8.17 row audit is complete** at the level tracked by this
370
+ file: prescribed modes, success/failure behavior, individual error conditions,
371
+ and conditional/not-applicable processor branches all have explicit outcomes.
372
+ ISO 7.12 simultaneous-error selection remains an implementation-dependent
373
+ processor choice unless more specific normative text constrains it.
203
374
 
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`.
375
+ The remaining #65 work is therefore above/below this built-in table: Clause 6
376
+ text/rejection mapping, Clause 7 processor/control semantics, Clause 9
377
+ exceptional-value rows, residual Clause 5 dependency rows, and the external
378
+ conversion/variable-name conformance corpora.