parseman 0.43.0 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/analysis/diagnose.d.ts +118 -0
  2. package/dist/analysis/diagnose.d.ts.map +1 -0
  3. package/dist/analysis/gating.d.ts +9 -9
  4. package/dist/analysis/gating.d.ts.map +1 -1
  5. package/dist/analysis/grammar.d.ts.map +1 -1
  6. package/dist/combinators/attempt.d.ts.map +1 -1
  7. package/dist/combinators/choice.d.ts.map +1 -1
  8. package/dist/combinators/dispatch.d.ts +19 -1
  9. package/dist/combinators/dispatch.d.ts.map +1 -1
  10. package/dist/combinators/expect.d.ts.map +1 -1
  11. package/dist/combinators/first-set.d.ts.map +1 -1
  12. package/dist/combinators/grammar.d.ts +7 -0
  13. package/dist/combinators/grammar.d.ts.map +1 -1
  14. package/dist/combinators/map.d.ts +11 -0
  15. package/dist/combinators/map.d.ts.map +1 -1
  16. package/dist/combinators/node.d.ts +19 -0
  17. package/dist/combinators/node.d.ts.map +1 -1
  18. package/dist/combinators/token.d.ts +3 -2
  19. package/dist/combinators/token.d.ts.map +1 -1
  20. package/dist/combinators/trivia-skip.d.ts +1 -0
  21. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  22. package/dist/compiler/build-arity.d.ts +28 -0
  23. package/dist/compiler/build-arity.d.ts.map +1 -1
  24. package/dist/compiler/codegen.d.ts +80 -60
  25. package/dist/compiler/codegen.d.ts.map +1 -1
  26. package/dist/compiler/degradation.d.ts +123 -0
  27. package/dist/compiler/degradation.d.ts.map +1 -0
  28. package/dist/compiler/fields.d.ts.map +1 -1
  29. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  30. package/dist/compiler/inline-build.d.ts.map +1 -1
  31. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  32. package/dist/compiler/linker.d.ts +1 -1
  33. package/dist/compiler/linker.d.ts.map +1 -1
  34. package/dist/compiler/scannable-run.d.ts +1 -1
  35. package/dist/compiler/scannable-run.d.ts.map +1 -1
  36. package/dist/compiler/trivia-fast-path.d.ts +4 -3
  37. package/dist/compiler/trivia-fast-path.d.ts.map +1 -1
  38. package/dist/coverage.d.ts +13 -0
  39. package/dist/coverage.d.ts.map +1 -1
  40. package/dist/cst/reflection.d.ts.map +1 -1
  41. package/dist/cst/trivia-entries.d.ts +15 -9
  42. package/dist/cst/trivia-entries.d.ts.map +1 -1
  43. package/dist/cst/trivia-kinds.d.ts +15 -11
  44. package/dist/cst/trivia-kinds.d.ts.map +1 -1
  45. package/dist/functional/run.d.ts +22 -16
  46. package/dist/functional/run.d.ts.map +1 -1
  47. package/dist/index.cjs +2131 -1594
  48. package/dist/index.cjs.map +4 -4
  49. package/dist/index.d.ts +6 -8
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +2131 -1594
  52. package/dist/index.js.map +4 -4
  53. package/dist/language-service/index.cjs +201 -132
  54. package/dist/language-service/index.cjs.map +4 -4
  55. package/dist/language-service/index.js +201 -132
  56. package/dist/language-service/index.js.map +4 -4
  57. package/dist/oracle/digest.d.ts +132 -6
  58. package/dist/oracle/digest.d.ts.map +1 -1
  59. package/dist/oracle/index.cjs +136 -305
  60. package/dist/oracle/index.cjs.map +4 -4
  61. package/dist/oracle/index.d.ts +43 -31
  62. package/dist/oracle/index.d.ts.map +1 -1
  63. package/dist/oracle/index.js +136 -305
  64. package/dist/oracle/index.js.map +4 -4
  65. package/dist/plugin/evaluator.d.ts +3 -0
  66. package/dist/plugin/evaluator.d.ts.map +1 -1
  67. package/dist/plugin/index.cjs +2333 -1721
  68. package/dist/plugin/index.cjs.map +4 -4
  69. package/dist/plugin/index.d.ts +18 -0
  70. package/dist/plugin/index.d.ts.map +1 -1
  71. package/dist/plugin/index.js +2333 -1721
  72. package/dist/plugin/index.js.map +4 -4
  73. package/dist/plugin/reducer-resolver.d.ts +41 -0
  74. package/dist/plugin/reducer-resolver.d.ts.map +1 -0
  75. package/dist/run/index.cjs +185 -132
  76. package/dist/run/index.cjs.map +3 -3
  77. package/dist/run/index.d.ts +1 -1
  78. package/dist/run/index.d.ts.map +1 -1
  79. package/dist/run/index.js +185 -132
  80. package/dist/run/index.js.map +3 -3
  81. package/dist/spec/index.cjs +1637 -1322
  82. package/dist/spec/index.cjs.map +4 -4
  83. package/dist/spec/index.js +1637 -1322
  84. package/dist/spec/index.js.map +4 -4
  85. package/dist/spec/model.d.ts.map +1 -1
  86. package/dist/types.d.ts +31 -1
  87. package/dist/types.d.ts.map +1 -1
  88. package/dist/version.d.ts +1 -1
  89. package/package.json +7 -3
  90. package/dist/combinators/guard.d.ts +0 -8
  91. package/dist/combinators/guard.d.ts.map +0 -1
  92. package/dist/cst/offset-model.d.ts +0 -119
  93. package/dist/cst/offset-model.d.ts.map +0 -1
  94. package/dist/oracle/corpus.d.ts +0 -32
  95. package/dist/oracle/corpus.d.ts.map +0 -1
  96. package/dist/oracle/identity.d.ts +0 -117
  97. package/dist/oracle/identity.d.ts.map +0 -1
