eyeling 1.11.20 → 1.11.21
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 +4 -4
- package/eyeling.js +20 -17
- package/lib/builtins.js +3 -2
- package/lib/engine.js +17 -15
- package/package.json +1 -1
package/HANDBOOK.md
CHANGED
|
@@ -182,7 +182,7 @@ Eyeling interns IRIs and Literals by string value. Interning is a quiet performa
|
|
|
182
182
|
|
|
183
183
|
In addition, interned **Iri**/**Literal** terms (and generated **Blank** terms) get a small, non-enumerable integer id `.__tid` that is stable for the lifetime of the process. This `__tid` is used as the engine’s “fast key”:
|
|
184
184
|
|
|
185
|
-
- fact indexes (`__byPred` / `__byPS` / `__byPO`) key by `__tid` values (predicate buckets are keyed by `predicate.__tid`, and PS/PO buckets are keyed by the subject/object `.__tid`)
|
|
185
|
+
- fact indexes (`__byPred` / `__byPS` / `__byPO`) key by `__tid` values **and store fact *indices*** (predicate buckets are keyed by `predicate.__tid`, and PS/PO buckets are keyed by the subject/object `.__tid`; buckets contain integer indices into the `facts` array)
|
|
186
186
|
- duplicate detection uses `"sid pid oid"` where each component is a `__tid`
|
|
187
187
|
- unification/equality has an early-out when two terms share the same `__tid`
|
|
188
188
|
|
|
@@ -441,9 +441,9 @@ Facts live in an array `facts: Triple[]`.
|
|
|
441
441
|
|
|
442
442
|
Eyeling attaches hidden (non-enumerable) index fields:
|
|
443
443
|
|
|
444
|
-
* `facts.__byPred: Map<predicateId,
|
|
445
|
-
* `facts.__byPS: Map<predicateId, Map<termId,
|
|
446
|
-
* `facts.__byPO: Map<predicateId, Map<termId,
|
|
444
|
+
* `facts.__byPred: Map<predicateId, number[]>` where each entry is an index into `facts` (and `predicateId` is `predicate.__tid`)
|
|
445
|
+
* `facts.__byPS: Map<predicateId, Map<termId, number[]>>` where each entry is an index into `facts` (and `termId` is `term.__tid`)
|
|
446
|
+
* `facts.__byPO: Map<predicateId, Map<termId, number[]>>` where each entry is an index into `facts` (and `termId` is `term.__tid`)
|
|
447
447
|
* `facts.__keySet: Set<string>` for a fast-path `"sid pid oid"` key (all three are `__tid` values)
|
|
448
448
|
|
|
449
449
|
`termFastKey(term)` returns a `termId` (`term.__tid`) for **Iri**, **Literal**, and **Blank** terms, and `null` for structured terms (lists, quoted graphs) and variables.
|
package/eyeling.js
CHANGED
|
@@ -1027,14 +1027,15 @@ function __rdfListObjectsForSP(facts, predIri, subj) {
|
|
|
1027
1027
|
const ps = facts.__byPS.get(pk);
|
|
1028
1028
|
if (ps) {
|
|
1029
1029
|
const bucket = ps.get(sk);
|
|
1030
|
-
if (bucket && bucket.length) return bucket.map((
|
|
1030
|
+
if (bucket && bucket.length) return bucket.map((i) => facts[i].o);
|
|
1031
1031
|
}
|
|
1032
1032
|
}
|
|
1033
1033
|
|
|
1034
1034
|
// Fallback scan (covers non-indexable terms)
|
|
1035
1035
|
const pb = facts.__byPred.get(pk) || [];
|
|
1036
1036
|
const out = [];
|
|
1037
|
-
for (const
|
|
1037
|
+
for (const i of pb) {
|
|
1038
|
+
const tr = facts[i];
|
|
1038
1039
|
if (termsEqual(tr.s, subj)) out.push(tr.o);
|
|
1039
1040
|
}
|
|
1040
1041
|
return out;
|
|
@@ -4955,9 +4956,9 @@ function alphaEqGraphTriples(xs, ys) {
|
|
|
4955
4956
|
// ===========================================================================
|
|
4956
4957
|
//
|
|
4957
4958
|
// Facts:
|
|
4958
|
-
// - __byPred: Map<predicateId,
|
|
4959
|
-
// - __byPS: Map<predicateId, Map<subjectId,
|
|
4960
|
-
// - __byPO: Map<predicateId, Map<objectId,
|
|
4959
|
+
// - __byPred: Map<predicateId, number[]> (indices into facts array)
|
|
4960
|
+
// - __byPS: Map<predicateId, Map<subjectId, number[]>>
|
|
4961
|
+
// - __byPO: Map<predicateId, Map<objectId, number[]>>
|
|
4961
4962
|
// - __keySet: Set<"S\tP\tO"> for Iri/Literal/Blank-only triples (fast dup check)
|
|
4962
4963
|
//
|
|
4963
4964
|
// Backward rules:
|
|
@@ -5001,10 +5002,10 @@ function ensureFactIndexes(facts) {
|
|
|
5001
5002
|
writable: true,
|
|
5002
5003
|
});
|
|
5003
5004
|
|
|
5004
|
-
for (
|
|
5005
|
+
for (let i = 0; i < facts.length; i++) indexFact(facts, facts[i], i);
|
|
5005
5006
|
}
|
|
5006
5007
|
|
|
5007
|
-
function indexFact(facts, tr) {
|
|
5008
|
+
function indexFact(facts, tr, idx) {
|
|
5008
5009
|
if (tr.p instanceof Iri) {
|
|
5009
5010
|
// Use predicate term id as the primary key to avoid hashing long IRI strings.
|
|
5010
5011
|
const pk = tr.p.__tid;
|
|
@@ -5014,7 +5015,7 @@ function indexFact(facts, tr) {
|
|
|
5014
5015
|
pb = [];
|
|
5015
5016
|
facts.__byPred.set(pk, pb);
|
|
5016
5017
|
}
|
|
5017
|
-
pb.push(
|
|
5018
|
+
pb.push(idx);
|
|
5018
5019
|
|
|
5019
5020
|
const sk = termFastKey(tr.s);
|
|
5020
5021
|
if (sk !== null) {
|
|
@@ -5028,7 +5029,7 @@ function indexFact(facts, tr) {
|
|
|
5028
5029
|
psb = [];
|
|
5029
5030
|
ps.set(sk, psb);
|
|
5030
5031
|
}
|
|
5031
|
-
psb.push(
|
|
5032
|
+
psb.push(idx);
|
|
5032
5033
|
}
|
|
5033
5034
|
|
|
5034
5035
|
const ok = termFastKey(tr.o);
|
|
@@ -5043,7 +5044,7 @@ function indexFact(facts, tr) {
|
|
|
5043
5044
|
pob = [];
|
|
5044
5045
|
po.set(ok, pob);
|
|
5045
5046
|
}
|
|
5046
|
-
pob.push(
|
|
5047
|
+
pob.push(idx);
|
|
5047
5048
|
}
|
|
5048
5049
|
}
|
|
5049
5050
|
|
|
@@ -5060,14 +5061,14 @@ function candidateFacts(facts, goal) {
|
|
|
5060
5061
|
const sk = termFastKey(goal.s);
|
|
5061
5062
|
const ok = termFastKey(goal.o);
|
|
5062
5063
|
|
|
5063
|
-
/** @type {
|
|
5064
|
+
/** @type {number[] | null} */
|
|
5064
5065
|
let byPS = null;
|
|
5065
5066
|
if (sk !== null) {
|
|
5066
5067
|
const ps = facts.__byPS.get(pk);
|
|
5067
5068
|
if (ps) byPS = ps.get(sk) || null;
|
|
5068
5069
|
}
|
|
5069
5070
|
|
|
5070
|
-
/** @type {
|
|
5071
|
+
/** @type {number[] | null} */
|
|
5071
5072
|
let byPO = null;
|
|
5072
5073
|
if (ok !== null) {
|
|
5073
5074
|
const po = facts.__byPO.get(pk);
|
|
@@ -5081,7 +5082,7 @@ function candidateFacts(facts, goal) {
|
|
|
5081
5082
|
return facts.__byPred.get(pk) || [];
|
|
5082
5083
|
}
|
|
5083
5084
|
|
|
5084
|
-
return
|
|
5085
|
+
return null;
|
|
5085
5086
|
}
|
|
5086
5087
|
|
|
5087
5088
|
function hasFactIndexed(facts, tr) {
|
|
@@ -5102,12 +5103,12 @@ function hasFactIndexed(facts, tr) {
|
|
|
5102
5103
|
// different existentials unless explicitly connected. Do NOT treat
|
|
5103
5104
|
// triples as duplicates modulo blank renaming, or you'll incorrectly
|
|
5104
5105
|
// drop facts like: _:sk_0 :x 8.0 (because _:b8 :x 8.0 exists).
|
|
5105
|
-
return pob.some((
|
|
5106
|
+
return pob.some((i) => triplesEqual(facts[i], tr));
|
|
5106
5107
|
}
|
|
5107
5108
|
}
|
|
5108
5109
|
|
|
5109
5110
|
const pb = facts.__byPred.get(pk) || [];
|
|
5110
|
-
return pb.some((
|
|
5111
|
+
return pb.some((i) => triplesEqual(facts[i], tr));
|
|
5111
5112
|
}
|
|
5112
5113
|
|
|
5113
5114
|
// Non-IRI predicate: fall back to strict triple equality.
|
|
@@ -5116,8 +5117,9 @@ function hasFactIndexed(facts, tr) {
|
|
|
5116
5117
|
|
|
5117
5118
|
function pushFactIndexed(facts, tr) {
|
|
5118
5119
|
ensureFactIndexes(facts);
|
|
5120
|
+
const idx = facts.length;
|
|
5119
5121
|
facts.push(tr);
|
|
5120
|
-
indexFact(facts, tr);
|
|
5122
|
+
indexFact(facts, tr, idx);
|
|
5121
5123
|
}
|
|
5122
5124
|
|
|
5123
5125
|
function ensureBackRuleIndexes(backRules) {
|
|
@@ -5934,7 +5936,8 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
|
|
|
5934
5936
|
// 4) Try to satisfy the goal from known facts
|
|
5935
5937
|
if (goal0.p instanceof Iri) {
|
|
5936
5938
|
const candidates = candidateFacts(facts, goal0);
|
|
5937
|
-
for (const
|
|
5939
|
+
for (const idx of candidates) {
|
|
5940
|
+
const f = facts[idx];
|
|
5938
5941
|
const mark = trail.length;
|
|
5939
5942
|
if (!unifyTripleTrail(goal0, f)) {
|
|
5940
5943
|
undoTo(mark);
|
package/lib/builtins.js
CHANGED
|
@@ -1015,14 +1015,15 @@ function __rdfListObjectsForSP(facts, predIri, subj) {
|
|
|
1015
1015
|
const ps = facts.__byPS.get(pk);
|
|
1016
1016
|
if (ps) {
|
|
1017
1017
|
const bucket = ps.get(sk);
|
|
1018
|
-
if (bucket && bucket.length) return bucket.map((
|
|
1018
|
+
if (bucket && bucket.length) return bucket.map((i) => facts[i].o);
|
|
1019
1019
|
}
|
|
1020
1020
|
}
|
|
1021
1021
|
|
|
1022
1022
|
// Fallback scan (covers non-indexable terms)
|
|
1023
1023
|
const pb = facts.__byPred.get(pk) || [];
|
|
1024
1024
|
const out = [];
|
|
1025
|
-
for (const
|
|
1025
|
+
for (const i of pb) {
|
|
1026
|
+
const tr = facts[i];
|
|
1026
1027
|
if (termsEqual(tr.s, subj)) out.push(tr.o);
|
|
1027
1028
|
}
|
|
1028
1029
|
return out;
|
package/lib/engine.js
CHANGED
|
@@ -579,9 +579,9 @@ function alphaEqGraphTriples(xs, ys) {
|
|
|
579
579
|
// ===========================================================================
|
|
580
580
|
//
|
|
581
581
|
// Facts:
|
|
582
|
-
// - __byPred: Map<predicateId,
|
|
583
|
-
// - __byPS: Map<predicateId, Map<subjectId,
|
|
584
|
-
// - __byPO: Map<predicateId, Map<objectId,
|
|
582
|
+
// - __byPred: Map<predicateId, number[]> (indices into facts array)
|
|
583
|
+
// - __byPS: Map<predicateId, Map<subjectId, number[]>>
|
|
584
|
+
// - __byPO: Map<predicateId, Map<objectId, number[]>>
|
|
585
585
|
// - __keySet: Set<"S\tP\tO"> for Iri/Literal/Blank-only triples (fast dup check)
|
|
586
586
|
//
|
|
587
587
|
// Backward rules:
|
|
@@ -625,10 +625,10 @@ function ensureFactIndexes(facts) {
|
|
|
625
625
|
writable: true,
|
|
626
626
|
});
|
|
627
627
|
|
|
628
|
-
for (
|
|
628
|
+
for (let i = 0; i < facts.length; i++) indexFact(facts, facts[i], i);
|
|
629
629
|
}
|
|
630
630
|
|
|
631
|
-
function indexFact(facts, tr) {
|
|
631
|
+
function indexFact(facts, tr, idx) {
|
|
632
632
|
if (tr.p instanceof Iri) {
|
|
633
633
|
// Use predicate term id as the primary key to avoid hashing long IRI strings.
|
|
634
634
|
const pk = tr.p.__tid;
|
|
@@ -638,7 +638,7 @@ function indexFact(facts, tr) {
|
|
|
638
638
|
pb = [];
|
|
639
639
|
facts.__byPred.set(pk, pb);
|
|
640
640
|
}
|
|
641
|
-
pb.push(
|
|
641
|
+
pb.push(idx);
|
|
642
642
|
|
|
643
643
|
const sk = termFastKey(tr.s);
|
|
644
644
|
if (sk !== null) {
|
|
@@ -652,7 +652,7 @@ function indexFact(facts, tr) {
|
|
|
652
652
|
psb = [];
|
|
653
653
|
ps.set(sk, psb);
|
|
654
654
|
}
|
|
655
|
-
psb.push(
|
|
655
|
+
psb.push(idx);
|
|
656
656
|
}
|
|
657
657
|
|
|
658
658
|
const ok = termFastKey(tr.o);
|
|
@@ -667,7 +667,7 @@ function indexFact(facts, tr) {
|
|
|
667
667
|
pob = [];
|
|
668
668
|
po.set(ok, pob);
|
|
669
669
|
}
|
|
670
|
-
pob.push(
|
|
670
|
+
pob.push(idx);
|
|
671
671
|
}
|
|
672
672
|
}
|
|
673
673
|
|
|
@@ -684,14 +684,14 @@ function candidateFacts(facts, goal) {
|
|
|
684
684
|
const sk = termFastKey(goal.s);
|
|
685
685
|
const ok = termFastKey(goal.o);
|
|
686
686
|
|
|
687
|
-
/** @type {
|
|
687
|
+
/** @type {number[] | null} */
|
|
688
688
|
let byPS = null;
|
|
689
689
|
if (sk !== null) {
|
|
690
690
|
const ps = facts.__byPS.get(pk);
|
|
691
691
|
if (ps) byPS = ps.get(sk) || null;
|
|
692
692
|
}
|
|
693
693
|
|
|
694
|
-
/** @type {
|
|
694
|
+
/** @type {number[] | null} */
|
|
695
695
|
let byPO = null;
|
|
696
696
|
if (ok !== null) {
|
|
697
697
|
const po = facts.__byPO.get(pk);
|
|
@@ -705,7 +705,7 @@ function candidateFacts(facts, goal) {
|
|
|
705
705
|
return facts.__byPred.get(pk) || [];
|
|
706
706
|
}
|
|
707
707
|
|
|
708
|
-
return
|
|
708
|
+
return null;
|
|
709
709
|
}
|
|
710
710
|
|
|
711
711
|
function hasFactIndexed(facts, tr) {
|
|
@@ -726,12 +726,12 @@ function hasFactIndexed(facts, tr) {
|
|
|
726
726
|
// different existentials unless explicitly connected. Do NOT treat
|
|
727
727
|
// triples as duplicates modulo blank renaming, or you'll incorrectly
|
|
728
728
|
// drop facts like: _:sk_0 :x 8.0 (because _:b8 :x 8.0 exists).
|
|
729
|
-
return pob.some((
|
|
729
|
+
return pob.some((i) => triplesEqual(facts[i], tr));
|
|
730
730
|
}
|
|
731
731
|
}
|
|
732
732
|
|
|
733
733
|
const pb = facts.__byPred.get(pk) || [];
|
|
734
|
-
return pb.some((
|
|
734
|
+
return pb.some((i) => triplesEqual(facts[i], tr));
|
|
735
735
|
}
|
|
736
736
|
|
|
737
737
|
// Non-IRI predicate: fall back to strict triple equality.
|
|
@@ -740,8 +740,9 @@ function hasFactIndexed(facts, tr) {
|
|
|
740
740
|
|
|
741
741
|
function pushFactIndexed(facts, tr) {
|
|
742
742
|
ensureFactIndexes(facts);
|
|
743
|
+
const idx = facts.length;
|
|
743
744
|
facts.push(tr);
|
|
744
|
-
indexFact(facts, tr);
|
|
745
|
+
indexFact(facts, tr, idx);
|
|
745
746
|
}
|
|
746
747
|
|
|
747
748
|
function ensureBackRuleIndexes(backRules) {
|
|
@@ -1558,7 +1559,8 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
|
|
|
1558
1559
|
// 4) Try to satisfy the goal from known facts
|
|
1559
1560
|
if (goal0.p instanceof Iri) {
|
|
1560
1561
|
const candidates = candidateFacts(facts, goal0);
|
|
1561
|
-
for (const
|
|
1562
|
+
for (const idx of candidates) {
|
|
1563
|
+
const f = facts[idx];
|
|
1562
1564
|
const mark = trail.length;
|
|
1563
1565
|
if (!unifyTripleTrail(goal0, f)) {
|
|
1564
1566
|
undoTo(mark);
|