eyeling 1.22.7 → 1.22.9

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.
@@ -511,6 +511,7 @@
511
511
  internLiteral,
512
512
  PrefixEnv,
513
513
  literalParts,
514
+ copyQuotedGraphMetadata,
514
515
  } = require('./prelude');
515
516
 
516
517
  const { decodeN3StringEscapes } = require('./lexer');
@@ -813,8 +814,9 @@
813
814
  for (const tr of triples) __builtinCollectVarsInTriple(tr, out);
814
815
  }
815
816
 
816
- function __existentializeBlankTerm(t, mapping, varGen) {
817
+ function __existentializeBlankTerm(t, mapping, varGen, localBlankLabels) {
817
818
  if (t instanceof Blank) {
819
+ if (localBlankLabels instanceof Set && !localBlankLabels.has(t.label)) return t;
818
820
  let v = mapping[t.label];
819
821
  if (v === undefined) {
820
822
  const n =
@@ -827,34 +829,48 @@
827
829
  if (t instanceof ListTerm) return t;
828
830
  if (t instanceof OpenListTerm) return t;
829
831
  if (t instanceof GraphTerm) {
832
+ const nestedLocalBlankLabels =
833
+ Object.prototype.hasOwnProperty.call(t, '__quotedLocalBlankLabels') &&
834
+ t.__quotedLocalBlankLabels instanceof Set
835
+ ? t.__quotedLocalBlankLabels
836
+ : localBlankLabels;
830
837
  let changed = false;
831
838
  const triples = t.triples.map((tr) => {
832
- const s2 = __existentializeBlankTerm(tr.s, mapping, varGen);
833
- const p2 = __existentializeBlankTerm(tr.p, mapping, varGen);
834
- const o2 = __existentializeBlankTerm(tr.o, mapping, varGen);
839
+ const s2 = __existentializeBlankTerm(tr.s, mapping, varGen, nestedLocalBlankLabels);
840
+ const p2 = __existentializeBlankTerm(tr.p, mapping, varGen, nestedLocalBlankLabels);
841
+ const o2 = __existentializeBlankTerm(tr.o, mapping, varGen, nestedLocalBlankLabels);
835
842
  if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
836
843
  return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
837
844
  });
838
- return changed ? new GraphTerm(triples) : t;
845
+ return changed ? copyQuotedGraphMetadata(t, new GraphTerm(triples)) : t;
839
846
  }
840
847
  return t;
841
848
  }
842
849
 
843
- function __existentializeBlankTriples(triples, varGen) {
850
+ function __existentializeBlankTriples(rawGraphOrTriples, varGen) {
851
+ const triples =
852
+ rawGraphOrTriples instanceof GraphTerm ? Array.from(rawGraphOrTriples.triples) : Array.from(rawGraphOrTriples);
853
+ const localBlankLabels =
854
+ rawGraphOrTriples instanceof GraphTerm &&
855
+ Object.prototype.hasOwnProperty.call(rawGraphOrTriples, '__quotedLocalBlankLabels') &&
856
+ rawGraphOrTriples.__quotedLocalBlankLabels instanceof Set
857
+ ? rawGraphOrTriples.__quotedLocalBlankLabels
858
+ : null;
859
+
844
860
  const mapping = Object.create(null);
845
861
  let changed = false;
846
862
  const out = triples.map((tr) => {
847
- const s2 = __existentializeBlankTerm(tr.s, mapping, varGen);
848
- const p2 = __existentializeBlankTerm(tr.p, mapping, varGen);
849
- const o2 = __existentializeBlankTerm(tr.o, mapping, varGen);
863
+ const s2 = __existentializeBlankTerm(tr.s, mapping, varGen, localBlankLabels);
864
+ const p2 = __existentializeBlankTerm(tr.p, mapping, varGen, localBlankLabels);
865
+ const o2 = __existentializeBlankTerm(tr.o, mapping, varGen, localBlankLabels);
850
866
  if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
851
867
  return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
852
868
  });
853
869
  return changed ? out : triples;
854
870
  }
855
871
 
856
- function __prepareQuotedPatternTriples(rawTriples, subst, varGen) {
857
- const ex = __existentializeBlankTriples(Array.from(rawTriples), varGen);
872
+ function __prepareQuotedPatternTriples(rawGraphOrTriples, subst, varGen) {
873
+ const ex = __existentializeBlankTriples(rawGraphOrTriples, varGen);
858
874
  let changed = false;
859
875
  const out = ex.map((tr) => {
860
876
  const tr2 = applySubstTriple(tr, subst);
@@ -4126,7 +4142,7 @@
4126
4142
  const keepVars = new Set();
4127
4143
  if (g.s instanceof GraphTerm) __builtinCollectVarsInTriples(g.s.triples, keepVars);
4128
4144
 
4129
- const goalTriples = __prepareQuotedPatternTriples(goal.o.triples, subst, varGen);
4145
+ const goalTriples = __prepareQuotedPatternTriples(goal.o, subst, varGen);
4130
4146
  const goalVariants = __expandScopedVarPredicateGoals(goalTriples);
4131
4147
  const out = [];
4132
4148
  for (const variant of goalVariants) {
@@ -4209,7 +4225,7 @@
4209
4225
 
4210
4226
  const visited2 = [];
4211
4227
  const sols = proveGoals(
4212
- __prepareQuotedPatternTriples(goal.o.triples, subst, varGen),
4228
+ __prepareQuotedPatternTriples(goal.o, subst, varGen),
4213
4229
  { ...subst },
4214
4230
  scopeFacts,
4215
4231
  scopeBackRules,
@@ -4306,8 +4322,8 @@
4306
4322
  const rawClauseTerm = goal.s instanceof ListTerm ? goal.s.elems[1] : clauseTerm;
4307
4323
  const clauseGoals =
4308
4324
  rawClauseTerm instanceof GraphTerm
4309
- ? __prepareQuotedPatternTriples(rawClauseTerm.triples, subst, varGen)
4310
- : __prepareQuotedPatternTriples(clauseTerm.triples, subst, varGen);
4325
+ ? __prepareQuotedPatternTriples(rawClauseTerm, subst, varGen)
4326
+ : __prepareQuotedPatternTriples(clauseTerm, subst, varGen);
4311
4327
  const sols = proveGoals(clauseGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited2, varGen);
4312
4328
 
4313
4329
  const collected = sols.map((sBody) => applySubstTerm(valueTempl, sBody));
@@ -4365,12 +4381,12 @@
4365
4381
  const rawThenClause = goal.s instanceof ListTerm ? goal.s.elems[1] : thenClause;
4366
4382
  const whereGoals =
4367
4383
  rawWhereClause instanceof GraphTerm
4368
- ? __prepareQuotedPatternTriples(rawWhereClause.triples, subst, varGen)
4369
- : __prepareQuotedPatternTriples(whereClause.triples, subst, varGen);
4384
+ ? __prepareQuotedPatternTriples(rawWhereClause, subst, varGen)
4385
+ : __prepareQuotedPatternTriples(whereClause, subst, varGen);
4370
4386
  const thenGoals =
4371
4387
  rawThenClause instanceof GraphTerm
4372
- ? __prepareQuotedPatternTriples(rawThenClause.triples, subst, varGen)
4373
- : __prepareQuotedPatternTriples(thenClause.triples, subst, varGen);
4388
+ ? __prepareQuotedPatternTriples(rawThenClause, subst, varGen)
4389
+ : __prepareQuotedPatternTriples(thenClause, subst, varGen);
4374
4390
 
4375
4391
  const visited1 = [];
4376
4392
  const sols1 = proveGoals(whereGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited1, varGen);
@@ -4777,7 +4793,7 @@
4777
4793
  if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
4778
4794
  return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
4779
4795
  });
4780
- return changed ? new GraphTerm(triples2) : t;
4796
+ return changed ? copyQuotedGraphMetadata(t, new GraphTerm(triples2)) : t;
4781
4797
  }
4782
4798
  return t;
4783
4799
  }
@@ -4835,7 +4851,7 @@
4835
4851
  if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
4836
4852
  return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
4837
4853
  });
4838
- return changed ? new GraphTerm(triples2) : t;
4854
+ return changed ? copyQuotedGraphMetadata(t, new GraphTerm(triples2)) : t;
4839
4855
  }
4840
4856
  return t;
4841
4857
  }
@@ -5839,6 +5855,7 @@
5839
5855
  DerivedFact,
5840
5856
  internIri,
5841
5857
  collectBlankLabelsInTriples,
5858
+ copyQuotedGraphMetadata,
5842
5859
  } = require('./prelude');
5843
5860
 
5844
5861
  // In N3/Turtle, rdf:nil is the canonical IRI for the empty RDF list.
@@ -6660,14 +6677,44 @@
6660
6677
  }
