eyeprolog 1.3.53 → 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.53",
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;
@@ -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 pins direct-variable precedence, arithmetic operand 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. |
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, 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. |
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
@@ -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,7 +68,7 @@ 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`. |
@@ -110,7 +110,7 @@ families; `--iso-strict` is intended to remove their Part 1 interpretation.
110
110
 
111
111
  | Part 1 extension hook | EyeProlog normal-profile feature | Strict-core disposition |
112
112
  | --- | --- | --- |
113
- | 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. |
114
114
  | 5.5.2 Predefined operators | Part 3 `|` and EyeProlog's labelable infix `(?-)/2`; CLP(Z) operators when that library is imported | Only the Part 1 operator table is predefined; a conforming `op/3` may still add permitted operators. |
115
115
  | 5.5.3 Character-conversion mapping | No non-identity initial `Convc` extension | Identity initial mapping. |
116
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 |
@@ -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
 
@@ -35,6 +35,9 @@ regressions.
35
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. |
36
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. |
37
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. |
38
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. |
39
42
 
40
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,12 @@ 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
+
303
326
  reporter.test('applies integer-to-float conversion before floating evaluable functors', () => {
304
327
  const huge = `1${'0'.repeat(400)}`;
305
328
  for (const functor of ['float', 'sin', 'cos', 'atan', 'asin', 'acos', 'tan', 'exp', 'log', 'sqrt']) {
@@ -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