parseman 0.59.3 → 0.60.1

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 (94) hide show
  1. package/dist/analysis/diagnostics.js +876 -190
  2. package/dist/analysis/diagnostics.js.map +3 -3
  3. package/dist/cli/index.js +876 -190
  4. package/dist/cli/index.js.map +3 -3
  5. package/dist/combinators/dispatch.d.ts +13 -1
  6. package/dist/combinators/dispatch.d.ts.map +1 -1
  7. package/dist/combinators/first-set.d.ts +35 -2
  8. package/dist/combinators/first-set.d.ts.map +1 -1
  9. package/dist/combinators/regex.d.ts.map +1 -1
  10. package/dist/combinators/trivia-boundary.d.ts +11 -0
  11. package/dist/combinators/trivia-boundary.d.ts.map +1 -1
  12. package/dist/combinators/trivia-skip.d.ts +1 -1
  13. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  14. package/dist/compiler/token-alphabet.d.ts +2 -0
  15. package/dist/compiler/token-alphabet.d.ts.map +1 -1
  16. package/dist/compiler/token-capability.js +65 -53
  17. package/dist/compiler/token-capability.js.map +2 -2
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +896 -199
  21. package/dist/index.js.map +3 -3
  22. package/dist/plugin/evaluator.d.ts.map +1 -1
  23. package/dist/plugin/index.js +903 -201
  24. package/dist/plugin/index.js.map +3 -3
  25. package/dist/regex/first-set.d.ts +19 -0
  26. package/dist/regex/first-set.d.ts.map +1 -1
  27. package/dist/table/assemble.d.ts +15 -1
  28. package/dist/table/assemble.d.ts.map +1 -1
  29. package/dist/table/child-slots.d.ts.map +1 -1
  30. package/dist/table/compile-rule-map.d.ts +1 -1
  31. package/dist/table/compile-rule-map.d.ts.map +1 -1
  32. package/dist/table/compile.d.ts +1 -1
  33. package/dist/table/emit-assembly.d.ts +8 -2
  34. package/dist/table/emit-assembly.d.ts.map +1 -1
  35. package/dist/table/emit.d.ts +4 -3
  36. package/dist/table/emit.d.ts.map +1 -1
  37. package/dist/table/encode.d.ts.map +1 -1
  38. package/dist/table/exec.d.ts +0 -35
  39. package/dist/table/exec.d.ts.map +1 -1
  40. package/dist/table/factor-choice.d.ts +27 -0
  41. package/dist/table/factor-choice.d.ts.map +1 -1
  42. package/dist/table/gap-gate.d.ts +11 -0
  43. package/dist/table/gap-gate.d.ts.map +1 -0
  44. package/dist/table/index.js +926 -201
  45. package/dist/table/index.js.map +3 -3
  46. package/dist/table/lex-program.d.ts.map +1 -1
  47. package/dist/table/ops.d.ts +35 -16
  48. package/dist/table/ops.d.ts.map +1 -1
  49. package/dist/table/program.d.ts +6 -1
  50. package/dist/table/program.d.ts.map +1 -1
  51. package/dist/types.d.ts +16 -4
  52. package/dist/types.d.ts.map +1 -1
  53. package/dist/version.d.ts +1 -1
  54. package/package.json +2 -4
  55. package/src/analysis/duplication.ts +1 -1
  56. package/src/combinators/attempt.ts +1 -1
  57. package/src/combinators/dispatch.ts +30 -6
  58. package/src/combinators/first-set.ts +55 -12
  59. package/src/combinators/node.ts +1 -1
  60. package/src/combinators/regex.ts +9 -4
  61. package/src/combinators/repeat.ts +6 -6
  62. package/src/combinators/sequence.ts +1 -1
  63. package/src/combinators/trivia-boundary.ts +18 -0
  64. package/src/combinators/trivia-skip.ts +1 -1
  65. package/src/compiler/ir-serialize.ts +1 -1
  66. package/src/compiler/token-alphabet.ts +3 -1
  67. package/src/coverage.ts +1 -1
  68. package/src/index.ts +1 -1
  69. package/src/plugin/evaluator.ts +8 -3
  70. package/src/regex/first-set.ts +37 -3
  71. package/src/table/assemble.ts +176 -30
  72. package/src/table/child-slots.ts +8 -3
  73. package/src/table/compile-rule-map.ts +7 -4
  74. package/src/table/compile.ts +1 -1
  75. package/src/table/emit-assembly.ts +184 -33
  76. package/src/table/emit.ts +6 -4
  77. package/src/table/encode.ts +469 -74
  78. package/src/table/exec.ts +38 -82
  79. package/src/table/factor-choice.ts +62 -0
  80. package/src/table/fail-clean.ts +2 -2
  81. package/src/table/gap-gate.ts +55 -0
  82. package/src/table/hold-gap.ts +2 -2
  83. package/src/table/lex-program.ts +15 -6
  84. package/src/table/ops.ts +38 -16
  85. package/src/table/program.ts +7 -2
  86. package/src/table/select-hot.ts +7 -2
  87. package/src/types.ts +12 -5
  88. package/src/version.ts +1 -1
  89. package/dist/table/encode-baseline.d.ts +0 -16
  90. package/dist/table/encode-baseline.d.ts.map +0 -1
  91. package/dist/table/exec-baseline.d.ts +0 -9
  92. package/dist/table/exec-baseline.d.ts.map +0 -1
  93. package/src/table/encode-baseline.ts +0 -346
  94. package/src/table/exec-baseline.ts +0 -557
@@ -35,15 +35,56 @@ function implicitLeadsDropped(ranges) {
35
35
  return out;
36
36
  }
37
37
 
38
+ // src/combinators/trivia-boundary.ts
39
+ var MAX_DEPTH = 12;
40
+ function hasOwnTriviaBoundary(p, depth = MAX_DEPTH) {
41
+ if (depth === 0) return true;
42
+ const d = p._def;
43
+ switch (d.tag) {
44
+ // Multi-term forms: the gaps BETWEEN terms are the ambient-trivia positions.
45
+ case "sequence":
46
+ return d.parsers.length > 1;
47
+ case "many":
48
+ case "oneOrMore":
49
+ case "sepBy":
50
+ return true;
51
+ // Transparent wrappers — they add no position, so ask what they wrap.
52
+ case "node":
53
+ case "transform":
54
+ case "leaf":
55
+ case "label":
56
+ case "field":
57
+ case "token":
58
+ case "attempt":
59
+ case "optional":
60
+ case "expect":
61
+ case "withCtx":
62
+ case "recover":
63
+ case "trivia":
64
+ return hasOwnTriviaBoundary(d.parser, depth - 1);
65
+ // An alternation adds no position either, but ONE arm that has one is enough
66
+ // to make the scope meaningful for this rule.
67
+ case "choice":
68
+ return d.parsers.some((a) => hasOwnTriviaBoundary(a, depth - 1));
69
+ default:
70
+ return false;
71
+ }
72
+ }
73
+ function triviaThroughRef(ref2, target, trivia2) {
74
+ if (trivia2 !== void 0 && trivia2 !== null) return trivia2;
75
+ const amb = ref2._meta.grammarTrivia ?? target._meta.grammarTrivia;
76
+ return amb !== void 0 && hasOwnTriviaBoundary(target) ? amb : trivia2;
77
+ }
78
+
38
79
  // src/combinators/first-set.ts
