eyeling 1.26.0 → 1.26.2
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 +247 -314
- package/examples/proof/age.n3 +14 -5
- package/examples/proof/socrates.n3 +7 -2
- package/examples/proof/witch.n3 +27 -9
- package/eyeling.js +247 -314
- package/lib/cli.js +2 -2
- package/lib/engine.js +130 -216
- package/lib/explain.js +98 -79
- package/lib/multisource.js +17 -17
- package/package.json +1 -1
package/lib/cli.js
CHANGED
|
@@ -211,8 +211,8 @@ function main() {
|
|
|
211
211
|
baseIri: __sourceLabelToBaseIri(sourceLabel),
|
|
212
212
|
label: sourceLabel,
|
|
213
213
|
collectUsedPrefixes: streamMode,
|
|
214
|
-
collectSourceLocations: engine.getProofCommentsEnabled(),
|
|
215
214
|
keepSourceArtifacts: false,
|
|
215
|
+
sourceLocations: engine.getProofCommentsEnabled(),
|
|
216
216
|
rdf: rdfMode,
|
|
217
217
|
}),
|
|
218
218
|
);
|
|
@@ -368,7 +368,7 @@ function main() {
|
|
|
368
368
|
}
|
|
369
369
|
|
|
370
370
|
if (engine.getProofCommentsEnabled()) {
|
|
371
|
-
process.stdout.write(engine.renderProofDocument(outDerived, derived, triples, prefixes, brules));
|
|
371
|
+
process.stdout.write(engine.renderProofDocument(outDerived, derived.concat(outDerived || []), triples, prefixes, brules));
|
|
372
372
|
return;
|
|
373
373
|
}
|
|
374
374
|
|
package/lib/engine.js
CHANGED
|
@@ -25,6 +25,7 @@ const {
|
|
|
25
25
|
Triple,
|
|
26
26
|
Rule,
|
|
27
27
|
DerivedFact,
|
|
28
|
+
varsInRule,
|
|
28
29
|
internIri,
|
|
29
30
|
collectBlankLabelsInTriples,
|
|
30
31
|
copyQuotedGraphMetadata,
|
|
@@ -42,7 +43,7 @@ const EMPTY_LIST_TERM = new ListTerm([]);
|
|
|
42
43
|
const { lex, N3SyntaxError } = require('./lexer');
|
|
43
44
|
const { Parser } = require('./parser');
|
|
44
45
|
const { liftBlankRuleVars } = require('./rules');
|
|
45
|
-
const { parseN3SourceList } = require('./multisource');
|
|
46
|
+
const { parseN3SourceList, parseN3Text } = require('./multisource');
|
|
46
47
|
|
|
47
48
|
const {
|
|
48
49
|
makeBuiltins,
|
|
@@ -2435,217 +2436,6 @@ function unifyTriple(pat, fact, subst) {
|
|
|
2435
2436
|
return s3;
|
|
2436
2437
|
}
|
|
2437
2438
|
|
|
2438
|
-
|
|
2439
|
-
function __copyProofSourceMetadata(from, to) {
|
|
2440
|
-
if (!from || !to) return to;
|
|
2441
|
-
for (const key of ['__sourceOffset', '__source']) {
|
|
2442
|
-
if (hasOwn.call(from, key)) {
|
|
2443
|
-
Object.defineProperty(to, key, {
|
|
2444
|
-
value: from[key],
|
|
2445
|
-
enumerable: false,
|
|
2446
|
-
writable: false,
|
|
2447
|
-
configurable: true,
|
|
2448
|
-
});
|
|
2449
|
-
}
|
|
2450
|
-
}
|
|
2451
|
-
return to;
|
|
2452
|
-
}
|
|
2453
|
-
|
|
2454
|
-
function __standardizeRuleForProof(rule, gen) {
|
|
2455
|
-
const proofVarNames = Object.create(null);
|
|
2456
|
-
|
|
2457
|
-
function renameTerm(t, vmapName, vmapVar, genArr) {
|
|
2458
|
-
if (t instanceof Var) {
|
|
2459
|
-
if (!hasOwn.call(vmapVar, t.name)) {
|
|
2460
|
-
const name = `${t.name}__proof_${genArr[0]}`;
|
|
2461
|
-
genArr[0] += 1;
|
|
2462
|
-
vmapName[t.name] = name;
|
|
2463
|
-
vmapVar[t.name] = new Var(name);
|
|
2464
|
-
proofVarNames[name] = t.name;
|
|
2465
|
-
}
|
|
2466
|
-
return vmapVar[t.name];
|
|
2467
|
-
}
|
|
2468
|
-
if (t instanceof ListTerm) {
|
|
2469
|
-
let changed = false;
|
|
2470
|
-
const elems2 = t.elems.map((e) => {
|
|
2471
|
-
const e2 = renameTerm(e, vmapName, vmapVar, genArr);
|
|
2472
|
-
if (e2 !== e) changed = true;
|
|
2473
|
-
return e2;
|
|
2474
|
-
});
|
|
2475
|
-
return changed ? new ListTerm(elems2) : t;
|
|
2476
|
-
}
|
|
2477
|
-
if (t instanceof OpenListTerm) {
|
|
2478
|
-
let changed = false;
|
|
2479
|
-
const newXs = t.prefix.map((e) => {
|
|
2480
|
-
const e2 = renameTerm(e, vmapName, vmapVar, genArr);
|
|
2481
|
-
if (e2 !== e) changed = true;
|
|
2482
|
-
return e2;
|
|
2483
|
-
});
|
|
2484
|
-
if (!hasOwn.call(vmapName, t.tailVar)) {
|
|
2485
|
-
const name = `${t.tailVar}__proof_${genArr[0]}`;
|
|
2486
|
-
genArr[0] += 1;
|
|
2487
|
-
vmapName[t.tailVar] = name;
|
|
2488
|
-
vmapVar[t.tailVar] = new Var(name);
|
|
2489
|
-
proofVarNames[name] = t.tailVar;
|
|
2490
|
-
}
|
|
2491
|
-
const newTail = vmapName[t.tailVar];
|
|
2492
|
-
if (newTail !== t.tailVar) changed = true;
|
|
2493
|
-
return changed ? new OpenListTerm(newXs, newTail) : t;
|
|
2494
|
-
}
|
|
2495
|
-
if (t instanceof GraphTerm) {
|
|
2496
|
-
let changed = false;
|
|
2497
|
-
const triples2 = t.triples.map((tr) => {
|
|
2498
|
-
const s2 = renameTerm(tr.s, vmapName, vmapVar, genArr);
|
|
2499
|
-
const p2 = renameTerm(tr.p, vmapName, vmapVar, genArr);
|
|
2500
|
-
const o2 = renameTerm(tr.o, vmapName, vmapVar, genArr);
|
|
2501
|
-
if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
|
|
2502
|
-
return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
|
|
2503
|
-
});
|
|
2504
|
-
return changed ? copyQuotedGraphMetadata(t, new GraphTerm(triples2)) : t;
|
|
2505
|
-
}
|
|
2506
|
-
return t;
|
|
2507
|
-
}
|
|
2508
|
-
|
|
2509
|
-
const vmapName = Object.create(null);
|
|
2510
|
-
const vmapVar = Object.create(null);
|
|
2511
|
-
const premise = rule.premise.map((tr) => {
|
|
2512
|
-
const s2 = renameTerm(tr.s, vmapName, vmapVar, gen);
|
|
2513
|
-
const p2 = renameTerm(tr.p, vmapName, vmapVar, gen);
|
|
2514
|
-
const o2 = renameTerm(tr.o, vmapName, vmapVar, gen);
|
|
2515
|
-
return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
|
|
2516
|
-
});
|
|
2517
|
-
const conclusion = rule.conclusion.map((tr) => {
|
|
2518
|
-
const s2 = renameTerm(tr.s, vmapName, vmapVar, gen);
|
|
2519
|
-
const p2 = renameTerm(tr.p, vmapName, vmapVar, gen);
|
|
2520
|
-
const o2 = renameTerm(tr.o, vmapName, vmapVar, gen);
|
|
2521
|
-
return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
|
|
2522
|
-
});
|
|
2523
|
-
const out = new Rule(premise, conclusion, rule.isForward, rule.isFuse, rule.headBlankLabels);
|
|
2524
|
-
Object.defineProperty(out, '__proofVarSourceNames', {
|
|
2525
|
-
value: proofVarNames,
|
|
2526
|
-
enumerable: false,
|
|
2527
|
-
writable: false,
|
|
2528
|
-
configurable: true,
|
|
2529
|
-
});
|
|
2530
|
-
return __copyProofSourceMetadata(rule, out);
|
|
2531
|
-
}
|
|
2532
|
-
|
|
2533
|
-
function __mergeProofSubst(base, delta) {
|
|
2534
|
-
const out = __cloneSubst(base);
|
|
2535
|
-
for (const k in delta || {}) {
|
|
2536
|
-
if (!hasOwn.call(delta, k)) continue;
|
|
2537
|
-
const v = applySubstTerm(delta[k], out);
|
|
2538
|
-
if (hasOwn.call(out, k)) {
|
|
2539
|
-
if (!termsEqual(applySubstTerm(out[k], out), v)) return null;
|
|
2540
|
-
} else {
|
|
2541
|
-
out[k] = v;
|
|
2542
|
-
}
|
|
2543
|
-
}
|
|
2544
|
-
return out;
|
|
2545
|
-
}
|
|
2546
|
-
|
|
2547
|
-
function __sourceSortKey(obj) {
|
|
2548
|
-
const src = obj && obj.__source;
|
|
2549
|
-
const label = src && typeof src.label === 'string' ? src.label : '';
|
|
2550
|
-
const line = src && Number.isInteger(src.line) ? src.line : 0;
|
|
2551
|
-
return `${label}\t${line}`;
|
|
2552
|
-
}
|
|
2553
|
-
|
|
2554
|
-
function __candidateFactsForProof(facts, goal) {
|
|
2555
|
-
if (!Array.isArray(facts)) return [];
|
|
2556
|
-
ensureFactIndexes(facts);
|
|
2557
|
-
const candidates = candidateFacts(facts, goal);
|
|
2558
|
-
if (!candidates) return facts;
|
|
2559
|
-
const out = [];
|
|
2560
|
-
for (let i = 0; i < candidates.exactLen; i++) out.push(facts[candidates.exact[i]]);
|
|
2561
|
-
for (let i = 0; i < candidates.wildLen; i++) out.push(facts[candidates.wild[i]]);
|
|
2562
|
-
return out;
|
|
2563
|
-
}
|
|
2564
|
-
|
|
2565
|
-
function findBackwardProofForGoal(goal, facts, backRules, opts = {}) {
|
|
2566
|
-
const maxDepth = Number.isInteger(opts.maxDepth) && opts.maxDepth > 0 ? opts.maxDepth : 64;
|
|
2567
|
-
const varGen = [0];
|
|
2568
|
-
|
|
2569
|
-
function proofKey(tr) {
|
|
2570
|
-
return `${skolemKeyFromTerm(tr.s)}\t${skolemKeyFromTerm(tr.p)}\t${skolemKeyFromTerm(tr.o)}`;
|
|
2571
|
-
}
|
|
2572
|
-
|
|
2573
|
-
function proveGoal(goal0, subst, depth, visited) {
|
|
2574
|
-
if (depth > maxDepth) return [];
|
|
2575
|
-
const g = applySubstTriple(goal0, subst);
|
|
2576
|
-
const key = proofKey(g);
|
|
2577
|
-
if (visited.has(key)) return [];
|
|
2578
|
-
|
|
2579
|
-
if (isBuiltinPred(g.p)) {
|
|
2580
|
-
const builtinGoalForEval = g.p instanceof Iri && g.p.value === LOG_NS + 'rawType' ? goal0 : g;
|
|
2581
|
-
const builtinSubstForEval = g.p instanceof Iri && g.p.value === LOG_NS + 'rawType' ? subst : __emptySubst();
|
|
2582
|
-
const deltas = evalBuiltin(builtinGoalForEval, builtinSubstForEval, facts, backRules, depth, varGen, 1) || [];
|
|
2583
|
-
const out = [];
|
|
2584
|
-
for (const delta of deltas) {
|
|
2585
|
-
const subst2 = __mergeProofSubst(subst, delta);
|
|
2586
|
-
if (subst2 === null) continue;
|
|
2587
|
-
const fact = applySubstTriple(g, subst2);
|
|
2588
|
-
out.push({ subst: subst2, proof: { kind: 'builtin', fact, builtin: fact.p } });
|
|
2589
|
-
break;
|
|
2590
|
-
}
|
|
2591
|
-
return out;
|
|
2592
|
-
}
|
|
2593
|
-
|
|
2594
|
-
const factResults = [];
|
|
2595
|
-
for (const f of __candidateFactsForProof(facts, g)) {
|
|
2596
|
-
const subst2 = unifyTriple(g, f, subst);
|
|
2597
|
-
if (subst2 === null) continue;
|
|
2598
|
-
factResults.push({ subst: subst2, proof: { kind: 'fact', fact: applySubstTriple(f, subst2), source: f.__source } });
|
|
2599
|
-
break;
|
|
2600
|
-
}
|
|
2601
|
-
if (factResults.length) return factResults;
|
|
2602
|
-
|
|
2603
|
-
if (!(g.p instanceof Iri)) return [];
|
|
2604
|
-
ensureBackRuleIndexes(backRules);
|
|
2605
|
-
const rules = (backRules.__byHeadPred.get(g.p.__tid) || []).concat(backRules.__wildHeadPred || []);
|
|
2606
|
-
rules.sort((a, b) => (__sourceSortKey(a) < __sourceSortKey(b) ? -1 : __sourceSortKey(a) > __sourceSortKey(b) ? 1 : 0));
|
|
2607
|
-
|
|
2608
|
-
const visited2 = new Set(visited);
|
|
2609
|
-
visited2.add(key);
|
|
2610
|
-
|
|
2611
|
-
for (const r of rules) {
|
|
2612
|
-
if (!r || !Array.isArray(r.conclusion) || r.conclusion.length !== 1) continue;
|
|
2613
|
-
const rStd = __standardizeRuleForProof(r, varGen);
|
|
2614
|
-
const head = rStd.conclusion[0];
|
|
2615
|
-
if (head.p instanceof Iri && head.p.__tid !== g.p.__tid) continue;
|
|
2616
|
-
const subst1 = unifyTriple(head, g, subst);
|
|
2617
|
-
if (subst1 === null) continue;
|
|
2618
|
-
const bodyProof = proveGoalList(rStd.premise || [], subst1, depth + 1, visited2);
|
|
2619
|
-
if (!bodyProof) continue;
|
|
2620
|
-
const fact = applySubstTriple(g, bodyProof.subst);
|
|
2621
|
-
const premises = (rStd.premise || []).map((prem) => applySubstTriple(prem, bodyProof.subst));
|
|
2622
|
-
const df = new DerivedFact(fact, rStd, premises, bodyProof.subst);
|
|
2623
|
-
return [{ subst: bodyProof.subst, proof: { kind: 'rule', df, children: bodyProof.proofs } }];
|
|
2624
|
-
}
|
|
2625
|
-
|
|
2626
|
-
return [];
|
|
2627
|
-
}
|
|
2628
|
-
|
|
2629
|
-
function proveGoalList(goals, subst, depth, visited) {
|
|
2630
|
-
let states = [{ subst, proofs: [] }];
|
|
2631
|
-
for (const goal1 of goals || []) {
|
|
2632
|
-
const next = [];
|
|
2633
|
-
for (const state of states) {
|
|
2634
|
-
const proofs = proveGoal(goal1, state.subst, depth, visited);
|
|
2635
|
-
for (const pr of proofs) next.push({ subst: pr.subst, proofs: state.proofs.concat([pr.proof]) });
|
|
2636
|
-
}
|
|
2637
|
-
if (!next.length) return null;
|
|
2638
|
-
// Keep proof reconstruction deterministic and cheap: one proof is enough
|
|
2639
|
-
// for an explanation of the already-derived enclosing fact.
|
|
2640
|
-
states = [next[0]];
|
|
2641
|
-
}
|
|
2642
|
-
return states[0] || { subst, proofs: [] };
|
|
2643
|
-
}
|
|
2644
|
-
|
|
2645
|
-
const found = proveGoal(goal, __emptySubst(), 0, new Set());
|
|
2646
|
-
return found.length ? found[0].proof : null;
|
|
2647
|
-
}
|
|
2648
|
-
|
|
2649
2439
|
// Strategy: when the substitution is "large" or search depth is high,
|
|
2650
2440
|
// keep only bindings that are still relevant to:
|
|
2651
2441
|
// - variables appearing in the remaining goals
|
|
@@ -3367,6 +3157,120 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
|
|
|
3367
3157
|
return results;
|
|
3368
3158
|
}
|
|
3369
3159
|
|
|
3160
|
+
|
|
3161
|
+
// Reconstruct one backward proof for N3 proof output. This is intentionally
|
|
3162
|
+
// called only by renderProofDocument(), so normal reasoning does not pay for it.
|
|
3163
|
+
function __proofTripleKey(tr) {
|
|
3164
|
+
if (!tr) return '';
|
|
3165
|
+
return `${skolemKeyFromTerm(tr.s)}\t${skolemKeyFromTerm(tr.p)}\t${skolemKeyFromTerm(tr.o)}`;
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
function __copyProofSource(from, to) {
|
|
3169
|
+
if (from && to && Object.prototype.hasOwnProperty.call(from, '__source')) {
|
|
3170
|
+
Object.defineProperty(to, '__source', {
|
|
3171
|
+
value: from.__source,
|
|
3172
|
+
enumerable: false,
|
|
3173
|
+
writable: false,
|
|
3174
|
+
configurable: true,
|
|
3175
|
+
});
|
|
3176
|
+
}
|
|
3177
|
+
return to;
|
|
3178
|
+
}
|
|
3179
|
+
|
|
3180
|
+
function __annotateProofVarSourceNames(rule) {
|
|
3181
|
+
if (!rule) return rule;
|
|
3182
|
+
const sourceNames = Object.create(null);
|
|
3183
|
+
for (const name of varsInRule(rule)) {
|
|
3184
|
+
const m = /^(.*)__\d+$/.exec(name);
|
|
3185
|
+
sourceNames[name] = m ? m[1] : name;
|
|
3186
|
+
}
|
|
3187
|
+
Object.defineProperty(rule, '__proofVarSourceNames', {
|
|
3188
|
+
value: sourceNames,
|
|
3189
|
+
enumerable: false,
|
|
3190
|
+
writable: false,
|
|
3191
|
+
configurable: true,
|
|
3192
|
+
});
|
|
3193
|
+
return rule;
|
|
3194
|
+
}
|
|
3195
|
+
|
|
3196
|
+
function findBackwardProofForGoal(goal, facts, backRules, opts = {}) {
|
|
3197
|
+
const maxDepth = Number.isInteger(opts.maxDepth) && opts.maxDepth >= 0 ? opts.maxDepth : 64;
|
|
3198
|
+
const varGen = Array.isArray(opts.varGen) ? opts.varGen : [1];
|
|
3199
|
+
const visited = opts.visited instanceof Set ? opts.visited : new Set();
|
|
3200
|
+
const factList = Array.isArray(facts) ? facts : [];
|
|
3201
|
+
const ruleList = Array.isArray(backRules) ? backRules : [];
|
|
3202
|
+
|
|
3203
|
+
function matchingBaseFact(g) {
|
|
3204
|
+
const candidates = g && g.p instanceof Iri ? candidateFacts(factList, g) : null;
|
|
3205
|
+
const total = candidates ? candidates.totalLen : factList.length;
|
|
3206
|
+
for (let i = 0; i < total; i++) {
|
|
3207
|
+
let f;
|
|
3208
|
+
if (candidates) {
|
|
3209
|
+
if (i < candidates.exactLen) f = factList[candidates.exact[i]];
|
|
3210
|
+
else f = factList[candidates.wild[i - candidates.exactLen]];
|
|
3211
|
+
} else {
|
|
3212
|
+
f = factList[i];
|
|
3213
|
+
}
|
|
3214
|
+
if (unifyTriple(g, f, __emptySubst()) !== null) return f;
|
|
3215
|
+
}
|
|
3216
|
+
return null;
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3219
|
+
function builtinProof(g) {
|
|
3220
|
+
if (!(g && isBuiltinPred(g.p))) return null;
|
|
3221
|
+
return { kind: 'builtin', fact: g, builtin: g.p };
|
|
3222
|
+
}
|
|
3223
|
+
|
|
3224
|
+
function solve(g, depth) {
|
|
3225
|
+
if (!g || depth > maxDepth) return null;
|
|
3226
|
+
|
|
3227
|
+
const base = matchingBaseFact(g);
|
|
3228
|
+
if (base) return { kind: 'fact', fact: base, source: base.__source };
|
|
3229
|
+
|
|
3230
|
+
const builtin = builtinProof(g);
|
|
3231
|
+
if (builtin) return builtin;
|
|
3232
|
+
|
|
3233
|
+
if (!(g.p instanceof Iri)) return null;
|
|
3234
|
+
const gKey = __proofTripleKey(g);
|
|
3235
|
+
if (visited.has(gKey)) return null;
|
|
3236
|
+
visited.add(gKey);
|
|
3237
|
+
|
|
3238
|
+
try {
|
|
3239
|
+
ensureBackRuleIndexes(ruleList);
|
|
3240
|
+
const candRules = (ruleList.__byHeadPred.get(g.p.__tid) || []).concat(ruleList.__wildHeadPred);
|
|
3241
|
+
for (const r of candRules) {
|
|
3242
|
+
if (!r || !Array.isArray(r.conclusion) || r.conclusion.length !== 1) continue;
|
|
3243
|
+
const rawHead = r.conclusion[0];
|
|
3244
|
+
if (rawHead.p instanceof Iri && rawHead.p.__tid !== g.p.__tid) continue;
|
|
3245
|
+
|
|
3246
|
+
const rStd = __annotateProofVarSourceNames(__copyProofSource(r, standardizeRule(r, varGen)));
|
|
3247
|
+
const head = rStd.conclusion[0];
|
|
3248
|
+
const s0 = unifyTriple(head, g, __emptySubst());
|
|
3249
|
+
if (s0 === null) continue;
|
|
3250
|
+
|
|
3251
|
+
const solutions = proveGoals(rStd.premise, s0, factList, ruleList, depth + 1, [], varGen, 1, {
|
|
3252
|
+
keepVars: varsInRule(rStd),
|
|
3253
|
+
deferBuiltins: true,
|
|
3254
|
+
});
|
|
3255
|
+
if (!solutions.length) continue;
|
|
3256
|
+
|
|
3257
|
+
const subst = solutions[0];
|
|
3258
|
+
const fact = applySubstTriple(head, subst);
|
|
3259
|
+
const premises = rStd.premise.map((prem) => applySubstTriple(prem, subst));
|
|
3260
|
+
const df = new DerivedFact(fact, rStd, premises, __cloneSubst(subst));
|
|
3261
|
+
const children = premises.map((prem) => builtinProof(prem) || solve(prem, depth + 1));
|
|
3262
|
+
return { kind: 'rule', df, children };
|
|
3263
|
+
}
|
|
3264
|
+
} finally {
|
|
3265
|
+
visited.delete(gKey);
|
|
3266
|
+
}
|
|
3267
|
+
|
|
3268
|
+
return null;
|
|
3269
|
+
}
|
|
3270
|
+
|
|
3271
|
+
return solve(goal, 0);
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3370
3274
|
// ===========================================================================
|
|
3371
3275
|
// Forward chaining to fixpoint
|
|
3372
3276
|
// ===========================================================================
|
|
@@ -4048,12 +3952,22 @@ function reasonStream(input, opts = {}) {
|
|
|
4048
3952
|
skipUnsupportedRdfJs = false,
|
|
4049
3953
|
builtinModules = null,
|
|
4050
3954
|
rdf = false,
|
|
3955
|
+
sourceLabel = '<input>',
|
|
4051
3956
|
} = opts;
|
|
4052
3957
|
|
|
4053
3958
|
const useRdfCompatibility = !!rdf;
|
|
4054
3959
|
|
|
4055
|
-
const parsedSourceList = parseN3SourceList(input, { baseIri, rdf: useRdfCompatibility });
|
|
4056
|
-
const
|
|
3960
|
+
const parsedSourceList = parseN3SourceList(input, { baseIri, rdf: useRdfCompatibility, sourceLocations: proof });
|
|
3961
|
+
const parsedTextInput = (!parsedSourceList && proof && typeof input === 'string')
|
|
3962
|
+
? parseN3Text(input, {
|
|
3963
|
+
baseIri: baseIri || '',
|
|
3964
|
+
label: sourceLabel || '<input>',
|
|
3965
|
+
keepSourceArtifacts: false,
|
|
3966
|
+
sourceLocations: true,
|
|
3967
|
+
rdf: useRdfCompatibility,
|
|
3968
|
+
})
|
|
3969
|
+
: null;
|
|
3970
|
+
const parsedInput = parsedSourceList || parsedTextInput || normalizeParsedReasonerInputSync(input);
|
|
4057
3971
|
const rdfFactory = rdfjs ? getDataFactory(dataFactory) : null;
|
|
4058
3972
|
|
|
4059
3973
|
const __oldEnforceHttps = deref.getEnforceHttpsEnabled();
|
|
@@ -4163,7 +4077,7 @@ function reasonStream(input, opts = {}) {
|
|
|
4163
4077
|
const closureN3 = proof
|
|
4164
4078
|
? renderProofDocument(
|
|
4165
4079
|
Array.isArray(logQueryRules) && logQueryRules.length ? queryDerived : derived,
|
|
4166
|
-
derived,
|
|
4080
|
+
derived.concat(queryDerived || []),
|
|
4167
4081
|
triples,
|
|
4168
4082
|
prefixes,
|
|
4169
4083
|
brules,
|
|
@@ -4216,7 +4130,7 @@ function reasonRdfJs(input, opts = {}) {
|
|
|
4216
4130
|
|
|
4217
4131
|
Promise.resolve().then(async () => {
|
|
4218
4132
|
try {
|
|
4219
|
-
const normalizedInput = parseN3SourceList(input, restOpts) || (await normalizeReasonerInputAsync(input));
|
|
4133
|
+
const normalizedInput = parseN3SourceList(input, { ...restOpts, sourceLocations: !!(restOpts.proof || restOpts.proofComments) }) || (await normalizeReasonerInputAsync(input));
|
|
4220
4134
|
reasonStream(normalizedInput, {
|
|
4221
4135
|
...restOpts,
|
|
4222
4136
|
rdfjs: false,
|
package/lib/explain.js
CHANGED
|
@@ -135,7 +135,6 @@ ${lineIndent(body, ' ')}
|
|
|
135
135
|
}`;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
|
|
139
138
|
function clonePrefixEnvWithProofVocabulary(prefixes) {
|
|
140
139
|
const map = { ...(prefixes && prefixes.map ? prefixes.map : {}) };
|
|
141
140
|
if (!map.pe) map.pe = PE_NS;
|
|
@@ -153,6 +152,11 @@ ${lineIndent(body, ' ')}
|
|
|
153
152
|
return source.label.replace(/\\/g, '/').split('/').pop() || source.label;
|
|
154
153
|
}
|
|
155
154
|
|
|
155
|
+
function sourceKeyForProof(source) {
|
|
156
|
+
if (!source) return '<unknown>';
|
|
157
|
+
return `${sourceLabelForProof(source)}:${Number.isInteger(source.line) ? source.line : ''}`;
|
|
158
|
+
}
|
|
159
|
+
|
|
156
160
|
function byBlankNode(kind, source) {
|
|
157
161
|
const src = source || {};
|
|
158
162
|
const props = [`pe:${kind} ${n3String(sourceLabelForProof(src))}`];
|
|
@@ -160,121 +164,129 @@ ${lineIndent(body, ' ')}
|
|
|
160
164
|
return `[ ${props.join('; ')} ]`;
|
|
161
165
|
}
|
|
162
166
|
|
|
163
|
-
function
|
|
164
|
-
if (!df || !df.rule || !df.subst) return
|
|
167
|
+
function renderBindingItems(df, prefixes) {
|
|
168
|
+
if (!df || !df.rule || !df.subst) return [];
|
|
165
169
|
const ruleVars = varsInRule(df.rule);
|
|
166
170
|
const visibleNames = Object.keys(df.subst)
|
|
167
171
|
.filter((name) => ruleVars.has(name))
|
|
168
172
|
.sort();
|
|
169
|
-
if (!visibleNames.length) return
|
|
173
|
+
if (!visibleNames.length) return [];
|
|
170
174
|
const sourceNames = (df.rule && df.rule.__proofVarSourceNames) || null;
|
|
171
|
-
return visibleNames
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
})
|
|
177
|
-
.join(', ');
|
|
175
|
+
return visibleNames.map((name) => {
|
|
176
|
+
const value = applySubstTerm(new Var(name), df.subst);
|
|
177
|
+
const displayName = sourceNames && sourceNames[name] ? sourceNames[name] : name;
|
|
178
|
+
return `[ pe:var ${n3String(displayName)}; pe:value ${termToN3(value, prefixes)} ]`;
|
|
179
|
+
});
|
|
178
180
|
}
|
|
179
181
|
|
|
180
|
-
function
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
return `${label}\t${line}`;
|
|
182
|
+
function withLastLineSuffix(text, suffix) {
|
|
183
|
+
const lines = String(text).split(/\r?\n/);
|
|
184
|
+
lines[lines.length - 1] += suffix;
|
|
185
|
+
return lines.join('\n');
|
|
185
186
|
}
|
|
186
187
|
|
|
187
|
-
function
|
|
188
|
-
|
|
188
|
+
function renderPredicateObjects(predicate, objects, isLast) {
|
|
189
|
+
const values = (objects || []).filter((value) => value);
|
|
190
|
+
if (!values.length) return [];
|
|
191
|
+
const end = isLast ? '.' : ';';
|
|
192
|
+
if (values.length === 1 && !values[0].includes('\n')) {
|
|
193
|
+
return [` ${predicate} ${values[0]}${end}`];
|
|
194
|
+
}
|
|
195
|
+
const out = [` ${predicate}`];
|
|
196
|
+
for (let i = 0; i < values.length; i++) {
|
|
197
|
+
const suffix = i === values.length - 1 ? end : ',';
|
|
198
|
+
out.push(lineIndent(withLastLineSuffix(values[i], suffix), ' '));
|
|
199
|
+
}
|
|
200
|
+
return out;
|
|
189
201
|
}
|
|
190
202
|
|
|
191
|
-
function
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
203
|
+
function proofEntryKey(entry) {
|
|
204
|
+
if (!entry) return '';
|
|
205
|
+
if (entry.kind === 'rule') {
|
|
206
|
+
const df = entry.df;
|
|
207
|
+
const source = df && df.rule && df.rule.__source;
|
|
208
|
+
const premiseKey = (df && df.premises ? df.premises : []).map(proofTripleKey).join('|');
|
|
209
|
+
return `rule:${proofTripleKey(df && df.fact)}:${sourceKeyForProof(source)}:${premiseKey}`;
|
|
210
|
+
}
|
|
211
|
+
if (entry.kind === 'builtin') return `builtin:${proofTripleKey(entry.fact)}`;
|
|
212
|
+
return `fact:${proofTripleKey(entry.fact)}:${sourceKeyForProof(entry.source)}`;
|
|
195
213
|
}
|
|
196
214
|
|
|
197
215
|
function collectProofEntries(rootDf, derivedByKey, baseFactByKey, resolveBackwardProof) {
|
|
198
216
|
const entries = [];
|
|
199
217
|
const seen = new Set();
|
|
200
218
|
|
|
201
|
-
function
|
|
202
|
-
|
|
203
|
-
if (
|
|
204
|
-
|
|
205
|
-
|
|
219
|
+
function remember(entry) {
|
|
220
|
+
const key = proofEntryKey(entry);
|
|
221
|
+
if (!key || seen.has(key)) return false;
|
|
222
|
+
seen.add(key);
|
|
223
|
+
entries.push(entry);
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function derivedCandidatesForKey(key) {
|
|
228
|
+
const found = derivedByKey.get(key);
|
|
229
|
+
if (!found) return [];
|
|
230
|
+
return Array.isArray(found) ? found : [found];
|
|
206
231
|
}
|
|
207
232
|
|
|
208
|
-
function visitProofNode(proof, fallbackTriple) {
|
|
233
|
+
function visitProofNode(proof, fallbackTriple, parentDf) {
|
|
209
234
|
if (!proof) {
|
|
210
|
-
visitFactTriple(fallbackTriple);
|
|
235
|
+
visitFactTriple(fallbackTriple, parentDf);
|
|
211
236
|
return;
|
|
212
237
|
}
|
|
213
|
-
const key = entryKeyForProof(proof, fallbackTriple);
|
|
214
|
-
if (!key || seen.has(key)) return;
|
|
215
238
|
if (proof.kind === 'rule' && proof.df) {
|
|
216
239
|
visitDerivedFact(proof.df, proof.children || null);
|
|
217
240
|
return;
|
|
218
241
|
}
|
|
219
|
-
seen.add(key);
|
|
220
242
|
if (proof.kind === 'builtin') {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
fact: proof.fact || fallbackTriple,
|
|
224
|
-
builtin: proof.builtin || (proof.fact && proof.fact.p),
|
|
225
|
-
});
|
|
226
|
-
} else {
|
|
227
|
-
entries.push({ kind: 'fact', fact: proof.fact || fallbackTriple, source: proof.source });
|
|
243
|
+
remember({ kind: 'builtin', fact: proof.fact || fallbackTriple, builtin: proof.builtin || (proof.fact && proof.fact.p) });
|
|
244
|
+
return;
|
|
228
245
|
}
|
|
246
|
+
remember({ kind: 'fact', fact: proof.fact || fallbackTriple, source: proof.source });
|
|
229
247
|
}
|
|
230
248
|
|
|
231
|
-
function visitFactTriple(tr) {
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
visitDerivedFact(df);
|
|
249
|
+
function visitFactTriple(tr, parentDf) {
|
|
250
|
+
const key = proofTripleKey(tr);
|
|
251
|
+
const base = baseFactByKey.get(key) || null;
|
|
252
|
+
if (base) {
|
|
253
|
+
remember({ kind: 'fact', fact: base, source: base.__source });
|
|
237
254
|
return;
|
|
238
255
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
entries.push({ kind: 'fact', fact: base, source: base.__source });
|
|
256
|
+
|
|
257
|
+
const candidates = derivedCandidatesForKey(key);
|
|
258
|
+
const df = candidates.find((candidate) => candidate !== parentDf) || null;
|
|
259
|
+
if (df) {
|
|
260
|
+
visitDerivedFact(df);
|
|
245
261
|
return;
|
|
246
262
|
}
|
|
263
|
+
|
|
247
264
|
if (tr && isBuiltinPred(tr.p)) {
|
|
248
|
-
|
|
249
|
-
if (seen.has(key)) return;
|
|
250
|
-
seen.add(key);
|
|
251
|
-
entries.push({ kind: 'builtin', fact: tr, builtin: tr.p });
|
|
265
|
+
remember({ kind: 'builtin', fact: tr, builtin: tr.p });
|
|
252
266
|
return;
|
|
253
267
|
}
|
|
268
|
+
|
|
254
269
|
if (resolveBackwardProof) {
|
|
255
270
|
const proof = resolveBackwardProof(tr);
|
|
256
271
|
if (proof) {
|
|
257
|
-
visitProofNode(proof, tr);
|
|
272
|
+
visitProofNode(proof, tr, parentDf);
|
|
258
273
|
return;
|
|
259
274
|
}
|
|
260
275
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
seen.add(key);
|
|
264
|
-
entries.push({ kind: 'fact', fact: tr, source: null });
|
|
276
|
+
|
|
277
|
+
remember({ kind: 'fact', fact: tr, source: null });
|
|
265
278
|
}
|
|
266
279
|
|
|
267
280
|
function visitDerivedFact(df, children) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
entries.push({ kind: 'rule', df });
|
|
281
|
+
if (!df || !df.fact) return;
|
|
282
|
+
const entry = { kind: 'rule', df };
|
|
283
|
+
if (!remember(entry)) return;
|
|
272
284
|
|
|
273
285
|
if (Array.isArray(children) && children.length) {
|
|
274
|
-
for (const child of children) visitProofNode(child);
|
|
286
|
+
for (const child of children) visitProofNode(child, null, df);
|
|
275
287
|
return;
|
|
276
288
|
}
|
|
277
|
-
for (const prem of df.premises || []) visitFactTriple(prem);
|
|
289
|
+
for (const prem of df.premises || []) visitFactTriple(prem, df);
|
|
278
290
|
}
|
|
279
291
|
|
|
280
292
|
visitDerivedFact(rootDf);
|
|
@@ -304,16 +316,18 @@ ${lineIndent(body, ' ')}
|
|
|
304
316
|
}
|
|
305
317
|
|
|
306
318
|
const df = entry.df;
|
|
307
|
-
const
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
319
|
+
const bindingItems = renderBindingItems(df, prefixes);
|
|
320
|
+
const useItems = (df.premises || []).map((prem) => graphForTriple(prem, prefixes));
|
|
321
|
+
const propertyGroups = [
|
|
322
|
+
{ predicate: 'pe:by', objects: [byBlankNode('rule', df.rule && df.rule.__source)] },
|
|
323
|
+
{ predicate: 'pe:binding', objects: bindingItems },
|
|
324
|
+
{ predicate: 'pe:uses', objects: useItems },
|
|
325
|
+
].filter((group) => group.objects.length);
|
|
313
326
|
|
|
314
327
|
let out = ` ${graphForTriple(df.fact, prefixes)}\n`;
|
|
315
|
-
for (let i = 0; i <
|
|
316
|
-
|
|
328
|
+
for (let i = 0; i < propertyGroups.length; i++) {
|
|
329
|
+
const group = propertyGroups[i];
|
|
330
|
+
out += renderPredicateObjects(group.predicate, group.objects, i === propertyGroups.length - 1).join('\n') + '\n';
|
|
317
331
|
}
|
|
318
332
|
return out.trimEnd();
|
|
319
333
|
}
|
|
@@ -334,13 +348,18 @@ ${proofBody}
|
|
|
334
348
|
|
|
335
349
|
const proofPrefixes = clonePrefixEnvWithProofVocabulary(prefixes);
|
|
336
350
|
const derivedByKey = new Map();
|
|
337
|
-
|
|
338
|
-
if (!df || !df.fact)
|
|
351
|
+
function addDerived(df) {
|
|
352
|
+
if (!df || !df.fact) return;
|
|
339
353
|
const key = proofTripleKey(df.fact);
|
|
340
|
-
|
|
354
|
+
let bucket = derivedByKey.get(key);
|
|
355
|
+
if (!bucket) {
|
|
356
|
+
bucket = [];
|
|
357
|
+
derivedByKey.set(key, bucket);
|
|
358
|
+
}
|
|
359
|
+
if (!bucket.includes(df)) bucket.push(df);
|
|
341
360
|
}
|
|
342
|
-
|
|
343
|
-
|
|
361
|
+
for (const df of allDerived || []) addDerived(df);
|
|
362
|
+
for (const df of selectedDerived) addDerived(df);
|
|
344
363
|
|
|
345
364
|
const baseFactByKey = new Map();
|
|
346
365
|
for (const tr of baseFacts || []) {
|