parseman 0.59.3 → 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 (84) hide show
  1. package/dist/analysis/diagnostics.js +874 -188
  2. package/dist/analysis/diagnostics.js.map +3 -3
  3. package/dist/cli/index.js +874 -188
  4. package/dist/cli/index.js.map +3 -3
  5. package/dist/combinators/dispatch.d.ts +13 -1
  6. package/dist/combinators/dispatch.d.ts.map +1 -1
  7. package/dist/combinators/first-set.d.ts +35 -2
  8. package/dist/combinators/first-set.d.ts.map +1 -1
  9. package/dist/combinators/regex.d.ts.map +1 -1
  10. package/dist/combinators/trivia-boundary.d.ts +11 -0
  11. package/dist/combinators/trivia-boundary.d.ts.map +1 -1
  12. package/dist/combinators/trivia-skip.d.ts +1 -1
  13. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  14. package/dist/compiler/token-alphabet.d.ts +2 -0
  15. package/dist/compiler/token-alphabet.d.ts.map +1 -1
  16. package/dist/compiler/token-capability.js +65 -53
  17. package/dist/compiler/token-capability.js.map +2 -2
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +894 -197
  21. package/dist/index.js.map +3 -3
  22. package/dist/plugin/evaluator.d.ts.map +1 -1
  23. package/dist/plugin/index.js +901 -199
  24. package/dist/plugin/index.js.map +3 -3
  25. package/dist/regex/first-set.d.ts +19 -0
  26. package/dist/regex/first-set.d.ts.map +1 -1
  27. package/dist/table/assemble.d.ts +14 -0
  28. package/dist/table/assemble.d.ts.map +1 -1
  29. package/dist/table/child-slots.d.ts.map +1 -1
  30. package/dist/table/compile-rule-map.d.ts.map +1 -1
  31. package/dist/table/emit-assembly.d.ts +8 -2
  32. package/dist/table/emit-assembly.d.ts.map +1 -1
  33. package/dist/table/emit.d.ts +4 -3
  34. package/dist/table/emit.d.ts.map +1 -1
  35. package/dist/table/encode.d.ts.map +1 -1
  36. package/dist/table/exec.d.ts.map +1 -1
  37. package/dist/table/factor-choice.d.ts +27 -0
  38. package/dist/table/factor-choice.d.ts.map +1 -1
  39. package/dist/table/gap-gate.d.ts +11 -0
  40. package/dist/table/gap-gate.d.ts.map +1 -0
  41. package/dist/table/index.js +924 -199
  42. package/dist/table/index.js.map +3 -3
  43. package/dist/table/lex-program.d.ts.map +1 -1
  44. package/dist/table/ops.d.ts +34 -6
  45. package/dist/table/ops.d.ts.map +1 -1
  46. package/dist/table/program.d.ts +6 -1
  47. package/dist/table/program.d.ts.map +1 -1
  48. package/dist/types.d.ts +16 -4
  49. package/dist/types.d.ts.map +1 -1
  50. package/dist/version.d.ts +1 -1
  51. package/package.json +1 -1
  52. package/src/analysis/duplication.ts +1 -1
  53. package/src/combinators/attempt.ts +1 -1
  54. package/src/combinators/dispatch.ts +30 -6
  55. package/src/combinators/first-set.ts +55 -12
  56. package/src/combinators/node.ts +1 -1
  57. package/src/combinators/regex.ts +9 -4
  58. package/src/combinators/repeat.ts +6 -6
  59. package/src/combinators/sequence.ts +1 -1
  60. package/src/combinators/trivia-boundary.ts +18 -0
  61. package/src/combinators/trivia-skip.ts +1 -1
  62. package/src/compiler/ir-serialize.ts +1 -1
  63. package/src/compiler/token-alphabet.ts +3 -1
  64. package/src/coverage.ts +1 -1
  65. package/src/index.ts +1 -1
  66. package/src/plugin/evaluator.ts +8 -3
  67. package/src/regex/first-set.ts +37 -3
  68. package/src/table/assemble.ts +171 -25
  69. package/src/table/child-slots.ts +8 -3
  70. package/src/table/compile-rule-map.ts +6 -3
  71. package/src/table/emit-assembly.ts +182 -31
  72. package/src/table/emit.ts +6 -4
  73. package/src/table/encode.ts +468 -73
  74. package/src/table/exec.ts +38 -13
  75. package/src/table/factor-choice.ts +62 -0
  76. package/src/table/fail-clean.ts +2 -2
  77. package/src/table/gap-gate.ts +55 -0
  78. package/src/table/hold-gap.ts +2 -2
  79. package/src/table/lex-program.ts +15 -6
  80. package/src/table/ops.ts +37 -6
  81. package/src/table/program.ts +7 -2
  82. package/src/table/select-hot.ts +7 -2
  83. package/src/types.ts +12 -5
  84. package/src/version.ts +1 -1
@@ -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": {
@@ -2018,10 +2065,6 @@ function scanTriviaCompact(input, cur, ctx) {
2018
2065
  const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
2019
2066
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
2020
2067
  }
2021
- function scanTrivia(input, cur, ctx) {
2022
- const scan = scanTriviaCompact(input, cur, ctx);
2023
- return typeof scan === "number" ? { end: scan, commit: NOOP_COMMIT } : scan;
2024
- }
2025
2068
  function skipTriviaScanned(s, input, cur, ctx) {
2026
2069
  const end = s(input, cur);
2027
2070
  if (end !== cur) {
@@ -2837,9 +2880,9 @@ function many(combinator, opts = {}) {
2837
2880
  canMatchNewline: combinator._meta.canMatchNewline,
2838
2881
  isTrivia: false
2839
2882
  };
2840
- const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max } };
2841
- let expected;
2842
2883
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
2884
+ const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max }, ...guardable ? { guarded: true } : {} };
2885
+ let expected;
2843
2886
  const scalarChild = scalarOf(combinator);
2844
2887
  const parseScalar = (input, pos, ctx) => repeatScalarTail(combinator, scalarChild, input, pos, ctx, guardable, def.valueUnused ? void 0 : [], max);
2845
2888
  return {
@@ -2886,9 +2929,9 @@ function atLeast(combinator, min, max) {
2886
2929
  canMatchNewline: combinator._meta.canMatchNewline,
2887
2930
  isTrivia: false
2888
2931
  };
2889
- const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max } };
2890
- let expected;
2891
2932
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
2933
+ const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max }, ...guardable ? { guarded: true } : {} };
2934
+ let expected;
2892
2935
  const scalarChild = scalarOf(combinator);
2893
2936
  const parseScalar = (input, pos, ctx) => {
2894
2937
  let cur = scalarChild(input, pos, ctx);
@@ -2952,6 +2995,42 @@ function atLeast(combinator, min, max) {
2952
2995
  }
2953
2996
  };
2954
2997
  }
2998
+ function optional(combinator) {
2999
+ const meta = {
3000
+ firstSet: combinator._meta.firstSet,
3001
+ canMatchNewline: combinator._meta.canMatchNewline,
3002
+ isTrivia: false
3003
+ };
3004
+ const firstSetSkippable = !matchesEmpty(combinator);
3005
+ const scalarChild = scalarOf(combinator);
3006
+ const parseScalar = (input, pos, ctx) => {
3007
+ if (firstSetSkippable && !startsFirstSet(combinator, input, pos)) {
3008
+ ctx._sv = null;
3009
+ return pos;
3010
+ }
3011
+ const end = scalarChild(input, pos, ctx);
3012
+ if (end >= 0) return end;
3013
+ ctx._sv = null;
3014
+ return pos;
3015
+ };
3016
+ return {
3017
+ _tag: "optional",
3018
+ _meta: meta,
3019
+ _def: { tag: "optional", parser: combinator },
3020
+ _parseScalar: parseScalar,
3021
+ parse(input, pos, ctx) {
3022
+ if (firstSetSkippable && ctx._probe === void 0 && !startsFirstSet(combinator, input, pos)) {
3023
+ return { ok: true, value: null, span: { start: pos, end: pos } };
3024
+ }
3025
+ const mark = saveTriviaMark(ctx);
3026
+ const result = combinator.parse(input, pos, ctx);
3027
+ if (result.ok) return result;
3028
+ rollbackTrivia(ctx, mark);
3029
+ if (result.committed) return result;
3030
+ return { ok: true, value: null, span: { start: pos, end: pos } };
3031
+ }
3032
+ };
3033
+ }
2955
3034
 
2956
3035
  // src/combinators/map.ts
2957
3036
  var JSON_STRING_BODY = String.raw`(?:[^"\\]|\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4}))*`;
@@ -3155,10 +3234,12 @@ function parseRegex(src, unicode = false, sets = false) {
3155
3234
  const parseGroup = () => {
3156
3235
  i++;
3157
3236
  let lookaround = false;
3237
+ let lookahead = false;
3158
3238
  if (src[i] === "?") {
3159
3239
  const c1 = src[i + 1];
3160
3240
  if (c1 === ":") i += 2;
3161
3241
  else if (c1 === "=" || c1 === "!") {
3242
+ lookahead = c1 === "=";
3162
3243
  i += 2;
3163
3244
  lookaround = true;
3164
3245
  } else if (c1 === "<") {
@@ -3176,7 +3257,8 @@ function parseRegex(src, unicode = false, sets = false) {
3176
3257
  const inner = parseAlt();
3177
3258
  if (src[i] !== ")") throw BAIL;
3178
3259
  i++;
3179
- return lookaround ? EMPTY : inner;
3260
+ if (lookaround) return lookahead ? { t: "empty", look: inner } : EMPTY;
3261
+ return inner;
3180
3262
  };
3181
3263
  const parseClass = () => {
3182
3264
  let k = i + 1;
@@ -3369,6 +3451,13 @@ function regexCanMatchEmpty(source) {
3369
3451
  return true;
3370
3452
  }
3371
3453
  }
3454
+ function consumingFirstSetFromRegex(source, flags = "") {
3455
+ try {
3456
+ return firstSet(parseRegex(source, flags.includes("u") || flags.includes("v"), flags.includes("v")));
3457
+ } catch {
3458
+ return any();
3459
+ }
3460
+ }
3372
3461
  function firstSetFromRegex(source, flags = "") {
3373
3462
  let ast;
3374
3463
  try {
@@ -3379,6 +3468,20 @@ function firstSetFromRegex(source, flags = "") {
3379
3468
  if (nullable(ast)) return { firstSet: any(), canMatchNewline: canConsumeNewline(ast) };
3380
3469
  return { firstSet: firstSet(ast), canMatchNewline: canConsumeNewline(ast) };
3381
3470
  }
3471
+ function regexFirstTwo(source, flags = "") {
3472
+ let ast;
3473
+ try {
3474
+ ast = parseRegex(source, flags.includes("u") || flags.includes("v"), flags.includes("v"));
3475
+ } catch {
3476
+ return void 0;
3477
+ }
3478
+ const parts = ast.t === "seq" ? ast.parts : [ast];
3479
+ const a = parts[0];
3480
+ const b = parts[1];
3481
+ if (a === void 0 || a.t !== "char" || a.set.kind !== "ranges" || b === void 0) return void 0;
3482
+ const second = b.t === "char" ? b.set : b.t === "empty" && b.look !== void 0 && !nullable(b.look) ? firstSet(b.look) : void 0;
3483
+ return second !== void 0 && second.kind === "ranges" ? { first: a.set, second } : void 0;
3484
+ }
3382
3485
 
3383
3486
  // src/combinators/regex.ts
3384
3487
  import { PlainLeaf as PlainLeaf2, PlainSpan as PlainSpan4 } from "../cst/plain.js";
@@ -3459,9 +3562,14 @@ function regex(pattern, flags = "") {
3459
3562
  const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
3460
3563
  const scan = shortScanner(source, resolvedFlags);
3461
3564
  const raw = firstSetFromRegex(source, resolvedFlags);
3462
- const firstSet2 = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
3565
+ const forFlags = (fs) => !resolvedFlags.includes("i") ? fs : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(fs);
3566
+ const firstSet2 = forFlags(raw.firstSet);
3463
3567
  const canMatchNewline = raw.canMatchNewline;
3464
3568
  const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
3569
+ if (firstSet2.kind === "any" && regexCanMatchEmpty(source)) {
3570
+ const consuming = forFlags(consumingFirstSetFromRegex(source, resolvedFlags));
3571
+ if (consuming.kind !== "any") meta.consumingFirstSet = consuming;
3572
+ }
3465
3573
  const def = { tag: "regex", source, flags: resolvedFlags };
3466
3574
  const expected = directTerminalFailureExpected(def);
3467
3575
  const parseScalar = (input, pos, ctx) => {
@@ -3736,14 +3844,15 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
3736
3844
  return end < 0 ? next(input, pos) : end;
3737
3845
  };
3738
3846
  }
3739
- let tail = (_input, pos, ctx, _lead, acc) => {
3847
+ let tail = (input, pos, ctx, _lead, acc, _best, entered) => {
3848
+ if (!entered && ctx._probe !== void 0) for (const arm of arms) arm.run(input, pos, ctx);
3740
3849
  ctx._fe = pos;
3741
3850
  ctx._fx = acc ?? choiceExpected;
3742
3851
  return -1;
3743
3852
  };
3744
3853
  for (let i = arms.length - 1; i >= 0; i--) {
3745
3854
  const armRun = arms[i].run, cls = classes[i], staticExpected = armExpected[i], next = tail;
3746
- tail = (input, pos, ctx, lead2, acc, best) => {
3855
+ tail = (input, pos, ctx, lead2, acc, best, entered) => {
3747
3856
  if (cls !== void 0 && !classHas(cls, lead2)) {
3748
3857
  return next(
3749
3858
  input,
@@ -3751,7 +3860,8 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
3751
3860
  ctx,
3752
3861
  lead2,
3753
3862
  best === pos ? appendExpected(acc, staticExpected) : acc,
3754
- best
3863
+ best,
3864
+ entered
3755
3865
  );
3756
3866
  }
3757
3867
  ctx._fc = false;
@@ -3767,7 +3877,7 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
3767
3877
  if (nextAcc !== void 0) ctx._fx = nextAcc;
3768
3878
  return -1;
3769
3879
  }
3770
- return next(input, pos, ctx, lead2, nextAcc, best);
3880
+ return next(input, pos, ctx, lead2, nextAcc, best, true);
3771
3881
  };
3772
3882
  }
3773
3883
  return {
@@ -3778,7 +3888,8 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
3778
3888
  ctx,
3779
3889
  pos < input.length ? input.codePointAt(pos) : -1,
3780
3890
  void 0,
3781
- pos
3891
+ pos,
3892
+ false
3782
3893
  )
3783
3894
  };
3784
3895
  }
@@ -3816,6 +3927,7 @@ function exclusiveChoiceNode(arms, classes, choiceFx) {
3816
3927
  return -1;
3817
3928
  }
3818
3929
  }
3930
+ if (arm < 0 && ctx._probe !== void 0) for (const a of arms) a.run(input, pos, ctx);
3819
3931
  ctx._fe = pos;
3820
3932
  ctx._fx = choiceFx;
3821
3933
  return -1;
@@ -3862,10 +3974,10 @@ function repeatNode(item, min, max) {
3862
3974
  let count = 0;
3863
3975
  for (; ; ) {
3864
3976
  if (max >= 0 && count >= max) break;
3865
- const optional = count >= min && (count > 0 || min === 0);
3866
- if (optional && cur >= input.length) break;
3977
+ const optional2 = count >= min && (count > 0 || min === 0);
3978
+ if (optional2 && cur >= input.length) break;
3867
3979
  const end = itemMatch(input, cur);
3868
- if (end < 0 || optional && end === cur) break;
3980
+ if (end < 0 || optional2 && end === cur) break;
3869
3981
  cur = end;
3870
3982
  count++;
3871
3983
  }
@@ -3876,15 +3988,15 @@ function repeatNode(item, min, max) {
3876
3988
  let count = 0;
3877
3989
  for (; ; ) {
3878
3990
  if (max >= 0 && count >= max) break;
3879
- const optional = count >= min && (count > 0 || min === 0);
3880
- if (optional && cur >= input.length) break;
3991
+ const optional2 = count >= min && (count > 0 || min === 0);
3992
+ if (optional2 && cur >= input.length) break;
3881
3993
  ctx._fc = false;
3882
3994
  const end = itemRun(input, cur, ctx);
3883
3995
  if (end < 0) {
3884
3996
  if (committed(ctx)) return -1;
3885
3997
  break;
3886
3998
  }
3887
- if (optional && end === cur) break;
3999
+ if (optional2 && end === cur) break;
3888
4000
  cur = end;
3889
4001
  count++;
3890
4002
  }
@@ -4040,6 +4152,7 @@ function buildLexProgramRunner(spec, matchers, classes, expected) {
4040
4152
  if (!classHas(cls, lead2)) {
4041
4153
  ctx._fe = pos;
4042
4154
  ctx._fx = choiceFx.slice();
4155
+ if (ctx._probe !== void 0) failAt(ctx, [...x0, ...x1, ...x2, ...x3], pos);
4043
4156
  return -1;
4044
4157
  }
4045
4158
  let acc;
@@ -4151,7 +4264,7 @@ function validateDispatchSpec(spec, arity, fallbackRouted) {
4151
4264
  for (const arm of arms) if (!Number.isInteger(arm) || arm < 0 || arm >= arity) return true;
4152
4265
  return false;
4153
4266
  };
4154
- 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]]))) {
4267
+ 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]]))) {
4155
4268
  throw new TypeError("table: malformed dispatch");
4156
4269
  }
