eyeling 1.25.1 → 1.25.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/eyeling.browser.js +189 -60
- package/eyeling.js +189 -60
- package/lib/engine.js +93 -21
- package/lib/lexer.js +81 -28
- package/lib/parser.js +15 -11
- package/package.json +1 -1
|
@@ -5849,6 +5849,14 @@ function __prepareForwardRule(r) {
|
|
|
5849
5849
|
configurable: true,
|
|
5850
5850
|
});
|
|
5851
5851
|
}
|
|
5852
|
+
if (!hasOwn.call(r, '__needsForwardSkipCheck')) {
|
|
5853
|
+
Object.defineProperty(r, '__needsForwardSkipCheck', {
|
|
5854
|
+
value: !!(r.__headIsStrictGround || (r.__scopedSkipInfo && r.__scopedSkipInfo.needsSnap)),
|
|
5855
|
+
enumerable: false,
|
|
5856
|
+
writable: false,
|
|
5857
|
+
configurable: true,
|
|
5858
|
+
});
|
|
5859
|
+
}
|
|
5852
5860
|
}
|
|
5853
5861
|
|
|
5854
5862
|
function __graphTriplesOrTrue(term) {
|
|
@@ -6167,6 +6175,11 @@ function skolemizeTermForHeadBlanks(t, headBlankLabels, mapping, skCounter, firi
|
|
|
6167
6175
|
}
|
|
6168
6176
|
|
|
6169
6177
|
function skolemizeTripleForHeadBlanks(tr, headBlankLabels, mapping, skCounter, firingKey, globalMap) {
|
|
6178
|
+
// Fast path: the common case has no explicit head blanks. Do not allocate a
|
|
6179
|
+
// replacement Triple or compute a firing key when skolemization cannot change
|
|
6180
|
+
// anything. This matters for long single-premise chains such as
|
|
6181
|
+
// deep-taxonomy-100000, where every derived head triple is otherwise copied.
|
|
6182
|
+
if (!headBlankLabels || headBlankLabels.size === 0) return tr;
|
|
6170
6183
|
return new Triple(
|
|
6171
6184
|
skolemizeTermForHeadBlanks(tr.s, headBlankLabels, mapping, skCounter, firingKey, globalMap),
|
|
6172
6185
|
skolemizeTermForHeadBlanks(tr.p, headBlankLabels, mapping, skCounter, firingKey, globalMap),
|
|
@@ -6642,6 +6655,35 @@ function ensureFactIndexes(facts) {
|
|
|
6642
6655
|
for (let i = 0; i < facts.length; i++) indexFact(facts, facts[i], i, false);
|
|
6643
6656
|
}
|
|
6644
6657
|
|
|
6658
|
+
function cloneFactIndexesForSnapshot(src, dest) {
|
|
6659
|
+
ensureFactIndexes(src);
|
|
6660
|
+
|
|
6661
|
+
function cloneArrayMap(map) {
|
|
6662
|
+
const out = new Map();
|
|
6663
|
+
for (const [k, arr] of map) out.set(k, arr.slice());
|
|
6664
|
+
return out;
|
|
6665
|
+
}
|
|
6666
|
+
|
|
6667
|
+
function cloneNestedArrayMap(map) {
|
|
6668
|
+
const out = new Map();
|
|
6669
|
+
for (const [k, inner] of map) {
|
|
6670
|
+
const innerOut = new Map();
|
|
6671
|
+
for (const [k2, arr] of inner) innerOut.set(k2, arr.slice());
|
|
6672
|
+
out.set(k, innerOut);
|
|
6673
|
+
}
|
|
6674
|
+
return out;
|
|
6675
|
+
}
|
|
6676
|
+
|
|
6677
|
+
Object.defineProperty(dest, '__byPred', { value: cloneArrayMap(src.__byPred), enumerable: false, writable: true });
|
|
6678
|
+
Object.defineProperty(dest, '__byPS', { value: cloneNestedArrayMap(src.__byPS), enumerable: false, writable: true });
|
|
6679
|
+
Object.defineProperty(dest, '__byPO', { value: cloneNestedArrayMap(src.__byPO), enumerable: false, writable: true });
|
|
6680
|
+
Object.defineProperty(dest, '__wildPred', { value: src.__wildPred.slice(), enumerable: false, writable: true });
|
|
6681
|
+
Object.defineProperty(dest, '__wildPS', { value: cloneArrayMap(src.__wildPS), enumerable: false, writable: true });
|
|
6682
|
+
Object.defineProperty(dest, '__wildPO', { value: cloneArrayMap(src.__wildPO), enumerable: false, writable: true });
|
|
6683
|
+
Object.defineProperty(dest, '__keySet', { value: new Set(src.__keySet), enumerable: false, writable: true });
|
|
6684
|
+
Object.defineProperty(dest, '__keySetComplete', { value: !!src.__keySetComplete, enumerable: false, writable: true });
|
|
6685
|
+
}
|
|
6686
|
+
|
|
6645
6687
|
function indexFact(facts, tr, idx, addKeySet = true) {
|
|
6646
6688
|
const sk = termFastKey(tr.s);
|
|
6647
6689
|
const ok = termFastKey(tr.o);
|
|
@@ -6928,13 +6970,20 @@ function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
|
|
|
6928
6970
|
if (!isSinglePremiseAgendaRuleSafe(r, backRules)) continue;
|
|
6929
6971
|
|
|
6930
6972
|
const goal = r.premise[0];
|
|
6973
|
+
const goalSKey = termFastKey(goal.s);
|
|
6974
|
+
const goalOKey = termFastKey(goal.o);
|
|
6975
|
+
const fastSubjectVar = goal.p instanceof Iri && goal.s instanceof Var && goalOKey !== null ? goal.s.name : null;
|
|
6976
|
+
const fastObjectVar = goal.p instanceof Iri && goal.o instanceof Var && goalSKey !== null ? goal.o.name : null;
|
|
6931
6977
|
const entry = {
|
|
6932
6978
|
rule: r,
|
|
6933
6979
|
ruleIndex: i,
|
|
6934
6980
|
goal,
|
|
6935
6981
|
goalPredTid: goal.p instanceof Iri ? goal.p.__tid : null,
|
|
6936
|
-
goalSKey
|
|
6937
|
-
goalOKey
|
|
6982
|
+
goalSKey,
|
|
6983
|
+
goalOKey,
|
|
6984
|
+
needsSkipCheck: !!r.__needsForwardSkipCheck,
|
|
6985
|
+
fastSubjectVar,
|
|
6986
|
+
fastObjectVar,
|
|
6938
6987
|
};
|
|
6939
6988
|
|
|
6940
6989
|
index.indexed.add(r);
|
|
@@ -8395,7 +8444,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8395
8444
|
|
|
8396
8445
|
function makeScopedSnapshot() {
|
|
8397
8446
|
const snap = facts.slice();
|
|
8398
|
-
|
|
8447
|
+
cloneFactIndexesForSnapshot(facts, snap);
|
|
8399
8448
|
Object.defineProperty(snap, '__scopedSnapshot', {
|
|
8400
8449
|
value: snap,
|
|
8401
8450
|
enumerable: false,
|
|
@@ -8449,10 +8498,21 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8449
8498
|
let changedHere = false;
|
|
8450
8499
|
let rulesChanged = false;
|
|
8451
8500
|
|
|
8452
|
-
// IMPORTANT: one skolem map per *rule firing
|
|
8501
|
+
// IMPORTANT: one skolem map per *rule firing*. Instantiate premise
|
|
8502
|
+
// triples and build the firing key lazily: normal CLI runs do not capture
|
|
8503
|
+
// proof records, and most rules have no explicit head blanks, so the eager
|
|
8504
|
+
// work was pure allocation on large forward chains.
|
|
8453
8505
|
const skMap = {};
|
|
8454
|
-
|
|
8455
|
-
|
|
8506
|
+
let instantiatedPremises = null;
|
|
8507
|
+
let fireKey = null;
|
|
8508
|
+
function getInstantiatedPremises() {
|
|
8509
|
+
if (instantiatedPremises === null) instantiatedPremises = r.premise.map((b) => applySubstTriple(b, s));
|
|
8510
|
+
return instantiatedPremises;
|
|
8511
|
+
}
|
|
8512
|
+
function getFireKey() {
|
|
8513
|
+
if (fireKey === null) fireKey = __firingKey(ruleIndex, getInstantiatedPremises());
|
|
8514
|
+
return fireKey;
|
|
8515
|
+
}
|
|
8456
8516
|
|
|
8457
8517
|
// Support "dynamic" rule heads where the consequent is a term that
|
|
8458
8518
|
// (after substitution) evaluates to a quoted formula.
|
|
@@ -8505,7 +8565,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8505
8565
|
if (isFwRuleTriple || isBwRuleTriple) {
|
|
8506
8566
|
if (!hasFactIndexed(facts, instantiated)) {
|
|
8507
8567
|
pushFactIndexed(facts, instantiated);
|
|
8508
|
-
const df = makeDerivedRecord(instantiated, r,
|
|
8568
|
+
const df = makeDerivedRecord(instantiated, r, getInstantiatedPremises(), s, captureExplanations);
|
|
8509
8569
|
derivedForward.push(df);
|
|
8510
8570
|
if (typeof onDerived === 'function') onDerived(df);
|
|
8511
8571
|
changedHere = true;
|
|
@@ -8553,20 +8613,23 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8553
8613
|
}
|
|
8554
8614
|
|
|
8555
8615
|
// Only skolemize blank nodes that occur explicitly in the rule head
|
|
8556
|
-
const inst =
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
|
|
8563
|
-
|
|
8616
|
+
const inst =
|
|
8617
|
+
headBlankLabelsHere && headBlankLabelsHere.size
|
|
8618
|
+
? skolemizeTripleForHeadBlanks(
|
|
8619
|
+
instantiated,
|
|
8620
|
+
headBlankLabelsHere,
|
|
8621
|
+
skMap,
|
|
8622
|
+
skCounter,
|
|
8623
|
+
getFireKey(),
|
|
8624
|
+
headSkolemCache,
|
|
8625
|
+
)
|
|
8626
|
+
: instantiated;
|
|
8564
8627
|
|
|
8565
8628
|
if (!isGroundTriple(inst)) continue;
|
|
8566
8629
|
if (hasFactIndexed(facts, inst)) continue;
|
|
8567
8630
|
|
|
8568
8631
|
pushFactIndexed(facts, inst);
|
|
8569
|
-
const df = makeDerivedRecord(inst, r,
|
|
8632
|
+
const df = makeDerivedRecord(inst, r, getInstantiatedPremises(), s, captureExplanations);
|
|
8570
8633
|
derivedForward.push(df);
|
|
8571
8634
|
if (typeof onDerived === 'function') onDerived(df);
|
|
8572
8635
|
|
|
@@ -8593,10 +8656,19 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8593
8656
|
for (let ci = 0; ci < total; ci++) {
|
|
8594
8657
|
const entry = ci < candidates.exactLen ? candidates.exact[ci] : candidates.wild[ci - candidates.exactLen];
|
|
8595
8658
|
const r = entry.rule;
|
|
8596
|
-
if (__skipForwardRuleNow(r)) continue;
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
if (
|
|
8659
|
+
if (entry.needsSkipCheck && __skipForwardRuleNow(r)) continue;
|
|
8660
|
+
|
|
8661
|
+
let s;
|
|
8662
|
+
if (entry.fastSubjectVar !== null) {
|
|
8663
|
+
s = __emptySubst();
|
|
8664
|
+
s[entry.fastSubjectVar] = fact.s;
|
|
8665
|
+
} else if (entry.fastObjectVar !== null) {
|
|
8666
|
+
s = __emptySubst();
|
|
8667
|
+
s[entry.fastObjectVar] = fact.o;
|
|
8668
|
+
} else {
|
|
8669
|
+
s = unifyTriple(entry.goal, fact, __emptySubst());
|
|
8670
|
+
if (s === null) continue;
|
|
8671
|
+
}
|
|
8600
8672
|
|
|
8601
8673
|
const outcome = __emitForwardRuleSolution(r, entry.ruleIndex, s);
|
|
8602
8674
|
if (outcome.rulesChanged) {
|
|
@@ -8613,7 +8685,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8613
8685
|
for (let i = 0; i < forwardRules.length; i++) {
|
|
8614
8686
|
const r = forwardRules[i];
|
|
8615
8687
|
if (agendaIndex.indexed.has(r)) continue;
|
|
8616
|
-
if (__skipForwardRuleNow(r)) continue;
|
|
8688
|
+
if (r.__needsForwardSkipCheck && __skipForwardRuleNow(r)) continue;
|
|
8617
8689
|
|
|
8618
8690
|
const headIsStrictGround = r.__headIsStrictGround;
|
|
8619
8691
|
const maxSols = r.isFuse || headIsStrictGround ? 1 : undefined;
|
|
@@ -10639,6 +10711,16 @@ function normalizeRdfCompatibility(inputText) {
|
|
|
10639
10711
|
return text;
|
|
10640
10712
|
}
|
|
10641
10713
|
|
|
10714
|
+
|
|
10715
|
+
function isNumericLikeIdentifier(word) {
|
|
10716
|
+
if (typeof word !== 'string' || word.length === 0) return false;
|
|
10717
|
+
for (let j = 0; j < word.length; j++) {
|
|
10718
|
+
const code = word.charCodeAt(j);
|
|
10719
|
+
if (!((code >= 48 && code <= 57) || code === 46 || code === 45)) return false;
|
|
10720
|
+
}
|
|
10721
|
+
return true;
|
|
10722
|
+
}
|
|
10723
|
+
|
|
10642
10724
|
function lex(inputText, opts = {}) {
|
|
10643
10725
|
const rdf = !!(opts && opts.rdf);
|
|
10644
10726
|
if (rdf) inputText = normalizeRdfCompatibility(inputText);
|
|
@@ -10806,22 +10888,47 @@ function lex(inputText, opts = {}) {
|
|
|
10806
10888
|
continue;
|
|
10807
10889
|
}
|
|
10808
10890
|
|
|
10809
|
-
// 5) Single-character punctuation
|
|
10810
|
-
|
|
10811
|
-
|
|
10812
|
-
|
|
10813
|
-
|
|
10814
|
-
|
|
10815
|
-
|
|
10816
|
-
|
|
10817
|
-
|
|
10818
|
-
|
|
10819
|
-
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
|
|
10824
|
-
|
|
10891
|
+
// 5) Single-character punctuation. Use a switch rather than allocating a
|
|
10892
|
+
// mapping object for every punctuation token in large inputs.
|
|
10893
|
+
switch (c) {
|
|
10894
|
+
case '{':
|
|
10895
|
+
tokens.push(new Token('LBrace', null, i));
|
|
10896
|
+
i++;
|
|
10897
|
+
continue;
|
|
10898
|
+
case '}':
|
|
10899
|
+
tokens.push(new Token('RBrace', null, i));
|
|
10900
|
+
i++;
|
|
10901
|
+
continue;
|
|
10902
|
+
case '(':
|
|
10903
|
+
tokens.push(new Token('LParen', null, i));
|
|
10904
|
+
i++;
|
|
10905
|
+
continue;
|
|
10906
|
+
case ')':
|
|
10907
|
+
tokens.push(new Token('RParen', null, i));
|
|
10908
|
+
i++;
|
|
10909
|
+
continue;
|
|
10910
|
+
case '[':
|
|
10911
|
+
tokens.push(new Token('LBracket', null, i));
|
|
10912
|
+
i++;
|
|
10913
|
+
continue;
|
|
10914
|
+
case ']':
|
|
10915
|
+
tokens.push(new Token('RBracket', null, i));
|
|
10916
|
+
i++;
|
|
10917
|
+
continue;
|
|
10918
|
+
case ';':
|
|
10919
|
+
tokens.push(new Token('Semicolon', null, i));
|
|
10920
|
+
i++;
|
|
10921
|
+
continue;
|
|
10922
|
+
case ',':
|
|
10923
|
+
tokens.push(new Token('Comma', null, i));
|
|
10924
|
+
i++;
|
|
10925
|
+
continue;
|
|
10926
|
+
case '.':
|
|
10927
|
+
tokens.push(new Token('Dot', null, i));
|
|
10928
|
+
i++;
|
|
10929
|
+
continue;
|
|
10930
|
+
default:
|
|
10931
|
+
break;
|
|
10825
10932
|
}
|
|
10826
10933
|
|
|
10827
10934
|
// String literal: short "..." or long """..."""
|
|
@@ -10880,26 +10987,36 @@ function lex(inputText, opts = {}) {
|
|
|
10880
10987
|
continue;
|
|
10881
10988
|
}
|
|
10882
10989
|
|
|
10883
|
-
// Short string literal " ... "
|
|
10990
|
+
// Short string literal " ... ". Most data files contain plain
|
|
10991
|
+
// unescaped labels; keep that path slice-based and avoid building an
|
|
10992
|
+
// intermediate character array + raw quoted string.
|
|
10884
10993
|
i++; // consume opening "
|
|
10885
|
-
const
|
|
10994
|
+
const contentStart = i;
|
|
10995
|
+
let sChars = null;
|
|
10996
|
+
let closed = false;
|
|
10886
10997
|
while (i < n) {
|
|
10887
10998
|
const cc = chars[i];
|
|
10888
10999
|
i++;
|
|
10889
11000
|
if (cc === '\\') {
|
|
11001
|
+
if (sChars === null) sChars = [sliceChars(contentStart, i - 1)];
|
|
10890
11002
|
if (i < n) {
|
|
10891
11003
|
const esc = chars[i];
|
|
10892
11004
|
i++;
|
|
10893
11005
|
sChars.push('\\');
|
|
10894
11006
|
sChars.push(esc);
|
|
11007
|
+
} else {
|
|
11008
|
+
sChars.push('\\');
|
|
10895
11009
|
}
|
|
10896
11010
|
continue;
|
|
10897
11011
|
}
|
|
10898
|
-
if (cc === '"')
|
|
10899
|
-
|
|
11012
|
+
if (cc === '"') {
|
|
11013
|
+
closed = true;
|
|
11014
|
+
break;
|
|
11015
|
+
}
|
|
11016
|
+
if (sChars !== null) sChars.push(cc);
|
|
10900
11017
|
}
|
|
10901
|
-
const
|
|
10902
|
-
const decoded = decodeN3StringEscapes(
|
|
11018
|
+
const rawContent = sChars === null ? sliceChars(contentStart, closed ? i - 1 : i) : sChars.join('');
|
|
11019
|
+
const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
|
|
10903
11020
|
assertValidStringLiteralValue(decoded, start);
|
|
10904
11021
|
const s = JSON.stringify(decoded); // canonical short quoted form
|
|
10905
11022
|
tokens.push(new Token('Literal', s, start));
|
|
@@ -10964,24 +11081,32 @@ function lex(inputText, opts = {}) {
|
|
|
10964
11081
|
|
|
10965
11082
|
// Short string literal ' ... '
|
|
10966
11083
|
i++; // consume opening '
|
|
10967
|
-
const
|
|
11084
|
+
const contentStart = i;
|
|
11085
|
+
let sChars = null;
|
|
11086
|
+
let closed = false;
|
|
10968
11087
|
while (i < n) {
|
|
10969
11088
|
const cc = chars[i];
|
|
10970
11089
|
i++;
|
|
10971
11090
|
if (cc === '\\') {
|
|
11091
|
+
if (sChars === null) sChars = [sliceChars(contentStart, i - 1)];
|
|
10972
11092
|
if (i < n) {
|
|
10973
11093
|
const esc = chars[i];
|
|
10974
11094
|
i++;
|
|
10975
11095
|
sChars.push('\\');
|
|
10976
11096
|
sChars.push(esc);
|
|
11097
|
+
} else {
|
|
11098
|
+
sChars.push('\\');
|
|
10977
11099
|
}
|
|
10978
11100
|
continue;
|
|
10979
11101
|
}
|
|
10980
|
-
if (cc === "'")
|
|
10981
|
-
|
|
11102
|
+
if (cc === "'") {
|
|
11103
|
+
closed = true;
|
|
11104
|
+
break;
|
|
11105
|
+
}
|
|
11106
|
+
if (sChars !== null) sChars.push(cc);
|
|
10982
11107
|
}
|
|
10983
|
-
const
|
|
10984
|
-
const decoded = decodeN3StringEscapes(
|
|
11108
|
+
const rawContent = sChars === null ? sliceChars(contentStart, closed ? i - 1 : i) : sChars.join('');
|
|
11109
|
+
const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
|
|
10985
11110
|
assertValidStringLiteralValue(decoded, start);
|
|
10986
11111
|
const s = JSON.stringify(decoded); // canonical short quoted form
|
|
10987
11112
|
tokens.push(new Token('Literal', s, start));
|
|
@@ -11106,7 +11231,7 @@ function lex(inputText, opts = {}) {
|
|
|
11106
11231
|
}
|
|
11107
11232
|
if (word === 'true' || word === 'false') {
|
|
11108
11233
|
tokens.push(new Token('Literal', word, start));
|
|
11109
|
-
} else if (
|
|
11234
|
+
} else if (isNumericLikeIdentifier(word)) {
|
|
11110
11235
|
tokens.push(new Token('Literal', word, start));
|
|
11111
11236
|
} else {
|
|
11112
11237
|
tokens.push(new Token('Ident', word, start));
|
|
@@ -11691,7 +11816,7 @@ class Parser {
|
|
|
11691
11816
|
} else if (tok2.typ === 'Ident') {
|
|
11692
11817
|
const qn = tok2.value || '';
|
|
11693
11818
|
if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok2, qn);
|
|
11694
|
-
assertValidQNamePrefix(qn.
|
|
11819
|
+
assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok2, '@prefix directive IRI');
|
|
11695
11820
|
iri = this.prefixes.expandQName(qn);
|
|
11696
11821
|
} else {
|
|
11697
11822
|
this.fail(`Expected IRI after @prefix, got ${tok2.toString()}`, tok2);
|
|
@@ -11708,7 +11833,7 @@ class Parser {
|
|
|
11708
11833
|
} else if (tok.typ === 'Ident') {
|
|
11709
11834
|
const qn = tok.value || '';
|
|
11710
11835
|
if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok, qn);
|
|
11711
|
-
assertValidQNamePrefix(qn.
|
|
11836
|
+
assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok, '@base directive IRI');
|
|
11712
11837
|
iri = this.prefixes.expandQName(qn);
|
|
11713
11838
|
} else {
|
|
11714
11839
|
this.fail(`Expected IRI after @base, got ${tok.toString()}`, tok);
|
|
@@ -11737,7 +11862,7 @@ class Parser {
|
|
|
11737
11862
|
} else if (tok2.typ === 'Ident') {
|
|
11738
11863
|
const qn = tok2.value || '';
|
|
11739
11864
|
if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok2, qn);
|
|
11740
|
-
assertValidQNamePrefix(qn.
|
|
11865
|
+
assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok2, '@prefix directive IRI');
|
|
11741
11866
|
iri = this.prefixes.expandQName(qn);
|
|
11742
11867
|
} else {
|
|
11743
11868
|
this.fail(`Expected IRI after PREFIX, got ${tok2.toString()}`, tok2);
|
|
@@ -11758,7 +11883,7 @@ class Parser {
|
|
|
11758
11883
|
} else if (tok.typ === 'Ident') {
|
|
11759
11884
|
const qn = tok.value || '';
|
|
11760
11885
|
if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok, qn);
|
|
11761
|
-
assertValidQNamePrefix(qn.
|
|
11886
|
+
assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok, 'BASE directive IRI');
|
|
11762
11887
|
iri = this.prefixes.expandQName(qn);
|
|
11763
11888
|
} else {
|
|
11764
11889
|
this.fail(`Expected IRI after BASE, got ${tok.toString()}`, tok);
|
|
@@ -11805,14 +11930,18 @@ class Parser {
|
|
|
11805
11930
|
const name = val || '';
|
|
11806
11931
|
if (name === 'a') {
|
|
11807
11932
|
return internIri(RDF_NS + 'type');
|
|
11808
|
-
}
|
|
11933
|
+
}
|
|
11934
|
+
const sep = name.indexOf(':');
|
|
11935
|
+
if (sep === 1 && name.charCodeAt(0) === 95) {
|
|
11809
11936
|
return new Blank(name);
|
|
11810
|
-
} else if (name.includes(':')) {
|
|
11811
|
-
assertValidQNamePrefix(name.split(':', 1)[0], this.fail.bind(this), tok);
|
|
11812
|
-
return internIri(this.prefixes.expandQName(name));
|
|
11813
|
-
} else {
|
|
11814
|
-
failInvalidKeywordLikeIdent(this.fail.bind(this), tok, name);
|
|
11815
11937
|
}
|
|
11938
|
+
if (sep >= 0) {
|
|
11939
|
+
const prefixName = name.slice(0, sep);
|
|
11940
|
+
assertValidQNamePrefix(prefixName, this.fail.bind(this), tok);
|
|
11941
|
+
const base = this.prefixes.map[prefixName] || '';
|
|
11942
|
+
return internIri(base ? base + name.slice(sep + 1) : name);
|
|
11943
|
+
}
|
|
11944
|
+
failInvalidKeywordLikeIdent(this.fail.bind(this), tok, name);
|
|
11816
11945
|
}
|
|
11817
11946
|
|
|
11818
11947
|
if (typ === 'Literal') {
|
|
@@ -11843,7 +11972,7 @@ class Parser {
|
|
|
11843
11972
|
} else if (dtTok.typ === 'Ident') {
|
|
11844
11973
|
const qn = dtTok.value || '';
|
|
11845
11974
|
if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), dtTok, qn);
|
|
11846
|
-
assertValidQNamePrefix(qn.
|
|
11975
|
+
assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), dtTok, 'datatype prefixed name');
|
|
11847
11976
|
dtIri = this.prefixes.expandQName(qn);
|
|
11848
11977
|
} else {
|
|
11849
11978
|
this.fail(`Expected datatype after ^^, got ${dtTok.toString()}`, dtTok);
|
package/eyeling.js
CHANGED
|
@@ -5849,6 +5849,14 @@ function __prepareForwardRule(r) {
|
|
|
5849
5849
|
configurable: true,
|
|
5850
5850
|
});
|
|
5851
5851
|
}
|
|
5852
|
+
if (!hasOwn.call(r, '__needsForwardSkipCheck')) {
|
|
5853
|
+
Object.defineProperty(r, '__needsForwardSkipCheck', {
|
|
5854
|
+
value: !!(r.__headIsStrictGround || (r.__scopedSkipInfo && r.__scopedSkipInfo.needsSnap)),
|
|
5855
|
+
enumerable: false,
|
|
5856
|
+
writable: false,
|
|
5857
|
+
configurable: true,
|
|
5858
|
+
});
|
|
5859
|
+
}
|
|
5852
5860
|
}
|
|
5853
5861
|
|
|
5854
5862
|
function __graphTriplesOrTrue(term) {
|
|
@@ -6167,6 +6175,11 @@ function skolemizeTermForHeadBlanks(t, headBlankLabels, mapping, skCounter, firi
|
|
|
6167
6175
|
}
|
|
6168
6176
|
|
|
6169
6177
|
function skolemizeTripleForHeadBlanks(tr, headBlankLabels, mapping, skCounter, firingKey, globalMap) {
|
|
6178
|
+
// Fast path: the common case has no explicit head blanks. Do not allocate a
|
|
6179
|
+
// replacement Triple or compute a firing key when skolemization cannot change
|
|
6180
|
+
// anything. This matters for long single-premise chains such as
|
|
6181
|
+
// deep-taxonomy-100000, where every derived head triple is otherwise copied.
|
|
6182
|
+
if (!headBlankLabels || headBlankLabels.size === 0) return tr;
|
|
6170
6183
|
return new Triple(
|
|
6171
6184
|
skolemizeTermForHeadBlanks(tr.s, headBlankLabels, mapping, skCounter, firingKey, globalMap),
|
|
6172
6185
|
skolemizeTermForHeadBlanks(tr.p, headBlankLabels, mapping, skCounter, firingKey, globalMap),
|
|
@@ -6642,6 +6655,35 @@ function ensureFactIndexes(facts) {
|
|
|
6642
6655
|
for (let i = 0; i < facts.length; i++) indexFact(facts, facts[i], i, false);
|
|
6643
6656
|
}
|
|
6644
6657
|
|
|
6658
|
+
function cloneFactIndexesForSnapshot(src, dest) {
|
|
6659
|
+
ensureFactIndexes(src);
|
|
6660
|
+
|
|
6661
|
+
function cloneArrayMap(map) {
|
|
6662
|
+
const out = new Map();
|
|
6663
|
+
for (const [k, arr] of map) out.set(k, arr.slice());
|
|
6664
|
+
return out;
|
|
6665
|
+
}
|
|
6666
|
+
|
|
6667
|
+
function cloneNestedArrayMap(map) {
|
|
6668
|
+
const out = new Map();
|
|
6669
|
+
for (const [k, inner] of map) {
|
|
6670
|
+
const innerOut = new Map();
|
|
6671
|
+
for (const [k2, arr] of inner) innerOut.set(k2, arr.slice());
|
|
6672
|
+
out.set(k, innerOut);
|
|
6673
|
+
}
|
|
6674
|
+
return out;
|
|
6675
|
+
}
|
|
6676
|
+
|
|
6677
|
+
Object.defineProperty(dest, '__byPred', { value: cloneArrayMap(src.__byPred), enumerable: false, writable: true });
|
|
6678
|
+
Object.defineProperty(dest, '__byPS', { value: cloneNestedArrayMap(src.__byPS), enumerable: false, writable: true });
|
|
6679
|
+
Object.defineProperty(dest, '__byPO', { value: cloneNestedArrayMap(src.__byPO), enumerable: false, writable: true });
|
|
6680
|
+
Object.defineProperty(dest, '__wildPred', { value: src.__wildPred.slice(), enumerable: false, writable: true });
|
|
6681
|
+
Object.defineProperty(dest, '__wildPS', { value: cloneArrayMap(src.__wildPS), enumerable: false, writable: true });
|
|
6682
|
+
Object.defineProperty(dest, '__wildPO', { value: cloneArrayMap(src.__wildPO), enumerable: false, writable: true });
|
|
6683
|
+
Object.defineProperty(dest, '__keySet', { value: new Set(src.__keySet), enumerable: false, writable: true });
|
|
6684
|
+
Object.defineProperty(dest, '__keySetComplete', { value: !!src.__keySetComplete, enumerable: false, writable: true });
|
|
6685
|
+
}
|
|
6686
|
+
|
|
6645
6687
|
function indexFact(facts, tr, idx, addKeySet = true) {
|
|
6646
6688
|
const sk = termFastKey(tr.s);
|
|
6647
6689
|
const ok = termFastKey(tr.o);
|
|
@@ -6928,13 +6970,20 @@ function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
|
|
|
6928
6970
|
if (!isSinglePremiseAgendaRuleSafe(r, backRules)) continue;
|
|
6929
6971
|
|
|
6930
6972
|
const goal = r.premise[0];
|
|
6973
|
+
const goalSKey = termFastKey(goal.s);
|
|
6974
|
+
const goalOKey = termFastKey(goal.o);
|
|
6975
|
+
const fastSubjectVar = goal.p instanceof Iri && goal.s instanceof Var && goalOKey !== null ? goal.s.name : null;
|
|
6976
|
+
const fastObjectVar = goal.p instanceof Iri && goal.o instanceof Var && goalSKey !== null ? goal.o.name : null;
|
|
6931
6977
|
const entry = {
|
|
6932
6978
|
rule: r,
|
|
6933
6979
|
ruleIndex: i,
|
|
6934
6980
|
goal,
|
|
6935
6981
|
goalPredTid: goal.p instanceof Iri ? goal.p.__tid : null,
|
|
6936
|
-
goalSKey
|
|
6937
|
-
goalOKey
|
|
6982
|
+
goalSKey,
|
|
6983
|
+
goalOKey,
|
|
6984
|
+
needsSkipCheck: !!r.__needsForwardSkipCheck,
|
|
6985
|
+
fastSubjectVar,
|
|
6986
|
+
fastObjectVar,
|
|
6938
6987
|
};
|
|
6939
6988
|
|
|
6940
6989
|
index.indexed.add(r);
|
|
@@ -8395,7 +8444,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8395
8444
|
|
|
8396
8445
|
function makeScopedSnapshot() {
|
|
8397
8446
|
const snap = facts.slice();
|
|
8398
|
-
|
|
8447
|
+
cloneFactIndexesForSnapshot(facts, snap);
|
|
8399
8448
|
Object.defineProperty(snap, '__scopedSnapshot', {
|
|
8400
8449
|
value: snap,
|
|
8401
8450
|
enumerable: false,
|
|
@@ -8449,10 +8498,21 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8449
8498
|
let changedHere = false;
|
|
8450
8499
|
let rulesChanged = false;
|
|
8451
8500
|
|
|
8452
|
-
// IMPORTANT: one skolem map per *rule firing
|
|
8501
|
+
// IMPORTANT: one skolem map per *rule firing*. Instantiate premise
|
|
8502
|
+
// triples and build the firing key lazily: normal CLI runs do not capture
|
|
8503
|
+
// proof records, and most rules have no explicit head blanks, so the eager
|
|
8504
|
+
// work was pure allocation on large forward chains.
|
|
8453
8505
|
const skMap = {};
|
|
8454
|
-
|
|
8455
|
-
|
|
8506
|
+
let instantiatedPremises = null;
|
|
8507
|
+
let fireKey = null;
|
|
8508
|
+
function getInstantiatedPremises() {
|
|
8509
|
+
if (instantiatedPremises === null) instantiatedPremises = r.premise.map((b) => applySubstTriple(b, s));
|
|
8510
|
+
return instantiatedPremises;
|
|
8511
|
+
}
|
|
8512
|
+
function getFireKey() {
|
|
8513
|
+
if (fireKey === null) fireKey = __firingKey(ruleIndex, getInstantiatedPremises());
|
|
8514
|
+
return fireKey;
|
|
8515
|
+
}
|
|
8456
8516
|
|
|
8457
8517
|
// Support "dynamic" rule heads where the consequent is a term that
|
|
8458
8518
|
// (after substitution) evaluates to a quoted formula.
|
|
@@ -8505,7 +8565,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8505
8565
|
if (isFwRuleTriple || isBwRuleTriple) {
|
|
8506
8566
|
if (!hasFactIndexed(facts, instantiated)) {
|
|
8507
8567
|
pushFactIndexed(facts, instantiated);
|
|
8508
|
-
const df = makeDerivedRecord(instantiated, r,
|
|
8568
|
+
const df = makeDerivedRecord(instantiated, r, getInstantiatedPremises(), s, captureExplanations);
|
|
8509
8569
|
derivedForward.push(df);
|
|
8510
8570
|
if (typeof onDerived === 'function') onDerived(df);
|
|
8511
8571
|
changedHere = true;
|
|
@@ -8553,20 +8613,23 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8553
8613
|
}
|
|
8554
8614
|
|
|
8555
8615
|
// Only skolemize blank nodes that occur explicitly in the rule head
|
|
8556
|
-
const inst =
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
|
|
8563
|
-
|
|
8616
|
+
const inst =
|
|
8617
|
+
headBlankLabelsHere && headBlankLabelsHere.size
|
|
8618
|
+
? skolemizeTripleForHeadBlanks(
|
|
8619
|
+
instantiated,
|
|
8620
|
+
headBlankLabelsHere,
|
|
8621
|
+
skMap,
|
|
8622
|
+
skCounter,
|
|
8623
|
+
getFireKey(),
|
|
8624
|
+
headSkolemCache,
|
|
8625
|
+
)
|
|
8626
|
+
: instantiated;
|
|
8564
8627
|
|
|
8565
8628
|
if (!isGroundTriple(inst)) continue;
|
|
8566
8629
|
if (hasFactIndexed(facts, inst)) continue;
|
|
8567
8630
|
|
|
8568
8631
|
pushFactIndexed(facts, inst);
|
|
8569
|
-
const df = makeDerivedRecord(inst, r,
|
|
8632
|
+
const df = makeDerivedRecord(inst, r, getInstantiatedPremises(), s, captureExplanations);
|
|
8570
8633
|
derivedForward.push(df);
|
|
8571
8634
|
if (typeof onDerived === 'function') onDerived(df);
|
|
8572
8635
|
|
|
@@ -8593,10 +8656,19 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8593
8656
|
for (let ci = 0; ci < total; ci++) {
|
|
8594
8657
|
const entry = ci < candidates.exactLen ? candidates.exact[ci] : candidates.wild[ci - candidates.exactLen];
|
|
8595
8658
|
const r = entry.rule;
|
|
8596
|
-
if (__skipForwardRuleNow(r)) continue;
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
if (
|
|
8659
|
+
if (entry.needsSkipCheck && __skipForwardRuleNow(r)) continue;
|
|
8660
|
+
|
|
8661
|
+
let s;
|
|
8662
|
+
if (entry.fastSubjectVar !== null) {
|
|
8663
|
+
s = __emptySubst();
|
|
8664
|
+
s[entry.fastSubjectVar] = fact.s;
|
|
8665
|
+
} else if (entry.fastObjectVar !== null) {
|
|
8666
|
+
s = __emptySubst();
|
|
8667
|
+
s[entry.fastObjectVar] = fact.o;
|
|
8668
|
+
} else {
|
|
8669
|
+
s = unifyTriple(entry.goal, fact, __emptySubst());
|
|
8670
|
+
if (s === null) continue;
|
|
8671
|
+
}
|
|
8600
8672
|
|
|
8601
8673
|
const outcome = __emitForwardRuleSolution(r, entry.ruleIndex, s);
|
|
8602
8674
|
if (outcome.rulesChanged) {
|
|
@@ -8613,7 +8685,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
8613
8685
|
for (let i = 0; i < forwardRules.length; i++) {
|
|
8614
8686
|
const r = forwardRules[i];
|
|
8615
8687
|
if (agendaIndex.indexed.has(r)) continue;
|
|
8616
|
-
if (__skipForwardRuleNow(r)) continue;
|
|
8688
|
+
if (r.__needsForwardSkipCheck && __skipForwardRuleNow(r)) continue;
|
|
8617
8689
|
|
|
8618
8690
|
const headIsStrictGround = r.__headIsStrictGround;
|
|
8619
8691
|
const maxSols = r.isFuse || headIsStrictGround ? 1 : undefined;
|
|
@@ -10639,6 +10711,16 @@ function normalizeRdfCompatibility(inputText) {
|
|
|
10639
10711
|
return text;
|
|
10640
10712
|
}
|
|
10641
10713
|
|
|
10714
|
+
|
|
10715
|
+
function isNumericLikeIdentifier(word) {
|
|
10716
|
+
if (typeof word !== 'string' || word.length === 0) return false;
|
|
10717
|
+
for (let j = 0; j < word.length; j++) {
|
|
10718
|
+
const code = word.charCodeAt(j);
|
|
10719
|
+
if (!((code >= 48 && code <= 57) || code === 46 || code === 45)) return false;
|
|
10720
|
+
}
|
|
10721
|
+
return true;
|
|
10722
|
+
}
|
|
10723
|
+
|
|
10642
10724
|
function lex(inputText, opts = {}) {
|
|
10643
10725
|
const rdf = !!(opts && opts.rdf);
|
|
10644
10726
|
if (rdf) inputText = normalizeRdfCompatibility(inputText);
|
|
@@ -10806,22 +10888,47 @@ function lex(inputText, opts = {}) {
|
|
|
10806
10888
|
continue;
|
|
10807
10889
|
}
|
|
10808
10890
|
|
|
10809
|
-
// 5) Single-character punctuation
|
|
10810
|
-
|
|
10811
|
-
|
|
10812
|
-
|
|
10813
|
-
|
|
10814
|
-
|
|
10815
|
-
|
|
10816
|
-
|
|
10817
|
-
|
|
10818
|
-
|
|
10819
|
-
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
|
|
10824
|
-
|
|
10891
|
+
// 5) Single-character punctuation. Use a switch rather than allocating a
|
|
10892
|
+
// mapping object for every punctuation token in large inputs.
|
|
10893
|
+
switch (c) {
|
|
10894
|
+
case '{':
|
|
10895
|
+
tokens.push(new Token('LBrace', null, i));
|
|
10896
|
+
i++;
|
|
10897
|
+
continue;
|
|
10898
|
+
case '}':
|
|
10899
|
+
tokens.push(new Token('RBrace', null, i));
|
|
10900
|
+
i++;
|
|
10901
|
+
continue;
|
|
10902
|
+
case '(':
|
|
10903
|
+
tokens.push(new Token('LParen', null, i));
|
|
10904
|
+
i++;
|
|
10905
|
+
continue;
|
|
10906
|
+
case ')':
|
|
10907
|
+
tokens.push(new Token('RParen', null, i));
|
|
10908
|
+
i++;
|
|
10909
|
+
continue;
|
|
10910
|
+
case '[':
|
|
10911
|
+
tokens.push(new Token('LBracket', null, i));
|
|
10912
|
+
i++;
|
|
10913
|
+
continue;
|
|
10914
|
+
case ']':
|
|
10915
|
+
tokens.push(new Token('RBracket', null, i));
|
|
10916
|
+
i++;
|
|
10917
|
+
continue;
|
|
10918
|
+
case ';':
|
|
10919
|
+
tokens.push(new Token('Semicolon', null, i));
|
|
10920
|
+
i++;
|
|
10921
|
+
continue;
|
|
10922
|
+
case ',':
|
|
10923
|
+
tokens.push(new Token('Comma', null, i));
|
|
10924
|
+
i++;
|
|
10925
|
+
continue;
|
|
10926
|
+
case '.':
|
|
10927
|
+
tokens.push(new Token('Dot', null, i));
|
|
10928
|
+
i++;
|
|
10929
|
+
continue;
|
|
10930
|
+
default:
|
|
10931
|
+
break;
|
|
10825
10932
|
}
|
|
10826
10933
|
|
|
10827
10934
|
// String literal: short "..." or long """..."""
|
|
@@ -10880,26 +10987,36 @@ function lex(inputText, opts = {}) {
|
|
|
10880
10987
|
continue;
|
|
10881
10988
|
}
|
|
10882
10989
|
|
|
10883
|
-
// Short string literal " ... "
|
|
10990
|
+
// Short string literal " ... ". Most data files contain plain
|
|
10991
|
+
// unescaped labels; keep that path slice-based and avoid building an
|
|
10992
|
+
// intermediate character array + raw quoted string.
|
|
10884
10993
|
i++; // consume opening "
|
|
10885
|
-
const
|
|
10994
|
+
const contentStart = i;
|
|
10995
|
+
let sChars = null;
|
|
10996
|
+
let closed = false;
|
|
10886
10997
|
while (i < n) {
|
|
10887
10998
|
const cc = chars[i];
|
|
10888
10999
|
i++;
|
|
10889
11000
|
if (cc === '\\') {
|
|
11001
|
+
if (sChars === null) sChars = [sliceChars(contentStart, i - 1)];
|
|
10890
11002
|
if (i < n) {
|
|
10891
11003
|
const esc = chars[i];
|
|
10892
11004
|
i++;
|
|
10893
11005
|
sChars.push('\\');
|
|
10894
11006
|
sChars.push(esc);
|
|
11007
|
+
} else {
|
|
11008
|
+
sChars.push('\\');
|
|
10895
11009
|
}
|
|
10896
11010
|
continue;
|
|
10897
11011
|
}
|
|
10898
|
-
if (cc === '"')
|
|
10899
|
-
|
|
11012
|
+
if (cc === '"') {
|
|
11013
|
+
closed = true;
|
|
11014
|
+
break;
|
|
11015
|
+
}
|
|
11016
|
+
if (sChars !== null) sChars.push(cc);
|
|
10900
11017
|
}
|
|
10901
|
-
const
|
|
10902
|
-
const decoded = decodeN3StringEscapes(
|
|
11018
|
+
const rawContent = sChars === null ? sliceChars(contentStart, closed ? i - 1 : i) : sChars.join('');
|
|
11019
|
+
const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
|
|
10903
11020
|
assertValidStringLiteralValue(decoded, start);
|
|
10904
11021
|
const s = JSON.stringify(decoded); // canonical short quoted form
|
|
10905
11022
|
tokens.push(new Token('Literal', s, start));
|
|
@@ -10964,24 +11081,32 @@ function lex(inputText, opts = {}) {
|
|
|
10964
11081
|
|
|
10965
11082
|
// Short string literal ' ... '
|
|
10966
11083
|
i++; // consume opening '
|
|
10967
|
-
const
|
|
11084
|
+
const contentStart = i;
|
|
11085
|
+
let sChars = null;
|
|
11086
|
+
let closed = false;
|
|
10968
11087
|
while (i < n) {
|
|
10969
11088
|
const cc = chars[i];
|
|
10970
11089
|
i++;
|
|
10971
11090
|
if (cc === '\\') {
|
|
11091
|
+
if (sChars === null) sChars = [sliceChars(contentStart, i - 1)];
|
|
10972
11092
|
if (i < n) {
|
|
10973
11093
|
const esc = chars[i];
|
|
10974
11094
|
i++;
|
|
10975
11095
|
sChars.push('\\');
|
|
10976
11096
|
sChars.push(esc);
|
|
11097
|
+
} else {
|
|
11098
|
+
sChars.push('\\');
|
|
10977
11099
|
}
|
|
10978
11100
|
continue;
|
|
10979
11101
|
}
|
|
10980
|
-
if (cc === "'")
|
|
10981
|
-
|
|
11102
|
+
if (cc === "'") {
|
|
11103
|
+
closed = true;
|
|
11104
|
+
break;
|
|
11105
|
+
}
|
|
11106
|
+
if (sChars !== null) sChars.push(cc);
|
|
10982
11107
|
}
|
|
10983
|
-
const
|
|
10984
|
-
const decoded = decodeN3StringEscapes(
|
|
11108
|
+
const rawContent = sChars === null ? sliceChars(contentStart, closed ? i - 1 : i) : sChars.join('');
|
|
11109
|
+
const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
|
|
10985
11110
|
assertValidStringLiteralValue(decoded, start);
|
|
10986
11111
|
const s = JSON.stringify(decoded); // canonical short quoted form
|
|
10987
11112
|
tokens.push(new Token('Literal', s, start));
|
|
@@ -11106,7 +11231,7 @@ function lex(inputText, opts = {}) {
|
|
|
11106
11231
|
}
|
|
11107
11232
|
if (word === 'true' || word === 'false') {
|
|
11108
11233
|
tokens.push(new Token('Literal', word, start));
|
|
11109
|
-
} else if (
|
|
11234
|
+
} else if (isNumericLikeIdentifier(word)) {
|
|
11110
11235
|
tokens.push(new Token('Literal', word, start));
|
|
11111
11236
|
} else {
|
|
11112
11237
|
tokens.push(new Token('Ident', word, start));
|
|
@@ -11691,7 +11816,7 @@ class Parser {
|
|
|
11691
11816
|
} else if (tok2.typ === 'Ident') {
|
|
11692
11817
|
const qn = tok2.value || '';
|
|
11693
11818
|
if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok2, qn);
|
|
11694
|
-
assertValidQNamePrefix(qn.
|
|
11819
|
+
assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok2, '@prefix directive IRI');
|
|
11695
11820
|
iri = this.prefixes.expandQName(qn);
|
|
11696
11821
|
} else {
|
|
11697
11822
|
this.fail(`Expected IRI after @prefix, got ${tok2.toString()}`, tok2);
|
|
@@ -11708,7 +11833,7 @@ class Parser {
|
|
|
11708
11833
|
} else if (tok.typ === 'Ident') {
|
|
11709
11834
|
const qn = tok.value || '';
|
|
11710
11835
|
if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok, qn);
|
|
11711
|
-
assertValidQNamePrefix(qn.
|
|
11836
|
+
assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok, '@base directive IRI');
|
|
11712
11837
|
iri = this.prefixes.expandQName(qn);
|
|
11713
11838
|
} else {
|
|
11714
11839
|
this.fail(`Expected IRI after @base, got ${tok.toString()}`, tok);
|
|
@@ -11737,7 +11862,7 @@ class Parser {
|
|
|
11737
11862
|
} else if (tok2.typ === 'Ident') {
|
|
11738
11863
|
const qn = tok2.value || '';
|
|
11739
11864
|
if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok2, qn);
|
|
11740
|
-
assertValidQNamePrefix(qn.
|
|
11865
|
+
assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok2, '@prefix directive IRI');
|
|
11741
11866
|
iri = this.prefixes.expandQName(qn);
|
|
11742
11867
|
} else {
|
|
11743
11868
|
this.fail(`Expected IRI after PREFIX, got ${tok2.toString()}`, tok2);
|
|
@@ -11758,7 +11883,7 @@ class Parser {
|
|
|
11758
11883
|
} else if (tok.typ === 'Ident') {
|
|
11759
11884
|
const qn = tok.value || '';
|
|
11760
11885
|
if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok, qn);
|
|
11761
|
-
assertValidQNamePrefix(qn.
|
|
11886
|
+
assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok, 'BASE directive IRI');
|
|
11762
11887
|
iri = this.prefixes.expandQName(qn);
|
|
11763
11888
|
} else {
|
|
11764
11889
|
this.fail(`Expected IRI after BASE, got ${tok.toString()}`, tok);
|
|
@@ -11805,14 +11930,18 @@ class Parser {
|
|
|
11805
11930
|
const name = val || '';
|
|
11806
11931
|
if (name === 'a') {
|
|
11807
11932
|
return internIri(RDF_NS + 'type');
|
|
11808
|
-
}
|
|
11933
|
+
}
|
|
11934
|
+
const sep = name.indexOf(':');
|
|
11935
|
+
if (sep === 1 && name.charCodeAt(0) === 95) {
|
|
11809
11936
|
return new Blank(name);
|
|
11810
|
-
} else if (name.includes(':')) {
|
|
11811
|
-
assertValidQNamePrefix(name.split(':', 1)[0], this.fail.bind(this), tok);
|
|
11812
|
-
return internIri(this.prefixes.expandQName(name));
|
|
11813
|
-
} else {
|
|
11814
|
-
failInvalidKeywordLikeIdent(this.fail.bind(this), tok, name);
|
|
11815
11937
|
}
|
|
11938
|
+
if (sep >= 0) {
|
|
11939
|
+
const prefixName = name.slice(0, sep);
|
|
11940
|
+
assertValidQNamePrefix(prefixName, this.fail.bind(this), tok);
|
|
11941
|
+
const base = this.prefixes.map[prefixName] || '';
|
|
11942
|
+
return internIri(base ? base + name.slice(sep + 1) : name);
|
|
11943
|
+
}
|
|
11944
|
+
failInvalidKeywordLikeIdent(this.fail.bind(this), tok, name);
|
|
11816
11945
|
}
|
|
11817
11946
|
|
|
11818
11947
|
if (typ === 'Literal') {
|
|
@@ -11843,7 +11972,7 @@ class Parser {
|
|
|
11843
11972
|
} else if (dtTok.typ === 'Ident') {
|
|
11844
11973
|
const qn = dtTok.value || '';
|
|
11845
11974
|
if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), dtTok, qn);
|
|
11846
|
-
assertValidQNamePrefix(qn.
|
|
11975
|
+
assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), dtTok, 'datatype prefixed name');
|
|
11847
11976
|
dtIri = this.prefixes.expandQName(qn);
|
|
11848
11977
|
} else {
|
|
11849
11978
|
this.fail(`Expected datatype after ^^, got ${dtTok.toString()}`, dtTok);
|
package/lib/engine.js
CHANGED
|
@@ -362,6 +362,14 @@ function __prepareForwardRule(r) {
|
|
|
362
362
|
configurable: true,
|
|
363
363
|
});
|
|
364
364
|
}
|
|
365
|
+
if (!hasOwn.call(r, '__needsForwardSkipCheck')) {
|
|
366
|
+
Object.defineProperty(r, '__needsForwardSkipCheck', {
|
|
367
|
+
value: !!(r.__headIsStrictGround || (r.__scopedSkipInfo && r.__scopedSkipInfo.needsSnap)),
|
|
368
|
+
enumerable: false,
|
|
369
|
+
writable: false,
|
|
370
|
+
configurable: true,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
365
373
|
}
|
|
366
374
|
|
|
367
375
|
function __graphTriplesOrTrue(term) {
|
|
@@ -680,6 +688,11 @@ function skolemizeTermForHeadBlanks(t, headBlankLabels, mapping, skCounter, firi
|
|
|
680
688
|
}
|
|
681
689
|
|
|
682
690
|
function skolemizeTripleForHeadBlanks(tr, headBlankLabels, mapping, skCounter, firingKey, globalMap) {
|
|
691
|
+
// Fast path: the common case has no explicit head blanks. Do not allocate a
|
|
692
|
+
// replacement Triple or compute a firing key when skolemization cannot change
|
|
693
|
+
// anything. This matters for long single-premise chains such as
|
|
694
|
+
// deep-taxonomy-100000, where every derived head triple is otherwise copied.
|
|
695
|
+
if (!headBlankLabels || headBlankLabels.size === 0) return tr;
|
|
683
696
|
return new Triple(
|
|
684
697
|
skolemizeTermForHeadBlanks(tr.s, headBlankLabels, mapping, skCounter, firingKey, globalMap),
|
|
685
698
|
skolemizeTermForHeadBlanks(tr.p, headBlankLabels, mapping, skCounter, firingKey, globalMap),
|
|
@@ -1155,6 +1168,35 @@ function ensureFactIndexes(facts) {
|
|
|
1155
1168
|
for (let i = 0; i < facts.length; i++) indexFact(facts, facts[i], i, false);
|
|
1156
1169
|
}
|
|
1157
1170
|
|
|
1171
|
+
function cloneFactIndexesForSnapshot(src, dest) {
|
|
1172
|
+
ensureFactIndexes(src);
|
|
1173
|
+
|
|
1174
|
+
function cloneArrayMap(map) {
|
|
1175
|
+
const out = new Map();
|
|
1176
|
+
for (const [k, arr] of map) out.set(k, arr.slice());
|
|
1177
|
+
return out;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
function cloneNestedArrayMap(map) {
|
|
1181
|
+
const out = new Map();
|
|
1182
|
+
for (const [k, inner] of map) {
|
|
1183
|
+
const innerOut = new Map();
|
|
1184
|
+
for (const [k2, arr] of inner) innerOut.set(k2, arr.slice());
|
|
1185
|
+
out.set(k, innerOut);
|
|
1186
|
+
}
|
|
1187
|
+
return out;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
Object.defineProperty(dest, '__byPred', { value: cloneArrayMap(src.__byPred), enumerable: false, writable: true });
|
|
1191
|
+
Object.defineProperty(dest, '__byPS', { value: cloneNestedArrayMap(src.__byPS), enumerable: false, writable: true });
|
|
1192
|
+
Object.defineProperty(dest, '__byPO', { value: cloneNestedArrayMap(src.__byPO), enumerable: false, writable: true });
|
|
1193
|
+
Object.defineProperty(dest, '__wildPred', { value: src.__wildPred.slice(), enumerable: false, writable: true });
|
|
1194
|
+
Object.defineProperty(dest, '__wildPS', { value: cloneArrayMap(src.__wildPS), enumerable: false, writable: true });
|
|
1195
|
+
Object.defineProperty(dest, '__wildPO', { value: cloneArrayMap(src.__wildPO), enumerable: false, writable: true });
|
|
1196
|
+
Object.defineProperty(dest, '__keySet', { value: new Set(src.__keySet), enumerable: false, writable: true });
|
|
1197
|
+
Object.defineProperty(dest, '__keySetComplete', { value: !!src.__keySetComplete, enumerable: false, writable: true });
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1158
1200
|
function indexFact(facts, tr, idx, addKeySet = true) {
|
|
1159
1201
|
const sk = termFastKey(tr.s);
|
|
1160
1202
|
const ok = termFastKey(tr.o);
|
|
@@ -1441,13 +1483,20 @@ function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
|
|
|
1441
1483
|
if (!isSinglePremiseAgendaRuleSafe(r, backRules)) continue;
|
|
1442
1484
|
|
|
1443
1485
|
const goal = r.premise[0];
|
|
1486
|
+
const goalSKey = termFastKey(goal.s);
|
|
1487
|
+
const goalOKey = termFastKey(goal.o);
|
|
1488
|
+
const fastSubjectVar = goal.p instanceof Iri && goal.s instanceof Var && goalOKey !== null ? goal.s.name : null;
|
|
1489
|
+
const fastObjectVar = goal.p instanceof Iri && goal.o instanceof Var && goalSKey !== null ? goal.o.name : null;
|
|
1444
1490
|
const entry = {
|
|
1445
1491
|
rule: r,
|
|
1446
1492
|
ruleIndex: i,
|
|
1447
1493
|
goal,
|
|
1448
1494
|
goalPredTid: goal.p instanceof Iri ? goal.p.__tid : null,
|
|
1449
|
-
goalSKey
|
|
1450
|
-
goalOKey
|
|
1495
|
+
goalSKey,
|
|
1496
|
+
goalOKey,
|
|
1497
|
+
needsSkipCheck: !!r.__needsForwardSkipCheck,
|
|
1498
|
+
fastSubjectVar,
|
|
1499
|
+
fastObjectVar,
|
|
1451
1500
|
};
|
|
1452
1501
|
|
|
1453
1502
|
index.indexed.add(r);
|
|
@@ -2908,7 +2957,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
2908
2957
|
|
|
2909
2958
|
function makeScopedSnapshot() {
|
|
2910
2959
|
const snap = facts.slice();
|
|
2911
|
-
|
|
2960
|
+
cloneFactIndexesForSnapshot(facts, snap);
|
|
2912
2961
|
Object.defineProperty(snap, '__scopedSnapshot', {
|
|
2913
2962
|
value: snap,
|
|
2914
2963
|
enumerable: false,
|
|
@@ -2962,10 +3011,21 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
2962
3011
|
let changedHere = false;
|
|
2963
3012
|
let rulesChanged = false;
|
|
2964
3013
|
|
|
2965
|
-
// IMPORTANT: one skolem map per *rule firing
|
|
3014
|
+
// IMPORTANT: one skolem map per *rule firing*. Instantiate premise
|
|
3015
|
+
// triples and build the firing key lazily: normal CLI runs do not capture
|
|
3016
|
+
// proof records, and most rules have no explicit head blanks, so the eager
|
|
3017
|
+
// work was pure allocation on large forward chains.
|
|
2966
3018
|
const skMap = {};
|
|
2967
|
-
|
|
2968
|
-
|
|
3019
|
+
let instantiatedPremises = null;
|
|
3020
|
+
let fireKey = null;
|
|
3021
|
+
function getInstantiatedPremises() {
|
|
3022
|
+
if (instantiatedPremises === null) instantiatedPremises = r.premise.map((b) => applySubstTriple(b, s));
|
|
3023
|
+
return instantiatedPremises;
|
|
3024
|
+
}
|
|
3025
|
+
function getFireKey() {
|
|
3026
|
+
if (fireKey === null) fireKey = __firingKey(ruleIndex, getInstantiatedPremises());
|
|
3027
|
+
return fireKey;
|
|
3028
|
+
}
|
|
2969
3029
|
|
|
2970
3030
|
// Support "dynamic" rule heads where the consequent is a term that
|
|
2971
3031
|
// (after substitution) evaluates to a quoted formula.
|
|
@@ -3018,7 +3078,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
3018
3078
|
if (isFwRuleTriple || isBwRuleTriple) {
|
|
3019
3079
|
if (!hasFactIndexed(facts, instantiated)) {
|
|
3020
3080
|
pushFactIndexed(facts, instantiated);
|
|
3021
|
-
const df = makeDerivedRecord(instantiated, r,
|
|
3081
|
+
const df = makeDerivedRecord(instantiated, r, getInstantiatedPremises(), s, captureExplanations);
|
|
3022
3082
|
derivedForward.push(df);
|
|
3023
3083
|
if (typeof onDerived === 'function') onDerived(df);
|
|
3024
3084
|
changedHere = true;
|
|
@@ -3066,20 +3126,23 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
3066
3126
|
}
|
|
3067
3127
|
|
|
3068
3128
|
// Only skolemize blank nodes that occur explicitly in the rule head
|
|
3069
|
-
const inst =
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3129
|
+
const inst =
|
|
3130
|
+
headBlankLabelsHere && headBlankLabelsHere.size
|
|
3131
|
+
? skolemizeTripleForHeadBlanks(
|
|
3132
|
+
instantiated,
|
|
3133
|
+
headBlankLabelsHere,
|
|
3134
|
+
skMap,
|
|
3135
|
+
skCounter,
|
|
3136
|
+
getFireKey(),
|
|
3137
|
+
headSkolemCache,
|
|
3138
|
+
)
|
|
3139
|
+
: instantiated;
|
|
3077
3140
|
|
|
3078
3141
|
if (!isGroundTriple(inst)) continue;
|
|
3079
3142
|
if (hasFactIndexed(facts, inst)) continue;
|
|
3080
3143
|
|
|
3081
3144
|
pushFactIndexed(facts, inst);
|
|
3082
|
-
const df = makeDerivedRecord(inst, r,
|
|
3145
|
+
const df = makeDerivedRecord(inst, r, getInstantiatedPremises(), s, captureExplanations);
|
|
3083
3146
|
derivedForward.push(df);
|
|
3084
3147
|
if (typeof onDerived === 'function') onDerived(df);
|
|
3085
3148
|
|
|
@@ -3106,10 +3169,19 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
3106
3169
|
for (let ci = 0; ci < total; ci++) {
|
|
3107
3170
|
const entry = ci < candidates.exactLen ? candidates.exact[ci] : candidates.wild[ci - candidates.exactLen];
|
|
3108
3171
|
const r = entry.rule;
|
|
3109
|
-
if (__skipForwardRuleNow(r)) continue;
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
if (
|
|
3172
|
+
if (entry.needsSkipCheck && __skipForwardRuleNow(r)) continue;
|
|
3173
|
+
|
|
3174
|
+
let s;
|
|
3175
|
+
if (entry.fastSubjectVar !== null) {
|
|
3176
|
+
s = __emptySubst();
|
|
3177
|
+
s[entry.fastSubjectVar] = fact.s;
|
|
3178
|
+
} else if (entry.fastObjectVar !== null) {
|
|
3179
|
+
s = __emptySubst();
|
|
3180
|
+
s[entry.fastObjectVar] = fact.o;
|
|
3181
|
+
} else {
|
|
3182
|
+
s = unifyTriple(entry.goal, fact, __emptySubst());
|
|
3183
|
+
if (s === null) continue;
|
|
3184
|
+
}
|
|
3113
3185
|
|
|
3114
3186
|
const outcome = __emitForwardRuleSolution(r, entry.ruleIndex, s);
|
|
3115
3187
|
if (outcome.rulesChanged) {
|
|
@@ -3126,7 +3198,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
|
|
|
3126
3198
|
for (let i = 0; i < forwardRules.length; i++) {
|
|
3127
3199
|
const r = forwardRules[i];
|
|
3128
3200
|
if (agendaIndex.indexed.has(r)) continue;
|
|
3129
|
-
if (__skipForwardRuleNow(r)) continue;
|
|
3201
|
+
if (r.__needsForwardSkipCheck && __skipForwardRuleNow(r)) continue;
|
|
3130
3202
|
|
|
3131
3203
|
const headIsStrictGround = r.__headIsStrictGround;
|
|
3132
3204
|
const maxSols = r.isFuse || headIsStrictGround ? 1 : undefined;
|
package/lib/lexer.js
CHANGED
|
@@ -1189,6 +1189,16 @@ function normalizeRdfCompatibility(inputText) {
|
|
|
1189
1189
|
return text;
|
|
1190
1190
|
}
|
|
1191
1191
|
|
|
1192
|
+
|
|
1193
|
+
function isNumericLikeIdentifier(word) {
|
|
1194
|
+
if (typeof word !== 'string' || word.length === 0) return false;
|
|
1195
|
+
for (let j = 0; j < word.length; j++) {
|
|
1196
|
+
const code = word.charCodeAt(j);
|
|
1197
|
+
if (!((code >= 48 && code <= 57) || code === 46 || code === 45)) return false;
|
|
1198
|
+
}
|
|
1199
|
+
return true;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1192
1202
|
function lex(inputText, opts = {}) {
|
|
1193
1203
|
const rdf = !!(opts && opts.rdf);
|
|
1194
1204
|
if (rdf) inputText = normalizeRdfCompatibility(inputText);
|
|
@@ -1356,22 +1366,47 @@ function lex(inputText, opts = {}) {
|
|
|
1356
1366
|
continue;
|
|
1357
1367
|
}
|
|
1358
1368
|
|
|
1359
|
-
// 5) Single-character punctuation
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1369
|
+
// 5) Single-character punctuation. Use a switch rather than allocating a
|
|
1370
|
+
// mapping object for every punctuation token in large inputs.
|
|
1371
|
+
switch (c) {
|
|
1372
|
+
case '{':
|
|
1373
|
+
tokens.push(new Token('LBrace', null, i));
|
|
1374
|
+
i++;
|
|
1375
|
+
continue;
|
|
1376
|
+
case '}':
|
|
1377
|
+
tokens.push(new Token('RBrace', null, i));
|
|
1378
|
+
i++;
|
|
1379
|
+
continue;
|
|
1380
|
+
case '(':
|
|
1381
|
+
tokens.push(new Token('LParen', null, i));
|
|
1382
|
+
i++;
|
|
1383
|
+
continue;
|
|
1384
|
+
case ')':
|
|
1385
|
+
tokens.push(new Token('RParen', null, i));
|
|
1386
|
+
i++;
|
|
1387
|
+
continue;
|
|
1388
|
+
case '[':
|
|
1389
|
+
tokens.push(new Token('LBracket', null, i));
|
|
1390
|
+
i++;
|
|
1391
|
+
continue;
|
|
1392
|
+
case ']':
|
|
1393
|
+
tokens.push(new Token('RBracket', null, i));
|
|
1394
|
+
i++;
|
|
1395
|
+
continue;
|
|
1396
|
+
case ';':
|
|
1397
|
+
tokens.push(new Token('Semicolon', null, i));
|
|
1398
|
+
i++;
|
|
1399
|
+
continue;
|
|
1400
|
+
case ',':
|
|
1401
|
+
tokens.push(new Token('Comma', null, i));
|
|
1402
|
+
i++;
|
|
1403
|
+
continue;
|
|
1404
|
+
case '.':
|
|
1405
|
+
tokens.push(new Token('Dot', null, i));
|
|
1406
|
+
i++;
|
|
1407
|
+
continue;
|
|
1408
|
+
default:
|
|
1409
|
+
break;
|
|
1375
1410
|
}
|
|
1376
1411
|
|
|
1377
1412
|
// String literal: short "..." or long """..."""
|
|
@@ -1430,26 +1465,36 @@ function lex(inputText, opts = {}) {
|
|
|
1430
1465
|
continue;
|
|
1431
1466
|
}
|
|
1432
1467
|
|
|
1433
|
-
// Short string literal " ... "
|
|
1468
|
+
// Short string literal " ... ". Most data files contain plain
|
|
1469
|
+
// unescaped labels; keep that path slice-based and avoid building an
|
|
1470
|
+
// intermediate character array + raw quoted string.
|
|
1434
1471
|
i++; // consume opening "
|
|
1435
|
-
const
|
|
1472
|
+
const contentStart = i;
|
|
1473
|
+
let sChars = null;
|
|
1474
|
+
let closed = false;
|
|
1436
1475
|
while (i < n) {
|
|
1437
1476
|
const cc = chars[i];
|
|
1438
1477
|
i++;
|
|
1439
1478
|
if (cc === '\\') {
|
|
1479
|
+
if (sChars === null) sChars = [sliceChars(contentStart, i - 1)];
|
|
1440
1480
|
if (i < n) {
|
|
1441
1481
|
const esc = chars[i];
|
|
1442
1482
|
i++;
|
|
1443
1483
|
sChars.push('\\');
|
|
1444
1484
|
sChars.push(esc);
|
|
1485
|
+
} else {
|
|
1486
|
+
sChars.push('\\');
|
|
1445
1487
|
}
|
|
1446
1488
|
continue;
|
|
1447
1489
|
}
|
|
1448
|
-
if (cc === '"')
|
|
1449
|
-
|
|
1490
|
+
if (cc === '"') {
|
|
1491
|
+
closed = true;
|
|
1492
|
+
break;
|
|
1493
|
+
}
|
|
1494
|
+
if (sChars !== null) sChars.push(cc);
|
|
1450
1495
|
}
|
|
1451
|
-
const
|
|
1452
|
-
const decoded = decodeN3StringEscapes(
|
|
1496
|
+
const rawContent = sChars === null ? sliceChars(contentStart, closed ? i - 1 : i) : sChars.join('');
|
|
1497
|
+
const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
|
|
1453
1498
|
assertValidStringLiteralValue(decoded, start);
|
|
1454
1499
|
const s = JSON.stringify(decoded); // canonical short quoted form
|
|
1455
1500
|
tokens.push(new Token('Literal', s, start));
|
|
@@ -1514,24 +1559,32 @@ function lex(inputText, opts = {}) {
|
|
|
1514
1559
|
|
|
1515
1560
|
// Short string literal ' ... '
|
|
1516
1561
|
i++; // consume opening '
|
|
1517
|
-
const
|
|
1562
|
+
const contentStart = i;
|
|
1563
|
+
let sChars = null;
|
|
1564
|
+
let closed = false;
|
|
1518
1565
|
while (i < n) {
|
|
1519
1566
|
const cc = chars[i];
|
|
1520
1567
|
i++;
|
|
1521
1568
|
if (cc === '\\') {
|
|
1569
|
+
if (sChars === null) sChars = [sliceChars(contentStart, i - 1)];
|
|
1522
1570
|
if (i < n) {
|
|
1523
1571
|
const esc = chars[i];
|
|
1524
1572
|
i++;
|
|
1525
1573
|
sChars.push('\\');
|
|
1526
1574
|
sChars.push(esc);
|
|
1575
|
+
} else {
|
|
1576
|
+
sChars.push('\\');
|
|
1527
1577
|
}
|
|
1528
1578
|
continue;
|
|
1529
1579
|
}
|
|
1530
|
-
if (cc === "'")
|
|
1531
|
-
|
|
1580
|
+
if (cc === "'") {
|
|
1581
|
+
closed = true;
|
|
1582
|
+
break;
|
|
1583
|
+
}
|
|
1584
|
+
if (sChars !== null) sChars.push(cc);
|
|
1532
1585
|
}
|
|
1533
|
-
const
|
|
1534
|
-
const decoded = decodeN3StringEscapes(
|
|
1586
|
+
const rawContent = sChars === null ? sliceChars(contentStart, closed ? i - 1 : i) : sChars.join('');
|
|
1587
|
+
const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
|
|
1535
1588
|
assertValidStringLiteralValue(decoded, start);
|
|
1536
1589
|
const s = JSON.stringify(decoded); // canonical short quoted form
|
|
1537
1590
|
tokens.push(new Token('Literal', s, start));
|
|
@@ -1656,7 +1709,7 @@ function lex(inputText, opts = {}) {
|
|
|
1656
1709
|
}
|
|
1657
1710
|
if (word === 'true' || word === 'false') {
|
|
1658
1711
|
tokens.push(new Token('Literal', word, start));
|
|
1659
|
-
} else if (
|
|
1712
|
+
} else if (isNumericLikeIdentifier(word)) {
|
|
1660
1713
|
tokens.push(new Token('Literal', word, start));
|
|
1661
1714
|
} else {
|
|
1662
1715
|
tokens.push(new Token('Ident', word, start));
|
package/lib/parser.js
CHANGED
|
@@ -241,7 +241,7 @@ class Parser {
|
|
|
241
241
|
} else if (tok2.typ === 'Ident') {
|
|
242
242
|
const qn = tok2.value || '';
|
|
243
243
|
if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok2, qn);
|
|
244
|
-
assertValidQNamePrefix(qn.
|
|
244
|
+
assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok2, '@prefix directive IRI');
|
|
245
245
|
iri = this.prefixes.expandQName(qn);
|
|
246
246
|
} else {
|
|
247
247
|
this.fail(`Expected IRI after @prefix, got ${tok2.toString()}`, tok2);
|
|
@@ -258,7 +258,7 @@ class Parser {
|
|
|
258
258
|
} else if (tok.typ === 'Ident') {
|
|
259
259
|
const qn = tok.value || '';
|
|
260
260
|
if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok, qn);
|
|
261
|
-
assertValidQNamePrefix(qn.
|
|
261
|
+
assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok, '@base directive IRI');
|
|
262
262
|
iri = this.prefixes.expandQName(qn);
|
|
263
263
|
} else {
|
|
264
264
|
this.fail(`Expected IRI after @base, got ${tok.toString()}`, tok);
|
|
@@ -287,7 +287,7 @@ class Parser {
|
|
|
287
287
|
} else if (tok2.typ === 'Ident') {
|
|
288
288
|
const qn = tok2.value || '';
|
|
289
289
|
if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok2, qn);
|
|
290
|
-
assertValidQNamePrefix(qn.
|
|
290
|
+
assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok2, '@prefix directive IRI');
|
|
291
291
|
iri = this.prefixes.expandQName(qn);
|
|
292
292
|
} else {
|
|
293
293
|
this.fail(`Expected IRI after PREFIX, got ${tok2.toString()}`, tok2);
|
|
@@ -308,7 +308,7 @@ class Parser {
|
|
|
308
308
|
} else if (tok.typ === 'Ident') {
|
|
309
309
|
const qn = tok.value || '';
|
|
310
310
|
if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok, qn);
|
|
311
|
-
assertValidQNamePrefix(qn.
|
|
311
|
+
assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok, 'BASE directive IRI');
|
|
312
312
|
iri = this.prefixes.expandQName(qn);
|
|
313
313
|
} else {
|
|
314
314
|
this.fail(`Expected IRI after BASE, got ${tok.toString()}`, tok);
|
|
@@ -355,14 +355,18 @@ class Parser {
|
|
|
355
355
|
const name = val || '';
|
|
356
356
|
if (name === 'a') {
|
|
357
357
|
return internIri(RDF_NS + 'type');
|
|
358
|
-
}
|
|
358
|
+
}
|
|
359
|
+
const sep = name.indexOf(':');
|
|
360
|
+
if (sep === 1 && name.charCodeAt(0) === 95) {
|
|
359
361
|
return new Blank(name);
|
|
360
|
-
} else if (name.includes(':')) {
|
|
361
|
-
assertValidQNamePrefix(name.split(':', 1)[0], this.fail.bind(this), tok);
|
|
362
|
-
return internIri(this.prefixes.expandQName(name));
|
|
363
|
-
} else {
|
|
364
|
-
failInvalidKeywordLikeIdent(this.fail.bind(this), tok, name);
|
|
365
362
|
}
|
|
363
|
+
if (sep >= 0) {
|
|
364
|
+
const prefixName = name.slice(0, sep);
|
|
365
|
+
assertValidQNamePrefix(prefixName, this.fail.bind(this), tok);
|
|
366
|
+
const base = this.prefixes.map[prefixName] || '';
|
|
367
|
+
return internIri(base ? base + name.slice(sep + 1) : name);
|
|
368
|
+
}
|
|
369
|
+
failInvalidKeywordLikeIdent(this.fail.bind(this), tok, name);
|
|
366
370
|
}
|
|
367
371
|
|
|
368
372
|
if (typ === 'Literal') {
|
|
@@ -393,7 +397,7 @@ class Parser {
|
|
|
393
397
|
} else if (dtTok.typ === 'Ident') {
|
|
394
398
|
const qn = dtTok.value || '';
|
|
395
399
|
if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), dtTok, qn);
|
|
396
|
-
assertValidQNamePrefix(qn.
|
|
400
|
+
assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), dtTok, 'datatype prefixed name');
|
|
397
401
|
dtIri = this.prefixes.expandQName(qn);
|
|
398
402
|
} else {
|
|
399
403
|
this.fail(`Expected datatype after ^^, got ${dtTok.toString()}`, dtTok);
|