parseman 0.38.0 → 0.39.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.
Files changed (37) hide show
  1. package/dist/analysis/duplication.d.ts.map +1 -1
  2. package/dist/analysis/gating.d.ts.map +1 -1
  3. package/dist/combinators/attempt.d.ts.map +1 -1
  4. package/dist/combinators/choice.d.ts.map +1 -1
  5. package/dist/combinators/dispatch.d.ts +20 -0
  6. package/dist/combinators/dispatch.d.ts.map +1 -0
  7. package/dist/combinators/expect.d.ts.map +1 -1
  8. package/dist/combinators/first-set.d.ts.map +1 -1
  9. package/dist/combinators/repeat.d.ts.map +1 -1
  10. package/dist/compiler/codegen.d.ts.map +1 -1
  11. package/dist/compiler/fields.d.ts.map +1 -1
  12. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  13. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  14. package/dist/compiler/value-usage.d.ts.map +1 -1
  15. package/dist/coverage.d.ts.map +1 -1
  16. package/dist/index.cjs +383 -31
  17. package/dist/index.cjs.map +4 -4
  18. package/dist/index.d.ts +3 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +383 -31
  21. package/dist/index.js.map +4 -4
  22. package/dist/language-service/index.cjs.map +2 -2
  23. package/dist/language-service/index.js.map +2 -2
  24. package/dist/plugin/evaluator.d.ts.map +1 -1
  25. package/dist/plugin/index.cjs +436 -31
  26. package/dist/plugin/index.cjs.map +4 -4
  27. package/dist/plugin/index.js +436 -31
  28. package/dist/plugin/index.js.map +4 -4
  29. package/dist/spec/index.cjs +107 -0
  30. package/dist/spec/index.cjs.map +4 -4
  31. package/dist/spec/index.js +107 -0
  32. package/dist/spec/index.js.map +4 -4
  33. package/dist/spec/model.d.ts.map +1 -1
  34. package/dist/types.d.ts +17 -0
  35. package/dist/types.d.ts.map +1 -1
  36. package/dist/version.d.ts +1 -1
  37. package/package.json +1 -1
@@ -85,6 +85,8 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve2) {
85
85
  return d.parsers.every(me);
86
86
  case "choice":
87
87
  return d.parsers.some(me);
88
+ case "dispatch":
89
+ return me(d.selector);
88
90
  case "transform":
89
91
  case "label":
90
92
  case "trivia":
@@ -158,6 +160,8 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve2) {
158
160
  case "keywords":
159
161
  return p._meta.firstSet;
160
162
  // terminals: no refs, cached set is exact
163
+ case "dispatch":
164
+ return fs2(d.selector);
161
165
  case "lazy":
162
166
  try {
163
167
  return fs2(d.thunk());
@@ -1106,6 +1110,8 @@ function derive(c, seen) {
1106
1110
  return [def.label];
1107
1111
  case "choice":
1108
1112
  return def.parsers.flatMap(deriveExpected2);
1113
+ case "dispatch":
1114
+ return deriveExpected2(def.selector);
1109
1115
  // Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
1110
1116
  // `optional(…)`/`many(…)` can match nothing, so the term after it is equally
1111
1117
  // able to fail first — deriving from term 0 alone named a token the parse
@@ -1244,6 +1250,7 @@ function choice(...args) {
1244
1250
  const result = parsers[idx].parse(input, pos, ctx);
1245
1251
  if (result.ok) return result;
1246
1252
  expected.push(...result.expected);
1253
+ if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
1247
1254
  return { ok: false, expected, span: { start: pos, end: pos } };
1248
1255
  }
1249
1256
  return {
@@ -1284,6 +1291,7 @@ function choice(...args) {
1284
1291
  rollbackCstCapture(ctx, mark);
1285
1292
  if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
1286
1293
  expected.push(...result.expected);
1294
+ if (result.committed) return { ok: false, expected, span: result.span, committed: true };
1287
1295
  continue;
1288
1296
  }
1289
1297
  const checks = autoNot[i];
@@ -1963,6 +1971,75 @@ function sequence(...parsers) {
1963
1971
  };
1964
1972
  }
1965
1973
 
1974
+ // src/combinators/dispatch.ts
1975
+ function when(key, parser2) {
1976
+ const keys = Array.isArray(key) ? [...key] : [key];
1977
+ if (keys.length === 0) throw new RangeError("parseman: when() requires at least one key");
1978
+ for (const item of keys) {
1979
+ if (typeof item !== "string") throw new TypeError("parseman: when() keys must be strings");
1980
+ }
1981
+ return { kind: "when", keys, parser: parser2 };
1982
+ }
1983
+ function otherwise(parser2) {
1984
+ return { kind: "otherwise", parser: parser2 };
1985
+ }
1986
+ function dispatch(selector, ...arms) {
1987
+ let fallback;
1988
+ const cases = [];
1989
+ const seen = /* @__PURE__ */ new Set();
1990
+ for (let i = 0; i < arms.length; i++) {
1991
+ const arm = arms[i];
1992
+ if (arm.kind === "otherwise") {
1993
+ if (fallback !== void 0) throw new Error("parseman: dispatch() accepts at most one otherwise() arm");
1994
+ if (i !== arms.length - 1) throw new Error("parseman: otherwise() must be the last dispatch() arm");
1995
+ fallback = arm.parser;
1996
+ continue;
1997
+ }
1998
+ for (const key of arm.keys) {
1999
+ if (seen.has(key)) throw new Error(`parseman: duplicate dispatch key ${JSON.stringify(key)}`);
2000
+ seen.add(key);
2001
+ }
2002
+ cases.push({ keys: arm.keys, parser: arm.parser });
2003
+ }
2004
+ const meta = {
2005
+ firstSet: selector._meta.firstSet,
2006
+ canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
2007
+ isTrivia: false
2008
+ };
2009
+ const byKey = /* @__PURE__ */ new Map();
2010
+ for (const entry of cases) for (const key of entry.keys) byKey.set(key, entry.parser);
2011
+ return {
2012
+ _tag: "dispatch",
2013
+ _meta: meta,
2014
+ _def: {
2015
+ tag: "dispatch",
2016
+ selector,
2017
+ cases,
2018
+ ...fallback === void 0 ? {} : { otherwise: fallback }
2019
+ },
2020
+ parse(input, pos, ctx) {
2021
+ const selected = selector.parse(input, pos, ctx);
2022
+ if (!selected.ok) return selected;
2023
+ const tail = byKey.get(selected.value) ?? fallback;
2024
+ if (tail === void 0) {
2025
+ const expected = cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key)));
2026
+ return { ok: false, expected, span: { start: selected.span.end, end: selected.span.end } };
2027
+ }
2028
+ const mark = saveTriviaMark(ctx);
2029
+ const result = tail.parse(input, selected.span.end, ctx);
2030
+ if (!result.ok) {
2031
+ rollbackTrivia(ctx, mark);
2032
+ return { ...result, committed: true };
2033
+ }
2034
+ return {
2035
+ ok: true,
2036
+ value: [selected.value, result.value],
2037
+ span: { start: pos, end: result.span.end }
2038
+ };
2039
+ }
2040
+ };
2041
+ }
2042
+
1966
2043
  // src/combinators/attempt.ts
1967
2044
  function attempt(parser2) {
1968
2045
  const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
@@ -1985,6 +2062,7 @@ function attempt(parser2) {
1985
2062
  if (result.ok) return result;
1986
2063
  rollbackCstCapture(ctx, mark);
1987
2064
  if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
2065
+ if (result.committed) return result;
1988
2066
  return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
1989
2067
  }
1990
2068
  };