39
- function classifyFinalChoice(arms, resolve3, activeTrivia) {
80
+ function classifyFinalChoice(arms, resolve3, activeTrivia, consuming = true, constructionSets = false) {
40
81
  const firstSets = [];
41
82
  const nullable2 = [];
42
83
  let exclusive = true;
43
84
  for (const arm of arms) {
44
85
  const empty2 = matchesEmpty(arm, /* @__PURE__ */ new Set(), resolve3);
45
86
  nullable2.push(empty2);
46
- firstSets.push(empty2 ? { kind: "any" } : firstSetOf(arm, /* @__PURE__ */ new Set(), resolve3, activeTrivia));
87
+ firstSets.push(empty2 ? { kind: "any" } : constructionSets ? arm._meta.firstSet : firstSetOf(arm, /* @__PURE__ */ new Set(), resolve3, activeTrivia, consuming));
47
88
  if (empty2) exclusive = false;
48
89
  }
49
90
  if (exclusive) {
@@ -228,7 +269,7 @@ function narrowBy(acc, constraint) {
228
269
  function applyAssertion(fs3, assertion) {
229
270
  return assertion === null ? fs3 : fs3.kind === "empty" ? assertion : narrowBy(fs3, assertion);
230
271
  }
231
- function sequenceSet(parsers, fs3, empties, trivia2) {
272
+ function sequenceSet(parsers, fs3, empties, trivia2, consuming = true) {
232
273
  const afterBoundary = (i) => {
233
274
  const rest = at(i);
234
275
  return trivia2 ? union(rest, fs3(trivia2)) : rest;
@@ -244,24 +285,24 @@ function sequenceSet(parsers, fs3, empties, trivia2) {
244
285
  }
245
286
  const first = fs3(p);
246
287
  if (!empties(p) || i + 1 === parsers.length) return first;
247
- return union(first, afterBoundary(i + 1));
288
+ return union((consuming ? p._meta.consumingFirstSet : void 0) ?? first, afterBoundary(i + 1));
248
289
  };
249
290
  return at(0);
250
291
  }
251
292
  function sequenceFirstSet(parsers) {
252
- return sequenceSet(parsers, (p) => p._meta.firstSet, matchesEmpty);
293
+ return sequenceSet(parsers, (p) => p._meta.firstSet, matchesEmpty, void 0, false);
253
294
  }
254
- function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve3, activeTrivia) {
295
+ function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve3, activeTrivia, consuming = true) {
255
296
  if (seen.has(p)) return any();
256
297
  seen.add(p);
257
298
  try {
258
- return firstSetBody(p, seen, resolve3, activeTrivia);
299
+ return firstSetBody(p, seen, resolve3, activeTrivia, consuming);
259
300
  } finally {
260
301
  seen.delete(p);
261
302
  }
262
303
  }
263
- function firstSetBody(p, seen, resolve3, activeTrivia) {
264
- const fs3 = (c) => firstSetOf(c, seen, resolve3, activeTrivia);
304
+ function firstSetBody(p, seen, resolve3, activeTrivia, consuming) {
305
+ const fs3 = (c) => firstSetOf(c, seen, resolve3, activeTrivia, consuming);
265
306
  const empties = (c) => matchesEmpty(c, /* @__PURE__ */ new Set(), resolve3);
266
307
  const d = p._def;
267
308
  switch (d.tag) {
@@ -273,13 +314,16 @@ function firstSetBody(p, seen, resolve3, activeTrivia) {
273
314
  case "dispatch":
274
315
  return fs3(d.selector);
275
316
  case "lazy": {
276
- const w = winnerForRef(p, resolve3);
277
- if (w !== void 0) return fs3(w);
278
- try {
279
- return fs3(d.thunk());
280
- } catch {
281
- return any();
317
+ let target = winnerForRef(p, resolve3);
318
+ if (target === void 0) {
319
+ try {
320
+ target = d.thunk();
321
+ } catch {
322
+ return any();
323
+ }
282
324
  }
325
+ const through = activeTrivia === null && resolve3 !== void 0;
326
+ return firstSetOf(target, seen, resolve3, through ? triviaThroughRef(p, target, null) : activeTrivia, consuming);
283
327
  }
284
328
  case "choice": {
285
329
  let out = empty();
@@ -287,7 +331,7 @@ function firstSetBody(p, seen, resolve3, activeTrivia) {
287
331
  return out;
288
332
  }
289
333
  case "sequence": {
290
- return sequenceSet(d.parsers, fs3, empties, activeTrivia);
334
+ return sequenceSet(d.parsers, fs3, empties, activeTrivia, consuming);
291
335
  }
292
336
  case "peek": {
293
337
  const inner = d.parser;
@@ -299,7 +343,6 @@ function firstSetBody(p, seen, resolve3, activeTrivia) {
299
343
  case "transform":
300
344
  case "label":
301
345
  case "trivia":
302
- case "token":
303
346
  case "leaf":
304
347
  case "node":
305
348
  case "sepBy":
@@ -309,12 +352,16 @@ function firstSetBody(p, seen, resolve3, activeTrivia) {
309
352
  // with a rule reference — resolve through it like any other wrapper.
310
353
  case "attempt":
311
354
  return fs3(d.parser);
355
+ // A token runs its body with trivia cleared (`token.ts`).
356
+ case "token":
357
+ return firstSetOf(d.parser, seen, resolve3, activeTrivia === void 0 ? void 0 : null, consuming);
312
358
  case "grammar":
313
359
  return firstSetOf(
314
360
  d.parser,
315
361
  seen,
316
362
  resolve3,
317
- d.clearTrivia ? null : d.triviaParser ?? activeTrivia
363
+ d.clearTrivia ? null : d.triviaParser ?? activeTrivia,
364
+ consuming
318
365
  );
319
366
  // A run starts with its first line's prefix; a nullable prefix starts with anything.
320
367
  case "lines": {
@@ -341,42 +388,6 @@ function mergeRanges(ranges) {
341
388
  return out;
342
389
  }
343
390
 
344
- // src/combinators/trivia-boundary.ts
345
- var MAX_DEPTH = 12;
346
- function hasOwnTriviaBoundary(p, depth = MAX_DEPTH) {
347
- if (depth === 0) return true;
348
- const d = p._def;
349
- switch (d.tag) {
350
- // Multi-term forms: the gaps BETWEEN terms are the ambient-trivia positions.
351
- case "sequence":
352
- return d.parsers.length > 1;
353
- case "many":
354
- case "oneOrMore":
355
- case "sepBy":
356
- return true;
357
- // Transparent wrappers — they add no position, so ask what they wrap.
358
- case "node":
359
- case "transform":
360
- case "leaf":
361
- case "label":
362
- case "field":
363
- case "token":
364
- case "attempt":
365
- case "optional":
366
- case "expect":
367
- case "withCtx":
368
- case "recover":
369
- case "trivia":
370
- return hasOwnTriviaBoundary(d.parser, depth - 1);
371
- // An alternation adds no position either, but ONE arm that has one is enough
372
- // to make the scope meaningful for this rule.
373
- case "choice":
374
- return d.parsers.some((a) => hasOwnTriviaBoundary(a, depth - 1));
375
- default:
376
- return false;
377
- }
378
- }
379
-
380
391
  // src/combinators/scalar.ts
381
392
  var SCALAR_TAG = /^(?:literal|regex|keywords|sequence|choice|many|oneOrMore|optional|transform|trivia|grammar|sepBy|lazy)$/;
382
393
  function scalarOf(combinator) {
@@ -2233,10 +2244,12 @@ function parseRegex(src, unicode = false, sets = false) {
2233
2244
  const parseGroup = () => {
2234
2245
  i++;
2235
2246
  let lookaround = false;
2247
+ let lookahead = false;
2236
2248
  if (src[i] === "?") {
2237
2249
  const c1 = src[i + 1];
2238
2250
  if (c1 === ":") i += 2;
2239
2251
  else if (c1 === "=" || c1 === "!") {
2252
+ lookahead = c1 === "=";
2240
2253
  i += 2;
2241
2254
  lookaround = true;
2242
2255
  } else if (c1 === "<") {
@@ -2254,7 +2267,8 @@ function parseRegex(src, unicode = false, sets = false) {
2254
2267
  const inner = parseAlt();
2255
2268
  if (src[i] !== ")") throw BAIL;
2256
2269
  i++;
2257
- return lookaround ? EMPTY2 : inner;
2270
+ if (lookaround) return lookahead ? { t: "empty", look: inner } : EMPTY2;
2271
+ return inner;
2258
2272
  };
2259
2273
  const parseClass = () => {
2260
2274
  let k = i + 1;
@@ -2447,6 +2461,13 @@ function regexCanMatchEmpty(source) {
2447
2461
  return true;
2448
2462
  }
2449
2463
  }
2464
+ function consumingFirstSetFromRegex(source, flags = "") {
2465
+ try {
2466
+ return firstSet(parseRegex(source, flags.includes("u") || flags.includes("v"), flags.includes("v")));
2467
+ } catch {
2468
+ return any();
2469
+ }
2470
+ }
2450
2471
  function firstSetFromRegex(source, flags = "") {
2451
2472
  let ast2;
2452
2473
  try {
@@ -2457,6 +2478,20 @@ function firstSetFromRegex(source, flags = "") {
2457
2478
  if (nullable(ast2)) return { firstSet: any(), canMatchNewline: canConsumeNewline(ast2) };
2458
2479
  return { firstSet: firstSet(ast2), canMatchNewline: canConsumeNewline(ast2) };
2459
2480
  }
2481
+ function regexFirstTwo(source, flags = "") {
2482
+ let ast2;
2483
+ try {
2484
+ ast2 = parseRegex(source, flags.includes("u") || flags.includes("v"), flags.includes("v"));
2485
+ } catch {
2486
+ return void 0;
2487
+ }
2488
+ const parts = ast2.t === "seq" ? ast2.parts : [ast2];
2489
+ const a = parts[0];
2490
+ const b = parts[1];
2491
+ if (a === void 0 || a.t !== "char" || a.set.kind !== "ranges" || b === void 0) return void 0;
2492
+ const second = b.t === "char" ? b.set : b.t === "empty" && b.look !== void 0 && !nullable(b.look) ? firstSet(b.look) : void 0;
2493
+ return second !== void 0 && second.kind === "ranges" ? { first: a.set, second } : void 0;
2494
+ }
2460
2495
 
2461
2496
  // src/combinators/regex.ts
2462
2497
  import { PlainLeaf as PlainLeaf3, PlainSpan as PlainSpan3 } from "../cst/plain.js";
@@ -2537,9 +2572,14 @@ function regex(pattern, flags = "") {
2537
2572
  const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
2538
2573
  const scan = shortScanner(source, resolvedFlags);
2539
2574
  const raw = firstSetFromRegex(source, resolvedFlags);
2540
- const firstSet2 = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
2575
+ const forFlags = (fs3) => !resolvedFlags.includes("i") ? fs3 : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(fs3);
2576
+ const firstSet2 = forFlags(raw.firstSet);
2541
2577
  const canMatchNewline = raw.canMatchNewline;
2542
2578
  const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
2579
+ if (firstSet2.kind === "any" && regexCanMatchEmpty(source)) {
2580
+ const consuming = forFlags(consumingFirstSetFromRegex(source, resolvedFlags));
2581
+ if (consuming.kind !== "any") meta.consumingFirstSet = consuming;
2582
+ }
2543
2583
  const def = { tag: "regex", source, flags: resolvedFlags };
2544
2584
  const expected = directTerminalFailureExpected(def);
2545
2585
  const parseScalar = (input, pos, ctx) => {
@@ -2922,10 +2962,6 @@ function scanTriviaCompact(input, cur, ctx) {
2922
2962
  const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
2923
2963
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
2924
2964
  }
2925
- function scanTrivia(input, cur, ctx) {
2926
- const scan = scanTriviaCompact(input, cur, ctx);
2927
- return typeof scan === "number" ? { end: scan, commit: NOOP_COMMIT } : scan;
2928
- }
2929
2965
  function skipTriviaScanned(s, input, cur, ctx) {
2930
2966
  const end = s(input, cur);
2931
2967
  if (end !== cur) {
@@ -3552,7 +3588,7 @@ function sequence(...parsers) {
3552
3588
  if (tolerant) {
3553
3589
  followSentinels ??= parsers.map((_, i) => firstSetSentinel(
3554
3590
  parsers.slice(i + 1).reduce(
3555
- (set, parser2) => union(set, firstSetOf(parser2)),
3591
+ (set, parser2) => union(set, firstSetOf(parser2, /* @__PURE__ */ new Set(), void 0, void 0, false)),
3556
3592
  { kind: "empty" }
3557
3593
  )
3558
3594
  ));
@@ -4169,8 +4205,9 @@ function when(key, parser2, opts = {}) {
4169
4205
  }
4170
4206
  return { kind: "when", keys, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
4171
4207
  }
4172
- function otherwise(parser2) {
4173
- return { kind: "otherwise", parser: parser2, usesRouted: parserUsesRouted(parser2) };
4208
+ function otherwise(parser2, opts = {}) {
4209
+ const usesRouted = parserUsesRouted(parser2);
4210
+ return opts.commit === false ? { kind: "otherwise", parser: parser2, usesRouted, commit: false } : { kind: "otherwise", parser: parser2, usesRouted };
4174
4211
  }
4175
4212
  function routed(fallback2) {
4176
4213
  return {
@@ -4202,6 +4239,7 @@ function dispatch(selector, ...arms) {
4202
4239
  }
4203
4240
  let fallback2;
4204
4241
  let fallbackUsesRouted = false;
4242
+ let fallbackCommits = true;
4205
4243
  const matchers = [];
4206
4244
  const cases = [];
4207
4245
  const seen = [];
@@ -4212,6 +4250,7 @@ function dispatch(selector, ...arms) {
4212
4250
  if (i !== arms.length - 1) throw new Error("parseman: otherwise() must be the last dispatch() arm");
4213
4251
  fallback2 = arm.parser;
4214
4252
  fallbackUsesRouted = arm.usesRouted === true;
4253
+ fallbackCommits = arm.commit !== false;
4215
4254
  continue;
4216
4255
  }
4217
4256
  if (arm.kind === "whenMatcher") {
@@ -4262,7 +4301,11 @@ function dispatch(selector, ...arms) {
4262
4301
  selector,
4263
4302
  cases,
4264
4303
  ...matchers.length === 0 ? {} : { matchers },
4265
- ...fallback2 === void 0 ? {} : { otherwise: fallback2, ...fallbackUsesRouted ? { otherwiseUsesRouted: true } : {} }
4304
+ ...fallback2 === void 0 ? {} : {
4305
+ otherwise: fallback2,
4306
+ ...fallbackUsesRouted ? { otherwiseUsesRouted: true } : {},
4307
+ ...fallbackCommits ? {} : { otherwiseCommit: false }
4308
+ }
4266
4309
  },
4267
4310
  parse(input, pos, ctx) {
4268
4311
  if (parserUsesRouted(selector)) {
@@ -4271,7 +4314,8 @@ function dispatch(selector, ...arms) {
4271
4314
  const selectorMark = saveTriviaMark(ctx);
4272
4315
  const selected = selector.parse(input, pos, ctx);
4273
4316
  if (!selected.ok) return selected;
4274
- const branch = byKey.get(selected.value) ?? (byFoldedKey.size === 0 ? void 0 : byFoldedKey.get(asciiFoldKey(selected.value))) ?? matchers.find((matcher) => matchesDispatchMatcher(selected.value, matcher)) ?? (fallback2 === void 0 ? void 0 : { keys: [], parser: fallback2, caseInsensitive: false, usesRouted: fallbackUsesRouted });
4317
+ const chosen = byKey.get(selected.value) ?? (byFoldedKey.size === 0 ? void 0 : byFoldedKey.get(asciiFoldKey(selected.value))) ?? matchers.find((matcher) => matchesDispatchMatcher(selected.value, matcher));
4318
+ const branch = chosen ?? (fallback2 === void 0 ? void 0 : { keys: [], parser: fallback2, caseInsensitive: false, usesRouted: fallbackUsesRouted });
4275
4319
  if (branch === void 0) {
4276
4320
  const expected = cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key)));
4277
4321
  return { ok: false, expected, span: { start: selected.span.end, end: selected.span.end } };
@@ -4292,7 +4336,7 @@ function dispatch(selector, ...arms) {
4292
4336
  }
4293
4337
  if (!result.ok) {
4294
4338
  rollbackTrivia(ctx, mark);
4295
- return { ...result, committed: true };
4339
+ return chosen === void 0 && !fallbackCommits ? result : { ...result, committed: true };
4296
4340
  }
4297
4341
  return {
4298
4342
  ok: true,
@@ -4321,7 +4365,7 @@ function attempt(parser2, options) {
4321
4365
  return {
4322
4366
  _tag: "attempt",
4323
4367
  _meta: parser2._meta,
4324
- _def: contain ? { tag: "attempt", parser: parser2, contain: true } : { tag: "attempt", parser: parser2 },
4368
+ _def: { tag: "attempt", parser: parser2, ...contain ? { contain: true } : {}, ...guardable ? { guarded: true } : {} },
4325
4369
  parse(input, pos, ctx) {
4326
4370
  if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(parser2, input, pos)) {
4327
4371
  if (failExpected === void 0) {
@@ -4411,9 +4455,9 @@ function many(combinator, opts = {}) {
4411
4455
  canMatchNewline: combinator._meta.canMatchNewline,
4412
4456
  isTrivia: false
4413
4457
  };
4414
- const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max } };
4415
- let expected;
4416
4458
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
4459
+ const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max }, ...guardable ? { guarded: true } : {} };
4460
+ let expected;
4417
4461
  const scalarChild = scalarOf(combinator);
4418
4462
  const parseScalar = (input, pos, ctx) => repeatScalarTail(combinator, scalarChild, input, pos, ctx, guardable, def.valueUnused ? void 0 : [], max);
4419
4463
  return {
@@ -4460,9 +4504,9 @@ function atLeast(combinator, min, max) {
4460
4504
  canMatchNewline: combinator._meta.canMatchNewline,
4461
4505
  isTrivia: false
4462
4506
  };
4463
- const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max } };
4464
- let expected;
4465
4507
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
4508
+ const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max }, ...guardable ? { guarded: true } : {} };
4509
+ let expected;
4466
4510
  const scalarChild = scalarOf(combinator);
4467
4511
  const parseScalar = (input, pos, ctx) => {
4468
4512
  let cur = scalarChild(input, pos, ctx);
@@ -5181,7 +5225,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
5181
5225
  if (project !== void 0 && build !== void 0) {
5182
5226
  throw new Error("node() options cannot combine project with a build callback");
5183
5227
  }
5184
- const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build } };
5228
+ const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build }, ...combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator) ? { guarded: true } : {} };
5185
5229
  const meta = {
5186
5230
  firstSet: combinator._meta.firstSet,
5187
5231
  canMatchNewline: combinator._meta.canMatchNewline,
@@ -5740,14 +5784,15 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
5740
5784
  return end < 0 ? next(input, pos) : end;
5741
5785
  };
5742
5786
  }
5743
- let tail = (_input, pos, ctx, _lead, acc) => {
5787
+ let tail = (input, pos, ctx, _lead, acc, _best, entered) => {
5788
+ if (!entered && ctx._probe !== void 0) for (const arm of arms) arm.run(input, pos, ctx);
5744
5789
  ctx._fe = pos;
5745
5790
  ctx._fx = acc ?? choiceExpected;
5746
5791
  return -1;
5747
5792
  };
5748
5793
  for (let i = arms.length - 1; i >= 0; i--) {
5749
5794
  const armRun = arms[i].run, cls = classes[i], staticExpected = armExpected[i], next = tail;
5750
- tail = (input, pos, ctx, lead2, acc, best) => {
5795
+ tail = (input, pos, ctx, lead2, acc, best, entered) => {
5751
5796
  if (cls !== void 0 && !classHas(cls, lead2)) {
5752
5797
  return next(
5753
5798
  input,
@@ -5755,7 +5800,8 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
5755
5800
  ctx,
5756
5801
  lead2,
5757
5802
  best === pos ? appendExpected(acc, staticExpected) : acc,
5758
- best
5803
+ best,
5804
+ entered
5759
5805
  );
5760
5806
  }
5761
5807
  ctx._fc = false;
@@ -5771,7 +5817,7 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
5771
5817
  if (nextAcc !== void 0) ctx._fx = nextAcc;
5772
5818
  return -1;
5773
5819
  }
5774
- return next(input, pos, ctx, lead2, nextAcc, best);
5820
+ return next(input, pos, ctx, lead2, nextAcc, best, true);
5775
5821
  };
5776
5822
  }
5777
5823
  return {
@@ -5782,7 +5828,8 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
5782
5828
  ctx,
5783
5829
  pos < input.length ? input.codePointAt(pos) : -1,
5784
5830
  void 0,
5785
- pos
5831
+ pos,
5832
+ false
5786
5833
  )
5787
5834
  };
5788
5835
  }
@@ -5820,6 +5867,7 @@ function exclusiveChoiceNode(arms, classes, choiceFx) {
5820
5867
  return -1;
5821
5868
  }
5822
5869
  }
5870
+ if (arm < 0 && ctx._probe !== void 0) for (const a of arms) a.run(input, pos, ctx);
5823
5871
  ctx._fe = pos;
5824
5872
  ctx._fx = choiceFx;
5825
5873
  return -1;
@@ -6044,6 +6092,7 @@ function buildLexProgramRunner(spec, matchers, classes, expected) {
6044
6092
  if (!classHas(cls, lead2)) {
6045
6093
  ctx._fe = pos;
6046
6094
  ctx._fx = choiceFx.slice();
6095
+ if (ctx._probe !== void 0) failAt(ctx, [...x0, ...x1, ...x2, ...x3], pos);
6047
6096
  return -1;
6048
6097
  }
6049
6098
  let acc;
@@ -6155,7 +6204,7 @@ function validateDispatchSpec(spec, arity, fallbackRouted) {
6155
6204
  for (const arm of arms) if (!Number.isInteger(arm) || arm < 0 || arm >= arity) return true;
6156
6205
  return false;
6157
6206
  };
6158
- if (spec === void 0 || !Number.isInteger(arity) || arity < 0 || fallbackRouted !== 0 && fallbackRouted !== 1 || spec.routed.length !== arity || spec.routed.some((value) => value !== 0 && value !== 1) || invalid(spec.byKey.values()) || invalid(spec.byFold.values()) || spec.match.some((matcher) => invalid([matcher[3]]))) {
6207
+ if (spec === void 0 || !Number.isInteger(arity) || arity < 0 || !Number.isInteger(fallbackRouted) || fallbackRouted < 0 || fallbackRouted > 3 || spec.routed.length !== arity || spec.routed.some((value) => value !== 0 && value !== 1) || invalid(spec.byKey.values()) || invalid(spec.byFold.values()) || spec.match.some((matcher) => invalid([matcher[3]]))) {
6159
6208
  throw new TypeError("table: malformed dispatch");
6160
6209
  }
6161
6210
  }
@@ -6851,6 +6900,7 @@ function classifyRuleMap(ruleMap) {
6851
6900
  }
6852
6901
 
6853
6902
  // src/table/factor-choice.ts
6903
+ var factoredChoices = /* @__PURE__ */ new WeakSet();
6854
6904
  var DERIVED = /* @__PURE__ */ new Set(["_tag", "_meta", "_def", "_parseScalar", "_stickyRegex", "parse"]);
6855
6905
  function isCombinator(v) {
6856
6906
  return typeof v === "object" && v !== null && "_def" in v && "_meta" in v;
@@ -6942,6 +6992,7 @@ function factorSharedEnds(d) {
6942
6992
  for (let i = at + 1; i < n; i++) if (!never(first.terms[i])) return void 0;
6943
6993
  if (arms.some((a) => mayCommitFailure(a))) return void 0;
6944
6994
  const middle = choice(...shapes.map((s) => s.terms[at]));
6995
+ factoredChoices.add(middle);
6945
6996
  if (middle._def.tag === "choice" && middle._def.autoNot.some((a) => a !== null)) return void 0;
6946
6997
  const st = middle._def.tag === "choice" ? middle._def.strategy : void 0;
6947
6998
  if (st === void 0 || st.tag !== "firstMatch" && st.tag !== "sharedPrefix" && !(st.tag === "literalsLongestFirst" && st.sortedIndices.every((x, i) => x === i))) return void 0;
@@ -6954,6 +7005,37 @@ function factorSharedEnds(d) {
6954
7005
  for (let j = first.wrappers.length - 1; j >= 0 && out !== void 0; j--) out = rewrap(first.wrappers[j], out);
6955
7006
  return out === void 0 ? void 0 : attempt(out);
6956
7007
  }
7008
+ function factorPrefixArms(d) {
7009
+ const arms = d.parsers;
7010
+ if (arms.length < 2) return void 0;
7011
+ if (d.strategy.tag !== "firstMatch" && d.strategy.tag !== "sharedPrefix") return void 0;
7012
+ if (d.gates.some((g) => g !== null) || d.autoNot.some((a) => a !== null)) return void 0;
7013
+ const out = [...arms];
7014
+ let changed = false;
7015
+ for (let i = 0; i + 1 < arms.length; i++) {
7016
+ const arm = arms[i];
7017
+ if (arm._def.tag !== "sequence" || arm._def.valueUnused !== true) continue;
7018
+ const terms = arm._def.parsers;
7019
+ const next = arms[i + 1];
7020
+ const prefix = next._def.tag === "sequence" && next._def.valueUnused === true ? next._def.parsers : [next];
7021
+ if (prefix.length >= terms.length) continue;
7022
+ if (!prefix.every((t, j) => sameComb(t, terms[j], 0))) continue;
7023
+ const rest = terms.slice(prefix.length);
7024
+ if (rest.every(never)) continue;
7025
+ const tail = rest.length === 1 ? rest[0] : sequence(...rest);
7026
+ if (tail !== rest[0]) tail._def.valueUnused = true;
7027
+ const parts = [...terms.slice(0, prefix.length), optional(tail)];
7028
+ const seq = sequence(...parts);
7029
+ seq._def.valueUnused = true;
7030
+ out[i] = seq;
7031
+ changed = true;
7032
+ }
7033
+ if (!changed) return void 0;
7034
+ const rebuilt = choice(...out);
7035
+ factoredChoices.add(rebuilt);
7036
+ const st = rebuilt._def.tag === "choice" ? rebuilt._def.strategy : void 0;
7037
+ return st !== void 0 && (st.tag === "firstMatch" || st.tag === "sharedPrefix") ? rebuilt : void 0;
7038
+ }
6957
7039
 
6958
7040
  // src/table/ops.ts
6959
7041
  var OP_LIT = 1;
@@ -6999,6 +7081,9 @@ var OP_LEX_BODY = 42;
6999
7081
  var OP_LEX_PROGRAM = 43;
7000
7082
  var OP_SOURCE_LEAF = 44;
7001
7083
  var OP_RGATE = 45;
7084
+ var OP_GAPGATE = 46;
7085
+ var OP_RGATE2 = 47;
7086
+ var OP_STRICT = 48;
7002
7087
  var OP_DISPATCH = 25;
7003
7088
  var OP_ROUTED = 26;
7004
7089
  var OP_NAMES = {
@@ -7046,7 +7131,10 @@ var OP_NAMES = {
7046
7131
  [OP_COV]: "COV",
7047
7132
  [OP_SCOPE_PLAIN]: "SCOPE_PLAIN",
7048
7133
  [OP_LEX_BODY]: "LEX_BODY",
7049
- [OP_LEX_PROGRAM]: "LEX_PROGRAM"
7134
+ [OP_LEX_PROGRAM]: "LEX_PROGRAM",
7135
+ [OP_GAPGATE]: "GAPGATE",
7136
+ [OP_RGATE2]: "RGATE2",
7137
+ [OP_STRICT]: "STRICT"
7050
7138
  };
7051
7139
 
7052
7140
  // src/compiler/direct-projection.ts
@@ -7337,6 +7425,7 @@ function directFixedLexNode(parser2, resolve3, active, pureOnly = false, cleared
7337
7425
  arms,
7338
7426
  parsers: def.parsers,
7339
7427
  cleared,
7428
+ disjoint: def.disjoint,
7340
7429
  armExpected: def.parsers.map(deriveExpected),
7341
7430
  expected: def.parsers.flatMap(deriveExpected)
7342
7431
  };
@@ -7446,6 +7535,29 @@ var UnsupportedConstruct = class extends Error {
7446
7535
  this.tag = tag;
7447
7536
  }
7448
7537
  };
7538
+ function firstTwo(p) {
7539
+ const d = p._def;
7540
+ if (d.tag === "label" || d.tag === "grammar") return firstTwo(d.parser);
7541
+ let two;
7542
+ if (d.tag === "literal" && d.value.length >= 2 && d.caseInsensitive !== true) {
7543
+ const second = d.value.codePointAt(1);
7544
+ two = { first: { kind: "ranges", ranges: [{ lo: d.value.charCodeAt(0), hi: d.value.charCodeAt(0) }] }, second: { kind: "ranges", ranges: [{ lo: second, hi: second }] } };
7545
+ } else if (d.tag === "regex" && !d.flags.includes("i")) {
7546
+ const units = regexFirstTwo(d.source, d.flags);
7547
+ if (units !== void 0) two = units;
7548
+ }
7549
+ if (two === void 0 || two.first.kind !== "ranges") return void 0;
7550
+ return two.first.ranges.every((r) => r.hi < 55296 || r.lo > 57343 && r.hi <= 65535) ? two : void 0;
7551
+ }
7552
+ var GAP_CHARS = /* @__PURE__ */ new Set([9, 10, 12, 13, 32, 47]);
7553
+ function isGap(p) {
7554
+ if (matchesEmpty(p)) return true;
7555
+ const fs3 = p._meta.firstSet;
7556
+ return fs3.kind === "ranges" && fs3.ranges.every((r) => {
7557
+ for (let c = r.lo; c <= r.hi; c++) if (!GAP_CHARS.has(c)) return false;
7558
+ return true;
7559
+ });
7560
+ }
7449
7561
  var Encoder = class {
7450
7562
  code = [];
7451
7563
  k = [];
@@ -7493,8 +7605,33 @@ var Encoder = class {
7493
7605
  */
7494
7606
  commitFailureMemo = /* @__PURE__ */ new Map();
7495
7607
  labels = void 0;
7608
+ /** The choice being encoded as written, for `OP_STRICT`'s tolerant child. */
7609
+ unfactoredChoice = void 0;
7496
7610
  /** Trivia table in scope at the row being encoded — codegen's `ctx.activeTrivia`. */
7497
7611
  activeTrivia = void 0;
7612
+ /**
7613
+ * Is the trivia live at this row known? It is not in the body of an entry
7614
+ * that installs none of its own: that body runs under whatever trivia its
7615
+ * caller has (a compiled rule inside another grammar, `run({ trivia })`).
7616
+ * Every scope (`parser({ trivia })`, `noTrivia`, `token()`, a rule's install)
7617
+ * makes it known again.
7618
+ */
7619
+ triviaKnown = true;
7620
+ /**
7621
+ * The trivia a gate's first set is derived under: the live table, `null` for a
7622
+ * scope known to have none, or `undefined` where it is not known.
7623
+ */
7624
+ gateTrivia() {
7625
+ return this.activeTrivia ?? (this.triviaKnown ? null : void 0);
7626
+ }
7627
+ /**
7628
+ * Whether a gate may read a nullable pad through its non-empty matches
7629
+ * (`firstSetOf`'s `consuming`): only where the trivia that may follow the pad
7630
+ * is known. Elsewhere a gate reads first sets as 0.58.0 did.
7631
+ */
7632
+ gateConsuming() {
7633
+ return this.activeTrivia !== void 0 || this.triviaKnown;
7634
+ }
7498
7635
  classified = false;
7499
7636
  scans = [];
7500
7637
  /** Ambient `scanSkip` sets, pooled by the ARRAY's identity (stable per grammar). */
@@ -7621,10 +7758,12 @@ var Encoder = class {
7621
7758
  const final = classifyFinalChoice(
7622
7759
  node2.parsers,
7623
7760
  this.refResolver(),
7624
- node2.cleared ? void 0 : this.activeTrivia
7761
+ node2.cleared ? null : this.gateTrivia(),
7762
+ false,
7763
+ node2.disjoint
7625
7764
  );
7626
7765
  const classes = final.firstSets.map((first, i) => final.nullable[i] ? -1 : this.charClass(first));
7627
- const exclusive = final.exclusive && classes.every((cls) => cls >= 0);
7766
+ const exclusive = final.exclusive && classes.every((cls) => cls >= 0) && this.wideArms(node2.parsers, final.firstSets, node2.disjoint, node2.cleared ? null : this.gateTrivia(), node2.cleared ? void 0 : this.activeTrivia).length === 0;
7628
7767
  words.push(LEX_NODE_CHOICE, node2.arms.length, exclusive ? 1 : 0, this.expected(node2.expected));
7629
7768
  for (let i = 0; i < node2.arms.length; i++) {
7630
7769
  words.push(classes[i], this.expected(node2.armExpected[i]));
@@ -7779,16 +7918,117 @@ var Encoder = class {
7779
7918
  kIndex = /* @__PURE__ */ new Map();
7780
7919
  ccIndex = /* @__PURE__ */ new Map();
7781
7920
  fxIndex = /* @__PURE__ */ new Map();
7782
- /** Memoized by combinator identity: a shared sub-combinator is ONE row. */
7783
- memo = /* @__PURE__ */ new Map();
7784
7921
  /**
7785
- * Memo for `lazy` REFERENCES, keyed by the trivia scope the reference is made
7786
- * from. A cross-rule reference's row depends on that scope (`scopedRef`), so the
7787
- * single `memo` slot every other construct uses would freeze the FIRST
7788
- * reference's scope onto every later one and undo the fix.
7922
+ * Memoized by combinator identity AND the trivia scope it is encoded under
7923
+ * (`scopeKey`): a shared sub-combinator is ONE row per scope it runs in.
7924
+ *
7925
+ * A construct's rows depend on that scope. A first set derived through a
7926
+ * nullable term takes in the trivia that may follow it, a gap gate holds only
7927
+ * where no trivia runs, a routed gate reads what may follow its token, and a
7928
+ * cross-rule reference installs its rule's trivia only from a cleared region
7929
+ * (`scopedRef`). One memo slot per combinator froze the FIRST scope it was
7930
+ * reached under onto every later one: a rule body encoded as an entry with no
7931
+ * trivia, then run under a caller's comment trivia, gated its repeats as if no
7932
+ * comment could come between their terms.
7789
7933
  */
7790
- refMemo = /* @__PURE__ */ new Map();
7934
+ memo = /* @__PURE__ */ new Map();
7791
7935
  pending = /* @__PURE__ */ new Map();
7936
+ triviaSignatures = /* @__PURE__ */ new Map();
7937
+ /**
7938
+ * What of the live trivia an encoding can depend on: none, or its first set.
7939
+ * Every derivation reads trivia only through `firstSetOf(trivia)` or whether it
7940
+ * is live, so two scopes whose trivia opens on the same characters encode the
7941
+ * same rows and share them. A construct that reads no scope (`readsScope`)
7942
+ * has one encoding.
7943
+ */
7944
+ scopeKey(p) {
7945
+ const t = this.activeTrivia;
7946
+ if (!this.readsScope(p)) return "";
7947
+ if (t === void 0) return this.triviaKnown ? "" : "?";
7948
+ let sig = this.triviaSignatures.get(t);
7949
+ if (sig === void 0) {
7950
+ sig = `t${JSON.stringify(firstSetOf(t, /* @__PURE__ */ new Set(), this.refResolver()))}`;
7951
+ this.triviaSignatures.set(t, sig);
7952
+ }
7953
+ return sig;
7954
+ }
7955
+ readsScopeMemo = /* @__PURE__ */ new Map();
7956
+ /**
7957
+ * Can `p`'s rows depend on the trivia scope it is encoded under? No for a
7958
+ * terminal, a scope of its own (`parser({ trivia })`, `noTrivia`, `token()`),
7959
+ * and a wrapper over a construct that reads none; yes wherever a first set, a gate or
7960
+ * a reference's install is derived with the scope: a sequence with a nullable
7961
+ * term before another (the trivia between them), a routed arm (the trivia after
7962
+ * its token), a reference that installs its rule's trivia from a cleared
7963
+ * region, and anything not modelled here — a cycle included.
7964
+ */
7965
+ readsScope(p) {
7966
+ const hit = this.readsScopeMemo.get(p);
7967
+ if (hit !== void 0) return hit;
7968
+ this.readsScopeMemo.set(p, true);
7969
+ const d = p._def;
7970
+ const rr = this.refResolver();
7971
+ let out;
7972
+ switch (d.tag) {
7973
+ case "literal":
7974
+ case "regex":
7975
+ case "keywords":
7976
+ case "token":
7977
+ out = false;
7978
+ break;
7979
+ // `routed()` encodes its fallback under the scope that reaches it.
7980
+ case "routed":
7981
+ out = d.fallback !== void 0 && this.readsScope(d.fallback);
7982
+ break;
7983
+ case "grammar":
7984
+ out = d.clearTrivia !== true && d.triviaParser === void 0 && this.readsScope(d.parser);
7985
+ break;
7986
+ case "node":
7987
+ case "label":
7988
+ case "leaf":
7989
+ case "transform":
7990
+ case "attempt":
7991
+ case "expect":
7992
+ case "not":
7993
+ case "peek":
7994
+ case "optional":
7995
+ case "many":
7996
+ case "oneOrMore":
7997
+ case "field":
7998
+ out = this.readsScope(d.parser);
7999
+ break;
8000
+ case "sepBy":
8001
+ out = this.readsScope(d.parser) || this.readsScope(d.separator);
8002
+ break;
8003
+ case "choice":
8004
+ out = d.parsers.some((a) => this.readsScope(a));
8005
+ break;
8006
+ case "dispatch":
8007
+ out = this.readsScope(d.selector) || d.cases.some((c) => this.readsScope(c.parser)) || (d.matchers?.some((m) => this.readsScope(m.parser)) ?? false) || d.otherwise !== void 0 && this.readsScope(d.otherwise);
8008
+ break;
8009
+ case "sequence": {
8010
+ const ps = d.parsers;
8011
+ out = ps.some((t) => this.readsScope(t)) || ps[0] !== void 0 && (ps[0]._def.tag === "routed" || ps[0]._def.tag === "regex" && isGap(ps[0])) || ps.some((t, i) => i + 1 < ps.length && matchesEmpty(t, /* @__PURE__ */ new Set(), rr));
8012
+ break;
8013
+ }
8014
+ case "lazy": {
8015
+ let target = winnerForRef(p, rr);
8016
+ if (target === void 0) {
8017
+ try {
8018
+ target = d.thunk();
8019
+ } catch {
8020
+ target = void 0;
8021
+ }
8022
+ }
8023
+ out = target === void 0 || triviaThroughRef(p, target, null) !== null || this.readsScope(target);
8024
+ break;
8025
+ }
8026
+ default:
8027
+ out = true;
8028
+ }
8029
+ this.readsScopeMemo.set(p, out);
8030
+ return out;
8031
+ }
7792
8032
  settings;
7793
8033
  /** Resolved once, HERE, at table-build time — never consulted at run time. */
7794
8034
  track;
@@ -7816,7 +8056,7 @@ var Encoder = class {
7816
8056
  let fs3 = { kind: "empty" };
7817
8057
  const rr = this.refResolver();
7818
8058
  for (let j = i + 1; j < parsers.length; j++) {
7819
- fs3 = union(fs3, firstSetOf(parsers[j], /* @__PURE__ */ new Set(), rr, this.activeTrivia));
8059
+ fs3 = union(fs3, firstSetOf(parsers[j], /* @__PURE__ */ new Set(), rr, void 0, false));
7820
8060
  }
7821
8061
  return this.charClass(fs3);
7822
8062
  }
@@ -7894,7 +8134,7 @@ var Encoder = class {
7894
8134
  * simply parses it with the gate switched off at every choice arm that reaches
7895
8135
  * a hole.
7896
8136
  *
7897
- * Measured on jess's less grammar (`bench/jess/macro-program-diff.ts`): 195 of
8137
+ * Measured on jess's less grammar (with the one-off `macro-program-diff` bench, last at `4a5d0341`): 195 of
7898
8138
  * 562 reachable choice arms carried NO first set in the shipped artifact
7899
8139
  * against 103 of 540 in the runtime encode, and 10 dispatch sites lost the O(1)
7900
8140
  * `exclusive` piece (`assemble.ts:1758`) for the candidate-mask loop. With this
@@ -7984,7 +8224,7 @@ var Encoder = class {
7984
8224
  if (op === OP_GATE || op === OP_LIT || op === OP_LIT_CI || op === OP_LIT_TRACK || op === OP_LIT_CI_TRACK || op === OP_RX || op === OP_RX_TRACK) return child;
7985
8225
  const rr = this.refResolver();
7986
8226
  if (matchesEmpty(p, /* @__PURE__ */ new Set(), rr)) return child;
7987
- const cls = this.charClass(firstSetOf(p, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
8227
+ const cls = this.charClass(firstSetOf(p, /* @__PURE__ */ new Set(), rr, this.activeTrivia, false));
7988
8228
  if (cls < 0) return child;
7989
8229
  const e = deriveExpected(p);
7990
8230
  return this.emit(OP_GATE, cls, child, this.expected(e.length > 0 ? e : [p._tag]));
@@ -8010,9 +8250,27 @@ var Encoder = class {
8010
8250
  * `{`, `Call` at `}`). The resolved set is the one a choice arm's gate uses,
8011
8251
  * over the final merged map.
8012
8252
  */
8013
- gateFirstSet(p) {
8253
+ /**
8254
+ * The arms whose first set under `gateTrivia()` is wider than under the
8255
+ * active trivia alone — a reference's installed trivia read through from a
8256
+ * region with none. Empty for construction-set (disjoint) choices, whose
8257
+ * sets do not depend on trivia.
8258
+ */
8259
+ wideArms(arms, wide, constructionSets, wideTrivia = this.gateTrivia(), base = this.activeTrivia) {
8260
+ if (constructionSets || wideTrivia === base) return [];
8261
+ const narrow = classifyFinalChoice(arms, this.refResolver(), base, false, false).firstSets;
8262
+ const out = [];
8263
+ for (let i = 0; i < arms.length; i++) {
8264
+ if (this.charClass(wide[i]) !== this.charClass(narrow[i])) out.push(i);
8265
+ }
8266
+ return out;
8267
+ }
8268
+ gateFirstSet(p, guarded, narrowed) {
8014
8269
  const cached = p._meta.firstSet;
8015
- return p._def.tag === "lazy" || cached.kind === "any" ? firstSetOf(p, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : cached;
8270
+ if (p._def.tag !== "lazy" && cached.kind !== "any") return cached;
8271
+ const rr = this.refResolver();
8272
+ if (guarded) return firstSetOf(p, /* @__PURE__ */ new Set(), rr, void 0, false);
8273
+ return firstSetOf(p, /* @__PURE__ */ new Set(), rr, this.gateTrivia(), narrowed && this.gateConsuming());
8016
8274
  }
8017
8275
  /**
8018
8276
  * Wrap one choice arm in its `autoNot` checks (`OP_REJECT`).
@@ -8044,6 +8302,135 @@ var Encoder = class {
8044
8302
  for (const w of words) this.code.push(w);
8045
8303
  return ip;
8046
8304
  }
8305
+ /**
8306
+ * An optional repeat item tests the item's class before it runs it (`OP_REP`
8307
+ * ip+7), wherever the item's own first-set gate would also run. So a sequence
8308
+ * item whose first term is a gate on that same class gets its own copy of the
8309
+ * sequence row with the gate's child as that term: the test runs once. The
8310
+ * shared row is left as it is for its other references.
8311
+ */
8312
+ withoutLeadGate(seq, cls) {
8313
+ const op = this.code[seq];
8314
+ if (op !== OP_SEQ && op !== OP_SEQV) return seq;
8315
+ const first = this.code[seq + 2];
8316
+ if (this.code[first] !== OP_GATE || this.code[first + 1] !== cls) return seq;
8317
+ const n = this.code[seq + 1];
8318
+ const width = 1 + 2 * n;
8319
+ const head = this.emitHead(op, width);
8320
+ for (let i = 1; i <= width; i++) this.code[head + i] = this.code[seq + i];
8321
+ this.code[head + 2] = this.code[first + 2];
8322
+ return head;
8323
+ }
8324
+ /**
8325
+ * An optional repeat item that opens with a GAP — a regex that may match
8326
+ * empty, or one that starts only with whitespace or a comment's `/` — and
8327
+ * then a term with a finite first set gets an `OP_GAPGATE` row
8328
+ * (`gap-gate.ts`): the repeat reads the gap once, ends where the character
8329
+ * after it opens neither that term nor an arm without the gap, and hands the
8330
+ * gap's end to the regex row. Anything the walk cannot prove keeps the item as
8331
+ * written.
8332
+ */
8333
+ gapGated(item, ip) {
8334
+ if (this.activeTrivia === void 0 && !this.triviaKnown) return ip;
8335
+ const lead2 = this.gapLead(item, this.activeTrivia, 0);
8336
+ if (lead2 === void 0) return ip;
8337
+ const cls = this.charClass(lead2.follow);
8338
+ if (cls < 0) return ip;
8339
+ const other = lead2.other.kind === "empty" ? -1 : this.charClass(lead2.other);
8340
+ if (lead2.other.kind !== "empty" && other < 0) return ip;
8341
+ const p = this.node(lead2.p).ip;
8342
+ if (this.code[p] !== OP_RX && this.code[p] !== OP_RX_TRACK) return ip;
8343
+ return this.emit(OP_GAPGATE, ip, p, cls, other);
8344
+ }
8345
+ /**
8346
+ * The gap an item opens with: the regex `p`, the first set `follow` of what
8347
+ * must come straight after it, and the first set `other` of choice arms that
8348
+ * do not open with `p`. Only through wrappers that consume nothing before
8349
+ * their body, a sequence's first term, and a choice's arms; the gap must sit
8350
+ * in a sequence with no trivia between its terms.
8351
+ */
8352
+ gapLead(c, trivia2, depth) {
8353
+ if (depth > 24) return void 0;
8354
+ const rr = this.refResolver();
8355
+ const d = c._def;
8356
+ switch (d.tag) {
8357
+ case "lazy": {
8358
+ let target = winnerForRef(c, rr);
8359
+ if (target === void 0) {
8360
+ try {
8361
+ target = d.thunk();
8362
+ } catch {
8363
+ return void 0;
8364
+ }
8365
+ }
8366
+ return this.gapLead(target, triviaThroughRef(c, target, trivia2), depth + 1);
8367
+ }
8368
+ case "node":
8369
+ case "leaf":
8370
+ case "label":
8371
+ case "attempt":
8372
+ return this.gapLead(d.parser, trivia2, depth + 1);
8373
+ case "field":
8374
+ return d.leadingTrivia === true ? void 0 : this.gapLead(d.parser, trivia2, depth + 1);
8375
+ case "grammar":
8376
+ return this.gapLead(d.parser, d.clearTrivia === true ? null : d.triviaParser ?? trivia2, depth + 1);
8377
+ case "sequence": {
8378
+ const [first, ...rest] = d.parsers;
8379
+ if (first === void 0) return void 0;
8380
+ if (first._def.tag !== "regex" || !isGap(first)) {
8381
+ return this.gapLead(first, trivia2, depth + 1);
8382
+ }
8383
+ if (trivia2 !== void 0 && trivia2 !== null || rest.length === 0) return void 0;
8384
+ let follow = empty();
8385
+ let closed = false;
8386
+ for (const t of rest) {
8387
+ const fs3 = firstSetOf(t, /* @__PURE__ */ new Set(), rr, null);
8388
+ if (fs3.kind === "any") return void 0;
8389
+ follow = union(follow, fs3);
8390
+ if (!matchesEmpty(t, /* @__PURE__ */ new Set(), rr)) {
8391
+ closed = true;
8392
+ break;
8393
+ }
8394
+ }
8395
+ return closed ? { p: first, follow, other: empty() } : void 0;
8396
+ }
8397
+ case "choice": {
8398
+ let p;
8399
+ let follow = empty();
8400
+ let other = empty();
8401
+ for (const arm of d.parsers) {
8402
+ const lead2 = this.gapLead(arm, trivia2, depth + 1);
8403
+ if (lead2 !== void 0 && (p === void 0 || lead2.p === p)) {
8404
+ p = lead2.p;
8405
+ follow = union(follow, lead2.follow);
8406
+ other = union(other, lead2.other);
8407
+ continue;
8408
+ }
8409
+ if (matchesEmpty(arm, /* @__PURE__ */ new Set(), rr)) return void 0;
8410
+ const fs3 = firstSetOf(arm, /* @__PURE__ */ new Set(), rr, trivia2 ?? null);
8411
+ if (fs3.kind === "any") return void 0;
8412
+ other = union(other, fs3);
8413
+ }
8414
+ return p === void 0 ? void 0 : { p, follow, other };
8415
+ }
8416
+ default:
8417
+ return void 0;
8418
+ }
8419
+ }
8420
+ /**
8421
+ * The class a strict list may end on — a character its separator `p` (row
8422
+ * `ip`) cannot start — without entering the separator, or −1.
8423
+ *
8424
+ * The test runs before every separator, to save the one that fails at the
8425
+ * list's end, so it is laid down only where entering the separator costs more
8426
+ * than the test: not on a terminal row, which is itself one character test.
8427
+ */
8428
+ sepGate(p, ip) {
8429
+ const op = this.code[ip];
8430
+ if (op === OP_LIT || op === OP_LIT_CI || op === OP_RX || op === OP_LIT_TRACK || op === OP_RX_TRACK || op === OP_LIT_CI_TRACK) return -1;
8431
+ if (matchesEmpty(p, /* @__PURE__ */ new Set(), this.refResolver())) return -1;
8432
+ return this.charClass(firstSetOf(p, /* @__PURE__ */ new Set(), this.refResolver(), this.gateTrivia(), this.gateConsuming()));
8433
+ }
8047
8434
  /** Reserve `n` operand slots to patch once children are laid out. */
8048
8435
  emitHead(op, n) {
8049
8436
  const ip = this.code.length;
@@ -8087,8 +8474,10 @@ var Encoder = class {
8087
8474
  }
8088
8475
  encodeRule(name, p) {
8089
8476
  this.activeTrivia = p._meta.grammarTrivia;
8477
+ this.triviaKnown = p._meta.grammarTrivia !== void 0;
8090
8478
  const body = this.node(p).ip;
8091
8479
  this.activeTrivia = void 0;
8480
+ this.triviaKnown = true;
8092
8481
  const gss = p._meta.grammarScanSkip;
8093
8482
  this.scanSkipOf.push(gss === void 0 ? -1 : this.scanSkipSlot(gss));
8094
8483
  const amb = p._meta.grammarTrivia;
@@ -8134,11 +8523,15 @@ var Encoder = class {
8134
8523
  * silently produced the ONE keyword `bc`.
8135
8524
  */
8136
8525
  scopedRef(p, target) {
8526
+ const runsUnder = triviaThroughRef(p, target, this.activeTrivia);
8527
+ if (this.activeTrivia !== void 0 || runsUnder === void 0 || runsUnder === null) return this.node(target).ip;
8528
+ const savedKnown = this.triviaKnown;
8529
+ this.activeTrivia = runsUnder;
8530
+ this.triviaKnown = true;
8137
8531
  const ip = this.node(target).ip;
8138
- const amb = p._meta.grammarTrivia ?? target._meta.grammarTrivia;
8139
- if (amb === void 0 || this.activeTrivia !== void 0) return ip;
8140
- if (!hasOwnTriviaBoundary(target)) return ip;
8141
- return this.emit(OP_SCOPE_PLAIN, this.triviaSlot(amb), ip);
8532
+ this.activeTrivia = void 0;
8533
+ this.triviaKnown = savedKnown;
8534
+ return this.emit(OP_SCOPE_PLAIN, this.triviaSlot(runsUnder), ip);
8142
8535
  }
8143
8536
  /**
8144
8537
  * The `OP_RGATE` operands of a choice arm that opens with `routed()`, or
@@ -8179,38 +8572,43 @@ var Encoder = class {
8179
8572
  if (matchesEmpty(next, /* @__PURE__ */ new Set(), rr) || mayCommitFailure(next, rr, this.commitFailureMemo)) return void 0;
8180
8573
  const nextDef = next._def;
8181
8574
  if (nextDef.tag !== "literal" && nextDef.tag !== "regex" && nextDef.tag !== "keywords" && nextDef.tag !== "label") return void 0;
8182
- const first = firstSetOf(next, /* @__PURE__ */ new Set(), rr, boundary);
8575
+ if (boundary === void 0 && !this.triviaKnown) return void 0;
8576
+ const first = firstSetOf(next, /* @__PURE__ */ new Set(), rr, boundary ?? null);
8183
8577
  const cls = this.charClass(boundary === void 0 ? first : union(first, firstSetOf(boundary, /* @__PURE__ */ new Set(), rr)));
8184
8578
  if (cls < 0) return void 0;
8185
- const miss = nextDef.tag === "label" ? deriveExpected(next) : directTerminalFailureExpected(nextDef);
8186
- return { cls, fx: this.expected(miss) };
8579
+ const fx = this.expected(nextDef.tag === "label" ? deriveExpected(next) : directTerminalFailureExpected(nextDef));
8580
+ const two = firstTwo(next);
8581
+ if (two === void 0) return { cls, fx };
8582
+ if (boundary !== void 0 && intersects(two.first, firstSetOf(boundary, /* @__PURE__ */ new Set(), rr))) return { cls, fx };
8583
+ const a = this.charClass(two.first);
8584
+ const b = this.charClass(two.second);
8585
+ return a < 0 || b < 0 ? { cls, fx } : { cls, fx, two: [a, b] };
8187
8586
  }
8188
8587
  node(p) {
8189
- const lazyRef = p._def.tag === "lazy";
8190
- const scopeKey = lazyRef ? this.activeTrivia : void 0;
8191
- const refMemo = this.refMemo;
8192
- const hit = lazyRef ? refMemo.get(p)?.get(scopeKey) : this.memo.get(p);
8588
+ const key = this.scopeKey(p);
8589
+ let byScope = this.memo.get(p);
8590
+ const hit = byScope?.get(key);
8193
8591
  if (hit !== void 0) return { ip: hit };
8194
- const inFlight = this.pending.get(p);
8592
+ let inFlightByScope = this.pending.get(p);
8593
+ const inFlight = inFlightByScope?.get(key);
8195
8594
  if (inFlight !== void 0) {
8196
8595
  const ip2 = this.emitHead(OP_RULE, 1);
8197
8596
  inFlight.push(ip2 + 1);
8198
8597
  return { ip: ip2 };
8199
8598
  }
8200
8599
  const patches = [];
8201
- this.pending.set(p, patches);
8600
+ if (inFlightByScope === void 0) {
8601
+ inFlightByScope = /* @__PURE__ */ new Map();
8602
+ this.pending.set(p, inFlightByScope);
8603
+ }
8604
+ inFlightByScope.set(key, patches);
8202
8605
  const ip = this.covWrap(this.encodeDef(p), this.plan?.rules.get(p), 0);
8203
- this.pending.delete(p);
8204
- if (lazyRef) {
8205
- let byScope = refMemo.get(p);
8206
- if (byScope === void 0) {
8207
- byScope = /* @__PURE__ */ new Map();
8208
- refMemo.set(p, byScope);
8209
- }
8210
- byScope.set(scopeKey, ip);
8211
- } else {
8212
- this.memo.set(p, ip);
8606
+ inFlightByScope.delete(key);
8607
+ if (byScope === void 0) {
8608
+ byScope = /* @__PURE__ */ new Map();
8609
+ this.memo.set(p, byScope);
8213
8610
  }
8611
+ byScope.set(key, ip);
8214
8612
  for (const slot of patches) {
8215
8613
  if (slot - 1 === ip) {
8216
8614
  const rule = p._ruleName;
@@ -8308,8 +8706,19 @@ var Encoder = class {
8308
8706
  }
8309
8707
  case "choice": {
8310
8708
  if (this.plan === void 0) {
8311
- const factored = factorSharedEnds(d);
8312
- if (factored !== void 0) return this.node(factored).ip;
8709
+ const shared = factorSharedEnds(d);
8710
+ if (shared !== void 0) return this.node(shared).ip;
8711
+ if (this.unfactoredChoice !== p) {
8712
+ const prefix = factorPrefixArms(d);
8713
+ if (prefix !== void 0) {
8714
+ const strict = this.node(prefix).ip;
8715
+ const saved = this.unfactoredChoice;
8716
+ this.unfactoredChoice = p;
8717
+ const tolerant = this.encodeDef(p);
8718
+ this.unfactoredChoice = saved;
8719
+ return this.emit(OP_STRICT, strict, tolerant);
8720
+ }
8721
+ }
8313
8722
  }
8314
8723
  if (d.strategy.tag === "greedyClassify") {
8315
8724
  const superIndex = d.strategy.superIndex;
@@ -8338,18 +8747,20 @@ var Encoder = class {
8338
8747
  const plain = (checks === null || checks === void 0 || checks.length === 0) && (gate2 === null || gate2 === void 0);
8339
8748
  const routedGate = plain ? this.routedArmGate(c) : void 0;
8340
8749
  const body = this.node(c).ip;
8341
- const inner = routedGate === void 0 ? body : this.emit(OP_RGATE, routedGate.cls, body, routedGate.fx);
8750
+ const inner = routedGate === void 0 ? body : routedGate.two === void 0 ? this.emit(OP_RGATE, routedGate.cls, body, routedGate.fx) : this.emit(OP_RGATE2, routedGate.cls, body, routedGate.fx, routedGate.two[0], routedGate.two[1]);
8342
8751
  const ip = checks === null || checks === void 0 || checks.length === 0 ? inner : this.reject(inner, checks);
8343
8752
  const gated = gate2 === null || gate2 === void 0 ? ip : this.emit(OP_ARMGATE, this.fn(gate2, d.gateSrcs?.[src] ?? null), ip, this.expected(deriveExpected(c)));
8344
8753
  return this.covWrap(gated, armCovIds?.[src], 1);
8345
8754
  });
8346
8755
  const rr = this.refResolver();
8347
- const finalChoice = classifyFinalChoice(arms, rr, this.activeTrivia);
8756
+ const constructionSets = d.disjoint && !factoredChoices.has(p);
8757
+ const finalChoice = classifyFinalChoice(arms, rr, this.gateTrivia(), false, constructionSets);
8758
+ const wideArms = this.wideArms(arms, finalChoice.firstSets, constructionSets);
8348
8759
  const transactionalFirst = arms.map((arm, i) => {
8349
8760
  if (!finalChoice.nullable[i]) return void 0;
8350
8761
  const def = arm._def;
8351
8762
  if (def.tag !== "attempt" || matchesEmpty(def.parser, /* @__PURE__ */ new Set(), rr)) return void 0;
8352
- return firstSetOf(def.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia);
8763
+ return firstSetOf(def.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia, false);
8353
8764
  });
8354
8765
  const gateFirst = finalChoice.firstSets.map((first, i) => transactionalFirst[i] ?? first);
8355
8766
  const classes = gateFirst.map((first, i) => finalChoice.nullable[i] && transactionalFirst[i] === void 0 ? -1 : this.charClass(first));
@@ -8367,6 +8778,9 @@ var Encoder = class {
8367
8778
  if (transactionalFirst[i] !== void 0) classes[i] = -1;
8368
8779
  }
8369
8780
  }
8781
+ if (wideArms.length > 0 && dispatchIsExclusive(classes, this.cc)) {
8782
+ for (const i of wideArms) classes[i] = -1;
8783
+ }
8370
8784
  if (d.gates.some((g) => g !== null && g !== void 0) && !dispatchIsExclusive(classes, this.cc)) {
8371
8785
  for (let k = 0; k < classes.length; k++) {
8372
8786
  const gate2 = d.gates[order[k]];
@@ -8400,27 +8814,34 @@ var Encoder = class {
8400
8814
  }
8401
8815
  case "many":
8402
8816
  case "oneOrMore": {
8403
- const child = this.node(d.parser).ip;
8404
8817
  const rr = this.refResolver();
8405
- const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.charClass(firstSetOf(d.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
8818
+ const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.charClass(this.gateFirstSet(d.parser, d.guarded === true, true));
8406
8819
  const op = d.valueUnused ? OP_REPV : OP_REP;
8407
8820
  const min = d.tag === "many" ? 0 : d.min;
8821
+ const item = min === 0 && itemClass >= 0 ? this.withoutLeadGate(this.node(d.parser).ip, itemClass) : this.node(d.parser).ip;
8822
+ const child = min === 0 && itemClass >= 0 ? this.gapGated(d.parser, item) : item;
8408
8823
  return this.emit(op, child, min, d.max ?? -1, -1, 0, this.expected(deriveExpected(d.parser)), itemClass);
8409
8824
  }
8410
8825
  case "sepBy": {
8411
8826
  const child = this.node(d.parser).ip;
8412
8827
  const sep2 = this.node(d.separator).ip;
8413
- const flags = (d.trailing === "allow" ? 1 : 0) | (d.keepSeparators === true ? 2 : 0) | (d.min >= 2 ? 4 : 0);
8414
- return this.emit(
8415
- OP_REP,
8828
+ const gate2 = this.sepGate(d.separator, sep2);
8829
+ const itemSet = deriveExpected(d.parser);
8830
+ const itemFx = this.expected(itemSet.length > 0 ? itemSet : [d.parser._tag]);
8831
+ const flags = (d.trailing === "allow" ? 1 : 0) | (d.keepSeparators === true ? 2 : 0) | (d.min >= 1 ? 4 : 0) | (gate2 >= 0 ? 8 : 0) | (itemSet.length === 0 ? 16 : 0);
8832
+ const ops = [
8416
8833
  child,
8417
8834
  d.min,
8418
8835
  d.max ?? -1,
8419
8836
  sep2,
8420
8837
  flags,
8421
- this.expected(deriveExpected(d.parser)),
8422
- this.charClass(firstSetOf(d.separator, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia))
8423
- );
8838
+ itemFx,
8839
+ // As 0.58.0: the interpreter resyncs on the separator itself
8840
+ // (`orSentinel`), which runs under the live trivia.
8841
+ this.charClass(firstSetOf(d.separator, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia, false))
8842
+ ];
8843
+ if (gate2 >= 0) ops.push(gate2);
8844
+ return this.emit(OP_REP, ...ops);
8424
8845
  }
8425
8846
  case "optional": {
8426
8847
  const head = this.emit(OP_OPT, this.lookaheadGate(d.parser, this.node(d.parser).ip));
@@ -8429,7 +8850,7 @@ var Encoder = class {
8429
8850
  }
8430
8851
  case "transform": {
8431
8852
  const inner = d.parser._def;
8432
- if (inner.tag === "sequence" && !this.memo.has(d.parser) && !this.pending.has(d.parser)) {
8853
+ if (inner.tag === "sequence" && !this.memo.get(d.parser)?.has(this.scopeKey(d.parser)) && !this.pending.get(d.parser)?.has(this.scopeKey(d.parser))) {
8433
8854
  const kids = inner.parsers.map((c) => this.node(c).ip);
8434
8855
  const head = this.emitHead(OP_SEQX, 2 + 2 * kids.length);
8435
8856
  const projection = directArrayProjection(d.fn, d.fnSrc, kids.length);
@@ -8470,7 +8891,7 @@ var Encoder = class {
8470
8891
  d.tags === void 0 ? -1 : this.constant(d.tags)
8471
8892
  );
8472
8893
  if (matchesEmpty(d.parser, /* @__PURE__ */ new Set(), this.refResolver())) return body;
8473
- const fs3 = this.gateFirstSet(d.parser);
8894
+ const fs3 = this.gateFirstSet(d.parser, d.guarded === true, false);
8474
8895
  if (fs3.kind === "any") return body;
8475
8896
  const cls = this.charClass(fs3);
8476
8897
  if (cls < 0) return body;
@@ -8478,7 +8899,14 @@ var Encoder = class {
8478
8899
  return this.emit(OP_GATE, cls, body, this.expected(e.length > 0 ? e : [d.parser._tag]));
8479
8900
  }
8480
8901
  case "token": {
8481
- return this.emit(OP_TOKEN, this.node(d.parser).ip);
8902
+ const saved = this.activeTrivia;
8903
+ const savedKnown = this.triviaKnown;
8904
+ this.activeTrivia = void 0;
8905
+ this.triviaKnown = true;
8906
+ const body = this.node(d.parser).ip;
8907
+ this.activeTrivia = saved;
8908
+ this.triviaKnown = savedKnown;
8909
+ return this.emit(OP_TOKEN, body);
8482
8910
  }
8483
8911
  case "routed":
8484
8912
  return this.emit(OP_ROUTED, d.fallback === void 0 ? -1 : this.node(d.fallback).ip);
@@ -8534,7 +8962,7 @@ var Encoder = class {
8534
8962
  this.code[head + 2] = dspIdx;
8535
8963
  this.code[head + 3] = other;
8536
8964
  const otherRouted = d.otherwise === void 0 ? false : branchUsesRouted({ parser: d.otherwise, usesRouted: d.otherwiseUsesRouted === true });
8537
- this.code[head + 4] = otherRouted ? 1 : 0;
8965
+ this.code[head + 4] = (otherRouted ? 1 : 0) | (d.otherwiseCommit === false ? 2 : 0);
8538
8966
  this.code[head + 5] = arms.length;
8539
8967
  for (let i = 0; i < arms.length; i++) this.code[head + 6 + i] = arms[i];
8540
8968
  return head;
@@ -8589,7 +9017,7 @@ var Encoder = class {
8589
9017
  case "attempt": {
8590
9018
  const inner = this.emit(OP_ATTEMPT, this.node(d.parser).ip, d.contain === true ? 1 : 0);
8591
9019
  if (!this.failureNeedsRollback(d.parser, void 0)) this.failureRollbackCleanSites.add(inner);
8592
- const fs3 = this.gateFirstSet(d.parser);
9020
+ const fs3 = this.gateFirstSet(d.parser, d.guarded === true, false);
8593
9021
  if (fs3.kind === "any" || matchesEmpty(d.parser, /* @__PURE__ */ new Set(), this.refResolver())) return inner;
8594
9022
  const cls = this.charClass(fs3);
8595
9023
  if (cls < 0) return inner;
@@ -8615,10 +9043,17 @@ var Encoder = class {
8615
9043
  const op = cap ? OP_SCOPE_CAP : OP_SCOPE;
8616
9044
  const flags = (d.rootCapture === "opaque" ? 1 : 0) | (d.triviaParser !== void 0 && d.triviaParser._meta.rootTriviaClassified !== true && d.rootCapture !== "opaque" ? 2 : 0);
8617
9045
  const savedTrivia = this.activeTrivia;
8618
- if (d.clearTrivia === true) this.activeTrivia = void 0;
8619
- else if (d.triviaParser !== void 0) this.activeTrivia = d.triviaParser;
9046
+ const savedKnown = this.triviaKnown;
9047
+ if (d.clearTrivia === true) {
9048
+ this.activeTrivia = void 0;
9049
+ this.triviaKnown = true;
9050
+ } else if (d.triviaParser !== void 0) {
9051
+ this.activeTrivia = d.triviaParser;
9052
+ this.triviaKnown = true;
9053
+ }
8620
9054
  const bodyIp = this.node(d.parser).ip;
8621
9055
  this.activeTrivia = savedTrivia;
9056
+ this.triviaKnown = savedKnown;
8622
9057
  if (d.clearTrivia === true) return this.emit(op, -1, bodyIp, flags);
8623
9058
  if (d.triviaParser === void 0) {
8624
9059
  return cap || flags !== 0 ? this.emit(op, -1, bodyIp, flags) : bodyIp;
@@ -8659,6 +9094,7 @@ var Encoder = class {
8659
9094
  switch (this.code[ip]) {
8660
9095
  case OP_GATE:
8661
9096
  case OP_RGATE:
9097
+ case OP_RGATE2:
8662
9098
  return [ip + 2];
8663
9099
  case OP_RULE:
8664
9100
  case OP_OPT:
@@ -8668,6 +9104,7 @@ var Encoder = class {
8668
9104
  case OP_ATTEMPT:
8669
9105
  case OP_LABEL:
8670
9106
  case OP_COV:
9107
+ case OP_GAPGATE:
8671
9108
  return [ip + 1];
8672
9109
  case OP_SCOPE:
8673
9110
  case OP_SCOPE_CAP:
@@ -8704,6 +9141,8 @@ var Encoder = class {
8704
9141
  case OP_REP:
8705
9142
  case OP_REPV:
8706
9143
  return this.code[ip + 4] >= 0 ? [ip + 1, ip + 4] : [ip + 1];
9144
+ case OP_STRICT:
9145
+ return [ip + 1, ip + 2];
8707
9146
  default:
8708
9147
  return [];
8709
9148
  }
@@ -9861,6 +10300,7 @@ function childSlots(code, ip, out) {
9861
10300
  case OP_LABEL:
9862
10301
  case OP_COV:
9863
10302
  case OP_REJECT:
10303
+ case OP_GAPGATE:
9864
10304
  out.push(code[ip + 1]);
9865
10305
  return true;
9866
10306
  // Single child at `ip + 2`.
@@ -9877,8 +10317,13 @@ function childSlots(code, ip, out) {
9877
10317
  case OP_SOURCE_LEAF:
9878
10318
  case OP_ARMGATE:
9879
10319
  case OP_RGATE:
10320
+ case OP_RGATE2:
9880
10321
  out.push(code[ip + 2]);
9881
10322
  return true;
10323
+ // `STRICT strict tolerant` — both encodings are reachable; an assembly keeps one.
10324
+ case OP_STRICT:
10325
+ out.push(code[ip + 1], code[ip + 2]);
10326
+ return true;
9882
10327
  // `ROUTED fallback` — the operand is an offset or −1. The routed token itself is
9883
10328
  // data the enclosing `dispatch()` already consumed, so the only edge is the
9884
10329
  // fallback.
@@ -10326,7 +10771,8 @@ var WRAPPERS = /* @__PURE__ */ new Set([
10326
10771
  OP_WITHCTX,
10327
10772
  OP_RULE,
10328
10773
  OP_ATTEMPT,
10329
- OP_COV
10774
+ OP_COV,
10775
+ OP_GAPGATE
10330
10776
  ]);
10331
10777
  function rowNullable(code, k, ip, memo) {
10332
10778
  const hit = memo.get(ip);
@@ -10363,7 +10809,31 @@ function holdsGap(code, k, base, n, i, memo) {
10363
10809
  return !rowNullable(code, k, next, memo);
10364
10810
  }
10365
10811
 
10812
+ // src/table/gap-gate.ts
10813
+ function newGapSlot() {
10814
+ return { row: -1, at: -1, end: -1, input: void 0 };
10815
+ }
10816
+ function gapRows(code, roots) {
10817
+ const rows = /* @__PURE__ */ new Set();
10818
+ const seen = /* @__PURE__ */ new Set();
10819
+ const stack = [...roots];
10820
+ const kids = [];
10821
+ while (stack.length > 0) {
10822
+ const ip = stack.pop();
10823
+ if (seen.has(ip)) continue;
10824
+ seen.add(ip);
10825
+ if (code[ip] === OP_GAPGATE) rows.add(code[ip + 2]);
10826
+ kids.length = 0;
10827
+ childSlots(code, ip, kids);
10828
+ for (const c of kids) stack.push(c);
10829
+ }
10830
+ return rows;
10831
+ }
10832
+
10366
10833
  // src/table/emit-assembly.ts
10834
+ function gapRegexLowers(re) {
10835
+ return re instanceof RegExp && scanShapeFromRegex(re.source, re.flags) !== null;
10836
+ }
10367
10837
  var Unemittable = class extends Error {
10368
10838
  construct;
10369
10839
  constructor(construct) {
@@ -10382,7 +10852,6 @@ var EMITTED_PARAMS = [
10382
10852
  "CLS",
10383
10853
  "AFX",
10384
10854
  "TRIVIA",
10385
- "TRIVIALABELS",
10386
10855
  "TRIVIASCAN",
10387
10856
  "SCANS",
10388
10857
  "DISP",
@@ -10406,7 +10875,6 @@ var EMITTED_PARAMS = [
10406
10875
  "cstLeavesLen",
10407
10876
  "skipTriviaScanned",
10408
10877
  "needsDeferredTriviaCommit",
10409
- "scanTrivia",
10410
10878
  "advanceTrivia",
10411
10879
  "refuseUnclassifiedRootScope",
10412
10880
  "spanLines",
@@ -10426,34 +10894,31 @@ var EMITTED_PARAMS = [
10426
10894
  "orSentinel",
10427
10895
  "captureError",
10428
10896
  // Pure scalar terminal recognizers, indexed by the terminal's existing const
10429
- // operand. Appended for compatibility with older precompiled factories.
10897
+ // operand; selected lexical bodies; the adjacency test.
10430
10898
  "RECOG",
10431
- // Appended so precompiled factories produced by earlier runtimes keep every
10432
- // positional helper binding. Older factories ignore this trailing argument.
10433
- "commitTriviaScan",
10434
- "scanTriviaCompact",
10435
10899
  "LEX",
10436
- // Appended for old precompiled-factory ABI: old factories ignore it.
10437
10900
  "adjacencyHolds",
10438
- // Appended for selected composite lexical programs; old factories ignore it.
10901
+ // Selected composite lexical programs.
10439
10902
  "LEXPROG",
10440
- // Appended for trivia-attributing fields (`field(…, { leadingTrivia })`);
10441
- // old factories ignore them.
10903
+ // Trivia-attributing fields (`field(…, { leadingTrivia })`).
10442
10904
  "LEAD",
10443
10905
  "triviaFieldCapture",
10444
- // Appended for the deferred-commit trivia skip (`trivia-gap.ts`); old
10445
- // factories ignore it.
10906
+ // The deferred-commit trivia skip (`trivia-gap.ts`).
10446
10907
  "skipDeferredTrivia",
10447
- // Appended for the pass-through collapse (`run-support.ts`); old factories
10448
- // ignore it.
10908
+ // The pass-through collapse (`run-support.ts`).
10449
10909
  "collapseEmpty",
10450
- // Appended for constructor-built tree objects (`cst/plain.d.ts`): a `new` call has
10451
- // no allocation site, so V8 cannot pretenure them. Old factories ignore them.
10910
+ // Constructor-built tree objects (`cst/plain.d.ts`): a `new` call has no
10911
+ // allocation site, so V8 cannot pretenure them.
10452
10912
  "_SP",
10453
10913
  "_LF",
10454
10914
  "_ND",
10455
10915
  "_FC",
10456
- "_BUF"
10916
+ "_BUF",
10917
+ // The gap gate's slot (`gap-gate.ts`).
10918
+ "GAP"
10919
+ // Artifacts are version-locked (docs/design/artifact-format.md): a factory is
10920
+ // only ever called by the runtime that emitted it, so a name nothing emitted
10921
+ // reads is removed rather than kept for an older factory's positions.
10457
10922
  ];
10458
10923
  var RUNTIME_PRELUDE = `
10459
10924
  let _pfScan=null
@@ -10704,7 +11169,7 @@ function rebuildPools(cc, fx, disp, plan) {
10704
11169
  })
10705
11170
  };
10706
11171
  }
10707
- function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false, select, scanSinkNeutral = false) {
11172
+ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false, select, scanSinkNeutral = false, gapNeutral = false) {
10708
11173
  const { code, k, fx, disp, dsp, triviaLabelled } = t;
10709
11174
  const swapLegal = !cfg.trackLines;
10710
11175
  const hostCst = cfg.hostCst;
@@ -10712,6 +11177,7 @@ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false, se
10712
11177
  if (cfg.coverage) throw new Unemittable("a coverage assembly");
10713
11178
  const roots = [...Object.values(prog.rules), ...extraIps];
10714
11179
  const reachable = [...reachableSites(code, roots)];
11180
+ const GAP_ROWS = gapNeutral ? /* @__PURE__ */ new Set() : new Set([...gapRows(code, roots)].filter((row) => !gapRegexLowers(k[code[row + 1]])));
10715
11181
  const labels = computeSiteLabels(code, roots, hostCst);
10716
11182
  const scalarSpecs = /* @__PURE__ */ new Set();
10717
11183
  const nullable2 = /* @__PURE__ */ new Map();
@@ -11049,13 +11515,15 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
11049
11515
  function aliasOf(ip) {
11050
11516
  const op = code[ip];
11051
11517
  if (op === OP_GATE && (cfg.tolerant || cfg.probe)) return code[ip + 2];
11052
- if (op === OP_RULE) return code[ip + 1];
11518
+ if (op === OP_RULE || op === OP_GAPGATE) return code[ip + 1];
11519
+ if (op === OP_STRICT) return code[cfg.tolerant || cfg.probe ? ip + 2 : ip + 1];
11053
11520
  if (scopeIsIdentity(code, ip, labels.at(ip))) return code[ip + 2];
11054
11521
  return void 0;
11055
11522
  }
11056
11523
  function lower(ip, fname) {
11057
11524
  const op = code[ip];
11058
11525
  const head = `function ${fname}(input,pos,ctx){`;
11526
+ const outerHead = head;
11059
11527
  const L = labels.at(ip);
11060
11528
  const sinks = sinksAt(ip);
11061
11529
  const pushLeaf = L.buf && !cfg.trackLines ? L.raw === RAW_OMIT ? "_pushLeafNoRawBuf" : L.raw === RAW_CAPTURE ? "_pushLeafBuf" : "_pushLeaf" : "_pushLeaf";
@@ -11131,9 +11599,27 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
11131
11599
  case OP_RX_TRACK: {
11132
11600
  const xf = fxRef(code[ip + 2]);
11133
11601
  const track = op === OP_RX_TRACK;
11602
+ if (GAP_ROWS.has(ip)) {
11603
+ const re2 = hoist("gx", `K[${code[ip + 1]}]`);
11604
+ return `${outerHead}
11605
+ let e
11606
+ if(GAP.row===${ip}&&GAP.at===pos&&GAP.input===input)e=GAP.end
11607
+ else{${re2}.lastIndex=pos;e=${re2}.test(input)?${re2}.lastIndex:-1}
11608
+ if(e>=0){
11609
+ const v=input.slice(pos,e)
11610
+ ${captureLeaf("v")}
11611
+ ${track ? "_trackLines(ctx,input,e)\n" : ""}EC.e=e
11612
+ return v
11613
+ }
11614
+ ctx._fe=pos;ctx._fx=${xf}
11615
+ ${cfg.probe ? `failAt(ctx,${xf},pos)
11616
+ ` : ""}return FAIL
11617
+ }`;
11618
+ }
11619
+ const head2 = outerHead;
11134
11620
  if (!track && scalarSpecs.has(code[ip + 1])) {
11135
11621
  const recognize = recognizerRef(code[ip + 1]);
11136
- return `${head}
11622
+ return `${head2}
11137
11623
  const e=${recognize}(input,pos)
11138
11624
  if(e>=0){
11139
11625
  const v=input.slice(pos,e)
@@ -11146,11 +11632,11 @@ return FAIL
11146
11632
  }`;
11147
11633
  }
11148
11634
  const scanned = emitScan(code[ip + 1], xf, track, captureLeaf);
11149
- if (scanned !== void 0) return `${head}${scanned}}`;
11635
+ if (scanned !== void 0) return `${head2}${scanned}}`;
11150
11636
  const rxk = k[code[ip + 1]];
11151
11637
  if (!(rxk instanceof RegExp) || !rxk.sticky) throw new Unemittable("a non-sticky regex row");
11152
11638
  const re = kRef(code[ip + 1]);
11153
- return `${head}
11639
+ return `${head2}
11154
11640
  ${re}.lastIndex=pos
11155
11641
  if(${re}.test(input)){
11156
11642
  const e=${re}.lastIndex
@@ -11188,6 +11674,28 @@ return ${child}(input,pos,ctx)
11188
11674
  const it=ctx._routed
11189
11675
  if(it!==undefined&&it.span.start===pos&&!classHas(${cls},lead(input,it.span.end))){ctx._fe=it.span.end;ctx._fx=${xf};return FAIL}
11190
11676
  return ${child}(input,pos,ctx)
11677
+ }`;
11678
+ }
11679
+ case OP_RGATE2: {
11680
+ const child = link(code[ip + 2]);
11681
+ if (cfg.tolerant || cfg.probe) return `${head}
11682
+ return ${child}(input,pos,ctx)
11683
+ }`;
11684
+ const idxs = [code[ip + 1], code[ip + 4], code[ip + 5]];
11685
+ const ascii = idxs.every((i) => isAsciiOnlyClass(t.cc[i]));
11686
+ const ref2 = (idx, tag) => {
11687
+ const ci = classes.push([t.cc[idx]]) - 1;
11688
+ classPlan.push([idx]);
11689
+ return hoist(tag, `CLS[${ci}][0]`);
11690
+ };
11691
+ const [cls, first, second] = [ref2(idxs[0], "cl"), ref2(idxs[1], "c1"), ref2(idxs[2], "c2")];
11692
+ const has = (c, at) => ascii ? `${c}.ascii[${at}]===1` : `classHas(${c},${at})`;
11693
+ const xf = fxRef(code[ip + 3]);
11694
+ return `${head}
11695
+ const it=ctx._routed
11696
+ if(it!==undefined&&it.span.start===pos){const e=it.span.end;const c=${ascii ? "input.charCodeAt(e)" : "lead(input,e)"}
11697
+ if(!(${has(cls, "c")})||(${has(first, "c")}&&!(${has(second, ascii ? "input.charCodeAt(e+1)" : "lead(input,e+1)")}))){ctx._fe=e;ctx._fx=${xf};return FAIL}}
11698
+ return ${child}(input,pos,ctx)
11191
11699
  }`;
11192
11700
  }
11193
11701
  case OP_ARMGATE: {
@@ -11597,7 +12105,27 @@ if(ctx._fc===true)return FAIL
11597
12105
  const af=ctx._fx
11598
12106
  if(af!==undefined&&af.length>0){ctx._fe=pos;return FAIL}
11599
12107
  }
11600
- ctx._fe=pos;ctx._fx=${choiceFx}
12108
+ ${cfg.probe ? `if(arm<0){${arms.map((a) => `${a}(input,pos,ctx)`).join(";")}}
12109
+ ` : ""}ctx._fe=pos;ctx._fx=${choiceFx}
12110
+ return FAIL
12111
+ }`;
12112
+ }
12113
+ if (cfg.probe) {
12114
+ const p = tmp();
12115
+ return `${head}
12116
+ ${emitMark(p, L.buf, L.raw, sinks)}
12117
+ let acc
12118
+ let best=pos
12119
+ ${arms.map((arm) => `{ctx._fc=false
12120
+ const v=${arm}(input,pos,ctx)
12121
+ if(v!==FAIL)return v
12122
+ const at=ctx._fe??pos
12123
+ if(at>best){best=at;acc=undefined}
12124
+ if(at===best)acc=_accSet(ctx._fx,acc)
12125
+ if(ctx._fc===true){if(acc!==undefined)ctx._fx=acc;return FAIL}
12126
+ ${emitRollback(p, L.buf, L.raw, sinks)}
12127
+ }`).join("\n")}
12128
+ ctx._fe=pos;ctx._fx=acc??${choiceFx}
11601
12129
  return FAIL
11602
12130
  }`;
11603
12131
  }
@@ -11804,7 +12332,8 @@ return FAIL
11804
12332
  const selector = link(code[ip + 1]);
11805
12333
  const otherIp = code[ip + 3];
11806
12334
  const other = otherIp >= 0 ? link(otherIp) : void 0;
11807
- const otherRouted = code[ip + 4] === 1;
12335
+ const otherRouted = (code[ip + 4] & 1) === 1;
12336
+ const otherCommits = (code[ip + 4] & 2) === 0;
11808
12337
  const n = code[ip + 5];
11809
12338
  validateDispatchSpec(spec, n, code[ip + 4]);
11810
12339
  const armBase = ip + 6;
@@ -11858,7 +12387,7 @@ ${fallbackCase}
11858
12387
  }
11859
12388
  if(v===FAIL){
11860
12389
  ${emitRollback(m2, L.buf, L.raw, sinks)}
11861
- ctx._fc=true
12390
+ ${other !== void 0 && !otherCommits ? "if(arm!==undefined)" : ""}ctx._fc=true
11862
12391
  return FAIL
11863
12392
  }
11864
12393
  return [key,v]
@@ -11890,6 +12419,63 @@ return [key,v]
11890
12419
  }
11891
12420
  }
11892
12421
  const itemMiss = (at) => itemAscii ? `${itemCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${itemCls},lead(input,${at}))`;
12422
+ let sepCls;
12423
+ let sepAscii = false;
12424
+ if (!REC2 && sepIp >= 0 && (code[ip + 5] & 8) !== 0 && !leadingTriviaFieldAt(code, k, sepIp) && !rowNullable(code, k, sepIp, nullable2)) {
12425
+ const resolved = t.cc[code[ip + 8]];
12426
+ const ci = classes.push([resolved]) - 1;
12427
+ classPlan.push([code[ip + 8]]);
12428
+ sepCls = hoist("rs", `CLS[${ci}][0]`);
12429
+ sepAscii = isAsciiOnlyClass(resolved);
12430
+ }
12431
+ const gapIp = !gapNeutral && sepIp < 0 && min === 0 && !REC2 && code[code[ip + 1]] === OP_GAPGATE ? code[ip + 1] : -1;
12432
+ let gapCode = (_rb) => "";
12433
+ if (gapIp >= 0) {
12434
+ const row = code[gapIp + 2];
12435
+ const rx = k[code[row + 1]];
12436
+ const shape = gapRegexLowers(rx) ? scanShapeFromRegex(rx.source, rx.flags) : null;
12437
+ const cls = (idx) => {
12438
+ const resolved = t.cc[idx];
12439
+ const ci = classes.push([resolved]) - 1;
12440
+ classPlan.push([idx]);
12441
+ const ref2 = hoist("gc", `CLS[${ci}][0]`);
12442
+ return isAsciiOnlyClass(resolved) ? `${ref2}.ascii[input.charCodeAt(${"@"})]!==1` : `!classHas(${ref2},lead(input,${"@"}))`;
12443
+ };
12444
+ const follow = cls(code[gapIp + 3]).replaceAll("@", "gq");
12445
+ const other = code[gapIp + 4] >= 0 ? cls(code[gapIp + 4]).replaceAll("@", "itemStart") : "true";
12446
+ if (shape !== null) {
12447
+ let n = 0;
12448
+ const q2 = tmp();
12449
+ const m = emitShapeMatch(shape, "itemStart", (prefix = "_v") => `${q2}${prefix}${n++}`, "");
12450
+ gapCode = (rbCode) => `let gapped=false
12451
+ if(ctx._probe===undefined){
12452
+ ${m.setup.join("\n")}
12453
+ const gq=${m.ok}?${m.end}:-1
12454
+ if((gq<0||${follow})&&${other}){
12455
+ ${rbCode}
12456
+ break
12457
+ }
12458
+ }
12459
+ `;
12460
+ } else {
12461
+ const re = hoist("gr", `K[${code[row + 1]}]`);
12462
+ gapCode = (rbCode) => `let gapped=false
12463
+ if(ctx._probe===undefined){
12464
+ ${re}.lastIndex=itemStart
12465
+ const gq=${re}.test(input)?${re}.lastIndex:-1
12466
+ if((gq<0||${follow})&&${other}){
12467
+ ${rbCode}
12468
+ break
12469
+ }
12470
+ if(gq>=0){
12471
+ GAP.row=${row};GAP.at=itemStart;GAP.end=gq;GAP.input=input
12472
+ gapped=true
12473
+ }
12474
+ }
12475
+ `;
12476
+ }
12477
+ }
12478
+ const sepMiss = (at) => sepAscii ? `${sepCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${sepCls},lead(input,${at}))`;
11893
12479
  const skipBeforeFirst = sepIp < 0 && min === 0;
11894
12480
  const p = tmp();
11895
12481
  const via = sep2 !== void 0 ? "false" : min === 0 ? skipBeforeFirst ? "true" : "count>0" : `count>=${min}&&count>0`;
@@ -11927,7 +12513,7 @@ ${p}lg=ctx._triviaLog!==undefined?ctx._triviaLog.length:0
11927
12513
  ${p}rt=ctx._rootTriviaLog!==undefined?ctx._rootTriviaLog.length:0${markSinks}
11928
12514
  }`;
11929
12515
  const my = `${p}my`;
11930
- const recFx = REC2 ? fxRef(code[ip + 6]) : "";
12516
+ const recFx = !REC2 ? "" : (flags & 16) !== 0 ? "[]" : fxRef(code[ip + 6]);
11931
12517
  const sepSent = REC2 ? sentRef(code[ip + 7]) : "undefined";
11932
12518
  const recSent = sep2 === void 0 ? my : sepSent === "undefined" ? `orSentinel(${my},undefined)` : `orSentinel(${sepSent}??${my},${sepSent}===undefined?undefined:${my})`;
11933
12519
  const recGate = sep2 !== void 0 || min === 0 ? "" : `count>=${min}&&`;
@@ -11943,7 +12529,7 @@ continue
11943
12529
  return `${head}
11944
12530
  const out=${collect ? "[]" : "undefined"}
11945
12531
  ${knownTrivia === void 0 ? "const hasTrivia=ctx.trivia!==undefined\n" : ""}${L.buf ? "" : "const needMark=_rollbackNeeded(ctx)\n"}${REC2 ? `const ${my}=ctx._sync
11946
- ` : ""}${itemCls === void 0 ? "" : `const ${p}gate=ctx._probe===undefined
12532
+ ` : ""}${itemCls === void 0 && sepCls === void 0 ? "" : `const ${p}gate=ctx._probe===undefined
11947
12533
  `}let cur=pos
11948
12534
  let count=0
11949
12535
  for(;;){
@@ -11959,6 +12545,10 @@ const lb=cstLeavesLen(ctx)
11959
12545
  let sp=cur
11960
12546
  ${hasTrivia === "false" ? "" : hasTrivia === "true" ? `sp=${skip}(input,sp,ctx)
11961
12547
  ` : `if(hasTrivia)sp=${skip}(input,sp,ctx)
12548
+ `}${sepCls === void 0 ? "" : `if(count>=${min}&&${p}gate&&${sepMiss("sp")}){
12549
+ ${rb}
12550
+ break
12551
+ }
11962
12552
  `}ctx._fc=false
11963
12553
  ${sepIp >= 0 && leadingTriviaFieldAt(code, k, sepIp) ? "LEAD.at=cur\n" : ""}const sv=${sep2}(input,sp,ctx)
11964
12554
  if(sv===FAIL){
@@ -11974,13 +12564,13 @@ itemStart=${hasTrivia === "false" ? "EC.e" : hasTrivia === "true" ? `${skip}(inp
11974
12564
  ${rb}
11975
12565
  ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
11976
12566
  }
11977
- ${itemCls === void 0 ? "" : `if(count>=${min}&&${p}gate&&${itemMiss("itemStart")}){
12567
+ ${itemCls === void 0 || sep2 === void 0 && hasTrivia === "false" ? "" : `if(count>=${min}&&${p}gate&&${sep2 === void 0 && hasTrivia === "hasTrivia" ? "hasTrivia&&" : ""}${itemMiss("itemStart")}){
11978
12568
  ${rb}
11979
12569
  ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
11980
12570
  }
11981
- `}ctx._fc=false
12571
+ `}${gapCode(rb)}ctx._fc=false
11982
12572
  const v=${child}(input,itemStart,ctx)
11983
- if(v===FAIL){
12573
+ ${gapIp >= 0 ? "if(gapped)GAP.at=-1\n" : ""}if(v===FAIL){
11984
12574
  ${REC2 ? `if(ctx._fc===true){
11985
12575
  ${rb}
11986
12576
  return FAIL
@@ -12796,6 +13386,9 @@ function clearOf(ranges, input, from, to) {
12796
13386
  return true;
12797
13387
  }
12798
13388
 
13389
+ // src/version.ts
13390
+ var PARSEMAN_VERSION = "0.60.1";
13391
+
12799
13392
  // src/table/assemble.ts
12800
13393
  import { PlainBuf as PlainBuf2, PlainField, PlainLeaf as PlainLeaf9, PlainNode as PlainNode2, PlainSpan as PlainSpan10 } from "../cst/plain.js";
12801
13394
 
@@ -12806,6 +13399,7 @@ function passChild(code, ip) {
12806
13399
  case OP_LABEL:
12807
13400
  case OP_COV:
12808
13401
  case OP_TOKEN:
13402
+ case OP_GAPGATE:
12809
13403
  return code[ip + 1];
12810
13404
  case OP_GATE:
12811
13405
  case OP_NODE:
@@ -13071,6 +13665,13 @@ function cfgKeyOf(hostCst, trackLines, tolerant, coverage, probe, hostReadsChild
13071
13665
  function cfgKey(c) {
13072
13666
  return cfgKeyOf(c.hostCst, c.trackLines, c.tolerant, c.coverage, c.probe, c.hostReadsChildren);
13073
13667
  }
13668
+ function assertFactoryArity(pre) {
13669
+ if (pre.arity === EMITTED_PARAMS.length) return;
13670
+ const printed = pre.arity === void 0 ? "carries no helper count, so it was printed before parseman 0.60.0" : `takes ${pre.arity} helpers, but parseman ${PARSEMAN_VERSION} passes ${EMITTED_PARAMS.length}: it was printed by an older or newer parseman`;
13671
+ throw new Error(
13672
+ `parseman: this parser's printed assembly ${printed}. Reprint the parser with parseman ${PARSEMAN_VERSION}, or run it on the parseman version that printed it.`
13673
+ );
13674
+ }
13074
13675
  function assemble(t, prog, cfg) {
13075
13676
  const { code, k, fns, cc, fx, disp, dsp, trivia: trivia2, triviaLabelled } = t;
13076
13677
  const triviaScan = t.triviaScan;
@@ -13105,6 +13706,8 @@ function assemble(t, prog, cfg) {
13105
13706
  return recognize;
13106
13707
  }
13107
13708
  const labelExtraIps = assemblyExtraIps(prog);
13709
+ const GAP_ROWS = gapRows(code, [...Object.values(prog.rules), ...labelExtraIps]);
13710
+ const GAP = newGapSlot();
13108
13711
  const closureLabels = computeSiteLabels(
13109
13712
  code,
13110
13713
  [...Object.values(prog.rules), ...labelExtraIps],
@@ -13323,6 +13926,27 @@ function assemble(t, prog, cfg) {
13323
13926
  return node2;
13324
13927
  };
13325
13928
  }
13929
+ function gapRegexPiece(row, re, xf, track) {
13930
+ return (input, pos, ctx) => {
13931
+ let e;
13932
+ if (GAP.row === row && GAP.at === pos && GAP.input === input) e = GAP.end;
13933
+ else {
13934
+ re.lastIndex = pos;
13935
+ e = re.test(input) ? re.lastIndex : -1;
13936
+ }
13937
+ if (e >= 0) {
13938
+ const v = input.slice(pos, e);
13939
+ if (cstCaptureActive(ctx)) pushLeaf(ctx, v, pos, e);
13940
+ if (track) trackLinesInto(ctx, input, e);
13941
+ EC.e = e;
13942
+ return v;
13943
+ }
13944
+ ctx._fe = pos;
13945
+ ctx._fx = xf;
13946
+ if (ctx._probe !== void 0) failAt(ctx, xf, pos);
13947
+ return FAIL;
13948
+ };
13949
+ }
13326
13950
  function passCollapseNode(child, build, readsRaw) {
13327
13951
  return (input, pos, ctx) => {
13328
13952
  const v = child(input, pos, ctx);
@@ -13725,7 +14349,7 @@ function assemble(t, prog, cfg) {
13725
14349
  return next?.(key);
13726
14350
  };
13727
14351
  }
13728
- function dispatchBranch(child, usesRouted) {
14352
+ function dispatchBranch(child, usesRouted, commits = true) {
13729
14353
  if (!usesRouted) {
13730
14354
  return (input, _pos, selEnd, key, ctx) => {
13731
14355
  const need = markCst(ctx);
@@ -13739,7 +14363,7 @@ function assemble(t, prog, cfg) {
13739
14363
  const v = child(input, selEnd, ctx);
13740
14364
  if (v === FAIL) {
13741
14365
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
13742
- ctx._fc = true;
14366
+ if (commits) ctx._fc = true;
13743
14367
  return FAIL;
13744
14368
  }
13745
14369
  return [key, v];
@@ -13757,7 +14381,7 @@ function assemble(t, prog, cfg) {
13757
14381
  }
13758
14382
  if (v === FAIL) {
13759
14383
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
13760
- ctx._fc = true;
14384
+ if (commits) ctx._fc = true;
13761
14385
  return FAIL;
13762
14386
  }
13763
14387
  return [key, v];
@@ -13998,6 +14622,7 @@ function assemble(t, prog, cfg) {
13998
14622
  case OP_RX: {
13999
14623
  const re = k[code[ip + 1]];
14000
14624
  const xf = fx[code[ip + 2]];
14625
+ if (GAP_ROWS.has(ip)) return gapRegexPiece(ip, re, xf, false);
14001
14626
  const shared = rawScalarSpecs.has(code[ip + 1]) ? scalarFor(ip) : void 0;
14002
14627
  if (shared !== void 0) {
14003
14628
  return (input, pos, ctx) => {
@@ -14032,6 +14657,7 @@ function assemble(t, prog, cfg) {
14032
14657
  case OP_RX_TRACK: {
14033
14658
  const re = k[code[ip + 1]];
14034
14659
  const xf = fx[code[ip + 2]];
14660
+ if (GAP_ROWS.has(ip)) return gapRegexPiece(ip, re, xf, true);
14035
14661
  return (input, pos, ctx) => {
14036
14662
  re.lastIndex = pos;
14037
14663
  if (re.test(input)) {
@@ -14162,6 +14788,12 @@ function assemble(t, prog, cfg) {
14162
14788
  return v;
14163
14789
  };
14164
14790
  }
14791
+ // Transparent: only the repeat holding it reads its operands.
14792
+ case OP_GAPGATE:
14793
+ return link(code[ip + 1]);
14794
+ // One encoding per assembly: strict, or the construct as written.
14795
+ case OP_STRICT:
14796
+ return link(code[cfg.tolerant || cfg.probe ? ip + 2 : ip + 1]);
14165
14797
  case OP_RULE:
14166
14798
  return link(code[ip + 1]);
14167
14799
  case OP_LIVE: {
@@ -15087,7 +15719,7 @@ function assemble(t, prog, cfg) {
15087
15719
  if (table.exclusive) {
15088
15720
  const ascii = table.ascii;
15089
15721
  const hi = table.hi;
15090
- if (n === 2) {
15722
+ if (n === 2 && !cfg.probe) {
15091
15723
  const a0 = link(code[base]), a1 = link(code[base + 1]);
15092
15724
  return (input, pos, ctx) => {
15093
15725
  const c = lead(input, pos);
@@ -15119,7 +15751,7 @@ function assemble(t, prog, cfg) {
15119
15751
  return FAIL;
15120
15752
  };
15121
15753
  }
15122
- if (n === 3) {
15754
+ if (n === 3 && !cfg.probe) {
15123
15755
  const a0 = link(code[base]), a1 = link(code[base + 1]), a2 = link(code[base + 2]);
15124
15756
  return (input, pos, ctx) => {
15125
15757
  const c = lead(input, pos);
@@ -15164,6 +15796,7 @@ function assemble(t, prog, cfg) {
15164
15796
  );
15165
15797
  };
15166
15798
  const selected = bindExclusive(0);
15799
+ const probeArms = cfg.probe ? Array.from({ length: n }, (_, i) => link(code[base + i])) : void 0;
15167
15800
  return (input, pos, ctx) => {
15168
15801
  const c = lead(input, pos);
15169
15802
  let arm = -1;
@@ -15189,12 +15822,13 @@ function assemble(t, prog, cfg) {
15189
15822
  return FAIL;
15190
15823
  }
15191
15824
  }
15825
+ if (arm < 0 && probeArms !== void 0) for (const a of probeArms) a(input, pos, ctx);
15192
15826
  ctx._fe = pos;
15193
15827
  ctx._fx = choiceFx;
15194
15828
  return FAIL;
15195
15829
  };
15196
15830
  }
15197
- const armCls = table.armCls;
15831
+ const armCls = cfg.probe ? table.armCls.map(() => null) : table.armCls;
15198
15832
  if (n === 2 || n === 3) {
15199
15833
  const a0 = link(code[base]), a1 = link(code[base + 1]);
15200
15834
  const a2 = n === 3 ? link(code[base + 2]) : void 0;
@@ -15614,6 +16248,43 @@ function assemble(t, prog, cfg) {
15614
16248
  return child(input, pos, ctx);
15615
16249
  };
15616
16250
  }
16251
+ case OP_RGATE2: {
16252
+ const cls = cc[code[ip + 1]];
16253
+ const xf = fx[code[ip + 3]];
16254
+ const first = cc[code[ip + 4]];
16255
+ const second = cc[code[ip + 5]];
16256
+ const child = link(code[ip + 2]);
16257
+ if (cfg.tolerant || cfg.probe) return child;
16258
+ if (cls.hi.length === 0 && first.hi.length === 0 && second.hi.length === 0) {
16259
+ const a0 = cls.ascii, a1 = first.ascii, a2 = second.ascii;
16260
+ return (input, pos, ctx) => {
16261
+ const item = ctx._routed;
16262
+ if (item !== void 0 && item.span.start === pos) {
16263
+ const e = item.span.end;
16264
+ const c = input.charCodeAt(e);
16265
+ if (a0[c] !== 1 || a1[c] === 1 && a2[input.charCodeAt(e + 1)] !== 1) {
16266
+ ctx._fe = e;
16267
+ ctx._fx = xf;
16268
+ return FAIL;
16269
+ }
16270
+ }
16271
+ return child(input, pos, ctx);
16272
+ };
16273
+ }
16274
+ return (input, pos, ctx) => {
16275
+ const item = ctx._routed;
16276
+ if (item !== void 0 && item.span.start === pos) {
16277
+ const e = item.span.end;
16278
+ const c = lead(input, e);
16279
+ if (!classHas2(cls, c) || classHas2(first, c) && !classHas2(second, lead(input, e + 1))) {
16280
+ ctx._fe = e;
16281
+ ctx._fx = xf;
16282
+ return FAIL;
16283
+ }
16284
+ }
16285
+ return child(input, pos, ctx);
16286
+ };
16287
+ }
15617
16288
  case OP_ARMGATE: {
15618
16289
  const pred = fns[code[ip + 1]];
15619
16290
  const child = link(code[ip + 2]);
@@ -15681,10 +16352,18 @@ function assemble(t, prog, cfg) {
15681
16352
  const itemClassIndex = sepIp < 0 ? code[ip + 7] : -1;
15682
16353
  const itemClass = !REC2 && itemClassIndex >= 0 ? cc[itemClassIndex] : void 0;
15683
16354
  const itemMayStart = itemClass === void 0 ? void 0 : (input, pos) => classHas2(itemClass, lead(input, pos));
16355
+ const sepClassIndex = sepIp >= 0 && !sepLeads && (flags & 8) !== 0 ? code[ip + 8] : -1;
16356
+ const sepClass = !REC2 && sepClassIndex >= 0 && !rowNullable(code, k, sepIp, NULLABLE) ? cc[sepClassIndex] : void 0;
16357
+ const gapIp = sepIp < 0 && min === 0 && !REC2 && code[code[ip + 1]] === OP_GAPGATE ? code[ip + 1] : -1;
16358
+ const gapRow = gapIp >= 0 ? code[gapIp + 2] : -1;
16359
+ const gapRe = gapIp >= 0 ? k[code[gapRow + 1]] : void 0;
16360
+ const gapFollow = gapIp >= 0 ? cc[code[gapIp + 3]] : void 0;
16361
+ const gapOther = gapIp >= 0 && code[gapIp + 4] >= 0 ? cc[code[gapIp + 4]] : void 0;
15684
16362
  const skipBeforeFirst = sepIp < 0 && min === 0;
15685
16363
  const itemClean = sepIp < 0 && CLEAN(code[ip + 1]) && !rowNullable(code, k, code[ip + 1], NULLABLE);
15686
16364
  if (REC2) {
15687
16365
  const itemFx2 = fx[code[ip + 6]];
16366
+ const recFx = (flags & 16) !== 0 ? EMPTY_FX2 : itemFx2;
15688
16367
  const sepSent = sentinelFor(code[ip + 7]);
15689
16368
  return (input, pos, ctx) => {
15690
16369
  const out = collect ? [] : void 0;
@@ -15744,7 +16423,7 @@ function assemble(t, prog, cfg) {
15744
16423
  itemStart,
15745
16424
  ctx,
15746
16425
  sep2 === void 0 ? mySync : orSentinel(sepSent ?? mySync, sepSent === void 0 ? void 0 : mySync),
15747
- itemFx2
16426
+ recFx
15748
16427
  );
15749
16428
  if (out !== void 0) out.push(rr.error);
15750
16429
  captureError(ctx, rr.error);
@@ -15782,6 +16461,7 @@ function assemble(t, prog, cfg) {
15782
16461
  const markLater = needMark && (!itemClean || hasTrivia);
15783
16462
  const markFirst = needMark && (!itemClean || hasTrivia && skipBeforeFirst);
15784
16463
  const gateItems = itemMayStart !== void 0 && ctx._probe === void 0;
16464
+ const gateSep = sepClass !== void 0 && ctx._probe === void 0;
15785
16465
  let cur = pos;
15786
16466
  let count = 0;
15787
16467
  for (; ; ) {
@@ -15804,6 +16484,10 @@ function assemble(t, prog, cfg) {
15804
16484
  const leavesBefore = cstLeavesLen(ctx);
15805
16485
  let sp = cur;
15806
16486
  if (hasTrivia) sp = skipTrivia(input, sp, ctx);
16487
+ if (count >= min && gateSep && !classHas2(sepClass, lead(input, sp))) {
16488
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
16489
+ break;
16490
+ }
15807
16491
  ctx._fc = false;
15808
16492
  if (sepLeads) LEAD.at = cur;
15809
16493
  const sv = sep2(input, sp, ctx);
@@ -15823,13 +16507,30 @@ function assemble(t, prog, cfg) {
15823
16507
  if (trailingAllowed && sepEnd >= 0) cur = sepEnd;
15824
16508
  break;
15825
16509
  }
15826
- if (count >= min && gateItems && !itemMayStart(input, itemStart)) {
16510
+ if (count >= min && gateItems && (sep2 !== void 0 || hasTrivia) && !itemMayStart(input, itemStart)) {
15827
16511
  if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
15828
16512
  if (trailingAllowed && sepEnd >= 0) cur = sepEnd;
15829
16513
  break;
15830
16514
  }
16515
+ let gapped = false;
16516
+ if (gapRe !== void 0 && ctx._probe === void 0) {
16517
+ gapRe.lastIndex = itemStart;
16518
+ const q2 = gapRe.test(input) ? gapRe.lastIndex : -1;
16519
+ if ((q2 < 0 || !classHas2(gapFollow, lead(input, q2))) && (gapOther === void 0 || !classHas2(gapOther, lead(input, itemStart)))) {
16520
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
16521
+ break;
16522
+ }
16523
+ if (q2 >= 0) {
16524
+ GAP.row = gapRow;
16525
+ GAP.at = itemStart;
16526
+ GAP.end = q2;
16527
+ GAP.input = input;
16528
+ gapped = true;
16529
+ }
16530
+ }
15831
16531
  ctx._fc = false;
15832
16532
  const v = child(input, itemStart, ctx);
16533
+ if (gapped) GAP.at = -1;
15833
16534
  if (v === FAIL) {
15834
16535
  if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
15835
16536
  if (committed3(ctx)) return FAIL;
@@ -15861,7 +16562,8 @@ function assemble(t, prog, cfg) {
15861
16562
  const selector = link(code[ip + 1]);
15862
16563
  const otherIp = code[ip + 3];
15863
16564
  const other = otherIp >= 0 ? link(otherIp) : void 0;
15864
- const otherRouted = code[ip + 4] === 1;
16565
+ const otherRouted = (code[ip + 4] & 1) === 1;
16566
+ const otherCommits = (code[ip + 4] & 2) === 0;
15865
16567
  const n = code[ip + 5];
15866
16568
  const armBase = ip + 6;
15867
16569
  const byKey = spec.byKey;
@@ -15909,7 +16611,7 @@ function assemble(t, prog, cfg) {
15909
16611
  );
15910
16612
  };
15911
16613
  const runArm = n === 0 ? void 0 : bindArms(0);
15912
- const runOther = other === void 0 ? void 0 : dispatchBranch(other, otherRouted);
16614
+ const runOther = other === void 0 ? void 0 : dispatchBranch(other, otherRouted, otherCommits);
15913
16615
  return (input, pos, ctx) => {
15914
16616
  const need = markCst(ctx);
15915
16617
  const mRaw = MRAW;
@@ -16170,6 +16872,7 @@ function assemble(t, prog, cfg) {
16170
16872
  }
16171
16873
  const pre = cfg.hostCaptureTrivia === void 0 ? prog.asm?.find((a) => a.key === cfgKey(cfg)) : void 0;
16172
16874
  if (pre !== void 0) {
16875
+ assertFactoryArity(pre);
16173
16876
  const pools = rebuildPools(t.cc, t.fx, t.disp, pre.plan);
16174
16877
  emitted = pre.factory(
16175
16878
  EC,
@@ -16181,7 +16884,6 @@ function assemble(t, prog, cfg) {
16181
16884
  pools.classes,
16182
16885
  pools.armExpected,
16183
16886
  trivia2,
16184
- trivia2.map((tv) => tv?._meta.triviaKindLabels),
16185
16887
  triviaScan,
16186
16888
  scansArr,
16187
16889
  disp,
@@ -16205,7 +16907,6 @@ function assemble(t, prog, cfg) {
16205
16907
  cstLeavesLen,
16206
16908
  skipTriviaScanned,
16207
16909
  needsDeferredTriviaCommit,
16208
- scanTrivia,
16209
16910
  advanceTrivia,
16210
16911
  refuseUnclassifiedRootScope,
16211
16912
  spanLines,
@@ -16219,8 +16920,6 @@ function assemble(t, prog, cfg) {
16219
16920
  orSentinel,
16220
16921
  captureError,
16221
16922
  scalarRecognizers,
16222
- commitTriviaScan,
16223
- scanTriviaCompact,
16224
16923
  t.lex,
16225
16924
  adjacencyHolds,
16226
16925
  t.lexPrograms,
@@ -16232,7 +16931,8 @@ function assemble(t, prog, cfg) {
16232
16931
  PlainLeaf9,
16233
16932
  PlainNode2,
16234
16933
  PlainField,
16235
- PlainBuf2
16934
+ PlainBuf2,
16935
+ GAP
16236
16936
  );
16237
16937
  emitReached = new Set(pre.reached);
16238
16938
  if (emitted.ext !== void 0) HYBRID = emitted;
@@ -16268,7 +16968,6 @@ function assemble(t, prog, cfg) {
16268
16968
  em.classes,
16269
16969
  em.armExpected,
16270
16970
  trivia2,
16271
- trivia2.map((tv) => tv?._meta.triviaKindLabels),
16272
16971
  triviaScan,
16273
16972
  scansArr,
16274
16973
  disp,
@@ -16292,7 +16991,6 @@ function assemble(t, prog, cfg) {
16292
16991
  cstLeavesLen,
16293
16992
  skipTriviaScanned,
16294
16993
  needsDeferredTriviaCommit,
16295
- scanTrivia,
16296
16994
  advanceTrivia,
16297
16995
  refuseUnclassifiedRootScope,
16298
16996
  spanLines,
@@ -16311,8 +17009,6 @@ function assemble(t, prog, cfg) {
16311
17009
  orSentinel,
16312
17010
  captureError,
16313
17011
  scalarRecognizers,
16314
- commitTriviaScan,
16315
- scanTriviaCompact,
16316
17012
  t.lex,
16317
17013
  adjacencyHolds,
16318
17014
  t.lexPrograms,
@@ -16324,7 +17020,8 @@ function assemble(t, prog, cfg) {
16324
17020
  PlainLeaf9,
16325
17021
  PlainNode2,
16326
17022
  PlainField,
16327
- PlainBuf2
17023
+ PlainBuf2,
17024
+ GAP
16328
17025
  );
16329
17026
  emitReached = em.reached;
16330
17027
  } catch (e) {
@@ -16501,7 +17198,7 @@ var AssemblyCache = class {
16501
17198
  * That is not a hole in G5, it is G5's own first clause: "quickly building the
16502
17199
  * grammar reference ON RUN START, making some swaps on rules or sub-rules, and
16503
17200
  * then the run actually runs with no logic branching for that option input"
16504
- * (`notes/TABLE-DRIVER.md`). This IS the run-start step. What the criterion
17201
+ * (`notes/history/TABLE-DRIVER.md`). This IS the run-start step. What the criterion
16505
17202
  * forbids is the second sentence, and past this call there is no option read
16506
17203
  * anywhere — `scripts/check-invariants.mjs` INV-6 decides that mechanically.
16507
17204
  *
@@ -16593,11 +17290,8 @@ function tableRules(source, metadataOrEntry = {}) {
16593
17290
  return map[entry];
16594
17291
  }
16595
17292
 
16596
- // src/version.ts
16597
- var PARSEMAN_VERSION = "0.59.3";
16598
-
16599
17293
  // src/table/emit.ts
16600
- var FACTORY_ARITY_GUARD = `if(arguments.length<${EMITTED_PARAMS.length})throw new Error(${JSON.stringify(`parseman: this parser was printed by parseman ${PARSEMAN_VERSION}; upgrade the parseman runtime to at least that version`)});`;
17294
+ var FACTORY_ARITY_GUARD = `if(arguments.length!==${EMITTED_PARAMS.length})throw new Error(${JSON.stringify(`parseman: this parser was printed by parseman ${PARSEMAN_VERSION}; run it on parseman ${PARSEMAN_VERSION} or reprint it`)});`;
16601
17295
  function jsString(s) {
16602
17296
  return JSON.stringify(s);
16603
17297
  }
@@ -16683,7 +17377,7 @@ function emitAssemblies(prog, cfgs) {
16683
17377
  throw e;
16684
17378
  }
16685
17379
  const plan = em.plan;
16686
- out.push(`{key:${key},factory:function(${EMITTED_PARAMS.join(",")}){${FACTORY_ARITY_GUARD}${em.source}
17380
+ out.push(`{key:${key},arity:${EMITTED_PARAMS.length},factory:function(${EMITTED_PARAMS.join(",")}){${FACTORY_ARITY_GUARD}${em.source}
16687
17381
  },plan:{classes:[${plan.classes.map((r) => `[${r.join(",")}]`).join(",")}],armExpected:[${plan.armExpected.map((r) => `[${r.join(",")}]`).join(",")}],masks:[${plan.masks.join(",")}]},reached:[${[...em.reached].join(",")}]}`);
16688
17382
  }
16689
17383
  return [`a:[${out.join(",")}],`];
@@ -16959,7 +17653,7 @@ function payloadKey(p, d) {
16959
17653
  case "sepBy":
16960
17654
  return `sepBy\0${d.min}\0${d.max ?? ""}\0${d.trailing ?? ""}`;
16961
17655
  case "dispatch":
16962
- return `dispatch\0${d.cases.map((c) => JSON.stringify(c.keys)).join("")}\0${d.otherwise !== void 0}`;
17656
+ return `dispatch\0${d.cases.map((c) => JSON.stringify(c.keys)).join("")}\0${d.otherwise !== void 0}${d.otherwiseCommit === false ? "\0uncommitted" : ""}`;
16963
17657
  case "expect":
16964
17658
  return `expect\0${d.label ?? ""}\0${d.expected.join("")}`;
16965
17659
  case "scanTo":
@@ -18265,7 +18959,10 @@ function staticWeights(code, entries) {
18265
18959
  childSlots(code, ip, kids);
18266
18960
  const op = code[ip];
18267
18961
  const scale = op === OP_REP || op === OP_REPV ? LOOP : op === OP_CHOICE || op === OP_DISPATCH ? 1 / Math.max(1, kids.length) : 1;
18268
- for (const kid of kids) next.set(kid, Math.min(CAP, (next.get(kid) ?? 0) + w * scale));
18962
+ for (const k of kids) {
18963
+ const kid = code[k] === OP_GAPGATE ? code[k + 1] : k;
18964
+ next.set(kid, Math.min(CAP, (next.get(kid) ?? 0) + w * scale));
18965
+ }
18269
18966
  }
18270
18967
  weight = next;
18271
18968
  }
@@ -18382,7 +19079,7 @@ function compileRuleMap(ruleMap, opts = {}) {
18382
19079
  const roots = assemblyExtraIps(artifact);
18383
19080
  let siteBytes, fixed;
18384
19081
  try {
18385
- siteBytes = emitAssemblySource(table, artifact, cfg, roots, true, void 0, true).siteBytes;
19082
+ siteBytes = emitAssemblySource(table, artifact, cfg, roots, true, void 0, true, true).siteBytes;
18386
19083
  fixed = emitAssemblySource(table, artifact, cfg, roots, true, /* @__PURE__ */ new Set(), true).source.length;
18387
19084
  } catch (e) {
18388
19085
  if (e instanceof Unemittable) return { ...none, refusal: e.construct };
@@ -18930,7 +19627,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
18930
19627
  arms.push(`when(${matcherExpr(entry)}, ${kid(entry.parser)}${entry.caseInsensitive ? ", { caseInsensitive: true }" : ""})`);
18931
19628
  }
18932
19629
  }
18933
- if (def.otherwise) arms.push(`otherwise(${kid(def.otherwise)})`);
19630
+ if (def.otherwise) arms.push(`otherwise(${kid(def.otherwise)}${def.otherwiseCommit === false ? ", { commit: false }" : ""})`);
18934
19631
  return `dispatch(${kid(def.selector)}${arms.length === 0 ? "" : `, ${arms.join(", ")}`})`;
18935
19632
  }
18936
19633
  case "many":
@@ -19973,11 +20670,16 @@ function dispatchArmValue(node2, scope, code, mfs) {
19973
20670
  return null;
19974
20671
  }
19975
20672
  if (callee.name === "otherwise") {
19976
- const [parserArg] = node2.arguments;
19977
- if (!parserArg || parserArg.type === "SpreadElement") return null;
20673
+ const [parserArg, optsArg] = node2.arguments;
20674
+ if (!parserArg || parserArg.type === "SpreadElement" || optsArg?.type === "SpreadElement") return null;
19978
20675
  const parserValue = anyValue(parserArg, scope, code, mfs);
19979
20676
  if (!isCombinator3(parserValue)) return null;
19980
- return otherwise(parserValue);
20677
+ if (optsArg === void 0) return otherwise(parserValue);
20678
+ const opts = anyValue(optsArg, scope, code, mfs);
20679
+ if (typeof opts !== "object" || opts === null) return null;
20680
+ const commit = opts.commit;
20681
+ if (commit !== void 0 && typeof commit !== "boolean") return null;
20682
+ return otherwise(parserValue, commit === void 0 ? {} : { commit });
19981
20683
  }
19982
20684
  if (callee.name === "when") {
19983
20685
  const [keyArg, parserArg, optsArg] = node2.arguments;