eyeprolog 1.3.57 → 1.3.59

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
@@ -226,9 +226,22 @@ precedence for `read_term/3`, `write_term/3`, `op/3`, and `current_op/3`, and
226
226
  the complete Part 1 flag defaults/value domains/changeability rules. The
227
227
  ongoing issue #65 audit additionally tightens Prolog-text declaration ordering,
228
228
  initialization lifetime, stream/open/close behavior, character/byte stream
229
- errors, `keysort/2` error cases, and strict arithmetic-extension boundaries.
229
+ errors, `keysort/2` error cases, the now-closed 5.5.6 side-effect and 5.5.10 evaluable-functor strict
230
+ extension boundaries, and the standard's permitted arithmetic variation points: mixed-type Corrigendum 2
231
+ `max/2`/`min/2` and signed Clause 9.4 bitwise/shift behavior.
230
232
  Those additions improve the Part 1 surface without turning the remaining audit
231
- rows into a blanket conformance claim.
233
+ rows into a blanket conformance claim. The post-N289 WG17/STC working draft is
234
+ reviewed separately rather than treated as a fourth Corrigendum. The
235
+ 2026-08-23 draft items #73-#76 are now pinned explicitly: float input limits for
236
+ `read*` and `number_*` conversions, invalid-character term input, and the draft
237
+ proposal that would make the two power-underflow rows depend on the processor's
238
+ 9.1.4.2 `resultF` choice. Strict mode retains the published Part 1 +
239
+ Corrigenda 1-3 behavior where the draft has not been standardized.
240
+
241
+ As an additional 5.5.1 extension-safety gate, every vendored WG17 syntax case
242
+ that succeeds in the strict Part 1 reader is re-run in the normal profile and
243
+ must retain the same observable outcome; normal mode may accept extra extension
244
+ syntax, but it must not reinterpret text already accepted as standard syntax.
232
245
 
233
246
  The auditable processor-requirement checklist lives in
234
247
  [`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.57",
6
+ "version": "1.3.59",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
@@ -260,8 +260,8 @@ The Corrigendum 2 additions have the same shape.
260
260
  | --- | --- | --- | --- |
261
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
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 |
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`; finite binary64 input limits remain documented separately |
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
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
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
267
  | 8.14.2 option/argument errors | stream/option instantiation, non-list options, invalid write option | covered | strict 8.14.2 assertions |
@@ -27,24 +27,24 @@ relevant implementation-dependent overlap choice to an explicit audit outcome.
27
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. |
28
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. |
29
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. |
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. |
30
+ | 5.5 extension boundaries | audit | [ISO-PROCESSOR-REQUIREMENTS.md](ISO-PROCESSOR-REQUIREMENTS.md) splits 5.5 into its individual extension hooks. Operators, character conversion, the normal-profile host string type, directives, control constructs, flags, built-ins, evaluable functors, and reserved atoms now have explicit decisions. The remaining open extension work is now concentrated in exhaustive syntax-preservation (5.5.1); the Prolog-visible additional-side-effect boundary (5.5.6) is explicitly filtered in strict mode and host instrumentation is documented as an embedding concern. |
31
31
 
32
32
  ## Normative language families
33
33
 
34
34
  | Standard area | Status | Current evidence |
35
35
  | --- | --- | --- |
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. |
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. A cross-profile gate now verifies that every WG17 case accepted by the strict Part 1 reader has the same observable outcome under normal-mode syntax extensions. The implementation-defined 6.5/6.6 character-model decisions are closed; the remaining work is the explicit production-by-production shall map. |
37
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. |
38
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. |
39
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. Clause-body conversion now applies 7.6.2 both to prepared source clauses and runtime `asserta/1` / `assertz/1`: variables become `call/1`, `,/2`, `;/2`, and `->/2` are converted recursively, and head/body variable identity is preserved so `clause/2` and `retract/1` observe the converted term correctly. 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. |
40
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. |
41
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. |
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. |
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), the published `exp/1`/power underflow rows, and prescribed negative/zero power errors before later conversion overflow. Post-N289 STC #75's proposal to make the two power-underflow rows conditional on the 9.1.4.2 choice is tracked separately and is not silently substituted for the licensed baseline. 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. |
43
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 complete 8.11-8.14 built-in mode/error family is now row-audited in [ISO-BUILTIN-MODE-ERROR-MATRIX.md](ISO-BUILTIN-MODE-ERROR-MATRIX.md), including lifecycle/current-stream handling, stream positions, text/binary permissions, EOF actions, `force(true)`, input-code representation timing, term-I/O options, operators, and character conversion. `stream_property/2` now also distinguishes a malformed stream-term (domain error) from a valid stream-term whose stream is no longer open (ordinary failure). The higher-level 7.10 source/sink, buffering, positioning, and implementation-defined stream semantics remain under shall-by-shall audit. |
44
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. |
45
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 mode/error-condition outcomes across the complete 8.2-8.17 built-in family, including Corrigendum additions and not-applicable conditional branches. The audit records the 7.12 rule that simultaneous error conditions are implementation dependent: overlap regressions document stable EyeProlog choices rather than inventing a universal table-order requirement. Remaining work is concentrated in non-built-in Clause 7 semantics, Clause 9 exceptional interactions, and processor-level error conditions. |
46
46
  | 8.2-8.17 — built-in predicates | covered | [ISO-BUILTIN-MODE-ERROR-MATRIX.md](ISO-BUILTIN-MODE-ERROR-MATRIX.md) now records the complete Part 1 + Corrigenda built-in family at one row per prescribed mode, success/failure behavior, individual error condition, and conditional/not-applicable branch. The row audit exposed and fixed source/runtime 7.6.2 body conversion/variable sharing, strict host-string leakage, input-code error timing, `close/2` force handling, and closed-stream `stream_property/2` behavior. Simultaneous-error choices are documented as implementation dependent under 7.12 unless separately constrained. |
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. |
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 published `exp/1`, `**/2`, and Corrigendum 2 `^/2` underflow conditions, tracks STC #75's proposed conditional power interpretation without adopting it as published text, 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. Corrigendum 2 mixed-type `max/2`/`min/2` now have an explicit implementation-dependent decision: exact mathematical comparison selects one original operand and preserves its numeric type without mandatory integer-to-float conversion. Clause 9.4 signed bitwise operations and negative shift counts are likewise pinned to the documented BigInt infinite-two's-complement semantics. 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. |
48
48
  | Corrigendum 1 | covered | Double-quoted atom/operator-priority corrections have dedicated cases. |
