eyeling 1.21.9 → 1.21.10

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.
@@ -813,6 +813,57 @@
813
813
  for (const tr of triples) __builtinCollectVarsInTriple(tr, out);
814
814
  }
815
815
 
816
+ function __existentializeBlankTerm(t, mapping, varGen) {
817
+ if (t instanceof Blank) {
818
+ let v = mapping[t.label];
819
+ if (v === undefined) {
820
+ const n =
821
+ Array.isArray(varGen) && typeof varGen[0] === 'number' ? varGen[0]++ : Object.keys(mapping).length + 1;
822
+ v = new Var(`__qb_${n}`);
823
+ mapping[t.label] = v;
824
+ }
825
+ return v;
826
+ }
827
+ if (t instanceof ListTerm) return t;
828
+ if (t instanceof OpenListTerm) return t;
829
+ if (t instanceof GraphTerm) {
830
+ let changed = false;
831
+ 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);
835
+ if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
836
+ return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
837
+ });
838
+ return changed ? new GraphTerm(triples) : t;
839
+ }
840
+ return t;
841
+ }
842
+
843
+ function __existentializeBlankTriples(triples, varGen) {
844
+ const mapping = Object.create(null);
845
+ let changed = false;
846
+ 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);
850
+ if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
851
+ return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
852
+ });
853
+ return changed ? out : triples;
854
+ }
855
+
856
+ function __prepareQuotedPatternTriples(rawTriples, subst, varGen) {
857
+ const ex = __existentializeBlankTriples(Array.from(rawTriples), varGen);
858
+ let changed = false;
859
+ const out = ex.map((tr) => {
860
+ const tr2 = applySubstTriple(tr, subst);
861
+ if (tr2 !== tr) changed = true;
862
+ return tr2;
863
+ });
864
+ return changed ? out : ex;
865
+ }
866
+
816
867
  function literalHasLangTag(lit) {
817
868
  // True iff the literal is a quoted string literal with a language tag suffix,
818
869
  // e.g. "hello"@en or """hello"""@en.
@@ -4014,7 +4065,8 @@
4014
4065
  const keepVars = new Set();
4015
4066
  if (g.s instanceof GraphTerm) __builtinCollectVarsInTriples(g.s.triples, keepVars);
4016
4067
 
4017
- const goalVariants = __expandScopedVarPredicateGoals(Array.from(g.o.triples));
4068
+ const goalTriples = __prepareQuotedPatternTriples(goal.o.triples, subst, varGen);
4069
+ const goalVariants = __expandScopedVarPredicateGoals(goalTriples);
4018
4070
  const out = [];
4019
4071
  for (const variant of goalVariants) {
4020
4072
  const sols = proveGoals(
@@ -4096,7 +4148,7 @@
4096
4148
 
4097
4149
  const visited2 = [];
4098
4150
  const sols = proveGoals(
4099
- Array.from(g.o.triples),
4151
+ __prepareQuotedPatternTriples(goal.o.triples, subst, varGen),
4100
4152
  { ...subst },
4101
4153
  scopeFacts,
4102
4154
  scopeBackRules,
@@ -4190,15 +4242,12 @@
4190
4242
  }
4191
4243
 
4192
4244
  const visited2 = [];
4193
- const sols = proveGoals(
4194
- Array.from(clauseTerm.triples),
4195
- {},
4196
- scopeFacts,
4197
- scopeBackRules,
4198
- depth + 1,
4199
- visited2,
4200
- varGen,
4201
- );
4245
+ const rawClauseTerm = goal.s instanceof ListTerm ? goal.s.elems[1] : clauseTerm;
4246
+ const clauseGoals =
4247
+ rawClauseTerm instanceof GraphTerm
4248
+ ? __prepareQuotedPatternTriples(rawClauseTerm.triples, subst, varGen)
4249
+ : __prepareQuotedPatternTriples(clauseTerm.triples, subst, varGen);
4250
+ const sols = proveGoals(clauseGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited2, varGen);
4202
4251
 
4203
4252
  const collected = sols.map((sBody) => applySubstTerm(valueTempl, sBody));
4204
4253
  const collectedList = new ListTerm(collected);
@@ -4251,28 +4300,23 @@
4251
4300
  scopeFacts = snap;
4252
4301
  }
4253
4302
 
4303
+ const rawWhereClause = goal.s instanceof ListTerm ? goal.s.elems[0] : whereClause;
4304
+ const rawThenClause = goal.s instanceof ListTerm ? goal.s.elems[1] : thenClause;
4305
+ const whereGoals =
4306
+ rawWhereClause instanceof GraphTerm
4307
+ ? __prepareQuotedPatternTriples(rawWhereClause.triples, subst, varGen)
4308
+ : __prepareQuotedPatternTriples(whereClause.triples, subst, varGen);
4309
+ const thenGoals =
4310
+ rawThenClause instanceof GraphTerm
4311
+ ? __prepareQuotedPatternTriples(rawThenClause.triples, subst, varGen)
4312
+ : __prepareQuotedPatternTriples(thenClause.triples, subst, varGen);
4313
+
4254
4314
  const visited1 = [];
4255
- const sols1 = proveGoals(
4256
- Array.from(whereClause.triples),
4257
- {},
4258
- scopeFacts,
4259
- scopeBackRules,
4260
- depth + 1,
4261
- visited1,
4262
- varGen,
4263
- );
4315
+ const sols1 = proveGoals(whereGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited1, varGen);
4264
4316
 
4265
4317
  for (const s1 of sols1) {
4266
4318
  const visited2 = [];
4267
- const sols2 = proveGoals(
4268
- Array.from(thenClause.triples),
4269
- s1,
4270
- scopeFacts,
4271
- scopeBackRules,
4272
- depth + 1,
4273
- visited2,
4274
- varGen,
4275
- );
4319
+ const sols2 = proveGoals(thenGoals, s1, scopeFacts, scopeBackRules, depth + 1, visited2, varGen);
4276
4320
  if (!sols2.length) return [];
4277
4321
  }
4278
4322
  return [outSubst];
@@ -12735,14 +12779,26 @@ ${triples.map((tr) => ` ${tripleToN3(tr, prefixes)}`).join('\n')}
12735
12779
  return new Var(name);
12736
12780
  }
12737
12781
 
12782
+ function copyQuotedTerm(t) {
12783
+ // Quoted formulas are data terms with their own local blank scope.
12784
+ // Copy them structurally so later in-place rewrites cannot mutate shared AST,
12785
+ // but do not lift their blank nodes into rule-body variables.
12786
+ if (t instanceof ListTerm) return new ListTerm(t.elems.map(copyQuotedTerm));
12787
+ if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(copyQuotedTerm), t.tailVar);
12788
+ if (t instanceof GraphTerm) {
12789
+ const triples = t.triples.map(
12790
+ (tr) => new Triple(copyQuotedTerm(tr.s), copyQuotedTerm(tr.p), copyQuotedTerm(tr.o)),
12791
+ );
12792
+ return new GraphTerm(triples);
12793
+ }
12794
+ return t;
12795
+ }
12796
+
12738
12797
  function convertTerm(t) {
12739
12798
  if (t instanceof Blank) return blankToVar(t.label);
12740
12799
  if (t instanceof ListTerm) return new ListTerm(t.elems.map(convertTerm));
12741
12800
  if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(convertTerm), t.tailVar);
