eyeling 1.25.1 → 1.25.3

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.
@@ -4965,12 +4965,14 @@ function main() {
4965
4965
  outTriples = res.queryTriples;
4966
4966
  outDerived = res.queryDerived;
4967
4967
  } else {
4968
+ const skipDerivedCollection = mayAutoRenderOutputStrings && !engine.getProofCommentsEnabled();
4968
4969
  derived = engine.forwardChain(facts, frules, brules, null, {
4969
4970
  captureExplanations: engine.getProofCommentsEnabled(),
4971
+ collectDerived: !skipDerivedCollection,
4970
4972
  prefixes,
4971
4973
  });
4972
4974
  outDerived = derived;
4973
- outTriples = derived.map((df) => df.fact);
4975
+ outTriples = skipDerivedCollection ? [] : derived.map((df) => df.fact);
4974
4976
  }
4975
4977
 
4976
4978
  const renderedOutputTriples = hasQueries ? outTriples : facts;
@@ -5849,6 +5851,14 @@ function __prepareForwardRule(r) {
5849
5851
  configurable: true,
5850
5852
  });
5851
5853
  }
5854
+ if (!hasOwn.call(r, '__needsForwardSkipCheck')) {
5855
+ Object.defineProperty(r, '__needsForwardSkipCheck', {
5856
+ value: !!(r.__headIsStrictGround || (r.__scopedSkipInfo && r.__scopedSkipInfo.needsSnap)),
5857
+ enumerable: false,
5858
+ writable: false,
5859
+ configurable: true,
5860
+ });
5861
+ }
5852
5862
  }
5853
5863
 
