eyeprolog 1.5.28 → 1.5.30
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/solver.js +90 -44
- package/src/term.js +246 -0
package/package.json
CHANGED
package/src/iso.js
CHANGED
|
@@ -860,7 +860,7 @@ function* retractSolutions({ solver, goal, env }, state) {
|
|
|
860
860
|
if (!unify(parts.head, copied.args[0], next)) continue;
|
|
861
861
|
if (parts.rule && !unify(parts.body, copied.args[1], next)) continue;
|
|
862
862
|
if (!parts.rule && !(copied.args[1].type === ATOM && copied.args[1].name === 'true')) continue;
|
|
863
|
-
matches.push({ clause, next });
|
|
863
|
+
matches.push({ clause, next: next._toPersistentEnv?.() ?? next });
|
|
864
864
|
}
|
|
865
865
|
for (let index = 0; index < matches.length; index++) {
|
|
866
866
|
const { clause, next } = matches[index];
|
package/src/solver.js
CHANGED
|
@@ -392,6 +392,8 @@ export class Solver {
|
|
|
392
392
|
|
|
393
393
|
*solve(goals, env = new Env(), depth = 0) {
|
|
394
394
|
if (!Array.isArray(goals)) goals = [goals];
|
|
395
|
+
const ownsSolverBindings = env._isSolverEnv?.() !== true;
|
|
396
|
+
if (ownsSolverBindings) env = env._asSolverEnv();
|
|
395
397
|
env.setOccursCheckHandler(this.occursCheckHandler);
|
|
396
398
|
env.setAttributeHookRunner(this.attributeHookRunner);
|
|
397
399
|
if (this.isoStrict) rejectStrictIsoStringTerms(goals, env);
|
|
@@ -437,6 +439,42 @@ export class Solver {
|
|
|
437
439
|
});
|
|
438
440
|
continue;
|
|
439
441
|
}
|
|
442
|
+
if (frame.kind === 'unifyArgsBranch') {
|
|
443
|
+
if (this.solutionsSeen >= this.solutionLimit) continue;
|
|
444
|
+
const next = frame.env.clone();
|
|
445
|
+
const sourceArgs = frame.freshNonGround && !frame.args.every((arg) => termIsGround(arg))
|
|
446
|
+
? freshTerm(compound('$branch_args', frame.args), nextFreshId()).args
|
|
447
|
+
: frame.args;
|
|
448
|
+
let ok = true;
|
|
449
|
+
for (let i = 0; i < frame.goal.arity; i++) {
|
|
450
|
+
this.stats.unify_calls++;
|
|
451
|
+
if (!unify(frame.goal.args[i], sourceArgs[i], next)) { ok = false; break; }
|
|
452
|
+
}
|
|
453
|
+
if (ok) {
|
|
454
|
+
stack.push({
|
|
455
|
+
kind: 'goals',
|
|
456
|
+
goals: frame.goals,
|
|
457
|
+
env: next,
|
|
458
|
+
depth: frame.depth,
|
|
459
|
+
active: frame.active,
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
if (frame.kind === 'bindBranch') {
|
|
465
|
+
if (this.solutionsSeen >= this.solutionLimit) continue;
|
|
466
|
+
const next = frame.env.clone();
|
|
467
|
+
for (let i = 0; i < frame.names.length; i++) next.bind(frame.names[i], frame.values[i]);
|
|
468
|
+
if (next._variableConstraints != null && !next.validateVariableConstraints()) continue;
|
|
469
|
+
stack.push({
|
|
470
|
+
kind: 'goals',
|
|
471
|
+
goals: frame.goals,
|
|
472
|
+
env: next,
|
|
473
|
+
depth: frame.depth,
|
|
474
|
+
active: frame.active,
|
|
475
|
+
});
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
440
478
|
if (frame.kind === 'userClause') {
|
|
441
479
|
if (this.solutionsSeen >= this.solutionLimit) continue;
|
|
442
480
|
const { clause, goal: clauseGoal, rest: clauseRest, env: clauseEnv, depth: clauseDepth,
|
|
@@ -585,7 +623,7 @@ export class Solver {
|
|
|
585
623
|
this.solutionsSeen++;
|
|
586
624
|
this.stats.completed_goal_lists++;
|
|
587
625
|
this.active = active;
|
|
588
|
-
yield env;
|
|
626
|
+
yield ownsSolverBindings && env._isSolverEnv?.() === true ? env._toPersistentEnv() : env;
|
|
589
627
|
break;
|
|
590
628
|
}
|
|
591
629
|
|
|
@@ -673,7 +711,7 @@ export class Solver {
|
|
|
673
711
|
// pending exactly as they do for a direct call. A fresh active marker
|
|
674
712
|
// keeps cut opaque at the call/N boundary.
|
|
675
713
|
const expanded = def.expandGoal({ solver: this, goal, env });
|
|
676
|
-
const invocation = { goal, env };
|
|
714
|
+
const invocation = { goal, env: env._snapshotForSolverRead() };
|
|
677
715
|
active = [...active, invocation];
|
|
678
716
|
goals = [expanded, { kind: 'releaseActive' }, ...rest];
|
|
679
717
|
depth++;
|
|
@@ -810,15 +848,15 @@ export class Solver {
|
|
|
810
848
|
if (!entry.computing) {
|
|
811
849
|
if (!this.tableCoordinator) {
|
|
812
850
|
this.tableCoordinator = { entry, cycleSeen: false, entries: new Set([entry]) };
|
|
813
|
-
scheduleTableFixpointRound(stack, this, { entry, group, goal, rest, env, depth, active });
|
|
851
|
+
scheduleTableFixpointRound(stack, this, { entry, group, goal, rest, env, depth, active, mapKey });
|
|
814
852
|
} else {
|
|
815
853
|
entry.computing = true;
|
|
816
854
|
stack.push({ kind: 'completeMemo', entry, revision: this.programRevision });
|
|
817
|
-
pushUserGoalUncachedFrames(stack, this, group, goal, [{ kind: 'memoStore', entry, goal, revision: this.programRevision }, ...rest], env, depth, active);
|
|
855
|
+
pushUserGoalUncachedFrames(stack, this, group, goal, [{ kind: 'memoStore', entry, goal, revision: this.programRevision }, ...rest], env, depth, active, mapKey);
|
|
818
856
|
}
|
|
819
857
|
break;
|
|
820
858
|
}
|
|
821
|
-
if (this.tableCoordinator && (usingBroadTable ||
|
|
859
|
+
if (this.tableCoordinator && (usingBroadTable || activeTableKeyIn(mapKey, active))) {
|
|
822
860
|
this.tableCoordinator.cycleSeen = true;
|
|
823
861
|
}
|
|
824
862
|
pushMemoAnswerFrames(stack, entry, goal, rest, env, depth, active, this);
|
|
@@ -1096,12 +1134,12 @@ export class Solver {
|
|
|
1096
1134
|
// expansion of the current rule body, but it must be released before
|
|
1097
1135
|
// the caller's remaining goals are solved. Keeping the goal active
|
|
1098
1136
|
// through rest goals over-prunes valid transitive/recursive derivations.
|
|
1099
|
-
this.active.push({ goal, env: goalEnv });
|
|
1137
|
+
this.active.push({ goal, env: goalEnv._snapshotForSolverRead() });
|
|
1100
1138
|
for (const bodyEnv of this.solve(body, env, depth + 1)) {
|
|
1101
1139
|
if (this.solutionsSeen > 0) this.solutionsSeen--;
|
|
1102
1140
|
this.active.pop();
|
|
1103
1141
|
yield* this.solve(rest, bodyEnv, depth + 1);
|
|
1104
|
-
this.active.push({ goal, env: goalEnv });
|
|
1142
|
+
this.active.push({ goal, env: goalEnv._snapshotForSolverRead() });
|
|
1105
1143
|
if (this.solutionsSeen >= this.solutionLimit) break;
|
|
1106
1144
|
}
|
|
1107
1145
|
this.active.pop();
|
|
@@ -1214,6 +1252,7 @@ function scheduleTableFixpointRound(stack, solver, frame) {
|
|
|
1214
1252
|
frame.env,
|
|
1215
1253
|
frame.depth,
|
|
1216
1254
|
frame.active,
|
|
1255
|
+
frame.mapKey,
|
|
1217
1256
|
);
|
|
1218
1257
|
}
|
|
1219
1258
|
|
|
@@ -1231,34 +1270,33 @@ function pushMemoAnswerFrames(stack, entry, goal, rest, env, depth, active, solv
|
|
|
1231
1270
|
selected = { bucket, fallback, length: candidateLength };
|
|
1232
1271
|
}
|
|
1233
1272
|
}
|
|
1234
|
-
const
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
:
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
}
|
|
1245
|
-
if (ok) stack.push({ kind: 'goals', goals: rest, env: next, depth: depth + 1, active });
|
|
1273
|
+
const pushReplay = (answerIndex) => {
|
|
1274
|
+
stack.push({
|
|
1275
|
+
kind: 'unifyArgsBranch',
|
|
1276
|
+
goal,
|
|
1277
|
+
args: entry.answers[answerIndex],
|
|
1278
|
+
freshNonGround: true,
|
|
1279
|
+
goals: rest,
|
|
1280
|
+
env,
|
|
1281
|
+
depth: depth + 1,
|
|
1282
|
+
active,
|
|
1283
|
+
});
|
|
1246
1284
|
};
|
|
1247
1285
|
if (selected != null) {
|
|
1248
|
-
for (let i = selected.fallback.length - 1; i >= 0; i--)
|
|
1249
|
-
for (let i = selected.bucket.length - 1; i >= 0; i--)
|
|
1286
|
+
for (let i = selected.fallback.length - 1; i >= 0; i--) pushReplay(selected.fallback[i]);
|
|
1287
|
+
for (let i = selected.bucket.length - 1; i >= 0; i--) pushReplay(selected.bucket[i]);
|
|
1250
1288
|
return;
|
|
1251
1289
|
}
|
|
1252
|
-
for (let answerIndex = entry.answers.length - 1; answerIndex >= 0; answerIndex--)
|
|
1290
|
+
for (let answerIndex = entry.answers.length - 1; answerIndex >= 0; answerIndex--) pushReplay(answerIndex);
|
|
1253
1291
|
}
|
|
1254
1292
|
|
|
1255
|
-
function pushUserGoalUncachedFrames(stack, solver, group, goal, rest, env, depth, active) {
|
|
1293
|
+
function pushUserGoalUncachedFrames(stack, solver, group, goal, rest, env, depth, active, tableMapKey = null) {
|
|
1256
1294
|
if (group.fastPi && pushFastPiFrames(stack, goal, rest, env, depth, active)) return;
|
|
1257
1295
|
if (tryPushGroundScalarRuleFrame(stack, solver, group, goal, rest, env, depth, active)) return;
|
|
1258
1296
|
if (tryPushGroundChainFrames(stack, solver, group, goal, rest, env, depth, active)) return;
|
|
1259
1297
|
const candidates = selectClauseCandidates(group, goal, env);
|
|
1260
1298
|
const frames = [];
|
|
1261
|
-
const invocation = { goal, env };
|
|
1299
|
+
const invocation = { goal, env: env._snapshotForSolverRead(), tableMapKey };
|
|
1262
1300
|
// Active frames serve two purposes: they delimit cut and detect variants in
|
|
1263
1301
|
// recursive user predicates. Cut-free, non-recursive library helpers need
|
|
1264
1302
|
// neither. Copying their full active stack at every recursive step made
|
|
@@ -1535,18 +1573,20 @@ function pushWfsAnswerFrames(stack, model, group, goal, rest, env, depth, active
|
|
|
1535
1573
|
if (!relation) return;
|
|
1536
1574
|
for (let rowIndex = relation.rows.length - 1; rowIndex >= 0; rowIndex--) {
|
|
1537
1575
|
const row = relation.rows[rowIndex];
|
|
1538
|
-
const next = env.clone();
|
|
1539
|
-
let ok = true;
|
|
1540
|
-
for (let i = 0; i < goal.arity; i++) {
|
|
1541
|
-
solver.stats.unify_calls++;
|
|
1542
|
-
if (!unify(goal.args[i], row[i], next)) { ok = false; break; }
|
|
1543
|
-
}
|
|
1544
|
-
if (!ok) continue;
|
|
1545
1576
|
if (!lower?.has(row)) {
|
|
1546
1577
|
solver.stats.wfs_undefined_answers++;
|
|
1547
1578
|
continue;
|
|
1548
1579
|
}
|
|
1549
|
-
stack.push({
|
|
1580
|
+
stack.push({
|
|
1581
|
+
kind: 'unifyArgsBranch',
|
|
1582
|
+
goal,
|
|
1583
|
+
args: row,
|
|
1584
|
+
freshNonGround: false,
|
|
1585
|
+
goals: rest,
|
|
1586
|
+
env,
|
|
1587
|
+
depth: depth + 1,
|
|
1588
|
+
active,
|
|
1589
|
+
});
|
|
1550
1590
|
}
|
|
1551
1591
|
}
|
|
1552
1592
|
|
|
@@ -2030,14 +2070,8 @@ function termReferencesResolvedVariable(term, variableName, env) {
|
|
|
2030
2070
|
}
|
|
2031
2071
|
|
|
2032
2072
|
function envHasObservableAliasTo(variableName, env) {
|
|
2033
|
-
for (
|
|
2034
|
-
if (
|
|
2035
|
-
termReferencesResolvedVariable(state.bindingValue, variableName, env)) return true;
|
|
2036
|
-
if (state.bindings) {
|
|
2037
|
-
for (const [name, value] of state.bindings) {
|
|
2038
|
-
if (name !== variableName && termReferencesResolvedVariable(value, variableName, env)) return true;
|
|
2039
|
-
}
|
|
2040
|
-
}
|
|
2073
|
+
for (const [name, value] of env?._bindingEntries?.() ?? []) {
|
|
2074
|
+
if (name !== variableName && termReferencesResolvedVariable(value, variableName, env)) return true;
|
|
2041
2075
|
}
|
|
2042
2076
|
return false;
|
|
2043
2077
|
}
|
|
@@ -2315,10 +2349,15 @@ function tryPushScalarFactRunFrames(stack, solver, goals, env, depth, active) {
|
|
|
2315
2349
|
const state = localStack.pop();
|
|
2316
2350
|
solver.stats.max_depth = Math.max(solver.stats.max_depth, state.depth);
|
|
2317
2351
|
if (state.index === runLength) {
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2352
|
+
frames.push({
|
|
2353
|
+
kind: 'bindBranch',
|
|
2354
|
+
names: state.names,
|
|
2355
|
+
values: state.values,
|
|
2356
|
+
goals: rest,
|
|
2357
|
+
env,
|
|
2358
|
+
depth: state.depth,
|
|
2359
|
+
active,
|
|
2360
|
+
});
|
|
2322
2361
|
if (frames.length > frameLimit) {
|
|
2323
2362
|
// Do not repeat the old bug of abandoning the optimization and then
|
|
2324
2363
|
// re-solving through the generic engine. Restart this rare oversized
|
|
@@ -2977,6 +3016,13 @@ function rememberMemoAnswer(entry, goal, env) {
|
|
|
2977
3016
|
}
|
|
2978
3017
|
}
|
|
2979
3018
|
|
|
3019
|
+
function activeTableKeyIn(mapKey, active) {
|
|
3020
|
+
for (let index = active.length - 1; index >= 0; index--) {
|
|
3021
|
+
if (active[index]?.tableMapKey === mapKey) return true;
|
|
3022
|
+
}
|
|
3023
|
+
return false;
|
|
3024
|
+
}
|
|
3025
|
+
|
|
2980
3026
|
function activeVariantIn(goal, env, active) {
|
|
2981
3027
|
if (active.length === 0) return false;
|
|
2982
3028
|
let goalShape = null;
|
package/src/term.js
CHANGED
|
@@ -167,6 +167,15 @@ export class Env {
|
|
|
167
167
|
this._attributeHookRunner = typeof handler === 'function' ? handler : null;
|
|
168
168
|
return this;
|
|
169
169
|
}
|
|
170
|
+
_asSolverEnv() {
|
|
171
|
+
return SolverEnv.from(this);
|
|
172
|
+
}
|
|
173
|
+
_isSolverEnv() {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
_snapshotForSolverRead() {
|
|
177
|
+
return this.clone();
|
|
178
|
+
}
|
|
170
179
|
adopt(other) {
|
|
171
180
|
if (!(other instanceof Env)) throw new TypeError('Env.adopt expects Env');
|
|
172
181
|
this._state = other._state;
|
|
@@ -333,6 +342,22 @@ export class Env {
|
|
|
333
342
|
}
|
|
334
343
|
return undefined;
|
|
335
344
|
}
|
|
345
|
+
*_bindingEntries() {
|
|
346
|
+
const seen = new Set();
|
|
347
|
+
for (let state = this._state; state != null; state = state.parent) {
|
|
348
|
+
if (state.bindingName != null && !seen.has(state.bindingName)) {
|
|
349
|
+
seen.add(state.bindingName);
|
|
350
|
+
yield [state.bindingName, state.bindingValue];
|
|
351
|
+
}
|
|
352
|
+
if (state.bindings) {
|
|
353
|
+
for (const [name, value] of state.bindings) {
|
|
354
|
+
if (seen.has(name)) continue;
|
|
355
|
+
seen.add(name);
|
|
356
|
+
yield [name, value];
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
336
361
|
compactForDeepContinuation(segmentLimit = 16) {
|
|
337
362
|
if ((this._state.segmentCount ?? 0) < segmentLimit) return false;
|
|
338
363
|
const flattened = new Map();
|
|
@@ -648,6 +673,227 @@ export class Env {
|
|
|
648
673
|
}
|
|
649
674
|
}
|
|
650
675
|
|
|
676
|
+
|
|
677
|
+
class SolverBindingStore {
|
|
678
|
+
constructor(bindings) {
|
|
679
|
+
this.slots = new Map();
|
|
680
|
+
this.names = [];
|
|
681
|
+
this.values = [];
|
|
682
|
+
this.baseValues = [];
|
|
683
|
+
this.slotLastTrail = [];
|
|
684
|
+
this.freeSlots = [];
|
|
685
|
+
this.trailNames = [];
|
|
686
|
+
this.trailSlots = [];
|
|
687
|
+
this.trailOldValues = [];
|
|
688
|
+
this.trailNewValues = [];
|
|
689
|
+
this.trailPreviousForSlot = [];
|
|
690
|
+
this.trailCreated = [];
|
|
691
|
+
if (bindings != null) {
|
|
692
|
+
for (const [name, value] of bindings) {
|
|
693
|
+
const slot = this.names.length;
|
|
694
|
+
this.slots.set(name, slot);
|
|
695
|
+
this.names.push(name);
|
|
696
|
+
this.values.push(value);
|
|
697
|
+
this.baseValues.push(value);
|
|
698
|
+
this.slotLastTrail.push(-1);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
mark() {
|
|
704
|
+
return this.trailSlots.length;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
rollback(mark) {
|
|
708
|
+
if (mark > this.trailSlots.length) {
|
|
709
|
+
throw new Error('stale solver binding mark');
|
|
710
|
+
}
|
|
711
|
+
while (this.trailSlots.length > mark) {
|
|
712
|
+
const created = this.trailCreated.pop();
|
|
713
|
+
const previousForSlot = this.trailPreviousForSlot.pop();
|
|
714
|
+
this.trailNewValues.pop();
|
|
715
|
+
const oldValue = this.trailOldValues.pop();
|
|
716
|
+
const slot = this.trailSlots.pop();
|
|
717
|
+
const name = this.trailNames.pop();
|
|
718
|
+
this.slotLastTrail[slot] = previousForSlot;
|
|
719
|
+
if (created) {
|
|
720
|
+
this.slots.delete(name);
|
|
721
|
+
this.values[slot] = ENV_UNBOUND;
|
|
722
|
+
this.baseValues[slot] = ENV_UNBOUND;
|
|
723
|
+
this.names[slot] = null;
|
|
724
|
+
this.freeSlots.push(slot);
|
|
725
|
+
} else {
|
|
726
|
+
this.values[slot] = oldValue;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
bind(name, value) {
|
|
732
|
+
let slot = this.slots.get(name);
|
|
733
|
+
const created = slot === undefined;
|
|
734
|
+
if (created) {
|
|
735
|
+
slot = this.freeSlots.length === 0 ? this.values.length : this.freeSlots.pop();
|
|
736
|
+
this.slots.set(name, slot);
|
|
737
|
+
this.names[slot] = name;
|
|
738
|
+
this.values[slot] = ENV_UNBOUND;
|
|
739
|
+
this.baseValues[slot] = ENV_UNBOUND;
|
|
740
|
+
this.slotLastTrail[slot] = -1;
|
|
741
|
+
}
|
|
742
|
+
const entry = this.trailSlots.length;
|
|
743
|
+
this.trailNames.push(name);
|
|
744
|
+
this.trailSlots.push(slot);
|
|
745
|
+
this.trailOldValues.push(this.values[slot]);
|
|
746
|
+
this.trailNewValues.push(value);
|
|
747
|
+
this.trailPreviousForSlot.push(this.slotLastTrail[slot] ?? -1);
|
|
748
|
+
this.trailCreated.push(created);
|
|
749
|
+
this.slotLastTrail[slot] = entry;
|
|
750
|
+
this.values[slot] = value;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
getAtMark(name, mark) {
|
|
754
|
+
if (mark > this.trailSlots.length) throw new Error('stale solver binding mark');
|
|
755
|
+
const slot = this.slots.get(name);
|
|
756
|
+
if (slot === undefined) return undefined;
|
|
757
|
+
let entry = this.slotLastTrail[slot] ?? -1;
|
|
758
|
+
while (entry >= mark) entry = this.trailPreviousForSlot[entry] ?? -1;
|
|
759
|
+
const value = entry >= 0 ? this.trailNewValues[entry] : this.baseValues[slot];
|
|
760
|
+
return value === ENV_UNBOUND ? undefined : value;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function copyEnvBranchState(source, target) {
|
|
765
|
+
target._delays = source._delays;
|
|
766
|
+
target._variableConstraints = source._variableConstraints;
|
|
767
|
+
target._variableAnnotations = source._variableAnnotations;
|
|
768
|
+
target._prologAttributes = source._prologAttributes;
|
|
769
|
+
target._attributeHookRunner = source._attributeHookRunner;
|
|
770
|
+
target._pendingAttributeGoals = source._pendingAttributeGoals;
|
|
771
|
+
target._backtrackableBlackboard = source._backtrackableBlackboard;
|
|
772
|
+
target._occursCheckHandler = source._occursCheckHandler;
|
|
773
|
+
target._localVariables = source._localVariables;
|
|
774
|
+
target._compactVariableOrigins = source._compactVariableOrigins;
|
|
775
|
+
target._compactVariableRisks = source._compactVariableRisks;
|
|
776
|
+
return target;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
class SolverEnvReadView {
|
|
780
|
+
constructor(source) {
|
|
781
|
+
this._solverBindings = source._solverBindings;
|
|
782
|
+
this._solverBindingMark = source._solverBindingMark;
|
|
783
|
+
this._localVariables = source._localVariables;
|
|
784
|
+
}
|
|
785
|
+
get(name) {
|
|
786
|
+
return this._solverBindings.getAtMark(name, this._solverBindingMark);
|
|
787
|
+
}
|
|
788
|
+
has(name) {
|
|
789
|
+
return this.get(name) !== undefined;
|
|
790
|
+
}
|
|
791
|
+
isLocalVariable(name) {
|
|
792
|
+
return this._localVariables?.has(name) === true;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// SolverEnv replaces persistent binding layers with one solver-owned mutable
|
|
797
|
+
// slot store and a linear trail. Clones are just trail marks plus the existing
|
|
798
|
+
// copy-on-write logical side stores. The solver explores these marks in depth-
|
|
799
|
+
// first order, so rollback can discard unreachable branch history immediately.
|
|
800
|
+
// Public Env remains persistent and is used for stable API snapshots.
|
|
801
|
+
class SolverEnv extends Env {
|
|
802
|
+
constructor() {
|
|
803
|
+
super();
|
|
804
|
+
this._solverBindings = new SolverBindingStore();
|
|
805
|
+
this._solverBindingMark = 0;
|
|
806
|
+
this._state = null;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
static from(env) {
|
|
810
|
+
if (env instanceof SolverEnv) return env;
|
|
811
|
+
const solverEnv = Object.create(SolverEnv.prototype);
|
|
812
|
+
solverEnv._state = null;
|
|
813
|
+
solverEnv._solverBindings = new SolverBindingStore(env?._bindingEntries?.() ?? []);
|
|
814
|
+
solverEnv._solverBindingMark = 0;
|
|
815
|
+
copyEnvBranchState(env ?? new Env(), solverEnv);
|
|
816
|
+
return solverEnv;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
_asSolverEnv() {
|
|
820
|
+
return this;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
_isSolverEnv() {
|
|
824
|
+
return true;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
_snapshotForSolverRead() {
|
|
828
|
+
return new SolverEnvReadView(this);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
_activateBindings() {
|
|
832
|
+
if (this._solverBindingMark !== this._solverBindings.trailSlots.length) {
|
|
833
|
+
this._solverBindings.rollback(this._solverBindingMark);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
clone() {
|
|
838
|
+
this._activateBindings();
|
|
839
|
+
const clone = Object.create(SolverEnv.prototype);
|
|
840
|
+
clone._state = null;
|
|
841
|
+
clone._solverBindings = this._solverBindings;
|
|
842
|
+
clone._solverBindingMark = this._solverBindingMark;
|
|
843
|
+
return copyEnvBranchState(this, clone);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
adopt(other) {
|
|
847
|
+
if (!(other instanceof Env)) throw new TypeError('Env.adopt expects Env');
|
|
848
|
+
if (other instanceof SolverEnv && other._solverBindings === this._solverBindings) {
|
|
849
|
+
other._activateBindings();
|
|
850
|
+
this._solverBindingMark = other._solverBindingMark;
|
|
851
|
+
} else {
|
|
852
|
+
const replacement = SolverEnv.from(other);
|
|
853
|
+
this._solverBindings = replacement._solverBindings;
|
|
854
|
+
this._solverBindingMark = replacement._solverBindingMark;
|
|
855
|
+
}
|
|
856
|
+
copyEnvBranchState(other, this);
|
|
857
|
+
return this;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
has(name) {
|
|
861
|
+
return this.get(name) !== undefined;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
get(name) {
|
|
865
|
+
this._activateBindings();
|
|
866
|
+
const slot = this._solverBindings.slots.get(name);
|
|
867
|
+
if (slot === undefined) return undefined;
|
|
868
|
+
const value = this._solverBindings.values[slot];
|
|
869
|
+
return value === ENV_UNBOUND ? undefined : value;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
*_bindingEntries() {
|
|
873
|
+
this._activateBindings();
|
|
874
|
+
for (const [name, slot] of this._solverBindings.slots) {
|
|
875
|
+
const value = this._solverBindings.values[slot];
|
|
876
|
+
if (value !== ENV_UNBOUND) yield [name, value];
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
bind(name, term) {
|
|
881
|
+
this._activateBindings();
|
|
882
|
+
this._solverBindings.bind(name, term);
|
|
883
|
+
this._solverBindingMark = this._solverBindings.mark();
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
compactForDeepContinuation() {
|
|
887
|
+
return false;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
_toPersistentEnv() {
|
|
891
|
+
const stable = new Env(this._bindingEntries());
|
|
892
|
+
copyEnvBranchState(this, stable);
|
|
893
|
+
return stable;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
651
897
|
export function deref(term, env) {
|
|
652
898
|
// Follow variable bindings until a non-variable term is reached. The seen set
|
|
653
899
|
// protects readback from accidental cycles in partially constructed terms.
|