eyeprolog 1.1.23 → 1.1.24
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 +3 -2
- package/src/repl.js +22 -0
- package/src/solver.js +6 -3
- package/test/run-regression.mjs +4 -4
- package/the-art-of-eyeprolog.md +9 -2
package/package.json
CHANGED
package/src/iso.js
CHANGED
|
@@ -1784,7 +1784,8 @@ function* phraseBuiltin({ solver, goal, env }) {
|
|
|
1784
1784
|
}
|
|
1785
1785
|
}
|
|
1786
1786
|
function formalErrorTerm(error) {
|
|
1787
|
-
|
|
1787
|
+
const context = error.contextTerm ?? atom('eyeprolog');
|
|
1788
|
+
if (error.formalTerm != null) return compound('error', [error.formalTerm, context]);
|
|
1788
1789
|
const parse = (text) => {
|
|
1789
1790
|
const open = text.indexOf('(');
|
|
1790
1791
|
if (open === -1) return atom(text);
|
|
@@ -1811,7 +1812,7 @@ function formalErrorTerm(error) {
|
|
|
1811
1812
|
formal = compound(formal.name, [error.culprit]);
|
|
1812
1813
|
}
|
|
1813
1814
|
}
|
|
1814
|
-
return compound('error', [formal,
|
|
1815
|
+
return compound('error', [formal, context]);
|
|
1815
1816
|
}
|
|
1816
1817
|
function* catchBuiltin({ solver, goal, env }) {
|
|
1817
1818
|
const child = solver.cloneForInnerGoal();
|
package/src/repl.js
CHANGED
|
@@ -415,21 +415,43 @@ function formatError(engine, state, error) {
|
|
|
415
415
|
let generated = 0;
|
|
416
416
|
if (error.formalTerm != null) {
|
|
417
417
|
collectUnboundVariables(engine, error.formalTerm, env, variableNames, () => `_${letterName(generated++)}`);
|
|
418
|
+
if (error.contextTerm != null) {
|
|
419
|
+
collectUnboundVariables(engine, error.contextTerm, env, variableNames, () => `_${letterName(generated++)}`);
|
|
420
|
+
}
|
|
418
421
|
const formal = engine.formatTermForWrite(error.formalTerm, env, {
|
|
419
422
|
quoted: true,
|
|
420
423
|
operators: [...state.program.operators.values()],
|
|
421
424
|
variableNames,
|
|
422
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
|
+
}
|
|
423
434
|
return `error(${formal}).`;
|
|
424
435
|
}
|
|
425
436
|
if (error.culprit != null) {
|
|
426
437
|
collectUnboundVariables(engine, error.culprit, env, variableNames, () => `_${letterName(generated++)}`);
|
|
427
438
|
}
|
|
439
|
+
if (error.contextTerm != null) {
|
|
440
|
+
collectUnboundVariables(engine, error.contextTerm, env, variableNames, () => `_${letterName(generated++)}`);
|
|
441
|
+
}
|
|
428
442
|
const culprit = error.culprit == null ? '' : `, ${engine.formatTermForWrite(error.culprit, env, {
|
|
429
443
|
quoted: true,
|
|
430
444
|
operators: [...state.program.operators.values()],
|
|
431
445
|
variableNames,
|
|
432
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
|
+
}
|
|
433
455
|
return `error(${error.formal}${culprit}).`;
|
|
434
456
|
}
|
|
435
457
|
const message = error?.message ?? String(error);
|
package/src/solver.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Depth-first EyeProlog solver with builtin dispatch, memoization, and guarded recursion handling.
|
|
2
2
|
// Most semantic decisions still flow through unification; optimizations only select candidates earlier.
|
|
3
3
|
import {
|
|
4
|
-
COMPOUND, Env, compound, copyResolved, deref, flattenConjunction, freshTerm,
|
|
4
|
+
COMPOUND, Env, compound, copyResolved, deref, emptyList, flattenConjunction, freshTerm,
|
|
5
5
|
numberTerm, numberTextFromDouble, termIsGround, termToString, unify, variantTerms,
|
|
6
6
|
} from './term.js';
|
|
7
7
|
import { PrologError } from './iso.js';
|
|
@@ -24,8 +24,11 @@ export function nextFreshId() {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function raiseOccursCheckError(left, right, env) {
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
// occurs_check=error is an implementation-specific STO diagnostic. Report
|
|
28
|
+
// the unrepresentable cyclic result through the standard error envelope.
|
|
29
|
+
// Keep the implementation-defined context empty for stable, portable output.
|
|
30
|
+
const error = new PrologError('representation_error(term)');
|
|
31
|
+
error.contextTerm = emptyList();
|
|
29
32
|
throw error;
|
|
30
33
|
}
|
|
31
34
|
|
package/test/run-regression.mjs
CHANGED
|
@@ -523,7 +523,7 @@ c4 ?- call((!;1)).
|
|
|
523
523
|
},
|
|
524
524
|
},
|
|
525
525
|
{
|
|
526
|
-
name: 'occurs_check error mode
|
|
526
|
+
name: 'occurs_check error mode reports representation_error(term) while ISO occurs-check unification still fails',
|
|
527
527
|
run: () => {
|
|
528
528
|
const filename = path.join(tmp, `occurs-check-${++tmpCounter}.pl`);
|
|
529
529
|
fs.writeFileSync(filename, ':- set_prolog_flag(occurs_check, error).\nsame(X, X).\n');
|
|
@@ -544,11 +544,11 @@ c4 ?- call((!;1)).
|
|
|
544
544
|
assertEqual(result.stdout,
|
|
545
545
|
'?- Mode = true.\n' +
|
|
546
546
|
'?- true.\n' +
|
|
547
|
-
'?- error(
|
|
548
|
-
'?- E = error(
|
|
547
|
+
'?- error(representation_error(term), []).\n' +
|
|
548
|
+
'?- E = error(representation_error(term), []).\n' +
|
|
549
549
|
'?- false.\n' +
|
|
550
550
|
'?- true.\n' +
|
|
551
|
-
'?- error(
|
|
551
|
+
'?- error(representation_error(term), []).\n' +
|
|
552
552
|
'?- true.\n' +
|
|
553
553
|
'?- false.\n' +
|
|
554
554
|
'?- ',
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -785,15 +785,22 @@ subject-to-occurs-check (STO). EyeProlog's default remains the sound finite-tree
|
|
|
785
785
|
behavior above. For diagnosis, EyeProlog additionally provides the
|
|
786
786
|
implementation-specific flag `occurs_check`; setting it to `error` turns a
|
|
787
787
|
normal unification that would otherwise fail because of the occurs check into
|
|
788
|
-
|
|
788
|
+
a representation error:
|
|
789
789
|
|
|
790
790
|
```eyeprolog
|
|
791
791
|
:- set_prolog_flag(occurs_check, error).
|
|
792
792
|
|
|
793
793
|
sto_example :- X = wrapper(X).
|
|
794
|
-
% error(
|
|
794
|
+
% error(representation_error(term), [])
|
|
795
795
|
```
|
|
796
796
|
|
|
797
|
+
The ISO error mechanism wraps an error term together with an
|
|
798
|
+
implementation-defined context term. For this implementation-specific STO
|
|
799
|
+
diagnostic EyeProlog uses `representation_error(term)` and currently uses the
|
|
800
|
+
empty list `[]` as that context. This reports that the cyclic result of a
|
|
801
|
+
succeeding STO unification cannot be represented by EyeProlog's finite-tree
|
|
802
|
+
term model, without exposing a non-standard `occurs_check/2` error term.
|
|
803
|
+
|
|
797
804
|
The supported values are `true` (the default finite-tree behavior) and `error`
|
|
798
805
|
(STO detection). EyeProlog deliberately does not provide `occurs_check=false`,
|
|
799
806
|
because its term model does not construct cyclic terms. The ISO predicate
|