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.
@@ -26,6 +26,15 @@ function intersects(a, b) {
26
26
  function fromChar(code) {
27
27
  return { kind: "ranges", ranges: [{ lo: code, hi: code }] };
28
28
  }
29
+ function startsFirstSet(combinator, input, pos) {
30
+ const fs2 = combinator._meta.firstSet;
31
+ if (fs2.kind === "any") return true;
32
+ if (fs2.kind === "empty") return false;
33
+ const code = input.codePointAt(pos);
34
+ if (code === void 0) return false;
35
+ for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
36
+ return false;
37
+ }
29
38
  function fromRange(lo, hi) {
30
39
  return { kind: "ranges", ranges: [{ lo, hi }] };
31
40
  }
@@ -985,6 +994,17 @@ function deriveExpected(c) {
985
994
  return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
986
995
  case "attempt":
987
996
  return deriveExpected(def.parser);
997
+ // Delegating wrappers whose first-token failure is their inner parser's — must
998
+ // pass through so a start-failure (and the first-set-miss fast-path in attempt/
999
+ // node, which reads this) names the real expected token, not the wrapper tag.
1000
+ case "field":
1001
+ case "withCtx":
1002
+ case "recover":
1003
+ return deriveExpected(def.parser);
1004
+ case "skip":
1005
+ return deriveExpected(def.main);
1006
+ case "expect":
1007
+ return def.expected;
988
1008
  case "node":
989
1009
  case "grammar":
990
1010
  case "trivia":
@@ -1045,7 +1065,7 @@ function choice(...args) {
1045
1065
  disjoint
1046
1066
  };
1047
1067
  const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
1048
- const autoNot = !disjoint && !hasGates && strategy?.tag === "firstMatch" ? computeAutoNot(parsers) : parsers.map(() => null);
1068
+ const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
1049
1069
  let greedyLitMap = null;
1050
1070
  let sortedParsers = null;
1051
1071
  const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
@@ -1171,8 +1191,47 @@ function detectStrategy(parsers) {
1171
1191
  );
1172
1192
  return { tag: "literalsLongestFirst", sortedIndices };
1173
1193
  }
1194
+ const shared = detectSharedPrefix(parsers);
1195
+ if (shared !== null) return shared;
1174
1196
  return { tag: "firstMatch" };
1175
1197
  }
