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 +1 -1
- package/src/repl.js +20 -5
- package/test/run-regression.mjs +17 -11
- package/the-art-of-eyeprolog.md +7 -4
package/package.json
CHANGED
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('
|
|
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('
|
|
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(
|
|
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('
|
|
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 ? '
|
|
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) {
|
package/test/run-regression.mjs
CHANGED
|
@@ -820,7 +820,7 @@ c4 ?- call((!;1)).
|
|
|
820
820
|
},
|
|
821
821
|
},
|
|
822
822
|
{
|
|
823
|
-
name: 'REPL answer
|
|
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
|
-
"?-
|
|
945
|
-
"?-
|
|
946
|
-
"?-
|
|
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, "?-
|
|
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, "?-
|
|
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
|
-
'?-
|
|
1114
|
-
"?-
|
|
1115
|
-
'?-
|
|
1119
|
+
'?- |: X = foo.\n' +
|
|
1120
|
+
"?- |: Y = '\\a'.\n" +
|
|
1121
|
+
'?- |: Z = bar.\n' +
|
|
1116
1122
|
'?- ',
|
|
1117
1123
|
'stdout');
|
|
1118
1124
|
assertEqual(result.stderr, '', 'stderr');
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -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.
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
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
|