eyeprolog 1.2.16 → 1.2.18

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 | 218 | 0 | 0 | 385 |
14
+ | iso | 168 | 218 | 0 | 0 | 386 |
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** | **269** | **19** | **21** | **791** |
26
+ | **Total** | **483** | **269** | **19** | **21** | **792** |
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.2.16",
6
+ "version": "1.2.18",
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
@@ -1556,7 +1556,15 @@ function parseIsoNumber(text) {
1556
1556
  let sign = '';
1557
1557
 
1558
1558
  if (text[position] === '-') {
1559
- if (/[\u0009-\u000d\u0020]/.test(text[position + 1] ?? '')) {
1559
+ const next = text[position + 1] ?? '';
1560
+ // Every token class may carry leading layout (6.4). Thus a negative
1561
+ // number may have layout between the name `-` and its numeric token. A
1562
+ // `%...\n` comment can start immediately after `-` because `%` cannot
1563
+ // continue a graphic name token. In contrast `/*...*/` cannot start
1564
+ // there without separating layout: `/` *can* continue the graphic token,
1565
+ // and the eager-consumer rule therefore keeps `-/**/1` ill-formed (the
1566
+ // number_chars continuation corpus case 24).
1567
+ if (/[\u0009-\u000d\u0020]/.test(next) || next === '%') {
1560
1568
  position = skipNumberLayout(text, position + 1);
1561
1569
  sign = '-';
1562
1570
  }
package/src/repl.js CHANGED
@@ -9,7 +9,7 @@ const ANSWER_HELP = `
9
9
  SPACE, "n" or ";": next solution, if any
10
10
  RETURN or ".": stop enumeration
11
11
  "a": enumerate all solutions
12
- "f": enumerate the next 5 solutions
12
+ "f": enumerate through the next group of 5 solutions
13
13
  "h": display this help message
14
14
  "w": write terms without depth limit
15
15
  "p": print terms with depth limit
@@ -486,11 +486,13 @@ async function solveQuery(engine, state, goal, reader, output) {
486
486
  }
487
487
 
488
488
  let automatic = 0;
489
+ let answersShown = 0;
489
490
  let firstAnswer = true;
490
491
  while (!current.result.done) {
491
492
  const next = pullSolution(solver, solutions, reader);
492
493
  output.write(current.output);
493
494
  output.write(`${firstAnswer ? ' ' : ''}${formatAnswer(engine, state, variables, current.result.value)}`);
495
+ answersShown++;
494
496
  firstAnswer = false;
495
497
  if (!next.error && next.result.done) {
496
498
  output.write('.\n');
@@ -516,7 +518,12 @@ async function solveQuery(engine, state, goal, reader, output) {
516
518
  break;
517
519
  }
518
520
  if (control === 'f') {
519
- automatic = 4;
521
+ // `f` groups leaf answers in blocks of five, rather than merely
522
+ // adding five more answers after whatever the user has already
523
+ // inspected. Stop for control again at the next 5-answer boundary.
524
+ const remainder = answersShown % 5;
525
+ const answersToBoundary = remainder === 0 ? 5 : 5 - remainder;
526
+ automatic = answersToBoundary - 1;
520
527
  break;
521
528
  }
522
529
  if (control === 'w' || control === 'p') {
@@ -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 385 cases in `iso/` and 791 file-based conformance cases in
105
+ The corpus has 386 cases in `iso/` and 792 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.
@@ -0,0 +1,8 @@
1
+ % ISO 13211-1 8.16.7/8.16.8 with 6.3.1 and token layout from 6.4.
2
+ %% goal: answer(X0, X1, X2, X3)
3
+
4
+ answer(CharZero, CharOne, CodeZero, CodeOne) :-
5
+ number_chars(CharZero, "-%\n0"),
6
+ number_chars(CharOne, "-% comment\n1"),
7
+ number_codes(CodeZero, [45, 37, 10, 48]),
8
+ number_codes(CodeOne, [45, 37, 32, 99, 111, 109, 109, 101, 110, 116, 10, 49]).
@@ -0,0 +1 @@
1
+ answer(0, -1, 0, -1).
@@ -39,6 +39,9 @@
39
39
  73 ?- number_chars(N,"(0)").
40
40
  syntax_error(...).
41
41
 
42
+ 74 ?- number_chars(N,"-%\n0").
43
+ N = 0.
44
+
42
45
  4 ?- number_chars(1,"a").
43
46
  syntax_error(...).
44
47
 
@@ -491,13 +491,51 @@ c4 ?- call((!;1)).
491
491
  run: () => {
492
492
  const filename = path.join(testRoot, 'fixtures', 'number_chars_cont_quad.pl');
493
493
  const source = fs.readFileSync(filename, 'utf8');
494
+ const numbered = new Set([...source.matchAll(/^(\d+)\s+\?-/gm)].map((match) => Number(match[1])));
495
+ assertEqual(numbered.size, 74, 'numbered case total');
496
+ for (let id = 1; id <= 74; id++) {
497
+ if (!numbered.has(id)) throw new Error(`number_chars continuation case #${id} is missing`);
498
+ }
494
499
  const result = publicApi.runQuads(Program.parseSources([{
495
500
  text: source,
496
501
  filename,
497
502
  }]));
498
- assertEqual(result.total, 77, 'answer-description total');
499
- assertEqual(result.passed, 77, 'answer-description passed');
500
- assertEqual(result.stdout, 'quads: 77 run, 77 passed, 0 failed.\n', 'quad report');
503
+ assertEqual(result.total, 78, 'answer-description total');
504
+ assertEqual(result.passed, 78, 'answer-description passed');
505
+ assertEqual(result.stdout, 'quads: 78 run, 78 passed, 0 failed.\n', 'quad report');
506
+ },
507
+ },
508
+ {
509
+ name: 'number conversion accepts line-comment layout after a minus token',
510
+ run: () => {
511
+ const source = String.raw`
512
+ ?- number_chars(N,"-%\n0").
513
+ N = 0.
514
+ ?- number_chars(N,"-% comment\n1").
515
+ N = -1.
516
+ ?- number_codes(N,[45,37,10,48]).
517
+ N = 0.
518
+ ?- number_codes(N,[45,37,32,99,111,109,109,101,110,116,10,49]).
519
+ N = -1.
520
+ `;
521
+ const result = publicApi.runQuads(source);
522
+ assertEqual(result.total, 4, 'quad total');
523
+ assertEqual(result.passed, 4, 'quad passed');
524
+ assertEqual(result.stdout, 'quads: 4 run, 4 passed, 0 failed.\n', 'quad report');
525
+
526
+ // Keep the eager-consumer distinction from continuation case #24:
527
+ // an adjacent bracketed comment can be consumed as part of a graphic
528
+ // token after `-`, so it is not equivalent to the `%` line comment.
529
+ for (const goal of ['number_chars(N,"-/**/1")', 'number_codes(N,[45,47,42,42,47,49])']) {
530
+ let caught = null;
531
+ try {
532
+ publicApi.run('', { goal });
533
+ } catch (error) {
534
+ caught = error;
535
+ }
536
+ if (caught == null) throw new Error(`${goal} should throw`);
537
+ assertIncludes(String(caught?.message ?? caught), 'syntax_error(number)', goal);
538
+ }
501
539
  },
502
540
  },
