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.
- package/dist/browser/eyeling.browser.js +89 -33
- package/eyeling.js +88 -33
- package/lib/builtins.js +72 -29
- package/lib/rules.js +16 -4
- package/package.json +1 -1
- package/test/api.test.js +117 -0
|
@@ -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
|
|
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
|
-
|
|
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
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
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
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;
|