eyeling 1.7.4 → 1.7.6
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/examples/list-map.n3 +17 -0
- package/examples/log-conclusion.n3 +20 -0
- package/examples/output/list-map.n3 +6 -0
- package/examples/output/log-conclusion.n3 +13 -0
- package/eyeling.js +129 -8
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# ========================================================
|
|
2
|
+
# List map
|
|
3
|
+
# behaves like the functional programming operator flatMap
|
|
4
|
+
# Examples from Giacomo Citi
|
|
5
|
+
# ========================================================
|
|
6
|
+
|
|
7
|
+
@prefix list: <http://www.w3.org/2000/10/swap/list#> .
|
|
8
|
+
@prefix : <http://example.org/#>.
|
|
9
|
+
|
|
10
|
+
:s1 :p1 :o1 .
|
|
11
|
+
:s2 :p1 :o2 .
|
|
12
|
+
:s3 :p1 :o3, :o4 .
|
|
13
|
+
|
|
14
|
+
{ ((:s1 :s2) :p1) list:map (:o1 :o2) } => { :test1 :is true } .
|
|
15
|
+
{ ((:s1 :s2 :s3) :p1) list:map (:o1 :o2 :o3 :o4) } => { :test2 :is true } .
|
|
16
|
+
{ ((:s4) :p1) list:map () } => { :test3 :is true } .
|
|
17
|
+
{ ((:s1) :p2) list:map () } => { :test4 :is true } .
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# ======================
|
|
2
|
+
# log:conclusion example
|
|
3
|
+
# ======================
|
|
4
|
+
|
|
5
|
+
@prefix : <http://example.org/>.
|
|
6
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
7
|
+
|
|
8
|
+
:let :param {
|
|
9
|
+
:Felix a :Cat .
|
|
10
|
+
{ ?X a :Cat . } => { ?X :says "Meow" . } .
|
|
11
|
+
} .
|
|
12
|
+
|
|
13
|
+
{
|
|
14
|
+
:let :param ?param .
|
|
15
|
+
?param log:conclusion ?conclusion .
|
|
16
|
+
}
|
|
17
|
+
=>
|
|
18
|
+
{
|
|
19
|
+
:result :is ?conclusion .
|
|
20
|
+
} .
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
@prefix : <http://example.org/> .
|
|
2
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
3
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
|
4
|
+
|
|
5
|
+
:result :is {
|
|
6
|
+
:Felix a :Cat .
|
|
7
|
+
{
|
|
8
|
+
?X a :Cat .
|
|
9
|
+
} => {
|
|
10
|
+
?X :says "Meow" .
|
|
11
|
+
} .
|
|
12
|
+
:Felix :says "Meow" .
|
|
13
|
+
} .
|
package/eyeling.js
CHANGED
|
@@ -89,6 +89,7 @@ const jsonPointerCache = new Map();
|
|
|
89
89
|
// Key is the dereferenced document IRI *without* fragment.
|
|
90
90
|
const __logContentCache = new Map(); // iri -> string | null (null means fetch/read failed)
|
|
91
91
|
const __logSemanticsCache = new Map(); // iri -> GraphTerm | null (null means parse failed)
|
|
92
|
+
const __logConclusionCache = new WeakMap(); // GraphTerm -> GraphTerm (deductive closure)
|
|
92
93
|
|
|
93
94
|
function __stripFragment(iri) {
|
|
94
95
|
const i = iri.indexOf('#');
|
|
@@ -233,6 +234,87 @@ function __derefSemanticsSync(iriNoFrag) {
|
|
|
233
234
|
return null;
|
|
234
235
|
}
|
|
235
236
|
}
|
|
237
|
+
function __makeRuleFromTerms(left, right, isForward) {
|
|
238
|
+
// Mirror Parser.makeRule, but usable at runtime (e.g., log:conclusion).
|
|
239
|
+
let premiseTerm, conclTerm;
|
|
240
|
+
|
|
241
|
+
if (isForward) {
|
|
242
|
+
premiseTerm = left;
|
|
243
|
+
conclTerm = right;
|
|
244
|
+
} else {
|
|
245
|
+
premiseTerm = right;
|
|
246
|
+
conclTerm = left;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
let isFuse = false;
|
|
250
|
+
if (isForward) {
|
|
251
|
+
if (conclTerm instanceof Literal && conclTerm.value === 'false') {
|
|
252
|
+
isFuse = true;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
let rawPremise;
|
|
257
|
+
if (premiseTerm instanceof GraphTerm) {
|
|
258
|
+
rawPremise = premiseTerm.triples;
|
|
259
|
+
} else if (premiseTerm instanceof Literal && premiseTerm.value === 'true') {
|
|
260
|
+
rawPremise = [];
|
|
261
|
+
} else {
|
|
262
|
+
rawPremise = [];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
let rawConclusion;
|
|
266
|
+
if (conclTerm instanceof GraphTerm) {
|
|
267
|
+
rawConclusion = conclTerm.triples;
|
|
268
|
+
} else if (conclTerm instanceof Literal && conclTerm.value === 'false') {
|
|
269
|
+
rawConclusion = [];
|
|
270
|
+
} else {
|
|
271
|
+
rawConclusion = [];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const headBlankLabels = collectBlankLabelsInTriples(rawConclusion);
|
|
275
|
+
const [premise0, conclusion] = liftBlankRuleVars(rawPremise, rawConclusion);
|
|
276
|
+
const premise = isForward ? reorderPremiseForConstraints(premise0) : premise0;
|
|
277
|
+
return new Rule(premise, conclusion, isForward, isFuse, headBlankLabels);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function __computeConclusionFromFormula(formula) {
|
|
281
|
+
if (!(formula instanceof GraphTerm)) return null;
|
|
282
|
+
|
|
283
|
+
const cached = __logConclusionCache.get(formula);
|
|
284
|
+
if (cached) return cached;
|
|
285
|
+
|
|
286
|
+
// Facts start as *all* triples in the formula, including rule triples.
|
|
287
|
+
const facts2 = formula.triples.slice();
|
|
288
|
+
|
|
289
|
+
// Extract rules from rule-triples present inside the formula.
|
|
290
|
+
const fw = [];
|
|
291
|
+
const bw = [];
|
|
292
|
+
|
|
293
|
+
for (const tr of formula.triples) {
|
|
294
|
+
// Treat {A} => {B} as a forward rule.
|
|
295
|
+
if (isLogImplies(tr.p)) {
|
|
296
|
+
fw.push(__makeRuleFromTerms(tr.s, tr.o, true));
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Treat {A} <= {B} as the same rule in the other direction, i.e., {B} => {A},
|
|
301
|
+
// so it participates in deductive closure even if only <= is used.
|
|
302
|
+
if (isLogImpliedBy(tr.p)) {
|
|
303
|
+
fw.push(__makeRuleFromTerms(tr.o, tr.s, true));
|
|
304
|
+
// Also index it as a backward rule for completeness (helps proveGoals in some cases).
|
|
305
|
+
bw.push(__makeRuleFromTerms(tr.s, tr.o, false));
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Saturate within this local formula only.
|
|
311
|
+
forwardChain(facts2, fw, bw);
|
|
312
|
+
|
|
313
|
+
const out = new GraphTerm(facts2.slice());
|
|
314
|
+
__logConclusionCache.set(formula, out);
|
|
315
|
+
return out;
|
|
316
|
+
}
|
|
317
|
+
|
|
236
318
|
|
|
237
319
|
|
|
238
320
|
// Controls whether human-readable proof comments are printed.
|
|
@@ -4722,19 +4804,26 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4722
4804
|
const inputList = inputTerm.elems;
|
|
4723
4805
|
if (!(predTerm instanceof Iri)) return [];
|
|
4724
4806
|
const pred = internIri(predTerm.value);
|
|
4725
|
-
|
|
4807
|
+
|
|
4808
|
+
// Allow mapping *any* predicate (not just builtins).
|
|
4809
|
+
// Semantics: for each input element `el`, collect *all* solutions of `el pred ?y`
|
|
4810
|
+
// (facts, rules, and builtins), in order, and concatenate them into the output list.
|
|
4811
|
+
// If an element has no solutions, it contributes nothing.
|
|
4726
4812
|
if (!inputList.every((e) => isGroundTerm(e))) return [];
|
|
4727
4813
|
|
|
4728
4814
|
const results = [];
|
|
4729
4815
|
for (const el of inputList) {
|
|
4730
4816
|
const yvar = new Var('_mapY');
|
|
4731
4817
|
const goal2 = new Triple(el, pred, yvar);
|
|
4732
|
-
const sols =
|
|
4733
|
-
|
|
4734
|
-
const
|
|
4735
|
-
|
|
4736
|
-
|
|
4818
|
+
const sols = proveGoals([goal2], subst, facts, backRules, depth + 1, [], varGen);
|
|
4819
|
+
|
|
4820
|
+
for (const sol of sols) {
|
|
4821
|
+
const yval = applySubstTerm(yvar, sol);
|
|
4822
|
+
if (yval instanceof Var) continue;
|
|
4823
|
+
results.push(yval);
|
|
4824
|
+
}
|
|
4737
4825
|
}
|
|
4826
|
+
|
|
4738
4827
|
const outList = new ListTerm(results);
|
|
4739
4828
|
const s2 = unifyTerm(g.o, outList, subst);
|
|
4740
4829
|
return s2 !== null ? [s2] : [];
|
|
@@ -4843,6 +4932,31 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4843
4932
|
return s2 !== null ? [s2] : [];
|
|
4844
4933
|
}
|
|
4845
4934
|
|
|
4935
|
+
|
|
4936
|
+
// log:conclusion
|
|
4937
|
+
// Schema: $s+ log:conclusion $o?
|
|
4938
|
+
// $o is the deductive closure of the subject formula $s (including rule inferences).
|
|
4939
|
+
if (pv === LOG_NS + 'conclusion') {
|
|
4940
|
+
// Accept 'true' as the empty formula.
|
|
4941
|
+
let inFormula = null;
|
|
4942
|
+
if (g.s instanceof GraphTerm) inFormula = g.s;
|
|
4943
|
+
else if (g.s instanceof Literal && g.s.value === 'true') inFormula = new GraphTerm([]);
|
|
4944
|
+
else return [];
|
|
4945
|
+
|
|
4946
|
+
const conclusion = __computeConclusionFromFormula(inFormula);
|
|
4947
|
+
if (!(conclusion instanceof GraphTerm)) return [];
|
|
4948
|
+
|
|
4949
|
+
if (g.o instanceof Var) {
|
|
4950
|
+
const s2 = { ...subst };
|
|
4951
|
+
s2[g.o.name] = conclusion;
|
|
4952
|
+
return [s2];
|
|
4953
|
+
}
|
|
4954
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
4955
|
+
|
|
4956
|
+
const s2 = unifyTerm(g.o, conclusion, subst);
|
|
4957
|
+
return s2 !== null ? [s2] : [];
|
|
4958
|
+
}
|
|
4959
|
+
|
|
4846
4960
|
// log:content
|
|
4847
4961
|
// Schema: $s+ log:content $o?
|
|
4848
4962
|
// Dereferences $s and returns the online resource as an xsd:string.
|
|
@@ -5991,10 +6105,17 @@ function termToN3(t, pref) {
|
|
|
5991
6105
|
return '(' + inside.join(' ') + ')';
|
|
5992
6106
|
}
|
|
5993
6107
|
if (t instanceof GraphTerm) {
|
|
6108
|
+
const indent = ' ';
|
|
6109
|
+
const indentBlock = (str) =>
|
|
6110
|
+
str
|
|
6111
|
+
.split(/\r?\n/)
|
|
6112
|
+
.map((ln) => (ln.length ? indent + ln : ln))
|
|
6113
|
+
.join('\n');
|
|
6114
|
+
|
|
5994
6115
|
let s = '{\n';
|
|
5995
6116
|
for (const tr of t.triples) {
|
|
5996
|
-
|
|
5997
|
-
if (
|
|
6117
|
+
const block = tripleToN3(tr, pref).trimEnd();
|
|
6118
|
+
if (block) s += indentBlock(block) + '\n';
|
|
5998
6119
|
}
|
|
5999
6120
|
s += '}';
|
|
6000
6121
|
return s;
|