eyeprolog 1.1.26 → 1.1.28
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/iso.js +1 -1
- package/src/repl.js +22 -38
- package/src/write.js +4 -1
- package/test/run-regression.mjs +34 -2
- package/the-art-of-eyeprolog.md +13 -6
package/package.json
CHANGED
package/src/iso.js
CHANGED
|
@@ -1783,7 +1783,7 @@ function* phraseBuiltin({ solver, goal, env }) {
|
|
|
1783
1783
|
solver.absorbStatsFrom(child);
|
|
1784
1784
|
}
|
|
1785
1785
|
}
|
|
1786
|
-
function formalErrorTerm(error) {
|
|
1786
|
+
export function formalErrorTerm(error) {
|
|
1787
1787
|
const context = error.contextTerm ?? atom('eyeprolog');
|
|
1788
1788
|
if (error.formalTerm != null) return compound('error', [error.formalTerm, context]);
|
|
1789
1789
|
const parse = (text) => {
|
package/src/repl.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { createInterface } from 'node:readline';
|
|
5
|
+
import { formalErrorTerm } from './iso.js';
|
|
5
6
|
|
|
6
7
|
const ANSWER_HELP = `
|
|
7
8
|
SPACE, "n" or ";": next solution, if any
|
|
@@ -370,6 +371,17 @@ function formatAnswer(engine, state, variables, env) {
|
|
|
370
371
|
const bindings = [];
|
|
371
372
|
const queryVariableNames = new Set(variables.map((variable) => variable.name));
|
|
372
373
|
const names = new Map(variables.map((variable) => [variable.name, variable.name]));
|
|
374
|
+
const operators = [...state.program.operators.values()];
|
|
375
|
+
// Answer substitutions are displayed as `Variable = Value`. Format Value
|
|
376
|
+
// in the right-operand context of the active infix `=/2` definition so an
|
|
377
|
+
// operator term such as `a = b` is parenthesized rather than producing the
|
|
378
|
+
// invalid `T = a = b`. ISO does not standardize a top level, but the term
|
|
379
|
+
// syntax used by the display must still respect the current operator table.
|
|
380
|
+
const equality = operators.find((definition) =>
|
|
381
|
+
definition.name === '=' && ['xfx', 'xfy', 'yfx'].includes(definition.specifier));
|
|
382
|
+
const valueMaxPriority = equality == null
|
|
383
|
+
? 699
|
|
384
|
+
: equality.specifier === 'xfy' ? equality.priority : equality.priority - 1;
|
|
373
385
|
let generated = 0;
|
|
374
386
|
|
|
375
387
|
for (const variable of variables) collectUnboundVariables(engine, variable, env, names, () => `_${letterName(generated++)}`);
|
|
@@ -379,8 +391,9 @@ function formatAnswer(engine, state, variables, env) {
|
|
|
379
391
|
(value.name === variable.name || !queryVariableNames.has(value.name))) continue;
|
|
380
392
|
bindings.push(`${variable.name} = ${engine.formatTermForWrite(value, env, {
|
|
381
393
|
quoted: true,
|
|
382
|
-
operators
|
|
394
|
+
operators,
|
|
383
395
|
variableNames: names,
|
|
396
|
+
maxPriority: valueMaxPriority,
|
|
384
397
|
})}`);
|
|
385
398
|
}
|
|
386
399
|
return bindings.length === 0 ? 'true' : bindings.join(', ');
|
|
@@ -413,46 +426,17 @@ function formatError(engine, state, error) {
|
|
|
413
426
|
const env = new engine.Env();
|
|
414
427
|
const variableNames = new Map();
|
|
415
428
|
let generated = 0;
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
operators: [...state.program.operators.values()],
|
|
424
|
-
variableNames,
|
|
425
|
-
});
|
|
426
|
-
if (error.contextTerm != null) {
|
|
427
|
-
const context = engine.formatTermForWrite(error.contextTerm, env, {
|
|
428
|
-
quoted: true,
|
|
429
|
-
operators: [...state.program.operators.values()],
|
|
430
|
-
variableNames,
|
|
431
|
-
});
|
|
432
|
-
return `error(${formal}, ${context}).`;
|
|
433
|
-
}
|
|
434
|
-
return `error(${formal}).`;
|
|
435
|
-
}
|
|
436
|
-
if (error.culprit != null) {
|
|
437
|
-
collectUnboundVariables(engine, error.culprit, env, variableNames, () => `_${letterName(generated++)}`);
|
|
438
|
-
}
|
|
439
|
-
if (error.contextTerm != null) {
|
|
440
|
-
collectUnboundVariables(engine, error.contextTerm, env, variableNames, () => `_${letterName(generated++)}`);
|
|
441
|
-
}
|
|
442
|
-
const culprit = error.culprit == null ? '' : `, ${engine.formatTermForWrite(error.culprit, env, {
|
|
429
|
+
// ISO 7.12.1 represents processor errors as error(ErrorTerm, ImpDef).
|
|
430
|
+
// Reuse the same conversion as catch/3 so uncaught errors at the top
|
|
431
|
+
// level cannot lose the implementation-defined context or misplace a
|
|
432
|
+
// culprit as the second argument of error/2.
|
|
433
|
+
const term = formalErrorTerm(error);
|
|
434
|
+
collectUnboundVariables(engine, term, env, variableNames, () => `_${letterName(generated++)}`);
|
|
435
|
+
return `${engine.formatTermForWrite(term, env, {
|
|
443
436
|
quoted: true,
|
|
444
437
|
operators: [...state.program.operators.values()],
|
|
445
438
|
variableNames,
|
|
446
|
-
})}
|
|
447
|
-
if (error.contextTerm != null) {
|
|
448
|
-
const context = engine.formatTermForWrite(error.contextTerm, env, {
|
|
449
|
-
quoted: true,
|
|
450
|
-
operators: [...state.program.operators.values()],
|
|
451
|
-
variableNames,
|
|
452
|
-
});
|
|
453
|
-
return `error(${error.formal}${culprit}, ${context}).`;
|
|
454
|
-
}
|
|
455
|
-
return `error(${error.formal}${culprit}).`;
|
|
439
|
+
})}.`;
|
|
456
440
|
}
|
|
457
441
|
const message = error?.message ?? String(error);
|
|
458
442
|
return message.endsWith('.') ? message : `${message}.`;
|
package/src/write.js
CHANGED
|
@@ -208,5 +208,8 @@ export function formatTermForWrite(term, env = new Env(), options = {}) {
|
|
|
208
208
|
compact: options.compact === true,
|
|
209
209
|
operatorAtomsAsArgs: options.operatorAtomsAsArgs === true,
|
|
210
210
|
};
|
|
211
|
-
|
|
211
|
+
const maxPriority = Number.isInteger(options.maxPriority)
|
|
212
|
+
? Math.max(0, Math.min(1200, options.maxPriority))
|
|
213
|
+
: 1200;
|
|
214
|
+
return format(term, env, normalized, operatorTable(options.operators), maxPriority);
|
|
212
215
|
}
|
package/test/run-regression.mjs
CHANGED
|
@@ -533,8 +533,23 @@ c4 ?- call((!;1)).
|
|
|
533
533
|
});
|
|
534
534
|
assertEqual(result.status, 0, 'exit status');
|
|
535
535
|
assertEqual(result.stdout,
|
|
536
|
-
'?- error(type_error(list
|
|
537
|
-
'?- error(type_error(list
|
|
536
|
+
'?- error(type_error(list, [1, [], _A | 2]), eyeprolog).\n' +
|
|
537
|
+
'?- error(type_error(list, [1, [], _A | 2]), eyeprolog).\n' +
|
|
538
|
+
'?- ',
|
|
539
|
+
'stdout');
|
|
540
|
+
assertEqual(result.stderr, '', 'stderr');
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
name: 'REPL uncaught ISO errors retain the error/2 implementation context',
|
|
545
|
+
run: () => {
|
|
546
|
+
const result = runCli([], {
|
|
547
|
+
input: '_ is _.\ncatch(_ is _, error(Error, Imp_def), true).\nhalt.\n',
|
|
548
|
+
});
|
|
549
|
+
assertEqual(result.status, 0, 'exit status');
|
|
550
|
+
assertEqual(result.stdout,
|
|
551
|
+
'?- error(instantiation_error, eyeprolog).\n' +
|
|
552
|
+
'?- Error = instantiation_error, Imp_def = eyeprolog.\n' +
|
|
538
553
|
'?- ',
|
|
539
554
|
'stdout');
|
|
540
555
|
assertEqual(result.stderr, '', 'stderr');
|
|
@@ -585,6 +600,23 @@ c4 ?- call((!;1)).
|
|
|
585
600
|
assertEqual(result.stderr, '', 'stderr');
|
|
586
601
|
},
|
|
587
602
|
},
|
|
603
|
+
{
|
|
604
|
+
name: 'REPL parenthesizes operator-valued answer substitutions',
|
|
605
|
+
run: () => {
|
|
606
|
+
const result = runCli([], {
|
|
607
|
+
input: 'T = (a=b).\nU = (a,b).\nV = (a;b).\nW = (a+b).\nhalt.\n',
|
|
608
|
+
});
|
|
609
|
+
assertEqual(result.status, 0, 'exit status');
|
|
610
|
+
assertEqual(result.stdout,
|
|
611
|
+
'?- T = (a = b).\n' +
|
|
612
|
+
'?- U = (a, b).\n' +
|
|
613
|
+
'?- V = (a ; b).\n' +
|
|
614
|
+
'?- W = a + b.\n' +
|
|
615
|
+
'?- ',
|
|
616
|
+
'stdout');
|
|
617
|
+
assertEqual(result.stderr, '', 'stderr');
|
|
618
|
+
},
|
|
619
|
+
},
|
|
588
620
|
{
|
|
589
621
|
name: 'REPL accepts multiline period-terminated queries',
|
|
590
622
|
run: () => {
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -5803,11 +5803,14 @@ instances whose message contains the corresponding Prolog error term.
|
|
|
5803
5803
|
`error(Formal,eyeprolog)` term. `throw/1` copies its ball before unwinding: bound
|
|
5804
5804
|
parts are preserved, repeated variables remain shared within the copied ball,
|
|
5805
5805
|
and unbound variables are fresh with respect to the protected goal and catcher.
|
|
5806
|
-
Catchable error terms follow the same variable-freshening rule.
|
|
5807
|
-
top level
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5806
|
+
Catchable error terms follow the same variable-freshening rule. The interactive
|
|
5807
|
+
top level uses the same ISO error envelope for uncaught processor errors, so an
|
|
5808
|
+
ordinary runtime error is displayed as `error(Formal,eyeprolog)` rather than
|
|
5809
|
+
dropping the implementation-defined second argument. Variables that occur in an
|
|
5810
|
+
uncaught ISO error are rendered with fresh answer names such as `_A` rather than
|
|
5811
|
+
reusing query-variable spellings such as `X` or `Xx`; this keeps the displayed
|
|
5812
|
+
error consistent with the copied exception term. An unmatched ball or error
|
|
5813
|
+
continues outward.
|
|
5811
5814
|
|
|
5812
5815
|
Streams belong to one solver run and are shared by nested calls, exceptions,
|
|
5813
5816
|
and solution collectors. `user_input` and `user_output` are always present.
|
|
@@ -6191,7 +6194,11 @@ When another answer exists in an interactive terminal, press `;`, Space, or
|
|
|
6191
6194
|
enumeration, `a` enumerates all remaining answers, `f` enumerates the next
|
|
6192
6195
|
five, and `h` displays the answer-control help. A
|
|
6193
6196
|
period-terminated query with no solutions prints `false.`; a solution without
|
|
6194
|
-
visible variable bindings prints `true.`.
|
|
6197
|
+
visible variable bindings prints `true.`. Answer substitutions are rendered as
|
|
6198
|
+
valid Prolog syntax under the current operator table: when a bound value would
|
|
6199
|
+
not be a valid right operand of the displayed `=/2`, EyeProlog adds parentheses,
|
|
6200
|
+
for example `T = (a = b).` rather than the invalid `T = a = b.`. Use `[file].`
|
|
6201
|
+
or `['file.pl'].` to
|
|
6195
6202
|
consult local source, and `halt.` or `halt(Status).` to leave the top level.
|
|
6196
6203
|
Up and Down recall queries from the current session. Explicit `eyeprolog -h`
|
|
6197
6204
|
displays command-line help.
|