@@ -48,8 +48,9 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve) {
48
48
  return d.value.length === 0;
49
49
  case "keywords":
50
50
  return false;
51
+ // The routed token itself is the selector's match, never empty; a fallback can be.
51
52
  case "routed":
52
- return false;
53
+ return d.fallback === void 0 ? false : me(d.fallback);
53
54
  case "regex":
54
55
  try {
55
56
  const m = new RegExp(d.source).exec("");
@@ -576,1448 +577,1539 @@ function pushTriviaLogEntry(ctx, start, end, kindIndex) {
576
577
  }
577
578
  }
578
579
 
579
- // src/recovery/scan.ts
580
- function probeAt(sentinel, input, pos, ctx) {
581
- const s = {
582
- _probe: ctx._probe,
583
- _errors: ctx._errors,
584
- _cstLeaves: ctx._cstLeaves,
585
- _cstChildren: ctx._cstChildren,
586
- _cstRawChildren: ctx._cstRawChildren,
587
- _cstTriviaLog: ctx._cstTriviaLog,
588
- _triviaLog: ctx._triviaLog,
589
- _fields: ctx._fields,
590
- _cstBuf: ctx._cstBuf,
591
- _tolerant: ctx._tolerant,
592
- _sync: ctx._sync
593
- };
594
- ctx._probe = void 0;
595
- ctx._errors = void 0;
596
- ctx._cstLeaves = void 0;
597
- ctx._cstChildren = void 0;
598
- ctx._cstRawChildren = void 0;
599
- ctx._cstTriviaLog = void 0;
600
- ctx._triviaLog = void 0;
601
- ctx._fields = void 0;
602
- ctx._cstBuf = void 0;
603
- ctx._tolerant = void 0;
604
- ctx._sync = void 0;
605
- try {
606
- return sentinel.parse(input, pos, ctx).ok;
607
- } finally {
608
- ctx._probe = s._probe;
609
- ctx._errors = s._errors;
610
- ctx._cstLeaves = s._cstLeaves;
611
- ctx._cstChildren = s._cstChildren;
612
- ctx._cstRawChildren = s._cstRawChildren;
613
- ctx._cstTriviaLog = s._cstTriviaLog;
614
- ctx._triviaLog = s._triviaLog;
615
- ctx._fields = s._fields;
616
- ctx._cstBuf = s._cstBuf;
617
- ctx._tolerant = s._tolerant;
618
- ctx._sync = s._sync;
619
- }
580
+ // src/cst/trivia-kinds.ts
581
+ var labeledTriviaSpecs = /* @__PURE__ */ new WeakMap();
582
+ function unwrapTrivia(p) {
583
+ let cur = p;
584
+ while (cur._def.tag === "trivia") cur = cur._def.parser;
585
+ return cur;
620
586
  }
621
- function orSentinel(a, b) {
622
- if (b === void 0) return a;
623
- return {
624
- _tag: "orSentinel",
625
- _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
626
- _def: { tag: "unknown" },
627
- parse(input, pos, ctx) {
628
- const ra = a.parse(input, pos, ctx);
629
- if (ra.ok) return ra;
630
- return b.parse(input, pos, ctx);
631
- }
632
- };
587
+ function peelLabel(p) {
588
+ if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
589
+ return null;
633
590
  }
634
- function firstSetSentinel(fs) {
635
- if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
636
- const ranges = fs.ranges;
637
- return {
638
- _tag: "firstSetSentinel",
639
- _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },
640
- _def: { tag: "unknown" },
641
- parse(input, pos) {
642
- const code = pos < input.length ? input.codePointAt(pos) : void 0;
643
- if (code !== void 0) {
644
- for (const r of ranges) {
645
- if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
646
- }
591
+ function analyzeLabeledTrivia(trivia2) {
592
+ const cached = labeledTriviaSpecs.get(trivia2);
593
+ if (cached !== void 0) return cached;
594
+ let core = unwrapTrivia(trivia2);
595
+ let minRepeats = 1;
596
+ if (core._def.tag === "oneOrMore") {
597
+ core = core._def.parser;
598
+ minRepeats = 1;
599
+ } else if (core._def.tag === "many") {
600
+ core = core._def.parser;
601
+ minRepeats = 0;
602
+ }
603
+ const arms = [];
604
+ if (core._def.tag === "choice") {
605
+ for (let i = 0; i < core._def.parsers.length; i++) {
606
+ const peeled = peelLabel(core._def.parsers[i]);
607
+ if (!peeled) {
608
+ labeledTriviaSpecs.set(trivia2, null);
609
+ return null;
647
610
  }
648
- return { ok: false, expected: [], span: { start: pos, end: pos } };
611
+ arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
649
612
  }
613
+ } else {
614
+ const peeled = peelLabel(core);
615
+ if (!peeled) {
616
+ labeledTriviaSpecs.set(trivia2, null);
617
+ return null;
618
+ }
619
+ arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
620
+ }
621
+ const spec = {
622
+ labels: arms.map((a) => a.label),
623
+ arms,
624
+ minRepeats
650
625
  };
626
+ labeledTriviaSpecs.set(trivia2, spec);
627
+ return spec;
651
628
  }
652
- function recoverScan(input, from, ctx, sync, expected) {
653
- let scanPos = from;
654
- while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
655
- const span = { start: from, end: scanPos };
656
- if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
657
- const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
658
- ctx._errors?.push(error);
659
- return { error, end: scanPos };
660
- }
661
- function matchesAt(sentinel, input, pos, ctx) {
662
- return probeAt(sentinel, input, pos, ctx);
629
+ function matchArmAt(input, pos, arm, state) {
630
+ if (!startsFirstSet(arm, input, pos)) return null;
631
+ const r = arm.parse(input, pos, { trackLines: false, state });
632
+ if (!r.ok || r.span.end <= pos) return null;
633
+ return { end: r.span.end };
663
634
  }
664
- function captureError(ctx, error) {
665
- if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
635
+ function scanLabeledTriviaChunks(input, cur, spec, state) {
636
+ const chunks = [];
637
+ let pos = cur;
638
+ while (pos < input.length) {
639
+ let matched = null;
640
+ for (const arm of spec.arms) {
641
+ const m = matchArmAt(input, pos, arm.parser, state);
642
+ if (m) {
643
+ matched = { end: m.end, kindIndex: arm.kindIndex };
644
+ break;
645
+ }
646
+ }
647
+ if (!matched) break;
648
+ chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
649
+ pos = matched.end;
650
+ }
651
+ if (chunks.length < spec.minRepeats) {
652
+ return { end: cur, chunks: [] };
653
+ }
654
+ return { end: pos, chunks };
666
655
  }
667
-
668
- // src/combinators/expect.ts
669
- function deriveExpected(c) {
670
- return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
656
+ function scanLabeledTriviaEnd(input, cur, spec, state) {
657
+ let pos = cur;
658
+ let count = 0;
659
+ while (pos < input.length) {
660
+ let matched = null;
661
+ for (const arm of spec.arms) {
662
+ matched = matchArmAt(input, pos, arm.parser, state);
663
+ if (matched) break;
664
+ }
665
+ if (!matched) break;
666
+ pos = matched.end;
667
+ count++;
668
+ }
669
+ return count < spec.minRepeats ? cur : pos;
671
670
  }
672
- function derive(c, seen) {
673
- const deriveExpected2 = (p) => derive(p, seen);
674
- const def = c._def;
675
- switch (def.tag) {
676
- case "literal":
677
- return [JSON.stringify(def.value)];
678
- case "regex":
679
- return [`/${def.source}/`];
680
- case "keywords":
681
- return def.words.map((w) => JSON.stringify(w));
682
- case "label":
683
- return [def.label];
684
- case "choice":
685
- return def.parsers.flatMap(deriveExpected2);
686
- case "dispatch":
687
- return deriveExpected2(def.selector);
688
- // Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
689
- // `optional(…)`/`many(…)` can match nothing, so the term after it is equally
690
- // able to fail first — deriving from term 0 alone named a token the parse
691
- // never actually required. A leading `not(…)` is zero-width and contributes
692
- // nothing (its own expectation is about what must NOT be here), but it is
693
- // nullable, so keep scanning past it. Stop at the first term that must match.
694
- case "sequence": {
695
- const out = [];
696
- for (const term of def.parsers) {
697
- if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
698
- if (!matchesEmpty(term)) break;
671
+ function visitLabeledTrivia(input, cur, spec, state, visit) {
672
+ if (spec.minRepeats > 1) return void 0;
673
+ let pos = cur;
674
+ let count = 0;
675
+ while (pos < input.length) {
676
+ let matched = null;
677
+ for (const arm of spec.arms) {
678
+ const match = matchArmAt(input, pos, arm.parser, state);
679
+ if (match) {
680
+ matched = { end: match.end, kindIndex: arm.kindIndex };
681
+ break;
699
682
  }
700
- return out;
701
683
  }
702
- case "attempt":
703
- return deriveExpected2(def.parser);
704
- // Delegating wrappers whose first-token failure is their inner parser's — must
705
- // pass through so a start-failure (and the first-set-miss fast-path in attempt/
706
- // node, which reads this) names the real expected token, not the wrapper tag.
707
- case "field":
708
- case "withCtx":
709
- case "recover":
710
- return deriveExpected2(def.parser);
711
- case "skip":
712
- return deriveExpected2(def.main);
713
- case "expect":
714
- return def.expected;
715
- case "node":
716
- case "grammar":
717
- case "trivia":
718
- case "token":
719
- case "leaf":
720
- case "optional":
721
- case "many":
722
- case "oneOrMore":
723
- case "transform":
724
- case "not":
725
- case "peek":
726
- return deriveExpected2(def.parser);
727
- case "routed":
728
- return ["routed()"];
729
- case "lazy": {
730
- const name = c._ruleName;
731
- if (seen.has(c)) return name ? [name] : [];
732
- seen.add(c);
733
- try {
734
- return deriveExpected2(def.thunk());
735
- } catch {
736
- return name ? [name] : [];
737
- } finally {
738
- seen.delete(c);
684
+ if (!matched) break;
685
+ visit(pos, matched.end, matched.kindIndex);
686
+ pos = matched.end;
687
+ count++;
688
+ }
689
+ return count < spec.minRepeats ? cur : pos;
690
+ }
691
+ function recordTriviaChunks(ctx, chunks) {
692
+ const kinds = ctx.triviaKindLabels;
693
+ const mask = ctx._triviaCaptureMask;
694
+ const rootLog = ctx._rootTriviaLog;
695
+ const rootKinds = ctx._rootTriviaKindIndex;
696
+ const rootMark = rootLog?.length ?? 0;
697
+ for (const ch of chunks) {
698
+ pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
699
+ const rootKindIndex = ctx._rootTriviaCapture === false || rootLog === void 0 || rootKinds === void 0 || kinds === void 0 ? -1 : rootKinds[kinds[ch.kindIndex] ?? ""] ?? -1;
700
+ if (rootKindIndex >= 0) {
701
+ rootLog.push(0, 0, ch.start, ch.end, rootKindIndex);
702
+ }
703
+ if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
704
+ if (mask === void 0 || kinds === void 0 || (mask & 1 << ch.kindIndex) !== 0) {
705
+ pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
739
706
  }
740
707
  }
741
- default:
742
- return [];
743
708
  }
744
- }
745
- function expect(combinator, label2) {
746
- const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
747
- const meta = {
748
- firstSet: combinator._meta.firstSet,
749
- canMatchNewline: combinator._meta.canMatchNewline,
750
- isTrivia: false
751
- };
752
- return {
753
- _tag: "expect",
754
- _meta: meta,
755
- _def: { tag: "expect", parser: combinator, label: label2, expected },
756
- parse(input, pos, ctx) {
757
- const result = combinator.parse(input, pos, ctx);
758
- if (result.ok) return result;
759
- const span = { start: pos, end: pos };
760
- const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
761
- ctx._errors?.push(error);
762
- if (ctx._tolerant) captureError(ctx, error);
763
- return { ok: true, value: error, span: { start: pos, end: pos } };
709
+ if (rootLog !== void 0 && rootLog.length !== rootMark && chunks.length > 0) {
710
+ const start = chunks[0].start;
711
+ const end = chunks[chunks.length - 1].end;
712
+ for (let offset = rootMark; offset < rootLog.length; offset += 5) {
713
+ rootLog[offset] = start;
714
+ rootLog[offset + 1] = end;
764
715
  }
765
- };
716
+ }
766
717
  }
767
-
768
- // src/combinators/choice.ts
769
- function choice(...args) {
770
- const parsers = args.map((a) => "gate" in a ? a.combinator : a);
771
- const gates = args.map((a) => "gate" in a ? a.gate : null);
772
- const hasGates = gates.some((g) => g !== null);
773
- const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
774
- let combined = { kind: "empty" };
775
- for (const p of parsers) combined = union(combined, p._meta.firstSet);
776
- const meta = {
777
- firstSet: combined,
778
- canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
779
- isTrivia: false,
780
- disjoint
781
- };
782
- const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
783
- const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
784
- let greedyLitMap = null;
785
- let sortedParsers = null;
786
- const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
787
- if (strategy?.tag === "greedyClassify") {
788
- greedyLitMap = /* @__PURE__ */ new Map();
789
- for (let i = 0; i < parsers.length; i++) {
790
- if (i === strategy.superIndex) continue;
791
- const litVal = getCoreLiteralValue(parsers[i]);
792
- if (litVal !== null) greedyLitMap.set(litVal, i);
793
- }
794
- } else if (strategy?.tag === "literalsLongestFirst") {
795
- sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
718
+ function triviaKindMask(labels, keep) {
719
+ if (!labels) return void 0;
720
+ let mask = 0;
721
+ for (const name of keep) {
722
+ const idx = labels.indexOf(name);
723
+ if (idx >= 0) mask |= 1 << idx;
724
+ }
725
+ return mask;
726
+ }
727
+
728
+ // src/combinators/grammar.ts
729
+ function annotateResultLines(result, index) {
730
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
731
+ const errors = annotated.errors;
732
+ if (errors) {
733
+ ;
734
+ annotated.errors = errors.map((error) => ({
735
+ ...error,
736
+ span: annotateSpan(error.span, index)
737
+ }));
738
+ }
739
+ return annotated;
740
+ }
741
+ function createParseLineContext(input, pos) {
742
+ const lineIndex = createLineIndex();
743
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
744
+ return { lineIndex, lineScannedTo: pos };
745
+ }
746
+ function parser(opts, root) {
747
+ const clearTrivia = opts.trivia === null;
748
+ const opaqueRootCapture = opts.rootCapture === "opaque";
749
+ if (opaqueRootCapture && opts.trivia === void 0) {
750
+ throw new TypeError("parser({ rootCapture: 'opaque' }) requires an explicit trivia scope.");
796
751
  }
797
752
  return {
798
- _tag: "choice",
799
- _meta: meta,
753
+ _tag: "grammar",
754
+ _meta: {
755
+ ...root._meta,
756
+ // Trivia classification describes this grammar's own active trivia
757
+ // scope. A directly nested grammar may have classified trivia of its
758
+ // own, but that must not advertise classification for this wrapper.
759
+ triviaKindLabels: void 0,
760
+ rootTriviaClassified: void 0,
761
+ ...opts.trivia?._meta?.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {},
762
+ ...opts.trivia?._meta?.rootTriviaClassified ? { rootTriviaClassified: true } : {}
763
+ },
800
764
  _def: {
801
- tag: "choice",
802
- parsers,
803
- gates,
804
- disjoint,
805
- strategy: strategy ?? { tag: "firstMatch" },
806
- autoNot
765
+ tag: "grammar",
766
+ parser: root,
767
+ triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
768
+ clearTrivia,
769
+ ...opaqueRootCapture ? { rootCapture: "opaque" } : {},
770
+ ...opts.captureTrivia ? { captureTrivia: true } : {},
771
+ trackLines: opts.trackLines ?? false
807
772
  },
808
- parse(input, pos, ctx) {
809
- const expected = [];
810
- if (disjoint && pos < input.length) {
811
- const code = input.codePointAt(pos);
812
- let idx = code < 128 ? asciiDispatch[code] : -1;
813
- if (idx < 0) {
814
- for (let i = 0; i < parsers.length; i++) {
815
- if (inFirstSet(code, parsers[i]._meta.firstSet)) {
816
- idx = i;
817
- break;
818
- }
819
- }
820
- }
821
- if (idx >= 0) {
822
- const gate = gates[idx];
823
- if (gate && !gate(ctx.state)) {
824
- return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
825
- }
826
- const result = parsers[idx].parse(input, pos, ctx);
827
- if (result.ok) return result;
828
- expected.push(...result.expected);
829
- if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
830
- return { ok: false, expected, span: { start: pos, end: pos } };
831
- }
832
- return {
833
- ok: false,
834
- expected: parsers.flatMap((p) => {
835
- const r = p.parse(input, pos, ctx);
836
- return r.ok ? [] : r.expected;
837
- }),
838
- span: { start: pos, end: pos }
839
- };
840
- }
841
- if (strategy?.tag === "greedyClassify") {
842
- const superResult = parsers[strategy.superIndex].parse(input, pos, ctx);
843
- if (!superResult.ok) return superResult;
844
- const end = superResult.span.end;
845
- const litIdx = greedyLitMap.get(input.slice(pos, end));
846
- if (litIdx !== void 0) {
847
- const litVal = getCoreLiteralValue(parsers[litIdx]);
848
- const value = applyTransforms(parsers[litIdx], litVal, { start: pos, end });
849
- return { ok: true, value, span: { start: pos, end } };
850
- }
851
- return superResult;
852
- }
853
- if (strategy?.tag === "literalsLongestFirst") {
854
- for (const p of sortedParsers) {
855
- const r = p.parse(input, pos, ctx);
856
- if (r.ok) return r;
857
- expected.push(...r.expected);
858
- }
859
- return { ok: false, expected, span: { start: pos, end: pos } };
773
+ parse(input, pos, _ctx) {
774
+ if (_ctx?._rootTriviaStrictScopes && opts.trivia !== void 0 && opts.trivia !== null && !opts.trivia._meta.rootTriviaClassified && !opaqueRootCapture) {
775
+ throw new TypeError(
776
+ "parser(): selected root trivia requires classifiedTrivia() for every local trivia scope, or rootCapture: 'opaque'."
777
+ );
860
778
  }
861
- for (let i = 0; i < parsers.length; i++) {
862
- if (gates[i] && !gates[i](ctx.state)) continue;
863
- const mark = saveCstMark(ctx);
864
- const logLen = ctx._triviaLog?.length;
865
- const result = parsers[i].parse(input, pos, ctx);
866
- if (!result.ok) {
867
- rollbackCstCapture(ctx, mark);
868
- if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
869
- expected.push(...result.expected);
870
- if (result.committed) return { ok: false, expected, span: result.span, committed: true };
871
- continue;
872
- }
873
- const checks = autoNot[i];
874
- if (checks && autoNotFires(input, result.span.end, checks)) {
875
- rollbackCstCapture(ctx, mark);
876
- if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
877
- continue;
878
- }
879
- return result;
779
+ const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
780
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
781
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
782
+ const ctx = {
783
+ ..._ctx,
784
+ trackLines,
785
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
786
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
787
+ // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
788
+ ...clearTrivia ? {
789
+ trivia: void 0,
790
+ triviaKindLabels: void 0
791
+ } : opts.trivia != null ? {
792
+ trivia: opts.trivia,
793
+ ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
794
+ } : {},
795
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
796
+ // Kind-filter for per-node capture. Resolve against this scope's trivia
797
+ // labels — this parser's own trivia if it declares one, else the INHERITED
798
+ // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
799
+ // trivia is inherited rather than re-declared here. No labels → undefined
800
+ // (capture all).
801
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {},
802
+ // This is a property of the local trivia scope, not a post-parse filter:
803
+ // nested recognition can still skip its trivia, but selected root rows
804
+ // must never be written for an explicitly opaque region.
805
+ ...opaqueRootCapture ? { _rootTriviaCapture: false } : {}
806
+ };
807
+ const result = root.parse(input, pos ?? 0, ctx);
808
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
809
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
880
810
  }
881
- return { ok: false, expected, span: { start: pos, end: pos } };
811
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
882
812
  }
883
813
  };
884
814
  }
885
- function detectStrategy(parsers) {
886
- const regexIndices = [];
887
- const literalIndices = [];
888
- for (let i = 0; i < parsers.length; i++) {
889
- if (getCoreRegexDef(parsers[i]) !== null) regexIndices.push(i);
890
- else if (getCoreLiteralValue(parsers[i]) !== null) literalIndices.push(i);
815
+
816
+ // src/cst/reflection.ts
817
+ var GRAMMAR_REFLECTION = /* @__PURE__ */ Symbol.for("parseman.grammarReflection");
818
+ function childrenOf(def) {
819
+ switch (def.tag) {
820
+ case "sequence":
821
+ case "choice":
822
+ return def.parsers;
823
+ case "dispatch":
824
+ return [
825
+ def.selector,
826
+ ...def.cases.map((c) => c.parser),
827
+ ...def.matchers ? def.matchers.map((c) => c.parser) : [],
828
+ ...def.otherwise ? [def.otherwise] : []
829
+ ];
830
+ case "many":
831
+ case "oneOrMore":
832
+ case "optional":
833
+ case "attempt":
834
+ case "transform":
835
+ case "trivia":
836
+ case "token":
837
+ case "leaf":
838
+ case "label":
839
+ case "field":
840
+ case "not":
841
+ case "peek":
842
+ case "node":
843
+ case "withCtx":
844
+ case "recover":
845
+ case "expect":
846
+ return [def.parser];
847
+ case "grammar":
848
+ return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
849
+ case "sepBy":
850
+ return [def.parser, def.separator];
851
+ case "skip":
852
+ return [def.main, def.skipped];
853
+ case "scanTo":
854
+ return [def.sentinel, ...def.skip];
855
+ case "routed":
856
+ return def.fallback ? [def.fallback] : [];
857
+ case "literal":
858
+ case "regex":
859
+ case "keywords":
860
+ case "guard":
861
+ case "lazy":
862
+ case "unknown":
863
+ return [];
891
864
  }
892
- if (regexIndices.length === 1 && literalIndices.length === parsers.length - 1 && literalIndices.length > 0) {
893
- const superIndex = regexIndices[0];
894
- const regexDef = getCoreRegexDef(parsers[superIndex]);
895
- const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
896
- const re = new RegExp(regexDef.source, flags);
897
- const allSubsumed = literalIndices.every((i) => {
898
- const litVal = getCoreLiteralValue(parsers[i]);
899
- re.lastIndex = 0;
900
- const m = re.exec(litVal);
901
- return m !== null && m[0] === litVal;
902
- });
903
- if (allSubsumed) return { tag: "greedyClassify", superIndex };
865
+ }
866
+ function addNode(out, def) {
867
+ if (def.type === void 0) return;
868
+ const existing = out.get(def.type);
869
+ if (existing === void 0) {
870
+ out.set(def.type, def.tags === void 0 ? [] : [...def.tags]);
871
+ return;
904
872
  }
905
- if (parsers.length === literalIndices.length) {
906
- const sortedIndices = [...literalIndices].sort(
907
- (a, b) => getCoreLiteralValue(parsers[b]).length - getCoreLiteralValue(parsers[a]).length
908
- );
909
- return { tag: "literalsLongestFirst", sortedIndices };
873
+ if (def.tags === void 0) return;
874
+ for (const tag of def.tags) {
875
+ if (!existing.includes(tag)) existing.push(tag);
910
876
  }
911
- const shared = detectSharedPrefix(parsers);
912
- if (shared !== null) return shared;
913
- return { tag: "firstMatch" };
914
- }
915
- function bareLeadingTermKey(term) {
916
- const d = term._def;
917
- if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
918
- if (d.tag === "regex") return `R:${d.source}\0${d.flags}`;
919
- return null;
920
877
  }
921
- function detectSharedPrefix(parsers) {
922
- if (parsers.length < 2) return null;
923
- let key = null;
924
- let prefix = null;
925
- const members = [];
926
- for (let i = 0; i < parsers.length; i++) {
927
- const term0 = leadingTermOfArm(parsers[i]);
928
- if (term0 === null) return null;
929
- const k = bareLeadingTermKey(term0);
930
- if (k === null) return null;
931
- if (key === null) {
932
- key = k;
933
- prefix = term0;
934
- } else if (k !== key) return null;
935
- members.push(i);
878
+ function collectFromCombinator(parser2, out, seen, followLazy = true) {
879
+ if (seen.has(parser2)) return;
880
+ seen.add(parser2);
881
+ const def = parser2._def;
882
+ if (def.tag === "lazy") {
883
+ if (followLazy) {
884
+ try {
885
+ collectFromCombinator(def.thunk(), out, seen, followLazy);
886
+ } catch {
887
+ }
888
+ }
889
+ return;
936
890
  }
937
- if (prefix === null || members.length < 2) return null;
938
- return { tag: "sharedPrefix", prefix, members };
891
+ if (def.tag === "node") addNode(out, def);
892
+ for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy);
939
893
  }
940
- function leadingTermOfArm(arm) {
941
- let d = arm._def;
942
- for (; ; ) {
943
- if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
944
- d = d.parser._def;
894
+ function collectGrammarReflection(ruleMap, opts) {
895
+ const byType = /* @__PURE__ */ new Map();
896
+ const followLazy = opts?.followLazy !== false;
897
+ for (const [, rule] of ruleMap) {
898
+ if (!followLazy && rule._def.tag === "lazy") {
899
+ try {
900
+ const resolved = rule._def.thunk();
901
+ if (resolved._def.tag !== "lazy") collectFromCombinator(resolved, byType, /* @__PURE__ */ new Set(), false);
902
+ } catch {
903
+ }
945
904
  continue;
946
905
  }
947
- break;
906
+ collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
948
907
  }
949
- if (d.tag !== "sequence" || d.parsers.length < 2) return null;
950
- return d.parsers[0];
908
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
951
909
  }
952
- function computeAutoNot(parsers) {
953
- return parsers.map((p, i) => {
954
- const litVal = getCoreLiteralValue(p);
955
- if (litVal === null) return null;
956
- const checks = [];
957
- for (let j2 = i + 1; j2 < parsers.length; j2++) {
958
- const other = parsers[j2];
959
- const otherLit = getCoreLiteralValue(other);
960
- if (otherLit !== null && otherLit.startsWith(litVal) && otherLit.length > litVal.length) {
961
- checks.push({ kind: "startsWith", value: otherLit.slice(litVal.length) });
962
- continue;
910
+ function attachGrammarReflection(grammar, reflection) {
911
+ Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true });
912
+ return grammar;
913
+ }
914
+
915
+ // src/combinators/parser.ts
916
+ var RULE_ORDER = "__parsemanRuleOrder";
917
+ function tagRule(r, key) {
918
+ ;
919
+ r._ruleName = key;
920
+ if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
921
+ }
922
+ function isDefinableRef(v) {
923
+ return !!v && typeof v === "object" && "_def" in v && v._def.tag === "lazy" && typeof v.define === "function";
924
+ }
925
+ function ruleNameOf(r) {
926
+ return r._ruleName;
927
+ }
928
+ function isNamedRuleRefForAnotherRule(r, key) {
929
+ const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
930
+ return name !== void 0 && name !== key;
931
+ }
932
+ function rules(a, b) {
933
+ const factory = typeof a === "function" ? a : b;
934
+ const options = typeof a === "function" ? b : a;
935
+ const cache = {};
936
+ const proxy = new Proxy(cache, {
937
+ get(target, key) {
938
+ if (typeof key !== "string") return void 0;
939
+ const record = target;
940
+ if (!(key in record)) {
941
+ const r = ref();
942
+ tagRule(r, key);
943
+ record[key] = r;
963
944
  }
964
- const regexDef = getCoreRegexDef(other);
965
- if (regexDef !== null) {
966
- const contSet = continuationFirstSet(litVal, regexDef.source, regexDef.flags);
967
- if (contSet !== null) checks.push({ kind: "firstSet", set: contSet });
945
+ return record[key];
946
+ }
947
+ });
948
+ const definitions = factory(proxy);
949
+ for (const key of Object.keys(definitions)) {
950
+ const placeholder = cache[key];
951
+ const parser2 = definitions[key];
952
+ if (placeholder === parser2) {
953
+ throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
954
+ }
955
+ if (isDefinableRef(placeholder)) {
956
+ if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
957
+ placeholder.define(parser2);
958
+ placeholder._meta.firstSet = parser2._meta.firstSet;
959
+ placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
960
+ } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
961
+ const alias = ref();
962
+ tagRule(alias, key);
963
+ alias.define(parser2);
964
+ alias._meta.firstSet = parser2._meta.firstSet;
965
+ alias._meta.canMatchNewline = parser2._meta.canMatchNewline;
966
+ cache[key] = alias;
967
+ } else {
968
+ tagRule(parser2, key);
969
+ cache[key] = parser2;
970
+ }
971
+ }
972
+ if (options?.trivia != null) {
973
+ for (const key of Object.keys(definitions)) {
974
+ const rule = cache[key];
975
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
976
+ }
977
+ }
978
+ if (options?.scanSkip != null) {
979
+ for (const key of Object.keys(definitions)) {
980
+ const rule = cache[key];
981
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
982
+ }
983
+ }
984
+ if (options?.hostMode === "cst") {
985
+ for (const key of Object.keys(definitions)) {
986
+ const rule = cache[key];
987
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
988
+ }
989
+ }
990
+ if (options?.trackLines === true) {
991
+ for (const key of Object.keys(definitions)) {
992
+ const rule = cache[key];
993
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
994
+ }
995
+ for (const key of Object.keys(definitions)) {
996
+ const rule = cache[key];
997
+ if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
998
+ const wrapped = parser({ trackLines: true }, rule);
999
+ tagRule(wrapped, key);
1000
+ wrapped._meta.grammarTrackLines = true;
1001
+ cache[key] = wrapped;
968
1002
  }
969
1003
  }
970
- return checks.length > 0 ? checks : null;
971
- });
1004
+ }
1005
+ for (const key of Object.keys(definitions)) {
1006
+ markUnusedValues(cache[key]);
1007
+ }
1008
+ Object.defineProperty(cache, RULE_ORDER, {
1009
+ value: Object.keys(definitions),
1010
+ enumerable: false,
1011
+ configurable: true
1012
+ });
1013
+ attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
1014
+ return cache;
1015
+ }
1016
+
1017
+ // src/recovery/scan.ts
1018
+ function probeAt(sentinel, input, pos, ctx) {
1019
+ const s = {
1020
+ _probe: ctx._probe,
1021
+ _errors: ctx._errors,
1022
+ _cstLeaves: ctx._cstLeaves,
1023
+ _cstChildren: ctx._cstChildren,
1024
+ _cstRawChildren: ctx._cstRawChildren,
1025
+ _cstTriviaLog: ctx._cstTriviaLog,
1026
+ _triviaLog: ctx._triviaLog,
1027
+ _rootTriviaLog: ctx._rootTriviaLog,
1028
+ _fields: ctx._fields,
1029
+ _cstBuf: ctx._cstBuf,
1030
+ _tolerant: ctx._tolerant,
1031
+ _sync: ctx._sync
1032
+ };
1033
+ ctx._probe = void 0;
1034
+ ctx._errors = void 0;
1035
+ ctx._cstLeaves = void 0;
1036
+ ctx._cstChildren = void 0;
1037
+ ctx._cstRawChildren = void 0;
1038
+ ctx._cstTriviaLog = void 0;
1039
+ ctx._triviaLog = void 0;
1040
+ ctx._rootTriviaLog = void 0;
1041
+ ctx._fields = void 0;
1042
+ ctx._cstBuf = void 0;
1043
+ ctx._tolerant = void 0;
1044
+ ctx._sync = void 0;
1045
+ try {
1046
+ return sentinel.parse(input, pos, ctx).ok;
1047
+ } finally {
1048
+ ctx._probe = s._probe;
1049
+ ctx._errors = s._errors;
1050
+ ctx._cstLeaves = s._cstLeaves;
1051
+ ctx._cstChildren = s._cstChildren;
1052
+ ctx._cstRawChildren = s._cstRawChildren;
1053
+ ctx._cstTriviaLog = s._cstTriviaLog;
1054
+ ctx._triviaLog = s._triviaLog;
1055
+ ctx._rootTriviaLog = s._rootTriviaLog;
1056
+ ctx._fields = s._fields;
1057
+ ctx._cstBuf = s._cstBuf;
1058
+ ctx._tolerant = s._tolerant;
1059
+ ctx._sync = s._sync;
1060
+ }
1061
+ }
1062
+ function orSentinel(a, b) {
1063
+ if (b === void 0) return a;
1064
+ return {
1065
+ _tag: "orSentinel",
1066
+ _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
1067
+ _def: { tag: "unknown" },
1068
+ parse(input, pos, ctx) {
1069
+ const ra = a.parse(input, pos, ctx);
1070
+ if (ra.ok) return ra;
1071
+ return b.parse(input, pos, ctx);
1072
+ }
1073
+ };
972
1074
  }
973
- function autoNotFires(input, end, checks) {
974
- for (const check of checks) {
975
- if (check.kind === "firstSet") {
976
- const code = end < input.length ? input.codePointAt(end) ?? -1 : -1;
977
- if (inFirstSet(code, check.set)) return true;
978
- } else {
979
- if (input.startsWith(check.value, end)) return true;
1075
+ function firstSetSentinel(fs) {
1076
+ if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
1077
+ const ranges = fs.ranges;
1078
+ return {
1079
+ _tag: "firstSetSentinel",
1080
+ _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },
1081
+ _def: { tag: "unknown" },
1082
+ parse(input, pos) {
1083
+ const code = pos < input.length ? input.codePointAt(pos) : void 0;
1084
+ if (code !== void 0) {
1085
+ for (const r of ranges) {
1086
+ if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
1087
+ }
1088
+ }
1089
+ return { ok: false, expected: [], span: { start: pos, end: pos } };
980
1090
  }
981
- }
982
- return false;
1091
+ };
983
1092
  }
984
- function getCoreLiteralValue(p) {
985
- const def = p._def;
986
- if (def.tag === "literal" && !def.caseInsensitive) return def.value;
987
- if (def.tag === "transform") return getCoreLiteralValue(def.parser);
988
- return null;
1093
+ function recoverScan(input, from, ctx, sync, expected) {
1094
+ let scanPos = from;
1095
+ while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1096
+ const span = { start: from, end: scanPos };
1097
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
1098
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1099
+ ctx._errors?.push(error);
1100
+ return { error, end: scanPos };
989
1101
  }
990
- function getCoreRegexDef(p) {
991
- const def = p._def;
992
- if (def.tag === "regex") return { source: def.source, flags: def.flags };
993
- if (def.tag === "transform") return getCoreRegexDef(def.parser);
994
- if (def.tag === "label") return getCoreRegexDef(def.parser);
995
- return null;
1102
+ function matchesAt(sentinel, input, pos, ctx) {
1103
+ return probeAt(sentinel, input, pos, ctx);
996
1104
  }
997
- function applyTransforms(p, value, span) {
998
- const def = p._def;
999
- if (def.tag === "transform") {
1000
- const inner = applyTransforms(def.parser, value, span);
1001
- return def.fn(inner, span);
1002
- }
1003
- return value;
1105
+ function captureError(ctx, error) {
1106
+ if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
1004
1107
  }
1005
- function continuationFirstSet(lit, source, flags) {
1006
- const re = new RegExp(source, "y" + flags.replace(/[gy]/g, ""));
1007
- re.lastIndex = 0;
1008
- const base = re.exec(lit);
1009
- if (!base || base[0] !== lit) return null;
1010
- const contCodes = [];
1011
- for (let code = 1; code < 128; code++) {
1012
- re.lastIndex = 0;
1013
- const m = re.exec(lit + String.fromCharCode(code));
1014
- if (m && m[0].length > lit.length) contCodes.push(code);
1015
- }
1016
- if (contCodes.length === 0) return null;
1017
- return codesToFirstSet(contCodes);
1108
+
1109
+ // src/combinators/expect.ts
1110
+ function deriveExpected(c) {
1111
+ return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
1018
1112
  }
1019
- function codesToFirstSet(codes) {
1020
- codes.sort((a, b) => a - b);
1021
- const ranges = [];
1022
- let lo = codes[0], hi = codes[0];
1023
- for (let i = 1; i < codes.length; i++) {
1024
- if (codes[i] === hi + 1) {
1025
- hi = codes[i];
1026
- } else {
1027
- ranges.push({ lo, hi });
1028
- lo = hi = codes[i];
1113
+ function derive(c, seen) {
1114
+ const deriveExpected2 = (p) => derive(p, seen);
1115
+ const def = c._def;
1116
+ switch (def.tag) {
1117
+ case "literal":
1118
+ return [JSON.stringify(def.value)];
1119
+ case "regex":
1120
+ return [`/${def.source}/`];
1121
+ case "keywords":
1122
+ return def.words.map((w) => JSON.stringify(w));
1123
+ case "label":
1124
+ return [def.label];
1125
+ case "choice":
1126
+ return def.parsers.flatMap(deriveExpected2);
1127
+ case "dispatch":
1128
+ return deriveExpected2(def.selector);
1129
+ // Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
1130
+ // `optional(…)`/`many(…)` can match nothing, so the term after it is equally
1131
+ // able to fail first — deriving from term 0 alone named a token the parse
1132
+ // never actually required. A leading `not(…)` is zero-width and contributes
1133
+ // nothing (its own expectation is about what must NOT be here), but it is
1134
+ // nullable, so keep scanning past it. Stop at the first term that must match.
1135
+ case "sequence": {
1136
+ const out = [];
1137
+ for (const term of def.parsers) {
1138
+ if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
1139
+ if (!matchesEmpty(term)) break;
1140
+ }
1141
+ return out;
1029
1142
  }
1030
- }
1031
- ranges.push({ lo, hi });
1032
- return { kind: "ranges", ranges };
1033
- }
1034
- function inFirstSet(code, fs) {
1035
- if (fs.kind === "any") return true;
1036
- if (fs.kind === "empty") return false;
1037
- for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
1038
- return false;
1039
- }
1040
- function areDisjoint(sets) {
1041
- if (sets.some((s) => s.kind === "any")) return false;
1042
- for (let i = 0; i < sets.length; i++)
1043
- for (let j2 = i + 1; j2 < sets.length; j2++)
1044
- if (intersects(sets[i], sets[j2])) return false;
1045
- return true;
1046
- }
1047
- function buildAsciiDispatch(parsers) {
1048
- const table = Array(128).fill(-1);
1049
- for (let i = 0; i < parsers.length; i++) {
1050
- const fs = parsers[i]._meta.firstSet;
1051
- if (fs.kind !== "ranges") continue;
1052
- for (const { lo, hi } of fs.ranges) {
1053
- for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
1054
- table[code] = i;
1143
+ case "attempt":
1144
+ return deriveExpected2(def.parser);
1145
+ // Delegating wrappers whose first-token failure is their inner parser's — must
1146
+ // pass through so a start-failure (and the first-set-miss fast-path in attempt/
1147
+ // node, which reads this) names the real expected token, not the wrapper tag.
1148
+ case "field":
1149
+ case "withCtx":
1150
+ case "recover":
1151
+ return deriveExpected2(def.parser);
1152
+ case "skip":
1153
+ return deriveExpected2(def.main);
1154
+ case "expect":
1155
+ return def.expected;
1156
+ case "node":
1157
+ case "grammar":
1158
+ case "trivia":
1159
+ case "token":
1160
+ case "leaf":
1161
+ case "optional":
1162
+ case "many":
1163
+ case "oneOrMore":
1164
+ case "transform":
1165
+ case "not":
1166
+ case "peek":
1167
+ return deriveExpected2(def.parser);
1168
+ // A bare routed() can only fail as "there is no routed token here". With a
1169
+ // fallback, THAT is the branch that can fail, so its expectations are the useful
1170
+ // ones — the routed path either succeeds or is not taken.
1171
+ case "routed":
1172
+ return def.fallback === void 0 ? ["routed()"] : deriveExpected2(def.fallback);
1173
+ case "lazy": {
1174
+ const name = c._ruleName;
1175
+ if (seen.has(c)) return name ? [name] : [];
1176
+ seen.add(c);
1177
+ try {
1178
+ return deriveExpected2(def.thunk());
1179
+ } catch {
1180
+ return name ? [name] : [];
1181
+ } finally {
1182
+ seen.delete(c);
1055
1183
  }
1056
1184
  }
1185
+ default:
1186
+ return [];
1057
1187
  }
1058
- return table;
1059
- }
1060
-
1061
- // src/cst/trivia-kinds.ts
1062
- function unwrapTrivia(p) {
1063
- let cur = p;
1064
- while (cur._def.tag === "trivia") cur = cur._def.parser;
1065
- return cur;
1066
1188
  }
1067
- function peelLabel(p) {
1068
- if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
1069
- return null;
1070
- }
1071
- function analyzeLabeledTrivia(trivia2) {
1072
- let core = unwrapTrivia(trivia2);
1073
- let minRepeats = 1;
1074
- if (core._def.tag === "oneOrMore") {
1075
- core = core._def.parser;
1076
- minRepeats = 1;
1077
- } else if (core._def.tag === "many") {
1078
- core = core._def.parser;
1079
- minRepeats = 0;
1080
- }
1081
- const arms = [];
1082
- if (core._def.tag === "choice") {
1083
- for (let i = 0; i < core._def.parsers.length; i++) {
1084
- const peeled = peelLabel(core._def.parsers[i]);
1085
- if (!peeled) return null;
1086
- arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
1189
+ function expect(combinator, label2) {
1190
+ const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
1191
+ const meta = {
1192
+ firstSet: combinator._meta.firstSet,
1193
+ canMatchNewline: combinator._meta.canMatchNewline,
1194
+ isTrivia: false
1195
+ };
1196
+ return {
1197
+ _tag: "expect",
1198
+ _meta: meta,
1199
+ _def: { tag: "expect", parser: combinator, label: label2, expected },
1200
+ parse(input, pos, ctx) {
1201
+ const result = combinator.parse(input, pos, ctx);
1202
+ if (result.ok) return result;
1203
+ const span = { start: pos, end: pos };
1204
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1205
+ ctx._errors?.push(error);
1206
+ if (ctx._tolerant) captureError(ctx, error);
1207
+ return { ok: true, value: error, span: { start: pos, end: pos } };
1087
1208
  }
1088
- } else {
1089
- const peeled = peelLabel(core);
1090
- if (!peeled) return null;
1091
- arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
1092
- }
1093
- return { labels: arms.map((a) => a.label), arms, minRepeats };
1209
+ };
1210
+ }
1211
+
1212
+ // src/regex/classes.ts
1213
+ var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1214
+ var SPACE_RANGES = [
1215
+ [9, 13],
1216
+ [32, 32],
1217
+ [160, 160],
1218
+ [5760, 5760],
1219
+ [8192, 8202],
1220
+ [8232, 8232],
1221
+ [8233, 8233],
1222
+ [8239, 8239],
1223
+ [8287, 8287],
1224
+ [12288, 12288],
1225
+ [65279, 65279]
1226
+ ];
1227
+ function shorthandRanges(ch) {
1228
+ if (ch === "d") return [[48, 57]];
1229
+ if (ch === "s") return SPACE_RANGES;
1230
+ return [[48, 57], [65, 90], [97, 122], [95, 95]];
1094
1231
  }
1095
- function matchArmAt(input, pos, arm) {
1096
- const r = arm.parse(input, pos, { trackLines: false });
1097
- if (!r.ok || r.span.end <= pos) return null;
1098
- return { end: r.span.end };
1232
+ function readUnicodeEscape(body, i) {
1233
+ if (body[i] !== "\\" || body[i + 1] !== "u") return null;
1234
+ const hex = body.slice(i + 2, i + 6);
1235
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
1236
+ return { cp: Number.parseInt(hex, 16), next: i + 6 };
1099
1237
  }
1100
- function scanLabeledTriviaChunks(input, cur, spec) {
1101
- const chunks = [];
1102
- let pos = cur;
1103
- while (pos < input.length) {
1104
- let matched = null;
1105
- for (const arm of spec.arms) {
1106
- const m = matchArmAt(input, pos, arm.parser);
1107
- if (m) {
1108
- matched = { end: m.end, kindIndex: arm.kindIndex };
1109
- break;
1238
+ function parseClassRanges(body) {
1239
+ const ranges = [];
1240
+ let i = 0;
1241
+ const readAtom = () => {
1242
+ const ch = body[i];
1243
+ if (ch === void 0) return null;
1244
+ if (ch === "\\") {
1245
+ const uni = readUnicodeEscape(body, i);
1246
+ if (uni) {
1247
+ i = uni.next;
1248
+ return { cp: uni.cp };
1110
1249
  }
1250
+ const e = body[i + 1];
1251
+ if (e === void 0) return null;
1252
+ i += 2;
1253
+ if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1254
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
1255
+ if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1256
+ return { cp: e.codePointAt(0) };
1111
1257
  }
1112
- if (!matched) break;
1113
- chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
1114
- pos = matched.end;
1115
- }
1116
- if (chunks.length < spec.minRepeats) {
1117
- return { end: cur, chunks: [] };
1118
- }
1119
- return { end: pos, chunks };
1120
- }
1121
- function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
1122
- const chunks = [];
1123
- let pos = cur;
1124
- while (pos < input.length) {
1125
- const c = input.charCodeAt(pos);
1126
- if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {
1127
- const start = pos;
1128
- pos++;
1129
- while (pos < input.length) {
1130
- const c2 = input.charCodeAt(pos);
1131
- if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) pos++;
1132
- else break;
1133
- }
1134
- chunks.push({ start, end: pos, kindIndex: wsKind });
1258
+ i += ch.length;
1259
+ return { cp: ch.codePointAt(0) };
1260
+ };
1261
+ while (i < body.length) {
1262
+ const lo = readAtom();
1263
+ if (lo === null) return null;
1264
+ if ("set" in lo) {
1265
+ ranges.push(...lo.set);
1135
1266
  continue;
1136
1267
  }
1137
- if (c === 47 && input.charCodeAt(pos + 1) === 42) {
1138
- let j2 = pos + 2;
1139
- while (j2 + 1 < input.length && !(input.charCodeAt(j2) === 42 && input.charCodeAt(j2 + 1) === 47)) j2++;
1140
- if (j2 + 1 < input.length && input.charCodeAt(j2) === 42 && input.charCodeAt(j2 + 1) === 47) {
1141
- const start = pos;
1142
- pos = j2 + 2;
1143
- chunks.push({ start, end: pos, kindIndex: commentKind });
1144
- continue;
1145
- }
1146
- break;
1268
+ if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
1269
+ i += 1;
1270
+ const hi = readAtom();
1271
+ if (hi === null || "set" in hi) return null;
1272
+ ranges.push([lo.cp, hi.cp]);
1273
+ } else {
1274
+ ranges.push([lo.cp, lo.cp]);
1147
1275
  }
1148
- break;
1149
1276
  }
1150
- return { end: pos, chunks };
1277
+ return ranges.length ? ranges : null;
1151
1278
  }
1152
- function tryFastLabeledScan(input, cur, trivia2) {
1153
- const spec = analyzeLabeledTrivia(trivia2);
1154
- if (!spec || spec.arms.length !== 2) return null;
1155
- const wsArm = spec.arms.find((a) => {
1156
- const src = getCoreRegexDef(a.parser)?.source;
1157
- return src != null && !src.includes("\\*");
1158
- });
1159
- const commentArm = spec.arms.find((a) => {
1160
- const src = getCoreRegexDef(a.parser)?.source;
1161
- return src != null && src.includes("\\*");
1162
- });
1163
- if (!wsArm || !commentArm) return null;
1164
- const { chunks } = scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1165
- if (chunks.length < spec.minRepeats) return { end: cur, chunks: [] };
1166
- return scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1279
+
1280
+ // src/combinators/trivia-skip.ts
1281
+ var NOOP_COMMIT = () => {
1282
+ };
1283
+ var fastTriviaCache = /* @__PURE__ */ new WeakMap();
1284
+ function needsDeferredTriviaCommit(ctx) {
1285
+ return ctx._triviaLog !== void 0 || ctx._rootTriviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
1167
1286
  }
1168
- function recordTriviaChunks(ctx, chunks) {
1169
- const kinds = ctx.triviaKindLabels;
1170
- const mask = ctx._triviaCaptureMask;
1171
- for (const ch of chunks) {
1172
- pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1173
- if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
1174
- if (mask === void 0 || kinds === void 0 || (mask & 1 << ch.kindIndex) !== 0) {
1175
- pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1176
- }
1177
- }
1178
- }
1287
+ function saveTriviaMark(ctx) {
1288
+ const m = saveCstMark(ctx);
1289
+ return {
1290
+ raw: m.raw,
1291
+ tlog: m.tlog,
1292
+ leaves: m.leaves,
1293
+ fields: m.fields,
1294
+ errors: m.errors,
1295
+ log: ctx._triviaLog ? ctx._triviaLog.length : 0,
1296
+ rootLog: ctx._rootTriviaLog ? ctx._rootTriviaLog.length : 0
1297
+ };
1179
1298
  }
1180
- function triviaKindMask(labels, keep) {
1181
- if (!labels) return void 0;
1182
- let mask = 0;
1183
- for (const name of keep) {
1184
- const idx = labels.indexOf(name);
1185
- if (idx >= 0) mask |= 1 << idx;
1186
- }
1187
- return mask;
1299
+ function rollbackTrivia(ctx, mark) {
1300
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1301
+ if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1302
+ if (ctx._rootTriviaLog && ctx._rootTriviaLog.length !== mark.rootLog) ctx._rootTriviaLog.length = mark.rootLog;
1188
1303
  }
1189
-
1190
- // src/combinators/grammar.ts
1191
- function annotateResultLines(result, index) {
1192
- const annotated = { ...result, span: annotateSpan(result.span, index) };
1193
- const errors = annotated.errors;
1194
- if (errors) {
1195
- ;
1196
- annotated.errors = errors.map((error) => ({
1197
- ...error,
1198
- span: annotateSpan(error.span, index)
1199
- }));
1200
- }
1201
- return annotated;
1304
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1305
+ const probeCtx = {
1306
+ trackLines: ctx.trackLines,
1307
+ state: ctx.state,
1308
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1309
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1310
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1311
+ };
1312
+ const result = triviaP.parse(input, cur, probeCtx);
1313
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1314
+ return result;
1202
1315
  }
1203
- function createParseLineContext(input, pos) {
1204
- const lineIndex = createLineIndex();
1205
- if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
1206
- return { lineIndex, lineScannedTo: pos };
1316
+ function saveLookaheadMark(ctx) {
1317
+ return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1207
1318
  }
1208
- function parser(opts, root) {
1209
- const clearTrivia = opts.trivia === null;
1210
- return {
1211
- _tag: "grammar",
1212
- _meta: root._meta,
1213
- _def: {
1214
- tag: "grammar",
1215
- parser: root,
1216
- triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
1217
- clearTrivia,
1218
- ...opts.captureTrivia ? { captureTrivia: true } : {},
1219
- trackLines: opts.trackLines ?? false
1220
- },
1221
- parse(input, pos, _ctx) {
1222
- const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
1223
- const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
1224
- const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
1225
- const ctx = {
1226
- ..._ctx,
1227
- trackLines,
1228
- ...lineIndex ? { _lineIndex: lineIndex } : {},
1229
- ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
1230
- // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
1231
- ...clearTrivia ? {
1232
- trivia: void 0,
1233
- triviaKindLabels: void 0
1234
- } : opts.trivia != null ? {
1235
- trivia: opts.trivia,
1236
- ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
1237
- } : {},
1238
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
1239
- // Kind-filter for per-node capture. Resolve against this scope's trivia
1240
- // labels — this parser's own trivia if it declares one, else the INHERITED
1241
- // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
1242
- // trivia is inherited rather than re-declared here. No labels → undefined
1243
- // (capture all).
1244
- ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
1245
- };
1246
- const result = root.parse(input, pos ?? 0, ctx);
1247
- if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
1248
- _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
1249
- }
1250
- return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
1319
+ function rollbackLookahead(ctx, mark) {
1320
+ rollbackTrivia(ctx, mark);
1321
+ if (ctx._probe) ctx._probe.best = mark.probeBest;
1322
+ }
1323
+ function scanWithLabels(input, cur, ctx) {
1324
+ const triviaP = ctx.trivia;
1325
+ const spec = analyzeLabeledTrivia(triviaP);
1326
+ if (!spec) return { end: cur, commit: NOOP_COMMIT };
1327
+ const log = ctx._triviaLog;
1328
+ const rootLog = ctx._rootTriviaLog;
1329
+ const rootKinds = ctx._rootTriviaKindIndex;
1330
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1331
+ const mask = ctx._triviaCaptureMask;
1332
+ let fullRows;
1333
+ let rootRows;
1334
+ let cstRows;
1335
+ const visitedEnd = visitLabeledTrivia(input, cur, spec, ctx.state, (start, matchEnd, kindIndex) => {
1336
+ if (log !== void 0) (fullRows ??= []).push(start, matchEnd, kindIndex);
1337
+ const selectedKind = rootLog === void 0 || rootKinds === void 0 || ctx._rootTriviaCapture === false ? void 0 : rootKinds[spec.labels[kindIndex] ?? ""];
1338
+ if (selectedKind !== void 0) (rootRows ??= []).push(start, matchEnd, selectedKind);
1339
+ if (captureTl && (mask === void 0 || (mask & 1 << kindIndex) !== 0)) {
1340
+ (cstRows ??= []).push(start, matchEnd, kindIndex);
1251
1341
  }
1342
+ });
1343
+ if (visitedEnd !== void 0) {
1344
+ if (visitedEnd === cur) return { end: cur, commit: NOOP_COMMIT };
1345
+ return {
1346
+ end: visitedEnd,
1347
+ commit: () => {
1348
+ if (fullRows !== void 0 && log !== void 0) log.push(...fullRows);
1349
+ if (rootRows !== void 0 && rootLog !== void 0) {
1350
+ for (let i = 0; i < rootRows.length; i += 3) {
1351
+ rootLog.push(cur, visitedEnd, rootRows[i], rootRows[i + 1], rootRows[i + 2]);
1352
+ }
1353
+ }
1354
+ if (cstRows !== void 0) {
1355
+ for (let i = 0; i < cstRows.length; i += 3) {
1356
+ pushCstTriviaEntry(ctx, cstRows[i], cstRows[i + 1], cstRows[i + 2]);
1357
+ }
1358
+ }
1359
+ }
1360
+ };
1361
+ }
1362
+ const { end, chunks } = scanLabeledTriviaChunks(input, cur, spec, ctx.state);
1363
+ if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1364
+ return {
1365
+ end,
1366
+ commit: () => recordTriviaChunks(ctx, chunks)
1252
1367
  };
1253
1368
  }
1254
-
1255
- // src/cst/reflection.ts
1256
- var GRAMMAR_REFLECTION = /* @__PURE__ */ Symbol.for("parseman.grammarReflection");
1257
- function childrenOf(def) {
1258
- switch (def.tag) {
1259
- case "sequence":
1260
- case "choice":
1261
- return def.parsers;
1262
- case "dispatch":
1263
- return [
1264
- def.selector,
1265
- ...def.cases.map((c) => c.parser),
1266
- ...def.matchers ? def.matchers.map((c) => c.parser) : [],
1267
- ...def.otherwise ? [def.otherwise] : []
1268
- ];
1269
- case "many":
1270
- case "oneOrMore":
1271
- case "optional":
1272
- case "attempt":
1273
- case "transform":
1274
- case "trivia":
1275
- case "token":
1276
- case "leaf":
1277
- case "label":
1278
- case "field":
1279
- case "not":
1280
- case "peek":
1281
- case "node":
1282
- case "withCtx":
1283
- case "recover":
1284
- case "expect":
1285
- return [def.parser];
1286
- case "grammar":
1287
- return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
1288
- case "sepBy":
1289
- return [def.parser, def.separator];
1290
- case "skip":
1291
- return [def.main, def.skipped];
1292
- case "scanTo":
1293
- return [def.sentinel, ...def.skip];
1294
- case "literal":
1295
- case "regex":
1296
- case "keywords":
1297
- case "routed":
1298
- case "guard":
1299
- case "lazy":
1300
- case "unknown":
1301
- return [];
1369
+ function skipWithLabels(input, cur, ctx) {
1370
+ const spec = analyzeLabeledTrivia(ctx.trivia);
1371
+ if (spec) return scanLabeledTriviaEnd(input, cur, spec, ctx.state);
1372
+ const tr = parseTriviaNoCapture(ctx.trivia, input, cur, ctx);
1373
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1374
+ }
1375
+ function advanceTrivia(input, cur, ctx) {
1376
+ const triviaP = ctx.trivia;
1377
+ if (!triviaP) return cur;
1378
+ if (!ctx.trackLines) {
1379
+ const fast2 = fastTriviaScanner(triviaP);
1380
+ if (fast2) return fast2(input, cur);
1381
+ if (ctx.triviaKindLabels) return skipWithLabels(input, cur, ctx);
1382
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1383
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1302
1384
  }
1303
- }
1304
- function addNode(out, def) {
1305
- if (def.type === void 0) return;
1306
- const existing = out.get(def.type);
1307
- if (existing === void 0) {
1308
- out.set(def.type, def.tags === void 0 ? [] : [...def.tags]);
1309
- return;
1385
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1386
+ const fast = fastTriviaScanner(triviaP);
1387
+ if (fast) {
1388
+ const end = fast(input, cur);
1389
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1390
+ return end;
1310
1391
  }
1311
- if (def.tags === void 0) return;
1312
- for (const tag of def.tags) {
1313
- if (!existing.includes(tag)) existing.push(tag);
1392
+ if (ctx.triviaKindLabels) {
1393
+ const end = skipWithLabels(input, cur, ctx);
1394
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1395
+ return end;
1314
1396
  }
1397
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1398
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1315
1399
  }
1316
- function collectFromCombinator(parser2, out, seen, followLazy = true) {
1317
- if (seen.has(parser2)) return;
1318
- seen.add(parser2);
1319
- const def = parser2._def;
1320
- if (def.tag === "lazy") {
1321
- if (followLazy) {
1322
- try {
1323
- collectFromCombinator(def.thunk(), out, seen, followLazy);
1324
- } catch {
1325
- }
1400
+ function scanTrivia(input, cur, ctx) {
1401
+ const triviaP = ctx.trivia;
1402
+ if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1403
+ const log = ctx._triviaLog;
1404
+ const rootLog = ctx._rootTriviaLog;
1405
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1406
+ if (!ctx.trackLines) {
1407
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1408
+ if (fast2 && log === void 0 && !captureTl) {
1409
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1326
1410
  }
1327
- return;
1411
+ if (ctx.triviaKindLabels && (log !== void 0 || rootLog !== void 0 || captureTl)) {
1412
+ return scanWithLabels(input, cur, ctx);
1413
+ }
1414
+ if (ctx.triviaKindLabels) return { end: skipWithLabels(input, cur, ctx), commit: NOOP_COMMIT };
1415
+ if (log !== void 0 || rootLog !== void 0 || captureTl) {
1416
+ const tr3 = triviaP.parse(input, cur, {
1417
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1418
+ state: ctx.state
1419
+ });
1420
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1421
+ const end = tr3.span.end;
1422
+ return {
1423
+ end,
1424
+ commit: () => {
1425
+ pushTriviaLogEntry(ctx, cur, end);
1426
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1427
+ }
1428
+ };
1429
+ }
1430
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1431
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1328
1432
  }
1329
- if (def.tag === "node") addNode(out, def);
1330
- for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy);
1433
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1434
+ const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1435
+ if (fast && log === void 0 && !captureTl) {
1436
+ const end = fast(input, cur);
1437
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1438
+ return { end, commit: NOOP_COMMIT };
1439
+ }
1440
+ if (ctx.triviaKindLabels && (log !== void 0 || rootLog !== void 0 || captureTl)) {
1441
+ const scan = scanWithLabels(input, cur, ctx);
1442
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1443
+ return scan;
1444
+ }
1445
+ if (ctx.triviaKindLabels) {
1446
+ const end = skipWithLabels(input, cur, ctx);
1447
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1448
+ return { end, commit: NOOP_COMMIT };
1449
+ }
1450
+ if (log !== void 0 || rootLog !== void 0 || captureTl) {
1451
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1452
+ if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1453
+ const end = tr2.span.end;
1454
+ return {
1455
+ end,
1456
+ commit: () => {
1457
+ pushTriviaLogEntry(ctx, cur, end);
1458
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1459
+ }
1460
+ };
1461
+ }
1462
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1463
+ return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1331
1464
  }
1332
- function collectGrammarReflection(ruleMap, opts) {
1333
- const byType = /* @__PURE__ */ new Map();
1334
- const followLazy = opts?.followLazy !== false;
1335
- for (const [, rule] of ruleMap) {
1336
- if (!followLazy && rule._def.tag === "lazy") {
1337
- try {
1338
- const resolved = rule._def.thunk();
1339
- if (resolved._def.tag !== "lazy") collectFromCombinator(resolved, byType, /* @__PURE__ */ new Set(), false);
1340
- } catch {
1465
+ function consumeTrivia(input, cur, ctx) {
1466
+ if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1467
+ const scan = scanTrivia(input, cur, ctx);
1468
+ scan.commit();
1469
+ return scan.end;
1470
+ }
1471
+ function fastTriviaScanner(trivia2) {
1472
+ const cached = fastTriviaCache.get(trivia2);
1473
+ if (cached !== void 0) return cached;
1474
+ const scanner = buildFastTriviaScanner(trivia2);
1475
+ fastTriviaCache.set(trivia2, scanner);
1476
+ return scanner;
1477
+ }
1478
+ function buildFastTriviaScanner(trivia2) {
1479
+ const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1480
+ const direct = regexTriviaScanner(core);
1481
+ if (direct) return direct;
1482
+ const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1483
+ if (!repeat) return null;
1484
+ const one = regexTriviaScanner(repeat);
1485
+ if (one) return loopScanner([one]);
1486
+ if (repeat._def.tag !== "choice") return null;
1487
+ const arms = repeat._def.parsers.map(regexTriviaScanner);
1488
+ if (arms.some((s) => s === null)) return null;
1489
+ return loopScanner(arms);
1490
+ }
1491
+ function loopScanner(arms) {
1492
+ return (input, cur) => {
1493
+ let pos = cur;
1494
+ scan: while (pos < input.length) {
1495
+ for (const arm of arms) {
1496
+ const end = arm(input, pos);
1497
+ if (end > pos) {
1498
+ pos = end;
1499
+ continue scan;
1500
+ }
1341
1501
  }
1342
- continue;
1502
+ break;
1343
1503
  }
1344
- collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
1504
+ return pos;
1505
+ };
1506
+ }
1507
+ function regexTriviaScanner(parser2) {
1508
+ if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1509
+ const source = parser2._def.source;
1510
+ return classRunSource(source) ?? altStarSource(source);
1511
+ }
1512
+ function inRanges(cp, ranges) {
1513
+ for (let i = 0; i < ranges.length; i++) {
1514
+ const r = ranges[i];
1515
+ if (cp >= r[0] && cp <= r[1]) return true;
1345
1516
  }
1346
- return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
1517
+ return false;
1347
1518
  }
1348
- function attachGrammarReflection(grammar, reflection) {
1349
- Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true });
1350
- return grammar;
1519
+ function classScanner(classBody) {
1520
+ const ranges = parseClassRanges(classBody);
1521
+ if (!ranges) return null;
1522
+ return (input, cur) => {
1523
+ let pos = cur;
1524
+ while (pos < input.length && inRanges(input.charCodeAt(pos), ranges)) pos++;
1525
+ return pos;
1526
+ };
1351
1527
  }
1352
-
1353
- // src/combinators/parser.ts
1354
- var RULE_ORDER = "__parsemanRuleOrder";
1355
- function tagRule(r, key) {
1356
- ;
1357
- r._ruleName = key;
1358
- if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
1528
+ function untilLineBreakScanner(leaderCode) {
1529
+ return (input, cur) => {
1530
+ if (input.charCodeAt(cur) !== leaderCode) return cur;
1531
+ let pos = cur + 1;
1532
+ while (pos < input.length) {
1533
+ const cc = input.charCodeAt(pos);
1534
+ if (cc === 10 || cc === 13) break;
1535
+ pos++;
1536
+ }
1537
+ return pos;
1538
+ };
1359
1539
  }
1360
- function isDefinableRef(v) {
1361
- return !!v && typeof v === "object" && "_def" in v && v._def.tag === "lazy" && typeof v.define === "function";
1540
+ function classRunSource(source) {
1541
+ const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1542
+ return m ? classScanner(m[1]) : null;
1362
1543
  }
1363
- function ruleNameOf(r) {
1364
- return r._ruleName;
1544
+ function classifyTriviaArm(arm) {
1545
+ const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1546
+ if (cls) {
1547
+ const ranges = parseClassRanges(cls[1]);
1548
+ return ranges ? { kind: "class", ranges } : null;
1549
+ }
1550
+ const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1551
+ if (lc) {
1552
+ const marker = lc[1];
1553
+ return { kind: "untilLineBreak", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
1554
+ }
1555
+ return null;
1365
1556
  }
1366
- function isNamedRuleRefForAnotherRule(r, key) {
1367
- const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
1368
- return name !== void 0 && name !== key;
1557
+ function armScanner(arm) {
1558
+ if (arm.kind === "untilLineBreak") return untilLineBreakScanner(arm.code);
1559
+ const ranges = arm.ranges;
1560
+ return (input, cur) => {
1561
+ let pos = cur;
1562
+ while (pos < input.length && inRanges(input.charCodeAt(pos), ranges)) pos++;
1563
+ return pos;
1564
+ };
1369
1565
  }
1370
- function rules(a, b) {
1371
- const factory = typeof a === "function" ? a : b;
1372
- const options = typeof a === "function" ? b : a;
1373
- const cache = {};
1374
- const proxy = new Proxy(cache, {
1375
- get(target, key) {
1376
- if (typeof key !== "string") return void 0;
1377
- const record = target;
1378
- if (!(key in record)) {
1379
- const r = ref();
1380
- tagRule(r, key);
1381
- record[key] = r;
1566
+ function fusedTriviaScanner(ranges, leaders) {
1567
+ const c0 = leaders[0];
1568
+ const single = leaders.length === 1;
1569
+ return (input, cur) => {
1570
+ let pos = cur;
1571
+ const len = input.length;
1572
+ for (; ; ) {
1573
+ const c = input.charCodeAt(pos);
1574
+ if (pos < len && inRanges(c, ranges)) {
1575
+ pos++;
1576
+ continue;
1382
1577
  }
1383
- return record[key];
1384
- }
1385
- });
1386
- const definitions = factory(proxy);
1387
- for (const key of Object.keys(definitions)) {
1388
- const placeholder = cache[key];
1389
- const parser2 = definitions[key];
1390
- if (placeholder === parser2) {
1391
- throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
1392
- }
1393
- if (isDefinableRef(placeholder)) {
1394
- if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
1395
- placeholder.define(parser2);
1396
- placeholder._meta.firstSet = parser2._meta.firstSet;
1397
- placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
1398
- } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
1399
- const alias = ref();
1400
- tagRule(alias, key);
1401
- alias.define(parser2);
1402
- alias._meta.firstSet = parser2._meta.firstSet;
1403
- alias._meta.canMatchNewline = parser2._meta.canMatchNewline;
1404
- cache[key] = alias;
1405
- } else {
1406
- tagRule(parser2, key);
1407
- cache[key] = parser2;
1578
+ if (single ? c === c0 : leaders.includes(c)) {
1579
+ pos++;
1580
+ while (pos < len) {
1581
+ const cc = input.charCodeAt(pos);
1582
+ if (cc === 10 || cc === 13) break;
1583
+ pos++;
1584
+ }
1585
+ continue;
1586
+ }
1587
+ break;
1408
1588
  }
1409
- }
1410
- if (options?.trivia != null) {
1411
- for (const key of Object.keys(definitions)) {
1412
- const rule = cache[key];
1413
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
1589
+ return pos;
1590
+ };
1591
+ }
1592
+ function splitTopLevelAlts(body) {
1593
+ const arms = [];
1594
+ let inClass = false;
1595
+ let start = 0;
1596
+ for (let i = 0; i < body.length; i++) {
1597
+ const c = body[i];
1598
+ if (c === "\\") {
1599
+ i++;
1600
+ continue;
1414
1601
  }
1415
- }
1416
- if (options?.scanSkip != null) {
1417
- for (const key of Object.keys(definitions)) {
1418
- const rule = cache[key];
1419
- if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
1602
+ if (inClass) {
1603
+ if (c === "]") inClass = false;
1604
+ continue;
1420
1605
  }
1421
- }
1422
- if (options?.hostMode === "cst") {
1423
- for (const key of Object.keys(definitions)) {
1424
- const rule = cache[key];
1425
- if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
1606
+ if (c === "[") inClass = true;
1607
+ else if (c === "(") return null;
1608
+ else if (c === "|") {
1609
+ arms.push(body.slice(start, i));
1610
+ start = i + 1;
1426
1611
  }
1427
1612
  }
1428
- if (options?.trackLines === true) {
1429
- for (const key of Object.keys(definitions)) {
1430
- const rule = cache[key];
1431
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
1432
- }
1433
- for (const key of Object.keys(definitions)) {
1434
- const rule = cache[key];
1435
- if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
1436
- const wrapped = parser({ trackLines: true }, rule);
1437
- tagRule(wrapped, key);
1438
- wrapped._meta.grammarTrackLines = true;
1439
- cache[key] = wrapped;
1440
- }
1441
- }
1613
+ arms.push(body.slice(start));
1614
+ return arms;
1615
+ }
1616
+ function altStarSource(source) {
1617
+ const m = /^\(\?:(.*)\)[*+]$/.exec(source);
1618
+ if (!m) return null;
1619
+ const armSrcs = splitTopLevelAlts(m[1]);
1620
+ if (!armSrcs || armSrcs.length < 2) return null;
1621
+ const arms = [];
1622
+ for (const src of armSrcs) {
1623
+ const arm = classifyTriviaArm(src);
1624
+ if (!arm) return null;
1625
+ arms.push(arm);
1442
1626
  }
1443
- for (const key of Object.keys(definitions)) {
1444
- markUnusedValues(cache[key]);
1627
+ const ranges = [];
1628
+ const leaders = [];
1629
+ for (const arm of arms) {
1630
+ if (arm.kind === "class") ranges.push(...arm.ranges);
1631
+ else leaders.push(arm.code);
1445
1632
  }
1446
- Object.defineProperty(cache, RULE_ORDER, {
1447
- value: Object.keys(definitions),
1448
- enumerable: false,
1449
- configurable: true
1450
- });
1451
- attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
1452
- return cache;
1453
- }
1454
-
1455
- // src/analysis/duplication.ts
1456
- var WORD_RE_SRC = "-?[A-Za-z][A-Za-z0-9_-]*";
1457
- var WORDS_RE = new RegExp(`^${WORD_RE_SRC}(?:\\|${WORD_RE_SRC})*$`);
1458
-
1459
- // src/combinators/probe.ts
1460
- function failAt(ctx, expected, pos) {
1461
- const r = { ok: false, expected, span: { start: pos, end: pos } };
1462
- const probe = ctx._probe;
1463
- if (probe !== void 0 && pos <= probe.offset) {
1464
- const best = probe.best;
1465
- if (best === null || pos > best.span.start) {
1466
- probe.best = r;
1467
- } else if (pos === best.span.start) {
1468
- probe.best = { ...best, expected: [...best.expected, ...expected] };
1469
- }
1633
+ if (leaders.some((code) => inRanges(code, ranges))) {
1634
+ return loopScanner(arms.map(armScanner));
1470
1635
  }
1471
- return r;
1636
+ if (leaders.length === 0) return armScanner({ kind: "class", ranges });
1637
+ return fusedTriviaScanner(ranges, leaders);
1472
1638
  }
1473
1639
 
1474
- // src/combinators/literal.ts
1475
- function asciiFoldEq(a, b) {
1476
- if (a.length !== b.length) return false;
1477
- for (let i = 0; i < a.length; i++) {
1478
- let ca = a.charCodeAt(i);
1479
- let cb = b.charCodeAt(i);
1480
- if (ca >= 65 && ca <= 90) ca += 32;
1481
- if (cb >= 65 && cb <= 90) cb += 32;
1482
- if (ca !== cb) return false;
1483
- }
1484
- return true;
1485
- }
1486
- function literal(value, opts = {}) {
1487
- const caseInsensitive = opts.caseInsensitive ?? false;
1488
- const firstSet = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
1640
+ // src/combinators/choice.ts
1641
+ function choice(...args) {
1642
+ const parsers = args.map((a) => "gate" in a ? a.combinator : a);
1643
+ const gates = args.map((a) => "gate" in a ? a.gate : null);
1644
+ const hasGates = gates.some((g) => g !== null);
1645
+ const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
1646
+ let combined = { kind: "empty" };
1647
+ for (const p of parsers) combined = union(combined, p._meta.firstSet);
1489
1648
  const meta = {
1490
- firstSet,
1491
- canMatchNewline: value.includes("\n"),
1492
- isTrivia: false
1649
+ firstSet: combined,
1650
+ canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
1651
+ isTrivia: false,
1652
+ disjoint
1493
1653
  };
1494
- const canMatchNewline = meta.canMatchNewline;
1495
- if (caseInsensitive) {
1496
- const upper = value.toUpperCase();
1497
- const lower = value.toLowerCase();
1498
- const firstUpper = upper.codePointAt(0);
1499
- const firstLower = lower.codePointAt(0);
1500
- meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
1501
- { lo: firstLower, hi: firstLower },
1502
- { lo: firstUpper, hi: firstUpper }
1503
- ] } : firstSet;
1504
- }
1505
- const expected = [JSON.stringify(value)];
1506
- const parse = !caseInsensitive && value.length === 1 ? (() => {
1507
- const code = value.charCodeAt(0);
1508
- return function parse2(input, pos, ctx) {
1509
- if (input.charCodeAt(pos) === code) {
1510
- const span = { start: pos, end: pos + 1 };
1511
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1512
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
1513
- return { ok: true, value, span };
1514
- }
1515
- return failAt(ctx, expected, pos);
1516
- };
1517
- })() : function parse2(input, pos, ctx) {
1518
- const end = pos + value.length;
1519
- if (end > input.length) {
1520
- return failAt(ctx, expected, pos);
1521
- }
1522
- const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
1523
- if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
1524
- const span = { start: pos, end };
1525
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
1526
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
1527
- return { ok: true, value: matchedValue, span };
1654
+ const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
1655
+ const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
1656
+ let greedyLitMap = null;
1657
+ let sortedParsers = null;
1658
+ const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
1659
+ if (strategy?.tag === "greedyClassify") {
1660
+ greedyLitMap = /* @__PURE__ */ new Map();
1661
+ for (let i = 0; i < parsers.length; i++) {
1662
+ if (i === strategy.superIndex) continue;
1663
+ const litVal = getCoreLiteralValue(parsers[i]);
1664
+ if (litVal !== null) greedyLitMap.set(litVal, i);
1528
1665
  }
1529
- return failAt(ctx, expected, pos);
1530
- };
1666
+ } else if (strategy?.tag === "literalsLongestFirst") {
1667
+ sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
1668
+ }
1531
1669
  return {
1532
- _tag: "literal",
1670
+ _tag: "choice",
1533
1671
  _meta: meta,
1534
- _def: { tag: "literal", value, caseInsensitive },
1535
- parse
1536
- };
1537
- }
1538
-
1539
- // src/regex/classes.ts
1540
- var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1541
- var SPACE_RANGES = [
1542
- [9, 13],
1543
- [32, 32],
1544
- [160, 160],
1545
- [5760, 5760],
1546
- [8192, 8202],
1547
- [8232, 8232],
1548
- [8233, 8233],
1549
- [8239, 8239],
1550
- [8287, 8287],
1551
- [12288, 12288],
1552
- [65279, 65279]
1553
- ];
1554
- function shorthandRanges(ch) {
1555
- if (ch === "d") return [[48, 57]];
1556
- if (ch === "s") return SPACE_RANGES;
1557
- return [[48, 57], [65, 90], [97, 122], [95, 95]];
1558
- }
1559
- function readUnicodeEscape(body, i) {
1560
- if (body[i] !== "\\" || body[i + 1] !== "u") return null;
1561
- const hex = body.slice(i + 2, i + 6);
1562
- if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
1563
- return { cp: Number.parseInt(hex, 16), next: i + 6 };
1564
- }
1565
- function parseClassRanges(body) {
1566
- const ranges = [];
1567
- let i = 0;
1568
- const readAtom = () => {
1569
- const ch = body[i];
1570
- if (ch === void 0) return null;
1571
- if (ch === "\\") {
1572
- const uni = readUnicodeEscape(body, i);
1573
- if (uni) {
1574
- i = uni.next;
1575
- return { cp: uni.cp };
1672
+ _def: {
1673
+ tag: "choice",
1674
+ parsers,
1675
+ gates,
1676
+ disjoint,
1677
+ strategy: strategy ?? { tag: "firstMatch" },
1678
+ autoNot
1679
+ },
1680
+ parse(input, pos, ctx) {
1681
+ const expected = [];
1682
+ if (disjoint && pos < input.length) {
1683
+ const code = input.codePointAt(pos);
1684
+ let idx = code < 128 ? asciiDispatch[code] : -1;
1685
+ if (idx < 0) {
1686
+ for (let i = 0; i < parsers.length; i++) {
1687
+ if (inFirstSet(code, parsers[i]._meta.firstSet)) {
1688
+ idx = i;
1689
+ break;
1690
+ }
1691
+ }
1692
+ }
1693
+ if (idx >= 0) {
1694
+ const gate = gates[idx];
1695
+ if (gate && !gate(ctx.state)) {
1696
+ return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
1697
+ }
1698
+ const result = parsers[idx].parse(input, pos, ctx);
1699
+ if (result.ok) return result;
1700
+ expected.push(...result.expected);
1701
+ if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
1702
+ return { ok: false, expected, span: { start: pos, end: pos } };
1703
+ }
1704
+ return {
1705
+ ok: false,
1706
+ expected: parsers.flatMap((p) => {
1707
+ const r = p.parse(input, pos, ctx);
1708
+ return r.ok ? [] : r.expected;
1709
+ }),
1710
+ span: { start: pos, end: pos }
1711
+ };
1576
1712
  }
1577
- const e = body[i + 1];
1578
- if (e === void 0) return null;
1579
- i += 2;
1580
- if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1581
- if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
1582
- if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1583
- return { cp: e.codePointAt(0) };
1713
+ if (strategy?.tag === "greedyClassify") {
1714
+ const superResult = parsers[strategy.superIndex].parse(input, pos, ctx);
1715
+ if (!superResult.ok) return superResult;
1716
+ const end = superResult.span.end;
1717
+ const litIdx = greedyLitMap.get(input.slice(pos, end));
1718
+ if (litIdx !== void 0) {
1719
+ const litVal = getCoreLiteralValue(parsers[litIdx]);
1720
+ const value = applyTransforms(parsers[litIdx], litVal, { start: pos, end });
1721
+ return { ok: true, value, span: { start: pos, end } };
1722
+ }
1723
+ return superResult;
1724
+ }
1725
+ if (strategy?.tag === "literalsLongestFirst") {
1726
+ for (const p of sortedParsers) {
1727
+ const r = p.parse(input, pos, ctx);
1728
+ if (r.ok) return r;
1729
+ expected.push(...r.expected);
1730
+ }
1731
+ return { ok: false, expected, span: { start: pos, end: pos } };
1732
+ }
1733
+ for (let i = 0; i < parsers.length; i++) {
1734
+ if (gates[i] && !gates[i](ctx.state)) continue;
1735
+ const mark = saveTriviaMark(ctx);
1736
+ const result = parsers[i].parse(input, pos, ctx);
1737
+ if (!result.ok) {
1738
+ rollbackTrivia(ctx, mark);
1739
+ expected.push(...result.expected);
1740
+ if (result.committed) return { ok: false, expected, span: result.span, committed: true };
1741
+ continue;
1742
+ }
1743
+ const checks = autoNot[i];
1744
+ if (checks && autoNotFires(input, result.span.end, checks)) {
1745
+ rollbackTrivia(ctx, mark);
1746
+ continue;
1747
+ }
1748
+ return result;
1749
+ }
1750
+ return { ok: false, expected, span: { start: pos, end: pos } };
1584
1751
  }
1585
- i += ch.length;
1586
- return { cp: ch.codePointAt(0) };
1587
1752
  };
1588
- while (i < body.length) {
1589
- const lo = readAtom();
1590
- if (lo === null) return null;
1591
- if ("set" in lo) {
1592
- ranges.push(...lo.set);
1593
- continue;
1594
- }
1595
- if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
1596
- i += 1;
1597
- const hi = readAtom();
1598
- if (hi === null || "set" in hi) return null;
1599
- ranges.push([lo.cp, hi.cp]);
1600
- } else {
1601
- ranges.push([lo.cp, lo.cp]);
1602
- }
1603
- }
1604
- return ranges.length ? ranges : null;
1605
1753
  }
1606
-
1607
- // src/combinators/regex.ts
1608
- var firstSetAnalyzer = null;
1609
- var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
1610
- var SCAN_BAIL_AT = 64;
1611
- function inRanges(cp, ranges) {
1612
- for (let i = 0; i < ranges.length; i++) {
1613
- const [lo, hi] = ranges[i];
1614
- if (cp >= lo && cp <= hi) return true;
1754
+ function detectStrategy(parsers) {
1755
+ const regexIndices = [];
1756
+ const literalIndices = [];
1757
+ for (let i = 0; i < parsers.length; i++) {
1758
+ if (getCoreRegexDef(parsers[i]) !== null) regexIndices.push(i);
1759
+ else if (getCoreLiteralValue(parsers[i]) !== null) literalIndices.push(i);
1615
1760
  }
1616
- return false;
1761
+ if (regexIndices.length === 1 && literalIndices.length === parsers.length - 1 && literalIndices.length > 0) {
1762
+ const superIndex = regexIndices[0];
1763
+ const regexDef = getCoreRegexDef(parsers[superIndex]);
1764
+ const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
1765
+ const re = new RegExp(regexDef.source, flags);
1766
+ const allSubsumed = literalIndices.every((i) => {
1767
+ const litVal = getCoreLiteralValue(parsers[i]);
1768
+ re.lastIndex = 0;
1769
+ const m = re.exec(litVal);
1770
+ return m !== null && m[0] === litVal;
1771
+ });
1772
+ if (allSubsumed) return { tag: "greedyClassify", superIndex };
1773
+ }
1774
+ if (parsers.length === literalIndices.length) {
1775
+ const sortedIndices = [...literalIndices].sort(
1776
+ (a, b) => getCoreLiteralValue(parsers[b]).length - getCoreLiteralValue(parsers[a]).length
1777
+ );
1778
+ return { tag: "literalsLongestFirst", sortedIndices };
1779
+ }
1780
+ const shared = detectSharedPrefix(parsers);
1781
+ if (shared !== null) return shared;
1782
+ return { tag: "firstMatch" };
1617
1783
  }
1618
- function readClassRanges(body) {
1619
- if (body.startsWith("^")) return null;
1620
- return parseClassRanges(body);
1784
+ function bareLeadingTermKey(term) {
1785
+ const d = term._def;
1786
+ if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
1787
+ if (d.tag === "regex") return `R:${JSON.stringify([d.source, d.flags])}`;
1788
+ return null;
1621
1789
  }
1622
- function shortScanner(source, flags) {
1623
- if (/[imsuvy]/.test(flags)) return null;
1624
- let ranges = null;
1625
- let quant = "";
1626
- if (source[0] === "[") {
1627
- let end = 1;
1628
- while (end < source.length && source[end] !== "]") {
1629
- if (source[end] === "\\") end += 2;
1630
- else end++;
1631
- }
1632
- if (source[end] !== "]") return null;
1633
- ranges = readClassRanges(source.slice(1, end));
1634
- quant = source.slice(end + 1);
1635
- } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
1636
- ranges = shorthandRanges(source[1]);
1637
- quant = source.slice(2);
1790
+ function detectSharedPrefix(parsers) {
1791
+ if (parsers.length < 2) return null;
1792
+ let key = null;
1793
+ let prefix = null;
1794
+ const members = [];
1795
+ for (let i = 0; i < parsers.length; i++) {
1796
+ const term0 = leadingTermOfArm(parsers[i]);
1797
+ if (term0 === null) return null;
1798
+ const k = bareLeadingTermKey(term0);
1799
+ if (k === null) return null;
1800
+ if (key === null) {
1801
+ key = k;
1802
+ prefix = term0;
1803
+ } else if (k !== key) return null;
1804
+ members.push(i);
1638
1805
  }
1639
- if (!ranges || quant !== "+" && quant !== "*") return null;
1640
- const minOne = quant === "+";
1641
- return (input, pos) => {
1642
- let end = pos;
1643
- while (end < input.length && inRanges(input.charCodeAt(end), ranges)) {
1644
- end++;
1645
- if (end - pos >= SCAN_BAIL_AT) return void 0;
1646
- }
1647
- return minOne && end === pos ? null : end;
1648
- };
1806
+ if (prefix === null || members.length < 2) return null;
1807
+ return { tag: "sharedPrefix", prefix, members };
1649
1808
  }
1650
- function asciiCaseFold(fs) {
1651
- if (fs.kind !== "ranges") return fs;
1652
- const ranges = fs.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
1653
- const add = (lo, hi) => {
1654
- if (lo <= hi) ranges.push({ lo, hi });
1655
- };
1656
- for (const r of fs.ranges) {
1657
- const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
1658
- add(uLo + 32, uHi + 32);
1659
- const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
1660
- add(lLo - 32, lHi - 32);
1661
- }
1662
- ranges.sort((a, b) => a.lo - b.lo);
1663
- const merged = [];
1664
- for (const r of ranges) {
1665
- const last = merged[merged.length - 1];
1666
- if (last && r.lo <= last.hi + 1) last.hi = Math.max(last.hi, r.hi);
1667
- else merged.push({ lo: r.lo, hi: r.hi });
1809
+ function leadingTermOfArm(arm) {
1810
+ let d = arm._def;
1811
+ for (; ; ) {
1812
+ if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
1813
+ d = d.parser._def;
1814
+ continue;
1815
+ }
1816
+ break;
1668
1817
  }
1669
- return { kind: "ranges", ranges: merged };
1818
+ if (d.tag !== "sequence" || d.parsers.length < 2) return null;
1819
+ return d.parsers[0];
1670
1820
  }
1671
- function regex(pattern, flags = "") {
1672
- const source = typeof pattern === "string" ? pattern : pattern.source;
1673
- const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
1674
- const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
1675
- const scan = shortScanner(source, resolvedFlags);
1676
- const raw = (firstSetAnalyzer ?? permissiveFirstSet)(source);
1677
- const firstSet = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
1678
- const canMatchNewline = raw.canMatchNewline;
1679
- const meta = { firstSet, canMatchNewline, isTrivia: false };
1680
- return {
1681
- _tag: "regex",
1682
- _meta: meta,
1683
- _def: { tag: "regex", source, flags: resolvedFlags },
1684
- parse(input, pos, ctx) {
1685
- const scanEnd = scan?.(input, pos);
1686
- if (scanEnd !== void 0) {
1687
- if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
1688
- const value = input.slice(pos, scanEnd);
1689
- const span2 = { start: pos, end: scanEnd };
1690
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
1691
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
1692
- return { ok: true, value, span: span2 };
1821
+ function computeAutoNot(parsers) {
1822
+ return parsers.map((p, i) => {
1823
+ const litVal = getCoreLiteralValue(p);
1824
+ if (litVal === null) return null;
1825
+ const checks = [];
1826
+ for (let j2 = i + 1; j2 < parsers.length; j2++) {
1827
+ const other = parsers[j2];
1828
+ const otherLit = getCoreLiteralValue(other);
1829
+ if (otherLit !== null && otherLit.startsWith(litVal) && otherLit.length > litVal.length) {
1830
+ checks.push({ kind: "startsWith", value: otherLit.slice(litVal.length) });
1831
+ continue;
1693
1832
  }
1694
- anchored.lastIndex = pos;
1695
- const m = anchored.exec(input);
1696
- if (m === null) {
1697
- return failAt(ctx, [`/${source}/`], pos);
1833
+ const regexDef = getCoreRegexDef(other);
1834
+ if (regexDef !== null) {
1835
+ const contSet = continuationFirstSet(litVal, regexDef.source, regexDef.flags);
1836
+ if (contSet !== null) checks.push({ kind: "firstSet", set: contSet });
1698
1837
  }
1699
- const span = { start: pos, end: pos + m[0].length };
1700
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1701
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
1702
- return { ok: true, value: m[0], span };
1703
1838
  }
1704
- };
1839
+ return checks.length > 0 ? checks : null;
1840
+ });
1705
1841
  }
1706
-
1707
- // src/combinators/trivia-skip.ts
1708
- var NOOP_COMMIT = () => {
1709
- };
1710
- var fastTriviaCache = /* @__PURE__ */ new WeakMap();
1711
- function needsDeferredTriviaCommit(ctx) {
1712
- return ctx._triviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
1842
+ function autoNotFires(input, end, checks) {
1843
+ for (const check of checks) {
1844
+ if (check.kind === "firstSet") {
1845
+ const code = end < input.length ? input.codePointAt(end) ?? -1 : -1;
1846
+ if (inFirstSet(code, check.set)) return true;
1847
+ } else {
1848
+ if (input.startsWith(check.value, end)) return true;
1849
+ }
1850
+ }
1851
+ return false;
1713
1852
  }
1714
- function saveTriviaMark(ctx) {
1715
- const m = saveCstMark(ctx);
1716
- return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, errors: m.errors, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1853
+ function getCoreLiteralValue(p) {
1854
+ const def = p._def;
1855
+ if (def.tag === "literal" && !def.caseInsensitive) return def.value;
1856
+ if (def.tag === "transform") return getCoreLiteralValue(def.parser);
1857
+ return null;
1858
+ }
1859
+ function getCoreRegexDef(p) {
1860
+ const def = p._def;
1861
+ if (def.tag === "regex") return { source: def.source, flags: def.flags };
1862
+ if (def.tag === "transform") return getCoreRegexDef(def.parser);
1863
+ if (def.tag === "label") return getCoreRegexDef(def.parser);
1864
+ return null;
1717
1865
  }
1718
- function rollbackTrivia(ctx, mark) {
1719
- rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1720
- if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1866
+ function applyTransforms(p, value, span) {
1867
+ const def = p._def;
1868
+ if (def.tag === "transform") {
1869
+ const inner = applyTransforms(def.parser, value, span);
1870
+ return def.fn(inner, span);
1871
+ }
1872
+ return value;
1721
1873
  }
1722
- function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1723
- const probeCtx = {
1724
- trackLines: ctx.trackLines,
1725
- state: ctx.state,
1726
- ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1727
- ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1728
- ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1729
- };
1730
- const result = triviaP.parse(input, cur, probeCtx);
1731
- ctx._lineScannedTo = probeCtx._lineScannedTo;
1732
- return result;
1874
+ function continuationFirstSet(lit, source, flags) {
1875
+ const re = new RegExp(source, "y" + flags.replace(/[gy]/g, ""));
1876
+ re.lastIndex = 0;
1877
+ const base = re.exec(lit);
1878
+ if (!base || base[0] !== lit) return null;
1879
+ const contCodes = [];
1880
+ for (let code = 1; code < 128; code++) {
1881
+ re.lastIndex = 0;
1882
+ const m = re.exec(lit + String.fromCharCode(code));
1883
+ if (m && m[0].length > lit.length) contCodes.push(code);
1884
+ }
1885
+ if (contCodes.length === 0) return null;
1886
+ return codesToFirstSet(contCodes);
1733
1887
  }
1734
- function saveLookaheadMark(ctx) {
1735
- return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1888
+ function codesToFirstSet(codes) {
1889
+ codes.sort((a, b) => a - b);
1890
+ const ranges = [];
1891
+ let lo = codes[0], hi = codes[0];
1892
+ for (let i = 1; i < codes.length; i++) {
1893
+ if (codes[i] === hi + 1) {
1894
+ hi = codes[i];
1895
+ } else {
1896
+ ranges.push({ lo, hi });
1897
+ lo = hi = codes[i];
1898
+ }
1899
+ }
1900
+ ranges.push({ lo, hi });
1901
+ return { kind: "ranges", ranges };
1736
1902
  }
1737
- function rollbackLookahead(ctx, mark) {
1738
- rollbackTrivia(ctx, mark);
1739
- if (ctx._probe) ctx._probe.best = mark.probeBest;
1903
+ function inFirstSet(code, fs) {
1904
+ if (fs.kind === "any") return true;
1905
+ if (fs.kind === "empty") return false;
1906
+ for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
1907
+ return false;
1740
1908
  }
1741
- function scanWithLabels(input, cur, ctx) {
1742
- const triviaP = ctx.trivia;
1743
- const spec = analyzeLabeledTrivia(triviaP);
1744
- if (!spec) return { end: cur, commit: NOOP_COMMIT };
1745
- const fast = tryFastLabeledScan(input, cur, triviaP);
1746
- const { end, chunks } = fast ?? scanLabeledTriviaChunks(input, cur, spec);
1747
- if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1748
- return {
1749
- end,
1750
- commit: () => recordTriviaChunks(ctx, chunks)
1751
- };
1909
+ function areDisjoint(sets) {
1910
+ if (sets.some((s) => s.kind === "any")) return false;
1911
+ for (let i = 0; i < sets.length; i++)
1912
+ for (let j2 = i + 1; j2 < sets.length; j2++)
1913
+ if (intersects(sets[i], sets[j2])) return false;
1914
+ return true;
1752
1915
  }
1753
- function advanceTrivia(input, cur, ctx) {
1754
- const triviaP = ctx.trivia;
1755
- if (!triviaP) return cur;
1756
- if (!ctx.trackLines) {
1757
- const fast2 = fastTriviaScanner(triviaP);
1758
- if (fast2) return fast2(input, cur);
1759
- if (ctx.triviaKindLabels) {
1760
- const scan = scanWithLabels(input, cur, ctx);
1761
- return scan.end;
1916
+ function buildAsciiDispatch(parsers) {
1917
+ const table = Array(128).fill(-1);
1918
+ for (let i = 0; i < parsers.length; i++) {
1919
+ const fs = parsers[i]._meta.firstSet;
1920
+ if (fs.kind !== "ranges") continue;
1921
+ for (const { lo, hi } of fs.ranges) {
1922
+ for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
1923
+ table[code] = i;
1924
+ }
1762
1925
  }
1763
- const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1764
- return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1765
- }
1766
- const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1767
- const fast = fastTriviaScanner(triviaP);
1768
- if (fast) {
1769
- const end = fast(input, cur);
1770
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1771
- return end;
1772
- }
1773
- if (ctx.triviaKindLabels) {
1774
- const scan = scanWithLabels(input, cur, ctx);
1775
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1776
- return scan.end;
1777
1926
  }
1778
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1779
- return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1927
+ return table;
1780
1928
  }
1781
- function scanTrivia(input, cur, ctx) {
1782
- const triviaP = ctx.trivia;
1783
- if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1784
- const log = ctx._triviaLog;
1785
- const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1786
- if (!ctx.trackLines) {
1787
- const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1788
- if (fast2 && log === void 0 && !captureTl) {
1789
- return { end: fast2(input, cur), commit: NOOP_COMMIT };
1790
- }
1791
- if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1792
- return scanWithLabels(input, cur, ctx);
1793
- }
1794
- if (log !== void 0 || captureTl) {
1795
- const tr3 = triviaP.parse(input, cur, {
1796
- trackLines: log !== void 0 ? false : ctx.trackLines,
1797
- state: ctx.state
1798
- });
1799
- if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1800
- const end = tr3.span.end;
1801
- return {
1802
- end,
1803
- commit: () => {
1804
- pushTriviaLogEntry(ctx, cur, end);
1805
- if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1806
- }
1807
- };
1929
+
1930
+ // src/analysis/duplication.ts
1931
+ var WORD_RE_SRC = "-?[A-Za-z][A-Za-z0-9_-]*";
1932
+ var WORDS_RE = new RegExp(`^${WORD_RE_SRC}(?:\\|${WORD_RE_SRC})*$`);
1933
+
1934
+ // src/combinators/probe.ts
1935
+ function failAt(ctx, expected, pos) {
1936
+ const r = { ok: false, expected, span: { start: pos, end: pos } };
1937
+ const probe = ctx._probe;
1938
+ if (probe !== void 0 && pos <= probe.offset) {
1939
+ const best = probe.best;
1940
+ if (best === null || pos > best.span.start) {
1941
+ probe.best = r;
1942
+ } else if (pos === best.span.start) {
1943
+ probe.best = { ...best, expected: [...best.expected, ...expected] };
1808
1944
  }
1809
- const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1810
- return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1811
1945
  }
1812
- const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1813
- const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1814
- if (fast && log === void 0 && !captureTl) {
1815
- const end = fast(input, cur);
1816
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1817
- return { end, commit: NOOP_COMMIT };
1946
+ return r;
1947
+ }
1948
+
1949
+ // src/combinators/literal.ts
1950
+ function asciiFoldEq(a, b) {
1951
+ if (a.length !== b.length) return false;
1952
+ for (let i = 0; i < a.length; i++) {
1953
+ let ca = a.charCodeAt(i);
1954
+ let cb = b.charCodeAt(i);
1955
+ if (ca >= 65 && ca <= 90) ca += 32;
1956
+ if (cb >= 65 && cb <= 90) cb += 32;
1957
+ if (ca !== cb) return false;
1818
1958
  }
1819
- if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1820
- const scan = scanWithLabels(input, cur, ctx);
1821
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1822
- return scan;
1959
+ return true;
1960
+ }
1961
+ function literal(value, opts = {}) {
1962
+ const caseInsensitive = opts.caseInsensitive ?? false;
1963
+ const firstSet = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
1964
+ const meta = {
1965
+ firstSet,
1966
+ canMatchNewline: value.includes("\n"),
1967
+ isTrivia: false
1968
+ };
1969
+ const canMatchNewline = meta.canMatchNewline;
1970
+ if (caseInsensitive) {
1971
+ const upper = value.toUpperCase();
1972
+ const lower = value.toLowerCase();
1973
+ const firstUpper = upper.codePointAt(0);
1974
+ const firstLower = lower.codePointAt(0);
1975
+ meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
1976
+ { lo: firstLower, hi: firstLower },
1977
+ { lo: firstUpper, hi: firstUpper }
1978
+ ] } : firstSet;
1823
1979
  }
1824
- if (log !== void 0 || captureTl) {
1825
- const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1826
- if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1827
- const end = tr2.span.end;
1828
- return {
1829
- end,
1830
- commit: () => {
1831
- pushTriviaLogEntry(ctx, cur, end);
1832
- if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1980
+ const expected = [JSON.stringify(value)];
1981
+ const parse = !caseInsensitive && value.length === 1 ? (() => {
1982
+ const code = value.charCodeAt(0);
1983
+ return function parse2(input, pos, ctx) {
1984
+ if (input.charCodeAt(pos) === code) {
1985
+ const span = { start: pos, end: pos + 1 };
1986
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1987
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
1988
+ return { ok: true, value, span };
1833
1989
  }
1990
+ return failAt(ctx, expected, pos);
1834
1991
  };
1835
- }
1836
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1837
- return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1838
- }
1839
- function consumeTrivia(input, cur, ctx) {
1840
- if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1841
- const scan = scanTrivia(input, cur, ctx);
1842
- scan.commit();
1843
- return scan.end;
1844
- }
1845
- function fastTriviaScanner(trivia2) {
1846
- const cached = fastTriviaCache.get(trivia2);
1847
- if (cached !== void 0) return cached;
1848
- const scanner = buildFastTriviaScanner(trivia2);
1849
- fastTriviaCache.set(trivia2, scanner);
1850
- return scanner;
1851
- }
1852
- function buildFastTriviaScanner(trivia2) {
1853
- const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1854
- const direct = regexTriviaScanner(core);
1855
- if (direct) return direct;
1856
- const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1857
- if (!repeat) return null;
1858
- const one = regexTriviaScanner(repeat);
1859
- if (one) return loopScanner([one]);
1860
- if (repeat._def.tag !== "choice") return null;
1861
- const arms = repeat._def.parsers.map(regexTriviaScanner);
1862
- if (arms.some((s) => s === null)) return null;
1863
- return loopScanner(arms);
1864
- }
1865
- function loopScanner(arms) {
1866
- return (input, cur) => {
1867
- let pos = cur;
1868
- scan: while (pos < input.length) {
1869
- for (const arm of arms) {
1870
- const end = arm(input, pos);
1871
- if (end > pos) {
1872
- pos = end;
1873
- continue scan;
1874
- }
1875
- }
1876
- break;
1992
+ })() : function parse2(input, pos, ctx) {
1993
+ const end = pos + value.length;
1994
+ if (end > input.length) {
1995
+ return failAt(ctx, expected, pos);
1996
+ }
1997
+ const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
1998
+ if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
1999
+ const span = { start: pos, end };
2000
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
2001
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
2002
+ return { ok: true, value: matchedValue, span };
1877
2003
  }
1878
- return pos;
2004
+ return failAt(ctx, expected, pos);
2005
+ };
2006
+ return {
2007
+ _tag: "literal",
2008
+ _meta: meta,
2009
+ _def: { tag: "literal", value, caseInsensitive },
2010
+ parse
1879
2011
  };
1880
2012
  }
1881
- function regexTriviaScanner(parser2) {
1882
- if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1883
- const source = parser2._def.source;
1884
- return classRunSource(source) ?? altStarSource(source) ?? (blockCommentSource(source) ? scanBlockComment : null);
1885
- }
2013
+
2014
+ // src/combinators/regex.ts
2015
+ var firstSetAnalyzer = null;
2016
+ var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
2017
+ var SCAN_BAIL_AT = 64;
1886
2018
  function inRanges2(cp, ranges) {
1887
2019
  for (let i = 0; i < ranges.length; i++) {
1888
- const r = ranges[i];
1889
- if (cp >= r[0] && cp <= r[1]) return true;
2020
+ const [lo, hi] = ranges[i];
2021
+ if (cp >= lo && cp <= hi) return true;
1890
2022
  }
1891
2023
  return false;
1892
2024
  }
1893
- function classScanner(classBody) {
1894
- const ranges = parseClassRanges(classBody);
1895
- if (!ranges) return null;
1896
- return (input, cur) => {
1897
- let pos = cur;
1898
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1899
- return pos;
1900
- };
2025
+ function readClassRanges(body) {
2026
+ if (body.startsWith("^")) return null;
2027
+ return parseClassRanges(body);
1901
2028
  }
1902
- function lineCommentScanner(commentCode) {
1903
- return (input, cur) => {
1904
- if (input.charCodeAt(cur) !== commentCode) return cur;
1905
- let pos = cur + 1;
1906
- while (pos < input.length) {
1907
- const cc = input.charCodeAt(pos);
1908
- if (cc === 10 || cc === 13) break;
1909
- pos++;
2029
+ function shortScanner(source, flags) {
2030
+ if (/[imsuvy]/.test(flags)) return null;
2031
+ let ranges = null;
2032
+ let quant = "";
2033
+ if (source[0] === "[") {
2034
+ let end = 1;
2035
+ while (end < source.length && source[end] !== "]") {
2036
+ if (source[end] === "\\") end += 2;
2037
+ else end++;
1910
2038
  }
1911
- return pos;
2039
+ if (source[end] !== "]") return null;
2040
+ ranges = readClassRanges(source.slice(1, end));
2041
+ quant = source.slice(end + 1);
2042
+ } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
2043
+ ranges = shorthandRanges(source[1]);
2044
+ quant = source.slice(2);
2045
+ }
2046
+ if (!ranges || quant !== "+" && quant !== "*") return null;
2047
+ const minOne = quant === "+";
2048
+ return (input, pos) => {
2049
+ let end = pos;
2050
+ while (end < input.length && inRanges2(input.charCodeAt(end), ranges)) {
2051
+ end++;
2052
+ if (end - pos >= SCAN_BAIL_AT) return void 0;
2053
+ }
2054
+ return minOne && end === pos ? null : end;
1912
2055
  };
1913
2056
  }
1914
- function classRunSource(source) {
1915
- const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1916
- return m ? classScanner(m[1]) : null;
1917
- }
1918
- function classifyTriviaArm(arm) {
1919
- const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1920
- if (cls) {
1921
- const ranges = parseClassRanges(cls[1]);
1922
- return ranges ? { kind: "class", ranges } : null;
2057
+ function asciiCaseFold(fs) {
2058
+ if (fs.kind !== "ranges") return fs;
2059
+ const ranges = fs.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
2060
+ const add = (lo, hi) => {
2061
+ if (lo <= hi) ranges.push({ lo, hi });
2062
+ };
2063
+ for (const r of fs.ranges) {
2064
+ const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
2065
+ add(uLo + 32, uHi + 32);
2066
+ const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
2067
+ add(lLo - 32, lHi - 32);
1923
2068
  }
1924
- const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1925
- if (lc) {
1926
- const marker = lc[1];
1927
- return { kind: "comment", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
2069
+ ranges.sort((a, b) => a.lo - b.lo);
2070
+ const merged = [];
2071
+ for (const r of ranges) {
2072
+ const last = merged[merged.length - 1];
2073
+ if (last && r.lo <= last.hi + 1) last.hi = Math.max(last.hi, r.hi);
2074
+ else merged.push({ lo: r.lo, hi: r.hi });
1928
2075
  }
1929
- return null;
1930
- }
1931
- function armScanner(arm) {
1932
- if (arm.kind === "comment") return lineCommentScanner(arm.code);
1933
- const ranges = arm.ranges;
1934
- return (input, cur) => {
1935
- let pos = cur;
1936
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1937
- return pos;
1938
- };
2076
+ return { kind: "ranges", ranges: merged };
1939
2077
  }
1940
- function fusedTriviaScanner(ranges, commentCodes) {
1941
- const c0 = commentCodes[0];
1942
- const single = commentCodes.length === 1;
1943
- return (input, cur) => {
1944
- let pos = cur;
1945
- const len = input.length;
1946
- for (; ; ) {
1947
- const c = input.charCodeAt(pos);
1948
- if (pos < len && inRanges2(c, ranges)) {
1949
- pos++;
1950
- continue;
2078
+ function regex(pattern, flags = "") {
2079
+ const source = typeof pattern === "string" ? pattern : pattern.source;
2080
+ const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
2081
+ const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
2082
+ const scan = shortScanner(source, resolvedFlags);
2083
+ const raw = (firstSetAnalyzer ?? permissiveFirstSet)(source);
2084
+ const firstSet = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
2085
+ const canMatchNewline = raw.canMatchNewline;
2086
+ const meta = { firstSet, canMatchNewline, isTrivia: false };
2087
+ return {
2088
+ _tag: "regex",
2089
+ _meta: meta,
2090
+ _def: { tag: "regex", source, flags: resolvedFlags },
2091
+ parse(input, pos, ctx) {
2092
+ const scanEnd = scan?.(input, pos);
2093
+ if (scanEnd !== void 0) {
2094
+ if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
2095
+ const value = input.slice(pos, scanEnd);
2096
+ const span2 = { start: pos, end: scanEnd };
2097
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
2098
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
2099
+ return { ok: true, value, span: span2 };
1951
2100
  }
1952
- if (single ? c === c0 : commentCodes.includes(c)) {
1953
- pos++;
1954
- while (pos < len) {
1955
- const cc = input.charCodeAt(pos);
1956
- if (cc === 10 || cc === 13) break;
1957
- pos++;
1958
- }
1959
- continue;
2101
+ anchored.lastIndex = pos;
2102
+ const m = anchored.exec(input);
2103
+ if (m === null) {
2104
+ return failAt(ctx, [`/${source}/`], pos);
1960
2105
  }
1961
- break;
2106
+ const span = { start: pos, end: pos + m[0].length };
2107
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
2108
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
2109
+ return { ok: true, value: m[0], span };
1962
2110
  }
1963
- return pos;
1964
2111
  };
1965
2112
  }
1966
- function splitTopLevelAlts(body) {
1967
- const arms = [];
1968
- let inClass = false;
1969
- let start = 0;
1970
- for (let i = 0; i < body.length; i++) {
1971
- const c = body[i];
1972
- if (c === "\\") {
1973
- i++;
1974
- continue;
1975
- }
1976
- if (inClass) {
1977
- if (c === "]") inClass = false;
1978
- continue;
1979
- }
1980
- if (c === "[") inClass = true;
1981
- else if (c === "(") return null;
1982
- else if (c === "|") {
1983
- arms.push(body.slice(start, i));
1984
- start = i + 1;
1985
- }
1986
- }
1987
- arms.push(body.slice(start));
1988
- return arms;
1989
- }
1990
- function altStarSource(source) {
1991
- const m = /^\(\?:(.*)\)[*+]$/.exec(source);
1992
- if (!m) return null;
1993
- const armSrcs = splitTopLevelAlts(m[1]);
1994
- if (!armSrcs || armSrcs.length < 2) return null;
1995
- const arms = [];
1996
- for (const src of armSrcs) {
1997
- const arm = classifyTriviaArm(src);
1998
- if (!arm) return null;
1999
- arms.push(arm);
2000
- }
2001
- const ranges = [];
2002
- const commentCodes = [];
2003
- for (const arm of arms) {
2004
- if (arm.kind === "class") ranges.push(...arm.ranges);
2005
- else commentCodes.push(arm.code);
2006
- }
2007
- if (commentCodes.some((code) => inRanges2(code, ranges))) {
2008
- return loopScanner(arms.map(armScanner));
2009
- }
2010
- if (commentCodes.length === 0) return armScanner({ kind: "class", ranges });
2011
- return fusedTriviaScanner(ranges, commentCodes);
2012
- }
2013
- function blockCommentSource(source) {
2014
- return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
2015
- }
2016
- function scanBlockComment(input, cur) {
2017
- if (input.charCodeAt(cur) !== 47 || input.charCodeAt(cur + 1) !== 42) return cur;
2018
- const close = input.indexOf("*/", cur + 2);
2019
- return close === -1 ? cur : close + 2;
2020
- }
2021
2113
 
2022
2114
  // src/combinators/sequence.ts
2023
2115
  function sequence(...parsers) {
@@ -2433,6 +2525,25 @@ function trivia(combinator) {
2433
2525
  parse: combinator.parse.bind(combinator)
2434
2526
  };
2435
2527
  }
2528
+ function classifiedTrivia(arms) {
2529
+ const entries = Object.entries(arms);
2530
+ if (entries.length === 0) {
2531
+ throw new TypeError("classifiedTrivia() requires at least one named trivia arm.");
2532
+ }
2533
+ for (let i = 0; i < entries.length; i++) {
2534
+ const [name, arm] = entries[i];
2535
+ const first = arm._meta.firstSet;
2536
+ if (matchesEmpty(arm) || first.kind !== "ranges" || first.ranges.length === 0) {
2537
+ throw new TypeError(
2538
+ `classifiedTrivia(): ${JSON.stringify(name)} must be non-nullable with a concrete finite first set.`
2539
+ );
2540
+ }
2541
+ }
2542
+ const labeledArms = entries.map(([name, arm]) => label(name, arm));
2543
+ const result = trivia(oneOrMore(choice(...labeledArms)));
2544
+ result._meta.rootTriviaClassified = true;
2545
+ return result;
2546
+ }
2436
2547
  function label(name, combinator) {
2437
2548
  return {
2438
2549
  _tag: combinator._tag,
@@ -2526,8 +2637,48 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2526
2637
  };
2527
2638
  }
2528
2639
 
2640
+ // src/compiler/degradation.ts
2641
+ function formatDegradation(d) {
2642
+ return `[parseman] degraded [${d.code}] ${d.where}: ${d.subject} \u2014 ${d.fellBackTo}; otherwise ${d.otherwise}`;
2643
+ }
2644
+ var _sinks = [];
2645
+ function resolveDegradationLevel(explicit) {
2646
+ if (explicit !== void 0) return explicit;
2647
+ const env = typeof process !== "undefined" ? process.env?.PARSEMAN_DEGRADATION : void 0;
2648
+ if (env === "off" || env === "warn" || env === "error") return env;
2649
+ return "warn";
2650
+ }
2651
+ function degradationKey(d) {
2652
+ return JSON.stringify([d.code, d.where, d.subject]);
2653
+ }
2654
+ function recordDegradation(d) {
2655
+ const level = resolveDegradationLevel();
2656
+ if (level === "off") return;
2657
+ const key = degradationKey(d);
2658
+ const sink = _sinks[_sinks.length - 1];
2659
+ if (sink) {
2660
+ if (!sink.has(key)) sink.set(key, d);
2661
+ return;
2662
+ }
2663
+ if (_immediate.has(key)) return;
2664
+ _immediate.add(key);
2665
+ if (level === "error") throw new Error(`parseman: degraded compilation path
2666
+ ${formatDegradation(d)}`);
2667
+ console.warn(formatDegradation(d));
2668
+ }
2669
+ var _immediate = /* @__PURE__ */ new Set();
2670
+
2671
+ // src/cst/trivia-entries.ts
2672
+ var EMPTY_INDICES = Object.freeze([]);
2673
+ var EMPTY_GAPS = Object.freeze([]);
2674
+ var SELECTED_ROOT_STRIDE = 5;
2675
+
2529
2676
  // src/compiler/scannable-run.ts
2530
2677
  var META = new Set("()[]{}*+?|^$.".split(""));
2678
+ var SELECTED_ROOT_OWNED_RANGE_PLACEHOLDER = Array.from(
2679
+ { length: SELECTED_ROOT_STRIDE - 3 },
2680
+ () => "0"
2681
+ ).join(", ");
2531
2682
 
2532
2683
  // src/compiler/trivia-fast-path.ts
2533
2684
  var CAP_RECORD = [
@@ -2536,50 +2687,193 @@ var CAP_RECORD = [
2536
2687
  ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
2537
2688
  ` }`
2538
2689
  ].join("\n");
2690
+ var SELECTED_ROOT_FINISH = [
2691
+ ` if (_cap && _ctx._rootTriviaCapture !== false && _ctx._rootTriviaLog !== undefined && _ctx._rootTriviaLog.length !== _rootMark) {`,
2692
+ ` for (let _ri = _rootMark; _ri < _ctx._rootTriviaLog.length; _ri += ${SELECTED_ROOT_STRIDE}) { _ctx._rootTriviaLog[_ri] = _pos; _ctx._rootTriviaLog[_ri + 1] = _e }`,
2693
+ ` }`
2694
+ ].join("\n");
2539
2695
 
2540
2696
  // src/compiler/build-arity.ts
2541
2697
  function cstOutputHost(build) {
2542
2698
  return build?._parsemanCstOutput === true;
2543
2699
  }
2544
- var PARAM_LIST_RE = (
2545
- // (params) => ... | function name?(params) ... | single-ident arrow `x => ...`
2546
- /^(?:function\b[^(]*\(([^)]*)\)|\(([^)]*)\)\s*=>|([A-Za-z_$][\w$]*)\s*=>)/
2547
- );
2548
- var CONFIRMABLE_PARAM_RE = /^[A-Za-z_$][\w$]*\s*\??\s*(?::[^,=]+)?$/;
2700
+ var SINGLE_IDENT_ARROW_RE = /^[A-Za-z_$][\w$]*\s*=>/;
2701
+ var PARAM_LIST_OPEN_RE = /^(?:function\b[^(]*)?\(/;
2702
+ var NATIVE_CODE_RE = /\{\s*\[native code\]\s*\}/;
2703
+ var PARAM_HEAD_RE = /^[A-Za-z_$][\w$]*\s*\??\s*/;
2549
2704
  function confirmedBuildArity(src) {
2550
2705
  const s = src.trim();
2551
- const m = PARAM_LIST_RE.exec(s);
2552
- if (!m) return null;
2553
- if (m[3] !== void 0) return 1;
2554
- const inner = (m[1] ?? m[2] ?? "").trim();
2555
- if (inner === "") return 0;
2556
- const parts = inner.split(",");
2706
+ if (NATIVE_CODE_RE.test(s)) return null;
2707
+ if (/\barguments\b/.test(s)) return null;
2708
+ if (SINGLE_IDENT_ARROW_RE.test(s)) return 1;
2709
+ const inner = paramListText(s);
2710
+ if (inner === null) return null;
2711
+ if (inner.trim() === "") return 0;
2712
+ const parts = splitTopLevel(inner);
2713
+ if (parts === null) return null;
2557
2714
  for (const part of parts) {
2558
- if (!CONFIRMABLE_PARAM_RE.test(part.trim())) return null;
2715
+ if (!isConfirmableParam(part.trim())) return null;
2559
2716
  }
2560
- if (/\barguments\b/.test(s)) return null;
2561
2717
  return parts.length;
2562
2718
  }
2719
+ function paramListText(s) {
2720
+ const open = PARAM_LIST_OPEN_RE.exec(s);
2721
+ if (!open) return null;
2722
+ const start = open[0].length;
2723
+ const end = matchingParen(s, start);
2724
+ if (end === null) return null;
2725
+ if (!s.startsWith("function") && !/^\s*=>/.test(s.slice(end + 1))) return null;
2726
+ return s.slice(start, end);
2727
+ }
2728
+ function matchingParen(s, start) {
2729
+ let depth = 0;
2730
+ for (let i = start; i < s.length; i++) {
2731
+ const c = s[i];
2732
+ if (c === '"' || c === "'" || c === "`") {
2733
+ const close = skipString(s, i);
2734
+ if (close === null) return null;
2735
+ i = close;
2736
+ continue;
2737
+ }
2738
+ if (c === "(" || c === "[" || c === "{") depth++;
2739
+ else if (c === "]" || c === "}") {
2740
+ if (--depth < 0) return null;
2741
+ } else if (c === ")") {
2742
+ if (depth === 0) return i;
2743
+ depth--;
2744
+ }
2745
+ }
2746
+ return null;
2747
+ }
2748
+ function skipString(s, open) {
2749
+ const quote2 = s[open];
2750
+ for (let i = open + 1; i < s.length; i++) {
2751
+ if (s[i] === "\\") {
2752
+ i++;
2753
+ continue;
2754
+ }
2755
+ if (s[i] === quote2) return i;
2756
+ }
2757
+ return null;
2758
+ }
2759
+ function splitTopLevel(inner) {
2760
+ const parts = [];
2761
+ let depth = 0;
2762
+ let angle = 0;
2763
+ let last = 0;
2764
+ for (let i = 0; i < inner.length; i++) {
2765
+ const c = inner[i];
2766
+ if (c === "=" && inner[i + 1] === ">") {
2767
+ i++;
2768
+ continue;
2769
+ }
2770
+ if (c === '"' || c === "'" || c === "`") {
2771
+ const close = skipString(inner, i);
2772
+ if (close === null) return null;
2773
+ i = close;
2774
+ continue;
2775
+ }
2776
+ if (c === "(" || c === "[" || c === "{") depth++;
2777
+ else if (c === ")" || c === "]" || c === "}") {
2778
+ if (--depth < 0) return null;
2779
+ } else if (c === "<") angle++;
2780
+ else if (c === ">") {
2781
+ if (--angle < 0) return null;
2782
+ } else if (c === "," && depth === 0 && angle === 0) {
2783
+ parts.push(inner.slice(last, i));
2784
+ last = i + 1;
2785
+ }
2786
+ }
2787
+ if (depth !== 0 || angle !== 0) return null;
2788
+ parts.push(inner.slice(last));
2789
+ return parts;
2790
+ }
2791
+ function isConfirmableParam(part) {
2792
+ const head = PARAM_HEAD_RE.exec(part);
2793
+ if (!head) return false;
2794
+ const rest = part.slice(head[0].length);
2795
+ if (rest === "") return true;
2796
+ if (rest[0] !== ":") return false;
2797
+ return !hasDefaultAssign(rest.slice(1));
2798
+ }
2799
+ function hasDefaultAssign(annotation) {
2800
+ for (let i = 0; i < annotation.length; i++) {
2801
+ if (annotation[i] !== "=") continue;
2802
+ if (annotation[i + 1] === ">" || annotation[i + 1] === "=") {
2803
+ i++;
2804
+ continue;
2805
+ }
2806
+ const prev = annotation[i - 1];
2807
+ if (prev === "<" || prev === ">" || prev === "!" || prev === "=") continue;
2808
+ return true;
2809
+ }
2810
+ return false;
2811
+ }
2812
+ function buildAnalysisSrc(def) {
2813
+ return def.buildSigSrc ?? def.buildSrc ?? def.build.toString();
2814
+ }
2815
+ var RESOLVER_REASONS = {
2816
+ "rest-parameter": "its parameter list uses a rest parameter, so the declared arity is unbounded",
2817
+ "arguments": "its body references `arguments`",
2818
+ "reassigned": "the binding it names is reassigned, so the function read here is not decidable",
2819
+ "not-a-function": "the binding it names is not a function declaration",
2820
+ "unresolved-import": "its module could not be resolved or parsed",
2821
+ "not-found": "the name did not resolve to any binding",
2822
+ "computed": "it is a computed expression",
2823
+ "foreign-source": "it is named inside an imported factory, whose offsets the entry-file resolver cannot index"
2824
+ };
2825
+ function unconfirmableReason(def, src) {
2826
+ const declared = def.buildArityUnresolved;
2827
+ if (declared !== void 0 && RESOLVER_REASONS[declared]) return RESOLVER_REASONS[declared];
2828
+ const s = src.trim();
2829
+ const params = s.slice(0, s.indexOf(")") + 1);
2830
+ if (params.includes("...")) return RESOLVER_REASONS["rest-parameter"];
2831
+ if (/\barguments\b/.test(s)) return RESOLVER_REASONS["arguments"];
2832
+ if (/^[A-Za-z_$][\w$]*(\s*\.\s*[A-Za-z_$][\w$]*)?$/.test(s)) {
2833
+ return "it names a binding this build could not resolve to a function declaration";
2834
+ }
2835
+ return "its parameter list could not be parsed";
2836
+ }
2837
+ function confirmedArityForDef(def) {
2838
+ if (def.buildArity !== void 0) return def.buildArity;
2839
+ const src = buildAnalysisSrc(def);
2840
+ const arity = confirmedBuildArity(src);
2841
+ if (arity !== null) return arity;
2842
+ const subject = `build reducer \`${(def.buildSrc ?? "<runtime fn>").trim().split("\n")[0].slice(0, 60)}\``;
2843
+ recordDegradation({
2844
+ code: "build-arity-unconfirmed",
2845
+ severity: "warn",
2846
+ where: `node("${def.type ?? "<inferred>"}")`,
2847
+ subject,
2848
+ fellBackTo: `could not confirm its declared arity \u2014 ${unconfirmableReason(def, src)} \u2014 so this node captures children, fields and raw children, logs trivia, and clones \`_ctx.state\` on every match`,
2849
+ otherwise: "only the tiers the reducer actually declares would be captured (arity >= 1 children, >= 2 fields, >= 4 raw, >= 5 trivia, >= 6 state). Declare it with `node(..., { buildArity: n })` if it cannot be derived"
2850
+ });
2851
+ return null;
2852
+ }
2563
2853
  function buildReadsTrivia(def) {
2564
2854
  if (!def.build) return true;
2565
- const src = def.buildSrc ?? def.build.toString();
2566
- const arity = confirmedBuildArity(src);
2855
+ const arity = confirmedArityForDef(def);
2567
2856
  if (arity === null) return true;
2568
2857
  return arity >= 5;
2569
2858
  }
2570
2859
  function buildReadsState(def) {
2571
2860
  if (!def.build) return true;
2572
- const src = def.buildSrc ?? def.build.toString();
2573
- const arity = confirmedBuildArity(src);
2861
+ const arity = confirmedArityForDef(def);
2574
2862
  if (arity === null) return true;
2575
2863
  return arity >= 6;
2576
2864
  }
2577
2865
 
2866
+ // src/compiler/inline-build.ts
2867
+ var ANN = String.raw`\s*\??\s*(?::[^,)=]+)?`;
2868
+ var ANY_P = String.raw`\w+${ANN}`;
2869
+ var MK_BUILD_RE = new RegExp(
2870
+ String.raw`^\(\s*(\w+)${ANN}\s*,\s*${ANY_P}\s*,\s*(\w+)${ANN}\s*,\s*(\w+)${ANN}\s*,\s*(\w+)${ANN}\s*\)\s*=>\s*mk\s*\(\s*(['"])([^'"]+)\5\s*,\s*\1\s*,\s*\3\s*,\s*\2\s*,\s*\4\s*\)\s*$`
2871
+ );
2872
+
2578
2873
  // src/compiler/fields.ts
2579
2874
  function buildReadsFields(def) {
2580
2875
  if (!def.build) return true;
2581
- const src = def.buildSrc ?? def.build.toString();
2582
- const arity = confirmedBuildArity(src);
2876
+ const arity = confirmedArityForDef(def);
2583
2877
  if (arity === null) return true;
2584
2878
  return arity >= 2;
2585
2879
  }
@@ -2830,17 +3124,21 @@ function when(key, parser2, opts = {}) {
2830
3124
  function otherwise(parser2) {
2831
3125
  return { kind: "otherwise", parser: parser2, usesRouted: parserUsesRouted(parser2) };
2832
3126
  }
2833
- function routed() {
3127
+ function routed(fallback) {
2834
3128
  return {
2835
3129
  _tag: "routed",
2836
- _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
2837
- _def: { tag: "routed" },
3130
+ _meta: {
3131
+ // The routed token is whatever the selector matched, so the first set stays
3132
+ // `any` even with a fallback — `any` already subsumes the fallback's.
3133
+ firstSet: { kind: "any" },
3134
+ canMatchNewline: fallback?._meta.canMatchNewline ?? false,
3135
+ isTrivia: false
3136
+ },
3137
+ _def: fallback === void 0 ? { tag: "routed" } : { tag: "routed", fallback },
2838
3138
  parse(_input, pos, ctx) {
2839
3139
  const item = ctx._routed;
2840
- if (item === void 0) {
2841
- return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
2842
- }
2843
- if (pos !== item.span.start) {
3140
+ if (item === void 0 || pos !== item.span.start) {
3141
+ if (fallback !== void 0) return fallback.parse(_input, pos, ctx);
2844
3142
  return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
2845
3143
  }
2846
3144
  if (cstCaptureActive(ctx)) {
@@ -2975,10 +3273,12 @@ function attempt(parser2) {
2975
3273
  }
2976
3274
  const mark = saveCstMark(ctx);
2977
3275
  const log = ctx._triviaLog?.length;
3276
+ const rootLog = ctx._rootTriviaLog?.length;
2978
3277
  const result = parser2.parse(input, pos, ctx);
2979
3278
  if (result.ok) return result;
2980
3279
  rollbackCstCapture(ctx, mark);
2981
3280
  if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
3281
+ if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
2982
3282
  if (result.committed) return result;
2983
3283
  return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
2984
3284
  }
@@ -3041,6 +3341,13 @@ function isCstChild(value) {
3041
3341
  function missingInferredType() {
3042
3342
  throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
3043
3343
  }
3344
+ function normalizeBuildArity(arity) {
3345
+ if (arity === void 0) return void 0;
3346
+ if (!Number.isInteger(arity) || arity < 0 || arity > 6) {
3347
+ throw new Error("node() buildArity must be an integer 0..6 (children, fields, span, rawChildren, triviaLog, state)");
3348
+ }
3349
+ return arity;
3350
+ }
3044
3351
  function normalizeProject(project) {
3045
3352
  if (project === void 0) return void 0;
3046
3353
  if (!Number.isInteger(project) || project < 0) {
@@ -3076,13 +3383,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3076
3383
  const captureTrivia = opts?.captureTrivia === true;
3077
3384
  const trailingTrivia = opts?.trailingTrivia === true;
3078
3385
  const tags = opts?.tags;
3386
+ const buildArity = normalizeBuildArity(opts?.buildArity);
3079
3387
  if (unwrap && collapse) {
3080
3388
  throw new Error("node() options cannot set both unwrap and collapse");
3081
3389
  }
3082
3390
  if (project !== void 0 && (unwrap || collapse)) {
3083
3391
  throw new Error("node() options cannot combine project with unwrap or collapse");
3084
3392
  }
3085
- const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia || tags !== void 0 && tags.length > 0 ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {}, ...tags !== void 0 && tags.length > 0 ? { tags } : {} } : baseDef;
3393
+ const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia || buildArity !== void 0 || tags !== void 0 && tags.length > 0 ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {}, ...buildArity !== void 0 ? { buildArity } : {}, ...tags !== void 0 && tags.length > 0 ? { tags } : {} } : baseDef;
3086
3394
  const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
3087
3395
  const clonesState = build ? buildReadsState(def) : project === void 0;
3088
3396
  const hasOwnFields = parserHasOwnFields(combinator);
@@ -3128,7 +3436,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3128
3436
  const cstOutput = cstOutputHost(ctx.build);
3129
3437
  const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3130
3438
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3131
- const built = unwrap && children.length === 1 ? unwrapChild(children[0]) : collapse && children.length === 1 ? children[0] : !build && project === void 0 && ctx.build?._parsemanCstCollapse && children.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children[0], children, rawChildren) ? children[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, span, rawChildren, triviaLog, hostState, tags) : projectChild(children, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, span, rawChildren, triviaLog, hostState, tags) : build(children, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children, fields, span, rawChildren, triviaLog, st, tags) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children };
3439
+ const built = unwrap && children.length === 1 ? unwrapChild(children[0]) : collapse && children.length === 1 ? children[0] : (cstOutput || !build && project === void 0) && ctx.build?._parsemanCstCollapse && children.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children[0], children, rawChildren) ? children[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, span, rawChildren, triviaLog, hostState, tags) : projectChild(children, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, span, rawChildren, triviaLog, hostState, tags) : build(children, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children, fields, span, rawChildren, triviaLog, st, tags) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children };
3132
3440
  const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span };
3133
3441
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3134
3442
  pushCstChild(ctx, built, rawEntry);
@@ -3158,6 +3466,7 @@ function token(root) {
3158
3466
  const savedRaw = ctx._cstRawChildren;
3159
3467
  const savedTriviaLog = ctx._cstTriviaLog;
3160
3468
  const savedOuterTriviaLog = ctx._triviaLog;
3469
+ const savedRootTriviaLog = ctx._rootTriviaLog;
3161
3470
  const wasCapturing = cstCaptureActive(ctx);
3162
3471
  ctx.trivia = void 0;
3163
3472
  ctx.triviaKindLabels = void 0;
@@ -3167,6 +3476,7 @@ function token(root) {
3167
3476
  ctx._cstRawChildren = void 0;
3168
3477
  ctx._cstTriviaLog = void 0;
3169
3478
  delete ctx._triviaLog;
3479
+ delete ctx._rootTriviaLog;
3170
3480
  let result;
3171
3481
  try {
3172
3482
  result = root.parse(input, pos, ctx);
@@ -3180,6 +3490,8 @@ function token(root) {
3180
3490
  ctx._cstTriviaLog = savedTriviaLog;
3181
3491
  if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
3182
3492
  else ctx._triviaLog = savedOuterTriviaLog;
3493
+ if (savedRootTriviaLog === void 0) delete ctx._rootTriviaLog;
3494
+ else ctx._rootTriviaLog = savedRootTriviaLog;
3183
3495
  }
3184
3496
  if (!result.ok) return result;
3185
3497
  const span = result.span;
@@ -3279,7 +3591,7 @@ function evalRuleMapIR(ir) {
3279
3591
  l._def.fnSrc = src;
3280
3592
  return l;
3281
3593
  };
3282
- const _nd = (type, child, src, opts, staticError) => {
3594
+ const _nd = (type, child, src, opts, staticError, sigSrc) => {
3283
3595
  if (staticError !== void 0 && staticError.length > 0) {
3284
3596
  throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
3285
3597
  }
@@ -3288,6 +3600,7 @@ function evalRuleMapIR(ir) {
3288
3600
  }), opts);
3289
3601
  n._def.buildSrc = src;
3290
3602
  if (staticError !== void 0) n._def.buildStaticError = staticError;
3603
+ if (sigSrc !== void 0) n._def.buildSigSrc = sigSrc;
3291
3604
  return n;
3292
3605
  };
3293
3606
  const _gch = (items) => {
@@ -3359,6 +3672,7 @@ function evalRuleMapIR(ir) {
3359
3672
  "transform",
3360
3673
  "skip",
3361
3674
  "trivia",
3675
+ "classifiedTrivia",
3362
3676
  "label",
3363
3677
  "field",
3364
3678
  "expect",
@@ -3399,6 +3713,7 @@ function evalRuleMapIR(ir) {
3399
3713
  transform,
3400
3714
  skip,
3401
3715
  trivia,
3716
+ classifiedTrivia,
3402
3717
  label,
3403
3718
  field,
3404
3719
  expect,
@@ -3618,7 +3933,7 @@ var Builder = class {
3618
3933
  case "peek":
3619
3934
  return { kind: "peek", item: this.walk(def.parser) };
3620
3935
  case "routed":
3621
- return { kind: "empty" };
3936
+ return def.fallback === void 0 ? { kind: "empty" } : this.walk(def.fallback);
3622
3937
  // Transparent semantic wrappers — render the inner syntax.
3623
3938
  case "transform":
3624
3939
  case "token":