12742
- if (t instanceof GraphTerm) {
12743
- const triples = t.triples.map((tr) => new Triple(convertTerm(tr.s), convertTerm(tr.p), convertTerm(tr.o)));
12744
- return new GraphTerm(triples);
12745
- }
12801
+ if (t instanceof GraphTerm) return copyQuotedTerm(t);
12746
12802
  return t;
12747
12803
  }
12748
12804
 
package/eyeling.js CHANGED
@@ -809,6 +809,56 @@ function __builtinCollectVarsInTriples(triples, out) {
809
809
  for (const tr of triples) __builtinCollectVarsInTriple(tr, out);
810
810
  }
811
811
 
812
+ function __existentializeBlankTerm(t, mapping, varGen) {
813
+ if (t instanceof Blank) {
814
+ let v = mapping[t.label];
815
+ if (v === undefined) {
816
+ const n = Array.isArray(varGen) && typeof varGen[0] === 'number' ? varGen[0]++ : Object.keys(mapping).length + 1;
817
+ v = new Var(`__qb_${n}`);
818
+ mapping[t.label] = v;
819
+ }
820
+ return v;
821
+ }
822
+ if (t instanceof ListTerm) return t;
823
+ if (t instanceof OpenListTerm) return t;
824
+ if (t instanceof GraphTerm) {
825
+ let changed = false;
826
+ 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);
830
+ if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
831
+ return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
832
+ });
833
+ return changed ? new GraphTerm(triples) : t;
834
+ }
835
+ return t;
836
+ }
837
+
838
+ function __existentializeBlankTriples(triples, varGen) {
839
+ const mapping = Object.create(null);
840
+ let changed = false;
841
+ 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);
845
+ if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
846
+ return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
847
+ });
848
+ return changed ? out : triples;
849
+ }
850
+
851
+ function __prepareQuotedPatternTriples(rawTriples, subst, varGen) {
852
+ const ex = __existentializeBlankTriples(Array.from(rawTriples), varGen);
853
+ let changed = false;
854
+ const out = ex.map((tr) => {
855
+ const tr2 = applySubstTriple(tr, subst);
856
+ if (tr2 !== tr) changed = true;
857
+ return tr2;
858
+ });
859
+ return changed ? out : ex;
860
+ }
861
+
812
862
  function literalHasLangTag(lit) {
813
863
  // True iff the literal is a quoted string literal with a language tag suffix,
814
864
  // e.g. "hello"@en or """hello"""@en.
@@ -4001,7 +4051,8 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4001
4051
  const keepVars = new Set();
4002
4052
  if (g.s instanceof GraphTerm) __builtinCollectVarsInTriples(g.s.triples, keepVars);
4003
4053
 
4004
- const goalVariants = __expandScopedVarPredicateGoals(Array.from(g.o.triples));
4054
+ const goalTriples = __prepareQuotedPatternTriples(goal.o.triples, subst, varGen);
4055
+ const goalVariants = __expandScopedVarPredicateGoals(goalTriples);
4005
4056
  const out = [];
4006
4057
  for (const variant of goalVariants) {
4007
4058
  const sols = proveGoals(
@@ -4083,7 +4134,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4083
4134
 
4084
4135
  const visited2 = [];
4085
4136
  const sols = proveGoals(
4086
- Array.from(g.o.triples),
4137
+ __prepareQuotedPatternTriples(goal.o.triples, subst, varGen),
4087
4138
  { ...subst },
4088
4139
  scopeFacts,
4089
4140
  scopeBackRules,
@@ -4177,15 +4228,12 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4177
4228
  }
4178
4229
 
4179
4230
  const visited2 = [];
4180
- const sols = proveGoals(
4181
- Array.from(clauseTerm.triples),
4182
- {},
4183
- scopeFacts,
4184
- scopeBackRules,
4185
- depth + 1,
4186
- visited2,
4187
- varGen,
4188
- );
4231
+ const rawClauseTerm = goal.s instanceof ListTerm ? goal.s.elems[1] : clauseTerm;
4232
+ const clauseGoals =
4233
+ rawClauseTerm instanceof GraphTerm
4234
+ ? __prepareQuotedPatternTriples(rawClauseTerm.triples, subst, varGen)
4235
+ : __prepareQuotedPatternTriples(clauseTerm.triples, subst, varGen);
4236
+ const sols = proveGoals(clauseGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited2, varGen);
4189
4237
 
4190
4238
  const collected = sols.map((sBody) => applySubstTerm(valueTempl, sBody));
4191
4239
  const collectedList = new ListTerm(collected);
@@ -4238,28 +4286,23 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4238
4286
  scopeFacts = snap;
4239
4287
  }
4240
4288
 
4289
+ const rawWhereClause = goal.s instanceof ListTerm ? goal.s.elems[0] : whereClause;
4290
+ const rawThenClause = goal.s instanceof ListTerm ? goal.s.elems[1] : thenClause;
4291
+ const whereGoals =
4292
+ rawWhereClause instanceof GraphTerm
4293
+ ? __prepareQuotedPatternTriples(rawWhereClause.triples, subst, varGen)
4294
+ : __prepareQuotedPatternTriples(whereClause.triples, subst, varGen);
4295
+ const thenGoals =
4296
+ rawThenClause instanceof GraphTerm
4297
+ ? __prepareQuotedPatternTriples(rawThenClause.triples, subst, varGen)
4298
+ : __prepareQuotedPatternTriples(thenClause.triples, subst, varGen);
4299
+
4241
4300
  const visited1 = [];
4242
- const sols1 = proveGoals(
4243
- Array.from(whereClause.triples),
4244
- {},
4245
- scopeFacts,
4246
- scopeBackRules,
4247
- depth + 1,
4248
- visited1,
4249
- varGen,
4250
- );
4301
+ const sols1 = proveGoals(whereGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited1, varGen);
4251
4302
 
4252
4303
  for (const s1 of sols1) {
4253
4304
  const visited2 = [];
4254
- const sols2 = proveGoals(
4255
- Array.from(thenClause.triples),
4256
- s1,
4257
- scopeFacts,
4258
- scopeBackRules,
4259
- depth + 1,
4260
- visited2,
4261
- varGen,
4262
- );
4305
+ const sols2 = proveGoals(thenGoals, s1, scopeFacts, scopeBackRules, depth + 1, visited2, varGen);
4263
4306
  if (!sols2.length) return [];
4264
4307
  }
4265
4308
  return [outSubst];
@@ -12696,14 +12739,26 @@ function liftBlankRuleVars(premise, conclusion) {
12696
12739
  return new Var(name);
12697
12740
  }
12698
12741
 
12742
+ function copyQuotedTerm(t) {
12743
+ // Quoted formulas are data terms with their own local blank scope.
12744
+ // Copy them structurally so later in-place rewrites cannot mutate shared AST,
12745
+ // but do not lift their blank nodes into rule-body variables.
12746
+ if (t instanceof ListTerm) return new ListTerm(t.elems.map(copyQuotedTerm));
12747
+ if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(copyQuotedTerm), t.tailVar);
12748
+ if (t instanceof GraphTerm) {
12749
+ const triples = t.triples.map(
12750
+ (tr) => new Triple(copyQuotedTerm(tr.s), copyQuotedTerm(tr.p), copyQuotedTerm(tr.o)),
12751
+ );
12752
+ return new GraphTerm(triples);
12753
+ }
12754
+ return t;
12755
+ }
12756
+
12699
12757
  function convertTerm(t) {
12700
12758
  if (t instanceof Blank) return blankToVar(t.label);
12701
12759
  if (t instanceof ListTerm) return new ListTerm(t.elems.map(convertTerm));
12702
12760
  if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(convertTerm), t.tailVar);
12703
- if (t instanceof GraphTerm) {
12704
- const triples = t.triples.map((tr) => new Triple(convertTerm(tr.s), convertTerm(tr.p), convertTerm(tr.o)));
12705
- return new GraphTerm(triples);
12706
- }
12761
+ if (t instanceof GraphTerm) return copyQuotedTerm(t);
12707
12762
  return t;
12708
12763
  }
