eyeprolog 1.2.15 → 1.2.17

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.2.15",
6
+ "version": "1.2.17",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
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') {
package/src/solver.js CHANGED
@@ -490,10 +490,11 @@ function normalizeHostResourceError(error) {
490
490
  const message = String(error?.message ?? '');
491
491
  // V8 reports exhausted Map/Set capacity as a host RangeError. ISO 7.12.2 h
492
492
  // requires processor resource exhaustion to surface as resource_error/1,
493
- // with the resource atom implementation dependent. EyeProlog uses the
494
- // finite_memory spelling already accepted by its ISO conformance corpus.
493
+ // with the resource atom implementation dependent. A finite host capacity
494
+ // ceiling is reported as `memory`; reserve `finite_memory` for the separate
495
+ // convention where no finite amount of memory can complete the computation.
495
496
  if (/^(?:Map|Set) maximum size exceeded$/.test(message)) {
496
- return new PrologError('resource_error(finite_memory)');
497
+ return new PrologError('resource_error(memory)');
497
498
  }
498
499
  return error;
499
500
  }
@@ -752,6 +752,48 @@ c4 ?- call((!;1)).
752
752
  assertEqual(result.stderr, '', 'stderr');
753
753
  },
754
754
  },
755
+ {
756
+ name: 'REPL f stops at five-answer boundaries instead of adding five answers',
757
+ run: () => {
758
+ const result = runCli([], {
759
+ input:
760
+ 'use_module(library(prologue), [between/3]).\n' +
761
+ 'between(0,11,I).\nf\n\n' +
762
+ 'between(0,11,J).\n;\n;\nf\n\n' +
763
+ 'between(0,11,K).\nf\nf\n\n' +
764
+ 'halt.\n',
765
+ });
766
+ assertEqual(result.status, 0, 'exit status');
767
+ assertEqual(result.stdout,
768
+ '?- true.\n' +
769
+ '?- I = 0\n' +
770
+ '; I = 1\n' +
771
+ '; I = 2\n' +
772
+ '; I = 3\n' +
773
+ '; I = 4\n' +
774
+ '; ... .\n' +
775
+ '?- J = 0\n' +
776
+ '; J = 1\n' +
777
+ '; J = 2\n' +
778
+ '; J = 3\n' +
779
+ '; J = 4\n' +
780
+ '; ... .\n' +
781
+ '?- K = 0\n' +
782
+ '; K = 1\n' +
783
+ '; K = 2\n' +
784
+ '; K = 3\n' +
785
+ '; K = 4\n' +
786
+ '; K = 5\n' +
787
+ '; K = 6\n' +
788
+ '; K = 7\n' +
789
+ '; K = 8\n' +
790
+ '; K = 9\n' +
791
+ '; ... .\n' +
792
+ '?- ',
793
+ 'stdout');
794
+ assertEqual(result.stderr, '', 'stderr');
795
+ },
796
+ },
755
797
  {
756
798
  name: 'REPL parenthesizes operator-valued answer substitutions',
757
799
  run: () => {
@@ -2221,22 +2263,27 @@ open(X) :- candidate(X), \\+ closed(X).
2221
2263
  },
2222
2264
  },
2223
2265
  {
2224
- name: 'host Map capacity errors become ISO resource errors',
2266
+ name: 'host Map/Set capacity errors become resource_error(memory)',
2225
2267
  run: () => {
2226
- const registry = new BuiltinRegistry();
2227
- registry.add('exhaust_map', 0, function* () {
2228
- throw new RangeError('Map maximum size exceeded');
2229
- });
2230
- const solver = new Solver(Program.parse(''), { registry });
2231
- const goal = parseGoalText('exhaust_map');
2232
- let caught = null;
2233
- try {
2234
- [...solver.solve([goal], new Env(), 0)];
2235
- } catch (error) {
2236
- caught = error;
2268
+ for (const [predicate, message] of [
2269
+ ['exhaust_map', 'Map maximum size exceeded'],
2270
+ ['exhaust_set', 'Set maximum size exceeded'],
2271
+ ]) {
2272
+ const registry = new BuiltinRegistry();
2273
+ registry.add(predicate, 0, function* () {
2274
+ throw new RangeError(message);
2275
+ });
2276
+ const solver = new Solver(Program.parse(''), { registry });
2277
+ const goal = parseGoalText(predicate);
2278
+ let caught = null;
2279
+ try {
2280
+ [...solver.solve([goal], new Env(), 0)];
2281
+ } catch (error) {
2282
+ caught = error;
2283
+ }
2284
+ assertEqual(caught?.name, 'PrologError', `${predicate} normalized error type`);
2285
+ assertEqual(caught?.formal, 'resource_error(memory)', `${predicate} normalized resource error`);
2237
2286
  }
2238
- assertEqual(caught?.name, 'PrologError', 'normalized error type');
2239
- assertEqual(caught?.formal, 'resource_error(finite_memory)', 'normalized resource error');
2240
2287
  },
2241
2288
  },
2242
2289
  {
@@ -1823,8 +1823,12 @@ sorted-list operation. No process-global variable registry or creation ordinal
1823
1823
  is retained or exposed through later comparisons.
1824
1824
 
1825
1825
  Host capacity failures that V8 reports as `Map maximum size exceeded` or `Set`
1826
- `maximum size exceeded` are normalized at the solver boundary to the ISO error
1827
- `resource_error(finite_memory)` instead of leaking a JavaScript `RangeError`.
1826
+ `maximum size exceeded` are normalized at the solver boundary to
1827
+ `resource_error(memory)` instead of leaking a JavaScript `RangeError`. ISO
1828
+ 13211-1 leaves the resource atom implementation dependent. EyeProlog uses
1829
+ `memory` for a finite host allocation/capacity ceiling and reserves the
1830
+ `finite_memory` spelling for the distinct convention where no finite amount of
1831
+ memory could complete the computation.
1828
1832
 
1829
1833
  ### Implementation boundary
1830
1834
 
@@ -6260,8 +6264,10 @@ may span lines and end with a full stop, as in Scryer Prolog:
6260
6264
 
6261
6265
  When another answer exists in an interactive terminal, press `;`, Space, or
6262
6266
  `n` to ask for it immediately; no Return is needed. Return or `.` stops
6263
- enumeration, `a` enumerates all remaining answers, `f` enumerates the next
6264
- five, and `h` displays the answer-control help. While a query is actively
6267
+ enumeration, `a` enumerates all remaining answers, and `f` advances to the
6268
+ next five-answer boundary (5, 10, 15, ... displayed leaf answers), regardless
6269
+ of how many answers were stepped through individually beforehand. `h` displays
6270
+ the answer-control help. While a query is actively
6265
6271
  computing, EyeProlog releases readline's terminal signal handling: `Ctrl-C`
6266
6272
  therefore terminates the current EyeProlog process immediately, and on POSIX
6267
6273
  terminals `Ctrl-Z` suspends it in the usual shell-managed way. This remains a