eyeprolog 1.2.21 → 1.2.22

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.21",
6
+ "version": "1.2.22",
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
@@ -31,13 +31,23 @@ export async function runRepl(engine, options = {}) {
31
31
  if (text == null) break;
32
32
  if (!text.trim()) continue;
33
33
 
34
+ let resultIndent = ' ';
34
35
  try {
35
36
  const goal = parseGoal(engine, state, text);
37
+ // A complete, successfully parsed query has left the top-level reader
38
+ // and is about to execute. Show two spaces immediately so a terminal
39
+ // user can distinguish that state from a reader waiting for more text;
40
+ // the final result adds the third indentation space below. A direct
41
+ // halt has no result and exits immediately, so it needs no marker.
42
+ if (!isHaltGoal(goal)) {
43
+ output.write(' ');
44
+ resultIndent = ' ';
45
+ }
36
46
  if (!options.isoStrict && isUseModuleGoal(goal)) {
37
47
  sources.push({ text: `:- ${text}.\n`, filename: '<repl>' });
38
48
  state = makeState(engine, sources, output, options, state, reader);
39
49
  runWithTerminalSignals(reader, () => state.solver.runInitializations());
40
- output.write(' true.\n');
50
+ output.write(' true.\n');
41
51
  continue;
42
52
  }
43
53
  const consultFiles = options.isoStrict ? null : consultDesignations(engine, goal);
@@ -45,7 +55,7 @@ export async function runRepl(engine, options = {}) {
45
55
  for (const filename of consultFiles) sources.push(await readSource(filename));
46
56
  state = makeState(engine, sources, output, options, state, reader);
47
57
  runWithTerminalSignals(reader, () => state.solver.runInitializations());
48
- output.write(' true.\n');
58
+ output.write(' true.\n');
49
59
  continue;
50
60
  }
51
61
 
@@ -65,7 +75,7 @@ export async function runRepl(engine, options = {}) {
65
75
  exitCode = error.code;
66
76
  break;
67
77
  }
68
- output.write(` ${formatError(engine, state, error)}\n`);
78
+ output.write(`${resultIndent}${formatError(engine, state, error)}\n`);
69
79
  }
70
80
  }
71
81
  } catch (error) {
@@ -442,6 +452,11 @@ function isUseModuleGoal(goal) {
442
452
  return goal.type === 'compound' && goal.name === 'use_module' && [1, 2].includes(goal.arity);
443
453
  }
444
454
 
455
+ function isHaltGoal(goal) {
456
+ return (goal.type === 'atom' && goal.name === 'halt') ||
457
+ (goal.type === 'compound' && goal.name === 'halt' && goal.arity === 1);
458
+ }
459
+
445
460
  function consultDesignations(engine, goal) {
446
461
  if (goal.type === 'atom' && goal.name === '[]') return [];
447
462
  if (goal.type !== 'compound' || goal.name !== '.' || goal.arity !== 2) return null;
@@ -481,7 +496,7 @@ async function solveQuery(engine, state, goal, reader, output) {
481
496
  }
482
497
 
483
498
  if (current.result.done) {
484
- output.write(' false.\n');
499
+ output.write(' false.\n');
485
500
  return null;
486
501
  }
487
502
 
@@ -498,7 +513,7 @@ async function solveQuery(engine, state, goal, reader, output) {
498
513
  if (formattingAfterAdvance) output.write(' ');
499
514
  formattingAfterAdvance = false;
500
515
  output.write(current.output);
501
- output.write(`${firstAnswer ? ' ' : ''}${formatAnswer(engine, state, variables, current.result.value)}`);
516
+ output.write(`${firstAnswer ? ' ' : ''}${formatAnswer(engine, state, variables, current.result.value)}`);
502
517
  answersShown++;
503
518
  firstAnswer = false;
504
519
  if (!next.error && next.result.done) {
@@ -820,7 +820,7 @@ c4 ?- call((!;1)).
820
820
  },
821
821
  },
822
822
  {
823
- name: 'REPL answer prompt distinguishes waiting from computation',
823
+ name: 'REPL query and answer prompts distinguish waiting from computation',
824
824
  run: () => {
825
825
  const helper = `
826
826
  import { spawn } from 'node:child_process';
@@ -833,9 +833,11 @@ c4 ?- call((!;1)).
833
833
  child.stderr.setEncoding('utf8');
834
834
  let stdout = '';
835
835
  let stderr = '';
836
+ let sawQueryComputingPrompt = false;
836
837
  let sawComputingPrompt = false;
837
838
  child.stdout.on('data', (text) => {
838
839
  stdout += text;
840
+ if (stdout.endsWith('?- ')) sawQueryComputingPrompt = true;
839
841
  if (stdout.endsWith('\\n; ')) sawComputingPrompt = true;
840
842
  });
841
843
  child.stderr.on('data', (text) => { stderr += text; });
@@ -852,6 +854,10 @@ c4 ?- call((!;1)).
852
854
 
853
855
  child.stdin.write('use_module(library(prologue)).\\n');
854
856
  await waitFor(() => stdout.includes(' true.\\n?- '), 'module import');
857
+ sawQueryComputingPrompt = false;
858
+ child.stdin.write('between(0,0xff,I),I<0.\\n');
859
+ await waitFor(() => sawQueryComputingPrompt, 'query computing prompt');
860
+ await waitFor(() => stdout.endsWith(' false.\\n?- '), 'query result');
855
861
  child.stdin.write('(N = 0; N = 1; (call_nth(repeat, 100000), N = 2)).\\n');
856
862
  await waitFor(() => stdout.endsWith(' N = 0\\n;'), 'waiting prompt');
857
863
  child.stdin.write(';\\n');
@@ -862,7 +868,7 @@ c4 ?- call((!;1)).
862
868
  child.stdin.write('halt.\\n');
863
869
  const status = await new Promise((resolve) => child.once('exit', resolve));
864
870
  if (status !== 0) throw new Error('child status ' + status + '; stderr=' + stderr);
865
- process.stdout.write('waiting;computing;formatting');
871
+ process.stdout.write('query-computing;waiting;computing;formatting');
866
872
  `;
867
873
  const result = spawnSync(process.execPath, [
868
874
  '--input-type=module',
@@ -871,7 +877,7 @@ c4 ?- call((!;1)).
871
877
  ], { cwd: packageRoot, encoding: 'utf8', timeout: 10000 });
872
878
  if (result.error) throw result.error;
873
879
  assertEqual(result.status, 0, `prompt helper status; stderr=${result.stderr}`);
874
- assertEqual(result.stdout, 'waiting;computing;formatting', 'prompt state sequence');
880
+ assertEqual(result.stdout, 'query-computing;waiting;computing;formatting', 'prompt state sequence');
875
881
  },
876
882
  },
877
883
  {
@@ -941,9 +947,9 @@ c4 ?- call((!;1)).
941
947
  });
942
948
  assertEqual(result.status, 0, 'exit status');
943
949
  assertEqual(result.stdout,
944
- "?- '\\a' true.\n" +
945
- "?- '\\a' true.\n" +
946
- "?- '\\a' true.\n" +
950
+ "?- '\\a' true.\n" +
951
+ "?- '\\a' true.\n" +
952
+ "?- '\\a' true.\n" +
947
953
  '?- ',
948
954
  'stdout');
949
955
  assertEqual(result.stderr, '', 'stderr');
@@ -956,7 +962,7 @@ c4 ?- call((!;1)).
956
962
  input: "writeq('\\0\\').\nhalt.\n",
957
963
  });
