parseman 0.29.0 → 0.31.0

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/index.js CHANGED
@@ -18,6 +18,15 @@ function intersects(a, b) {
18
18
  function fromChar(code) {
19
19
  return { kind: "ranges", ranges: [{ lo: code, hi: code }] };
20
20
  }
21
+ function startsFirstSet(combinator, input, pos) {
22
+ const fs = combinator._meta.firstSet;
23
+ if (fs.kind === "any") return true;
24
+ if (fs.kind === "empty") return false;
25
+ const code = input.codePointAt(pos);
26
+ if (code === void 0) return false;
27
+ for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
28
+ return false;
29
+ }
21
30
  function fromRange(lo, hi) {
22
31
  return { kind: "ranges", ranges: [{ lo, hi }] };
23
32
  }
@@ -944,6 +953,17 @@ function deriveExpected(c) {
944
953
  return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
945
954
  case "attempt":
946
955
  return deriveExpected(def.parser);
956
+ // Delegating wrappers whose first-token failure is their inner parser's — must
957
+ // pass through so a start-failure (and the first-set-miss fast-path in attempt/
958
+ // node, which reads this) names the real expected token, not the wrapper tag.
959
+ case "field":
960
+ case "withCtx":
961
+ case "recover":
962
+ return deriveExpected(def.parser);
963
+ case "skip":
964
+ return deriveExpected(def.main);
965
+ case "expect":
966
+ return def.expected;
947
967
  case "node":
948
968
  case "grammar":
949
969
  case "trivia":
@@ -1007,7 +1027,7 @@ function choice(...args) {
1007
1027
  disjoint
1008
1028
  };
1009
1029
  const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
1010
- const autoNot = !disjoint && !hasGates && strategy?.tag === "firstMatch" ? computeAutoNot(parsers) : parsers.map(() => null);
1030
+ const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
1011
1031
  let greedyLitMap = null;
1012
1032
  let sortedParsers = null;
1013
1033
  const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
@@ -1133,8 +1153,47 @@ function detectStrategy(parsers) {
1133
1153
  );
1134
1154
  return { tag: "literalsLongestFirst", sortedIndices };
1135
1155
  }
1156
+ const shared = detectSharedPrefix(parsers);
1157
+ if (shared !== null) return shared;
1136
1158
  return { tag: "firstMatch" };
1137
1159
  }