@@ -2014,6 +2092,7 @@ function repItem(combinator, input, cur, ctx, guardable) {
2014
2092
  const result = combinator.parse(input, pos, ctx);
2015
2093
  if (!result.ok) {
2016
2094
  rollbackTrivia(ctx, mark);
2095
+ if (result.committed) return { fail: result, failPos: pos };
2017
2096
  return { fail: result, failPos: pos };
2018
2097
  }
2019
2098
  if (result.span.end === pos) {
@@ -2057,6 +2136,7 @@ function many(combinator, opts = {}) {
2057
2136
  const item = repItem(combinator, input, cur, ctx, guardable);
2058
2137
  if (item === "stop") break;
2059
2138
  if ("fail" in item) {
2139
+ if (!item.fail.ok && item.fail.committed) return item.fail;
2060
2140
  const sync = ctx._tolerant ? ctx._sync : void 0;
2061
2141
  if (sync === void 0) break;
2062
2142
  if (matchesAt(sync, input, item.failPos, ctx)) break;
@@ -2101,6 +2181,7 @@ function atLeast(combinator, min, max) {
2101
2181
  while (count < min) {
2102
2182
  const item = repItem(combinator, input, cur, ctx, guardable);
2103
2183
  if (item === "stop" || "fail" in item) {
2184
+ if (item !== "stop" && !item.fail.ok && item.fail.committed) return item.fail;
2104
2185
  expected ??= deriveExpected(combinator);
2105
2186
  return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
2106
2187
  }
@@ -2113,6 +2194,7 @@ function atLeast(combinator, min, max) {
2113
2194
  const item = repItem(combinator, input, cur, ctx, guardable);
2114
2195
  if (item === "stop") break;
2115
2196
  if ("fail" in item) {
2197
+ if (!item.fail.ok && item.fail.committed) return item.fail;
2116
2198
  const sync = ctx._tolerant ? ctx._sync : void 0;
2117
2199
  if (sync === void 0) break;
2118
2200
  if (matchesAt(sync, input, item.failPos, ctx)) break;
@@ -2151,6 +2233,7 @@ function optional(combinator) {
2151
2233
  const result = combinator.parse(input, pos, ctx);
2152
2234
  if (result.ok) return result;
2153
2235
  rollbackTrivia(ctx, mark);
2236
+ if (result.committed) return result;
2154
2237
  return { ok: true, value: null, span: { start: pos, end: pos } };
2155
2238
  }
2156
2239
  };
@@ -2190,6 +2273,7 @@ function sepBy(combinator, separator, opts = {}) {
2190
2273
  values.push(first.value);
2191
2274
  cur = first.span.end;
2192
2275
  } else {
2276
+ if (first.committed) return first;
2193
2277
  const term = ctx._tolerant ? ctx._sync : void 0;
2194
2278
  if (term === void 0 || matchesAt(term, input, pos, ctx)) {
2195
2279
  if (min >= 1) return failAt2(pos);
@@ -2217,6 +2301,7 @@ function sepBy(combinator, separator, opts = {}) {
2217
2301
  const sep2 = separator.parse(input, sepPos, ctx);
2218
2302
  if (!sep2.ok) {
2219
2303
  rollbackTrivia(ctx, loopMark);
2304
+ if (sep2.committed) return sep2;
2220
2305
  break;
2221
2306
  }
2222
2307
  const sepMark = trailing === "forbid" ? void 0 : saveTriviaMark(ctx);
@@ -2232,6 +2317,7 @@ function sepBy(combinator, separator, opts = {}) {
2232
2317
  }
2233
2318
  const next = combinator.parse(input, nextPos, ctx);
2234
2319
  if (!next.ok) {
2320
+ if (next.committed) return next;
2235
2321
  const term = ctx._tolerant ? ctx._sync : void 0;
2236
2322
  if (term !== void 0 && !matchesAt(term, input, nextPos, ctx)) {
2237
2323
  expected ??= deriveExpected(combinator);
@@ -2287,6 +2373,11 @@ function markUnusedValues(root) {
2287
2373
  case "choice":
2288
2374
  for (const p of def.parsers) visit(p, consumed);
2289
2375
  return;
2376
+ case "dispatch":
2377
+ visit(def.selector, consumed);
2378
+ for (const entry of def.cases) visit(entry.parser, consumed);
2379
+ if (def.otherwise) visit(def.otherwise, consumed);
2380
+ return;
2290
2381
  case "node":
2291
2382
  visit(def.parser, false);
2292
2383
  return;
@@ -2562,6 +2653,8 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
2562
2653
  case "sequence":
2563
2654
  case "choice":
2564
2655
  return d.parsers.some((x) => parserHasOwnFields(x, seen));
2656
+ case "dispatch":
2657
+ return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
2565
2658
  case "sepBy":
2566
2659
  return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
2567
2660
  case "skip":
@@ -2629,6 +2722,8 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
2629
2722
  return parserHasTriviaSite(d.parser, seen);
2630
2723
  case "choice":
2631
2724
  return d.parsers.some((x) => parserHasTriviaSite(x, seen));
2725
+ case "dispatch":
2726
+ return parserHasTriviaSite(d.selector, seen) || d.cases.some((x) => parserHasTriviaSite(x.parser, seen)) || (d.otherwise ? parserHasTriviaSite(d.otherwise, seen) : false);
2632
2727
  case "grammar":
2633
2728
  return parserHasTriviaSite(d.parser, seen);
2634
2729
  case "lazy": {
@@ -2654,6 +2749,8 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
2654
2749
  case "sequence":
2655
2750
  case "choice":
2656
2751
  return d.parsers.some((x) => parserEnablesTriviaCapture(x, seen));
2752
+ case "dispatch":
2753
+ return parserEnablesTriviaCapture(d.selector, seen) || d.cases.some((x) => parserEnablesTriviaCapture(x.parser, seen)) || (d.otherwise ? parserEnablesTriviaCapture(d.otherwise, seen) : false);
2657
2754
  case "sepBy":
2658
2755
  return parserEnablesTriviaCapture(d.parser, seen) || parserEnablesTriviaCapture(d.separator, seen);
2659
2756
  case "skip":
@@ -3028,7 +3125,7 @@ function leaf(root, fn) {
3028
3125
  }
3029
3126
 
3030
3127
  // src/version.ts
3031
- var PARSEMAN_VERSION = "0.38.0";
3128
+ var PARSEMAN_VERSION = "0.39.0";
3032
3129
 
3033
3130
  // src/cst/host-mode.ts
3034
3131
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -3184,6 +3281,8 @@ function classifyBroadArm(arm, resolve2) {
3184
3281
  }
3185
3282
  return null;
3186
3283
  }
3284
+ case "dispatch":
3285
+ return walk2(d.selector);
3187
3286
  default:
3188
3287
  return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
3189
3288
  }
@@ -3283,6 +3382,11 @@ function analyzeGatingRules(ruleMap, opts) {
3283
3382
  if (Array.isArray(rec.parsers)) kids.push(...rec.parsers);
3284
3383
  for (const k of ["parser", "main", "skipped", "separator", "sentinel"])
3285
3384
  if (rec[k]) kids.push(rec[k]);
3385
+ if (d.tag === "dispatch") {
3386
+ kids.push(d.selector);
3387
+ for (const c of d.cases) kids.push(c.parser);
3388
+ if (d.otherwise !== void 0) kids.push(d.otherwise);
3389
+ }
3286
3390
  if (Array.isArray(rec.skip)) kids.push(...rec.skip);
3287
3391
  if (d.tag === "lazy") {
3288
3392
  try {
@@ -3389,6 +3493,12 @@ function childrenOf(d) {
3389
3493
  case "sequence":
3390
3494
  case "choice":
3391
3495
  return d.parsers;
3496
+ case "dispatch":
3497
+ return [
3498
+ d.selector,
3499
+ ...d.cases.map((c) => c.parser),
3500
+ ...d.otherwise === void 0 ? [] : [d.otherwise]
3501
+ ];
3392
3502
  case "skip":
3393
3503
  return [d.main, d.skipped];
3394
3504
  case "sepBy":
@@ -3445,6 +3555,8 @@ function payloadKey(p, d) {
3445
3555
  return `${d.tag}\0${d.min}\0${d.max ?? ""}`;
3446
3556
  case "sepBy":
3447
3557
  return `sepBy\0${d.min}\0${d.max ?? ""}\0${d.trailing ?? ""}`;
3558
+ case "dispatch":
3559
+ return `dispatch\0${d.cases.map((c) => JSON.stringify(c.keys)).join("")}\0${d.otherwise !== void 0}`;
3448
3560
  case "expect":
3449
3561
  return `expect\0${d.label ?? ""}\0${d.expected.join("")}`;
3450
3562
  case "scanTo":
@@ -3516,6 +3628,11 @@ function render(p, depth = 3) {
3516
3628
  return `sequence(${kids.join(", ")})`;
3517
3629
  case "choice":
3518
3630
  return `choice(${kids.join(", ")})`;
3631
+ case "dispatch": {
3632
+ const arms = d.cases.map((c, i) => `when([${c.keys.map((k) => JSON.stringify(k)).join(", ")}], ${kids[i + 1] ?? ""})`);
3633
+ if (d.otherwise !== void 0) arms.push(`otherwise(${kids[kids.length - 1] ?? ""})`);
3634
+ return `dispatch(${kids[0] ?? ""}, ${arms.join(", ")})`;
3635
+ }
3519
3636
  case "node":
3520
3637
  return `node(${d.type ? `'${d.type}', ` : ""}${kids[0] ?? ""})`;
3521
3638
  case "label":
@@ -3539,6 +3656,10 @@ function segment(d, index) {
3539
3656
  return `seq[${index}]`;
3540
3657
  case "choice":
3541
3658
  return `choice[${index}]`;
3659
+ case "dispatch":
3660
+ if (index === 0) return "dispatch.selector";
3661
+ if (index <= d.cases.length) return `dispatch.when[${index - 1}]`;
3662
+ return "dispatch.otherwise";
3542
3663
  case "node":
3543
3664
  return `node(${d.type ?? ""})`;
3544
3665
  case "label":
@@ -4792,6 +4913,8 @@ function children(def, winners) {
4792
4913
  case "sequence":
4793
4914
  case "choice":
4794
4915
  return def.parsers;
4916
+ case "dispatch":
4917
+ return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
4795
4918
  case "many":
4796
4919
  case "oneOrMore":
4797
4920
  case "optional":
@@ -6256,6 +6379,8 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
6256
6379
  for (const arm of d.parsers) alts.push(...rec(arm).alts);
6257
6380
  return { alts };
6258
6381
  }
6382
+ case "dispatch":
6383
+ return rec(d.selector);
6259
6384
  case "sequence": {
6260
6385
  const chain = [];
6261
6386
  for (const term of d.parsers) {
@@ -6338,6 +6463,9 @@ function failReturn(expected, posExpr) {
6338
6463
  function failReturnArr(expectedArr, posExpr) {
6339
6464
  return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
6340
6465
  }
6466
+ function committedReturnArr(expectedArr, posExpr) {
6467
+ return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} }, committed: true }`;
6468
+ }
6341
6469
  function hoistExpected(ctx, constArrSource) {
6342
6470
  let name = ctx.expectedMap.get(constArrSource);
6343
6471
  if (name === void 0) {
@@ -6374,15 +6502,22 @@ function failArrBody(ctx, expectedArr, posExpr, constant = true) {
6374
6502
  return probe + trace + failReturnArr(expectedArr, posExpr);
6375
6503
  }
6376
6504
  function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
6377
- return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
6505
+ return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} }, ...(_ctx._fc ? { committed: true } : {}) }`;
6506
+ }
6507
+ function committedFailBody(ctx, expectedArr = "_ctx._fx", posExpr = "_ctx._fe") {
6508
+ if (ctx.failLabel) {
6509
+ if (!ctx.recordFail) return `{ _ctx._fc = true; break ${ctx.failLabel} }`;
6510
+ return `{ _ctx._fc = true; _ctx._fe = ${posExpr}; _ctx._fx = ${expectedArr}; break ${ctx.failLabel} }`;
6511
+ }
6512
+ return committedReturnArr(expectedArr, posExpr);
6378
6513
  }
6379
6514
  function propagateFailBody(ctx, srcCtx = "_ctx") {
6380
6515
  if (srcCtx !== "_ctx") {
6381
6516
  if (ctx.failLabel) {
6382
6517
  if (!ctx.recordFail) return `break ${ctx.failLabel}`;
6383
- return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; break ${ctx.failLabel} }`;
6518
+ return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; break ${ctx.failLabel} }`;
6384
6519
  }
6385
- return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; ${resultFromRecorded()} }`;
6520
+ return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; ${resultFromRecorded()} }`;
6386
6521
  }
6387
6522
  if (ctx.failLabel) return `break ${ctx.failLabel}`;
6388
6523
  return resultFromRecorded();
@@ -6595,6 +6730,8 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
6595
6730
  // nothing committed remains.
6596
6731
  case "choice":
6597
6732
  return false;
6733
+ case "dispatch":
6734
+ return capturesLeaf(d.selector, seen) || d.cases.some((x) => mayLeavePartialCapture(x.parser, seen) || capturesLeaf(x.parser, seen)) || (d.otherwise ? mayLeavePartialCapture(d.otherwise, seen) || capturesLeaf(d.otherwise, seen) : false);
6598
6735
  case "attempt":
6599
6736
  return false;
6600
6737
  // optional never fails; many/oneOrMore only "fail" with zero captured items.
@@ -6639,6 +6776,50 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
6639
6776
  return true;
6640
6777
  }
6641
6778
  }
6779
+ function mayCommitFailure(p, seen = /* @__PURE__ */ new Set()) {
6780
+ if (seen.has(p)) return false;
6781
+ seen.add(p);
6782
+ const d = p._def;
6783
+ switch (d.tag) {
6784
+ case "dispatch":
6785
+ return true;
6786
+ case "choice":
6787
+ case "sequence":
6788
+ return d.parsers.some((x) => mayCommitFailure(x, seen));
6789
+ case "many":
6790
+ case "oneOrMore":
6791
+ case "optional":
6792
+ case "attempt":
6793
+ case "transform":
6794
+ case "label":
6795
+ case "field":
6796
+ case "grammar":
6797
+ case "node":
6798
+ return mayCommitFailure(d.parser, seen);
6799
+ case "sepBy":
6800
+ return mayCommitFailure(d.parser, seen) || mayCommitFailure(d.separator, seen);
6801
+ case "skip":
6802
+ return mayCommitFailure(d.main, seen) || mayCommitFailure(d.skipped, seen);
6803
+ case "token":
6804
+ case "leaf":
6805
+ return mayCommitFailure(d.parser, seen);
6806
+ case "withCtx":
6807
+ return mayCommitFailure(d.parser, seen);
6808
+ case "scanTo":
6809
+ return mayCommitFailure(d.sentinel, seen) || d.skip.some((x) => mayCommitFailure(x, seen));
6810
+ case "lazy": {
6811
+ try {
6812
+ return mayCommitFailure(d.thunk(), seen);
6813
+ } catch {
6814
+ return true;
6815
+ }
6816
+ }
6817
+ case "unknown":
6818
+ return true;
6819
+ default:
6820
+ return false;
6821
+ }
6822
+ }
6642
6823
  function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
6643
6824
  if (seen.has(p)) return false;
6644
6825
  seen.add(p);
@@ -6660,6 +6841,8 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
6660
6841
  case "sequence":
6661
6842
  case "choice":
6662
6843
  return d.parsers.some((x) => capturesLeaf(x, seen));
6844
+ case "dispatch":
6845
+ return capturesLeaf(d.selector, seen) || d.cases.some((x) => capturesLeaf(x.parser, seen)) || (d.otherwise ? capturesLeaf(d.otherwise, seen) : false);
6663
6846
  case "sepBy":
6664
6847
  return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
6665
6848
  case "many":
@@ -6706,6 +6889,7 @@ function emitFallible(inner, ctx, pos, swallow = false) {
6706
6889
  ctx.recordFail = savedRecord;
6707
6890
  const ind0 = ind(ctx);
6708
6891
  const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
6892
+ const mayCommit = mayCommitFailure(inner);
6709
6893
  const needsFieldRollback = needsRollback && parserHasOwnFields(inner);
6710
6894
  const mL = needsRollback ? v(ctx, "_fcl") : null;
6711
6895
  const mR = needsRollback ? v(ctx, "_fcr") : null;
@@ -6719,6 +6903,7 @@ function emitFallible(inner, ctx, pos, swallow = false) {
6719
6903
  `${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0`,
6720
6904
  ...mF ? [`${ind0}const ${mF} = _ctx._fields?.length ?? 0`] : []
6721
6905
  ] : [],
6906
+ ...mayCommit ? [`${ind0}_ctx._fc = false`] : [],
6722
6907
  `${ind0}${lbl}: {`,
6723
6908
  ...r.stmts,
6724
6909
  `${ind0} ${valV} = ${r.valueVar}; ${endV} = ${r.endVar}; ${okV} = true`,
@@ -6727,7 +6912,7 @@ function emitFallible(inner, ctx, pos, swallow = false) {
6727
6912
  `${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null, mF)} }`
6728
6913
  ] : []
6729
6914
  ];
6730
- return { stmts, okVar: okV, valVar: valV, endVar: endV };
6915
+ return { stmts, okVar: okV, valVar: valV, endVar: endV, mayCommit };
6731
6916
  }
6732
6917
  var CHARCODE_CHAIN_MAX = 16;
6733
6918
  function emitLit(def, ctx, pos) {
@@ -7104,6 +7289,118 @@ function emitChoice(parser2, def, ctx, pos) {
7104
7289
  }
7105
7290
  return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos, coverageBase);
7106
7291
  }
7292
+ function emitTailFallible(parser2, ctx, pos) {
7293
+ const savedRecord = ctx.recordFail;
7294
+ ctx.recordFail = true;
7295
+ const out = emitFallible(parser2, ctx, pos);
7296
+ ctx.recordFail = savedRecord;
7297
+ return out;
7298
+ }
7299
+ function mayRecordRecoveryError(p, recovery, seen = /* @__PURE__ */ new Set()) {
7300
+ if (seen.has(p)) return false;
7301
+ seen.add(p);
7302
+ const d = p._def;
7303
+ switch (d.tag) {
7304
+ case "expect":
7305
+ case "recover":
7306
+ return true;
7307
+ case "many":
7308
+ case "oneOrMore":
7309
+ case "sepBy":
7310
+ return recovery || mayRecordRecoveryError(d.parser, recovery, seen);
7311
+ case "choice":
7312
+ case "sequence": {
7313
+ for (const item of d.parsers) {
7314
+ if (mayRecordRecoveryError(item, recovery, seen)) return true;
7315
+ }
7316
+ return false;
7317
+ }
7318
+ case "dispatch": {
7319
+ if (mayRecordRecoveryError(d.selector, recovery, seen)) return true;
7320
+ for (const item of d.cases) {
7321
+ if (mayRecordRecoveryError(item.parser, recovery, seen)) return true;
7322
+ }
7323
+ return d.otherwise ? mayRecordRecoveryError(d.otherwise, recovery, seen) : false;
7324
+ }
7325
+ case "attempt":
7326
+ case "transform":
7327
+ case "label":
7328
+ case "field":
7329
+ case "grammar":
7330
+ case "node":
7331
+ case "token":
7332
+ case "leaf":
7333
+ case "withCtx":
7334
+ case "optional":
7335
+ return mayRecordRecoveryError(d.parser, recovery, seen);
7336
+ case "skip":
7337
+ return mayRecordRecoveryError(d.main, recovery, seen) || mayRecordRecoveryError(d.skipped, recovery, seen);
7338
+ case "scanTo":
7339
+ if (mayRecordRecoveryError(d.sentinel, recovery, seen)) return true;
7340
+ for (const item of d.skip) {
7341
+ if (mayRecordRecoveryError(item, recovery, seen)) return true;
7342
+ }
7343
+ return false;
7344
+ case "lazy": {
7345
+ try {
7346
+ return mayRecordRecoveryError(d.thunk(), recovery, seen);
7347
+ } catch {
7348
+ return true;
7349
+ }
7350
+ }
7351
+ case "unknown":
7352
+ return true;
7353
+ default:
7354
+ return false;
7355
+ }
7356
+ }
7357
+ function emitDispatchCombinator(def, ctx, pos) {
7358
+ const selector = emit(def.selector, ctx, pos);
7359
+ const outV = v(ctx, "_dval");
7360
+ const outE = v(ctx, "_dend");
7361
+ const keyV = v(ctx, "_dkey");
7362
+ const stmts = [
7363
+ ...selector.stmts,
7364
+ `${ind(ctx)}const ${keyV} = ${selector.valueVar}`,
7365
+ `${ind(ctx)}let ${outV}, ${outE} = ${selector.endVar}`
7366
+ ];
7367
+ let wroteBranch = false;
7368
+ const emitSelectedTail = (parser2, keyword, condition) => {
7369
+ const head = keyword === "else" ? `${ind(ctx)}else {` : `${ind(ctx)}${keyword} (${condition}) {`;
7370
+ stmts.push(head);
7371
+ ctx.indent++;
7372
+ const mayError = mayRecordRecoveryError(parser2, !!ctx.recovery);
7373
+ const errMark = mayError ? v(ctx, "_derr") : null;
7374
+ const logMark = ctx.activeTrivia ? v(ctx, "_dlog") : null;
7375
+ if (errMark) stmts.push(`${ind(ctx)}const ${errMark} = _ctx._errors?.length ?? 0`);
7376
+ if (logMark) stmts.push(`${ind(ctx)}const ${logMark} = _ctx._triviaLog?.length ?? 0`);
7377
+ const tail = emitTailFallible(parser2, ctx, selector.endVar);
7378
+ const errorRollback = errMark ? `if (_ctx._errors && _ctx._errors.length !== ${errMark}) _ctx._errors.length = ${errMark}; ` : "";
7379
+ const logRollback = logMark ? `if (_ctx._triviaLog && _ctx._triviaLog.length !== ${logMark}) _ctx._triviaLog.length = ${logMark}; ` : "";
7380
+ stmts.push(
7381
+ ...tail.stmts,
7382
+ `${ind(ctx)}if (!${tail.okVar}) { ${errorRollback}${logRollback}${committedFailBody(ctx)} }`,
7383
+ `${ind(ctx)}${outV} = [${selector.valueVar}, ${tail.valVar}]`,
7384
+ `${ind(ctx)}${outE} = ${tail.endVar}`
7385
+ );
7386
+ ctx.indent--;
7387
+ stmts.push(`${ind(ctx)}}`);
7388
+ };
7389
+ for (const entry of def.cases) {
7390
+ const condition = entry.keys.map((key) => `${keyV} === ${JSON.stringify(key)}`).join(" || ");
7391
+ emitSelectedTail(entry.parser, wroteBranch ? "else if" : "if", condition);
7392
+ wroteBranch = true;
7393
+ }
7394
+ if (def.otherwise) {
7395
+ if (wroteBranch) emitSelectedTail(def.otherwise, "else");
7396
+ else emitSelectedTail(def.otherwise, "if", "true");
7397
+ } else {
7398
+ const expected = JSON.stringify(def.cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key))));
7399
+ const fail = failArrBody(ctx, expected, selector.endVar);
7400
+ stmts.push(wroteBranch ? `${ind(ctx)}else { ${fail} }` : `${ind(ctx)}${fail}`);
7401
+ }
7402
+ return { stmts, valueVar: outV, endVar: outE };
7403
+ }
7107
7404
  function emitGreedyClassify(def, superIndex, ctx, pos, coverageBase) {
7108
7405
  const superParser = def.parsers[superIndex];
7109
7406
  const regexDef = getCoreRegexDef(superParser);
@@ -7225,12 +7522,16 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
7225
7522
  const armHasAutoNot = !!(autoNot && autoNot.length > 0);
7226
7523
  const armNeedsRollback = ctx.capturing && (mayLeavePartialCapture(p) || armHasAutoNot && capturesLeaf(p));
7227
7524
  const armNeedsFieldRollback = armNeedsRollback && parserHasOwnFields(p);
7525
+ const armMayRecordError = mayRecordRecoveryError(p, !!ctx.recovery);
7228
7526
  const markLeaves = armNeedsRollback ? v(ctx, "_cml") : null;
7229
7527
  const markRaw = armNeedsRollback ? v(ctx, "_cmr") : null;
7230
7528
  const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
7231
7529
  const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
7232
7530
  const markFields = armNeedsFieldRollback ? v(ctx, "_cmf") : null;
7233
- const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog, markFields) : "";
7531
+ const markErrors = armMayRecordError ? v(ctx, "_cme") : null;
7532
+ const captureRollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog, markFields) : "";
7533
+ const errorRollback = markErrors ? `if (_ctx._errors && _ctx._errors.length !== ${markErrors}) _ctx._errors.length = ${markErrors}` : "";
7534
+ const rollback = [captureRollback, errorRollback].filter(Boolean).join("; ");
7234
7535
  const failSlot = atStart ? staticFx : "_ctx._fx";
7235
7536
  stmts.push(`${ind0}if (${skipCond}) {`);
7236
7537
  if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
@@ -7245,7 +7546,9 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
7245
7546
  ...markFields ? [`${ind(ctx)}const ${markFields} = _ctx._fields?.length ?? 0`] : []
7246
7547
  );
7247
7548
  }
7248
- const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
7549
+ if (markErrors) stmts.push(`${ind(ctx)}const ${markErrors} = _ctx._errors?.length ?? 0`);
7550
+ const arm = emitFallible(p, ctx, pos, atStart);
7551
+ const { stmts: armStmts, okVar, valVar, endVar } = arm;
7249
7552
  stmts.push(...armStmts);
7250
7553
  if (autoNot && autoNot.length > 0) {
7251
7554
  const anCode = v(ctx, "_anc");
@@ -7269,10 +7572,12 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
7269
7572
  stmts.push(`${ind(ctx)}}`);
7270
7573
  stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}; ${coverageBase === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'failure', offset: ${atStart ? pos : "_ctx._fe"} }); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'backtrack', offset: ${pos} });`} }`);
7271
7574
  if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
7575
+ if (arm.mayCommit) stmts.push(`${ind(ctx)}if (!${resOkV} && _ctx._fc) ${committedFailBody(ctx)}`);
7272
7576
  } else {
7273
7577
  const rejectedFlush = autoRejectedV === void 0 ? "" : `; for (const _rejectedId of ${autoRejectedV}) { _ctx._grammarTrace?.write({ id: _rejectedId, phase: 'failure', offset: ${pos} }); _ctx._grammarTrace?.write({ id: _rejectedId, phase: 'backtrack', offset: ${pos} }) }`;
7274
7578
  stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true${coverageBase === void 0 ? "" : `${rejectedFlush}; _ctx._grammarCoverage?.(${JSON.stringify(`${coverageBase}${i}`)}); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'selected', offset: ${pos}, end: ${endVar} }); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'success', offset: ${pos}, end: ${endVar} })`} }`);
7275
7579
  stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""}${coverageBase === void 0 ? "" : `; _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'failure', offset: ${atStart ? pos : "_ctx._fe"} }); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'backtrack', offset: ${pos} })`} }`);
