parseman 0.59.3 → 0.60.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/dist/analysis/diagnostics.js +876 -190
  2. package/dist/analysis/diagnostics.js.map +3 -3
  3. package/dist/cli/index.js +876 -190
  4. package/dist/cli/index.js.map +3 -3
  5. package/dist/combinators/dispatch.d.ts +13 -1
  6. package/dist/combinators/dispatch.d.ts.map +1 -1
  7. package/dist/combinators/first-set.d.ts +35 -2
  8. package/dist/combinators/first-set.d.ts.map +1 -1
  9. package/dist/combinators/regex.d.ts.map +1 -1
  10. package/dist/combinators/trivia-boundary.d.ts +11 -0
  11. package/dist/combinators/trivia-boundary.d.ts.map +1 -1
  12. package/dist/combinators/trivia-skip.d.ts +1 -1
  13. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  14. package/dist/compiler/token-alphabet.d.ts +2 -0
  15. package/dist/compiler/token-alphabet.d.ts.map +1 -1
  16. package/dist/compiler/token-capability.js +65 -53
  17. package/dist/compiler/token-capability.js.map +2 -2
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +896 -199
  21. package/dist/index.js.map +3 -3
  22. package/dist/plugin/evaluator.d.ts.map +1 -1
  23. package/dist/plugin/index.js +903 -201
  24. package/dist/plugin/index.js.map +3 -3
  25. package/dist/regex/first-set.d.ts +19 -0
  26. package/dist/regex/first-set.d.ts.map +1 -1
  27. package/dist/table/assemble.d.ts +15 -1
  28. package/dist/table/assemble.d.ts.map +1 -1
  29. package/dist/table/child-slots.d.ts.map +1 -1
  30. package/dist/table/compile-rule-map.d.ts +1 -1
  31. package/dist/table/compile-rule-map.d.ts.map +1 -1
  32. package/dist/table/compile.d.ts +1 -1
  33. package/dist/table/emit-assembly.d.ts +8 -2
  34. package/dist/table/emit-assembly.d.ts.map +1 -1
  35. package/dist/table/emit.d.ts +4 -3
  36. package/dist/table/emit.d.ts.map +1 -1
  37. package/dist/table/encode.d.ts.map +1 -1
  38. package/dist/table/exec.d.ts +0 -35
  39. package/dist/table/exec.d.ts.map +1 -1
  40. package/dist/table/factor-choice.d.ts +27 -0
  41. package/dist/table/factor-choice.d.ts.map +1 -1
  42. package/dist/table/gap-gate.d.ts +11 -0
  43. package/dist/table/gap-gate.d.ts.map +1 -0
  44. package/dist/table/index.js +926 -201
  45. package/dist/table/index.js.map +3 -3
  46. package/dist/table/lex-program.d.ts.map +1 -1
  47. package/dist/table/ops.d.ts +35 -16
  48. package/dist/table/ops.d.ts.map +1 -1
  49. package/dist/table/program.d.ts +6 -1
  50. package/dist/table/program.d.ts.map +1 -1
  51. package/dist/types.d.ts +16 -4
  52. package/dist/types.d.ts.map +1 -1
  53. package/dist/version.d.ts +1 -1
  54. package/package.json +2 -4
  55. package/src/analysis/duplication.ts +1 -1
  56. package/src/combinators/attempt.ts +1 -1
  57. package/src/combinators/dispatch.ts +30 -6
  58. package/src/combinators/first-set.ts +55 -12
  59. package/src/combinators/node.ts +1 -1
  60. package/src/combinators/regex.ts +9 -4
  61. package/src/combinators/repeat.ts +6 -6
  62. package/src/combinators/sequence.ts +1 -1
  63. package/src/combinators/trivia-boundary.ts +18 -0
  64. package/src/combinators/trivia-skip.ts +1 -1
  65. package/src/compiler/ir-serialize.ts +1 -1
  66. package/src/compiler/token-alphabet.ts +3 -1
  67. package/src/coverage.ts +1 -1
  68. package/src/index.ts +1 -1
  69. package/src/plugin/evaluator.ts +8 -3
  70. package/src/regex/first-set.ts +37 -3
  71. package/src/table/assemble.ts +176 -30
  72. package/src/table/child-slots.ts +8 -3
  73. package/src/table/compile-rule-map.ts +7 -4
  74. package/src/table/compile.ts +1 -1
  75. package/src/table/emit-assembly.ts +184 -33
  76. package/src/table/emit.ts +6 -4
  77. package/src/table/encode.ts +469 -74
  78. package/src/table/exec.ts +38 -82
  79. package/src/table/factor-choice.ts +62 -0
  80. package/src/table/fail-clean.ts +2 -2
  81. package/src/table/gap-gate.ts +55 -0
  82. package/src/table/hold-gap.ts +2 -2
  83. package/src/table/lex-program.ts +15 -6
  84. package/src/table/ops.ts +38 -16
  85. package/src/table/program.ts +7 -2
  86. package/src/table/select-hot.ts +7 -2
  87. package/src/types.ts +12 -5
  88. package/src/version.ts +1 -1
  89. package/dist/table/encode-baseline.d.ts +0 -16
  90. package/dist/table/encode-baseline.d.ts.map +0 -1
  91. package/dist/table/exec-baseline.d.ts +0 -9
  92. package/dist/table/exec-baseline.d.ts.map +0 -1
  93. package/src/table/encode-baseline.ts +0 -346
  94. package/src/table/exec-baseline.ts +0 -557
package/dist/cli/index.js CHANGED
@@ -35,15 +35,56 @@ function implicitLeadsDropped(ranges) {
35
35
  return out;
36
36
  }
37
37
 
38
+ // src/combinators/trivia-boundary.ts
39
+ var MAX_DEPTH = 12;
40
+ function hasOwnTriviaBoundary(p, depth = MAX_DEPTH) {
41
+ if (depth === 0) return true;
42
+ const d = p._def;
43
+ switch (d.tag) {
44
+ // Multi-term forms: the gaps BETWEEN terms are the ambient-trivia positions.
45
+ case "sequence":
46
+ return d.parsers.length > 1;
47
+ case "many":
48
+ case "oneOrMore":
49
+ case "sepBy":
50
+ return true;
51
+ // Transparent wrappers — they add no position, so ask what they wrap.
52
+ case "node":
53
+ case "transform":
54
+ case "leaf":
55
+ case "label":
56
+ case "field":
57
+ case "token":
58
+ case "attempt":
59
+ case "optional":
60
+ case "expect":
61
+ case "withCtx":
62
+ case "recover":
63
+ case "trivia":
64
+ return hasOwnTriviaBoundary(d.parser, depth - 1);
65
+ // An alternation adds no position either, but ONE arm that has one is enough
66
+ // to make the scope meaningful for this rule.
67
+ case "choice":
68
+ return d.parsers.some((a) => hasOwnTriviaBoundary(a, depth - 1));
69
+ default:
70
+ return false;
71
+ }
72
+ }
73
+ function triviaThroughRef(ref2, target, trivia2) {
74
+ if (trivia2 !== void 0 && trivia2 !== null) return trivia2;
75
+ const amb = ref2._meta.grammarTrivia ?? target._meta.grammarTrivia;
76
+ return amb !== void 0 && hasOwnTriviaBoundary(target) ? amb : trivia2;
77
+ }
78
+
38
79
  // src/combinators/first-set.ts
