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.
@@ -61,6 +61,15 @@ function intersects(a, b) {
61
61
  function fromChar(code) {
62
62
  return { kind: "ranges", ranges: [{ lo: code, hi: code }] };
63
63
  }
64
+ function startsFirstSet(combinator, input, pos) {
65
+ const fs2 = combinator._meta.firstSet;
66
+ if (fs2.kind === "any") return true;
67
+ if (fs2.kind === "empty") return false;
68
+ const code = input.codePointAt(pos);
69
+ if (code === void 0) return false;
70
+ for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
71
+ return false;
72
+ }
64
73
  function fromRange(lo, hi) {
65
74
  return { kind: "ranges", ranges: [{ lo, hi }] };
66
75
  }
@@ -1020,6 +1029,17 @@ function deriveExpected(c) {
1020
1029
  return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
1021
1030
  case "attempt":
1022
1031
  return deriveExpected(def.parser);
1032
+ // Delegating wrappers whose first-token failure is their inner parser's — must
1033
+ // pass through so a start-failure (and the first-set-miss fast-path in attempt/
1034
+ // node, which reads this) names the real expected token, not the wrapper tag.
1035
+ case "field":
1036
+ case "withCtx":
1037
+ case "recover":
1038
+ return deriveExpected(def.parser);
1039
+ case "skip":
1040
+ return deriveExpected(def.main);
1041
+ case "expect":
1042
+ return def.expected;
1023
1043
  case "node":
1024
1044
  case "grammar":
1025
1045
  case "trivia":
@@ -1080,7 +1100,7 @@ function choice(...args) {
1080
1100
  disjoint
1081
1101
  };
1082
1102
  const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
1083
- const autoNot = !disjoint && !hasGates && strategy?.tag === "firstMatch" ? computeAutoNot(parsers) : parsers.map(() => null);
1103
+ const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
1084
1104
  let greedyLitMap = null;
1085
1105
  let sortedParsers = null;
1086
1106
  const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
@@ -1206,8 +1226,47 @@ function detectStrategy(parsers) {
1206
1226
  );
1207
1227
  return { tag: "literalsLongestFirst", sortedIndices };
1208
1228
  }
1229
+ const shared = detectSharedPrefix(parsers);
1230
+ if (shared !== null) return shared;
1209
1231
  return { tag: "firstMatch" };
1210
1232
  }