7580
+ if (arm.mayCommit) stmts.push(`${ind(ctx)}if (!${resOkV} && _ctx._fc) ${committedFailBody(ctx)}`);
7276
7581
  }
7277
7582
  ctx.indent -= fsGuard ? 2 : 1;
7278
7583
  if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
@@ -7429,7 +7734,8 @@ function emitMany(def, ctx, pos) {
7429
7734
  `${ind(ctx)}if (!(${firstSetCond(lcV, def.parser._meta.firstSet)})) { ${rollback}break }`
7430
7735
  );
7431
7736
  }
7432
- const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
7737
+ const iter = emitFallible(def.parser, ctx, itemPos, true);
7738
+ const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = iter;
7433
7739
  stmts.push(...iterStmts);
7434
7740
  if (ctx.recovery) {
7435
7741
  const rrV = v(ctx, "_rr");
@@ -7448,7 +7754,8 @@ function emitMany(def, ctx, pos) {
7448
7754
  `${ind(ctx)}if (${iterEnd} <= ${itemPos}) { ${rollback}break }`
7449
7755
  );
7450
7756
  } else {
7451
- stmts.push(`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`);
7757
+ stmts.push(`${ind(ctx)}if (!${iterOk}) { ${rollback}${iter.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
7758
+ stmts.push(`${ind(ctx)}if (${iterEnd} <= ${itemPos}) { ${rollback}break }`);
7452
7759
  }
7453
7760
  if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
7454
7761
  else if (maxV !== null) stmts.push(`${ind(ctx)}${maxV}++`);
@@ -7460,10 +7767,12 @@ function emitMany(def, ctx, pos) {
7460
7767
  function emitOptional(def, ctx, pos) {
7461
7768
  const valV = v(ctx, "_opt");
7462
7769
  const endV = v(ctx, "_opte");
7463
- const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos, true);
7770
+ const inner = emitFallible(def.parser, ctx, pos, true);
7771
+ const { stmts: lblStmts, okVar, valVar, endVar } = inner;
7464
7772
  const ind0 = ind(ctx);
7465
7773
  const stmts = [
7466
7774
  ...lblStmts,
7775
+ ...inner.mayCommit ? [`${ind0}if (!${okVar} && _ctx._fc) ${committedFailBody(ctx)}`] : [],
7467
7776
  `${ind0}const ${valV} = ${okVar} ? ${valVar} : null`,
7468
7777
  `${ind0}const ${endV} = ${okVar} ? ${endVar} : ${pos}`
7469
7778
  ];
@@ -7495,7 +7804,7 @@ function emitAttempt(p, def, ctx, pos) {
7495
7804
  ...preGuard,
7496
7805
  `${ind(ctx)}const ${leaves} = _ctx._cstLeaves?.length ?? 0, ${raw} = _ctx._cstRawChildren?.length ?? 0, ${trivia2} = _ctx._cstTriviaLog?.length ?? 0, ${log} = _ctx._triviaLog?.length ?? 0, ${fields} = _ctx._fields?.length ?? 0, ${errors} = _ctx._errors?.length ?? 0`,
7497
7806
  ...inner.stmts,
7498
- ...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
7807
+ ...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback}${inner.mayCommit ? ` if (_ctx._fc) ${committedFailBody(ctx)};` : ""} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
7499
7808
  ],
7500
7809
  valueVar: inner.valVar,
7501
7810
  endVar: inner.endVar
@@ -7509,18 +7818,21 @@ function emitSepBy(_p, def, ctx, pos) {
7509
7818
  const exp = rec ? hoistExpected(ctx, deriveExpectedArr([def.parser])) : "";
7510
7819
  const sepSent = rec ? syncSentinelExpr(firstSetOf(def.separator)) : null;
7511
7820
  const scanSync = sepSent ? `_ctx._rec.or(${sepSent}, ${mySyncV})` : mySyncV;
7512
- const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
7821
+ const first = emitFallible(def.parser, ctx, pos, true);
7822
+ const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = first;
7513
7823
  const stmts = [
7514
7824
  `${ind(ctx)}const ${arrV} = []`,
7515
7825
  `${ind(ctx)}let ${curV} = ${pos}`,
7516
7826
  ...rec ? [`${ind(ctx)}const ${mySyncV} = _ctx._sync`] : [],
7517
7827
  ...firstStmts
7518
7828
  ];
7519
- const failItem = (nextOkVar, itemPosVar, breakStmt) => {
7520
- if (!rec) return [`${ind(ctx)}if (!${nextOkVar}) ${breakStmt === "break" ? "break" : `{ ${breakStmt} }`}`];
7829
+ const failItem = (nextOkVar, nextMayCommit, itemPosVar, breakStmt) => {
7830
+ const commit = nextMayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : "";
7831
+ if (!rec) return [`${ind(ctx)}if (!${nextOkVar}) { ${commit}${breakStmt} }`];
7521
7832
  const rr = v(ctx, "_rr");
7522
7833
  return [
7523
7834
  `${ind(ctx)}if (!${nextOkVar}) {`,
7835
+ ...nextMayCommit ? [`${ind(ctx)} if (_ctx._fc) ${committedFailBody(ctx)}`] : [],
7524
7836
  `${ind(ctx)} if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${itemPosVar}, _ctx)) {`,
7525
7837
  `${ind(ctx)} const ${rr} = _ctx._rec.scan(input, ${itemPosVar}, _ctx, ${scanSync}, ${exp})`,
7526
7838
  `${ind(ctx)} ${arrV}.push(${rr}.error); _ctx._rec.capture(_ctx, ${rr}.error); ${curV} = ${rr}.end; continue`,
@@ -7549,6 +7861,7 @@ function emitSepBy(_p, def, ctx, pos) {
7549
7861
  stmts.push(
7550
7862
  `${ind(ctx)}let ${didV} = false`,
7551
7863
  `${ind(ctx)}if (${firstOk}) { ${arrV}.push(${firstVal}); ${curV} = ${firstEnd}; ${didV} = true }`,
7864
+ ...first.mayCommit ? [`${ind(ctx)}else if (_ctx._fc) ${committedFailBody(ctx)}`] : [],
7552
7865
  `${ind(ctx)}else if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${pos}, _ctx)) {`,
7553
7866
  `${ind(ctx)} const ${rr0} = _ctx._rec.scan(input, ${pos}, _ctx, ${scanSync}, ${exp})`,
7554
7867
  `${ind(ctx)} ${arrV}.push(${rr0}.error); _ctx._rec.capture(_ctx, ${rr0}.error); ${curV} = ${rr0}.end; ${didV} = true`,
@@ -7559,6 +7872,7 @@ function emitSepBy(_p, def, ctx, pos) {
7559
7872
  stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
7560
7873
  ctx.indent++;
7561
7874
  } else {
7875
+ if (first.mayCommit) stmts.push(`${ind(ctx)}if (!${firstOk} && _ctx._fc) ${committedFailBody(ctx)}`);
7562
7876
  stmts.push(`${ind(ctx)}if (${firstOk}) {`);
7563
7877
  ctx.indent++;
7564
7878
  stmts.push(
@@ -7587,16 +7901,18 @@ function emitSepBy(_p, def, ctx, pos) {
7587
7901
  );
7588
7902
  sepAtPos = spV;
7589
7903
  const rollbackToSep = `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${markLv}) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markV}) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${markTl}) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}; `;
7590
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
7591
- stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
7904
+ const sep2 = emitFallible(def.separator, ctx, sepAtPos, true);
7905
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep2;
7906
+ stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
7592
7907
  const post = postSepMarks();
7593
7908
  stmts.push(...post.decl);
7594
7909
  const npV = v(ctx, "_np");
7595
7910
  stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
7596
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
7911
+ const next = emitFallible(def.parser, ctx, npV, true);
7912
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
7597
7913
  stmts.push(
7598
7914
  ...nextStmts,
7599
- ...failItem(nextOk, npV, itemFailBreak(sepEnd, rollbackToSep, post.rb)),
7915
+ ...failItem(nextOk, next.mayCommit, npV, itemFailBreak(sepEnd, rollbackToSep, post.rb)),
7600
7916
  `${ind(ctx)}${arrV}.push(${nextVal})`,
7601
7917
  `${ind(ctx)}${curV} = ${nextEnd}`
7602
7918
  );
@@ -7605,14 +7921,16 @@ function emitSepBy(_p, def, ctx, pos) {
7605
7921
  const spV = v(ctx, "_sp");
7606
7922
  stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
7607
7923
  sepAtPos = spV;
7608
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
7609
- stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
7924
+ const sep2 = emitFallible(def.separator, ctx, sepAtPos, true);
7925
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep2;
7926
+ stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
7610
7927
  const npV = v(ctx, "_np");
7611
7928
  stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
7612
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
7929
+ const next = emitFallible(def.parser, ctx, npV, true);
7930
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
7613
7931
  stmts.push(
7614
7932
  ...nextStmts,
7615
- ...failItem(nextOk, npV, itemFailBreak(sepEnd, "", "")),
7933
+ ...failItem(nextOk, next.mayCommit, npV, itemFailBreak(sepEnd, "", "")),
7616
7934
  `${ind(ctx)}${arrV}.push(${nextVal})`,
7617
7935
  `${ind(ctx)}${curV} = ${nextEnd}`
7618
7936
  );
@@ -7626,15 +7944,17 @@ function emitSepBy(_p, def, ctx, pos) {
7626
7944
  `${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
7627
7945
  );
