parseman 0.59.2 → 0.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/dist/analysis/commitment.d.ts +1 -1
  2. package/dist/analysis/diagnostics.js +877 -191
  3. package/dist/analysis/diagnostics.js.map +3 -3
  4. package/dist/cli/index.js +877 -191
  5. package/dist/cli/index.js.map +3 -3
  6. package/dist/combinators/completions.d.ts +3 -3
  7. package/dist/combinators/completions.d.ts.map +1 -1
  8. package/dist/combinators/dispatch.d.ts +13 -1
  9. package/dist/combinators/dispatch.d.ts.map +1 -1
  10. package/dist/combinators/first-set.d.ts +35 -2
  11. package/dist/combinators/first-set.d.ts.map +1 -1
  12. package/dist/combinators/gate.d.ts +1 -1
  13. package/dist/combinators/regex.d.ts.map +1 -1
  14. package/dist/combinators/trivia-boundary.d.ts +11 -0
  15. package/dist/combinators/trivia-boundary.d.ts.map +1 -1
  16. package/dist/combinators/trivia-skip.d.ts +1 -1
  17. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  18. package/dist/compiler/compile-linkable-table.d.ts +2 -3
  19. package/dist/compiler/compile-linkable-table.d.ts.map +1 -1
  20. package/dist/compiler/linker.d.ts.map +1 -1
  21. package/dist/compiler/token-alphabet.d.ts +2 -0
  22. package/dist/compiler/token-alphabet.d.ts.map +1 -1
  23. package/dist/compiler/token-capability.js +65 -53
  24. package/dist/compiler/token-capability.js.map +2 -2
  25. package/dist/cst/host-mode.d.ts +1 -1
  26. package/dist/cst/root-trivia-scope.d.ts +1 -1
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +897 -200
  30. package/dist/index.js.map +3 -3
  31. package/dist/language-service/index.d.ts +1 -1
  32. package/dist/language-service/index.js.map +1 -1
  33. package/dist/plugin/entry-supercompiler.d.ts.map +1 -1
  34. package/dist/plugin/evaluator.d.ts.map +1 -1
  35. package/dist/plugin/index.d.ts +3 -7
  36. package/dist/plugin/index.d.ts.map +1 -1
  37. package/dist/plugin/index.js +904 -202
  38. package/dist/plugin/index.js.map +3 -3
  39. package/dist/regex/first-set.d.ts +19 -0
  40. package/dist/regex/first-set.d.ts.map +1 -1
  41. package/dist/spec/index.js.map +1 -1
  42. package/dist/table/assemble.d.ts +16 -3
  43. package/dist/table/assemble.d.ts.map +1 -1
  44. package/dist/table/child-slots.d.ts.map +1 -1
  45. package/dist/table/compile-rule-map.d.ts +3 -2
  46. package/dist/table/compile-rule-map.d.ts.map +1 -1
  47. package/dist/table/compile.d.ts +2 -2
  48. package/dist/table/emit-assembly.d.ts +8 -2
  49. package/dist/table/emit-assembly.d.ts.map +1 -1
  50. package/dist/table/emit.d.ts +8 -8
  51. package/dist/table/emit.d.ts.map +1 -1
  52. package/dist/table/encode.d.ts +6 -7
  53. package/dist/table/encode.d.ts.map +1 -1
  54. package/dist/table/exec.d.ts.map +1 -1
  55. package/dist/table/factor-choice.d.ts +27 -0
  56. package/dist/table/factor-choice.d.ts.map +1 -1
  57. package/dist/table/gap-gate.d.ts +11 -0
  58. package/dist/table/gap-gate.d.ts.map +1 -0
  59. package/dist/table/index.js +927 -202
  60. package/dist/table/index.js.map +3 -3
  61. package/dist/table/lex-program.d.ts.map +1 -1
  62. package/dist/table/ops.d.ts +48 -20
  63. package/dist/table/ops.d.ts.map +1 -1
  64. package/dist/table/program.d.ts +13 -8
  65. package/dist/table/program.d.ts.map +1 -1
  66. package/dist/types.d.ts +17 -5
  67. package/dist/types.d.ts.map +1 -1
  68. package/dist/version.d.ts +6 -6
  69. package/package.json +1 -1
  70. package/src/analysis/commitment.ts +1 -1
  71. package/src/analysis/duplication.ts +1 -1
  72. package/src/analysis/terminal.ts +1 -1
  73. package/src/combinators/attempt.ts +1 -1
  74. package/src/combinators/choice.ts +1 -1
  75. package/src/combinators/completions.ts +3 -3
  76. package/src/combinators/dispatch.ts +30 -6
  77. package/src/combinators/first-set.ts +55 -12
  78. package/src/combinators/gate.ts +1 -1
  79. package/src/combinators/node.ts +1 -1
  80. package/src/combinators/regex.ts +9 -4
  81. package/src/combinators/repeat.ts +6 -6
  82. package/src/combinators/sequence.ts +1 -1
  83. package/src/combinators/trivia-boundary.ts +18 -0
  84. package/src/combinators/trivia-skip.ts +1 -1
  85. package/src/compiler/compile-linkable-table.ts +2 -3
  86. package/src/compiler/degradation.ts +2 -2
  87. package/src/compiler/ir-serialize.ts +1 -1
  88. package/src/compiler/linker.ts +5 -3
  89. package/src/compiler/token-alphabet.ts +3 -1
  90. package/src/coverage.ts +1 -1
  91. package/src/cst/capture-buffer.ts +2 -2
  92. package/src/cst/host-mode.ts +1 -1
  93. package/src/cst/root-trivia-scope.ts +1 -1
  94. package/src/index.ts +1 -1
  95. package/src/language-service/index.ts +1 -1
  96. package/src/plugin/entry-supercompiler.ts +2 -1
  97. package/src/plugin/evaluator.ts +8 -3
  98. package/src/plugin/index.ts +11 -16
  99. package/src/regex/first-set.ts +37 -3
  100. package/src/table/assemble.ts +180 -35
  101. package/src/table/child-slots.ts +8 -3
  102. package/src/table/compile-rule-map.ts +10 -6
  103. package/src/table/compile.ts +3 -3
  104. package/src/table/emit-assembly.ts +186 -35
  105. package/src/table/emit.ts +19 -18
  106. package/src/table/encode.ts +482 -88
  107. package/src/table/exec.ts +42 -18
  108. package/src/table/factor-choice.ts +62 -0
  109. package/src/table/fail-clean.ts +2 -2
  110. package/src/table/gap-gate.ts +55 -0
  111. package/src/table/hold-gap.ts +2 -2
  112. package/src/table/lex-program.ts +15 -6
  113. package/src/table/ops.ts +51 -20
  114. package/src/table/program.ts +14 -9
  115. package/src/table/select-hot.ts +7 -2
  116. package/src/table/stamp.ts +1 -1
  117. package/src/types.ts +13 -6
  118. package/src/version.ts +6 -6
package/dist/index.js CHANGED
@@ -27,15 +27,56 @@ function implicitLeadsDropped(ranges) {
27
27
  return out;
28
28
  }
29
29
 
30
+ // src/combinators/trivia-boundary.ts
31
+ var MAX_DEPTH = 12;
32
+ function hasOwnTriviaBoundary(p, depth = MAX_DEPTH) {
33
+ if (depth === 0) return true;
34
+ const d = p._def;
35
+ switch (d.tag) {
36
+ // Multi-term forms: the gaps BETWEEN terms are the ambient-trivia positions.
37
+ case "sequence":
38
+ return d.parsers.length > 1;
39
+ case "many":
40
+ case "oneOrMore":
41
+ case "sepBy":
42
+ return true;
43
+ // Transparent wrappers — they add no position, so ask what they wrap.
44
+ case "node":
45
+ case "transform":
46
+ case "leaf":
47
+ case "label":
48
+ case "field":
49
+ case "token":
50
+ case "attempt":
51
+ case "optional":
52
+ case "expect":
53
+ case "withCtx":
54
+ case "recover":
55
+ case "trivia":
56
+ return hasOwnTriviaBoundary(d.parser, depth - 1);
57
+ // An alternation adds no position either, but ONE arm that has one is enough
58
+ // to make the scope meaningful for this rule.
59
+ case "choice":
60
+ return d.parsers.some((a) => hasOwnTriviaBoundary(a, depth - 1));
61
+ default:
62
+ return false;
63
+ }
64
+ }
65
+ function triviaThroughRef(ref2, target, trivia2) {
66
+ if (trivia2 !== void 0 && trivia2 !== null) return trivia2;
67
+ const amb = ref2._meta.grammarTrivia ?? target._meta.grammarTrivia;
68
+ return amb !== void 0 && hasOwnTriviaBoundary(target) ? amb : trivia2;
69
+ }
70
+
30
71
  // src/combinators/first-set.ts