6661
6678
  }
6662
6679
 
6663
- function collectProtectedNamesFromSubst(subst) {
6680
+ function collectProtectedNamesFromTermViaSubst(term, subst, protectedVars, protectedBlanks, seenVarNames) {
6681
+ if (term instanceof Var) {
6682
+ if (!subst || !Object.prototype.hasOwnProperty.call(subst, term.name)) return;
6683
+ if (seenVarNames.has(term.name)) return;
6684
+ seenVarNames.add(term.name);
6685
+ collectProtectedNamesInTerm(subst[term.name], protectedVars, protectedBlanks);
6686
+ return;
6687
+ }
6688
+
6689
+ if (term instanceof ListTerm) {
6690
+ for (const e of term.elems)
6691
+ collectProtectedNamesFromTermViaSubst(e, subst, protectedVars, protectedBlanks, seenVarNames);
6692
+ return;
6693
+ }
6694
+
6695
+ if (term instanceof OpenListTerm) {
6696
+ for (const e of term.prefix)
6697
+ collectProtectedNamesFromTermViaSubst(e, subst, protectedVars, protectedBlanks, seenVarNames);
6698
+ if (subst && Object.prototype.hasOwnProperty.call(subst, term.tailVar) && !seenVarNames.has(term.tailVar)) {
6699
+ seenVarNames.add(term.tailVar);
6700
+ collectProtectedNamesInTerm(subst[term.tailVar], protectedVars, protectedBlanks);
6701
+ }
6702
+ return;
6703
+ }
6704
+
6705
+ if (term instanceof GraphTerm) {
6706
+ for (const tr of term.triples) {
6707
+ collectProtectedNamesFromTermViaSubst(tr.s, subst, protectedVars, protectedBlanks, seenVarNames);
6708
+ collectProtectedNamesFromTermViaSubst(tr.p, subst, protectedVars, protectedBlanks, seenVarNames);
6709
+ collectProtectedNamesFromTermViaSubst(tr.o, subst, protectedVars, protectedBlanks, seenVarNames);
6710
+ }
6711
+ }
6712
+ }
6713
+
6714
+ function collectProtectedNamesForTerm(term, subst) {
6664
6715
  const protectedVars = new Set();
6665
6716
  const protectedBlanks = new Set();
6666
- if (!subst) return { protectedVars, protectedBlanks };
6667
- for (const k in subst) {
6668
- if (!Object.prototype.hasOwnProperty.call(subst, k)) continue;
6669
- collectProtectedNamesInTerm(subst[k], protectedVars, protectedBlanks);
6670
- }
6717
+ collectProtectedNamesFromTermViaSubst(term, subst, protectedVars, protectedBlanks, new Set());
6671
6718
  return { protectedVars, protectedBlanks };
6672
6719
  }
6673
6720
 
@@ -7547,7 +7594,7 @@
7547
7594
  out.push(v);
7548
7595
  }
7549
7596
  }
7550
- return out ? new GraphTerm(out) : t;
7597
+ return out ? copyQuotedGraphMetadata(t, new GraphTerm(out)) : t;
7551
7598
  }
7552
7599
 
7553
7600
  return t;
@@ -7629,6 +7676,9 @@
7629
7676
  }
7630
7677
 