7628
7946
  }
7629
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
7630
- stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
7947
+ const sep2 = emitFallible(def.separator, ctx, sepAtPos, true);
7948
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep2;
7949
+ stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
7631
7950
  const nextRb = markLv ? `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${markLv}) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markRw}) _ctx._cstRawChildren.length = ${markRw}; ` : "";
7632
7951
  const post = postSepMarks();
7633
7952
  stmts.push(...post.decl);
7634
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
7953
+ const next = emitFallible(def.parser, ctx, sepEnd, true);
7954
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
7635
7955
  stmts.push(
7636
7956
  ...nextStmts,
7637
- ...failItem(nextOk, sepEnd, itemFailBreak(sepEnd, nextRb, post.rb)),
7957
+ ...failItem(nextOk, next.mayCommit, sepEnd, itemFailBreak(sepEnd, nextRb, post.rb)),
7638
7958
  `${ind(ctx)}${arrV}.push(${nextVal})`,
7639
7959
  `${ind(ctx)}${curV} = ${nextEnd}`
7640
7960
  );
@@ -8015,7 +8335,7 @@ function emitRuntimeFallback(parser2, ctx, pos) {
8015
8335
  const rv = v(ctx, "_rt");
8016
8336
  const vv = v(ctx, "_rtv");
8017
8337
  const ev = v(ctx, "_rte");
8018
- const failStmt = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${rv}.span.start; _ctx._fx = ${rv}.expected; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : `return { ok: false, expected: ${rv}.expected, span: ${rv}.span }`;
8338
+ const failStmt = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${rv}.span.start; _ctx._fx = ${rv}.expected; _ctx._fc = ${rv}.committed === true; break ${ctx.failLabel} }` : `{ _ctx._fc = ${rv}.committed === true; break ${ctx.failLabel} }` : `return { ok: false, expected: ${rv}.expected, span: ${rv}.span, ...(${rv}.committed ? { committed: true } : {}) }`;
8019
8339
  const stmts = [
8020
8340
  `${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
8021
8341
  ...emitIfFail(ctx, `!${rv}.ok`, failStmt),
@@ -8096,7 +8416,7 @@ function emitRecover(def, ctx, pos) {
8096
8416
  return { stmts, valueVar: valVar, endVar };
8097
8417
  }
8098
8418
  function emitExpect(def, ctx, pos) {
8099
- const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
8419
+ const { stmts: innerStmts, okVar, valVar, endVar, mayCommit } = emitFallible(def.parser, ctx, pos);
8100
8420
  const ind0 = ind(ctx);
8101
8421
  const errV = v(ctx, "_err");
8102
8422
  const stmts = [
@@ -8108,6 +8428,7 @@ function emitExpect(def, ctx, pos) {
8108
8428
  // interpreter (expect.ts) and the list-recovery emit sites — guarded on
8109
8429
  // `_ctx._tolerant`, under which the driver always installs `_ctx._rec`.
8110
8430
  `${ind0} if (_ctx._tolerant) _ctx._rec.capture(_ctx, ${errV})`,
8431
+ ...mayCommit ? [`${ind0} _ctx._fc = false`] : [],
8111
8432
  `${ind0} ${valVar} = ${errV}`,
8112
8433
  `${ind0} ${endVar} = ${pos}`,
8113
8434
  `${ind0} ${okVar} = true`,
@@ -8118,7 +8439,7 @@ function emitExpect(def, ctx, pos) {
8118
8439
  function emit(p, ctx, pos) {
8119
8440
  const replay = ctx.replayPrefix?.get(p);
8120
8441
  if (replay !== void 0) return emitReplayPrefixLeaf(ctx, pos, replay);
8121
- const dispatch = (emissionPos = pos) => {
8442
+ const dispatch2 = (emissionPos = pos) => {
8122
8443
  const savedRule = ctx.activeCoverageRuleId;
8123
8444
  const rule = ctx.coverage?.plan.rules.get(p);
8124
8445
  if (rule && p !== ctx.coverage?.entry && !ctx.suppressCoverageFailure) ctx.activeCoverageRuleId = rule;
@@ -8158,14 +8479,14 @@ function emit(p, ctx, pos) {
8158
8479
  ctx.indent = 1;
8159
8480
  ctx.failLabel = "_pfail";
8160
8481
  ctx.recordFail = true;
8161
- const r = dispatch("_pos");
8482
+ const r = dispatch2("_pos");
8162
8483
  ctx.indent = savedIndent;
8163
8484
  ctx.failLabel = savedFailLabel;
8164
8485
  ctx.recordFail = savedRecord;
8165
8486
  pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
8166
8487
  return instrument(emitNamedFnCall(ctx, fnName, pos));
8167
8488
  }
8168
- return instrument(dispatch());
8489
+ return instrument(dispatch2());
8169
8490
  }
8170
8491
  function emitDispatch(p, ctx, pos) {
8171
8492
  const bal = p._balancedAmbient;
@@ -8198,6 +8519,8 @@ function emitDispatch(p, ctx, pos) {
8198
8519
  return emitSeq(def, ctx, pos);
8199
8520
  case "choice":
8200
8521
  return emitChoice(p, def, ctx, pos);
8522
+ case "dispatch":
8523
+ return emitDispatchCombinator(def, ctx, pos);
8201
8524
  case "attempt":
8202
8525
  return emitAttempt(p, def, ctx, pos);
8203
8526
  case "many":
@@ -8418,6 +8741,8 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
8418
8741
  case "sequence":
8419
8742
  case "choice":
8420
8743
  return d.parsers.some((x) => hasNodeDef(x, seen));
8744
+ case "dispatch":
8745
+ return hasNodeDef(d.selector, seen) || d.cases.some((x) => hasNodeDef(x.parser, seen)) || (d.otherwise ? hasNodeDef(d.otherwise, seen) : false);
8421
8746
  case "sepBy":
8422
8747
  return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
8423
8748
  case "scanTo":
@@ -8451,6 +8776,8 @@ function childrenOf2(def) {
8451
8776
  case "sequence":
8452
8777
  case "choice":
8453
8778
  return def.parsers;
8779
+ case "dispatch":
8780
+ return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
8454
8781
  case "many":
8455
8782
  case "attempt":
8456
8783
  case "optional":
@@ -8494,6 +8821,7 @@ function childrenOf2(def) {
8494
8821
  var HOISTABLE_TAGS = /* @__PURE__ */ new Set([
8495
8822
  "sequence",
8496
8823
  "choice",
8824
+ "dispatch",
8497
8825
  "many",
8498
8826
  "oneOrMore",
8499
8827
  "optional",
@@ -9318,6 +9646,8 @@ function childrenOf3(def) {
9318
9646
  case "sequence":
9319
9647
  case "choice":
9320
9648
  return def.parsers;
9649
+ case "dispatch":
9650
+ return [def.selector, ...def.cases.map((entry) => entry.parser), ...def.otherwise ? [def.otherwise] : []];
9321
9651
  case "many":
9322
9652
  case "oneOrMore":
9323
9653
  case "optional":
@@ -9467,6 +9797,9 @@ function evalRuleMapIR(ir) {
9467
9797
  "keywords",
9468
9798
  "sequence",
9469
9799
  "choice",
9800
+ "dispatch",
9801
+ "when",
9802
+ "otherwise",
9470
9803
  "attempt",
9471
9804
  "many",
9472
9805
  "oneOrMore",
@@ -9500,6 +9833,9 @@ function evalRuleMapIR(ir) {
9500
9833
  keywords,
9501
9834
  sequence,
9502
9835
  choice,
9836
+ dispatch,
9837
+ when,
9838
+ otherwise,
9503
9839
  attempt,
9504
9840
  many,
9505
9841
  oneOrMore,
@@ -9685,6 +10021,13 @@ ${outerDecls.map((d) => " " + d).join("\n")}
9685
10021
  });
9686
10022
  return `_gch([${items.join(", ")}])`;
9687
10023
  }
10024
+ case "dispatch": {
10025
+ const arms = def.cases.map(
10026
+ (entry) => `when(${entry.keys.length === 1 ? JSON.stringify(entry.keys[0]) : JSON.stringify(entry.keys)}, ${kid(entry.parser)})`
10027
+ );
10028
+ if (def.otherwise) arms.push(`otherwise(${kid(def.otherwise)})`);
10029
+ return `dispatch(${kid(def.selector)}${arms.length === 0 ? "" : `, ${arms.join(", ")}`})`;
10030
+ }
9688
10031
  case "many":
9689
10032
  return `many(${kid(def.parser)}${repeatOpts(def.min, def.max)})`;
9690
10033
  // A `oneOrMore` def with min > 1 came from `many(x, { min: n })`; round-trip
@@ -10170,6 +10513,9 @@ function stripTsFromSource(node2, code) {
10170
10513
  function isWordFactory(v2) {
10171
10514
  return !!v2 && typeof v2 === "object" && v2.tag === "wordFactory";
10172
10515
  }
10516
+ function isStaticValueEntry(v2) {
10517
+ return !!v2 && typeof v2 === "object" && "value" in v2 && "mfSrcs" in v2;
10518
+ }
10173
10519
  function wordFactoryFromArgs(args, scope, code, mfs) {
10174
10520
  const [boundaryOrOptsArg, optsArg] = args;
10175
10521
  if (boundaryOrOptsArg?.type === "SpreadElement" || optsArg?.type === "SpreadElement") return null;
@@ -10239,9 +10585,41 @@ function gatedArmParts(e) {
10239
10585
  }
10240
10586
  return gate2 && combinator ? { gate: gate2, combinator } : null;
10241
10587
  }
10588
+ function dispatchArmValue(node2, scope, code, mfs) {
10589
+ if (node2.type === "Identifier") {
10590
+ const value = anyValue(node2, scope, code, mfs);
10591
+ return isDispatchArm(value) ? value : null;
10592
+ }
10593
+ if (node2.type !== "CallExpression") return null;
10594
+ const callee = node2.callee;
10595
+ if (callee.type !== "Identifier") return null;
10596
+ if (callee.name === "otherwise") {
10597
+ const [parserArg] = node2.arguments;
10598
+ if (!parserArg || parserArg.type === "SpreadElement") return null;
10599
+ const parserValue = anyValue(parserArg, scope, code, mfs);
10600
+ if (!isCombinator(parserValue)) return null;
10601
+ return otherwise(parserValue);
10602
+ }
10603
+ if (callee.name === "when") {
10604
+ const [keyArg, parserArg] = node2.arguments;
10605
+ if (!keyArg || !parserArg || keyArg.type === "SpreadElement" || parserArg.type === "SpreadElement") return null;
10606
+ const key = anyValue(keyArg, scope, code, mfs);
10607
+ const parserValue = anyValue(parserArg, scope, code, mfs);
10608
+ if (!isCombinator(parserValue)) return null;
10609
+ if (typeof key === "string") return when(key, parserValue);
10610
+ if (Array.isArray(key) && key.every((item) => typeof item === "string")) return when(key, parserValue);
10611
+ }
10612
+ return null;
10613
+ }
10242
10614
  function isCombinator(v2) {
10243
10615
  return !!v2 && typeof v2 === "object" && "_def" in v2;
10244
10616
  }
10617
+ function isDispatchArm(v2) {
10618
+ if (!v2 || typeof v2 !== "object") return false;
10619
+ const rec = v2;
10620
+ if (rec.kind === "otherwise") return isCombinator(rec.parser);
10621
+ return rec.kind === "when" && Array.isArray(rec.keys) && rec.keys.every((key) => typeof key === "string") && isCombinator(rec.parser);
10622
+ }
10245
10623
  function scopeGet(scope, name, mfs) {
10246
10624
  const entry = scope.get(name);
10247
10625
  if (!entry) return null;
@@ -10498,6 +10876,25 @@ function exprToCombi(node2, scope, code, mfs) {
10498
10876
  return null;
10499
10877
  }
10500
10878
  }
10879
+ if (callee.name === "dispatch") {
10880
+ const [selectorArg, ...armArgs] = node2.arguments;
10881
+ if (!selectorArg || selectorArg.type === "SpreadElement") return null;
10882
+ const selector = anyValue(selectorArg, scope, code, mfs);
10883
+ if (!isCombinator(selector)) return null;
10884
+ const arms = [];
10885
+ for (const arg of armArgs) {
10886
+ if (arg.type === "SpreadElement") return null;
10887
+ const arm = dispatchArmValue(arg, scope, code, mfs);
10888
+ if (!isDispatchArm(arm)) return null;
10889
+ arms.push(arm);
10890
+ }
10891
+ try {
10892
+ const unsafeDispatch = dispatch;
10893
+ return unsafeDispatch(selector, ...arms);
10894
+ } catch {
10895
+ return null;
10896
+ }
10897
+ }
10501
10898
  if (callee.name === "choice" && node2.arguments.some((a) => a.type !== "SpreadElement" && isGatedArmExpr(a))) {
10502
10899
  if (code === void 0 || mfs === void 0) return null;
10503
10900
  const arms = [];
@@ -10583,6 +10980,10 @@ function anyValue(node2, scope, code, mfs) {
10583
10980
  if (mfs && entry.mfSrcs.length > 0) mfs.push(...entry.mfSrcs);
10584
10981
  return entry.combi;
10585
10982
  }
10983
+ if (isStaticValueEntry(entry)) {
10984
+ if (mfs && entry.mfSrcs.length > 0) mfs.push(...entry.mfSrcs);
10985
+ return entry.value;
10986
+ }
10586
10987
  return entry;
10587
10988
  }
10588
10989
  if (node2.type === "MemberExpression") {
@@ -10603,6 +11004,9 @@ function anyValue(node2, scope, code, mfs) {
10603
11004
  if (callee.type === "Identifier" && callee.name === "makeWord") {
10604
11005
  return wordFactoryFromArgs(node2.arguments, scope, code, mfs);
10605
11006
  }
11007
+ if (callee.type === "Identifier" && (callee.name === "when" || callee.name === "otherwise")) {
11008
+ return dispatchArmValue(node2, scope, code, mfs);
11009
+ }
10606
11010
  return exprToCombi(node2, scope, code, mfs);
10607
11011
  }
10608
11012
  return null;
@@ -10661,6 +11065,7 @@ function evalBodyStatements(statements, scope, code) {
10661
11065
  if (val === null) return false;
10662
11066
  const thisDeclMfSrcs = bodyMfs.slice(before);
10663
11067
  if (isCombinator(val)) scope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
11068
+ else if (isDispatchArm(val)) scope.set(name, { value: val, mfSrcs: thisDeclMfSrcs });
10664
11069
  else scope.set(name, val);
10665
11070
  }
10666
11071
  }