parseman 0.50.0 → 0.50.3

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 (55) hide show
  1. package/README.md +14 -10
  2. package/dist/analysis/diagnostics.js +455 -226
  3. package/dist/analysis/diagnostics.js.map +3 -3
  4. package/dist/cli/index.js +445 -191
  5. package/dist/cli/index.js.map +3 -3
  6. package/dist/combinators/choice.d.ts.map +1 -1
  7. package/dist/combinators/grammar.d.ts.map +1 -1
  8. package/dist/combinators/keywords.d.ts.map +1 -1
  9. package/dist/combinators/literal.d.ts.map +1 -1
  10. package/dist/combinators/map.d.ts.map +1 -1
  11. package/dist/combinators/ref.d.ts.map +1 -1
  12. package/dist/combinators/regex.d.ts.map +1 -1
  13. package/dist/combinators/repeat.d.ts.map +1 -1
  14. package/dist/combinators/scalar.d.ts +6 -0
  15. package/dist/combinators/scalar.d.ts.map +1 -0
  16. package/dist/combinators/sequence.d.ts.map +1 -1
  17. package/dist/combinators/trivia-skip.d.ts +2 -0
  18. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  19. package/dist/compiler/token-capability.js +0 -3
  20. package/dist/compiler/token-capability.js.map +2 -2
  21. package/dist/index.js +446 -192
  22. package/dist/index.js.map +3 -3
  23. package/dist/language-service/index.js +68 -172
  24. package/dist/language-service/index.js.map +3 -3
  25. package/dist/parse-context.d.ts.map +1 -1
  26. package/dist/plugin/index.js +491 -306
  27. package/dist/plugin/index.js.map +3 -3
  28. package/dist/run/index.js +1 -0
  29. package/dist/run/index.js.map +1 -1
  30. package/dist/spec/index.js +399 -277
  31. package/dist/spec/index.js.map +3 -3
  32. package/dist/table/emit-assembly.d.ts.map +1 -1
  33. package/dist/table/encode.d.ts.map +1 -1
  34. package/dist/table/index.js +331 -188
  35. package/dist/table/index.js.map +3 -3
  36. package/dist/types.d.ts +8 -0
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/version.d.ts +1 -1
  39. package/package.json +4 -2
  40. package/src/combinators/choice.ts +34 -1
  41. package/src/combinators/grammar.ts +39 -0
  42. package/src/combinators/keywords.ts +14 -3
  43. package/src/combinators/literal.ts +26 -0
  44. package/src/combinators/map.ts +60 -0
  45. package/src/combinators/ref.ts +26 -7
  46. package/src/combinators/regex.ts +23 -0
  47. package/src/combinators/repeat.ts +109 -16
  48. package/src/combinators/scalar.ts +30 -0
  49. package/src/combinators/sequence.ts +76 -161
  50. package/src/combinators/trivia-skip.ts +14 -1
  51. package/src/parse-context.ts +1 -0
  52. package/src/table/emit-assembly.ts +39 -16
  53. package/src/table/encode.ts +30 -10
  54. package/src/types.ts +8 -0
  55. package/src/version.ts +1 -1
@@ -314,9 +314,43 @@ function hasOwnTriviaBoundary(p, depth = MAX_DEPTH) {
314
314
  }
315
315
  }
316
316
 
317
+ // src/combinators/scalar.ts
318
+ var SCALAR_TAG = /^(?:literal|regex|keywords|sequence|choice|many|oneOrMore|optional|transform|trivia|grammar|sepBy|lazy)$/;
319
+ function scalarOf(combinator) {
320
+ return combinator._parseScalar;
321
+ }
322
+ function scalarTree(parser2, seen) {
323
+ if (seen.has(parser2)) return true;
324
+ if (parser2._parseScalar === void 0) return false;
325
+ seen.add(parser2);
326
+ const def = parser2._def;
327
+ if (!SCALAR_TAG.test(def.tag)) return false;
328
+ if ("parsers" in def && !def.parsers.every((child) => scalarTree(child, seen))) return false;
329
+ if ("parser" in def && !scalarTree(def.parser, seen)) return false;
330
+ if ("separator" in def && !scalarTree(def.separator, seen)) return false;
331
+ return def.tag !== "lazy" || scalarTree(def.thunk(), seen);
332
+ }
333
+ function scalarRootOf(combinator) {
334
+ const scalar = combinator._parseScalar;
335
+ const def = combinator._def;
336
+ return scalar !== void 0 && def.tag === "grammar" && scalarTree(def.parser, /* @__PURE__ */ new Set()) ? scalar : void 0;
337
+ }
338
+
317
339
  // src/combinators/ref.ts