7631
7678
  function unifyTermWithOptions(a, b, subst, opts) {
7679
+ const aRaw = a;
7680
+ const bRaw = b;
7681
+
7632
7682
  a = applySubstTerm(a, subst);
7633
7683
  b = applySubstTerm(b, subst);
7634
7684
 
@@ -7736,13 +7786,14 @@
7736
7786
 
7737
7787
  // Graphs
7738
7788
  if (a instanceof GraphTerm && b instanceof GraphTerm) {
7739
- const protectedNames = collectProtectedNamesFromSubst(subst);
7789
+ const protectedNamesA = collectProtectedNamesForTerm(aRaw, subst);
7790
+ const protectedNamesB = collectProtectedNamesForTerm(bRaw, subst);
7740
7791
  if (
7741
7792
  alphaEqGraphTriples(a.triples, b.triples, {
7742
- protectedVarsA: protectedNames.protectedVars,
7743
- protectedVarsB: protectedNames.protectedVars,
7744
- protectedBlanksA: protectedNames.protectedBlanks,
7745
- protectedBlanksB: protectedNames.protectedBlanks,
7793
+ protectedVarsA: protectedNamesA.protectedVars,
7794
+ protectedVarsB: protectedNamesB.protectedVars,
7795
+ protectedBlanksA: protectedNamesA.protectedBlanks,
7796
+ protectedBlanksB: protectedNamesB.protectedBlanks,
7746
7797
  })
7747
7798
  ) {
7748
7799
  return subst;
@@ -8046,6 +8097,9 @@
8046
8097
  }
8047
8098
 
8048
8099
  function unifyTermTrail(a, b) {
8100
+ const aRaw = a;
8101
+ const bRaw = b;
8102
+
8049
8103
  a = applySubstTerm(a, substMut);
8050
8104
  b = applySubstTerm(b, substMut);
8051
8105
 
@@ -8123,24 +8177,25 @@
8123
8177
 
8124
8178
  // Graphs
8125
8179
  if (a instanceof GraphTerm && b instanceof GraphTerm) {
8126
- const protectedNames = collectProtectedNamesFromSubst(substMut);
8180
+ const protectedNamesA = collectProtectedNamesForTerm(aRaw, substMut);
8181
+ const protectedNamesB = collectProtectedNamesForTerm(bRaw, substMut);
8127
8182
  if (
8128
8183
  alphaEqGraphTriples(a.triples, b.triples, {
8129
- protectedVarsA: protectedNames.protectedVars,
8130
- protectedVarsB: protectedNames.protectedVars,
8131
- protectedBlanksA: protectedNames.protectedBlanks,
8132
- protectedBlanksB: protectedNames.protectedBlanks,
8184
+ protectedVarsA: protectedNamesA.protectedVars,
8185
+ protectedVarsB: protectedNamesB.protectedVars,
8186
+ protectedBlanksA: protectedNamesA.protectedBlanks,
8187
+ protectedBlanksB: protectedNamesB.protectedBlanks,
8133
8188
  })
8134
8189
  ) {
8135
8190
  return true;
8136
8191
  }
8137
- // Fallback: reuse allocation-heavy graph unifier rarely hit in typical workloads.
8138
- const delta = unifyGraphTriples(a.triples, b.triples, {});
8139
- if (delta === null) return false;
8192
+ const merged = unifyGraphTriples(a.triples, b.triples, substMut);
8193
+ if (merged === null) return false;
8140
8194
  const mark = trail.length;
8141
- for (const k in delta) {
8142
- if (!Object.prototype.hasOwnProperty.call(delta, k)) continue;
8143
- if (!bindVarTrail(k, delta[k])) {
8195
+ for (const k in merged) {
8196
+ if (!Object.prototype.hasOwnProperty.call(merged, k)) continue;
8197
+ if (Object.prototype.hasOwnProperty.call(substMut, k)) continue;
8198
+ if (!bindVarTrail(k, merged[k])) {
8144
8199
  undoTo(mark);
8145
8200
  return false;
8146
8201
  }
@@ -10488,6 +10543,7 @@ ${triples.map((tr) => ` ${tripleToN3(tr, prefixes)}`).join('\n')}
10488
10543
  isLogImplies,
10489
10544
  isLogImpliedBy,
10490
10545
  isLogQuery,
10546
+ annotateQuotedGraphTerm,
10491
10547
  } = require('./prelude');
10492
10548
 
10493
10549
  const { N3SyntaxError } = require('./lexer');
@@ -11035,7 +11091,7 @@ ${triples.map((tr) => ` ${tripleToN3(tr, prefixes)}`).join('\n')}
11035
11091
  }
11036
11092
  }
11037
11093
  this.next(); // consume '}'
11038
- return new GraphTerm(triples);
11094
+ return annotateQuotedGraphTerm(new GraphTerm(triples));
11039
11095
  }
11040
11096
 
11041
11097
  parseStatementVerb() {
@@ -11706,6 +11762,32 @@ ${triples.map((tr) => ` ${tripleToN3(tr, prefixes)}`).join('\n')}
11706
11762
  return acc;
11707
11763
  }
11708
11764
 
11765
+ function annotateQuotedGraphTerm(graph) {
11766
+ if (!(graph instanceof GraphTerm)) return graph;
11767
+ const labels = collectBlankLabelsInTriples(graph.triples);
11768
+ Object.defineProperty(graph, '__quotedLocalBlankLabels', {
11769
+ value: labels,
11770
+ enumerable: false,
11771
+ writable: true,
11772
+ configurable: true,
11773
+ });
11774
+ return graph;
11775
+ }
11776
+
11777
+ function copyQuotedGraphMetadata(src, dst) {
11778
+ if (!(src instanceof GraphTerm) || !(dst instanceof GraphTerm)) return dst;
11779
+ if (!Object.prototype.hasOwnProperty.call(src, '__quotedLocalBlankLabels')) return dst;
11780
+
11781
+ const labels = src.__quotedLocalBlankLabels;
11782
+ Object.defineProperty(dst, '__quotedLocalBlankLabels', {
11783
+ value: labels instanceof Set ? new Set(labels) : labels,
11784
+ enumerable: false,
11785
+ writable: true,
11786
+ configurable: true,
11787
+ });
11788
+ return dst;
11789
+ }
11790
+
11709
11791
  module.exports = {
11710
11792
  RDF_NS,
11711
11793
  RDFS_NS,
@@ -11745,6 +11827,8 @@ ${triples.map((tr) => ` ${tripleToN3(tr, prefixes)}`).join('\n')}
11745
11827
  collectIrisInTerm,
11746
11828
  varsInRule,
11747
11829
  collectBlankLabelsInTriples,
11830
+ annotateQuotedGraphTerm,
11831
+ copyQuotedGraphMetadata,
11748
11832
  };
11749
11833
  };
11750
11834
  __modules['lib/printing.js'] = function (require, module, exports) {
@@ -12922,7 +13006,7 @@ ${triples.map((tr) => ` ${tripleToN3(tr, prefixes)}`).join('\n')}
12922
13006
 
12923
13007
  'use strict';
12924
13008
 
12925
- const { Var, Blank, ListTerm, OpenListTerm, GraphTerm, Triple } = require('./prelude');
13009
+ const { Var, Blank, ListTerm, OpenListTerm, GraphTerm, Triple, copyQuotedGraphMetadata } = require('./prelude');
12926
13010
 
12927
13011
  function liftBlankRuleVars(premise, conclusion) {
12928
13012
  // Map blank labels to stable rule-local variable names.
@@ -12950,7 +13034,7 @@ ${triples.map((tr) => ` ${tripleToN3(tr, prefixes)}`).join('\n')}
12950
13034
  const triples = t.triples.map(
12951
13035
  (tr) => new Triple(copyQuotedTerm(tr.s), copyQuotedTerm(tr.p), copyQuotedTerm(tr.o)),
12952
13036
  );
12953
- return new GraphTerm(triples);
13037
+ return copyQuotedGraphMetadata(t, new GraphTerm(triples));
12954
13038
  }
12955
13039
  return t;
12956
13040
  }
package/eyeling.js CHANGED
@@ -507,6 +507,7 @@ const {
507
507
  internLiteral,
508
508
  PrefixEnv,
509
509
  literalParts,
510
+ copyQuotedGraphMetadata,
510
511
  } = require('./prelude');
511
512
 
512
513
  const { decodeN3StringEscapes } = require('./lexer');
@@ -809,8 +810,9 @@ function __builtinCollectVarsInTriples(triples, out) {
809
810
  for (const tr of triples) __builtinCollectVarsInTriple(tr, out);
810
811
  }
811
812
 
812
- function __existentializeBlankTerm(t, mapping, varGen) {
813
+ function __existentializeBlankTerm(t, mapping, varGen, localBlankLabels) {
813
814
  if (t instanceof Blank) {
815
+ if (localBlankLabels instanceof Set && !localBlankLabels.has(t.label)) return t;
814
816
  let v = mapping[t.label];
815
817
  if (v === undefined) {
816
818
  const n = Array.isArray(varGen) && typeof varGen[0] === 'number' ? varGen[0]++ : Object.keys(mapping).length + 1;
@@ -822,34 +824,46 @@ function __existentializeBlankTerm(t, mapping, varGen) {
822
824
  if (t instanceof ListTerm) return t;
823
825
  if (t instanceof OpenListTerm) return t;
824
826
  if (t instanceof GraphTerm) {
827
+ const nestedLocalBlankLabels =
828
+ Object.prototype.hasOwnProperty.call(t, '__quotedLocalBlankLabels') && t.__quotedLocalBlankLabels instanceof Set
829
+ ? t.__quotedLocalBlankLabels
830
+ : localBlankLabels;
825
831
  let changed = false;
826
832
  const triples = t.triples.map((tr) => {
827
- const s2 = __existentializeBlankTerm(tr.s, mapping, varGen);
828
- const p2 = __existentializeBlankTerm(tr.p, mapping, varGen);
829
- const o2 = __existentializeBlankTerm(tr.o, mapping, varGen);
833
+ const s2 = __existentializeBlankTerm(tr.s, mapping, varGen, nestedLocalBlankLabels);
834
+ const p2 = __existentializeBlankTerm(tr.p, mapping, varGen, nestedLocalBlankLabels);
835
+ const o2 = __existentializeBlankTerm(tr.o, mapping, varGen, nestedLocalBlankLabels);
830
836
  if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
831
837
  return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
832
838
  });
833
- return changed ? new GraphTerm(triples) : t;
839
+ return changed ? copyQuotedGraphMetadata(t, new GraphTerm(triples)) : t;
834
840
  }
835
841
  return t;
836
842
  }
837
843
 
838
- function __existentializeBlankTriples(triples, varGen) {
844
+ function __existentializeBlankTriples(rawGraphOrTriples, varGen) {
845
+ const triples = rawGraphOrTriples instanceof GraphTerm ? Array.from(rawGraphOrTriples.triples) : Array.from(rawGraphOrTriples);
846
+ const localBlankLabels =
847
+ rawGraphOrTriples instanceof GraphTerm &&
848
+ Object.prototype.hasOwnProperty.call(rawGraphOrTriples, '__quotedLocalBlankLabels') &&
849
+ rawGraphOrTriples.__quotedLocalBlankLabels instanceof Set
850
+ ? rawGraphOrTriples.__quotedLocalBlankLabels
851
+ : null;
852
+
839
853
  const mapping = Object.create(null);
840
854
  let changed = false;
841
855
  const out = triples.map((tr) => {
842
- const s2 = __existentializeBlankTerm(tr.s, mapping, varGen);
843
- const p2 = __existentializeBlankTerm(tr.p, mapping, varGen);
844
- const o2 = __existentializeBlankTerm(tr.o, mapping, varGen);
856
+ const s2 = __existentializeBlankTerm(tr.s, mapping, varGen, localBlankLabels);
857
+ const p2 = __existentializeBlankTerm(tr.p, mapping, varGen, localBlankLabels);
858
+ const o2 = __existentializeBlankTerm(tr.o, mapping, varGen, localBlankLabels);
845
859
  if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
846
860
  return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
847
861
  });
848
862
  return changed ? out : triples;
849
863
  }
850
864
 
851
- function __prepareQuotedPatternTriples(rawTriples, subst, varGen) {
852
- const ex = __existentializeBlankTriples(Array.from(rawTriples), varGen);
865
+ function __prepareQuotedPatternTriples(rawGraphOrTriples, subst, varGen) {
866
+ const ex = __existentializeBlankTriples(rawGraphOrTriples, varGen);
853
867
  let changed = false;
854
868
  const out = ex.map((tr) => {
855
869
  const tr2 = applySubstTriple(tr, subst);
@@ -4112,7 +4126,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4112
4126
  const keepVars = new Set();
4113
4127
  if (g.s instanceof GraphTerm) __builtinCollectVarsInTriples(g.s.triples, keepVars);
4114
4128
 
4115
- const goalTriples = __prepareQuotedPatternTriples(goal.o.triples, subst, varGen);
4129
+ const goalTriples = __prepareQuotedPatternTriples(goal.o, subst, varGen);
4116
4130
  const goalVariants = __expandScopedVarPredicateGoals(goalTriples);
4117
4131
  const out = [];
4118
4132
  for (const variant of goalVariants) {
@@ -4195,7 +4209,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4195
4209
 
4196
4210
  const visited2 = [];
4197
4211
  const sols = proveGoals(
4198
- __prepareQuotedPatternTriples(goal.o.triples, subst, varGen),
4212
+ __prepareQuotedPatternTriples(goal.o, subst, varGen),
4199
4213
  { ...subst },
4200
4214
  scopeFacts,
4201
4215
  scopeBackRules,
@@ -4292,8 +4306,8 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4292
4306
  const rawClauseTerm = goal.s instanceof ListTerm ? goal.s.elems[1] : clauseTerm;
4293
4307
  const clauseGoals =
4294
4308
  rawClauseTerm instanceof GraphTerm
4295
- ? __prepareQuotedPatternTriples(rawClauseTerm.triples, subst, varGen)
4296
- : __prepareQuotedPatternTriples(clauseTerm.triples, subst, varGen);
4309
+ ? __prepareQuotedPatternTriples(rawClauseTerm, subst, varGen)
4310
+ : __prepareQuotedPatternTriples(clauseTerm, subst, varGen);
4297
4311
  const sols = proveGoals(clauseGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited2, varGen);
4298
4312
 
4299
4313
  const collected = sols.map((sBody) => applySubstTerm(valueTempl, sBody));
@@ -4351,12 +4365,12 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4351
4365
  const rawThenClause = goal.s instanceof ListTerm ? goal.s.elems[1] : thenClause;
4352
4366
  const whereGoals =
4353
4367
  rawWhereClause instanceof GraphTerm
4354
- ? __prepareQuotedPatternTriples(rawWhereClause.triples, subst, varGen)
4355
- : __prepareQuotedPatternTriples(whereClause.triples, subst, varGen);
4368
+ ? __prepareQuotedPatternTriples(rawWhereClause, subst, varGen)
4369
+ : __prepareQuotedPatternTriples(whereClause, subst, varGen);
4356
4370
  const thenGoals =
4357
4371
  rawThenClause instanceof GraphTerm
4358
- ? __prepareQuotedPatternTriples(rawThenClause.triples, subst, varGen)
4359
- : __prepareQuotedPatternTriples(thenClause.triples, subst, varGen);
4372
+ ? __prepareQuotedPatternTriples(rawThenClause, subst, varGen)
4373
+ : __prepareQuotedPatternTriples(thenClause, subst, varGen);
4360
4374
 
4361
4375
  const visited1 = [];
4362
4376
  const sols1 = proveGoals(whereGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited1, varGen);
@@ -4763,7 +4777,7 @@ function standardizeTermApart(term, gen) {
4763
4777
  if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
4764
4778
  return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
4765
4779
  });
4766
- return changed ? new GraphTerm(triples2) : t;
4780
+ return changed ? copyQuotedGraphMetadata(t, new GraphTerm(triples2)) : t;
4767
4781
  }
4768
4782
  return t;
4769
4783
  }
@@ -4821,7 +4835,7 @@ function standardizeRule(rule, gen) {
4821
4835
  if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
4822
4836
  return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
4823
4837
  });
4824
- return changed ? new GraphTerm(triples2) : t;
4838
+ return changed ? copyQuotedGraphMetadata(t, new GraphTerm(triples2)) : t;
4825
4839
  }
4826
4840
  return t;
4827
4841
  }
@@ -5822,6 +5836,7 @@ const {
5822
5836
  DerivedFact,
5823
5837
  internIri,
5824
5838
  collectBlankLabelsInTriples,
5839
+ copyQuotedGraphMetadata,
5825
5840
  } = require('./prelude');
5826
5841
 
5827
5842
  // In N3/Turtle, rdf:nil is the canonical IRI for the empty RDF list.
@@ -6641,14 +6656,44 @@ function collectProtectedNamesInTerm(t, protectedVars, protectedBlanks) {
6641
6656
  }
6642
6657
  }
6643
6658
 
6644
- function collectProtectedNamesFromSubst(subst) {
6659
+ function collectProtectedNamesFromTermViaSubst(term, subst, protectedVars, protectedBlanks, seenVarNames) {
6660
+ if (term instanceof Var) {
6661
+ if (!subst || !Object.prototype.hasOwnProperty.call(subst, term.name)) return;
6662
+ if (seenVarNames.has(term.name)) return;
6663
+ seenVarNames.add(term.name);
6664
+ collectProtectedNamesInTerm(subst[term.name], protectedVars, protectedBlanks);
6665
+ return;
6666
+ }
6667
+
6668
+ if (term instanceof ListTerm) {
6669
+ for (const e of term.elems)
6670
+ collectProtectedNamesFromTermViaSubst(e, subst, protectedVars, protectedBlanks, seenVarNames);
6671
+ return;
6672
+ }
6673
+
6674
+ if (term instanceof OpenListTerm) {
6675
+ for (const e of term.prefix)
6676
+ collectProtectedNamesFromTermViaSubst(e, subst, protectedVars, protectedBlanks, seenVarNames);
6677
+ if (subst && Object.prototype.hasOwnProperty.call(subst, term.tailVar) && !seenVarNames.has(term.tailVar)) {
6678
+ seenVarNames.add(term.tailVar);
6679
+ collectProtectedNamesInTerm(subst[term.tailVar], protectedVars, protectedBlanks);
6680
+ }
6681
+ return;
6682
+ }
6683
+
6684
+ if (term instanceof GraphTerm) {
6685
+ for (const tr of term.triples) {
6686
+ collectProtectedNamesFromTermViaSubst(tr.s, subst, protectedVars, protectedBlanks, seenVarNames);
6687
+ collectProtectedNamesFromTermViaSubst(tr.p, subst, protectedVars, protectedBlanks, seenVarNames);
6688
+ collectProtectedNamesFromTermViaSubst(tr.o, subst, protectedVars, protectedBlanks, seenVarNames);
6689
+ }
6690
+ }
6691
+ }
6692
+
6693
+ function collectProtectedNamesForTerm(term, subst) {
6645
6694
  const protectedVars = new Set();
6646
6695
  const protectedBlanks = new Set();
6647
- if (!subst) return { protectedVars, protectedBlanks };
6648
- for (const k in subst) {
6649
- if (!Object.prototype.hasOwnProperty.call(subst, k)) continue;
6650
- collectProtectedNamesInTerm(subst[k], protectedVars, protectedBlanks);
6651
- }
6696
+ collectProtectedNamesFromTermViaSubst(term, subst, protectedVars, protectedBlanks, new Set());
6652
6697
  return { protectedVars, protectedBlanks };
6653
6698
  }
6654
6699
 
@@ -7528,7 +7573,7 @@ function applySubstTerm(t, s) {
7528
7573
  out.push(v);
7529
7574
  }
7530
7575
  }
7531
- return out ? new GraphTerm(out) : t;
7576
+ return out ? copyQuotedGraphMetadata(t, new GraphTerm(out)) : t;
7532
7577
  }
7533
7578
 
7534
7579
  return t;
@@ -7610,6 +7655,9 @@ function unifyTermListAppend(a, b, subst) {
7610
7655
  }
7611
7656
 
7612
7657
  function unifyTermWithOptions(a, b, subst, opts) {
7658
+ const aRaw = a;
7659
+ const bRaw = b;
7660
+
7613
7661
  a = applySubstTerm(a, subst);
7614
7662
  b = applySubstTerm(b, subst);
7615
7663
 
@@ -7717,13 +7765,14 @@ function unifyTermWithOptions(a, b, subst, opts) {
7717
7765
 
7718
7766
  // Graphs
7719
7767
  if (a instanceof GraphTerm && b instanceof GraphTerm) {
7720
- const protectedNames = collectProtectedNamesFromSubst(subst);
7768
+ const protectedNamesA = collectProtectedNamesForTerm(aRaw, subst);
7769
+ const protectedNamesB = collectProtectedNamesForTerm(bRaw, subst);
7721
7770
  if (
7722
7771
  alphaEqGraphTriples(a.triples, b.triples, {
7723
- protectedVarsA: protectedNames.protectedVars,
7724
- protectedVarsB: protectedNames.protectedVars,
7725
- protectedBlanksA: protectedNames.protectedBlanks,
7726
- protectedBlanksB: protectedNames.protectedBlanks,
7772
+ protectedVarsA: protectedNamesA.protectedVars,
7773
+ protectedVarsB: protectedNamesB.protectedVars,
7774
+ protectedBlanksA: protectedNamesA.protectedBlanks,
7775
+ protectedBlanksB: protectedNamesB.protectedBlanks,
7727
7776
  })
7728
7777
  ) {
7729
7778
  return subst;
@@ -8027,6 +8076,9 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
8027
8076
  }
8028
8077
 
8029
8078
  function unifyTermTrail(a, b) {
8079
+ const aRaw = a;
8080
+ const bRaw = b;
8081
+
8030
8082
  a = applySubstTerm(a, substMut);
8031
8083
  b = applySubstTerm(b, substMut);
8032
8084
 
@@ -8104,24 +8156,25 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
8104
8156
 
8105
8157
  // Graphs
8106
8158
  if (a instanceof GraphTerm && b instanceof GraphTerm) {
8107
- const protectedNames = collectProtectedNamesFromSubst(substMut);
8159
+ const protectedNamesA = collectProtectedNamesForTerm(aRaw, substMut);
8160
+ const protectedNamesB = collectProtectedNamesForTerm(bRaw, substMut);
8108
8161
  if (
8109
8162
  alphaEqGraphTriples(a.triples, b.triples, {
8110
- protectedVarsA: protectedNames.protectedVars,
8111
- protectedVarsB: protectedNames.protectedVars,
8112
- protectedBlanksA: protectedNames.protectedBlanks,
8113
- protectedBlanksB: protectedNames.protectedBlanks,
8163
+ protectedVarsA: protectedNamesA.protectedVars,
8164
+ protectedVarsB: protectedNamesB.protectedVars,
8165
+ protectedBlanksA: protectedNamesA.protectedBlanks,
8166
+ protectedBlanksB: protectedNamesB.protectedBlanks,
8114
8167
  })
8115
8168
  ) {
8116
8169
  return true;
8117
8170
  }
8118
- // Fallback: reuse allocation-heavy graph unifier rarely hit in typical workloads.
8119
- const delta = unifyGraphTriples(a.triples, b.triples, {});
8120
- if (delta === null) return false;
8171
+ const merged = unifyGraphTriples(a.triples, b.triples, substMut);
8172
+ if (merged === null) return false;
8121
8173
  const mark = trail.length;
8122
- for (const k in delta) {
8123
- if (!Object.prototype.hasOwnProperty.call(delta, k)) continue;
8124
- if (!bindVarTrail(k, delta[k])) {
8174
+ for (const k in merged) {
8175
+ if (!Object.prototype.hasOwnProperty.call(merged, k)) continue;
8176
+ if (Object.prototype.hasOwnProperty.call(substMut, k)) continue;
8177
+ if (!bindVarTrail(k, merged[k])) {
8125
8178
  undoTo(mark);
8126
8179
  return false;
8127
8180
  }
@@ -10457,6 +10510,7 @@ const {
10457
10510
  isLogImplies,
10458
10511
  isLogImpliedBy,
10459
10512
  isLogQuery,
10513
+ annotateQuotedGraphTerm,
10460
10514
  } = require('./prelude');
10461
10515
 
10462
10516
  const { N3SyntaxError } = require('./lexer');
@@ -11004,7 +11058,7 @@ class Parser {
11004
11058
  }
11005
11059
  }
11006
11060
  this.next(); // consume '}'
11007
- return new GraphTerm(triples);
11061
+ return annotateQuotedGraphTerm(new GraphTerm(triples));
11008
11062
  }
11009
11063
 
11010
11064
  parseStatementVerb() {
@@ -11676,6 +11730,32 @@ function collectBlankLabelsInTriples(triples) {
11676
11730
  return acc;
11677
11731
  }
11678
11732
 
11733
+ function annotateQuotedGraphTerm(graph) {
11734
+ if (!(graph instanceof GraphTerm)) return graph;
11735
+ const labels = collectBlankLabelsInTriples(graph.triples);
11736
+ Object.defineProperty(graph, '__quotedLocalBlankLabels', {
11737
+ value: labels,
11738
+ enumerable: false,
11739
+ writable: true,
11740
+ configurable: true,
11741
+ });
11742
+ return graph;
11743
+ }
11744
+
11745
+ function copyQuotedGraphMetadata(src, dst) {
11746
+ if (!(src instanceof GraphTerm) || !(dst instanceof GraphTerm)) return dst;
11747
+ if (!Object.prototype.hasOwnProperty.call(src, '__quotedLocalBlankLabels')) return dst;
11748
+
11749
+ const labels = src.__quotedLocalBlankLabels;
11750
+ Object.defineProperty(dst, '__quotedLocalBlankLabels', {
11751
+ value: labels instanceof Set ? new Set(labels) : labels,
11752
+ enumerable: false,
11753
+ writable: true,
11754
+ configurable: true,
11755
+ });
11756
+ return dst;
11757
+ }
11758
+
11679
11759
  module.exports = {
11680
11760
  RDF_NS,
11681
11761
  RDFS_NS,
@@ -11715,6 +11795,8 @@ module.exports = {
11715
11795
  collectIrisInTerm,
11716
11796
  varsInRule,
11717
11797
  collectBlankLabelsInTriples,
11798
+ annotateQuotedGraphTerm,
11799
+ copyQuotedGraphMetadata,
11718
11800
  };
11719
11801
 
11720
11802
  };
@@ -12882,7 +12964,7 @@ module.exports = {
12882
12964
 
12883
12965
  'use strict';
12884
12966
 
12885
- const { Var, Blank, ListTerm, OpenListTerm, GraphTerm, Triple } = require('./prelude');
12967
+ const { Var, Blank, ListTerm, OpenListTerm, GraphTerm, Triple, copyQuotedGraphMetadata } = require('./prelude');
12886
12968
 
12887
12969
  function liftBlankRuleVars(premise, conclusion) {
12888
12970
  // Map blank labels to stable rule-local variable names.
@@ -12910,7 +12992,7 @@ function liftBlankRuleVars(premise, conclusion) {
12910
12992
  const triples = t.triples.map(
12911
12993
  (tr) => new Triple(copyQuotedTerm(tr.s), copyQuotedTerm(tr.p), copyQuotedTerm(tr.o)),
12912
12994
  );
12913
- return new GraphTerm(triples);
12995
+ return copyQuotedGraphMetadata(t, new GraphTerm(triples));
12914
12996
  }
12915
12997
  return t;
12916
12998
  }
package/lib/builtins.js CHANGED
@@ -28,6 +28,7 @@ const {
28
28
  internLiteral,
29
29
  PrefixEnv,
30
30
  literalParts,
31
+ copyQuotedGraphMetadata,
31
32
  } = require('./prelude');
32
33
 
33
34
  const { decodeN3StringEscapes } = require('./lexer');
@@ -330,8 +331,9 @@ function __builtinCollectVarsInTriples(triples, out) {
330
331
  for (const tr of triples) __builtinCollectVarsInTriple(tr, out);
331
332
  }
332
333
 
333
- function __existentializeBlankTerm(t, mapping, varGen) {
334
+ function __existentializeBlankTerm(t, mapping, varGen, localBlankLabels) {
334
335
  if (t instanceof Blank) {
336
+ if (localBlankLabels instanceof Set && !localBlankLabels.has(t.label)) return t;
335
337
  let v = mapping[t.label];
336
338
  if (v === undefined) {
337
339
  const n = Array.isArray(varGen) && typeof varGen[0] === 'number' ? varGen[0]++ : Object.keys(mapping).length + 1;
@@ -343,34 +345,47 @@ function __existentializeBlankTerm(t, mapping, varGen) {
343
345
  if (t instanceof ListTerm) return t;
344
346
  if (t instanceof OpenListTerm) return t;
345
347
  if (t instanceof GraphTerm) {
348
+ const nestedLocalBlankLabels =
349
+ Object.prototype.hasOwnProperty.call(t, '__quotedLocalBlankLabels') && t.__quotedLocalBlankLabels instanceof Set
350
+ ? t.__quotedLocalBlankLabels
351
+ : localBlankLabels;
346
352
  let changed = false;
347
353
  const triples = t.triples.map((tr) => {
348
- const s2 = __existentializeBlankTerm(tr.s, mapping, varGen);
349
- const p2 = __existentializeBlankTerm(tr.p, mapping, varGen);
350
- const o2 = __existentializeBlankTerm(tr.o, mapping, varGen);
354
+ const s2 = __existentializeBlankTerm(tr.s, mapping, varGen, nestedLocalBlankLabels);
355
+ const p2 = __existentializeBlankTerm(tr.p, mapping, varGen, nestedLocalBlankLabels);
356
+ const o2 = __existentializeBlankTerm(tr.o, mapping, varGen, nestedLocalBlankLabels);
351
357
  if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
352
358
  return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
353
359
  });
354
- return changed ? new GraphTerm(triples) : t;
360
+ return changed ? copyQuotedGraphMetadata(t, new GraphTerm(triples)) : t;
355
361
  }
356
362
  return t;
357
363
  }
358
364
 
359
- function __existentializeBlankTriples(triples, varGen) {
365
+ function __existentializeBlankTriples(rawGraphOrTriples, varGen) {
366
+ const triples =
367
+ rawGraphOrTriples instanceof GraphTerm ? Array.from(rawGraphOrTriples.triples) : Array.from(rawGraphOrTriples);
368
+ const localBlankLabels =
369
+ rawGraphOrTriples instanceof GraphTerm &&
370
+ Object.prototype.hasOwnProperty.call(rawGraphOrTriples, '__quotedLocalBlankLabels') &&
371
+ rawGraphOrTriples.__quotedLocalBlankLabels instanceof Set
372
+ ? rawGraphOrTriples.__quotedLocalBlankLabels
373
+ : null;
374
+
360
375
  const mapping = Object.create(null);
361
376
  let changed = false;
362
377
  const out = triples.map((tr) => {
363
- const s2 = __existentializeBlankTerm(tr.s, mapping, varGen);
364
- const p2 = __existentializeBlankTerm(tr.p, mapping, varGen);
365
- const o2 = __existentializeBlankTerm(tr.o, mapping, varGen);
378
+ const s2 = __existentializeBlankTerm(tr.s, mapping, varGen, localBlankLabels);
379
+ const p2 = __existentializeBlankTerm(tr.p, mapping, varGen, localBlankLabels);
380
+ const o2 = __existentializeBlankTerm(tr.o, mapping, varGen, localBlankLabels);
366
381
  if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
367
382
  return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
368
383
  });
369
384
  return changed ? out : triples;
370
385
  }
371
386
 
372
- function __prepareQuotedPatternTriples(rawTriples, subst, varGen) {
373
- const ex = __existentializeBlankTriples(Array.from(rawTriples), varGen);
387
+ function __prepareQuotedPatternTriples(rawGraphOrTriples, subst, varGen) {
388
+ const ex = __existentializeBlankTriples(rawGraphOrTriples, varGen);
374
389
  let changed = false;
375
390
  const out = ex.map((tr) => {
376
391
  const tr2 = applySubstTriple(tr, subst);
@@ -3633,7 +3648,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3633
3648
  const keepVars = new Set();
3634
3649
  if (g.s instanceof GraphTerm) __builtinCollectVarsInTriples(g.s.triples, keepVars);
3635
3650
 
3636
- const goalTriples = __prepareQuotedPatternTriples(goal.o.triples, subst, varGen);
3651
+ const goalTriples = __prepareQuotedPatternTriples(goal.o, subst, varGen);
3637
3652
  const goalVariants = __expandScopedVarPredicateGoals(goalTriples);
3638
3653
  const out = [];
3639
3654
  for (const variant of goalVariants) {
@@ -3716,7 +3731,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3716
3731
 
3717
3732
  const visited2 = [];
3718
3733
  const sols = proveGoals(
3719
- __prepareQuotedPatternTriples(goal.o.triples, subst, varGen),
3734
+ __prepareQuotedPatternTriples(goal.o, subst, varGen),
3720
3735
  { ...subst },
3721
3736
  scopeFacts,
3722
3737
  scopeBackRules,
@@ -3813,8 +3828,8 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3813
3828
  const rawClauseTerm = goal.s instanceof ListTerm ? goal.s.elems[1] : clauseTerm;
3814
3829
  const clauseGoals =
3815
3830
  rawClauseTerm instanceof GraphTerm
3816
- ? __prepareQuotedPatternTriples(rawClauseTerm.triples, subst, varGen)
3817
- : __prepareQuotedPatternTriples(clauseTerm.triples, subst, varGen);
3831
+ ? __prepareQuotedPatternTriples(rawClauseTerm, subst, varGen)
3832
+ : __prepareQuotedPatternTriples(clauseTerm, subst, varGen);
3818
3833
  const sols = proveGoals(clauseGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited2, varGen);
3819
3834
 
3820
3835
  const collected = sols.map((sBody) => applySubstTerm(valueTempl, sBody));
@@ -3872,12 +3887,12 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3872
3887
  const rawThenClause = goal.s instanceof ListTerm ? goal.s.elems[1] : thenClause;
3873
3888
  const whereGoals =
3874
3889
  rawWhereClause instanceof GraphTerm
3875
- ? __prepareQuotedPatternTriples(rawWhereClause.triples, subst, varGen)
3876
- : __prepareQuotedPatternTriples(whereClause.triples, subst, varGen);
3890
+ ? __prepareQuotedPatternTriples(rawWhereClause, subst, varGen)
3891
+ : __prepareQuotedPatternTriples(whereClause, subst, varGen);
3877
3892
  const thenGoals =
3878
3893
  rawThenClause instanceof GraphTerm
3879
- ? __prepareQuotedPatternTriples(rawThenClause.triples, subst, varGen)
3880
- : __prepareQuotedPatternTriples(thenClause.triples, subst, varGen);
3894
+ ? __prepareQuotedPatternTriples(rawThenClause, subst, varGen)
3895
+ : __prepareQuotedPatternTriples(thenClause, subst, varGen);
3881
3896
 
3882
3897
  const visited1 = [];
3883
3898
  const sols1 = proveGoals(whereGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited1, varGen);
@@ -4284,7 +4299,7 @@ function standardizeTermApart(term, gen) {
4284
4299
  if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
4285
4300
  return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
4286
4301
  });
4287
- return changed ? new GraphTerm(triples2) : t;
4302
+ return changed ? copyQuotedGraphMetadata(t, new GraphTerm(triples2)) : t;
4288
4303
  }
4289
4304
  return t;
4290
4305
  }
@@ -4342,7 +4357,7 @@ function standardizeRule(rule, gen) {
4342
4357
  if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
4343
4358
  return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
4344
4359
  });
4345
- return changed ? new GraphTerm(triples2) : t;
4360
+ return changed ? copyQuotedGraphMetadata(t, new GraphTerm(triples2)) : t;
4346
4361
  }
4347
4362
  return t;
4348
4363
  }
package/lib/engine.js CHANGED
@@ -27,6 +27,7 @@ const {
27
27
  DerivedFact,
28
28
  internIri,
29
29
  collectBlankLabelsInTriples,
30
+ copyQuotedGraphMetadata,
30
31
  } = require('./prelude');
31
32
 
32
33
  // In N3/Turtle, rdf:nil is the canonical IRI for the empty RDF list.
@@ -846,14 +847,44 @@ function collectProtectedNamesInTerm(t, protectedVars, protectedBlanks) {
846
847
  }
847
848
  }
848
849
 
849
- function collectProtectedNamesFromSubst(subst) {
850
+ function collectProtectedNamesFromTermViaSubst(term, subst, protectedVars, protectedBlanks, seenVarNames) {
851
+ if (term instanceof Var) {
852
+ if (!subst || !Object.prototype.hasOwnProperty.call(subst, term.name)) return;
853
+ if (seenVarNames.has(term.name)) return;
854
+ seenVarNames.add(term.name);
855
+ collectProtectedNamesInTerm(subst[term.name], protectedVars, protectedBlanks);
856
+ return;
857
+ }
858
+
859
+ if (term instanceof ListTerm) {
860
+ for (const e of term.elems)
861
+ collectProtectedNamesFromTermViaSubst(e, subst, protectedVars, protectedBlanks, seenVarNames);
862
+ return;
863
+ }
864
+
865
+ if (term instanceof OpenListTerm) {
866
+ for (const e of term.prefix)
867
+ collectProtectedNamesFromTermViaSubst(e, subst, protectedVars, protectedBlanks, seenVarNames);
868
+ if (subst && Object.prototype.hasOwnProperty.call(subst, term.tailVar) && !seenVarNames.has(term.tailVar)) {
869
+ seenVarNames.add(term.tailVar);
870
+ collectProtectedNamesInTerm(subst[term.tailVar], protectedVars, protectedBlanks);
871
+ }
872
+ return;
873
+ }
874
+
875
+ if (term instanceof GraphTerm) {
876
+ for (const tr of term.triples) {
877
+ collectProtectedNamesFromTermViaSubst(tr.s, subst, protectedVars, protectedBlanks, seenVarNames);
878
+ collectProtectedNamesFromTermViaSubst(tr.p, subst, protectedVars, protectedBlanks, seenVarNames);
879
+ collectProtectedNamesFromTermViaSubst(tr.o, subst, protectedVars, protectedBlanks, seenVarNames);
880
+ }
881
+ }
882
+ }
883
+
884
+ function collectProtectedNamesForTerm(term, subst) {
850
885
  const protectedVars = new Set();
851
886
  const protectedBlanks = new Set();
852
- if (!subst) return { protectedVars, protectedBlanks };
853
- for (const k in subst) {
854
- if (!Object.prototype.hasOwnProperty.call(subst, k)) continue;
855
- collectProtectedNamesInTerm(subst[k], protectedVars, protectedBlanks);
856
- }
887
+ collectProtectedNamesFromTermViaSubst(term, subst, protectedVars, protectedBlanks, new Set());
857
888
  return { protectedVars, protectedBlanks };
858
889
  }
859
890
 
@@ -1733,7 +1764,7 @@ function applySubstTerm(t, s) {
1733
1764
  out.push(v);
1734
1765
  }
1735
1766
  }
1736
- return out ? new GraphTerm(out) : t;
1767
+ return out ? copyQuotedGraphMetadata(t, new GraphTerm(out)) : t;
1737
1768
  }
1738
1769
 
1739
1770
  return t;
@@ -1815,6 +1846,9 @@ function unifyTermListAppend(a, b, subst) {
1815
1846
  }
1816
1847
 
1817
1848
  function unifyTermWithOptions(a, b, subst, opts) {
1849
+ const aRaw = a;
1850
+ const bRaw = b;
1851
+
1818
1852
  a = applySubstTerm(a, subst);
1819
1853
  b = applySubstTerm(b, subst);
1820
1854
 
@@ -1922,13 +1956,14 @@ function unifyTermWithOptions(a, b, subst, opts) {
1922
1956
 
1923
1957
  // Graphs
1924
1958
  if (a instanceof GraphTerm && b instanceof GraphTerm) {
1925
- const protectedNames = collectProtectedNamesFromSubst(subst);
1959
+ const protectedNamesA = collectProtectedNamesForTerm(aRaw, subst);
1960
+ const protectedNamesB = collectProtectedNamesForTerm(bRaw, subst);
1926
1961
  if (
1927
1962
  alphaEqGraphTriples(a.triples, b.triples, {
1928
- protectedVarsA: protectedNames.protectedVars,
1929
- protectedVarsB: protectedNames.protectedVars,
1930
- protectedBlanksA: protectedNames.protectedBlanks,
1931
- protectedBlanksB: protectedNames.protectedBlanks,
1963
+ protectedVarsA: protectedNamesA.protectedVars,
1964
+ protectedVarsB: protectedNamesB.protectedVars,
1965
+ protectedBlanksA: protectedNamesA.protectedBlanks,
1966
+ protectedBlanksB: protectedNamesB.protectedBlanks,
1932
1967
  })
1933
1968
  ) {
1934
1969
  return subst;
@@ -2232,6 +2267,9 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
2232
2267
  }
2233
2268
 
2234
2269
  function unifyTermTrail(a, b) {
2270
+ const aRaw = a;
2271
+ const bRaw = b;
2272
+
2235
2273
  a = applySubstTerm(a, substMut);
2236
2274
  b = applySubstTerm(b, substMut);
2237
2275
 
@@ -2309,24 +2347,25 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
2309
2347
 
2310
2348
  // Graphs
2311
2349
  if (a instanceof GraphTerm && b instanceof GraphTerm) {
2312
- const protectedNames = collectProtectedNamesFromSubst(substMut);
2350
+ const protectedNamesA = collectProtectedNamesForTerm(aRaw, substMut);
2351
+ const protectedNamesB = collectProtectedNamesForTerm(bRaw, substMut);
2313
2352
  if (
2314
2353
  alphaEqGraphTriples(a.triples, b.triples, {
2315
- protectedVarsA: protectedNames.protectedVars,
2316
- protectedVarsB: protectedNames.protectedVars,
2317
- protectedBlanksA: protectedNames.protectedBlanks,
2318
- protectedBlanksB: protectedNames.protectedBlanks,
2354
+ protectedVarsA: protectedNamesA.protectedVars,
2355
+ protectedVarsB: protectedNamesB.protectedVars,
2356
+ protectedBlanksA: protectedNamesA.protectedBlanks,
2357
+ protectedBlanksB: protectedNamesB.protectedBlanks,
2319
2358
  })
2320
2359
  ) {
2321
2360
  return true;
2322
2361
  }
2323
- // Fallback: reuse allocation-heavy graph unifier rarely hit in typical workloads.
2324
- const delta = unifyGraphTriples(a.triples, b.triples, {});
2325
- if (delta === null) return false;
2362
+ const merged = unifyGraphTriples(a.triples, b.triples, substMut);
2363
+ if (merged === null) return false;
2326
2364
  const mark = trail.length;
2327
- for (const k in delta) {
2328
- if (!Object.prototype.hasOwnProperty.call(delta, k)) continue;
2329
- if (!bindVarTrail(k, delta[k])) {
2365
+ for (const k in merged) {
2366
+ if (!Object.prototype.hasOwnProperty.call(merged, k)) continue;
2367
+ if (Object.prototype.hasOwnProperty.call(substMut, k)) continue;
2368
+ if (!bindVarTrail(k, merged[k])) {
2330
2369
  undoTo(mark);
2331
2370
  return false;
2332
2371
  }
package/lib/parser.js CHANGED
@@ -26,6 +26,7 @@ const {
26
26
  isLogImplies,
27
27
  isLogImpliedBy,
28
28
  isLogQuery,
29
+ annotateQuotedGraphTerm,
29
30
  } = require('./prelude');
30
31
 
31
32
  const { N3SyntaxError } = require('./lexer');
@@ -573,7 +574,7 @@ class Parser {
573
574
  }
574
575
  }
575
576
  this.next(); // consume '}'
576
- return new GraphTerm(triples);
577
+ return annotateQuotedGraphTerm(new GraphTerm(triples));
577
578
  }
578
579
 
579
580
  parseStatementVerb() {
package/lib/prelude.js CHANGED
@@ -501,6 +501,32 @@ function collectBlankLabelsInTriples(triples) {
501
501
  return acc;
502
502
  }
503
503
 
504
+ function annotateQuotedGraphTerm(graph) {
505
+ if (!(graph instanceof GraphTerm)) return graph;
506
+ const labels = collectBlankLabelsInTriples(graph.triples);
507
+ Object.defineProperty(graph, '__quotedLocalBlankLabels', {
508
+ value: labels,
509
+ enumerable: false,
510
+ writable: true,
511
+ configurable: true,
512
+ });
513
+ return graph;
514
+ }
515
+
516
+ function copyQuotedGraphMetadata(src, dst) {
517
+ if (!(src instanceof GraphTerm) || !(dst instanceof GraphTerm)) return dst;
518
+ if (!Object.prototype.hasOwnProperty.call(src, '__quotedLocalBlankLabels')) return dst;
519
+
520
+ const labels = src.__quotedLocalBlankLabels;
521
+ Object.defineProperty(dst, '__quotedLocalBlankLabels', {
522
+ value: labels instanceof Set ? new Set(labels) : labels,
523
+ enumerable: false,
524
+ writable: true,
525
+ configurable: true,
526
+ });
527
+ return dst;
528
+ }
529
+
504
530
  module.exports = {
505
531
  RDF_NS,
506
532
  RDFS_NS,
@@ -540,4 +566,6 @@ module.exports = {
540
566
  collectIrisInTerm,
541
567
  varsInRule,
542
568
  collectBlankLabelsInTriples,
569
+ annotateQuotedGraphTerm,
570
+ copyQuotedGraphMetadata,
543
571
  };
package/lib/rules.js CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  'use strict';
9
9
 
10
- const { Var, Blank, ListTerm, OpenListTerm, GraphTerm, Triple } = require('./prelude');
10
+ const { Var, Blank, ListTerm, OpenListTerm, GraphTerm, Triple, copyQuotedGraphMetadata } = require('./prelude');
11
11
 
12
12
  function liftBlankRuleVars(premise, conclusion) {
13
13
  // Map blank labels to stable rule-local variable names.
@@ -35,7 +35,7 @@ function liftBlankRuleVars(premise, conclusion) {
35
35
  const triples = t.triples.map(
36
36
  (tr) => new Triple(copyQuotedTerm(tr.s), copyQuotedTerm(tr.p), copyQuotedTerm(tr.o)),
37
37
  );
38
- return new GraphTerm(triples);
38
+ return copyQuotedGraphMetadata(t, new GraphTerm(triples));
39
39
  }
40
40
  return t;
41
41
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.22.7",
3
+ "version": "1.22.9",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
package/test/api.test.js CHANGED
@@ -2095,6 +2095,29 @@ _:x :hates { _:foo :making :mess }.
2095
2095
  /:result\s+:status\s+:matched\s*\./,
2096
2096
  ],
2097
2097
  },
2098
+
2099
+ {
2100
+ name: '243aa regression: collectAllIn keeps outer blank-node bindings fixed in quoted formulas',
2101
+ opt: { proofComments: false },
2102
+ input: `@prefix log: <http://www.w3.org/2000/10/swap/log#> .
2103
+ @prefix ex: <http://example.org/> .
2104
+
2105
+ ex:a a ex:Person ; ex:name "A" .
2106
+ _:b a ex:Person ; ex:name "B" .
2107
+
2108
+ {
2109
+ ?person a ex:Person .
2110
+ (?x { ?person ex:name ?x } ?xs) log:collectAllIn ?SCOPE .
2111
+ }
2112
+ =>
2113
+ {
2114
+ ?person ex:names ?xs .
2115
+ } .
2116
+ `,
2117
+ expect: [/ex:a\s+ex:names\s+\("A"\)\s*\./, /_:[^\s]+\s+ex:names\s+\("B"\)\s*\./],
2118
+ notExpect: [/_:[^\s]+\s+ex:names\s+\("A"\s+"B"\)\s*\./, /_:[^\s]+\s+ex:names\s+\("B"\s+"A"\)\s*\./],
2119
+ },
2120
+
2098
2121
  {
2099
2122
  name: '243a regression: collectAllIn treats quoted-formula blanks existentially',
2100
2123
  opt: { proofComments: false },
@@ -2305,6 +2328,37 @@ _:x :hates { _:foo :making :mess }.
2305
2328
  }
2306
2329
  },
2307
2330
  },
2331
+ {
2332
+ name: 'regression: unrelated blank bindings must not block alpha-equivalent quoted-formula matches',
2333
+ opt: { proofComments: false },
2334
+ input: `@prefix log: <http://www.w3.org/2000/10/swap/log#> .
2335
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
2336
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
2337
+ @prefix : <http://example.org/ns#> .
2338
+
2339
+ {
2340
+ _:b1 a :Mortal .
2341
+ } :because {
2342
+ :Socrates a :Human .
2343
+ :Human rdfs:subClassOf :Mortal .
2344
+ } .
2345
+
2346
+ <> :step {
2347
+ [ a :Mortal ].
2348
+ }.
2349
+
2350
+ {
2351
+ ?A :step ?B .
2352
+ ?B log:includes { ?S ?P ?O }.
2353
+ { _:b2 a :Mortal } :because ?Y.
2354
+ }
2355
+ =>
2356
+ {
2357
+ :test :is true .
2358
+ }.
2359
+ `,
2360
+ expect: [/^:test\s+:is\s+true\s*\./m],
2361
+ },
2308
2362
  ];
2309
2363
 
2310
2364
  let passed = 0;