12709
12764
 
package/lib/builtins.js CHANGED
@@ -330,6 +330,56 @@ function __builtinCollectVarsInTriples(triples, out) {
330
330
  for (const tr of triples) __builtinCollectVarsInTriple(tr, out);
331
331
  }
332
332
 
333
+ function __existentializeBlankTerm(t, mapping, varGen) {
334
+ if (t instanceof Blank) {
335
+ let v = mapping[t.label];
336
+ if (v === undefined) {
337
+ const n = Array.isArray(varGen) && typeof varGen[0] === 'number' ? varGen[0]++ : Object.keys(mapping).length + 1;
338
+ v = new Var(`__qb_${n}`);
339
+ mapping[t.label] = v;
340
+ }
341
+ return v;
342
+ }
343
+ if (t instanceof ListTerm) return t;
344
+ if (t instanceof OpenListTerm) return t;
345
+ if (t instanceof GraphTerm) {
346
+ let changed = false;
347
+ 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);
351
+ if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
352
+ return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
353
+ });
354
+ return changed ? new GraphTerm(triples) : t;
355
+ }
356
+ return t;
357
+ }
358
+
359
+ function __existentializeBlankTriples(triples, varGen) {
360
+ const mapping = Object.create(null);
361
+ let changed = false;
362
+ 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);
366
+ if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
367
+ return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
368
+ });
369
+ return changed ? out : triples;
370
+ }
371
+
372
+ function __prepareQuotedPatternTriples(rawTriples, subst, varGen) {
373
+ const ex = __existentializeBlankTriples(Array.from(rawTriples), varGen);
374
+ let changed = false;
375
+ const out = ex.map((tr) => {
376
+ const tr2 = applySubstTriple(tr, subst);
377
+ if (tr2 !== tr) changed = true;
378
+ return tr2;
379
+ });
380
+ return changed ? out : ex;
381
+ }
382
+
333
383
  function literalHasLangTag(lit) {
334
384
  // True iff the literal is a quoted string literal with a language tag suffix,
335
385
  // e.g. "hello"@en or """hello"""@en.
@@ -3522,7 +3572,8 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3522
3572
  const keepVars = new Set();
3523
3573
  if (g.s instanceof GraphTerm) __builtinCollectVarsInTriples(g.s.triples, keepVars);
3524
3574
 
3525
- const goalVariants = __expandScopedVarPredicateGoals(Array.from(g.o.triples));
3575
+ const goalTriples = __prepareQuotedPatternTriples(goal.o.triples, subst, varGen);
3576
+ const goalVariants = __expandScopedVarPredicateGoals(goalTriples);
3526
3577
  const out = [];
3527
3578
  for (const variant of goalVariants) {
3528
3579
  const sols = proveGoals(
@@ -3604,7 +3655,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3604
3655
 
3605
3656
  const visited2 = [];
3606
3657
  const sols = proveGoals(
3607
- Array.from(g.o.triples),
3658
+ __prepareQuotedPatternTriples(goal.o.triples, subst, varGen),
3608
3659
  { ...subst },
3609
3660
  scopeFacts,
3610
3661
  scopeBackRules,
@@ -3698,15 +3749,12 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3698
3749
  }
3699
3750
 
3700
3751
  const visited2 = [];
3701
- const sols = proveGoals(
3702
- Array.from(clauseTerm.triples),
3703
- {},
3704
- scopeFacts,
3705
- scopeBackRules,
3706
- depth + 1,
3707
- visited2,
3708
- varGen,
3709
- );
3752
+ const rawClauseTerm = goal.s instanceof ListTerm ? goal.s.elems[1] : clauseTerm;
3753
+ const clauseGoals =
3754
+ rawClauseTerm instanceof GraphTerm
3755
+ ? __prepareQuotedPatternTriples(rawClauseTerm.triples, subst, varGen)
3756
+ : __prepareQuotedPatternTriples(clauseTerm.triples, subst, varGen);
3757
+ const sols = proveGoals(clauseGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited2, varGen);
3710
3758
 
3711
3759
  const collected = sols.map((sBody) => applySubstTerm(valueTempl, sBody));
3712
3760
  const collectedList = new ListTerm(collected);
@@ -3759,28 +3807,23 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3759
3807
  scopeFacts = snap;
3760
3808
  }
3761
3809
 
3810
+ const rawWhereClause = goal.s instanceof ListTerm ? goal.s.elems[0] : whereClause;
3811
+ const rawThenClause = goal.s instanceof ListTerm ? goal.s.elems[1] : thenClause;
3812
+ const whereGoals =
3813
+ rawWhereClause instanceof GraphTerm
3814
+ ? __prepareQuotedPatternTriples(rawWhereClause.triples, subst, varGen)
3815
+ : __prepareQuotedPatternTriples(whereClause.triples, subst, varGen);
3816
+ const thenGoals =
3817
+ rawThenClause instanceof GraphTerm
3818
+ ? __prepareQuotedPatternTriples(rawThenClause.triples, subst, varGen)
3819
+ : __prepareQuotedPatternTriples(thenClause.triples, subst, varGen);
3820
+
3762
3821
  const visited1 = [];
3763
- const sols1 = proveGoals(
3764
- Array.from(whereClause.triples),
3765
- {},
3766
- scopeFacts,
3767
- scopeBackRules,
3768
- depth + 1,
3769
- visited1,
3770
- varGen,
3771
- );
3822
+ const sols1 = proveGoals(whereGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited1, varGen);
3772
3823
 
3773
3824
  for (const s1 of sols1) {
3774
3825
  const visited2 = [];
3775
- const sols2 = proveGoals(
3776
- Array.from(thenClause.triples),
3777
- s1,
3778
- scopeFacts,
3779
- scopeBackRules,
3780
- depth + 1,
3781
- visited2,
3782
- varGen,
3783
- );
3826
+ const sols2 = proveGoals(thenGoals, s1, scopeFacts, scopeBackRules, depth + 1, visited2, varGen);
3784
3827
  if (!sols2.length) return [];
3785
3828
  }
3786
3829
  return [outSubst];
package/lib/rules.js CHANGED
@@ -25,14 +25,26 @@ function liftBlankRuleVars(premise, conclusion) {
25
25
  return new Var(name);
26
26
  }
27
27
 
28
+ function copyQuotedTerm(t) {
29
+ // Quoted formulas are data terms with their own local blank scope.
30
+ // Copy them structurally so later in-place rewrites cannot mutate shared AST,
31
+ // but do not lift their blank nodes into rule-body variables.
32
+ if (t instanceof ListTerm) return new ListTerm(t.elems.map(copyQuotedTerm));
33
+ if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(copyQuotedTerm), t.tailVar);
34
+ if (t instanceof GraphTerm) {
35
+ const triples = t.triples.map(
36
+ (tr) => new Triple(copyQuotedTerm(tr.s), copyQuotedTerm(tr.p), copyQuotedTerm(tr.o)),
37
+ );
38
+ return new GraphTerm(triples);
39
+ }
40
+ return t;
41
+ }
42
+
28
43
  function convertTerm(t) {
29
44
  if (t instanceof Blank) return blankToVar(t.label);
30
45
  if (t instanceof ListTerm) return new ListTerm(t.elems.map(convertTerm));
31
46
  if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(convertTerm), t.tailVar);
32
- if (t instanceof GraphTerm) {
33
- const triples = t.triples.map((tr) => new Triple(convertTerm(tr.s), convertTerm(tr.p), convertTerm(tr.o)));
34
- return new GraphTerm(triples);
35
- }
47
+ if (t instanceof GraphTerm) return copyQuotedTerm(t);
36
48
  return t;
37
49
  }