1198
+ function bareLeadingTermKey(term) {
1199
+ const d = term._def;
1200
+ if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
1201
+ if (d.tag === "regex") return `R:${d.source}\0${d.flags}`;
1202
+ return null;
1203
+ }
1204
+ function detectSharedPrefix(parsers) {
1205
+ if (parsers.length < 2) return null;
1206
+ let key = null;
1207
+ let prefix = null;
1208
+ const members = [];
1209
+ for (let i = 0; i < parsers.length; i++) {
1210
+ const term0 = leadingTermOfArm(parsers[i]);
1211
+ if (term0 === null) return null;
1212
+ const k = bareLeadingTermKey(term0);
1213
+ if (k === null) return null;
1214
+ if (key === null) {
1215
+ key = k;
1216
+ prefix = term0;
1217
+ } else if (k !== key) return null;
1218
+ members.push(i);
1219
+ }
1220
+ if (prefix === null || members.length < 2) return null;
1221
+ return { tag: "sharedPrefix", prefix, members };
1222
+ }
1223
+ function leadingTermOfArm(arm) {
1224
+ let d = arm._def;
1225
+ for (; ; ) {
1226
+ if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
1227
+ d = d.parser._def;
1228
+ continue;
1229
+ }
1230
+ break;
1231
+ }
1232
+ if (d.tag !== "sequence" || d.parsers.length < 2) return null;
1233
+ return d.parsers[0];
1234
+ }
1176
1235
  function computeAutoNot(parsers) {
1177
1236
  return parsers.map((p, i) => {
1178
1237
  const litVal = getCoreLiteralValue(p);
@@ -1766,11 +1825,20 @@ function sequence(...parsers) {
1766
1825
 
1767
1826
  // src/combinators/attempt.ts
1768
1827
  function attempt(parser2) {
1828
+ const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
1829
+ let failExpected;
1769
1830
  return {
1770
1831
  _tag: "attempt",
1771
1832
  _meta: parser2._meta,
1772
1833
  _def: { tag: "attempt", parser: parser2 },
1773
1834
  parse(input, pos, ctx) {
1835
+ if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(parser2, input, pos)) {
1836
+ if (failExpected === void 0) {
1837
+ const e = deriveExpected(parser2);
1838
+ failExpected = e.length > 0 ? e : [parser2._tag];
1839
+ }
1840
+ return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
1841
+ }
1774
1842
  const mark = saveCstMark(ctx);
1775
1843
  const log = ctx._triviaLog?.length;
1776
1844
  const result = parser2.parse(input, pos, ctx);
@@ -1783,7 +1851,7 @@ function attempt(parser2) {
1783
1851
  }
1784
1852
 
1785
1853
  // src/combinators/repeat.ts
1786
- function repItem(combinator, input, cur, ctx) {
1854
+ function repItem(combinator, input, cur, ctx, guardable) {
1787
1855
  const mark = saveTriviaMark(ctx);
1788
1856
  let pos = cur;
1789
1857
  if (ctx.trivia) {
@@ -1799,6 +1867,10 @@ function repItem(combinator, input, cur, ctx) {
1799
1867
  rollbackTrivia(ctx, mark);
1800
1868
  return "stop";
1801
1869
  }
1870
+ if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
1871
+ rollbackTrivia(ctx, mark);
1872
+ return "stop";
1873
+ }
1802
1874
  const result = combinator.parse(input, pos, ctx);
1803
1875
  if (!result.ok) {
1804
1876
  rollbackTrivia(ctx, mark);
@@ -1818,6 +1890,7 @@ function many(combinator) {
1818
1890
  };
1819
1891
  const def = { tag: "many", parser: combinator, min: 0 };
1820
1892
  let expected;
1893
+ const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
1821
1894
  return {
1822
1895
  _tag: "many",
1823
1896
  _meta: meta,
@@ -1826,7 +1899,7 @@ function many(combinator) {
1826
1899
  const values = def.valueUnused ? void 0 : [];
1827
1900
  let cur = pos;
1828
1901
  while (cur < input.length) {
1829
- const item = repItem(combinator, input, cur, ctx);
1902
+ const item = repItem(combinator, input, cur, ctx, guardable);
1830
1903
  if (item === "stop") break;
1831
1904
  if ("fail" in item) {
1832
1905
  const sync = ctx._tolerant ? ctx._sync : void 0;
@@ -1854,6 +1927,7 @@ function oneOrMore(combinator) {
1854
1927
  };
1855
1928
  const def = { tag: "oneOrMore", parser: combinator, min: 1 };
1856
1929
  let expected;
1930
+ const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
1857
1931
  return {
1858
1932
  _tag: "oneOrMore",
1859
1933
  _meta: meta,
@@ -1864,7 +1938,7 @@ function oneOrMore(combinator) {
1864
1938
  const values = def.valueUnused ? void 0 : [first.value];
1865
1939
  let cur = first.span.end;
1866
1940
  while (cur < input.length) {
1867
- const item = repItem(combinator, input, cur, ctx);
1941
+ const item = repItem(combinator, input, cur, ctx, guardable);
1868
1942
  if (item === "stop") break;
1869
1943
  if ("fail" in item) {
1870
1944
  const sync = ctx._tolerant ? ctx._sync : void 0;
@@ -1907,15 +1981,6 @@ function optional(combinator) {
1907
1981
  }
1908
1982
  };
1909
1983
  }
1910
- function startsFirstSet(combinator, input, pos) {
1911
- const fs2 = combinator._meta.firstSet;
1912
- if (fs2.kind === "any") return true;
1913
- if (fs2.kind === "empty") return false;
1914
- const code = input.codePointAt(pos);
1915
- if (code === void 0) return false;
1916
- for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
1917
- return false;
1918
- }
1919
1984
  function sepBy(combinator, separator) {
1920
1985
  const meta = {
1921
1986
  firstSet: combinator._meta.firstSet,
@@ -2412,11 +2477,20 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2412
2477
  const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : true);
2413
2478
  const clonesState = build ? buildReadsState(def) : true;
2414
2479
  const capturesFields = parserHasOwnFields(combinator) && (build ? buildReadsFields(def) : true);
2480
+ const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
2481
+ let failExpected;
2415
2482
  return {
2416
2483
  _tag: "node",
2417
2484
  _meta: meta,
2418
2485
  _def: def,
2419
2486
  parse(input, pos, ctx) {
2487
+ if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
2488
+ if (failExpected === void 0) {
2489
+ const e = deriveExpected(combinator);
2490
+ failExpected = e.length > 0 ? e : [combinator._tag];
2491
+ }
2492
+ return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
2493
+ }
2420
2494
  const saved = beginCstNodeCapture(ctx);
2421
2495
  const savedFields = ctx._fields;
2422
2496
  ctx._fields = capturesFields ? [] : void 0;
@@ -4311,9 +4385,9 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
4311
4385
  const i = ind(ctx);
4312
4386
  const lf = v(ctx, "_lf");
4313
4387
  return [
4314
- `${i}if (_ctx._cstLeaves) {`,
4388
+ `${i}if (_ctx._cstLeaves || _ctx._cstRawChildren) {`,
4315
4389
  `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } }`,
4316
- `${i} _ctx._cstLeaves.push(${lf})`,
4390
+ `${i} if (_ctx._cstLeaves) _ctx._cstLeaves.push(${lf})`,
4317
4391
  `${i} if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(${lf})`,
4318
4392
  `${i}}`
4319
4393
  ];
@@ -4412,6 +4486,7 @@ var SWITCH_RANGE_LIMIT = 4;
4412
4486
  var SWITCH_MAX_CASES = 48;
4413
4487
  var SWITCH_MIN_CASES = 3;
4414
4488
  var _forceDisjointIf = false;
4489
+ var _forceNoSharedPrefix = false;
4415
4490
  function planDisjointDispatch(parsers) {
4416
4491
  const cases = [];
4417
4492
  let total = 0;
@@ -5047,7 +5122,7 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos, cov
5047
5122
  stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
5048
5123
  return { stmts, valueVar: valV, endVar: endV };
5049
5124
  }
5050
- function emitFirstMatch(def, ctx, pos, coverageBase) {
5125
+ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
5051
5126
  const resValV = v(ctx, "_crv");
5052
5127
  const resEndV = v(ctx, "_cre");
5053
5128
  const resOkV = v(ctx, "_crok");
@@ -5061,6 +5136,7 @@ function emitFirstMatch(def, ctx, pos, coverageBase) {
5061
5136
  ...autoRejectedV === void 0 ? [] : [`${ind0}const ${autoRejectedV} = []`],
5062
5137
  `${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
5063
5138
  ];
5139
+ if (preStmts) stmts.push(...preStmts);
5064
5140
  for (let i = 0; i < def.parsers.length; i++) {
5065
5141
  const p = def.parsers[i];
5066
5142
  const gate = def.gates[i];
@@ -5068,11 +5144,12 @@ function emitFirstMatch(def, ctx, pos, coverageBase) {
5068
5144
  const atStart = failsAtStart(p);
5069
5145
  const staticFx = armStaticExpected(ctx, p);
5070
5146
  const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
5071
- const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : (() => {
5147
+ const baseFsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : (() => {
5072
5148
  if (p._meta.firstSet.kind !== "any" || matchesEmpty(p)) return null;
5073
5149
  const deep = firstSetOf(p);
5074
5150
  return deep.kind === "ranges" ? firstSetCond(codeV, deep) : null;
5075
5151
  })();
5152
+ const fsGuard = armGuardPrefix === void 0 ? baseFsGuard : baseFsGuard === null ? armGuardPrefix : `${armGuardPrefix} && (${baseFsGuard})`;
5076
5153
  let gateCond = null;
5077
5154
  if (gate) {
5078
5155
  const gateIdx = ctx.mapFns.length;
@@ -5141,11 +5218,64 @@ function emitFirstMatch(def, ctx, pos, coverageBase) {
5141
5218
  stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
5142
5219
  return { stmts, valueVar: resValV, endVar: resEndV };
5143
5220
  }
5221
+ function emitsAsNamedFn(p, ctx) {
5222
+ if (p._def.tag === "lazy") return true;
5223
+ if (ctx.ruleNames?.has(p)) return true;
5224
+ if (ctx.namedParsers.has(p)) return true;
5225
+ const usage = ctx.lazyUsage;
5226
+ return !!usage && !ctx.noHoist && !ctx.capAsTrivia && isHoistableTag(p._def.tag) && (usage.counts.get(p) ?? 0) > 1 && (usage.sizes.get(p) ?? 0) >= HOIST_MIN_SUBTREE;
5227
+ }
5228
+ function armReplayInScope(arm, ctx) {
5229
+ let p = arm;
5230
+ for (; ; ) {
5231
+ if (emitsAsNamedFn(p, ctx)) return false;
5232
+ const d = p._def;
5233
+ if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
5234
+ p = d.parser;
5235
+ continue;
5236
+ }
5237
+ return d.tag === "sequence";
5238
+ }
5239
+ }
5240
+ function emitSharedPrefix(def, strategy, ctx, pos) {
5241
+ const { prefix, members } = strategy;
5242
+ const savedCapturing = ctx.capturing;
5243
+ ctx.capturing = false;
5244
+ const pfx = emitFallible(prefix, ctx, pos, false);
5245
+ ctx.capturing = savedCapturing;
5246
+ const map = ctx.replayPrefix ??= /* @__PURE__ */ new Map();
5247
+ const keys = [];
5248
+ for (const i of members) {
5249
+ const term0 = leadingTermOfArm(def.parsers[i]);
5250
+ if (term0 === null) continue;
5251
+ map.set(term0, { valVar: pfx.valVar, endVar: pfx.endVar });
5252
+ keys.push(term0);
5253
+ }
5254
+ const r = emitFirstMatch(def, ctx, pos, void 0, pfx.stmts, pfx.okVar);
5255
+ for (const k of keys) map.delete(k);
5256
+ return r;
5257
+ }
5258
+ function emitReplayPrefixLeaf(ctx, pos, replay) {
5259
+ const vv = v(ctx);
5260
+ return {
5261
+ stmts: [
5262
+ `${ind(ctx)}const ${vv} = ${replay.valVar}`,
5263
+ ...emitLeafCapture(ctx, vv, pos, replay.endVar)
5264
+ ],
5265
+ valueVar: vv,
5266
+ endVar: replay.endVar
5267
+ };
5268
+ }
5144
5269
  function emitNonDisjoint(def, strategy, allExpected, ctx, pos, coverageBase) {
5145
5270
  if (strategy.tag === "greedyClassify")
5146
5271
  return emitGreedyClassify(def, strategy.superIndex, ctx, pos, coverageBase);
5147
5272
  if (strategy.tag === "literalsLongestFirst")
5148
5273
  return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos, coverageBase);
5274
+ if (strategy.tag === "sharedPrefix") {
5275
+ if (!_forceNoSharedPrefix && coverageBase === void 0 && !ctx.recovery && strategy.members.every((i) => armReplayInScope(def.parsers[i], ctx)))
5276
+ return emitSharedPrefix(def, strategy, ctx, pos);
5277
+ return emitFirstMatch(def, ctx, pos, coverageBase);
5278
+ }
5149
5279
  return emitFirstMatch(def, ctx, pos, coverageBase);
5150
5280
  }
5151
5281
  function pushMapFn(ctx, fn, src) {
@@ -5661,8 +5791,10 @@ function emitNode(def, ctx, pos) {
5661
5791
  const directCstV = !structural && (!capturesChildren || !capturesRaw) ? v(ctx, "_dcst") : null;
5662
5792
  const directCstGate = directCstV === null ? "false" : `${profileCapture} || _ctx.build?._parsemanCstOutput === true`;
5663
5793
  const structuralCapturesTrivia = structural && (def.captureTrivia === true || def.trailingTrivia === true);
5794
+ const chNeededExpr = def.unwrap || def.collapse ? "true" : `(_ctx._pmReadsCh ??= (_ctx.build === undefined || _ctx.build._parsemanReadsChildren !== false || _ctx.build._parsemanCstCollapse !== undefined))`;
5795
+ const chAlloc = def.unwrap || def.collapse ? `${profileRecognizer} ? undefined : []` : `${profileRecognizer} ? undefined : ((${profileCapture} || ${chNeededExpr}) ? [] : undefined)`;
5664
5796
  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)))` : ""}
5665
- ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : ${innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`}` : capturesTrivia ? `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5797
+ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : ${innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`}` : capturesTrivia ? `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5666
5798
  ` : ""}${i}const ${capTLv} = !(${profileRecognizer}), ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}, ${tlV} = ${profileRecognizer} ? undefined : []` : `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5667
5799
  ` : ""}${i}const ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}`;
5668
5800
  const innerTl = structural || capturesTrivia ? structural && !innerEnablesTriviaCapture ? `${capTLv} ? ${tlV} : undefined` : tlV : "undefined";
@@ -5859,6 +5991,8 @@ function emitExpect(def, ctx, pos) {
5859
5991
  return { stmts, valueVar: valVar, endVar };
5860
5992
  }
5861
5993
  function emit(p, ctx, pos) {
5994
+ const replay = ctx.replayPrefix?.get(p);
5995
+ if (replay !== void 0) return emitReplayPrefixLeaf(ctx, pos, replay);
5862
5996
  const dispatch = (emissionPos = pos) => {
5863
5997
  const savedRule = ctx.activeCoverageRuleId;
5864
5998
  const rule = ctx.coverage?.plan.rules.get(p);
@@ -7332,7 +7466,7 @@ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triv
7332
7466
  return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
7333
7467
  }
7334
7468
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
7335
- const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3, fields, span2, rawChildren2, triviaLog2, state2);
7469
+ const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2);
7336
7470
  host._parsemanCstOutput = true;
7337
7471
  if (collapse) host._parsemanCstCollapse = collapse;
7338
7472
  return host;