eyeprolog 1.2.4 → 1.2.5

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.
@@ -11,7 +11,7 @@ This report summarizes the file-based conformance corpus under `test/conformance
11
11
  | builtins | 11 | 0 | 0 | 0 | 11 |
12
12
  | context | 11 | 0 | 0 | 0 | 11 |
13
13
  | control | 15 | 0 | 0 | 0 | 15 |
14
- | iso | 167 | 209 | 0 | 0 | 376 |
14
+ | iso | 167 | 210 | 0 | 0 | 377 |
15
15
  | lists | 52 | 3 | 0 | 0 | 55 |
16
16
  | modules | 2 | 0 | 0 | 0 | 2 |
17
17
  | negation | 8 | 0 | 19 | 0 | 27 |
@@ -23,4 +23,4 @@ This report summarizes the file-based conformance corpus under `test/conformance
23
23
  | terms | 26 | 3 | 0 | 0 | 29 |
24
24
  | unification | 18 | 0 | 0 | 0 | 18 |
25
25
  | variables | 16 | 9 | 0 | 0 | 25 |
26
- | **Total** | **482** | **260** | **19** | **21** | **782** |
26
+ | **Total** | **482** | **261** | **19** | **21** | **783** |
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.2.4",
6
+ "version": "1.2.5",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/iso.js CHANGED
@@ -1075,9 +1075,12 @@ function quotedEscapeEnd(source, index) {
1075
1075
  while (/^[0-9A-Fa-f]$/.test(source[cursor] ?? '')) cursor++;
1076
1076
  return source[cursor] === '\\' ? cursor : Math.max(index + 1, cursor - 1);
1077
1077
  }
1078
- if (/^[0-7]$/.test(escaped)) {
1078
+ if (/^[0-9]$/.test(escaped)) {
1079
1079
  let cursor = index + 1;
1080
- while (/^[0-7]$/.test(source[cursor] ?? '')) cursor++;
1080
+ // Scan all decimal digits here, including 8 and 9. This scanner only
1081
+ // locates the end of a candidate quoted escape; the parser remains
1082
+ // authoritative and rejects non-octal digits.
1083
+ while (/^[0-9]$/.test(source[cursor] ?? '')) cursor++;
1081
1084
  return source[cursor] === '\\' ? cursor : Math.max(index + 1, cursor - 1);
1082
1085
  }
1083
1086
 
package/src/parser.js CHANGED
@@ -303,6 +303,10 @@ class Parser {
303
303
  if (this.take() !== '\\') throw new Error(`parse line ${line}: bad octal escape`);
304
304
  return String.fromCodePoint(Number.parseInt(digits, 8));
305
305
  }
306
+ // A backslash followed by a decimal digit is numeric-escape syntax, but
307
+ // ISO octal digits are limited to 0..7. Do not reinterpret \8 or \9 as
308
+ // implementation-specific one-character escapes.
309
+ if (/^[0-9]$/.test(escaped)) throw new Error(`parse line ${line}: bad octal escape`);
306
310
  return escaped;
307
311
  }