1160
+ function bareLeadingTermKey(term) {
1161
+ const d = term._def;
1162
+ if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
1163
+ if (d.tag === "regex") return `R:${d.source}\0${d.flags}`;
1164
+ return null;
1165
+ }
1166
+ function detectSharedPrefix(parsers) {
1167
+ if (parsers.length < 2) return null;
1168
+ let key = null;
1169
+ let prefix = null;
1170
+ const members = [];
1171
+ for (let i = 0; i < parsers.length; i++) {
1172
+ const term0 = leadingTermOfArm(parsers[i]);
1173
+ if (term0 === null) return null;
1174
+ const k = bareLeadingTermKey(term0);
1175
+ if (k === null) return null;
1176
+ if (key === null) {
1177
+ key = k;
1178
+ prefix = term0;
1179
+ } else if (k !== key) return null;
1180
+ members.push(i);
1181
+ }
1182
+ if (prefix === null || members.length < 2) return null;
1183
+ return { tag: "sharedPrefix", prefix, members };
1184
+ }
1185
+ function leadingTermOfArm(arm) {
1186
+ let d = arm._def;
1187
+ for (; ; ) {
1188
+ if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
1189
+ d = d.parser._def;
1190
+ continue;
1191
+ }
1192
+ break;
1193
+ }
1194
+ if (d.tag !== "sequence" || d.parsers.length < 2) return null;
1195
+ return d.parsers[0];
1196
+ }
1138
1197
  function computeAutoNot(parsers) {
1139
1198
  return parsers.map((p, i) => {
1140
1199
  const litVal = getCoreLiteralValue(p);
@@ -1728,11 +1787,20 @@ function sequence(...parsers) {
1728
1787
 
1729
1788
  // src/combinators/attempt.ts
1730
1789
  function attempt(parser2) {
1790
+ const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
1791
+ let failExpected;
1731
1792
  return {
1732
1793
  _tag: "attempt",
1733
1794
  _meta: parser2._meta,
1734
1795
  _def: { tag: "attempt", parser: parser2 },
1735
1796
  parse(input, pos, ctx) {
1797
+ if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(parser2, input, pos)) {
1798
+ if (failExpected === void 0) {
1799
+ const e = deriveExpected(parser2);
1800
+ failExpected = e.length > 0 ? e : [parser2._tag];
1801
+ }
1802
+ return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
1803
+ }
1736
1804
  const mark = saveCstMark(ctx);
1737
1805
  const log = ctx._triviaLog?.length;
1738
1806
  const result = parser2.parse(input, pos, ctx);
@@ -1745,7 +1813,7 @@ function attempt(parser2) {
1745
1813
  }
1746
1814
 
1747
1815
  // src/combinators/repeat.ts
1748
- function repItem(combinator, input, cur, ctx) {
1816
+ function repItem(combinator, input, cur, ctx, guardable) {
1749
1817
  const mark = saveTriviaMark(ctx);
1750
1818
  let pos = cur;
1751
1819
  if (ctx.trivia) {
@@ -1761,6 +1829,10 @@ function repItem(combinator, input, cur, ctx) {
1761
1829
  rollbackTrivia(ctx, mark);
1762
1830
  return "stop";
1763
1831
  }
1832
+ if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
1833
+ rollbackTrivia(ctx, mark);
1834
+ return "stop";
1835
+ }
1764
1836
  const result = combinator.parse(input, pos, ctx);
1765
1837
  if (!result.ok) {
1766
1838
  rollbackTrivia(ctx, mark);
@@ -1780,6 +1852,7 @@ function many(combinator) {
1780
1852
  };
1781
1853
  const def = { tag: "many", parser: combinator, min: 0 };
1782
1854
  let expected;
1855
+ const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
1783
1856
  return {
1784
1857
  _tag: "many",
1785
1858
  _meta: meta,
@@ -1788,7 +1861,7 @@ function many(combinator) {
1788
1861
  const values = def.valueUnused ? void 0 : [];
1789
1862
  let cur = pos;
1790
1863
  while (cur < input.length) {
1791
- const item = repItem(combinator, input, cur, ctx);
1864
+ const item = repItem(combinator, input, cur, ctx, guardable);
1792
1865
  if (item === "stop") break;
1793
1866
  if ("fail" in item) {
1794
1867
  const sync = ctx._tolerant ? ctx._sync : void 0;
@@ -1816,6 +1889,7 @@ function oneOrMore(combinator) {
1816
1889
  };
1817
1890
  const def = { tag: "oneOrMore", parser: combinator, min: 1 };
1818
1891
  let expected;
1892
+ const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
1819
1893
  return {
1820
1894
  _tag: "oneOrMore",
1821
1895
  _meta: meta,
@@ -1826,7 +1900,7 @@ function oneOrMore(combinator) {
1826
1900
  const values = def.valueUnused ? void 0 : [first.value];
1827
1901
  let cur = first.span.end;
1828
1902
  while (cur < input.length) {
1829
- const item = repItem(combinator, input, cur, ctx);
1903
+ const item = repItem(combinator, input, cur, ctx, guardable);
1830
1904
  if (item === "stop") break;
1831
1905
  if ("fail" in item) {
1832
1906
  const sync = ctx._tolerant ? ctx._sync : void 0;
@@ -1869,15 +1943,6 @@ function optional(combinator) {
1869
1943
  }
1870
1944
  };
1871
1945
  }
1872
- function startsFirstSet(combinator, input, pos) {
1873
- const fs = combinator._meta.firstSet;
1874
- if (fs.kind === "any") return true;
1875
- if (fs.kind === "empty") return false;
1876
- const code = input.codePointAt(pos);
1877
- if (code === void 0) return false;
1878
- for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
1879
- return false;
1880
- }
1881
1946
  function sepBy(combinator, separator) {
1882
1947
  const meta = {
1883
1948
  firstSet: combinator._meta.firstSet,
@@ -2411,11 +2476,20 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2411
2476
  const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : true);
2412
2477
  const clonesState = build ? buildReadsState(def) : true;
2413
2478
  const capturesFields = parserHasOwnFields(combinator) && (build ? buildReadsFields(def) : true);
2479
+ const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
2480
+ let failExpected;
2414
2481
  return {
2415
2482
  _tag: "node",
2416
2483
  _meta: meta,
2417
2484
  _def: def,
2418
2485
  parse(input, pos, ctx) {
2486
+ if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
2487
+ if (failExpected === void 0) {
2488
+ const e = deriveExpected(combinator);
2489
+ failExpected = e.length > 0 ? e : [combinator._tag];
2490
+ }
2491
+ return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
2492
+ }
2419
2493
  const saved = beginCstNodeCapture(ctx);
2420
2494
  const savedFields = ctx._fields;
2421
2495
  ctx._fields = capturesFields ? [] : void 0;
@@ -4327,9 +4401,9 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
4327
4401
  const i = ind(ctx);
4328
4402
  const lf = v(ctx, "_lf");
4329
4403
  return [
4330
- `${i}if (_ctx._cstLeaves) {`,
4404
+ `${i}if (_ctx._cstLeaves || _ctx._cstRawChildren) {`,
4331
4405
  `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } }`,
4332
- `${i} _ctx._cstLeaves.push(${lf})`,
4406
+ `${i} if (_ctx._cstLeaves) _ctx._cstLeaves.push(${lf})`,
4333
4407
  `${i} if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(${lf})`,
4334
4408
  `${i}}`
4335
4409
  ];
@@ -4428,6 +4502,7 @@ var SWITCH_RANGE_LIMIT = 4;
4428
4502
  var SWITCH_MAX_CASES = 48;
4429
4503
  var SWITCH_MIN_CASES = 3;
4430
4504
  var _forceDisjointIf = false;
4505
+ var _forceNoSharedPrefix = false;
4431
4506
  function planDisjointDispatch(parsers) {
4432
4507
  const cases = [];
4433
4508
  let total = 0;
@@ -5063,7 +5138,7 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos, cov
5063
5138
  stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
5064
5139
  return { stmts, valueVar: valV, endVar: endV };
5065
5140
  }
5066
- function emitFirstMatch(def, ctx, pos, coverageBase) {
5141
+ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
5067
5142
  const resValV = v(ctx, "_crv");
5068
5143
  const resEndV = v(ctx, "_cre");
5069
5144
  const resOkV = v(ctx, "_crok");
@@ -5077,6 +5152,7 @@ function emitFirstMatch(def, ctx, pos, coverageBase) {
5077
5152
  ...autoRejectedV === void 0 ? [] : [`${ind0}const ${autoRejectedV} = []`],
5078
5153
  `${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
5079
5154
  ];
5155
+ if (preStmts) stmts.push(...preStmts);
5080
5156
  for (let i = 0; i < def.parsers.length; i++) {
5081
5157
  const p = def.parsers[i];
5082
5158
  const gate = def.gates[i];
@@ -5084,11 +5160,12 @@ function emitFirstMatch(def, ctx, pos, coverageBase) {
5084
5160
  const atStart = failsAtStart(p);
5085
5161
  const staticFx = armStaticExpected(ctx, p);
5086
5162
  const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
5087
- const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : (() => {
5163
+ const baseFsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : (() => {
5088
5164
  if (p._meta.firstSet.kind !== "any" || matchesEmpty(p)) return null;
5089
5165
  const deep = firstSetOf(p);
5090
5166
  return deep.kind === "ranges" ? firstSetCond(codeV, deep) : null;
5091
5167
  })();
5168
+ const fsGuard = armGuardPrefix === void 0 ? baseFsGuard : baseFsGuard === null ? armGuardPrefix : `${armGuardPrefix} && (${baseFsGuard})`;
5092
5169
  let gateCond = null;
5093
5170
  if (gate) {
5094
5171
  const gateIdx = ctx.mapFns.length;
@@ -5157,11 +5234,64 @@ function emitFirstMatch(def, ctx, pos, coverageBase) {
5157
5234
  stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
5158
5235
  return { stmts, valueVar: resValV, endVar: resEndV };
5159
5236
  }
5237
+ function emitsAsNamedFn(p, ctx) {
5238
+ if (p._def.tag === "lazy") return true;
5239
+ if (ctx.ruleNames?.has(p)) return true;
5240
+ if (ctx.namedParsers.has(p)) return true;
5241
+ const usage = ctx.lazyUsage;
5242
+ return !!usage && !ctx.noHoist && !ctx.capAsTrivia && isHoistableTag(p._def.tag) && (usage.counts.get(p) ?? 0) > 1 && (usage.sizes.get(p) ?? 0) >= HOIST_MIN_SUBTREE;
5243
+ }
5244
+ function armReplayInScope(arm, ctx) {
5245
+ let p = arm;
5246
+ for (; ; ) {
5247
+ if (emitsAsNamedFn(p, ctx)) return false;
5248
+ const d = p._def;
5249
+ if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
5250
+ p = d.parser;
5251
+ continue;
5252
+ }
5253
+ return d.tag === "sequence";
5254
+ }
5255
+ }
5256
+ function emitSharedPrefix(def, strategy, ctx, pos) {
5257
+ const { prefix, members } = strategy;
5258
+ const savedCapturing = ctx.capturing;
5259
+ ctx.capturing = false;
5260
+ const pfx = emitFallible(prefix, ctx, pos, false);
5261
+ ctx.capturing = savedCapturing;
5262
+ const map = ctx.replayPrefix ??= /* @__PURE__ */ new Map();
5263
+ const keys = [];
5264
+ for (const i of members) {
5265
+ const term0 = leadingTermOfArm(def.parsers[i]);
5266
+ if (term0 === null) continue;
5267
+ map.set(term0, { valVar: pfx.valVar, endVar: pfx.endVar });
5268
+ keys.push(term0);
5269
+ }
5270
+ const r = emitFirstMatch(def, ctx, pos, void 0, pfx.stmts, pfx.okVar);
5271
+ for (const k of keys) map.delete(k);
5272
+ return r;
5273
+ }
5274
+ function emitReplayPrefixLeaf(ctx, pos, replay) {
5275
+ const vv = v(ctx);
5276
+ return {
5277
+ stmts: [
5278
+ `${ind(ctx)}const ${vv} = ${replay.valVar}`,
5279
+ ...emitLeafCapture(ctx, vv, pos, replay.endVar)
5280
+ ],
5281
+ valueVar: vv,
5282
+ endVar: replay.endVar
5283
+ };
5284
+ }
5160
5285
  function emitNonDisjoint(def, strategy, allExpected, ctx, pos, coverageBase) {
5161
5286
  if (strategy.tag === "greedyClassify")
5162
5287
  return emitGreedyClassify(def, strategy.superIndex, ctx, pos, coverageBase);
5163
5288
  if (strategy.tag === "literalsLongestFirst")
5164
5289
  return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos, coverageBase);
5290
+ if (strategy.tag === "sharedPrefix") {
5291
+ if (!_forceNoSharedPrefix && coverageBase === void 0 && !ctx.recovery && strategy.members.every((i) => armReplayInScope(def.parsers[i], ctx)))
5292
+ return emitSharedPrefix(def, strategy, ctx, pos);
5293
+ return emitFirstMatch(def, ctx, pos, coverageBase);
5294
+ }
5165
5295
  return emitFirstMatch(def, ctx, pos, coverageBase);
5166
5296
  }
5167
5297
  function pushMapFn(ctx, fn, src) {
@@ -5677,8 +5807,10 @@ function emitNode(def, ctx, pos) {
5677
5807
  const directCstV = !structural && (!capturesChildren || !capturesRaw) ? v(ctx, "_dcst") : null;
5678
5808
  const directCstGate = directCstV === null ? "false" : `${profileCapture} || _ctx.build?._parsemanCstOutput === true`;
5679
5809
  const structuralCapturesTrivia = structural && (def.captureTrivia === true || def.trailingTrivia === true);
5810
+ const chNeededExpr = def.unwrap || def.collapse ? "true" : `(_ctx._pmReadsCh ??= (_ctx.build === undefined || _ctx.build._parsemanReadsChildren !== false || _ctx.build._parsemanCstCollapse !== undefined))`;
5811
+ const chAlloc = def.unwrap || def.collapse ? `${profileRecognizer} ? undefined : []` : `${profileRecognizer} ? undefined : ((${profileCapture} || ${chNeededExpr}) ? [] : undefined)`;
5680
5812
  const allocStmt = structural ? `${i}const ${capTLv} = !(${profileRecognizer}) && (${profileCapture} || ${structuralCapturesTrivia ? "true" : hostTriviaGate}), ${capSTv} = !(${profileRecognizer} || ${profileCapture}) && (_ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 6)))${capFv ? `, ${capFv} = !(${profileRecognizer}) && (${profileCapture} || (_ctx.build !== undefined && _hostReads(_ctx.build, 2)))` : ""}
5681
- ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : ${innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`}` : capturesTrivia ? `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5813
+ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : ${innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`}` : capturesTrivia ? `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5682
5814
  ` : ""}${i}const ${capTLv} = !(${profileRecognizer}), ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}, ${tlV} = ${profileRecognizer} ? undefined : []` : `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5683
5815
  ` : ""}${i}const ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}`;
5684
5816
  const innerTl = structural || capturesTrivia ? structural && !innerEnablesTriviaCapture ? `${capTLv} ? ${tlV} : undefined` : tlV : "undefined";
@@ -5875,6 +6007,8 @@ function emitExpect(def, ctx, pos) {
5875
6007
  return { stmts, valueVar: valVar, endVar };
5876
6008
  }
5877
6009
  function emit(p, ctx, pos) {
6010
+ const replay = ctx.replayPrefix?.get(p);
6011
+ if (replay !== void 0) return emitReplayPrefixLeaf(ctx, pos, replay);
5878
6012
  const dispatch = (emissionPos = pos) => {
5879
6013
  const savedRule = ctx.activeCoverageRuleId;
5880
6014
  const rule = ctx.coverage?.plan.rules.get(p);
@@ -7244,7 +7378,7 @@ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triv
7244
7378
  return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
7245
7379
  }
7246
7380
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
7247
- const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3, fields, span2, rawChildren2, triviaLog2, state2);
7381
+ const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2);
7248
7382
  host._parsemanCstOutput = true;
7249
7383
  if (collapse) host._parsemanCstCollapse = collapse;
7250
7384
  return host;