eyeprolog 1.3.52 → 1.3.54

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
@@ -210,13 +210,13 @@ expansion/`phrase/2-3`; it also removes the EyeProlog `occurs_check` flag,
210
210
  Normal mode is unchanged and continues to support modules, DCGs, quads,
211
211
  libraries, proofs, cleanup-aware control, and the other documented extensions.
212
212
 
213
- Strict mode also makes the Part 1 processor-character-set choice explicit: its
214
- PCS is 7-bit ASCII (U+0000..U+007F), C0 controls and DEL are classified as
215
- extended layout characters, and the collating-sequence integer of each
216
- character is its ASCII code. Characters or character codes outside that set
217
- raise representation errors in strict parsing and character operations. Normal
218
- mode retains Unicode scalar character data as an implementation-specific
219
- extension.
213
+ The Part 1 processor character set is an implementation-defined processor
214
+ choice, so strict mode does not replace it with a smaller repertoire. EyeProlog
215
+ uses Unicode scalar values as its PCS in both normal and strict profiles, with
216
+ the Unicode scalar value as the collating-sequence integer. ASCII keeps the
217
+ standard lexical classes; extended Unicode letters participate in unquoted name
218
+ syntax and other non-ASCII symbols are treated as extended graphic characters.
219
+ Surrogates and values above U+10FFFF remain representation errors.
220
220
 
221
221
  Strict term I/O likewise keeps the standardized option boundary: `read_term/2-3`