308
312
  nextToken() {
package/src/repl.js CHANGED
@@ -253,9 +253,12 @@ function quotedEscapeEnd(source, index) {
253
253
  while (/^[0-9A-Fa-f]$/.test(source[cursor] ?? '')) cursor++;
254
254
  return source[cursor] === '\\' ? cursor : Math.max(index + 1, cursor - 1);
255
255
  }
256
- if (/^[0-7]$/.test(escaped)) {
256
+ if (/^[0-9]$/.test(escaped)) {
257
257
  let cursor = index + 1;
258
- while (/^[0-7]$/.test(source[cursor] ?? '')) cursor++;
258
+ // Scan all decimal digits here, including 8 and 9. This scanner only
259
+ // locates the end of a candidate quoted escape; the parser remains
260
+ // authoritative and rejects non-octal digits.
261
+ while (/^[0-9]$/.test(source[cursor] ?? '')) cursor++;
259
262
  return source[cursor] === '\\' ? cursor : Math.max(index + 1, cursor - 1);
260
263
  }
261
264
 
package/src/write.js CHANGED
@@ -22,6 +22,7 @@ function quoteAtom(name) {
22
22
  for (const ch of name) {
23
23
  if (ch === "'") out += "''";
24
24
  else if (ch === '\\') out += '\\\\';
25
+ else if (ch === '\x00') out += '\\0\\';
25
26
  else if (ch === '\x07') out += '\\a';
26
27
  else if (ch === '\b') out += '\\b';
27
28
  else if (ch === '\r') out += '\\r';
@@ -59,6 +60,7 @@ function writeString(value) {
59
60
  let out = '"';
60
61
  for (const ch of value) {
61
62
  if (ch === '"' || ch === '\\') out += `\\${ch}`;
63
+ else if (ch === '\x00') out += '\\0\\';
62
64
  else if (ch === '\x07') out += '\\a';
63
65
  else if (ch === '\b') out += '\\b';
64
66
  else if (ch === '\r') out += '\\r';
@@ -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 | `scryer_lexical_terms`, `lexical_and_curly_terms`, `double_quoted_lists`, `corrigendum1_double_quote_operator`, `wg17_syntax_high_risk`, syntax error cases |
11
+ | Clause 6 lexical and term syntax | tokenizer, operator parser, lists, curly terms, quotes, numeric syntax, comments | `scryer_lexical_terms`, `lexical_and_curly_terms`, `double_quoted_lists`, `corrigendum1_double_quote_operator`, `wg17_syntax_high_risk`, `wg17_invalid_octal_escape`, 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 |
@@ -102,7 +102,7 @@ Selected cases are adapted from the ISO and standard-core suites of Logtalk,
102
102
  Scryer Prolog, Trealla Prolog, and SWI-Prolog. Their upstream identifiers and licenses
103
103
  are recorded in [THIRD_PARTY.md](THIRD_PARTY.md).
104
104
 
105
- The corpus has 376 cases in `iso/` and 782 file-based conformance cases in
105
+ The corpus has 377 cases in `iso/` and 783 file-based conformance cases in
106
106
  total. The generated `conformance-report.md` is the authoritative source for
107
107
  current category totals. Together with regression, documentation-sync, API,
108
108
  example, and book-example checks, `npm test` is the release gate.
@@ -1,6 +1,6 @@
1
1
  % High-risk ISO syntax/write regressions, independently derived from
2
2
  % ISO/IEC 13211-1 clauses 6.3, 6.4 and 7.10 and cross-checked against the
3
- % public WG17 conformity-testing syntax cases (#1, #14-15, #28-31, #33-34).
3
+ % public WG17 conformity-testing syntax cases (#1, #14-15, #28-31, #33-34, #301).
4
4
 
5
5
  %% goal: wg17_numeric_escape
6
6
  wg17_numeric_escape :-
@@ -24,3 +24,7 @@ wg17_operator_precedence :-
24
24
  %% goal: wg17_canonical_list
25
25
  wg17_canonical_list :-
26
26
  write_canonical([a]), nl.
27
+
28
+ %% goal: wg17_zero_character_escape
29
+ wg17_zero_character_escape :-
30
+ writeq('\0\'), nl.
@@ -11,3 +11,5 @@ a :- b, c
11
11
  wg17_operator_precedence.
12
12
  '.'(a,[])
13
13
  wg17_canonical_list.
14
+ '\0\'
15
+ wg17_zero_character_escape.
@@ -0,0 +1 @@
1
+ parse line 1: bad octal escape
@@ -671,6 +671,31 @@ c4 ?- call((!;1)).
671
671
  assertEqual(result.stderr, '', 'stderr');
672
672
  },
673
673
  },
674
+ {
675
+ name: 'writeq preserves the NUL character with an ISO octal escape',
676
+ run: () => {
677
+ const result = runCli([], {
678
+ input: "writeq('\\0\\').\nhalt.\n",
679
+ });
680
+ assertEqual(result.status, 0, 'exit status');
681
+ assertEqual(result.stdout, "?- '\\0\\' true.\n?- ", 'stdout');
682
+ assertEqual(result.stderr, '', 'stderr');
683
+ },
684
+ },
685
+ {
686
+ name: 'REPL rejects non-octal numeric escapes without waiting for continuation',
687
+ run: () => {
688
+ const result = runCli([], {
689
+ input: "'\\8\\'.\nhalt.\n",
690
+ });
691
+ assertEqual(result.status, 0, 'exit status');
692
+ assertEqual(result.stdout,
693
+ '?- parse line 1: bad octal escape.\n' +
694
+ '?- ',
695
+ 'stdout');
696
+ assertEqual(result.stderr, '', 'stderr');
697
+ },
698
+ },
674
699
  {
675
700
  name: 'REPL read predicates consume following interactive term input',
676
701
  run: () => {
@@ -1312,6 +1337,16 @@ c4 ?- call((!;1)).
1312
1337
  "answer('\\a').\n",
1313
1338
  'read/1 user_input numeric escape',
1314
1339
  );
1340
+
1341
+ const invalidOctal = String.fromCharCode(39, 92, 56, 92, 39, 46);
1342
+ assertEqual(
1343
+ run('answer(T) :- catch(read(T), E, T=E).\n', {
1344
+ goal: 'answer(T)',
1345
+ ioOptions: { input: invalidOctal },
1346
+ }).stdout,
1347
+ 'answer(error(syntax_error(read_term), eyeprolog)).\n',
1348
+ 'read/1 rejects non-octal numeric escape',
1349
+ );
1315
1350
  },
1316
1351
  },
1317
1352
  {
@@ -5279,7 +5279,10 @@ message("café").
5279
5279
  Inside a quoted atom, a single quote is doubled: `'don''t'`. Quoted characters
5280
5280
  support the ISO symbolic control escapes such as `\a`, `\n`, and `\t`, and numeric
5281
5281
  octal or hexadecimal escapes are terminated by a backslash; for example, `'\7\'`
5282
- and `'\x7\'` both denote the alert character. Double-quoted lists use the same
5282
+ and `'\x7\'` both denote the alert character. The NUL character, when present in
5283
+ the processor character set, is written readably as `'\0\'`; digits `8` and `9`
5284
+ are not octal digits, so forms such as `'\8\'` are syntax errors rather than
5285
+ continuation input. Double-quoted lists use the same
5283
5286
  quoted-character escapes. Whitespace is insignificant
5284
5287
  between tokens, and a `%` comment continues to the end of its line. Doubling
5285
5288
  the active delimiter is also accepted inside either quoted form, so `""`
@@ -6946,7 +6949,7 @@ precedence still need one-by-one closure. `test/conformance/ISO-MATRIX.md`
6946
6949
  maps language families to representative executable cases.
6947
6950
 
6948
6951
  The complete suite must pass before release. The file-based conformance corpus
6949
- contains 782 cases, including 376 focused ISO
6952
+ contains 783 cases, including 377 focused ISO
6950
6953
  cases derived from the success, failure, mode, and error behavior in
6951
6954
  ISO/IEC 13211-1 clauses 7 and 8, Part 2 modules, and Part 3 grammar rules.
6952
6955
  Separate exact-output suites check 189 normal