958
964
  assertEqual(result.status, 0, 'exit status');
959
- assertEqual(result.stdout, "?- '\\0\\' true.\n?- ", 'stdout');
965
+ assertEqual(result.stdout, "?- '\\0\\' true.\n?- ", 'stdout');
960
966
  assertEqual(result.stderr, '', 'stderr');
961
967
  },
962
968
  },
@@ -1091,7 +1097,7 @@ c4 ?- call((!;1)).
1091
1097
  input: "writeq('\\033\\').\nhalt.\n",
1092
1098
  });
1093
1099
  assertEqual(result.status, 0, 'exit status');
1094
- assertEqual(result.stdout, "?- '\\33\\' true.\n?- ", 'stdout');
1100
+ assertEqual(result.stdout, "?- '\\33\\' true.\n?- ", 'stdout');
1095
1101
  assertEqual(result.stderr, '', 'stderr');
1096
1102
  },
1097
1103
  },
@@ -1110,9 +1116,9 @@ c4 ?- call((!;1)).
1110
1116
  });
1111
1117
  assertEqual(result.status, 0, 'exit status');
1112
1118
  assertEqual(result.stdout,
1113
- '?- |: X = foo.\n' +
1114
- "?- |: Y = '\\a'.\n" +
1115
- '?- |: Z = bar.\n' +
1119
+ '?- |: X = foo.\n' +
1120
+ "?- |: Y = '\\a'.\n" +
1121
+ '?- |: Z = bar.\n' +
1116
1122
  '?- ',
1117
1123
  'stdout');
1118
1124
  assertEqual(result.stderr, '', 'stderr');
@@ -6285,10 +6285,13 @@ When another answer exists in an interactive terminal, press `;`, Space, or
6285
6285
  enumeration, `a` enumerates all remaining answers, and `f` advances to the
6286
6286
  next five-answer boundary (5, 10, 15, ... displayed leaf answers), regardless
6287
6287
  of how many answers were stepped through individually beforehand. `h` displays
6288
- the answer-control help. The answer prompt is `;` with no trailing space while
6289
- it waits for input; after an advance command, one space marks active search and
6290
- a second marks an answer ready for formatting. While a query is actively
6291
- computing, EyeProlog releases readline's terminal signal handling: `Ctrl-C`
6288
+ the answer-control help. Once the top-level reader has accepted a complete
6289
+ query, the following line begins with two spaces to mark active execution; a
6290
+ third space appears when its result is ready for formatting. The answer prompt
6291
+ is `;` with no trailing space while it waits for input; after an advance
6292
+ command, one space marks active search and a second marks an answer ready for
6293
+ formatting. While a query is actively computing, EyeProlog releases readline's
6294
+ terminal signal handling: `Ctrl-C`
6292
6295
  therefore terminates the current EyeProlog process immediately, and on POSIX
6293
6296
  terminals `Ctrl-Z` suspends it in the usual shell-managed way. This remains a
6294
6297
  host top-level convention rather than an ISO/IEC 13211-1 language feature. A