222
222
  and `write_term/2-3` accept the Part 1 plus Corrigendum 3 options, while the
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.3.52",
6
+ "version": "1.3.54",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
@@ -41,7 +41,12 @@ function evaluate(term, env, options = {}) {
41
41
  // arguments before selecting an implementation-dependent evaluation order.
42
42
  const operands = term.args.map((arg) => deref(arg, env));
43
43
  if (operands.some((arg) => arg.type === VAR)) throw new PrologError('instantiation_error');
44
- args = operands.map((arg) => evaluateOperand(term, arg, env, options));
44
+ // Once the direct-variable precedence is satisfied, each operand is
45
+ // itself an expression. An atom such as foo therefore fails as the
46
+ // non-evaluable functor foo/0 (7.9.2(c)); it is not a numeric value that
47
+ // can subsequently trigger type_error(number,...). STC #69 corrects the
48
+ // misleading 9.1.7 example accordingly.
49
+ args = operands.map((arg) => evaluate(arg, env, options));
45
50
  } else {
46
51
  // Preserve the normal EyeProlog profile's established left-to-right
47
52
  // evaluator and diagnostics; the stricter prescribed errors belong to
@@ -51,38 +56,6 @@ function evaluate(term, env, options = {}) {
51
56
  return evaluateOperation(term, args, options);
52
57
  }
53
58
 
54
- function evaluatesAtomDirectly(term, options) {
55
- return term.type === ATOM && (term.name === 'pi' || (term.name === 'e' && options.isoStrict !== true));
56
- }
57
-
58
- function directOperandType(term) {
59
- const name = term.name;
60
- const arity = term.arity;
61
- if ((arity === 1 && name === '\\') ||
62
- (arity === 2 && ['>>', '<<', '/\\', '\\/', 'xor'].includes(name))) return 'integer';
63
-
64
- if ((arity === 1 && ['+', '-', 'abs', 'sign', 'float', 'truncate', 'round', 'ceiling', 'floor',
65
- 'float_integer_part', 'float_fractional_part', 'sin', 'cos', 'atan', 'asin', 'acos', 'tan',
66
- 'exp', 'log', 'sqrt'].includes(name)) ||
67
- (arity === 2 && ['+', '-', '*', '/', '//', 'div', 'mod', 'rem', '**', '^', 'max', 'min', 'atan2'].includes(name))) {
68
- return 'number';
69
- }
70
- return null;
71
- }
72
-
73
- function evaluateOperand(parent, operand, env, options) {
74
- // The arithmetic-functor error clauses use the offending operand as the
75
- // culprit when an atomic operand is not numeric (e.g. +(foo,77),
76
- // truncate(foo), and the bitwise families). A nested compound still goes
77
- // through ordinary expression evaluation so an unknown nested F/N retains
78
- // type_error(evaluable,F/N).
79
- if (operand.type === ATOM && !evaluatesAtomDirectly(operand, options)) {
80
- const expected = directOperandType(parent);
81
- if (expected) throw new PrologError(`type_error(${expected})`, operand);
82
- }
83
- return evaluate(operand, env, options);
84
- }
85
-
86
59
  function evaluateOperation(term, args, options = {}) {
87
60
  const name = term.name;
88
61
  const arity = term.arity;
@@ -104,9 +77,17 @@ function evaluateOperation(term, args, options = {}) {
104
77
  if (options.isoStrict === true && ['truncate', 'round', 'ceiling', 'floor'].includes(name) && args[0].integer) {
105
78
  throw new PrologError('type_error(float)', numericTerm(args[0]));
106
79
  }
107
- const a = Number(args[0].value);
108
80
  if (name === 'abs' && args[0].integer) return { integer: true, value: args[0].value < 0n ? -args[0].value : args[0].value };
109
81
  if (name === 'sign' && args[0].integer) return { integer: true, value: args[0].value < 0n ? -1n : args[0].value > 0n ? 1n : 0n };
82
+ const a = Number(args[0].value);
83
+ // The int-exp -> float templates first apply the processor's I->F
84
+ // conversion. With unbounded integers that conversion can overflow even
85
+ // though the integer itself is representable. Do not let a host math
86
+ // function observe +/-Infinity and turn the required float-overflow into
87
+ // a different result (for example atan(Huge) = pi/2).
88
+ if (options.isoStrict === true && args[0].integer && !Number.isFinite(a)) {
89
+ throw new PrologError('evaluation_error(float_overflow)');
90
+ }
110
91
  if (name === 'truncate' || name === 'round' || name === 'ceiling' || name === 'floor') {
111
92
  const fn = name === 'truncate' ? Math.trunc : name === 'round' ? Math.round : name === 'ceiling' ? Math.ceil : Math.floor;
112
93
  return { integer: true, value: BigInt(fn(a)) };
@@ -120,6 +101,13 @@ function evaluateOperation(term, args, options = {}) {
120
101
  const value = fn(a);
121
102
  if (Number.isNaN(value) || (name === 'log' && a === 0)) throw new PrologError('evaluation_error(undefined)');
122
103
  if (!Number.isFinite(value)) throw new PrologError('evaluation_error(float_overflow)');
104
+ // Unlike the implementation-defined resultF choice for ordinary floating
105
+ // arithmetic, exp/1 has an explicit exceptional condition when its
106
+ // mathematical non-zero result is too small to represent. Math.exp()
107
+ // signals that boundary by rounding the positive result to zero.
108
+ if (options.isoStrict === true && name === 'exp' && value === 0) {
109
+ throw new PrologError('evaluation_error(underflow)');
110
+ }
123
111
  return { integer: false, value };
124
112
  }
125
113
  if (arity !== 2) throw new PrologError('type_error(evaluable)', compound('/', [atom(name), numberTerm(arity)]));
@@ -158,6 +146,28 @@ function evaluateOperation(term, args, options = {}) {
158
146
  if (name === '<<') return { integer: true, value: integerResource(() => a << b) };
159
147
  if (name === '>>') return { integer: true, value: integerResource(() => a >> b) };
160
148
  }
149
+ // Power has prescribed exceptional conditions that depend on the evaluated
150
+ // operand values/types and therefore precede any I->F conversion needed by
151
+ // the selected floating template. Checking these first matters with
152
+ // unbounded integers: conversion overflow must not hide an already-satisfied
153
+ // undefined-power condition.
154
+ const aNegative = args[0].integer ? a < 0n : a < 0;
155
+ const aZero = args[0].integer ? a === 0n : a === 0;
156
+ const bNegative = args[1].integer ? b < 0n : b < 0;
157
+ if (options.isoStrict === true && name === '**' && aNegative && !args[1].integer) {
158
+ // Corrigendum 3 corrects the operand name in 9.3.1.3(c): **/2 requires
159
+ // an integer-typed exponent for a negative base. A float such as 2.0 is
160
+ // still not an integer value of the required type here.
161
+ throw new PrologError('evaluation_error(undefined)');
162
+ }
163
+ if (options.isoStrict === true && name === '^' && aNegative && !args[1].integer && !Number.isInteger(b)) {
164
+ // Corrigendum 2 ^/2 additionally accepts a float with an integer value.
165
+ throw new PrologError('evaluation_error(undefined)');
166
+ }
167
+ if ((name === '**' || name === '^') && aZero && bNegative) {
168
+ throw new PrologError('evaluation_error(undefined)');
169
+ }
170
+
161
171
  const x = Number(a), y = Number(b);
162
172
  if ((!Number.isFinite(x) || !Number.isFinite(y)) && name !== 'max' && name !== 'min') {
163
173
  throw new PrologError('evaluation_error(float_overflow)');
@@ -173,11 +183,6 @@ function evaluateOperation(term, args, options = {}) {
173
183
  if (x === 0 && y === 0) throw new PrologError('evaluation_error(undefined)');
174
184
  value = Math.atan2(x, y);
175
185
  }
176
- else if ((name === '**' || name === '^') && x === 0 && y < 0) {
177
- // 9.3.1.3(d) and Cor.2 9.3.10.3(d): zero to a negative power is
178
- // undefined, not floating-point overflow.
179
- throw new PrologError('evaluation_error(undefined)');
180
- }
181
186
  else if (name === '+') value = x + y;
182
187
  else if (name === '-') value = x - y;
183
188
  else if (name === '*') value = x * y;
@@ -186,6 +191,14 @@ function evaluateOperation(term, args, options = {}) {
186
191
  else throw new PrologError('type_error(evaluable)', compound('/', [atom(name), numberTerm(arity)]));
187
192
  if (Number.isNaN(value)) throw new PrologError('evaluation_error(undefined)');
188
193
  if (!Number.isFinite(value)) throw new PrologError('evaluation_error(float_overflow)');
194
+ // Part 1 **/2 and Corrigendum 2 ^/2 prescribe underflow when a
195
+ // mathematically non-zero floating power is too small. A non-zero finite
196
+ // base cannot have an exact zero power result, so a host result of +/-0 is
197
+ // the observable underflow boundary. Zero raised to a positive power remains
198
+ // the ordinary exact zero case.
199
+ if (options.isoStrict === true && (name === '**' || name === '^') && value === 0 && x !== 0) {
200
+ throw new PrologError('evaluation_error(underflow)');
201
+ }
189
202
  return { integer: false, value };
190
203
  }
191
204
  export function arithmeticValueTerm(value) {
@@ -1,9 +1,10 @@
1
- // ISO/IEC 13211-1 processor-character-set choices used by --iso-strict.
1
+ // ISO/IEC 13211-1 processor-character-set choices used by EyeProlog.
2
2
  //
3
- // EyeProlog's strict Part 1 profile deliberately chooses a finite PCS so every
4
- // accepted character has one documented lexical classification and one stable
5
- // collating-sequence integer. Normal mode keeps the broader Unicode surface as
6
- // an implementation-specific extension.
3
+ // The PCS is implementation defined (6.5), so --iso-strict must not replace
4
+ // the processor's ordinary character repertoire with a smaller one. EyeProlog
5
+ // uses Unicode scalar values in both normal and strict profiles. Strict mode
6
+ // still rejects implementation-specific language features; it does not alter
7
+ // implementation-defined character-set choices.
7
8
 
8
9
  export class CharacterRepresentationError extends Error {
9
10
  constructor(formal = 'representation_error(character)') {
@@ -13,13 +14,9 @@ export class CharacterRepresentationError extends Error {
13
14
  }
14
15
  }
15
16
 
16
- // Strict PCS: the 128 ASCII characters U+0000..U+007F. Printable ASCII
17
- // carries the Part 1 lexical classes; the remaining C0 controls plus DEL are
18
- // implementation-defined extended layout characters. This lets every ISO
19
- // octal/hexadecimal character escape in the ASCII range denote a PCS member.
20
- // The collating-sequence integer is the Unicode/ASCII code point.
21
17
  export function isStrictIsoPcsCodePoint(code) {
22
- return Number.isInteger(code) && code >= 0 && code <= 0x7f;
18
+ return Number.isInteger(code) && code >= 0 && code <= 0x10ffff &&
19
+ !(code >= 0xd800 && code <= 0xdfff);
23
20
  }
24
21
 
25
22
  export function isStrictIsoPcsCharacter(character) {
@@ -27,6 +24,7 @@ export function isStrictIsoPcsCharacter(character) {
27
24
  return isStrictIsoPcsCodePoint(character.codePointAt(0));
28
25
  }
29
26
 
27
+ // EyeProlog chooses the Unicode scalar value as the collating-sequence integer.
30
28
  export function strictIsoCollatingInteger(character) {
31
29
  return isStrictIsoPcsCharacter(character) ? character.codePointAt(0) : null;
32
30
  }
package/src/iso.js CHANGED
@@ -1352,9 +1352,10 @@ function* termTextCandidates(stream, solver) {
1352
1352
  let quote = null, lineComment = false, blockComment = false;
1353
1353
  for (let i = stream.position; i < source.length; i++) {
1354
1354
  const ch = source[i], next = source[i + 1];
1355
- if (solver.isoStrict && !isStrictIsoPcsCodePoint(ch.charCodeAt(0))) {
1356
- throw new PrologError('representation_error(character)');
1357
- }
1355
+ // The PCS is shared by normal and strict modes (issue #67). UTF-16
1356
+ // source indexing here must not reject one half of a supplementary scalar.
1357
+ // Character/code predicates validate scalar boundaries where a character
1358
+ // value is actually required.
1358
1359
  // Text streams preserve invalid UTF-8 bytes as an impossible Unicode
1359
1360
  // sentinel. read/1-2 and read_term/2-3 must surface the same character
1360
1361
  // representation error as get_char/1-2 instead of misclassifying the
package/src/parser.js CHANGED
@@ -35,12 +35,27 @@ const TOK = {
35
35
  };
36
36
 
37
37
  function isWhitespaceCode(code) {
38
- // EyeProlog classifies ASCII C0 controls and DEL as layout characters. In
39
- // strict mode these are the implementation-defined extended-layout members
40
- // of the ASCII processor character set.
41
38
  return (code >= 0 && code <= 32) || code === 127;
42
39
  }
43
40
 
41
+ function isWhitespaceCharacter(character) {
42
+ if (!character) return false;
43
+ const code = character.charCodeAt(0);
44
+ return isWhitespaceCode(code) || /\p{White_Space}/u.test(character);
45
+ }
46
+
47
+ function isUnicodeUpperCharacter(character) {
48
+ return Boolean(character) && /[\p{Lu}\p{Lt}]/u.test(character);
49
+ }
50
+
51
+ function isUnicodeLetterCharacter(character) {
52
+ return Boolean(character) && /\p{L}/u.test(character);
53
+ }
54
+
55
+ function isUnicodeNameContinueCharacter(character) {
56
+ return Boolean(character) && /[\p{L}\p{M}\p{Nd}]/u.test(character);
57
+ }
58
+
44
59
  function isDigitCode(code) {
45
60
  return code >= 48 && code <= 57;
46
61
  }
@@ -53,13 +68,22 @@ function isNameContinueCode(code) {
53
68
  return code === 95 || isAsciiLetterCode(code) || isDigitCode(code);
54
69
  }
55
70
 
71
+ function isNameContinueCharacter(character) {
72
+ return Boolean(character) && (isNameContinueCode(character.charCodeAt(0)) ||
73
+ isUnicodeNameContinueCharacter(character));
74
+ }
56
75
 
57
- function isVariableStartCode(code) {
58
- return code === 95 || (code >= 65 && code <= 90);
76
+ function isVariableStartCharacter(character) {
77
+ if (!character) return false;
78
+ const code = character.charCodeAt(0);
79
+ return code === 95 || (code >= 65 && code <= 90) || isUnicodeUpperCharacter(character);
59
80
  }
60
81
 
61
- function isPlainAtomStartCode(code) {
62
- return code >= 97 && code <= 122;
82
+ function isPlainAtomStartCharacter(character) {
83
+ if (!character) return false;
84
+ const code = character.charCodeAt(0);
85
+ return (code >= 97 && code <= 122) ||
86
+ (isUnicodeLetterCharacter(character) && !isUnicodeUpperCharacter(character));
63
87
  }
64
88
 
65
89
  const graphicAtomChars = '#$&*+-./<=>?@^~\\:';
@@ -161,6 +185,19 @@ function isGraphicAtomCode(code) {
161
185
  return graphicAtomChars.includes(String.fromCharCode(code));
162
186
  }
163
187
 
188
+ function isGraphicAtomCharacter(character) {
189
+ if (!character) return false;
190
+ const code = character.charCodeAt(0);
191
+ if (isGraphicAtomCode(code)) return true;
192
+ if (code <= 0x7f || isWhitespaceCharacter(character) ||
193
+ isUnicodeNameContinueCharacter(character)) return false;
194
+ // Non-ASCII symbols/punctuation are EyeProlog extended graphic characters.
195
+ // Surrogate code units are kept together by the maximal-token scan, so a
196
+ // supplementary scalar remains one atom spelling even though source offsets
197
+ // are UTF-16 based.
198
+ return true;
199
+ }
200
+
164
201
  function defineParserOperator(state, priority, specifier, name) {
165
202
  const strength = operatorStrength(priority);
166
203
  if (['xfx', 'xfy', 'yfx'].includes(specifier)) {
@@ -237,11 +274,10 @@ class Parser {
237
274
  return this.parserFlagState.charConversions.get(character) ?? character;
238
275
  }
239
276
  rawPeek(offset = 0) {
240
- const ch = this.source[this.pos + offset] ?? '';
241
- if (this.strictIso && ch && !isStrictIsoPcsCodePoint(ch.charCodeAt(0))) {
242
- throw new CharacterRepresentationError();
243
- }
244
- return ch;
277
+ // The processor character set is an implementation-defined processor
278
+ // choice, shared by normal and strict profiles. Do not narrow it merely
279
+ // because ISO conformance checking is enabled (issue #67).
280
+ return this.source[this.pos + offset] ?? '';
245
281
  }
246
282
  rawTake() {
247
283
  const ch = this.rawPeek();
@@ -367,7 +403,7 @@ class Parser {
367
403
  while (true) {
368
404
  while (this.rawPeek()) {
369
405
  const ch = this.peek();
370
- if (!isWhitespaceCode(ch.charCodeAt(0))) break;
406
+ if (!isWhitespaceCharacter(ch)) break;
371
407
  this.take();
372
408
  }
373
409
  if (this.peek() === '%') {
@@ -461,7 +497,7 @@ class Parser {
461
497
  if (ch === '.' && !this.terminatingFullStop()) {
462
498
  const start = this.pos;
463
499
  this.take();
464
- while (isGraphicAtomCode(this.peek().charCodeAt(0)) &&
500
+ while (isGraphicAtomCharacter(this.peek()) &&
465
501
  !this.terminatingFullStop()) this.take();
466
502
  return { type: TOK.ATOM, text: this.convertedSlice(start, this.pos), line };
467
503
  }
@@ -627,25 +663,25 @@ class Parser {
627
663
  return { type: TOK.NUMBER, text, line };
628
664
  }
629
665
 
630
- if (isVariableStartCode(ch.charCodeAt(0))) {
666
+ if (isVariableStartCharacter(ch)) {
631
667
  const start = this.pos;
632
668
  this.take();
633
- while (isNameContinueCode(this.peek().charCodeAt(0))) this.take();
669
+ while (isNameContinueCharacter(this.peek())) this.take();
634
670
  const text = this.convertedSlice(start, this.pos);
635
671
  return { type: TOK.VAR, text, line };
636
672
  }
637
673
 
638
- if (isPlainAtomStartCode(ch.charCodeAt(0))) {
674
+ if (isPlainAtomStartCharacter(ch)) {
639
675
  const start = this.pos;
640
676
  this.take();
641
- while (isNameContinueCode(this.peek().charCodeAt(0))) this.take();
677
+ while (isNameContinueCharacter(this.peek())) this.take();
642
678
  return { type: TOK.ATOM, text: this.convertedSlice(start, this.pos), line };
643
679
  }
644
680
 
645
- if (isGraphicAtomCode(ch.charCodeAt(0))) {
681
+ if (isGraphicAtomCharacter(ch)) {
646
682
  const start = this.pos;
647
683
  this.take();
648
- while (isGraphicAtomCode(this.peek().charCodeAt(0)) &&
684
+ while (isGraphicAtomCharacter(this.peek()) &&
649
685
  !this.terminatingFullStop()) this.take();
650
686
  return { type: TOK.ATOM, text: this.convertedSlice(start, this.pos), line };
651
687
  }
package/src/term.js CHANGED
@@ -664,6 +664,19 @@ export function variantTerms(left, leftEnv, right, rightEnv, pairs = new Map(),
664
664
  return true;
665
665
  }
666
666
 
667
+
668
+ function compareCharacterText(left, right) {
669
+ const a = Array.from(left);
670
+ const b = Array.from(right);
671
+ const length = Math.min(a.length, b.length);
672
+ for (let i = 0; i < length; i++) {
673
+ const ac = a[i].codePointAt(0);
674
+ const bc = b[i].codePointAt(0);
675
+ if (ac !== bc) return ac < bc ? -1 : 1;
676
+ }
677
+ return a.length < b.length ? -1 : a.length > b.length ? 1 : 0;
678
+ }
679
+
667
680
  export function compareTerms(left, right, variableRanks = null) {
668
681
  // ISO 7.2.1 deliberately leaves the order of distinct variables
669
682
  // implementation dependent. Do not attach a permanent ordinal to a
@@ -703,9 +716,9 @@ function compareTermsWithRanks(left, right, variableRanks) {
703
716
  const rightOrder = variableRank(right.name, variableRanks);
704
717
  return leftOrder < rightOrder ? -1 : 1;
705
718
  }
706
- if (left.type === ATOM || left.type === STRING) return left.name < right.name ? -1 : left.name > right.name ? 1 : 0;
719
+ if (left.type === ATOM || left.type === STRING) return compareCharacterText(left.name, right.name);
707
720
  if (left.arity !== right.arity) return left.arity < right.arity ? -1 : 1;
708
- if (left.name !== right.name) return left.name < right.name ? -1 : 1;
721
+ if (left.name !== right.name) return compareCharacterText(left.name, right.name);
709
722
  for (let i = 0; i < left.arity; i++) {
710
723
  const cmp = compareTermsWithRanks(left.args[i], right.args[i], variableRanks);
711
724
  if (cmp) return cmp;
@@ -30,18 +30,18 @@ error-ordering alternative to an individual executable assertion.
30
30
 
31
31
  | Standard area | Status | Current evidence |
32
32
  | --- | --- | --- |
33
- | 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 strict ASCII 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. |
33
+ | Clause 6 — tokens, terms, lists, operators, quoted text | audit | Complete vendored WG17 syntax matrix, `lexical_and_curly_terms`, `scryer_lexical_terms`, operator suites, syntax-error cases, quoted-layout/escape error cases, writer/read-back regressions, and Unicode-scalar PCS/collation boundary tests. The implementation-defined 6.5/6.6 character-model decisions are now closed; wider shall-by-shall lexical mapping remains open. |
34
34
  | 7.1-7.3 — term types, term order, unification | audit | Standard-order, identity, finite-tree and occurs-check suites, Corrigendum 2 term predicates, plus strict checks for the required `variable < float < integer < atom < compound` type order, PCS-based atom collation, and the `max_arity=unbounded` compound-term model. |
35
35
  | 7.4 — Prolog text and directives | audit | All Part 1 directive indicators are parsed; include/ensure-loaded/operator/flag/character-conversion behavior has executable coverage. Preparation-time `char_conversion/2` affects later unquoted source text and respects `char_conversion=off`. Strict preparation now enforces declaration-before-clause ordering for `dynamic/1`, `multifile/1`, and `discontiguous/1`, cross-text `multifile/1`, discontiguous clause grouping, empty declared procedures, include textual-replacement behavior, and one-time initialization per prepared program. The remaining shall-by-shall mapping is still open. |
36
36
  | 7.5-7.6 — database and term/clause conversion | audit | Dynamic database and logical-update-view suites. Strict mode restores Part 1 private-static/public-dynamic `clause/2` access; focused strict checks now cover `current_predicate/1`, `clause/2`, `asserta/1`, `assertz/1`, `retract/1`, Corrigendum 2 `retractall/1`, and `abolish/1` errors plus empty-procedure lifetime. Runtime protection includes the solver-native conjunction control construct `','/2` and, following STC #56's accepted-direction action item, the source syntax functors `(:-)/1-2` for database modification/private access while leaving their call behavior distinct. The remaining clause-conversion shall-by-shall mapping is still open. |
37
37
  | 7.7 — execution and backtracking | audit | Control/search suites. Strict mode disables EyeProlog automatic tabling, cycle guards, and recursive numeric shortcuts so core execution uses ordinary clause selection/backtracking. |
38
38
  | 7.8 — control constructs and exceptions | audit | call, cut, conjunction, disjunction (including failed branches after callee-local cuts), if-then, catch/throw, renamed-copy tests. |
39
- | 7.9 — expression evaluation | audit | Arithmetic/evaluation/error suites, including Corrigenda. Strict mode now also pins direct-variable precedence, arithmetic operand type errors, float-only rounding conversions, zero-to-negative-power undefined errors, and the Part 1 mixed integer/float comparison conversion rule. 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. Exceptional-value/error-precedence mapping remains to be exhaustively enumerated. |
39
+ | 7.9 — expression evaluation | audit | Arithmetic/evaluation/error suites, including Corrigenda. Strict mode now pins direct-variable precedence, 7.9.2 non-evaluable `F/N` errors (including STC #69), arithmetic numeric/integer type errors, float-only rounding conversions, the Part 1 mixed integer/float comparison conversion rule, I->F overflow before floating evaluable functors (STC #42), explicit `exp/1`/power underflow, and prescribed negative/zero power errors before later conversion overflow. Unbounded integer powers/shifts no longer leak host `RangeError`; finite-host exhaustion is normalized to `resource_error(memory)` in line with the Part 1 resource-error note and STC #21. The remaining exceptional-value/error-precedence rows are still being exhaustively enumerated. |
40
40
  | 7.10 — input/output concepts | audit | Stream, character/byte I/O, read/write options, operator-sensitive write-back, and Corrigendum 3 writer cases. The audit now also covers write-mode creation/truncation, append creation, repositioned overwrite, flushing, EOF actions, close/current-stream handling, strict stream-position terms, stream-property validation, stream-term requirements, text-vs-binary permission errors, and the prescribed `alias(A)` culprit for `open/4` alias collisions. The full option/mode cross-product is still being mapped. |
41
- | 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. Strict mode excludes the EyeProlog `occurs_check` extension. |
41
+ | 7.11 — flags | covered | The complete Part 1 flag set, selected defaults, standard value domains, changeability, `current_prolog_flag/2`, and `set_prolog_flag/2` error behavior have dedicated strict tests. EyeProlog selects `bounded=false` and `integer_rounding_function=toward_zero`; valid alternative values of those fixed flags reach `permission_error(modify,flag,...)`, while `max_integer` and `min_integer` have no current value. STC #70 is recorded explicitly: EyeProlog has no separate finite procedure-arity ceiling, so the optional `max_procedure_arity` flag is absent while `max_arity` remains `unbounded`. Strict mode excludes the EyeProlog `occurs_check` extension. |
42
42
  | 7.12 — errors | audit | ISO `error(Error, Context)` envelope, type/domain/permission/representation/evaluation/syntax/resource families and focused error cases. Exact ISO 8.14.1-8.14.4 error precedence is covered for `read_term/3`, `write_term/3`, `op/3`, and `current_op/3`; the continuing audit has corrected additional 8.11-8.13 stream/character/byte errors (including the complete `alias(A)` culprit for `open/4` alias collisions), Corrigendum 2 `keysort/2` errors, atomic-conversion list-shape precedence, `arg/3`, `atom_concat/3`, `sub_atom/5`, `char_conversion/2`, runtime control-construct/database protection, arithmetic culprit/precedence reporting, and host-resource normalization for unbounded term/integer operations. A complete one-row-per-prescribed-error ordering map remains open. |
43
43
  | 8.2-8.17 — built-in predicates | audit | Predicate-family coverage is mapped in ISO-MATRIX.md; Corrigendum 2 additions (`subsumes_term/2`, `term_variables/2`, `call/2..8`, `false/0`) are in the strict registry. The audit now includes corrected `keysort/2` variable/non-pair errors, stricter 8.11-8.13 stream and character/byte modes/errors, the 8.14 option/error-order subfamily, closed 8.17 flags, additional prescribed precedence for `arg/3`, `atom_concat/3`, `sub_atom/5`, number/atom list conversions, and `char_conversion/2`, plus finite-host resource handling for unbounded `functor/3` construction. The remaining mode/error matrix is not yet one-row-per-standard-row. |
44
- | 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, enforces the Part 1 numeric/integer operand errors and float-only rounding modes, and uses the Part 1 integer-to-float conversion rule for mixed arithmetic comparisons. Normal mode retains EyeProlog's exact mixed-type comparison extension. 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; exhaustive exceptional-value precedence remains open. |
44
+ | Clause 9 — evaluable functors | audit | Integer/float/rounding/transcendental/bitwise suites and corrigendum cases. Strict mode excludes the EyeProlog-only evaluable atom `e`, retains Corrigendum 2 arithmetic additions, reports unknown zero-arity evaluables with the required `F/0` culprit shape, distinguishes non-evaluable `F/N` errors from numeric/integer operand errors per 7.9.2 and STC #69, and enforces float-only rounding modes, performs I->F conversion before floating functors (including overflow), reports the explicit `exp/1`, `**/2`, and Corrigendum 2 `^/2` underflow conditions, preserves the power-specific undefined conditions ahead of conversion overflow, and uses the Part 1 integer-to-float rule for mixed arithmetic comparisons. Normal mode retains EyeProlog's exact mixed-type comparison and round-to-zero arithmetic behavior as extensions. Unbounded BigInt resource exhaustion is translated into the Prolog error model rather than leaking host exceptions. Host floating-point representation choices remain documented implementation-defined behavior; the remaining exceptional-value rows are still under audit. |
45
45
  | Corrigendum 1 | covered | Double-quoted atom/operator-priority corrections have dedicated cases. |
46
46
  | Corrigendum 2 | covered | Added predicates/functors, catch corrections, bar/operator and uninstantiation corrections have dedicated cases. |
47
47
  | Corrigendum 3 | covered | Writer options, `variable_names/1`, canonical list output and negative-power corrections have dedicated cases. |
@@ -59,15 +59,16 @@ conformance claims:
59
59
  when the `char_conversion` flag is `on`, leaves quoted characters unchanged,
60
60
  and feeds the same mapping into execution-time term input.
61
61
 
62
- A follow-on audit closes the processor-character-set/collation choices rather
63
- than leaving them implicit. `--iso-strict` now selects the 128-character ASCII
64
- PCS U+0000..U+007F, classifies C0 controls and DEL as extended layout
65
- characters, and uses the code point itself as each collating-sequence integer.
66
- Characters/codes outside that PCS raise representation errors in strict
67
- parsing, term input, character conversion, and character-code predicates. The
68
- normal profile retains Unicode scalar character data as an explicit extension.
69
- The complete WG17 syntax matrix remains green under this narrower strict
70
- boundary.
62
+ A follow-on audit made the processor-character-set/collation choices explicit.
63
+ Issue #67 then corrected an over-strict interpretation: because PCS membership
64
+ and extended-character classification are implementation defined by Part 1,
65
+ `--iso-strict` must not replace EyeProlog's ordinary processor choice. Both
66
+ profiles now use Unicode scalar values as PCS members and collating integers;
67
+ Unicode letters/white-space/graphics receive documented extended lexical
68
+ classes, while surrogates and values above U+10FFFF remain representation
69
+ errors. Strict mode continues to reject implementation-specific facilities
70
+ without changing these implementation-defined character choices. The complete
71
+ WG17 syntax matrix remains a release gate.
71
72
 
72
73
 
73
74
  The subsequent arity audit originally selected a finite `max_arity=65535`, but
@@ -137,6 +138,19 @@ its exact cross-type comparison as an extension. Dedicated strict regressions
137
138
  cover these distinctions without changing the normal-profile arithmetic error
138
139
  contract.
139
140
 
141
+ A further Clause 9 pass closes the floating conversion and exceptional-value
142
+ boundary exposed by unbounded integers. Strict floating evaluable functors now
143
+ perform the required integer-to-float conversion before invoking the host math
144
+ operation, so an integer outside the selected finite binary64 range reports
145
+ `evaluation_error(float_overflow)` rather than producing a secondary host-math
146
+ result. The explicit underflow clauses for `exp/1`, Part 1 `**/2`, and
147
+ Corrigendum 2 `^/2` are enforced even though EyeProlog retains the
148
+ implementation-defined `round(x)` choice for generic `resultF` arithmetic. The
149
+ power audit also preserves the specified negative-base and zero/negative
150
+ exception conditions before any later I->F overflow, including Corrigendum 3's
151
+ correction to the `**/2` operand wording and Corrigendum 2's distinct rule that
152
+ `^/2` may accept an integer-valued float exponent.
153
+
140
154
  ## Post-Corrigendum STC cross-check
141
155
 
142
156
  The public WG17 STC draft is tracked as useful defect-discovery material, but it
@@ -28,12 +28,12 @@ Status values are:
28
28
  | Clause | Decision completed by ISO 5.4 documentation | EyeProlog choice | Status / implementation evidence |
29
29
  | --- | --- | --- | --- |
30
30
  | 5.5.11 | Reserved atoms and the effect of instantiating a variable to one | EyeProlog reserves no Prolog atom under 5.5.11. Atoms with implementation-looking names remain ordinary terms unless a particular predicate interprets them. | **defined** — term representation and built-ins in `src/term.js`, `src/iso.js`. |
31
- | 6.5 | Processor character set (PCS) | In `--iso-strict`, PCS is the 128-character 7-bit ASCII set U+0000..U+007F. Normal mode additionally accepts Unicode scalar values in character data as an implementation-specific extension. | **defined** — `src/iso-character.js`, strict parser/character-I/O guards, and strict-core/WG17 coverage. |
32
- | 6.5 | Classification of additional/extended PCS characters | Printable ASCII uses the lexical classes specified by Part 1. ASCII C0 controls U+0000..U+001F and DEL U+007F are EyeProlog's extended **layout** characters; they may therefore separate tokens, while quoted control values are written/read through the ISO escape forms. No non-ASCII character belongs to strict PCS. | **defined** — `src/parser.js`, `src/syntax-scan.js`, `src/iso-character.js`. |
33
- | 6.6 | Collating-sequence integers | In strict mode each PCS character's collating-sequence integer is its ASCII/Unicode code point, 0..127. Atom comparison is lexicographic by the same code-unit values, which coincide with those integers throughout strict PCS and satisfy the required capital-letter, small-letter, and decimal-digit constraints. | **defined** — `src/iso-character.js`, `src/term.js` (`compareTerms`), `src/iso.js` character-code predicates. |
34
- | 6.6 | Collating values of control escapes and extended characters | Strict control/extended-layout characters use their ASCII code point as collating integer, so octal/hexadecimal escapes and character-code constants map to the same 0..127 PCS. Normal-mode Unicode character codes use Unicode scalar values; that broader ordering is outside the strict Part 1 profile. | **defined** — `src/iso-character.js`, parser escape handling, `char_code/2`, `atom_codes/2`, and WG17 escape cases. |
31
+ | 6.5 | Processor character set (PCS) | EyeProlog's PCS is the Unicode scalar repertoire U+0000..U+10FFFF excluding surrogates. This implementation-defined processor choice is shared by normal and `--iso-strict` modes; strict conformance does not narrow it. | **defined** — `src/iso-character.js`, parser/character-I/O validation, and strict-core/WG17 coverage. |
32
+ | 6.5 | Classification of additional/extended PCS characters | Printable ASCII uses the lexical classes specified by Part 1. C0 controls and DEL are extended layout characters. Non-ASCII Unicode letters extend alphanumeric name syntax; Unicode white-space is layout; remaining non-ASCII symbols/punctuation are extended graphic characters. | **defined** — `src/parser.js`, `src/syntax-scan.js`, `src/iso-character.js`. |
33
+ | 6.6 | Collating-sequence integers | Each PCS character's collating-sequence integer is its Unicode scalar value. Atom/functor comparison is lexicographic by those scalar integers, satisfying the required monotonic ASCII capital-letter, small-letter, and contiguous decimal-digit constraints while remaining well-defined for supplementary characters. | **defined** — `src/iso-character.js`, `src/term.js` (`compareTerms`), `src/iso.js` character-code predicates. |
34
+ | 6.6 | Collating values of control escapes and extended characters | Control escapes, octal/hexadecimal escapes, character-code constants, and extended Unicode characters all use the denoted Unicode scalar value as collating integer. | **defined** — `src/iso-character.js`, parser escape handling, `char_code/2`, `atom_codes/2`, and WG17 escape cases. |
35
35
  | 7.1.2.2 | Mapping between a character code and bytes | Text file streams decode and encode UTF-8. Binary streams expose bytes 0..255 directly. | **defined** — `src/io.js`. |
36
- | 7.1.4.1 | Set `C` of characters represented by one-char atoms | In strict mode `C` is exactly the ASCII PCS U+0000..U+007F. Normal mode extends character predicates to Unicode scalar values U+0000..U+10FFFF excluding surrogates. | **defined** — `src/iso-character.js`, `src/iso.js` character-code validation. |
36
+ | 7.1.4.1 | Set `C` of characters represented by one-char atoms | In both profiles `C` is the Unicode scalar repertoire U+0000..U+10FFFF excluding surrogates. | **defined** — `src/iso-character.js`, `src/iso.js` character-code validation. |
37
37
  | 7.4.2.4 | Whether `op/3` directives affect other Prolog texts or execution | An `op/3` directive changes parsing of subsequent text loaded into the same `Program`; the resulting operator table is also used by execution-time term I/O. Separately created `Program` objects are independent. | **defined** — `src/parser.js`, `src/program.js`, `src/iso.js`. |
38
38
  | 7.4.2.5 | Whether directive-created `Convc` affects other text/execution | Yes. A `char_conversion/2` directive updates preparation-time conversion for later unquoted source characters and the recorded mapping initializes execution-time term input. Quoted characters are not converted; `char_conversion=off` disables following preparation-time conversion. | **defined** — `src/parser.js`, `src/program.js`, `src/solver.js`; strict-core regression coverage. |
39
39
  | 7.4.2.6 | Order of `initialization/1` goals | Initialization goals run once per prepared `Program`, in source/inclusion order, before requested goals; each must obtain a first solution. Reusing the same prepared program in another solver does not execute its initialization goals again. | **defined** — `Program.initializations`, preparation state, `Solver.runInitializations()`. |
@@ -68,12 +68,12 @@ Status values are:
68
68
  | 7.11.2.3 | Default `max_arity` | `unbounded`: EyeProlog imposes no fixed semantic ceiling on compound-term arity. Practical host allocation exhaustion is a resource condition. This flag is distinct from any potential implementation-specific procedure-arity limit; EyeProlog currently declares no separate finite procedure limit. | **defined** — `src/iso-limits.js`, `src/solver.js`, `src/parser.js`, `src/iso.js`; issue #66 regression coverage. |
69
69
  | 7.11.2.5 | Default `double_quotes` | `chars`. | **defined** — `src/solver.js`, parser flag state. |
70
70
  | 7.12.1 | Second argument of `error/2` | The default context term is the atom `eyeprolog`. A few implementation-specific diagnostics may deliberately supply a more specific context term. | **defined** — `formalErrorTerm()` in `src/iso.js`. |
71
- | 7.12.2(f) | Implementation-defined representation limits | Strict character and character-code operations are limited to the selected ASCII PCS/collating integers 0..127. Normal mode extends characters to Unicode scalar values. Arity/integer values are modeled as unbounded but may hit host/resource limits. Float input overflow uses the implementation-specific `max_float`/`min_float` representation names documented by the STC-oriented tests. | **defined** — parser/ISO numeric and character guards. |
71
+ | 7.12.2(f) | Implementation-defined representation limits | Character and character-code operations are limited to Unicode scalar values; surrogates and values above U+10FFFF are representation errors. Arity/integer values are modeled as unbounded but may hit host/resource limits. Float input overflow uses the implementation-specific `max_float`/`min_float` representation names documented by the STC-oriented tests. | **defined** — parser/ISO numeric and character guards. |
72
72
  | 8.17.1 | Implementation-defined flag value ranges | Strict mode exposes only Part 1 core flags and their standard value sets. Normal mode additionally exposes EyeProlog's `occurs_check` flag. With `bounded=false`, `max_integer` and `min_integer` have no current or selectable value and their `current_prolog_flag/2` queries fail. Valid alternative values of fixed standard flags are distinguished from invalid values so `set_prolog_flag/2` reports permission versus domain errors as prescribed. | **defined** — strict registry/flag filtering in `src/solver.js`; strict flag tests. |
73
73
  | 8.17.3 | Other effects of `halt/0` | Terminates EyeProlog execution and returns host/process status `0`; it produces no Prolog solution. | **defined** — `HaltSignal`, `haltBuiltin()`, CLI/runner handling. |
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, including tiny non-zero arithmetic results | EyeProlog chooses `round(x)` rather than the exceptional value `underflow`. ECMAScript binary64 arithmetic therefore preserves a representable subnormal result and rounds a still-smaller result to `0.0`, consistently with float-token and `number_chars/2` input. | **defined** — arithmetic evaluation in `src/iso-arithmetic.js` and parser/number conversion; executable issue-56 regression below. |
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. |
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
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
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`. |
@@ -86,15 +86,20 @@ Status values are:
86
86
  ### Issue #56: one underflow policy
87
87
 
88
88
  Clause 9.1.4.2 permits the processor to choose `round(x)` or the exceptional
89
- value `underflow` for a tiny non-zero arithmetic result. EyeProlog chooses
90
- `round(x)`. Because its floating-point profile is ECMAScript IEEE-754 binary64,
91
- representable subnormal values remain non-zero and smaller results round to
89
+ value `underflow` for a tiny non-zero result when an operation is governed by
90
+ the generic floating result function. EyeProlog chooses `round(x)`. Because its
91
+ floating-point profile is ECMAScript IEEE-754 binary64, representable
92
+ subnormal values remain non-zero and still-smaller generic results round to
92
93
  `0.0`. Float-token and `number_chars/2` input use the same finite-double
93
- rounding policy, so the same mathematical magnitude is no longer accepted as
94
- `0.0` on input while rejected as an arithmetic result.
94
+ rounding policy.
95
95
 
96
- The regression suite checks both arithmetic and token input, and the existing
97
- `stc/float_underflow_input` conformance case covers the related input conversion.
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.
98
103
 
99
104
  ## Implementation-specific features required to be documented by 5.4
100
105
 
@@ -105,7 +110,7 @@ families; `--iso-strict` is intended to remove their Part 1 interpretation.
105
110
 
106
111
  | Part 1 extension hook | EyeProlog normal-profile feature | Strict-core disposition |
107
112
  | --- | --- | --- |
108
- | 5.5.1 Syntax | Part 2 modules, Part 3 grammar-rule expansion, embedded quad syntax, and normal-mode Unicode character data | Module directives are rejected; grammar rules remain ordinary `-->/2` terms rather than being expanded; quad syntax is rejected; strict character syntax/data is limited to the documented ASCII PCS. |
113
+ | 5.5.1 Syntax | Part 2 modules, Part 3 grammar-rule expansion, and embedded quad syntax | Module directives are rejected; grammar rules remain ordinary `-->/2` terms rather than being expanded; quad syntax is rejected. Unicode PCS membership/classification is implementation defined and therefore shared with normal mode rather than treated as an extension. |
109
114
  | 5.5.2 Predefined operators | Part 3 `|` and EyeProlog's labelable infix `(?-)/2`; CLP(Z) operators when that library is imported | Only the Part 1 operator table is predefined; a conforming `op/3` may still add permitted operators. |
110
115
  | 5.5.3 Character-conversion mapping | No non-identity initial `Convc` extension | Identity initial mapping. |
111
116
  | 5.5.4 Types | No additional runtime Prolog term type is exposed by the core solver | Only variable, integer, float, atom, and compound term ordering participates in strict mode. |
@@ -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, strict ASCII 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 |
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, Corrigendum 2 `keysort/2` variable/non-pair errors, and strict `arg/3`, `functor/3` / `=../2` error-order plus unbounded-arity/resource checks |
@@ -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 strict Corrigendum 2 resulting-goal `max_arity` error coverage |
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 error cases, strict Corrigendum 2 partial/improper-list precedence for atom/number conversions, and prescribed `atom_concat/3` / `sub_atom/5` ordering checks |
20
20
  | 8.17 flags and hooks | complete required Part 1 flag set, selected defaults, standard value domains/changeability, halt and character conversion | `exceptions_and_flags`, `remaining_builtins_and_directives`, flag error cases, strict `char_conversion/2` precedence, and the complete strict flag audit in `run-iso-strict.mjs` |
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 negative-power errors, float-only rounding modes, 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, explicit `exp/1`/`**/2`/`^/2` underflow, Part 1 mixed integer/float comparison conversion, and resource-error normalization for finite-host exhaustion of unbounded integer operations |
22
22
  | Module compatibility profile (related to ISO/IEC 13211-2 and later WG17 amendment work) | module declarations, exports, imports, qualification, meta-predicate context | `modules/qualified_call`, `modules/selective_library_import`, `dcg_module_nonterminal_indicator` |
23
23
  | Part 3-oriented DCG compatibility profile | `-->`, terminal and partial sequences, grammar control constructs, semicontexts, nonterminal indicators, modules, `phrase/2-3`, steadfastness and errors | `dcg_terminals_and_remainder`, `dcg_control_constructs`, `dcg_partial_sequences`, `dcg_phrase_steadfastness`, `dcg_dynamic_nonterminal_indicator`, `logtalk_dcg_phrase_identity`, `logtalk_dcg_semicontexts`, DCG error and precedence cases |
24
24
 
@@ -33,7 +33,8 @@ clause-by-clause in [ISO-IMPLEMENTATION-DEFINED.md](ISO-IMPLEMENTATION-DEFINED.m
33
33
  with *The Art of EyeProlog* remaining the implementation reference. In
34
34
  particular, the index records the unbounded integer model, `double_quotes=chars`,
35
35
  `//` rounding toward zero, the ECMAScript binary64 float policy (including the
36
- 9.1.4.2 choice to round tiny arithmetic results rather than raise underflow),
36
+ 9.1.4.2 choice to round generic tiny results while retaining explicit
37
+ functor-specific underflow errors),
37
38
  stream/character decisions, and the normal-profile extension boundary.
38
39
 
39
40
  This is an executable conformance/compatibility matrix, not a certification issued by an
@@ -29,10 +29,10 @@ identify standards-derived behavior; other directories cover EyeProlog host
29
29
  contracts and extensions. EyeProlog-only execution features such as automatic
30
30
  tabling and `tnot/1` well-founded negation are outside the Part 1 strict-core
31
31
  claim. Their focused semantic coverage lives primarily in regression tests;
32
- `tnot/1` is absent from the strict ISO registry. The strict processor character
33
- set is the documented 7-bit ASCII PCS with ASCII-code collation; normal-mode
34
- Unicode character data is tested as an extension rather than folded into the
35
- Part 1 claim.
32
+ `tnot/1` is absent from the strict ISO registry. The processor character set is documented as the Unicode scalar repertoire with
33
+ scalar-value collation in both normal and strict profiles; `--iso-strict`
34
+ therefore changes only implementation-specific language facilities, not this
35
+ implementation-defined processor choice.
36
36
 
37
37
  All conformance files live under topic directories such as `arithmetic/`, `lists/`, `syntax/`, or `variables/`; new top-level numbered files should not be added. The report uses those directories as coverage categories.
38
38
 
@@ -17,20 +17,27 @@ regressions.
17
17
 
18
18
  | STC item | Topic | EyeProlog evidence / finding |
19
19
  | --- | --- | --- |
20
+ | [#17](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#17) | expression-error overlap | The strict suite pins both `X is 1/0+_` and `X is _+1/0` to `instantiation_error`, so the direct-variable rule is not masked by implementation evaluation order or a zero-divisor on the other operand. |
20
21
  | [#18](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#18) | `eof_action(error)` | Stream regressions verify the first EOF observation yields the EOF value and a subsequent read with `eof_action(error)` raises `permission_error(input,past_end_of_stream,...)`; `eof_code` and `reset` remain distinct. |
21
22
  | [#21](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#21) | unbounded-integer resource exhaustion | The strict suite now prevents host `RangeError` leakage from oversized integer shifts/powers and reports `resource_error(memory)`, consistent with the Part 1 resource-error note and the STC correction proposal. The same principle is used when unbounded `functor/3` construction exhausts a finite host resource. |
22
23
  | [#27](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#27)-[#29](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#29) | overlapping character/code/byte stream errors | Existing stream tests plus strict probes preserve argument validation before later stream-state errors where the published error clauses require it; the STC examples with an open alias produce the stated instantiation/type errors. |
24
+ | [#32](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#32) | `number_chars/2` declarative/procedural wording | The strict suite follows the published procedural number-syntax path: `number_chars(N,['0','1'])` succeeds with `N = 1`. This records the executable behavior without treating the draft wording proposal as a new normative clause. |
23
25
  | [#37](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#37) | `clause/2` variable identity | `stc/clause_variable_identity` verifies sharing between head and body is preserved. |
24
26
  | [#39](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#39) | negative-number syntax | Covered extensively by the upstream-first WG17 syntax matrix; unary-minus read-back discrepancies found by that matrix were fixed in v1.3.27. |
25
27
  | [#40](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#40) | inexact float representation | EyeProlog uses the host IEEE-754 binary64 value and requires written floats to read back to that same value. Input underflow is covered by `stc/float_underflow_input`. |
26
- | [#42](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#42) | integer-to-float conversion for float functions | `stc/integer_to_float_evaluable` verifies an integer expression is accepted by `sin/1` and produces a float. |
28
+ | [#41](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#41) | preparation-time `op/3` ordering | A strict parser regression verifies an `op/3` directive affects following text and cannot retroactively make preceding text parse with the new operator. The same preparation machinery is used for `set_prolog_flag/2`. |
29
+ | [#42](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#42) | integer-to-float conversion for float functions | `stc/integer_to_float_evaluable` verifies an integer expression is accepted by `sin/1` and produces a float. The strict suite additionally verifies that an unbounded integer outside the finite binary64 range raises `evaluation_error(float_overflow)` during I->F conversion before `sin/1`, `cos/1`, `atan/1`, the Corrigendum 2 inverse/trigonometric additions, `exp/1`, `log/1`, `sqrt/1`, or `float/1` can observe a host infinity. |
27
30
  | [#44](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#44) | `char_code/2` error classification | Strict coverage distinguishes `type_error(integer,Code)` for a non-integer code from `representation_error(character_code)` for an integer outside the character-code set. |
31
+ | [#48](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#48) | `read_term/3` option wording with `variables([])` | The strict suite verifies `read_term(T,[variables([])])` on input `T.` has no solution because the actual variable list cannot unify with `[]`; this keeps the operational option semantics clear despite the draft's wording concern. |
28
32
  | [#49](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#49) | `read_term/3` and EOF | Covered by strict reader/conformance tests and the interactive-read regressions. |
29
33
  | [#50](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#50) | mixed integer/float arithmetic comparison | The STC-facing normal-profile case requires exact cross-type ordering and `max/2`/`min/2` use the same comparison. `--iso-strict` deliberately retains the published Part 1 integer-to-float comparison rule; the draft case is not silently substituted for that normative baseline. |
30
34
  | [#55](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#55) | integer rounding function | Existing flag conformance verifies `integer_rounding_function = toward_zero`. |
31
35
  | [#56](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#56) | protect `(:-)/1` and `(:-)/2` from database modification | **Found a gap during the #65 audit.** Strict database operations now treat both functors as static/private for `assert*`, `retract*`, `abolish/1`, declarations, and `clause/2`, while ordinary calls still follow the separate procedure-existence behavior described by the STC item. |
32
36
  | [#58](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#58) | `set_prolog_flag/2` instantiation error | Existing strict/error coverage requires an instantiation error when a required flag value is a variable. |
33
37
  | [#67](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#67) | `bagof/3` answer-order example | `stc/bagof_answer_order` verifies the proposed clarifying example produces `[2,1]`. |
38
+ | [#68](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#68) | division examples | Existing arithmetic coverage evaluates signed integer `/` through the floating operation; no implementation-defined signed-division result is used. |
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
+ | [#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. |
34
41
  | [#73](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#73) | float-reading limits discussed from issue #54 | Positive/negative literal and `number_chars/2` overflow are kept as explicit draft cases; underflow is tested separately. See the note below. |
35
42
 
36
43
  ## Float-reading note for #73
@@ -99,6 +99,14 @@ export function runIsoStrict(reporter = new TestReporter()) {
99
99
  1, 'large nonexistent predicate indicator');
100
100
  });
101
101
 
102
+ reporter.test('does not invent max_procedure_arity without a separate procedure ceiling', () => {
103
+ // STC #70 makes max_procedure_arity implementation defined and needed
104
+ // only when a processor has a smaller procedure-arity ceiling. EyeProlog
105
+ // has no such separate declared ceiling, so the optional flag is absent.
106
+ equal(capture(() => run('', { isoStrict: true, goal: 'current_prolog_flag(max_procedure_arity,_)' })).formal,
107
+ 'domain_error(prolog_flag)', 'STC #70 optional flag absent');
108
+ });
109
+
102
110
  reporter.test('preparation-time char_conversion affects later unquoted source only', () => {
103
111
  const program = Program.parse(
104
112
  ":- char_conversion(x,y).\np(x).\nquoted('x').\n:- set_prolog_flag(char_conversion,off).\nraw(x).\n",
@@ -110,18 +118,19 @@ export function runIsoStrict(reporter = new TestReporter()) {
110
118
  });
111
119
 
112
120
 
113
- reporter.test('uses a documented 7-bit ASCII processor character set and collation', () => {
121
+ reporter.test('uses the implementation-defined Unicode scalar PCS and collation', () => {
114
122
  const program = Program.parse('', { isoStrict: true });
115
123
  const solver = new Solver(program, { isoStrict: true });
116
124
  const answers = (text) => [...solver.solve([parseGoalText(text, { isoStrict: true })], new Env(), 0)].length;
117
125
  equal(answers("char_code('\\0\\',0)"), 1, 'NUL collating integer');
118
126
  equal(answers("char_code('A',65)"), 1, 'A collating integer');
119
- equal(answers("char_code('\\177\\',127)"), 1, 'DEL collating integer');
127
+ equal(answers("char_code('ä',228)"), 1, 'extended Latin character');
128
+ equal(answers("char_code('😀',128512)"), 1, 'supplementary Unicode scalar');
120
129
  equal(answers("'\\0\\' @< 'A'"), 1, 'control before capital');
121
130
  equal(answers("'A' @< 'a'"), 1, 'capital before small letter');
131
+ equal(answers("'\\xe000\\' @< '\\x10000\\'"), 1, 'atom order follows scalar collating integers');
122
132
  });
123
133
 
124
-
125
134
  reporter.test('follows the Part 1 standard term-type and atom ordering', () => {
126
135
  const program = Program.parse('', { isoStrict: true });
127
136
  const solver = new Solver(program, { isoStrict: true });
@@ -134,30 +143,38 @@ export function runIsoStrict(reporter = new TestReporter()) {
134
143
  equal(answers("'A' @< 'B'"), 1, 'atom collation');
135
144
  });
136
145
 
137
- reporter.test('rejects characters outside the strict processor character set', () => {
138
- const sourceError = capture(() => Program.parse("p('é').\n", { isoStrict: true }));
139
- equal(sourceError.formal, 'representation_error(character)', 'source representation error');
146
+ reporter.test('does not narrow implementation-defined character choices in strict mode', () => {
147
+ const program = Program.parse("p('é').\nä.\n", { isoStrict: true });
148
+ equal(Boolean(program.findGroup('p', 1)), true, 'quoted extended character in source');
149
+ equal(Boolean(program.findGroup('ä', 0)), true, 'extended small-letter atom in source');
140
150
 
141
- const readError = capture(() => run('', {
151
+ const readResult = run('', {
142
152
  isoStrict: true,
143
- goal: 'read(X)',
153
+ goal: "read('é')",
144
154
  ioOptions: { input: "'é'." },
145
- }));
146
- equal(readError.formal, 'representation_error(character)', 'read representation error');
155
+ });
156
+ equal(readResult.stats.completed_goal_lists, 1, 'extended character through strict text input');
157
+
158
+ const written = run('', { isoStrict: true, goal: 'writeq("ä")' });
159
+ includes(written.stdout, 'ä', 'issue #67 writeq example');
147
160
  });
148
161
 
149
- reporter.test('restricts strict character codes to the processor character set', () => {
150
- const charCodeError = capture(() => run('', { isoStrict: true, goal: 'char_code(_,128)' }));
151
- equal(charCodeError.formal, 'representation_error(character_code)', 'char_code/2');
152
- const atomCodesError = capture(() => run('', { isoStrict: true, goal: 'atom_codes(_, [128])' }));
153
- equal(atomCodesError.formal, 'representation_error(character_code)', 'atom_codes/2');
154
- const putCodeError = capture(() => run('', { isoStrict: true, goal: 'put_code(128)' }));
155
- equal(putCodeError.formal, 'representation_error(character_code)', 'put_code/1');
162
+ reporter.test('restricts character codes only to the Unicode scalar PCS boundary', () => {
163
+ equal(run('', { isoStrict: true, goal: 'char_code(_,128)' }).stats.completed_goal_lists, 1,
164
+ 'code 128 is a PCS member');
165
+ equal(capture(() => run('', { isoStrict: true, goal: 'char_code(_,55296)' })).formal,
166
+ 'representation_error(character_code)', 'surrogate is not a scalar');
167
+ equal(capture(() => run('', { isoStrict: true, goal: 'atom_codes(_, [1114112])' })).formal,
168
+ 'representation_error(character_code)', 'above Unicode scalar range');
169
+ equal(capture(() => run('', { isoStrict: true, goal: 'put_code(1114112)' })).formal,
170
+ 'representation_error(character_code)', 'put_code/1 above scalar range');
156
171
  });
157
172
 
158
- reporter.test('keeps broader Unicode character handling as a normal-mode extension', () => {
159
- const result = run('', { goal: "char_code('é',233)" });
160
- equal(result.stdout, "char_code('é', 233).\n", 'normal Unicode char_code/2');
173
+ reporter.test('uses the same processor character repertoire in normal and strict profiles', () => {
174
+ const normal = run('', { goal: "char_code('é',233)" });
175
+ const strict = run('', { isoStrict: true, goal: "char_code('é',233)" });
176
+ equal(normal.stats.completed_goal_lists, 1, 'normal Unicode char_code/2');
177
+ equal(strict.stats.completed_goal_lists, 1, 'strict Unicode char_code/2');
161
178
  });
162
179
 
163
180
  reporter.test('keeps the strict write-option surface to Part 1 plus Corrigendum 3', () => {
@@ -282,11 +299,11 @@ export function runIsoStrict(reporter = new TestReporter()) {
282
299
 
283
300
  reporter.test('follows Part 1 arithmetic type and exceptional errors', () => {
284
301
  const cases = [
285
- ["X is '+'(foo,77)", 'type_error(number)', 'simple arithmetic atomic operand'],
286
- ['X is mod(foo,77)', 'type_error(number)', 'integer arithmetic non-number operand'],
302
+ ["X is '+'(foo,77)", 'type_error(evaluable)', 'STC #69 simple arithmetic atom is foo/0'],
303
+ ['X is mod(foo,77)', 'type_error(evaluable)', 'integer arithmetic atom is non-evaluable'],
287
304
  ['X is mod(7.5,2)', 'type_error(integer)', 'integer arithmetic numeric type'],
288
- ['X is truncate(foo)', 'type_error(number)', 'rounding non-number operand'],
289
- ['X is sin(foo)', 'type_error(number)', 'transcendental non-number operand'],
305
+ ['X is truncate(foo)', 'type_error(evaluable)', 'rounding atom is non-evaluable'],
306
+ ['X is sin(foo)', 'type_error(evaluable)', 'transcendental atom is non-evaluable'],
290
307
  ['X is foo+Y', 'instantiation_error', 'direct variable before another operand error'],
291
308
  ['X is floor(7)', 'type_error(float)', 'floor integer operand'],
292
309
  ['X is truncate(7)', 'type_error(float)', 'truncate integer operand'],
@@ -300,6 +317,56 @@ export function runIsoStrict(reporter = new TestReporter()) {
300
317
  }
301
318
  });
302
319
 
320
+ reporter.test('uses the STC #69 evaluable culprit for atomic arithmetic expressions', () => {
321
+ const error = capture(() => run('', { isoStrict: true, goal: "X is '+'(foo,77)" }));
322
+ equal(error.formal, 'type_error(evaluable)', 'formal error');
323
+ includes(error.message, '/(foo, 0)', 'foo/0 culprit');
324
+ });
325
+
326
+ reporter.test('applies integer-to-float conversion before floating evaluable functors', () => {
327
+ const huge = `1${'0'.repeat(400)}`;
328
+ for (const functor of ['float', 'sin', 'cos', 'atan', 'asin', 'acos', 'tan', 'exp', 'log', 'sqrt']) {
329
+ equal(capture(() => run('', { isoStrict: true, goal: `X is ${functor}(${huge})` })).formal,
330
+ 'evaluation_error(float_overflow)', `${functor}/1 integer-to-float overflow`);
331
+ }
332
+ });
333
+
334
+ reporter.test('reports explicit transcendental and power underflow in strict mode', () => {
335
+ for (const [goal, label] of [
336
+ ['X is exp(-1000.0)', 'exp/1 underflow'],
337
+ ['X is 2.0 ** -1075.0', '**/2 underflow'],
338
+ ['X is 2.0 ^ -1075.0', 'Corrigendum 2 ^/2 underflow'],
339
+ ]) {
340
+ equal(capture(() => run('', { isoStrict: true, goal })).formal,
341
+ 'evaluation_error(underflow)', label);
342
+ }
343
+
344
+ equal(run('', { isoStrict: true, goal: 'X is 0.0 ** 2.0' }).stats.completed_goal_lists,
345
+ 1, 'exact zero power remains zero');
346
+ equal(run('', { isoStrict: true, goal: 'X is 4.9406564584124654e-324 * 0.5' }).stats.completed_goal_lists,
347
+ 1, 'generic resultF retains the selected round-to-zero policy');
348
+
349
+ // Keep these stricter exceptional conditions confined to --iso-strict.
350
+ equal(run('', { goal: 'X is exp(-1000.0)' }).stats.completed_goal_lists, 1, 'normal exp behavior');
351
+ equal(run('', { goal: 'X is 2.0 ** -1075.0' }).stats.completed_goal_lists, 1, 'normal power behavior');
352
+ });
353
+
354
+ reporter.test('preserves prescribed power errors before integer-to-float conversion', () => {
355
+ const hugeNegative = `-1${'0'.repeat(400)}`;
356
+ equal(capture(() => run('', { isoStrict: true, goal: 'X is (-2) ** 2.0' })).formal,
357
+ 'evaluation_error(undefined)', '**/2 negative base requires integer-typed exponent');
358
+ equal(run('', { isoStrict: true, goal: 'X is (-2.0) ** 2' }).stats.completed_goal_lists,
359
+ 1, '**/2 negative base with integer exponent');
360
+ equal(run('', { isoStrict: true, goal: 'X is (-2) ^ 2.0' }).stats.completed_goal_lists,
361
+ 1, 'Corrigendum 2 ^/2 accepts integer-valued float exponent');
362
+ equal(capture(() => run('', { isoStrict: true, goal: 'X is (-2) ^ 0.5' })).formal,
363
+ 'evaluation_error(undefined)', 'Corrigendum 2 ^/2 fractional exponent');
364
+ equal(capture(() => run('', { isoStrict: true, goal: `X is ${hugeNegative} ** 0.5` })).formal,
365
+ 'evaluation_error(undefined)', 'undefined power precedes base conversion overflow');
366
+ equal(capture(() => run('', { isoStrict: true, goal: `X is 0.0 ** (${hugeNegative})` })).formal,
367
+ 'evaluation_error(undefined)', 'zero-negative power precedes exponent conversion overflow');
368
+ });
369
+
303
370
  reporter.test('uses the Part 1 mixed arithmetic comparison operations', () => {
304
371
  // 8.7 converts the integer operand to float in mixed comparisons.
305
372
  equal(run('', { isoStrict: true, goal: '18014398509481985 =:= 18014398509481984.0' }).stats.completed_goal_lists,
@@ -362,8 +429,15 @@ export function runIsoStrict(reporter = new TestReporter()) {
362
429
  });
363
430
 
364
431
  reporter.test('pins applicable post-Corrigendum STC clarifications', () => {
432
+ equal(capture(() => run('', { isoStrict: true, goal: 'X is 1/0+_' })).formal,
433
+ 'instantiation_error', 'STC 17 direct variable precedes zero divisor on the other branch');
434
+ equal(capture(() => run('', { isoStrict: true, goal: 'X is _+1/0' })).formal,
435
+ 'instantiation_error', 'STC 17 is independent of operand order');
436
+
365
437
  equal(run('', { isoStrict: true, goal: 'integer(- /**/ 1)' }).stats.completed_goal_lists,
366
438
  1, 'layout between minus and integer token');
439
+ equal(run('', { isoStrict: true, goal: "number_chars(N,['0','1']),N=1" }).stats.completed_goal_lists,
440
+ 1, 'STC 32 number_chars/2 follows the procedural number syntax for leading zero');
367
441
 
368
442
  const shared = run(
369
443
  ':- dynamic(a/1).\na(X) :- b(X).\n',
@@ -387,7 +461,13 @@ export function runIsoStrict(reporter = new TestReporter()) {
387
461
 
388
462
  Program.parse(':- op(500,xfx,foo).\na foo b.\n', { isoStrict: true });
389
463
  const beforeDefinition = capture(() => Program.parse('a foo b.\n:- op(500,xfx,foo).\n', { isoStrict: true }));
390
- includes(beforeDefinition.message, 'expected .', 'op/3 directive applies only to following text');
464
+ includes(beforeDefinition.message, 'expected .', 'STC 41 op/3 directive applies only to following text');
465
+
466
+ equal(run('', {
467
+ isoStrict: true,
468
+ goal: 'read_term(T,[variables([])])',
469
+ ioOptions: { input: 'T.' },
470
+ }).stats.completed_goal_lists, 0, 'STC 48 variables([]) does not match an input variable');
391
471
  });
392
472
 
393
473
  reporter.test('rejects EyeProlog module directives', () => {
@@ -5119,12 +5119,11 @@ this part makes control, reflection, state, operators, and streams explicit.
5119
5119
  For Part 1 portability work, EyeProlog also provides a strict core mode:
5120
5120
  `--iso-strict` on the CLI or `isoStrict: true` in the JavaScript API restricts
5121
5121
  the language/runtime surface to ISO/IEC 13211-1:1995 plus Technical Corrigenda
5122
- 1–3. Its processor character set is the 128-character ASCII set U+0000..U+007F;
5123
- C0 controls and DEL are implementation-defined extended layout characters, and
5124
- collating-sequence integers are the corresponding ASCII codes. Character data
5125
- outside that PCS is rejected with a representation error in strict mode. Normal
5126
- mode keeps EyeProlog's broader Unicode scalar character support as an explicit
5127
- extension. Isolated mode and error cases live in `test/conformance/cases/iso/`.
5122
+ 1–3. The processor character set is an implementation-defined choice shared by
5123
+ normal and strict profiles: EyeProlog uses Unicode scalar values U+0000..U+10FFFF
5124
+ excluding surrogates, with the scalar value as the collating-sequence integer.
5125
+ Strict mode restricts implementation-specific language facilities, but it does
5126
+ not narrow this processor-defined character repertoire. Isolated mode and error cases live in `test/conformance/cases/iso/`.
5128
5127
  The examples here compose those operations into programs worth changing and
5129
5128
  rerunning.
5130
5129
 
@@ -5510,13 +5509,14 @@ lowercase ASCII letter. Variables begin with uppercase or underscore. The bare
5510
5509
  ISO double-quoted-list notation. Integers, decimals, scientific notation,
5511
5510
  binary/octal/hexadecimal integers, and character-code constants are accepted.
5512
5511
 
5513
- For `--iso-strict`, the processor character set is deliberately narrower and
5514
- fully documented: U+0000..U+007F. Printable ASCII uses the Part 1 lexical
5515
- classes; C0 controls and DEL are extended layout characters; character-code and
5516
- collation values are the same ASCII integers 0..127. Non-ASCII input is a
5517
- representation error. In normal mode, unquoted names still deliberately use
5518
- ASCII spelling while Unicode scalar values may appear inside quoted atoms and
5519
- double-quoted lists:
5512
+ The processor character set is shared by normal and `--iso-strict` modes because
5513
+ Part 1 makes it implementation defined rather than an extension boundary.
5514
+ EyeProlog's PCS is the Unicode scalar repertoire. Printable ASCII keeps the Part
5515
+ 1 lexical classes; Unicode letters extend alphanumeric name syntax, Unicode
5516
+ white-space characters are layout, and remaining non-ASCII symbols/punctuation
5517
+ are extended graphic characters. Character-code and collation values are the
5518
+ corresponding Unicode scalar integers. Quoting remains available for any atom
5519
+ spelling that should not depend on an extended lexical class:
5520
5520
 
5521
5521
  ```eyeprolog
5522
5522
 
@@ -6049,8 +6049,8 @@ quoted_atom("ab"). % quoted_atom(ab)
6049
6049
  | `atom_length(+Atom,?Length)` | Counts Unicode code points, not UTF-16 code units. A supplied length must be a nonnegative integer. |
6050
6050
  | `atom_concat(?Prefix,?Suffix,?Whole)` | Concatenates two atoms, removes a supplied prefix or suffix, or enumerates every split when only `Whole` is bound. At least `Whole`, or both parts, must determine the operation. |
6051
6051
  | `sub_atom(+Atom,?Before,?Length,?After,?SubAtom)` | Enumerates substrings and their Unicode-code-point offsets. Supplied counts must be nonnegative integers. |
6052
- | `atom_chars(?Atom,?Chars)`, `atom_codes(?Atom,?Codes)` | Convert between an atom and a proper list of one-character atoms or character codes. Strict mode uses its ASCII PCS/codes `0..127`; normal mode extends codes to Unicode scalar values. At least one side must be instantiated. |
6053
- | `char_code(?Character,?Code)` | Converts one character atom and its collating/code value. Strict mode accepts only the ASCII PCS `0..127`; normal mode accepts Unicode scalar codes and rejects surrogates/out-of-range values. |
6052
+ | `atom_chars(?Atom,?Chars)`, `atom_codes(?Atom,?Codes)` | Convert between an atom and a proper list of one-character atoms or character codes. Both profiles use EyeProlog's Unicode scalar PCS/codes; surrogates and values above U+10FFFF are rejected. At least one side must be instantiated. |
6053
+ | `char_code(?Character,?Code)` | Converts one character atom and its collating/code value. Both profiles accept Unicode scalar codes and reject surrogates/out-of-range values. |
6054
6054
  | `number_chars(?Number,?Chars)`, `number_codes(?Number,?Codes)` | Convert finite numbers to canonical text or parse a proper character/code list using ISO number and negative-number syntax, including radix integers, character-code constants, and leading layout. The input is not parsed as a general term: grouping such as `(0)` is a syntax error. At least one side must be instantiated; malformed numeric input raises *syntax_error(number)*. |
6055
6055
 
6056
6056
  Conversions accept partial output lists when the atomic input is known, but
@@ -6091,12 +6091,12 @@ input or output.
6091
6091
  | `at_end_of_stream`, `at_end_of_stream(+Stream)` | Succeeds when the current or selected input position is at or beyond its content. |
6092
6092
  | `get_char(?Character)`, `get_char(+Stream,?Character)` | Reads one text character; end of input is `end_of_file`. |
6093
6093
  | `peek_char(?Character)`, `peek_char(+Stream,?Character)` | Observes the next text character without advancing. |
6094
- | `get_code(?Code)`, `get_code(+Stream,?Code)` | Reads a character code; end of input is `-1`. Strict mode requires the ASCII PCS, while normal mode returns Unicode scalar codes. |
6095
- | `peek_code(?Code)`, `peek_code(+Stream,?Code)` | Observes the next character code without advancing; strict mode requires the ASCII PCS. |
6094
+ | `get_code(?Code)`, `get_code(+Stream,?Code)` | Reads a character code; end of input is `-1`. Both profiles return codes from EyeProlog's Unicode scalar PCS. |
6095
+ | `peek_code(?Code)`, `peek_code(+Stream,?Code)` | Observes the next Unicode scalar character code without advancing. |
6096
6096
  | `get_byte(?Byte)`, `get_byte(+Stream,?Byte)` | Reads one unit from a binary stream; end of input is `-1`. |
6097
6097
  | `peek_byte(?Byte)`, `peek_byte(+Stream,?Byte)` | Observes the next binary unit without advancing. |
6098
6098
  | `put_char(+Character)`, `put_char(+Stream,+Character)` | Writes one character atom to a text stream. |
6099
- | `put_code(+Code)`, `put_code(+Stream,+Code)` | Writes one character code to a text stream. Strict mode accepts `0..127`; normal mode accepts Unicode scalar codes. |
6099
+ | `put_code(+Code)`, `put_code(+Stream,+Code)` | Writes one character code to a text stream. Both profiles accept Unicode scalar codes. |
6100
6100
  | `put_byte(+Byte)`, `put_byte(+Stream,+Byte)` | Writes an integer in `0..255` to a binary stream. |
6101
6101
  | `nl`, `nl(+Stream)` | Writes a newline to a text stream. |
6102
6102
 
package/why-eyeprolog.md CHANGED
@@ -19,10 +19,11 @@ 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. Strict mode makes its processor character
23
- model explicit: 7-bit ASCII is the PCS, ASCII code points are the collating
24
- integers, and normal-mode Unicode character data is an extension rather than an
25
- implicit part of the Part 1 claim. Its executable conformance matrix and tests
22
+ profiles for normal-mode programs. The processor character model is explicitly
23
+ implementation defined: EyeProlog uses Unicode scalar values as the PCS and as
24
+ collating-sequence integers in both normal and strict profiles. Strict mode
25
+ therefore rejects implementation-specific language extensions without changing
26
+ that processor choice. Its executable conformance matrix and tests
26
27
  document the supported behavior, including an executable trace of the vendored
27
28
  active WG17 syntax cases. This is extensive implementation evidence, not a
28
29
  claim that every Part 1, Part 2, or Part 3 normative requirement has already