eyeprolog 1.3.75 → 1.3.76
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 +11 -2
- package/src/repl.js +6 -0
- package/src/term.js +4 -3
- package/test/run-regression.mjs +17 -1
package/package.json
CHANGED
package/src/iso.js
CHANGED
|
@@ -202,7 +202,12 @@ function difConstraint(left, right) {
|
|
|
202
202
|
status(env) {
|
|
203
203
|
if (identical(left, right, env)) return 'violated';
|
|
204
204
|
const probe = env.clone();
|
|
205
|
-
|
|
205
|
+
// Test structural unifiability without waking freeze/2 or other
|
|
206
|
+
// attributed-variable hooks in this disposable environment.
|
|
207
|
+
return unify(left, right, probe, {
|
|
208
|
+
skipVariableConstraints: true,
|
|
209
|
+
skipAttributeHooks: true,
|
|
210
|
+
}) ? 'pending' : 'entailed';
|
|
206
211
|
},
|
|
207
212
|
residualGoal() {
|
|
208
213
|
return compound('dif', [left, right]);
|
|
@@ -218,8 +223,12 @@ function* difBuiltin({ goal, env }) {
|
|
|
218
223
|
// In EyeProlog's finite-tree model, terms that cannot unify are already
|
|
219
224
|
// provably different and need no residual constraint. Otherwise attach a
|
|
220
225
|
// disequality constraint to every currently unbound variable it mentions.
|
|
226
|
+
// This probe must not wake goals attached to those variables.
|
|
221
227
|
const probe = env.clone();
|
|
222
|
-
if (!unify(left, right, probe, {
|
|
228
|
+
if (!unify(left, right, probe, {
|
|
229
|
+
skipVariableConstraints: true,
|
|
230
|
+
skipAttributeHooks: true,
|
|
231
|
+
})) {
|
|
223
232
|
yield env;
|
|
224
233
|
return;
|
|
225
234
|
}
|
package/src/repl.js
CHANGED
|
@@ -722,6 +722,12 @@ function formatAnswer(engine, state, variables, env) {
|
|
|
722
722
|
: equality.specifier === 'xfy' ? equality.priority : equality.priority - 1;
|
|
723
723
|
let generated = 0;
|
|
724
724
|
|
|
725
|
+
// Meta-predicate wrappers can make a query variable alias an internal fresh
|
|
726
|
+
// variable. Keep residual constraints tied to the query's visible name.
|
|
727
|
+
for (const variable of variables) {
|
|
728
|
+
const root = engine.deref(variable, env);
|
|
729
|
+
if (root.type === 'var' && !names.has(root.name)) names.set(root.name, variable.name);
|
|
730
|
+
}
|
|
725
731
|
for (const variable of variables) collectUnboundVariables(engine, variable, env, names, () => `_${letterName(generated++)}`);
|
|
726
732
|
for (const variable of variables) {
|
|
727
733
|
const value = engine.deref(variable, env);
|
package/src/term.js
CHANGED
|
@@ -650,6 +650,7 @@ export function unify(left, right, env, options = {}) {
|
|
|
650
650
|
// unification: a variable cannot be bound to a term containing itself.
|
|
651
651
|
// Bindings are written into the supplied Env.
|
|
652
652
|
const occursCheckHandler = options.occursCheck === 'fail' ? null : env?._occursCheckHandler;
|
|
653
|
+
const runAttributeHooks = options.skipAttributeHooks !== true;
|
|
653
654
|
// Callers may provide a proof that selected variables cannot occur in the
|
|
654
655
|
// term they are about to receive. Source-level first-use analysis and a few
|
|
655
656
|
// construction fast paths share this internal proof; ordinary unification
|
|
@@ -663,7 +664,7 @@ export function unify(left, right, env, options = {}) {
|
|
|
663
664
|
|
|
664
665
|
if (a.type === VAR && b.type === VAR && a.name === b.name) continue;
|
|
665
666
|
if (a.type === VAR && b.type === VAR) {
|
|
666
|
-
if (env?._prologAttributes != null) {
|
|
667
|
+
if (runAttributeHooks && env?._prologAttributes != null) {
|
|
667
668
|
if (!env.preparePrologAttributeUnification(a, b)) return false;
|
|
668
669
|
a = deref(a, env);
|
|
669
670
|
b = deref(b, env);
|
|
@@ -694,7 +695,7 @@ export function unify(left, right, env, options = {}) {
|
|
|
694
695
|
occursCheckHandler?.(a, b, env);
|
|
695
696
|
return false;
|
|
696
697
|
}
|
|
697
|
-
if (env?._prologAttributes != null && env.hasPrologAttributes(a.name)) {
|
|
698
|
+
if (runAttributeHooks && env?._prologAttributes != null && env.hasPrologAttributes(a.name)) {
|
|
698
699
|
if (!env.preparePrologAttributeUnification(a, b)) return false;
|
|
699
700
|
a = deref(a, env);
|
|
700
701
|
b = deref(b, env);
|
|
@@ -712,7 +713,7 @@ export function unify(left, right, env, options = {}) {
|
|
|
712
713
|
occursCheckHandler?.(b, a, env);
|
|
713
714
|
return false;
|
|
714
715
|
}
|
|
715
|
-
if (env?._prologAttributes != null && env.hasPrologAttributes(b.name)) {
|
|
716
|
+
if (runAttributeHooks && env?._prologAttributes != null && env.hasPrologAttributes(b.name)) {
|
|
716
717
|
if (!env.preparePrologAttributeUnification(b, a)) return false;
|
|
717
718
|
b = deref(b, env);
|
|
718
719
|
a = deref(a, env);
|
package/test/run-regression.mjs
CHANGED
|
@@ -127,6 +127,17 @@ function regressionCases() {
|
|
|
127
127
|
'scalar-fact fast matching still validates annotated variables');
|
|
128
128
|
assertEqual(run('p(b).', { goal: 'dif(X,a),p(X)' }).stats.completed_goal_lists, 1,
|
|
129
129
|
'scalar-fact fast matching can discharge an annotated disequality');
|
|
130
|
+
|
|
131
|
+
const freezeProgram = Program.parse(':- use_module(library(freeze), [freeze/2]).');
|
|
132
|
+
const freezeSolver = new Solver(freezeProgram);
|
|
133
|
+
const freezeDifGoal = parseGoalText('freeze(X,a),dif(X,c)');
|
|
134
|
+
const freezeDifAnswer = freezeSolver.solve([freezeDifGoal], new Env(), 0).next();
|
|
135
|
+
assertEqual(freezeDifAnswer.done, false, 'freeze/2 and dif/2 answer');
|
|
136
|
+
const freezeResiduals = freezeSolver.attributeResidualGoals(
|
|
137
|
+
freezeDifGoal.args[0].args[0], freezeDifAnswer.value,
|
|
138
|
+
);
|
|
139
|
+
assertEqual(freezeResiduals.length, 1,
|
|
140
|
+
'freeze/2 residual projection terminates alongside dif/2 (issue #73)');
|
|
130
141
|
},
|
|
131
142
|
},
|
|
132
143
|
{
|
|
@@ -190,6 +201,7 @@ check :- call_residue_vars(X in 1..3, Vs), Vs = [X].
|
|
|
190
201
|
|
|
191
202
|
const residualFile = path.join(tmp, `atts-residual-${++tmpCounter}.pl`);
|
|
192
203
|
fs.writeFileSync(residualFile, `:- use_module(library(atts)).
|
|
204
|
+
:- use_module(library(freeze), [freeze/2]).
|
|
193
205
|
:- attribute required/1.
|
|
194
206
|
attach(X,V) :- put_atts(X, required(V)).
|
|
195
207
|
verify_attributes(Var, Other, Goals) :-
|
|
@@ -199,9 +211,13 @@ verify_attributes(Var, Other, Goals) :-
|
|
|
199
211
|
same(X,X).
|
|
200
212
|
attribute_goals(X) --> { get_atts(X, required(V)) }, [required(X,V)].
|
|
201
213
|
`);
|
|
202
|
-
const repl = runCli([], {
|
|
214
|
+
const repl = runCli([], {
|
|
215
|
+
input: `[${sourceAtom(residualFile)}].\nattach(X,a).\nfreeze(Y,a),dif(Y,c).\nhalt.\n`,
|
|
216
|
+
});
|
|
203
217
|
assertEqual(repl.status, 0, 'attribute residual REPL status');
|
|
204
218
|
assertIncludes(repl.stdout, 'required(X, a).', 'attribute_goals//1 residual projection');
|
|
219
|
+
assertIncludes(repl.stdout, 'dif(Y, c), freeze:freeze(Y, a).',
|
|
220
|
+
'freeze/2 and dif/2 residuals share the query variable (issue #73)');
|
|
205
221
|
|
|
206
222
|
const scryerShape = Program.parse(`:- module(scryer_shape, [op(700, xfx, #=), probe/0]).
|
|
207
223
|
:- op(700, xfx, #=).
|