39
- function classifyFinalChoice(arms, resolve2, activeTrivia) {
80
+ function classifyFinalChoice(arms, resolve2, activeTrivia, consuming = true, constructionSets = false) {
40
81
  const firstSets = [];
41
82
  const nullable2 = [];
42
83
  let exclusive = true;
43
84
  for (const arm of arms) {
44
85
  const empty2 = matchesEmpty(arm, /* @__PURE__ */ new Set(), resolve2);
45
86
  nullable2.push(empty2);
46
- firstSets.push(empty2 ? { kind: "any" } : firstSetOf(arm, /* @__PURE__ */ new Set(), resolve2, activeTrivia));
87
+ firstSets.push(empty2 ? { kind: "any" } : constructionSets ? arm._meta.firstSet : firstSetOf(arm, /* @__PURE__ */ new Set(), resolve2, activeTrivia, consuming));
47
88
  if (empty2) exclusive = false;
48
89
  }
49
90
  if (exclusive) {
@@ -228,7 +269,7 @@ function narrowBy(acc, constraint) {
228
269
  function applyAssertion(fs, assertion) {
229
270
  return assertion === null ? fs : fs.kind === "empty" ? assertion : narrowBy(fs, assertion);
230
271
  }
231
- function sequenceSet(parsers, fs, empties, trivia2) {
272
+ function sequenceSet(parsers, fs, empties, trivia2, consuming = true) {
232
273
  const afterBoundary = (i) => {
233
274
  const rest = at(i);
234
275
  return trivia2 ? union(rest, fs(trivia2)) : rest;
@@ -244,24 +285,24 @@ function sequenceSet(parsers, fs, empties, trivia2) {
244
285
  }
245
286
  const first = fs(p);
246
287
  if (!empties(p) || i + 1 === parsers.length) return first;
247
- return union(first, afterBoundary(i + 1));
288
+ return union((consuming ? p._meta.consumingFirstSet : void 0) ?? first, afterBoundary(i + 1));
248
289
  };
249
290
  return at(0);
250
291
  }
251
292
  function sequenceFirstSet(parsers) {
252
- return sequenceSet(parsers, (p) => p._meta.firstSet, matchesEmpty);
293
+ return sequenceSet(parsers, (p) => p._meta.firstSet, matchesEmpty, void 0, false);
253
294
  }
254
- function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve2, activeTrivia) {
295
+ function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve2, activeTrivia, consuming = true) {
255
296
  if (seen.has(p)) return any();
256
297
  seen.add(p);
257
298
  try {
258
- return firstSetBody(p, seen, resolve2, activeTrivia);
299
+ return firstSetBody(p, seen, resolve2, activeTrivia, consuming);
259
300
  } finally {
260
301
  seen.delete(p);
261
302
  }
262
303
  }
263
- function firstSetBody(p, seen, resolve2, activeTrivia) {
264
- const fs = (c) => firstSetOf(c, seen, resolve2, activeTrivia);
304
+ function firstSetBody(p, seen, resolve2, activeTrivia, consuming) {
305
+ const fs = (c) => firstSetOf(c, seen, resolve2, activeTrivia, consuming);
265
306
  const empties = (c) => matchesEmpty(c, /* @__PURE__ */ new Set(), resolve2);
266
307
  const d = p._def;
267
308
  switch (d.tag) {
@@ -273,13 +314,16 @@ function firstSetBody(p, seen, resolve2, activeTrivia) {
273
314
  case "dispatch":
274
315
  return fs(d.selector);
275
316
  case "lazy": {
276
- const w = winnerForRef(p, resolve2);
277
- if (w !== void 0) return fs(w);
278
- try {
279
- return fs(d.thunk());
280
- } catch {
281
- return any();
317
+ let target = winnerForRef(p, resolve2);
318
+ if (target === void 0) {
319
+ try {
320
+ target = d.thunk();
321
+ } catch {
322
+ return any();
323
+ }
282
324
  }
325
+ const through = activeTrivia === null && resolve2 !== void 0;
326
+ return firstSetOf(target, seen, resolve2, through ? triviaThroughRef(p, target, null) : activeTrivia, consuming);
283
327
  }
284
328
  case "choice": {
285
329
  let out = empty();
@@ -287,7 +331,7 @@ function firstSetBody(p, seen, resolve2, activeTrivia) {
287
331
  return out;
288
332
  }
289
333
  case "sequence": {
290
- return sequenceSet(d.parsers, fs, empties, activeTrivia);
334
+ return sequenceSet(d.parsers, fs, empties, activeTrivia, consuming);
291
335
  }
292
336
  case "peek": {
293
337
  const inner = d.parser;
@@ -299,7 +343,6 @@ function firstSetBody(p, seen, resolve2, activeTrivia) {
299
343
  case "transform":
300
344
  case "label":
301
345
  case "trivia":
302
- case "token":
303
346
  case "leaf":
304
347
  case "node":
305
348
  case "sepBy":
@@ -309,12 +352,16 @@ function firstSetBody(p, seen, resolve2, activeTrivia) {
309
352
  // with a rule reference — resolve through it like any other wrapper.
310
353
  case "attempt":
311
354
  return fs(d.parser);
355
+ // A token runs its body with trivia cleared (`token.ts`).
356
+ case "token":
357
+ return firstSetOf(d.parser, seen, resolve2, activeTrivia === void 0 ? void 0 : null, consuming);
312
358
  case "grammar":
313
359
  return firstSetOf(
314
360
  d.parser,
315
361
  seen,
316
362
  resolve2,
317
- d.clearTrivia ? null : d.triviaParser ?? activeTrivia
363
+ d.clearTrivia ? null : d.triviaParser ?? activeTrivia,
364
+ consuming
318
365
  );
319
366
  // A run starts with its first line's prefix; a nullable prefix starts with anything.
320
367
  case "lines": {
@@ -2052,10 +2099,6 @@ function scanTriviaCompact(input, cur, ctx) {
2052
2099
  const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
2053
2100
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
2054
2101
  }
2055
- function scanTrivia(input, cur, ctx) {
2056
- const scan = scanTriviaCompact(input, cur, ctx);
2057
- return typeof scan === "number" ? { end: scan, commit: NOOP_COMMIT } : scan;
2058
- }
2059
2102
  function skipTriviaScanned(s, input, cur, ctx) {
2060
2103
  const end = s(input, cur);
2061
2104
  if (end !== cur) {
@@ -2828,42 +2871,6 @@ function keywords(words, opts = {}) {
2828
2871
  };
2829
2872
  }
2830
2873
 
2831
- // src/combinators/trivia-boundary.ts
2832
- var MAX_DEPTH = 12;
2833
- function hasOwnTriviaBoundary(p, depth = MAX_DEPTH) {
2834
- if (depth === 0) return true;
2835
- const d = p._def;
2836
- switch (d.tag) {
2837
- // Multi-term forms: the gaps BETWEEN terms are the ambient-trivia positions.
2838
- case "sequence":
2839
- return d.parsers.length > 1;
2840
- case "many":
2841
- case "oneOrMore":
2842
- case "sepBy":
2843
- return true;
2844
- // Transparent wrappers — they add no position, so ask what they wrap.
2845
- case "node":
2846
- case "transform":
2847
- case "leaf":
2848
- case "label":
2849
- case "field":
2850
- case "token":
2851
- case "attempt":
2852
- case "optional":
2853
- case "expect":
2854
- case "withCtx":
2855
- case "recover":
2856
- case "trivia":
2857
- return hasOwnTriviaBoundary(d.parser, depth - 1);
2858
- // An alternation adds no position either, but ONE arm that has one is enough
2859
- // to make the scope meaningful for this rule.
2860
- case "choice":
2861
- return d.parsers.some((a) => hasOwnTriviaBoundary(a, depth - 1));
2862
- default:
2863
- return false;
2864
- }
2865
- }
2866
-
2867
2874
  // src/compiler/token-alphabet.ts
2868
2875
  function optionalSuffixLexBody(ir) {
2869
2876
  if (ir.kind !== "sequence" || ir.parts.length !== 2) return void 0;
@@ -3121,6 +3128,7 @@ function directFixedLexNode(parser2, resolve2, active, pureOnly = false, cleared
3121
3128
  arms,
3122
3129
  parsers: def.parsers,
3123
3130
  cleared,
3131
+ disjoint: def.disjoint,
3124
3132
  armExpected: def.parsers.map(deriveExpected),
3125
3133
  expected: def.parsers.flatMap(deriveExpected)
3126
3134
  };
@@ -3471,10 +3479,12 @@ function parseRegex(src, unicode = false, sets = false) {
3471
3479
  const parseGroup = () => {
3472
3480
  i++;
3473
3481
  let lookaround = false;
3482
+ let lookahead = false;
3474
3483
  if (src[i] === "?") {
3475
3484
  const c1 = src[i + 1];
3476
3485
  if (c1 === ":") i += 2;
3477
3486
  else if (c1 === "=" || c1 === "!") {
3487
+ lookahead = c1 === "=";
3478
3488
  i += 2;
3479
3489
  lookaround = true;
3480
3490
  } else if (c1 === "<") {
@@ -3492,7 +3502,8 @@ function parseRegex(src, unicode = false, sets = false) {
3492
3502
  const inner = parseAlt();
3493
3503
  if (src[i] !== ")") throw BAIL;
3494
3504
  i++;
3495
- return lookaround ? EMPTY2 : inner;
3505
+ if (lookaround) return lookahead ? { t: "empty", look: inner } : EMPTY2;
3506
+ return inner;
3496
3507
  };
3497
3508
  const parseClass = () => {
3498
3509
  let k = i + 1;
@@ -3685,6 +3696,13 @@ function regexCanMatchEmpty(source) {
3685
3696
  return true;
3686
3697
  }
3687
3698
  }
3699
+ function consumingFirstSetFromRegex(source, flags = "") {
3700
+ try {
3701
+ return firstSet(parseRegex(source, flags.includes("u") || flags.includes("v"), flags.includes("v")));
3702
+ } catch {
3703
+ return any();
3704
+ }
3705
+ }
3688
3706
  function firstSetFromRegex(source, flags = "") {
3689
3707
  let ast;
3690
3708
  try {
@@ -3695,6 +3713,20 @@ function firstSetFromRegex(source, flags = "") {
3695
3713
  if (nullable(ast)) return { firstSet: any(), canMatchNewline: canConsumeNewline(ast) };
3696
3714
  return { firstSet: firstSet(ast), canMatchNewline: canConsumeNewline(ast) };
3697
3715
  }
3716
+ function regexFirstTwo(source, flags = "") {
3717
+ let ast;
3718
+ try {
3719
+ ast = parseRegex(source, flags.includes("u") || flags.includes("v"), flags.includes("v"));
3720
+ } catch {
3721
+ return void 0;
3722
+ }
3723
+ const parts = ast.t === "seq" ? ast.parts : [ast];
3724
+ const a = parts[0];
3725
+ const b = parts[1];
3726
+ if (a === void 0 || a.t !== "char" || a.set.kind !== "ranges" || b === void 0) return void 0;
3727
+ const second = b.t === "char" ? b.set : b.t === "empty" && b.look !== void 0 && !nullable(b.look) ? firstSet(b.look) : void 0;
3728
+ return second !== void 0 && second.kind === "ranges" ? { first: a.set, second } : void 0;
3729
+ }
3698
3730
 
3699
3731
  // src/analysis/commitment.ts
3700
3732
  function mayFail(p, seen = /* @__PURE__ */ new Set()) {
@@ -4540,9 +4572,9 @@ function many(combinator, opts = {}) {
4540
4572
  canMatchNewline: combinator._meta.canMatchNewline,
4541
4573
  isTrivia: false
4542
4574
  };
4543
- const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max } };
4544
- let expected;
4545
4575
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
4576
+ const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max }, ...guardable ? { guarded: true } : {} };
4577
+ let expected;
4546
4578
  const scalarChild = scalarOf(combinator);
4547
4579
  const parseScalar = (input, pos, ctx) => repeatScalarTail(combinator, scalarChild, input, pos, ctx, guardable, def.valueUnused ? void 0 : [], max);
4548
4580
  return {
@@ -4589,9 +4621,9 @@ function atLeast(combinator, min, max) {
4589
4621
  canMatchNewline: combinator._meta.canMatchNewline,
4590
4622
  isTrivia: false
4591
4623
  };
4592
- const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max } };
4593
- let expected;
4594
4624
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
4625
+ const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max }, ...guardable ? { guarded: true } : {} };
4626
+ let expected;
4595
4627
  const scalarChild = scalarOf(combinator);
4596
4628
  const parseScalar = (input, pos, ctx) => {
4597
4629
  let cur = scalarChild(input, pos, ctx);
@@ -5063,9 +5095,14 @@ function regex(pattern, flags = "") {
5063
5095
  const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
5064
5096
  const scan = shortScanner(source, resolvedFlags);
5065
5097
  const raw = firstSetFromRegex(source, resolvedFlags);
5066
- const firstSet2 = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
5098
+ const forFlags = (fs) => !resolvedFlags.includes("i") ? fs : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(fs);
5099
+ const firstSet2 = forFlags(raw.firstSet);
5067
5100
  const canMatchNewline = raw.canMatchNewline;
5068
5101
  const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
5102
+ if (firstSet2.kind === "any" && regexCanMatchEmpty(source)) {
5103
+ const consuming = forFlags(consumingFirstSetFromRegex(source, resolvedFlags));
5104
+ if (consuming.kind !== "any") meta.consumingFirstSet = consuming;
5105
+ }
5069
5106
  const def = { tag: "regex", source, flags: resolvedFlags };
5070
5107
  const expected = directTerminalFailureExpected(def);
5071
5108
  const parseScalar = (input, pos, ctx) => {
@@ -5340,14 +5377,15 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
5340
5377
  return end < 0 ? next(input, pos) : end;
5341
5378
  };
5342
5379
  }
5343
- let tail = (_input, pos, ctx, _lead, acc) => {
5380
+ let tail = (input, pos, ctx, _lead, acc, _best, entered) => {
5381
+ if (!entered && ctx._probe !== void 0) for (const arm of arms) arm.run(input, pos, ctx);
5344
5382
  ctx._fe = pos;
5345
5383
  ctx._fx = acc ?? choiceExpected;
5346
5384
  return -1;
5347
5385
  };
5348
5386
  for (let i = arms.length - 1; i >= 0; i--) {
5349
5387
  const armRun = arms[i].run, cls = classes[i], staticExpected = armExpected[i], next = tail;
5350
- tail = (input, pos, ctx, lead2, acc, best) => {
5388
+ tail = (input, pos, ctx, lead2, acc, best, entered) => {
5351
5389
  if (cls !== void 0 && !classHas(cls, lead2)) {
5352
5390
  return next(
5353
5391
  input,
@@ -5355,7 +5393,8 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
5355
5393
  ctx,
5356
5394
  lead2,
5357
5395
  best === pos ? appendExpected(acc, staticExpected) : acc,
5358
- best
5396
+ best,
5397
+ entered
5359
5398
  );
5360
5399
  }
5361
5400
  ctx._fc = false;
@@ -5371,7 +5410,7 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
5371
5410
  if (nextAcc !== void 0) ctx._fx = nextAcc;
5372
5411
  return -1;
5373
5412
  }
5374
- return next(input, pos, ctx, lead2, nextAcc, best);
5413
+ return next(input, pos, ctx, lead2, nextAcc, best, true);
5375
5414
  };
5376
5415
  }
5377
5416
  return {
@@ -5382,7 +5421,8 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
5382
5421
  ctx,
5383
5422
  pos < input.length ? input.codePointAt(pos) : -1,
5384
5423
  void 0,
5385
- pos
5424
+ pos,
5425
+ false
5386
5426
  )
5387
5427
  };
5388
5428
  }
@@ -5420,6 +5460,7 @@ function exclusiveChoiceNode(arms, classes, choiceFx) {
5420
5460
  return -1;
5421
5461
  }
5422
5462
  }
5463
+ if (arm < 0 && ctx._probe !== void 0) for (const a of arms) a.run(input, pos, ctx);
5423
5464
  ctx._fe = pos;
5424
5465
  ctx._fx = choiceFx;
5425
5466
  return -1;
@@ -5644,6 +5685,7 @@ function buildLexProgramRunner(spec, matchers, classes, expected) {
5644
5685
  if (!classHas(cls, lead2)) {
5645
5686
  ctx._fe = pos;
5646
5687
  ctx._fx = choiceFx.slice();
5688
+ if (ctx._probe !== void 0) failAt(ctx, [...x0, ...x1, ...x2, ...x3], pos);
5647
5689
  return -1;
5648
5690
  }
5649
5691
  let acc;
@@ -5755,7 +5797,7 @@ function validateDispatchSpec(spec, arity, fallbackRouted) {
5755
5797
  for (const arm of arms) if (!Number.isInteger(arm) || arm < 0 || arm >= arity) return true;
5756
5798
  return false;
5757
5799
  };
5758
- 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]]))) {
5800
+ 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]]))) {
5759
5801
  throw new TypeError("table: malformed dispatch");
5760
5802
  }
5761
5803
  }
@@ -6069,7 +6111,7 @@ function sequence(...parsers) {
6069
6111
  if (tolerant) {
6070
6112
  followSentinels ??= parsers.map((_, i) => firstSetSentinel(
6071
6113
  parsers.slice(i + 1).reduce(
6072
- (set, parser2) => union(set, firstSetOf(parser2)),
6114
+ (set, parser2) => union(set, firstSetOf(parser2, /* @__PURE__ */ new Set(), void 0, void 0, false)),
6073
6115
  { kind: "empty" }
6074
6116
  )
6075
6117
  ));
@@ -6788,7 +6830,7 @@ function attempt(parser2, options) {
6788
6830
  return {
6789
6831
  _tag: "attempt",
6790
6832
  _meta: parser2._meta,
6791
- _def: contain ? { tag: "attempt", parser: parser2, contain: true } : { tag: "attempt", parser: parser2 },
6833
+ _def: { tag: "attempt", parser: parser2, ...contain ? { contain: true } : {}, ...guardable ? { guarded: true } : {} },
6792
6834
  parse(input, pos, ctx) {
6793
6835
  if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(parser2, input, pos)) {
6794
6836
  if (failExpected === void 0) {
@@ -6814,6 +6856,7 @@ function attempt(parser2, options) {
6814
6856
  }
6815
6857
 
6816
6858
  // src/table/factor-choice.ts
6859
+ var factoredChoices = /* @__PURE__ */ new WeakSet();
6817
6860
  var DERIVED = /* @__PURE__ */ new Set(["_tag", "_meta", "_def", "_parseScalar", "_stickyRegex", "parse"]);
6818
6861
  function isCombinator(v) {
6819
6862
  return typeof v === "object" && v !== null && "_def" in v && "_meta" in v;
@@ -6905,6 +6948,7 @@ function factorSharedEnds(d) {
6905
6948
  for (let i = at + 1; i < n; i++) if (!never(first.terms[i])) return void 0;
6906
6949
  if (arms.some((a) => mayCommitFailure(a))) return void 0;
6907
6950
  const middle = choice(...shapes.map((s) => s.terms[at]));
6951
+ factoredChoices.add(middle);
6908
6952
  if (middle._def.tag === "choice" && middle._def.autoNot.some((a) => a !== null)) return void 0;
6909
6953
  const st = middle._def.tag === "choice" ? middle._def.strategy : void 0;
6910
6954
  if (st === void 0 || st.tag !== "firstMatch" && st.tag !== "sharedPrefix" && !(st.tag === "literalsLongestFirst" && st.sortedIndices.every((x, i) => x === i))) return void 0;
@@ -6917,6 +6961,37 @@ function factorSharedEnds(d) {
6917
6961
  for (let j = first.wrappers.length - 1; j >= 0 && out !== void 0; j--) out = rewrap(first.wrappers[j], out);
6918
6962
  return out === void 0 ? void 0 : attempt(out);
6919
6963
  }
6964
+ function factorPrefixArms(d) {
6965
+ const arms = d.parsers;
6966
+ if (arms.length < 2) return void 0;
6967
+ if (d.strategy.tag !== "firstMatch" && d.strategy.tag !== "sharedPrefix") return void 0;
6968
+ if (d.gates.some((g) => g !== null) || d.autoNot.some((a) => a !== null)) return void 0;
6969
+ const out = [...arms];
6970
+ let changed = false;
6971
+ for (let i = 0; i + 1 < arms.length; i++) {
6972
+ const arm = arms[i];
6973
+ if (arm._def.tag !== "sequence" || arm._def.valueUnused !== true) continue;
6974
+ const terms = arm._def.parsers;
6975
+ const next = arms[i + 1];
6976
+ const prefix = next._def.tag === "sequence" && next._def.valueUnused === true ? next._def.parsers : [next];
6977
+ if (prefix.length >= terms.length) continue;
6978
+ if (!prefix.every((t2, j) => sameComb(t2, terms[j], 0))) continue;
6979
+ const rest = terms.slice(prefix.length);
6980
+ if (rest.every(never)) continue;
6981
+ const tail = rest.length === 1 ? rest[0] : sequence(...rest);
6982
+ if (tail !== rest[0]) tail._def.valueUnused = true;
6983
+ const parts = [...terms.slice(0, prefix.length), optional(tail)];
6984
+ const seq = sequence(...parts);
6985
+ seq._def.valueUnused = true;
6986
+ out[i] = seq;
6987
+ changed = true;
6988
+ }
6989
+ if (!changed) return void 0;
6990
+ const rebuilt = choice(...out);
6991
+ factoredChoices.add(rebuilt);
6992
+ const st = rebuilt._def.tag === "choice" ? rebuilt._def.strategy : void 0;
6993
+ return st !== void 0 && (st.tag === "firstMatch" || st.tag === "sharedPrefix") ? rebuilt : void 0;
6994
+ }
6920
6995
 
6921
6996
  // src/compiler/fields.ts
6922
6997
  function buildReadsFields(def) {
@@ -7033,6 +7108,9 @@ var OP_LEX_BODY = 42;
7033
7108
  var OP_LEX_PROGRAM = 43;
7034
7109
  var OP_SOURCE_LEAF = 44;
7035
7110
  var OP_RGATE = 45;
7111
+ var OP_GAPGATE = 46;
7112
+ var OP_RGATE2 = 47;
7113
+ var OP_STRICT = 48;
7036
7114
  var OP_DISPATCH = 25;
7037
7115
  var OP_ROUTED = 26;
7038
7116
  var OP_NAMES = {
@@ -7080,7 +7158,10 @@ var OP_NAMES = {
7080
7158
  [OP_COV]: "COV",
7081
7159
  [OP_SCOPE_PLAIN]: "SCOPE_PLAIN",
7082
7160
  [OP_LEX_BODY]: "LEX_BODY",
7083
- [OP_LEX_PROGRAM]: "LEX_PROGRAM"
7161
+ [OP_LEX_PROGRAM]: "LEX_PROGRAM",
7162
+ [OP_GAPGATE]: "GAPGATE",
7163
+ [OP_RGATE2]: "RGATE2",
7164
+ [OP_STRICT]: "STRICT"
7084
7165
  };
7085
7166
 
7086
7167
  // src/combinators/node.ts
@@ -7125,7 +7206,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
7125
7206
  if (project !== void 0 && build !== void 0) {
7126
7207
  throw new Error("node() options cannot combine project with a build callback");
7127
7208
  }
7128
- const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build } };
7209
+ const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build }, ...combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator) ? { guarded: true } : {} };
7129
7210
  const meta = {
7130
7211
  firstSet: combinator._meta.firstSet,
7131
7212
  canMatchNewline: combinator._meta.canMatchNewline,
@@ -7248,6 +7329,29 @@ var UnsupportedConstruct = class extends Error {
7248
7329
  this.tag = tag;
7249
7330
  }
7250
7331
  };
7332
+ function firstTwo(p) {
7333
+ const d = p._def;
7334
+ if (d.tag === "label" || d.tag === "grammar") return firstTwo(d.parser);
7335
+ let two;
7336
+ if (d.tag === "literal" && d.value.length >= 2 && d.caseInsensitive !== true) {
7337
+ const second = d.value.codePointAt(1);
7338
+ two = { first: { kind: "ranges", ranges: [{ lo: d.value.charCodeAt(0), hi: d.value.charCodeAt(0) }] }, second: { kind: "ranges", ranges: [{ lo: second, hi: second }] } };
7339
+ } else if (d.tag === "regex" && !d.flags.includes("i")) {
7340
+ const units = regexFirstTwo(d.source, d.flags);
7341
+ if (units !== void 0) two = units;
7342
+ }
7343
+ if (two === void 0 || two.first.kind !== "ranges") return void 0;
7344
+ return two.first.ranges.every((r) => r.hi < 55296 || r.lo > 57343 && r.hi <= 65535) ? two : void 0;
7345
+ }
7346
+ var GAP_CHARS = /* @__PURE__ */ new Set([9, 10, 12, 13, 32, 47]);
7347
+ function isGap(p) {
7348
+ if (matchesEmpty(p)) return true;
7349
+ const fs = p._meta.firstSet;
7350
+ return fs.kind === "ranges" && fs.ranges.every((r) => {
7351
+ for (let c = r.lo; c <= r.hi; c++) if (!GAP_CHARS.has(c)) return false;
7352
+ return true;
7353
+ });
7354
+ }
7251
7355
  var Encoder = class {
7252
7356
  code = [];
7253
7357
  k = [];
@@ -7295,8 +7399,33 @@ var Encoder = class {
7295
7399
  */
7296
7400
  commitFailureMemo = /* @__PURE__ */ new Map();
7297
7401
  labels = void 0;
7402
+ /** The choice being encoded as written, for `OP_STRICT`'s tolerant child. */
7403
+ unfactoredChoice = void 0;
7298
7404
  /** Trivia table in scope at the row being encoded — codegen's `ctx.activeTrivia`. */
7299
7405
  activeTrivia = void 0;
7406
+ /**
7407
+ * Is the trivia live at this row known? It is not in the body of an entry
7408
+ * that installs none of its own: that body runs under whatever trivia its
7409
+ * caller has (a compiled rule inside another grammar, `run({ trivia })`).
7410
+ * Every scope (`parser({ trivia })`, `noTrivia`, `token()`, a rule's install)
7411
+ * makes it known again.
7412
+ */
7413
+ triviaKnown = true;
7414
+ /**
7415
+ * The trivia a gate's first set is derived under: the live table, `null` for a
7416
+ * scope known to have none, or `undefined` where it is not known.
7417
+ */
7418
+ gateTrivia() {
7419
+ return this.activeTrivia ?? (this.triviaKnown ? null : void 0);
7420
+ }
7421
+ /**
7422
+ * Whether a gate may read a nullable pad through its non-empty matches
7423
+ * (`firstSetOf`'s `consuming`): only where the trivia that may follow the pad
7424
+ * is known. Elsewhere a gate reads first sets as 0.58.0 did.
7425
+ */
7426
+ gateConsuming() {
7427
+ return this.activeTrivia !== void 0 || this.triviaKnown;
7428
+ }
7300
7429
  classified = false;
7301
7430
  scans = [];
7302
7431
  /** Ambient `scanSkip` sets, pooled by the ARRAY's identity (stable per grammar). */
@@ -7423,10 +7552,12 @@ var Encoder = class {
7423
7552
  const final = classifyFinalChoice(
7424
7553
  node2.parsers,
7425
7554
  this.refResolver(),
7426
- node2.cleared ? void 0 : this.activeTrivia
7555
+ node2.cleared ? null : this.gateTrivia(),
7556
+ false,
7557
+ node2.disjoint
7427
7558
  );
7428
7559
  const classes = final.firstSets.map((first, i) => final.nullable[i] ? -1 : this.charClass(first));
7429
- const exclusive = final.exclusive && classes.every((cls) => cls >= 0);
7560
+ const exclusive = final.exclusive && classes.every((cls) => cls >= 0) && this.wideArms(node2.parsers, final.firstSets, node2.disjoint, node2.cleared ? null : this.gateTrivia(), node2.cleared ? void 0 : this.activeTrivia).length === 0;
7430
7561
  words.push(LEX_NODE_CHOICE, node2.arms.length, exclusive ? 1 : 0, this.expected(node2.expected));
7431
7562
  for (let i = 0; i < node2.arms.length; i++) {
7432
7563
  words.push(classes[i], this.expected(node2.armExpected[i]));
@@ -7581,16 +7712,117 @@ var Encoder = class {
7581
7712
  kIndex = /* @__PURE__ */ new Map();
7582
7713
  ccIndex = /* @__PURE__ */ new Map();
7583
7714
  fxIndex = /* @__PURE__ */ new Map();
7584
- /** Memoized by combinator identity: a shared sub-combinator is ONE row. */
7585
- memo = /* @__PURE__ */ new Map();
7586
7715
  /**
7587
- * Memo for `lazy` REFERENCES, keyed by the trivia scope the reference is made
7588
- * from. A cross-rule reference's row depends on that scope (`scopedRef`), so the
7589
- * single `memo` slot every other construct uses would freeze the FIRST
7590
- * reference's scope onto every later one and undo the fix.
7716
+ * Memoized by combinator identity AND the trivia scope it is encoded under
7717
+ * (`scopeKey`): a shared sub-combinator is ONE row per scope it runs in.
7718
+ *
7719
+ * A construct's rows depend on that scope. A first set derived through a
7720
+ * nullable term takes in the trivia that may follow it, a gap gate holds only
7721
+ * where no trivia runs, a routed gate reads what may follow its token, and a
7722
+ * cross-rule reference installs its rule's trivia only from a cleared region
7723
+ * (`scopedRef`). One memo slot per combinator froze the FIRST scope it was
7724
+ * reached under onto every later one: a rule body encoded as an entry with no
7725
+ * trivia, then run under a caller's comment trivia, gated its repeats as if no
7726
+ * comment could come between their terms.
7591
7727
  */
7592
- refMemo = /* @__PURE__ */ new Map();
7728
+ memo = /* @__PURE__ */ new Map();
7593
7729
  pending = /* @__PURE__ */ new Map();
7730
+ triviaSignatures = /* @__PURE__ */ new Map();
7731
+ /**
7732
+ * What of the live trivia an encoding can depend on: none, or its first set.
7733
+ * Every derivation reads trivia only through `firstSetOf(trivia)` or whether it
7734
+ * is live, so two scopes whose trivia opens on the same characters encode the
7735
+ * same rows and share them. A construct that reads no scope (`readsScope`)
7736
+ * has one encoding.
7737
+ */
7738
+ scopeKey(p) {
7739
+ const t2 = this.activeTrivia;
7740
+ if (!this.readsScope(p)) return "";
7741
+ if (t2 === void 0) return this.triviaKnown ? "" : "?";
7742
+ let sig = this.triviaSignatures.get(t2);
7743
+ if (sig === void 0) {
7744
+ sig = `t${JSON.stringify(firstSetOf(t2, /* @__PURE__ */ new Set(), this.refResolver()))}`;
7745
+ this.triviaSignatures.set(t2, sig);
7746
+ }
7747
+ return sig;
7748
+ }
7749
+ readsScopeMemo = /* @__PURE__ */ new Map();
7750
+ /**
7751
+ * Can `p`'s rows depend on the trivia scope it is encoded under? No for a
7752
+ * terminal, a scope of its own (`parser({ trivia })`, `noTrivia`, `token()`),
7753
+ * and a wrapper over a construct that reads none; yes wherever a first set, a gate or
7754
+ * a reference's install is derived with the scope: a sequence with a nullable
7755
+ * term before another (the trivia between them), a routed arm (the trivia after
7756
+ * its token), a reference that installs its rule's trivia from a cleared
7757
+ * region, and anything not modelled here — a cycle included.
7758
+ */
7759
+ readsScope(p) {
7760
+ const hit = this.readsScopeMemo.get(p);
7761
+ if (hit !== void 0) return hit;
7762
+ this.readsScopeMemo.set(p, true);
7763
+ const d = p._def;
7764
+ const rr = this.refResolver();
7765
+ let out;
7766
+ switch (d.tag) {
7767
+ case "literal":
7768
+ case "regex":
7769
+ case "keywords":
7770
+ case "token":
7771
+ out = false;
7772
+ break;
7773
+ // `routed()` encodes its fallback under the scope that reaches it.
7774
+ case "routed":
7775
+ out = d.fallback !== void 0 && this.readsScope(d.fallback);
7776
+ break;
7777
+ case "grammar":
7778
+ out = d.clearTrivia !== true && d.triviaParser === void 0 && this.readsScope(d.parser);
7779
+ break;
7780
+ case "node":
7781
+ case "label":
7782
+ case "leaf":
7783
+ case "transform":
7784
+ case "attempt":
7785
+ case "expect":
7786
+ case "not":
7787
+ case "peek":
7788
+ case "optional":
7789
+ case "many":
7790
+ case "oneOrMore":
7791
+ case "field":
7792
+ out = this.readsScope(d.parser);
7793
+ break;
7794
+ case "sepBy":
7795
+ out = this.readsScope(d.parser) || this.readsScope(d.separator);
7796
+ break;
7797
+ case "choice":
7798
+ out = d.parsers.some((a) => this.readsScope(a));
7799
+ break;
7800
+ case "dispatch":
7801
+ 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);
7802
+ break;
7803
+ case "sequence": {
7804
+ const ps = d.parsers;
7805
+ out = ps.some((t2) => this.readsScope(t2)) || ps[0] !== void 0 && (ps[0]._def.tag === "routed" || ps[0]._def.tag === "regex" && isGap(ps[0])) || ps.some((t2, i) => i + 1 < ps.length && matchesEmpty(t2, /* @__PURE__ */ new Set(), rr));
7806
+ break;
7807
+ }
7808
+ case "lazy": {
7809
+ let target = winnerForRef(p, rr);
7810
+ if (target === void 0) {
7811
+ try {
7812
+ target = d.thunk();
7813
+ } catch {
7814
+ target = void 0;
7815
+ }
7816
+ }
7817
+ out = target === void 0 || triviaThroughRef(p, target, null) !== null || this.readsScope(target);
7818
+ break;
7819
+ }
7820
+ default:
7821
+ out = true;
7822
+ }
7823
+ this.readsScopeMemo.set(p, out);
7824
+ return out;
7825
+ }
7594
7826
  settings;
7595
7827
  /** Resolved once, HERE, at table-build time — never consulted at run time. */
7596
7828
  track;
@@ -7618,7 +7850,7 @@ var Encoder = class {
7618
7850
  let fs = { kind: "empty" };
7619
7851
  const rr = this.refResolver();
7620
7852
  for (let j = i + 1; j < parsers.length; j++) {
7621
- fs = union(fs, firstSetOf(parsers[j], /* @__PURE__ */ new Set(), rr, this.activeTrivia));
7853
+ fs = union(fs, firstSetOf(parsers[j], /* @__PURE__ */ new Set(), rr, void 0, false));
7622
7854
  }
7623
7855
  return this.charClass(fs);
7624
7856
  }
@@ -7696,7 +7928,7 @@ var Encoder = class {
7696
7928
  * simply parses it with the gate switched off at every choice arm that reaches
7697
7929
  * a hole.
7698
7930
  *
7699
- * Measured on jess's less grammar (`bench/jess/macro-program-diff.ts`): 195 of
7931
+ * Measured on jess's less grammar (with the one-off `macro-program-diff` bench, last at `4a5d0341`): 195 of
7700
7932
  * 562 reachable choice arms carried NO first set in the shipped artifact
7701
7933
  * against 103 of 540 in the runtime encode, and 10 dispatch sites lost the O(1)
7702
7934
  * `exclusive` piece (`assemble.ts:1758`) for the candidate-mask loop. With this
@@ -7786,7 +8018,7 @@ var Encoder = class {
7786
8018
  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;
7787
8019
  const rr = this.refResolver();
7788
8020
  if (matchesEmpty(p, /* @__PURE__ */ new Set(), rr)) return child;
7789
- const cls = this.charClass(firstSetOf(p, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
8021
+ const cls = this.charClass(firstSetOf(p, /* @__PURE__ */ new Set(), rr, this.activeTrivia, false));
7790
8022
  if (cls < 0) return child;
7791
8023
  const e = deriveExpected(p);
7792
8024
  return this.emit(OP_GATE, cls, child, this.expected(e.length > 0 ? e : [p._tag]));
@@ -7812,9 +8044,27 @@ var Encoder = class {
7812
8044
  * `{`, `Call` at `}`). The resolved set is the one a choice arm's gate uses,
7813
8045
  * over the final merged map.
7814
8046
  */
7815
- gateFirstSet(p) {
8047
+ /**
8048
+ * The arms whose first set under `gateTrivia()` is wider than under the
8049
+ * active trivia alone — a reference's installed trivia read through from a
8050
+ * region with none. Empty for construction-set (disjoint) choices, whose
8051
+ * sets do not depend on trivia.
8052
+ */
8053
+ wideArms(arms, wide, constructionSets, wideTrivia = this.gateTrivia(), base = this.activeTrivia) {
8054
+ if (constructionSets || wideTrivia === base) return [];
8055
+ const narrow = classifyFinalChoice(arms, this.refResolver(), base, false, false).firstSets;
8056
+ const out = [];
8057
+ for (let i = 0; i < arms.length; i++) {
8058
+ if (this.charClass(wide[i]) !== this.charClass(narrow[i])) out.push(i);
8059
+ }
8060
+ return out;
8061
+ }
8062
+ gateFirstSet(p, guarded, narrowed) {
7816
8063
  const cached = p._meta.firstSet;
7817
- return p._def.tag === "lazy" || cached.kind === "any" ? firstSetOf(p, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : cached;
8064
+ if (p._def.tag !== "lazy" && cached.kind !== "any") return cached;
8065
+ const rr = this.refResolver();
8066
+ if (guarded) return firstSetOf(p, /* @__PURE__ */ new Set(), rr, void 0, false);
8067
+ return firstSetOf(p, /* @__PURE__ */ new Set(), rr, this.gateTrivia(), narrowed && this.gateConsuming());
7818
8068
  }
7819
8069
  /**
7820
8070
  * Wrap one choice arm in its `autoNot` checks (`OP_REJECT`).
@@ -7846,6 +8096,135 @@ var Encoder = class {
7846
8096
  for (const w of words) this.code.push(w);
7847
8097
  return ip;
7848
8098
  }
8099
+ /**
8100
+ * An optional repeat item tests the item's class before it runs it (`OP_REP`
8101
+ * ip+7), wherever the item's own first-set gate would also run. So a sequence
8102
+ * item whose first term is a gate on that same class gets its own copy of the
8103
+ * sequence row with the gate's child as that term: the test runs once. The
8104
+ * shared row is left as it is for its other references.
8105
+ */
8106
+ withoutLeadGate(seq, cls) {
8107
+ const op = this.code[seq];
8108
+ if (op !== OP_SEQ && op !== OP_SEQV) return seq;
8109
+ const first = this.code[seq + 2];
8110
+ if (this.code[first] !== OP_GATE || this.code[first + 1] !== cls) return seq;
8111
+ const n = this.code[seq + 1];
8112
+ const width = 1 + 2 * n;
8113
+ const head = this.emitHead(op, width);
8114
+ for (let i = 1; i <= width; i++) this.code[head + i] = this.code[seq + i];
8115
+ this.code[head + 2] = this.code[first + 2];
8116
+ return head;
8117
+ }
8118
+ /**
8119
+ * An optional repeat item that opens with a GAP — a regex that may match
8120
+ * empty, or one that starts only with whitespace or a comment's `/` — and
8121
+ * then a term with a finite first set gets an `OP_GAPGATE` row
8122
+ * (`gap-gate.ts`): the repeat reads the gap once, ends where the character
8123
+ * after it opens neither that term nor an arm without the gap, and hands the
8124
+ * gap's end to the regex row. Anything the walk cannot prove keeps the item as
8125
+ * written.
8126
+ */
8127
+ gapGated(item, ip) {
8128
+ if (this.activeTrivia === void 0 && !this.triviaKnown) return ip;
8129
+ const lead2 = this.gapLead(item, this.activeTrivia, 0);
8130
+ if (lead2 === void 0) return ip;
8131
+ const cls = this.charClass(lead2.follow);
8132
+ if (cls < 0) return ip;
8133
+ const other = lead2.other.kind === "empty" ? -1 : this.charClass(lead2.other);
8134
+ if (lead2.other.kind !== "empty" && other < 0) return ip;
8135
+ const p = this.node(lead2.p).ip;
8136
+ if (this.code[p] !== OP_RX && this.code[p] !== OP_RX_TRACK) return ip;
8137
+ return this.emit(OP_GAPGATE, ip, p, cls, other);
8138
+ }
8139
+ /**
8140
+ * The gap an item opens with: the regex `p`, the first set `follow` of what
8141
+ * must come straight after it, and the first set `other` of choice arms that
8142
+ * do not open with `p`. Only through wrappers that consume nothing before
8143
+ * their body, a sequence's first term, and a choice's arms; the gap must sit
8144
+ * in a sequence with no trivia between its terms.
8145
+ */
8146
+ gapLead(c, trivia2, depth) {
8147
+ if (depth > 24) return void 0;
8148
+ const rr = this.refResolver();
8149
+ const d = c._def;
8150
+ switch (d.tag) {
8151
+ case "lazy": {
8152
+ let target = winnerForRef(c, rr);
8153
+ if (target === void 0) {
8154
+ try {
8155
+ target = d.thunk();
8156
+ } catch {
8157
+ return void 0;
8158
+ }
8159
+ }
8160
+ return this.gapLead(target, triviaThroughRef(c, target, trivia2), depth + 1);
8161
+ }
8162
+ case "node":
8163
+ case "leaf":
8164
+ case "label":
8165
+ case "attempt":
8166
+ return this.gapLead(d.parser, trivia2, depth + 1);
8167
+ case "field":
8168
+ return d.leadingTrivia === true ? void 0 : this.gapLead(d.parser, trivia2, depth + 1);
8169
+ case "grammar":
8170
+ return this.gapLead(d.parser, d.clearTrivia === true ? null : d.triviaParser ?? trivia2, depth + 1);
8171
+ case "sequence": {
8172
+ const [first, ...rest] = d.parsers;
8173
+ if (first === void 0) return void 0;
8174
+ if (first._def.tag !== "regex" || !isGap(first)) {
8175
+ return this.gapLead(first, trivia2, depth + 1);
8176
+ }
8177
+ if (trivia2 !== void 0 && trivia2 !== null || rest.length === 0) return void 0;
8178
+ let follow = empty();
8179
+ let closed = false;
8180
+ for (const t2 of rest) {
8181
+ const fs = firstSetOf(t2, /* @__PURE__ */ new Set(), rr, null);
8182
+ if (fs.kind === "any") return void 0;
8183
+ follow = union(follow, fs);
8184
+ if (!matchesEmpty(t2, /* @__PURE__ */ new Set(), rr)) {
8185
+ closed = true;
8186
+ break;
8187
+ }
8188
+ }
8189
+ return closed ? { p: first, follow, other: empty() } : void 0;
8190
+ }
8191
+ case "choice": {
8192
+ let p;
8193
+ let follow = empty();
8194
+ let other = empty();
8195
+ for (const arm of d.parsers) {
8196
+ const lead2 = this.gapLead(arm, trivia2, depth + 1);
8197
+ if (lead2 !== void 0 && (p === void 0 || lead2.p === p)) {
8198
+ p = lead2.p;
8199
+ follow = union(follow, lead2.follow);
8200
+ other = union(other, lead2.other);
8201
+ continue;
8202
+ }
8203
+ if (matchesEmpty(arm, /* @__PURE__ */ new Set(), rr)) return void 0;
8204
+ const fs = firstSetOf(arm, /* @__PURE__ */ new Set(), rr, trivia2 ?? null);
8205
+ if (fs.kind === "any") return void 0;
8206
+ other = union(other, fs);
8207
+ }
8208
+ return p === void 0 ? void 0 : { p, follow, other };
8209
+ }
8210
+ default:
8211
+ return void 0;
8212
+ }
8213
+ }
8214
+ /**
8215
+ * The class a strict list may end on — a character its separator `p` (row
8216
+ * `ip`) cannot start — without entering the separator, or −1.
8217
+ *
8218
+ * The test runs before every separator, to save the one that fails at the
8219
+ * list's end, so it is laid down only where entering the separator costs more
8220
+ * than the test: not on a terminal row, which is itself one character test.
8221
+ */
8222
+ sepGate(p, ip) {
8223
+ const op = this.code[ip];
8224
+ 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;
8225
+ if (matchesEmpty(p, /* @__PURE__ */ new Set(), this.refResolver())) return -1;
8226
+ return this.charClass(firstSetOf(p, /* @__PURE__ */ new Set(), this.refResolver(), this.gateTrivia(), this.gateConsuming()));
8227
+ }
7849
8228
  /** Reserve `n` operand slots to patch once children are laid out. */
7850
8229
  emitHead(op, n) {
7851
8230
  const ip = this.code.length;
@@ -7889,8 +8268,10 @@ var Encoder = class {
7889
8268
  }
7890
8269
  encodeRule(name, p) {
7891
8270
  this.activeTrivia = p._meta.grammarTrivia;
8271
+ this.triviaKnown = p._meta.grammarTrivia !== void 0;
7892
8272
  const body = this.node(p).ip;
7893
8273
  this.activeTrivia = void 0;
8274
+ this.triviaKnown = true;
7894
8275
  const gss = p._meta.grammarScanSkip;
7895
8276
  this.scanSkipOf.push(gss === void 0 ? -1 : this.scanSkipSlot(gss));
7896
8277
  const amb = p._meta.grammarTrivia;
@@ -7936,11 +8317,15 @@ var Encoder = class {
7936
8317
  * silently produced the ONE keyword `bc`.
7937
8318
  */
7938
8319
  scopedRef(p, target) {
8320
+ const runsUnder = triviaThroughRef(p, target, this.activeTrivia);
8321
+ if (this.activeTrivia !== void 0 || runsUnder === void 0 || runsUnder === null) return this.node(target).ip;
8322
+ const savedKnown = this.triviaKnown;
8323
+ this.activeTrivia = runsUnder;
8324
+ this.triviaKnown = true;
7939
8325
  const ip = this.node(target).ip;
7940
- const amb = p._meta.grammarTrivia ?? target._meta.grammarTrivia;
7941
- if (amb === void 0 || this.activeTrivia !== void 0) return ip;
7942
- if (!hasOwnTriviaBoundary(target)) return ip;
7943
- return this.emit(OP_SCOPE_PLAIN, this.triviaSlot(amb), ip);
8326
+ this.activeTrivia = void 0;
8327
+ this.triviaKnown = savedKnown;
8328
+ return this.emit(OP_SCOPE_PLAIN, this.triviaSlot(runsUnder), ip);
7944
8329
  }
7945
8330
  /**
7946
8331
  * The `OP_RGATE` operands of a choice arm that opens with `routed()`, or
@@ -7981,38 +8366,43 @@ var Encoder = class {
7981
8366
  if (matchesEmpty(next, /* @__PURE__ */ new Set(), rr) || mayCommitFailure(next, rr, this.commitFailureMemo)) return void 0;
7982
8367
  const nextDef = next._def;
7983
8368
  if (nextDef.tag !== "literal" && nextDef.tag !== "regex" && nextDef.tag !== "keywords" && nextDef.tag !== "label") return void 0;
7984
- const first = firstSetOf(next, /* @__PURE__ */ new Set(), rr, boundary);
8369
+ if (boundary === void 0 && !this.triviaKnown) return void 0;
8370
+ const first = firstSetOf(next, /* @__PURE__ */ new Set(), rr, boundary ?? null);
7985
8371
  const cls = this.charClass(boundary === void 0 ? first : union(first, firstSetOf(boundary, /* @__PURE__ */ new Set(), rr)));
7986
8372
  if (cls < 0) return void 0;
7987
- const miss = nextDef.tag === "label" ? deriveExpected(next) : directTerminalFailureExpected(nextDef);
7988
- return { cls, fx: this.expected(miss) };
8373
+ const fx = this.expected(nextDef.tag === "label" ? deriveExpected(next) : directTerminalFailureExpected(nextDef));
8374
+ const two = firstTwo(next);
8375
+ if (two === void 0) return { cls, fx };
8376
+ if (boundary !== void 0 && intersects(two.first, firstSetOf(boundary, /* @__PURE__ */ new Set(), rr))) return { cls, fx };
8377
+ const a = this.charClass(two.first);
8378
+ const b = this.charClass(two.second);
8379
+ return a < 0 || b < 0 ? { cls, fx } : { cls, fx, two: [a, b] };
7989
8380
  }
7990
8381
  node(p) {
7991
- const lazyRef = p._def.tag === "lazy";
7992
- const scopeKey = lazyRef ? this.activeTrivia : void 0;
7993
- const refMemo = this.refMemo;
7994
- const hit = lazyRef ? refMemo.get(p)?.get(scopeKey) : this.memo.get(p);
8382
+ const key = this.scopeKey(p);
8383
+ let byScope = this.memo.get(p);
8384
+ const hit = byScope?.get(key);
7995
8385
  if (hit !== void 0) return { ip: hit };
7996
- const inFlight = this.pending.get(p);
8386
+ let inFlightByScope = this.pending.get(p);
8387
+ const inFlight = inFlightByScope?.get(key);
7997
8388
  if (inFlight !== void 0) {
7998
8389
  const ip2 = this.emitHead(OP_RULE, 1);
7999
8390
  inFlight.push(ip2 + 1);
8000
8391
  return { ip: ip2 };
8001
8392
  }
8002
8393
  const patches = [];
8003
- this.pending.set(p, patches);
8394
+ if (inFlightByScope === void 0) {
8395
+ inFlightByScope = /* @__PURE__ */ new Map();
8396
+ this.pending.set(p, inFlightByScope);
8397
+ }
8398
+ inFlightByScope.set(key, patches);
8004
8399
  const ip = this.covWrap(this.encodeDef(p), this.plan?.rules.get(p), 0);
8005
- this.pending.delete(p);
8006
- if (lazyRef) {
8007
- let byScope = refMemo.get(p);
8008
- if (byScope === void 0) {
8009
- byScope = /* @__PURE__ */ new Map();
8010
- refMemo.set(p, byScope);
8011
- }
8012
- byScope.set(scopeKey, ip);
8013
- } else {
8014
- this.memo.set(p, ip);
8400
+ inFlightByScope.delete(key);
8401
+ if (byScope === void 0) {
8402
+ byScope = /* @__PURE__ */ new Map();
8403
+ this.memo.set(p, byScope);
8015
8404
  }
8405
+ byScope.set(key, ip);
8016
8406
  for (const slot of patches) {
8017
8407
  if (slot - 1 === ip) {
8018
8408
  const rule2 = p._ruleName;
@@ -8110,8 +8500,19 @@ var Encoder = class {
8110
8500
  }
8111
8501
  case "choice": {
8112
8502
  if (this.plan === void 0) {
8113
- const factored = factorSharedEnds(d);
8114
- if (factored !== void 0) return this.node(factored).ip;
8503
+ const shared = factorSharedEnds(d);
8504
+ if (shared !== void 0) return this.node(shared).ip;
8505
+ if (this.unfactoredChoice !== p) {
8506
+ const prefix = factorPrefixArms(d);
8507
+ if (prefix !== void 0) {
8508
+ const strict = this.node(prefix).ip;
8509
+ const saved = this.unfactoredChoice;
8510
+ this.unfactoredChoice = p;
8511
+ const tolerant = this.encodeDef(p);
8512
+ this.unfactoredChoice = saved;
8513
+ return this.emit(OP_STRICT, strict, tolerant);
8514
+ }
8515
+ }
8115
8516
  }
8116
8517
  if (d.strategy.tag === "greedyClassify") {
8117
8518
  const superIndex = d.strategy.superIndex;
@@ -8140,18 +8541,20 @@ var Encoder = class {
8140
8541
  const plain2 = (checks === null || checks === void 0 || checks.length === 0) && (gate === null || gate === void 0);
8141
8542
  const routedGate = plain2 ? this.routedArmGate(c) : void 0;
8142
8543
  const body = this.node(c).ip;
8143
- const inner = routedGate === void 0 ? body : this.emit(OP_RGATE, routedGate.cls, body, routedGate.fx);
8544
+ 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]);
8144
8545
  const ip = checks === null || checks === void 0 || checks.length === 0 ? inner : this.reject(inner, checks);
8145
8546
  const gated = gate === null || gate === void 0 ? ip : this.emit(OP_ARMGATE, this.fn(gate, d.gateSrcs?.[src] ?? null), ip, this.expected(deriveExpected(c)));
8146
8547
  return this.covWrap(gated, armCovIds?.[src], 1);
8147
8548
  });
8148
8549
  const rr = this.refResolver();
8149
- const finalChoice = classifyFinalChoice(arms, rr, this.activeTrivia);
8550
+ const constructionSets = d.disjoint && !factoredChoices.has(p);
8551
+ const finalChoice = classifyFinalChoice(arms, rr, this.gateTrivia(), false, constructionSets);
8552
+ const wideArms = this.wideArms(arms, finalChoice.firstSets, constructionSets);
8150
8553
  const transactionalFirst = arms.map((arm, i) => {
8151
8554
  if (!finalChoice.nullable[i]) return void 0;
8152
8555
  const def = arm._def;
8153
8556
  if (def.tag !== "attempt" || matchesEmpty(def.parser, /* @__PURE__ */ new Set(), rr)) return void 0;
8154
- return firstSetOf(def.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia);
8557
+ return firstSetOf(def.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia, false);
8155
8558
  });
8156
8559
  const gateFirst = finalChoice.firstSets.map((first, i) => transactionalFirst[i] ?? first);
8157
8560
  const classes = gateFirst.map((first, i) => finalChoice.nullable[i] && transactionalFirst[i] === void 0 ? -1 : this.charClass(first));
@@ -8169,6 +8572,9 @@ var Encoder = class {
8169
8572
  if (transactionalFirst[i] !== void 0) classes[i] = -1;
8170
8573
  }
8171
8574
  }
8575
+ if (wideArms.length > 0 && dispatchIsExclusive(classes, this.cc)) {
8576
+ for (const i of wideArms) classes[i] = -1;
8577
+ }
8172
8578
  if (d.gates.some((g) => g !== null && g !== void 0) && !dispatchIsExclusive(classes, this.cc)) {
8173
8579
  for (let k = 0; k < classes.length; k++) {
8174
8580
  const gate = d.gates[order[k]];
@@ -8202,27 +8608,34 @@ var Encoder = class {
8202
8608
  }
8203
8609
  case "many":
8204
8610
  case "oneOrMore": {
8205
- const child = this.node(d.parser).ip;
8206
8611
  const rr = this.refResolver();
8207
- const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.charClass(firstSetOf(d.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
8612
+ const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.charClass(this.gateFirstSet(d.parser, d.guarded === true, true));
8208
8613
  const op = d.valueUnused ? OP_REPV : OP_REP;
8209
8614
  const min = d.tag === "many" ? 0 : d.min;
8615
+ const item = min === 0 && itemClass >= 0 ? this.withoutLeadGate(this.node(d.parser).ip, itemClass) : this.node(d.parser).ip;
8616
+ const child = min === 0 && itemClass >= 0 ? this.gapGated(d.parser, item) : item;
8210
8617
  return this.emit(op, child, min, d.max ?? -1, -1, 0, this.expected(deriveExpected(d.parser)), itemClass);
8211
8618
  }
8212
8619
  case "sepBy": {
8213
8620
  const child = this.node(d.parser).ip;
8214
8621
  const sep = this.node(d.separator).ip;
8215
- const flags = (d.trailing === "allow" ? 1 : 0) | (d.keepSeparators === true ? 2 : 0) | (d.min >= 2 ? 4 : 0);
8216
- return this.emit(
8217
- OP_REP,
8622
+ const gate = this.sepGate(d.separator, sep);
8623
+ const itemSet = deriveExpected(d.parser);
8624
+ const itemFx = this.expected(itemSet.length > 0 ? itemSet : [d.parser._tag]);
8625
+ 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);
8626
+ const ops = [
8218
8627
  child,
8219
8628
  d.min,
8220
8629
  d.max ?? -1,
8221
8630
  sep,
8222
8631
  flags,
8223
- this.expected(deriveExpected(d.parser)),
8224
- this.charClass(firstSetOf(d.separator, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia))
8225
- );
8632
+ itemFx,
8633
+ // As 0.58.0: the interpreter resyncs on the separator itself
8634
+ // (`orSentinel`), which runs under the live trivia.
8635
+ this.charClass(firstSetOf(d.separator, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia, false))
8636
+ ];
8637
+ if (gate >= 0) ops.push(gate);
8638
+ return this.emit(OP_REP, ...ops);
8226
8639
  }
8227
8640
  case "optional": {
8228
8641
  const head = this.emit(OP_OPT, this.lookaheadGate(d.parser, this.node(d.parser).ip));
@@ -8231,7 +8644,7 @@ var Encoder = class {
8231
8644
  }
8232
8645
  case "transform": {
8233
8646
  const inner = d.parser._def;
8234
- if (inner.tag === "sequence" && !this.memo.has(d.parser) && !this.pending.has(d.parser)) {
8647
+ if (inner.tag === "sequence" && !this.memo.get(d.parser)?.has(this.scopeKey(d.parser)) && !this.pending.get(d.parser)?.has(this.scopeKey(d.parser))) {
8235
8648
  const kids = inner.parsers.map((c) => this.node(c).ip);
8236
8649
  const head = this.emitHead(OP_SEQX, 2 + 2 * kids.length);
8237
8650
  const projection = directArrayProjection(d.fn, d.fnSrc, kids.length);
@@ -8272,7 +8685,7 @@ var Encoder = class {
8272
8685
  d.tags === void 0 ? -1 : this.constant(d.tags)
8273
8686
  );
8274
8687
  if (matchesEmpty(d.parser, /* @__PURE__ */ new Set(), this.refResolver())) return body;
8275
- const fs = this.gateFirstSet(d.parser);
8688
+ const fs = this.gateFirstSet(d.parser, d.guarded === true, false);
8276
8689
  if (fs.kind === "any") return body;
8277
8690
  const cls = this.charClass(fs);
8278
8691
  if (cls < 0) return body;
@@ -8280,7 +8693,14 @@ var Encoder = class {
8280
8693
  return this.emit(OP_GATE, cls, body, this.expected(e.length > 0 ? e : [d.parser._tag]));
8281
8694
  }
8282
8695
  case "token": {
8283
- return this.emit(OP_TOKEN, this.node(d.parser).ip);
8696
+ const saved = this.activeTrivia;
8697
+ const savedKnown = this.triviaKnown;
8698
+ this.activeTrivia = void 0;
8699
+ this.triviaKnown = true;
8700
+ const body = this.node(d.parser).ip;
8701
+ this.activeTrivia = saved;
8702
+ this.triviaKnown = savedKnown;
8703
+ return this.emit(OP_TOKEN, body);
8284
8704
  }
8285
8705
  case "routed":
8286
8706
  return this.emit(OP_ROUTED, d.fallback === void 0 ? -1 : this.node(d.fallback).ip);
@@ -8336,7 +8756,7 @@ var Encoder = class {
8336
8756
  this.code[head + 2] = dspIdx;
8337
8757
  this.code[head + 3] = other;
8338
8758
  const otherRouted = d.otherwise === void 0 ? false : branchUsesRouted({ parser: d.otherwise, usesRouted: d.otherwiseUsesRouted === true });
8339
- this.code[head + 4] = otherRouted ? 1 : 0;
8759
+ this.code[head + 4] = (otherRouted ? 1 : 0) | (d.otherwiseCommit === false ? 2 : 0);
8340
8760
  this.code[head + 5] = arms.length;
8341
8761
  for (let i = 0; i < arms.length; i++) this.code[head + 6 + i] = arms[i];
8342
8762
  return head;
@@ -8391,7 +8811,7 @@ var Encoder = class {
8391
8811
  case "attempt": {
8392
8812
  const inner = this.emit(OP_ATTEMPT, this.node(d.parser).ip, d.contain === true ? 1 : 0);
8393
8813
  if (!this.failureNeedsRollback(d.parser, void 0)) this.failureRollbackCleanSites.add(inner);
8394
- const fs = this.gateFirstSet(d.parser);
8814
+ const fs = this.gateFirstSet(d.parser, d.guarded === true, false);
8395
8815
  if (fs.kind === "any" || matchesEmpty(d.parser, /* @__PURE__ */ new Set(), this.refResolver())) return inner;
8396
8816
  const cls = this.charClass(fs);
8397
8817
  if (cls < 0) return inner;
@@ -8417,10 +8837,17 @@ var Encoder = class {
8417
8837
  const op = cap ? OP_SCOPE_CAP : OP_SCOPE;
8418
8838
  const flags = (d.rootCapture === "opaque" ? 1 : 0) | (d.triviaParser !== void 0 && d.triviaParser._meta.rootTriviaClassified !== true && d.rootCapture !== "opaque" ? 2 : 0);
8419
8839
  const savedTrivia = this.activeTrivia;
8420
- if (d.clearTrivia === true) this.activeTrivia = void 0;
8421
- else if (d.triviaParser !== void 0) this.activeTrivia = d.triviaParser;
8840
+ const savedKnown = this.triviaKnown;
8841
+ if (d.clearTrivia === true) {
8842
+ this.activeTrivia = void 0;
8843
+ this.triviaKnown = true;
8844
+ } else if (d.triviaParser !== void 0) {
8845
+ this.activeTrivia = d.triviaParser;
8846
+ this.triviaKnown = true;
8847
+ }
8422
8848
  const bodyIp = this.node(d.parser).ip;
8423
8849
  this.activeTrivia = savedTrivia;
8850
+ this.triviaKnown = savedKnown;
8424
8851
  if (d.clearTrivia === true) return this.emit(op, -1, bodyIp, flags);
8425
8852
  if (d.triviaParser === void 0) {
8426
8853
  return cap || flags !== 0 ? this.emit(op, -1, bodyIp, flags) : bodyIp;
@@ -8461,6 +8888,7 @@ var Encoder = class {
8461
8888
  switch (this.code[ip]) {
8462
8889
  case OP_GATE:
8463
8890
  case OP_RGATE:
8891
+ case OP_RGATE2:
8464
8892
  return [ip + 2];
8465
8893
  case OP_RULE:
8466
8894
  case OP_OPT:
@@ -8470,6 +8898,7 @@ var Encoder = class {
8470
8898
  case OP_ATTEMPT:
8471
8899
  case OP_LABEL:
8472
8900
  case OP_COV:
8901
+ case OP_GAPGATE:
8473
8902
  return [ip + 1];
8474
8903
  case OP_SCOPE:
8475
8904
  case OP_SCOPE_CAP:
@@ -8506,6 +8935,8 @@ var Encoder = class {
8506
8935
  case OP_REP:
8507
8936
  case OP_REPV:
8508
8937
  return this.code[ip + 4] >= 0 ? [ip + 1, ip + 4] : [ip + 1];
8938
+ case OP_STRICT:
8939
+ return [ip + 1, ip + 2];
8509
8940
  default:
8510
8941
  return [];
8511
8942
  }
@@ -9663,6 +10094,7 @@ function childSlots(code, ip, out) {
9663
10094
  case OP_LABEL:
9664
10095
  case OP_COV:
9665
10096
  case OP_REJECT:
10097
+ case OP_GAPGATE:
9666
10098
  out.push(code[ip + 1]);
9667
10099
  return true;
9668
10100
  // Single child at `ip + 2`.
@@ -9679,8 +10111,13 @@ function childSlots(code, ip, out) {
9679
10111
  case OP_SOURCE_LEAF:
9680
10112
  case OP_ARMGATE:
9681
10113
  case OP_RGATE:
10114
+ case OP_RGATE2:
9682
10115
  out.push(code[ip + 2]);
9683
10116
  return true;
10117
+ // `STRICT strict tolerant` — both encodings are reachable; an assembly keeps one.
10118
+ case OP_STRICT:
10119
+ out.push(code[ip + 1], code[ip + 2]);
10120
+ return true;
9684
10121
  // `ROUTED fallback` — the operand is an offset or −1. The routed token itself is
9685
10122
  // data the enclosing `dispatch()` already consumed, so the only edge is the
9686
10123
  // fallback.
@@ -10128,7 +10565,8 @@ var WRAPPERS = /* @__PURE__ */ new Set([
10128
10565
  OP_WITHCTX,
10129
10566
  OP_RULE,
10130
10567
  OP_ATTEMPT,
10131
- OP_COV
10568
+ OP_COV,
10569
+ OP_GAPGATE
10132
10570
  ]);
10133
10571
  function rowNullable(code, k, ip, memo) {
10134
10572
  const hit = memo.get(ip);
@@ -10165,7 +10603,31 @@ function holdsGap(code, k, base, n, i, memo) {
10165
10603
  return !rowNullable(code, k, next, memo);
10166
10604
  }
10167
10605
 
10606
+ // src/table/gap-gate.ts
10607
+ function newGapSlot() {
10608
+ return { row: -1, at: -1, end: -1, input: void 0 };
10609
+ }
10610
+ function gapRows(code, roots) {
10611
+ const rows = /* @__PURE__ */ new Set();
10612
+ const seen = /* @__PURE__ */ new Set();
10613
+ const stack = [...roots];
10614
+ const kids = [];
10615
+ while (stack.length > 0) {
10616
+ const ip = stack.pop();
10617
+ if (seen.has(ip)) continue;
10618
+ seen.add(ip);
10619
+ if (code[ip] === OP_GAPGATE) rows.add(code[ip + 2]);
10620
+ kids.length = 0;
10621
+ childSlots(code, ip, kids);
10622
+ for (const c of kids) stack.push(c);
10623
+ }
10624
+ return rows;
10625
+ }
10626
+
10168
10627
  // src/table/emit-assembly.ts
10628
+ function gapRegexLowers(re) {
10629
+ return re instanceof RegExp && scanShapeFromRegex(re.source, re.flags) !== null;
10630
+ }
10169
10631
  var Unemittable = class extends Error {
10170
10632
  construct;
10171
10633
  constructor(construct) {
@@ -10184,7 +10646,6 @@ var EMITTED_PARAMS = [
10184
10646
  "CLS",
10185
10647
  "AFX",
10186
10648
  "TRIVIA",
10187
- "TRIVIALABELS",
10188
10649
  "TRIVIASCAN",
10189
10650
  "SCANS",
10190
10651
  "DISP",
@@ -10208,7 +10669,6 @@ var EMITTED_PARAMS = [
10208
10669
  "cstLeavesLen",
10209
10670
  "skipTriviaScanned",
10210
10671
  "needsDeferredTriviaCommit",
10211
- "scanTrivia",
10212
10672
  "advanceTrivia",
10213
10673
  "refuseUnclassifiedRootScope",
10214
10674
  "spanLines",
@@ -10228,34 +10688,31 @@ var EMITTED_PARAMS = [
10228
10688
  "orSentinel",
10229
10689
  "captureError",
10230
10690
  // Pure scalar terminal recognizers, indexed by the terminal's existing const
10231
- // operand. Appended for compatibility with older precompiled factories.
10691
+ // operand; selected lexical bodies; the adjacency test.
10232
10692
  "RECOG",
10233
- // Appended so precompiled factories produced by earlier runtimes keep every
10234
- // positional helper binding. Older factories ignore this trailing argument.
10235
- "commitTriviaScan",
10236
- "scanTriviaCompact",
10237
10693
  "LEX",
10238
- // Appended for old precompiled-factory ABI: old factories ignore it.
10239
10694
  "adjacencyHolds",
10240
- // Appended for selected composite lexical programs; old factories ignore it.
10695
+ // Selected composite lexical programs.
10241
10696
  "LEXPROG",
10242
- // Appended for trivia-attributing fields (`field(…, { leadingTrivia })`);
10243
- // old factories ignore them.
10697
+ // Trivia-attributing fields (`field(…, { leadingTrivia })`).
10244
10698
  "LEAD",
10245
10699
  "triviaFieldCapture",
10246
- // Appended for the deferred-commit trivia skip (`trivia-gap.ts`); old
10247
- // factories ignore it.
10700
+ // The deferred-commit trivia skip (`trivia-gap.ts`).
10248
10701
  "skipDeferredTrivia",
10249
- // Appended for the pass-through collapse (`run-support.ts`); old factories
10250
- // ignore it.
10702
+ // The pass-through collapse (`run-support.ts`).
10251
10703
  "collapseEmpty",
10252
- // Appended for constructor-built tree objects (`cst/plain.d.ts`): a `new` call has
10253
- // no allocation site, so V8 cannot pretenure them. Old factories ignore them.
10704
+ // Constructor-built tree objects (`cst/plain.d.ts`): a `new` call has no
10705
+ // allocation site, so V8 cannot pretenure them.
10254
10706
  "_SP",
10255
10707
  "_LF",
10256
10708
  "_ND",
10257
10709
  "_FC",
10258
- "_BUF"
10710
+ "_BUF",
10711
+ // The gap gate's slot (`gap-gate.ts`).
10712
+ "GAP"
10713
+ // Artifacts are version-locked (docs/design/artifact-format.md): a factory is
10714
+ // only ever called by the runtime that emitted it, so a name nothing emitted
10715
+ // reads is removed rather than kept for an older factory's positions.
10259
10716
  ];
10260
10717
  var RUNTIME_PRELUDE = `
10261
10718
  let _pfScan=null
@@ -10506,7 +10963,7 @@ function rebuildPools(cc, fx, disp, plan) {
10506
10963
  })
10507
10964
  };
10508
10965
  }
10509
- function emitAssemblySource(t2, prog, cfg, extraIps = [], staticBuild = false, select, scanSinkNeutral = false) {
10966
+ function emitAssemblySource(t2, prog, cfg, extraIps = [], staticBuild = false, select, scanSinkNeutral = false, gapNeutral = false) {
10510
10967
  const { code, k, fx, disp, dsp, triviaLabelled } = t2;
10511
10968
  const swapLegal = !cfg.trackLines;
10512
10969
  const hostCst = cfg.hostCst;
@@ -10514,6 +10971,7 @@ function emitAssemblySource(t2, prog, cfg, extraIps = [], staticBuild = false, s
10514
10971
  if (cfg.coverage) throw new Unemittable("a coverage assembly");
10515
10972
  const roots = [...Object.values(prog.rules), ...extraIps];
10516
10973
  const reachable = [...reachableSites(code, roots)];
10974
+ const GAP_ROWS = gapNeutral ? /* @__PURE__ */ new Set() : new Set([...gapRows(code, roots)].filter((row) => !gapRegexLowers(k[code[row + 1]])));
10517
10975
  const labels = computeSiteLabels(code, roots, hostCst);
10518
10976
  const scalarSpecs = /* @__PURE__ */ new Set();
10519
10977
  const nullable2 = /* @__PURE__ */ new Map();
@@ -10851,13 +11309,15 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
10851
11309
  function aliasOf(ip) {
10852
11310
  const op = code[ip];
10853
11311
  if (op === OP_GATE && (cfg.tolerant || cfg.probe)) return code[ip + 2];
10854
- if (op === OP_RULE) return code[ip + 1];
11312
+ if (op === OP_RULE || op === OP_GAPGATE) return code[ip + 1];
11313
+ if (op === OP_STRICT) return code[cfg.tolerant || cfg.probe ? ip + 2 : ip + 1];
10855
11314
  if (scopeIsIdentity(code, ip, labels.at(ip))) return code[ip + 2];
10856
11315
  return void 0;
10857
11316
  }
10858
11317
  function lower(ip, fname) {
10859
11318
  const op = code[ip];
10860
11319
  const head = `function ${fname}(input,pos,ctx){`;
11320
+ const outerHead = head;
10861
11321
  const L = labels.at(ip);
10862
11322
  const sinks = sinksAt(ip);
10863
11323
  const pushLeaf = L.buf && !cfg.trackLines ? L.raw === RAW_OMIT ? "_pushLeafNoRawBuf" : L.raw === RAW_CAPTURE ? "_pushLeafBuf" : "_pushLeaf" : "_pushLeaf";
@@ -10933,9 +11393,27 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
10933
11393
  case OP_RX_TRACK: {
10934
11394
  const xf = fxRef(code[ip + 2]);
10935
11395
  const track = op === OP_RX_TRACK;
11396
+ if (GAP_ROWS.has(ip)) {
11397
+ const re2 = hoist("gx", `K[${code[ip + 1]}]`);
11398
+ return `${outerHead}
11399
+ let e
11400
+ if(GAP.row===${ip}&&GAP.at===pos&&GAP.input===input)e=GAP.end
11401
+ else{${re2}.lastIndex=pos;e=${re2}.test(input)?${re2}.lastIndex:-1}
11402
+ if(e>=0){
11403
+ const v=input.slice(pos,e)
11404
+ ${captureLeaf("v")}
11405
+ ${track ? "_trackLines(ctx,input,e)\n" : ""}EC.e=e
11406
+ return v
11407
+ }
11408
+ ctx._fe=pos;ctx._fx=${xf}
11409
+ ${cfg.probe ? `failAt(ctx,${xf},pos)
11410
+ ` : ""}return FAIL
11411
+ }`;
11412
+ }
11413
+ const head2 = outerHead;
10936
11414
  if (!track && scalarSpecs.has(code[ip + 1])) {
10937
11415
  const recognize = recognizerRef(code[ip + 1]);
10938
- return `${head}
11416
+ return `${head2}
10939
11417
  const e=${recognize}(input,pos)
10940
11418
  if(e>=0){
10941
11419
  const v=input.slice(pos,e)
@@ -10948,11 +11426,11 @@ return FAIL
10948
11426
  }`;
10949
11427
  }
10950
11428
  const scanned = emitScan(code[ip + 1], xf, track, captureLeaf);
10951
- if (scanned !== void 0) return `${head}${scanned}}`;
11429
+ if (scanned !== void 0) return `${head2}${scanned}}`;
10952
11430
  const rxk = k[code[ip + 1]];
10953
11431
  if (!(rxk instanceof RegExp) || !rxk.sticky) throw new Unemittable("a non-sticky regex row");
10954
11432
  const re = kRef(code[ip + 1]);
10955
- return `${head}
11433
+ return `${head2}
10956
11434
  ${re}.lastIndex=pos
10957
11435
  if(${re}.test(input)){
10958
11436
  const e=${re}.lastIndex
@@ -10990,6 +11468,28 @@ return ${child}(input,pos,ctx)
10990
11468
  const it=ctx._routed
10991
11469
  if(it!==undefined&&it.span.start===pos&&!classHas(${cls},lead(input,it.span.end))){ctx._fe=it.span.end;ctx._fx=${xf};return FAIL}
10992
11470
  return ${child}(input,pos,ctx)
11471
+ }`;
11472
+ }
11473
+ case OP_RGATE2: {
11474
+ const child = link(code[ip + 2]);
11475
+ if (cfg.tolerant || cfg.probe) return `${head}
11476
+ return ${child}(input,pos,ctx)
11477
+ }`;
11478
+ const idxs = [code[ip + 1], code[ip + 4], code[ip + 5]];
11479
+ const ascii = idxs.every((i) => isAsciiOnlyClass(t2.cc[i]));
11480
+ const ref2 = (idx, tag) => {
11481
+ const ci = classes.push([t2.cc[idx]]) - 1;
11482
+ classPlan.push([idx]);
11483
+ return hoist(tag, `CLS[${ci}][0]`);
11484
+ };
11485
+ const [cls, first, second] = [ref2(idxs[0], "cl"), ref2(idxs[1], "c1"), ref2(idxs[2], "c2")];
11486
+ const has = (c, at) => ascii ? `${c}.ascii[${at}]===1` : `classHas(${c},${at})`;
11487
+ const xf = fxRef(code[ip + 3]);
11488
+ return `${head}
11489
+ const it=ctx._routed
11490
+ if(it!==undefined&&it.span.start===pos){const e=it.span.end;const c=${ascii ? "input.charCodeAt(e)" : "lead(input,e)"}
11491
+ 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}}
11492
+ return ${child}(input,pos,ctx)
10993
11493
  }`;
10994
11494
  }
10995
11495
  case OP_ARMGATE: {
@@ -11399,7 +11899,27 @@ if(ctx._fc===true)return FAIL
11399
11899
  const af=ctx._fx
11400
11900
  if(af!==undefined&&af.length>0){ctx._fe=pos;return FAIL}
11401
11901
  }
11402
- ctx._fe=pos;ctx._fx=${choiceFx}
11902
+ ${cfg.probe ? `if(arm<0){${arms.map((a) => `${a}(input,pos,ctx)`).join(";")}}
11903
+ ` : ""}ctx._fe=pos;ctx._fx=${choiceFx}
11904
+ return FAIL
11905
+ }`;
11906
+ }
11907
+ if (cfg.probe) {
11908
+ const p = tmp();
11909
+ return `${head}
11910
+ ${emitMark(p, L.buf, L.raw, sinks)}
11911
+ let acc
11912
+ let best=pos
11913
+ ${arms.map((arm) => `{ctx._fc=false
11914
+ const v=${arm}(input,pos,ctx)
11915
+ if(v!==FAIL)return v
11916
+ const at=ctx._fe??pos
11917
+ if(at>best){best=at;acc=undefined}
11918
+ if(at===best)acc=_accSet(ctx._fx,acc)
11919
+ if(ctx._fc===true){if(acc!==undefined)ctx._fx=acc;return FAIL}
11920
+ ${emitRollback(p, L.buf, L.raw, sinks)}
11921
+ }`).join("\n")}
11922
+ ctx._fe=pos;ctx._fx=acc??${choiceFx}
11403
11923
  return FAIL
11404
11924
  }`;
11405
11925
  }
@@ -11606,7 +12126,8 @@ return FAIL
11606
12126
  const selector = link(code[ip + 1]);
11607
12127
  const otherIp = code[ip + 3];
11608
12128
  const other = otherIp >= 0 ? link(otherIp) : void 0;
11609
- const otherRouted = code[ip + 4] === 1;
12129
+ const otherRouted = (code[ip + 4] & 1) === 1;
12130
+ const otherCommits = (code[ip + 4] & 2) === 0;
11610
12131
  const n = code[ip + 5];
11611
12132
  validateDispatchSpec(spec, n, code[ip + 4]);
11612
12133
  const armBase = ip + 6;
@@ -11660,7 +12181,7 @@ ${fallbackCase}
11660
12181
  }
11661
12182
  if(v===FAIL){
11662
12183
  ${emitRollback(m2, L.buf, L.raw, sinks)}
11663
- ctx._fc=true
12184
+ ${other !== void 0 && !otherCommits ? "if(arm!==undefined)" : ""}ctx._fc=true
11664
12185
  return FAIL
11665
12186
  }
11666
12187
  return [key,v]
@@ -11692,6 +12213,63 @@ return [key,v]
11692
12213
  }
11693
12214
  }
11694
12215
  const itemMiss = (at) => itemAscii ? `${itemCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${itemCls},lead(input,${at}))`;
12216
+ let sepCls;
12217
+ let sepAscii = false;
12218
+ if (!REC && sepIp >= 0 && (code[ip + 5] & 8) !== 0 && !leadingTriviaFieldAt(code, k, sepIp) && !rowNullable(code, k, sepIp, nullable2)) {
12219
+ const resolved = t2.cc[code[ip + 8]];
12220
+ const ci = classes.push([resolved]) - 1;
12221
+ classPlan.push([code[ip + 8]]);
12222
+ sepCls = hoist("rs", `CLS[${ci}][0]`);
12223
+ sepAscii = isAsciiOnlyClass(resolved);
12224
+ }
12225
+ const gapIp = !gapNeutral && sepIp < 0 && min === 0 && !REC && code[code[ip + 1]] === OP_GAPGATE ? code[ip + 1] : -1;
12226
+ let gapCode = (_rb) => "";
12227
+ if (gapIp >= 0) {
12228
+ const row = code[gapIp + 2];
12229
+ const rx = k[code[row + 1]];
12230
+ const shape = gapRegexLowers(rx) ? scanShapeFromRegex(rx.source, rx.flags) : null;
12231
+ const cls = (idx) => {
12232
+ const resolved = t2.cc[idx];
12233
+ const ci = classes.push([resolved]) - 1;
12234
+ classPlan.push([idx]);
12235
+ const ref2 = hoist("gc", `CLS[${ci}][0]`);
12236
+ return isAsciiOnlyClass(resolved) ? `${ref2}.ascii[input.charCodeAt(${"@"})]!==1` : `!classHas(${ref2},lead(input,${"@"}))`;
12237
+ };
12238
+ const follow = cls(code[gapIp + 3]).replaceAll("@", "gq");
12239
+ const other = code[gapIp + 4] >= 0 ? cls(code[gapIp + 4]).replaceAll("@", "itemStart") : "true";
12240
+ if (shape !== null) {
12241
+ let n = 0;
12242
+ const q2 = tmp();
12243
+ const m = emitShapeMatch(shape, "itemStart", (prefix = "_v") => `${q2}${prefix}${n++}`, "");
12244
+ gapCode = (rbCode) => `let gapped=false
12245
+ if(ctx._probe===undefined){
12246
+ ${m.setup.join("\n")}
12247
+ const gq=${m.ok}?${m.end}:-1
12248
+ if((gq<0||${follow})&&${other}){
12249
+ ${rbCode}
12250
+ break
12251
+ }
12252
+ }
12253
+ `;
12254
+ } else {
12255
+ const re = hoist("gr", `K[${code[row + 1]}]`);
12256
+ gapCode = (rbCode) => `let gapped=false
12257
+ if(ctx._probe===undefined){
12258
+ ${re}.lastIndex=itemStart
12259
+ const gq=${re}.test(input)?${re}.lastIndex:-1
12260
+ if((gq<0||${follow})&&${other}){
12261
+ ${rbCode}
12262
+ break
12263
+ }
12264
+ if(gq>=0){
12265
+ GAP.row=${row};GAP.at=itemStart;GAP.end=gq;GAP.input=input
12266
+ gapped=true
12267
+ }
12268
+ }
12269
+ `;
12270
+ }
12271
+ }
12272
+ const sepMiss = (at) => sepAscii ? `${sepCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${sepCls},lead(input,${at}))`;
11695
12273
  const skipBeforeFirst = sepIp < 0 && min === 0;
11696
12274
  const p = tmp();
11697
12275
  const via = sep !== void 0 ? "false" : min === 0 ? skipBeforeFirst ? "true" : "count>0" : `count>=${min}&&count>0`;
@@ -11729,7 +12307,7 @@ ${p}lg=ctx._triviaLog!==undefined?ctx._triviaLog.length:0
11729
12307
  ${p}rt=ctx._rootTriviaLog!==undefined?ctx._rootTriviaLog.length:0${markSinks}
11730
12308
  }`;
11731
12309
  const my = `${p}my`;
11732
- const recFx = REC ? fxRef(code[ip + 6]) : "";
12310
+ const recFx = !REC ? "" : (flags & 16) !== 0 ? "[]" : fxRef(code[ip + 6]);
11733
12311
  const sepSent = REC ? sentRef(code[ip + 7]) : "undefined";
11734
12312
  const recSent = sep === void 0 ? my : sepSent === "undefined" ? `orSentinel(${my},undefined)` : `orSentinel(${sepSent}??${my},${sepSent}===undefined?undefined:${my})`;
11735
12313
  const recGate = sep !== void 0 || min === 0 ? "" : `count>=${min}&&`;
@@ -11745,7 +12323,7 @@ continue
11745
12323
  return `${head}
11746
12324
  const out=${collect ? "[]" : "undefined"}
11747
12325
  ${knownTrivia === void 0 ? "const hasTrivia=ctx.trivia!==undefined\n" : ""}${L.buf ? "" : "const needMark=_rollbackNeeded(ctx)\n"}${REC ? `const ${my}=ctx._sync
11748
- ` : ""}${itemCls === void 0 ? "" : `const ${p}gate=ctx._probe===undefined
12326
+ ` : ""}${itemCls === void 0 && sepCls === void 0 ? "" : `const ${p}gate=ctx._probe===undefined
11749
12327
  `}let cur=pos
11750
12328
  let count=0
11751
12329
  for(;;){
@@ -11761,6 +12339,10 @@ const lb=cstLeavesLen(ctx)
11761
12339
  let sp=cur
11762
12340
  ${hasTrivia === "false" ? "" : hasTrivia === "true" ? `sp=${skip}(input,sp,ctx)
11763
12341
  ` : `if(hasTrivia)sp=${skip}(input,sp,ctx)
12342
+ `}${sepCls === void 0 ? "" : `if(count>=${min}&&${p}gate&&${sepMiss("sp")}){
12343
+ ${rb}
12344
+ break
12345
+ }
11764
12346
  `}ctx._fc=false
11765
12347
  ${sepIp >= 0 && leadingTriviaFieldAt(code, k, sepIp) ? "LEAD.at=cur\n" : ""}const sv=${sep}(input,sp,ctx)
11766
12348
  if(sv===FAIL){
@@ -11776,13 +12358,13 @@ itemStart=${hasTrivia === "false" ? "EC.e" : hasTrivia === "true" ? `${skip}(inp
11776
12358
  ${rb}
11777
12359
  ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
11778
12360
  }
11779
- ${itemCls === void 0 ? "" : `if(count>=${min}&&${p}gate&&${itemMiss("itemStart")}){
12361
+ ${itemCls === void 0 || sep === void 0 && hasTrivia === "false" ? "" : `if(count>=${min}&&${p}gate&&${sep === void 0 && hasTrivia === "hasTrivia" ? "hasTrivia&&" : ""}${itemMiss("itemStart")}){
11780
12362
  ${rb}
11781
12363
  ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
11782
12364
  }
11783
- `}ctx._fc=false
12365
+ `}${gapCode(rb)}ctx._fc=false
11784
12366
  const v=${child}(input,itemStart,ctx)
11785
- if(v===FAIL){
12367
+ ${gapIp >= 0 ? "if(gapped)GAP.at=-1\n" : ""}if(v===FAIL){
11786
12368
  ${REC ? `if(ctx._fc===true){
11787
12369
  ${rb}
11788
12370
  return FAIL
@@ -12754,6 +13336,9 @@ function clearOf(ranges, input, from, to) {
12754
13336
  return true;
12755
13337
  }
12756
13338
 
13339
+ // src/version.ts
13340
+ var PARSEMAN_VERSION = "0.60.1";
13341
+
12757
13342
  // src/table/assemble.ts
12758
13343
  import { PlainBuf as PlainBuf2, PlainField, PlainLeaf as PlainLeaf9, PlainNode as PlainNode2, PlainSpan as PlainSpan10 } from "../cst/plain.js";
12759
13344
 
@@ -12764,6 +13349,7 @@ function passChild(code, ip) {
12764
13349
  case OP_LABEL:
12765
13350
  case OP_COV:
12766
13351
  case OP_TOKEN:
13352
+ case OP_GAPGATE:
12767
13353
  return code[ip + 1];
12768
13354
  case OP_GATE:
12769
13355
  case OP_NODE:
@@ -13012,6 +13598,13 @@ function cfgKeyOf(hostCst, trackLines, tolerant, coverage, probe, hostReadsChild
13012
13598
  function cfgKey(c) {
13013
13599
  return cfgKeyOf(c.hostCst, c.trackLines, c.tolerant, c.coverage, c.probe, c.hostReadsChildren);
13014
13600
  }
13601
+ function assertFactoryArity(pre) {
13602
+ if (pre.arity === EMITTED_PARAMS.length) return;
13603
+ 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`;
13604
+ throw new Error(
13605
+ `parseman: this parser's printed assembly ${printed}. Reprint the parser with parseman ${PARSEMAN_VERSION}, or run it on the parseman version that printed it.`
13606
+ );
13607
+ }
13015
13608
  function assemble(t2, prog, cfg) {
13016
13609
  const { code, k, fns, cc, fx, disp, dsp, trivia: trivia2, triviaLabelled } = t2;
13017
13610
  const triviaScan = t2.triviaScan;
@@ -13046,6 +13639,8 @@ function assemble(t2, prog, cfg) {
13046
13639
  return recognize;
13047
13640
  }
13048
13641
  const labelExtraIps = assemblyExtraIps(prog);
13642
+ const GAP_ROWS = gapRows(code, [...Object.values(prog.rules), ...labelExtraIps]);
13643
+ const GAP = newGapSlot();
13049
13644
  const closureLabels = computeSiteLabels(
13050
13645
  code,
13051
13646
  [...Object.values(prog.rules), ...labelExtraIps],
@@ -13264,6 +13859,27 @@ function assemble(t2, prog, cfg) {
13264
13859
  return node2;
13265
13860
  };
13266
13861
  }
13862
+ function gapRegexPiece(row, re, xf, track) {
13863
+ return (input, pos, ctx) => {
13864
+ let e;
13865
+ if (GAP.row === row && GAP.at === pos && GAP.input === input) e = GAP.end;
13866
+ else {
13867
+ re.lastIndex = pos;
13868
+ e = re.test(input) ? re.lastIndex : -1;
13869
+ }
13870
+ if (e >= 0) {
13871
+ const v = input.slice(pos, e);
13872
+ if (cstCaptureActive(ctx)) pushLeaf(ctx, v, pos, e);
13873
+ if (track) trackLinesInto(ctx, input, e);
13874
+ EC.e = e;
13875
+ return v;
13876
+ }
13877
+ ctx._fe = pos;
13878
+ ctx._fx = xf;
13879
+ if (ctx._probe !== void 0) failAt(ctx, xf, pos);
13880
+ return FAIL;
13881
+ };
13882
+ }
13267
13883
  function passCollapseNode(child, build, readsRaw) {
13268
13884
  return (input, pos, ctx) => {
13269
13885
  const v = child(input, pos, ctx);
@@ -13666,7 +14282,7 @@ function assemble(t2, prog, cfg) {
13666
14282
  return next?.(key);
13667
14283
  };
13668
14284
  }
13669
- function dispatchBranch(child, usesRouted) {
14285
+ function dispatchBranch(child, usesRouted, commits = true) {
13670
14286
  if (!usesRouted) {
13671
14287
  return (input, _pos, selEnd, key, ctx) => {
13672
14288
  const need = markCst(ctx);
@@ -13680,7 +14296,7 @@ function assemble(t2, prog, cfg) {
13680
14296
  const v = child(input, selEnd, ctx);
13681
14297
  if (v === FAIL) {
13682
14298
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
13683
- ctx._fc = true;
14299
+ if (commits) ctx._fc = true;
13684
14300
  return FAIL;
13685
14301
  }
13686
14302
  return [key, v];
@@ -13698,7 +14314,7 @@ function assemble(t2, prog, cfg) {
13698
14314
  }
13699
14315
  if (v === FAIL) {
13700
14316
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
13701
- ctx._fc = true;
14317
+ if (commits) ctx._fc = true;
13702
14318
  return FAIL;
13703
14319
  }
13704
14320
  return [key, v];
@@ -13939,6 +14555,7 @@ function assemble(t2, prog, cfg) {
13939
14555
  case OP_RX: {
13940
14556
  const re = k[code[ip + 1]];
13941
14557
  const xf = fx[code[ip + 2]];
14558
+ if (GAP_ROWS.has(ip)) return gapRegexPiece(ip, re, xf, false);
13942
14559
  const shared = rawScalarSpecs.has(code[ip + 1]) ? scalarFor(ip) : void 0;
13943
14560
  if (shared !== void 0) {
13944
14561
  return (input, pos, ctx) => {
@@ -13973,6 +14590,7 @@ function assemble(t2, prog, cfg) {
13973
14590
  case OP_RX_TRACK: {
13974
14591
  const re = k[code[ip + 1]];
13975
14592
  const xf = fx[code[ip + 2]];
14593
+ if (GAP_ROWS.has(ip)) return gapRegexPiece(ip, re, xf, true);
13976
14594
  return (input, pos, ctx) => {
13977
14595
  re.lastIndex = pos;
13978
14596
  if (re.test(input)) {
@@ -14103,6 +14721,12 @@ function assemble(t2, prog, cfg) {
14103
14721
  return v;
14104
14722
  };
14105
14723
  }
14724
+ // Transparent: only the repeat holding it reads its operands.
14725
+ case OP_GAPGATE:
14726
+ return link(code[ip + 1]);
14727
+ // One encoding per assembly: strict, or the construct as written.
14728
+ case OP_STRICT:
14729
+ return link(code[cfg.tolerant || cfg.probe ? ip + 2 : ip + 1]);
14106
14730
  case OP_RULE:
14107
14731
  return link(code[ip + 1]);
14108
14732
  case OP_LIVE: {
@@ -15028,7 +15652,7 @@ function assemble(t2, prog, cfg) {
15028
15652
  if (table.exclusive) {
15029
15653
  const ascii = table.ascii;
15030
15654
  const hi = table.hi;
15031
- if (n === 2) {
15655
+ if (n === 2 && !cfg.probe) {
15032
15656
  const a0 = link(code[base]), a1 = link(code[base + 1]);
15033
15657
  return (input, pos, ctx) => {
15034
15658
  const c = lead(input, pos);
@@ -15060,7 +15684,7 @@ function assemble(t2, prog, cfg) {
15060
15684
  return FAIL;
15061
15685
  };
15062
15686
  }
15063
- if (n === 3) {
15687
+ if (n === 3 && !cfg.probe) {
15064
15688
  const a0 = link(code[base]), a1 = link(code[base + 1]), a2 = link(code[base + 2]);
15065
15689
  return (input, pos, ctx) => {
15066
15690
  const c = lead(input, pos);
@@ -15105,6 +15729,7 @@ function assemble(t2, prog, cfg) {
15105
15729
  );
15106
15730
  };
15107
15731
  const selected = bindExclusive(0);
15732
+ const probeArms = cfg.probe ? Array.from({ length: n }, (_, i) => link(code[base + i])) : void 0;
15108
15733
  return (input, pos, ctx) => {
15109
15734
  const c = lead(input, pos);
15110
15735
  let arm = -1;
@@ -15130,12 +15755,13 @@ function assemble(t2, prog, cfg) {
15130
15755
  return FAIL;
15131
15756
  }
15132
15757
  }
15758
+ if (arm < 0 && probeArms !== void 0) for (const a of probeArms) a(input, pos, ctx);
15133
15759
  ctx._fe = pos;
15134
15760
  ctx._fx = choiceFx;
15135
15761
  return FAIL;
15136
15762
  };
15137
15763
  }
15138
- const armCls = table.armCls;
15764
+ const armCls = cfg.probe ? table.armCls.map(() => null) : table.armCls;
15139
15765
  if (n === 2 || n === 3) {
15140
15766
  const a0 = link(code[base]), a1 = link(code[base + 1]);
15141
15767
  const a2 = n === 3 ? link(code[base + 2]) : void 0;
@@ -15555,6 +16181,43 @@ function assemble(t2, prog, cfg) {
15555
16181
  return child(input, pos, ctx);
15556
16182
  };
15557
16183
  }
16184
+ case OP_RGATE2: {
16185
+ const cls = cc[code[ip + 1]];
16186
+ const xf = fx[code[ip + 3]];
16187
+ const first = cc[code[ip + 4]];
16188
+ const second = cc[code[ip + 5]];
16189
+ const child = link(code[ip + 2]);
16190
+ if (cfg.tolerant || cfg.probe) return child;
16191
+ if (cls.hi.length === 0 && first.hi.length === 0 && second.hi.length === 0) {
16192
+ const a0 = cls.ascii, a1 = first.ascii, a2 = second.ascii;
16193
+ return (input, pos, ctx) => {
16194
+ const item = ctx._routed;
16195
+ if (item !== void 0 && item.span.start === pos) {
16196
+ const e = item.span.end;
16197
+ const c = input.charCodeAt(e);
16198
+ if (a0[c] !== 1 || a1[c] === 1 && a2[input.charCodeAt(e + 1)] !== 1) {
16199
+ ctx._fe = e;
16200
+ ctx._fx = xf;
16201
+ return FAIL;
16202
+ }
16203
+ }
16204
+ return child(input, pos, ctx);
16205
+ };
16206
+ }
16207
+ return (input, pos, ctx) => {
16208
+ const item = ctx._routed;
16209
+ if (item !== void 0 && item.span.start === pos) {
16210
+ const e = item.span.end;
16211
+ const c = lead(input, e);
16212
+ if (!classHas2(cls, c) || classHas2(first, c) && !classHas2(second, lead(input, e + 1))) {
16213
+ ctx._fe = e;
16214
+ ctx._fx = xf;
16215
+ return FAIL;
16216
+ }
16217
+ }
16218
+ return child(input, pos, ctx);
16219
+ };
16220
+ }
15558
16221
  case OP_ARMGATE: {
15559
16222
  const pred = fns[code[ip + 1]];
15560
16223
  const child = link(code[ip + 2]);
@@ -15622,10 +16285,18 @@ function assemble(t2, prog, cfg) {
15622
16285
  const itemClassIndex = sepIp < 0 ? code[ip + 7] : -1;
15623
16286
  const itemClass = !REC && itemClassIndex >= 0 ? cc[itemClassIndex] : void 0;
15624
16287
  const itemMayStart = itemClass === void 0 ? void 0 : (input, pos) => classHas2(itemClass, lead(input, pos));
16288
+ const sepClassIndex = sepIp >= 0 && !sepLeads && (flags & 8) !== 0 ? code[ip + 8] : -1;
16289
+ const sepClass = !REC && sepClassIndex >= 0 && !rowNullable(code, k, sepIp, NULLABLE) ? cc[sepClassIndex] : void 0;
16290
+ const gapIp = sepIp < 0 && min === 0 && !REC && code[code[ip + 1]] === OP_GAPGATE ? code[ip + 1] : -1;
16291
+ const gapRow = gapIp >= 0 ? code[gapIp + 2] : -1;
16292
+ const gapRe = gapIp >= 0 ? k[code[gapRow + 1]] : void 0;
16293
+ const gapFollow = gapIp >= 0 ? cc[code[gapIp + 3]] : void 0;
16294
+ const gapOther = gapIp >= 0 && code[gapIp + 4] >= 0 ? cc[code[gapIp + 4]] : void 0;
15625
16295
  const skipBeforeFirst = sepIp < 0 && min === 0;
15626
16296
  const itemClean = sepIp < 0 && CLEAN(code[ip + 1]) && !rowNullable(code, k, code[ip + 1], NULLABLE);
15627
16297
  if (REC) {
15628
16298
  const itemFx2 = fx[code[ip + 6]];
16299
+ const recFx = (flags & 16) !== 0 ? EMPTY_FX2 : itemFx2;
15629
16300
  const sepSent = sentinelFor(code[ip + 7]);
15630
16301
  return (input, pos, ctx) => {
15631
16302
  const out = collect ? [] : void 0;
@@ -15685,7 +16356,7 @@ function assemble(t2, prog, cfg) {
15685
16356
  itemStart,
15686
16357
  ctx,
15687
16358
  sep === void 0 ? mySync : orSentinel(sepSent ?? mySync, sepSent === void 0 ? void 0 : mySync),
15688
- itemFx2
16359
+ recFx
15689
16360
  );
15690
16361
  if (out !== void 0) out.push(rr.error);
15691
16362
  captureError(ctx, rr.error);
@@ -15723,6 +16394,7 @@ function assemble(t2, prog, cfg) {
15723
16394
  const markLater = needMark && (!itemClean || hasTrivia);
15724
16395
  const markFirst = needMark && (!itemClean || hasTrivia && skipBeforeFirst);
15725
16396
  const gateItems = itemMayStart !== void 0 && ctx._probe === void 0;
16397
+ const gateSep = sepClass !== void 0 && ctx._probe === void 0;
15726
16398
  let cur = pos;
15727
16399
  let count = 0;
15728
16400
  for (; ; ) {
@@ -15745,6 +16417,10 @@ function assemble(t2, prog, cfg) {
15745
16417
  const leavesBefore = cstLeavesLen(ctx);
15746
16418
  let sp = cur;
15747
16419
  if (hasTrivia) sp = skipTrivia(input, sp, ctx);
16420
+ if (count >= min && gateSep && !classHas2(sepClass, lead(input, sp))) {
16421
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
16422
+ break;
16423
+ }
15748
16424
  ctx._fc = false;
15749
16425
  if (sepLeads) LEAD.at = cur;
15750
16426
  const sv = sep(input, sp, ctx);
@@ -15764,13 +16440,30 @@ function assemble(t2, prog, cfg) {
15764
16440
  if (trailingAllowed && sepEnd >= 0) cur = sepEnd;
15765
16441
  break;
15766
16442
  }
15767
- if (count >= min && gateItems && !itemMayStart(input, itemStart)) {
16443
+ if (count >= min && gateItems && (sep !== void 0 || hasTrivia) && !itemMayStart(input, itemStart)) {
15768
16444
  if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
15769
16445
  if (trailingAllowed && sepEnd >= 0) cur = sepEnd;
15770
16446
  break;
15771
16447
  }
16448
+ let gapped = false;
16449
+ if (gapRe !== void 0 && ctx._probe === void 0) {
16450
+ gapRe.lastIndex = itemStart;
16451
+ const q2 = gapRe.test(input) ? gapRe.lastIndex : -1;
16452
+ if ((q2 < 0 || !classHas2(gapFollow, lead(input, q2))) && (gapOther === void 0 || !classHas2(gapOther, lead(input, itemStart)))) {
16453
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
16454
+ break;
16455
+ }
16456
+ if (q2 >= 0) {
16457
+ GAP.row = gapRow;
16458
+ GAP.at = itemStart;
16459
+ GAP.end = q2;
16460
+ GAP.input = input;
16461
+ gapped = true;
16462
+ }
16463
+ }
15772
16464
  ctx._fc = false;
15773
16465
  const v = child(input, itemStart, ctx);
16466
+ if (gapped) GAP.at = -1;
15774
16467
  if (v === FAIL) {
15775
16468
  if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
15776
16469
  if (committed3(ctx)) return FAIL;
@@ -15802,7 +16495,8 @@ function assemble(t2, prog, cfg) {
15802
16495
  const selector = link(code[ip + 1]);
15803
16496
  const otherIp = code[ip + 3];
15804
16497
  const other = otherIp >= 0 ? link(otherIp) : void 0;
15805
- const otherRouted = code[ip + 4] === 1;
16498
+ const otherRouted = (code[ip + 4] & 1) === 1;
16499
+ const otherCommits = (code[ip + 4] & 2) === 0;
15806
16500
  const n = code[ip + 5];
15807
16501
  const armBase = ip + 6;
15808
16502
  const byKey = spec.byKey;
@@ -15850,7 +16544,7 @@ function assemble(t2, prog, cfg) {
15850
16544
  );
15851
16545
  };
15852
16546
  const runArm = n === 0 ? void 0 : bindArms(0);
15853
- const runOther = other === void 0 ? void 0 : dispatchBranch(other, otherRouted);
16547
+ const runOther = other === void 0 ? void 0 : dispatchBranch(other, otherRouted, otherCommits);
15854
16548
  return (input, pos, ctx) => {
15855
16549
  const need = markCst(ctx);
15856
16550
  const mRaw = MRAW;
@@ -16111,6 +16805,7 @@ function assemble(t2, prog, cfg) {
16111
16805
  }
16112
16806
  const pre = cfg.hostCaptureTrivia === void 0 ? prog.asm?.find((a) => a.key === cfgKey(cfg)) : void 0;
16113
16807
  if (pre !== void 0) {
16808
+ assertFactoryArity(pre);
16114
16809
  const pools = rebuildPools(t2.cc, t2.fx, t2.disp, pre.plan);
16115
16810
  emitted = pre.factory(
16116
16811
  EC,
@@ -16122,7 +16817,6 @@ function assemble(t2, prog, cfg) {
16122
16817
  pools.classes,
16123
16818
  pools.armExpected,
16124
16819
  trivia2,
16125
- trivia2.map((tv) => tv?._meta.triviaKindLabels),
16126
16820
  triviaScan,
16127
16821
  scansArr,
16128
16822
  disp,
@@ -16146,7 +16840,6 @@ function assemble(t2, prog, cfg) {
16146
16840
  cstLeavesLen,
16147
16841
  skipTriviaScanned,
16148
16842
  needsDeferredTriviaCommit,
16149
- scanTrivia,
16150
16843
  advanceTrivia,
16151
16844
  refuseUnclassifiedRootScope,
16152
16845
  spanLines,
@@ -16160,8 +16853,6 @@ function assemble(t2, prog, cfg) {
16160
16853
  orSentinel,
16161
16854
  captureError,
16162
16855
  scalarRecognizers,
16163
- commitTriviaScan,
16164
- scanTriviaCompact,
16165
16856
  t2.lex,
16166
16857
  adjacencyHolds,
16167
16858
  t2.lexPrograms,
@@ -16173,7 +16864,8 @@ function assemble(t2, prog, cfg) {
16173
16864
  PlainLeaf9,
16174
16865
  PlainNode2,
16175
16866
  PlainField,
16176
- PlainBuf2
16867
+ PlainBuf2,
16868
+ GAP
16177
16869
  );
16178
16870
  emitReached = new Set(pre.reached);
16179
16871
  if (emitted.ext !== void 0) HYBRID = emitted;
@@ -16209,7 +16901,6 @@ function assemble(t2, prog, cfg) {
16209
16901
  em.classes,
16210
16902
  em.armExpected,
16211
16903
  trivia2,
16212
- trivia2.map((tv) => tv?._meta.triviaKindLabels),
16213
16904
  triviaScan,
16214
16905
  scansArr,
16215
16906
  disp,
@@ -16233,7 +16924,6 @@ function assemble(t2, prog, cfg) {
16233
16924
  cstLeavesLen,
16234
16925
  skipTriviaScanned,
16235
16926
  needsDeferredTriviaCommit,
16236
- scanTrivia,
16237
16927
  advanceTrivia,
16238
16928
  refuseUnclassifiedRootScope,
16239
16929
  spanLines,
@@ -16252,8 +16942,6 @@ function assemble(t2, prog, cfg) {
16252
16942
  orSentinel,
16253
16943
  captureError,
16254
16944
  scalarRecognizers,
16255
- commitTriviaScan,
16256
- scanTriviaCompact,
16257
16945
  t2.lex,
16258
16946
  adjacencyHolds,
16259
16947
  t2.lexPrograms,
@@ -16265,7 +16953,8 @@ function assemble(t2, prog, cfg) {
16265
16953
  PlainLeaf9,
16266
16954
  PlainNode2,
16267
16955
  PlainField,
16268
- PlainBuf2
16956
+ PlainBuf2,
16957
+ GAP
16269
16958
  );
16270
16959
  emitReached = em.reached;
16271
16960
  } catch (e) {
@@ -16431,7 +17120,7 @@ var AssemblyCache = class {
16431
17120
  * That is not a hole in G5, it is G5's own first clause: "quickly building the
16432
17121
  * grammar reference ON RUN START, making some swaps on rules or sub-rules, and
16433
17122
  * then the run actually runs with no logic branching for that option input"
16434
- * (`notes/TABLE-DRIVER.md`). This IS the run-start step. What the criterion
17123
+ * (`notes/history/TABLE-DRIVER.md`). This IS the run-start step. What the criterion
16435
17124
  * forbids is the second sentence, and past this call there is no option read
16436
17125
  * anywhere — `scripts/check-invariants.mjs` INV-6 decides that mechanically.
16437
17126
  *
@@ -16523,11 +17212,8 @@ function tableRules(source, metadataOrEntry = {}) {
16523
17212
  return map[entry];
16524
17213
  }
16525
17214
 
16526
- // src/version.ts
16527
- var PARSEMAN_VERSION = "0.59.3";
16528
-
16529
17215
  // src/table/emit.ts
16530
- 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`)});`;
17216
+ 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`)});`;
16531
17217
  function jsString(s) {
16532
17218
  return JSON.stringify(s);
16533
17219
  }
@@ -16602,7 +17288,7 @@ function emitAssemblies(prog, cfgs) {
16602
17288
  throw e;
16603
17289
  }
16604
17290
  const plan = em.plan;
16605
- out.push(`{key:${key},factory:function(${EMITTED_PARAMS.join(",")}){${FACTORY_ARITY_GUARD}${em.source}
17291
+ out.push(`{key:${key},arity:${EMITTED_PARAMS.length},factory:function(${EMITTED_PARAMS.join(",")}){${FACTORY_ARITY_GUARD}${em.source}
16606
17292
  },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(",")}]}`);
16607
17293
  }
16608
17294
  return [`a:[${out.join(",")}],`];
@@ -16878,7 +17564,7 @@ function payloadKey(p, d) {
16878
17564
  case "sepBy":
16879
17565
  return `sepBy\0${d.min}\0${d.max ?? ""}\0${d.trailing ?? ""}`;
16880
17566
  case "dispatch":
16881
- return `dispatch\0${d.cases.map((c) => JSON.stringify(c.keys)).join("")}\0${d.otherwise !== void 0}`;
17567
+ return `dispatch\0${d.cases.map((c) => JSON.stringify(c.keys)).join("")}\0${d.otherwise !== void 0}${d.otherwiseCommit === false ? "\0uncommitted" : ""}`;
16882
17568
  case "expect":
16883
17569
  return `expect\0${d.label ?? ""}\0${d.expected.join("")}`;
16884
17570
  case "scanTo":