340
+ function withRuleTrivia(trivia2, ctx, run) {
341
+ const savedLabels = ctx.triviaKindLabels;
342
+ ctx.trivia = trivia2;
343
+ ctx.triviaKindLabels = trivia2._meta.triviaKindLabels;
344
+ try {
345
+ return run();
346
+ } finally {
347
+ ctx.trivia = void 0;
348
+ ctx.triviaKindLabels = savedLabels;
349
+ }
350
+ }
318
351
  function ref() {
319
352
  let resolved = null;
353
+ let resolvedScalar = null;
320
354
  let ownBoundary = false;
321
355
  const meta = {
322
356
  firstSet: any(),
@@ -333,25 +367,28 @@ function ref() {
333
367
  return resolved;
334
368
  }
335
369
  },
370
+ _parseScalar(input, pos, ctx) {
371
+ const scalar = resolvedScalar;
372
+ if (!scalar) throw new Error("ref<T>() used before .define() was called");
373
+ const gt = meta.grammarTrivia;
374
+ if (gt !== void 0 && ownBoundary && ctx.trivia === void 0) {
375
+ return withRuleTrivia(gt, ctx, () => scalar(input, pos, ctx));
376
+ }
377
+ return scalar(input, pos, ctx);
378
+ },
336
379
  parse(input, pos, ctx) {
337
- if (!resolved) throw new Error("ref<T>() used before .define() was called");
380
+ const parser2 = resolved;
381
+ if (!parser2) throw new Error("ref<T>() used before .define() was called");
338
382
  const gt = meta.grammarTrivia;
339
383
  if (gt !== void 0 && ownBoundary && ctx.trivia === void 0) {
340
- const savedLabels = ctx.triviaKindLabels;
341
- ctx.trivia = gt;
342
- ctx.triviaKindLabels = gt._meta.triviaKindLabels;
343
- try {
344
- return resolved.parse(input, pos, ctx);
345
- } finally {
346
- ctx.trivia = void 0;
347
- ctx.triviaKindLabels = savedLabels;
348
- }
384
+ return withRuleTrivia(gt, ctx, () => parser2.parse(input, pos, ctx));
349
385
  }
350
- return resolved.parse(input, pos, ctx);
386
+ return parser2.parse(input, pos, ctx);
351
387
  },
352
388
  define(p) {
353
389
  if (resolved) throw new Error("ref<T>() already defined");
354
390
  resolved = p;
391
+ resolvedScalar = scalarOf(p);
355
392
  ownBoundary = hasOwnTriviaBoundary(p);
356
393
  meta.firstSet = p._meta.firstSet;
357
394
  meta.canMatchNewline = p._meta.canMatchNewline;
@@ -737,6 +774,7 @@ function createParseContext() {
737
774
  _fe: void 0,
738
775
  _fx: void 0,
739
776
  _fc: void 0,
777
+ _sv: void 0,
740
778
  _probe: void 0,
741
779
  _cstChildren: void 0,
742
780
  _cstLeaves: void 0,
@@ -1574,6 +1612,24 @@ function parser(opts, root) {
1574
1612
  const captureKinds = clearTrivia ? void 0 : opts.captureTriviaKinds;
1575
1613
  const ownCaptureMask = captureKinds === void 0 ? void 0 : scopeLabels !== void 0 ? triviaKindMask(scopeLabels, captureKinds) : void 0;
1576
1614
  const inheritCaptureMask = captureKinds !== void 0 && scopeLabels === void 0;
1615
+ const rootScalar = scalarOf(root);
1616
+ const scalarEligible = opts.trackLines !== true && opts.recover !== true && !opaqueRootCapture && !forceCaptureTrivia && captureKinds === void 0;
1617
+ const parseScalar = scalarEligible ? (input, pos, ctx) => {
1618
+ const savedTrivia = ctx.trivia;
1619
+ const savedLabels = ctx.triviaKindLabels;
1620
+ if (clearTrivia) {
1621
+ ctx.trivia = void 0;
1622
+ ctx.triviaKindLabels = void 0;
1623
+ } else if (scopeTrivia !== void 0) {
1624
+ ctx.trivia = scopeTrivia;
1625
+ ctx.triviaKindLabels = scopeLabels;
1626
+ }
1627
+ const end = rootScalar(input, pos, ctx);
1628
+ ctx.trivia = savedTrivia;
1629
+ ctx.triviaKindLabels = savedLabels;
1630
+ return end;
1631
+ } : void 0;
1632
+ let publicScalar;
1577
1633
  const grammar = {
1578
1634
  _tag: "grammar",
1579
1635
  _meta: {
@@ -1597,7 +1653,20 @@ function parser(opts, root) {
1597
1653
  constructionTrackLines: trackLinesPolicy,
1598
1654
  ...captureKinds === void 0 ? {} : { constructionCaptureTriviaKinds: captureKinds }
1599
1655
  },
1656
+ _parseScalar: parseScalar,
1600
1657
  parse(input, pos, _ctx) {
1658
+ if (pos === void 0 && _ctx === void 0) {
1659
+ publicScalar ??= scalarRootOf(grammar) ?? null;
1660
+ if (publicScalar) {
1661
+ const ctx2 = createParseContext();
1662
+ const end = publicScalar(input, 0, ctx2);
1663
+ if (end < 0) {
1664
+ const at = ~end;
1665
+ return { ok: false, expected: ctx2._fx ?? [], span: { start: at, end: at } };
1666
+ }
1667
+ return { ok: true, value: ctx2._sv, span: { start: 0, end } };
1668
+ }
1669
+ }
1601
1670
  if (refuseUnclassified) refuseUnclassifiedRootScope(_ctx?._rootTriviaStrictScopes);
1602
1671
  const trackLines = trackLinesOf(_ctx);
1603
1672
  const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
@@ -1889,6 +1958,28 @@ function literal(value, opts = {}) {
1889
1958
  isTrivia: false
1890
1959
  };
1891
1960
  const canMatchNewline = meta.canMatchNewline;
1961
+ const parseScalar = !caseInsensitive && value.length === 1 ? (() => {
1962
+ const code = value.charCodeAt(0);
1963
+ return (input, pos, ctx) => {
1964
+ if (input.charCodeAt(pos) === code) {
1965
+ ctx._sv = value;
1966
+ return pos + 1;
1967
+ }
1968
+ ctx._fx = expected;
1969
+ return ~pos;
1970
+ };
1971
+ })() : (input, pos, ctx) => {
1972
+ const end = pos + value.length;
1973
+ if (end <= input.length) {
1974
+ const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
1975
+ if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
1976
+ ctx._sv = matchedValue;
1977
+ return end;
1978
+ }
1979
+ }
1980
+ ctx._fx = expected;
1981
+ return ~pos;
1982
+ };
1892
1983
  if (caseInsensitive) {
1893
1984
  const upper = value.toUpperCase();
1894
1985
  const lower = value.toLowerCase();
@@ -1929,6 +2020,7 @@ function literal(value, opts = {}) {
1929
2020
  _tag: "literal",
1930
2021
  _meta: meta,
1931
2022
  _def: { tag: "literal", value, caseInsensitive },
2023
+ _parseScalar: parseScalar,
1932
2024
  parse: parse2
1933
2025
  };
1934
2026
  }
@@ -2268,10 +2360,32 @@ function regex(pattern, flags = "") {
2268
2360
  const canMatchNewline = raw.canMatchNewline;
2269
2361
  const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
2270
2362
  const def = { tag: "regex", source, flags: resolvedFlags };
2363
+ const expected = directTerminalFailureExpected(def);
2364
+ const parseScalar = (input, pos, ctx) => {
2365
+ const scanEnd = scan?.(input, pos);
2366
+ if (scanEnd !== void 0) {
2367
+ if (scanEnd === null) {
2368
+ ctx._fx = expected;
2369
+ return ~pos;
2370
+ }
2371
+ ctx._sv = input.slice(pos, scanEnd);
2372
+ return scanEnd;
2373
+ }
2374
+ anchored.lastIndex = pos;
2375
+ const match = anchored.exec(input);
2376
+ if (match === null) {
2377
+ ctx._fx = expected;
2378
+ return ~pos;
2379
+ }
2380
+ ctx._sv = match[0];
2381
+ return pos + match[0].length;
2382
+ };
2271
2383
  return {
2272
2384
  _tag: "regex",
2273
2385
  _meta: meta,
2274
2386
  _def: def,
2387
+ _stickyRegex: anchored,
2388
+ _parseScalar: parseScalar,
2275
2389
  parse(input, pos, ctx) {
2276
2390
  const scanEnd = scan?.(input, pos);
2277
2391
  if (scanEnd !== void 0) {
@@ -2363,16 +2477,26 @@ function keywords(words, opts = {}) {
2363
2477
  caseInsensitive: opts.caseInsensitive ?? false,
2364
2478
  boundary: opts.boundary
2365
2479
  };
2480
+ const expected = directTerminalFailureExpected(def);
2481
+ const parseScalar = (input, pos, ctx) => {
2482
+ re.lastIndex = pos;
2483
+ const m = re.exec(input);
2484
+ if (m === null) {
2485
+ ctx._fx = expected;
2486
+ return ~pos;
2487
+ }
2488
+ ctx._sv = m[0];
2489
+ return pos + m[0].length;
2490
+ };
2366
2491
  return {
2367
2492
  _tag: "keywords",
2368
2493
  _meta: meta,
2369
2494
  _def: def,
2495
+ _parseScalar: parseScalar,
2370
2496
  parse(input, pos, ctx) {
2371
2497
  re.lastIndex = pos;
2372
2498
  const m = re.exec(input);
2373
- if (m === null || m.index !== pos) {
2374
- return failAt(ctx, directTerminalFailureExpected(def), pos);
2375
- }
2499
+ if (m === null) return failAt(ctx, directTerminalFailureExpected(def), pos);
2376
2500
  const value = m[0];
2377
2501
  const span = { start: pos, end: pos + value.length };
2378
2502
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
@@ -2403,6 +2527,9 @@ var fastScanner3;
2403
2527
  function needsDeferredTriviaCommit(ctx) {
2404
2528
  return ctx._triviaLog !== void 0 || ctx._rootTriviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
2405
2529
  }
2530
+ function needsTriviaRollback(ctx) {
2531
+ return ctx._cstBuf !== void 0 || ctx._cstLeaves !== void 0 || ctx._cstRawChildren !== void 0 || ctx._cstTriviaLog !== void 0 || ctx._fields !== void 0 || ctx._errors !== void 0 || ctx._triviaLog !== void 0 || ctx._rootTriviaLog !== void 0;
2532
+ }
2406
2533
  function saveTriviaMark(ctx) {
2407
2534
  const m = saveCstMark(ctx);
2408
2535
  return {
@@ -2519,7 +2646,8 @@ function advanceTrivia(input, cur, ctx) {
2519
2646
  const triviaP = ctx.trivia;
2520
2647
  if (!triviaP) return cur;
2521
2648
  if (!ctx.trackLines) {
2522
- const fast2 = fastTriviaScanner(triviaP);
2649
+ const classified = triviaP._meta.triviaScanner;
2650
+ const fast2 = classified === void 0 ? fastTriviaScanner(triviaP) : classified;
2523
2651
  if (fast2) return fast2(input, cur);
2524
2652
  if (ctx.triviaKindLabels) return skipWithLabels(input, cur, ctx);
2525
2653
  const tr2 = triviaP.parse(input, cur, createDetachedParseContext(ctx.trackLines, ctx.state));
@@ -3124,6 +3252,7 @@ function sequence(...parsers) {
3124
3252
  (adjacency ??= Array.from({ length: parsers.length }, () => null))[i] = a;
3125
3253
  }
3126
3254
  const hasAdjacency = adjacency !== null;
3255
+ const strictArity = !hasAdjacency && (parsers.length === 2 && !matchesEmpty(parsers[1]) || parsers.length === 3 && parsers.every((p) => !matchesEmpty(p))) ? parsers.length : 0;
3127
3256
  const meta = {
3128
3257
  // Union through the nullable prefix — a leading optional()/many() lets a later
3129
3258
  // term's first char start the sequence. Just `parsers[0]` under-approximates.
@@ -3132,165 +3261,96 @@ function sequence(...parsers) {
3132
3261
  isTrivia: false
3133
3262
  };
3134
3263
  const def = { tag: "sequence", parsers };
3135
- let followSentinels;
3136
- function parseTolerant(input, pos, ctx) {
3137
- followSentinels ??= parsers.map((_, i) => {
3138
- const fs3 = parsers.slice(i + 1).reduce(
3139
- (acc, p) => union(acc, firstSetOf(p)),
3140
- { kind: "empty" }
3141
- );
3142
- return firstSetSentinel(fs3);
3143
- });
3144
- const values = def.valueUnused ? void 0 : [];
3264
+ const scalarParsers = parsers.map(scalarOf);
3265
+ const parseScalar = (input, pos, ctx) => {
3145
3266
  let cur = pos;
3146
- const inheritedSync = ctx._sync;
3147
- try {
3148
- for (let i = 0; i < parsers.length; i++) {
3149
- ctx._sync = followSentinels[i] ?? inheritedSync;
3150
- const adj = adjacency === null ? null : adjacency[i];
3151
- if (adj) {
3152
- if (!holdsAdjacency(input, cur, ctx, adj)) return adjacencyFail(cur, adj);
3153
- if (values !== void 0) values.push(null);
3154
- continue;
3155
- }
3156
- if (ctx.trivia && i > 0) {
3157
- const mTlog = cstTlLen(ctx);
3158
- const mLog = ctx._triviaLog?.length ?? 0;
3159
- const mRootLog = ctx._rootTriviaLog?.length ?? 0;
3160
- let scanEnd;
3161
- if (needsDeferredTriviaCommit(ctx)) {
3162
- scanEnd = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
3163
- } else {
3164
- scanEnd = advanceTrivia(input, cur, ctx);
3165
- }
3166
- const scanTlog = cstTlLen(ctx);
3167
- const scanLog = ctx._triviaLog?.length ?? 0;
3168
- const scanRootLog = ctx._rootTriviaLog?.length ?? 0;
3169
- const result2 = parsers[i].parse(input, scanEnd, ctx);
3170
- if (!result2.ok) return result2;
3171
- if (result2.span.end > scanEnd) cur = result2.span.end;
3172
- else rollbackScannedTriviaAt(
3173
- ctx,
3174
- mTlog,
3175
- scanTlog,
3176
- mLog,
3177
- scanLog,
3178
- mRootLog,
3179
- scanRootLog
3180
- );
3181
- if (values !== void 0) values.push(result2.value);
3182
- continue;
3183
- }
3184
- const result = parsers[i].parse(input, cur, ctx);
3185
- if (!result.ok) return result;
3186
- if (values !== void 0) values.push(result.value);
3187
- cur = result.span.end;
3267
+ if (strictArity !== 0) {
3268
+ cur = scalarParsers[0](input, cur, ctx);
3269
+ if (cur < 0) return cur;
3270
+ const first = ctx._sv;
3271
+ if (ctx.trivia) cur = advanceTrivia(input, cur, ctx);
3272
+ cur = scalarParsers[1](input, cur, ctx);
3273
+ if (cur < 0) return cur;
3274
+ const second = ctx._sv;
3275
+ if (strictArity === 2) {
3276
+ ctx._sv = def.valueUnused ? void 0 : [first, second];
3277
+ return cur;
3188
3278
  }
3189
- } finally {
3190
- ctx._sync = inheritedSync;
3279
+ if (ctx.trivia) cur = advanceTrivia(input, cur, ctx);
3280
+ cur = scalarParsers[2](input, cur, ctx);
3281
+ if (cur < 0) return cur;
3282
+ ctx._sv = def.valueUnused ? void 0 : [first, second, ctx._sv];
3283
+ return cur;
3191
3284
  }
3192
- return {
3193
- ok: true,
3194
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
3195
- value: values ?? void 0,
3196
- span: { start: pos, end: cur }
3197
- };
3198
- }
3199
- function parseAdjacent(input, pos, ctx) {
3200
3285
  const values = def.valueUnused ? void 0 : [];
3201
- let cur = pos;
3202
- for (let i = 0; i < parsers.length; i++) {
3203
- const adj = adjacency[i];
3204
- if (adj) {
3205
- if (!holdsAdjacency(input, cur, ctx, adj)) return adjacencyFail(cur, adj);
3206
- if (values !== void 0) values.push(null);
3207
- continue;
3208
- }
3209
- if (ctx.trivia && i > 0) {
3210
- let scanEnd;
3211
- const mTlog = cstTlLen(ctx);
3212
- const mLog = ctx._triviaLog?.length ?? 0;
3213
- const mRootLog = ctx._rootTriviaLog?.length ?? 0;
3214
- if (needsDeferredTriviaCommit(ctx)) {
3215
- scanEnd = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
3216
- } else {
3217
- scanEnd = advanceTrivia(input, cur, ctx);
3218
- }
3219
- const scanTlog = cstTlLen(ctx);
3220
- const scanLog = ctx._triviaLog?.length ?? 0;
3221
- const scanRootLog = ctx._rootTriviaLog?.length ?? 0;
3222
- const result2 = parsers[i].parse(input, scanEnd, ctx);
3223
- if (!result2.ok) return result2;
3224
- if (result2.span.end > scanEnd) cur = result2.span.end;
3225
- else rollbackScannedTriviaAt(
3226
- ctx,
3227
- mTlog,
3228
- scanTlog,
3229
- mLog,
3230
- scanLog,
3231
- mRootLog,
3232
- scanRootLog
3233
- );
3234
- if (values !== void 0) values.push(result2.value);
3235
- continue;
3236
- }
3237
- const result = parsers[i].parse(input, cur, ctx);
3238
- if (!result.ok) return result;
3239
- if (values !== void 0) values.push(result.value);
3240
- cur = result.span.end;
3241
- }
3242
- return {
3243
- ok: true,
3244
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
3245
- value: values ?? void 0,
3246
- span: { start: pos, end: cur }
3247
- };
3248
- }
3286
+ for (let i = 0; i < scalarParsers.length; i++) {
3287
+ let termPos = cur;
3288
+ if (i > 0 && ctx.trivia) termPos = advanceTrivia(input, cur, ctx);
3289
+ const end = scalarParsers[i](input, termPos, ctx);
3290
+ if (end < 0) return end;
3291
+ if (values !== void 0) values.push(ctx._sv);
3292
+ if (end > termPos || termPos === cur) cur = end;
3293
+ }
3294
+ ctx._sv = values;
3295
+ return cur;
3296
+ };
3297
+ let followSentinels;
3249
3298
  return {
3250
3299
  _tag: "sequence",
3251
3300
  _meta: meta,
3252
3301
  _def: def,
3302
+ _parseScalar: hasAdjacency ? void 0 : parseScalar,
3253
3303
  parse(input, pos, ctx) {
3254
- if (ctx._tolerant) return parseTolerant(input, pos, ctx);
3255
- if (hasAdjacency) return parseAdjacent(input, pos, ctx);
3304
+ const tolerant = ctx._tolerant === true;
3305
+ if (tolerant) {
3306
+ followSentinels ??= parsers.map((_, i) => firstSetSentinel(
3307
+ parsers.slice(i + 1).reduce(
3308
+ (set, parser2) => union(set, firstSetOf(parser2)),
3309
+ { kind: "empty" }
3310
+ )
3311
+ ));
3312
+ }
3313
+ const inheritedSync = ctx._sync;
3314
+ const deferredTrivia = needsDeferredTriviaCommit(ctx);
3256
3315
  const values = def.valueUnused ? void 0 : [];
3257
3316
  let cur = pos;
3258
- for (let i = 0; i < parsers.length; i++) {
3259
- if (ctx.trivia && i > 0) {
3260
- let scanEnd;
3261
- const mTlog = cstTlLen(ctx);
3262
- const mLog = ctx._triviaLog?.length ?? 0;
3263
- const mRootLog = ctx._rootTriviaLog?.length ?? 0;
3264
- if (needsDeferredTriviaCommit(ctx)) {
3265
- scanEnd = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
3266
- } else {
3267
- scanEnd = advanceTrivia(input, cur, ctx);
3317
+ try {
3318
+ for (let i = 0; i < parsers.length; i++) {
3319
+ if (tolerant) ctx._sync = followSentinels[i] ?? inheritedSync;
3320
+ const assertion = adjacency?.[i];
3321
+ if (assertion) {
3322
+ if (!holdsAdjacency(input, cur, ctx, assertion)) return adjacencyFail(cur, assertion);
3323
+ if (values !== void 0) values.push(null);
3324
+ continue;
3268
3325
  }
3269
- const scanTlog = cstTlLen(ctx);
3270
- const scanLog = ctx._triviaLog?.length ?? 0;
3271
- const scanRootLog = ctx._rootTriviaLog?.length ?? 0;
3272
- const result2 = parsers[i].parse(input, scanEnd, ctx);
3273
- if (!result2.ok) return result2;
3274
- if (result2.span.end > scanEnd) {
3275
- cur = result2.span.end;
3276
- } else {
3326
+ let termPos = cur;
3327
+ const markTlog = deferredTrivia ? cstTlLen(ctx) : 0;
3328
+ const markLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
3329
+ const markRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
3330
+ if (ctx.trivia && i > 0) {
3331
+ termPos = deferredTrivia ? commitTriviaScan(scanTriviaCompact(input, cur, ctx)) : advanceTrivia(input, cur, ctx);
3332
+ }
3333
+ const scanTlog = deferredTrivia ? cstTlLen(ctx) : 0;
3334
+ const scanLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
3335
+ const scanRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
3336
+ const result = parsers[i].parse(input, termPos, ctx);
3337
+ if (!result.ok) return result;
3338
+ if (values !== void 0) values.push(result.value);
3339
+ if (result.span.end > termPos || termPos === cur) cur = result.span.end;
3340
+ else if (deferredTrivia) {
3277
3341
  rollbackScannedTriviaAt(
3278
3342
  ctx,
3279
- mTlog,
3343
+ markTlog,
3280
3344
  scanTlog,
3281
- mLog,
3345
+ markLog,
3282
3346
  scanLog,
3283
- mRootLog,
3347
+ markRootLog,
3284
3348
  scanRootLog
3285
3349
  );
3286
3350
  }
3287
- if (values !== void 0) values.push(result2.value);
3288
- continue;
3289
3351
  }
3290
- const result = parsers[i].parse(input, cur, ctx);
3291
- if (!result.ok) return result;
3292
- if (values !== void 0) values.push(result.value);
3293
- cur = result.span.end;
3352
+ } finally {
3353
+ if (tolerant) ctx._sync = inheritedSync;
3294
3354
  }
3295
3355
  return {
3296
3356
  ok: true,
@@ -3422,7 +3482,8 @@ function choice(...args) {
3422
3482
  disjoint
3423
3483
  };
3424
3484
  const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
3425
- const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
3485
+ const ordered = strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix";
3486
+ const autoNot = !disjoint && !hasGates && ordered ? computeAutoNot(parsers) : parsers.map(() => null);
3426
3487
  let greedyLitMap = null;
3427
3488
  let sortedParsers = null;
3428
3489
  const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
@@ -3436,6 +3497,37 @@ function choice(...args) {
3436
3497
  } else if (strategy?.tag === "literalsLongestFirst") {
3437
3498
  sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
3438
3499
  }
3500
+ const scalarParsers = parsers.map(scalarOf);
3501
+ const scalarEligible = !hasGates && parsers.every((p) => p._parseScalar !== void 0) && (disjoint || ordered);
3502
+ const scalarExpected = parsers.flatMap(deriveExpected);
3503
+ const parseScalar = (input, pos, ctx) => {
3504
+ if (disjoint) {
3505
+ const code = pos < input.length ? input.codePointAt(pos) : -1;
3506
+ let idx = code >= 0 && code < 128 ? asciiDispatch[code] : -1;
3507
+ if (idx < 0 && code >= 0) {
3508
+ for (let i = 0; i < parsers.length; i++) {
3509
+ if (inFirstSet(code, parsers[i]._meta.firstSet)) {
3510
+ idx = i;
3511
+ break;
3512
+ }
3513
+ }
3514
+ }
3515
+ if (idx >= 0) {
3516
+ const gate2 = gates[idx];
3517
+ if (!gate2 || gate2(ctx.state)) return scalarParsers[idx](input, pos, ctx);
3518
+ ctx._fx = deriveExpected(parsers[idx]);
3519
+ return ~pos;
3520
+ }
3521
+ } else {
3522
+ for (let i = 0; i < scalarParsers.length; i++) {
3523
+ if (gates[i] && !gates[i](ctx.state)) continue;
3524
+ const end = scalarParsers[i](input, pos, ctx);
3525
+ if (end >= 0 && (!autoNot[i] || !autoNotFires(input, end, autoNot[i]))) return end;
3526
+ }
3527
+ }
3528
+ ctx._fx = scalarExpected;
3529
+ return ~pos;
3530
+ };
3439
3531
  return {
3440
3532
  _tag: "choice",
3441
3533
  _meta: meta,
@@ -3447,6 +3539,7 @@ function choice(...args) {
3447
3539
  strategy: strategy ?? { tag: "firstMatch" },
3448
3540
  autoNot
3449
3541
  },
3542
+ _parseScalar: scalarEligible ? parseScalar : void 0,
3450
3543
  parse(input, pos, ctx) {
3451
3544
  const expected = [];
3452
3545
  let expectedAt = pos;
@@ -3978,8 +4071,8 @@ function attempt(parser2) {
3978
4071
  }
3979
4072
 
3980
4073
  // src/combinators/repeat.ts
3981
- function repItem(combinator, input, cur, ctx, guardable, mandatory) {
3982
- const mark = saveTriviaMark(ctx);
4074
+ function repItem(combinator, input, cur, ctx, guardable, mandatory, rollbackNeeded) {
4075
+ const mark = rollbackNeeded ? saveTriviaMark(ctx) : void 0;
3983
4076
  let pos = cur;
3984
4077
  if (ctx.trivia) {
3985
4078
  if (needsDeferredTriviaCommit(ctx)) {
@@ -3989,24 +4082,24 @@ function repItem(combinator, input, cur, ctx, guardable, mandatory) {
3989
4082
  }
3990
4083
  }
3991
4084
  if (pos >= input.length && !mandatory) {
3992
- rollbackTrivia(ctx, mark);
4085
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
3993
4086
  return "stop";
3994
4087
  }
3995
4088
  if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
3996
- rollbackTrivia(ctx, mark);
4089
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
3997
4090
  return "stop";
3998
4091
  }
3999
4092
  const result = combinator.parse(input, pos, ctx);
4000
4093
  if (!result.ok) {
4001
- rollbackTrivia(ctx, mark);
4094
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
4002
4095
  if (result.committed) return { fail: result, failPos: pos };
4003
4096
  return { fail: result, failPos: pos };
4004
4097
  }
4005
4098
  if (result.span.end === pos && !mandatory) {
4006
- rollbackTrivia(ctx, mark);
4099
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
4007
4100
  return "stop";
4008
4101
  }
4009
- return { value: result.value, end: result.span.end };
4102
+ return result;
4010
4103
  }
4011
4104
  function resolveBounds(what, opts) {
4012
4105
  const min = opts.min ?? 0;
@@ -4019,6 +4112,21 @@ function resolveBounds(what, opts) {
4019
4112
  if (max < min) bad(`max (${max}) is less than min (${min}) \u2014 the combinator could never succeed`);
4020
4113
  return { min, max };
4021
4114
  }
4115
+ function repeatScalarTail(combinator, child, input, cur, ctx, guardable, values, remaining) {
4116
+ while (cur < input.length && remaining > 0) {
4117
+ let itemPos = cur;
4118
+ if (ctx.trivia) itemPos = advanceTrivia(input, cur, ctx);
4119
+ if (itemPos >= input.length) break;
4120
+ if (guardable && !startsFirstSet(combinator, input, itemPos)) break;
4121
+ const end = child(input, itemPos, ctx);
4122
+ if (end <= itemPos) break;
4123
+ if (values !== void 0) values.push(ctx._sv);
4124
+ cur = end;
4125
+ remaining--;
4126
+ }
4127
+ ctx._sv = values;
4128
+ return cur;
4129
+ }
4022
4130
  function many(combinator, opts = {}) {
4023
4131
  const { min, max } = resolveBounds("many()", opts);
4024
4132
  if (min >= 1) return atLeast(combinator, min, max);
@@ -4030,17 +4138,21 @@ function many(combinator, opts = {}) {
4030
4138
  const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max } };
4031
4139
  let expected;
4032
4140
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
4141
+ const scalarChild = scalarOf(combinator);
4142
+ const parseScalar = (input, pos, ctx) => repeatScalarTail(combinator, scalarChild, input, pos, ctx, guardable, def.valueUnused ? void 0 : [], max);
4033
4143
  return {
4034
4144
  _tag: "many",
4035
4145
  _meta: meta,
4036
4146
  _def: def,
4147
+ _parseScalar: parseScalar,
4037
4148
  parse(input, pos, ctx) {
4038
4149
  const values = def.valueUnused ? void 0 : [];
4150
+ const rollbackNeeded = needsTriviaRollback(ctx);
4039
4151
  let cur = pos;
4040
4152
  let count = 0;
4041
4153
  while (cur < input.length) {
4042
4154
  if (count >= max) break;
4043
- const item = repItem(combinator, input, cur, ctx, guardable, false);
4155
+ const item = repItem(combinator, input, cur, ctx, guardable, false, rollbackNeeded);
4044
4156
  if (item === "stop") break;
4045
4157
  if ("fail" in item) {
4046
4158
  if (!item.fail.ok && item.fail.committed) return item.fail;
@@ -4057,7 +4169,7 @@ function many(combinator, opts = {}) {
4057
4169
  }
4058
4170
  if (values !== void 0) values.push(item.value);
4059
4171
  count++;
4060
- cur = item.end;
4172
+ cur = item.span.end;
4061
4173
  }
4062
4174
  return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
4063
4175
  }
@@ -4075,30 +4187,47 @@ function atLeast(combinator, min, max) {
4075
4187
  const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max } };
4076
4188
  let expected;
4077
4189
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
4190
+ const scalarChild = scalarOf(combinator);
4191
+ const parseScalar = (input, pos, ctx) => {
4192
+ let cur = scalarChild(input, pos, ctx);
4193
+ if (cur < 0) return cur;
4194
+ const values = def.valueUnused ? void 0 : [ctx._sv];
4195
+ for (let count = 1; count < min; count++) {
4196
+ let itemPos = cur;
4197
+ if (ctx.trivia) itemPos = advanceTrivia(input, cur, ctx);
4198
+ const end = scalarChild(input, itemPos, ctx);
4199
+ if (end < 0) return ~cur;
4200
+ cur = end;
4201
+ if (values !== void 0) values.push(ctx._sv);
4202
+ }
4203
+ return repeatScalarTail(combinator, scalarChild, input, cur, ctx, guardable, values, max - min);
4204
+ };
4078
4205
  return {
4079
4206
  _tag: "oneOrMore",
4080
4207
  _meta: meta,
4081
4208
  _def: def,
4209
+ _parseScalar: parseScalar,
4082
4210
  parse(input, pos, ctx) {
4083
4211
  const first = combinator.parse(input, pos, ctx);
4084
4212
  if (!first.ok) return first;
4085
4213
  const values = def.valueUnused ? void 0 : [first.value];
4214
+ const rollbackNeeded = needsTriviaRollback(ctx);
4086
4215
  let cur = first.span.end;
4087
4216
  let count = 1;
4088
4217
  while (count < min) {
4089
- const item = repItem(combinator, input, cur, ctx, guardable, true);
4218
+ const item = repItem(combinator, input, cur, ctx, guardable, true, rollbackNeeded);
4090
4219
  if (item === "stop" || "fail" in item) {
4091
4220
  if (item !== "stop" && !item.fail.ok && item.fail.committed) return item.fail;
4092
4221
  expected ??= deriveExpected(combinator);
4093
4222
  return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
4094
4223
  }
4095
4224
  if (values !== void 0) values.push(item.value);
4096
- cur = item.end;
4225
+ cur = item.span.end;
4097
4226
  count++;
4098
4227
  }
4099
4228
  while (cur < input.length) {
4100
4229
  if (count >= max) break;
4101
- const item = repItem(combinator, input, cur, ctx, guardable, false);
4230
+ const item = repItem(combinator, input, cur, ctx, guardable, false, rollbackNeeded);
4102
4231
  if (item === "stop") break;
4103
4232
  if ("fail" in item) {
4104
4233
  if (!item.fail.ok && item.fail.committed) return item.fail;
@@ -4115,7 +4244,7 @@ function atLeast(combinator, min, max) {
4115
4244
  }
4116
4245
  if (values !== void 0) values.push(item.value);
4117
4246
  count++;
4118
- cur = item.end;
4247
+ cur = item.span.end;
4119
4248
  }
4120
4249
  return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
4121
4250
  }
@@ -4128,10 +4257,22 @@ function optional(combinator) {
4128
4257
  isTrivia: false
4129
4258
  };
4130
4259
  const firstSetSkippable = !matchesEmpty(combinator);
4260
+ const scalarChild = scalarOf(combinator);
4261
+ const parseScalar = (input, pos, ctx) => {
4262
+ if (firstSetSkippable && !startsFirstSet(combinator, input, pos)) {
4263
+ ctx._sv = null;
4264
+ return pos;
4265
+ }
4266
+ const end = scalarChild(input, pos, ctx);
4267
+ if (end >= 0) return end;
4268
+ ctx._sv = null;
4269
+ return pos;
4270
+ };
4131
4271
  return {
4132
4272
  _tag: "optional",
4133
4273
  _meta: meta,
4134
4274
  _def: { tag: "optional", parser: combinator },
4275
+ _parseScalar: parseScalar,
4135
4276
  parse(input, pos, ctx) {
4136
4277
  if (firstSetSkippable && ctx._probe === void 0 && !startsFirstSet(combinator, input, pos)) {
4137
4278
  return { ok: true, value: null, span: { start: pos, end: pos } };
@@ -4168,6 +4309,43 @@ function sepBy(combinator, separatorArg, opts = {}) {
4168
4309
  expected ??= deriveExpected(combinator);
4169
4310
  return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: at, end: at } };
4170
4311
  };
4312
+ const scalarExpected = deriveExpected(combinator);
4313
+ const scalarChild = scalarOf(combinator);
4314
+ const scalarSeparator = scalarOf(separator);
4315
+ const parseScalar = (input, pos, ctx) => {
4316
+ let cur = scalarChild(input, pos, ctx);
4317
+ if (cur < 0) {
4318
+ if (min >= 1) {
4319
+ ctx._fx = scalarExpected.length > 0 ? scalarExpected : [combinator._tag];
4320
+ return ~pos;
4321
+ }
4322
+ ctx._sv = [];
4323
+ return pos;
4324
+ }
4325
+ const values = [ctx._sv];
4326
+ while (values.length < max && (cur < input.length || values.length < min)) {
4327
+ const beforeSeparator = cur;
4328
+ let separatorPos = cur;
4329
+ if (ctx.trivia) separatorPos = advanceTrivia(input, cur, ctx);
4330
+ const separatorEnd = scalarSeparator(input, separatorPos, ctx);
4331
+ if (separatorEnd < 0) break;
4332
+ let itemPos = separatorEnd;
4333
+ if (ctx.trivia) itemPos = advanceTrivia(input, itemPos, ctx);
4334
+ const itemEnd = scalarChild(input, itemPos, ctx);
4335
+ if (itemEnd < 0) {
4336
+ cur = trailing === "allow" ? separatorEnd : beforeSeparator;
4337
+ break;
4338
+ }
4339
+ values.push(ctx._sv);
4340
+ cur = itemEnd;
4341
+ }
4342
+ if (values.length < min) {
4343
+ ctx._fx = scalarExpected.length > 0 ? scalarExpected : [combinator._tag];
4344
+ return ~cur;
4345
+ }
4346
+ ctx._sv = values;
4347
+ return cur;
4348
+ };
4171
4349
  return {
4172
4350
  _tag: "sepBy",
4173
4351
  _meta: meta,
@@ -4183,6 +4361,7 @@ function sepBy(combinator, separatorArg, opts = {}) {
4183
4361
  // this option surface already; parity is held by test/parity, not by hope.
4184
4362
  ...keepSeparators ? { keepSeparators: true } : {}
4185
4363
  },
4364
+ _parseScalar: keepSeparators ? void 0 : parseScalar,
4186
4365
  parse(input, pos, ctx) {
4187
4366
  const first = combinator.parse(input, pos, ctx);
4188
4367
  const values = [];
@@ -4798,11 +4977,54 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
4798
4977
  }
4799
4978
 
4800
4979
  // src/combinators/map.ts
4980
+ var JSON_STRING_BODY = String.raw`(?:[^"\\]|\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4}))*`;
4801
4981
  function transform(combinator, fn) {
4982
+ const observesSpan = fn.length > 1;
4983
+ const sequenceDef = combinator._def.tag === "sequence" ? combinator._def : void 0;
4984
+ const terms = sequenceDef?.parsers;
4985
+ const inner = terms?.[1];
4986
+ const fused = terms?.length === 3 && terms[0]._def.tag === "literal" && terms[0]._def.value === '"' && !terms[0]._def.caseInsensitive && inner._def.tag === "regex" && inner._def.source === JSON_STRING_BODY && inner._def.flags === "" && terms[2]._def.tag === "literal" && terms[2]._def.value === '"' && !terms[2]._def.caseInsensitive && inner._stickyRegex !== void 0;
4987
+ const child = scalarOf(combinator);
4988
+ const parseScalar = fused ? (() => {
4989
+ const body = inner._stickyRegex;
4990
+ const tuple = ['"', "", '"'];
4991
+ const expected = [JSON.stringify('"')];
4992
+ return (input, pos, ctx) => {
4993
+ if (ctx.trivia !== void 0) {
4994
+ const end2 = child(input, pos, ctx);
4995
+ if (end2 < 0) return end2;
4996
+ ctx._sv = observesSpan ? fn(ctx._sv, { start: pos, end: end2 }) : fn(ctx._sv);
4997
+ return end2;
4998
+ }
4999
+ if (input.charCodeAt(pos) !== 34) {
5000
+ ctx._fx = expected;
5001
+ return ~pos;
5002
+ }
5003
+ body.lastIndex = pos + 1;
5004
+ const match = body.exec(input);
5005
+ const close = pos + 1 + match[0].length;
5006
+ if (input.charCodeAt(close) !== 34) {
5007
+ ctx._fx = expected;
5008
+ return ~close;
5009
+ }
5010
+ tuple[1] = match[0];
5011
+ const end = close + 1;
5012
+ ctx._sv = observesSpan ? fn(tuple, { start: pos, end }) : fn(tuple);
5013
+ return end;
5014
+ };
5015
+ })() : /* @__PURE__ */ (() => {
5016
+ return (input, pos, ctx) => {
5017
+ const end = child(input, pos, ctx);
5018
+ if (end < 0) return end;
5019
+ ctx._sv = observesSpan ? fn(ctx._sv, { start: pos, end }) : fn(ctx._sv);
5020
+ return end;
5021
+ };
5022
+ })();
4802
5023
  return {
4803
5024
  _tag: "transform",
4804
5025
  _meta: combinator._meta,
4805
5026
  _def: { tag: "transform", parser: combinator, fn },
5027
+ _parseScalar: parseScalar,
4806
5028
  parse(input, pos, ctx) {
4807
5029
  const result = combinator.parse(input, pos, ctx);
4808
5030
  if (!result.ok) return result;
@@ -4817,9 +5039,11 @@ function trivia(combinator) {
4817
5039
  _meta: {
4818
5040
  ...combinator._meta,
4819
5041
  isTrivia: true,
5042
+ triviaScanner: fastTriviaScanner(combinator),
4820
5043
  ...kindLabels ? { triviaKindLabels: kindLabels } : {}
4821
5044
  },
4822
5045
  _def: { tag: "trivia", parser: combinator },
5046
+ _parseScalar: scalarOf(combinator),
4823
5047
  parse: combinator.parse.bind(combinator)
4824
5048
  };
4825
5049
  }
@@ -5774,7 +5998,7 @@ function buildLexProgramRunner(spec, matchers, classes, expected) {
5774
5998
  if (!Number.isInteger(spec[2]) || spec[2] < 0) {
5775
5999
  throw new TypeError("table balanced lexical program has an invalid scan reference");
5776
6000
  }
5777
- const run3 = (_input, _pos, ctx, scan) => {
6001
+ const run = (_input, _pos, ctx, scan) => {
5778
6002
  if (scan === void 0) throw new TypeError("table balanced lexical program lost its scan body");
5779
6003
  const result = scan.parse(_input, _pos, ctx);
5780
6004
  if (!result.ok) {
@@ -5784,7 +6008,7 @@ function buildLexProgramRunner(spec, matchers, classes, expected) {
5784
6008
  }
5785
6009
  return result.span.end;
5786
6010
  };
5787
- return Object.assign(run3, { scan: spec[2] });
6011
+ return Object.assign(run, { scan: spec[2] });
5788
6012
  }
5789
6013
  if (spec[0] === 2) return fixedTreeRunner(spec.slice(2), matchers, classes, expected);
5790
6014
  if (spec[0] === 1) {
@@ -6540,6 +6764,22 @@ var Encoder = class {
6540
6764
  choiceRollbackMasks = /* @__PURE__ */ new Map();
6541
6765
  nonCommittingChoiceSites = /* @__PURE__ */ new Set();
6542
6766
  failureRollbackCleanSites = /* @__PURE__ */ new Set();
6767
+ /**
6768
+ * `hasSideWriter` memo for `failureNeedsRollback`, shared across every call
6769
+ * for the lifetime of this `Encoder`. A cached entry is written only when
6770
+ * `complete` is true, which means the answer is a path-independent fact
6771
+ * about that combinator node — it does not depend on which root's traversal
6772
+ * asked, only on the node's own reachable subgraph. Scoping the memo per call
6773
+ * (as a local inside `failureNeedsRollback`) was correct but discarded that
6774
+ * fact after every arm, so a grammar with many ordered-choice arms / optional
6775
+ * / attempt sites re-walked the SAME shared rule graph from scratch for each
6776
+ * site — O(sites × graph size) instead of O(graph size). Measured on the
6777
+ * scss-parser grammar (composed CSS-base superset, the deepest/largest of the
6778
+ * four grammars): 47-50s for one `compileRuleMap` with the per-call memo,
6779
+ * versus the whole macro transform completing in under a second once this
6780
+ * memo is hoisted here.
6781
+ */
6782
+ sideWriterMemo = /* @__PURE__ */ new Map();
6543
6783
  labels = void 0;
6544
6784
  /** Trivia table in scope at the row being encoded — codegen's `ctx.activeTrivia`. */
6545
6785
  activeTrivia = void 0;
@@ -6965,13 +7205,13 @@ var Encoder = class {
6965
7205
  const refName = p._ruleName;
6966
7206
  const winner = refName === void 0 ? void 0 : this.winners?.[refName];
6967
7207
  const root = winner !== void 0 && !winnerWrapsReference2(winner, p) ? winner : p;
6968
- const sideWriterMemo = /* @__PURE__ */ new Map();
6969
- const hasSideWriter = (current, seen = /* @__PURE__ */ new Set()) => {
7208
+ const sideWriterMemo = this.sideWriterMemo;
7209
+ const seen = /* @__PURE__ */ new Set();
7210
+ const hasSideWriter = (current) => {
6970
7211
  const cached = sideWriterMemo.get(current);
6971
7212
  if (cached !== void 0) return { found: cached, complete: true };
6972
7213
  if (seen.has(current)) return { found: false, complete: false };
6973
- const next = new Set(seen);
6974
- next.add(current);
7214
+ seen.add(current);
6975
7215
  const def = current._def;
6976
7216
  let result;
6977
7217
  switch (def.tag) {
@@ -6986,11 +7226,11 @@ var Encoder = class {
6986
7226
  const name = current._ruleName;
6987
7227
  const resolvedWinner = name === void 0 ? void 0 : this.winners?.[name];
6988
7228
  if (resolvedWinner !== void 0 && !winnerWrapsReference2(resolvedWinner, current)) {
6989
- result = hasSideWriter(resolvedWinner, next);
7229
+ result = hasSideWriter(resolvedWinner);
6990
7230
  break;
6991
7231
  }
6992
7232
  try {
6993
- result = hasSideWriter(def.thunk(), next);
7233
+ result = hasSideWriter(def.thunk());
6994
7234
  } catch {
6995
7235
  result = { found: true, complete: true };
6996
7236
  }
@@ -7000,7 +7240,7 @@ var Encoder = class {
7000
7240
  let complete = true;
7001
7241
  result = { found: false, complete: true };
7002
7242
  for (const child of childrenOf2(def)) {
7003
- const childResult = hasSideWriter(child, next);
7243
+ const childResult = hasSideWriter(child);
7004
7244
  if (childResult.found) {
7005
7245
  result = { found: true, complete: true };
7006
7246
  break;
@@ -7010,6 +7250,7 @@ var Encoder = class {
7010
7250
  if (!result.found) result = { found: false, complete };
7011
7251
  }
7012
7252
  }
7253
+ seen.delete(current);
7013
7254
  if (result.found || result.complete) sideWriterMemo.set(current, result.found);
7014
7255
  return result;
7015
7256
  };
@@ -7783,15 +8024,15 @@ function parseStringShape(source) {
7783
8024
  if (source.length < 3 || source[source.length - 1] !== q2) return null;
7784
8025
  const inner = source.slice(1, -1);
7785
8026
  const CLS = String.raw`\[\^((?:\\.|[^\]])+)\]`;
7786
- const ESC2 = String.raw`\\\\(\.|\[\\s\\S\]|\[\\S\\s\])`;
8027
+ const ESC = String.raw`\\\\(\.|\[\\s\\S\]|\[\\S\\s\])`;
7787
8028
  let body;
7788
8029
  let esc;
7789
- let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${ESC2}\\)\\*$`).exec(inner);
8030
+ let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${ESC}\\)\\*$`).exec(inner);
7790
8031
  if (m) {
7791
8032
  body = m[1];
7792
8033
  esc = m[2];
7793
8034
  } else {
7794
- m = new RegExp(`^\\((?:\\?:)?${ESC2}\\|${CLS}\\)\\*$`).exec(inner);
8035
+ m = new RegExp(`^\\((?:\\?:)?${ESC}\\|${CLS}\\)\\*$`).exec(inner);
7795
8036
  if (m) {
7796
8037
  esc = m[1];
7797
8038
  body = m[2];
@@ -9354,6 +9595,9 @@ if(ctx._rootTriviaLog!==undefined&&ctx._rootTriviaLog.length!==rt)ctx._rootTrivi
9354
9595
  function q(s) {
9355
9596
  return JSON.stringify(s);
9356
9597
  }
9598
+ function isAsciiOnlyClass(cls) {
9599
+ return cls !== null && cls !== void 0 && cls.hi.length === 0;
9600
+ }
9357
9601
  var NO_SINKS = { fd: false, er: false };
9358
9602
  function sinkSlots(t, s) {
9359
9603
  return `${s.fd ? `,${t}fd=0` : ""}${s.er ? `,${t}er=0` : ""}`;
@@ -9464,7 +9708,7 @@ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false) {
9464
9708
  const { code, k, fx, disp, dsp, triviaLabelled } = t;
9465
9709
  const swapLegal = !cfg.trackLines;
9466
9710
  const hostCst = cfg.hostCst;
9467
- const REC2 = prog.rec === 1 && cfg.tolerant;
9711
+ const REC = prog.rec === 1 && cfg.tolerant;
9468
9712
  if (cfg.coverage) throw new Unemittable("a coverage assembly");
9469
9713
  const roots = [...Object.values(prog.rules), ...extraIps];
9470
9714
  const reachable = [...reachableSites(code, roots)];
@@ -9543,7 +9787,7 @@ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false) {
9543
9787
  const op = code[ip];
9544
9788
  sinkBits.set(
9545
9789
  ip,
9546
- op === OP_SCAN ? 3 : (op === OP_FIELD ? 1 : 0) | (op === OP_EXPECT || REC2 && (op === OP_REP || op === OP_REPV) ? 2 : 0)
9790
+ op === OP_SCAN ? 3 : (op === OP_FIELD ? 1 : 0) | (op === OP_EXPECT || REC && (op === OP_REP || op === OP_REPV) ? 2 : 0)
9547
9791
  );
9548
9792
  }
9549
9793
  const sinkKids = [];
@@ -9900,12 +10144,13 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
9900
10144
  return `${head}EC.e=pos;return ''}`;
9901
10145
  case OP_GATE: {
9902
10146
  const child = link(code[ip + 2]);
9903
- const ci = classes.push([t.cc[code[ip + 1]]]) - 1;
10147
+ const resolved = t.cc[code[ip + 1]];
10148
+ const ci = classes.push([resolved]) - 1;
9904
10149
  classPlan.push([code[ip + 1]]);
9905
10150
  const cls = hoist("cl", `CLS[${ci}][0]`);
9906
10151
  const xf = fxRef(code[ip + 3]);
9907
10152
  return `${head}
9908
- if(!classHas(${cls},lead(input,pos))){ctx._fe=pos;ctx._fx=${xf};return FAIL}
10153
+ if(${staticBuild && isAsciiOnlyClass(resolved) ? `${cls}.ascii[input.charCodeAt(pos)]!==1` : `!classHas(${cls},lead(input,pos))`}){ctx._fe=pos;ctx._fx=${xf};return FAIL}
9909
10154
  return ${child}(input,pos,ctx)
9910
10155
  }`;
9911
10156
  }
@@ -9947,7 +10192,7 @@ if(v!==FAIL)return v
9947
10192
  const err={_tag:'parseError',span:${prog.lines === 1 ? "spanLines(ctx,pos,pos)" : "{start:pos,end:pos}"},expected:${xf}}
9948
10193
  const es=ctx._errors
9949
10194
  if(es!==undefined)es.push(err)
9950
- ${REC2 ? "if(ctx._tolerant===true)captureError(ctx,err)\n" : ""}ctx._fc=false
10195
+ ${REC ? "if(ctx._tolerant===true)captureError(ctx,err)\n" : ""}ctx._fc=false
9951
10196
  EC.e=pos
9952
10197
  return err
9953
10198
  }`;
@@ -10066,7 +10311,7 @@ return v
10066
10311
  }
10067
10312
  case OP_LEX_PROGRAM: {
10068
10313
  const programId = code[ip + 1];
10069
- const run3 = hoist("lexProgram", `LEXPROG[${programId}]`);
10314
+ const run = hoist("lexProgram", `LEXPROG[${programId}]`);
10070
10315
  const scanId = t.lexPrograms[programId].scan;
10071
10316
  if (scanId !== void 0) return `${head}
10072
10317
  const sTri=ctx.trivia,sKinds=ctx.triviaKindLabels,sScan=_pfScan
@@ -10084,7 +10329,7 @@ ctx._cstTriviaLog=undefined
10084
10329
  ctx._triviaLog=undefined
10085
10330
  ctx._rootTriviaLog=undefined
10086
10331
  let e
10087
- try{e=${run3}(input,pos,ctx,SCANS[${scanId}])}finally{
10332
+ try{e=${run}(input,pos,ctx,SCANS[${scanId}])}finally{
10088
10333
  _pfScan=sScan
10089
10334
  ctx.trivia=sTri
10090
10335
  ctx.triviaKindLabels=sKinds
@@ -10103,7 +10348,7 @@ EC.e=e
10103
10348
  return v
10104
10349
  }`;
10105
10350
  return `${head}
10106
- const e=${run3}(input,pos,ctx)
10351
+ const e=${run}(input,pos,ctx)
10107
10352
  if(e<0)return FAIL
10108
10353
  const v=input.slice(pos,e)
10109
10354
  if(ctx._cstBuf!==undefined||ctx._cstLeaves!==undefined)pushCstLeaf(ctx,{_tag:'leaf',value:v,span:{start:pos,end:e}})
@@ -10114,7 +10359,7 @@ return v
10114
10359
  /* ── transaction ─────────────────────────────────────────────────────── */
10115
10360
  case OP_ATTEMPT: {
10116
10361
  const child = link(code[ip + 1]);
10117
- const clean = !REC2 && failureRollbackClean(prog, ip);
10362
+ const clean = !REC && failureRollbackClean(prog, ip);
10118
10363
  const p = tmp();
10119
10364
  return `${head}
10120
10365
  ${clean ? "" : emitMark(p, L.buf, L.raw, sinks)}
@@ -10167,7 +10412,7 @@ return null
10167
10412
  }
10168
10413
  case OP_OPT: {
10169
10414
  const child = link(code[ip + 1]);
10170
- const clean = !REC2 && failureRollbackClean(prog, ip);
10415
+ const clean = !REC && failureRollbackClean(prog, ip);
10171
10416
  const p = tmp();
10172
10417
  return `${head}
10173
10418
  ${clean ? "" : emitMark(p, L.buf, L.raw, sinks)}
@@ -10197,15 +10442,15 @@ return v
10197
10442
  const childIp = code[base + i];
10198
10443
  kids.push(code[childIp] === OP_ADJ ? "" : link(childIp));
10199
10444
  }
10200
- const sy = REC2 ? `${tmp()}sy` : "";
10445
+ const sy = REC ? `${tmp()}sy` : "";
10201
10446
  const pub = (i) => {
10202
10447
  const s = sentRef(code[base + n + i]);
10203
10448
  return s === "undefined" ? `ctx._sync=${sy}` : `ctx._sync=${s}??${sy}`;
10204
10449
  };
10205
10450
  const parts = [head];
10206
- if (REC2) parts.push(`const ${sy}=ctx._sync`, "try{", pub(0));
10451
+ if (REC) parts.push(`const ${sy}=ctx._sync`, "try{", pub(0));
10207
10452
  parts.push(`const v0=${kids[0]}(input,pos,ctx)`, "if(v0===FAIL)return FAIL");
10208
- const close = () => REC2 ? `${parts.join("\n")}
10453
+ const close = () => REC ? `${parts.join("\n")}
10209
10454
  }finally{ctx._sync=${sy}}
10210
10455
  }` : `${parts.join("\n")}
10211
10456
  }`;
@@ -10218,7 +10463,7 @@ return v
10218
10463
  for (let i = 1; i < n; i++) {
10219
10464
  const vn = `v${i}`;
10220
10465
  parts.push(`let ${vn}`);
10221
- if (REC2) parts.push(pub(i));
10466
+ if (REC) parts.push(pub(i));
10222
10467
  const childIp = code[base + i];
10223
10468
  if (code[childIp] === OP_ADJ) {
10224
10469
  const negated = code[childIp + 1] === 1;
@@ -10248,10 +10493,11 @@ return v
10248
10493
  const asc = hoist("as", `DISP[${di}].ascii`);
10249
10494
  const hiArr = table.hi;
10250
10495
  const armSwitch = arms.map((a, i) => `case ${i}:v=${a}(input,pos,ctx);break`).join("\n");
10496
+ const directAscii = staticBuild && table.hi.length === 0;
10251
10497
  return `${head}
10252
- const c=lead(input,pos)
10498
+ const c=${directAscii ? "input.charCodeAt(pos)" : "lead(input,pos)"}
10253
10499
  let arm=-1
10254
- if(c>=0&&c<128){const a=${asc}[c];if(a!==0)arm=a-1}
10500
+ if(${directAscii ? "c<128" : "c>=0&&c<128"}){const a=${asc}[c];if(a!==0)arm=a-1}
10255
10501
  ${hiArr.length === 0 ? "" : `else if(c>=128){const h=${hoist("hi", `DISP[${di}].hi`)}
10256
10502
  for(let i=0;i<h.length;i+=3){if(c>=h[i]&&c<=h[i+1]){arm=h[i+2];break}}}`}
10257
10503
  if(arm>=0){
@@ -10273,7 +10519,9 @@ return FAIL
10273
10519
  const di = code[ip + 1];
10274
10520
  const expected = Array.from({ length: n }, (_, i) => fxRef(code[base + n + i]));
10275
10521
  const gates = Array.from({ length: n }, (_, i) => table.armCls[i] ?? null);
10276
- const gateRefs = Array.from({ length: n }, (_, i) => hoist("g", `DISP[${di}].armCls[${i}]`));
10522
+ const maskable = n <= 32;
10523
+ const directAsciiMask = staticBuild && maskable && gates.every(isAsciiOnlyClass);
10524
+ const gateRefs = directAsciiMask ? [] : Array.from({ length: n }, (_, i) => hoist("g", `DISP[${di}].armCls[${i}]`));
10277
10525
  const tokenCandidate = tokenChoiceCandidates.get(ip);
10278
10526
  const pretests = Array.from({ length: n }, (_, i) => {
10279
10527
  if (tokenCandidate?.arm === i) {
@@ -10293,12 +10541,11 @@ return FAIL
10293
10541
  const sequence6 = largeChoiceScalarSequences.get(code[base + i]);
10294
10542
  return sequence6 === void 0 ? void 0 : { sequence: scalarSequenceRef(sequence6) };
10295
10543
  });
10296
- const maskable = n <= 32;
10297
10544
  const maskName = maskable ? hoist("mk", `MASK[${masks.push(maskForClassRow(gates)) - 1}]`) : "";
10298
10545
  if (maskable) maskPlan.push(~di);
10299
10546
  const p = tmp();
10300
10547
  const encodedRollbackMask = choiceRollbackMask(prog, ip) ?? -1;
10301
- const rollbackMask = REC2 ? -1 : encodedRollbackMask;
10548
+ const rollbackMask = REC ? -1 : encodedRollbackMask;
10302
10549
  const hasRollback = rollbackMask !== 0;
10303
10550
  const startFailureExact = arms.every((_arm, i) => {
10304
10551
  const terminal = leadingScalarTerminal(code, code[base + i], 0, false);
@@ -10355,16 +10602,16 @@ ${merge}
10355
10602
  ctx._fe=pos;ctx._fx=acc??${choiceFx}
10356
10603
  return FAIL`;
10357
10604
  return `${head}
10358
- const c=lead(input,pos)
10605
+ const c=${directAsciiMask ? "input.charCodeAt(pos)" : "lead(input,pos)"}
10359
10606
  ${hasRollback ? emitMark(p, L.buf, L.raw, sinks) : ""}
10360
10607
  ${deepAt.map((at, i) => `let ${at},${deepFx[i]}`).join("\n")}
10361
10608
  ${maskable ? `if(c<128){
10362
- const bits=${maskName}[c<0?128:c]
10609
+ const bits=${maskName}[${directAsciiMask ? "c" : "c<0?128:c"}]
10363
10610
  if(bits===0){ctx._fe=pos;ctx._fx=${choiceFx};return FAIL}
10364
10611
  ${lazyArms(true)}
10365
- }else{
10612
+ }${directAsciiMask ? "" : `else{
10366
10613
  ${lazyArms(false)}
10367
- }
10614
+ }`}
10368
10615
  ` : lazyArms(false)}
10369
10616
  ${finish}
10370
10617
  }`;
@@ -10415,7 +10662,7 @@ if(ctx._fc===true){${startFailureExact ? `if(best===pos){acc=${catchName}(${i},0
10415
10662
  ${rollbackFor(i)}
10416
10663
  }${routeMiss}${sequenceMiss}`;
10417
10664
  }).join("\n") : "";
10418
- const generalArms = arms.map((arm, i) => {
10665
+ const generalArms = directAsciiMask ? "" : arms.map((arm, i) => {
10419
10666
  const pretest = pretests[i];
10420
10667
  const decision = pretest?.token === void 0 && pretest?.sequence === void 0 ? "" : tmp();
10421
10668
  const scalarCondition = pretest?.scalar === void 0 ? "" : pretest.scalar.length === 1 ? `&&${pretest.scalar[0]}(input,pos)>=0` : `&&(${pretest.scalar.map((recognize) => `${recognize}(input,pos)>=0`).join("||")})`;
@@ -10445,12 +10692,12 @@ ${rollbackFor(i)}
10445
10692
  }${miss}`;
10446
10693
  }).join("\n");
10447
10694
  return `${head}
10448
- const c=lead(input,pos)
10695
+ const c=${directAsciiMask ? "input.charCodeAt(pos)" : "lead(input,pos)"}
10449
10696
  ${hasRollback ? emitMark(p, L.buf, L.raw, sinks) : ""}
10450
10697
  let acc
10451
10698
  let best=pos
10452
10699
  ${maskable ? `if(c<128){
10453
- const bits=${maskName}[c<0?128:c]
10700
+ const bits=${maskName}[${directAsciiMask ? "c" : "c<0?128:c"}]
10454
10701
  if(bits===0){ctx._fe=pos;ctx._fx=${choiceFx};return FAIL}
10455
10702
  ${startFailureExact ? "" : "let prev=0"}
10456
10703
  ${maskArms}
@@ -10545,14 +10792,18 @@ return [key,v]
10545
10792
  const itemFx = reportItem ? fxRef(code[ip + 6]) : "EMPTY_FX";
10546
10793
  const collect = op === OP_REP;
10547
10794
  let itemCls;
10548
- if (!REC2) {
10795
+ let itemAscii = false;
10796
+ if (!REC) {
10549
10797
  const itemClassIndex = sepIp < 0 ? code[ip + 7] : -1;
10550
10798
  if (itemClassIndex >= 0) {
10551
- const ci = classes.push([t.cc[itemClassIndex]]) - 1;
10799
+ const resolved = t.cc[itemClassIndex];
10800
+ const ci = classes.push([resolved]) - 1;
10552
10801
  classPlan.push([itemClassIndex]);
10553
10802
  itemCls = hoist("ri", `CLS[${ci}][0]`);
10803
+ itemAscii = staticBuild && isAsciiOnlyClass(resolved);
10554
10804
  }
10555
10805
  }
10806
+ const itemMiss = (at) => itemAscii ? `${itemCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${itemCls},lead(input,${at}))`;
10556
10807
  const skipBeforeFirst = sepIp < 0 && min === 0;
10557
10808
  const p = tmp();
10558
10809
  const via = sep2 !== void 0 ? "false" : min === 0 ? skipBeforeFirst ? "true" : "count>0" : `count>=${min}&&count>0`;
@@ -10590,11 +10841,11 @@ ${p}lg=ctx._triviaLog!==undefined?ctx._triviaLog.length:0
10590
10841
  ${p}rt=ctx._rootTriviaLog!==undefined?ctx._rootTriviaLog.length:0${markSinks}
10591
10842
  }`;
10592
10843
  const my = `${p}my`;
10593
- const recFx = REC2 ? fxRef(code[ip + 6]) : "";
10594
- const sepSent = REC2 ? sentRef(code[ip + 7]) : "undefined";
10844
+ const recFx = REC ? fxRef(code[ip + 6]) : "";
10845
+ const sepSent = REC ? sentRef(code[ip + 7]) : "undefined";
10595
10846
  const recSent = sep2 === void 0 ? my : sepSent === "undefined" ? `orSentinel(${my},undefined)` : `orSentinel(${sepSent}??${my},${sepSent}===undefined?undefined:${my})`;
10596
10847
  const recGate = sep2 !== void 0 || min === 0 ? "" : `count>=${min}&&`;
10597
- const recBranch = !REC2 ? "" : `if(ctx._tolerant===true&&${my}!==undefined&&${recGate}!matchesAt(${my},input,itemStart,ctx)){
10848
+ const recBranch = !REC ? "" : `if(ctx._tolerant===true&&${my}!==undefined&&${recGate}!matchesAt(${my},input,itemStart,ctx)){
10598
10849
  ${sep2 === void 0 ? `${rb}
10599
10850
  ` : ""}const rr=recoverScan(input,itemStart,ctx,${recSent},${recFx})
10600
10851
  ${collect ? "out.push(rr.error)\n" : ""}captureError(ctx,rr.error)
@@ -10605,14 +10856,14 @@ continue
10605
10856
  `;
10606
10857
  return `${head}
10607
10858
  const out=${collect ? "[]" : "undefined"}
10608
- ${knownTrivia === void 0 ? "const hasTrivia=ctx.trivia!==undefined\n" : ""}${L.buf ? "" : "const needMark=_rollbackNeeded(ctx)\n"}${REC2 ? `const ${my}=ctx._sync
10859
+ ${knownTrivia === void 0 ? "const hasTrivia=ctx.trivia!==undefined\n" : ""}${L.buf ? "" : "const needMark=_rollbackNeeded(ctx)\n"}${REC ? `const ${my}=ctx._sync
10609
10860
  ` : ""}${itemCls === void 0 ? "" : `const ${p}gate=ctx._probe===undefined
10610
10861
  `}let cur=pos
10611
10862
  let count=0
10612
10863
  for(;;){
10613
10864
  ${max >= 0 ? `if(count>=${max})break
10614
10865
  ` : ""}${sep2 !== void 0 ? `if(count>0&&count>=${min}&&cur>=input.length)break
10615
- ` : ""}${itemCls !== void 0 && sep2 === void 0 ? `if(count>=${min}&&${p}gate&&${hasTrivia === "false" ? "true" : hasTrivia === "true" ? "false" : "!hasTrivia"}&&!classHas(${itemCls},lead(input,cur)))break
10866
+ ` : ""}${itemCls !== void 0 && sep2 === void 0 ? `if(count>=${min}&&${p}gate&&${hasTrivia === "false" ? "true" : hasTrivia === "true" ? "false" : "!hasTrivia"}&&${itemMiss("cur")})break
10616
10867
  ` : ""}let ${p}raw=0,${p}tl=0,${p}lv=0,${p}lg=0,${p}rt=0${sinks.fd ? `,${p}fd=0` : ""}${sinks.er ? `,${p}er=0` : ""}
10617
10868
  ${markBody}
10618
10869
  let itemStart=cur
@@ -10637,14 +10888,14 @@ itemStart=${hasTrivia === "false" ? "EC.e" : hasTrivia === "true" ? `${skip}(inp
10637
10888
  ${rb}
10638
10889
  ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
10639
10890
  }
10640
- ${itemCls === void 0 ? "" : `if(count>=${min}&&${p}gate&&!classHas(${itemCls},lead(input,itemStart))){
10891
+ ${itemCls === void 0 ? "" : `if(count>=${min}&&${p}gate&&${itemMiss("itemStart")}){
10641
10892
  ${rb}
10642
10893
  ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
10643
10894
  }
10644
10895
  `}ctx._fc=false
10645
10896
  const v=${child}(input,itemStart,ctx)
10646
10897
  if(v===FAIL){
10647
- ${REC2 ? `if(ctx._fc===true){
10898
+ ${REC ? `if(ctx._fc===true){
10648
10899
  ${rb}
10649
10900
  return FAIL
10650
10901
  }
@@ -11215,7 +11466,7 @@ function assemble(t, prog, cfg) {
11215
11466
  const triviaScan = t.triviaScan;
11216
11467
  const swapLegal = !cfg.trackLines;
11217
11468
  const hostCst = cfg.hostCst;
11218
- const REC2 = prog.rec === 1 && cfg.tolerant;
11469
+ const REC = prog.rec === 1 && cfg.tolerant;
11219
11470
  const sentinels = /* @__PURE__ */ new Map();
11220
11471
  function sentinelFor(cls) {
11221
11472
  if (cls < 0) return void 0;
@@ -12216,7 +12467,7 @@ function assemble(t, prog, cfg) {
12216
12467
  const child = link(code[ip + 1]);
12217
12468
  const xf = fx[code[ip + 2]];
12218
12469
  const expectSpan = (ctx, pos) => prog.lines === 1 ? spanLines(ctx, pos, pos) : { start: pos, end: pos };
12219
- if (REC2) {
12470
+ if (REC) {
12220
12471
  return (input, pos, ctx) => {
12221
12472
  const v = child(input, pos, ctx);
12222
12473
  if (v !== FAIL) return v;
@@ -12523,8 +12774,8 @@ function assemble(t, prog, cfg) {
12523
12774
  };
12524
12775
  }
12525
12776
  case OP_LEX_PROGRAM: {
12526
- const run3 = t.lexPrograms[code[ip + 1]];
12527
- const scanId = run3.scan;
12777
+ const run = t.lexPrograms[code[ip + 1]];
12778
+ const scanId = run.scan;
12528
12779
  if (scanId !== void 0) {
12529
12780
  return (input, pos, ctx) => {
12530
12781
  const sTrivia = ctx.trivia, sKinds = ctx.triviaKindLabels;
@@ -12545,7 +12796,7 @@ function assemble(t, prog, cfg) {
12545
12796
  ctx._rootTriviaLog = void 0;
12546
12797
  let end;
12547
12798
  try {
12548
- end = run3(input, pos, ctx, scans[scanId]);
12799
+ end = run(input, pos, ctx, scans[scanId]);
12549
12800
  } finally {
12550
12801
  SCAN = sScan;
12551
12802
  ctx.trivia = sTrivia;
@@ -12566,7 +12817,7 @@ function assemble(t, prog, cfg) {
12566
12817
  };
12567
12818
  }
12568
12819
  return (input, pos, ctx) => {
12569
- const end = run3(input, pos, ctx);
12820
+ const end = run(input, pos, ctx);
12570
12821
  if (end < 0) return FAIL;
12571
12822
  const value = input.slice(pos, end);
12572
12823
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: { start: pos, end } });
@@ -12663,7 +12914,7 @@ function assemble(t, prog, cfg) {
12663
12914
  return next === void 0 ? cur : next(input, cur, ctx, values, inherited);
12664
12915
  };
12665
12916
  };
12666
- if (REC2) {
12917
+ if (REC) {
12667
12918
  const first2 = link(code[base]);
12668
12919
  const firstSync = sentinelFor(code[base + n]);
12669
12920
  const runnerAt = (i) => {
@@ -13120,7 +13371,7 @@ function assemble(t, prog, cfg) {
13120
13371
  if (n === 2 || n === 3) {
13121
13372
  const a0 = link(code[base]), a1 = link(code[base + 1]);
13122
13373
  const a2 = n === 3 ? link(code[base + 2]) : void 0;
13123
- const predecide = !hostCst && !REC2 && !cfg.probe && !cfg.coverage && !cfg.trackLines;
13374
+ const predecide = !hostCst && !REC && !cfg.probe && !cfg.coverage && !cfg.trackLines;
13124
13375
  const p0ip = predecide ? leadingScalarTerminal(code, code[base], 2, true, true) : -1;
13125
13376
  const p1ip = predecide ? leadingScalarTerminal(code, code[base + 1], 2, true, true) : -1;
13126
13377
  const p2ip = predecide && n === 3 ? leadingScalarTerminal(code, code[base + 2], 2, true, true) : -1;
@@ -13511,10 +13762,10 @@ function assemble(t, prog, cfg) {
13511
13762
  const itemFx = reportItem ? fx[code[ip + 6]] : EMPTY_FX2;
13512
13763
  const collect = op === OP_REP;
13513
13764
  const itemClassIndex = sepIp < 0 ? code[ip + 7] : -1;
13514
- const itemClass = !REC2 && itemClassIndex >= 0 ? cc[itemClassIndex] : void 0;
13765
+ const itemClass = !REC && itemClassIndex >= 0 ? cc[itemClassIndex] : void 0;
13515
13766
  const itemMayStart = itemClass === void 0 ? void 0 : (input, pos) => classHas2(itemClass, lead(input, pos));
13516
13767
  const skipBeforeFirst = sepIp < 0 && min === 0;
13517
- if (REC2) {
13768
+ if (REC) {
13518
13769
  const itemFx2 = fx[code[ip + 6]];
13519
13770
  const sepSent = sentinelFor(code[ip + 7]);
13520
13771
  return (input, pos, ctx) => {
@@ -14021,7 +14272,7 @@ function assemble(t, prog, cfg) {
14021
14272
  lead,
14022
14273
  asciiFoldKey,
14023
14274
  ROUTED_FX,
14024
- REC2 ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
14275
+ REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
14025
14276
  matchesAt,
14026
14277
  recoverScan,
14027
14278
  orSentinel,
@@ -14103,7 +14354,7 @@ function assemble(t, prog, cfg) {
14103
14354
  // — a strict one emits no reader — and through the SAME `sentinelFor` memo
14104
14355
  // the closure pieces use, so both engines resync to the same combinator
14105
14356
  // object rather than to two separately constructed ones.
14106
- REC2 ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
14357
+ REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
14107
14358
  matchesAt,
14108
14359
  recoverScan,
14109
14360
  orSentinel,
@@ -15136,36 +15387,36 @@ function findRegexFragments(visited) {
15136
15387
  if (branches.length >= 2) runs.add(branches.join("|"));
15137
15388
  for (let i = 0; i < branches.length; i++) {
15138
15389
  for (let n = 2; n <= Math.min(MAX_RUN, branches.length - i); n++) {
15139
- const run3 = branches.slice(i, i + n).join("|");
15140
- if (run3.length < 4) continue;
15141
- runs.add(run3);
15390
+ const run = branches.slice(i, i + n).join("|");
15391
+ if (run.length < 4) continue;
15392
+ runs.add(run);
15142
15393
  }
15143
15394
  }
15144
15395
  }
15145
- for (const run3 of runs) {
15146
- let e = byRun.get(run3);
15396
+ for (const run of runs) {
15397
+ let e = byRun.get(run);
15147
15398
  if (e === void 0) {
15148
- e = { occ: [], nodes: /* @__PURE__ */ new Set(), branches: run3.split("|").length };
15149
- byRun.set(run3, e);
15399
+ e = { occ: [], nodes: /* @__PURE__ */ new Set(), branches: run.split("|").length };
15400
+ byRun.set(run, e);
15150
15401
  }
15151
15402
  if (e.nodes.has(v.p)) continue;
15152
15403
  e.nodes.add(v.p);
15153
15404
  e.occ.push({ site: v.site, source: d.source });
15154
15405
  }
15155
15406
  }
15156
- const candidates = [...byRun.entries()].filter(([, e]) => e.occ.length >= 2).map(([run3, e]) => ({ run: run3, e, savings: (e.occ.length - 1) * run3.length })).sort((a, b) => b.e.occ.length - a.e.occ.length || b.savings - a.savings || b.run.length - a.run.length);
15407
+ const candidates = [...byRun.entries()].filter(([, e]) => e.occ.length >= 2).map(([run, e]) => ({ run, e, savings: (e.occ.length - 1) * run.length })).sort((a, b) => b.e.occ.length - a.e.occ.length || b.savings - a.savings || b.run.length - a.run.length);
15157
15408
  const kept = [];
15158
15409
  for (const c of candidates) {
15159
15410
  if (kept.some((k) => k.e.occ.length >= c.e.occ.length && `|${k.run}|`.includes(`|${c.run}|`))) continue;
15160
15411
  kept.push(c);
15161
15412
  }
15162
- return kept.map(({ run: run3, e }) => ({
15413
+ return kept.map(({ run, e }) => ({
15163
15414
  kind: "regex-fragment",
15164
- id: `regex-fragment:${run3}`,
15165
- fragment: run3,
15415
+ id: `regex-fragment:${run}`,
15416
+ fragment: run,
15166
15417
  branches: e.branches,
15167
15418
  count: e.occ.length,
15168
- savings: (e.occ.length - 1) * run3.length,
15419
+ savings: (e.occ.length - 1) * run.length,
15169
15420
  sources: e.occ.map((o) => `/${o.source}/`),
15170
15421
  sites: e.occ.map((o) => o.site),
15171
15422
  suggestion: `this ${e.branches}-branch alternation is re-spelled in ${e.occ.length} DISTINCT regex() terminals. Hoist it to one shared source string and interpolate it (\`new RegExp(\\\`\u2026\\\${OP}\u2026\\\`)\`) or lift the shared part into its own terminal \u2014 an edit to the operator set currently has to land in ${e.occ.length} places, and the analysis cannot tell you when one of them drifts.`
@@ -16675,7 +16926,7 @@ function compileRuleMap(ruleMap, opts = {}) {
16675
16926
  }
16676
16927
 
16677
16928
  // src/version.ts
16678
- var PARSEMAN_VERSION = "0.50.0";
16929
+ var PARSEMAN_VERSION = "0.50.3";
16679
16930
 
16680
16931
  // src/compiler/compile-linkable-table.ts
16681
16932
  function isLocal(rule) {
@@ -16768,72 +17019,6 @@ function cstBuildHost(typeOrOptions, children3, _fields, span, rawChildren, triv
16768
17019
  cstBuildHost._parsemanCstOutput = true;
16769
17020
  var COMPOSED_PIECES = /* @__PURE__ */ Symbol.for("parseman.composedPieces");
16770
17021
 
16771
- // src/cst/trivia-entries.ts
16772
- var EMPTY_INDICES = Object.freeze([]);
16773
- var EMPTY_GAPS = Object.freeze([]);
16774
-
16775
- // src/functional/run.ts
16776
- var SELECT_EXAMPLE = "run(entry, input, { rootTrivia: { select: ['blockComment', 'lineComment'] } })";
16777
- var REMOVED_RUN_RESULT_FIELDS = [
16778
- [
16779
- "triviaMap",
16780
- `It was a dense root-trivia index built on every parse; root trivia is now an OPT-IN sparse capture, because most grammars paid for an index they never read. Migration: name the trivia labels your grammar defines \u2014 ${SELECT_EXAMPLE} \u2014 and read \`RunResult.rootTrivia\`, whose \`.index\` carries the labels and gap lookups \`triviaMap\` exposed.`
16781
- ],
16782
- [
16783
- "triviaLog",
16784
- `It was the flat \`start, end[, kindIndex]\` log every other trivia view was derived from, and it is no longer allocated at all \u2014 not empty, absent. Migration: request the labels you need \u2014 ${SELECT_EXAMPLE} \u2014 and read \`RunResult.rootTrivia.rows\`. Do NOT reuse a stride-2/3 reader: rows are a different width and a different unit (one row per SELECTED marker, not one per trivia chunk), so an old loop reads confidently wrong offsets rather than failing.`
16785
- ],
16786
- [
16787
- "triviaKindLabels",
16788
- `It was already optional, so nothing ever flagged its disappearance \u2014 and \`triviaKindMask(undefined, \u2026)\` means "capture everything", so reading it now silently WIDENS capture instead of erroring. Migration: request labels \u2014 ${SELECT_EXAMPLE} \u2014 then the set you asked for is \`RunResult.rootTrivia.select\` and the resolved table is \`RunResult.rootTrivia.index.labels\`.`
16789
- ]
16790
- ];
16791
- var RunResultRecord = class {
16792
- constructor(ok, value, span, expected, errors, unconsumedFrom) {
16793
- this.ok = ok;
16794
- this.value = value;
16795
- this.span = span;
16796
- this.expected = expected;
16797
- this.errors = errors;
16798
- this.unconsumedFrom = unconsumedFrom;
16799
- }
16800
- };
16801
- var RunResultWithRootTrivia = class {
16802
- constructor(ok, value, span, expected, errors, rootTrivia, unconsumedFrom) {
16803
- this.ok = ok;
16804
- this.value = value;
16805
- this.span = span;
16806
- this.expected = expected;
16807
- this.errors = errors;
16808
- this.rootTrivia = rootTrivia;
16809
- this.unconsumedFrom = unconsumedFrom;
16810
- }
16811
- };
16812
- for (const proto of [RunResultRecord.prototype, RunResultWithRootTrivia.prototype]) {
16813
- for (const [name, detail] of REMOVED_RUN_RESULT_FIELDS) {
16814
- Object.defineProperty(proto, name, {
16815
- configurable: true,
16816
- enumerable: false,
16817
- get() {
16818
- throw new TypeError(
16819
- `RunResult.${name} was REMOVED in parseman 0.44.0 and has no default replacement. ${detail}`
16820
- );
16821
- }
16822
- });
16823
- }
16824
- }
16825
-
16826
- // src/analysis/choice-cost-render.ts
16827
- var ESC = "\x1B";
16828
- var ANSI = {
16829
- dim: `${ESC}[2m`,
16830
- bold: `${ESC}[1m`,
16831
- red: `${ESC}[31m`,
16832
- yellow: `${ESC}[33m`,
16833
- cyan: `${ESC}[36m`,
16834
- reset: `${ESC}[0m`
16835
- };
16836
-
16837
17022
  // src/combinators/gate.ts
16838
17023
  function gate(predicate) {
16839
17024
  const meta = {