38
50
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.21.9",
3
+ "version": "1.21.10",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
package/test/api.test.js CHANGED
@@ -2035,6 +2035,123 @@ _:x :hates { _:foo :making :mess }.
2035
2035
  },
2036
2036
  expect: [/:x :value "world" \./m],
2037
2037
  },
2038
+
2039
+ {
2040
+ name: '241 regression: quoted-formula blanks in rule bodies stay blank through log:conjunction',
2041
+ opt: { proofComments: false },
2042
+ input: `@prefix log: <http://www.w3.org/2000/10/swap/log#> .
2043
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
2044
+ @prefix : <http://example.org/ns#> .
2045
+
2046
+ {
2047
+ ( { ?S a :Subject } { [] a :Thing } ) log:conjunction ?Z.
2048
+ }
2049
+ =>
2050
+ {
2051
+ :result :is ?Z.
2052
+ }.
2053
+ `,
2054
+ expect: [/:result\s+:is\s+\{[\s\S]*\?S\s+a\s+:Subject\s*\.[\s\S]*_:(?:b\d+)\s+a\s+:Thing\s*\.[\s\S]*\}\s*\./m],
2055
+ notExpect: [/\?_b\d+\s+a\s+:Thing\s*\./],
2056
+ },
2057
+ {
2058
+ name: '242 regression: log:includes existentializes blank nodes inside quoted formula patterns',
2059
+ opt: { proofComments: false },
2060
+ input: `@prefix : <http://example.org/> .
2061
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
2062
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
2063
+
2064
+ :doc :graph {
2065
+ :perm :duty [
2066
+ :action :inform ;
2067
+ :constraint [
2068
+ :kind :notice ;
2069
+ :days 3
2070
+ ]
2071
+ ]
2072
+ } .
2073
+
2074
+ {
2075
+ :doc :graph ?G .
2076
+ ?G log:includes {
2077
+ :perm :duty [
2078
+ :action :inform ;
2079
+ :constraint [
2080
+ :kind :notice ;
2081
+ :days ?D
2082
+ ]
2083
+ ]
2084
+ } .
2085
+ }
2086
+ =>
2087
+ {
2088
+ :result :days ?D ;
2089
+ :status :matched .
2090
+ }.
2091
+ `,
2092
+ expect: [
2093
+ /:result\s+:days\s+3(?:\s*\^\^<http:\/\/www\.w3\.org\/2001\/XMLSchema#integer>)?\s*\./,
2094
+ /:result\s+:status\s+:matched\s*\./,
2095
+ ],
2096
+ },
2097
+ {
2098
+ name: '243a regression: collectAllIn treats quoted-formula blanks existentially',
2099
+ opt: { proofComments: false },
2100
+ input: `@prefix : <http://example.org/> .
2101
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
2102
+
2103
+ :a :p [ :q 1 ] .
2104
+ :b :p [ :q 2 ] .
2105
+
2106
+ {
2107
+ ( ?s { ?s :p [ :q 1 ] . } ?xs ) log:collectAllIn _:scope .
2108
+ ?xs log:equalTo ( :a ) .
2109
+ }
2110
+ =>
2111
+ {
2112
+ :test :is true .
2113
+ }.
2114
+ `,
2115
+ expect: [/:(?:test)\s+:(?:is)\s+true\s*\./],
2116
+ },
2117
+
2118
+ {
2119
+ name: '243 regression: quoted formulas remain isolated from collectAllIn rule-body rewrites',
2120
+ opt: { proofComments: false },
2121
+ input: `@prefix : <http://example.org/jade-eigen-loom#> .
2122
+ @prefix math: <http://www.w3.org/2000/10/swap/math#> .
2123
+ @prefix list: <http://www.w3.org/2000/10/swap/list#> .
2124
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
2125
+
2126
+ :PCA1 :points (
2127
+ [ :id 1 ; :x 2.0 ; :y 1.0 ]
2128
+ [ :id 2 ; :x 3.0 ; :y 2.0 ]
2129
+ [ :id 3 ; :x 4.0 ; :y 3.2 ]
2130
+ [ :id 4 ; :x 5.0 ; :y 5.1 ]
2131
+ [ :id 5 ; :x 6.0 ; :y 7.9 ]
2132
+ [ :id 6 ; :x 7.0 ; :y 13.0 ]
2133
+ [ :id 7 ; :x 20.0 ; :y -3.0 ]
2134
+ ) .
2135
+
2136
+ {
2137
+ :PCA1 :points ?pts .
2138
+ ?pts list:length ?n .
2139
+ ( ?x { ?pts list:member ?p . ?p :x ?x . } ?xs ) log:collectAllIn _:m1 .
2140
+ ?xs math:sum ?sumX .
2141
+ (?sumX ?n) math:quotient ?meanX .
2142
+ }
2143
+ =>
2144
+ {
2145
+ :result :xs ?xs ; :sumX ?sumX ; :meanX ?meanX .
2146
+ }.
2147
+ `,
2148
+ expect: [
2149
+ /:result\s+:xs\s+\(2\.0 3\.0 4\.0 5\.0 6\.0 7\.0 20\.0\)\s*\./,
2150
+ /:result\s+:sumX\s+"47"\^\^xsd:decimal\s*\./,
2151
+ /:result\s+:meanX\s+"6\.714285714285714"\^\^xsd:decimal\s*\./,
2152
+ ],
2153
+ notExpect: [/:result\s+:sumX\s+"329"\^\^xsd:decimal\s*\./, /:result\s+:meanX\s+"47"\^\^xsd:decimal\s*\./],
2154
+ },
2038
2155
  ];
2039
2156
 
2040
2157
  let passed = 0;