49
49
  | Corrigendum 2 | covered | Added predicates/functors, catch corrections, bar/operator and uninstantiation corrections have dedicated cases. |
50
50
  | Corrigendum 3 | covered | Writer options, `variable_names/1`, canonical list output and negative-power corrections have dedicated cases. |
@@ -232,8 +232,18 @@ compound terms. EyeProlog therefore selects `unbounded` and does not invent a
232
232
  finite predicate/procedure ceiling merely to preserve the former 65535 value.
233
233
  STC #56, which became a WG17 action item, is implemented narrowly for database
234
234
  protection: `(:-)/1-2` are static/private for modification and `clause/2`
235
- access, while ordinary calls retain their separate existence behavior. Other
236
- draft/editorial or deliberately controversial proposals (for example deleting
235
+ access, while ordinary calls retain their separate existence behavior. The 2026-08-23 post-N289 draft adds four more executable review points. STC
236
+ #73 confirms the draft `max_float` / `min_float` representation-limit shape for
237
+ `read/1-2` and `read_term/2-3`; STC #74 extends the same distinction to
238
+ `number_chars/2` and `number_codes/2`; STC #76 proposes the
239
+ `representation_error(character)` already exercised by EyeProlog's invalid-UTF-8
240
+ read regressions. STC #75 is deliberately different: it proposes changing the
241
+ published power-underflow conditions so that they depend on the 9.1.4.2
242
+ `resultF` choice. Because that proposal is not a published Corrigendum, strict
243
+ mode keeps the licensed Part 1/Corrigendum 2 power errors while the draft
244
+ divergence is pinned and documented.
245
+
246
+ Other draft/editorial or deliberately controversial proposals (for example deleting
237
247
  standalone if-then) remain outside the Part 1 + Corrigenda 1-3 strict baseline
238
248
  until standardized or adopted as an explicit compatibility extension.
239
249
 
@@ -73,11 +73,11 @@ Status values are:
73
73
  | 8.17.3 | Other effects of `halt/0` | Terminates EyeProlog execution and returns host/process status `0`; it produces no Prolog solution. | **defined** — `HaltSignal`, `haltBuiltin()`, CLI/runner handling. |
74
74
  | 8.17.4 | Meaning/effects of `halt(Status)` | Integer `Status` is converted to the host process/runner halt code; it produces no Prolog solution. | **defined** — `haltBuiltin()`, `src/execute.js`, `src/cli.js`. |
75
75
  | 9.1.4.1 | Floating-point rounding function `rndF` | Floating values and operations use ECMAScript `Number` (IEEE-754 binary64) and the host's specified binary64 arithmetic/conversions. | **defined** — `src/iso-arithmetic.js`, `src/number-value.js`. |
76
- | 9.1.4.2 | Floating-point result function for operations whose result is governed only by `resultF` | EyeProlog chooses `round(x)` rather than the optional exceptional value `underflow`. ECMAScript binary64 arithmetic therefore preserves a representable subnormal result and rounds a still-smaller generic arithmetic result to `0.0`, consistently with float-token and `number_chars/2` input. This implementation-defined choice does not suppress the explicit mandatory underflow conditions stated separately for Part 1 `**/2`, `exp/1`, and Corrigendum 2 `^/2`. | **defined** — arithmetic evaluation in `src/iso-arithmetic.js` and parser/number conversion; strict exceptional-value regressions. |
76
+ | 9.1.4.2 | Floating-point result function for operations whose result is governed only by `resultF` | EyeProlog chooses `round(x)` rather than the optional exceptional value `underflow`. ECMAScript binary64 arithmetic therefore preserves a representable subnormal result and rounds a still-smaller generic arithmetic result to `0.0`, consistently with float-token, `number_chars/2`, and `number_codes/2` input. The published Part 1 `**/2` and Corrigendum 2 `^/2` tables still contain separate unconditional underflow rows, which strict mode retains; post-N289 STC #75 proposes making those two power rows conditional on this `resultF` choice and is tracked separately as draft work. `exp/1` has its own published exceptional condition and is not the subject of STC #75. | **defined** — arithmetic evaluation in `src/iso-arithmetic.js` and parser/number conversion; strict exceptional-value regressions. |
77
77
  | 9.1.4.3 | Approximate-addition function | ECMAScript binary64 addition is used; subtraction is implemented through the corresponding host operation and all finite results remain binary64 values. | **defined** — arithmetic evaluation in `src/iso-arithmetic.js`. |
78
- | 9.4 | Representation of negative integers for bitwise operations | BigInt's unbounded signed binary semantics are used, equivalent to an infinite two's-complement sign extension for bitwise operations. | **defined** — `src/iso-arithmetic.js` BigInt bitwise operators. |
79
- | 9.4.1 | Right shift of negative integers and unusual shift counts | `>>` is arithmetic/sign-propagating. A negative count reverses direction according to JavaScript BigInt shift semantics; there is no finite integer bit-size ceiling in the Prolog model. | **defined** — `a >> b` in `src/iso-arithmetic.js`. |
80
- | 9.4.2 | Left shift of negative integers and unusual shift counts | `<<` uses BigInt signed shift semantics; a negative count reverses direction. Resource exhaustion remains possible for very large results. | **defined** — `a << b` in `src/iso-arithmetic.js`. |
78
+ | 9.4 | Representation of negative integers for bitwise operations | BigInt's unbounded signed binary semantics are used, equivalent to an infinite two's-complement sign extension for bitwise operations. | **defined** — `src/iso-arithmetic.js` BigInt bitwise operators and strict Clause 9.4 boundary regressions. |
79
+ | 9.4.1 | Right shift of negative integers and unusual shift counts | `>>` is arithmetic/sign-propagating. A negative count reverses direction according to JavaScript BigInt shift semantics; there is no finite integer bit-size ceiling in the Prolog model. | **defined** — `a >> b` in `src/iso-arithmetic.js`; strict regressions pin negative operands/counts. |
80
+ | 9.4.2 | Left shift of negative integers and unusual shift counts | `<<` uses BigInt signed shift semantics; a negative count reverses direction. Resource exhaustion remains possible for very large results. | **defined** — `a << b` in `src/iso-arithmetic.js`; strict regressions pin negative counts and resource exhaustion. |
81
81
  | 9.4.3 | Bitwise AND with negative operands | BigInt infinite-two's-complement semantics. | **defined** — `a & b`. |
82
82
  | 9.4.4 | Bitwise OR with negative operands | BigInt infinite-two's-complement semantics. | **defined** — `a \| b`. |
83
83
  | 9.4.5 | Bitwise complement | BigInt complement, i.e. `~N = -N-1`. | **defined** — `~a`. |
@@ -93,13 +93,16 @@ subnormal values remain non-zero and still-smaller generic results round to
93
93
  `0.0`. Float-token and `number_chars/2` input use the same finite-double
94
94
  rounding policy.
95
95
 
96
- That generic implementation-defined choice is distinct from evaluable functors
97
- whose own error clauses prescribe underflow. In strict mode, `exp/1`, Part 1
98
- `**/2`, and Corrigendum 2 `^/2` therefore raise
99
- `evaluation_error(underflow)` when their mathematical non-zero result falls
100
- below the representable range and the host would otherwise round it to zero.
101
- The regression suite pins both sides of this boundary, while
102
- `stc/float_underflow_input` covers the related input conversion policy.
96
+ That generic implementation-defined choice is distinct from the currently
97
+ published evaluable-functor error clauses. In the licensed Part 1 + Corrigenda
98
+ baseline, `exp/1`, Part 1 `**/2`, and Corrigendum 2 `^/2` have explicit
99
+ underflow conditions, so strict mode raises `evaluation_error(underflow)` when
100
+ those published conditions are met. The 2026-08-23 post-N289 draft adds STC
101
+ #75, proposing that the two *power* underflow rows apply only when the processor
102
+ selects `underflow` in 9.1.4.2. EyeProlog records that proposal but does not
103
+ silently replace the published strict baseline with it. The regression suite
104
+ pins both the published power behavior and the generic round-to-zero choice;
105
+ `stc/float_underflow_input` covers the separate input-conversion policy.
103
106
 
104
107
  ## Implementation-specific features required to be documented by 5.4
105
108
 
@@ -115,11 +118,11 @@ families; `--iso-strict` is intended to remove their Part 1 interpretation.
115
118
  | 5.5.3 Character-conversion mapping | No non-identity initial `Convc` extension | Identity initial mapping. |
116
119
  | 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
120
  | 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
- | 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. |
121
+ | 5.5.6 Side effects | Normal mode adds `statistics/0-2`, cleanup/library state, and optional proof/statistics host instrumentation. | The strict registry excludes Prolog-visible statistics/cleanup/library adapters; ordinary Part 1 I/O/database/flag/operator side effects remain. Host proof/statistics collection observes execution through the embedding API rather than adding a strict Prolog goal effect. **covered** — strict registry/execution regression. |
119
122
  | 5.5.7 Control constructs | `tnot/1` and normal-profile execution optimizations | `tnot/1` is absent; automatic tabling/recursion guards are disabled. |
120
123
  | 5.5.8 Flags | `occurs_check` | Absent in strict mode. |
121
124
  | 5.5.9 Built-in predicates | EyeProlog libraries, CLP(Z), statistics, Part 3 `phrase/2-3`, and interop autoloaded predicates | Strict registry contains only the Part 1 + Corrigenda core registry. |
122
- | 5.5.10 Evaluable functors | Library/runtime extensions when registered outside the strict core | Strict arithmetic registry is the Part 1 + Corrigenda set. |
125
+ | 5.5.10 Evaluable functors | Normal mode additionally accepts the EyeProlog evaluable atom `e`; the remaining arithmetic functors accepted by strict mode are the Part 1 + Corrigenda set. | Strict mode rejects `e/0` as non-evaluable and retains the Corrigendum arithmetic additions. **covered** — strict extension-boundary regression plus `src/iso-arithmetic.js`. |
123
126
  | 5.5.11 Reserved atoms | None | None. |
124
127
  | Cor.3 5.5.12 Options | Extra library/host options may exist outside core option lists | Normal mode additionally accepts the EyeProlog `write_term/2-3` option `double_quotes(true|false)`. Strict core excludes that extension and accepts only the Part 1 plus Corrigendum 3 write-option surface; unknown extension options raise `domain_error(write_option,...)`. |
125
128
 
@@ -136,6 +139,7 @@ For portability work, EyeProlog also documents these choices even though the
136
139
  - distinct variables are ordered by first encounter within the operation that
137
140
  needs a stable ordering;
138
141
  - expression arguments are evaluated left-to-right by the JavaScript evaluator;
142
+ - Corrigendum 2 mixed integer/float `max/2` and `min/2` use exact mathematical comparison and return one of the original operands without forcing integer-to-float conversion; equal values choose the left operand, so its original numeric type is preserved. EyeProlog does not elect the optional mixed-type `undefined` or `float_overflow` outcomes solely because the operand types differ;
139
143
  - generated variable names use EyeProlog's stable `_A`, `_B`, ... style within
140
144
  a write operation/top-level query;
141
145
  - resource and syntax-error detail atoms are EyeProlog implementation details;
@@ -8,7 +8,7 @@ compliance audit and the remaining work before a full conformance claim.
8
8
 
9
9
  | Standard area | Implementation | Representative executable coverage |
10
10
  | --- | --- | --- |
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 |
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, and a cross-profile check that all strict-success WG17 cases retain the same normal-profile outcome |
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
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 |
@@ -18,7 +18,7 @@ compliance audit and the remaining work before a full conformance claim.
18
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
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
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
- | 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 |
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, published `exp/1`/`**/2`/`^/2` underflow (with post-N289 STC #75 tracked separately), Part 1 mixed integer/float comparison conversion, the pinned Corrigendum 2 mixed-type `max/2`/`min/2` implementation-dependent choice, documented Clause 9.4 signed bitwise/negative-shift semantics, 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 |
24
24
 
@@ -35,16 +35,16 @@ certification claim.
35
35
  | Requirement | Status | EyeProlog decision / evidence |
36
36
  | --- | --- | --- |
37
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 |
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. Every vendored WG17 case that succeeds in the Part 1 strict reader now has an identical normal-profile observable outcome, directly checking that normal syntax extensions do not reinterpret accepted standard text. A complete one-row preservation map for every Clause 6 production remains open |
39
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
40
  | 5.5.3 initial character-conversion mapping | covered | identity initial mapping; user changes are exercised through preparation/execution `char_conversion/2` behavior |
41
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
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 |
43
+ | 5.5.6 additional side effects | covered | normal-profile `statistics/0-2`, cleanup/library state, proof/statistics host instrumentation, and other extension effects are documented outside the Part 1 core; strict registry tests exclude the Prolog-visible statistics/cleanup extensions. Host instrumentation is an embedding observation rather than an extra strict Prolog goal effect |
44
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
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
46
  | 5.5.9 additional built-in predicates and error forms | covered | strict registry excludes normal-profile library/native additions; `ISO-BUILTIN-MODE-ERROR-MATRIX.md` closes the complete 8.2-8.17 family row-by-row; simultaneous-error choices are documented per 7.12 rather than treated as a global table-order mandate |
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 |
47
+ | 5.5.10 additional evaluable functors/types | covered | strict mode rejects the normal-profile evaluable `e` extension while retaining the full Part 1 + Corrigenda arithmetic set. The 9.1.4.2 `resultF` choice, Corrigendum 2 mixed-type `max/2`/`min/2` choice, and Clause 9.4 signed bitwise/shift choices are documented and pinned by strict tests. Post-N289 STC #75 remains separate from the published baseline; the remaining Clause 9 semantic/error audit is tracked under 5.1(b)/5.3 rather than as an open extension-boundary decision |
48
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
49
 
50
50
  ## Exit-use rule
@@ -15,7 +15,7 @@ tracks the row-by-row built-in audit, while
15
15
  Clause 5 processor obligations. [WG17-SYNTAX-STATUS.md](WG17-SYNTAX-STATUS.md) records the
16
16
  complete one-to-one trace for the vendored active upstream WG17 syntax cases.
17
17
  [STC-DRAFT-STATUS.md](STC-DRAFT-STATUS.md) separately tracks executable
18
- implementation questions from the post-N289 working draft; those cases are
18
+ implementation questions from the post-N289 working draft (reviewed through the 2026-08-23 items #73-#76); those cases are
19
19
  review evidence, not normative ISO claims.
20
20
 
21
21
  “Conformance” here means conformance to EyeProlog's documented ISO compatibility
@@ -141,8 +141,8 @@ Selected cases are adapted from the ISO and standard-core suites of Logtalk,
141
141
  Scryer Prolog, Trealla Prolog, and SWI-Prolog. Their upstream identifiers and licenses
142
142
  are recorded in [THIRD_PARTY.md](THIRD_PARTY.md).
143
143
 
144
- The corpus has 386 cases in `iso/` and 800 file-based conformance cases in
145
- total. Of those, 9 cases in `stc/` are explicitly labelled working-draft
144
+ The corpus has 386 cases in `iso/` and 802 file-based conformance cases in
145
+ total. Of those, 11 cases in `stc/` are explicitly labelled working-draft
146
146
  review evidence rather than normative ISO claims. The separate strict-reader WG17 matrix has 366 executable dispositions.
147
147
  The generated `conformance-report.md` is the authoritative source for current
148
148
  category totals. Together with regression, documentation-sync, API, example,
@@ -39,7 +39,11 @@ regressions.
39
39
  | [#69](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#69) | arithmetic example culprit | **Found a #65 gap.** Strict expression evaluation now applies 7.9.2(c) to an atomic subexpression such as `foo`: it reports `type_error(evaluable,foo/0)` rather than the misleading `type_error(number,foo)` shown by the old 9.1.7 example. |
40
40
  | [#70](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#70) | optional `max_procedure_arity` | Reviewed after issue #66 corrected the earlier #71 pointer. EyeProlog has no declared procedure-arity limit smaller than its `max_arity=unbounded` term model, so the implementation-defined optional flag is intentionally absent. |
41
41
  | [#72](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#72) | tentative non-ground stream-term instances | Reviewed as a tentative post-2026 proposal. EyeProlog does not make this draft wording normative in `--iso-strict`; the published Part 1 stream-term/domain rules remain the baseline until WG17 settles the proposal. |
42
- | [issue #54](https://github.com/eyereasoner/eyeprolog/issues/54) | float-reading limits (historically discussed as a later STC item) | Positive/negative literal and `number_chars/2` overflow are kept as explicit draft-facing cases; underflow is tested separately. The current public STC page ends at #72, so this is no longer labelled as a current `stc#73` entry. See the note below. |
42
+ | [#73](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#73) | `read/1-2` and `read_term/2-3` representation limits | The 2026-08-23 post-N289 draft now explicitly proposes `max_float` / `min_float` alongside the existing representation-limit flags. EyeProlog already reports those errors while reading overflowing positive/negative float tokens; strict regression coverage now exercises both `read/1` and `read_term/2`, and the literal STC cases pin parser preparation. |
43
+ | [#74](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#74) | `number_chars/2` and `number_codes/2` representation limits | Positive and negative overflow now have explicit draft-facing cases for both conversion predicates, reporting `representation_error(max_float)` / `representation_error(min_float)` rather than a syntax error. |
44
+ | [#75](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#75) | power underflow versus the 9.1.4.2 `resultF` choice | The draft proposes making the Part 1 `**/2` and Corrigendum 2 `^/2` underflow rows conditional on the implementation-defined `resultF` underflow choice. EyeProlog's published-baseline strict mode intentionally retains the currently published unconditional power-underflow clauses; its separate 9.1.4.2 choice remains `round(x)`. This proposal is tracked, not silently adopted. |
45
+ | [#76](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#76) | `read/1-2` and `read_term/2-3` invalid input entity | Existing file-stream regressions already require `representation_error(character)` for invalid UTF-8 through all four read/read_term arities. The regression is now cross-referenced to the new draft item. |
46
+ | [issue #54](https://github.com/eyereasoner/eyeprolog/issues/54) | float input range and underflow background | The earlier EyeProlog issue remains useful background for finite-double input overflow/underflow. Its overflow cases are now tracked by STC #73/#74; input underflow remains a separate implementation-profile check and must not be conflated with STC #75's power-underflow proposal. |
43
47
 
44
48
  ## Float-reading note from issue #54
45
49
 
@@ -54,13 +58,13 @@ record the behavior discussed in issue #54:
54
58
  - overflow produced by arithmetic evaluation remains
55
59
  `evaluation_error(float_overflow)`.
56
60
 
57
- These `max_float` / `min_float` names are treated as a **provisional draft
58
- extension**, not as a claim about the currently published ISO core standard.
61
+ These `max_float` / `min_float` names are treated as a **draft-facing representation-limit extension**, now explicitly reflected by post-N289 STC #73/#74, not as a claim that the currently published ISO core standard already contains those flag names.
59
62
  The earlier WG17 float-update material also uses
60
- `representation_error(float_overflow)` for unsupported infinity input, while a
61
- separate reading-floats proposal discusses `max_float` for a finite value above
62
- the implementation's largest finite float. Those cases should remain distinct
63
- when WG17 settles the wording.
63
+ `representation_error(float_overflow)` for unsupported infinity input, while
64
+ STC #73/#74 use `max_float` / `min_float` for a finite numeric text that lies
65
+ outside the implementation's finite float range. Those cases should remain
66
+ distinct when WG17 settles the wording. STC #75 is independent again: it asks
67
+ how power underflow relates to the implementation-defined `resultF` choice.
64
68
 
65
69
  Relevant background:
66
70
 
@@ -1,6 +1,8 @@
1
- % Float-reading draft review associated with STC #73 / issue #54. EyeProlog's
2
- % finite-double profile permits input underflow to round to zero.
3
- % https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#73
1
+ % Issue #54 input-underflow review. EyeProlog's finite-double profile permits
2
+ % input underflow to round to zero. This is distinct from STC #75, which asks
3
+ % whether the published power-underflow errors should depend on the 9.1.4.2
4
+ % resultF choice.
5
+ % https://github.com/eyereasoner/eyeprolog/issues/54
4
6
  %% goal: float_underflow_input
5
7
 
6
8
  float_underflow_input :-
@@ -1,3 +1,3 @@
1
- % Draft float-reading limit review associated with STC #73 / issue #54.
2
- % https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#73
1
+ % STC #74: number_chars/2 reports the draft floating representation limit.
2
+ % https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#74
3
3
  %% goal: number_chars(_, "-1.0e99999")
@@ -0,0 +1,4 @@
1
+ % STC #74: number_codes/2 should report the same implementation-defined
2
+ % floating representation limit as number_chars/2.
3
+ % https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#74
4
+ %% goal: number_codes(_, [45,49,46,48,101,57,57,57,57,57])
@@ -1,3 +1,3 @@
1
- % Draft float-reading limit review associated with STC #73 / issue #54.
2
- % https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#73
1
+ % STC #74: number_chars/2 reports the draft floating representation limit.
2
+ % https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#74
3
3
  %% goal: number_chars(_, "1.0e99999")
@@ -0,0 +1,4 @@
1
+ % STC #74: number_codes/2 should report the same implementation-defined
2
+ % floating representation limit as number_chars/2.
3
+ % https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#74
4
+ %% goal: number_codes(_, [49,46,48,101,57,57,57,57,57])
@@ -0,0 +1 @@
1
+ error(representation_error(min_float))
@@ -0,0 +1 @@
1
+ error(representation_error(max_float))
@@ -8,6 +8,7 @@ import {
8
8
  Env,
9
9
  compound,
10
10
  createStrictIsoRegistry,
11
+ getEyePrologRegistry,
11
12
  parseGoalText,
12
13
  parseProgramText,
13
14
  run,
@@ -614,6 +615,29 @@ export function runIsoStrict(reporter = new TestReporter()) {
614
615
  }
615
616
  });
616
617
 
618
+ reporter.test('tracks post-N289 STC draft items 73-76 without replacing the published baseline', () => {
619
+ for (const [goal, input, expected, label] of [
620
+ ['read(X)', '1.0e99999.\n', 'representation_error(max_float)', 'STC #73 read/1 max_float'],
621
+ ['read_term(X,[])', '-1.0e99999.\n', 'representation_error(min_float)', 'STC #73 read_term/2 min_float'],
622
+ ]) {
623
+ equal(capture(() => run('', { isoStrict: true, goal, ioOptions: { input } })).formal, expected, label);
624
+ }
625
+
626
+ for (const [goal, expected, label] of [
627
+ ["number_chars(N,['1','.','0',e,'9','9','9','9','9'])", 'representation_error(max_float)', 'STC #74 number_chars/2'],
628
+ ['number_codes(N,[45,49,46,48,101,57,57,57,57,57])', 'representation_error(min_float)', 'STC #74 number_codes/2'],
629
+ ]) equal(capture(() => run('', { isoStrict: true, goal })).formal, expected, label);
630
+
631
+ // STC #75 proposes conditioning the published **/2 and Corrigendum 2 ^/2
632
+ // underflow rows on the implementation-defined 9.1.4.2 resultF choice.
633
+ // Until that proposal is standardized, strict mode keeps the published
634
+ // unconditional error clauses; the draft divergence is documented.
635
+ equal(capture(() => run('', { isoStrict: true, goal: 'X is 2.0 ** -1075.0' })).formal,
636
+ 'evaluation_error(underflow)', 'published **/2 underflow baseline');
637
+ equal(capture(() => run('', { isoStrict: true, goal: 'X is 2.0 ^ -1075.0' })).formal,
638
+ 'evaluation_error(underflow)', 'published ^/2 underflow baseline');
639
+ });
640
+
617
641
  reporter.test('reports explicit transcendental and power underflow in strict mode', () => {
618
642
  for (const [goal, label] of [
619
643
  ['X is exp(-1000.0)', 'exp/1 underflow'],
@@ -650,6 +674,72 @@ export function runIsoStrict(reporter = new TestReporter()) {
650
674
  'evaluation_error(undefined)', 'zero-negative power precedes exponent conversion overflow');
651
675
  });
652
676
 
677
+ reporter.test('closes the 5.5.6 additional side-effect extension boundary', () => {
678
+ const normalRegistry = getEyePrologRegistry();
679
+ const strictRegistry = createStrictIsoRegistry();
680
+ equal(Boolean(normalRegistry.get('statistics', 0)), true, 'normal statistics/0 side-effect extension');
681
+ equal(Boolean(strictRegistry.get('statistics', 0)), false, 'strict statistics/0 excluded');
682
+ equal(Boolean(strictRegistry.get('statistics', 2)), false, 'strict statistics/2 excluded');
683
+ equal(Boolean(strictRegistry.get('call_cleanup', 2)), false, 'strict cleanup extension excluded');
684
+ equal(Boolean(strictRegistry.get('setup_call_cleanup', 3)), false, 'strict setup/cleanup extension excluded');
685
+ equal(capture(() => run('', { isoStrict: true, goal: 'statistics' })).formal,
686
+ 'existence_error(procedure)', 'strict execution cannot invoke statistics/0');
687
+ });
688
+
689
+ reporter.test('closes the 5.5.10 evaluable-functor extension boundary', () => {
690
+ const strictExtension = capture(() => run('', { isoStrict: true, goal: 'X is e' }));
691
+ equal(strictExtension.formal, 'type_error(evaluable)', 'normal-only e/0 is not evaluable in strict mode');
692
+ equal(run('', { goal: 'X is e' }).stats.completed_goal_lists, 1, 'normal e/0 extension remains available');
693
+
694
+ for (const goal of [
695
+ 'X is +1',
696
+ 'X is -7 div 3',
697
+ 'X is max(2,3.0)',
698
+ 'X is min(2.0,3)',
699
+ 'X is 3^3',
700
+ 'X is asin(0)',
701
+ 'X is acos(1)',
702
+ 'X is atan2(1,0)',
703
+ 'X is tan(0)',
704
+ 'X is pi',
705
+ 'X is xor(10,12)',
706
+ ]) equal(run('', { isoStrict: true, goal }).stats.completed_goal_lists, 1, `Corrigendum evaluable ${goal}`);
707
+ });
708
+
709
+ reporter.test('pins Corrigendum 2 mixed-type max/min implementation-dependent behavior', () => {
710
+ for (const goal of [
711
+ 'X is max(2.0,3), X == 3',
712
+ 'X is max(2,3.0), X == 3.0',
713
+ 'X is min(2.0,3), X == 2.0',
714
+ 'X is min(2,3.0), X == 2',
715
+ 'X is max(0,0.0), X == 0',
716
+ 'X is max(0.0,0), X == 0.0',
717
+ ]) equal(run('', { isoStrict: true, goal }).stats.completed_goal_lists, 1, goal);
718
+
719
+ // Corrigendum 2 permits several mixed-type choices, including returning
720
+ // one of the original operands without converting the integer to float.
721
+ // EyeProlog compares the mathematical values exactly and preserves the
722
+ // selected operand's original type, so a huge integer does not create a
723
+ // float-overflow merely because the other operand is a float.
724
+ const huge = `1${'0'.repeat(400)}`;
725
+ equal(run('', { isoStrict: true, goal: `X is max(${huge},1.0), X == ${huge}` }).stats.completed_goal_lists,
726
+ 1, 'mixed max/2 does not force integer-to-float conversion');
727
+ equal(run('', { isoStrict: true, goal: `X is min(${huge},1.0), X == 1.0` }).stats.completed_goal_lists,
728
+ 1, 'mixed min/2 preserves selected float operand');
729
+ });
730
+
731
+ reporter.test('pins implementation-defined Clause 9.4 signed bitwise and shift behavior', () => {
732
+ for (const goal of [
733
+ 'X is ((-16) >> 2), X == -4',
734
+ 'X is (16 >> -2), X == 64',
735
+ 'X is (16 << -2), X == 4',
736
+ 'X is ((-1) /\\ 5), X == 5',
737
+ 'X is ((-1) \\/ 5), X == -1',
738
+ 'X is xor(-1,5), X == -6',
739
+ 'X is \\ 5, X == -6',
740
+ ]) equal(run('', { isoStrict: true, goal }).stats.completed_goal_lists, 1, goal);
741
+ });
742
+
653
743
  reporter.test('uses the Part 1 mixed arithmetic comparison operations', () => {
654
744
  // 8.7 converts the integer operand to float in mixed comparisons.
655
745
  equal(run('', { isoStrict: true, goal: '18014398509481985 =:= 18014398509481984.0' }).stats.completed_goal_lists,
@@ -845,6 +845,29 @@ c4 ?- call((!;1)).
845
845
  }
846
846
  assertEqual(readError?.formal, 'representation_error(max_float)', 'read/1 overflow');
847
847
 
848
+ let readTermError = null;
849
+ try {
850
+ runEyeProlog('', {
851
+ goal: 'read_term(X, [])',
852
+ ioOptions: { input: '-1.0e99999.\n' },
853
+ });
854
+ } catch (error) {
855
+ readTermError = error;
856
+ }
857
+ assertEqual(readTermError?.formal, 'representation_error(min_float)', 'read_term/2 overflow (STC #73)');
858
+
859
+ const numberCodes = createDefaultRegistry().get('number_codes', 2).handler;
860
+ let numberCodesError = null;
861
+ try {
862
+ numberCodes({
863
+ goal: compound('number_codes', [variable('N'), listFromItems(Array.from('1.0e99999', (c) => numberTerm(String(c.codePointAt(0))))) ]),
864
+ env: new Env(),
865
+ }).next();
866
+ } catch (error) {
867
+ numberCodesError = error;
868
+ }
869
+ assertEqual(numberCodesError?.formal, 'representation_error(max_float)', 'number_codes positive overflow (STC #74)');
870
+
848
871
  const isHandler = createDefaultRegistry().get('is', 2).handler;
849
872
  let hostTermError = null;
850
873
  try {
@@ -1222,21 +1245,34 @@ c4 ?- call((!;1)).
1222
1245
  run: () => {
1223
1246
  const filename = path.join(testRoot, 'fixtures', 'prologue_quad_runner.pl');
1224
1247
  const source = fs.readFileSync(filename, 'utf8');
1225
- const result = publicApi.runQuads(Program.parseSources([{
1248
+ const program = Program.parseSources([{
1226
1249
  text: source,
1227
1250
  filename,
1228
1251
  baseDir: path.dirname(filename),
1229
- }]));
1230
- assertEqual(result.total, 33, 'quad total');
1252
+ }]);
1253
+ assertEqual(program.quads.length, 33, 'vendored quad total');
1254
+ const maxIntegerQuads = program.quads.filter(({ query }) =>
1255
+ termToString(query).includes('current_prolog_flag(max_integer, Max)'));
1256
+ assertEqual(maxIntegerQuads.length, 1, 'max_integer quad count');
1257
+
1258
+ // This regression is about the one deliberate ISO divergence in the
1259
+ // upstream Prologue fixture. Running all 33 records also explores two
1260
+ // intentionally non-terminating STO examples and used to dominate the
1261
+ // regression suite by several seconds, without adding evidence for
1262
+ // max_integer. Keep the full vendored corpus intact, but execute only
1263
+ // the relevant record here.
1264
+ program.quads = maxIntegerQuads;
1265
+ const result = publicApi.runQuads(program);
1231
1266
  // The upstream working-draft quad accepts either integer overflow or
1232
1267
  // Max=unbounded. ISO/IEC 13211-1 7.11.1.1 instead says that when
1233
1268
  // bounded=false, current_prolog_flag(max_integer, N) fails. Preserve
1234
1269
  // the upstream fixture unchanged and make that one deliberate
1235
1270
  // standards-driven divergence explicit in the regression gate.
1236
- assertEqual(result.passed, 32, 'quad passed');
1271
+ assertEqual(result.total, 1, 'quad total');
1272
+ assertEqual(result.passed, 0, 'quad passed');
1237
1273
  assertEqual(result.failed, 1, 'quad failed');
1238
1274
  assertIncludes(result.stdout, 'current_prolog_flag(max_integer, Max)', 'max_integer divergence');
1239
- assertIncludes(result.stdout, 'quads: 33 run, 32 passed, 1 failed.', 'quad report');
1275
+ assertIncludes(result.stdout, 'quads: 1 run, 0 passed, 1 failed.', 'quad report');
1240
1276
  },
1241
1277
  },
1242
1278
  {
@@ -3323,7 +3359,7 @@ child.stdin.write(\`consult(${consultedAtom}).\\n\`);
3323
3359
  },
3324
3360
  },
3325
3361
  {
3326
- name: 'read and read_term report invalid UTF-8 as representation_error(character) (issue #64)',
3362
+ name: 'read and read_term report invalid UTF-8 as representation_error(character) (issue #64, STC #76)',
3327
3363
  run: () => {
3328
3364
  const invalidPath = path.join(tmp, `read-invalid-utf8-${++tmpCounter}.bin`);
3329
3365
  fs.writeFileSync(invalidPath, Buffer.from([0xff]));
@@ -3508,6 +3544,21 @@ function documentationSyncCases() {
3508
3544
  );
3509
3545
  },
3510
3546
  },
3547
+ {
3548
+ name: 'normal syntax extensions preserve successful WG17 Part 1 outcomes',
3549
+ run: () => {
3550
+ const fixture = readWg17SyntaxFixture();
3551
+ let checked = 0;
3552
+ for (const item of fixture.cases) {
3553
+ const strict = executeWg17Item(item);
3554
+ if (strict.type !== 'success') continue;
3555
+ const normal = executeWg17Item(item, { isoStrict: false });
3556
+ assertEqual(JSON.stringify(normal), JSON.stringify(strict), `WG17 #${item.id} cross-profile outcome`);
3557
+ checked++;
3558
+ }
3559
+ if (checked === 0) throw new Error('WG17 fixture has no successful Part 1 syntax cases');
3560
+ },
3561
+ },
3511
3562
  {
3512
3563
  name: 'WG17 stream-sensitive cases #270 and #271 follow the upstream input protocol',
3513
3564
  run: () => {
package/test/run-wg17.mjs CHANGED
@@ -33,13 +33,13 @@ function capturedStages(stdout) {
33
33
  .map((match) => ({ output: match[2], variables: match[3] }));
34
34
  }
35
35
 
36
- function executeFinite(item) {
36
+ function executeFinite(item, isoStrict = true) {
37
37
  try {
38
38
  // Match the upstream protocol: the Query cell plus its terminating newline
39
39
  // is input to read(G), G (and subsequent read/call stages when present).
40
40
  // This is essential for stream-sensitive cases such as #270 and #271.
41
41
  const result = run('', {
42
- isoStrict: true,
42
+ isoStrict,
43
43
  goal: runnerStage(1, item.readCount ?? 16),
44
44
  ioOptions: { input: `${item.input}\n` },
45
45
  });
@@ -50,10 +50,10 @@ function executeFinite(item) {
50
50
  }
51
51
  }
52
52
 
53
- function executeWait(item) {
54
- const program = Program.parse('', { isoStrict: true });
53
+ function executeWait(item, isoStrict = true) {
54
+ const program = Program.parse('', { isoStrict });
55
55
  const solver = new Solver(program, {
56
- isoStrict: true,
56
+ isoStrict,
57
57
  ioOptions: { input: `${item.input}\n` },
58
58
  });
59
59
  const stream = solver.io.resolve('user_input');
@@ -63,7 +63,7 @@ function executeWait(item) {
63
63
  return null;
64
64
  };
65
65
  const goal = parseGoalText('read_term(G, [])', {
66
- isoStrict: true,
66
+ isoStrict,
67
67
  operatorDefinitions: [...program.operators.values()],
68
68
  });
69
69
  try {
@@ -310,10 +310,11 @@ export function wg17TestDescription(item) {
310
310
  return `#${item.id} ${query} -> ${expected}`;
311
311
  }
312
312
 
313
- export function executeWg17Item(item) {
313
+ export function executeWg17Item(item, options = {}) {
314
+ const isoStrict = options.isoStrict ?? true;
314
315
  return usesWaitMatcher(item.expected) || item.outcome?.type === 'waits'
315
- ? executeWait(item)
316
- : executeFinite(item);
316
+ ? executeWait(item, isoStrict)
317
+ : executeFinite(item, isoStrict);
317
318
  }
318
319
 
319
320
  function assertOutcome(item) {
@@ -5495,9 +5495,15 @@ The long catalogs are meant to be entered locally, not memorized linearly.
5495
5495
  ## 38. Language and ISO profile
5496
5496
 
5497
5497
  The normative strict-core baseline is ISO/IEC 13211-1:1995, as corrected by
5498
- Technical Corrigenda 1:2007, 2:2012, and 3:2017. Normal EyeProlog additionally
5499
- provides a practical module interface aligned with later WG17 module amendment
5500
- work and a definite-clause-grammar profile following ISO/IEC TS 13211-3. Those
5498
+ Technical Corrigenda 1:2007, 2:2012, and 3:2017. The post-N289 WG17/STC
5499
+ working draft is used as defect-discovery input, not as an unpublished fourth
5500
+ Corrigendum. The 2026-08-23 draft through items #73-#76 is tracked by
5501
+ `test/conformance/STC-DRAFT-STATUS.md`; where a proposal changes published
5502
+ semantics, such as #75's conditional power-underflow proposal, strict mode keeps
5503
+ the licensed baseline until the change is standardized or explicitly adopted as
5504
+ a compatibility extension. Normal EyeProlog additionally provides a practical
5505
+ module interface aligned with later WG17 module amendment work and a
5506
+ definite-clause-grammar profile following ISO/IEC TS 13211-3. Those
5501
5507
  normal-mode profiles are documented and tested compatibility surfaces; they are
5502
5508
  not currently claimed as complete clause-by-clause certifications of Part 2 or
5503
5509
  Part 3.
@@ -7590,8 +7596,14 @@ gaps without turning draft WG17/STC proposals into the licensed baseline.
7590
7596
  upstream syntax cases. Reviewed cases can pin exact strict-reader outcomes, while
7591
7597
  newly upgraded cases execute directly against the upstream Codex expectation.
7592
7598
 
7599
+ The syntax audit also cross-checks extension safety: each vendored WG17 case
7600
+ accepted by the strict Part 1 reader is executed through the normal profile and
7601
+ must preserve the same observable outcome. Additional normal-mode syntax may
7602
+ accept texts outside the strict grammar, but it may not reinterpret an accepted
7603
+ standard case.
7604
+
7593
7605
  The complete suite must pass before release. The file-based conformance corpus
7594
- contains 800 cases, including 386 focused ISO
7606
+ contains 802 cases, including 386 focused ISO
7595
7607
  cases derived from the success, failure, mode, and error behavior in
7596
7608
  ISO/IEC 13211-1 clauses 7 and 8, Part 2 modules, and Part 3 grammar rules.
7597
7609
  Separate exact-output suites check 210 normal
@@ -7657,8 +7669,12 @@ strict boundary. The continuing issue #65 audit also enforces Part 1
7657
7669
  preparation constraints for `dynamic/1`, `multifile/1`, and `discontiguous/1`,
7658
7670
  keeps initialization tied to preparation of a program, tightens stream
7659
7671
  creation/position/EOF behavior and text-vs-binary permission errors, corrects
7660
- Corrigendum 2 `keysort/2` error cases, and keeps the non-standard evaluable atom
7661
- `e` outside strict mode. These are conformance improvements, while the
7672
+ Corrigendum 2 `keysort/2` error cases, closes the 5.5.6 Prolog-visible
7673
+ side-effect boundary by excluding statistics/cleanup extensions from strict
7674
+ mode, closes 5.5.10 by keeping the non-standard evaluable atom `e` outside
7675
+ strict mode, and pins the permitted arithmetic variation points for
7676
+ mixed-type Corrigendum 2 `max/2`/`min/2` plus signed Clause 9.4 bitwise and
7677
+ negative-shift behavior. These are conformance improvements, while the
7662
7678
  remaining qualifications are:
7663
7679
 
7664
7680
  - zero-arity compound syntax such as `ready()` is represented by the atom
package/why-eyeprolog.md CHANGED
@@ -19,13 +19,17 @@ syntax.
19
19
 
20
20
  EyeProlog targets the Part 1 core together with Technical Corrigenda 1, 2,
21
21
  and 3, and provides documented module and definite-clause-grammar compatibility
22
- profiles for normal-mode programs. The processor character model is explicitly
22
+ profiles for normal-mode programs. The post-N289 WG17/STC working draft is
23
+ tracked as audit input rather than silently treated as another published
24
+ Corrigendum; the conformance ledger records where draft wording differs from
25
+ the published strict baseline. The processor character model is explicitly
23
26
  implementation defined: EyeProlog uses Unicode scalar values as the PCS and as
24
27
  collating-sequence integers in both normal and strict profiles. Strict mode
25
28
  therefore rejects implementation-specific language extensions without changing
26
29
  that processor choice. Its executable conformance matrix and tests
27
30
  document the supported behavior, including an executable trace of the vendored
28
- active WG17 syntax cases. This is extensive implementation evidence, not a
31
+ active WG17 syntax cases, cross-profile preservation of all strict-success WG17 syntax outcomes, closed strict side-effect and evaluable-functor extension boundaries, and explicit choices at standardized variation points such as mixed-type
32
+ `max/2`/`min/2` and signed bitwise/shift operations. This is extensive implementation evidence, not a
29
33
  claim that every Part 1, Part 2, or Part 3 normative requirement has already
30
34
  been certified or closed.
31
35