eyeling 1.25.2 → 1.25.4
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/HANDBOOK.md +12 -8
- package/dist/browser/eyeling.browser.js +335 -131
- package/eyeling.js +335 -131
- package/lib/cli.js +3 -1
- package/lib/engine.js +278 -87
- package/lib/lexer.js +42 -16
- package/lib/parser.js +12 -27
- package/package.json +1 -1
- package/test/api.test.js +40 -0
|
@@ -4965,12 +4965,14 @@ function main() {
|
|
|
4965
4965
|
outTriples = res.queryTriples;
|
|
4966
4966
|
outDerived = res.queryDerived;
|
|
4967
4967
|
} else {
|
|
4968
|
+
const skipDerivedCollection = mayAutoRenderOutputStrings && !engine.getProofCommentsEnabled();
|
|
4968
4969
|
derived = engine.forwardChain(facts, frules, brules, null, {
|
|
4969
4970
|
captureExplanations: engine.getProofCommentsEnabled(),
|
|
4971
|
+
collectDerived: !skipDerivedCollection,
|
|
4970
4972
|
prefixes,
|
|
4971
4973
|
});
|
|
4972
4974
|
outDerived = derived;
|
|
4973
|
-
outTriples = derived.map((df) => df.fact);
|
|
4975
|
+
outTriples = skipDerivedCollection ? [] : derived.map((df) => df.fact);
|
|
4974
4976
|
}
|
|
4975
4977
|
|
|
4976
4978
|
const renderedOutputTriples = hasQueries ? outTriples : facts;
|
|
@@ -6521,6 +6523,12 @@ function alphaEqGraphTriples(xs, ys, opts) {
|
|
|
6521
6523
|
// - __byPred: Map<predicateId, number[]> (indices into facts array)
|
|
6522
6524
|
// - __byPS: Map<predicateId, Map<subjectId, number[]>>
|
|
6523
6525
|
// - __byPO: Map<predicateId, Map<objectId, number[]>>
|
|
6526
|
+
// - __byPNonFastS / __byPNonFastO: Map<predicateId, number[]>
|
|
6527
|
+
// IRI-predicate facts whose subject/object cannot be indexed by fast key.
|
|
6528
|
+
// These are the fallback clauses for a constrained subject/object, just
|
|
6529
|
+
// like a Prolog clause index keeps variable-headed clauses as fallback.
|
|
6530
|
+
// - __varPred* indexes: facts whose predicate is a Var. Only these non-IRI
|
|
6531
|
+
// predicate facts can unify with a ground IRI predicate goal.
|
|
6524
6532
|
// - __keySet: Set<"S\tP\tO"> for Iri/Literal/Blank-only triples (fast dup check)
|
|
6525
6533
|
//
|
|
6526
6534
|
// Backward rules:
|
|
@@ -6536,6 +6544,8 @@ const __compoundKeyToTid = new Map();
|
|
|
6536
6544
|
// Use a negative id space so we never collide with __tid (which is positive).
|
|
6537
6545
|
let __nextCompoundTid = -1;
|
|
6538
6546
|
|
|
6547
|
+
const EMPTY_FACT_INDEX_BUCKET = Object.freeze([]);
|
|
6548
|
+
|
|
6539
6549
|
function __internCompoundTid(key) {
|
|
6540
6550
|
const hit = __compoundKeyToTid.get(key);
|
|
6541
6551
|
if (hit !== undefined) return hit;
|
|
@@ -6587,6 +6597,71 @@ function termFastKey(t) {
|
|
|
6587
6597
|
return null;
|
|
6588
6598
|
}
|
|
6589
6599
|
|
|
6600
|
+
function encodeLookupKeyPart(k) {
|
|
6601
|
+
if (typeof k === 'number') return 'T' + k;
|
|
6602
|
+
const s = String(k);
|
|
6603
|
+
return 'K' + s.length + ':' + s;
|
|
6604
|
+
}
|
|
6605
|
+
|
|
6606
|
+
function literalLookupKey(t) {
|
|
6607
|
+
const boolInfo = parseBooleanLiteralInfo(t);
|
|
6608
|
+
if (boolInfo) return '\u0000B' + (boolInfo.value ? '1' : '0');
|
|
6609
|
+
|
|
6610
|
+
const numInfo = parseNumericLiteralInfo(t);
|
|
6611
|
+
if (numInfo) {
|
|
6612
|
+
if (numInfo.kind === 'bigint') return '\u0000N' + numInfo.dt + '\u0000' + numInfo.value.toString();
|
|
6613
|
+
|
|
6614
|
+
const n = numInfo.value;
|
|
6615
|
+
// Normal unification intentionally does not make NaN value-equal to NaN;
|
|
6616
|
+
// only identical lexical literals match through the ordinary __tid path.
|
|
6617
|
+
if (!Number.isNaN(n)) return '\u0000N' + numInfo.dt + '\u0000' + String(n);
|
|
6618
|
+
}
|
|
6619
|
+
|
|
6620
|
+
// Covers exact literals plus plain string / xsd:string canonicalization, which
|
|
6621
|
+
// Literal construction already normalizes into a shared __tid.
|
|
6622
|
+
return termFastKey(t);
|
|
6623
|
+
}
|
|
6624
|
+
|
|
6625
|
+
function termLookupKey(t) {
|
|
6626
|
+
// Lookup keys summarize the equality accepted by ordinary unifyTerm(), not
|
|
6627
|
+
// merely object identity. This keeps literal-index pruning complete for
|
|
6628
|
+
// value-equivalent booleans/numerics such as true/"1"^^xsd:boolean and
|
|
6629
|
+
// 1.0/1.00, while preserving exact fast ids for IRIs, blanks, and strings.
|
|
6630
|
+
if (t instanceof Iri) {
|
|
6631
|
+
if (t.value === RDF_NIL_IRI) return '\u0000L0';
|
|
6632
|
+
return t.__tid;
|
|
6633
|
+
}
|
|
6634
|
+
if (t instanceof Blank) return t.__tid;
|
|
6635
|
+
if (t instanceof Literal) return literalLookupKey(t);
|
|
6636
|
+
|
|
6637
|
+
if (t instanceof ListTerm) {
|
|
6638
|
+
const cached = t.__lookupKey;
|
|
6639
|
+
if (cached !== undefined) return cached === false ? null : cached;
|
|
6640
|
+
|
|
6641
|
+
const xs = t.elems;
|
|
6642
|
+
if (xs.length === 0) {
|
|
6643
|
+
Object.defineProperty(t, '__lookupKey', { value: '\u0000L0', enumerable: false });
|
|
6644
|
+
return '\u0000L0';
|
|
6645
|
+
}
|
|
6646
|
+
|
|
6647
|
+
const parts = new Array(xs.length);
|
|
6648
|
+
for (let i = 0; i < xs.length; i++) {
|
|
6649
|
+
const k = termLookupKey(xs[i]);
|
|
6650
|
+
if (k === null) {
|
|
6651
|
+
Object.defineProperty(t, '__lookupKey', { value: false, enumerable: false });
|
|
6652
|
+
return null;
|
|
6653
|
+
}
|
|
6654
|
+
parts[i] = encodeLookupKeyPart(k);
|
|
6655
|
+
}
|
|
6656
|
+
|
|
6657
|
+
const key = '\u0000L' + xs.length + '\u0001' + parts.join('\u0001');
|
|
6658
|
+
Object.defineProperty(t, '__lookupKey', { value: key, enumerable: false });
|
|
6659
|
+
return key;
|
|
6660
|
+
}
|
|
6661
|
+
|
|
6662
|
+
return null;
|
|
6663
|
+
}
|
|
6664
|
+
|
|
6590
6665
|
function tripleFastKey(tr) {
|
|
6591
6666
|
const ks = termFastKey(tr.s);
|
|
6592
6667
|
const kp = termFastKey(tr.p);
|
|
@@ -6600,9 +6675,13 @@ function ensureFactIndexes(facts) {
|
|
|
6600
6675
|
facts.__byPred &&
|
|
6601
6676
|
facts.__byPS &&
|
|
6602
6677
|
facts.__byPO &&
|
|
6603
|
-
facts.
|
|
6604
|
-
facts.
|
|
6605
|
-
facts.
|
|
6678
|
+
facts.__byPNonFastS &&
|
|
6679
|
+
facts.__byPNonFastO &&
|
|
6680
|
+
facts.__varPred &&
|
|
6681
|
+
facts.__varPredPS &&
|
|
6682
|
+
facts.__varPredPO &&
|
|
6683
|
+
facts.__varPredNonFastS &&
|
|
6684
|
+
facts.__varPredNonFastO &&
|
|
6606
6685
|
facts.__keySet
|
|
6607
6686
|
)
|
|
6608
6687
|
return;
|
|
@@ -6622,21 +6701,41 @@ function ensureFactIndexes(facts) {
|
|
|
6622
6701
|
enumerable: false,
|
|
6623
6702
|
writable: true,
|
|
6624
6703
|
});
|
|
6625
|
-
Object.defineProperty(facts, '
|
|
6704
|
+
Object.defineProperty(facts, '__byPNonFastS', {
|
|
6705
|
+
value: new Map(),
|
|
6706
|
+
enumerable: false,
|
|
6707
|
+
writable: true,
|
|
6708
|
+
});
|
|
6709
|
+
Object.defineProperty(facts, '__byPNonFastO', {
|
|
6710
|
+
value: new Map(),
|
|
6711
|
+
enumerable: false,
|
|
6712
|
+
writable: true,
|
|
6713
|
+
});
|
|
6714
|
+
Object.defineProperty(facts, '__varPred', {
|
|
6626
6715
|
value: [],
|
|
6627
6716
|
enumerable: false,
|
|
6628
6717
|
writable: true,
|
|
6629
6718
|
});
|
|
6630
|
-
Object.defineProperty(facts, '
|
|
6719
|
+
Object.defineProperty(facts, '__varPredPS', {
|
|
6631
6720
|
value: new Map(),
|
|
6632
6721
|
enumerable: false,
|
|
6633
6722
|
writable: true,
|
|
6634
6723
|
});
|
|
6635
|
-
Object.defineProperty(facts, '
|
|
6724
|
+
Object.defineProperty(facts, '__varPredPO', {
|
|
6636
6725
|
value: new Map(),
|
|
6637
6726
|
enumerable: false,
|
|
6638
6727
|
writable: true,
|
|
6639
6728
|
});
|
|
6729
|
+
Object.defineProperty(facts, '__varPredNonFastS', {
|
|
6730
|
+
value: [],
|
|
6731
|
+
enumerable: false,
|
|
6732
|
+
writable: true,
|
|
6733
|
+
});
|
|
6734
|
+
Object.defineProperty(facts, '__varPredNonFastO', {
|
|
6735
|
+
value: [],
|
|
6736
|
+
enumerable: false,
|
|
6737
|
+
writable: true,
|
|
6738
|
+
});
|
|
6640
6739
|
Object.defineProperty(facts, '__keySet', {
|
|
6641
6740
|
value: new Set(),
|
|
6642
6741
|
enumerable: false,
|
|
@@ -6677,16 +6776,53 @@ function cloneFactIndexesForSnapshot(src, dest) {
|
|
|
6677
6776
|
Object.defineProperty(dest, '__byPred', { value: cloneArrayMap(src.__byPred), enumerable: false, writable: true });
|
|
6678
6777
|
Object.defineProperty(dest, '__byPS', { value: cloneNestedArrayMap(src.__byPS), enumerable: false, writable: true });
|
|
6679
6778
|
Object.defineProperty(dest, '__byPO', { value: cloneNestedArrayMap(src.__byPO), enumerable: false, writable: true });
|
|
6680
|
-
Object.defineProperty(dest, '
|
|
6681
|
-
|
|
6682
|
-
|
|
6779
|
+
Object.defineProperty(dest, '__byPNonFastS', {
|
|
6780
|
+
value: cloneArrayMap(src.__byPNonFastS),
|
|
6781
|
+
enumerable: false,
|
|
6782
|
+
writable: true,
|
|
6783
|
+
});
|
|
6784
|
+
Object.defineProperty(dest, '__byPNonFastO', {
|
|
6785
|
+
value: cloneArrayMap(src.__byPNonFastO),
|
|
6786
|
+
enumerable: false,
|
|
6787
|
+
writable: true,
|
|
6788
|
+
});
|
|
6789
|
+
Object.defineProperty(dest, '__varPred', { value: src.__varPred.slice(), enumerable: false, writable: true });
|
|
6790
|
+
Object.defineProperty(dest, '__varPredPS', {
|
|
6791
|
+
value: cloneArrayMap(src.__varPredPS),
|
|
6792
|
+
enumerable: false,
|
|
6793
|
+
writable: true,
|
|
6794
|
+
});
|
|
6795
|
+
Object.defineProperty(dest, '__varPredPO', {
|
|
6796
|
+
value: cloneArrayMap(src.__varPredPO),
|
|
6797
|
+
enumerable: false,
|
|
6798
|
+
writable: true,
|
|
6799
|
+
});
|
|
6800
|
+
Object.defineProperty(dest, '__varPredNonFastS', {
|
|
6801
|
+
value: src.__varPredNonFastS.slice(),
|
|
6802
|
+
enumerable: false,
|
|
6803
|
+
writable: true,
|
|
6804
|
+
});
|
|
6805
|
+
Object.defineProperty(dest, '__varPredNonFastO', {
|
|
6806
|
+
value: src.__varPredNonFastO.slice(),
|
|
6807
|
+
enumerable: false,
|
|
6808
|
+
writable: true,
|
|
6809
|
+
});
|
|
6683
6810
|
Object.defineProperty(dest, '__keySet', { value: new Set(src.__keySet), enumerable: false, writable: true });
|
|
6684
6811
|
Object.defineProperty(dest, '__keySetComplete', { value: !!src.__keySetComplete, enumerable: false, writable: true });
|
|
6685
6812
|
}
|
|
6686
6813
|
|
|
6814
|
+
function addToIndexArrayMap(map, key, value) {
|
|
6815
|
+
let bucket = map.get(key);
|
|
6816
|
+
if (!bucket) {
|
|
6817
|
+
bucket = [];
|
|
6818
|
+
map.set(key, bucket);
|
|
6819
|
+
}
|
|
6820
|
+
bucket.push(value);
|
|
6821
|
+
}
|
|
6822
|
+
|
|
6687
6823
|
function indexFact(facts, tr, idx, addKeySet = true) {
|
|
6688
|
-
const sk =
|
|
6689
|
-
const ok =
|
|
6824
|
+
const sk = termLookupKey(tr.s);
|
|
6825
|
+
const ok = termLookupKey(tr.o);
|
|
6690
6826
|
let pkForKey = null;
|
|
6691
6827
|
|
|
6692
6828
|
if (tr.p instanceof Iri) {
|
|
@@ -6707,12 +6843,9 @@ function indexFact(facts, tr, idx, addKeySet = true) {
|
|
|
6707
6843
|
ps = new Map();
|
|
6708
6844
|
facts.__byPS.set(pk, ps);
|
|
6709
6845
|
}
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
ps.set(sk, psb);
|
|
6714
|
-
}
|
|
6715
|
-
psb.push(idx);
|
|
6846
|
+
addToIndexArrayMap(ps, sk, idx);
|
|
6847
|
+
} else {
|
|
6848
|
+
addToIndexArrayMap(facts.__byPNonFastS, pk, idx);
|
|
6716
6849
|
}
|
|
6717
6850
|
|
|
6718
6851
|
if (ok !== null) {
|
|
@@ -6721,32 +6854,23 @@ function indexFact(facts, tr, idx, addKeySet = true) {
|
|
|
6721
6854
|
po = new Map();
|
|
6722
6855
|
facts.__byPO.set(pk, po);
|
|
6723
6856
|
}
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
po.set(ok, pob);
|
|
6728
|
-
}
|
|
6729
|
-
pob.push(idx);
|
|
6857
|
+
addToIndexArrayMap(po, ok, idx);
|
|
6858
|
+
} else {
|
|
6859
|
+
addToIndexArrayMap(facts.__byPNonFastO, pk, idx);
|
|
6730
6860
|
}
|
|
6731
|
-
} else {
|
|
6732
|
-
facts.
|
|
6861
|
+
} else if (tr.p instanceof Var) {
|
|
6862
|
+
facts.__varPred.push(idx);
|
|
6733
6863
|
|
|
6734
6864
|
if (sk !== null) {
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
facts.__wildPS.set(sk, psb);
|
|
6739
|
-
}
|
|
6740
|
-
psb.push(idx);
|
|
6865
|
+
addToIndexArrayMap(facts.__varPredPS, sk, idx);
|
|
6866
|
+
} else {
|
|
6867
|
+
facts.__varPredNonFastS.push(idx);
|
|
6741
6868
|
}
|
|
6742
6869
|
|
|
6743
6870
|
if (ok !== null) {
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
facts.__wildPO.set(ok, pob);
|
|
6748
|
-
}
|
|
6749
|
-
pob.push(idx);
|
|
6871
|
+
addToIndexArrayMap(facts.__varPredPO, ok, idx);
|
|
6872
|
+
} else {
|
|
6873
|
+
facts.__varPredNonFastO.push(idx);
|
|
6750
6874
|
}
|
|
6751
6875
|
}
|
|
6752
6876
|
|
|
@@ -6756,55 +6880,86 @@ function indexFact(facts, tr, idx, addKeySet = true) {
|
|
|
6756
6880
|
}
|
|
6757
6881
|
}
|
|
6758
6882
|
|
|
6883
|
+
function mergeIndexBuckets(primary, fallback) {
|
|
6884
|
+
const a = primary && primary.length ? primary : null;
|
|
6885
|
+
const b = fallback && fallback.length ? fallback : null;
|
|
6886
|
+
if (!a && !b) return EMPTY_FACT_INDEX_BUCKET;
|
|
6887
|
+
if (!a) return b;
|
|
6888
|
+
if (!b) return a;
|
|
6889
|
+
const out = new Array(a.length + b.length);
|
|
6890
|
+
for (let i = 0; i < a.length; i++) out[i] = a[i];
|
|
6891
|
+
for (let i = 0; i < b.length; i++) out[a.length + i] = b[i];
|
|
6892
|
+
return out;
|
|
6893
|
+
}
|
|
6894
|
+
|
|
6895
|
+
function selectPositionIndexedCandidates(all, exactByS, fallbackS, sk, exactByO, fallbackO, ok) {
|
|
6896
|
+
if (sk === null && ok === null) return all && all.length ? all : EMPTY_FACT_INDEX_BUCKET;
|
|
6897
|
+
|
|
6898
|
+
let sBucket = null;
|
|
6899
|
+
if (sk !== null) sBucket = mergeIndexBuckets(exactByS || null, fallbackS || null);
|
|
6900
|
+
|
|
6901
|
+
let oBucket = null;
|
|
6902
|
+
if (ok !== null) oBucket = mergeIndexBuckets(exactByO || null, fallbackO || null);
|
|
6903
|
+
|
|
6904
|
+
if (sk !== null && ok !== null) return sBucket.length <= oBucket.length ? sBucket : oBucket;
|
|
6905
|
+
return sk !== null ? sBucket : oBucket;
|
|
6906
|
+
}
|
|
6907
|
+
|
|
6759
6908
|
function candidateFacts(facts, goal) {
|
|
6760
6909
|
ensureFactIndexes(facts);
|
|
6761
6910
|
|
|
6762
6911
|
if (goal.p instanceof Iri) {
|
|
6763
6912
|
const pk = goal.p.__tid;
|
|
6764
6913
|
|
|
6765
|
-
const sk =
|
|
6766
|
-
const ok =
|
|
6914
|
+
const sk = termLookupKey(goal.s);
|
|
6915
|
+
const ok = termLookupKey(goal.o);
|
|
6767
6916
|
|
|
6768
|
-
/** @type {number[] | null} */
|
|
6769
6917
|
let byPS = null;
|
|
6770
6918
|
if (sk !== null) {
|
|
6771
6919
|
const ps = facts.__byPS.get(pk);
|
|
6772
6920
|
if (ps) byPS = ps.get(sk) || null;
|
|
6773
6921
|
}
|
|
6922
|
+
const byPNonFastS = sk !== null ? facts.__byPNonFastS.get(pk) || null : null;
|
|
6774
6923
|
|
|
6775
|
-
/** @type {number[] | null} */
|
|
6776
6924
|
let byPO = null;
|
|
6777
6925
|
if (ok !== null) {
|
|
6778
6926
|
const po = facts.__byPO.get(pk);
|
|
6779
6927
|
if (po) byPO = po.get(ok) || null;
|
|
6780
6928
|
}
|
|
6929
|
+
const byPNonFastO = ok !== null ? facts.__byPNonFastO.get(pk) || null : null;
|
|
6781
6930
|
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6931
|
+
const exact = selectPositionIndexedCandidates(
|
|
6932
|
+
facts.__byPred.get(pk) || null,
|
|
6933
|
+
byPS,
|
|
6934
|
+
byPNonFastS,
|
|
6935
|
+
sk,
|
|
6936
|
+
byPO,
|
|
6937
|
+
byPNonFastO,
|
|
6938
|
+
ok,
|
|
6939
|
+
);
|
|
6787
6940
|
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
if (sk !== null) wildPS = facts.__wildPS.get(sk) || null;
|
|
6941
|
+
let varPredPS = null;
|
|
6942
|
+
if (sk !== null) varPredPS = facts.__varPredPS.get(sk) || null;
|
|
6791
6943
|
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
if (ok !== null) wildPO = facts.__wildPO.get(ok) || null;
|
|
6944
|
+
let varPredPO = null;
|
|
6945
|
+
if (ok !== null) varPredPO = facts.__varPredPO.get(ok) || null;
|
|
6795
6946
|
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6947
|
+
const wild = selectPositionIndexedCandidates(
|
|
6948
|
+
facts.__varPred,
|
|
6949
|
+
varPredPS,
|
|
6950
|
+
sk !== null ? facts.__varPredNonFastS : null,
|
|
6951
|
+
sk,
|
|
6952
|
+
varPredPO,
|
|
6953
|
+
ok !== null ? facts.__varPredNonFastO : null,
|
|
6954
|
+
ok,
|
|
6955
|
+
);
|
|
6801
6956
|
|
|
6802
6957
|
return {
|
|
6803
|
-
exact
|
|
6804
|
-
wild
|
|
6805
|
-
exactLen: exact
|
|
6806
|
-
wildLen: wild
|
|
6807
|
-
totalLen:
|
|
6958
|
+
exact,
|
|
6959
|
+
wild,
|
|
6960
|
+
exactLen: exact.length,
|
|
6961
|
+
wildLen: wild.length,
|
|
6962
|
+
totalLen: exact.length + wild.length,
|
|
6808
6963
|
};
|
|
6809
6964
|
}
|
|
6810
6965
|
|
|
@@ -6822,20 +6977,28 @@ function hasFactIndexed(facts, tr) {
|
|
|
6822
6977
|
|
|
6823
6978
|
if (tr.p instanceof Iri) {
|
|
6824
6979
|
const pk = tr.p.__tid;
|
|
6980
|
+
const sk = termLookupKey(tr.s);
|
|
6981
|
+
let best = null;
|
|
6982
|
+
|
|
6983
|
+
if (sk !== null) {
|
|
6984
|
+
const ps = facts.__byPS.get(pk);
|
|
6985
|
+
if (!ps) return false;
|
|
6986
|
+
const psb = ps.get(sk);
|
|
6987
|
+
if (!psb || psb.length === 0) return false;
|
|
6988
|
+
best = psb;
|
|
6989
|
+
}
|
|
6825
6990
|
|
|
6826
|
-
const ok =
|
|
6991
|
+
const ok = termLookupKey(tr.o);
|
|
6827
6992
|
if (ok !== null) {
|
|
6828
6993
|
const po = facts.__byPO.get(pk);
|
|
6829
|
-
if (po)
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
// triples as duplicates modulo blank renaming, or you'll incorrectly
|
|
6834
|
-
// drop facts like: _:sk_0 :x 8.0 (because _:b8 :x 8.0 exists).
|
|
6835
|
-
return pob.some((i) => triplesEqual(facts[i], tr));
|
|
6836
|
-
}
|
|
6994
|
+
if (!po) return false;
|
|
6995
|
+
const pob = po.get(ok);
|
|
6996
|
+
if (!pob || pob.length === 0) return false;
|
|
6997
|
+
if (!best || pob.length < best.length) best = pob;
|
|
6837
6998
|
}
|
|
6838
6999
|
|
|
7000
|
+
if (best) return best.some((i) => triplesEqual(facts[i], tr));
|
|
7001
|
+
|
|
6839
7002
|
const pb = facts.__byPred.get(pk) || [];
|
|
6840
7003
|
return pb.some((i) => triplesEqual(facts[i], tr));
|
|
6841
7004
|
}
|
|
@@ -6944,11 +7107,25 @@ function mergeSinglePremiseAgendaBuckets() {
|
|
|
6944
7107
|
return out;
|
|
6945
7108
|
}
|
|
6946
7109
|
|
|
7110
|
+
function termContainsVarForAgenda(t) {
|
|
7111
|
+
if (t instanceof Var) return true;
|
|
7112
|
+
if (t instanceof ListTerm) return t.elems.some(termContainsVarForAgenda);
|
|
7113
|
+
if (t instanceof OpenListTerm) return true;
|
|
7114
|
+
if (t instanceof GraphTerm)
|
|
7115
|
+
return t.triples.some(
|
|
7116
|
+
(tr) =>
|
|
7117
|
+
termContainsVarForAgenda(tr.s) || termContainsVarForAgenda(tr.p) || termContainsVarForAgenda(tr.o),
|
|
7118
|
+
);
|
|
7119
|
+
return false;
|
|
7120
|
+
}
|
|
7121
|
+
|
|
6947
7122
|
function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
|
|
6948
7123
|
const index = {
|
|
6949
7124
|
byPred: new Map(),
|
|
7125
|
+
byPredAll: new Map(),
|
|
6950
7126
|
byPS: new Map(),
|
|
6951
7127
|
byPO: new Map(),
|
|
7128
|
+
allIriPred: [],
|
|
6952
7129
|
wildPred: [],
|
|
6953
7130
|
wildPS: new Map(),
|
|
6954
7131
|
wildPO: new Map(),
|
|
@@ -6970,8 +7147,8 @@ function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
|
|
|
6970
7147
|
if (!isSinglePremiseAgendaRuleSafe(r, backRules)) continue;
|
|
6971
7148
|
|
|
6972
7149
|
const goal = r.premise[0];
|
|
6973
|
-
const goalSKey =
|
|
6974
|
-
const goalOKey =
|
|
7150
|
+
const goalSKey = termLookupKey(goal.s);
|
|
7151
|
+
const goalOKey = termLookupKey(goal.o);
|
|
6975
7152
|
const fastSubjectVar = goal.p instanceof Iri && goal.s instanceof Var && goalOKey !== null ? goal.s.name : null;
|
|
6976
7153
|
const fastObjectVar = goal.p instanceof Iri && goal.o instanceof Var && goalSKey !== null ? goal.o.name : null;
|
|
6977
7154
|
const entry = {
|
|
@@ -6990,6 +7167,8 @@ function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
|
|
|
6990
7167
|
index.size += 1;
|
|
6991
7168
|
|
|
6992
7169
|
if (entry.goalPredTid !== null) {
|
|
7170
|
+
addToMapArray(index.byPredAll, entry.goalPredTid, entry);
|
|
7171
|
+
index.allIriPred.push(entry);
|
|
6993
7172
|
if (entry.goalSKey === null && entry.goalOKey === null) addToMapArray(index.byPred, entry.goalPredTid, entry);
|
|
6994
7173
|
if (entry.goalSKey !== null) {
|
|
6995
7174
|
let ps = index.byPS.get(entry.goalPredTid);
|
|
@@ -7020,25 +7199,37 @@ function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
|
|
|
7020
7199
|
function getSinglePremiseAgendaCandidates(index, fact) {
|
|
7021
7200
|
if (!index || index.size === 0) return null;
|
|
7022
7201
|
|
|
7023
|
-
const sk =
|
|
7024
|
-
const ok =
|
|
7202
|
+
const sk = termLookupKey(fact.s);
|
|
7203
|
+
const ok = termLookupKey(fact.o);
|
|
7025
7204
|
|
|
7026
7205
|
let exact = null;
|
|
7027
7206
|
if (fact.p instanceof Iri) {
|
|
7028
7207
|
const pk = fact.p.__tid;
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7208
|
+
if ((sk === null && termContainsVarForAgenda(fact.s)) || (ok === null && termContainsVarForAgenda(fact.o))) {
|
|
7209
|
+
// A fact with a variable-bearing subject/object (most importantly a
|
|
7210
|
+
// top-level variable fact such as `?S :p ?O.`) can match rules whose
|
|
7211
|
+
// premise is fixed in that position. The ordinary `(p,s)` / `(p,o)` lookup
|
|
7212
|
+
// would miss those rules, so fall back to all agenda-indexed rules for
|
|
7213
|
+
// this predicate. Do not do this merely for non-fast quoted formulas:
|
|
7214
|
+
// they are not wildcards, and broad fallback would over-fire rules that
|
|
7215
|
+
// rely on protected blank-node/formula unification semantics.
|
|
7216
|
+
exact = index.byPredAll.get(pk) || null;
|
|
7217
|
+
} else {
|
|
7218
|
+
const byPred = index.byPred.get(pk) || null;
|
|
7219
|
+
let byPS = null;
|
|
7032
7220
|
const ps = index.byPS.get(pk);
|
|
7033
7221
|
if (ps) byPS = ps.get(sk) || null;
|
|
7034
|
-
|
|
7035
|
-
let byPO = null;
|
|
7036
|
-
if (ok !== null) {
|
|
7222
|
+
let byPO = null;
|
|
7037
7223
|
const po = index.byPO.get(pk);
|
|
7038
7224
|
if (po) byPO = po.get(ok) || null;
|
|
7039
|
-
}
|
|
7040
7225
|
|
|
7041
|
-
|
|
7226
|
+
exact = mergeSinglePremiseAgendaBuckets(byPred, byPS, byPO);
|
|
7227
|
+
}
|
|
7228
|
+
} else if (fact.p instanceof Var) {
|
|
7229
|
+
// A variable-predicate fact can match any IRI-predicate agenda rule.
|
|
7230
|
+
// This is deliberately broad and relies on final unification below; such
|
|
7231
|
+
// facts are uncommon and correctness matters more than over-indexing them.
|
|
7232
|
+
exact = index.allIriPred.length ? index.allIriPred : null;
|
|
7042
7233
|
}
|
|
7043
7234
|
|
|
7044
7235
|
const wildPred = index.wildPred.length ? index.wildPred : null;
|
|
@@ -8388,6 +8579,8 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8388
8579
|
__attachGoalTable(backRules, goalTable);
|
|
8389
8580
|
|
|
8390
8581
|
const captureExplanations = !(opts && opts.captureExplanations === false);
|
|
8582
|
+
const collectDerived = !(opts && opts.collectDerived === false);
|
|
8583
|
+
const hasDerivedCallback = typeof onDerived === 'function';
|
|
8391
8584
|
const derivedForward = [];
|
|
8392
8585
|
const varGen = [0];
|
|
8393
8586
|
const skCounter = [0];
|
|
@@ -8566,8 +8759,8 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8566
8759
|
if (!hasFactIndexed(facts, instantiated)) {
|
|
8567
8760
|
pushFactIndexed(facts, instantiated);
|
|
8568
8761
|
const df = makeDerivedRecord(instantiated, r, getInstantiatedPremises(), s, captureExplanations);
|
|
8569
|
-
derivedForward.push(df);
|
|
8570
|
-
if (
|
|
8762
|
+
if (collectDerived) derivedForward.push(df);
|
|
8763
|
+
if (hasDerivedCallback) onDerived(df);
|
|
8571
8764
|
changedHere = true;
|
|
8572
8765
|
}
|
|
8573
8766
|
|
|
@@ -8630,8 +8823,8 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8630
8823
|
|
|
8631
8824
|
pushFactIndexed(facts, inst);
|
|
8632
8825
|
const df = makeDerivedRecord(inst, r, getInstantiatedPremises(), s, captureExplanations);
|
|
8633
|
-
derivedForward.push(df);
|
|
8634
|
-
if (
|
|
8826
|
+
if (collectDerived) derivedForward.push(df);
|
|
8827
|
+
if (hasDerivedCallback) onDerived(df);
|
|
8635
8828
|
|
|
8636
8829
|
changedHere = true;
|
|
8637
8830
|
}
|
|
@@ -9643,15 +9836,6 @@ function isIdentChar(c) {
|
|
|
9643
9836
|
return c === ':' || isPnChars(c);
|
|
9644
9837
|
}
|
|
9645
9838
|
|
|
9646
|
-
function canContinueAfterDot(next) {
|
|
9647
|
-
// PN_LOCAL allows '.' but it cannot appear at the end.
|
|
9648
|
-
// We include '.' only if it is followed by something that could continue a name.
|
|
9649
|
-
if (next === null) return false;
|
|
9650
|
-
if (isIdentChar(next)) return true;
|
|
9651
|
-
if (next === '%' || next === '\\') return true;
|
|
9652
|
-
return false;
|
|
9653
|
-
}
|
|
9654
|
-
|
|
9655
9839
|
function isForbiddenNoncharacterCodePoint(cp) {
|
|
9656
9840
|
return (cp & 0xffff) === 0xfffe || (cp & 0xffff) === 0xffff;
|
|
9657
9841
|
}
|
|
@@ -10727,7 +10911,9 @@ function lex(inputText, opts = {}) {
|
|
|
10727
10911
|
// Avoid copying large ASCII/BMP inputs into an Array. Array.from() is
|
|
10728
10912
|
// only needed when the text contains surrogate pairs and we want the old
|
|
10729
10913
|
// code-point iteration behavior for non-BMP characters.
|
|
10730
|
-
const
|
|
10914
|
+
const hasSurrogates = /[\uD800-\uDFFF]/.test(inputText);
|
|
10915
|
+
const inputMayContainInvalidStringChar = hasSurrogates || /[\u0000\uFFFE\uFFFF]/.test(inputText);
|
|
10916
|
+
const chars = hasSurrogates ? Array.from(inputText) : inputText;
|
|
10731
10917
|
const n = chars.length;
|
|
10732
10918
|
let i = 0;
|
|
10733
10919
|
const tokens = [];
|
|
@@ -10762,14 +10948,47 @@ function lex(inputText, opts = {}) {
|
|
|
10762
10948
|
// Hard stops: delimiters cannot appear unescaped inside PNAME tokens.
|
|
10763
10949
|
if (cc === '{' || cc === '}' || cc === '(' || cc === ')' || cc === '[' || cc === ']' || cc === ';' || cc === ',') break;
|
|
10764
10950
|
|
|
10765
|
-
|
|
10766
|
-
|
|
10767
|
-
|
|
10768
|
-
|
|
10951
|
+
const code = cc.charCodeAt(0);
|
|
10952
|
+
|
|
10953
|
+
// Common ASCII QName/identifier characters. Keep this branch inline so
|
|
10954
|
+
// ordinary N3 files do not call through the full Unicode PN_CHARS predicate
|
|
10955
|
+
// for every character.
|
|
10956
|
+
if (
|
|
10957
|
+
code === 58 || // ':'
|
|
10958
|
+
code === 95 || // '_'
|
|
10959
|
+
code === 45 || // '-'
|
|
10960
|
+
(code >= 48 && code <= 57) ||
|
|
10961
|
+
(code >= 65 && code <= 90) ||
|
|
10962
|
+
(code >= 97 && code <= 122)
|
|
10963
|
+
) {
|
|
10964
|
+
if (out !== null) out.push(cc);
|
|
10769
10965
|
i++;
|
|
10770
10966
|
continue;
|
|
10771
10967
|
}
|
|
10772
10968
|
|
|
10969
|
+
// Dot is allowed inside PN_LOCAL, but not at the end.
|
|
10970
|
+
if (cc === '.') {
|
|
10971
|
+
const next = peek(1);
|
|
10972
|
+
if (next === null) break;
|
|
10973
|
+
const ncode = next.charCodeAt(0);
|
|
10974
|
+
if (
|
|
10975
|
+
next === '%' ||
|
|
10976
|
+
next === '\\' ||
|
|
10977
|
+
ncode === 58 ||
|
|
10978
|
+
ncode === 95 ||
|
|
10979
|
+
ncode === 45 ||
|
|
10980
|
+
(ncode >= 48 && ncode <= 57) ||
|
|
10981
|
+
(ncode >= 65 && ncode <= 90) ||
|
|
10982
|
+
(ncode >= 97 && ncode <= 122) ||
|
|
10983
|
+
isIdentChar(next)
|
|
10984
|
+
) {
|
|
10985
|
+
if (out !== null) out.push('.');
|
|
10986
|
+
i++;
|
|
10987
|
+
continue;
|
|
10988
|
+
}
|
|
10989
|
+
break;
|
|
10990
|
+
}
|
|
10991
|
+
|
|
10773
10992
|
// Percent escape: %HH
|
|
10774
10993
|
if (cc === '%') {
|
|
10775
10994
|
const h1 = peek(1);
|
|
@@ -11017,7 +11236,7 @@ function lex(inputText, opts = {}) {
|
|
|
11017
11236
|
}
|
|
11018
11237
|
const rawContent = sChars === null ? sliceChars(contentStart, closed ? i - 1 : i) : sChars.join('');
|
|
11019
11238
|
const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
|
|
11020
|
-
assertValidStringLiteralValue(decoded, start);
|
|
11239
|
+
if (sChars !== null || inputMayContainInvalidStringChar) assertValidStringLiteralValue(decoded, start);
|
|
11021
11240
|
const s = JSON.stringify(decoded); // canonical short quoted form
|
|
11022
11241
|
tokens.push(new Token('Literal', s, start));
|
|
11023
11242
|
continue;
|
|
@@ -11107,7 +11326,7 @@ function lex(inputText, opts = {}) {
|
|
|
11107
11326
|
}
|
|
11108
11327
|
const rawContent = sChars === null ? sliceChars(contentStart, closed ? i - 1 : i) : sChars.join('');
|
|
11109
11328
|
const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
|
|
11110
|
-
assertValidStringLiteralValue(decoded, start);
|
|
11329
|
+
if (sChars !== null || inputMayContainInvalidStringChar) assertValidStringLiteralValue(decoded, start);
|
|
11111
11330
|
const s = JSON.stringify(decoded); // canonical short quoted form
|
|
11112
11331
|
tokens.push(new Token('Literal', s, start));
|
|
11113
11332
|
continue;
|
|
@@ -12217,15 +12436,21 @@ class Parser {
|
|
|
12217
12436
|
|
|
12218
12437
|
while (true) {
|
|
12219
12438
|
const { verb, invert } = this.parseStatementVerb();
|
|
12220
|
-
const objects = this.parseObjectList();
|
|
12221
12439
|
|
|
12222
|
-
|
|
12223
|
-
|
|
12224
|
-
|
|
12440
|
+
while (true) {
|
|
12441
|
+
const o = this.parseTerm();
|
|
12442
|
+
const postTriples = this.pendingTriplesAfter;
|
|
12443
|
+
this.pendingTriplesAfter = [];
|
|
12444
|
+
|
|
12445
|
+
// If VERB or OBJECT contained paths, their helper triples must come
|
|
12446
|
+
// before the triple that consumes the path result (Easter depends on this).
|
|
12447
|
+
this.flushPendingTriples(out);
|
|
12225
12448
|
|
|
12226
|
-
for (const { term: o, postTriples } of objects) {
|
|
12227
12449
|
out.push(new Triple(invert ? o : subject, verb, invert ? subject : o));
|
|
12228
|
-
if (postTriples
|
|
12450
|
+
if (postTriples.length) out.push(...postTriples);
|
|
12451
|
+
|
|
12452
|
+
if (this.peek().typ !== 'Comma') break;
|
|
12453
|
+
this.next();
|
|
12229
12454
|
}
|
|
12230
12455
|
|
|
12231
12456
|
if (this.peek().typ === 'Semicolon') {
|
|
@@ -12239,27 +12464,6 @@ class Parser {
|
|
|
12239
12464
|
return out;
|
|
12240
12465
|
}
|
|
12241
12466
|
|
|
12242
|
-
parseObjectList() {
|
|
12243
|
-
// Capture any trailing property-list triples produced while parsing each
|
|
12244
|
-
// object term so we can emit them *after* the triple that references the
|
|
12245
|
-
// term. (See pendingTriplesAfter in the constructor.)
|
|
12246
|
-
|
|
12247
|
-
const objs = [];
|
|
12248
|
-
const readObj = () => {
|
|
12249
|
-
const o = this.parseTerm();
|
|
12250
|
-
const post = this.pendingTriplesAfter;
|
|
12251
|
-
this.pendingTriplesAfter = [];
|
|
12252
|
-
objs.push({ term: o, postTriples: post });
|
|
12253
|
-
};
|
|
12254
|
-
|
|
12255
|
-
readObj();
|
|
12256
|
-
while (this.peek().typ === 'Comma') {
|
|
12257
|
-
this.next();
|
|
12258
|
-
readObj();
|
|
12259
|
-
}
|
|
12260
|
-
return objs;
|
|
12261
|
-
}
|
|
12262
|
-
|
|
12263
12467
|
makeRule(left, right, isForward) {
|
|
12264
12468
|
let premiseTerm, conclTerm;
|
|
12265
12469
|
|