eyeprolog 1.6.26 → 1.6.27

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/explain.js +12 -1
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.6.26",
6
+ "version": "1.6.27",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/explain.js CHANGED
@@ -647,7 +647,18 @@ function originalVariableName(name) {
647
647
  function resolveForProof(term, env) {
648
648
  const resolved = deref(term, env);
649
649
  if (resolved.type === VAR) return new Term(VAR, originalVariableName(resolved.name), []);
650
- return new Term(resolved.type, resolved.name, resolved.args.map((arg) => resolveForProof(arg, env)));
650
+ // Runtime terms are immutable. A ground subtree already is a proof snapshot,
651
+ // so copy only the ancestors of arguments changed by variable resolution.
652
+ // Allocate the argument array on the first changed child, keeping ground
653
+ // lists allocation-free even when many proof steps share their suffixes.
654
+ let args = null;
655
+ for (let index = 0; index < resolved.args.length; index++) {
656
+ const child = resolveForProof(resolved.args[index], env);
657
+ if (args == null && child !== resolved.args[index]) args = resolved.args.slice();
658
+ if (args != null) args[index] = child;
659
+ }
660
+ if (args == null && resolved.module == null) return resolved;
661
+ return new Term(resolved.type, resolved.name, args ?? resolved.args.slice());
651
662
  }
652
663
 
653
664
  function collectClauseSubstitutions(clause, freshHead, freshBody) {