4157
4270
  }
@@ -4636,7 +4749,7 @@ function sequence(...parsers) {
4636
4749
  if (tolerant) {
4637
4750
  followSentinels ??= parsers.map((_, i) => firstSetSentinel(
4638
4751
  parsers.slice(i + 1).reduce(
4639
- (set, parser2) => union(set, firstSetOf(parser2)),
4752
+ (set, parser2) => union(set, firstSetOf(parser2, /* @__PURE__ */ new Set(), void 0, void 0, false)),
4640
4753
  { kind: "empty" }
4641
4754
  )
4642
4755
  ));
@@ -4694,42 +4807,6 @@ function sequence(...parsers) {
4694
4807
  };
4695
4808
  }
4696
4809
 
4697
- // src/combinators/trivia-boundary.ts
4698
- var MAX_DEPTH = 12;
4699
- function hasOwnTriviaBoundary(p, depth = MAX_DEPTH) {
4700
- if (depth === 0) return true;
4701
- const d = p._def;
4702
- switch (d.tag) {
4703
- // Multi-term forms: the gaps BETWEEN terms are the ambient-trivia positions.
4704
- case "sequence":
4705
- return d.parsers.length > 1;
4706
- case "many":
4707
- case "oneOrMore":
4708
- case "sepBy":
4709
- return true;
4710
- // Transparent wrappers — they add no position, so ask what they wrap.
4711
- case "node":
4712
- case "transform":
4713
- case "leaf":
4714
- case "label":
4715
- case "field":
4716
- case "token":
4717
- case "attempt":
4718
- case "optional":
4719
- case "expect":
4720
- case "withCtx":
4721
- case "recover":
4722
- case "trivia":
4723
- return hasOwnTriviaBoundary(d.parser, depth - 1);
4724
- // An alternation adds no position either, but ONE arm that has one is enough
4725
- // to make the scope meaningful for this rule.
4726
- case "choice":
4727
- return d.parsers.some((a) => hasOwnTriviaBoundary(a, depth - 1));
4728
- default:
4729
- return false;
4730
- }
4731
- }
4732
-
4733
4810
  // src/combinators/ref.ts
4734
4811
  function withRuleTrivia(trivia2, ctx, run) {
4735
4812
  const savedLabels = ctx.triviaKindLabels;
@@ -5650,6 +5727,9 @@ function projectChild(children2, project, type) {
5650
5727
  return unwrapChild(children2[project]);
5651
5728
  }
5652
5729
 
5730
+ // src/version.ts
5731
+ var PARSEMAN_VERSION = "0.60.0";
5732
+
5653
5733
  // src/table/assemble.ts
5654
5734
  import { PlainBuf as PlainBuf2, PlainField, PlainLeaf as PlainLeaf8, PlainNode as PlainNode2, PlainSpan as PlainSpan9 } from "../cst/plain.js";
5655
5735
 
@@ -5697,6 +5777,9 @@ var OP_LEX_BODY = 42;
5697
5777
  var OP_LEX_PROGRAM = 43;
5698
5778
  var OP_SOURCE_LEAF = 44;
5699
5779
  var OP_RGATE = 45;
5780
+ var OP_GAPGATE = 46;
5781
+ var OP_RGATE2 = 47;
5782
+ var OP_STRICT = 48;
5700
5783
  var OP_DISPATCH = 25;
5701
5784
  var OP_ROUTED = 26;
5702
5785
  var OP_NAMES = {
@@ -5744,7 +5827,10 @@ var OP_NAMES = {
5744
5827
  [OP_COV]: "COV",
5745
5828
  [OP_SCOPE_PLAIN]: "SCOPE_PLAIN",
5746
5829
  [OP_LEX_BODY]: "LEX_BODY",
5747
- [OP_LEX_PROGRAM]: "LEX_PROGRAM"
5830
+ [OP_LEX_PROGRAM]: "LEX_PROGRAM",
5831
+ [OP_GAPGATE]: "GAPGATE",
5832
+ [OP_RGATE2]: "RGATE2",
5833
+ [OP_STRICT]: "STRICT"
5748
5834
  };
5749
5835
 
5750
5836
  // src/table/scan-shapes.ts
@@ -6791,6 +6877,7 @@ function childSlots(code, ip, out) {
6791
6877
  case OP_LABEL:
6792
6878
  case OP_COV:
6793
6879
  case OP_REJECT:
6880
+ case OP_GAPGATE:
6794
6881
  out.push(code[ip + 1]);
6795
6882
  return true;
6796
6883
  // Single child at `ip + 2`.
@@ -6807,8 +6894,13 @@ function childSlots(code, ip, out) {
6807
6894
  case OP_SOURCE_LEAF:
6808
6895
  case OP_ARMGATE:
6809
6896
  case OP_RGATE:
6897
+ case OP_RGATE2:
6810
6898
  out.push(code[ip + 2]);
6811
6899
  return true;
6900
+ // `STRICT strict tolerant` — both encodings are reachable; an assembly keeps one.
6901
+ case OP_STRICT:
6902
+ out.push(code[ip + 1], code[ip + 2]);
6903
+ return true;
6812
6904
  // `ROUTED fallback` — the operand is an offset or −1. The routed token itself is
6813
6905
  // data the enclosing `dispatch()` already consumed, so the only edge is the
6814
6906
  // fallback.
@@ -7256,7 +7348,8 @@ var WRAPPERS = /* @__PURE__ */ new Set([
7256
7348
  OP_WITHCTX,
7257
7349
  OP_RULE,
7258
7350
  OP_ATTEMPT,
7259
- OP_COV
7351
+ OP_COV,
7352
+ OP_GAPGATE
7260
7353
  ]);
7261
7354
  function rowNullable(code, k, ip, memo) {
7262
7355
  const hit = memo.get(ip);
@@ -7293,7 +7386,31 @@ function holdsGap(code, k, base, n, i, memo) {
7293
7386
  return !rowNullable(code, k, next, memo);
7294
7387
  }
7295
7388
 
7389
+ // src/table/gap-gate.ts
7390
+ function newGapSlot() {
7391
+ return { row: -1, at: -1, end: -1, input: void 0 };
7392
+ }
7393
+ function gapRows(code, roots) {
7394
+ const rows = /* @__PURE__ */ new Set();
7395
+ const seen = /* @__PURE__ */ new Set();
7396
+ const stack = [...roots];
7397
+ const kids = [];
7398
+ while (stack.length > 0) {
7399
+ const ip = stack.pop();
7400
+ if (seen.has(ip)) continue;
7401
+ seen.add(ip);
7402
+ if (code[ip] === OP_GAPGATE) rows.add(code[ip + 2]);
7403
+ kids.length = 0;
7404
+ childSlots(code, ip, kids);
7405
+ for (const c of kids) stack.push(c);
7406
+ }
7407
+ return rows;
7408
+ }
7409
+
7296
7410
  // src/table/emit-assembly.ts
7411
+ function gapRegexLowers(re) {
7412
+ return re instanceof RegExp && scanShapeFromRegex(re.source, re.flags) !== null;
7413
+ }
7297
7414
  var Unemittable = class extends Error {
7298
7415
  construct;
7299
7416
  constructor(construct) {
@@ -7312,7 +7429,6 @@ var EMITTED_PARAMS = [
7312
7429
  "CLS",
7313
7430
  "AFX",
7314
7431
  "TRIVIA",
7315
- "TRIVIALABELS",
7316
7432
  "TRIVIASCAN",
7317
7433
  "SCANS",
7318
7434
  "DISP",
@@ -7336,7 +7452,6 @@ var EMITTED_PARAMS = [
7336
7452
  "cstLeavesLen",
7337
7453
  "skipTriviaScanned",
7338
7454
  "needsDeferredTriviaCommit",
7339
- "scanTrivia",
7340
7455
  "advanceTrivia",
7341
7456
  "refuseUnclassifiedRootScope",
7342
7457
  "spanLines",
@@ -7356,34 +7471,31 @@ var EMITTED_PARAMS = [
7356
7471
  "orSentinel",
7357
7472
  "captureError",
7358
7473
  // Pure scalar terminal recognizers, indexed by the terminal's existing const
7359
- // operand. Appended for compatibility with older precompiled factories.
7474
+ // operand; selected lexical bodies; the adjacency test.
7360
7475
  "RECOG",
7361
- // Appended so precompiled factories produced by earlier runtimes keep every
7362
- // positional helper binding. Older factories ignore this trailing argument.
7363
- "commitTriviaScan",
7364
- "scanTriviaCompact",
7365
7476
  "LEX",
7366
- // Appended for old precompiled-factory ABI: old factories ignore it.
7367
7477
  "adjacencyHolds",
7368
- // Appended for selected composite lexical programs; old factories ignore it.
7478
+ // Selected composite lexical programs.
7369
7479
  "LEXPROG",
7370
- // Appended for trivia-attributing fields (`field(…, { leadingTrivia })`);
7371
- // old factories ignore them.
7480
+ // Trivia-attributing fields (`field(…, { leadingTrivia })`).
7372
7481
  "LEAD",
7373
7482
  "triviaFieldCapture",
7374
- // Appended for the deferred-commit trivia skip (`trivia-gap.ts`); old
7375
- // factories ignore it.
7483
+ // The deferred-commit trivia skip (`trivia-gap.ts`).
7376
7484
  "skipDeferredTrivia",
7377
- // Appended for the pass-through collapse (`run-support.ts`); old factories
7378
- // ignore it.
7485
+ // The pass-through collapse (`run-support.ts`).
7379
7486
  "collapseEmpty",
7380
- // Appended for constructor-built tree objects (`cst/plain.d.ts`): a `new` call has
7381
- // no allocation site, so V8 cannot pretenure them. Old factories ignore them.
7487
+ // Constructor-built tree objects (`cst/plain.d.ts`): a `new` call has no
7488
+ // allocation site, so V8 cannot pretenure them.
7382
7489
  "_SP",
7383
7490
  "_LF",
7384
7491
  "_ND",
7385
7492
  "_FC",
7386
- "_BUF"
7493
+ "_BUF",
7494
+ // The gap gate's slot (`gap-gate.ts`).
7495
+ "GAP"
7496
+ // Artifacts are version-locked (docs/design/artifact-format.md): a factory is
7497
+ // only ever called by the runtime that emitted it, so a name nothing emitted
7498
+ // reads is removed rather than kept for an older factory's positions.
7387
7499
  ];
7388
7500
  var RUNTIME_PRELUDE = `
7389
7501
  let _pfScan=null
@@ -7634,7 +7746,7 @@ function rebuildPools(cc, fx, disp, plan) {
7634
7746
  })
7635
7747
  };
7636
7748
  }
7637
- function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false, select, scanSinkNeutral = false) {
7749
+ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false, select, scanSinkNeutral = false, gapNeutral = false) {
7638
7750
  const { code, k, fx, disp, dsp, triviaLabelled } = t;
7639
7751
  const swapLegal = !cfg.trackLines;
7640
7752
  const hostCst = cfg.hostCst;
@@ -7642,6 +7754,7 @@ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false, se
7642
7754
  if (cfg.coverage) throw new Unemittable("a coverage assembly");
7643
7755
  const roots = [...Object.values(prog.rules), ...extraIps];
7644
7756
  const reachable = [...reachableSites(code, roots)];
7757
+ const GAP_ROWS = gapNeutral ? /* @__PURE__ */ new Set() : new Set([...gapRows(code, roots)].filter((row) => !gapRegexLowers(k[code[row + 1]])));
7645
7758
  const labels = computeSiteLabels(code, roots, hostCst);
7646
7759
  const scalarSpecs = /* @__PURE__ */ new Set();
7647
7760
  const nullable2 = /* @__PURE__ */ new Map();
@@ -7979,13 +8092,15 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
7979
8092
  function aliasOf(ip) {
7980
8093
  const op = code[ip];
7981
8094
  if (op === OP_GATE && (cfg.tolerant || cfg.probe)) return code[ip + 2];
7982
- if (op === OP_RULE) return code[ip + 1];
8095
+ if (op === OP_RULE || op === OP_GAPGATE) return code[ip + 1];
8096
+ if (op === OP_STRICT) return code[cfg.tolerant || cfg.probe ? ip + 2 : ip + 1];
7983
8097
  if (scopeIsIdentity(code, ip, labels.at(ip))) return code[ip + 2];
7984
8098
  return void 0;
7985
8099
  }
7986
8100
  function lower(ip, fname) {
7987
8101
  const op = code[ip];
7988
8102
  const head = `function ${fname}(input,pos,ctx){`;
8103
+ const outerHead = head;
7989
8104
  const L = labels.at(ip);
7990
8105
  const sinks = sinksAt(ip);
7991
8106
  const pushLeaf = L.buf && !cfg.trackLines ? L.raw === RAW_OMIT ? "_pushLeafNoRawBuf" : L.raw === RAW_CAPTURE ? "_pushLeafBuf" : "_pushLeaf" : "_pushLeaf";
@@ -8061,9 +8176,27 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
8061
8176
  case OP_RX_TRACK: {
8062
8177
  const xf = fxRef(code[ip + 2]);
8063
8178
  const track = op === OP_RX_TRACK;
8179
+ if (GAP_ROWS.has(ip)) {
8180
+ const re2 = hoist("gx", `K[${code[ip + 1]}]`);
8181
+ return `${outerHead}
8182
+ let e
8183
+ if(GAP.row===${ip}&&GAP.at===pos&&GAP.input===input)e=GAP.end
8184
+ else{${re2}.lastIndex=pos;e=${re2}.test(input)?${re2}.lastIndex:-1}
8185
+ if(e>=0){
8186
+ const v=input.slice(pos,e)
8187
+ ${captureLeaf("v")}
8188
+ ${track ? "_trackLines(ctx,input,e)\n" : ""}EC.e=e
8189
+ return v
8190
+ }
8191
+ ctx._fe=pos;ctx._fx=${xf}
8192
+ ${cfg.probe ? `failAt(ctx,${xf},pos)
8193
+ ` : ""}return FAIL
8194
+ }`;
8195
+ }
8196
+ const head2 = outerHead;
8064
8197
  if (!track && scalarSpecs.has(code[ip + 1])) {
8065
8198
  const recognize = recognizerRef(code[ip + 1]);
8066
- return `${head}
8199
+ return `${head2}
8067
8200
  const e=${recognize}(input,pos)
8068
8201
  if(e>=0){
8069
8202
  const v=input.slice(pos,e)
@@ -8076,11 +8209,11 @@ return FAIL
8076
8209
  }`;
8077
8210
  }
8078
8211
  const scanned = emitScan(code[ip + 1], xf, track, captureLeaf);
8079
- if (scanned !== void 0) return `${head}${scanned}}`;
8212
+ if (scanned !== void 0) return `${head2}${scanned}}`;
8080
8213
  const rxk = k[code[ip + 1]];
8081
8214
  if (!(rxk instanceof RegExp) || !rxk.sticky) throw new Unemittable("a non-sticky regex row");
8082
8215
  const re = kRef(code[ip + 1]);
8083
- return `${head}
8216
+ return `${head2}
8084
8217
  ${re}.lastIndex=pos
8085
8218
  if(${re}.test(input)){
8086
8219
  const e=${re}.lastIndex
@@ -8118,6 +8251,28 @@ return ${child}(input,pos,ctx)
8118
8251
  const it=ctx._routed
8119
8252
  if(it!==undefined&&it.span.start===pos&&!classHas(${cls},lead(input,it.span.end))){ctx._fe=it.span.end;ctx._fx=${xf};return FAIL}
8120
8253
  return ${child}(input,pos,ctx)
8254
+ }`;
8255
+ }
8256
+ case OP_RGATE2: {
8257
+ const child = link(code[ip + 2]);
8258
+ if (cfg.tolerant || cfg.probe) return `${head}
8259
+ return ${child}(input,pos,ctx)
8260
+ }`;
8261
+ const idxs = [code[ip + 1], code[ip + 4], code[ip + 5]];
8262
+ const ascii = idxs.every((i) => isAsciiOnlyClass(t.cc[i]));
8263
+ const ref2 = (idx, tag) => {
8264
+ const ci = classes.push([t.cc[idx]]) - 1;
8265
+ classPlan.push([idx]);
8266
+ return hoist(tag, `CLS[${ci}][0]`);
8267
+ };
8268
+ const [cls, first, second] = [ref2(idxs[0], "cl"), ref2(idxs[1], "c1"), ref2(idxs[2], "c2")];
8269
+ const has = (c, at) => ascii ? `${c}.ascii[${at}]===1` : `classHas(${c},${at})`;
8270
+ const xf = fxRef(code[ip + 3]);
8271
+ return `${head}
8272
+ const it=ctx._routed
8273
+ if(it!==undefined&&it.span.start===pos){const e=it.span.end;const c=${ascii ? "input.charCodeAt(e)" : "lead(input,e)"}
8274
+ 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}}
8275
+ return ${child}(input,pos,ctx)
8121
8276
  }`;
8122
8277
  }
8123
8278
  case OP_ARMGATE: {
@@ -8527,7 +8682,27 @@ if(ctx._fc===true)return FAIL
8527
8682
  const af=ctx._fx
8528
8683
  if(af!==undefined&&af.length>0){ctx._fe=pos;return FAIL}
8529
8684
  }
8530
- ctx._fe=pos;ctx._fx=${choiceFx}
8685
+ ${cfg.probe ? `if(arm<0){${arms.map((a) => `${a}(input,pos,ctx)`).join(";")}}
8686
+ ` : ""}ctx._fe=pos;ctx._fx=${choiceFx}
8687
+ return FAIL
8688
+ }`;
8689
+ }
8690
+ if (cfg.probe) {
8691
+ const p = tmp();
8692
+ return `${head}
8693
+ ${emitMark(p, L.buf, L.raw, sinks)}
8694
+ let acc
8695
+ let best=pos
8696
+ ${arms.map((arm) => `{ctx._fc=false
8697
+ const v=${arm}(input,pos,ctx)
8698
+ if(v!==FAIL)return v
8699
+ const at=ctx._fe??pos
8700
+ if(at>best){best=at;acc=undefined}
8701
+ if(at===best)acc=_accSet(ctx._fx,acc)
8702
+ if(ctx._fc===true){if(acc!==undefined)ctx._fx=acc;return FAIL}
8703
+ ${emitRollback(p, L.buf, L.raw, sinks)}
8704
+ }`).join("\n")}
8705
+ ctx._fe=pos;ctx._fx=acc??${choiceFx}
8531
8706
  return FAIL
8532
8707
  }`;
8533
8708
  }
@@ -8734,7 +8909,8 @@ return FAIL
8734
8909
  const selector = link(code[ip + 1]);
8735
8910
  const otherIp = code[ip + 3];
8736
8911
  const other = otherIp >= 0 ? link(otherIp) : void 0;
8737
- const otherRouted = code[ip + 4] === 1;
8912
+ const otherRouted = (code[ip + 4] & 1) === 1;
8913
+ const otherCommits = (code[ip + 4] & 2) === 0;
8738
8914
  const n = code[ip + 5];
8739
8915
  validateDispatchSpec(spec, n, code[ip + 4]);
8740
8916
  const armBase = ip + 6;
@@ -8788,7 +8964,7 @@ ${fallbackCase}
8788
8964
  }
8789
8965
  if(v===FAIL){
8790
8966
  ${emitRollback(m2, L.buf, L.raw, sinks)}
8791
- ctx._fc=true
8967
+ ${other !== void 0 && !otherCommits ? "if(arm!==undefined)" : ""}ctx._fc=true
8792
8968
  return FAIL
8793
8969
  }
8794
8970
  return [key,v]
@@ -8820,6 +8996,63 @@ return [key,v]
8820
8996
  }
8821
8997
  }
8822
8998
  const itemMiss = (at) => itemAscii ? `${itemCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${itemCls},lead(input,${at}))`;
8999
+ let sepCls;
9000
+ let sepAscii = false;
9001
+ if (!REC && sepIp >= 0 && (code[ip + 5] & 8) !== 0 && !leadingTriviaFieldAt(code, k, sepIp) && !rowNullable(code, k, sepIp, nullable2)) {
9002
+ const resolved = t.cc[code[ip + 8]];
9003
+ const ci = classes.push([resolved]) - 1;
9004
+ classPlan.push([code[ip + 8]]);
9005
+ sepCls = hoist("rs", `CLS[${ci}][0]`);
9006
+ sepAscii = isAsciiOnlyClass(resolved);
9007
+ }
9008
+ const gapIp = !gapNeutral && sepIp < 0 && min === 0 && !REC && code[code[ip + 1]] === OP_GAPGATE ? code[ip + 1] : -1;
9009
+ let gapCode = (_rb) => "";
9010
+ if (gapIp >= 0) {
9011
+ const row = code[gapIp + 2];
9012
+ const rx = k[code[row + 1]];
9013
+ const shape = gapRegexLowers(rx) ? scanShapeFromRegex(rx.source, rx.flags) : null;
9014
+ const cls = (idx) => {
9015
+ const resolved = t.cc[idx];
9016
+ const ci = classes.push([resolved]) - 1;
9017
+ classPlan.push([idx]);
9018
+ const ref2 = hoist("gc", `CLS[${ci}][0]`);
9019
+ return isAsciiOnlyClass(resolved) ? `${ref2}.ascii[input.charCodeAt(${"@"})]!==1` : `!classHas(${ref2},lead(input,${"@"}))`;
9020
+ };
9021
+ const follow = cls(code[gapIp + 3]).replaceAll("@", "gq");
9022
+ const other = code[gapIp + 4] >= 0 ? cls(code[gapIp + 4]).replaceAll("@", "itemStart") : "true";
9023
+ if (shape !== null) {
9024
+ let n = 0;
9025
+ const q2 = tmp();
9026
+ const m = emitShapeMatch(shape, "itemStart", (prefix = "_v") => `${q2}${prefix}${n++}`, "");
9027
+ gapCode = (rbCode) => `let gapped=false
9028
+ if(ctx._probe===undefined){
9029
+ ${m.setup.join("\n")}
9030
+ const gq=${m.ok}?${m.end}:-1
9031
+ if((gq<0||${follow})&&${other}){
9032
+ ${rbCode}
9033
+ break
9034
+ }
9035
+ }
9036
+ `;
9037
+ } else {
9038
+ const re = hoist("gr", `K[${code[row + 1]}]`);
9039
+ gapCode = (rbCode) => `let gapped=false
9040
+ if(ctx._probe===undefined){
9041
+ ${re}.lastIndex=itemStart
9042
+ const gq=${re}.test(input)?${re}.lastIndex:-1
9043
+ if((gq<0||${follow})&&${other}){
9044
+ ${rbCode}
9045
+ break
9046
+ }
9047
+ if(gq>=0){
9048
+ GAP.row=${row};GAP.at=itemStart;GAP.end=gq;GAP.input=input
9049
+ gapped=true
9050
+ }
9051
+ }
9052
+ `;
9053
+ }
9054
+ }
9055
+ const sepMiss = (at) => sepAscii ? `${sepCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${sepCls},lead(input,${at}))`;
8823
9056
  const skipBeforeFirst = sepIp < 0 && min === 0;
8824
9057
  const p = tmp();
8825
9058
  const via = sep !== void 0 ? "false" : min === 0 ? skipBeforeFirst ? "true" : "count>0" : `count>=${min}&&count>0`;
@@ -8857,7 +9090,7 @@ ${p}lg=ctx._triviaLog!==undefined?ctx._triviaLog.length:0
8857
9090
  ${p}rt=ctx._rootTriviaLog!==undefined?ctx._rootTriviaLog.length:0${markSinks}
8858
9091
  }`;
8859
9092
  const my = `${p}my`;
8860
- const recFx = REC ? fxRef(code[ip + 6]) : "";
9093
+ const recFx = !REC ? "" : (flags & 16) !== 0 ? "[]" : fxRef(code[ip + 6]);
8861
9094
  const sepSent = REC ? sentRef(code[ip + 7]) : "undefined";
8862
9095
  const recSent = sep === void 0 ? my : sepSent === "undefined" ? `orSentinel(${my},undefined)` : `orSentinel(${sepSent}??${my},${sepSent}===undefined?undefined:${my})`;
8863
9096
  const recGate = sep !== void 0 || min === 0 ? "" : `count>=${min}&&`;
@@ -8873,7 +9106,7 @@ continue
8873
9106
  return `${head}
8874
9107
  const out=${collect ? "[]" : "undefined"}
8875
9108
  ${knownTrivia === void 0 ? "const hasTrivia=ctx.trivia!==undefined\n" : ""}${L.buf ? "" : "const needMark=_rollbackNeeded(ctx)\n"}${REC ? `const ${my}=ctx._sync
8876
- ` : ""}${itemCls === void 0 ? "" : `const ${p}gate=ctx._probe===undefined
9109
+ ` : ""}${itemCls === void 0 && sepCls === void 0 ? "" : `const ${p}gate=ctx._probe===undefined
8877
9110
  `}let cur=pos
8878
9111
  let count=0
8879
9112
  for(;;){
@@ -8889,6 +9122,10 @@ const lb=cstLeavesLen(ctx)
8889
9122
  let sp=cur
8890
9123
  ${hasTrivia === "false" ? "" : hasTrivia === "true" ? `sp=${skip}(input,sp,ctx)
8891
9124
  ` : `if(hasTrivia)sp=${skip}(input,sp,ctx)
9125
+ `}${sepCls === void 0 ? "" : `if(count>=${min}&&${p}gate&&${sepMiss("sp")}){
9126
+ ${rb}
9127
+ break
9128
+ }
8892
9129
  `}ctx._fc=false
8893
9130
  ${sepIp >= 0 && leadingTriviaFieldAt(code, k, sepIp) ? "LEAD.at=cur\n" : ""}const sv=${sep}(input,sp,ctx)
8894
9131
  if(sv===FAIL){
@@ -8904,13 +9141,13 @@ itemStart=${hasTrivia === "false" ? "EC.e" : hasTrivia === "true" ? `${skip}(inp
8904
9141
  ${rb}
8905
9142
  ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
8906
9143
  }
8907
- ${itemCls === void 0 ? "" : `if(count>=${min}&&${p}gate&&${itemMiss("itemStart")}){
9144
+ ${itemCls === void 0 || sep === void 0 && hasTrivia === "false" ? "" : `if(count>=${min}&&${p}gate&&${sep === void 0 && hasTrivia === "hasTrivia" ? "hasTrivia&&" : ""}${itemMiss("itemStart")}){
8908
9145
  ${rb}
8909
9146
  ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
8910
9147
  }
8911
- `}ctx._fc=false
9148
+ `}${gapCode(rb)}ctx._fc=false
8912
9149
  const v=${child}(input,itemStart,ctx)
8913
- if(v===FAIL){
9150
+ ${gapIp >= 0 ? "if(gapped)GAP.at=-1\n" : ""}if(v===FAIL){
8914
9151
  ${REC ? `if(ctx._fc===true){
8915
9152
  ${rb}
8916
9153
  return FAIL
@@ -9233,6 +9470,7 @@ function passChild(code, ip) {
9233
9470
  case OP_LABEL:
9234
9471
  case OP_COV:
9235
9472
  case OP_TOKEN:
9473
+ case OP_GAPGATE:
9236
9474
  return code[ip + 1];
9237
9475
  case OP_GATE:
9238
9476
  case OP_NODE:
@@ -9505,6 +9743,13 @@ function cfgKeyOf(hostCst, trackLines, tolerant, coverage, probe, hostReadsChild
9505
9743
  function cfgKey(c) {
9506
9744
  return cfgKeyOf(c.hostCst, c.trackLines, c.tolerant, c.coverage, c.probe, c.hostReadsChildren);
9507
9745
  }
9746
+ function assertFactoryArity(pre) {
9747
+ if (pre.arity === EMITTED_PARAMS.length) return;
9748
+ 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`;
9749
+ throw new Error(
9750
+ `parseman: this parser's printed assembly ${printed}. Reprint the parser with parseman ${PARSEMAN_VERSION}, or run it on the parseman version that printed it.`
9751
+ );
9752
+ }
9508
9753
  function assemble(t, prog, cfg) {
9509
9754
  const { code, k, fns, cc, fx, disp, dsp, trivia: trivia2, triviaLabelled } = t;
9510
9755
  const triviaScan = t.triviaScan;
@@ -9539,6 +9784,8 @@ function assemble(t, prog, cfg) {
9539
9784
  return recognize;
9540
9785
  }
9541
9786
  const labelExtraIps = assemblyExtraIps(prog);
9787
+ const GAP_ROWS = gapRows(code, [...Object.values(prog.rules), ...labelExtraIps]);
9788
+ const GAP = newGapSlot();
9542
9789
  const closureLabels = computeSiteLabels(
9543
9790
  code,
9544
9791
  [...Object.values(prog.rules), ...labelExtraIps],
@@ -9757,6 +10004,27 @@ function assemble(t, prog, cfg) {
9757
10004
  return node;
9758
10005
  };
9759
10006
  }
10007
+ function gapRegexPiece(row, re, xf, track) {
10008
+ return (input, pos, ctx) => {
10009
+ let e;
10010
+ if (GAP.row === row && GAP.at === pos && GAP.input === input) e = GAP.end;
10011
+ else {
10012
+ re.lastIndex = pos;
10013
+ e = re.test(input) ? re.lastIndex : -1;
10014
+ }
10015
+ if (e >= 0) {
10016
+ const v = input.slice(pos, e);
10017
+ if (cstCaptureActive(ctx)) pushLeaf(ctx, v, pos, e);
10018
+ if (track) trackLinesInto(ctx, input, e);
10019
+ EC.e = e;
10020
+ return v;
10021
+ }
10022
+ ctx._fe = pos;
10023
+ ctx._fx = xf;
10024
+ if (ctx._probe !== void 0) failAt(ctx, xf, pos);
10025
+ return FAIL;
10026
+ };
10027
+ }
9760
10028
  function passCollapseNode(child, build, readsRaw) {
9761
10029
  return (input, pos, ctx) => {
9762
10030
  const v = child(input, pos, ctx);
@@ -10159,7 +10427,7 @@ function assemble(t, prog, cfg) {
10159
10427
  return next?.(key);
10160
10428
  };
10161
10429
  }
10162
- function dispatchBranch(child, usesRouted) {
10430
+ function dispatchBranch(child, usesRouted, commits = true) {
10163
10431
  if (!usesRouted) {
10164
10432
  return (input, _pos, selEnd, key, ctx) => {
10165
10433
  const need = markCst(ctx);
@@ -10173,7 +10441,7 @@ function assemble(t, prog, cfg) {
10173
10441
  const v = child(input, selEnd, ctx);
10174
10442
  if (v === FAIL) {
10175
10443
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
10176
- ctx._fc = true;
10444
+ if (commits) ctx._fc = true;
10177
10445
  return FAIL;
10178
10446
  }
10179
10447
  return [key, v];
@@ -10191,7 +10459,7 @@ function assemble(t, prog, cfg) {
10191
10459
  }
10192
10460
  if (v === FAIL) {
10193
10461
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
10194
- ctx._fc = true;
10462
+ if (commits) ctx._fc = true;
10195
10463
  return FAIL;
10196
10464
  }
10197
10465
  return [key, v];
@@ -10432,6 +10700,7 @@ function assemble(t, prog, cfg) {
10432
10700
  case OP_RX: {
10433
10701
  const re = k[code[ip + 1]];
10434
10702
  const xf = fx[code[ip + 2]];
10703
+ if (GAP_ROWS.has(ip)) return gapRegexPiece(ip, re, xf, false);
10435
10704
  const shared = rawScalarSpecs.has(code[ip + 1]) ? scalarFor(ip) : void 0;
10436
10705
  if (shared !== void 0) {
10437
10706
  return (input, pos, ctx) => {
@@ -10466,6 +10735,7 @@ function assemble(t, prog, cfg) {
10466
10735
  case OP_RX_TRACK: {
10467
10736
  const re = k[code[ip + 1]];
10468
10737
  const xf = fx[code[ip + 2]];
10738
+ if (GAP_ROWS.has(ip)) return gapRegexPiece(ip, re, xf, true);
10469
10739
  return (input, pos, ctx) => {
10470
10740
  re.lastIndex = pos;
10471
10741
  if (re.test(input)) {
@@ -10596,6 +10866,12 @@ function assemble(t, prog, cfg) {
10596
10866
  return v;
10597
10867
  };
10598
10868
  }
10869
+ // Transparent: only the repeat holding it reads its operands.
10870
+ case OP_GAPGATE:
10871
+ return link(code[ip + 1]);
10872
+ // One encoding per assembly: strict, or the construct as written.
10873
+ case OP_STRICT:
10874
+ return link(code[cfg.tolerant || cfg.probe ? ip + 2 : ip + 1]);
10599
10875
  case OP_RULE:
10600
10876
  return link(code[ip + 1]);
10601
10877
  case OP_LIVE: {
@@ -11521,7 +11797,7 @@ function assemble(t, prog, cfg) {
11521
11797
  if (table.exclusive) {
11522
11798
  const ascii = table.ascii;
11523
11799
  const hi = table.hi;
11524
- if (n === 2) {
11800
+ if (n === 2 && !cfg.probe) {
11525
11801
  const a0 = link(code[base]), a1 = link(code[base + 1]);
11526
11802
  return (input, pos, ctx) => {
11527
11803
  const c = lead(input, pos);
@@ -11553,7 +11829,7 @@ function assemble(t, prog, cfg) {
11553
11829
  return FAIL;
11554
11830
  };
11555
11831
  }
11556
- if (n === 3) {
11832
+ if (n === 3 && !cfg.probe) {
11557
11833
  const a0 = link(code[base]), a1 = link(code[base + 1]), a2 = link(code[base + 2]);
11558
11834
  return (input, pos, ctx) => {
11559
11835
  const c = lead(input, pos);
@@ -11598,6 +11874,7 @@ function assemble(t, prog, cfg) {
11598
11874
  );
11599
11875
  };
11600
11876
  const selected = bindExclusive(0);
11877
+ const probeArms = cfg.probe ? Array.from({ length: n }, (_, i) => link(code[base + i])) : void 0;
11601
11878
  return (input, pos, ctx) => {
11602
11879
  const c = lead(input, pos);
11603
11880
  let arm = -1;
@@ -11623,12 +11900,13 @@ function assemble(t, prog, cfg) {
11623
11900
  return FAIL;
11624
11901
  }
11625
11902
  }
11903
+ if (arm < 0 && probeArms !== void 0) for (const a of probeArms) a(input, pos, ctx);
11626
11904
  ctx._fe = pos;
11627
11905
  ctx._fx = choiceFx;
11628
11906
  return FAIL;
11629
11907
  };
11630
11908
  }
11631
- const armCls = table.armCls;
11909
+ const armCls = cfg.probe ? table.armCls.map(() => null) : table.armCls;
11632
11910
  if (n === 2 || n === 3) {
11633
11911
  const a0 = link(code[base]), a1 = link(code[base + 1]);
11634
11912
  const a2 = n === 3 ? link(code[base + 2]) : void 0;
@@ -12048,6 +12326,43 @@ function assemble(t, prog, cfg) {
12048
12326
  return child(input, pos, ctx);
12049
12327
  };
12050
12328
  }
12329
+ case OP_RGATE2: {
12330
+ const cls = cc[code[ip + 1]];
12331
+ const xf = fx[code[ip + 3]];
12332
+ const first = cc[code[ip + 4]];
12333
+ const second = cc[code[ip + 5]];
12334
+ const child = link(code[ip + 2]);
12335
+ if (cfg.tolerant || cfg.probe) return child;
12336
+ if (cls.hi.length === 0 && first.hi.length === 0 && second.hi.length === 0) {
12337
+ const a0 = cls.ascii, a1 = first.ascii, a2 = second.ascii;
12338
+ return (input, pos, ctx) => {
12339
+ const item = ctx._routed;
12340
+ if (item !== void 0 && item.span.start === pos) {
12341
+ const e = item.span.end;
12342
+ const c = input.charCodeAt(e);
12343
+ if (a0[c] !== 1 || a1[c] === 1 && a2[input.charCodeAt(e + 1)] !== 1) {
12344
+ ctx._fe = e;
12345
+ ctx._fx = xf;
12346
+ return FAIL;
12347
+ }
12348
+ }
12349
+ return child(input, pos, ctx);
12350
+ };
12351
+ }
12352
+ return (input, pos, ctx) => {
12353
+ const item = ctx._routed;
12354
+ if (item !== void 0 && item.span.start === pos) {
12355
+ const e = item.span.end;
12356
+ const c = lead(input, e);
12357
+ if (!classHas2(cls, c) || classHas2(first, c) && !classHas2(second, lead(input, e + 1))) {
12358
+ ctx._fe = e;
12359
+ ctx._fx = xf;
12360
+ return FAIL;
12361
+ }
12362
+ }
12363
+ return child(input, pos, ctx);
12364
+ };
12365
+ }
12051
12366
  case OP_ARMGATE: {
12052
12367
  const pred = fns[code[ip + 1]];
12053
12368
  const child = link(code[ip + 2]);
@@ -12115,10 +12430,18 @@ function assemble(t, prog, cfg) {
12115
12430
  const itemClassIndex = sepIp < 0 ? code[ip + 7] : -1;
12116
12431
  const itemClass = !REC && itemClassIndex >= 0 ? cc[itemClassIndex] : void 0;
12117
12432
  const itemMayStart = itemClass === void 0 ? void 0 : (input, pos) => classHas2(itemClass, lead(input, pos));
12433
+ const sepClassIndex = sepIp >= 0 && !sepLeads && (flags & 8) !== 0 ? code[ip + 8] : -1;
12434
+ const sepClass = !REC && sepClassIndex >= 0 && !rowNullable(code, k, sepIp, NULLABLE) ? cc[sepClassIndex] : void 0;
12435
+ const gapIp = sepIp < 0 && min === 0 && !REC && code[code[ip + 1]] === OP_GAPGATE ? code[ip + 1] : -1;
12436
+ const gapRow = gapIp >= 0 ? code[gapIp + 2] : -1;
12437
+ const gapRe = gapIp >= 0 ? k[code[gapRow + 1]] : void 0;
12438
+ const gapFollow = gapIp >= 0 ? cc[code[gapIp + 3]] : void 0;
12439
+ const gapOther = gapIp >= 0 && code[gapIp + 4] >= 0 ? cc[code[gapIp + 4]] : void 0;
12118
12440
  const skipBeforeFirst = sepIp < 0 && min === 0;
12119
12441
  const itemClean = sepIp < 0 && CLEAN(code[ip + 1]) && !rowNullable(code, k, code[ip + 1], NULLABLE);
12120
12442
  if (REC) {
12121
12443
  const itemFx2 = fx[code[ip + 6]];
12444
+ const recFx = (flags & 16) !== 0 ? EMPTY_FX2 : itemFx2;
12122
12445
  const sepSent = sentinelFor(code[ip + 7]);
12123
12446
  return (input, pos, ctx) => {
12124
12447
  const out = collect ? [] : void 0;
@@ -12178,7 +12501,7 @@ function assemble(t, prog, cfg) {
12178
12501
  itemStart,
12179
12502
  ctx,
12180
12503
  sep === void 0 ? mySync : orSentinel(sepSent ?? mySync, sepSent === void 0 ? void 0 : mySync),
12181
- itemFx2
12504
+ recFx
12182
12505
  );
12183
12506
  if (out !== void 0) out.push(rr.error);
12184
12507
  captureError(ctx, rr.error);
@@ -12216,6 +12539,7 @@ function assemble(t, prog, cfg) {
12216
12539
  const markLater = needMark && (!itemClean || hasTrivia);
12217
12540
  const markFirst = needMark && (!itemClean || hasTrivia && skipBeforeFirst);
12218
12541
  const gateItems = itemMayStart !== void 0 && ctx._probe === void 0;
12542
+ const gateSep = sepClass !== void 0 && ctx._probe === void 0;
12219
12543
  let cur = pos;
12220
12544
  let count = 0;
12221
12545
  for (; ; ) {
@@ -12238,6 +12562,10 @@ function assemble(t, prog, cfg) {
12238
12562
  const leavesBefore = cstLeavesLen(ctx);
12239
12563
  let sp = cur;
12240
12564
  if (hasTrivia) sp = skipTrivia(input, sp, ctx);
12565
+ if (count >= min && gateSep && !classHas2(sepClass, lead(input, sp))) {
12566
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
12567
+ break;
12568
+ }
12241
12569
  ctx._fc = false;
12242
12570
  if (sepLeads) LEAD.at = cur;
12243
12571
  const sv = sep(input, sp, ctx);
@@ -12257,13 +12585,30 @@ function assemble(t, prog, cfg) {
12257
12585
  if (trailingAllowed && sepEnd >= 0) cur = sepEnd;
12258
12586
  break;
12259
12587
  }
12260
- if (count >= min && gateItems && !itemMayStart(input, itemStart)) {
12588
+ if (count >= min && gateItems && (sep !== void 0 || hasTrivia) && !itemMayStart(input, itemStart)) {
12261
12589
  if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
12262
12590
  if (trailingAllowed && sepEnd >= 0) cur = sepEnd;
12263
12591
  break;
12264
12592
  }
12593
+ let gapped = false;
12594
+ if (gapRe !== void 0 && ctx._probe === void 0) {
12595
+ gapRe.lastIndex = itemStart;
12596
+ const q2 = gapRe.test(input) ? gapRe.lastIndex : -1;
12597
+ if ((q2 < 0 || !classHas2(gapFollow, lead(input, q2))) && (gapOther === void 0 || !classHas2(gapOther, lead(input, itemStart)))) {
12598
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
12599
+ break;
12600
+ }
12601
+ if (q2 >= 0) {
12602
+ GAP.row = gapRow;
12603
+ GAP.at = itemStart;
12604
+ GAP.end = q2;
12605
+ GAP.input = input;
12606
+ gapped = true;
12607
+ }
12608
+ }
12265
12609
  ctx._fc = false;
12266
12610
  const v = child(input, itemStart, ctx);
12611
+ if (gapped) GAP.at = -1;
12267
12612
  if (v === FAIL) {
12268
12613
  if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
12269
12614
  if (committed3(ctx)) return FAIL;
@@ -12295,7 +12640,8 @@ function assemble(t, prog, cfg) {
12295
12640
  const selector = link(code[ip + 1]);
12296
12641
  const otherIp = code[ip + 3];
12297
12642
  const other = otherIp >= 0 ? link(otherIp) : void 0;
12298
- const otherRouted = code[ip + 4] === 1;
12643
+ const otherRouted = (code[ip + 4] & 1) === 1;
12644
+ const otherCommits = (code[ip + 4] & 2) === 0;
12299
12645
  const n = code[ip + 5];
12300
12646
  const armBase = ip + 6;
12301
12647
  const byKey = spec.byKey;
@@ -12343,7 +12689,7 @@ function assemble(t, prog, cfg) {
12343
12689
  );
12344
12690
  };
12345
12691
  const runArm = n === 0 ? void 0 : bindArms(0);
12346
- const runOther = other === void 0 ? void 0 : dispatchBranch(other, otherRouted);
12692
+ const runOther = other === void 0 ? void 0 : dispatchBranch(other, otherRouted, otherCommits);
12347
12693
  return (input, pos, ctx) => {
12348
12694
  const need = markCst(ctx);
12349
12695
  const mRaw = MRAW;
@@ -12604,6 +12950,7 @@ function assemble(t, prog, cfg) {
12604
12950
  }
12605
12951
  const pre = cfg.hostCaptureTrivia === void 0 ? prog.asm?.find((a) => a.key === cfgKey(cfg)) : void 0;
12606
12952
  if (pre !== void 0) {
12953
+ assertFactoryArity(pre);
12607
12954
  const pools = rebuildPools(t.cc, t.fx, t.disp, pre.plan);
12608
12955
  emitted = pre.factory(
12609
12956
  EC,
@@ -12615,7 +12962,6 @@ function assemble(t, prog, cfg) {
12615
12962
  pools.classes,
12616
12963
  pools.armExpected,
12617
12964
  trivia2,
12618
- trivia2.map((tv) => tv?._meta.triviaKindLabels),
12619
12965
  triviaScan,
12620
12966
  scansArr,
12621
12967
  disp,
@@ -12639,7 +12985,6 @@ function assemble(t, prog, cfg) {
12639
12985
  cstLeavesLen,
12640
12986
  skipTriviaScanned,
12641
12987
  needsDeferredTriviaCommit,
12642
- scanTrivia,
12643
12988
  advanceTrivia,
12644
12989
  refuseUnclassifiedRootScope,
12645
12990
  spanLines,
@@ -12653,8 +12998,6 @@ function assemble(t, prog, cfg) {
12653
12998
  orSentinel,
12654
12999
  captureError,
12655
13000
  scalarRecognizers,
12656
- commitTriviaScan,
12657
- scanTriviaCompact,
12658
13001
  t.lex,
12659
13002
  adjacencyHolds,
12660
13003
  t.lexPrograms,
@@ -12666,7 +13009,8 @@ function assemble(t, prog, cfg) {
12666
13009
  PlainLeaf8,
12667
13010
  PlainNode2,
12668
13011
  PlainField,
12669
- PlainBuf2
13012
+ PlainBuf2,
13013
+ GAP
12670
13014
  );
12671
13015
  emitReached = new Set(pre.reached);
12672
13016
  if (emitted.ext !== void 0) HYBRID = emitted;
@@ -12702,7 +13046,6 @@ function assemble(t, prog, cfg) {
12702
13046
  em.classes,
12703
13047
  em.armExpected,
12704
13048
  trivia2,
12705
- trivia2.map((tv) => tv?._meta.triviaKindLabels),
12706
13049
  triviaScan,
12707
13050
  scansArr,
12708
13051
  disp,
@@ -12726,7 +13069,6 @@ function assemble(t, prog, cfg) {
12726
13069
  cstLeavesLen,
12727
13070
  skipTriviaScanned,
12728
13071
  needsDeferredTriviaCommit,
12729
- scanTrivia,
12730
13072
  advanceTrivia,
12731
13073
  refuseUnclassifiedRootScope,
12732
13074
  spanLines,
@@ -12745,8 +13087,6 @@ function assemble(t, prog, cfg) {
12745
13087
  orSentinel,
12746
13088
  captureError,
12747
13089
  scalarRecognizers,
12748
- commitTriviaScan,
12749
- scanTriviaCompact,
12750
13090
  t.lex,
12751
13091
  adjacencyHolds,
12752
13092
  t.lexPrograms,
@@ -12758,7 +13098,8 @@ function assemble(t, prog, cfg) {
12758
13098
  PlainLeaf8,
12759
13099
  PlainNode2,
12760
13100
  PlainField,
12761
- PlainBuf2
13101
+ PlainBuf2,
13102
+ GAP
12762
13103
  );
12763
13104
  emitReached = em.reached;
12764
13105
  } catch (e) {
@@ -13157,7 +13498,7 @@ function attempt(parser2, options) {
13157
13498
  return {
13158
13499
  _tag: "attempt",
13159
13500
  _meta: parser2._meta,
13160
- _def: contain ? { tag: "attempt", parser: parser2, contain: true } : { tag: "attempt", parser: parser2 },
13501
+ _def: { tag: "attempt", parser: parser2, ...contain ? { contain: true } : {}, ...guardable ? { guarded: true } : {} },
13161
13502
  parse(input, pos, ctx) {
13162
13503
  if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(parser2, input, pos)) {
13163
13504
  if (failExpected === void 0) {
@@ -13650,6 +13991,7 @@ function reachesMarked(root, successors, memo) {
13650
13991
  }
13651
13992
 
13652
13993
  // src/table/factor-choice.ts
13994
+ var factoredChoices = /* @__PURE__ */ new WeakSet();
13653
13995
  var DERIVED = /* @__PURE__ */ new Set(["_tag", "_meta", "_def", "_parseScalar", "_stickyRegex", "parse"]);
13654
13996
  function isCombinator(v) {
13655
13997
  return typeof v === "object" && v !== null && "_def" in v && "_meta" in v;
@@ -13741,6 +14083,7 @@ function factorSharedEnds(d) {
13741
14083
  for (let i = at + 1; i < n; i++) if (!never(first.terms[i])) return void 0;
13742
14084
  if (arms.some((a) => mayCommitFailure(a))) return void 0;
13743
14085
  const middle = choice(...shapes.map((s) => s.terms[at]));
14086
+ factoredChoices.add(middle);
13744
14087
  if (middle._def.tag === "choice" && middle._def.autoNot.some((a) => a !== null)) return void 0;
13745
14088
  const st = middle._def.tag === "choice" ? middle._def.strategy : void 0;
13746
14089
  if (st === void 0 || st.tag !== "firstMatch" && st.tag !== "sharedPrefix" && !(st.tag === "literalsLongestFirst" && st.sortedIndices.every((x, i) => x === i))) return void 0;
@@ -13753,6 +14096,37 @@ function factorSharedEnds(d) {
13753
14096
  for (let j = first.wrappers.length - 1; j >= 0 && out !== void 0; j--) out = rewrap(first.wrappers[j], out);
13754
14097
  return out === void 0 ? void 0 : attempt(out);
13755
14098
  }
14099
+ function factorPrefixArms(d) {
14100
+ const arms = d.parsers;
14101
+ if (arms.length < 2) return void 0;
14102
+ if (d.strategy.tag !== "firstMatch" && d.strategy.tag !== "sharedPrefix") return void 0;
14103
+ if (d.gates.some((g) => g !== null) || d.autoNot.some((a) => a !== null)) return void 0;
14104
+ const out = [...arms];
14105
+ let changed = false;
14106
+ for (let i = 0; i + 1 < arms.length; i++) {
14107
+ const arm = arms[i];
14108
+ if (arm._def.tag !== "sequence" || arm._def.valueUnused !== true) continue;
14109
+ const terms = arm._def.parsers;
14110
+ const next = arms[i + 1];
14111
+ const prefix = next._def.tag === "sequence" && next._def.valueUnused === true ? next._def.parsers : [next];
14112
+ if (prefix.length >= terms.length) continue;
14113
+ if (!prefix.every((t, j) => sameComb(t, terms[j], 0))) continue;
14114
+ const rest = terms.slice(prefix.length);
14115
+ if (rest.every(never)) continue;
14116
+ const tail = rest.length === 1 ? rest[0] : sequence(...rest);
14117
+ if (tail !== rest[0]) tail._def.valueUnused = true;
14118
+ const parts = [...terms.slice(0, prefix.length), optional(tail)];
14119
+ const seq = sequence(...parts);
14120
+ seq._def.valueUnused = true;
14121
+ out[i] = seq;
14122
+ changed = true;
14123
+ }
14124
+ if (!changed) return void 0;
14125
+ const rebuilt = choice(...out);
14126
+ factoredChoices.add(rebuilt);
14127
+ const st = rebuilt._def.tag === "choice" ? rebuilt._def.strategy : void 0;
14128
+ return st !== void 0 && (st.tag === "firstMatch" || st.tag === "sharedPrefix") ? rebuilt : void 0;
14129
+ }
13756
14130
 
13757
14131
  // src/compiler/direct-projection.ts
13758
14132
  function directArrayProjection(fn, fnSrc, arity) {
@@ -13854,9 +14228,9 @@ function directOptionalSuffixTokenBody(parser2, resolve) {
13854
14228
  baseParser = resolvedLazyTarget(baseParser, resolve);
13855
14229
  }
13856
14230
  const base = baseParser?._def;
13857
- const optional = sequence6.parsers[1]?._def;
13858
- if (base?.tag !== "regex" || optional?.tag !== "optional") return void 0;
13859
- const suffix = optional.parser._def;
14231
+ const optional2 = sequence6.parsers[1]?._def;
14232
+ if (base?.tag !== "regex" || optional2?.tag !== "optional") return void 0;
14233
+ const suffix = optional2.parser._def;
13860
14234
  if (suffix.tag !== "literal" || suffix.caseInsensitive || suffix.value.length !== 1) return void 0;
13861
14235
  const selected = selectOptionalSuffixLexBody({
13862
14236
  kind: "sequence",
@@ -13875,7 +14249,7 @@ function directOptionalSuffixTokenBody(parser2, resolve) {
13875
14249
  return selected.strategy === "token" ? {
13876
14250
  ...selected.body,
13877
14251
  baseCanMatchNewline: baseParser._meta.canMatchNewline,
13878
- suffixCanMatchNewline: optional.parser._meta.canMatchNewline,
14252
+ suffixCanMatchNewline: optional2.parser._meta.canMatchNewline,
13879
14253
  expected: directTerminalFailureExpected(base)
13880
14254
  } : void 0;
13881
14255
  }
@@ -14053,6 +14427,7 @@ function directFixedLexNode(parser2, resolve, active, pureOnly = false, cleared
14053
14427
  arms,
14054
14428
  parsers: def.parsers,
14055
14429
  cleared,
14430
+ disjoint: def.disjoint,
14056
14431
  armExpected: def.parsers.map(deriveExpected),
14057
14432
  expected: def.parsers.flatMap(deriveExpected)
14058
14433
  };
@@ -14162,6 +14537,29 @@ var UnsupportedConstruct = class extends Error {
14162
14537
  this.tag = tag;
14163
14538
  }
14164
14539
  };
14540
+ function firstTwo(p) {
14541
+ const d = p._def;
14542
+ if (d.tag === "label" || d.tag === "grammar") return firstTwo(d.parser);
14543
+ let two;
14544
+ if (d.tag === "literal" && d.value.length >= 2 && d.caseInsensitive !== true) {
14545
+ const second = d.value.codePointAt(1);
14546
+ two = { first: { kind: "ranges", ranges: [{ lo: d.value.charCodeAt(0), hi: d.value.charCodeAt(0) }] }, second: { kind: "ranges", ranges: [{ lo: second, hi: second }] } };
14547
+ } else if (d.tag === "regex" && !d.flags.includes("i")) {
14548
+ const units = regexFirstTwo(d.source, d.flags);
14549
+ if (units !== void 0) two = units;
14550
+ }
14551
+ if (two === void 0 || two.first.kind !== "ranges") return void 0;
14552
+ return two.first.ranges.every((r) => r.hi < 55296 || r.lo > 57343 && r.hi <= 65535) ? two : void 0;
14553
+ }
14554
+ var GAP_CHARS = /* @__PURE__ */ new Set([9, 10, 12, 13, 32, 47]);
14555
+ function isGap(p) {
14556
+ if (matchesEmpty(p)) return true;
14557
+ const fs = p._meta.firstSet;
14558
+ return fs.kind === "ranges" && fs.ranges.every((r) => {
14559
+ for (let c = r.lo; c <= r.hi; c++) if (!GAP_CHARS.has(c)) return false;
14560
+ return true;
14561
+ });
14562
+ }
14165
14563
  var Encoder = class {
14166
14564
  code = [];
14167
14565
  k = [];
@@ -14209,8 +14607,33 @@ var Encoder = class {
14209
14607
  */
14210
14608
  commitFailureMemo = /* @__PURE__ */ new Map();
14211
14609
  labels = void 0;
14610
+ /** The choice being encoded as written, for `OP_STRICT`'s tolerant child. */
14611
+ unfactoredChoice = void 0;
14212
14612
  /** Trivia table in scope at the row being encoded — codegen's `ctx.activeTrivia`. */
14213
14613
  activeTrivia = void 0;
14614
+ /**
14615
+ * Is the trivia live at this row known? It is not in the body of an entry
14616
+ * that installs none of its own: that body runs under whatever trivia its
14617
+ * caller has (a compiled rule inside another grammar, `run({ trivia })`).
14618
+ * Every scope (`parser({ trivia })`, `noTrivia`, `token()`, a rule's install)
14619
+ * makes it known again.
14620
+ */
14621
+ triviaKnown = true;
14622
+ /**
14623
+ * The trivia a gate's first set is derived under: the live table, `null` for a
14624
+ * scope known to have none, or `undefined` where it is not known.
14625
+ */
14626
+ gateTrivia() {
14627
+ return this.activeTrivia ?? (this.triviaKnown ? null : void 0);
14628
+ }
14629
+ /**
14630
+ * Whether a gate may read a nullable pad through its non-empty matches
14631
+ * (`firstSetOf`'s `consuming`): only where the trivia that may follow the pad
14632
+ * is known. Elsewhere a gate reads first sets as 0.58.0 did.
14633
+ */
14634
+ gateConsuming() {
14635
+ return this.activeTrivia !== void 0 || this.triviaKnown;
14636
+ }
14214
14637
  classified = false;
14215
14638
  scans = [];
14216
14639
  /** Ambient `scanSkip` sets, pooled by the ARRAY's identity (stable per grammar). */
@@ -14337,10 +14760,12 @@ var Encoder = class {
14337
14760
  const final = classifyFinalChoice(
14338
14761
  node.parsers,
14339
14762
  this.refResolver(),
14340
- node.cleared ? void 0 : this.activeTrivia
14763
+ node.cleared ? null : this.gateTrivia(),
14764
+ false,
14765
+ node.disjoint
14341
14766
  );
14342
14767
  const classes = final.firstSets.map((first, i) => final.nullable[i] ? -1 : this.charClass(first));
14343
- const exclusive = final.exclusive && classes.every((cls) => cls >= 0);
14768
+ const exclusive = final.exclusive && classes.every((cls) => cls >= 0) && this.wideArms(node.parsers, final.firstSets, node.disjoint, node.cleared ? null : this.gateTrivia(), node.cleared ? void 0 : this.activeTrivia).length === 0;
14344
14769
  words.push(LEX_NODE_CHOICE, node.arms.length, exclusive ? 1 : 0, this.expected(node.expected));
14345
14770
  for (let i = 0; i < node.arms.length; i++) {
14346
14771
  words.push(classes[i], this.expected(node.armExpected[i]));
@@ -14495,16 +14920,117 @@ var Encoder = class {
14495
14920
  kIndex = /* @__PURE__ */ new Map();
14496
14921
  ccIndex = /* @__PURE__ */ new Map();
14497
14922
  fxIndex = /* @__PURE__ */ new Map();
14498
- /** Memoized by combinator identity: a shared sub-combinator is ONE row. */
14499
- memo = /* @__PURE__ */ new Map();
14500
14923
  /**
14501
- * Memo for `lazy` REFERENCES, keyed by the trivia scope the reference is made
14502
- * from. A cross-rule reference's row depends on that scope (`scopedRef`), so the
14503
- * single `memo` slot every other construct uses would freeze the FIRST
14504
- * reference's scope onto every later one and undo the fix.
14924
+ * Memoized by combinator identity AND the trivia scope it is encoded under
14925
+ * (`scopeKey`): a shared sub-combinator is ONE row per scope it runs in.
14926
+ *
14927
+ * A construct's rows depend on that scope. A first set derived through a
14928
+ * nullable term takes in the trivia that may follow it, a gap gate holds only
14929
+ * where no trivia runs, a routed gate reads what may follow its token, and a
14930
+ * cross-rule reference installs its rule's trivia only from a cleared region
14931
+ * (`scopedRef`). One memo slot per combinator froze the FIRST scope it was
14932
+ * reached under onto every later one: a rule body encoded as an entry with no
14933
+ * trivia, then run under a caller's comment trivia, gated its repeats as if no
14934
+ * comment could come between their terms.
14505
14935
  */
14506
- refMemo = /* @__PURE__ */ new Map();
14936
+ memo = /* @__PURE__ */ new Map();
14507
14937
  pending = /* @__PURE__ */ new Map();
14938
+ triviaSignatures = /* @__PURE__ */ new Map();
14939
+ /**
14940
+ * What of the live trivia an encoding can depend on: none, or its first set.
14941
+ * Every derivation reads trivia only through `firstSetOf(trivia)` or whether it
14942
+ * is live, so two scopes whose trivia opens on the same characters encode the
14943
+ * same rows and share them. A construct that reads no scope (`readsScope`)
14944
+ * has one encoding.
14945
+ */
14946
+ scopeKey(p) {
14947
+ const t = this.activeTrivia;
14948
+ if (!this.readsScope(p)) return "";
14949
+ if (t === void 0) return this.triviaKnown ? "" : "?";
14950
+ let sig = this.triviaSignatures.get(t);
14951
+ if (sig === void 0) {
14952
+ sig = `t${JSON.stringify(firstSetOf(t, /* @__PURE__ */ new Set(), this.refResolver()))}`;
14953
+ this.triviaSignatures.set(t, sig);
14954
+ }
14955
+ return sig;
14956
+ }
14957
+ readsScopeMemo = /* @__PURE__ */ new Map();
14958
+ /**
14959
+ * Can `p`'s rows depend on the trivia scope it is encoded under? No for a
14960
+ * terminal, a scope of its own (`parser({ trivia })`, `noTrivia`, `token()`),
14961
+ * and a wrapper over a construct that reads none; yes wherever a first set, a gate or
14962
+ * a reference's install is derived with the scope: a sequence with a nullable
14963
+ * term before another (the trivia between them), a routed arm (the trivia after
14964
+ * its token), a reference that installs its rule's trivia from a cleared
14965
+ * region, and anything not modelled here — a cycle included.
14966
+ */
14967
+ readsScope(p) {
14968
+ const hit = this.readsScopeMemo.get(p);
14969
+ if (hit !== void 0) return hit;
14970
+ this.readsScopeMemo.set(p, true);
14971
+ const d = p._def;
14972
+ const rr = this.refResolver();
14973
+ let out;
14974
+ switch (d.tag) {
14975
+ case "literal":
14976
+ case "regex":
14977
+ case "keywords":
14978
+ case "token":
14979
+ out = false;
14980
+ break;
14981
+ // `routed()` encodes its fallback under the scope that reaches it.
14982
+ case "routed":
14983
+ out = d.fallback !== void 0 && this.readsScope(d.fallback);
14984
+ break;
14985
+ case "grammar":
14986
+ out = d.clearTrivia !== true && d.triviaParser === void 0 && this.readsScope(d.parser);
14987
+ break;
14988
+ case "node":
14989
+ case "label":
14990
+ case "leaf":
14991
+ case "transform":
14992
+ case "attempt":
14993
+ case "expect":
14994
+ case "not":
14995
+ case "peek":
14996
+ case "optional":
14997
+ case "many":
14998
+ case "oneOrMore":
14999
+ case "field":
15000
+ out = this.readsScope(d.parser);
15001
+ break;
15002
+ case "sepBy":
15003
+ out = this.readsScope(d.parser) || this.readsScope(d.separator);
15004
+ break;
15005
+ case "choice":
15006
+ out = d.parsers.some((a) => this.readsScope(a));
15007
+ break;
15008
+ case "dispatch":
15009
+ 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);
15010
+ break;
15011
+ case "sequence": {
15012
+ const ps = d.parsers;
15013
+ 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));
15014
+ break;
15015
+ }
15016
+ case "lazy": {
15017
+ let target = winnerForRef(p, rr);
15018
+ if (target === void 0) {
15019
+ try {
15020
+ target = d.thunk();
15021
+ } catch {
15022
+ target = void 0;
15023
+ }
15024
+ }
15025
+ out = target === void 0 || triviaThroughRef(p, target, null) !== null || this.readsScope(target);
15026
+ break;
15027
+ }
15028
+ default:
15029
+ out = true;
15030
+ }
15031
+ this.readsScopeMemo.set(p, out);
15032
+ return out;
15033
+ }
14508
15034
  settings;
14509
15035
  /** Resolved once, HERE, at table-build time — never consulted at run time. */
14510
15036
  track;
@@ -14532,7 +15058,7 @@ var Encoder = class {
14532
15058
  let fs = { kind: "empty" };
14533
15059
  const rr = this.refResolver();
14534
15060
  for (let j = i + 1; j < parsers.length; j++) {
14535
- fs = union(fs, firstSetOf(parsers[j], /* @__PURE__ */ new Set(), rr, this.activeTrivia));
15061
+ fs = union(fs, firstSetOf(parsers[j], /* @__PURE__ */ new Set(), rr, void 0, false));
14536
15062
  }
14537
15063
  return this.charClass(fs);
14538
15064
  }
@@ -14700,7 +15226,7 @@ var Encoder = class {
14700
15226
  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;
14701
15227
  const rr = this.refResolver();
14702
15228
  if (matchesEmpty(p, /* @__PURE__ */ new Set(), rr)) return child;
14703
- const cls = this.charClass(firstSetOf(p, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
15229
+ const cls = this.charClass(firstSetOf(p, /* @__PURE__ */ new Set(), rr, this.activeTrivia, false));
14704
15230
  if (cls < 0) return child;
14705
15231
  const e = deriveExpected(p);
14706
15232
  return this.emit(OP_GATE, cls, child, this.expected(e.length > 0 ? e : [p._tag]));
@@ -14726,9 +15252,27 @@ var Encoder = class {
14726
15252
  * `{`, `Call` at `}`). The resolved set is the one a choice arm's gate uses,
14727
15253
  * over the final merged map.
14728
15254
  */
14729
- gateFirstSet(p) {
15255
+ /**
15256
+ * The arms whose first set under `gateTrivia()` is wider than under the
15257
+ * active trivia alone — a reference's installed trivia read through from a
15258
+ * region with none. Empty for construction-set (disjoint) choices, whose
15259
+ * sets do not depend on trivia.
15260
+ */
15261
+ wideArms(arms, wide, constructionSets, wideTrivia = this.gateTrivia(), base = this.activeTrivia) {
15262
+ if (constructionSets || wideTrivia === base) return [];
15263
+ const narrow = classifyFinalChoice(arms, this.refResolver(), base, false, false).firstSets;
15264
+ const out = [];
15265
+ for (let i = 0; i < arms.length; i++) {
15266
+ if (this.charClass(wide[i]) !== this.charClass(narrow[i])) out.push(i);
15267
+ }
15268
+ return out;
15269
+ }
15270
+ gateFirstSet(p, guarded, narrowed) {
14730
15271
  const cached = p._meta.firstSet;
14731
- return p._def.tag === "lazy" || cached.kind === "any" ? firstSetOf(p, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : cached;
15272
+ if (p._def.tag !== "lazy" && cached.kind !== "any") return cached;
15273
+ const rr = this.refResolver();
15274
+ if (guarded) return firstSetOf(p, /* @__PURE__ */ new Set(), rr, void 0, false);
15275
+ return firstSetOf(p, /* @__PURE__ */ new Set(), rr, this.gateTrivia(), narrowed && this.gateConsuming());
14732
15276
  }
14733
15277
  /**
14734
15278
  * Wrap one choice arm in its `autoNot` checks (`OP_REJECT`).
@@ -14760,6 +15304,135 @@ var Encoder = class {
14760
15304
  for (const w of words) this.code.push(w);
14761
15305
  return ip;
14762
15306
  }
15307
+ /**
15308
+ * An optional repeat item tests the item's class before it runs it (`OP_REP`
15309
+ * ip+7), wherever the item's own first-set gate would also run. So a sequence
15310
+ * item whose first term is a gate on that same class gets its own copy of the
15311
+ * sequence row with the gate's child as that term: the test runs once. The
15312
+ * shared row is left as it is for its other references.
15313
+ */
15314
+ withoutLeadGate(seq, cls) {
15315
+ const op = this.code[seq];
15316
+ if (op !== OP_SEQ && op !== OP_SEQV) return seq;
15317
+ const first = this.code[seq + 2];
15318
+ if (this.code[first] !== OP_GATE || this.code[first + 1] !== cls) return seq;
15319
+ const n = this.code[seq + 1];
15320
+ const width = 1 + 2 * n;
15321
+ const head = this.emitHead(op, width);
15322
+ for (let i = 1; i <= width; i++) this.code[head + i] = this.code[seq + i];
15323
+ this.code[head + 2] = this.code[first + 2];
15324
+ return head;
15325
+ }
15326
+ /**
15327
+ * An optional repeat item that opens with a GAP — a regex that may match
15328
+ * empty, or one that starts only with whitespace or a comment's `/` — and
15329
+ * then a term with a finite first set gets an `OP_GAPGATE` row
15330
+ * (`gap-gate.ts`): the repeat reads the gap once, ends where the character
15331
+ * after it opens neither that term nor an arm without the gap, and hands the
15332
+ * gap's end to the regex row. Anything the walk cannot prove keeps the item as
15333
+ * written.
15334
+ */
15335
+ gapGated(item, ip) {
15336
+ if (this.activeTrivia === void 0 && !this.triviaKnown) return ip;
15337
+ const lead2 = this.gapLead(item, this.activeTrivia, 0);
15338
+ if (lead2 === void 0) return ip;
15339
+ const cls = this.charClass(lead2.follow);
15340
+ if (cls < 0) return ip;
15341
+ const other = lead2.other.kind === "empty" ? -1 : this.charClass(lead2.other);
15342
+ if (lead2.other.kind !== "empty" && other < 0) return ip;
15343
+ const p = this.node(lead2.p).ip;
15344
+ if (this.code[p] !== OP_RX && this.code[p] !== OP_RX_TRACK) return ip;
15345
+ return this.emit(OP_GAPGATE, ip, p, cls, other);
15346
+ }
15347
+ /**
15348
+ * The gap an item opens with: the regex `p`, the first set `follow` of what
15349
+ * must come straight after it, and the first set `other` of choice arms that
15350
+ * do not open with `p`. Only through wrappers that consume nothing before
15351
+ * their body, a sequence's first term, and a choice's arms; the gap must sit
15352
+ * in a sequence with no trivia between its terms.
15353
+ */
15354
+ gapLead(c, trivia2, depth) {
15355
+ if (depth > 24) return void 0;
15356
+ const rr = this.refResolver();
15357
+ const d = c._def;
15358
+ switch (d.tag) {
15359
+ case "lazy": {
15360
+ let target = winnerForRef(c, rr);
15361
+ if (target === void 0) {
15362
+ try {
15363
+ target = d.thunk();
15364
+ } catch {
15365
+ return void 0;
15366
+ }
15367
+ }
15368
+ return this.gapLead(target, triviaThroughRef(c, target, trivia2), depth + 1);
15369
+ }
15370
+ case "node":
15371
+ case "leaf":
15372
+ case "label":
15373
+ case "attempt":
15374
+ return this.gapLead(d.parser, trivia2, depth + 1);
15375
+ case "field":
15376
+ return d.leadingTrivia === true ? void 0 : this.gapLead(d.parser, trivia2, depth + 1);
15377
+ case "grammar":
15378
+ return this.gapLead(d.parser, d.clearTrivia === true ? null : d.triviaParser ?? trivia2, depth + 1);
15379
+ case "sequence": {
15380
+ const [first, ...rest] = d.parsers;
15381
+ if (first === void 0) return void 0;
15382
+ if (first._def.tag !== "regex" || !isGap(first)) {
15383
+ return this.gapLead(first, trivia2, depth + 1);
15384
+ }
15385
+ if (trivia2 !== void 0 && trivia2 !== null || rest.length === 0) return void 0;
15386
+ let follow = empty();
15387
+ let closed = false;
15388
+ for (const t of rest) {
15389
+ const fs = firstSetOf(t, /* @__PURE__ */ new Set(), rr, null);
15390
+ if (fs.kind === "any") return void 0;
15391
+ follow = union(follow, fs);
15392
+ if (!matchesEmpty(t, /* @__PURE__ */ new Set(), rr)) {
15393
+ closed = true;
15394
+ break;
15395
+ }
15396
+ }
15397
+ return closed ? { p: first, follow, other: empty() } : void 0;
15398
+ }
15399
+ case "choice": {
15400
+ let p;
15401
+ let follow = empty();
15402
+ let other = empty();
15403
+ for (const arm of d.parsers) {
15404
+ const lead2 = this.gapLead(arm, trivia2, depth + 1);
15405
+ if (lead2 !== void 0 && (p === void 0 || lead2.p === p)) {
15406
+ p = lead2.p;
15407
+ follow = union(follow, lead2.follow);
15408
+ other = union(other, lead2.other);
15409
+ continue;
15410
+ }
15411
+ if (matchesEmpty(arm, /* @__PURE__ */ new Set(), rr)) return void 0;
15412
+ const fs = firstSetOf(arm, /* @__PURE__ */ new Set(), rr, trivia2 ?? null);
15413
+ if (fs.kind === "any") return void 0;
15414
+ other = union(other, fs);
15415
+ }
15416
+ return p === void 0 ? void 0 : { p, follow, other };
15417
+ }
15418
+ default:
15419
+ return void 0;
15420
+ }
15421
+ }
15422
+ /**
15423
+ * The class a strict list may end on — a character its separator `p` (row
15424
+ * `ip`) cannot start — without entering the separator, or −1.
15425
+ *
15426
+ * The test runs before every separator, to save the one that fails at the
15427
+ * list's end, so it is laid down only where entering the separator costs more
15428
+ * than the test: not on a terminal row, which is itself one character test.
15429
+ */
15430
+ sepGate(p, ip) {
15431
+ const op = this.code[ip];
15432
+ 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;
15433
+ if (matchesEmpty(p, /* @__PURE__ */ new Set(), this.refResolver())) return -1;
15434
+ return this.charClass(firstSetOf(p, /* @__PURE__ */ new Set(), this.refResolver(), this.gateTrivia(), this.gateConsuming()));
15435
+ }
14763
15436
  /** Reserve `n` operand slots to patch once children are laid out. */
14764
15437
  emitHead(op, n) {
14765
15438
  const ip = this.code.length;
@@ -14803,8 +15476,10 @@ var Encoder = class {
14803
15476
  }
14804
15477
  encodeRule(name, p) {
14805
15478
  this.activeTrivia = p._meta.grammarTrivia;
15479
+ this.triviaKnown = p._meta.grammarTrivia !== void 0;
14806
15480
  const body = this.node(p).ip;
14807
15481
  this.activeTrivia = void 0;
15482
+ this.triviaKnown = true;
14808
15483
  const gss = p._meta.grammarScanSkip;
14809
15484
  this.scanSkipOf.push(gss === void 0 ? -1 : this.scanSkipSlot(gss));
14810
15485
  const amb = p._meta.grammarTrivia;
@@ -14850,11 +15525,15 @@ var Encoder = class {
14850
15525
  * silently produced the ONE keyword `bc`.
14851
15526
  */
14852
15527
  scopedRef(p, target) {
15528
+ const runsUnder = triviaThroughRef(p, target, this.activeTrivia);
15529
+ if (this.activeTrivia !== void 0 || runsUnder === void 0 || runsUnder === null) return this.node(target).ip;
15530
+ const savedKnown = this.triviaKnown;
15531
+ this.activeTrivia = runsUnder;
15532
+ this.triviaKnown = true;
14853
15533
  const ip = this.node(target).ip;
14854
- const amb = p._meta.grammarTrivia ?? target._meta.grammarTrivia;
14855
- if (amb === void 0 || this.activeTrivia !== void 0) return ip;
14856
- if (!hasOwnTriviaBoundary(target)) return ip;
14857
- return this.emit(OP_SCOPE_PLAIN, this.triviaSlot(amb), ip);
15534
+ this.activeTrivia = void 0;
15535
+ this.triviaKnown = savedKnown;
15536
+ return this.emit(OP_SCOPE_PLAIN, this.triviaSlot(runsUnder), ip);
14858
15537
  }
14859
15538
  /**
14860
15539
  * The `OP_RGATE` operands of a choice arm that opens with `routed()`, or
@@ -14895,38 +15574,43 @@ var Encoder = class {
14895
15574
  if (matchesEmpty(next, /* @__PURE__ */ new Set(), rr) || mayCommitFailure(next, rr, this.commitFailureMemo)) return void 0;
14896
15575
  const nextDef = next._def;
14897
15576
  if (nextDef.tag !== "literal" && nextDef.tag !== "regex" && nextDef.tag !== "keywords" && nextDef.tag !== "label") return void 0;
14898
- const first = firstSetOf(next, /* @__PURE__ */ new Set(), rr, boundary);
15577
+ if (boundary === void 0 && !this.triviaKnown) return void 0;
15578
+ const first = firstSetOf(next, /* @__PURE__ */ new Set(), rr, boundary ?? null);
14899
15579
  const cls = this.charClass(boundary === void 0 ? first : union(first, firstSetOf(boundary, /* @__PURE__ */ new Set(), rr)));
14900
15580
  if (cls < 0) return void 0;
14901
- const miss = nextDef.tag === "label" ? deriveExpected(next) : directTerminalFailureExpected(nextDef);
14902
- return { cls, fx: this.expected(miss) };
15581
+ const fx = this.expected(nextDef.tag === "label" ? deriveExpected(next) : directTerminalFailureExpected(nextDef));
15582
+ const two = firstTwo(next);
15583
+ if (two === void 0) return { cls, fx };
15584
+ if (boundary !== void 0 && intersects(two.first, firstSetOf(boundary, /* @__PURE__ */ new Set(), rr))) return { cls, fx };
15585
+ const a = this.charClass(two.first);
15586
+ const b = this.charClass(two.second);
15587
+ return a < 0 || b < 0 ? { cls, fx } : { cls, fx, two: [a, b] };
14903
15588
  }
14904
15589
  node(p) {
14905
- const lazyRef = p._def.tag === "lazy";
14906
- const scopeKey = lazyRef ? this.activeTrivia : void 0;
14907
- const refMemo = this.refMemo;
14908
- const hit = lazyRef ? refMemo.get(p)?.get(scopeKey) : this.memo.get(p);
15590
+ const key = this.scopeKey(p);
15591
+ let byScope = this.memo.get(p);
15592
+ const hit = byScope?.get(key);
14909
15593
  if (hit !== void 0) return { ip: hit };
14910
- const inFlight = this.pending.get(p);
15594
+ let inFlightByScope = this.pending.get(p);
15595
+ const inFlight = inFlightByScope?.get(key);
14911
15596
  if (inFlight !== void 0) {
14912
15597
  const ip2 = this.emitHead(OP_RULE, 1);
14913
15598
  inFlight.push(ip2 + 1);
14914
15599
  return { ip: ip2 };
14915
15600
  }
14916
15601
  const patches = [];
14917
- this.pending.set(p, patches);
15602
+ if (inFlightByScope === void 0) {
15603
+ inFlightByScope = /* @__PURE__ */ new Map();
15604
+ this.pending.set(p, inFlightByScope);
15605
+ }
15606
+ inFlightByScope.set(key, patches);
14918
15607
  const ip = this.covWrap(this.encodeDef(p), this.plan?.rules.get(p), 0);
14919
- this.pending.delete(p);
14920
- if (lazyRef) {
14921
- let byScope = refMemo.get(p);
14922
- if (byScope === void 0) {
14923
- byScope = /* @__PURE__ */ new Map();
14924
- refMemo.set(p, byScope);
14925
- }
14926
- byScope.set(scopeKey, ip);
14927
- } else {
14928
- this.memo.set(p, ip);
15608
+ inFlightByScope.delete(key);
15609
+ if (byScope === void 0) {
15610
+ byScope = /* @__PURE__ */ new Map();
15611
+ this.memo.set(p, byScope);
14929
15612
  }
15613
+ byScope.set(key, ip);
14930
15614
  for (const slot of patches) {
14931
15615
  if (slot - 1 === ip) {
14932
15616
  const rule = p._ruleName;
@@ -15024,8 +15708,19 @@ var Encoder = class {
15024
15708
  }
15025
15709
  case "choice": {
15026
15710
  if (this.plan === void 0) {
15027
- const factored = factorSharedEnds(d);
15028
- if (factored !== void 0) return this.node(factored).ip;
15711
+ const shared = factorSharedEnds(d);
15712
+ if (shared !== void 0) return this.node(shared).ip;
15713
+ if (this.unfactoredChoice !== p) {
15714
+ const prefix = factorPrefixArms(d);
15715
+ if (prefix !== void 0) {
15716
+ const strict = this.node(prefix).ip;
15717
+ const saved = this.unfactoredChoice;
15718
+ this.unfactoredChoice = p;
15719
+ const tolerant = this.encodeDef(p);
15720
+ this.unfactoredChoice = saved;
15721
+ return this.emit(OP_STRICT, strict, tolerant);
15722
+ }
15723
+ }
15029
15724
  }
15030
15725
  if (d.strategy.tag === "greedyClassify") {
15031
15726
  const superIndex = d.strategy.superIndex;
@@ -15054,18 +15749,20 @@ var Encoder = class {
15054
15749
  const plain = (checks === null || checks === void 0 || checks.length === 0) && (gate === null || gate === void 0);
15055
15750
  const routedGate = plain ? this.routedArmGate(c) : void 0;
15056
15751
  const body = this.node(c).ip;
15057
- const inner = routedGate === void 0 ? body : this.emit(OP_RGATE, routedGate.cls, body, routedGate.fx);
15752
+ 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]);
15058
15753
  const ip = checks === null || checks === void 0 || checks.length === 0 ? inner : this.reject(inner, checks);
15059
15754
  const gated = gate === null || gate === void 0 ? ip : this.emit(OP_ARMGATE, this.fn(gate, d.gateSrcs?.[src] ?? null), ip, this.expected(deriveExpected(c)));
15060
15755
  return this.covWrap(gated, armCovIds?.[src], 1);
15061
15756
  });
15062
15757
  const rr = this.refResolver();
15063
- const finalChoice = classifyFinalChoice(arms, rr, this.activeTrivia);
15758
+ const constructionSets = d.disjoint && !factoredChoices.has(p);
15759
+ const finalChoice = classifyFinalChoice(arms, rr, this.gateTrivia(), false, constructionSets);
15760
+ const wideArms = this.wideArms(arms, finalChoice.firstSets, constructionSets);
15064
15761
  const transactionalFirst = arms.map((arm, i) => {
15065
15762
  if (!finalChoice.nullable[i]) return void 0;
15066
15763
  const def = arm._def;
15067
15764
  if (def.tag !== "attempt" || matchesEmpty(def.parser, /* @__PURE__ */ new Set(), rr)) return void 0;
15068
- return firstSetOf(def.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia);
15765
+ return firstSetOf(def.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia, false);
15069
15766
  });
15070
15767
  const gateFirst = finalChoice.firstSets.map((first, i) => transactionalFirst[i] ?? first);
15071
15768
  const classes = gateFirst.map((first, i) => finalChoice.nullable[i] && transactionalFirst[i] === void 0 ? -1 : this.charClass(first));
@@ -15083,6 +15780,9 @@ var Encoder = class {
15083
15780
  if (transactionalFirst[i] !== void 0) classes[i] = -1;
15084
15781
  }
15085
15782
  }
15783
+ if (wideArms.length > 0 && dispatchIsExclusive(classes, this.cc)) {
15784
+ for (const i of wideArms) classes[i] = -1;
15785
+ }
15086
15786
  if (d.gates.some((g) => g !== null && g !== void 0) && !dispatchIsExclusive(classes, this.cc)) {
15087
15787
  for (let k = 0; k < classes.length; k++) {
15088
15788
  const gate = d.gates[order[k]];
@@ -15116,27 +15816,34 @@ var Encoder = class {
15116
15816
  }
15117
15817
  case "many":
15118
15818
  case "oneOrMore": {
15119
- const child = this.node(d.parser).ip;
15120
15819
  const rr = this.refResolver();
15121
- const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.charClass(firstSetOf(d.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
15820
+ const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.charClass(this.gateFirstSet(d.parser, d.guarded === true, true));
15122
15821
  const op = d.valueUnused ? OP_REPV : OP_REP;
15123
15822
  const min = d.tag === "many" ? 0 : d.min;
15823
+ const item = min === 0 && itemClass >= 0 ? this.withoutLeadGate(this.node(d.parser).ip, itemClass) : this.node(d.parser).ip;
15824
+ const child = min === 0 && itemClass >= 0 ? this.gapGated(d.parser, item) : item;
15124
15825
  return this.emit(op, child, min, d.max ?? -1, -1, 0, this.expected(deriveExpected(d.parser)), itemClass);
15125
15826
  }
15126
15827
  case "sepBy": {
15127
15828
  const child = this.node(d.parser).ip;
15128
15829
  const sep = this.node(d.separator).ip;
15129
- const flags = (d.trailing === "allow" ? 1 : 0) | (d.keepSeparators === true ? 2 : 0) | (d.min >= 2 ? 4 : 0);
15130
- return this.emit(
15131
- OP_REP,
15830
+ const gate = this.sepGate(d.separator, sep);
15831
+ const itemSet = deriveExpected(d.parser);
15832
+ const itemFx = this.expected(itemSet.length > 0 ? itemSet : [d.parser._tag]);
15833
+ const flags = (d.trailing === "allow" ? 1 : 0) | (d.keepSeparators === true ? 2 : 0) | (d.min >= 1 ? 4 : 0) | (gate >= 0 ? 8 : 0) | (itemSet.length === 0 ? 16 : 0);
15834
+ const ops = [
15132
15835
  child,
15133
15836
  d.min,
15134
15837
  d.max ?? -1,
15135
15838
  sep,
15136
15839
  flags,
15137
- this.expected(deriveExpected(d.parser)),
15138
- this.charClass(firstSetOf(d.separator, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia))
15139
- );
15840
+ itemFx,
15841
+ // As 0.58.0: the interpreter resyncs on the separator itself
15842
+ // (`orSentinel`), which runs under the live trivia.
15843
+ this.charClass(firstSetOf(d.separator, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia, false))
15844
+ ];
15845
+ if (gate >= 0) ops.push(gate);
15846
+ return this.emit(OP_REP, ...ops);
15140
15847
  }
15141
15848
  case "optional": {
15142
15849
  const head = this.emit(OP_OPT, this.lookaheadGate(d.parser, this.node(d.parser).ip));
@@ -15145,7 +15852,7 @@ var Encoder = class {
15145
15852
  }
15146
15853
  case "transform": {
15147
15854
  const inner = d.parser._def;
15148
- if (inner.tag === "sequence" && !this.memo.has(d.parser) && !this.pending.has(d.parser)) {
15855
+ if (inner.tag === "sequence" && !this.memo.get(d.parser)?.has(this.scopeKey(d.parser)) && !this.pending.get(d.parser)?.has(this.scopeKey(d.parser))) {
15149
15856
  const kids = inner.parsers.map((c) => this.node(c).ip);
15150
15857
  const head = this.emitHead(OP_SEQX, 2 + 2 * kids.length);
15151
15858
  const projection = directArrayProjection(d.fn, d.fnSrc, kids.length);
@@ -15186,7 +15893,7 @@ var Encoder = class {
15186
15893
  d.tags === void 0 ? -1 : this.constant(d.tags)
15187
15894
  );
15188
15895
  if (matchesEmpty(d.parser, /* @__PURE__ */ new Set(), this.refResolver())) return body;
15189
- const fs = this.gateFirstSet(d.parser);
15896
+ const fs = this.gateFirstSet(d.parser, d.guarded === true, false);
15190
15897
  if (fs.kind === "any") return body;
15191
15898
  const cls = this.charClass(fs);
15192
15899
  if (cls < 0) return body;
@@ -15194,7 +15901,14 @@ var Encoder = class {
15194
15901
  return this.emit(OP_GATE, cls, body, this.expected(e.length > 0 ? e : [d.parser._tag]));
15195
15902
  }
15196
15903
  case "token": {
15197
- return this.emit(OP_TOKEN, this.node(d.parser).ip);
15904
+ const saved = this.activeTrivia;
15905
+ const savedKnown = this.triviaKnown;
15906
+ this.activeTrivia = void 0;
15907
+ this.triviaKnown = true;
15908
+ const body = this.node(d.parser).ip;
15909
+ this.activeTrivia = saved;
15910
+ this.triviaKnown = savedKnown;
15911
+ return this.emit(OP_TOKEN, body);
15198
15912
  }
15199
15913
  case "routed":
15200
15914
  return this.emit(OP_ROUTED, d.fallback === void 0 ? -1 : this.node(d.fallback).ip);
@@ -15250,7 +15964,7 @@ var Encoder = class {
15250
15964
  this.code[head + 2] = dspIdx;
15251
15965
  this.code[head + 3] = other;
15252
15966
  const otherRouted = d.otherwise === void 0 ? false : branchUsesRouted({ parser: d.otherwise, usesRouted: d.otherwiseUsesRouted === true });
15253
- this.code[head + 4] = otherRouted ? 1 : 0;
15967
+ this.code[head + 4] = (otherRouted ? 1 : 0) | (d.otherwiseCommit === false ? 2 : 0);
15254
15968
  this.code[head + 5] = arms.length;
15255
15969
  for (let i = 0; i < arms.length; i++) this.code[head + 6 + i] = arms[i];
15256
15970
  return head;
@@ -15305,7 +16019,7 @@ var Encoder = class {
15305
16019
  case "attempt": {
15306
16020
  const inner = this.emit(OP_ATTEMPT, this.node(d.parser).ip, d.contain === true ? 1 : 0);
15307
16021
  if (!this.failureNeedsRollback(d.parser, void 0)) this.failureRollbackCleanSites.add(inner);
15308
- const fs = this.gateFirstSet(d.parser);
16022
+ const fs = this.gateFirstSet(d.parser, d.guarded === true, false);
15309
16023
  if (fs.kind === "any" || matchesEmpty(d.parser, /* @__PURE__ */ new Set(), this.refResolver())) return inner;
15310
16024
  const cls = this.charClass(fs);
15311
16025
  if (cls < 0) return inner;
@@ -15331,10 +16045,17 @@ var Encoder = class {
15331
16045
  const op = cap ? OP_SCOPE_CAP : OP_SCOPE;
15332
16046
  const flags = (d.rootCapture === "opaque" ? 1 : 0) | (d.triviaParser !== void 0 && d.triviaParser._meta.rootTriviaClassified !== true && d.rootCapture !== "opaque" ? 2 : 0);
15333
16047
  const savedTrivia = this.activeTrivia;
15334
- if (d.clearTrivia === true) this.activeTrivia = void 0;
15335
- else if (d.triviaParser !== void 0) this.activeTrivia = d.triviaParser;
16048
+ const savedKnown = this.triviaKnown;
16049
+ if (d.clearTrivia === true) {
16050
+ this.activeTrivia = void 0;
16051
+ this.triviaKnown = true;
16052
+ } else if (d.triviaParser !== void 0) {
16053
+ this.activeTrivia = d.triviaParser;
16054
+ this.triviaKnown = true;
16055
+ }
15336
16056
  const bodyIp = this.node(d.parser).ip;
15337
16057
  this.activeTrivia = savedTrivia;
16058
+ this.triviaKnown = savedKnown;
15338
16059
  if (d.clearTrivia === true) return this.emit(op, -1, bodyIp, flags);
15339
16060
  if (d.triviaParser === void 0) {
15340
16061
  return cap || flags !== 0 ? this.emit(op, -1, bodyIp, flags) : bodyIp;
@@ -15375,6 +16096,7 @@ var Encoder = class {
15375
16096
  switch (this.code[ip]) {
15376
16097
  case OP_GATE:
15377
16098
  case OP_RGATE:
16099
+ case OP_RGATE2:
15378
16100
  return [ip + 2];
15379
16101
  case OP_RULE:
15380
16102
  case OP_OPT:
@@ -15384,6 +16106,7 @@ var Encoder = class {
15384
16106
  case OP_ATTEMPT:
15385
16107
  case OP_LABEL:
15386
16108
  case OP_COV:
16109
+ case OP_GAPGATE:
15387
16110
  return [ip + 1];
15388
16111
  case OP_SCOPE:
15389
16112
  case OP_SCOPE_CAP:
@@ -15420,6 +16143,8 @@ var Encoder = class {
15420
16143
  case OP_REP:
15421
16144
  case OP_REPV:
15422
16145
  return this.code[ip + 4] >= 0 ? [ip + 1, ip + 4] : [ip + 1];
16146
+ case OP_STRICT:
16147
+ return [ip + 1, ip + 2];
15423
16148
  default:
15424
16149
  return [];
15425
16150
  }
@@ -15536,11 +16261,8 @@ function encodeTableProgram(ruleMap, settings = {}) {
15536
16261
  return { prog, fnSrcs: enc.fnSrcs };
15537
16262
  }
15538
16263
 
15539
- // src/version.ts
15540
- var PARSEMAN_VERSION = "0.59.3";
15541
-
15542
16264
  // src/table/emit.ts
15543
- 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`)});`;
16265
+ 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`)});`;
15544
16266
  function jsString(s) {
15545
16267
  return JSON.stringify(s);
15546
16268
  }
@@ -15626,7 +16348,7 @@ function emitAssemblies(prog, cfgs) {
15626
16348
  throw e;
15627
16349
  }
15628
16350
  const plan = em.plan;
15629
- out.push(`{key:${key},factory:function(${EMITTED_PARAMS.join(",")}){${FACTORY_ARITY_GUARD}${em.source}
16351
+ out.push(`{key:${key},arity:${EMITTED_PARAMS.length},factory:function(${EMITTED_PARAMS.join(",")}){${FACTORY_ARITY_GUARD}${em.source}
15630
16352
  },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(",")}]}`);
15631
16353
  }
15632
16354
  return [`a:[${out.join(",")}],`];
@@ -15928,7 +16650,7 @@ function payloadKey(p, d) {
15928
16650
  case "sepBy":
15929
16651
  return `sepBy\0${d.min}\0${d.max ?? ""}\0${d.trailing ?? ""}`;
15930
16652
  case "dispatch":
15931
- return `dispatch\0${d.cases.map((c) => JSON.stringify(c.keys)).join("")}\0${d.otherwise !== void 0}`;
16653
+ return `dispatch\0${d.cases.map((c) => JSON.stringify(c.keys)).join("")}\0${d.otherwise !== void 0}${d.otherwiseCommit === false ? "\0uncommitted" : ""}`;
15932
16654
  case "expect":
15933
16655
  return `expect\0${d.label ?? ""}\0${d.expected.join("")}`;
15934
16656
  case "scanTo":
@@ -17340,7 +18062,10 @@ function staticWeights(code, entries) {
17340
18062
  childSlots(code, ip, kids);
17341
18063
  const op = code[ip];
17342
18064
  const scale = op === OP_REP || op === OP_REPV ? LOOP : op === OP_CHOICE || op === OP_DISPATCH ? 1 / Math.max(1, kids.length) : 1;
17343
- for (const kid of kids) next.set(kid, Math.min(CAP, (next.get(kid) ?? 0) + w * scale));
18065
+ for (const k of kids) {
18066
+ const kid = code[k] === OP_GAPGATE ? code[k + 1] : k;
18067
+ next.set(kid, Math.min(CAP, (next.get(kid) ?? 0) + w * scale));
18068
+ }
17344
18069
  }
17345
18070
  weight = next;
17346
18071
  }
@@ -17457,7 +18182,7 @@ function compileRuleMap(ruleMap, opts = {}) {
17457
18182
  const roots = assemblyExtraIps(artifact);
17458
18183
  let siteBytes, fixed;
17459
18184
  try {
17460
- siteBytes = emitAssemblySource(table, artifact, cfg, roots, true, void 0, true).siteBytes;
18185
+ siteBytes = emitAssemblySource(table, artifact, cfg, roots, true, void 0, true, true).siteBytes;
17461
18186
  fixed = emitAssemblySource(table, artifact, cfg, roots, true, /* @__PURE__ */ new Set(), true).source.length;
17462
18187
  } catch (e) {
17463
18188
  if (e instanceof Unemittable) return { ...none, refusal: e.construct };