eyeprolog 1.5.29 → 1.5.31
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/parser.js +23 -9
- package/src/program.js +18 -11
- package/src/solver.js +22 -8
package/package.json
CHANGED
package/src/parser.js
CHANGED
|
@@ -1382,10 +1382,14 @@ function parseClausesFastNoSource(source, emit = null, emitBinary = null, option
|
|
|
1382
1382
|
return atom(text);
|
|
1383
1383
|
};
|
|
1384
1384
|
|
|
1385
|
-
const
|
|
1385
|
+
const trimRangeStart = (text, start, end) => {
|
|
1386
1386
|
while (start < end && isWhitespaceCode(text.charCodeAt(start))) start++;
|
|
1387
|
+
return start;
|
|
1388
|
+
};
|
|
1389
|
+
|
|
1390
|
+
const trimRangeEnd = (text, start, end) => {
|
|
1387
1391
|
while (end > start && isWhitespaceCode(text.charCodeAt(end - 1))) end--;
|
|
1388
|
-
return
|
|
1392
|
+
return end;
|
|
1389
1393
|
};
|
|
1390
1394
|
|
|
1391
1395
|
const tokenKindInRange = (text, start, end) => {
|
|
@@ -1427,7 +1431,8 @@ function parseClausesFastNoSource(source, emit = null, emitBinary = null, option
|
|
|
1427
1431
|
};
|
|
1428
1432
|
|
|
1429
1433
|
const scalarOrVariableRange = (text, start, end) => {
|
|
1430
|
-
|
|
1434
|
+
start = trimRangeStart(text, start, end);
|
|
1435
|
+
end = trimRangeEnd(text, start, end);
|
|
1431
1436
|
const kind = tokenKindInRange(text, start, end);
|
|
1432
1437
|
const value = text.slice(start, end);
|
|
1433
1438
|
if (kind === 'var') {
|
|
@@ -1447,20 +1452,27 @@ function parseClausesFastNoSource(source, emit = null, emitBinary = null, option
|
|
|
1447
1452
|
const rawBody = {};
|
|
1448
1453
|
|
|
1449
1454
|
const scalarTokenRange = (text, start, end, out, slot) => {
|
|
1450
|
-
|
|
1455
|
+
start = trimRangeStart(text, start, end);
|
|
1456
|
+
end = trimRangeEnd(text, start, end);
|
|
1451
1457
|
const kind = tokenKindInRange(text, start, end);
|
|
1452
1458
|
let type = kind;
|
|
1453
1459
|
if (kind == null && simpleNumberInRange(text, start, end)) type = 'number';
|
|
1454
1460
|
if (type == null) return false;
|
|
1455
1461
|
let name = text.slice(start, end);
|
|
1456
1462
|
if (type === 'var' && name === '_') name = `__anon${anonymous++}`;
|
|
1457
|
-
|
|
1458
|
-
|
|
1463
|
+
if (slot === 0) {
|
|
1464
|
+
out.arg0Type = type;
|
|
1465
|
+
out.arg0Name = name;
|
|
1466
|
+
} else {
|
|
1467
|
+
out.arg1Type = type;
|
|
1468
|
+
out.arg1Name = name;
|
|
1469
|
+
}
|
|
1459
1470
|
return true;
|
|
1460
1471
|
};
|
|
1461
1472
|
|
|
1462
1473
|
const parseBinaryRawRange = (text, start, end, out) => {
|
|
1463
|
-
|
|
1474
|
+
start = trimRangeStart(text, start, end);
|
|
1475
|
+
end = trimRangeEnd(text, start, end);
|
|
1464
1476
|
let i = start;
|
|
1465
1477
|
const first = text.charCodeAt(i);
|
|
1466
1478
|
if (!(first >= 97 && first <= 122)) return false;
|
|
@@ -1487,7 +1499,8 @@ function parseClausesFastNoSource(source, emit = null, emitBinary = null, option
|
|
|
1487
1499
|
};
|
|
1488
1500
|
|
|
1489
1501
|
const parseBinaryCompoundRange = (text, start = 0, end = text.length) => {
|
|
1490
|
-
|
|
1502
|
+
start = trimRangeStart(text, start, end);
|
|
1503
|
+
end = trimRangeEnd(text, start, end);
|
|
1491
1504
|
let i = start;
|
|
1492
1505
|
const first = text.charCodeAt(i);
|
|
1493
1506
|
if (!(first >= 97 && first <= 122)) return null;
|
|
@@ -1680,7 +1693,8 @@ function parseClausesFastNoSource(source, emit = null, emitBinary = null, option
|
|
|
1680
1693
|
let contentStart = lineStart;
|
|
1681
1694
|
let contentEnd = lineEnd;
|
|
1682
1695
|
if (contentEnd > contentStart && source.charCodeAt(contentEnd - 1) === 13) contentEnd--;
|
|
1683
|
-
|
|
1696
|
+
contentStart = trimRangeStart(source, contentStart, contentEnd);
|
|
1697
|
+
contentEnd = trimRangeEnd(source, contentStart, contentEnd);
|
|
1684
1698
|
if (contentStart < contentEnd && source.charCodeAt(contentStart) !== 37) {
|
|
1685
1699
|
if (!chunk && source.charCodeAt(contentEnd - 1) === 46 && !preparationConversionActive()) {
|
|
1686
1700
|
if (emitFastBinaryRange(source, contentStart, contentEnd, lineNumber)) {
|
package/src/program.js
CHANGED
|
@@ -36,6 +36,11 @@ const DEFER_PROGRAM_BUILD = Symbol('deferProgramBuild');
|
|
|
36
36
|
const FAST_PARSE_ABORT = Symbol('fastParseAbort');
|
|
37
37
|
const PROGRAM_BUILD_BATCH_SIZE = 16384;
|
|
38
38
|
const preparedBundledLibraryCache = new Map();
|
|
39
|
+
const NORMAL_OPERATOR_DEFINITIONS = [
|
|
40
|
+
...ISO_OPERATOR_DEFINITIONS,
|
|
41
|
+
...PART3_OPERATOR_DEFINITIONS,
|
|
42
|
+
...QUAD_OPERATOR_DEFINITIONS,
|
|
43
|
+
];
|
|
39
44
|
|
|
40
45
|
function collectMultiplicitySensitiveDependencies(goal, out = []) {
|
|
41
46
|
if (goal?.type !== COMPOUND) return out;
|
|
@@ -151,13 +156,12 @@ export class Program {
|
|
|
151
156
|
// recursive predicates retain standard depth-first Prolog control.
|
|
152
157
|
this.tabledPredicates = new Set();
|
|
153
158
|
this.operators = new Map();
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
159
|
+
const predefinedOperators = this.strictIso ? ISO_OPERATOR_DEFINITIONS : NORMAL_OPERATOR_DEFINITIONS;
|
|
160
|
+
// The predefined lists contain no competing declarations in the same
|
|
161
|
+
// prefix, infix, or postfix class. Populate them directly; defineOperator
|
|
162
|
+
// still performs replacement semantics for source-level op/3 directives.
|
|
163
|
+
for (const [priority, specifier, name] of predefinedOperators) {
|
|
164
|
+
this.operators.set(`${specifier}\u0000${name}`, { priority, specifier, name });
|
|
161
165
|
}
|
|
162
166
|
this.initializations = [];
|
|
163
167
|
// Track how many initialization/1 directives have run so a REPL can
|
|
@@ -251,12 +255,15 @@ export class Program {
|
|
|
251
255
|
}
|
|
252
256
|
findGroup(name, arity, module = 'user') {
|
|
253
257
|
const indicator = `${name}/${arity}`;
|
|
254
|
-
const
|
|
255
|
-
|
|
258
|
+
const direct = this.groups.get(module === 'user' ? indicator : `${module}:${indicator}`);
|
|
259
|
+
if (direct) return direct;
|
|
260
|
+
let current = this.moduleImports.get(module)?.get(indicator);
|
|
261
|
+
if (current == null) return null;
|
|
262
|
+
const visited = new Set([module]);
|
|
256
263
|
while (!visited.has(current)) {
|
|
257
264
|
visited.add(current);
|
|
258
|
-
const
|
|
259
|
-
if (
|
|
265
|
+
const imported = this.groups.get(current === 'user' ? indicator : `${current}:${indicator}`);
|
|
266
|
+
if (imported) return imported;
|
|
260
267
|
const importedModule = this.moduleImports.get(current)?.get(indicator);
|
|
261
268
|
if (importedModule == null) return null;
|
|
262
269
|
current = importedModule;
|
package/src/solver.js
CHANGED
|
@@ -115,7 +115,11 @@ export class Solver {
|
|
|
115
115
|
reservationBytes: 0,
|
|
116
116
|
checks: 0,
|
|
117
117
|
};
|
|
118
|
-
|
|
118
|
+
// Nested meta-call solvers contribute to one execution and inspect the
|
|
119
|
+
// same process heap. Share their sampling deadline so each short-lived
|
|
120
|
+
// child does not repeat an expensive host memory query before the parent
|
|
121
|
+
// has advanced the guard interval.
|
|
122
|
+
this.memoryCheckState = options.memoryCheckState ?? { nextObservation: 0 };
|
|
119
123
|
// Do not impose an implicit answer cap. Infinite and very large searches are
|
|
120
124
|
// part of normal Prolog semantics; callers that need a resource bound can
|
|
121
125
|
// still supply solutionLimit explicitly.
|
|
@@ -215,6 +219,7 @@ export class Solver {
|
|
|
215
219
|
maxInferences: this.maxInferences,
|
|
216
220
|
maxMemoryBytes: this.maxMemoryBytes,
|
|
217
221
|
memoryRecovery: this.memoryRecovery,
|
|
222
|
+
memoryCheckState: this.memoryCheckState,
|
|
218
223
|
solutionLimit,
|
|
219
224
|
isoStrict: this.isoStrict,
|
|
220
225
|
prologFlags: this.prologFlags,
|
|
@@ -848,15 +853,15 @@ export class Solver {
|
|
|
848
853
|
if (!entry.computing) {
|
|
849
854
|
if (!this.tableCoordinator) {
|
|
850
855
|
this.tableCoordinator = { entry, cycleSeen: false, entries: new Set([entry]) };
|
|
851
|
-
scheduleTableFixpointRound(stack, this, { entry, group, goal, rest, env, depth, active });
|
|
856
|
+
scheduleTableFixpointRound(stack, this, { entry, group, goal, rest, env, depth, active, mapKey });
|
|
852
857
|
} else {
|
|
853
858
|
entry.computing = true;
|
|
854
859
|
stack.push({ kind: 'completeMemo', entry, revision: this.programRevision });
|
|
855
|
-
pushUserGoalUncachedFrames(stack, this, group, goal, [{ kind: 'memoStore', entry, goal, revision: this.programRevision }, ...rest], env, depth, active);
|
|
860
|
+
pushUserGoalUncachedFrames(stack, this, group, goal, [{ kind: 'memoStore', entry, goal, revision: this.programRevision }, ...rest], env, depth, active, mapKey);
|
|
856
861
|
}
|
|
857
862
|
break;
|
|
858
863
|
}
|
|
859
|
-
if (this.tableCoordinator && (usingBroadTable ||
|
|
864
|
+
if (this.tableCoordinator && (usingBroadTable || activeTableKeyIn(mapKey, active))) {
|
|
860
865
|
this.tableCoordinator.cycleSeen = true;
|
|
861
866
|
}
|
|
862
867
|
pushMemoAnswerFrames(stack, entry, goal, rest, env, depth, active, this);
|
|
@@ -1005,8 +1010,9 @@ export class Solver {
|
|
|
1005
1010
|
}
|
|
1006
1011
|
|
|
1007
1012
|
checkMemoryLimit(force = false) {
|
|
1008
|
-
|
|
1009
|
-
|
|
1013
|
+
const observation = this.inferenceObservation.value;
|
|
1014
|
+
if (!force && observation < this.memoryCheckState.nextObservation) return;
|
|
1015
|
+
this.memoryCheckState.nextObservation = observation + 256;
|
|
1010
1016
|
if (!Number.isFinite(this.maxMemoryBytes)) return;
|
|
1011
1017
|
const used = usedHeapSize();
|
|
1012
1018
|
if (used != null && used < this.maxMemoryBytes) this.finishMemoryRecovery();
|
|
@@ -1252,6 +1258,7 @@ function scheduleTableFixpointRound(stack, solver, frame) {
|
|
|
1252
1258
|
frame.env,
|
|
1253
1259
|
frame.depth,
|
|
1254
1260
|
frame.active,
|
|
1261
|
+
frame.mapKey,
|
|
1255
1262
|
);
|
|
1256
1263
|
}
|
|
1257
1264
|
|
|
@@ -1289,13 +1296,13 @@ function pushMemoAnswerFrames(stack, entry, goal, rest, env, depth, active, solv
|
|
|
1289
1296
|
for (let answerIndex = entry.answers.length - 1; answerIndex >= 0; answerIndex--) pushReplay(answerIndex);
|
|
1290
1297
|
}
|
|
1291
1298
|
|
|
1292
|
-
function pushUserGoalUncachedFrames(stack, solver, group, goal, rest, env, depth, active) {
|
|
1299
|
+
function pushUserGoalUncachedFrames(stack, solver, group, goal, rest, env, depth, active, tableMapKey = null) {
|
|
1293
1300
|
if (group.fastPi && pushFastPiFrames(stack, goal, rest, env, depth, active)) return;
|
|
1294
1301
|
if (tryPushGroundScalarRuleFrame(stack, solver, group, goal, rest, env, depth, active)) return;
|
|
1295
1302
|
if (tryPushGroundChainFrames(stack, solver, group, goal, rest, env, depth, active)) return;
|
|
1296
1303
|
const candidates = selectClauseCandidates(group, goal, env);
|
|
1297
1304
|
const frames = [];
|
|
1298
|
-
const invocation = { goal, env: env._snapshotForSolverRead() };
|
|
1305
|
+
const invocation = { goal, env: env._snapshotForSolverRead(), tableMapKey };
|
|
1299
1306
|
// Active frames serve two purposes: they delimit cut and detect variants in
|
|
1300
1307
|
// recursive user predicates. Cut-free, non-recursive library helpers need
|
|
1301
1308
|
// neither. Copying their full active stack at every recursive step made
|
|
@@ -3015,6 +3022,13 @@ function rememberMemoAnswer(entry, goal, env) {
|
|
|
3015
3022
|
}
|
|
3016
3023
|
}
|
|
3017
3024
|
|
|
3025
|
+
function activeTableKeyIn(mapKey, active) {
|
|
3026
|
+
for (let index = active.length - 1; index >= 0; index--) {
|
|
3027
|
+
if (active[index]?.tableMapKey === mapKey) return true;
|
|
3028
|
+
}
|
|
3029
|
+
return false;
|
|
3030
|
+
}
|
|
3031
|
+
|
|
3018
3032
|
function activeVariantIn(goal, env, active) {
|
|
3019
3033
|
if (active.length === 0) return false;
|
|
3020
3034
|
let goalShape = null;
|