eyeling 1.25.3 → 1.25.5
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 +446 -99
- package/eyeling.js +446 -99
- package/lib/engine.js +447 -91
- package/package.json +1 -1
- package/test/api.test.js +60 -0
- package/examples/library-and-path.n3 +0 -499
- package/examples/output/library-and-path.n3 +0 -33
|
@@ -5973,6 +5973,158 @@ function __varOccursElsewhereInPremise(premise, name, idx, field) {
|
|
|
5973
5973
|
return false;
|
|
5974
5974
|
}
|
|
5975
5975
|
|
|
5976
|
+
|
|
5977
|
+
function __scopedPriorityForTerm(t) {
|
|
5978
|
+
if (t instanceof GraphTerm) return 0;
|
|
5979
|
+
const p0 = __logNaturalPriorityFromTerm(t);
|
|
5980
|
+
return p0 !== null && p0 >= 1 ? p0 : 1;
|
|
5981
|
+
}
|
|
5982
|
+
|
|
5983
|
+
function __pushScopedQueryTriplesFromGraph(term, out) {
|
|
5984
|
+
if (!(term instanceof GraphTerm)) return;
|
|
5985
|
+
for (const tr of term.triples) out.push(tr);
|
|
5986
|
+
}
|
|
5987
|
+
|
|
5988
|
+
function __analyzeForwardRuleScopedUse(rule) {
|
|
5989
|
+
const out = {
|
|
5990
|
+
needsSnap: false,
|
|
5991
|
+
baseLevel: 0,
|
|
5992
|
+
queryTriples: [],
|
|
5993
|
+
hasScopedAggregate: false,
|
|
5994
|
+
};
|
|
5995
|
+
|
|
5996
|
+
if (!rule || !Array.isArray(rule.premise)) return out;
|
|
5997
|
+
|
|
5998
|
+
for (let i = 0; i < rule.premise.length; i++) {
|
|
5999
|
+
const tr = rule.premise[i];
|
|
6000
|
+
if (!(tr && tr.p instanceof Iri)) continue;
|
|
6001
|
+
const pv = tr.p.value;
|
|
6002
|
+
|
|
6003
|
+
if (pv === LOG_NS + 'includes' || pv === LOG_NS + 'notIncludes') {
|
|
6004
|
+
// Explicit quoted scopes are local formulas, not snapshots of the global closure.
|
|
6005
|
+
if (tr.s instanceof GraphTerm) continue;
|
|
6006
|
+
|
|
6007
|
+
// A scope variable that is bound by another premise may become an explicit GraphTerm.
|
|
6008
|
+
// Still, if it remains unbound at runtime the builtin treats it as priority 1.
|
|
6009
|
+
// We therefore record the scoped use, but only use quoted object triples for
|
|
6010
|
+
// dependency analysis; variables inside the quoted object do not bind the scope.
|
|
6011
|
+
out.needsSnap = true;
|
|
6012
|
+
out.baseLevel = Math.max(out.baseLevel, __scopedPriorityForTerm(tr.s));
|
|
6013
|
+
__pushScopedQueryTriplesFromGraph(tr.o, out.queryTriples);
|
|
6014
|
+
continue;
|
|
6015
|
+
}
|
|
6016
|
+
|
|
6017
|
+
if (pv === LOG_NS + 'collectAllIn') {
|
|
6018
|
+
if (tr.o instanceof GraphTerm) continue;
|
|
6019
|
+
|
|
6020
|
+
out.needsSnap = true;
|
|
6021
|
+
out.hasScopedAggregate = true;
|
|
6022
|
+
out.baseLevel = Math.max(out.baseLevel, __scopedPriorityForTerm(tr.o));
|
|
6023
|
+
if (tr.s instanceof ListTerm && tr.s.elems.length === 3) {
|
|
6024
|
+
__pushScopedQueryTriplesFromGraph(tr.s.elems[1], out.queryTriples);
|
|
6025
|
+
}
|
|
6026
|
+
continue;
|
|
6027
|
+
}
|
|
6028
|
+
|
|
6029
|
+
if (pv === LOG_NS + 'forAllIn') {
|
|
6030
|
+
if (tr.o instanceof GraphTerm) continue;
|
|
6031
|
+
|
|
6032
|
+
out.needsSnap = true;
|
|
6033
|
+
out.hasScopedAggregate = true;
|
|
6034
|
+
out.baseLevel = Math.max(out.baseLevel, __scopedPriorityForTerm(tr.o));
|
|
6035
|
+
if (tr.s instanceof ListTerm && tr.s.elems.length === 2) {
|
|
6036
|
+
__pushScopedQueryTriplesFromGraph(tr.s.elems[0], out.queryTriples);
|
|
6037
|
+
__pushScopedQueryTriplesFromGraph(tr.s.elems[1], out.queryTriples);
|
|
6038
|
+
}
|
|
6039
|
+
}
|
|
6040
|
+
}
|
|
6041
|
+
|
|
6042
|
+
return out;
|
|
6043
|
+
}
|
|
6044
|
+
|
|
6045
|
+
function __triplePatternsMayOverlap(a, b) {
|
|
6046
|
+
return unifyTriple(a, b, __emptySubst()) !== null || unifyTriple(b, a, __emptySubst()) !== null;
|
|
6047
|
+
}
|
|
6048
|
+
|
|
6049
|
+
function __ruleConclusionsMayFeedScopedQueries(producer, consumerMeta) {
|
|
6050
|
+
if (!consumerMeta || !consumerMeta.queryTriples || consumerMeta.queryTriples.length === 0) return false;
|
|
6051
|
+
|
|
6052
|
+
// Dynamic conclusions are only known after a proof solution. Conservatively
|
|
6053
|
+
// assume they can feed any scoped query if the producing rule is scoped.
|
|
6054
|
+
if (producer && producer.__dynamicConclusionTerm) return true;
|
|
6055
|
+
|
|
6056
|
+
if (!producer || !Array.isArray(producer.conclusion) || producer.conclusion.length === 0) return false;
|
|
6057
|
+
for (const q of consumerMeta.queryTriples) {
|
|
6058
|
+
for (const h of producer.conclusion) {
|
|
6059
|
+
if (__triplePatternsMayOverlap(q, h)) return true;
|
|
6060
|
+
}
|
|
6061
|
+
}
|
|
6062
|
+
return false;
|
|
6063
|
+
}
|
|
6064
|
+
|
|
6065
|
+
function __setForwardRuleScopedStratumInfo(rule, level) {
|
|
6066
|
+
const value =
|
|
6067
|
+
level > 0
|
|
6068
|
+
? { needsSnap: true, requiredLevel: level, exactLevel: true }
|
|
6069
|
+
: { needsSnap: false, requiredLevel: 0, exactLevel: false };
|
|
6070
|
+
|
|
6071
|
+
if (!hasOwn.call(rule, '__scopedStratumInfo')) {
|
|
6072
|
+
Object.defineProperty(rule, '__scopedStratumInfo', {
|
|
6073
|
+
value,
|
|
6074
|
+
enumerable: false,
|
|
6075
|
+
writable: true,
|
|
6076
|
+
configurable: true,
|
|
6077
|
+
});
|
|
6078
|
+
} else {
|
|
6079
|
+
rule.__scopedStratumInfo = value;
|
|
6080
|
+
}
|
|
6081
|
+
}
|
|
6082
|
+
|
|
6083
|
+
function __computeForwardRuleScopedStrata(forwardRules) {
|
|
6084
|
+
if (!Array.isArray(forwardRules) || forwardRules.length === 0) return 0;
|
|
6085
|
+
|
|
6086
|
+
const metas = forwardRules.map((r) => __analyzeForwardRuleScopedUse(r));
|
|
6087
|
+
const levels = metas.map((m) => (m.needsSnap ? Math.max(1, m.baseLevel || 1) : 0));
|
|
6088
|
+
|
|
6089
|
+
let maxLevel = 0;
|
|
6090
|
+
for (const lvl of levels) if (lvl > maxLevel) maxLevel = lvl;
|
|
6091
|
+
|
|
6092
|
+
// Stratify scoped rules by data dependency: if a scoped query can read facts
|
|
6093
|
+
// produced by another scoped rule, the reader must run against a later frozen
|
|
6094
|
+
// snapshot. This prevents non-monotonic scoped builtins such as collectAllIn
|
|
6095
|
+
// from emitting an early result before lower strata have derived their facts.
|
|
6096
|
+
const passLimit = Math.max(1, forwardRules.length) + maxLevel + 1;
|
|
6097
|
+
for (let pass = 0; pass < passLimit; pass++) {
|
|
6098
|
+
let changed = false;
|
|
6099
|
+
for (let ci = 0; ci < forwardRules.length; ci++) {
|
|
6100
|
+
if (!metas[ci].needsSnap || !metas[ci].hasScopedAggregate) continue;
|
|
6101
|
+
let needed = levels[ci];
|
|
6102
|
+
for (let pi = 0; pi < forwardRules.length; pi++) {
|
|
6103
|
+
if (pi === ci) continue;
|
|
6104
|
+
if (levels[pi] <= 0) continue;
|
|
6105
|
+
if (metas[pi].hasScopedAggregate) continue;
|
|
6106
|
+
if (!__ruleConclusionsMayFeedScopedQueries(forwardRules[pi], metas[ci])) continue;
|
|
6107
|
+
needed = Math.max(needed, levels[pi] + 1);
|
|
6108
|
+
}
|
|
6109
|
+
if (needed !== levels[ci]) {
|
|
6110
|
+
levels[ci] = needed;
|
|
6111
|
+
if (needed > maxLevel) maxLevel = needed;
|
|
6112
|
+
changed = true;
|
|
6113
|
+
}
|
|
6114
|
+
}
|
|
6115
|
+
if (!changed) break;
|
|
6116
|
+
}
|
|
6117
|
+
|
|
6118
|
+
|
|
6119
|
+
maxLevel = 0;
|
|
6120
|
+
for (let i = 0; i < forwardRules.length; i++) {
|
|
6121
|
+
__setForwardRuleScopedStratumInfo(forwardRules[i], levels[i]);
|
|
6122
|
+
if (levels[i] > maxLevel) maxLevel = levels[i];
|
|
6123
|
+
}
|
|
6124
|
+
|
|
6125
|
+
return maxLevel;
|
|
6126
|
+
}
|
|
6127
|
+
|
|
5976
6128
|
function __computeForwardRuleScopedSkipInfo(rule) {
|
|
5977
6129
|
let needsSnap = false;
|
|
5978
6130
|
let requiredLevel = 0;
|
|
@@ -6523,6 +6675,12 @@ function alphaEqGraphTriples(xs, ys, opts) {
|
|
|
6523
6675
|
// - __byPred: Map<predicateId, number[]> (indices into facts array)
|
|
6524
6676
|
// - __byPS: Map<predicateId, Map<subjectId, number[]>>
|
|
6525
6677
|
// - __byPO: Map<predicateId, Map<objectId, number[]>>
|
|
6678
|
+
// - __byPNonFastS / __byPNonFastO: Map<predicateId, number[]>
|
|
6679
|
+
// IRI-predicate facts whose subject/object cannot be indexed by fast key.
|
|
6680
|
+
// These are the fallback clauses for a constrained subject/object, just
|
|
6681
|
+
// like a Prolog clause index keeps variable-headed clauses as fallback.
|
|
6682
|
+
// - __varPred* indexes: facts whose predicate is a Var. Only these non-IRI
|
|
6683
|
+
// predicate facts can unify with a ground IRI predicate goal.
|
|
6526
6684
|
// - __keySet: Set<"S\tP\tO"> for Iri/Literal/Blank-only triples (fast dup check)
|
|
6527
6685
|
//
|
|
6528
6686
|
// Backward rules:
|
|
@@ -6538,6 +6696,8 @@ const __compoundKeyToTid = new Map();
|
|
|
6538
6696
|
// Use a negative id space so we never collide with __tid (which is positive).
|
|
6539
6697
|
let __nextCompoundTid = -1;
|
|
6540
6698
|
|
|
6699
|
+
const EMPTY_FACT_INDEX_BUCKET = Object.freeze([]);
|
|
6700
|
+
|
|
6541
6701
|
function __internCompoundTid(key) {
|
|
6542
6702
|
const hit = __compoundKeyToTid.get(key);
|
|
6543
6703
|
if (hit !== undefined) return hit;
|
|
@@ -6589,6 +6749,71 @@ function termFastKey(t) {
|
|
|
6589
6749
|
return null;
|
|
6590
6750
|
}
|
|
6591
6751
|
|
|
6752
|
+
function encodeLookupKeyPart(k) {
|
|
6753
|
+
if (typeof k === 'number') return 'T' + k;
|
|
6754
|
+
const s = String(k);
|
|
6755
|
+
return 'K' + s.length + ':' + s;
|
|
6756
|
+
}
|
|
6757
|
+
|
|
6758
|
+
function literalLookupKey(t) {
|
|
6759
|
+
const boolInfo = parseBooleanLiteralInfo(t);
|
|
6760
|
+
if (boolInfo) return '\u0000B' + (boolInfo.value ? '1' : '0');
|
|
6761
|
+
|
|
6762
|
+
const numInfo = parseNumericLiteralInfo(t);
|
|
6763
|
+
if (numInfo) {
|
|
6764
|
+
if (numInfo.kind === 'bigint') return '\u0000N' + numInfo.dt + '\u0000' + numInfo.value.toString();
|
|
6765
|
+
|
|
6766
|
+
const n = numInfo.value;
|
|
6767
|
+
// Normal unification intentionally does not make NaN value-equal to NaN;
|
|
6768
|
+
// only identical lexical literals match through the ordinary __tid path.
|
|
6769
|
+
if (!Number.isNaN(n)) return '\u0000N' + numInfo.dt + '\u0000' + String(n);
|
|
6770
|
+
}
|
|
6771
|
+
|
|
6772
|
+
// Covers exact literals plus plain string / xsd:string canonicalization, which
|
|
6773
|
+
// Literal construction already normalizes into a shared __tid.
|
|
6774
|
+
return termFastKey(t);
|
|
6775
|
+
}
|
|
6776
|
+
|
|
6777
|
+
function termLookupKey(t) {
|
|
6778
|
+
// Lookup keys summarize the equality accepted by ordinary unifyTerm(), not
|
|
6779
|
+
// merely object identity. This keeps literal-index pruning complete for
|
|
6780
|
+
// value-equivalent booleans/numerics such as true/"1"^^xsd:boolean and
|
|
6781
|
+
// 1.0/1.00, while preserving exact fast ids for IRIs, blanks, and strings.
|
|
6782
|
+
if (t instanceof Iri) {
|
|
6783
|
+
if (t.value === RDF_NIL_IRI) return '\u0000L0';
|
|
6784
|
+
return t.__tid;
|
|
6785
|
+
}
|
|
6786
|
+
if (t instanceof Blank) return t.__tid;
|
|
6787
|
+
if (t instanceof Literal) return literalLookupKey(t);
|
|
6788
|
+
|
|
6789
|
+
if (t instanceof ListTerm) {
|
|
6790
|
+
const cached = t.__lookupKey;
|
|
6791
|
+
if (cached !== undefined) return cached === false ? null : cached;
|
|
6792
|
+
|
|
6793
|
+
const xs = t.elems;
|
|
6794
|
+
if (xs.length === 0) {
|
|
6795
|
+
Object.defineProperty(t, '__lookupKey', { value: '\u0000L0', enumerable: false });
|
|
6796
|
+
return '\u0000L0';
|
|
6797
|
+
}
|
|
6798
|
+
|
|
6799
|
+
const parts = new Array(xs.length);
|
|
6800
|
+
for (let i = 0; i < xs.length; i++) {
|
|
6801
|
+
const k = termLookupKey(xs[i]);
|
|
6802
|
+
if (k === null) {
|
|
6803
|
+
Object.defineProperty(t, '__lookupKey', { value: false, enumerable: false });
|
|
6804
|
+
return null;
|
|
6805
|
+
}
|
|
6806
|
+
parts[i] = encodeLookupKeyPart(k);
|
|
6807
|
+
}
|
|
6808
|
+
|
|
6809
|
+
const key = '\u0000L' + xs.length + '\u0001' + parts.join('\u0001');
|
|
6810
|
+
Object.defineProperty(t, '__lookupKey', { value: key, enumerable: false });
|
|
6811
|
+
return key;
|
|
6812
|
+
}
|
|
6813
|
+
|
|
6814
|
+
return null;
|
|
6815
|
+
}
|
|
6816
|
+
|
|
6592
6817
|
function tripleFastKey(tr) {
|
|
6593
6818
|
const ks = termFastKey(tr.s);
|
|
6594
6819
|
const kp = termFastKey(tr.p);
|
|
@@ -6602,9 +6827,13 @@ function ensureFactIndexes(facts) {
|
|
|
6602
6827
|
facts.__byPred &&
|
|
6603
6828
|
facts.__byPS &&
|
|
6604
6829
|
facts.__byPO &&
|
|
6605
|
-
facts.
|
|
6606
|
-
facts.
|
|
6607
|
-
facts.
|
|
6830
|
+
facts.__byPNonFastS &&
|
|
6831
|
+
facts.__byPNonFastO &&
|
|
6832
|
+
facts.__varPred &&
|
|
6833
|
+
facts.__varPredPS &&
|
|
6834
|
+
facts.__varPredPO &&
|
|
6835
|
+
facts.__varPredNonFastS &&
|
|
6836
|
+
facts.__varPredNonFastO &&
|
|
6608
6837
|
facts.__keySet
|
|
6609
6838
|
)
|
|
6610
6839
|
return;
|
|
@@ -6624,21 +6853,41 @@ function ensureFactIndexes(facts) {
|
|
|
6624
6853
|
enumerable: false,
|
|
6625
6854
|
writable: true,
|
|
6626
6855
|
});
|
|
6627
|
-
Object.defineProperty(facts, '
|
|
6856
|
+
Object.defineProperty(facts, '__byPNonFastS', {
|
|
6857
|
+
value: new Map(),
|
|
6858
|
+
enumerable: false,
|
|
6859
|
+
writable: true,
|
|
6860
|
+
});
|
|
6861
|
+
Object.defineProperty(facts, '__byPNonFastO', {
|
|
6862
|
+
value: new Map(),
|
|
6863
|
+
enumerable: false,
|
|
6864
|
+
writable: true,
|
|
6865
|
+
});
|
|
6866
|
+
Object.defineProperty(facts, '__varPred', {
|
|
6628
6867
|
value: [],
|
|
6629
6868
|
enumerable: false,
|
|
6630
6869
|
writable: true,
|
|
6631
6870
|
});
|
|
6632
|
-
Object.defineProperty(facts, '
|
|
6871
|
+
Object.defineProperty(facts, '__varPredPS', {
|
|
6633
6872
|
value: new Map(),
|
|
6634
6873
|
enumerable: false,
|
|
6635
6874
|
writable: true,
|
|
6636
6875
|
});
|
|
6637
|
-
Object.defineProperty(facts, '
|
|
6876
|
+
Object.defineProperty(facts, '__varPredPO', {
|
|
6638
6877
|
value: new Map(),
|
|
6639
6878
|
enumerable: false,
|
|
6640
6879
|
writable: true,
|
|
6641
6880
|
});
|
|
6881
|
+
Object.defineProperty(facts, '__varPredNonFastS', {
|
|
6882
|
+
value: [],
|
|
6883
|
+
enumerable: false,
|
|
6884
|
+
writable: true,
|
|
6885
|
+
});
|
|
6886
|
+
Object.defineProperty(facts, '__varPredNonFastO', {
|
|
6887
|
+
value: [],
|
|
6888
|
+
enumerable: false,
|
|
6889
|
+
writable: true,
|
|
6890
|
+
});
|
|
6642
6891
|
Object.defineProperty(facts, '__keySet', {
|
|
6643
6892
|
value: new Set(),
|
|
6644
6893
|
enumerable: false,
|
|
@@ -6679,16 +6928,53 @@ function cloneFactIndexesForSnapshot(src, dest) {
|
|
|
6679
6928
|
Object.defineProperty(dest, '__byPred', { value: cloneArrayMap(src.__byPred), enumerable: false, writable: true });
|
|
6680
6929
|
Object.defineProperty(dest, '__byPS', { value: cloneNestedArrayMap(src.__byPS), enumerable: false, writable: true });
|
|
6681
6930
|
Object.defineProperty(dest, '__byPO', { value: cloneNestedArrayMap(src.__byPO), enumerable: false, writable: true });
|
|
6682
|
-
Object.defineProperty(dest, '
|
|
6683
|
-
|
|
6684
|
-
|
|
6931
|
+
Object.defineProperty(dest, '__byPNonFastS', {
|
|
6932
|
+
value: cloneArrayMap(src.__byPNonFastS),
|
|
6933
|
+
enumerable: false,
|
|
6934
|
+
writable: true,
|
|
6935
|
+
});
|
|
6936
|
+
Object.defineProperty(dest, '__byPNonFastO', {
|
|
6937
|
+
value: cloneArrayMap(src.__byPNonFastO),
|
|
6938
|
+
enumerable: false,
|
|
6939
|
+
writable: true,
|
|
6940
|
+
});
|
|
6941
|
+
Object.defineProperty(dest, '__varPred', { value: src.__varPred.slice(), enumerable: false, writable: true });
|
|
6942
|
+
Object.defineProperty(dest, '__varPredPS', {
|
|
6943
|
+
value: cloneArrayMap(src.__varPredPS),
|
|
6944
|
+
enumerable: false,
|
|
6945
|
+
writable: true,
|
|
6946
|
+
});
|
|
6947
|
+
Object.defineProperty(dest, '__varPredPO', {
|
|
6948
|
+
value: cloneArrayMap(src.__varPredPO),
|
|
6949
|
+
enumerable: false,
|
|
6950
|
+
writable: true,
|
|
6951
|
+
});
|
|
6952
|
+
Object.defineProperty(dest, '__varPredNonFastS', {
|
|
6953
|
+
value: src.__varPredNonFastS.slice(),
|
|
6954
|
+
enumerable: false,
|
|
6955
|
+
writable: true,
|
|
6956
|
+
});
|
|
6957
|
+
Object.defineProperty(dest, '__varPredNonFastO', {
|
|
6958
|
+
value: src.__varPredNonFastO.slice(),
|
|
6959
|
+
enumerable: false,
|
|
6960
|
+
writable: true,
|
|
6961
|
+
});
|
|
6685
6962
|
Object.defineProperty(dest, '__keySet', { value: new Set(src.__keySet), enumerable: false, writable: true });
|
|
6686
6963
|
Object.defineProperty(dest, '__keySetComplete', { value: !!src.__keySetComplete, enumerable: false, writable: true });
|
|
6687
6964
|
}
|
|
6688
6965
|
|
|
6966
|
+
function addToIndexArrayMap(map, key, value) {
|
|
6967
|
+
let bucket = map.get(key);
|
|
6968
|
+
if (!bucket) {
|
|
6969
|
+
bucket = [];
|
|
6970
|
+
map.set(key, bucket);
|
|
6971
|
+
}
|
|
6972
|
+
bucket.push(value);
|
|
6973
|
+
}
|
|
6974
|
+
|
|
6689
6975
|
function indexFact(facts, tr, idx, addKeySet = true) {
|
|
6690
|
-
const sk =
|
|
6691
|
-
const ok =
|
|
6976
|
+
const sk = termLookupKey(tr.s);
|
|
6977
|
+
const ok = termLookupKey(tr.o);
|
|
6692
6978
|
let pkForKey = null;
|
|
6693
6979
|
|
|
6694
6980
|
if (tr.p instanceof Iri) {
|
|
@@ -6709,12 +6995,9 @@ function indexFact(facts, tr, idx, addKeySet = true) {
|
|
|
6709
6995
|
ps = new Map();
|
|
6710
6996
|
facts.__byPS.set(pk, ps);
|
|
6711
6997
|
}
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
ps.set(sk, psb);
|
|
6716
|
-
}
|
|
6717
|
-
psb.push(idx);
|
|
6998
|
+
addToIndexArrayMap(ps, sk, idx);
|
|
6999
|
+
} else {
|
|
7000
|
+
addToIndexArrayMap(facts.__byPNonFastS, pk, idx);
|
|
6718
7001
|
}
|
|
6719
7002
|
|
|
6720
7003
|
if (ok !== null) {
|
|
@@ -6723,32 +7006,23 @@ function indexFact(facts, tr, idx, addKeySet = true) {
|
|
|
6723
7006
|
po = new Map();
|
|
6724
7007
|
facts.__byPO.set(pk, po);
|
|
6725
7008
|
}
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
po.set(ok, pob);
|
|
6730
|
-
}
|
|
6731
|
-
pob.push(idx);
|
|
7009
|
+
addToIndexArrayMap(po, ok, idx);
|
|
7010
|
+
} else {
|
|
7011
|
+
addToIndexArrayMap(facts.__byPNonFastO, pk, idx);
|
|
6732
7012
|
}
|
|
6733
|
-
} else {
|
|
6734
|
-
facts.
|
|
7013
|
+
} else if (tr.p instanceof Var) {
|
|
7014
|
+
facts.__varPred.push(idx);
|
|
6735
7015
|
|
|
6736
7016
|
if (sk !== null) {
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
facts.__wildPS.set(sk, psb);
|
|
6741
|
-
}
|
|
6742
|
-
psb.push(idx);
|
|
7017
|
+
addToIndexArrayMap(facts.__varPredPS, sk, idx);
|
|
7018
|
+
} else {
|
|
7019
|
+
facts.__varPredNonFastS.push(idx);
|
|
6743
7020
|
}
|
|
6744
7021
|
|
|
6745
7022
|
if (ok !== null) {
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
facts.__wildPO.set(ok, pob);
|
|
6750
|
-
}
|
|
6751
|
-
pob.push(idx);
|
|
7023
|
+
addToIndexArrayMap(facts.__varPredPO, ok, idx);
|
|
7024
|
+
} else {
|
|
7025
|
+
facts.__varPredNonFastO.push(idx);
|
|
6752
7026
|
}
|
|
6753
7027
|
}
|
|
6754
7028
|
|
|
@@ -6758,55 +7032,86 @@ function indexFact(facts, tr, idx, addKeySet = true) {
|
|
|
6758
7032
|
}
|
|
6759
7033
|
}
|
|
6760
7034
|
|
|
7035
|
+
function mergeIndexBuckets(primary, fallback) {
|
|
7036
|
+
const a = primary && primary.length ? primary : null;
|
|
7037
|
+
const b = fallback && fallback.length ? fallback : null;
|
|
7038
|
+
if (!a && !b) return EMPTY_FACT_INDEX_BUCKET;
|
|
7039
|
+
if (!a) return b;
|
|
7040
|
+
if (!b) return a;
|
|
7041
|
+
const out = new Array(a.length + b.length);
|
|
7042
|
+
for (let i = 0; i < a.length; i++) out[i] = a[i];
|
|
7043
|
+
for (let i = 0; i < b.length; i++) out[a.length + i] = b[i];
|
|
7044
|
+
return out;
|
|
7045
|
+
}
|
|
7046
|
+
|
|
7047
|
+
function selectPositionIndexedCandidates(all, exactByS, fallbackS, sk, exactByO, fallbackO, ok) {
|
|
7048
|
+
if (sk === null && ok === null) return all && all.length ? all : EMPTY_FACT_INDEX_BUCKET;
|
|
7049
|
+
|
|
7050
|
+
let sBucket = null;
|
|
7051
|
+
if (sk !== null) sBucket = mergeIndexBuckets(exactByS || null, fallbackS || null);
|
|
7052
|
+
|
|
7053
|
+
let oBucket = null;
|
|
7054
|
+
if (ok !== null) oBucket = mergeIndexBuckets(exactByO || null, fallbackO || null);
|
|
7055
|
+
|
|
7056
|
+
if (sk !== null && ok !== null) return sBucket.length <= oBucket.length ? sBucket : oBucket;
|
|
7057
|
+
return sk !== null ? sBucket : oBucket;
|
|
7058
|
+
}
|
|
7059
|
+
|
|
6761
7060
|
function candidateFacts(facts, goal) {
|
|
6762
7061
|
ensureFactIndexes(facts);
|
|
6763
7062
|
|
|
6764
7063
|
if (goal.p instanceof Iri) {
|
|
6765
7064
|
const pk = goal.p.__tid;
|
|
6766
7065
|
|
|
6767
|
-
const sk =
|
|
6768
|
-
const ok =
|
|
7066
|
+
const sk = termLookupKey(goal.s);
|
|
7067
|
+
const ok = termLookupKey(goal.o);
|
|
6769
7068
|
|
|
6770
|
-
/** @type {number[] | null} */
|
|
6771
7069
|
let byPS = null;
|
|
6772
7070
|
if (sk !== null) {
|
|
6773
7071
|
const ps = facts.__byPS.get(pk);
|
|
6774
7072
|
if (ps) byPS = ps.get(sk) || null;
|
|
6775
7073
|
}
|
|
7074
|
+
const byPNonFastS = sk !== null ? facts.__byPNonFastS.get(pk) || null : null;
|
|
6776
7075
|
|
|
6777
|
-
/** @type {number[] | null} */
|
|
6778
7076
|
let byPO = null;
|
|
6779
7077
|
if (ok !== null) {
|
|
6780
7078
|
const po = facts.__byPO.get(pk);
|
|
6781
7079
|
if (po) byPO = po.get(ok) || null;
|
|
6782
7080
|
}
|
|
7081
|
+
const byPNonFastO = ok !== null ? facts.__byPNonFastO.get(pk) || null : null;
|
|
6783
7082
|
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
7083
|
+
const exact = selectPositionIndexedCandidates(
|
|
7084
|
+
facts.__byPred.get(pk) || null,
|
|
7085
|
+
byPS,
|
|
7086
|
+
byPNonFastS,
|
|
7087
|
+
sk,
|
|
7088
|
+
byPO,
|
|
7089
|
+
byPNonFastO,
|
|
7090
|
+
ok,
|
|
7091
|
+
);
|
|
6789
7092
|
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
if (sk !== null) wildPS = facts.__wildPS.get(sk) || null;
|
|
7093
|
+
let varPredPS = null;
|
|
7094
|
+
if (sk !== null) varPredPS = facts.__varPredPS.get(sk) || null;
|
|
6793
7095
|
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
if (ok !== null) wildPO = facts.__wildPO.get(ok) || null;
|
|
7096
|
+
let varPredPO = null;
|
|
7097
|
+
if (ok !== null) varPredPO = facts.__varPredPO.get(ok) || null;
|
|
6797
7098
|
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
7099
|
+
const wild = selectPositionIndexedCandidates(
|
|
7100
|
+
facts.__varPred,
|
|
7101
|
+
varPredPS,
|
|
7102
|
+
sk !== null ? facts.__varPredNonFastS : null,
|
|
7103
|
+
sk,
|
|
7104
|
+
varPredPO,
|
|
7105
|
+
ok !== null ? facts.__varPredNonFastO : null,
|
|
7106
|
+
ok,
|
|
7107
|
+
);
|
|
6803
7108
|
|
|
6804
7109
|
return {
|
|
6805
|
-
exact
|
|
6806
|
-
wild
|
|
6807
|
-
exactLen: exact
|
|
6808
|
-
wildLen: wild
|
|
6809
|
-
totalLen:
|
|
7110
|
+
exact,
|
|
7111
|
+
wild,
|
|
7112
|
+
exactLen: exact.length,
|
|
7113
|
+
wildLen: wild.length,
|
|
7114
|
+
totalLen: exact.length + wild.length,
|
|
6810
7115
|
};
|
|
6811
7116
|
}
|
|
6812
7117
|
|
|
@@ -6824,20 +7129,28 @@ function hasFactIndexed(facts, tr) {
|
|
|
6824
7129
|
|
|
6825
7130
|
if (tr.p instanceof Iri) {
|
|
6826
7131
|
const pk = tr.p.__tid;
|
|
7132
|
+
const sk = termLookupKey(tr.s);
|
|
7133
|
+
let best = null;
|
|
6827
7134
|
|
|
6828
|
-
|
|
7135
|
+
if (sk !== null) {
|
|
7136
|
+
const ps = facts.__byPS.get(pk);
|
|
7137
|
+
if (!ps) return false;
|
|
7138
|
+
const psb = ps.get(sk);
|
|
7139
|
+
if (!psb || psb.length === 0) return false;
|
|
7140
|
+
best = psb;
|
|
7141
|
+
}
|
|
7142
|
+
|
|
7143
|
+
const ok = termLookupKey(tr.o);
|
|
6829
7144
|
if (ok !== null) {
|
|
6830
7145
|
const po = facts.__byPO.get(pk);
|
|
6831
|
-
if (po)
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
// triples as duplicates modulo blank renaming, or you'll incorrectly
|
|
6836
|
-
// drop facts like: _:sk_0 :x 8.0 (because _:b8 :x 8.0 exists).
|
|
6837
|
-
return pob.some((i) => triplesEqual(facts[i], tr));
|
|
6838
|
-
}
|
|
7146
|
+
if (!po) return false;
|
|
7147
|
+
const pob = po.get(ok);
|
|
7148
|
+
if (!pob || pob.length === 0) return false;
|
|
7149
|
+
if (!best || pob.length < best.length) best = pob;
|
|
6839
7150
|
}
|
|
6840
7151
|
|
|
7152
|
+
if (best) return best.some((i) => triplesEqual(facts[i], tr));
|
|
7153
|
+
|
|
6841
7154
|
const pb = facts.__byPred.get(pk) || [];
|
|
6842
7155
|
return pb.some((i) => triplesEqual(facts[i], tr));
|
|
6843
7156
|
}
|
|
@@ -6946,11 +7259,25 @@ function mergeSinglePremiseAgendaBuckets() {
|
|
|
6946
7259
|
return out;
|
|
6947
7260
|
}
|
|
6948
7261
|
|
|
7262
|
+
function termContainsVarForAgenda(t) {
|
|
7263
|
+
if (t instanceof Var) return true;
|
|
7264
|
+
if (t instanceof ListTerm) return t.elems.some(termContainsVarForAgenda);
|
|
7265
|
+
if (t instanceof OpenListTerm) return true;
|
|
7266
|
+
if (t instanceof GraphTerm)
|
|
7267
|
+
return t.triples.some(
|
|
7268
|
+
(tr) =>
|
|
7269
|
+
termContainsVarForAgenda(tr.s) || termContainsVarForAgenda(tr.p) || termContainsVarForAgenda(tr.o),
|
|
7270
|
+
);
|
|
7271
|
+
return false;
|
|
7272
|
+
}
|
|
7273
|
+
|
|
6949
7274
|
function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
|
|
6950
7275
|
const index = {
|
|
6951
7276
|
byPred: new Map(),
|
|
7277
|
+
byPredAll: new Map(),
|
|
6952
7278
|
byPS: new Map(),
|
|
6953
7279
|
byPO: new Map(),
|
|
7280
|
+
allIriPred: [],
|
|
6954
7281
|
wildPred: [],
|
|
6955
7282
|
wildPS: new Map(),
|
|
6956
7283
|
wildPO: new Map(),
|
|
@@ -6972,8 +7299,8 @@ function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
|
|
|
6972
7299
|
if (!isSinglePremiseAgendaRuleSafe(r, backRules)) continue;
|
|
6973
7300
|
|
|
6974
7301
|
const goal = r.premise[0];
|
|
6975
|
-
const goalSKey =
|
|
6976
|
-
const goalOKey =
|
|
7302
|
+
const goalSKey = termLookupKey(goal.s);
|
|
7303
|
+
const goalOKey = termLookupKey(goal.o);
|
|
6977
7304
|
const fastSubjectVar = goal.p instanceof Iri && goal.s instanceof Var && goalOKey !== null ? goal.s.name : null;
|
|
6978
7305
|
const fastObjectVar = goal.p instanceof Iri && goal.o instanceof Var && goalSKey !== null ? goal.o.name : null;
|
|
6979
7306
|
const entry = {
|
|
@@ -6983,7 +7310,7 @@ function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
|
|
|
6983
7310
|
goalPredTid: goal.p instanceof Iri ? goal.p.__tid : null,
|
|
6984
7311
|
goalSKey,
|
|
6985
7312
|
goalOKey,
|
|
6986
|
-
needsSkipCheck: !!r.__needsForwardSkipCheck,
|
|
7313
|
+
needsSkipCheck: !!(r.__needsForwardSkipCheck || (r.__scopedStratumInfo && r.__scopedStratumInfo.needsSnap)),
|
|
6987
7314
|
fastSubjectVar,
|
|
6988
7315
|
fastObjectVar,
|
|
6989
7316
|
};
|
|
@@ -6992,6 +7319,8 @@ function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
|
|
|
6992
7319
|
index.size += 1;
|
|
6993
7320
|
|
|
6994
7321
|
if (entry.goalPredTid !== null) {
|
|
7322
|
+
addToMapArray(index.byPredAll, entry.goalPredTid, entry);
|
|
7323
|
+
index.allIriPred.push(entry);
|
|
6995
7324
|
if (entry.goalSKey === null && entry.goalOKey === null) addToMapArray(index.byPred, entry.goalPredTid, entry);
|
|
6996
7325
|
if (entry.goalSKey !== null) {
|
|
6997
7326
|
let ps = index.byPS.get(entry.goalPredTid);
|
|
@@ -7022,25 +7351,37 @@ function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
|
|
|
7022
7351
|
function getSinglePremiseAgendaCandidates(index, fact) {
|
|
7023
7352
|
if (!index || index.size === 0) return null;
|
|
7024
7353
|
|
|
7025
|
-
const sk =
|
|
7026
|
-
const ok =
|
|
7354
|
+
const sk = termLookupKey(fact.s);
|
|
7355
|
+
const ok = termLookupKey(fact.o);
|
|
7027
7356
|
|
|
7028
7357
|
let exact = null;
|
|
7029
7358
|
if (fact.p instanceof Iri) {
|
|
7030
7359
|
const pk = fact.p.__tid;
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
7360
|
+
if ((sk === null && termContainsVarForAgenda(fact.s)) || (ok === null && termContainsVarForAgenda(fact.o))) {
|
|
7361
|
+
// A fact with a variable-bearing subject/object (most importantly a
|
|
7362
|
+
// top-level variable fact such as `?S :p ?O.`) can match rules whose
|
|
7363
|
+
// premise is fixed in that position. The ordinary `(p,s)` / `(p,o)` lookup
|
|
7364
|
+
// would miss those rules, so fall back to all agenda-indexed rules for
|
|
7365
|
+
// this predicate. Do not do this merely for non-fast quoted formulas:
|
|
7366
|
+
// they are not wildcards, and broad fallback would over-fire rules that
|
|
7367
|
+
// rely on protected blank-node/formula unification semantics.
|
|
7368
|
+
exact = index.byPredAll.get(pk) || null;
|
|
7369
|
+
} else {
|
|
7370
|
+
const byPred = index.byPred.get(pk) || null;
|
|
7371
|
+
let byPS = null;
|
|
7034
7372
|
const ps = index.byPS.get(pk);
|
|
7035
7373
|
if (ps) byPS = ps.get(sk) || null;
|
|
7036
|
-
|
|
7037
|
-
let byPO = null;
|
|
7038
|
-
if (ok !== null) {
|
|
7374
|
+
let byPO = null;
|
|
7039
7375
|
const po = index.byPO.get(pk);
|
|
7040
7376
|
if (po) byPO = po.get(ok) || null;
|
|
7041
|
-
}
|
|
7042
7377
|
|
|
7043
|
-
|
|
7378
|
+
exact = mergeSinglePremiseAgendaBuckets(byPred, byPS, byPO);
|
|
7379
|
+
}
|
|
7380
|
+
} else if (fact.p instanceof Var) {
|
|
7381
|
+
// A variable-predicate fact can match any IRI-predicate agenda rule.
|
|
7382
|
+
// This is deliberately broad and relies on final unification below; such
|
|
7383
|
+
// facts are uncommon and correctness matters more than over-indexing them.
|
|
7384
|
+
exact = index.allIriPred.length ? index.allIriPred : null;
|
|
7044
7385
|
}
|
|
7045
7386
|
|
|
7046
7387
|
const wildPred = index.wildPred.length ? index.wildPred : null;
|
|
@@ -8417,7 +8758,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8417
8758
|
let scopedClosureLevel = 0;
|
|
8418
8759
|
|
|
8419
8760
|
// Scan known rules for the maximum requested closure priority in scoped log:* goals.
|
|
8420
|
-
let maxScopedClosurePriorityNeeded =
|
|
8761
|
+
let maxScopedClosurePriorityNeeded = Math.max(
|
|
8762
|
+
__computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
|
|
8763
|
+
__computeForwardRuleScopedStrata(forwardRules),
|
|
8764
|
+
);
|
|
8421
8765
|
|
|
8422
8766
|
// Conservative fast-skip for forward rules that cannot possibly succeed
|
|
8423
8767
|
// until a scoped snapshot exists (or a given closure level is reached).
|
|
@@ -8471,12 +8815,16 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8471
8815
|
// until a snapshot exists (and a certain closure level is reached).
|
|
8472
8816
|
// This prevents expensive proofs that will definitely fail in Phase A
|
|
8473
8817
|
// and in early closure levels.
|
|
8474
|
-
const info = r.__scopedSkipInfo;
|
|
8818
|
+
const info = r.__scopedStratumInfo || r.__scopedSkipInfo;
|
|
8475
8819
|
if (info && info.needsSnap) {
|
|
8476
8820
|
const snapHere = facts.__scopedSnapshot || null;
|
|
8477
8821
|
const lvlHere = (facts && typeof facts.__scopedClosureLevel === 'number' && facts.__scopedClosureLevel) || 0;
|
|
8478
8822
|
if (!snapHere) return true;
|
|
8479
|
-
if (
|
|
8823
|
+
if (info.exactLevel) {
|
|
8824
|
+
if (lvlHere !== info.requiredLevel) return true;
|
|
8825
|
+
} else if (lvlHere < info.requiredLevel) {
|
|
8826
|
+
return true;
|
|
8827
|
+
}
|
|
8480
8828
|
}
|
|
8481
8829
|
|
|
8482
8830
|
// Optimization: if the rule head is **structurally ground** (no vars anywhere, even inside
|
|
@@ -8676,6 +9024,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8676
9024
|
|
|
8677
9025
|
const outcome = __emitForwardRuleSolution(r, entry.ruleIndex, s);
|
|
8678
9026
|
if (outcome.rulesChanged) {
|
|
9027
|
+
maxScopedClosurePriorityNeeded = Math.max(
|
|
9028
|
+
__computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
|
|
9029
|
+
__computeForwardRuleScopedStrata(forwardRules),
|
|
9030
|
+
);
|
|
8679
9031
|
agendaIndex = makeSinglePremiseAgendaIndex(forwardRules, backRules);
|
|
8680
9032
|
agendaCursor = 0;
|
|
8681
9033
|
}
|
|
@@ -8689,7 +9041,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8689
9041
|
for (let i = 0; i < forwardRules.length; i++) {
|
|
8690
9042
|
const r = forwardRules[i];
|
|
8691
9043
|
if (agendaIndex.indexed.has(r)) continue;
|
|
8692
|
-
if (r.__needsForwardSkipCheck && __skipForwardRuleNow(r)) continue;
|
|
9044
|
+
if ((r.__needsForwardSkipCheck || (r.__scopedStratumInfo && r.__scopedStratumInfo.needsSnap)) && __skipForwardRuleNow(r)) continue;
|
|
8693
9045
|
|
|
8694
9046
|
const headIsStrictGround = r.__headIsStrictGround;
|
|
8695
9047
|
const maxSols = r.isFuse || headIsStrictGround ? 1 : undefined;
|
|
@@ -8710,6 +9062,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8710
9062
|
for (const s of sols) {
|
|
8711
9063
|
const outcome = __emitForwardRuleSolution(r, i, s);
|
|
8712
9064
|
if (outcome.rulesChanged) {
|
|
9065
|
+
maxScopedClosurePriorityNeeded = Math.max(
|
|
9066
|
+
__computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
|
|
9067
|
+
__computeForwardRuleScopedStrata(forwardRules),
|
|
9068
|
+
);
|
|
8713
9069
|
agendaIndex = makeSinglePremiseAgendaIndex(forwardRules, backRules);
|
|
8714
9070
|
agendaCursor = 0;
|
|
8715
9071
|
}
|
|
@@ -8737,8 +9093,8 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8737
9093
|
// Rules may have been added dynamically (rule-producing triples), possibly
|
|
8738
9094
|
// introducing scoped builtins and/or higher closure priorities.
|
|
8739
9095
|
maxScopedClosurePriorityNeeded = Math.max(
|
|
8740
|
-
maxScopedClosurePriorityNeeded,
|
|
8741
9096
|
__computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
|
|
9097
|
+
__computeForwardRuleScopedStrata(forwardRules),
|
|
8742
9098
|
);
|
|
8743
9099
|
|
|
8744
9100
|
// If there are no scoped builtins in the entire program, Phase B is pure
|
|
@@ -8756,8 +9112,8 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8756
9112
|
|
|
8757
9113
|
// Phase B can also derive rule-producing triples.
|
|
8758
9114
|
maxScopedClosurePriorityNeeded = Math.max(
|
|
8759
|
-
maxScopedClosurePriorityNeeded,
|
|
8760
9115
|
__computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
|
|
9116
|
+
__computeForwardRuleScopedStrata(forwardRules),
|
|
8761
9117
|
);
|
|
8762
9118
|
|
|
8763
9119
|
if (!changedA && !changedB && scopedClosureLevel >= maxScopedClosurePriorityNeeded) break;
|
|
@@ -9647,15 +10003,6 @@ function isIdentChar(c) {
|
|
|
9647
10003
|
return c === ':' || isPnChars(c);
|
|
9648
10004
|
}
|
|
9649
10005
|
|
|
9650
|
-
function canContinueAfterDot(next) {
|
|
9651
|
-
// PN_LOCAL allows '.' but it cannot appear at the end.
|
|
9652
|
-
// We include '.' only if it is followed by something that could continue a name.
|
|
9653
|
-
if (next === null) return false;
|
|
9654
|
-
if (isIdentChar(next)) return true;
|
|
9655
|
-
if (next === '%' || next === '\\') return true;
|
|
9656
|
-
return false;
|
|
9657
|
-
}
|
|
9658
|
-
|
|
9659
10006
|
function isForbiddenNoncharacterCodePoint(cp) {
|
|
9660
10007
|
return (cp & 0xffff) === 0xfffe || (cp & 0xffff) === 0xffff;
|
|
9661
10008
|
}
|