eyeprolog 1.3.76 → 1.3.78
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/README.md +6 -6
- package/examples/binomial-vandermonde.pl +1 -1
- package/examples/book/chapter-26/02-triple.pl +1 -1
- package/examples/book/chapter-28/02-triangle.pl +1 -1
- package/examples/book/chapter-28/04-integer_rectangle.pl +1 -1
- package/examples/book/chapter-29/01-counterexample_to_odd_square.pl +1 -1
- package/examples/book/chapter-31/03-double.pl +1 -1
- package/examples/book/chapter-32/04-reference_square.pl +1 -1
- package/examples/book/chapter-33/03-chosen_pair.pl +1 -1
- package/examples/book/chapter-39/01-answer.pl +1 -1
- package/examples/catalan-convolution.pl +1 -1
- package/examples/collatz-1000.pl +1 -1
- package/examples/continued-fraction-sqrt2.pl +1 -1
- package/examples/easter-computus.pl +1 -1
- package/examples/equality-saturation.pl +1 -1
- package/examples/goldbach.pl +1 -1
- package/examples/integer-partitions.pl +1 -1
- package/examples/job-shop-scheduling.pl +1 -1
- package/examples/matrix-chain-order.pl +1 -1
- package/examples/missionaries-cannibals.pl +1 -1
- package/examples/pell-equation.pl +1 -1
- package/examples/polynomial.pl +1 -1
- package/examples/prime-range.pl +1 -1
- package/examples/stirling-bell-numbers.pl +1 -1
- package/examples/totient-summatory.pl +1 -1
- package/package.json +1 -1
- package/src/iso.js +74 -25
- package/src/lib/lists.pl +15 -5
- package/src/lib/prologue.pl +30 -166
- package/src/program.js +20 -8
- package/src/quads.js +24 -3
- package/src/repl.js +4 -2
- package/src/solver.js +75 -24
- package/src/standard-library.js +5 -6
- package/test/fixtures/README.md +8 -0
- package/test/fixtures/variable_names_quad.pl +163 -0
- package/test/run-regression.mjs +106 -6
- package/test/test-support.mjs +1 -1
- package/the-art-of-eyeprolog.md +30 -23
package/src/program.js
CHANGED
|
@@ -180,12 +180,18 @@ export class Program {
|
|
|
180
180
|
for (let i = 0; i < head.arity; i++) indexOne(group.argIndexes[i], head.args[i], clause, group.clauses, clausePosition);
|
|
181
181
|
}
|
|
182
182
|
findGroup(name, arity, module = 'user') {
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
183
|
+
const indicator = `${name}/${arity}`;
|
|
184
|
+
const visited = new Set();
|
|
185
|
+
let current = module;
|
|
186
|
+
while (!visited.has(current)) {
|
|
187
|
+
visited.add(current);
|
|
188
|
+
const direct = this.groups.get(modulePredicateKey(current, name, arity));
|
|
189
|
+
if (direct) return direct;
|
|
190
|
+
const importedModule = this.moduleImports.get(current)?.get(indicator);
|
|
191
|
+
if (importedModule == null) return null;
|
|
192
|
+
current = importedModule;
|
|
193
|
+
}
|
|
194
|
+
return null;
|
|
189
195
|
}
|
|
190
196
|
defineModule(name, exports, filename = '<input>') {
|
|
191
197
|
const indicators = new Map(exports.map((indicator) => [`${indicator.name}/${indicator.arity}`, indicator]));
|
|
@@ -202,10 +208,16 @@ export class Program {
|
|
|
202
208
|
throw new PrologError('existence_error(procedure)', compound('/', [atom(indicator.name), numberTerm(indicator.arity)]));
|
|
203
209
|
}
|
|
204
210
|
const previous = imports.get(key);
|
|
205
|
-
|
|
211
|
+
const resolved = this.findGroup(indicator.name, indicator.arity, source);
|
|
212
|
+
const canonicalSource = resolved?.module ?? source;
|
|
213
|
+
const previousResolved = previous == null
|
|
214
|
+
? null
|
|
215
|
+
: this.findGroup(indicator.name, indicator.arity, previous);
|
|
216
|
+
const canonicalPrevious = previousResolved?.module ?? previous;
|
|
217
|
+
if (previous != null && canonicalPrevious !== canonicalSource) {
|
|
206
218
|
throw new PrologError('permission_error(import, procedure)', compound('/', [atom(indicator.name), numberTerm(indicator.arity)]));
|
|
207
219
|
}
|
|
208
|
-
imports.set(key,
|
|
220
|
+
imports.set(key, canonicalSource);
|
|
209
221
|
}
|
|
210
222
|
this.moduleImports.set(target, imports);
|
|
211
223
|
}
|
package/src/quads.js
CHANGED
|
@@ -110,10 +110,15 @@ function checkAlternative(program, quad, alternative, options, context) {
|
|
|
110
110
|
const actual = executeQuery(program, quad.query, input, maxSolutions, {
|
|
111
111
|
...options,
|
|
112
112
|
detectLoops: leaves.some((leaf) => leaf.loops),
|
|
113
|
+
detectWaits: leaves.some((leaf) => leaf.waits),
|
|
113
114
|
inputBoundary: hasInputSpec,
|
|
114
115
|
inputPeek: peek,
|
|
115
116
|
});
|
|
116
117
|
|
|
118
|
+
if (leaves.some((leaf) => leaf.waits)) {
|
|
119
|
+
return { ok: leaves.length === 1 && actual.inputWaitObserved };
|
|
120
|
+
}
|
|
121
|
+
|
|
117
122
|
if (requiresSto) {
|
|
118
123
|
// Trealla currently treats the answer part of an `sto`-annotated leaf as
|
|
119
124
|
// implementation-dependent and skips it. EyeProlog can strengthen that
|
|
@@ -199,6 +204,7 @@ function describeLeaf(term) {
|
|
|
199
204
|
false: false,
|
|
200
205
|
truth: false,
|
|
201
206
|
loops: false,
|
|
207
|
+
waits: false,
|
|
202
208
|
error: null,
|
|
203
209
|
input: null,
|
|
204
210
|
peek: null,
|
|
@@ -213,7 +219,8 @@ function describeLeaf(term) {
|
|
|
213
219
|
else if (item.name === '...' || item.name === 'ad_infinitum') leaf.more = true;
|
|
214
220
|
else if (item.name === 'sto') leaf.sto = true;
|
|
215
221
|
else if (item.name === 'loops') leaf.loops = true;
|
|
216
|
-
else if (item.name === 'waits'
|
|
222
|
+
else if (item.name === 'waits') leaf.waits = true;
|
|
223
|
+
else if (item.name === 'other_answer_sequence') {
|
|
217
224
|
leaf.unsupported ??= item;
|
|
218
225
|
} else if (item.name === 'false') leaf.false = true;
|
|
219
226
|
else if (item.name === 'true') leaf.truth = true;
|
|
@@ -246,7 +253,8 @@ function describeLeaf(term) {
|
|
|
246
253
|
if (isErrorDescription(item)) leaf.error = item;
|
|
247
254
|
else leaf.malformed ??= item;
|
|
248
255
|
}
|
|
249
|
-
leaf.hasExpectation = leaf.bindings.length > 0 || leaf.truth || leaf.false || leaf.loops || leaf.
|
|
256
|
+
leaf.hasExpectation = leaf.bindings.length > 0 || leaf.truth || leaf.false || leaf.loops || leaf.waits ||
|
|
257
|
+
leaf.error != null || leaf.output != null;
|
|
250
258
|
if (!leaf.hasExpectation && !leaf.more && !leaf.sto && leaf.unsupported == null) leaf.malformed ??= term;
|
|
251
259
|
if ([leaf.false, leaf.truth, leaf.error != null].filter(Boolean).length > 1) leaf.malformed ??= term;
|
|
252
260
|
return leaf;
|
|
@@ -254,6 +262,7 @@ function describeLeaf(term) {
|
|
|
254
262
|
|
|
255
263
|
function executeQuery(program, query, input, maxSolutions, options) {
|
|
256
264
|
let pendingOutput = '';
|
|
265
|
+
let inputWaitObserved = false;
|
|
257
266
|
const boundedInput = options.inputBoundary
|
|
258
267
|
? `${input}${options.inputPeek ?? ''}${INVALID_UTF8_SENTINEL}`
|
|
259
268
|
: input;
|
|
@@ -274,13 +283,24 @@ function executeQuery(program, query, input, maxSolutions, options) {
|
|
|
274
283
|
write: (text) => { pendingOutput += String(text); },
|
|
275
284
|
},
|
|
276
285
|
});
|
|
286
|
+
if (options.detectWaits) {
|
|
287
|
+
const inputStream = solver.io.resolve('user_input');
|
|
288
|
+
const observeWait = () => {
|
|
289
|
+
inputWaitObserved = true;
|
|
290
|
+
return null;
|
|
291
|
+
};
|
|
292
|
+
inputStream.interactiveReadTerm = observeWait;
|
|
293
|
+
inputStream.interactiveReadUnit = observeWait;
|
|
294
|
+
}
|
|
277
295
|
if (options.inputBoundary) {
|
|
278
296
|
// Quads need a boundary that is neither EOF nor a valid character. This
|
|
279
297
|
// mirrors Trealla's sentinel technique: inputs/1 says exactly what was
|
|
280
298
|
// consumed, while peeks/1 supplies one additional character that must
|
|
281
299
|
// remain unread. If the query asks for anything beyond that boundary it
|
|
282
300
|
// sees representation_error(character), not an artificial EOF.
|
|
283
|
-
solver.io.resolve('user_input')
|
|
301
|
+
const inputStream = solver.io.resolve('user_input');
|
|
302
|
+
inputStream.strictUtf8 = true;
|
|
303
|
+
inputStream.syntheticInputBoundary = input.length;
|
|
284
304
|
}
|
|
285
305
|
// Undefined predicates are test failures rather than silent negative
|
|
286
306
|
// answers unless the source explicitly selected another unknown policy.
|
|
@@ -338,6 +358,7 @@ function executeQuery(program, query, input, maxSolutions, options) {
|
|
|
338
358
|
inputRemainder,
|
|
339
359
|
complete,
|
|
340
360
|
loopObserved,
|
|
361
|
+
inputWaitObserved,
|
|
341
362
|
stoObserved: solver.occursCheckObserved,
|
|
342
363
|
nstoObserved: complete && !solver.occursCheckObserved && !loopObserved &&
|
|
343
364
|
!bounded && !resourceInterrupted,
|
package/src/repl.js
CHANGED
|
@@ -803,7 +803,7 @@ function letterName(index) {
|
|
|
803
803
|
}
|
|
804
804
|
|
|
805
805
|
function formatError(engine, state, error) {
|
|
806
|
-
if (error?.name === 'PrologError') {
|
|
806
|
+
if (error?.name === 'PrologError' || (error?.name === 'ThrownTerm' && error.term != null)) {
|
|
807
807
|
const env = new engine.Env();
|
|
808
808
|
const variableNames = new Map();
|
|
809
809
|
let generated = 0;
|
|
@@ -811,7 +811,9 @@ function formatError(engine, state, error) {
|
|
|
811
811
|
// Reuse the same conversion as catch/3 so uncaught errors at the top
|
|
812
812
|
// level cannot lose the implementation-defined context or misplace a
|
|
813
813
|
// culprit as the second argument of error/2.
|
|
814
|
-
const term =
|
|
814
|
+
const term = error.name === 'ThrownTerm'
|
|
815
|
+
? engine.compound('throw', [error.term])
|
|
816
|
+
: formalErrorTerm(error);
|
|
815
817
|
collectUnboundVariables(engine, term, env, variableNames, () => `_${letterName(generated++)}`);
|
|
816
818
|
return `${engine.formatTermForWrite(term, env, {
|
|
817
819
|
quoted: true,
|
package/src/solver.js
CHANGED
|
@@ -607,14 +607,16 @@ export class Solver {
|
|
|
607
607
|
const def = callable ? this.registry.get(goal.name, goal.arity) : null;
|
|
608
608
|
this.active = active;
|
|
609
609
|
if (def && builtinIsReadyOrAuthoritative(def, this, goal, env)) {
|
|
610
|
+
const deterministic = def.deterministic ||
|
|
611
|
+
def.deterministicWhen?.({ solver: this, goal, env }) === true;
|
|
610
612
|
const iterator = def.handler({ solver: this, goal, env });
|
|
611
613
|
const firstResult = iterator.next();
|
|
612
|
-
if (
|
|
614
|
+
if (deterministic) {
|
|
613
615
|
if (!firstResult.done) this.stats.deterministic_builtin_successes++;
|
|
614
616
|
else this.stats.deterministic_builtin_failures++;
|
|
615
617
|
}
|
|
616
618
|
if (firstResult.done) break;
|
|
617
|
-
if (!
|
|
619
|
+
if (!deterministic) {
|
|
618
620
|
stack.push({
|
|
619
621
|
kind: 'resumeBuiltin',
|
|
620
622
|
iterator,
|
|
@@ -819,11 +821,22 @@ export class Solver {
|
|
|
819
821
|
}
|
|
820
822
|
}
|
|
821
823
|
|
|
822
|
-
hasPendingAlternatives() {
|
|
823
|
-
// When solve() is suspended at an answer, active solve stacks contain
|
|
824
|
-
// unexplored work
|
|
825
|
-
//
|
|
826
|
-
|
|
824
|
+
hasPendingAlternatives(excludedStacks = null) {
|
|
825
|
+
// When solve() is suspended at an answer, active solve stacks contain
|
|
826
|
+
// unexplored work or an iterator frame that can prove it just yielded its
|
|
827
|
+
// final answer. The demand-driven REPL uses this without speculatively
|
|
828
|
+
// pulling the next answer. Control builtins can exclude their caller's
|
|
829
|
+
// pre-existing stacks when inspecting a nested branch.
|
|
830
|
+
return this.solveStacks.some((stack) => !excludedStacks?.has(stack) && stack.some((frame) => {
|
|
831
|
+
if (frame.kind === 'resumeBuiltin' &&
|
|
832
|
+
typeof frame.iterator?.hasPendingAlternatives === 'function') {
|
|
833
|
+
return frame.iterator.hasPendingAlternatives();
|
|
834
|
+
}
|
|
835
|
+
if (frame.kind === 'userClause' && headCannotMatch(frame.goal, frame.clause.head, frame.env)) {
|
|
836
|
+
return false;
|
|
837
|
+
}
|
|
838
|
+
return true;
|
|
839
|
+
}));
|
|
827
840
|
}
|
|
828
841
|
|
|
829
842
|
fastCountGoal(goal, env) {
|
|
@@ -1428,19 +1441,22 @@ function pushWfsAnswerFrames(stack, model, group, goal, rest, env, depth, active
|
|
|
1428
1441
|
|
|
1429
1442
|
function bundledBetweenIterator(solver, group, goal, env) {
|
|
1430
1443
|
if (solver.registry.eyePrologLibrary !== true ||
|
|
1431
|
-
group.module !== '
|
|
1444
|
+
group.module !== 'between' || group.name !== 'between' || group.arity !== 3 ||
|
|
1432
1445
|
group.bundledLibrary !== true || group.clauses.length !== 1) {
|
|
1433
1446
|
return null;
|
|
1434
1447
|
}
|
|
1435
1448
|
|
|
1436
|
-
// The portable
|
|
1449
|
+
// The portable library(between) definition is kept as the semantic
|
|
1437
1450
|
// source of between/3. Its recursive helper, however, carries the output
|
|
1438
1451
|
// variable through one fresh clause instance per integer. With persistent
|
|
1439
1452
|
// environments that builds a growing variable-alias chain, so dereferencing
|
|
1440
1453
|
// the generated value in the caller repeatedly revisits all earlier frames.
|
|
1441
1454
|
// Enumerate the canonical bundled relation directly while leaving user
|
|
1442
1455
|
// definitions and non-EyeProlog registries on the ordinary Prolog path.
|
|
1443
|
-
|
|
1456
|
+
const state = { pending: true };
|
|
1457
|
+
const iterator = bundledBetweenSolutions(solver, goal, env, state);
|
|
1458
|
+
iterator.hasPendingAlternatives = () => state.pending;
|
|
1459
|
+
return iterator;
|
|
1444
1460
|
}
|
|
1445
1461
|
|
|
1446
1462
|
function requireBetweenInteger(term, env) {
|
|
@@ -1452,7 +1468,7 @@ function requireBetweenInteger(term, env) {
|
|
|
1452
1468
|
return BigInt(value.name);
|
|
1453
1469
|
}
|
|
1454
1470
|
|
|
1455
|
-
function* bundledBetweenSolutions(solver, goal, env) {
|
|
1471
|
+
function* bundledBetweenSolutions(solver, goal, env, state) {
|
|
1456
1472
|
const lower = requireBetweenInteger(goal.args[0], env);
|
|
1457
1473
|
const upper = requireBetweenInteger(goal.args[1], env);
|
|
1458
1474
|
const requested = deref(goal.args[2], env);
|
|
@@ -1462,20 +1478,28 @@ function* bundledBetweenSolutions(solver, goal, env) {
|
|
|
1462
1478
|
throw new PrologError('type_error(integer)', requested);
|
|
1463
1479
|
}
|
|
1464
1480
|
const value = BigInt(requested.name);
|
|
1465
|
-
if (value >= lower && value <= upper)
|
|
1481
|
+
if (value >= lower && value <= upper) {
|
|
1482
|
+
state.pending = false;
|
|
1483
|
+
yield env;
|
|
1484
|
+
}
|
|
1485
|
+
state.pending = false;
|
|
1466
1486
|
return;
|
|
1467
1487
|
}
|
|
1468
1488
|
|
|
1469
1489
|
for (let value = lower; value <= upper; value++) {
|
|
1470
1490
|
const next = env.clone();
|
|
1471
1491
|
solver.stats.unify_calls++;
|
|
1472
|
-
if (unify(goal.args[2], numberTerm(value), next))
|
|
1492
|
+
if (unify(goal.args[2], numberTerm(value), next)) {
|
|
1493
|
+
state.pending = value < upper;
|
|
1494
|
+
yield next;
|
|
1495
|
+
}
|
|
1473
1496
|
}
|
|
1497
|
+
state.pending = false;
|
|
1474
1498
|
}
|
|
1475
1499
|
|
|
1476
1500
|
function bundledMemberIterator(solver, group, goal, env) {
|
|
1477
1501
|
if (solver.registry.eyePrologLibrary !== true ||
|
|
1478
|
-
|
|
1502
|
+
group.module !== 'lists' || group.name !== 'member' || group.arity !== 2 ||
|
|
1479
1503
|
group.bundledLibrary !== true || group.clauses.length !== 2) {
|
|
1480
1504
|
return null;
|
|
1481
1505
|
}
|
|
@@ -1487,17 +1511,33 @@ function bundledMemberIterator(solver, group, goal, env) {
|
|
|
1487
1511
|
let cursor = deref(goal.args[1], env);
|
|
1488
1512
|
while (isCons(cursor)) cursor = deref(cursor.args[1], env);
|
|
1489
1513
|
if (!isEmptyList(cursor)) return null;
|
|
1490
|
-
|
|
1514
|
+
const state = { pending: true };
|
|
1515
|
+
const iterator = bundledMemberSolutions(solver, goal, env, state);
|
|
1516
|
+
iterator.hasPendingAlternatives = () => state.pending;
|
|
1517
|
+
return iterator;
|
|
1491
1518
|
}
|
|
1492
1519
|
|
|
1493
|
-
function* bundledMemberSolutions(solver, goal, env) {
|
|
1520
|
+
function* bundledMemberSolutions(solver, goal, env, state) {
|
|
1494
1521
|
let cursor = deref(goal.args[1], env);
|
|
1495
1522
|
while (isCons(cursor)) {
|
|
1523
|
+
const item = cursor.args[0];
|
|
1524
|
+
cursor = deref(cursor.args[1], env);
|
|
1496
1525
|
const next = env.clone();
|
|
1497
1526
|
solver.stats.unify_calls++;
|
|
1498
|
-
if (unify(goal.args[0],
|
|
1527
|
+
if (unify(goal.args[0], item, next)) {
|
|
1528
|
+
state.pending = bundledMemberMayMatch(goal.args[0], cursor, env);
|
|
1529
|
+
yield next;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
state.pending = false;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
function bundledMemberMayMatch(target, cursor, env) {
|
|
1536
|
+
while (isCons(cursor)) {
|
|
1537
|
+
if (!goalHeadTermsCannotMatch(target, cursor.args[0], env)) return true;
|
|
1499
1538
|
cursor = deref(cursor.args[1], env);
|
|
1500
1539
|
}
|
|
1540
|
+
return false;
|
|
1501
1541
|
}
|
|
1502
1542
|
|
|
1503
1543
|
function bundledEllipsisPlan(solver, group, goal, rest, env) {
|
|
@@ -1588,7 +1628,7 @@ function* bundledEllipsisSolutions(solver, output, input, env) {
|
|
|
1588
1628
|
|
|
1589
1629
|
function bundledLengthIterator(solver, group, goal, env) {
|
|
1590
1630
|
if (solver.registry.eyePrologLibrary !== true ||
|
|
1591
|
-
|
|
1631
|
+
group.module !== 'lists' || group.name !== 'length' || group.arity !== 2 ||
|
|
1592
1632
|
group.bundledLibrary !== true || group.clauses.length !== 2) {
|
|
1593
1633
|
return null;
|
|
1594
1634
|
}
|
|
@@ -1900,7 +1940,7 @@ function findallLengthFusion(solver, goal, rest, env) {
|
|
|
1900
1940
|
if (lengthGoal.type !== COMPOUND || lengthGoal.name !== 'length' || lengthGoal.arity !== 2) return null;
|
|
1901
1941
|
const lengthGroup = solver.program.findGroup('length', 2, lengthGoal.module ?? 'user');
|
|
1902
1942
|
if (!lengthGroup || lengthGroup.bundledLibrary !== true ||
|
|
1903
|
-
|
|
1943
|
+
lengthGroup.module !== 'lists') return null;
|
|
1904
1944
|
const lengthList = derefForLocal(lengthGoal.args[0], env);
|
|
1905
1945
|
const countArg = derefForLocal(lengthGoal.args[1], env);
|
|
1906
1946
|
if (lengthList.type !== 'var' || lengthList.name !== bag.name || countArg.type !== 'var') return null;
|
|
@@ -2849,11 +2889,22 @@ function headCannotMatch(goal, head, env) {
|
|
|
2849
2889
|
if (goal.type !== COMPOUND || head.type !== COMPOUND) return false;
|
|
2850
2890
|
if (goal.name !== head.name || goal.arity !== head.arity) return true;
|
|
2851
2891
|
for (let i = 0; i < goal.arity; i++) {
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2892
|
+
if (goalHeadTermsCannotMatch(goal.args[i], head.args[i], env)) return true;
|
|
2893
|
+
}
|
|
2894
|
+
return false;
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
function goalHeadTermsCannotMatch(goalTerm, headTerm, env) {
|
|
2898
|
+
const pending = [[goalTerm, headTerm]];
|
|
2899
|
+
while (pending.length > 0) {
|
|
2900
|
+
const [goalItem, headItem] = pending.pop();
|
|
2901
|
+
const actual = derefForLocal(goalItem, env);
|
|
2902
|
+
if (actual.type === VAR || headItem.type === VAR) continue;
|
|
2903
|
+
if (actual.type !== headItem.type || actual.name !== headItem.name || actual.arity !== headItem.arity) return true;
|
|
2904
|
+
if (actual.type !== COMPOUND) continue;
|
|
2905
|
+
for (let index = 0; index < actual.arity; index++) {
|
|
2906
|
+
pending.push([actual.args[index], headItem.args[index]]);
|
|
2907
|
+
}
|
|
2857
2908
|
}
|
|
2858
2909
|
return false;
|
|
2859
2910
|
}
|
package/src/standard-library.js
CHANGED
|
@@ -141,11 +141,10 @@ export const eyePrologInteropAutoload = Object.freeze({
|
|
|
141
141
|
// unchanged while their implementations remain outside the ISO core.
|
|
142
142
|
'time/1': 'iso_ext',
|
|
143
143
|
'.../2': 'iso_ext',
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
'between/3': 'prologue',
|
|
144
|
+
// Scryer provides library(between), while Trealla exposes the relation as a
|
|
145
|
+
// system predicate. Keep EyeProlog's canonical portable owner in the
|
|
146
|
+
// specialized module; library(prologue) re-exports it for compatibility.
|
|
147
|
+
'between/3': 'between',
|
|
149
148
|
});
|
|
150
149
|
|
|
151
150
|
export const eyePrologInteropLibraryIndicators = Object.freeze(
|
|
@@ -155,7 +154,7 @@ export const eyePrologInteropLibraryIndicators = Object.freeze(
|
|
|
155
154
|
// Libraries whose *name* is part of the current interop profile. A program
|
|
156
155
|
// may freely use_module/1 with these common module names; predicates in those
|
|
157
156
|
// modules outside eyePrologInteropLibraryIndicators are still diagnosed when used.
|
|
158
|
-
export const eyePrologInteropLibraryModules = Object.freeze(['lists', 'iso_ext', 'lambda', 'atts']);
|
|
157
|
+
export const eyePrologInteropLibraryModules = Object.freeze(['lists', 'iso_ext', 'lambda', 'atts', 'freeze']);
|
|
159
158
|
|
|
160
159
|
function* tabledNegationBuiltin({ solver, goal, env }) {
|
|
161
160
|
yield* solver.solveTabledNegation(goal.args[0], env);
|
package/test/fixtures/README.md
CHANGED
|
@@ -9,6 +9,14 @@ Retrieved on 2026-08-11. It is vendored so the regression suite exercises all
|
|
|
9
9
|
58 quads without depending on network access or availability of the source
|
|
10
10
|
server.
|
|
11
11
|
|
|
12
|
+
`variable_names_quad.pl` is an unmodified snapshot of the 75 machine-readable
|
|
13
|
+
quads for the ISO read and write option `variable_names/1`:
|
|
14
|
+
|
|
15
|
+
<https://www.complang.tuwien.ac.at/ulrich/iso-prolog/variable_names_quad.pl>
|
|
16
|
+
|
|
17
|
+
Retrieved on 2026-08-25. It is vendored so all input, output, waiting, and
|
|
18
|
+
error cases remain release-gated.
|
|
19
|
+
|
|
12
20
|
`prologue_quad.pl` is an unmodified snapshot of the 33 machine-readable quads
|
|
13
21
|
for the predicates proposed by the Prolog Prologue working draft:
|
|
14
22
|
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
1 ?- write_term(T,[quoted(true),variable_names([N=T])]).
|
|
2
|
+
instantiation_error.
|
|
3
|
+
2 ?- N = 'X', /**/write_term(T,[quoted(true),variable_names([N=T])]).
|
|
4
|
+
outputs("X"), N = 'X'.
|
|
5
|
+
outputs("X"), N = ... . % Same, ignoring 'X'
|
|
6
|
+
3 ?- N = T, /**/write_term(T,[quoted(true),variable_names([N=T])]).
|
|
7
|
+
instantiation_error.
|
|
8
|
+
4 ?- N = '_', /**/write_term(T,[quoted(true),variable_names([N=T])]).
|
|
9
|
+
outputs("_"), N = ... .
|
|
10
|
+
65 ?- N = '_/*.*/', /**/write_term(T,[quoted(true),variable_names([N=T])]).
|
|
11
|
+
outputs("_/*.*/"), N = ... .
|
|
12
|
+
5 ?- N = x, /**/write_term(T,[quoted(true),variable_names([N=T])]).
|
|
13
|
+
outputs("x"), N = ... .
|
|
14
|
+
6 ?- N = 'x+y', /**/write_term(T,[quoted(true),variable_names([N=T])]).
|
|
15
|
+
outputs("x+y"), N = ... .
|
|
16
|
+
50 ?- N = '))', /**/write_term(T,[quoted(true),variable_names([N=T])]).
|
|
17
|
+
outputs("))"), N = ... .
|
|
18
|
+
7 ?- N = 7, /**/write_term(T,[quoted(true),variable_names([N=T])]).
|
|
19
|
+
domain_error(write_option, variable_names(...)).
|
|
20
|
+
8 ?- N = 1+2, /**/write_term(T,[quoted(true),variable_names([N=T])]).
|
|
21
|
+
domain_error(write_option, variable_names(...)).
|
|
22
|
+
9 ?- N = '$VAR'(9), /**/write_term(T,[quoted(true),variable_names([N=T])]).
|
|
23
|
+
domain_error(write_option, variable_names(...)).
|
|
24
|
+
10 ?- T = a, /**/write_term(T,[quoted(true),variable_names([N=T])]).
|
|
25
|
+
instantiation_error.
|
|
26
|
+
11 ?- T = a, N = 'Any', /**/write_term(T,[quoted(true),variable_names([N=T])]).
|
|
27
|
+
outputs("a"), T = ..., N = ... .
|
|
28
|
+
12 ?- T = '$VAR'(9), N = '_', /**/write_term(T,[quoted(true),variable_names([N=T])]).
|
|
29
|
+
outputs("'$VAR'(9)"), T = ..., N = ... .
|
|
30
|
+
74 ?- T = f(_), N = 'Bad', /**/write_term(T,[quoted(true),variable_names([N=T])]).
|
|
31
|
+
outputs(("f(_",...,")")), T = ..., N = ... .
|
|
32
|
+
28 ?- freeze(T,throw(g(T))), N = 'X', /**/write_term(T,[quoted(true),variable_names([N=T])]), false.
|
|
33
|
+
outputs("X"), false.
|
|
34
|
+
13 ?- write_term(T,[quoted(true),variable_names(['X'=X,'Y'=Y,'Z'=Z])]).
|
|
35
|
+
outputs(("_",...)).
|
|
36
|
+
14 ?- T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['X'=X,'Y'=Y,'Z'=Z])]).
|
|
37
|
+
outputs("X,Y,Z"), T = ... .
|
|
38
|
+
15 ?- Z=Y, T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['X'=X,'Y'=Y,'Z'=Z])]).
|
|
39
|
+
outputs("X,Y,Y"), Z = Y, T = ... .
|
|
40
|
+
16 ?- Z=Y, Y=X, T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['X'=X,'Y'=Y,'Z'=Z])]).
|
|
41
|
+
outputs("X,X,X"), Z = X, Y = X, T = ... .
|
|
42
|
+
17 ?- T=(Y,Z), /**/write_term(T,[quoted(true),variable_names(['X'=X,'Y'=Y,'Z'=Z])]).
|
|
43
|
+
outputs("Y,Z"), T = ... .
|
|
44
|
+
18 ?- T=(Z,Y), /**/write_term(T,[quoted(true),variable_names(['X'=X,'Y'=Y,'Z'=Z])]).
|
|
45
|
+
outputs("Z,Y"), T = ... .
|
|
46
|
+
19 ?- write_term(T,[quoted(true),variable_names(['Z'=Z,'Y'=Y,'X'=X])]).
|
|
47
|
+
outputs(("_",...)).
|
|
48
|
+
20 ?- T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['Z'=Z,'Y'=Y,'X'=X])]).
|
|
49
|
+
outputs("X,Y,Z"), T = ... .
|
|
50
|
+
21 ?- Z=Y, T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['Z'=Z,'Y'=Y,'X'=X])]).
|
|
51
|
+
outputs("X,Z,Z"), Z = Y, T = ... .
|
|
52
|
+
22 ?- Z=Y, Y=X, T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['Z'=Z,'Y'=Y,'X'=X])]).
|
|
53
|
+
outputs("Z,Z,Z"), Z = X, Y = X, T = ... .
|
|
54
|
+
23 ?- T=(Y,Z), /**/write_term(T,[quoted(true),variable_names(['Z'=Z,'Y'=Y,'X'=X])]).
|
|
55
|
+
outputs("Y,Z"), T = ... .
|
|
56
|
+
24 ?- T=(Z,Y), /**/write_term(T,[quoted(true),variable_names(['Z'=Z,'Y'=Y,'X'=X])]).
|
|
57
|
+
outputs("Z,Y"), T = ... .
|
|
58
|
+
25 ?- write_term(T,[quoted(true),variable_names(['X'=Z,'X'=Y,'X'=X])]).
|
|
59
|
+
outputs(("_",...)).
|
|
60
|
+
26 ?- T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['X'=Z,'X'=Y,'X'=X])]).
|
|
61
|
+
outputs("X,X,X"), T = ... .
|
|
62
|
+
27 ?- T=(1,2,3), T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['X'=Z,'X'=Y,'X'=X])]).
|
|
63
|
+
outputs("1,2,3"), T = ..., X = 1, Y = 2, Z = 3.
|
|
64
|
+
32 ?- read_term(T,[variable_names(VN_list)]),VN_list=[_=1,_=2,_=3],writeq(VN_list).
|
|
65
|
+
waits.
|
|
66
|
+
29 ?- /**/read_term(T,[variable_names(VN_list)]),VN_list=[_=1,_=2,_=3],writeq(VN_list).
|
|
67
|
+
inputs("B+C+A+B+C+A."), peeks("\n"),
|
|
68
|
+
outputs("['B'=1,'C'=2,'A'=3]"),
|
|
69
|
+
T = 1+2+3+1+2+3,
|
|
70
|
+
VN_list=['B'=1,'C'=2,'A'=3].
|
|
71
|
+
30 ?- write_term(T,[variable_names(VN_list)]).
|
|
72
|
+
instantiation_error.
|
|
73
|
+
31 ?- VN_list = 1, /**/write_term(T,[variable_names(VN_list)]).
|
|
74
|
+
domain_error(write_option, variable_names(1)).
|
|
75
|
+
33 ?- VN_list = [[]], /**/write_term(T,[variable_names(VN_list)]).
|
|
76
|
+
domain_error(write_option, variable_names([[]])).
|
|
77
|
+
34 ?- VN_list = non_list, /**/write_term(T,[variable_names(VN_list)]).
|
|
78
|
+
domain_error(write_option, variable_names(non_list)).
|
|
79
|
+
35 ?- VN_list = [T='T'|non_list], /**/write_term(T,[variable_names(VN_list)]).
|
|
80
|
+
domain_error(write_option, variable_names([_='T'|non_list]))
|
|
81
|
+
| instantiation_error.
|
|
82
|
+
52 ?- VN_list = ['T'=T|_], /**/write_term(T,[variable_names(VN_list)]).
|
|
83
|
+
instantiation_error.
|
|
84
|
+
51 ?- VN_list = ['T'=T|non_list], /**/write_term(T,[variable_names(VN_list)]).
|
|
85
|
+
domain_error(write_option, variable_names(...)).
|
|
86
|
+
36 ?- VN_list = [T-'T'], /**/write_term(T,[variable_names(VN_list)]).
|
|
87
|
+
domain_error(write_option, variable_names(...)).
|
|
88
|
+
63 ?- VN_list = [_,a], /**/write_term(T,[variable_names(VN_list)]).
|
|
89
|
+
domain_error(write_option, variable_names(...))
|
|
90
|
+
| instantiation_error.
|
|
91
|
+
64 ?- VN_list = [a,_], /**/write_term(T,[variable_names(VN_list)]).
|
|
92
|
+
domain_error(write_option, variable_names(...))
|
|
93
|
+
| instantiation_error.
|
|
94
|
+
66 ?- VN_list = [a|_], /**/write_term(T,[variable_names(VN_list)]).
|
|
95
|
+
domain_error(write_option, variable_names(...))
|
|
96
|
+
| instantiation_error.
|
|
97
|
+
67 ?- VN_list = [i=i,7=i], /**/write_term(T,[variable_names(VN_list)]).
|
|
98
|
+
domain_error(write_option,variable_names([i=i,7=i])).
|
|
99
|
+
68 ?- VN_list = [_,_], /**/write_term(T,[variable_names(VN_list)]).
|
|
100
|
+
instantiation_error.
|
|
101
|
+
43 ?- write_term(-X^2,[variable_names(['X'=X])]).
|
|
102
|
+
outputs("- (X^2)").
|
|
103
|
+
44 ?- X=1, /**/write_term(-X^2,[variable_names(['X'=X])]).
|
|
104
|
+
outputs("- (1^2)"), X = ... .
|
|
105
|
+
37 ?- open(f,write,_,[O]).
|
|
106
|
+
instantiation_error.
|
|
107
|
+
38 ?- O = 1, /**/open(f,write,_,[O]).
|
|
108
|
+
domain_error(stream_option,1).
|
|
109
|
+
56 ?- O = typex(_), /**/open(f,write,_,[O]).
|
|
110
|
+
domain_error(stream_option,typex(_)).
|
|
111
|
+
57 ?- O = typex(1), /**/open(f,write,_,[O]).
|
|
112
|
+
domain_error(stream_option,typex(1)).
|
|
113
|
+
62 ?- O = typex(s(_)), /**/open(f,write,_,[O]).
|
|
114
|
+
domain_error(stream_option,typex(s(_))).
|
|
115
|
+
39 ?- O = type(text), /**/open(f,write,_,[O]).
|
|
116
|
+
O = ... .
|
|
117
|
+
40 ?- O = type(1), /**/open(f,write,_,[O]).
|
|
118
|
+
domain_error(stream_option,type(1)).
|
|
119
|
+
41 ?- O = type(_), /**/open(f,write,_,[O]).
|
|
120
|
+
instantiation_error.
|
|
121
|
+
60 ?- O = alias(_), /**/open(f,write,_,[O]).
|
|
122
|
+
instantiation_error.
|
|
123
|
+
42 ?- O = type(nontype), /**/open(f,write,_,[O]).
|
|
124
|
+
domain_error(stream_option,type(nontype)).
|
|
125
|
+
61 ?- O = alias(1), /**/open(f,write,_,[O]).
|
|
126
|
+
domain_error(stream_option,alias(1)).
|
|
127
|
+
45 ?- read_term(T,[variable_names(VN_list)]).
|
|
128
|
+
waits.
|
|
129
|
+
46 ?- /**/read_term(T,[variable_names(VN_list)]).
|
|
130
|
+
inputs("a."), peeks("\n"),
|
|
131
|
+
T = a, VN_list = [].
|
|
132
|
+
47 ?- VN_list = 42, /**/read_term(T,[variable_names(VN_list)]).
|
|
133
|
+
waits.
|
|
134
|
+
48 ?- VN_list = 42, /**/read_term(T,[variable_names(VN_list)]).
|
|
135
|
+
inputs("a."), peeks("\n"),
|
|
136
|
+
false.
|
|
137
|
+
49 ?- VN_list = 42, /**/read_term(T,[variable_names(VN_list)]).
|
|
138
|
+
inputs("a b."), peeks("\n"),
|
|
139
|
+
syntax_error(...).
|
|
140
|
+
53 ?- write_term(S,[quoted(true),variable_names([N=T])]).
|
|
141
|
+
instantiation_error.
|
|
142
|
+
54 ?- S=1+T,N='/*r*/V',/**/write_term(S,[quoted(true),variable_names([N=T])]).
|
|
143
|
+
outputs("1+/*r*/V"), S = ..., N = ...
|
|
144
|
+
| outputs("1+ /*r*/V"), S = ..., N = ... .
|
|
145
|
+
55 ?- S=1+T,N=' /*r*/V',/**/write_term(S,[quoted(true),variable_names([N=T])]).
|
|
146
|
+
outputs("1+ /*r*/V"), S = ..., N = ... .
|
|
147
|
+
58 ?- S=1+T,N=(+),/**/write_term(S,[quoted(true),variable_names([N=T])]).
|
|
148
|
+
outputs("1++"), S = ..., N = ...
|
|
149
|
+
| outputs("1+ +"), S = ..., N = ... .
|
|
150
|
+
59 ?- S=T+1,N=(+),/**/write_term(S,[quoted(true),variable_names([N=T])]).
|
|
151
|
+
outputs("++1"), S = ..., N = ...
|
|
152
|
+
| outputs("+ +1"), S = ..., N = ... .
|
|
153
|
+
73 ?- S=(1 is T),N='X',/**/write_term(S,[quoted(true),variable_names([N=T])]).
|
|
154
|
+
outputs("1 is X"), S = ..., N = ... .
|
|
155
|
+
69 ?- read_term(T,[singletons(1)]).
|
|
156
|
+
waits.
|
|
157
|
+
70 ?- /**/read_term(T,[singletons(1)]).
|
|
158
|
+
inputs("a."), peeks("\n"),
|
|
159
|
+
false.
|
|
160
|
+
71 ?- write_term(T,[variable_names(['Bad'=T]),variable_names(['Good'=T])]).
|
|
161
|
+
outputs("Good").
|
|
162
|
+
72 ?- read_term(T,[singletons([])]).
|
|
163
|
+
waits.
|