1233
+ function bareLeadingTermKey(term) {
1234
+ const d = term._def;
1235
+ if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
1236
+ if (d.tag === "regex") return `R:${d.source}\0${d.flags}`;
1237
+ return null;
1238
+ }
1239
+ function detectSharedPrefix(parsers) {
1240
+ if (parsers.length < 2) return null;
1241
+ let key = null;
1242
+ let prefix = null;
1243
+ const members = [];
1244
+ for (let i = 0; i < parsers.length; i++) {
1245
+ const term0 = leadingTermOfArm(parsers[i]);
1246
+ if (term0 === null) return null;
1247
+ const k = bareLeadingTermKey(term0);
1248
+ if (k === null) return null;
1249
+ if (key === null) {
1250
+ key = k;
1251
+ prefix = term0;
1252
+ } else if (k !== key) return null;
1253
+ members.push(i);
1254
+ }
1255
+ if (prefix === null || members.length < 2) return null;
1256
+ return { tag: "sharedPrefix", prefix, members };
1257
+ }
1258
+ function leadingTermOfArm(arm) {
1259
+ let d = arm._def;
1260
+ for (; ; ) {
1261
+ if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
1262
+ d = d.parser._def;
1263
+ continue;
1264
+ }
1265
+ break;
1266
+ }
1267
+ if (d.tag !== "sequence" || d.parsers.length < 2) return null;
1268
+ return d.parsers[0];
1269
+ }
1211
1270
  function computeAutoNot(parsers) {
1212
1271
  return parsers.map((p, i) => {
1213
1272
  const litVal = getCoreLiteralValue(p);
@@ -1801,11 +1860,20 @@ function sequence(...parsers) {
1801
1860
 
1802
1861
  // src/combinators/attempt.ts
1803
1862
  function attempt(parser2) {
1863
+ const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
1864
+ let failExpected;
1804
1865
  return {
1805
1866
  _tag: "attempt",
1806
1867
  _meta: parser2._meta,
1807
1868
  _def: { tag: "attempt", parser: parser2 },
1808
1869
  parse(input, pos, ctx) {
1870
+ if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(parser2, input, pos)) {
1871
+ if (failExpected === void 0) {
1872
+ const e = deriveExpected(parser2);
1873
+ failExpected = e.length > 0 ? e : [parser2._tag];
1874
+ }
1875
+ return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
1876
+ }
1809
1877
  const mark = saveCstMark(ctx);
1810
1878
  const log = ctx._triviaLog?.length;
1811
1879
  const result = parser2.parse(input, pos, ctx);
@@ -1818,7 +1886,7 @@ function attempt(parser2) {
1818
1886
  }
1819
1887
 
1820
1888
  // src/combinators/repeat.ts
1821
- function repItem(combinator, input, cur, ctx) {
1889
+ function repItem(combinator, input, cur, ctx, guardable) {
1822
1890
  const mark = saveTriviaMark(ctx);
1823
1891
  let pos = cur;
1824
1892
  if (ctx.trivia) {
@@ -1834,6 +1902,10 @@ function repItem(combinator, input, cur, ctx) {
1834
1902
  rollbackTrivia(ctx, mark);
1835
1903
  return "stop";
1836
1904
  }
1905
+ if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
1906
+ rollbackTrivia(ctx, mark);
1907
+ return "stop";
1908
+ }
1837
1909
  const result = combinator.parse(input, pos, ctx);
1838
1910
  if (!result.ok) {
1839
1911
  rollbackTrivia(ctx, mark);
@@ -1853,6 +1925,7 @@ function many(combinator) {
1853
1925
  };
1854
1926
  const def = { tag: "many", parser: combinator, min: 0 };
1855
1927
  let expected;
1928
+ const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
1856
1929
  return {
1857
1930
  _tag: "many",
1858
1931
  _meta: meta,
@@ -1861,7 +1934,7 @@ function many(combinator) {
1861
1934
  const values = def.valueUnused ? void 0 : [];
1862
1935
  let cur = pos;
1863
1936
  while (cur < input.length) {
1864
- const item = repItem(combinator, input, cur, ctx);
1937
+ const item = repItem(combinator, input, cur, ctx, guardable);
1865
1938
  if (item === "stop") break;
1866
1939
  if ("fail" in item) {
1867
1940
  const sync = ctx._tolerant ? ctx._sync : void 0;
@@ -1889,6 +1962,7 @@ function oneOrMore(combinator) {
1889
1962
  };
1890
1963
  const def = { tag: "oneOrMore", parser: combinator, min: 1 };
1891
1964
  let expected;
1965
+ const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
1892
1966
  return {
1893
1967
  _tag: "oneOrMore",
1894
1968
  _meta: meta,
@@ -1899,7 +1973,7 @@ function oneOrMore(combinator) {
1899
1973
  const values = def.valueUnused ? void 0 : [first.value];
1900
1974
  let cur = first.span.end;
1901
1975
  while (cur < input.length) {
1902
- const item = repItem(combinator, input, cur, ctx);
1976
+ const item = repItem(combinator, input, cur, ctx, guardable);
1903
1977
  if (item === "stop") break;
1904
1978
  if ("fail" in item) {
1905
1979
  const sync = ctx._tolerant ? ctx._sync : void 0;
@@ -1942,15 +2016,6 @@ function optional(combinator) {
1942
2016
  }
1943
2017
  };
1944
2018
  }
1945
- function startsFirstSet(combinator, input, pos) {
1946
- const fs2 = combinator._meta.firstSet;
1947
- if (fs2.kind === "any") return true;
1948
- if (fs2.kind === "empty") return false;
1949
- const code = input.codePointAt(pos);
1950
- if (code === void 0) return false;
1951
- for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
1952
- return false;
1953
- }
1954
2019
  function sepBy(combinator, separator) {
1955
2020
  const meta = {
1956
2021
  firstSet: combinator._meta.firstSet,
@@ -2447,11 +2512,20 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2447
2512
  const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : true);
2448
2513
  const clonesState = build ? buildReadsState(def) : true;
2449
2514
  const capturesFields = parserHasOwnFields(combinator) && (build ? buildReadsFields(def) : true);
2515
+ const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
2516
+ let failExpected;
2450
2517
  return {
2451
2518
  _tag: "node",
2452
2519
  _meta: meta,
2453
2520
  _def: def,
2454
2521
  parse(input, pos, ctx) {
2522
+ if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
2523
+ if (failExpected === void 0) {
2524
+ const e = deriveExpected(combinator);
2525
+ failExpected = e.length > 0 ? e : [combinator._tag];
2526
+ }
2527
+ return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
2528
+ }
2455
2529
  const saved = beginCstNodeCapture(ctx);
2456
2530
  const savedFields = ctx._fields;
2457
2531
  ctx._fields = capturesFields ? [] : void 0;
@@ -4346,9 +4420,9 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
4346
4420
  const i = ind(ctx);
4347
4421
  const lf = v(ctx, "_lf");
4348
4422
  return [
4349
- `${i}if (_ctx._cstLeaves) {`,
4423
+ `${i}if (_ctx._cstLeaves || _ctx._cstRawChildren) {`,
4350
4424
  `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } }`,
4351
- `${i} _ctx._cstLeaves.push(${lf})`,
4425
+ `${i} if (_ctx._cstLeaves) _ctx._cstLeaves.push(${lf})`,
4352
4426
  `${i} if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(${lf})`,
4353
4427
  `${i}}`
4354
4428
  ];
@@ -4447,6 +4521,7 @@ var SWITCH_RANGE_LIMIT = 4;
4447
4521
  var SWITCH_MAX_CASES = 48;
4448
4522
  var SWITCH_MIN_CASES = 3;
4449
4523
  var _forceDisjointIf = false;
4524
+ var _forceNoSharedPrefix = false;
4450
4525
  function planDisjointDispatch(parsers) {
4451
4526
  const cases = [];
4452
4527
  let total = 0;
@@ -5082,7 +5157,7 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos, cov
5082
5157
  stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
5083
5158
  return { stmts, valueVar: valV, endVar: endV };
5084
5159
  }
5085
- function emitFirstMatch(def, ctx, pos, coverageBase) {
5160
+ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
5086
5161
  const resValV = v(ctx, "_crv");
5087
5162
  const resEndV = v(ctx, "_cre");
5088
5163
  const resOkV = v(ctx, "_crok");
@@ -5096,6 +5171,7 @@ function emitFirstMatch(def, ctx, pos, coverageBase) {
5096
5171
  ...autoRejectedV === void 0 ? [] : [`${ind0}const ${autoRejectedV} = []`],
5097
5172
  `${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
5098
5173
  ];
5174
+ if (preStmts) stmts.push(...preStmts);
5099
5175
  for (let i = 0; i < def.parsers.length; i++) {
5100
5176
  const p = def.parsers[i];
5101
5177
  const gate = def.gates[i];
@@ -5103,11 +5179,12 @@ function emitFirstMatch(def, ctx, pos, coverageBase) {
5103
5179
  const atStart = failsAtStart(p);
5104
5180
  const staticFx = armStaticExpected(ctx, p);
5105
5181
  const deferRuleName = ctx.deferFirstSetRefs ? p._ruleName : void 0;
5106
- const fsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : (() => {
5182
+ const baseFsGuard = deferRuleName !== void 0 ? `/*@FS:${deferRuleName}:${codeV}@*/true` : needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : (() => {
5107
5183
  if (p._meta.firstSet.kind !== "any" || matchesEmpty(p)) return null;
5108
5184
  const deep = firstSetOf(p);
5109
5185
  return deep.kind === "ranges" ? firstSetCond(codeV, deep) : null;
5110
5186
  })();
5187
+ const fsGuard = armGuardPrefix === void 0 ? baseFsGuard : baseFsGuard === null ? armGuardPrefix : `${armGuardPrefix} && (${baseFsGuard})`;
5111
5188
  let gateCond = null;
5112
5189
  if (gate) {
5113
5190
  const gateIdx = ctx.mapFns.length;
@@ -5176,11 +5253,64 @@ function emitFirstMatch(def, ctx, pos, coverageBase) {
5176
5253
  stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
5177
5254
  return { stmts, valueVar: resValV, endVar: resEndV };
5178
5255
  }
5256
+ function emitsAsNamedFn(p, ctx) {
5257
+ if (p._def.tag === "lazy") return true;
5258
+ if (ctx.ruleNames?.has(p)) return true;
5259
+ if (ctx.namedParsers.has(p)) return true;
5260
+ const usage = ctx.lazyUsage;
5261
+ return !!usage && !ctx.noHoist && !ctx.capAsTrivia && isHoistableTag(p._def.tag) && (usage.counts.get(p) ?? 0) > 1 && (usage.sizes.get(p) ?? 0) >= HOIST_MIN_SUBTREE;
5262
+ }
5263
+ function armReplayInScope(arm, ctx) {
5264
+ let p = arm;
5265
+ for (; ; ) {
5266
+ if (emitsAsNamedFn(p, ctx)) return false;
5267
+ const d = p._def;
5268
+ if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
5269
+ p = d.parser;
5270
+ continue;
5271
+ }
5272
+ return d.tag === "sequence";
5273
+ }
5274
+ }
5275
+ function emitSharedPrefix(def, strategy, ctx, pos) {
5276
+ const { prefix, members } = strategy;
5277
+ const savedCapturing = ctx.capturing;
5278
+ ctx.capturing = false;
5279
+ const pfx = emitFallible(prefix, ctx, pos, false);
5280
+ ctx.capturing = savedCapturing;
5281
+ const map = ctx.replayPrefix ??= /* @__PURE__ */ new Map();
5282
+ const keys = [];
5283
+ for (const i of members) {
5284
+ const term0 = leadingTermOfArm(def.parsers[i]);
5285
+ if (term0 === null) continue;
5286
+ map.set(term0, { valVar: pfx.valVar, endVar: pfx.endVar });
5287
+ keys.push(term0);
5288
+ }
5289
+ const r = emitFirstMatch(def, ctx, pos, void 0, pfx.stmts, pfx.okVar);
5290
+ for (const k of keys) map.delete(k);
5291
+ return r;
5292
+ }
5293
+ function emitReplayPrefixLeaf(ctx, pos, replay) {
5294
+ const vv = v(ctx);
5295
+ return {
5296
+ stmts: [
5297
+ `${ind(ctx)}const ${vv} = ${replay.valVar}`,
5298
+ ...emitLeafCapture(ctx, vv, pos, replay.endVar)
5299
+ ],
5300
+ valueVar: vv,
5301
+ endVar: replay.endVar
5302
+ };
5303
+ }
5179
5304
  function emitNonDisjoint(def, strategy, allExpected, ctx, pos, coverageBase) {
5180
5305
  if (strategy.tag === "greedyClassify")
5181
5306
  return emitGreedyClassify(def, strategy.superIndex, ctx, pos, coverageBase);
5182
5307
  if (strategy.tag === "literalsLongestFirst")
5183
5308
  return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos, coverageBase);
5309
+ if (strategy.tag === "sharedPrefix") {
5310
+ if (!_forceNoSharedPrefix && coverageBase === void 0 && !ctx.recovery && strategy.members.every((i) => armReplayInScope(def.parsers[i], ctx)))
5311
+ return emitSharedPrefix(def, strategy, ctx, pos);
5312
+ return emitFirstMatch(def, ctx, pos, coverageBase);
5313
+ }
5184
5314
  return emitFirstMatch(def, ctx, pos, coverageBase);
5185
5315
  }
5186
5316
  function pushMapFn(ctx, fn, src) {
@@ -5696,8 +5826,10 @@ function emitNode(def, ctx, pos) {
5696
5826
  const directCstV = !structural && (!capturesChildren || !capturesRaw) ? v(ctx, "_dcst") : null;
5697
5827
  const directCstGate = directCstV === null ? "false" : `${profileCapture} || _ctx.build?._parsemanCstOutput === true`;
5698
5828
  const structuralCapturesTrivia = structural && (def.captureTrivia === true || def.trailingTrivia === true);
5829
+ const chNeededExpr = def.unwrap || def.collapse ? "true" : `(_ctx._pmReadsCh ??= (_ctx.build === undefined || _ctx.build._parsemanReadsChildren !== false || _ctx.build._parsemanCstCollapse !== undefined))`;
5830
+ const chAlloc = def.unwrap || def.collapse ? `${profileRecognizer} ? undefined : []` : `${profileRecognizer} ? undefined : ((${profileCapture} || ${chNeededExpr}) ? [] : undefined)`;
5699
5831
  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)))` : ""}
5700
- ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : ${innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`}` : capturesTrivia ? `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5832
+ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : ${innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`}` : capturesTrivia ? `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5701
5833
  ` : ""}${i}const ${capTLv} = !(${profileRecognizer}), ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}, ${tlV} = ${profileRecognizer} ? undefined : []` : `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5702
5834
  ` : ""}${i}const ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}`;
5703
5835
  const innerTl = structural || capturesTrivia ? structural && !innerEnablesTriviaCapture ? `${capTLv} ? ${tlV} : undefined` : tlV : "undefined";
@@ -5894,6 +6026,8 @@ function emitExpect(def, ctx, pos) {
5894
6026
  return { stmts, valueVar: valVar, endVar };
5895
6027
  }
5896
6028
  function emit(p, ctx, pos) {
6029
+ const replay = ctx.replayPrefix?.get(p);
6030
+ if (replay !== void 0) return emitReplayPrefixLeaf(ctx, pos, replay);
5897
6031
  const dispatch = (emissionPos = pos) => {
5898
6032
  const savedRule = ctx.activeCoverageRuleId;
5899
6033
  const rule = ctx.coverage?.plan.rules.get(p);
@@ -7367,7 +7501,7 @@ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triv
7367
7501
  return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
7368
7502
  }
7369
7503
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
7370
- const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3, fields, span2, rawChildren2, triviaLog2, state2);
7504
+ const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2);
7371
7505
  host._parsemanCstOutput = true;
7372
7506
  if (collapse) host._parsemanCstCollapse = collapse;
7373
7507
  return host;