eyeling 1.26.0 → 1.26.1

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.
@@ -4826,8 +4826,8 @@ function main() {
4826
4826
  baseIri: __sourceLabelToBaseIri(sourceLabel),
4827
4827
  label: sourceLabel,
4828
4828
  collectUsedPrefixes: streamMode,
4829
- collectSourceLocations: engine.getProofCommentsEnabled(),
4830
4829
  keepSourceArtifacts: false,
4830
+ sourceLocations: engine.getProofCommentsEnabled(),
4831
4831
  rdf: rdfMode,
4832
4832
  }),
4833
4833
  );
@@ -4983,7 +4983,7 @@ function main() {
4983
4983
  }
4984
4984
 
4985
4985
  if (engine.getProofCommentsEnabled()) {
4986
- process.stdout.write(engine.renderProofDocument(outDerived, derived, triples, prefixes, brules));
4986
+ process.stdout.write(engine.renderProofDocument(outDerived, derived.concat(outDerived || []), triples, prefixes, brules));
4987
4987
  return;
4988
4988
  }
4989
4989
 
@@ -5520,6 +5520,7 @@ const {
5520
5520
  Triple,
5521
5521
  Rule,
5522
5522
  DerivedFact,
5523
+ varsInRule,
5523
5524
  internIri,
5524
5525
  collectBlankLabelsInTriples,
5525
5526
  copyQuotedGraphMetadata,
@@ -5537,7 +5538,7 @@ const EMPTY_LIST_TERM = new ListTerm([]);
5537
5538
  const { lex, N3SyntaxError } = require('./lexer');
5538
5539
  const { Parser } = require('./parser');
5539
5540
  const { liftBlankRuleVars } = require('./rules');
5540
- const { parseN3SourceList } = require('./multisource');
5541
+ const { parseN3Text, parseN3SourceList } = require('./multisource');
5541
5542
 
5542
5543
  const {
5543
5544
  makeBuiltins,
@@ -7930,217 +7931,6 @@ function unifyTriple(pat, fact, subst) {
7930
7931
  return s3;
7931
7932
  }
7932
7933
 
7933
-
7934
- function __copyProofSourceMetadata(from, to) {
7935
- if (!from || !to) return to;
7936
- for (const key of ['__sourceOffset', '__source']) {
7937
- if (hasOwn.call(from, key)) {
7938
- Object.defineProperty(to, key, {
7939
- value: from[key],
7940
- enumerable: false,
7941
- writable: false,
7942
- configurable: true,
7943
- });
7944
- }
7945
- }
7946
- return to;
7947
- }
7948
-
7949
- function __standardizeRuleForProof(rule, gen) {
7950
- const proofVarNames = Object.create(null);
7951
-
7952
- function renameTerm(t, vmapName, vmapVar, genArr) {
7953
- if (t instanceof Var) {
7954
- if (!hasOwn.call(vmapVar, t.name)) {
7955
- const name = `${t.name}__proof_${genArr[0]}`;
7956
- genArr[0] += 1;
7957
- vmapName[t.name] = name;
7958
- vmapVar[t.name] = new Var(name);
7959
- proofVarNames[name] = t.name;
7960
- }
7961
- return vmapVar[t.name];
7962
- }
7963
- if (t instanceof ListTerm) {
7964
- let changed = false;
7965
- const elems2 = t.elems.map((e) => {
7966
- const e2 = renameTerm(e, vmapName, vmapVar, genArr);
7967
- if (e2 !== e) changed = true;
7968
- return e2;
7969
- });
7970
- return changed ? new ListTerm(elems2) : t;
7971
- }
7972
- if (t instanceof OpenListTerm) {
7973
- let changed = false;
7974
- const newXs = t.prefix.map((e) => {
7975
- const e2 = renameTerm(e, vmapName, vmapVar, genArr);
7976
- if (e2 !== e) changed = true;
7977
- return e2;
7978
- });
7979
- if (!hasOwn.call(vmapName, t.tailVar)) {
7980
- const name = `${t.tailVar}__proof_${genArr[0]}`;
7981
- genArr[0] += 1;
7982
- vmapName[t.tailVar] = name;
7983
- vmapVar[t.tailVar] = new Var(name);
7984
- proofVarNames[name] = t.tailVar;
7985
- }
7986
- const newTail = vmapName[t.tailVar];
7987
- if (newTail !== t.tailVar) changed = true;
7988
- return changed ? new OpenListTerm(newXs, newTail) : t;
7989
- }
7990
- if (t instanceof GraphTerm) {
7991
- let changed = false;
7992
- const triples2 = t.triples.map((tr) => {
7993
- const s2 = renameTerm(tr.s, vmapName, vmapVar, genArr);
7994
- const p2 = renameTerm(tr.p, vmapName, vmapVar, genArr);
7995
- const o2 = renameTerm(tr.o, vmapName, vmapVar, genArr);
7996
- if (s2 !== tr.s || p2 !== tr.p || o2 !== tr.o) changed = true;
7997
- return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
7998
- });
7999
- return changed ? copyQuotedGraphMetadata(t, new GraphTerm(triples2)) : t;
8000
- }
8001
- return t;
8002
- }
8003
-
8004
- const vmapName = Object.create(null);
8005
- const vmapVar = Object.create(null);
8006
- const premise = rule.premise.map((tr) => {
8007
- const s2 = renameTerm(tr.s, vmapName, vmapVar, gen);
8008
- const p2 = renameTerm(tr.p, vmapName, vmapVar, gen);
8009
- const o2 = renameTerm(tr.o, vmapName, vmapVar, gen);
8010
- return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
8011
- });
8012
- const conclusion = rule.conclusion.map((tr) => {
8013
- const s2 = renameTerm(tr.s, vmapName, vmapVar, gen);
8014
- const p2 = renameTerm(tr.p, vmapName, vmapVar, gen);
8015
- const o2 = renameTerm(tr.o, vmapName, vmapVar, gen);
8016
- return s2 === tr.s && p2 === tr.p && o2 === tr.o ? tr : new Triple(s2, p2, o2);
8017
- });
8018
- const out = new Rule(premise, conclusion, rule.isForward, rule.isFuse, rule.headBlankLabels);
8019
- Object.defineProperty(out, '__proofVarSourceNames', {
8020
- value: proofVarNames,
8021
- enumerable: false,
8022
- writable: false,
8023
- configurable: true,
8024
- });
8025
- return __copyProofSourceMetadata(rule, out);
8026
- }
8027
-
8028
- function __mergeProofSubst(base, delta) {
8029
- const out = __cloneSubst(base);
8030
- for (const k in delta || {}) {
8031
- if (!hasOwn.call(delta, k)) continue;
8032
- const v = applySubstTerm(delta[k], out);
8033
- if (hasOwn.call(out, k)) {
8034
- if (!termsEqual(applySubstTerm(out[k], out), v)) return null;
8035
- } else {
8036
- out[k] = v;
8037
- }
8038
- }
8039
- return out;
8040
- }
8041
-
8042
- function __sourceSortKey(obj) {
8043
- const src = obj && obj.__source;
8044
- const label = src && typeof src.label === 'string' ? src.label : '';
8045
- const line = src && Number.isInteger(src.line) ? src.line : 0;
8046
- return `${label}\t${line}`;
8047
- }
8048
-
8049
- function __candidateFactsForProof(facts, goal) {
8050
- if (!Array.isArray(facts)) return [];
8051
- ensureFactIndexes(facts);
8052
- const candidates = candidateFacts(facts, goal);
8053
- if (!candidates) return facts;
8054
- const out = [];
8055
- for (let i = 0; i < candidates.exactLen; i++) out.push(facts[candidates.exact[i]]);
8056
- for (let i = 0; i < candidates.wildLen; i++) out.push(facts[candidates.wild[i]]);
8057
- return out;
8058
- }
8059
-
8060
- function findBackwardProofForGoal(goal, facts, backRules, opts = {}) {
8061
- const maxDepth = Number.isInteger(opts.maxDepth) && opts.maxDepth > 0 ? opts.maxDepth : 64;
8062
- const varGen = [0];
8063
-
8064
- function proofKey(tr) {
8065
- return `${skolemKeyFromTerm(tr.s)}\t${skolemKeyFromTerm(tr.p)}\t${skolemKeyFromTerm(tr.o)}`;
8066
- }
8067
-
8068
- function proveGoal(goal0, subst, depth, visited) {
8069
- if (depth > maxDepth) return [];
8070
- const g = applySubstTriple(goal0, subst);
8071
- const key = proofKey(g);
8072
- if (visited.has(key)) return [];
8073
-
8074
- if (isBuiltinPred(g.p)) {
8075
- const builtinGoalForEval = g.p instanceof Iri && g.p.value === LOG_NS + 'rawType' ? goal0 : g;
8076
- const builtinSubstForEval = g.p instanceof Iri && g.p.value === LOG_NS + 'rawType' ? subst : __emptySubst();
8077
- const deltas = evalBuiltin(builtinGoalForEval, builtinSubstForEval, facts, backRules, depth, varGen, 1) || [];
8078
- const out = [];
8079
- for (const delta of deltas) {
8080
- const subst2 = __mergeProofSubst(subst, delta);
8081
- if (subst2 === null) continue;
8082
- const fact = applySubstTriple(g, subst2);
8083
- out.push({ subst: subst2, proof: { kind: 'builtin', fact, builtin: fact.p } });
8084
- break;
8085
- }
8086
- return out;
8087
- }
8088
-
8089
- const factResults = [];
8090
- for (const f of __candidateFactsForProof(facts, g)) {
8091
- const subst2 = unifyTriple(g, f, subst);
8092
- if (subst2 === null) continue;
8093
- factResults.push({ subst: subst2, proof: { kind: 'fact', fact: applySubstTriple(f, subst2), source: f.__source } });
8094
- break;
8095
- }
8096
- if (factResults.length) return factResults;
8097
-
8098
- if (!(g.p instanceof Iri)) return [];
8099
- ensureBackRuleIndexes(backRules);
8100
- const rules = (backRules.__byHeadPred.get(g.p.__tid) || []).concat(backRules.__wildHeadPred || []);
8101
- rules.sort((a, b) => (__sourceSortKey(a) < __sourceSortKey(b) ? -1 : __sourceSortKey(a) > __sourceSortKey(b) ? 1 : 0));
8102
-
8103
- const visited2 = new Set(visited);
8104
- visited2.add(key);
8105
-
8106
- for (const r of rules) {
8107
- if (!r || !Array.isArray(r.conclusion) || r.conclusion.length !== 1) continue;
8108
- const rStd = __standardizeRuleForProof(r, varGen);
8109
- const head = rStd.conclusion[0];
8110
- if (head.p instanceof Iri && head.p.__tid !== g.p.__tid) continue;
8111
- const subst1 = unifyTriple(head, g, subst);
8112
- if (subst1 === null) continue;
8113
- const bodyProof = proveGoalList(rStd.premise || [], subst1, depth + 1, visited2);
8114
- if (!bodyProof) continue;
8115
- const fact = applySubstTriple(g, bodyProof.subst);
8116
- const premises = (rStd.premise || []).map((prem) => applySubstTriple(prem, bodyProof.subst));
8117
- const df = new DerivedFact(fact, rStd, premises, bodyProof.subst);
8118
- return [{ subst: bodyProof.subst, proof: { kind: 'rule', df, children: bodyProof.proofs } }];
8119
- }
8120
-
8121
- return [];
8122
- }
8123
-
8124
- function proveGoalList(goals, subst, depth, visited) {
8125
- let states = [{ subst, proofs: [] }];
8126
- for (const goal1 of goals || []) {
8127
- const next = [];
8128
- for (const state of states) {
8129
- const proofs = proveGoal(goal1, state.subst, depth, visited);
8130
- for (const pr of proofs) next.push({ subst: pr.subst, proofs: state.proofs.concat([pr.proof]) });
8131
- }
8132
- if (!next.length) return null;
8133
- // Keep proof reconstruction deterministic and cheap: one proof is enough
8134
- // for an explanation of the already-derived enclosing fact.
8135
- states = [next[0]];
8136
- }
8137
- return states[0] || { subst, proofs: [] };
8138
- }
8139
-
8140
- const found = proveGoal(goal, __emptySubst(), 0, new Set());
8141
- return found.length ? found[0].proof : null;
8142
- }
8143
-
8144
7934
  // Strategy: when the substitution is "large" or search depth is high,
8145
7935
  // keep only bindings that are still relevant to:
8146
7936
  // - variables appearing in the remaining goals
@@ -8862,6 +8652,120 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
8862
8652
  return results;
8863
8653
  }
8864
8654
 
8655
+
8656
+ // Reconstruct one backward proof for N3 proof output. This is intentionally
8657
+ // called only by renderProofDocument(), so normal reasoning does not pay for it.
8658
+ function __proofTripleKey(tr) {
8659
+ if (!tr) return '';
8660
+ return `${skolemKeyFromTerm(tr.s)}\t${skolemKeyFromTerm(tr.p)}\t${skolemKeyFromTerm(tr.o)}`;
8661
+ }
8662
+
8663
+ function __copyProofSource(from, to) {
8664
+ if (from && to && Object.prototype.hasOwnProperty.call(from, '__source')) {
8665
+ Object.defineProperty(to, '__source', {
8666
+ value: from.__source,
8667
+ enumerable: false,
8668
+ writable: false,
8669
+ configurable: true,
8670
+ });
8671
+ }
8672
+ return to;
8673
+ }
8674
+
8675
+ function __annotateProofVarSourceNames(rule) {
8676
+ if (!rule) return rule;
8677
+ const sourceNames = Object.create(null);
8678
+ for (const name of varsInRule(rule)) {
8679
+ const m = /^(.*)__\d+$/.exec(name);
8680
+ sourceNames[name] = m ? m[1] : name;
8681
+ }
8682
+ Object.defineProperty(rule, '__proofVarSourceNames', {
8683
+ value: sourceNames,
8684
+ enumerable: false,
8685
+ writable: false,
8686
+ configurable: true,
8687
+ });
8688
+ return rule;
8689
+ }
8690
+
8691
+ function findBackwardProofForGoal(goal, facts, backRules, opts = {}) {
8692
+ const maxDepth = Number.isInteger(opts.maxDepth) && opts.maxDepth >= 0 ? opts.maxDepth : 64;
8693
+ const varGen = Array.isArray(opts.varGen) ? opts.varGen : [1];
8694
+ const visited = opts.visited instanceof Set ? opts.visited : new Set();
8695
+ const factList = Array.isArray(facts) ? facts : [];
8696
+ const ruleList = Array.isArray(backRules) ? backRules : [];
8697
+
8698
+ function matchingBaseFact(g) {
8699
+ const candidates = g && g.p instanceof Iri ? candidateFacts(factList, g) : null;
8700
+ const total = candidates ? candidates.totalLen : factList.length;
8701
+ for (let i = 0; i < total; i++) {
8702
+ let f;
8703
+ if (candidates) {
8704
+ if (i < candidates.exactLen) f = factList[candidates.exact[i]];
8705
+ else f = factList[candidates.wild[i - candidates.exactLen]];
8706
+ } else {
8707
+ f = factList[i];
8708
+ }
8709
+ if (unifyTriple(g, f, __emptySubst()) !== null) return f;
8710
+ }
8711
+ return null;
8712
+ }
8713
+
8714
+ function builtinProof(g) {
8715
+ if (!(g && isBuiltinPred(g.p))) return null;
8716
+ return { kind: 'builtin', fact: g, builtin: g.p };
8717
+ }
8718
+
8719
+ function solve(g, depth) {
8720
+ if (!g || depth > maxDepth) return null;
8721
+
8722
+ const base = matchingBaseFact(g);
8723
+ if (base) return { kind: 'fact', fact: base, source: base.__source };
8724
+
8725
+ const builtin = builtinProof(g);
8726
+ if (builtin) return builtin;
8727
+
8728
+ if (!(g.p instanceof Iri)) return null;
8729
+ const gKey = __proofTripleKey(g);
8730
+ if (visited.has(gKey)) return null;
8731
+ visited.add(gKey);
8732
+
8733
+ try {
8734
+ ensureBackRuleIndexes(ruleList);
8735
+ const candRules = (ruleList.__byHeadPred.get(g.p.__tid) || []).concat(ruleList.__wildHeadPred);
8736
+ for (const r of candRules) {
8737
+ if (!r || !Array.isArray(r.conclusion) || r.conclusion.length !== 1) continue;
8738
+ const rawHead = r.conclusion[0];
8739
+ if (rawHead.p instanceof Iri && rawHead.p.__tid !== g.p.__tid) continue;
8740
+
8741
+ const rStd = __annotateProofVarSourceNames(__copyProofSource(r, standardizeRule(r, varGen)));
8742
+ const head = rStd.conclusion[0];
8743
+ const s0 = unifyTriple(head, g, __emptySubst());
8744
+ if (s0 === null) continue;
8745
+
8746
+ const solutions = proveGoals(rStd.premise, s0, factList, ruleList, depth + 1, [], varGen, 1, {
8747
+ keepVars: varsInRule(rStd),
8748
+ deferBuiltins: true,
8749
+ });
8750
+ if (!solutions.length) continue;
8751
+
8752
+ const subst = solutions[0];
8753
+ const fact = applySubstTriple(head, subst);
8754
+ const premises = rStd.premise.map((prem) => applySubstTriple(prem, subst));
8755
+ const df = new DerivedFact(fact, rStd, premises, __cloneSubst(subst));
8756
+ const children = premises.map((prem) => builtinProof(prem) || solve(prem, depth + 1));
8757
+ return { kind: 'rule', df, children };
8758
+ }
8759
+ } finally {
8760
+ visited.delete(gKey);
8761
+ }
8762
+
8763
+ return null;
8764
+ }
8765
+
8766
+ return solve(goal, 0);
8767
+ }
8768
+
8865
8769
  // ===========================================================================
8866
8770
  // Forward chaining to fixpoint
8867
8771
  // ===========================================================================
@@ -9547,7 +9451,7 @@ function reasonStream(input, opts = {}) {
9547
9451
 
9548
9452
  const useRdfCompatibility = !!rdf;
9549
9453
 
9550
- const parsedSourceList = parseN3SourceList(input, { baseIri, rdf: useRdfCompatibility });
9454
+ const parsedSourceList = parseN3SourceList(input, { baseIri, rdf: useRdfCompatibility, sourceLocations: proof });
9551
9455
  const parsedInput = parsedSourceList || normalizeParsedReasonerInputSync(input);
9552
9456
  const rdfFactory = rdfjs ? getDataFactory(dataFactory) : null;
9553
9457
 
@@ -9583,11 +9487,22 @@ function reasonStream(input, opts = {}) {
9583
9487
  if (baseIri) prefixes.setBase(baseIri);
9584
9488
  } else {
9585
9489
  const n3Text = normalizeReasonerInputSync(input);
9586
- const toks = lex(n3Text, { rdf: useRdfCompatibility });
9587
- const parser = new Parser(toks);
9588
- if (baseIri) parser.prefixes.setBase(baseIri);
9490
+ if (proof) {
9491
+ const parsed = parseN3Text(n3Text, {
9492
+ label: 'input.n3',
9493
+ baseIri: baseIri || '',
9494
+ keepSourceArtifacts: false,
9495
+ sourceLocations: true,
9496
+ rdf: useRdfCompatibility,
9497
+ });
9498
+ ({ prefixes, triples, frules, brules, logQueryRules } = parsed);
9499
+ } else {
9500
+ const toks = lex(n3Text, { rdf: useRdfCompatibility });
9501
+ const parser = new Parser(toks);
9502
+ if (baseIri) parser.prefixes.setBase(baseIri);
9589
9503
 
9590
- [prefixes, triples, frules, brules, logQueryRules] = parser.parseDocument();
9504
+ [prefixes, triples, frules, brules, logQueryRules] = parser.parseDocument();
9505
+ }
9591
9506
  }
9592
9507
  // Make the parsed prefixes available to log:trace output
9593
9508
  trace.setTracePrefixes(prefixes);
@@ -9658,7 +9573,7 @@ function reasonStream(input, opts = {}) {
9658
9573
  const closureN3 = proof
9659
9574
  ? renderProofDocument(
9660
9575
  Array.isArray(logQueryRules) && logQueryRules.length ? queryDerived : derived,
9661
- derived,
9576
+ derived.concat(queryDerived || []),
9662
9577
  triples,
9663
9578
  prefixes,
9664
9579
  brules,
@@ -9711,7 +9626,7 @@ function reasonRdfJs(input, opts = {}) {
9711
9626
 
9712
9627
  Promise.resolve().then(async () => {
9713
9628
  try {
9714
- const normalizedInput = parseN3SourceList(input, restOpts) || (await normalizeReasonerInputAsync(input));
9629
+ const normalizedInput = parseN3SourceList(input, { ...restOpts, sourceLocations: !!(restOpts.proof || restOpts.proofComments) }) || (await normalizeReasonerInputAsync(input));
9715
9630
  reasonStream(normalizedInput, {
9716
9631
  ...restOpts,
9717
9632
  rdfjs: false,
@@ -10020,7 +9935,6 @@ ${lineIndent(body, ' ')}
10020
9935
  }`;
10021
9936
  }
10022
9937
 
10023
-
10024
9938
  function clonePrefixEnvWithProofVocabulary(prefixes) {
10025
9939
  const map = { ...(prefixes && prefixes.map ? prefixes.map : {}) };
10026
9940
  if (!map.pe) map.pe = PE_NS;
@@ -10038,6 +9952,11 @@ ${lineIndent(body, ' ')}
10038
9952
  return source.label.replace(/\\/g, '/').split('/').pop() || source.label;
10039
9953
  }
10040
9954
 
9955
+ function sourceKeyForProof(source) {
9956
+ if (!source) return '<unknown>';
9957
+ return `${sourceLabelForProof(source)}:${Number.isInteger(source.line) ? source.line : ''}`;
9958
+ }
9959
+
10041
9960
  function byBlankNode(kind, source) {
10042
9961
  const src = source || {};
10043
9962
  const props = [`pe:${kind} ${n3String(sourceLabelForProof(src))}`];
@@ -10045,121 +9964,129 @@ ${lineIndent(body, ' ')}
10045
9964
  return `[ ${props.join('; ')} ]`;
10046
9965
  }
10047
9966
 
10048
- function renderBindingList(df, prefixes) {
10049
- if (!df || !df.rule || !df.subst) return '';
9967
+ function renderBindingItems(df, prefixes) {
9968
+ if (!df || !df.rule || !df.subst) return [];
10050
9969
  const ruleVars = varsInRule(df.rule);
10051
9970
  const visibleNames = Object.keys(df.subst)
10052
9971
  .filter((name) => ruleVars.has(name))
10053
9972
  .sort();
10054
- if (!visibleNames.length) return '';
9973
+ if (!visibleNames.length) return [];
10055
9974
  const sourceNames = (df.rule && df.rule.__proofVarSourceNames) || null;
10056
- return visibleNames
10057
- .map((name) => {
10058
- const value = applySubstTerm(new Var(name), df.subst);
10059
- const displayName = sourceNames && sourceNames[name] ? sourceNames[name] : name;
10060
- return `[ pe:var ${n3String(displayName)}; pe:value ${termToN3(value, prefixes)} ]`;
10061
- })
10062
- .join(', ');
9975
+ return visibleNames.map((name) => {
9976
+ const value = applySubstTerm(new Var(name), df.subst);
9977
+ const displayName = sourceNames && sourceNames[name] ? sourceNames[name] : name;
9978
+ return `[ pe:var ${n3String(displayName)}; pe:value ${termToN3(value, prefixes)} ]`;
9979
+ });
10063
9980
  }
10064
9981
 
10065
- function proofSourceKey(source) {
10066
- if (!source) return '';
10067
- const label = typeof source.label === 'string' ? source.label : '';
10068
- const line = Number.isInteger(source.line) ? source.line : 0;
10069
- return `${label}\t${line}`;
9982
+ function withLastLineSuffix(text, suffix) {
9983
+ const lines = String(text).split(/\r?\n/);
9984
+ lines[lines.length - 1] += suffix;
9985
+ return lines.join('\n');
10070
9986
  }
10071
9987
 
10072
- function proofEntryKey(kind, fact, source, extra) {
10073
- return `${kind}\t${proofTripleKey(fact)}\t${proofSourceKey(source)}\t${extra || ''}`;
9988
+ function renderPredicateObjects(predicate, objects, isLast) {
9989
+ const values = (objects || []).filter((value) => value);
9990
+ if (!values.length) return [];
9991
+ const end = isLast ? '.' : ';';
9992
+ if (values.length === 1 && !values[0].includes('\n')) {
9993
+ return [` ${predicate} ${values[0]}${end}`];
9994
+ }
9995
+ const out = [` ${predicate}`];
9996
+ for (let i = 0; i < values.length; i++) {
9997
+ const suffix = i === values.length - 1 ? end : ',';
9998
+ out.push(lineIndent(withLastLineSuffix(values[i], suffix), ' '));
9999
+ }
10000
+ return out;
10074
10001
  }
10075
10002
 
10076
- function ruleEntryKey(df) {
10077
- const rule = df && df.rule;
10078
- const premises = (df && df.premises ? df.premises : []).map((prem) => proofTripleKey(prem)).join('\t');
10079
- return proofEntryKey('rule', df && df.fact, rule && rule.__source, premises);
10003
+ function proofEntryKey(entry) {
10004
+ if (!entry) return '';
10005
+ if (entry.kind === 'rule') {
10006
+ const df = entry.df;
10007
+ const source = df && df.rule && df.rule.__source;
10008
+ const premiseKey = (df && df.premises ? df.premises : []).map(proofTripleKey).join('|');
10009
+ return `rule:${proofTripleKey(df && df.fact)}:${sourceKeyForProof(source)}:${premiseKey}`;
10010
+ }
10011
+ if (entry.kind === 'builtin') return `builtin:${proofTripleKey(entry.fact)}`;
10012
+ return `fact:${proofTripleKey(entry.fact)}:${sourceKeyForProof(entry.source)}`;
10080
10013
  }
10081
10014
 
10082
10015
  function collectProofEntries(rootDf, derivedByKey, baseFactByKey, resolveBackwardProof) {
10083
10016
  const entries = [];
10084
10017
  const seen = new Set();
10085
10018
 
10086
- function entryKeyForProof(proof, fallbackTriple) {
10087
- if (!proof) return proofEntryKey('fact', fallbackTriple, null);
10088
- if (proof.kind === 'rule') return ruleEntryKey(proof.df);
10089
- if (proof.kind === 'builtin') return proofEntryKey('builtin', proof.fact || fallbackTriple, null);
10090
- return proofEntryKey('fact', proof.fact || fallbackTriple, proof.source);
10019
+ function remember(entry) {
10020
+ const key = proofEntryKey(entry);
10021
+ if (!key || seen.has(key)) return false;
10022
+ seen.add(key);
10023
+ entries.push(entry);
10024
+ return true;
10025
+ }
10026
+
10027
+ function derivedCandidatesForKey(key) {
10028
+ const found = derivedByKey.get(key);
10029
+ if (!found) return [];
10030
+ return Array.isArray(found) ? found : [found];
10091
10031
  }
10092
10032
 
10093
- function visitProofNode(proof, fallbackTriple) {
10033
+ function visitProofNode(proof, fallbackTriple, parentDf) {
10094
10034
  if (!proof) {
10095
- visitFactTriple(fallbackTriple);
10035
+ visitFactTriple(fallbackTriple, parentDf);
10096
10036
  return;
10097
10037
  }
10098
- const key = entryKeyForProof(proof, fallbackTriple);
10099
- if (!key || seen.has(key)) return;
10100
10038
  if (proof.kind === 'rule' && proof.df) {
10101
10039
  visitDerivedFact(proof.df, proof.children || null);
10102
10040
  return;
10103
10041
  }
10104
- seen.add(key);
10105
10042
  if (proof.kind === 'builtin') {
10106
- entries.push({
10107
- kind: 'builtin',
10108
- fact: proof.fact || fallbackTriple,
10109
- builtin: proof.builtin || (proof.fact && proof.fact.p),
10110
- });
10111
- } else {
10112
- entries.push({ kind: 'fact', fact: proof.fact || fallbackTriple, source: proof.source });
10043
+ remember({ kind: 'builtin', fact: proof.fact || fallbackTriple, builtin: proof.builtin || (proof.fact && proof.fact.p) });
10044
+ return;
10113
10045
  }
10046
+ remember({ kind: 'fact', fact: proof.fact || fallbackTriple, source: proof.source });
10114
10047
  }
10115
10048
 
10116
- function visitFactTriple(tr) {
10117
- const tripleKey = proofTripleKey(tr);
10118
- if (!tripleKey) return;
10119
- const df = derivedByKey.get(tripleKey);
10120
- if (df) {
10121
- visitDerivedFact(df);
10049
+ function visitFactTriple(tr, parentDf) {
10050
+ const key = proofTripleKey(tr);
10051
+ const base = baseFactByKey.get(key) || null;
10052
+ if (base) {
10053
+ remember({ kind: 'fact', fact: base, source: base.__source });
10122
10054
  return;
10123
10055
  }
10124
- const base = baseFactByKey.get(tripleKey) || null;
10125
- if (base) {
10126
- const key = proofEntryKey('fact', base, base.__source);
10127
- if (seen.has(key)) return;
10128
- seen.add(key);
10129
- entries.push({ kind: 'fact', fact: base, source: base.__source });
10056
+
10057
+ const candidates = derivedCandidatesForKey(key);
10058
+ const df = candidates.find((candidate) => candidate !== parentDf) || null;
10059
+ if (df) {
10060
+ visitDerivedFact(df);
10130
10061
  return;
10131
10062
  }
10063
+
10132
10064
  if (tr && isBuiltinPred(tr.p)) {
10133
- const key = proofEntryKey('builtin', tr, null);
10134
- if (seen.has(key)) return;
10135
- seen.add(key);
10136
- entries.push({ kind: 'builtin', fact: tr, builtin: tr.p });
10065
+ remember({ kind: 'builtin', fact: tr, builtin: tr.p });
10137
10066
  return;
10138
10067
  }
10068
+
10139
10069
  if (resolveBackwardProof) {
10140
10070
  const proof = resolveBackwardProof(tr);
10141
10071
  if (proof) {
10142
- visitProofNode(proof, tr);
10072
+ visitProofNode(proof, tr, parentDf);
10143
10073
  return;
10144
10074
  }
10145
10075
  }
10146
- const key = proofEntryKey('fact', tr, null);
10147
- if (seen.has(key)) return;
10148
- seen.add(key);
10149
- entries.push({ kind: 'fact', fact: tr, source: null });
10076
+
10077
+ remember({ kind: 'fact', fact: tr, source: null });
10150
10078
  }
10151
10079
 
10152
10080
  function visitDerivedFact(df, children) {
10153
- const key = ruleEntryKey(df);
10154
- if (!key || seen.has(key)) return;
10155
- seen.add(key);
10156
- entries.push({ kind: 'rule', df });
10081
+ if (!df || !df.fact) return;
10082
+ const entry = { kind: 'rule', df };
10083
+ if (!remember(entry)) return;
10157
10084
 
10158
10085
  if (Array.isArray(children) && children.length) {
10159
- for (const child of children) visitProofNode(child);
10086
+ for (const child of children) visitProofNode(child, null, df);
10160
10087
  return;
10161
10088
  }
10162
- for (const prem of df.premises || []) visitFactTriple(prem);
10089
+ for (const prem of df.premises || []) visitFactTriple(prem, df);
10163
10090
  }
10164
10091
 
10165
10092
  visitDerivedFact(rootDf);
@@ -10189,16 +10116,18 @@ ${lineIndent(body, ' ')}
10189
10116
  }
10190
10117
 
10191
10118
  const df = entry.df;
10192
- const props = [`pe:by ${byBlankNode('rule', df.rule && df.rule.__source)}`];
10193
- const bindings = renderBindingList(df, prefixes);
10194
- if (bindings) props.push(`pe:binding ${bindings}`);
10195
- if (df.premises && df.premises.length) {
10196
- props.push(`pe:uses ${df.premises.map((prem) => graphForTriple(prem, prefixes)).join(', ')}`);
10197
- }
10119
+ const bindingItems = renderBindingItems(df, prefixes);
10120
+ const useItems = (df.premises || []).map((prem) => graphForTriple(prem, prefixes));
10121
+ const propertyGroups = [
10122
+ { predicate: 'pe:by', objects: [byBlankNode('rule', df.rule && df.rule.__source)] },
10123
+ { predicate: 'pe:binding', objects: bindingItems },
10124
+ { predicate: 'pe:uses', objects: useItems },
10125
+ ].filter((group) => group.objects.length);
10198
10126
 
10199
10127
  let out = ` ${graphForTriple(df.fact, prefixes)}\n`;
10200
- for (let i = 0; i < props.length; i++) {
10201
- out += ` ${props[i]}${i === props.length - 1 ? '.' : ';'}\n`;
10128
+ for (let i = 0; i < propertyGroups.length; i++) {
10129
+ const group = propertyGroups[i];
10130
+ out += renderPredicateObjects(group.predicate, group.objects, i === propertyGroups.length - 1).join('\n') + '\n';
10202
10131
  }
10203
10132
  return out.trimEnd();
10204
10133
  }
@@ -10219,13 +10148,18 @@ ${proofBody}
10219
10148
 
10220
10149
  const proofPrefixes = clonePrefixEnvWithProofVocabulary(prefixes);
10221
10150
  const derivedByKey = new Map();
10222
- for (const df of allDerived || []) {
10223
- if (!df || !df.fact) continue;
10151
+ function addDerived(df) {
10152
+ if (!df || !df.fact) return;
10224
10153
  const key = proofTripleKey(df.fact);
10225
- if (!derivedByKey.has(key)) derivedByKey.set(key, df);
10154
+ let bucket = derivedByKey.get(key);
10155
+ if (!bucket) {
10156
+ bucket = [];
10157
+ derivedByKey.set(key, bucket);
10158
+ }
10159
+ if (!bucket.includes(df)) bucket.push(df);
10226
10160
  }
10227
- // Do not insert query-wrapper output facts here: those wrappers often derive
10228
- // the same triple they use, and would hide the underlying fact/rule proof.
10161
+ for (const df of allDerived || []) addDerived(df);
10162
+ for (const df of selectedDerived) addDerived(df);
10229
10163
 
10230
10164
  const baseFactByKey = new Map();
10231
10165
  for (const tr of baseFacts || []) {
@@ -12173,20 +12107,20 @@ function emptyParsedDocument() {
12173
12107
  }
12174
12108
 
12175
12109
  function lineStartsForText(text) {
12176
- const s = String(text);
12177
12110
  const starts = [0];
12178
- for (let i = 0; i < s.length; i++) {
12179
- const c = s.charCodeAt(i);
12180
- if (c === 10) starts.push(i + 1); // LF
12181
- else if (c === 13) { // CR or CRLF
12182
- if (i + 1 < s.length && s.charCodeAt(i + 1) === 10) i++;
12183
- starts.push(i + 1);
12111
+ const str = String(text);
12112
+ for (let i = 0; i < str.length; i++) {
12113
+ const c = str[i];
12114
+ if (c === '\n') starts.push(i + 1);
12115
+ else if (c === '\r') {
12116
+ starts.push(i + 1 < str.length && str[i + 1] === '\n' ? i + 2 : i + 1);
12117
+ if (i + 1 < str.length && str[i + 1] === '\n') i++;
12184
12118
  }
12185
12119
  }
12186
12120
  return starts;
12187
12121
  }
12188
12122
 
12189
- function offsetToLineFromStarts(offset, lineStarts) {
12123
+ function offsetToLine(lineStarts, offset) {
12190
12124
  if (typeof offset !== 'number') return null;
12191
12125
  let lo = 0;
12192
12126
  let hi = lineStarts.length;
@@ -12198,9 +12132,9 @@ function offsetToLineFromStarts(offset, lineStarts) {
12198
12132
  return lo + 1;
12199
12133
  }
12200
12134
 
12201
- function annotateSourceLocation(obj, label, lineStarts) {
12135
+ function annotateSourceLocation(obj, lineStarts, label) {
12202
12136
  if (!obj || typeof obj.__sourceOffset !== 'number') return obj;
12203
- const line = offsetToLineFromStarts(obj.__sourceOffset, lineStarts);
12137
+ const line = offsetToLine(lineStarts, obj.__sourceOffset);
12204
12138
  Object.defineProperty(obj, '__source', {
12205
12139
  value: { label, line },
12206
12140
  enumerable: false,
@@ -12212,10 +12146,10 @@ function annotateSourceLocation(obj, label, lineStarts) {
12212
12146
 
12213
12147
  function annotateParsedSourceLocations(doc, text, label) {
12214
12148
  const lineStarts = lineStartsForText(text);
12215
- for (const tr of doc.triples || []) annotateSourceLocation(tr, label, lineStarts);
12216
- for (const r of doc.frules || []) annotateSourceLocation(r, label, lineStarts);
12217
- for (const r of doc.brules || []) annotateSourceLocation(r, label, lineStarts);
12218
- for (const r of doc.logQueryRules || []) annotateSourceLocation(r, label, lineStarts);
12149
+ for (const tr of doc.triples || []) annotateSourceLocation(tr, lineStarts, label);
12150
+ for (const r of doc.frules || []) annotateSourceLocation(r, lineStarts, label);
12151
+ for (const r of doc.brules || []) annotateSourceLocation(r, lineStarts, label);
12152
+ for (const r of doc.logQueryRules || []) annotateSourceLocation(r, lineStarts, label);
12219
12153
  }
12220
12154
 
12221
12155
  function copySourceMetadata(from, to) {
@@ -12310,7 +12244,7 @@ function parseN3Text(text, opts = {}) {
12310
12244
  label = '<input>',
12311
12245
  keepSourceArtifacts = true,
12312
12246
  collectUsedPrefixes = false,
12313
- collectSourceLocations = true,
12247
+ sourceLocations = false,
12314
12248
  rdf = false,
12315
12249
  } = opts || {};
12316
12250
  const tokens = lex(text, { rdf });
@@ -12319,7 +12253,7 @@ function parseN3Text(text, opts = {}) {
12319
12253
  const [prefixes, triples, frules, brules, logQueryRules] = parser.parseDocument();
12320
12254
 
12321
12255
  const doc = { prefixes, triples, frules, brules, logQueryRules, label };
12322
- if (collectSourceLocations) annotateParsedSourceLocations(doc, text, label);
12256
+ if (sourceLocations) annotateParsedSourceLocations(doc, text, label);
12323
12257
 
12324
12258
  if (collectUsedPrefixes) {
12325
12259
  Object.defineProperty(doc, 'usedPrefixes', {
@@ -12510,8 +12444,8 @@ function parseN3SourceList(input, opts = {}) {
12510
12444
  label: source.label,
12511
12445
  baseIri: source.baseIri || (sources.length === 1 ? defaultBaseIri : ''),
12512
12446
  collectUsedPrefixes: true,
12513
- collectSourceLocations: !!opts.collectSourceLocations,
12514
12447
  keepSourceArtifacts: !!opts.keepSourceArtifacts,
12448
+ sourceLocations: !!opts.sourceLocations,
12515
12449
  rdf: !!opts.rdf,
12516
12450
  }),
12517
12451
  );