5854
5864
  function __graphTriplesOrTrue(term) {
@@ -6167,6 +6177,11 @@ function skolemizeTermForHeadBlanks(t, headBlankLabels, mapping, skCounter, firi
6167
6177
  }
6168
6178
 
6169
6179
  function skolemizeTripleForHeadBlanks(tr, headBlankLabels, mapping, skCounter, firingKey, globalMap) {
6180
+ // Fast path: the common case has no explicit head blanks. Do not allocate a
6181
+ // replacement Triple or compute a firing key when skolemization cannot change
6182
+ // anything. This matters for long single-premise chains such as
6183
+ // deep-taxonomy-100000, where every derived head triple is otherwise copied.
6184
+ if (!headBlankLabels || headBlankLabels.size === 0) return tr;
6170
6185
  return new Triple(
6171
6186
  skolemizeTermForHeadBlanks(tr.s, headBlankLabels, mapping, skCounter, firingKey, globalMap),
6172
6187
  skolemizeTermForHeadBlanks(tr.p, headBlankLabels, mapping, skCounter, firingKey, globalMap),
@@ -6642,6 +6657,35 @@ function ensureFactIndexes(facts) {
6642
6657
  for (let i = 0; i < facts.length; i++) indexFact(facts, facts[i], i, false);
6643
6658
  }
6644
6659
 
6660
+ function cloneFactIndexesForSnapshot(src, dest) {
6661
+ ensureFactIndexes(src);
6662
+
6663
+ function cloneArrayMap(map) {
6664
+ const out = new Map();
6665
+ for (const [k, arr] of map) out.set(k, arr.slice());
6666
+ return out;
6667
+ }
6668
+
6669
+ function cloneNestedArrayMap(map) {
6670
+ const out = new Map();
6671
+ for (const [k, inner] of map) {
6672
+ const innerOut = new Map();
6673
+ for (const [k2, arr] of inner) innerOut.set(k2, arr.slice());
6674
+ out.set(k, innerOut);
6675
+ }
6676
+ return out;
6677
+ }
6678
+
6679
+ Object.defineProperty(dest, '__byPred', { value: cloneArrayMap(src.__byPred), enumerable: false, writable: true });
6680
+ Object.defineProperty(dest, '__byPS', { value: cloneNestedArrayMap(src.__byPS), enumerable: false, writable: true });
6681
+ Object.defineProperty(dest, '__byPO', { value: cloneNestedArrayMap(src.__byPO), enumerable: false, writable: true });
6682
+ Object.defineProperty(dest, '__wildPred', { value: src.__wildPred.slice(), enumerable: false, writable: true });
6683
+ Object.defineProperty(dest, '__wildPS', { value: cloneArrayMap(src.__wildPS), enumerable: false, writable: true });
6684
+ Object.defineProperty(dest, '__wildPO', { value: cloneArrayMap(src.__wildPO), enumerable: false, writable: true });
6685
+ Object.defineProperty(dest, '__keySet', { value: new Set(src.__keySet), enumerable: false, writable: true });
6686
+ Object.defineProperty(dest, '__keySetComplete', { value: !!src.__keySetComplete, enumerable: false, writable: true });
6687
+ }
6688
+
6645
6689
  function indexFact(facts, tr, idx, addKeySet = true) {
6646
6690
  const sk = termFastKey(tr.s);
6647
6691
  const ok = termFastKey(tr.o);
@@ -6928,13 +6972,20 @@ function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
6928
6972
  if (!isSinglePremiseAgendaRuleSafe(r, backRules)) continue;
6929
6973
 
6930
6974
  const goal = r.premise[0];
6975
+ const goalSKey = termFastKey(goal.s);
6976
+ const goalOKey = termFastKey(goal.o);
6977
+ const fastSubjectVar = goal.p instanceof Iri && goal.s instanceof Var && goalOKey !== null ? goal.s.name : null;
6978
+ const fastObjectVar = goal.p instanceof Iri && goal.o instanceof Var && goalSKey !== null ? goal.o.name : null;
6931
6979
  const entry = {
6932
6980
  rule: r,
6933
6981
  ruleIndex: i,
6934
6982
  goal,
6935
6983
  goalPredTid: goal.p instanceof Iri ? goal.p.__tid : null,
6936
- goalSKey: termFastKey(goal.s),
6937
- goalOKey: termFastKey(goal.o),
6984
+ goalSKey,
6985
+ goalOKey,
6986
+ needsSkipCheck: !!r.__needsForwardSkipCheck,
6987
+ fastSubjectVar,
6988
+ fastObjectVar,
6938
6989
  };
6939
6990
 
6940
6991
  index.indexed.add(r);
@@ -8339,6 +8390,8 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8339
8390
  __attachGoalTable(backRules, goalTable);
8340
8391
 
8341
8392
  const captureExplanations = !(opts && opts.captureExplanations === false);
8393
+ const collectDerived = !(opts && opts.collectDerived === false);
8394
+ const hasDerivedCallback = typeof onDerived === 'function';
8342
8395
  const derivedForward = [];
8343
8396
  const varGen = [0];
8344
8397
  const skCounter = [0];
@@ -8395,7 +8448,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8395
8448
 
8396
8449
  function makeScopedSnapshot() {
8397
8450
  const snap = facts.slice();
8398
- ensureFactIndexes(snap);
8451
+ cloneFactIndexesForSnapshot(facts, snap);
8399
8452
  Object.defineProperty(snap, '__scopedSnapshot', {
8400
8453
  value: snap,
8401
8454
  enumerable: false,
@@ -8449,10 +8502,21 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8449
8502
  let changedHere = false;
8450
8503
  let rulesChanged = false;
8451
8504
 
8452
- // IMPORTANT: one skolem map per *rule firing*
8505
+ // IMPORTANT: one skolem map per *rule firing*. Instantiate premise
8506
+ // triples and build the firing key lazily: normal CLI runs do not capture
8507
+ // proof records, and most rules have no explicit head blanks, so the eager
8508
+ // work was pure allocation on large forward chains.
8453
8509
  const skMap = {};
8454
- const instantiatedPremises = r.premise.map((b) => applySubstTriple(b, s));
8455
- const fireKey = __firingKey(ruleIndex, instantiatedPremises);
8510
+ let instantiatedPremises = null;
8511
+ let fireKey = null;
8512
+ function getInstantiatedPremises() {
8513
+ if (instantiatedPremises === null) instantiatedPremises = r.premise.map((b) => applySubstTriple(b, s));
8514
+ return instantiatedPremises;
8515
+ }
8516
+ function getFireKey() {
8517
+ if (fireKey === null) fireKey = __firingKey(ruleIndex, getInstantiatedPremises());
8518
+ return fireKey;
8519
+ }
8456
8520
 
8457
8521
  // Support "dynamic" rule heads where the consequent is a term that
8458
8522
  // (after substitution) evaluates to a quoted formula.
@@ -8505,9 +8569,9 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8505
8569
  if (isFwRuleTriple || isBwRuleTriple) {
8506
8570
  if (!hasFactIndexed(facts, instantiated)) {
8507
8571
  pushFactIndexed(facts, instantiated);
8508
- const df = makeDerivedRecord(instantiated, r, instantiatedPremises, s, captureExplanations);
8509
- derivedForward.push(df);
8510
- if (typeof onDerived === 'function') onDerived(df);
8572
+ const df = makeDerivedRecord(instantiated, r, getInstantiatedPremises(), s, captureExplanations);
8573
+ if (collectDerived) derivedForward.push(df);
8574
+ if (hasDerivedCallback) onDerived(df);
8511
8575
  changedHere = true;
8512
8576
  }
8513
8577
 
@@ -8553,22 +8617,25 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8553
8617
  }
8554
8618
 
8555
8619
  // Only skolemize blank nodes that occur explicitly in the rule head
8556
- const inst = skolemizeTripleForHeadBlanks(
8557
- instantiated,
8558
- headBlankLabelsHere,
8559
- skMap,
8560
- skCounter,
8561
- fireKey,
8562
- headSkolemCache,
8563
- );
8620
+ const inst =
8621
+ headBlankLabelsHere && headBlankLabelsHere.size
8622
+ ? skolemizeTripleForHeadBlanks(
8623
+ instantiated,
8624
+ headBlankLabelsHere,
8625
+ skMap,
8626
+ skCounter,
8627
+ getFireKey(),
8628
+ headSkolemCache,
8629
+ )
8630
+ : instantiated;
8564
8631
 
8565
8632
  if (!isGroundTriple(inst)) continue;
8566
8633
  if (hasFactIndexed(facts, inst)) continue;
8567
8634
 
8568
8635
  pushFactIndexed(facts, inst);
8569
- const df = makeDerivedRecord(inst, r, instantiatedPremises, s, captureExplanations);
8570
- derivedForward.push(df);
8571
- if (typeof onDerived === 'function') onDerived(df);
8636
+ const df = makeDerivedRecord(inst, r, getInstantiatedPremises(), s, captureExplanations);
8637
+ if (collectDerived) derivedForward.push(df);
8638
+ if (hasDerivedCallback) onDerived(df);
8572
8639
 
8573
8640
  changedHere = true;
8574
8641
  }
@@ -8593,10 +8660,19 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8593
8660
  for (let ci = 0; ci < total; ci++) {
8594
8661
  const entry = ci < candidates.exactLen ? candidates.exact[ci] : candidates.wild[ci - candidates.exactLen];
8595
8662
  const r = entry.rule;
8596
- if (__skipForwardRuleNow(r)) continue;
8597
-
8598
- const s = unifyTriple(entry.goal, fact, __emptySubst());
8599
- if (s === null) continue;
8663
+ if (entry.needsSkipCheck && __skipForwardRuleNow(r)) continue;
8664
+
8665
+ let s;
8666
+ if (entry.fastSubjectVar !== null) {
8667
+ s = __emptySubst();
8668
+ s[entry.fastSubjectVar] = fact.s;
8669
+ } else if (entry.fastObjectVar !== null) {
8670
+ s = __emptySubst();
8671
+ s[entry.fastObjectVar] = fact.o;
8672
+ } else {
8673
+ s = unifyTriple(entry.goal, fact, __emptySubst());
8674
+ if (s === null) continue;
8675
+ }
8600
8676
 
8601
8677
  const outcome = __emitForwardRuleSolution(r, entry.ruleIndex, s);
8602
8678
  if (outcome.rulesChanged) {
@@ -8613,7 +8689,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8613
8689
  for (let i = 0; i < forwardRules.length; i++) {
8614
8690
  const r = forwardRules[i];
8615
8691
  if (agendaIndex.indexed.has(r)) continue;
8616
- if (__skipForwardRuleNow(r)) continue;
8692
+ if (r.__needsForwardSkipCheck && __skipForwardRuleNow(r)) continue;
8617
8693
 
8618
8694
  const headIsStrictGround = r.__headIsStrictGround;
8619
8695
  const maxSols = r.isFuse || headIsStrictGround ? 1 : undefined;
@@ -10639,13 +10715,25 @@ function normalizeRdfCompatibility(inputText) {
10639
10715
  return text;
10640
10716
  }
10641
10717
 
10718
+
10719
+ function isNumericLikeIdentifier(word) {
10720
+ if (typeof word !== 'string' || word.length === 0) return false;
10721
+ for (let j = 0; j < word.length; j++) {
10722
+ const code = word.charCodeAt(j);
10723
+ if (!((code >= 48 && code <= 57) || code === 46 || code === 45)) return false;
10724
+ }
10725
+ return true;
10726
+ }
10727
+
10642
10728
  function lex(inputText, opts = {}) {
10643
10729
  const rdf = !!(opts && opts.rdf);
10644
10730
  if (rdf) inputText = normalizeRdfCompatibility(inputText);
10645
10731
  // Avoid copying large ASCII/BMP inputs into an Array. Array.from() is
10646
10732
  // only needed when the text contains surrogate pairs and we want the old
10647
10733
  // code-point iteration behavior for non-BMP characters.
10648
- const chars = /[\uD800-\uDFFF]/.test(inputText) ? Array.from(inputText) : inputText;
10734
+ const hasSurrogates = /[\uD800-\uDFFF]/.test(inputText);
10735
+ const inputMayContainInvalidStringChar = hasSurrogates || /[\u0000\uFFFE\uFFFF]/.test(inputText);
10736
+ const chars = hasSurrogates ? Array.from(inputText) : inputText;
10649
10737
  const n = chars.length;
10650
10738
  let i = 0;
10651
10739
  const tokens = [];
@@ -10680,14 +10768,47 @@ function lex(inputText, opts = {}) {
10680
10768
  // Hard stops: delimiters cannot appear unescaped inside PNAME tokens.
10681
10769
  if (cc === '{' || cc === '}' || cc === '(' || cc === ')' || cc === '[' || cc === ']' || cc === ';' || cc === ',') break;
10682
10770
 
10683
- // Dot is allowed inside PN_LOCAL, but not at the end.
10684
- if (cc === '.') {
10685
- if (!canContinueAfterDot(peek(1))) break;
10686
- if (out !== null) out.push('.');
10771
+ const code = cc.charCodeAt(0);
10772
+
10773
+ // Common ASCII QName/identifier characters. Keep this branch inline so
10774
+ // ordinary N3 files do not call through the full Unicode PN_CHARS predicate
10775
+ // for every character.
10776
+ if (
10777
+ code === 58 || // ':'
10778
+ code === 95 || // '_'
10779
+ code === 45 || // '-'
10780
+ (code >= 48 && code <= 57) ||
10781
+ (code >= 65 && code <= 90) ||
10782
+ (code >= 97 && code <= 122)
10783
+ ) {
10784
+ if (out !== null) out.push(cc);
10687
10785
  i++;
10688
10786
  continue;
10689
10787
  }
10690
10788
 
10789
+ // Dot is allowed inside PN_LOCAL, but not at the end.
10790
+ if (cc === '.') {
10791
+ const next = peek(1);
10792
+ if (next === null) break;
10793
+ const ncode = next.charCodeAt(0);
10794
+ if (
10795
+ next === '%' ||
10796
+ next === '\\' ||
10797
+ ncode === 58 ||
10798
+ ncode === 95 ||
10799
+ ncode === 45 ||
10800
+ (ncode >= 48 && ncode <= 57) ||
10801
+ (ncode >= 65 && ncode <= 90) ||
10802
+ (ncode >= 97 && ncode <= 122) ||
10803
+ isIdentChar(next)
10804
+ ) {
10805
+ if (out !== null) out.push('.');
10806
+ i++;
10807
+ continue;
10808
+ }
10809
+ break;
10810
+ }
10811
+
10691
10812
  // Percent escape: %HH
10692
10813
  if (cc === '%') {
10693
10814
  const h1 = peek(1);
@@ -10806,22 +10927,47 @@ function lex(inputText, opts = {}) {
10806
10927
  continue;
10807
10928
  }
10808
10929
 
10809
- // 5) Single-character punctuation
10810
- if ('{}()[];,.'.includes(c)) {
10811
- const mapping = {
10812
- '{': 'LBrace',
10813
- '}': 'RBrace',
10814
- '(': 'LParen',
10815
- ')': 'RParen',
10816
- '[': 'LBracket',
10817
- ']': 'RBracket',
10818
- ';': 'Semicolon',
10819
- ',': 'Comma',
10820
- '.': 'Dot',
10821
- };
10822
- tokens.push(new Token(mapping[c], null, i));
10823
- i++;
10824
- continue;
10930
+ // 5) Single-character punctuation. Use a switch rather than allocating a
10931
+ // mapping object for every punctuation token in large inputs.
10932
+ switch (c) {
10933
+ case '{':
10934
+ tokens.push(new Token('LBrace', null, i));
10935
+ i++;
10936
+ continue;
10937
+ case '}':
10938
+ tokens.push(new Token('RBrace', null, i));
10939
+ i++;
10940
+ continue;
10941
+ case '(':
10942
+ tokens.push(new Token('LParen', null, i));
10943
+ i++;
10944
+ continue;
10945
+ case ')':
10946
+ tokens.push(new Token('RParen', null, i));
10947
+ i++;
10948
+ continue;
10949
+ case '[':
10950
+ tokens.push(new Token('LBracket', null, i));
10951
+ i++;
10952
+ continue;
10953
+ case ']':
10954
+ tokens.push(new Token('RBracket', null, i));
10955
+ i++;
10956
+ continue;
10957
+ case ';':
10958
+ tokens.push(new Token('Semicolon', null, i));
10959
+ i++;
10960
+ continue;
10961
+ case ',':
10962
+ tokens.push(new Token('Comma', null, i));
10963
+ i++;
10964
+ continue;
10965
+ case '.':
10966
+ tokens.push(new Token('Dot', null, i));
10967
+ i++;
10968
+ continue;
10969
+ default:
10970
+ break;
10825
10971
  }
10826
10972
 
10827
10973
  // String literal: short "..." or long """..."""
@@ -10880,27 +11026,37 @@ function lex(inputText, opts = {}) {
10880
11026
  continue;
10881
11027
  }
10882
11028
 
10883
- // Short string literal " ... "
11029
+ // Short string literal " ... ". Most data files contain plain
11030
+ // unescaped labels; keep that path slice-based and avoid building an
11031
+ // intermediate character array + raw quoted string.
10884
11032
  i++; // consume opening "
10885
- const sChars = [];
11033
+ const contentStart = i;
11034
+ let sChars = null;
11035
+ let closed = false;
10886
11036
  while (i < n) {
10887
11037
  const cc = chars[i];
10888
11038
  i++;
10889
11039
  if (cc === '\\') {
11040
+ if (sChars === null) sChars = [sliceChars(contentStart, i - 1)];
10890
11041
  if (i < n) {
10891
11042
  const esc = chars[i];
10892
11043
  i++;
10893
11044
  sChars.push('\\');
10894
11045
  sChars.push(esc);
11046
+ } else {
11047
+ sChars.push('\\');
10895
11048
  }
10896
11049
  continue;
10897
11050
  }
10898
- if (cc === '"') break;
10899
- sChars.push(cc);
11051
+ if (cc === '"') {
11052
+ closed = true;
11053
+ break;
11054
+ }
11055
+ if (sChars !== null) sChars.push(cc);
10900
11056
  }
10901
- const raw = '"' + sChars.join('') + '"';
10902
- const decoded = decodeN3StringEscapes(stripQuotes(raw), start);
10903
- assertValidStringLiteralValue(decoded, start);
11057
+ const rawContent = sChars === null ? sliceChars(contentStart, closed ? i - 1 : i) : sChars.join('');
11058
+ const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
11059
+ if (sChars !== null || inputMayContainInvalidStringChar) assertValidStringLiteralValue(decoded, start);
10904
11060
  const s = JSON.stringify(decoded); // canonical short quoted form
10905
11061
  tokens.push(new Token('Literal', s, start));
10906
11062
  continue;
@@ -10964,25 +11120,33 @@ function lex(inputText, opts = {}) {
10964
11120
 
10965
11121
  // Short string literal ' ... '
10966
11122
  i++; // consume opening '
10967
- const sChars = [];
11123
+ const contentStart = i;
11124
+ let sChars = null;
11125
+ let closed = false;
10968
11126
  while (i < n) {
10969
11127
  const cc = chars[i];
10970
11128
  i++;
10971
11129
  if (cc === '\\') {
11130
+ if (sChars === null) sChars = [sliceChars(contentStart, i - 1)];
10972
11131
  if (i < n) {
10973
11132
  const esc = chars[i];
10974
11133
  i++;
10975
11134
  sChars.push('\\');
10976
11135
  sChars.push(esc);
11136
+ } else {
11137
+ sChars.push('\\');
10977
11138
  }
10978
11139
  continue;
10979
11140
  }
10980
- if (cc === "'") break;
10981
- sChars.push(cc);
11141
+ if (cc === "'") {
11142
+ closed = true;
11143
+ break;
11144
+ }
11145
+ if (sChars !== null) sChars.push(cc);
10982
11146
  }
10983
- const raw = "'" + sChars.join('') + "'";
10984
- const decoded = decodeN3StringEscapes(stripQuotes(raw), start);
10985
- assertValidStringLiteralValue(decoded, start);
11147
+ const rawContent = sChars === null ? sliceChars(contentStart, closed ? i - 1 : i) : sChars.join('');
11148
+ const decoded = sChars === null ? rawContent : decodeN3StringEscapes(rawContent, start);
11149
+ if (sChars !== null || inputMayContainInvalidStringChar) assertValidStringLiteralValue(decoded, start);
10986
11150
  const s = JSON.stringify(decoded); // canonical short quoted form
10987
11151
  tokens.push(new Token('Literal', s, start));
10988
11152
  continue;
@@ -11106,7 +11270,7 @@ function lex(inputText, opts = {}) {
11106
11270
  }
11107
11271
  if (word === 'true' || word === 'false') {
11108
11272
  tokens.push(new Token('Literal', word, start));
11109
- } else if ([...word].every((ch) => /[0-9.-]/.test(ch))) {
11273
+ } else if (isNumericLikeIdentifier(word)) {
11110
11274
  tokens.push(new Token('Literal', word, start));
11111
11275
  } else {
11112
11276
  tokens.push(new Token('Ident', word, start));
@@ -11691,7 +11855,7 @@ class Parser {
11691
11855
  } else if (tok2.typ === 'Ident') {
11692
11856
  const qn = tok2.value || '';
11693
11857
  if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok2, qn);
11694
- assertValidQNamePrefix(qn.split(':', 1)[0], this.fail.bind(this), tok2, '@prefix directive IRI');
11858
+ assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok2, '@prefix directive IRI');
11695
11859
  iri = this.prefixes.expandQName(qn);
11696
11860
  } else {
11697
11861
  this.fail(`Expected IRI after @prefix, got ${tok2.toString()}`, tok2);
@@ -11708,7 +11872,7 @@ class Parser {
11708
11872
  } else if (tok.typ === 'Ident') {
11709
11873
  const qn = tok.value || '';
11710
11874
  if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok, qn);
11711
- assertValidQNamePrefix(qn.split(':', 1)[0], this.fail.bind(this), tok, '@base directive IRI');
11875
+ assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok, '@base directive IRI');
11712
11876
  iri = this.prefixes.expandQName(qn);
11713
11877
  } else {
11714
11878
  this.fail(`Expected IRI after @base, got ${tok.toString()}`, tok);
@@ -11737,7 +11901,7 @@ class Parser {
11737
11901
  } else if (tok2.typ === 'Ident') {
11738
11902
  const qn = tok2.value || '';
11739
11903
  if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok2, qn);
11740
- assertValidQNamePrefix(qn.split(':', 1)[0], this.fail.bind(this), tok2, '@prefix directive IRI');
11904
+ assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok2, '@prefix directive IRI');
11741
11905
  iri = this.prefixes.expandQName(qn);
11742
11906
  } else {
11743
11907
  this.fail(`Expected IRI after PREFIX, got ${tok2.toString()}`, tok2);
@@ -11758,7 +11922,7 @@ class Parser {
11758
11922
  } else if (tok.typ === 'Ident') {
11759
11923
  const qn = tok.value || '';
11760
11924
  if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), tok, qn);
11761
- assertValidQNamePrefix(qn.split(':', 1)[0], this.fail.bind(this), tok, 'BASE directive IRI');
11925
+ assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), tok, 'BASE directive IRI');
11762
11926
  iri = this.prefixes.expandQName(qn);
11763
11927
  } else {
11764
11928
  this.fail(`Expected IRI after BASE, got ${tok.toString()}`, tok);
@@ -11805,14 +11969,18 @@ class Parser {
11805
11969
  const name = val || '';
11806
11970
  if (name === 'a') {
11807
11971
  return internIri(RDF_NS + 'type');
11808
- } else if (name.startsWith('_:')) {
11972
+ }
11973
+ const sep = name.indexOf(':');
11974
+ if (sep === 1 && name.charCodeAt(0) === 95) {
11809
11975
  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
11976
  }
11977
+ if (sep >= 0) {
11978
+ const prefixName = name.slice(0, sep);
11979
+ assertValidQNamePrefix(prefixName, this.fail.bind(this), tok);
11980
+ const base = this.prefixes.map[prefixName] || '';
11981
+ return internIri(base ? base + name.slice(sep + 1) : name);
11982
+ }
11983
+ failInvalidKeywordLikeIdent(this.fail.bind(this), tok, name);
11816
11984
  }
11817
11985
 
11818
11986
  if (typ === 'Literal') {
@@ -11843,7 +12011,7 @@ class Parser {
11843
12011
  } else if (dtTok.typ === 'Ident') {
11844
12012
  const qn = dtTok.value || '';
11845
12013
  if (!qn.includes(':')) failInvalidKeywordLikeIdent(this.fail.bind(this), dtTok, qn);
11846
- assertValidQNamePrefix(qn.split(':', 1)[0], this.fail.bind(this), dtTok, 'datatype prefixed name');
12014
+ assertValidQNamePrefix(qn.slice(0, qn.indexOf(':')), this.fail.bind(this), dtTok, 'datatype prefixed name');
11847
12015
  dtIri = this.prefixes.expandQName(qn);
11848
12016
  } else {
11849
12017
  this.fail(`Expected datatype after ^^, got ${dtTok.toString()}`, dtTok);
@@ -12088,15 +12256,21 @@ class Parser {
12088
12256
 
12089
12257
  while (true) {
12090
12258
  const { verb, invert } = this.parseStatementVerb();
12091
- const objects = this.parseObjectList();
12092
12259
 
12093
- // If VERB or OBJECTS contained paths, their helper triples must come
12094
- // before the triples that consume the path results (Easter depends on this).
12095
- this.flushPendingTriples(out);
12260
+ while (true) {
12261
+ const o = this.parseTerm();
12262
+ const postTriples = this.pendingTriplesAfter;
12263
+ this.pendingTriplesAfter = [];
12264
+
12265
+ // If VERB or OBJECT contained paths, their helper triples must come
12266
+ // before the triple that consumes the path result (Easter depends on this).
12267
+ this.flushPendingTriples(out);
12096
12268
 
12097
- for (const { term: o, postTriples } of objects) {
12098
12269
  out.push(new Triple(invert ? o : subject, verb, invert ? subject : o));
12099
- if (postTriples && postTriples.length) out.push(...postTriples);
12270
+ if (postTriples.length) out.push(...postTriples);
12271
+
12272
+ if (this.peek().typ !== 'Comma') break;
12273
+ this.next();
12100
12274
  }
12101
12275
 
12102
12276
  if (this.peek().typ === 'Semicolon') {
@@ -12110,27 +12284,6 @@ class Parser {
12110
12284
  return out;
12111
12285
  }
12112
12286
 
12113
- parseObjectList() {
12114
- // Capture any trailing property-list triples produced while parsing each
12115
- // object term so we can emit them *after* the triple that references the
12116
- // term. (See pendingTriplesAfter in the constructor.)
12117
-
12118
- const objs = [];
12119
- const readObj = () => {
12120
- const o = this.parseTerm();
12121
- const post = this.pendingTriplesAfter;
12122
- this.pendingTriplesAfter = [];
12123
- objs.push({ term: o, postTriples: post });
12124
- };
12125
-
12126
- readObj();
12127
- while (this.peek().typ === 'Comma') {
12128
- this.next();
12129
- readObj();
12130
- }
12131
- return objs;
12132
- }
12133
-
12134
12287
  makeRule(left, right, isForward) {
12135
12288
  let premiseTerm, conclTerm;
12136
12289