503
541
  {
@@ -752,6 +790,48 @@ c4 ?- call((!;1)).
752
790
  assertEqual(result.stderr, '', 'stderr');
753
791
  },
754
792
  },
793
+ {
794
+ name: 'REPL f stops at five-answer boundaries instead of adding five answers',
795
+ run: () => {
796
+ const result = runCli([], {
797
+ input:
798
+ 'use_module(library(prologue), [between/3]).\n' +
799
+ 'between(0,11,I).\nf\n\n' +
800
+ 'between(0,11,J).\n;\n;\nf\n\n' +
801
+ 'between(0,11,K).\nf\nf\n\n' +
802
+ 'halt.\n',
803
+ });
804
+ assertEqual(result.status, 0, 'exit status');
805
+ assertEqual(result.stdout,
806
+ '?- true.\n' +
807
+ '?- I = 0\n' +
808
+ '; I = 1\n' +
809
+ '; I = 2\n' +
810
+ '; I = 3\n' +
811
+ '; I = 4\n' +
812
+ '; ... .\n' +
813
+ '?- J = 0\n' +
814
+ '; J = 1\n' +
815
+ '; J = 2\n' +
816
+ '; J = 3\n' +
817
+ '; J = 4\n' +
818
+ '; ... .\n' +
819
+ '?- K = 0\n' +
820
+ '; K = 1\n' +
821
+ '; K = 2\n' +
822
+ '; K = 3\n' +
823
+ '; K = 4\n' +
824
+ '; K = 5\n' +
825
+ '; K = 6\n' +
826
+ '; K = 7\n' +
827
+ '; K = 8\n' +
828
+ '; K = 9\n' +
829
+ '; ... .\n' +
830
+ '?- ',
831
+ 'stdout');
832
+ assertEqual(result.stderr, '', 'stderr');
833
+ },
834
+ },
755
835
  {
756
836
  name: 'REPL parenthesizes operator-valued answer substitutions',
757
837
  run: () => {
@@ -5758,12 +5758,16 @@ quoted_atom("ab"). % quoted_atom(ab)
5758
5758
 
5759
5759
  Conversions accept partial output lists when the atomic input is known, but
5760
5760
  constructing an atom or number requires a complete proper list with no unbound
5761
- elements. Numeric parsing accepts leading ISO layout characters, an optional
5762
- sign, decimal fractions, and decimal exponents; it rejects trailing material
5763
- and non-finite values. The regression gate vendors all 73 numbered cases from
5764
- Ulrich Neumerkel's contemporary `number_chars/2` comparison, including the
5765
- Cor.2 error-precedence cases; `number_codes/2` shares the same numeric parser
5766
- and has mirrored coverage for the parenthesized-number regression.
5761
+ elements. Numeric parsing accepts ISO layout before tokens, including layout between a
5762
+ minus token and the following numeric token. A single-line `%...` comment may
5763
+ therefore follow `-` directly because `%` cannot continue a graphic token; an
5764
+ adjacent bracketed comment in `-/**/1` remains a syntax error under the eager
5765
+ token-consumer rule. Decimal fractions and decimal exponents are supported;
5766
+ trailing material and non-finite values are rejected. The regression gate
5767
+ vendors all 74 numbered cases from Ulrich Neumerkel's contemporary
5768
+ `number_chars/2` comparison, including the Cor.2 error-precedence cases;
5769
+ `number_codes/2` shares the same numeric parser and has mirrored coverage for
5770
+ the recent numeric-syntax regressions.
5767
5771
 
5768
5772
  ### Streams and unit I/O
5769
5773
 
@@ -6264,8 +6268,10 @@ may span lines and end with a full stop, as in Scryer Prolog:
6264
6268
 
6265
6269
  When another answer exists in an interactive terminal, press `;`, Space, or
6266
6270
  `n` to ask for it immediately; no Return is needed. Return or `.` stops
6267
- enumeration, `a` enumerates all remaining answers, `f` enumerates the next
6268
- five, and `h` displays the answer-control help. While a query is actively
6271
+ enumeration, `a` enumerates all remaining answers, and `f` advances to the
6272
+ next five-answer boundary (5, 10, 15, ... displayed leaf answers), regardless
6273
+ of how many answers were stepped through individually beforehand. `h` displays
6274
+ the answer-control help. While a query is actively
6269
6275
  computing, EyeProlog releases readline's terminal signal handling: `Ctrl-C`
6270
6276
  therefore terminates the current EyeProlog process immediately, and on POSIX
6271
6277
  terminals `Ctrl-Z` suspends it in the usual shell-managed way. This remains a
@@ -7018,7 +7024,7 @@ precedence still need one-by-one closure. `test/conformance/ISO-MATRIX.md`
7018
7024
  maps language families to representative executable cases.
7019
7025
 
7020
7026
  The complete suite must pass before release. The file-based conformance corpus
7021
- contains 791 cases, including 385 focused ISO
7027
+ contains 792 cases, including 386 focused ISO
7022
7028
  cases derived from the success, failure, mode, and error behavior in
7023
7029
  ISO/IEC 13211-1 clauses 7 and 8, Part 2 modules, and Part 3 grammar rules.
7024
7030
  Separate exact-output suites check 189 normal