eyeprolog 1.3.17 → 1.3.19
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/write.js +37 -1
- package/test/conformance/wg17-syntax-cases.json +3 -3
- package/test/run-regression.mjs +24 -10
- package/test/run-wg17.mjs +1 -1
package/package.json
CHANGED
package/src/iso.js
CHANGED
|
@@ -1370,6 +1370,7 @@ function writeBuiltin(mode) {
|
|
|
1370
1370
|
const options = defaultTermWriteOptions(mode);
|
|
1371
1371
|
solver.io.writeUnit(stream, formatTermForWrite(goal.args[goal.arity - 1], env, {
|
|
1372
1372
|
...options,
|
|
1373
|
+
generateVariableNames: true,
|
|
1373
1374
|
operators: solver.program.operators.values(),
|
|
1374
1375
|
}));
|
|
1375
1376
|
yield env;
|
|
@@ -1386,6 +1387,7 @@ function* writeTermBuiltin({ solver, goal, env }) {
|
|
|
1386
1387
|
}
|
|
1387
1388
|
solver.io.writeUnit(stream, formatTermForWrite(goal.args[goal.arity - 2], env, {
|
|
1388
1389
|
...options,
|
|
1390
|
+
generateVariableNames: true,
|
|
1389
1391
|
operators: solver.program.operators.values(),
|
|
1390
1392
|
}));
|
|
1391
1393
|
yield env;
|
package/src/write.js
CHANGED
|
@@ -174,6 +174,40 @@ function printableReadVariableNames(term, env, explicit) {
|
|
|
174
174
|
return names;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
function generatedVariableName(index) {
|
|
178
|
+
const letter = String.fromCharCode(65 + (index % 26));
|
|
179
|
+
const suffix = Math.floor(index / 26);
|
|
180
|
+
return suffix === 0 ? `_${letter}` : `_${letter}${suffix}`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function printableGeneratedVariableNames(term, env, explicit) {
|
|
184
|
+
const names = new Map(explicit);
|
|
185
|
+
const used = new Set(names.values());
|
|
186
|
+
const seenVariables = new Set();
|
|
187
|
+
const seenTerms = new Set();
|
|
188
|
+
const stack = [term];
|
|
189
|
+
let generated = 0;
|
|
190
|
+
|
|
191
|
+
while (stack.length) {
|
|
192
|
+
const current = deref(stack.pop(), env);
|
|
193
|
+
if (current.type === VAR) {
|
|
194
|
+
if (seenVariables.has(current.name)) continue;
|
|
195
|
+
seenVariables.add(current.name);
|
|
196
|
+
if (names.has(current.name)) continue;
|
|
197
|
+
let candidate;
|
|
198
|
+
do candidate = generatedVariableName(generated++); while (used.has(candidate));
|
|
199
|
+
names.set(current.name, candidate);
|
|
200
|
+
used.add(candidate);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (current.type !== COMPOUND || seenTerms.has(current)) continue;
|
|
204
|
+
seenTerms.add(current);
|
|
205
|
+
for (let i = current.arity - 1; i >= 0; i--) stack.push(current.args[i]);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return names;
|
|
209
|
+
}
|
|
210
|
+
|
|
177
211
|
function format(term, env, options, table, maxPriority = 1200, context = 'term') {
|
|
178
212
|
const resolved = deref(term, env);
|
|
179
213
|
if (resolved.type === VAR) {
|
|
@@ -282,7 +316,9 @@ export function formatTermForWrite(term, env = new Env(), options = {}) {
|
|
|
282
316
|
ignoreOps: options.ignoreOps === true,
|
|
283
317
|
numbervars: options.numbervars !== false,
|
|
284
318
|
doubleQuotes: options.doubleQuotes,
|
|
285
|
-
variableNames:
|
|
319
|
+
variableNames: options.generateVariableNames === true
|
|
320
|
+
? printableGeneratedVariableNames(term, env, explicitVariableNames)
|
|
321
|
+
: printableReadVariableNames(term, env, explicitVariableNames),
|
|
286
322
|
compact: options.compact === true,
|
|
287
323
|
operatorAtomsAsArgs: options.operatorAtomsAsArgs === true,
|
|
288
324
|
dottedGraphicAtoms: options.dottedGraphicAtoms === true,
|
|
@@ -4128,7 +4128,7 @@
|
|
|
4128
4128
|
"type": "success",
|
|
4129
4129
|
"stages": [
|
|
4130
4130
|
{
|
|
4131
|
-
"output": "+(
|
|
4131
|
+
"output": "+(_A,_B)",
|
|
4132
4132
|
"variables": "[]"
|
|
4133
4133
|
}
|
|
4134
4134
|
]
|
|
@@ -4144,7 +4144,7 @@
|
|
|
4144
4144
|
"type": "success",
|
|
4145
4145
|
"stages": [
|
|
4146
4146
|
{
|
|
4147
|
-
"output": "+(
|
|
4147
|
+
"output": "+(_A,_A)",
|
|
4148
4148
|
"variables": "['B' = B]"
|
|
4149
4149
|
}
|
|
4150
4150
|
]
|
|
@@ -5221,7 +5221,7 @@
|
|
|
5221
5221
|
"type": "success",
|
|
5222
5222
|
"stages": [
|
|
5223
5223
|
{
|
|
5224
|
-
"output": "1 is
|
|
5224
|
+
"output": "1 is _A",
|
|
5225
5225
|
"variables": "[]"
|
|
5226
5226
|
}
|
|
5227
5227
|
]
|
package/test/run-regression.mjs
CHANGED
|
@@ -1860,7 +1860,7 @@ c4 ?- call((!;1)).
|
|
|
1860
1860
|
'check :- read(A), read(B), A \\== B, write_canonical(pair(A, B)), nl.\n',
|
|
1861
1861
|
{ goal: 'check', ioOptions: { input: 'V.\nV.\n' } },
|
|
1862
1862
|
);
|
|
1863
|
-
assertEqual(distinctReads.stdout, 'pair(
|
|
1863
|
+
assertEqual(distinctReads.stdout, 'pair(_A,_B)\ncheck.\n', 'separate read variable sets');
|
|
1864
1864
|
},
|
|
1865
1865
|
},
|
|
1866
1866
|
{
|
|
@@ -2189,7 +2189,7 @@ child.stdin.write(\`consult(${consultedAtom}).\\n\`);
|
|
|
2189
2189
|
{
|
|
2190
2190
|
name: 'npm exec can run package CLI bin from checkout',
|
|
2191
2191
|
run: () => {
|
|
2192
|
-
const result = spawnSync('npm', ['exec', '--loglevel=silent', '--yes', '--package=.', '--', 'eyeprolog', '--version'], {
|
|
2192
|
+
const result = spawnSync('npm', ['exec', '--offline', '--loglevel=silent', '--yes', '--package=.', '--', 'eyeprolog', '--version'], {
|
|
2193
2193
|
cwd: packageRoot,
|
|
2194
2194
|
encoding: 'utf8',
|
|
2195
2195
|
env: { ...process.env, npm_config_update_notifier: 'false' },
|
|
@@ -2975,6 +2975,20 @@ child.stdin.write(\`consult(${consultedAtom}).\\n\`);
|
|
|
2975
2975
|
}
|
|
2976
2976
|
},
|
|
2977
2977
|
},
|
|
2978
|
+
{
|
|
2979
|
+
name: 'writeq gives unnamed variables underscore-prefixed names (issue #53)',
|
|
2980
|
+
run: () => {
|
|
2981
|
+
const result = runCli([], {
|
|
2982
|
+
input: "writeq(E).\nwriteq(pair(X,X,Y)).\nwrite_term(pair(X,Y), [variable_names(['Left'=X])]).\nhalt.\n",
|
|
2983
|
+
});
|
|
2984
|
+
assertEqual(result.status, 0, 'exit status');
|
|
2985
|
+
assertIncludes(result.stdout, ' _A true.\n', 'single unnamed variable');
|
|
2986
|
+
assertIncludes(result.stdout, ' pair(_A,_A,_B) true.\n', 'stable repeated variable names');
|
|
2987
|
+
assertIncludes(result.stdout, ' pair(Left,_A) true.\n', 'explicit variable name plus generated fallback');
|
|
2988
|
+
const fresh = run('emit :- writeq(E), nl.\n', { goal: 'emit' });
|
|
2989
|
+
assertEqual(fresh.stdout, '_A\nemit.\n', 'fresh clause variable hides its internal suffix');
|
|
2990
|
+
},
|
|
2991
|
+
},
|
|
2978
2992
|
{
|
|
2979
2993
|
name: 'write predicates and write_term options select distinct formats',
|
|
2980
2994
|
run: () => {
|
|
@@ -3783,30 +3797,30 @@ answer(ok) :-
|
|
|
3783
3797
|
`;
|
|
3784
3798
|
const script = `
|
|
3785
3799
|
import { Program, Solver, Env, parseGoalText, getEyePrologRegistry } from ${JSON.stringify(engineUrl)};
|
|
3786
|
-
const program = Program.parse(${JSON.stringify(programText)}, { autoloadGoals: ['ti(R,
|
|
3800
|
+
const program = Program.parse(${JSON.stringify(programText)}, { autoloadGoals: ['ti(R,1,0)'] });
|
|
3787
3801
|
const listTailGroup = program.findGroup('...', 2, 'user');
|
|
3788
3802
|
if (listTailGroup?.listTailRecursive !== true) {
|
|
3789
3803
|
throw new Error('recursive DCG tail-consumption was not recognized');
|
|
3790
3804
|
}
|
|
3791
3805
|
const solver = new Solver(program, { registry: getEyePrologRegistry(), maxMemoryBytes: Infinity });
|
|
3792
|
-
const goal = parseGoalText('ti(R,
|
|
3806
|
+
const goal = parseGoalText('ti(R,1,0)', {
|
|
3793
3807
|
doubleQuotes: 'chars',
|
|
3794
3808
|
operatorDefinitions: [...program.operators.values()],
|
|
3795
3809
|
});
|
|
3796
3810
|
let count = 0;
|
|
3797
3811
|
for (const _ of solver.solve([goal], new Env(), 0)) {
|
|
3798
|
-
if (++count ===
|
|
3812
|
+
if (++count === 10) break;
|
|
3799
3813
|
}
|
|
3800
|
-
if (count !==
|
|
3814
|
+
if (count !== 10) throw new Error('unexpected checkpoint count: ' + count);
|
|
3801
3815
|
const scope = solver.innerTableScopes.get('phrase');
|
|
3802
3816
|
if (scope == null) throw new Error('missing phrase table scope');
|
|
3803
3817
|
if (scope.memo.size !== 0) throw new Error('distinct tail-DCG inputs should not retain phrase tables: ' + scope.memo.size);
|
|
3804
3818
|
process.stdout.write(count + ':' + scope.memo.size);
|
|
3805
3819
|
`;
|
|
3806
3820
|
const result = spawnSync(process.execPath, [
|
|
3807
|
-
// This
|
|
3808
|
-
//
|
|
3809
|
-
//
|
|
3821
|
+
// This covers 100 accepted number candidates, enough to exercise
|
|
3822
|
+
// many distinct recursive DCG inputs while keeping this a focused
|
|
3823
|
+
// bounded-cache regression rather than a memory stress benchmark.
|
|
3810
3824
|
'--max-old-space-size=32',
|
|
3811
3825
|
'--input-type=module',
|
|
3812
3826
|
'--eval',
|
|
@@ -3815,7 +3829,7 @@ answer(ok) :-
|
|
|
3815
3829
|
if (result.error) throw result.error;
|
|
3816
3830
|
assertEqual(result.status, 0, `bounded phrase-table child status; stderr=${result.stderr}`);
|
|
3817
3831
|
const [countText, cacheText] = result.stdout.trim().split(':');
|
|
3818
|
-
assertEqual(countText, '
|
|
3832
|
+
assertEqual(countText, '10', 'recursive phrase checkpoints');
|
|
3819
3833
|
assertEqual(cacheText, '0', 'distinct tail-DCG phrase cache');
|
|
3820
3834
|
},
|
|
3821
3835
|
},
|
package/test/run-wg17.mjs
CHANGED
|
@@ -18,7 +18,7 @@ function runnerStage(index, maximum) {
|
|
|
18
18
|
return `read_term(G${index}, [variable_names(V${index})]), ` +
|
|
19
19
|
`(G${index} == end_of_file -> write('\\n<WG17-COMPLETE>') ; (` +
|
|
20
20
|
`write('\\n<WG17-BEGIN-${index}>'), call(G${index}), ` +
|
|
21
|
-
`write('<WG17-VARS>'),
|
|
21
|
+
`write('<WG17-VARS>'), write_term(V${index}, [quoted(true), variable_names(V${index})]), write('<WG17-END>'), ` +
|
|
22
22
|
`${runnerStage(index + 1, maximum)}))`;
|
|
23
23
|
}
|
|
24
24
|
|