eyeprolog 1.3.22 → 1.3.23
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 +2 -0
- package/src/solver.js +18 -0
- package/src/write.js +12 -4
- package/test/run-regression.mjs +14 -0
package/package.json
CHANGED
package/src/iso.js
CHANGED
|
@@ -1371,6 +1371,7 @@ function writeBuiltin(mode) {
|
|
|
1371
1371
|
solver.io.writeUnit(stream, formatTermForWrite(goal.args[goal.arity - 1], env, {
|
|
1372
1372
|
...options,
|
|
1373
1373
|
generateVariableNames: true,
|
|
1374
|
+
variableNameState: solver.writeVariableState,
|
|
1374
1375
|
operators: solver.program.operators.values(),
|
|
1375
1376
|
}));
|
|
1376
1377
|
yield env;
|
|
@@ -1388,6 +1389,7 @@ function* writeTermBuiltin({ solver, goal, env }) {
|
|
|
1388
1389
|
solver.io.writeUnit(stream, formatTermForWrite(goal.args[goal.arity - 2], env, {
|
|
1389
1390
|
...options,
|
|
1390
1391
|
generateVariableNames: true,
|
|
1392
|
+
variableNameState: solver.writeVariableState,
|
|
1391
1393
|
operators: solver.program.operators.values(),
|
|
1392
1394
|
}));
|
|
1393
1395
|
yield env;
|
package/src/solver.js
CHANGED
|
@@ -116,6 +116,11 @@ export class Solver {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
this.io = options.io ?? new StreamManager(options.ioOptions);
|
|
119
|
+
// Keep generated write-variable names stable for the lifetime of one
|
|
120
|
+
// top-level query. Inner/meta-call solvers share this state so separate
|
|
121
|
+
// write/1, writeq/1, write_canonical/1, and write_term/2-3 calls can refer
|
|
122
|
+
// to the same logical variable by the same printed name.
|
|
123
|
+
this.writeVariableState = options.writeVariableState ?? { depth: 0, names: new Map(), next: 0 };
|
|
119
124
|
this.solveStacks = [];
|
|
120
125
|
this.active = [];
|
|
121
126
|
this.cutEpoch = 0;
|
|
@@ -163,6 +168,7 @@ export class Solver {
|
|
|
163
168
|
io: this.io,
|
|
164
169
|
innerTableScopes: this.innerTableScopes,
|
|
165
170
|
inferenceObservation: this.inferenceObservation,
|
|
171
|
+
writeVariableState: this.writeVariableState,
|
|
166
172
|
skipListTailTabling: options.skipListTailTabling ?? this.skipListTailTabling,
|
|
167
173
|
});
|
|
168
174
|
if (options.tableScope != null) {
|
|
@@ -279,6 +285,13 @@ export class Solver {
|
|
|
279
285
|
if (!Array.isArray(goals)) goals = [goals];
|
|
280
286
|
env.setOccursCheckHandler(this.occursCheckHandler);
|
|
281
287
|
|
|
288
|
+
const writeVariableState = this.writeVariableState;
|
|
289
|
+
if (writeVariableState.depth === 0) {
|
|
290
|
+
writeVariableState.names.clear();
|
|
291
|
+
writeVariableState.next = 0;
|
|
292
|
+
}
|
|
293
|
+
writeVariableState.depth++;
|
|
294
|
+
|
|
282
295
|
const savedActive = this.active;
|
|
283
296
|
let registeredStack = null;
|
|
284
297
|
try {
|
|
@@ -651,6 +664,11 @@ export class Solver {
|
|
|
651
664
|
const stackIndex = this.solveStacks.indexOf(registeredStack);
|
|
652
665
|
if (stackIndex >= 0) this.solveStacks.splice(stackIndex, 1);
|
|
653
666
|
this.active = savedActive;
|
|
667
|
+
writeVariableState.depth = Math.max(0, writeVariableState.depth - 1);
|
|
668
|
+
if (writeVariableState.depth === 0) {
|
|
669
|
+
writeVariableState.names.clear();
|
|
670
|
+
writeVariableState.next = 0;
|
|
671
|
+
}
|
|
654
672
|
}
|
|
655
673
|
}
|
|
656
674
|
|
package/src/write.js
CHANGED
|
@@ -180,13 +180,14 @@ function generatedVariableName(index) {
|
|
|
180
180
|
return suffix === 0 ? `_${letter}` : `_${letter}${suffix}`;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
function printableGeneratedVariableNames(term, env, explicit) {
|
|
183
|
+
function printableGeneratedVariableNames(term, env, explicit, state = null) {
|
|
184
184
|
const names = new Map(explicit);
|
|
185
|
-
const
|
|
185
|
+
const sharedNames = state?.names instanceof Map ? state.names : new Map();
|
|
186
|
+
const used = new Set([...sharedNames.values(), ...names.values()]);
|
|
186
187
|
const seenVariables = new Set();
|
|
187
188
|
const seenTerms = new Set();
|
|
188
189
|
const stack = [term];
|
|
189
|
-
let generated = 0;
|
|
190
|
+
let generated = Number.isSafeInteger(state?.next) ? state.next : 0;
|
|
190
191
|
|
|
191
192
|
while (stack.length) {
|
|
192
193
|
const current = deref(stack.pop(), env);
|
|
@@ -194,8 +195,14 @@ function printableGeneratedVariableNames(term, env, explicit) {
|
|
|
194
195
|
if (seenVariables.has(current.name)) continue;
|
|
195
196
|
seenVariables.add(current.name);
|
|
196
197
|
if (names.has(current.name)) continue;
|
|
198
|
+
const shared = sharedNames.get(current.name);
|
|
199
|
+
if (shared != null) {
|
|
200
|
+
names.set(current.name, shared);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
197
203
|
let candidate;
|
|
198
204
|
do candidate = generatedVariableName(generated++); while (used.has(candidate));
|
|
205
|
+
sharedNames.set(current.name, candidate);
|
|
199
206
|
names.set(current.name, candidate);
|
|
200
207
|
used.add(candidate);
|
|
201
208
|
continue;
|
|
@@ -205,6 +212,7 @@ function printableGeneratedVariableNames(term, env, explicit) {
|
|
|
205
212
|
for (let i = current.arity - 1; i >= 0; i--) stack.push(current.args[i]);
|
|
206
213
|
}
|
|
207
214
|
|
|
215
|
+
if (state != null) state.next = generated;
|
|
208
216
|
return names;
|
|
209
217
|
}
|
|
210
218
|
|
|
@@ -317,7 +325,7 @@ export function formatTermForWrite(term, env = new Env(), options = {}) {
|
|
|
317
325
|
numbervars: options.numbervars !== false,
|
|
318
326
|
doubleQuotes: options.doubleQuotes,
|
|
319
327
|
variableNames: options.generateVariableNames === true
|
|
320
|
-
? printableGeneratedVariableNames(term, env, explicitVariableNames)
|
|
328
|
+
? printableGeneratedVariableNames(term, env, explicitVariableNames, options.variableNameState)
|
|
321
329
|
: printableReadVariableNames(term, env, explicitVariableNames),
|
|
322
330
|
compact: options.compact === true,
|
|
323
331
|
operatorAtomsAsArgs: options.operatorAtomsAsArgs === true,
|
package/test/run-regression.mjs
CHANGED
|
@@ -2989,6 +2989,20 @@ child.stdin.write(\`consult(${consultedAtom}).\\n\`);
|
|
|
2989
2989
|
assertEqual(fresh.stdout, '_A\nemit.\n', 'fresh clause variable hides its internal suffix');
|
|
2990
2990
|
},
|
|
2991
2991
|
},
|
|
2992
|
+
{
|
|
2993
|
+
name: 'write predicates keep generated variable names stable across calls (issue #53 comment 5356861151)',
|
|
2994
|
+
run: () => {
|
|
2995
|
+
const result = run([
|
|
2996
|
+
"emit :- write_term(pair(A,B), []), write(' / '), write_term(user_output,B,[]), write(' / '), writeq(A), write(' / '), write_canonical(B), nl.",
|
|
2997
|
+
"again :- write_canonical(B+B), nl.",
|
|
2998
|
+
].join('\n'), { goals: ['emit', 'again'] });
|
|
2999
|
+
assertEqual(
|
|
3000
|
+
result.stdout,
|
|
3001
|
+
'pair(_A,_B) / _B / _A / _B\nemit.\n+(_A,_A)\nagain.\n',
|
|
3002
|
+
'stable names across calls and reset at the next top-level query',
|
|
3003
|
+
);
|
|
3004
|
+
},
|
|
3005
|
+
},
|
|
2992
3006
|
{
|
|
2993
3007
|
name: 'write predicates and write_term options select distinct formats',
|
|
2994
3008
|
run: () => {
|