31
- function classifyFinalChoice(arms, resolve, activeTrivia) {
72
+ function classifyFinalChoice(arms, resolve, activeTrivia, consuming = true, constructionSets = false) {
32
73
  const firstSets = [];
33
74
  const nullable2 = [];
34
75
  let exclusive = true;
35
76
  for (const arm of arms) {
36
77
  const empty2 = matchesEmpty(arm, /* @__PURE__ */ new Set(), resolve);
37
78
  nullable2.push(empty2);
38
- firstSets.push(empty2 ? { kind: "any" } : firstSetOf(arm, /* @__PURE__ */ new Set(), resolve, activeTrivia));
79
+ firstSets.push(empty2 ? { kind: "any" } : constructionSets ? arm._meta.firstSet : firstSetOf(arm, /* @__PURE__ */ new Set(), resolve, activeTrivia, consuming));
39
80
  if (empty2) exclusive = false;
40
81
  }
41
82
  if (exclusive) {
@@ -220,7 +261,7 @@ function narrowBy(acc, constraint) {
220
261
  function applyAssertion(fs, assertion) {
221
262
  return assertion === null ? fs : fs.kind === "empty" ? assertion : narrowBy(fs, assertion);
222
263
  }
223
- function sequenceSet(parsers, fs, empties, trivia2) {
264
+ function sequenceSet(parsers, fs, empties, trivia2, consuming = true) {
224
265
  const afterBoundary = (i) => {
225
266
  const rest = at(i);
226
267
  return trivia2 ? union(rest, fs(trivia2)) : rest;
@@ -236,24 +277,24 @@ function sequenceSet(parsers, fs, empties, trivia2) {
236
277
  }
237
278
  const first = fs(p);
238
279
  if (!empties(p) || i + 1 === parsers.length) return first;
239
- return union(first, afterBoundary(i + 1));
280
+ return union((consuming ? p._meta.consumingFirstSet : void 0) ?? first, afterBoundary(i + 1));
240
281
  };
241
282
  return at(0);
242
283
  }
243
284
  function sequenceFirstSet(parsers) {
244
- return sequenceSet(parsers, (p) => p._meta.firstSet, matchesEmpty);
285
+ return sequenceSet(parsers, (p) => p._meta.firstSet, matchesEmpty, void 0, false);
245
286
  }
246
- function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve, activeTrivia) {
287
+ function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve, activeTrivia, consuming = true) {
247
288
  if (seen.has(p)) return any();
248
289
  seen.add(p);
249
290
  try {
250
- return firstSetBody(p, seen, resolve, activeTrivia);
291
+ return firstSetBody(p, seen, resolve, activeTrivia, consuming);
251
292
  } finally {
252
293
  seen.delete(p);
253
294
  }
254
295
  }
255
- function firstSetBody(p, seen, resolve, activeTrivia) {
256
- const fs = (c) => firstSetOf(c, seen, resolve, activeTrivia);
296
+ function firstSetBody(p, seen, resolve, activeTrivia, consuming) {
297
+ const fs = (c) => firstSetOf(c, seen, resolve, activeTrivia, consuming);
257
298
  const empties = (c) => matchesEmpty(c, /* @__PURE__ */ new Set(), resolve);
258
299
  const d = p._def;
259
300
  switch (d.tag) {
@@ -265,13 +306,16 @@ function firstSetBody(p, seen, resolve, activeTrivia) {
265
306
  case "dispatch":
266
307
  return fs(d.selector);
267
308
  case "lazy": {
268
- const w = winnerForRef(p, resolve);
269
- if (w !== void 0) return fs(w);
270
- try {
271
- return fs(d.thunk());
272
- } catch {
273
- return any();
309
+ let target = winnerForRef(p, resolve);
310
+ if (target === void 0) {
311
+ try {
312
+ target = d.thunk();
313
+ } catch {
314
+ return any();
315
+ }
274
316
  }
317
+ const through = activeTrivia === null && resolve !== void 0;
318
+ return firstSetOf(target, seen, resolve, through ? triviaThroughRef(p, target, null) : activeTrivia, consuming);
275
319
  }
276
320
  case "choice": {
277
321
  let out = empty();
@@ -279,7 +323,7 @@ function firstSetBody(p, seen, resolve, activeTrivia) {
279
323
  return out;
280
324
  }
281
325
  case "sequence": {
282
- return sequenceSet(d.parsers, fs, empties, activeTrivia);
326
+ return sequenceSet(d.parsers, fs, empties, activeTrivia, consuming);
283
327
  }
284
328
  case "peek": {
285
329
  const inner = d.parser;
@@ -291,7 +335,6 @@ function firstSetBody(p, seen, resolve, activeTrivia) {
291
335
  case "transform":
292
336
  case "label":
293
337
  case "trivia":
294
- case "token":
295
338
  case "leaf":
296
339
  case "node":
297
340
  case "sepBy":
@@ -301,12 +344,16 @@ function firstSetBody(p, seen, resolve, activeTrivia) {
301
344
  // with a rule reference — resolve through it like any other wrapper.
302
345
  case "attempt":
303
346
  return fs(d.parser);
347
+ // A token runs its body with trivia cleared (`token.ts`).
348
+ case "token":
349
+ return firstSetOf(d.parser, seen, resolve, activeTrivia === void 0 ? void 0 : null, consuming);
304
350
  case "grammar":
305
351
  return firstSetOf(
306
352
  d.parser,
307
353
  seen,
308
354
  resolve,
309
- d.clearTrivia ? null : d.triviaParser ?? activeTrivia
355
+ d.clearTrivia ? null : d.triviaParser ?? activeTrivia,
356
+ consuming
310
357
  );
311
358
  // A run starts with its first line's prefix; a nullable prefix starts with anything.
312
359
  case "lines": {
@@ -999,10 +1046,12 @@ function parseRegex(src, unicode = false, sets = false) {
999
1046
  const parseGroup = () => {
1000
1047
  i++;
1001
1048
  let lookaround = false;
1049
+ let lookahead = false;
1002
1050
  if (src[i] === "?") {
1003
1051
  const c1 = src[i + 1];
1004
1052
  if (c1 === ":") i += 2;
1005
1053
  else if (c1 === "=" || c1 === "!") {
1054
+ lookahead = c1 === "=";
1006
1055
  i += 2;
1007
1056
  lookaround = true;
1008
1057
  } else if (c1 === "<") {
@@ -1020,7 +1069,8 @@ function parseRegex(src, unicode = false, sets = false) {
1020
1069
  const inner = parseAlt();
1021
1070
  if (src[i] !== ")") throw BAIL;
1022
1071
  i++;
1023
- return lookaround ? EMPTY2 : inner;
1072
+ if (lookaround) return lookahead ? { t: "empty", look: inner } : EMPTY2;
1073
+ return inner;
1024
1074
  };
1025
1075
  const parseClass = () => {
1026
1076
  let k = i + 1;
@@ -1213,6 +1263,13 @@ function regexCanMatchEmpty(source) {
1213
1263
  return true;
1214
1264
  }
1215
1265
  }
1266
+ function consumingFirstSetFromRegex(source, flags = "") {
1267
+ try {
1268
+ return firstSet(parseRegex(source, flags.includes("u") || flags.includes("v"), flags.includes("v")));
1269
+ } catch {
1270
+ return any();
1271
+ }
1272
+ }
1216
1273
  function firstSetFromRegex(source, flags = "") {
1217
1274
  let ast;
1218
1275
  try {
@@ -1223,6 +1280,20 @@ function firstSetFromRegex(source, flags = "") {
1223
1280
  if (nullable(ast)) return { firstSet: any(), canMatchNewline: canConsumeNewline(ast) };
1224
1281
  return { firstSet: firstSet(ast), canMatchNewline: canConsumeNewline(ast) };
1225
1282
  }
1283
+ function regexFirstTwo(source, flags = "") {
1284
+ let ast;
1285
+ try {
1286
+ ast = parseRegex(source, flags.includes("u") || flags.includes("v"), flags.includes("v"));
1287
+ } catch {
1288
+ return void 0;
1289
+ }
1290
+ const parts = ast.t === "seq" ? ast.parts : [ast];
1291
+ const a = parts[0];
1292
+ const b = parts[1];
1293
+ if (a === void 0 || a.t !== "char" || a.set.kind !== "ranges" || b === void 0) return void 0;
1294
+ const second = b.t === "char" ? b.set : b.t === "empty" && b.look !== void 0 && !nullable(b.look) ? firstSet(b.look) : void 0;
1295
+ return second !== void 0 && second.kind === "ranges" ? { first: a.set, second } : void 0;
1296
+ }
1226
1297
 
1227
1298
  // src/combinators/regex.ts
1228
1299
  import { PlainLeaf as PlainLeaf3, PlainSpan as PlainSpan3 } from "./cst/plain.js";
@@ -1303,9 +1374,14 @@ function regex(pattern, flags = "") {
1303
1374
  const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
1304
1375
  const scan = shortScanner(source, resolvedFlags);
1305
1376
  const raw = firstSetFromRegex(source, resolvedFlags);
1306
- const firstSet2 = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
1377
+ const forFlags = (fs) => !resolvedFlags.includes("i") ? fs : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(fs);
1378
+ const firstSet2 = forFlags(raw.firstSet);
1307
1379
  const canMatchNewline = raw.canMatchNewline;
1308
1380
  const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
1381
+ if (firstSet2.kind === "any" && regexCanMatchEmpty(source)) {
1382
+ const consuming = forFlags(consumingFirstSetFromRegex(source, resolvedFlags));
1383
+ if (consuming.kind !== "any") meta.consumingFirstSet = consuming;
1384
+ }
1309
1385
  const def = { tag: "regex", source, flags: resolvedFlags };
1310
1386
  const expected = directTerminalFailureExpected(def);
1311
1387
  const parseScalar = (input, pos, ctx) => {
@@ -2356,10 +2432,6 @@ function scanTriviaCompact(input, cur, ctx) {
2356
2432
  const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
2357
2433
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
2358
2434
  }
2359
- function scanTrivia(input, cur, ctx) {
2360
- const scan = scanTriviaCompact(input, cur, ctx);
2361
- return typeof scan === "number" ? { end: scan, commit: NOOP_COMMIT } : scan;
2362
- }
2363
2435
  function skipTriviaScanned(s, input, cur, ctx) {
2364
2436
  const end = s(input, cur);
2365
2437
  if (end !== cur) {
@@ -3008,7 +3080,7 @@ function sequence(...parsers) {
3008
3080
  if (tolerant) {
3009
3081
  followSentinels ??= parsers.map((_, i) => firstSetSentinel(
3010
3082
  parsers.slice(i + 1).reduce(
3011
- (set, parser2) => union(set, firstSetOf(parser2)),
3083
+ (set, parser2) => union(set, firstSetOf(parser2, /* @__PURE__ */ new Set(), void 0, void 0, false)),
3012
3084
  { kind: "empty" }
3013
3085
  )
3014
3086
  ));
@@ -3631,8 +3703,9 @@ function when(key, parser2, opts = {}) {
3631
3703
  function makeWhen(opts = {}) {
3632
3704
  return (key, parser2) => when(key, parser2, opts);
3633
3705
  }
3634
- function otherwise(parser2) {
3635
- return { kind: "otherwise", parser: parser2, usesRouted: parserUsesRouted(parser2) };
3706
+ function otherwise(parser2, opts = {}) {
3707
+ const usesRouted = parserUsesRouted(parser2);
3708
+ return opts.commit === false ? { kind: "otherwise", parser: parser2, usesRouted, commit: false } : { kind: "otherwise", parser: parser2, usesRouted };
3636
3709
  }
3637
3710
  function routed(fallback) {
3638
3711
  return {
@@ -3664,6 +3737,7 @@ function dispatch(selector, ...arms) {
3664
3737
  }
3665
3738
  let fallback;
3666
3739
  let fallbackUsesRouted = false;
3740
+ let fallbackCommits = true;
3667
3741
  const matchers = [];
3668
3742
  const cases = [];
3669
3743
  const seen = [];
@@ -3674,6 +3748,7 @@ function dispatch(selector, ...arms) {
3674
3748
  if (i !== arms.length - 1) throw new Error("parseman: otherwise() must be the last dispatch() arm");
3675
3749
  fallback = arm.parser;
3676
3750
  fallbackUsesRouted = arm.usesRouted === true;
3751
+ fallbackCommits = arm.commit !== false;
3677
3752
  continue;
3678
3753
  }
3679
3754
  if (arm.kind === "whenMatcher") {
@@ -3724,7 +3799,11 @@ function dispatch(selector, ...arms) {
3724
3799
  selector,
3725
3800
  cases,
3726
3801
  ...matchers.length === 0 ? {} : { matchers },
3727
- ...fallback === void 0 ? {} : { otherwise: fallback, ...fallbackUsesRouted ? { otherwiseUsesRouted: true } : {} }
3802
+ ...fallback === void 0 ? {} : {
3803
+ otherwise: fallback,
3804
+ ...fallbackUsesRouted ? { otherwiseUsesRouted: true } : {},
3805
+ ...fallbackCommits ? {} : { otherwiseCommit: false }
3806
+ }
3728
3807
  },
3729
3808
  parse(input, pos, ctx) {
3730
3809
  if (parserUsesRouted(selector)) {
@@ -3733,7 +3812,8 @@ function dispatch(selector, ...arms) {
3733
3812
  const selectorMark = saveTriviaMark(ctx);
3734
3813
  const selected = selector.parse(input, pos, ctx);
3735
3814
  if (!selected.ok) return selected;
3736
- const branch = byKey.get(selected.value) ?? (byFoldedKey.size === 0 ? void 0 : byFoldedKey.get(asciiFoldKey(selected.value))) ?? matchers.find((matcher) => matchesDispatchMatcher(selected.value, matcher)) ?? (fallback === void 0 ? void 0 : { keys: [], parser: fallback, caseInsensitive: false, usesRouted: fallbackUsesRouted });
3815
+ const chosen = byKey.get(selected.value) ?? (byFoldedKey.size === 0 ? void 0 : byFoldedKey.get(asciiFoldKey(selected.value))) ?? matchers.find((matcher) => matchesDispatchMatcher(selected.value, matcher));
3816
+ const branch = chosen ?? (fallback === void 0 ? void 0 : { keys: [], parser: fallback, caseInsensitive: false, usesRouted: fallbackUsesRouted });
3737
3817
  if (branch === void 0) {
3738
3818
  const expected = cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key)));
3739
3819
  return { ok: false, expected, span: { start: selected.span.end, end: selected.span.end } };
@@ -3754,7 +3834,7 @@ function dispatch(selector, ...arms) {
3754
3834
  }
3755
3835
  if (!result.ok) {
3756
3836
  rollbackTrivia(ctx, mark);
3757
- return { ...result, committed: true };
3837
+ return chosen === void 0 && !fallbackCommits ? result : { ...result, committed: true };
3758
3838
  }
3759
3839
  return {
3760
3840
  ok: true,
@@ -3783,7 +3863,7 @@ function attempt(parser2, options) {
3783
3863
  return {
3784
3864
  _tag: "attempt",
3785
3865
  _meta: parser2._meta,
3786
- _def: contain ? { tag: "attempt", parser: parser2, contain: true } : { tag: "attempt", parser: parser2 },
3866
+ _def: { tag: "attempt", parser: parser2, ...contain ? { contain: true } : {}, ...guardable ? { guarded: true } : {} },
3787
3867
  parse(input, pos, ctx) {
3788
3868
  if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(parser2, input, pos)) {
3789
3869
  if (failExpected === void 0) {
@@ -3873,9 +3953,9 @@ function many(combinator, opts = {}) {
3873
3953
  canMatchNewline: combinator._meta.canMatchNewline,
3874
3954
  isTrivia: false
3875
3955
  };
3876
- const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max } };
3877
- let expected;
3878
3956
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
3957
+ const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max }, ...guardable ? { guarded: true } : {} };
3958
+ let expected;
3879
3959
  const scalarChild = scalarOf(combinator);
3880
3960
  const parseScalar = (input, pos, ctx) => repeatScalarTail(combinator, scalarChild, input, pos, ctx, guardable, def.valueUnused ? void 0 : [], max);
3881
3961
  return {
@@ -3922,9 +4002,9 @@ function atLeast(combinator, min, max) {
3922
4002
  canMatchNewline: combinator._meta.canMatchNewline,
3923
4003
  isTrivia: false
3924
4004
  };
3925
- const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max } };
3926
- let expected;
3927
4005
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
4006
+ const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max }, ...guardable ? { guarded: true } : {} };
4007
+ let expected;
3928
4008
  const scalarChild = scalarOf(combinator);
3929
4009
  const parseScalar = (input, pos, ctx) => {
3930
4010
  let cur = scalarChild(input, pos, ctx);
@@ -4178,42 +4258,6 @@ function sepBy(combinator, separatorArg, opts = {}) {
4178
4258
  };
4179
4259
  }
4180
4260
 
4181
- // src/combinators/trivia-boundary.ts
4182
- var MAX_DEPTH = 12;
4183
- function hasOwnTriviaBoundary(p, depth = MAX_DEPTH) {
4184
- if (depth === 0) return true;
4185
- const d = p._def;
4186
- switch (d.tag) {
4187
- // Multi-term forms: the gaps BETWEEN terms are the ambient-trivia positions.
4188
- case "sequence":
4189
- return d.parsers.length > 1;
4190
- case "many":
4191
- case "oneOrMore":
4192
- case "sepBy":
4193
- return true;
4194
- // Transparent wrappers — they add no position, so ask what they wrap.
4195
- case "node":
4196
- case "transform":
4197
- case "leaf":
4198
- case "label":
4199
- case "field":
4200
- case "token":
4201
- case "attempt":
4202
- case "optional":
4203
- case "expect":
4204
- case "withCtx":
4205
- case "recover":
4206
- case "trivia":
4207
- return hasOwnTriviaBoundary(d.parser, depth - 1);
4208
- // An alternation adds no position either, but ONE arm that has one is enough
4209
- // to make the scope meaningful for this rule.
4210
- case "choice":
4211
- return d.parsers.some((a) => hasOwnTriviaBoundary(a, depth - 1));
4212
- default:
4213
- return false;
4214
- }
4215
- }
4216
-
4217
4261
  // src/combinators/ref.ts
4218
4262
  function withRuleTrivia(trivia2, ctx, run3) {
4219
4263
  const savedLabels = ctx.triviaKindLabels;
@@ -5205,7 +5249,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
5205
5249
  if (project !== void 0 && build !== void 0) {
5206
5250
  throw new Error("node() options cannot combine project with a build callback");
5207
5251
  }
5208
- const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build } };
5252
+ const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build }, ...combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator) ? { guarded: true } : {} };
5209
5253
  const meta = {
5210
5254
  firstSet: combinator._meta.firstSet,
5211
5255
  canMatchNewline: combinator._meta.canMatchNewline,
@@ -5764,14 +5808,15 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
5764
5808
  return end < 0 ? next(input, pos) : end;
5765
5809
  };
5766
5810
  }
5767
- let tail = (_input, pos, ctx, _lead, acc) => {
5811
+ let tail = (input, pos, ctx, _lead, acc, _best, entered) => {
5812
+ if (!entered && ctx._probe !== void 0) for (const arm of arms) arm.run(input, pos, ctx);
5768
5813
  ctx._fe = pos;
5769
5814
  ctx._fx = acc ?? choiceExpected;
5770
5815
  return -1;
5771
5816
  };
5772
5817
  for (let i = arms.length - 1; i >= 0; i--) {
5773
5818
  const armRun = arms[i].run, cls = classes[i], staticExpected = armExpected[i], next = tail;
5774
- tail = (input, pos, ctx, lead2, acc, best) => {
5819
+ tail = (input, pos, ctx, lead2, acc, best, entered) => {
5775
5820
  if (cls !== void 0 && !classHas(cls, lead2)) {
5776
5821
  return next(
5777
5822
  input,
@@ -5779,7 +5824,8 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
5779
5824
  ctx,
5780
5825
  lead2,
5781
5826
  best === pos ? appendExpected(acc, staticExpected) : acc,
5782
- best
5827
+ best,
5828
+ entered
5783
5829
  );
5784
5830
  }
5785
5831
  ctx._fc = false;
@@ -5795,7 +5841,7 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
5795
5841
  if (nextAcc !== void 0) ctx._fx = nextAcc;
5796
5842
  return -1;
5797
5843
  }
5798
- return next(input, pos, ctx, lead2, nextAcc, best);
5844
+ return next(input, pos, ctx, lead2, nextAcc, best, true);
5799
5845
  };
5800
5846
  }
5801
5847
  return {
@@ -5806,7 +5852,8 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
5806
5852
  ctx,
5807
5853
  pos < input.length ? input.codePointAt(pos) : -1,
5808
5854
  void 0,
5809
- pos
5855
+ pos,
5856
+ false
5810
5857
  )
5811
5858
  };
5812
5859
  }
@@ -5844,6 +5891,7 @@ function exclusiveChoiceNode(arms, classes, choiceFx) {
5844
5891
  return -1;
5845
5892
  }
5846
5893
  }
5894
+ if (arm < 0 && ctx._probe !== void 0) for (const a of arms) a.run(input, pos, ctx);
5847
5895
  ctx._fe = pos;
5848
5896
  ctx._fx = choiceFx;
5849
5897
  return -1;
@@ -6068,6 +6116,7 @@ function buildLexProgramRunner(spec, matchers, classes, expected) {
6068
6116
  if (!classHas(cls, lead2)) {
6069
6117
  ctx._fe = pos;
6070
6118
  ctx._fx = choiceFx.slice();
6119
+ if (ctx._probe !== void 0) failAt(ctx, [...x0, ...x1, ...x2, ...x3], pos);
6071
6120
  return -1;
6072
6121
  }
6073
6122
  let acc;
@@ -6179,7 +6228,7 @@ function validateDispatchSpec(spec, arity, fallbackRouted) {
6179
6228
  for (const arm of arms) if (!Number.isInteger(arm) || arm < 0 || arm >= arity) return true;
6180
6229
  return false;
6181
6230
  };
6182
- 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]]))) {
6231
+ 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]]))) {
6183
6232
  throw new TypeError("table: malformed dispatch");
6184
6233
  }
6185
6234
  }
@@ -7254,6 +7303,7 @@ function classifyRuleMap(ruleMap) {
7254
7303
  }
7255
7304
 
7256
7305
  // src/table/factor-choice.ts
7306
+ var factoredChoices = /* @__PURE__ */ new WeakSet();
7257
7307
  var DERIVED = /* @__PURE__ */ new Set(["_tag", "_meta", "_def", "_parseScalar", "_stickyRegex", "parse"]);
7258
7308
  function isCombinator(v) {
7259
7309
  return typeof v === "object" && v !== null && "_def" in v && "_meta" in v;
@@ -7345,6 +7395,7 @@ function factorSharedEnds(d) {
7345
7395
  for (let i = at + 1; i < n; i++) if (!never(first.terms[i])) return void 0;
7346
7396
  if (arms.some((a) => mayCommitFailure(a))) return void 0;
7347
7397
  const middle = choice(...shapes.map((s) => s.terms[at]));
7398
+ factoredChoices.add(middle);
7348
7399
  if (middle._def.tag === "choice" && middle._def.autoNot.some((a) => a !== null)) return void 0;
7349
7400
  const st = middle._def.tag === "choice" ? middle._def.strategy : void 0;
7350
7401
  if (st === void 0 || st.tag !== "firstMatch" && st.tag !== "sharedPrefix" && !(st.tag === "literalsLongestFirst" && st.sortedIndices.every((x, i) => x === i))) return void 0;
@@ -7357,6 +7408,37 @@ function factorSharedEnds(d) {
7357
7408
  for (let j = first.wrappers.length - 1; j >= 0 && out !== void 0; j--) out = rewrap(first.wrappers[j], out);
7358
7409
  return out === void 0 ? void 0 : attempt(out);
7359
7410
  }
7411
+ function factorPrefixArms(d) {
7412
+ const arms = d.parsers;
7413
+ if (arms.length < 2) return void 0;
7414
+ if (d.strategy.tag !== "firstMatch" && d.strategy.tag !== "sharedPrefix") return void 0;
7415
+ if (d.gates.some((g) => g !== null) || d.autoNot.some((a) => a !== null)) return void 0;
7416
+ const out = [...arms];
7417
+ let changed = false;
7418
+ for (let i = 0; i + 1 < arms.length; i++) {
7419
+ const arm = arms[i];
7420
+ if (arm._def.tag !== "sequence" || arm._def.valueUnused !== true) continue;
7421
+ const terms = arm._def.parsers;
7422
+ const next = arms[i + 1];
7423
+ const prefix = next._def.tag === "sequence" && next._def.valueUnused === true ? next._def.parsers : [next];
7424
+ if (prefix.length >= terms.length) continue;
7425
+ if (!prefix.every((t, j) => sameComb(t, terms[j], 0))) continue;
7426
+ const rest = terms.slice(prefix.length);
7427
+ if (rest.every(never)) continue;
7428
+ const tail = rest.length === 1 ? rest[0] : sequence(...rest);
7429
+ if (tail !== rest[0]) tail._def.valueUnused = true;
7430
+ const parts = [...terms.slice(0, prefix.length), optional(tail)];
7431
+ const seq = sequence(...parts);
7432
+ seq._def.valueUnused = true;
7433
+ out[i] = seq;
7434
+ changed = true;
7435
+ }
7436
+ if (!changed) return void 0;
7437
+ const rebuilt = choice(...out);
7438
+ factoredChoices.add(rebuilt);
7439
+ const st = rebuilt._def.tag === "choice" ? rebuilt._def.strategy : void 0;
7440
+ return st !== void 0 && (st.tag === "firstMatch" || st.tag === "sharedPrefix") ? rebuilt : void 0;
7441
+ }
7360
7442
 
7361
7443
  // src/table/ops.ts
7362
7444
  var OP_LIT = 1;
@@ -7402,6 +7484,9 @@ var OP_LEX_BODY = 42;
7402
7484
  var OP_LEX_PROGRAM = 43;
7403
7485
  var OP_SOURCE_LEAF = 44;
7404
7486
  var OP_RGATE = 45;
7487
+ var OP_GAPGATE = 46;
7488
+ var OP_RGATE2 = 47;
7489
+ var OP_STRICT = 48;
7405
7490
  var OP_DISPATCH = 25;
7406
7491
  var OP_ROUTED = 26;
7407
7492
  var OP_NAMES = {
@@ -7449,7 +7534,10 @@ var OP_NAMES = {
7449
7534
  [OP_COV]: "COV",
7450
7535
  [OP_SCOPE_PLAIN]: "SCOPE_PLAIN",
7451
7536
  [OP_LEX_BODY]: "LEX_BODY",
7452
- [OP_LEX_PROGRAM]: "LEX_PROGRAM"
7537
+ [OP_LEX_PROGRAM]: "LEX_PROGRAM",
7538
+ [OP_GAPGATE]: "GAPGATE",
7539
+ [OP_RGATE2]: "RGATE2",
7540
+ [OP_STRICT]: "STRICT"
7453
7541
  };
7454
7542
 
7455
7543
  // src/compiler/direct-projection.ts
@@ -7740,6 +7828,7 @@ function directFixedLexNode(parser2, resolve, active, pureOnly = false, cleared
7740
7828
  arms,
7741
7829
  parsers: def.parsers,
7742
7830
  cleared,
7831
+ disjoint: def.disjoint,
7743
7832
  armExpected: def.parsers.map(deriveExpected),
7744
7833
  expected: def.parsers.flatMap(deriveExpected)
7745
7834
  };
@@ -7849,6 +7938,29 @@ var UnsupportedConstruct = class extends Error {
7849
7938
  this.tag = tag;
7850
7939
  }
7851
7940
  };
7941
+ function firstTwo(p) {
7942
+ const d = p._def;
7943
+ if (d.tag === "label" || d.tag === "grammar") return firstTwo(d.parser);
7944
+ let two;
7945
+ if (d.tag === "literal" && d.value.length >= 2 && d.caseInsensitive !== true) {
7946
+ const second = d.value.codePointAt(1);
7947
+ two = { first: { kind: "ranges", ranges: [{ lo: d.value.charCodeAt(0), hi: d.value.charCodeAt(0) }] }, second: { kind: "ranges", ranges: [{ lo: second, hi: second }] } };
7948
+ } else if (d.tag === "regex" && !d.flags.includes("i")) {
7949
+ const units = regexFirstTwo(d.source, d.flags);
7950
+ if (units !== void 0) two = units;
7951
+ }
7952
+ if (two === void 0 || two.first.kind !== "ranges") return void 0;
7953
+ return two.first.ranges.every((r) => r.hi < 55296 || r.lo > 57343 && r.hi <= 65535) ? two : void 0;
7954
+ }
7955
+ var GAP_CHARS = /* @__PURE__ */ new Set([9, 10, 12, 13, 32, 47]);
7956
+ function isGap(p) {
7957
+ if (matchesEmpty(p)) return true;
7958
+ const fs = p._meta.firstSet;
7959
+ return fs.kind === "ranges" && fs.ranges.every((r) => {
7960
+ for (let c = r.lo; c <= r.hi; c++) if (!GAP_CHARS.has(c)) return false;
7961
+ return true;
7962
+ });
7963
+ }
7852
7964
  var Encoder = class {
7853
7965
  code = [];
7854
7966
  k = [];
@@ -7896,8 +8008,33 @@ var Encoder = class {
7896
8008
  */
7897
8009
  commitFailureMemo = /* @__PURE__ */ new Map();
7898
8010
  labels = void 0;
8011
+ /** The choice being encoded as written, for `OP_STRICT`'s tolerant child. */
8012
+ unfactoredChoice = void 0;
7899
8013
  /** Trivia table in scope at the row being encoded — codegen's `ctx.activeTrivia`. */
7900
8014
  activeTrivia = void 0;
8015
+ /**
8016
+ * Is the trivia live at this row known? It is not in the body of an entry
8017
+ * that installs none of its own: that body runs under whatever trivia its
8018
+ * caller has (a compiled rule inside another grammar, `run({ trivia })`).
8019
+ * Every scope (`parser({ trivia })`, `noTrivia`, `token()`, a rule's install)
8020
+ * makes it known again.
8021
+ */
8022
+ triviaKnown = true;
8023
+ /**
8024
+ * The trivia a gate's first set is derived under: the live table, `null` for a
8025
+ * scope known to have none, or `undefined` where it is not known.
8026
+ */
8027
+ gateTrivia() {
8028
+ return this.activeTrivia ?? (this.triviaKnown ? null : void 0);
8029
+ }
8030
+ /**
8031
+ * Whether a gate may read a nullable pad through its non-empty matches
8032
+ * (`firstSetOf`'s `consuming`): only where the trivia that may follow the pad
8033
+ * is known. Elsewhere a gate reads first sets as 0.58.0 did.
8034
+ */
8035
+ gateConsuming() {
8036
+ return this.activeTrivia !== void 0 || this.triviaKnown;
8037
+ }
7901
8038
  classified = false;
7902
8039
  scans = [];
7903
8040
  /** Ambient `scanSkip` sets, pooled by the ARRAY's identity (stable per grammar). */
@@ -8024,10 +8161,12 @@ var Encoder = class {
8024
8161
  const final = classifyFinalChoice(
8025
8162
  node2.parsers,
8026
8163
  this.refResolver(),
8027
- node2.cleared ? void 0 : this.activeTrivia
8164
+ node2.cleared ? null : this.gateTrivia(),
8165
+ false,
8166
+ node2.disjoint
8028
8167
  );
8029
8168
  const classes = final.firstSets.map((first, i) => final.nullable[i] ? -1 : this.charClass(first));
8030
- const exclusive = final.exclusive && classes.every((cls) => cls >= 0);
8169
+ 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;
8031
8170
  words.push(LEX_NODE_CHOICE, node2.arms.length, exclusive ? 1 : 0, this.expected(node2.expected));
8032
8171
  for (let i = 0; i < node2.arms.length; i++) {
8033
8172
  words.push(classes[i], this.expected(node2.armExpected[i]));
@@ -8182,16 +8321,117 @@ var Encoder = class {
8182
8321
  kIndex = /* @__PURE__ */ new Map();
8183
8322
  ccIndex = /* @__PURE__ */ new Map();
8184
8323
  fxIndex = /* @__PURE__ */ new Map();
8185
- /** Memoized by combinator identity: a shared sub-combinator is ONE row. */
8186
- memo = /* @__PURE__ */ new Map();
8187
8324
  /**
8188
- * Memo for `lazy` REFERENCES, keyed by the trivia scope the reference is made
8189
- * from. A cross-rule reference's row depends on that scope (`scopedRef`), so the
8190
- * single `memo` slot every other construct uses would freeze the FIRST
8191
- * reference's scope onto every later one and undo the fix.
8325
+ * Memoized by combinator identity AND the trivia scope it is encoded under
8326
+ * (`scopeKey`): a shared sub-combinator is ONE row per scope it runs in.
8327
+ *
8328
+ * A construct's rows depend on that scope. A first set derived through a
8329
+ * nullable term takes in the trivia that may follow it, a gap gate holds only
8330
+ * where no trivia runs, a routed gate reads what may follow its token, and a
8331
+ * cross-rule reference installs its rule's trivia only from a cleared region
8332
+ * (`scopedRef`). One memo slot per combinator froze the FIRST scope it was
8333
+ * reached under onto every later one: a rule body encoded as an entry with no
8334
+ * trivia, then run under a caller's comment trivia, gated its repeats as if no
8335
+ * comment could come between their terms.
8192
8336
  */
8193
- refMemo = /* @__PURE__ */ new Map();
8337
+ memo = /* @__PURE__ */ new Map();
8194
8338
  pending = /* @__PURE__ */ new Map();
8339
+ triviaSignatures = /* @__PURE__ */ new Map();
8340
+ /**
8341
+ * What of the live trivia an encoding can depend on: none, or its first set.
8342
+ * Every derivation reads trivia only through `firstSetOf(trivia)` or whether it
8343
+ * is live, so two scopes whose trivia opens on the same characters encode the
8344
+ * same rows and share them. A construct that reads no scope (`readsScope`)
8345
+ * has one encoding.
8346
+ */
8347
+ scopeKey(p) {
8348
+ const t = this.activeTrivia;
8349
+ if (!this.readsScope(p)) return "";
8350
+ if (t === void 0) return this.triviaKnown ? "" : "?";
8351
+ let sig = this.triviaSignatures.get(t);
8352
+ if (sig === void 0) {
8353
+ sig = `t${JSON.stringify(firstSetOf(t, /* @__PURE__ */ new Set(), this.refResolver()))}`;
8354
+ this.triviaSignatures.set(t, sig);
8355
+ }
8356
+ return sig;
8357
+ }
8358
+ readsScopeMemo = /* @__PURE__ */ new Map();
8359
+ /**
8360
+ * Can `p`'s rows depend on the trivia scope it is encoded under? No for a
8361
+ * terminal, a scope of its own (`parser({ trivia })`, `noTrivia`, `token()`),
8362
+ * and a wrapper over a construct that reads none; yes wherever a first set, a gate or
8363
+ * a reference's install is derived with the scope: a sequence with a nullable
8364
+ * term before another (the trivia between them), a routed arm (the trivia after
8365
+ * its token), a reference that installs its rule's trivia from a cleared
8366
+ * region, and anything not modelled here — a cycle included.
8367
+ */
8368
+ readsScope(p) {
8369
+ const hit = this.readsScopeMemo.get(p);
8370
+ if (hit !== void 0) return hit;
8371
+ this.readsScopeMemo.set(p, true);
8372
+ const d = p._def;
8373
+ const rr = this.refResolver();
8374
+ let out;
8375
+ switch (d.tag) {
8376
+ case "literal":
8377
+ case "regex":
8378
+ case "keywords":
8379
+ case "token":
8380
+ out = false;
8381
+ break;
8382
+ // `routed()` encodes its fallback under the scope that reaches it.
8383
+ case "routed":
8384
+ out = d.fallback !== void 0 && this.readsScope(d.fallback);
8385
+ break;
8386
+ case "grammar":
8387
+ out = d.clearTrivia !== true && d.triviaParser === void 0 && this.readsScope(d.parser);
8388
+ break;
8389
+ case "node":
8390
+ case "label":
8391
+ case "leaf":
8392
+ case "transform":
8393
+ case "attempt":
8394
+ case "expect":
8395
+ case "not":
8396
+ case "peek":
8397
+ case "optional":
8398
+ case "many":
8399
+ case "oneOrMore":
8400
+ case "field":
8401
+ out = this.readsScope(d.parser);
8402
+ break;
8403
+ case "sepBy":
8404
+ out = this.readsScope(d.parser) || this.readsScope(d.separator);
8405
+ break;
8406
+ case "choice":
8407
+ out = d.parsers.some((a) => this.readsScope(a));
8408
+ break;
8409
+ case "dispatch":
8410
+ 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);
8411
+ break;
8412
+ case "sequence": {
8413
+ const ps = d.parsers;
8414
+ 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));
8415
+ break;
8416
+ }
8417
+ case "lazy": {
8418
+ let target = winnerForRef(p, rr);
8419
+ if (target === void 0) {
8420
+ try {
8421
+ target = d.thunk();
8422
+ } catch {
8423
+ target = void 0;
8424
+ }
8425
+ }
8426
+ out = target === void 0 || triviaThroughRef(p, target, null) !== null || this.readsScope(target);
8427
+ break;
8428
+ }
8429
+ default:
8430
+ out = true;
8431
+ }
8432
+ this.readsScopeMemo.set(p, out);
8433
+ return out;
8434
+ }
8195
8435
  settings;
8196
8436
  /** Resolved once, HERE, at table-build time — never consulted at run time. */
8197
8437
  track;
@@ -8209,8 +8449,8 @@ var Encoder = class {
8209
8449
  /**
8210
8450
  * The class of the follow set of term `i` in a sequence — the sync sentinel a
8211
8451
  * list nested in that term resyncs to, INFERRED from structure exactly as
8212
- * `sequence()` (combinators/sequence.ts:58) and `emitSeqValues`
8213
- * (compiler/codegen.ts:1756) infer it: the union of every LATER term's first
8452
+ * `sequence()` (combinators/sequence.ts:58) and the source
8453
+ * lowering's `emitSeqValues` infer it: the union of every LATER term's first
8214
8454
  * set, not merely up to the first non-nullable one, so a mandatory middle term
8215
8455
  * cannot hide a later close. −1 where there is no usable sentinel, which is the
8216
8456
  * same set of cases `firstSetSentinel` answers `null` for.
@@ -8219,7 +8459,7 @@ var Encoder = class {
8219
8459
  let fs = { kind: "empty" };
8220
8460
  const rr = this.refResolver();
8221
8461
  for (let j = i + 1; j < parsers.length; j++) {
8222
- fs = union(fs, firstSetOf(parsers[j], /* @__PURE__ */ new Set(), rr, this.activeTrivia));
8462
+ fs = union(fs, firstSetOf(parsers[j], /* @__PURE__ */ new Set(), rr, void 0, false));
8223
8463
  }
8224
8464
  return this.charClass(fs);
8225
8465
  }
@@ -8387,7 +8627,7 @@ var Encoder = class {
8387
8627
  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;
8388
8628
  const rr = this.refResolver();
8389
8629
  if (matchesEmpty(p, /* @__PURE__ */ new Set(), rr)) return child;
8390
- const cls = this.charClass(firstSetOf(p, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
8630
+ const cls = this.charClass(firstSetOf(p, /* @__PURE__ */ new Set(), rr, this.activeTrivia, false));
8391
8631
  if (cls < 0) return child;
8392
8632
  const e = deriveExpected(p);
8393
8633
  return this.emit(OP_GATE, cls, child, this.expected(e.length > 0 ? e : [p._tag]));
@@ -8413,9 +8653,27 @@ var Encoder = class {
8413
8653
  * `{`, `Call` at `}`). The resolved set is the one a choice arm's gate uses,
8414
8654
  * over the final merged map.
8415
8655
  */
8416
- gateFirstSet(p) {
8656
+ /**
8657
+ * The arms whose first set under `gateTrivia()` is wider than under the
8658
+ * active trivia alone — a reference's installed trivia read through from a
8659
+ * region with none. Empty for construction-set (disjoint) choices, whose
8660
+ * sets do not depend on trivia.
8661
+ */
8662
+ wideArms(arms, wide, constructionSets, wideTrivia = this.gateTrivia(), base = this.activeTrivia) {
8663
+ if (constructionSets || wideTrivia === base) return [];
8664
+ const narrow = classifyFinalChoice(arms, this.refResolver(), base, false, false).firstSets;
8665
+ const out = [];
8666
+ for (let i = 0; i < arms.length; i++) {
8667
+ if (this.charClass(wide[i]) !== this.charClass(narrow[i])) out.push(i);
8668
+ }
8669
+ return out;
8670
+ }
8671
+ gateFirstSet(p, guarded, narrowed) {
8417
8672
  const cached = p._meta.firstSet;
8418
- return p._def.tag === "lazy" || cached.kind === "any" ? firstSetOf(p, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : cached;
8673
+ if (p._def.tag !== "lazy" && cached.kind !== "any") return cached;
8674
+ const rr = this.refResolver();
8675
+ if (guarded) return firstSetOf(p, /* @__PURE__ */ new Set(), rr, void 0, false);
8676
+ return firstSetOf(p, /* @__PURE__ */ new Set(), rr, this.gateTrivia(), narrowed && this.gateConsuming());
8419
8677
  }
8420
8678
  /**
8421
8679
  * Wrap one choice arm in its `autoNot` checks (`OP_REJECT`).
@@ -8447,6 +8705,135 @@ var Encoder = class {
8447
8705
  for (const w of words) this.code.push(w);
8448
8706
  return ip;
8449
8707
  }
8708
+ /**
8709
+ * An optional repeat item tests the item's class before it runs it (`OP_REP`
8710
+ * ip+7), wherever the item's own first-set gate would also run. So a sequence
8711
+ * item whose first term is a gate on that same class gets its own copy of the
8712
+ * sequence row with the gate's child as that term: the test runs once. The
8713
+ * shared row is left as it is for its other references.
8714
+ */
8715
+ withoutLeadGate(seq, cls) {
8716
+ const op = this.code[seq];
8717
+ if (op !== OP_SEQ && op !== OP_SEQV) return seq;
8718
+ const first = this.code[seq + 2];
8719
+ if (this.code[first] !== OP_GATE || this.code[first + 1] !== cls) return seq;
8720
+ const n = this.code[seq + 1];
8721
+ const width = 1 + 2 * n;
8722
+ const head = this.emitHead(op, width);
8723
+ for (let i = 1; i <= width; i++) this.code[head + i] = this.code[seq + i];
8724
+ this.code[head + 2] = this.code[first + 2];
8725
+ return head;
8726
+ }
8727
+ /**
8728
+ * An optional repeat item that opens with a GAP — a regex that may match
8729
+ * empty, or one that starts only with whitespace or a comment's `/` — and
8730
+ * then a term with a finite first set gets an `OP_GAPGATE` row
8731
+ * (`gap-gate.ts`): the repeat reads the gap once, ends where the character
8732
+ * after it opens neither that term nor an arm without the gap, and hands the
8733
+ * gap's end to the regex row. Anything the walk cannot prove keeps the item as
8734
+ * written.
8735
+ */
8736
+ gapGated(item, ip) {
8737
+ if (this.activeTrivia === void 0 && !this.triviaKnown) return ip;
8738
+ const lead2 = this.gapLead(item, this.activeTrivia, 0);
8739
+ if (lead2 === void 0) return ip;
8740
+ const cls = this.charClass(lead2.follow);
8741
+ if (cls < 0) return ip;
8742
+ const other = lead2.other.kind === "empty" ? -1 : this.charClass(lead2.other);
8743
+ if (lead2.other.kind !== "empty" && other < 0) return ip;
8744
+ const p = this.node(lead2.p).ip;
8745
+ if (this.code[p] !== OP_RX && this.code[p] !== OP_RX_TRACK) return ip;
8746
+ return this.emit(OP_GAPGATE, ip, p, cls, other);
8747
+ }
8748
+ /**
8749
+ * The gap an item opens with: the regex `p`, the first set `follow` of what
8750
+ * must come straight after it, and the first set `other` of choice arms that
8751
+ * do not open with `p`. Only through wrappers that consume nothing before
8752
+ * their body, a sequence's first term, and a choice's arms; the gap must sit
8753
+ * in a sequence with no trivia between its terms.
8754
+ */
8755
+ gapLead(c, trivia2, depth) {
8756
+ if (depth > 24) return void 0;
8757
+ const rr = this.refResolver();
8758
+ const d = c._def;
8759
+ switch (d.tag) {
8760
+ case "lazy": {
8761
+ let target = winnerForRef(c, rr);
8762
+ if (target === void 0) {
8763
+ try {
8764
+ target = d.thunk();
8765
+ } catch {
8766
+ return void 0;
8767
+ }
8768
+ }
8769
+ return this.gapLead(target, triviaThroughRef(c, target, trivia2), depth + 1);
8770
+ }
8771
+ case "node":
8772
+ case "leaf":
8773
+ case "label":
8774
+ case "attempt":
8775
+ return this.gapLead(d.parser, trivia2, depth + 1);
8776
+ case "field":
8777
+ return d.leadingTrivia === true ? void 0 : this.gapLead(d.parser, trivia2, depth + 1);
8778
+ case "grammar":
8779
+ return this.gapLead(d.parser, d.clearTrivia === true ? null : d.triviaParser ?? trivia2, depth + 1);
8780
+ case "sequence": {
8781
+ const [first, ...rest] = d.parsers;
8782
+ if (first === void 0) return void 0;
8783
+ if (first._def.tag !== "regex" || !isGap(first)) {
8784
+ return this.gapLead(first, trivia2, depth + 1);
8785
+ }
8786
+ if (trivia2 !== void 0 && trivia2 !== null || rest.length === 0) return void 0;
8787
+ let follow = empty();
8788
+ let closed = false;
8789
+ for (const t of rest) {
8790
+ const fs = firstSetOf(t, /* @__PURE__ */ new Set(), rr, null);
8791
+ if (fs.kind === "any") return void 0;
8792
+ follow = union(follow, fs);
8793
+ if (!matchesEmpty(t, /* @__PURE__ */ new Set(), rr)) {
8794
+ closed = true;
8795
+ break;
8796
+ }
8797
+ }
8798
+ return closed ? { p: first, follow, other: empty() } : void 0;
8799
+ }
8800
+ case "choice": {
8801
+ let p;
8802
+ let follow = empty();
8803
+ let other = empty();
8804
+ for (const arm of d.parsers) {
8805
+ const lead2 = this.gapLead(arm, trivia2, depth + 1);
8806
+ if (lead2 !== void 0 && (p === void 0 || lead2.p === p)) {
8807
+ p = lead2.p;
8808
+ follow = union(follow, lead2.follow);
8809
+ other = union(other, lead2.other);
8810
+ continue;
8811
+ }
8812
+ if (matchesEmpty(arm, /* @__PURE__ */ new Set(), rr)) return void 0;
8813
+ const fs = firstSetOf(arm, /* @__PURE__ */ new Set(), rr, trivia2 ?? null);
8814
+ if (fs.kind === "any") return void 0;
8815
+ other = union(other, fs);
8816
+ }
8817
+ return p === void 0 ? void 0 : { p, follow, other };
8818
+ }
8819
+ default:
8820
+ return void 0;
8821
+ }
8822
+ }
8823
+ /**
8824
+ * The class a strict list may end on — a character its separator `p` (row
8825
+ * `ip`) cannot start — without entering the separator, or −1.
8826
+ *
8827
+ * The test runs before every separator, to save the one that fails at the
8828
+ * list's end, so it is laid down only where entering the separator costs more
8829
+ * than the test: not on a terminal row, which is itself one character test.
8830
+ */
8831
+ sepGate(p, ip) {
8832
+ const op = this.code[ip];
8833
+ 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;
8834
+ if (matchesEmpty(p, /* @__PURE__ */ new Set(), this.refResolver())) return -1;
8835
+ return this.charClass(firstSetOf(p, /* @__PURE__ */ new Set(), this.refResolver(), this.gateTrivia(), this.gateConsuming()));
8836
+ }
8450
8837
  /** Reserve `n` operand slots to patch once children are laid out. */
8451
8838
  emitHead(op, n) {
8452
8839
  const ip = this.code.length;
@@ -8490,8 +8877,10 @@ var Encoder = class {
8490
8877
  }
8491
8878
  encodeRule(name, p) {
8492
8879
  this.activeTrivia = p._meta.grammarTrivia;
8880
+ this.triviaKnown = p._meta.grammarTrivia !== void 0;
8493
8881
  const body = this.node(p).ip;
8494
8882
  this.activeTrivia = void 0;
8883
+ this.triviaKnown = true;
8495
8884
  const gss = p._meta.grammarScanSkip;
8496
8885
  this.scanSkipOf.push(gss === void 0 ? -1 : this.scanSkipSlot(gss));
8497
8886
  const amb = p._meta.grammarTrivia;
@@ -8537,11 +8926,15 @@ var Encoder = class {
8537
8926
  * silently produced the ONE keyword `bc`.
8538
8927
  */
8539
8928
  scopedRef(p, target) {
8929
+ const runsUnder = triviaThroughRef(p, target, this.activeTrivia);
8930
+ if (this.activeTrivia !== void 0 || runsUnder === void 0 || runsUnder === null) return this.node(target).ip;
8931
+ const savedKnown = this.triviaKnown;
8932
+ this.activeTrivia = runsUnder;
8933
+ this.triviaKnown = true;
8540
8934
  const ip = this.node(target).ip;
8541
- const amb = p._meta.grammarTrivia ?? target._meta.grammarTrivia;
8542
- if (amb === void 0 || this.activeTrivia !== void 0) return ip;
8543
- if (!hasOwnTriviaBoundary(target)) return ip;
8544
- return this.emit(OP_SCOPE_PLAIN, this.triviaSlot(amb), ip);
8935
+ this.activeTrivia = void 0;
8936
+ this.triviaKnown = savedKnown;
8937
+ return this.emit(OP_SCOPE_PLAIN, this.triviaSlot(runsUnder), ip);
8545
8938
  }
8546
8939
  /**
8547
8940
  * The `OP_RGATE` operands of a choice arm that opens with `routed()`, or
@@ -8582,38 +8975,43 @@ var Encoder = class {
8582
8975
  if (matchesEmpty(next, /* @__PURE__ */ new Set(), rr) || mayCommitFailure(next, rr, this.commitFailureMemo)) return void 0;
8583
8976
  const nextDef = next._def;
8584
8977
  if (nextDef.tag !== "literal" && nextDef.tag !== "regex" && nextDef.tag !== "keywords" && nextDef.tag !== "label") return void 0;
8585
- const first = firstSetOf(next, /* @__PURE__ */ new Set(), rr, boundary);
8978
+ if (boundary === void 0 && !this.triviaKnown) return void 0;
8979
+ const first = firstSetOf(next, /* @__PURE__ */ new Set(), rr, boundary ?? null);
8586
8980
  const cls = this.charClass(boundary === void 0 ? first : union(first, firstSetOf(boundary, /* @__PURE__ */ new Set(), rr)));
8587
8981
  if (cls < 0) return void 0;
8588
- const miss = nextDef.tag === "label" ? deriveExpected(next) : directTerminalFailureExpected(nextDef);
8589
- return { cls, fx: this.expected(miss) };
8982
+ const fx = this.expected(nextDef.tag === "label" ? deriveExpected(next) : directTerminalFailureExpected(nextDef));
8983
+ const two = firstTwo(next);
8984
+ if (two === void 0) return { cls, fx };
8985
+ if (boundary !== void 0 && intersects(two.first, firstSetOf(boundary, /* @__PURE__ */ new Set(), rr))) return { cls, fx };
8986
+ const a = this.charClass(two.first);
8987
+ const b = this.charClass(two.second);
8988
+ return a < 0 || b < 0 ? { cls, fx } : { cls, fx, two: [a, b] };
8590
8989
  }
8591
8990
  node(p) {
8592
- const lazyRef = p._def.tag === "lazy";
8593
- const scopeKey = lazyRef ? this.activeTrivia : void 0;
8594
- const refMemo = this.refMemo;
8595
- const hit = lazyRef ? refMemo.get(p)?.get(scopeKey) : this.memo.get(p);
8991
+ const key = this.scopeKey(p);
8992
+ let byScope = this.memo.get(p);
8993
+ const hit = byScope?.get(key);
8596
8994
  if (hit !== void 0) return { ip: hit };
8597
- const inFlight = this.pending.get(p);
8995
+ let inFlightByScope = this.pending.get(p);
8996
+ const inFlight = inFlightByScope?.get(key);
8598
8997
  if (inFlight !== void 0) {
8599
8998
  const ip2 = this.emitHead(OP_RULE, 1);
8600
8999
  inFlight.push(ip2 + 1);
8601
9000
  return { ip: ip2 };
8602
9001
  }
8603
9002
  const patches = [];
8604
- this.pending.set(p, patches);
9003
+ if (inFlightByScope === void 0) {
9004
+ inFlightByScope = /* @__PURE__ */ new Map();
9005
+ this.pending.set(p, inFlightByScope);
9006
+ }
9007
+ inFlightByScope.set(key, patches);
8605
9008
  const ip = this.covWrap(this.encodeDef(p), this.plan?.rules.get(p), 0);
8606
- this.pending.delete(p);
8607
- if (lazyRef) {
8608
- let byScope = refMemo.get(p);
8609
- if (byScope === void 0) {
8610
- byScope = /* @__PURE__ */ new Map();
8611
- refMemo.set(p, byScope);
8612
- }
8613
- byScope.set(scopeKey, ip);
8614
- } else {
8615
- this.memo.set(p, ip);
9009
+ inFlightByScope.delete(key);
9010
+ if (byScope === void 0) {
9011
+ byScope = /* @__PURE__ */ new Map();
9012
+ this.memo.set(p, byScope);
8616
9013
  }
9014
+ byScope.set(key, ip);
8617
9015
  for (const slot of patches) {
8618
9016
  if (slot - 1 === ip) {
8619
9017
  const rule = p._ruleName;
@@ -8711,8 +9109,19 @@ var Encoder = class {
8711
9109
  }
8712
9110
  case "choice": {
8713
9111
  if (this.plan === void 0) {
8714
- const factored = factorSharedEnds(d);
8715
- if (factored !== void 0) return this.node(factored).ip;
9112
+ const shared = factorSharedEnds(d);
9113
+ if (shared !== void 0) return this.node(shared).ip;
9114
+ if (this.unfactoredChoice !== p) {
9115
+ const prefix = factorPrefixArms(d);
9116
+ if (prefix !== void 0) {
9117
+ const strict = this.node(prefix).ip;
9118
+ const saved = this.unfactoredChoice;
9119
+ this.unfactoredChoice = p;
9120
+ const tolerant = this.encodeDef(p);
9121
+ this.unfactoredChoice = saved;
9122
+ return this.emit(OP_STRICT, strict, tolerant);
9123
+ }
9124
+ }
8716
9125
  }
8717
9126
  if (d.strategy.tag === "greedyClassify") {
8718
9127
  const superIndex = d.strategy.superIndex;
@@ -8741,18 +9150,20 @@ var Encoder = class {
8741
9150
  const plain = (checks === null || checks === void 0 || checks.length === 0) && (gate2 === null || gate2 === void 0);
8742
9151
  const routedGate = plain ? this.routedArmGate(c) : void 0;
8743
9152
  const body = this.node(c).ip;
8744
- const inner = routedGate === void 0 ? body : this.emit(OP_RGATE, routedGate.cls, body, routedGate.fx);
9153
+ 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]);
8745
9154
  const ip = checks === null || checks === void 0 || checks.length === 0 ? inner : this.reject(inner, checks);
8746
9155
  const gated = gate2 === null || gate2 === void 0 ? ip : this.emit(OP_ARMGATE, this.fn(gate2, d.gateSrcs?.[src] ?? null), ip, this.expected(deriveExpected(c)));
8747
9156
  return this.covWrap(gated, armCovIds?.[src], 1);
8748
9157
  });
8749
9158
  const rr = this.refResolver();
8750
- const finalChoice = classifyFinalChoice(arms, rr, this.activeTrivia);
9159
+ const constructionSets = d.disjoint && !factoredChoices.has(p);
9160
+ const finalChoice = classifyFinalChoice(arms, rr, this.gateTrivia(), false, constructionSets);
9161
+ const wideArms = this.wideArms(arms, finalChoice.firstSets, constructionSets);
8751
9162
  const transactionalFirst = arms.map((arm, i) => {
8752
9163
  if (!finalChoice.nullable[i]) return void 0;
8753
9164
  const def = arm._def;
8754
9165
  if (def.tag !== "attempt" || matchesEmpty(def.parser, /* @__PURE__ */ new Set(), rr)) return void 0;
8755
- return firstSetOf(def.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia);
9166
+ return firstSetOf(def.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia, false);
8756
9167
  });
8757
9168
  const gateFirst = finalChoice.firstSets.map((first, i) => transactionalFirst[i] ?? first);
8758
9169
  const classes = gateFirst.map((first, i) => finalChoice.nullable[i] && transactionalFirst[i] === void 0 ? -1 : this.charClass(first));
@@ -8770,6 +9181,9 @@ var Encoder = class {
8770
9181
  if (transactionalFirst[i] !== void 0) classes[i] = -1;
8771
9182
  }
8772
9183
  }
9184
+ if (wideArms.length > 0 && dispatchIsExclusive(classes, this.cc)) {
9185
+ for (const i of wideArms) classes[i] = -1;
9186
+ }
8773
9187
  if (d.gates.some((g) => g !== null && g !== void 0) && !dispatchIsExclusive(classes, this.cc)) {
8774
9188
  for (let k = 0; k < classes.length; k++) {
8775
9189
  const gate2 = d.gates[order[k]];
@@ -8803,27 +9217,34 @@ var Encoder = class {
8803
9217
  }
8804
9218
  case "many":
8805
9219
  case "oneOrMore": {
8806
- const child = this.node(d.parser).ip;
8807
9220
  const rr = this.refResolver();
8808
- const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.charClass(firstSetOf(d.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
9221
+ const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.charClass(this.gateFirstSet(d.parser, d.guarded === true, true));
8809
9222
  const op = d.valueUnused ? OP_REPV : OP_REP;
8810
9223
  const min = d.tag === "many" ? 0 : d.min;
9224
+ const item = min === 0 && itemClass >= 0 ? this.withoutLeadGate(this.node(d.parser).ip, itemClass) : this.node(d.parser).ip;
9225
+ const child = min === 0 && itemClass >= 0 ? this.gapGated(d.parser, item) : item;
8811
9226
  return this.emit(op, child, min, d.max ?? -1, -1, 0, this.expected(deriveExpected(d.parser)), itemClass);
8812
9227
  }
8813
9228
  case "sepBy": {
8814
9229
  const child = this.node(d.parser).ip;
8815
9230
  const sep = this.node(d.separator).ip;
8816
- const flags = (d.trailing === "allow" ? 1 : 0) | (d.keepSeparators === true ? 2 : 0) | (d.min >= 2 ? 4 : 0);
8817
- return this.emit(
8818
- OP_REP,
9231
+ const gate2 = this.sepGate(d.separator, sep);
9232
+ const itemSet = deriveExpected(d.parser);
9233
+ const itemFx = this.expected(itemSet.length > 0 ? itemSet : [d.parser._tag]);
9234
+ 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);
9235
+ const ops = [
8819
9236
  child,
8820
9237
  d.min,
8821
9238
  d.max ?? -1,
8822
9239
  sep,
8823
9240
  flags,
8824
- this.expected(deriveExpected(d.parser)),
8825
- this.charClass(firstSetOf(d.separator, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia))
8826
- );
9241
+ itemFx,
9242
+ // As 0.58.0: the interpreter resyncs on the separator itself
9243
+ // (`orSentinel`), which runs under the live trivia.
9244
+ this.charClass(firstSetOf(d.separator, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia, false))
9245
+ ];
9246
+ if (gate2 >= 0) ops.push(gate2);
9247
+ return this.emit(OP_REP, ...ops);
8827
9248
  }
8828
9249
  case "optional": {
8829
9250
  const head = this.emit(OP_OPT, this.lookaheadGate(d.parser, this.node(d.parser).ip));
@@ -8832,7 +9253,7 @@ var Encoder = class {
8832
9253
  }
8833
9254
  case "transform": {
8834
9255
  const inner = d.parser._def;
8835
- if (inner.tag === "sequence" && !this.memo.has(d.parser) && !this.pending.has(d.parser)) {
9256
+ if (inner.tag === "sequence" && !this.memo.get(d.parser)?.has(this.scopeKey(d.parser)) && !this.pending.get(d.parser)?.has(this.scopeKey(d.parser))) {
8836
9257
  const kids = inner.parsers.map((c) => this.node(c).ip);
8837
9258
  const head = this.emitHead(OP_SEQX, 2 + 2 * kids.length);
8838
9259
  const projection = directArrayProjection(d.fn, d.fnSrc, kids.length);
@@ -8873,7 +9294,7 @@ var Encoder = class {
8873
9294
  d.tags === void 0 ? -1 : this.constant(d.tags)
8874
9295
  );
8875
9296
  if (matchesEmpty(d.parser, /* @__PURE__ */ new Set(), this.refResolver())) return body;
8876
- const fs = this.gateFirstSet(d.parser);
9297
+ const fs = this.gateFirstSet(d.parser, d.guarded === true, false);
8877
9298
  if (fs.kind === "any") return body;
8878
9299
  const cls = this.charClass(fs);
8879
9300
  if (cls < 0) return body;
@@ -8881,7 +9302,14 @@ var Encoder = class {
8881
9302
  return this.emit(OP_GATE, cls, body, this.expected(e.length > 0 ? e : [d.parser._tag]));
8882
9303
  }
8883
9304
  case "token": {
8884
- return this.emit(OP_TOKEN, this.node(d.parser).ip);
9305
+ const saved = this.activeTrivia;
9306
+ const savedKnown = this.triviaKnown;
9307
+ this.activeTrivia = void 0;
9308
+ this.triviaKnown = true;
9309
+ const body = this.node(d.parser).ip;
9310
+ this.activeTrivia = saved;
9311
+ this.triviaKnown = savedKnown;
9312
+ return this.emit(OP_TOKEN, body);
8885
9313
  }
8886
9314
  case "routed":
8887
9315
  return this.emit(OP_ROUTED, d.fallback === void 0 ? -1 : this.node(d.fallback).ip);
@@ -8937,7 +9365,7 @@ var Encoder = class {
8937
9365
  this.code[head + 2] = dspIdx;
8938
9366
  this.code[head + 3] = other;
8939
9367
  const otherRouted = d.otherwise === void 0 ? false : branchUsesRouted({ parser: d.otherwise, usesRouted: d.otherwiseUsesRouted === true });
8940
- this.code[head + 4] = otherRouted ? 1 : 0;
9368
+ this.code[head + 4] = (otherRouted ? 1 : 0) | (d.otherwiseCommit === false ? 2 : 0);
8941
9369
  this.code[head + 5] = arms.length;
8942
9370
  for (let i = 0; i < arms.length; i++) this.code[head + 6 + i] = arms[i];
8943
9371
  return head;
@@ -8992,7 +9420,7 @@ var Encoder = class {
8992
9420
  case "attempt": {
8993
9421
  const inner = this.emit(OP_ATTEMPT, this.node(d.parser).ip, d.contain === true ? 1 : 0);
8994
9422
  if (!this.failureNeedsRollback(d.parser, void 0)) this.failureRollbackCleanSites.add(inner);
8995
- const fs = this.gateFirstSet(d.parser);
9423
+ const fs = this.gateFirstSet(d.parser, d.guarded === true, false);
8996
9424
  if (fs.kind === "any" || matchesEmpty(d.parser, /* @__PURE__ */ new Set(), this.refResolver())) return inner;
8997
9425
  const cls = this.charClass(fs);
8998
9426
  if (cls < 0) return inner;
@@ -9001,7 +9429,7 @@ var Encoder = class {
9001
9429
  }
9002
9430
  // A LABEL IS A ROW — see `OP_LABEL`. It replaces the child's expected set,
9003
9431
  // which is the entire combinator.
9004
- // A LABEL IS CREDITED ON SUCCESS, as `codegen.ts:4530` credits it: its hook
9432
+ // A LABEL IS CREDITED ON SUCCESS, as the source lowering credited it: its hook
9005
9433
  // is emitted AFTER the child's statements, where a failed child has already
9006
9434
  // broken out of the labelled block.
9007
9435
  case "label":
@@ -9018,10 +9446,17 @@ var Encoder = class {
9018
9446
  const op = cap ? OP_SCOPE_CAP : OP_SCOPE;
9019
9447
  const flags = (d.rootCapture === "opaque" ? 1 : 0) | (d.triviaParser !== void 0 && d.triviaParser._meta.rootTriviaClassified !== true && d.rootCapture !== "opaque" ? 2 : 0);
9020
9448
  const savedTrivia = this.activeTrivia;
9021
- if (d.clearTrivia === true) this.activeTrivia = void 0;
9022
- else if (d.triviaParser !== void 0) this.activeTrivia = d.triviaParser;
9449
+ const savedKnown = this.triviaKnown;
9450
+ if (d.clearTrivia === true) {
9451
+ this.activeTrivia = void 0;
9452
+ this.triviaKnown = true;
9453
+ } else if (d.triviaParser !== void 0) {
9454
+ this.activeTrivia = d.triviaParser;
9455
+ this.triviaKnown = true;
9456
+ }
9023
9457
  const bodyIp = this.node(d.parser).ip;
9024
9458
  this.activeTrivia = savedTrivia;
9459
+ this.triviaKnown = savedKnown;
9025
9460
  if (d.clearTrivia === true) return this.emit(op, -1, bodyIp, flags);
9026
9461
  if (d.triviaParser === void 0) {
9027
9462
  return cap || flags !== 0 ? this.emit(op, -1, bodyIp, flags) : bodyIp;
@@ -9062,6 +9497,7 @@ var Encoder = class {
9062
9497
  switch (this.code[ip]) {
9063
9498
  case OP_GATE:
9064
9499
  case OP_RGATE:
9500
+ case OP_RGATE2:
9065
9501
  return [ip + 2];
9066
9502
  case OP_RULE:
9067
9503
  case OP_OPT:
@@ -9071,6 +9507,7 @@ var Encoder = class {
9071
9507
  case OP_ATTEMPT:
9072
9508
  case OP_LABEL:
9073
9509
  case OP_COV:
9510
+ case OP_GAPGATE:
9074
9511
  return [ip + 1];
9075
9512
  case OP_SCOPE:
9076
9513
  case OP_SCOPE_CAP:
@@ -9107,6 +9544,8 @@ var Encoder = class {
9107
9544
  case OP_REP:
9108
9545
  case OP_REPV:
9109
9546
  return this.code[ip + 4] >= 0 ? [ip + 1, ip + 4] : [ip + 1];
9547
+ case OP_STRICT:
9548
+ return [ip + 1, ip + 2];
9110
9549
  default:
9111
9550
  return [];
9112
9551
  }
@@ -10267,6 +10706,7 @@ function childSlots(code, ip, out) {
10267
10706
  case OP_LABEL:
10268
10707
  case OP_COV:
10269
10708
  case OP_REJECT:
10709
+ case OP_GAPGATE:
10270
10710
  out.push(code[ip + 1]);
10271
10711
  return true;
10272
10712
  // Single child at `ip + 2`.
@@ -10283,8 +10723,13 @@ function childSlots(code, ip, out) {
10283
10723
  case OP_SOURCE_LEAF:
10284
10724
  case OP_ARMGATE:
10285
10725
  case OP_RGATE:
10726
+ case OP_RGATE2:
10286
10727
  out.push(code[ip + 2]);
10287
10728
  return true;
10729
+ // `STRICT strict tolerant` — both encodings are reachable; an assembly keeps one.
10730
+ case OP_STRICT:
10731
+ out.push(code[ip + 1], code[ip + 2]);
10732
+ return true;
10288
10733
  // `ROUTED fallback` — the operand is an offset or −1. The routed token itself is
10289
10734
  // data the enclosing `dispatch()` already consumed, so the only edge is the
10290
10735
  // fallback.
@@ -10732,7 +11177,8 @@ var WRAPPERS = /* @__PURE__ */ new Set([
10732
11177
  OP_WITHCTX,
10733
11178
  OP_RULE,
10734
11179
  OP_ATTEMPT,
10735
- OP_COV
11180
+ OP_COV,
11181
+ OP_GAPGATE
10736
11182
  ]);
10737
11183
  function rowNullable(code, k, ip, memo) {
10738
11184
  const hit = memo.get(ip);
@@ -10769,7 +11215,31 @@ function holdsGap(code, k, base, n, i, memo) {
10769
11215
  return !rowNullable(code, k, next, memo);
10770
11216
  }
10771
11217
 
11218
+ // src/table/gap-gate.ts
11219
+ function newGapSlot() {
11220
+ return { row: -1, at: -1, end: -1, input: void 0 };
11221
+ }
11222
+ function gapRows(code, roots) {
11223
+ const rows = /* @__PURE__ */ new Set();
11224
+ const seen = /* @__PURE__ */ new Set();
11225
+ const stack = [...roots];
11226
+ const kids = [];
11227
+ while (stack.length > 0) {
11228
+ const ip = stack.pop();
11229
+ if (seen.has(ip)) continue;
11230
+ seen.add(ip);
11231
+ if (code[ip] === OP_GAPGATE) rows.add(code[ip + 2]);
11232
+ kids.length = 0;
11233
+ childSlots(code, ip, kids);
11234
+ for (const c of kids) stack.push(c);
11235
+ }
11236
+ return rows;
11237
+ }
11238
+
10772
11239
  // src/table/emit-assembly.ts
11240
+ function gapRegexLowers(re) {
11241
+ return re instanceof RegExp && scanShapeFromRegex(re.source, re.flags) !== null;
11242
+ }
10773
11243
  var Unemittable = class extends Error {
10774
11244
  construct;
10775
11245
  constructor(construct) {
@@ -10788,7 +11258,6 @@ var EMITTED_PARAMS = [
10788
11258
  "CLS",
10789
11259
  "AFX",
10790
11260
  "TRIVIA",
10791
- "TRIVIALABELS",
10792
11261
  "TRIVIASCAN",
10793
11262
  "SCANS",
10794
11263
  "DISP",
@@ -10812,7 +11281,6 @@ var EMITTED_PARAMS = [
10812
11281
  "cstLeavesLen",
10813
11282
  "skipTriviaScanned",
10814
11283
  "needsDeferredTriviaCommit",
10815
- "scanTrivia",
10816
11284
  "advanceTrivia",
10817
11285
  "refuseUnclassifiedRootScope",
10818
11286
  "spanLines",
@@ -10832,34 +11300,31 @@ var EMITTED_PARAMS = [
10832
11300
  "orSentinel",
10833
11301
  "captureError",
10834
11302
  // Pure scalar terminal recognizers, indexed by the terminal's existing const
10835
- // operand. Appended for compatibility with older precompiled factories.
11303
+ // operand; selected lexical bodies; the adjacency test.
10836
11304
  "RECOG",
10837
- // Appended so precompiled factories produced by earlier runtimes keep every
10838
- // positional helper binding. Older factories ignore this trailing argument.
10839
- "commitTriviaScan",
10840
- "scanTriviaCompact",
10841
11305
  "LEX",
10842
- // Appended for old precompiled-factory ABI: old factories ignore it.
10843
11306
  "adjacencyHolds",
10844
- // Appended for selected composite lexical programs; old factories ignore it.
11307
+ // Selected composite lexical programs.
10845
11308
  "LEXPROG",
10846
- // Appended for trivia-attributing fields (`field(…, { leadingTrivia })`);
10847
- // old factories ignore them.
11309
+ // Trivia-attributing fields (`field(…, { leadingTrivia })`).
10848
11310
  "LEAD",
10849
11311
  "triviaFieldCapture",
10850
- // Appended for the deferred-commit trivia skip (`trivia-gap.ts`); old
10851
- // factories ignore it.
11312
+ // The deferred-commit trivia skip (`trivia-gap.ts`).
10852
11313
  "skipDeferredTrivia",
10853
- // Appended for the pass-through collapse (`run-support.ts`); old factories
10854
- // ignore it.
11314
+ // The pass-through collapse (`run-support.ts`).
10855
11315
  "collapseEmpty",
10856
- // Appended for constructor-built tree objects (`cst/plain.d.ts`): a `new` call has
10857
- // no allocation site, so V8 cannot pretenure them. Old factories ignore them.
11316
+ // Constructor-built tree objects (`cst/plain.d.ts`): a `new` call has no
11317
+ // allocation site, so V8 cannot pretenure them.
10858
11318
  "_SP",
10859
11319
  "_LF",
10860
11320
  "_ND",
10861
11321
  "_FC",
10862
- "_BUF"
11322
+ "_BUF",
11323
+ // The gap gate's slot (`gap-gate.ts`).
11324
+ "GAP"
11325
+ // Artifacts are version-locked (docs/design/artifact-format.md): a factory is
11326
+ // only ever called by the runtime that emitted it, so a name nothing emitted
11327
+ // reads is removed rather than kept for an older factory's positions.
10863
11328
  ];
10864
11329
  var RUNTIME_PRELUDE = `
10865
11330
  let _pfScan=null
@@ -11110,7 +11575,7 @@ function rebuildPools(cc, fx, disp, plan) {
11110
11575
  })
11111
11576
  };
11112
11577
  }
11113
- function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false, select, scanSinkNeutral = false) {
11578
+ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false, select, scanSinkNeutral = false, gapNeutral = false) {
11114
11579
  const { code, k, fx, disp, dsp, triviaLabelled } = t;
11115
11580
  const swapLegal = !cfg.trackLines;
11116
11581
  const hostCst = cfg.hostCst;
@@ -11118,6 +11583,7 @@ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false, se
11118
11583
  if (cfg.coverage) throw new Unemittable("a coverage assembly");
11119
11584
  const roots = [...Object.values(prog.rules), ...extraIps];
11120
11585
  const reachable = [...reachableSites(code, roots)];
11586
+ const GAP_ROWS = gapNeutral ? /* @__PURE__ */ new Set() : new Set([...gapRows(code, roots)].filter((row) => !gapRegexLowers(k[code[row + 1]])));
11121
11587
  const labels = computeSiteLabels(code, roots, hostCst);
11122
11588
  const scalarSpecs = /* @__PURE__ */ new Set();
11123
11589
  const nullable2 = /* @__PURE__ */ new Map();
@@ -11455,13 +11921,15 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
11455
11921
  function aliasOf(ip) {
11456
11922
  const op = code[ip];
11457
11923
  if (op === OP_GATE && (cfg.tolerant || cfg.probe)) return code[ip + 2];
11458
- if (op === OP_RULE) return code[ip + 1];
11924
+ if (op === OP_RULE || op === OP_GAPGATE) return code[ip + 1];
11925
+ if (op === OP_STRICT) return code[cfg.tolerant || cfg.probe ? ip + 2 : ip + 1];
11459
11926
  if (scopeIsIdentity(code, ip, labels.at(ip))) return code[ip + 2];
11460
11927
  return void 0;
11461
11928
  }
11462
11929
  function lower(ip, fname) {
11463
11930
  const op = code[ip];
11464
11931
  const head = `function ${fname}(input,pos,ctx){`;
11932
+ const outerHead = head;
11465
11933
  const L = labels.at(ip);
11466
11934
  const sinks = sinksAt(ip);
11467
11935
  const pushLeaf = L.buf && !cfg.trackLines ? L.raw === RAW_OMIT ? "_pushLeafNoRawBuf" : L.raw === RAW_CAPTURE ? "_pushLeafBuf" : "_pushLeaf" : "_pushLeaf";
@@ -11537,9 +12005,27 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
11537
12005
  case OP_RX_TRACK: {
11538
12006
  const xf = fxRef(code[ip + 2]);
11539
12007
  const track = op === OP_RX_TRACK;
12008
+ if (GAP_ROWS.has(ip)) {
12009
+ const re2 = hoist("gx", `K[${code[ip + 1]}]`);
12010
+ return `${outerHead}
12011
+ let e
12012
+ if(GAP.row===${ip}&&GAP.at===pos&&GAP.input===input)e=GAP.end
12013
+ else{${re2}.lastIndex=pos;e=${re2}.test(input)?${re2}.lastIndex:-1}
12014
+ if(e>=0){
12015
+ const v=input.slice(pos,e)
12016
+ ${captureLeaf("v")}
12017
+ ${track ? "_trackLines(ctx,input,e)\n" : ""}EC.e=e
12018
+ return v
12019
+ }
12020
+ ctx._fe=pos;ctx._fx=${xf}
12021
+ ${cfg.probe ? `failAt(ctx,${xf},pos)
12022
+ ` : ""}return FAIL
12023
+ }`;
12024
+ }
12025
+ const head2 = outerHead;
11540
12026
  if (!track && scalarSpecs.has(code[ip + 1])) {
11541
12027
  const recognize = recognizerRef(code[ip + 1]);
11542
- return `${head}
12028
+ return `${head2}
11543
12029
  const e=${recognize}(input,pos)
11544
12030
  if(e>=0){
11545
12031
  const v=input.slice(pos,e)
@@ -11552,11 +12038,11 @@ return FAIL
11552
12038
  }`;
11553
12039
  }
11554
12040
  const scanned = emitScan(code[ip + 1], xf, track, captureLeaf);
11555
- if (scanned !== void 0) return `${head}${scanned}}`;
12041
+ if (scanned !== void 0) return `${head2}${scanned}}`;
11556
12042
  const rxk = k[code[ip + 1]];
11557
12043
  if (!(rxk instanceof RegExp) || !rxk.sticky) throw new Unemittable("a non-sticky regex row");
11558
12044
  const re = kRef(code[ip + 1]);
11559
- return `${head}
12045
+ return `${head2}
11560
12046
  ${re}.lastIndex=pos
11561
12047
  if(${re}.test(input)){
11562
12048
  const e=${re}.lastIndex
@@ -11594,6 +12080,28 @@ return ${child}(input,pos,ctx)
11594
12080
  const it=ctx._routed
11595
12081
  if(it!==undefined&&it.span.start===pos&&!classHas(${cls},lead(input,it.span.end))){ctx._fe=it.span.end;ctx._fx=${xf};return FAIL}
11596
12082
  return ${child}(input,pos,ctx)
12083
+ }`;
12084
+ }
12085
+ case OP_RGATE2: {
12086
+ const child = link(code[ip + 2]);
12087
+ if (cfg.tolerant || cfg.probe) return `${head}
12088
+ return ${child}(input,pos,ctx)
12089
+ }`;
12090
+ const idxs = [code[ip + 1], code[ip + 4], code[ip + 5]];
12091
+ const ascii = idxs.every((i) => isAsciiOnlyClass(t.cc[i]));
12092
+ const ref2 = (idx, tag) => {
12093
+ const ci = classes.push([t.cc[idx]]) - 1;
12094
+ classPlan.push([idx]);
12095
+ return hoist(tag, `CLS[${ci}][0]`);
12096
+ };
12097
+ const [cls, first, second] = [ref2(idxs[0], "cl"), ref2(idxs[1], "c1"), ref2(idxs[2], "c2")];
12098
+ const has = (c, at) => ascii ? `${c}.ascii[${at}]===1` : `classHas(${c},${at})`;
12099
+ const xf = fxRef(code[ip + 3]);
12100
+ return `${head}
12101
+ const it=ctx._routed
12102
+ if(it!==undefined&&it.span.start===pos){const e=it.span.end;const c=${ascii ? "input.charCodeAt(e)" : "lead(input,e)"}
12103
+ 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}}
12104
+ return ${child}(input,pos,ctx)
11597
12105
  }`;
11598
12106
  }
11599
12107
  case OP_ARMGATE: {
@@ -12003,7 +12511,27 @@ if(ctx._fc===true)return FAIL
12003
12511
  const af=ctx._fx
12004
12512
  if(af!==undefined&&af.length>0){ctx._fe=pos;return FAIL}
12005
12513
  }
12006
- ctx._fe=pos;ctx._fx=${choiceFx}
12514
+ ${cfg.probe ? `if(arm<0){${arms.map((a) => `${a}(input,pos,ctx)`).join(";")}}
12515
+ ` : ""}ctx._fe=pos;ctx._fx=${choiceFx}
12516
+ return FAIL
12517
+ }`;
12518
+ }
12519
+ if (cfg.probe) {
12520
+ const p = tmp();
12521
+ return `${head}
12522
+ ${emitMark(p, L.buf, L.raw, sinks)}
12523
+ let acc
12524
+ let best=pos
12525
+ ${arms.map((arm) => `{ctx._fc=false
12526
+ const v=${arm}(input,pos,ctx)
12527
+ if(v!==FAIL)return v
12528
+ const at=ctx._fe??pos
12529
+ if(at>best){best=at;acc=undefined}
12530
+ if(at===best)acc=_accSet(ctx._fx,acc)
12531
+ if(ctx._fc===true){if(acc!==undefined)ctx._fx=acc;return FAIL}
12532
+ ${emitRollback(p, L.buf, L.raw, sinks)}
12533
+ }`).join("\n")}
12534
+ ctx._fe=pos;ctx._fx=acc??${choiceFx}
12007
12535
  return FAIL
12008
12536
  }`;
12009
12537
  }
@@ -12210,7 +12738,8 @@ return FAIL
12210
12738
  const selector = link(code[ip + 1]);
12211
12739
  const otherIp = code[ip + 3];
12212
12740
  const other = otherIp >= 0 ? link(otherIp) : void 0;
12213
- const otherRouted = code[ip + 4] === 1;
12741
+ const otherRouted = (code[ip + 4] & 1) === 1;
12742
+ const otherCommits = (code[ip + 4] & 2) === 0;
12214
12743
  const n = code[ip + 5];
12215
12744
  validateDispatchSpec(spec, n, code[ip + 4]);
12216
12745
  const armBase = ip + 6;
@@ -12264,7 +12793,7 @@ ${fallbackCase}
12264
12793
  }
12265
12794
  if(v===FAIL){
12266
12795
  ${emitRollback(m2, L.buf, L.raw, sinks)}
12267
- ctx._fc=true
12796
+ ${other !== void 0 && !otherCommits ? "if(arm!==undefined)" : ""}ctx._fc=true
12268
12797
  return FAIL
12269
12798
  }
12270
12799
  return [key,v]
@@ -12296,6 +12825,63 @@ return [key,v]
12296
12825
  }
12297
12826
  }
12298
12827
  const itemMiss = (at) => itemAscii ? `${itemCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${itemCls},lead(input,${at}))`;
12828
+ let sepCls;
12829
+ let sepAscii = false;
12830
+ if (!REC2 && sepIp >= 0 && (code[ip + 5] & 8) !== 0 && !leadingTriviaFieldAt(code, k, sepIp) && !rowNullable(code, k, sepIp, nullable2)) {
12831
+ const resolved = t.cc[code[ip + 8]];
12832
+ const ci = classes.push([resolved]) - 1;
12833
+ classPlan.push([code[ip + 8]]);
12834
+ sepCls = hoist("rs", `CLS[${ci}][0]`);
12835
+ sepAscii = isAsciiOnlyClass(resolved);
12836
+ }
12837
+ const gapIp = !gapNeutral && sepIp < 0 && min === 0 && !REC2 && code[code[ip + 1]] === OP_GAPGATE ? code[ip + 1] : -1;
12838
+ let gapCode = (_rb) => "";
12839
+ if (gapIp >= 0) {
12840
+ const row = code[gapIp + 2];
12841
+ const rx = k[code[row + 1]];
12842
+ const shape = gapRegexLowers(rx) ? scanShapeFromRegex(rx.source, rx.flags) : null;
12843
+ const cls = (idx) => {
12844
+ const resolved = t.cc[idx];
12845
+ const ci = classes.push([resolved]) - 1;
12846
+ classPlan.push([idx]);
12847
+ const ref2 = hoist("gc", `CLS[${ci}][0]`);
12848
+ return isAsciiOnlyClass(resolved) ? `${ref2}.ascii[input.charCodeAt(${"@"})]!==1` : `!classHas(${ref2},lead(input,${"@"}))`;
12849
+ };
12850
+ const follow = cls(code[gapIp + 3]).replaceAll("@", "gq");
12851
+ const other = code[gapIp + 4] >= 0 ? cls(code[gapIp + 4]).replaceAll("@", "itemStart") : "true";
12852
+ if (shape !== null) {
12853
+ let n = 0;
12854
+ const q2 = tmp();
12855
+ const m = emitShapeMatch(shape, "itemStart", (prefix = "_v") => `${q2}${prefix}${n++}`, "");
12856
+ gapCode = (rbCode) => `let gapped=false
12857
+ if(ctx._probe===undefined){
12858
+ ${m.setup.join("\n")}
12859
+ const gq=${m.ok}?${m.end}:-1
12860
+ if((gq<0||${follow})&&${other}){
12861
+ ${rbCode}
12862
+ break
12863
+ }
12864
+ }
12865
+ `;
12866
+ } else {
12867
+ const re = hoist("gr", `K[${code[row + 1]}]`);
12868
+ gapCode = (rbCode) => `let gapped=false
12869
+ if(ctx._probe===undefined){
12870
+ ${re}.lastIndex=itemStart
12871
+ const gq=${re}.test(input)?${re}.lastIndex:-1
12872
+ if((gq<0||${follow})&&${other}){
12873
+ ${rbCode}
12874
+ break
12875
+ }
12876
+ if(gq>=0){
12877
+ GAP.row=${row};GAP.at=itemStart;GAP.end=gq;GAP.input=input
12878
+ gapped=true
12879
+ }
12880
+ }
12881
+ `;
12882
+ }
12883
+ }
12884
+ const sepMiss = (at) => sepAscii ? `${sepCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${sepCls},lead(input,${at}))`;
12299
12885
  const skipBeforeFirst = sepIp < 0 && min === 0;
12300
12886
  const p = tmp();
12301
12887
  const via = sep !== void 0 ? "false" : min === 0 ? skipBeforeFirst ? "true" : "count>0" : `count>=${min}&&count>0`;
@@ -12333,7 +12919,7 @@ ${p}lg=ctx._triviaLog!==undefined?ctx._triviaLog.length:0
12333
12919
  ${p}rt=ctx._rootTriviaLog!==undefined?ctx._rootTriviaLog.length:0${markSinks}
12334
12920
  }`;
12335
12921
  const my = `${p}my`;
12336
- const recFx = REC2 ? fxRef(code[ip + 6]) : "";
12922
+ const recFx = !REC2 ? "" : (flags & 16) !== 0 ? "[]" : fxRef(code[ip + 6]);
12337
12923
  const sepSent = REC2 ? sentRef(code[ip + 7]) : "undefined";
12338
12924
  const recSent = sep === void 0 ? my : sepSent === "undefined" ? `orSentinel(${my},undefined)` : `orSentinel(${sepSent}??${my},${sepSent}===undefined?undefined:${my})`;
12339
12925
  const recGate = sep !== void 0 || min === 0 ? "" : `count>=${min}&&`;
@@ -12349,7 +12935,7 @@ continue
12349
12935
  return `${head}
12350
12936
  const out=${collect ? "[]" : "undefined"}
12351
12937
  ${knownTrivia === void 0 ? "const hasTrivia=ctx.trivia!==undefined\n" : ""}${L.buf ? "" : "const needMark=_rollbackNeeded(ctx)\n"}${REC2 ? `const ${my}=ctx._sync
12352
- ` : ""}${itemCls === void 0 ? "" : `const ${p}gate=ctx._probe===undefined
12938
+ ` : ""}${itemCls === void 0 && sepCls === void 0 ? "" : `const ${p}gate=ctx._probe===undefined
12353
12939
  `}let cur=pos
12354
12940
  let count=0
12355
12941
  for(;;){
@@ -12365,6 +12951,10 @@ const lb=cstLeavesLen(ctx)
12365
12951
  let sp=cur
12366
12952
  ${hasTrivia === "false" ? "" : hasTrivia === "true" ? `sp=${skip}(input,sp,ctx)
12367
12953
  ` : `if(hasTrivia)sp=${skip}(input,sp,ctx)
12954
+ `}${sepCls === void 0 ? "" : `if(count>=${min}&&${p}gate&&${sepMiss("sp")}){
12955
+ ${rb}
12956
+ break
12957
+ }
12368
12958
  `}ctx._fc=false
12369
12959
  ${sepIp >= 0 && leadingTriviaFieldAt(code, k, sepIp) ? "LEAD.at=cur\n" : ""}const sv=${sep}(input,sp,ctx)
12370
12960
  if(sv===FAIL){
@@ -12380,13 +12970,13 @@ itemStart=${hasTrivia === "false" ? "EC.e" : hasTrivia === "true" ? `${skip}(inp
12380
12970
  ${rb}
12381
12971
  ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
12382
12972
  }
12383
- ${itemCls === void 0 ? "" : `if(count>=${min}&&${p}gate&&${itemMiss("itemStart")}){
12973
+ ${itemCls === void 0 || sep === void 0 && hasTrivia === "false" ? "" : `if(count>=${min}&&${p}gate&&${sep === void 0 && hasTrivia === "hasTrivia" ? "hasTrivia&&" : ""}${itemMiss("itemStart")}){
12384
12974
  ${rb}
12385
12975
  ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
12386
12976
  }
12387
- `}ctx._fc=false
12977
+ `}${gapCode(rb)}ctx._fc=false
12388
12978
  const v=${child}(input,itemStart,ctx)
12389
- if(v===FAIL){
12979
+ ${gapIp >= 0 ? "if(gapped)GAP.at=-1\n" : ""}if(v===FAIL){
12390
12980
  ${REC2 ? `if(ctx._fc===true){
12391
12981
  ${rb}
12392
12982
  return FAIL
@@ -13202,6 +13792,9 @@ function clearOf(ranges, input, from, to) {
13202
13792
  return true;
13203
13793
  }
13204
13794
 
13795
+ // src/version.ts
13796
+ var PARSEMAN_VERSION = "0.60.0";
13797
+
13205
13798
  // src/table/assemble.ts
13206
13799
  import { PlainBuf as PlainBuf2, PlainField, PlainLeaf as PlainLeaf9, PlainNode as PlainNode2, PlainSpan as PlainSpan10 } from "./cst/plain.js";
13207
13800
 
@@ -13212,6 +13805,7 @@ function passChild(code, ip) {
13212
13805
  case OP_LABEL:
13213
13806
  case OP_COV:
13214
13807
  case OP_TOKEN:
13808
+ case OP_GAPGATE:
13215
13809
  return code[ip + 1];
13216
13810
  case OP_GATE:
13217
13811
  case OP_NODE:
@@ -13477,6 +14071,13 @@ function cfgKeyOf(hostCst, trackLines, tolerant, coverage, probe, hostReadsChild
13477
14071
  function cfgKey(c) {
13478
14072
  return cfgKeyOf(c.hostCst, c.trackLines, c.tolerant, c.coverage, c.probe, c.hostReadsChildren);
13479
14073
  }
14074
+ function assertFactoryArity(pre) {
14075
+ if (pre.arity === EMITTED_PARAMS.length) return;
14076
+ 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`;
14077
+ throw new Error(
14078
+ `parseman: this parser's printed assembly ${printed}. Reprint the parser with parseman ${PARSEMAN_VERSION}, or run it on the parseman version that printed it.`
14079
+ );
14080
+ }
13480
14081
  function assemble(t, prog, cfg) {
13481
14082
  const { code, k, fns, cc, fx, disp, dsp, trivia: trivia2, triviaLabelled } = t;
13482
14083
  const triviaScan = t.triviaScan;
@@ -13511,6 +14112,8 @@ function assemble(t, prog, cfg) {
13511
14112
  return recognize;
13512
14113
  }
13513
14114
  const labelExtraIps = assemblyExtraIps(prog);
14115
+ const GAP_ROWS = gapRows(code, [...Object.values(prog.rules), ...labelExtraIps]);
14116
+ const GAP = newGapSlot();
13514
14117
  const closureLabels = computeSiteLabels(
13515
14118
  code,
13516
14119
  [...Object.values(prog.rules), ...labelExtraIps],
@@ -13729,6 +14332,27 @@ function assemble(t, prog, cfg) {
13729
14332
  return node2;
13730
14333
  };
13731
14334
  }
14335
+ function gapRegexPiece(row, re, xf, track) {
14336
+ return (input, pos, ctx) => {
14337
+ let e;
14338
+ if (GAP.row === row && GAP.at === pos && GAP.input === input) e = GAP.end;
14339
+ else {
14340
+ re.lastIndex = pos;
14341
+ e = re.test(input) ? re.lastIndex : -1;
14342
+ }
14343
+ if (e >= 0) {
14344
+ const v = input.slice(pos, e);
14345
+ if (cstCaptureActive(ctx)) pushLeaf(ctx, v, pos, e);
14346
+ if (track) trackLinesInto(ctx, input, e);
14347
+ EC.e = e;
14348
+ return v;
14349
+ }
14350
+ ctx._fe = pos;
14351
+ ctx._fx = xf;
14352
+ if (ctx._probe !== void 0) failAt(ctx, xf, pos);
14353
+ return FAIL;
14354
+ };
14355
+ }
13732
14356
  function passCollapseNode(child, build, readsRaw) {
13733
14357
  return (input, pos, ctx) => {
13734
14358
  const v = child(input, pos, ctx);
@@ -14131,7 +14755,7 @@ function assemble(t, prog, cfg) {
14131
14755
  return next?.(key);
14132
14756
  };
14133
14757
  }
14134
- function dispatchBranch(child, usesRouted) {
14758
+ function dispatchBranch(child, usesRouted, commits = true) {
14135
14759
  if (!usesRouted) {
14136
14760
  return (input, _pos, selEnd, key, ctx) => {
14137
14761
  const need = markCst(ctx);
@@ -14145,7 +14769,7 @@ function assemble(t, prog, cfg) {
14145
14769
  const v = child(input, selEnd, ctx);
14146
14770
  if (v === FAIL) {
14147
14771
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14148
- ctx._fc = true;
14772
+ if (commits) ctx._fc = true;
14149
14773
  return FAIL;
14150
14774
  }
14151
14775
  return [key, v];
@@ -14163,7 +14787,7 @@ function assemble(t, prog, cfg) {
14163
14787
  }
14164
14788
  if (v === FAIL) {
14165
14789
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14166
- ctx._fc = true;
14790
+ if (commits) ctx._fc = true;
14167
14791
  return FAIL;
14168
14792
  }
14169
14793
  return [key, v];
@@ -14404,6 +15028,7 @@ function assemble(t, prog, cfg) {
14404
15028
  case OP_RX: {
14405
15029
  const re = k[code[ip + 1]];
14406
15030
  const xf = fx[code[ip + 2]];
15031
+ if (GAP_ROWS.has(ip)) return gapRegexPiece(ip, re, xf, false);
14407
15032
  const shared = rawScalarSpecs.has(code[ip + 1]) ? scalarFor(ip) : void 0;
14408
15033
  if (shared !== void 0) {
14409
15034
  return (input, pos, ctx) => {
@@ -14438,6 +15063,7 @@ function assemble(t, prog, cfg) {
14438
15063
  case OP_RX_TRACK: {
14439
15064
  const re = k[code[ip + 1]];
14440
15065
  const xf = fx[code[ip + 2]];
15066
+ if (GAP_ROWS.has(ip)) return gapRegexPiece(ip, re, xf, true);
14441
15067
  return (input, pos, ctx) => {
14442
15068
  re.lastIndex = pos;
14443
15069
  if (re.test(input)) {
@@ -14568,6 +15194,12 @@ function assemble(t, prog, cfg) {
14568
15194
  return v;
14569
15195
  };
14570
15196
  }
15197
+ // Transparent: only the repeat holding it reads its operands.
15198
+ case OP_GAPGATE:
15199
+ return link(code[ip + 1]);
15200
+ // One encoding per assembly: strict, or the construct as written.
15201
+ case OP_STRICT:
15202
+ return link(code[cfg.tolerant || cfg.probe ? ip + 2 : ip + 1]);
14571
15203
  case OP_RULE:
14572
15204
  return link(code[ip + 1]);
14573
15205
  case OP_LIVE: {
@@ -15493,7 +16125,7 @@ function assemble(t, prog, cfg) {
15493
16125
  if (table.exclusive) {
15494
16126
  const ascii = table.ascii;
15495
16127
  const hi = table.hi;
15496
- if (n === 2) {
16128
+ if (n === 2 && !cfg.probe) {
15497
16129
  const a0 = link(code[base]), a1 = link(code[base + 1]);
15498
16130
  return (input, pos, ctx) => {
15499
16131
  const c = lead(input, pos);
@@ -15525,7 +16157,7 @@ function assemble(t, prog, cfg) {
15525
16157
  return FAIL;
15526
16158
  };
15527
16159
  }
15528
- if (n === 3) {
16160
+ if (n === 3 && !cfg.probe) {
15529
16161
  const a0 = link(code[base]), a1 = link(code[base + 1]), a2 = link(code[base + 2]);
15530
16162
  return (input, pos, ctx) => {
15531
16163
  const c = lead(input, pos);
@@ -15570,6 +16202,7 @@ function assemble(t, prog, cfg) {
15570
16202
  );
15571
16203
  };
15572
16204
  const selected = bindExclusive(0);
16205
+ const probeArms = cfg.probe ? Array.from({ length: n }, (_, i) => link(code[base + i])) : void 0;
15573
16206
  return (input, pos, ctx) => {
15574
16207
  const c = lead(input, pos);
15575
16208
  let arm = -1;
@@ -15595,12 +16228,13 @@ function assemble(t, prog, cfg) {
15595
16228
  return FAIL;
15596
16229
  }
15597
16230
  }
16231
+ if (arm < 0 && probeArms !== void 0) for (const a of probeArms) a(input, pos, ctx);
15598
16232
  ctx._fe = pos;
15599
16233
  ctx._fx = choiceFx;
15600
16234
  return FAIL;
15601
16235
  };
15602
16236
  }
15603
- const armCls = table.armCls;
16237
+ const armCls = cfg.probe ? table.armCls.map(() => null) : table.armCls;
15604
16238
  if (n === 2 || n === 3) {
15605
16239
  const a0 = link(code[base]), a1 = link(code[base + 1]);
15606
16240
  const a2 = n === 3 ? link(code[base + 2]) : void 0;
@@ -16020,6 +16654,43 @@ function assemble(t, prog, cfg) {
16020
16654
  return child(input, pos, ctx);
16021
16655
  };
16022
16656
  }
16657
+ case OP_RGATE2: {
16658
+ const cls = cc[code[ip + 1]];
16659
+ const xf = fx[code[ip + 3]];
16660
+ const first = cc[code[ip + 4]];
16661
+ const second = cc[code[ip + 5]];
16662
+ const child = link(code[ip + 2]);
16663
+ if (cfg.tolerant || cfg.probe) return child;
16664
+ if (cls.hi.length === 0 && first.hi.length === 0 && second.hi.length === 0) {
16665
+ const a0 = cls.ascii, a1 = first.ascii, a2 = second.ascii;
16666
+ return (input, pos, ctx) => {
16667
+ const item = ctx._routed;
16668
+ if (item !== void 0 && item.span.start === pos) {
16669
+ const e = item.span.end;
16670
+ const c = input.charCodeAt(e);
16671
+ if (a0[c] !== 1 || a1[c] === 1 && a2[input.charCodeAt(e + 1)] !== 1) {
16672
+ ctx._fe = e;
16673
+ ctx._fx = xf;
16674
+ return FAIL;
16675
+ }
16676
+ }
16677
+ return child(input, pos, ctx);
16678
+ };
16679
+ }
16680
+ return (input, pos, ctx) => {
16681
+ const item = ctx._routed;
16682
+ if (item !== void 0 && item.span.start === pos) {
16683
+ const e = item.span.end;
16684
+ const c = lead(input, e);
16685
+ if (!classHas2(cls, c) || classHas2(first, c) && !classHas2(second, lead(input, e + 1))) {
16686
+ ctx._fe = e;
16687
+ ctx._fx = xf;
16688
+ return FAIL;
16689
+ }
16690
+ }
16691
+ return child(input, pos, ctx);
16692
+ };
16693
+ }
16023
16694
  case OP_ARMGATE: {
16024
16695
  const pred = fns[code[ip + 1]];
16025
16696
  const child = link(code[ip + 2]);
@@ -16087,10 +16758,18 @@ function assemble(t, prog, cfg) {
16087
16758
  const itemClassIndex = sepIp < 0 ? code[ip + 7] : -1;
16088
16759
  const itemClass = !REC2 && itemClassIndex >= 0 ? cc[itemClassIndex] : void 0;
16089
16760
  const itemMayStart = itemClass === void 0 ? void 0 : (input, pos) => classHas2(itemClass, lead(input, pos));
16761
+ const sepClassIndex = sepIp >= 0 && !sepLeads && (flags & 8) !== 0 ? code[ip + 8] : -1;
16762
+ const sepClass = !REC2 && sepClassIndex >= 0 && !rowNullable(code, k, sepIp, NULLABLE) ? cc[sepClassIndex] : void 0;
16763
+ const gapIp = sepIp < 0 && min === 0 && !REC2 && code[code[ip + 1]] === OP_GAPGATE ? code[ip + 1] : -1;
16764
+ const gapRow = gapIp >= 0 ? code[gapIp + 2] : -1;
16765
+ const gapRe = gapIp >= 0 ? k[code[gapRow + 1]] : void 0;
16766
+ const gapFollow = gapIp >= 0 ? cc[code[gapIp + 3]] : void 0;
16767
+ const gapOther = gapIp >= 0 && code[gapIp + 4] >= 0 ? cc[code[gapIp + 4]] : void 0;
16090
16768
  const skipBeforeFirst = sepIp < 0 && min === 0;
16091
16769
  const itemClean = sepIp < 0 && CLEAN(code[ip + 1]) && !rowNullable(code, k, code[ip + 1], NULLABLE);
16092
16770
  if (REC2) {
16093
16771
  const itemFx2 = fx[code[ip + 6]];
16772
+ const recFx = (flags & 16) !== 0 ? EMPTY_FX2 : itemFx2;
16094
16773
  const sepSent = sentinelFor(code[ip + 7]);
16095
16774
  return (input, pos, ctx) => {
16096
16775
  const out = collect ? [] : void 0;
@@ -16150,7 +16829,7 @@ function assemble(t, prog, cfg) {
16150
16829
  itemStart,
16151
16830
  ctx,
16152
16831
  sep === void 0 ? mySync : orSentinel(sepSent ?? mySync, sepSent === void 0 ? void 0 : mySync),
16153
- itemFx2
16832
+ recFx
16154
16833
  );
16155
16834
  if (out !== void 0) out.push(rr.error);
16156
16835
  captureError(ctx, rr.error);
@@ -16188,6 +16867,7 @@ function assemble(t, prog, cfg) {
16188
16867
  const markLater = needMark && (!itemClean || hasTrivia);
16189
16868
  const markFirst = needMark && (!itemClean || hasTrivia && skipBeforeFirst);
16190
16869
  const gateItems = itemMayStart !== void 0 && ctx._probe === void 0;
16870
+ const gateSep = sepClass !== void 0 && ctx._probe === void 0;
16191
16871
  let cur = pos;
16192
16872
  let count = 0;
16193
16873
  for (; ; ) {
@@ -16210,6 +16890,10 @@ function assemble(t, prog, cfg) {
16210
16890
  const leavesBefore = cstLeavesLen(ctx);
16211
16891
  let sp = cur;
16212
16892
  if (hasTrivia) sp = skipTrivia(input, sp, ctx);
16893
+ if (count >= min && gateSep && !classHas2(sepClass, lead(input, sp))) {
16894
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
16895
+ break;
16896
+ }
16213
16897
  ctx._fc = false;
16214
16898
  if (sepLeads) LEAD.at = cur;
16215
16899
  const sv = sep(input, sp, ctx);
@@ -16229,13 +16913,30 @@ function assemble(t, prog, cfg) {
16229
16913
  if (trailingAllowed && sepEnd >= 0) cur = sepEnd;
16230
16914
  break;
16231
16915
  }
16232
- if (count >= min && gateItems && !itemMayStart(input, itemStart)) {
16916
+ if (count >= min && gateItems && (sep !== void 0 || hasTrivia) && !itemMayStart(input, itemStart)) {
16233
16917
  if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
16234
16918
  if (trailingAllowed && sepEnd >= 0) cur = sepEnd;
16235
16919
  break;
16236
16920
  }
16921
+ let gapped = false;
16922
+ if (gapRe !== void 0 && ctx._probe === void 0) {
16923
+ gapRe.lastIndex = itemStart;
16924
+ const q2 = gapRe.test(input) ? gapRe.lastIndex : -1;
16925
+ if ((q2 < 0 || !classHas2(gapFollow, lead(input, q2))) && (gapOther === void 0 || !classHas2(gapOther, lead(input, itemStart)))) {
16926
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
16927
+ break;
16928
+ }
16929
+ if (q2 >= 0) {
16930
+ GAP.row = gapRow;
16931
+ GAP.at = itemStart;
16932
+ GAP.end = q2;
16933
+ GAP.input = input;
16934
+ gapped = true;
16935
+ }
16936
+ }
16237
16937
  ctx._fc = false;
16238
16938
  const v = child(input, itemStart, ctx);
16939
+ if (gapped) GAP.at = -1;
16239
16940
  if (v === FAIL) {
16240
16941
  if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
16241
16942
  if (committed3(ctx)) return FAIL;
@@ -16267,7 +16968,8 @@ function assemble(t, prog, cfg) {
16267
16968
  const selector = link(code[ip + 1]);
16268
16969
  const otherIp = code[ip + 3];
16269
16970
  const other = otherIp >= 0 ? link(otherIp) : void 0;
16270
- const otherRouted = code[ip + 4] === 1;
16971
+ const otherRouted = (code[ip + 4] & 1) === 1;
16972
+ const otherCommits = (code[ip + 4] & 2) === 0;
16271
16973
  const n = code[ip + 5];
16272
16974
  const armBase = ip + 6;
16273
16975
  const byKey = spec.byKey;
@@ -16315,7 +17017,7 @@ function assemble(t, prog, cfg) {
16315
17017
  );
16316
17018
  };
16317
17019
  const runArm = n === 0 ? void 0 : bindArms(0);
16318
- const runOther = other === void 0 ? void 0 : dispatchBranch(other, otherRouted);
17020
+ const runOther = other === void 0 ? void 0 : dispatchBranch(other, otherRouted, otherCommits);
16319
17021
  return (input, pos, ctx) => {
16320
17022
  const need = markCst(ctx);
16321
17023
  const mRaw = MRAW;
@@ -16576,6 +17278,7 @@ function assemble(t, prog, cfg) {
16576
17278
  }
16577
17279
  const pre = cfg.hostCaptureTrivia === void 0 ? prog.asm?.find((a) => a.key === cfgKey(cfg)) : void 0;
16578
17280
  if (pre !== void 0) {
17281
+ assertFactoryArity(pre);
16579
17282
  const pools = rebuildPools(t.cc, t.fx, t.disp, pre.plan);
16580
17283
  emitted = pre.factory(
16581
17284
  EC,
@@ -16587,7 +17290,6 @@ function assemble(t, prog, cfg) {
16587
17290
  pools.classes,
16588
17291
  pools.armExpected,
16589
17292
  trivia2,
16590
- trivia2.map((tv) => tv?._meta.triviaKindLabels),
16591
17293
  triviaScan,
16592
17294
  scansArr,
16593
17295
  disp,
@@ -16611,7 +17313,6 @@ function assemble(t, prog, cfg) {
16611
17313
  cstLeavesLen,
16612
17314
  skipTriviaScanned,
16613
17315
  needsDeferredTriviaCommit,
16614
- scanTrivia,
16615
17316
  advanceTrivia,
16616
17317
  refuseUnclassifiedRootScope,
16617
17318
  spanLines,
@@ -16625,8 +17326,6 @@ function assemble(t, prog, cfg) {
16625
17326
  orSentinel,
16626
17327
  captureError,
16627
17328
  scalarRecognizers,
16628
- commitTriviaScan,
16629
- scanTriviaCompact,
16630
17329
  t.lex,
16631
17330
  adjacencyHolds,
16632
17331
  t.lexPrograms,
@@ -16638,7 +17337,8 @@ function assemble(t, prog, cfg) {
16638
17337
  PlainLeaf9,
16639
17338
  PlainNode2,
16640
17339
  PlainField,
16641
- PlainBuf2
17340
+ PlainBuf2,
17341
+ GAP
16642
17342
  );
16643
17343
  emitReached = new Set(pre.reached);
16644
17344
  if (emitted.ext !== void 0) HYBRID = emitted;
@@ -16674,7 +17374,6 @@ function assemble(t, prog, cfg) {
16674
17374
  em.classes,
16675
17375
  em.armExpected,
16676
17376
  trivia2,
16677
- trivia2.map((tv) => tv?._meta.triviaKindLabels),
16678
17377
  triviaScan,
16679
17378
  scansArr,
16680
17379
  disp,
@@ -16698,7 +17397,6 @@ function assemble(t, prog, cfg) {
16698
17397
  cstLeavesLen,
16699
17398
  skipTriviaScanned,
16700
17399
  needsDeferredTriviaCommit,
16701
- scanTrivia,
16702
17400
  advanceTrivia,
16703
17401
  refuseUnclassifiedRootScope,
16704
17402
  spanLines,
@@ -16717,8 +17415,6 @@ function assemble(t, prog, cfg) {
16717
17415
  orSentinel,
16718
17416
  captureError,
16719
17417
  scalarRecognizers,
16720
- commitTriviaScan,
16721
- scanTriviaCompact,
16722
17418
  t.lex,
16723
17419
  adjacencyHolds,
16724
17420
  t.lexPrograms,
@@ -16730,7 +17426,8 @@ function assemble(t, prog, cfg) {
16730
17426
  PlainLeaf9,
16731
17427
  PlainNode2,
16732
17428
  PlainField,
16733
- PlainBuf2
17429
+ PlainBuf2,
17430
+ GAP
16734
17431
  );
16735
17432
  emitReached = em.reached;
16736
17433
  } catch (e) {
@@ -16988,11 +17685,8 @@ function tableRules(source, metadataOrEntry = {}) {
16988
17685
  return map[entry];
16989
17686
  }
16990
17687
 
16991
- // src/version.ts
16992
- var PARSEMAN_VERSION = "0.59.2";
16993
-
16994
17688
  // src/table/emit.ts
16995
- 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`)});`;
17689
+ 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`)});`;
16996
17690
  function jsString(s) {
16997
17691
  return JSON.stringify(s);
16998
17692
  }
@@ -17078,7 +17772,7 @@ function emitAssemblies(prog, cfgs) {
17078
17772
  throw e;
17079
17773
  }
17080
17774
  const plan = em.plan;
17081
- out.push(`{key:${key},factory:function(${EMITTED_PARAMS.join(",")}){${FACTORY_ARITY_GUARD}${em.source}
17775
+ out.push(`{key:${key},arity:${EMITTED_PARAMS.length},factory:function(${EMITTED_PARAMS.join(",")}){${FACTORY_ARITY_GUARD}${em.source}
17082
17776
  },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(",")}]}`);
17083
17777
  }
17084
17778
  return [`a:[${out.join(",")}],`];
@@ -17354,7 +18048,7 @@ function payloadKey(p, d) {
17354
18048
  case "sepBy":
17355
18049
  return `sepBy\0${d.min}\0${d.max ?? ""}\0${d.trailing ?? ""}`;
17356
18050
  case "dispatch":
17357
- return `dispatch\0${d.cases.map((c) => JSON.stringify(c.keys)).join("")}\0${d.otherwise !== void 0}`;
18051
+ return `dispatch\0${d.cases.map((c) => JSON.stringify(c.keys)).join("")}\0${d.otherwise !== void 0}${d.otherwiseCommit === false ? "\0uncommitted" : ""}`;
17358
18052
  case "expect":
17359
18053
  return `expect\0${d.label ?? ""}\0${d.expected.join("")}`;
17360
18054
  case "scanTo":
@@ -18660,7 +19354,10 @@ function staticWeights(code, entries) {
18660
19354
  childSlots(code, ip, kids);
18661
19355
  const op = code[ip];
18662
19356
  const scale = op === OP_REP || op === OP_REPV ? LOOP : op === OP_CHOICE || op === OP_DISPATCH ? 1 / Math.max(1, kids.length) : 1;
18663
- for (const kid of kids) next.set(kid, Math.min(CAP, (next.get(kid) ?? 0) + w * scale));
19357
+ for (const k of kids) {
19358
+ const kid = code[k] === OP_GAPGATE ? code[k + 1] : k;
19359
+ next.set(kid, Math.min(CAP, (next.get(kid) ?? 0) + w * scale));
19360
+ }
18664
19361
  }
18665
19362
  weight = next;
18666
19363
  }
@@ -18777,7 +19474,7 @@ function compileRuleMap(ruleMap, opts = {}) {
18777
19474
  const roots = assemblyExtraIps(artifact);
18778
19475
  let siteBytes, fixed;
18779
19476
  try {
18780
- siteBytes = emitAssemblySource(table, artifact, cfg, roots, true, void 0, true).siteBytes;
19477
+ siteBytes = emitAssemblySource(table, artifact, cfg, roots, true, void 0, true, true).siteBytes;
18781
19478
  fixed = emitAssemblySource(table, artifact, cfg, roots, true, /* @__PURE__ */ new Set(), true).source.length;
18782
19479
  } catch (e) {
18783
19480
  if (e instanceof Unemittable) return { ...none, refusal: e.construct };
@@ -19322,7 +20019,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
19322
20019
  arms.push(`when(${matcherExpr(entry)}, ${kid(entry.parser)}${entry.caseInsensitive ? ", { caseInsensitive: true }" : ""})`);
19323
20020
  }
19324
20021
  }
19325
- if (def.otherwise) arms.push(`otherwise(${kid(def.otherwise)})`);
20022
+ if (def.otherwise) arms.push(`otherwise(${kid(def.otherwise)}${def.otherwiseCommit === false ? ", { commit: false }" : ""})`);
19326
20023
  return `dispatch(${kid(def.selector)}${arms.length === 0 ? "" : `, ${arms.join(", ")}`})`;
19327
20024
  }
19328
20025
  case "many":
@@ -21879,7 +22576,7 @@ function coverageEntry(entry, collector, maps, trace) {
21879
22576
  arms.push(when(dispatchMatcher(entry2), wrapArm(entry2.parser, ids[armIndex++]), entry2.caseInsensitive ? { caseInsensitive: true } : void 0));
21880
22577
  }
21881
22578
  }
21882
- if (def.otherwise) arms.push(otherwise(wrapArm(def.otherwise, ids[armIndex++])));
22579
+ if (def.otherwise) arms.push(otherwise(wrapArm(def.otherwise, ids[armIndex++]), def.otherwiseCommit === false ? { commit: false } : void 0));
21883
22580
  const unsafeDispatch = dispatch;
21884
22581
  const base2 = unsafeDispatch(build(def.selector), ...arms);
21885
22582
  return {