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
@@ -82,8 +82,9 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve) {
82
82
  return d.value.length === 0;
83
83
  case "keywords":
84
84
  return false;
85
+ // The routed token itself is the selector's match, never empty; a fallback can be.
85
86
  case "routed":
86
- return false;
87
+ return d.fallback === void 0 ? false : me(d.fallback);
87
88
  case "regex":
88
89
  try {
89
90
  const m = new RegExp(d.source).exec("");
@@ -610,1448 +611,1539 @@ function pushTriviaLogEntry(ctx, start, end, kindIndex) {
610
611
  }
611
612
  }
612
613
 
613
- // src/recovery/scan.ts
614
- function probeAt(sentinel, input, pos, ctx) {
615
- const s = {
616
- _probe: ctx._probe,
617
- _errors: ctx._errors,
618
- _cstLeaves: ctx._cstLeaves,
619
- _cstChildren: ctx._cstChildren,
620
- _cstRawChildren: ctx._cstRawChildren,
621
- _cstTriviaLog: ctx._cstTriviaLog,
622
- _triviaLog: ctx._triviaLog,
623
- _fields: ctx._fields,
624
- _cstBuf: ctx._cstBuf,
625
- _tolerant: ctx._tolerant,
626
- _sync: ctx._sync
627
- };
628
- ctx._probe = void 0;
629
- ctx._errors = void 0;
630
- ctx._cstLeaves = void 0;
631
- ctx._cstChildren = void 0;
632
- ctx._cstRawChildren = void 0;
633
- ctx._cstTriviaLog = void 0;
634
- ctx._triviaLog = void 0;
635
- ctx._fields = void 0;
636
- ctx._cstBuf = void 0;
637
- ctx._tolerant = void 0;
638
- ctx._sync = void 0;
639
- try {
640
- return sentinel.parse(input, pos, ctx).ok;
641
- } finally {
642
- ctx._probe = s._probe;
643
- ctx._errors = s._errors;
644
- ctx._cstLeaves = s._cstLeaves;
645
- ctx._cstChildren = s._cstChildren;
646
- ctx._cstRawChildren = s._cstRawChildren;
647
- ctx._cstTriviaLog = s._cstTriviaLog;
648
- ctx._triviaLog = s._triviaLog;
649
- ctx._fields = s._fields;
650
- ctx._cstBuf = s._cstBuf;
651
- ctx._tolerant = s._tolerant;
652
- ctx._sync = s._sync;
653
- }
614
+ // src/cst/trivia-kinds.ts
615
+ var labeledTriviaSpecs = /* @__PURE__ */ new WeakMap();
616
+ function unwrapTrivia(p) {
617
+ let cur = p;
618
+ while (cur._def.tag === "trivia") cur = cur._def.parser;
619
+ return cur;
654
620
  }
655
- function orSentinel(a, b) {
656
- if (b === void 0) return a;
657
- return {
658
- _tag: "orSentinel",
659
- _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
660
- _def: { tag: "unknown" },
661
- parse(input, pos, ctx) {
662
- const ra = a.parse(input, pos, ctx);
663
- if (ra.ok) return ra;
664
- return b.parse(input, pos, ctx);
665
- }
666
- };
621
+ function peelLabel(p) {
622
+ if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
623
+ return null;
667
624
  }
668
- function firstSetSentinel(fs) {
669
- if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
670
- const ranges = fs.ranges;
671
- return {
672
- _tag: "firstSetSentinel",
673
- _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },
674
- _def: { tag: "unknown" },
675
- parse(input, pos) {
676
- const code = pos < input.length ? input.codePointAt(pos) : void 0;
677
- if (code !== void 0) {
678
- for (const r of ranges) {
679
- if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
680
- }
625
+ function analyzeLabeledTrivia(trivia2) {
626
+ const cached = labeledTriviaSpecs.get(trivia2);
627
+ if (cached !== void 0) return cached;
628
+ let core = unwrapTrivia(trivia2);
629
+ let minRepeats = 1;
630
+ if (core._def.tag === "oneOrMore") {
631
+ core = core._def.parser;
632
+ minRepeats = 1;
633
+ } else if (core._def.tag === "many") {
634
+ core = core._def.parser;
635
+ minRepeats = 0;
636
+ }
637
+ const arms = [];
638
+ if (core._def.tag === "choice") {
639
+ for (let i = 0; i < core._def.parsers.length; i++) {
640
+ const peeled = peelLabel(core._def.parsers[i]);
641
+ if (!peeled) {
642
+ labeledTriviaSpecs.set(trivia2, null);
643
+ return null;
681
644
  }
682
- return { ok: false, expected: [], span: { start: pos, end: pos } };
645
+ arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
683
646
  }
647
+ } else {
648
+ const peeled = peelLabel(core);
649
+ if (!peeled) {
650
+ labeledTriviaSpecs.set(trivia2, null);
651
+ return null;
652
+ }
653
+ arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
654
+ }
655
+ const spec = {
656
+ labels: arms.map((a) => a.label),
657
+ arms,
658
+ minRepeats
684
659
  };
660
+ labeledTriviaSpecs.set(trivia2, spec);
661
+ return spec;
685
662
  }
686
- function recoverScan(input, from, ctx, sync, expected) {
687
- let scanPos = from;
688
- while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
689
- const span = { start: from, end: scanPos };
690
- if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
691
- const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
692
- ctx._errors?.push(error);
693
- return { error, end: scanPos };
694
- }
695
- function matchesAt(sentinel, input, pos, ctx) {
696
- return probeAt(sentinel, input, pos, ctx);
663
+ function matchArmAt(input, pos, arm, state) {
664
+ if (!startsFirstSet(arm, input, pos)) return null;
665
+ const r = arm.parse(input, pos, { trackLines: false, state });
666
+ if (!r.ok || r.span.end <= pos) return null;
667
+ return { end: r.span.end };
697
668
  }
698
- function captureError(ctx, error) {
699
- if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
669
+ function scanLabeledTriviaChunks(input, cur, spec, state) {
670
+ const chunks = [];
671
+ let pos = cur;
672
+ while (pos < input.length) {
673
+ let matched = null;
674
+ for (const arm of spec.arms) {
675
+ const m = matchArmAt(input, pos, arm.parser, state);
676
+ if (m) {
677
+ matched = { end: m.end, kindIndex: arm.kindIndex };
678
+ break;
679
+ }
680
+ }
681
+ if (!matched) break;
682
+ chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
683
+ pos = matched.end;
684
+ }
685
+ if (chunks.length < spec.minRepeats) {
686
+ return { end: cur, chunks: [] };
687
+ }
688
+ return { end: pos, chunks };
700
689
  }
701
-
702
- // src/combinators/expect.ts
703
- function deriveExpected(c) {
704
- return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
690
+ function scanLabeledTriviaEnd(input, cur, spec, state) {
691
+ let pos = cur;
692
+ let count = 0;
693
+ while (pos < input.length) {
694
+ let matched = null;
695
+ for (const arm of spec.arms) {
696
+ matched = matchArmAt(input, pos, arm.parser, state);
697
+ if (matched) break;
698
+ }
699
+ if (!matched) break;
700
+ pos = matched.end;
701
+ count++;
702
+ }
703
+ return count < spec.minRepeats ? cur : pos;
705
704
  }
706
- function derive(c, seen) {
707
- const deriveExpected2 = (p) => derive(p, seen);
708
- const def = c._def;
709
- switch (def.tag) {
710
- case "literal":
711
- return [JSON.stringify(def.value)];
712
- case "regex":
713
- return [`/${def.source}/`];
714
- case "keywords":
715
- return def.words.map((w) => JSON.stringify(w));
716
- case "label":
717
- return [def.label];
718
- case "choice":
719
- return def.parsers.flatMap(deriveExpected2);
720
- case "dispatch":
721
- return deriveExpected2(def.selector);
722
- // Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
723
- // `optional(…)`/`many(…)` can match nothing, so the term after it is equally
724
- // able to fail first — deriving from term 0 alone named a token the parse
725
- // never actually required. A leading `not(…)` is zero-width and contributes
726
- // nothing (its own expectation is about what must NOT be here), but it is
727
- // nullable, so keep scanning past it. Stop at the first term that must match.
728
- case "sequence": {
729
- const out = [];
730
- for (const term of def.parsers) {
731
- if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
732
- if (!matchesEmpty(term)) break;
705
+ function visitLabeledTrivia(input, cur, spec, state, visit) {
706
+ if (spec.minRepeats > 1) return void 0;
707
+ let pos = cur;
708
+ let count = 0;
709
+ while (pos < input.length) {
710
+ let matched = null;
711
+ for (const arm of spec.arms) {
712
+ const match = matchArmAt(input, pos, arm.parser, state);
713
+ if (match) {
714
+ matched = { end: match.end, kindIndex: arm.kindIndex };
715
+ break;
733
716
  }
734
- return out;
735
717
  }
736
- case "attempt":
737
- return deriveExpected2(def.parser);
738
- // Delegating wrappers whose first-token failure is their inner parser's — must
739
- // pass through so a start-failure (and the first-set-miss fast-path in attempt/
740
- // node, which reads this) names the real expected token, not the wrapper tag.
741
- case "field":
742
- case "withCtx":
743
- case "recover":
744
- return deriveExpected2(def.parser);
745
- case "skip":
746
- return deriveExpected2(def.main);
747
- case "expect":
748
- return def.expected;
749
- case "node":
750
- case "grammar":
751
- case "trivia":
752
- case "token":
753
- case "leaf":
754
- case "optional":
755
- case "many":
756
- case "oneOrMore":
757
- case "transform":
758
- case "not":
759
- case "peek":
760
- return deriveExpected2(def.parser);
761
- case "routed":
762
- return ["routed()"];
763
- case "lazy": {
764
- const name = c._ruleName;
765
- if (seen.has(c)) return name ? [name] : [];
766
- seen.add(c);
767
- try {
768
- return deriveExpected2(def.thunk());
769
- } catch {
770
- return name ? [name] : [];
771
- } finally {
772
- seen.delete(c);
718
+ if (!matched) break;
719
+ visit(pos, matched.end, matched.kindIndex);
720
+ pos = matched.end;
721
+ count++;
722
+ }
723
+ return count < spec.minRepeats ? cur : pos;
724
+ }
725
+ function recordTriviaChunks(ctx, chunks) {
726
+ const kinds = ctx.triviaKindLabels;
727
+ const mask = ctx._triviaCaptureMask;
728
+ const rootLog = ctx._rootTriviaLog;
729
+ const rootKinds = ctx._rootTriviaKindIndex;
730
+ const rootMark = rootLog?.length ?? 0;
731
+ for (const ch of chunks) {
732
+ pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
733
+ const rootKindIndex = ctx._rootTriviaCapture === false || rootLog === void 0 || rootKinds === void 0 || kinds === void 0 ? -1 : rootKinds[kinds[ch.kindIndex] ?? ""] ?? -1;
734
+ if (rootKindIndex >= 0) {
735
+ rootLog.push(0, 0, ch.start, ch.end, rootKindIndex);
736
+ }
737
+ if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
738
+ if (mask === void 0 || kinds === void 0 || (mask & 1 << ch.kindIndex) !== 0) {
739
+ pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
773
740
  }
774
741
  }
775
- default:
776
- return [];
777
742
  }
778
- }
779
- function expect(combinator, label2) {
780
- const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
781
- const meta = {
782
- firstSet: combinator._meta.firstSet,
783
- canMatchNewline: combinator._meta.canMatchNewline,
784
- isTrivia: false
785
- };
786
- return {
787
- _tag: "expect",
788
- _meta: meta,
789
- _def: { tag: "expect", parser: combinator, label: label2, expected },
790
- parse(input, pos, ctx) {
791
- const result = combinator.parse(input, pos, ctx);
792
- if (result.ok) return result;
793
- const span = { start: pos, end: pos };
794
- const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
795
- ctx._errors?.push(error);
796
- if (ctx._tolerant) captureError(ctx, error);
797
- return { ok: true, value: error, span: { start: pos, end: pos } };
743
+ if (rootLog !== void 0 && rootLog.length !== rootMark && chunks.length > 0) {
744
+ const start = chunks[0].start;
745
+ const end = chunks[chunks.length - 1].end;
746
+ for (let offset = rootMark; offset < rootLog.length; offset += 5) {
747
+ rootLog[offset] = start;
748
+ rootLog[offset + 1] = end;
798
749
  }
799
- };
750
+ }
800
751
  }
801
-
802
- // src/combinators/choice.ts
803
- function choice(...args) {
804
- const parsers = args.map((a) => "gate" in a ? a.combinator : a);
805
- const gates = args.map((a) => "gate" in a ? a.gate : null);
806
- const hasGates = gates.some((g) => g !== null);
807
- const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
808
- let combined = { kind: "empty" };
809
- for (const p of parsers) combined = union(combined, p._meta.firstSet);
810
- const meta = {
811
- firstSet: combined,
812
- canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
813
- isTrivia: false,
814
- disjoint
815
- };
816
- const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
817
- const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
818
- let greedyLitMap = null;
819
- let sortedParsers = null;
820
- const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
821
- if (strategy?.tag === "greedyClassify") {
822
- greedyLitMap = /* @__PURE__ */ new Map();
823
- for (let i = 0; i < parsers.length; i++) {
824
- if (i === strategy.superIndex) continue;
825
- const litVal = getCoreLiteralValue(parsers[i]);
826
- if (litVal !== null) greedyLitMap.set(litVal, i);
827
- }
828
- } else if (strategy?.tag === "literalsLongestFirst") {
829
- sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
752
+ function triviaKindMask(labels, keep) {
753
+ if (!labels) return void 0;
754
+ let mask = 0;
755
+ for (const name of keep) {
756
+ const idx = labels.indexOf(name);
757
+ if (idx >= 0) mask |= 1 << idx;
758
+ }
759
+ return mask;
760
+ }
761
+
762
+ // src/combinators/grammar.ts
763
+ function annotateResultLines(result, index) {
764
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
765
+ const errors = annotated.errors;
766
+ if (errors) {
767
+ ;
768
+ annotated.errors = errors.map((error) => ({
769
+ ...error,
770
+ span: annotateSpan(error.span, index)
771
+ }));
772
+ }
773
+ return annotated;
774
+ }
775
+ function createParseLineContext(input, pos) {
776
+ const lineIndex = createLineIndex();
777
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
778
+ return { lineIndex, lineScannedTo: pos };
779
+ }
780
+ function parser(opts, root) {
781
+ const clearTrivia = opts.trivia === null;
782
+ const opaqueRootCapture = opts.rootCapture === "opaque";
783
+ if (opaqueRootCapture && opts.trivia === void 0) {
784
+ throw new TypeError("parser({ rootCapture: 'opaque' }) requires an explicit trivia scope.");
830
785
  }
831
786
  return {
832
- _tag: "choice",
833
- _meta: meta,
787
+ _tag: "grammar",
788
+ _meta: {
789
+ ...root._meta,
790
+ // Trivia classification describes this grammar's own active trivia
791
+ // scope. A directly nested grammar may have classified trivia of its
792
+ // own, but that must not advertise classification for this wrapper.
793
+ triviaKindLabels: void 0,
794
+ rootTriviaClassified: void 0,
795
+ ...opts.trivia?._meta?.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {},
796
+ ...opts.trivia?._meta?.rootTriviaClassified ? { rootTriviaClassified: true } : {}
797
+ },
834
798
  _def: {
835
- tag: "choice",
836
- parsers,
837
- gates,
838
- disjoint,
839
- strategy: strategy ?? { tag: "firstMatch" },
840
- autoNot
799
+ tag: "grammar",
800
+ parser: root,
801
+ triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
802
+ clearTrivia,
803
+ ...opaqueRootCapture ? { rootCapture: "opaque" } : {},
804
+ ...opts.captureTrivia ? { captureTrivia: true } : {},
805
+ trackLines: opts.trackLines ?? false
841
806
  },
842
- parse(input, pos, ctx) {
843
- const expected = [];
844
- if (disjoint && pos < input.length) {
845
- const code = input.codePointAt(pos);
846
- let idx = code < 128 ? asciiDispatch[code] : -1;
847
- if (idx < 0) {
848
- for (let i = 0; i < parsers.length; i++) {
849
- if (inFirstSet(code, parsers[i]._meta.firstSet)) {
850
- idx = i;
851
- break;
852
- }
853
- }
854
- }
855
- if (idx >= 0) {
856
- const gate = gates[idx];
857
- if (gate && !gate(ctx.state)) {
858
- return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
859
- }
860
- const result = parsers[idx].parse(input, pos, ctx);
861
- if (result.ok) return result;
862
- expected.push(...result.expected);
863
- if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
864
- return { ok: false, expected, span: { start: pos, end: pos } };
865
- }
866
- return {
867
- ok: false,
868
- expected: parsers.flatMap((p) => {
869
- const r = p.parse(input, pos, ctx);
870
- return r.ok ? [] : r.expected;
871
- }),
872
- span: { start: pos, end: pos }
873
- };
874
- }
875
- if (strategy?.tag === "greedyClassify") {
876
- const superResult = parsers[strategy.superIndex].parse(input, pos, ctx);
877
- if (!superResult.ok) return superResult;
878
- const end = superResult.span.end;
879
- const litIdx = greedyLitMap.get(input.slice(pos, end));
880
- if (litIdx !== void 0) {
881
- const litVal = getCoreLiteralValue(parsers[litIdx]);
882
- const value = applyTransforms(parsers[litIdx], litVal, { start: pos, end });
883
- return { ok: true, value, span: { start: pos, end } };
884
- }
885
- return superResult;
886
- }
887
- if (strategy?.tag === "literalsLongestFirst") {
888
- for (const p of sortedParsers) {
889
- const r = p.parse(input, pos, ctx);
890
- if (r.ok) return r;
891
- expected.push(...r.expected);
892
- }
893
- return { ok: false, expected, span: { start: pos, end: pos } };
807
+ parse(input, pos, _ctx) {
808
+ if (_ctx?._rootTriviaStrictScopes && opts.trivia !== void 0 && opts.trivia !== null && !opts.trivia._meta.rootTriviaClassified && !opaqueRootCapture) {
809
+ throw new TypeError(
810
+ "parser(): selected root trivia requires classifiedTrivia() for every local trivia scope, or rootCapture: 'opaque'."
811
+ );
894
812
  }
895
- for (let i = 0; i < parsers.length; i++) {
896
- if (gates[i] && !gates[i](ctx.state)) continue;
897
- const mark = saveCstMark(ctx);
898
- const logLen = ctx._triviaLog?.length;
899
- const result = parsers[i].parse(input, pos, ctx);
900
- if (!result.ok) {
901
- rollbackCstCapture(ctx, mark);
902
- if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
903
- expected.push(...result.expected);
904
- if (result.committed) return { ok: false, expected, span: result.span, committed: true };
905
- continue;
906
- }
907
- const checks = autoNot[i];
908
- if (checks && autoNotFires(input, result.span.end, checks)) {
909
- rollbackCstCapture(ctx, mark);
910
- if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
911
- continue;
912
- }
913
- return result;
813
+ const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
814
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
815
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
816
+ const ctx = {
817
+ ..._ctx,
818
+ trackLines,
819
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
820
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
821
+ // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
822
+ ...clearTrivia ? {
823
+ trivia: void 0,
824
+ triviaKindLabels: void 0
825
+ } : opts.trivia != null ? {
826
+ trivia: opts.trivia,
827
+ ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
828
+ } : {},
829
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
830
+ // Kind-filter for per-node capture. Resolve against this scope's trivia
831
+ // labels — this parser's own trivia if it declares one, else the INHERITED
832
+ // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
833
+ // trivia is inherited rather than re-declared here. No labels → undefined
834
+ // (capture all).
835
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {},
836
+ // This is a property of the local trivia scope, not a post-parse filter:
837
+ // nested recognition can still skip its trivia, but selected root rows
838
+ // must never be written for an explicitly opaque region.
839
+ ...opaqueRootCapture ? { _rootTriviaCapture: false } : {}
840
+ };
841
+ const result = root.parse(input, pos ?? 0, ctx);
842
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
843
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
914
844
  }
915
- return { ok: false, expected, span: { start: pos, end: pos } };
845
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
916
846
  }
917
847
  };
918
848
  }
919
- function detectStrategy(parsers) {
920
- const regexIndices = [];
921
- const literalIndices = [];
922
- for (let i = 0; i < parsers.length; i++) {
923
- if (getCoreRegexDef(parsers[i]) !== null) regexIndices.push(i);
924
- else if (getCoreLiteralValue(parsers[i]) !== null) literalIndices.push(i);
849
+
850
+ // src/cst/reflection.ts
851
+ var GRAMMAR_REFLECTION = /* @__PURE__ */ Symbol.for("parseman.grammarReflection");
852
+ function childrenOf(def) {
853
+ switch (def.tag) {
854
+ case "sequence":
855
+ case "choice":
856
+ return def.parsers;
857
+ case "dispatch":
858
+ return [
859
+ def.selector,
860
+ ...def.cases.map((c) => c.parser),
861
+ ...def.matchers ? def.matchers.map((c) => c.parser) : [],
862
+ ...def.otherwise ? [def.otherwise] : []
863
+ ];
864
+ case "many":
865
+ case "oneOrMore":
866
+ case "optional":
867
+ case "attempt":
868
+ case "transform":
869
+ case "trivia":
870
+ case "token":
871
+ case "leaf":
872
+ case "label":
873
+ case "field":
874
+ case "not":
875
+ case "peek":
876
+ case "node":
877
+ case "withCtx":
878
+ case "recover":
879
+ case "expect":
880
+ return [def.parser];
881
+ case "grammar":
882
+ return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
883
+ case "sepBy":
884
+ return [def.parser, def.separator];
885
+ case "skip":
886
+ return [def.main, def.skipped];
887
+ case "scanTo":
888
+ return [def.sentinel, ...def.skip];
889
+ case "routed":
890
+ return def.fallback ? [def.fallback] : [];
891
+ case "literal":
892
+ case "regex":
893
+ case "keywords":
894
+ case "guard":
895
+ case "lazy":
896
+ case "unknown":
897
+ return [];
925
898
  }
926
- if (regexIndices.length === 1 && literalIndices.length === parsers.length - 1 && literalIndices.length > 0) {
927
- const superIndex = regexIndices[0];
928
- const regexDef = getCoreRegexDef(parsers[superIndex]);
929
- const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
930
- const re = new RegExp(regexDef.source, flags);
931
- const allSubsumed = literalIndices.every((i) => {
932
- const litVal = getCoreLiteralValue(parsers[i]);
933
- re.lastIndex = 0;
934
- const m = re.exec(litVal);
935
- return m !== null && m[0] === litVal;
936
- });
937
- if (allSubsumed) return { tag: "greedyClassify", superIndex };
899
+ }
900
+ function addNode(out, def) {
901
+ if (def.type === void 0) return;
902
+ const existing = out.get(def.type);
903
+ if (existing === void 0) {
904
+ out.set(def.type, def.tags === void 0 ? [] : [...def.tags]);
905
+ return;
938
906
  }
939
- if (parsers.length === literalIndices.length) {
940
- const sortedIndices = [...literalIndices].sort(
941
- (a, b) => getCoreLiteralValue(parsers[b]).length - getCoreLiteralValue(parsers[a]).length
942
- );
943
- return { tag: "literalsLongestFirst", sortedIndices };
907
+ if (def.tags === void 0) return;
908
+ for (const tag of def.tags) {
909
+ if (!existing.includes(tag)) existing.push(tag);
944
910
  }
945
- const shared = detectSharedPrefix(parsers);
946
- if (shared !== null) return shared;
947
- return { tag: "firstMatch" };
948
- }
949
- function bareLeadingTermKey(term) {
950
- const d = term._def;
951
- if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
952
- if (d.tag === "regex") return `R:${d.source}\0${d.flags}`;
953
- return null;
954
911
  }
955
- function detectSharedPrefix(parsers) {
956
- if (parsers.length < 2) return null;
957
- let key = null;
958
- let prefix = null;
959
- const members = [];
960
- for (let i = 0; i < parsers.length; i++) {
961
- const term0 = leadingTermOfArm(parsers[i]);
962
- if (term0 === null) return null;
963
- const k = bareLeadingTermKey(term0);
964
- if (k === null) return null;
965
- if (key === null) {
966
- key = k;
967
- prefix = term0;
968
- } else if (k !== key) return null;
969
- members.push(i);
912
+ function collectFromCombinator(parser2, out, seen, followLazy = true) {
913
+ if (seen.has(parser2)) return;
914
+ seen.add(parser2);
915
+ const def = parser2._def;
916
+ if (def.tag === "lazy") {
917
+ if (followLazy) {
918
+ try {
919
+ collectFromCombinator(def.thunk(), out, seen, followLazy);
920
+ } catch {
921
+ }
922
+ }
923
+ return;
970
924
  }
971
- if (prefix === null || members.length < 2) return null;
972
- return { tag: "sharedPrefix", prefix, members };
925
+ if (def.tag === "node") addNode(out, def);
926
+ for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy);
973
927
  }
974
- function leadingTermOfArm(arm) {
975
- let d = arm._def;
976
- for (; ; ) {
977
- if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
978
- d = d.parser._def;
928
+ function collectGrammarReflection(ruleMap, opts) {
929
+ const byType = /* @__PURE__ */ new Map();
930
+ const followLazy = opts?.followLazy !== false;
931
+ for (const [, rule] of ruleMap) {
932
+ if (!followLazy && rule._def.tag === "lazy") {
933
+ try {
934
+ const resolved = rule._def.thunk();
935
+ if (resolved._def.tag !== "lazy") collectFromCombinator(resolved, byType, /* @__PURE__ */ new Set(), false);
936
+ } catch {
937
+ }
979
938
  continue;
980
939
  }
981
- break;
940
+ collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
982
941
  }
983
- if (d.tag !== "sequence" || d.parsers.length < 2) return null;
984
- return d.parsers[0];
942
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
985
943
  }
986
- function computeAutoNot(parsers) {
987
- return parsers.map((p, i) => {
988
- const litVal = getCoreLiteralValue(p);
989
- if (litVal === null) return null;
990
- const checks = [];
991
- for (let j2 = i + 1; j2 < parsers.length; j2++) {
992
- const other = parsers[j2];
993
- const otherLit = getCoreLiteralValue(other);
994
- if (otherLit !== null && otherLit.startsWith(litVal) && otherLit.length > litVal.length) {
995
- checks.push({ kind: "startsWith", value: otherLit.slice(litVal.length) });
996
- continue;
944
+ function attachGrammarReflection(grammar, reflection) {
945
+ Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true });
946
+ return grammar;
947
+ }
948
+
949
+ // src/combinators/parser.ts
950
+ var RULE_ORDER = "__parsemanRuleOrder";
951
+ function tagRule(r, key) {
952
+ ;
953
+ r._ruleName = key;
954
+ if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
955
+ }
956
+ function isDefinableRef(v) {
957
+ return !!v && typeof v === "object" && "_def" in v && v._def.tag === "lazy" && typeof v.define === "function";
958
+ }
959
+ function ruleNameOf(r) {
960
+ return r._ruleName;
961
+ }
962
+ function isNamedRuleRefForAnotherRule(r, key) {
963
+ const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
964
+ return name !== void 0 && name !== key;
965
+ }
966
+ function rules(a, b) {
967
+ const factory = typeof a === "function" ? a : b;
968
+ const options = typeof a === "function" ? b : a;
969
+ const cache = {};
970
+ const proxy = new Proxy(cache, {
971
+ get(target, key) {
972
+ if (typeof key !== "string") return void 0;
973
+ const record = target;
974
+ if (!(key in record)) {
975
+ const r = ref();
976
+ tagRule(r, key);
977
+ record[key] = r;
997
978
  }
998
- const regexDef = getCoreRegexDef(other);
999
- if (regexDef !== null) {
1000
- const contSet = continuationFirstSet(litVal, regexDef.source, regexDef.flags);
1001
- if (contSet !== null) checks.push({ kind: "firstSet", set: contSet });
979
+ return record[key];
980
+ }
981
+ });
982
+ const definitions = factory(proxy);
983
+ for (const key of Object.keys(definitions)) {
984
+ const placeholder = cache[key];
985
+ const parser2 = definitions[key];
986
+ if (placeholder === parser2) {
987
+ throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
988
+ }
989
+ if (isDefinableRef(placeholder)) {
990
+ if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
991
+ placeholder.define(parser2);
992
+ placeholder._meta.firstSet = parser2._meta.firstSet;
993
+ placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
994
+ } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
995
+ const alias = ref();
996
+ tagRule(alias, key);
997
+ alias.define(parser2);
998
+ alias._meta.firstSet = parser2._meta.firstSet;
999
+ alias._meta.canMatchNewline = parser2._meta.canMatchNewline;
1000
+ cache[key] = alias;
1001
+ } else {
1002
+ tagRule(parser2, key);
1003
+ cache[key] = parser2;
1004
+ }
1005
+ }
1006
+ if (options?.trivia != null) {
1007
+ for (const key of Object.keys(definitions)) {
1008
+ const rule = cache[key];
1009
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
1010
+ }
1011
+ }
1012
+ if (options?.scanSkip != null) {
1013
+ for (const key of Object.keys(definitions)) {
1014
+ const rule = cache[key];
1015
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
1016
+ }
1017
+ }
1018
+ if (options?.hostMode === "cst") {
1019
+ for (const key of Object.keys(definitions)) {
1020
+ const rule = cache[key];
1021
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
1022
+ }
1023
+ }
1024
+ if (options?.trackLines === true) {
1025
+ for (const key of Object.keys(definitions)) {
1026
+ const rule = cache[key];
1027
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
1028
+ }
1029
+ for (const key of Object.keys(definitions)) {
1030
+ const rule = cache[key];
1031
+ if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
1032
+ const wrapped = parser({ trackLines: true }, rule);
1033
+ tagRule(wrapped, key);
1034
+ wrapped._meta.grammarTrackLines = true;
1035
+ cache[key] = wrapped;
1002
1036
  }
1003
1037
  }
1004
- return checks.length > 0 ? checks : null;
1005
- });
1038
+ }
1039
+ for (const key of Object.keys(definitions)) {
1040
+ markUnusedValues(cache[key]);
1041
+ }
1042
+ Object.defineProperty(cache, RULE_ORDER, {
1043
+ value: Object.keys(definitions),
1044
+ enumerable: false,
1045
+ configurable: true
1046
+ });
1047
+ attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
1048
+ return cache;
1049
+ }
1050
+
1051
+ // src/recovery/scan.ts
1052
+ function probeAt(sentinel, input, pos, ctx) {
1053
+ const s = {
1054
+ _probe: ctx._probe,
1055
+ _errors: ctx._errors,
1056
+ _cstLeaves: ctx._cstLeaves,
1057
+ _cstChildren: ctx._cstChildren,
1058
+ _cstRawChildren: ctx._cstRawChildren,
1059
+ _cstTriviaLog: ctx._cstTriviaLog,
1060
+ _triviaLog: ctx._triviaLog,
1061
+ _rootTriviaLog: ctx._rootTriviaLog,
1062
+ _fields: ctx._fields,
1063
+ _cstBuf: ctx._cstBuf,
1064
+ _tolerant: ctx._tolerant,
1065
+ _sync: ctx._sync
1066
+ };
1067
+ ctx._probe = void 0;
1068
+ ctx._errors = void 0;
1069
+ ctx._cstLeaves = void 0;
1070
+ ctx._cstChildren = void 0;
1071
+ ctx._cstRawChildren = void 0;
1072
+ ctx._cstTriviaLog = void 0;
1073
+ ctx._triviaLog = void 0;
1074
+ ctx._rootTriviaLog = void 0;
1075
+ ctx._fields = void 0;
1076
+ ctx._cstBuf = void 0;
1077
+ ctx._tolerant = void 0;
1078
+ ctx._sync = void 0;
1079
+ try {
1080
+ return sentinel.parse(input, pos, ctx).ok;
1081
+ } finally {
1082
+ ctx._probe = s._probe;
1083
+ ctx._errors = s._errors;
1084
+ ctx._cstLeaves = s._cstLeaves;
1085
+ ctx._cstChildren = s._cstChildren;
1086
+ ctx._cstRawChildren = s._cstRawChildren;
1087
+ ctx._cstTriviaLog = s._cstTriviaLog;
1088
+ ctx._triviaLog = s._triviaLog;
1089
+ ctx._rootTriviaLog = s._rootTriviaLog;
1090
+ ctx._fields = s._fields;
1091
+ ctx._cstBuf = s._cstBuf;
1092
+ ctx._tolerant = s._tolerant;
1093
+ ctx._sync = s._sync;
1094
+ }
1095
+ }
1096
+ function orSentinel(a, b) {
1097
+ if (b === void 0) return a;
1098
+ return {
1099
+ _tag: "orSentinel",
1100
+ _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
1101
+ _def: { tag: "unknown" },
1102
+ parse(input, pos, ctx) {
1103
+ const ra = a.parse(input, pos, ctx);
1104
+ if (ra.ok) return ra;
1105
+ return b.parse(input, pos, ctx);
1106
+ }
1107
+ };
1006
1108
  }
1007
- function autoNotFires(input, end, checks) {
1008
- for (const check of checks) {
1009
- if (check.kind === "firstSet") {
1010
- const code = end < input.length ? input.codePointAt(end) ?? -1 : -1;
1011
- if (inFirstSet(code, check.set)) return true;
1012
- } else {
1013
- if (input.startsWith(check.value, end)) return true;
1109
+ function firstSetSentinel(fs) {
1110
+ if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
1111
+ const ranges = fs.ranges;
1112
+ return {
1113
+ _tag: "firstSetSentinel",
1114
+ _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },
1115
+ _def: { tag: "unknown" },
1116
+ parse(input, pos) {
1117
+ const code = pos < input.length ? input.codePointAt(pos) : void 0;
1118
+ if (code !== void 0) {
1119
+ for (const r of ranges) {
1120
+ if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
1121
+ }
1122
+ }
1123
+ return { ok: false, expected: [], span: { start: pos, end: pos } };
1014
1124
  }
1015
- }
1016
- return false;
1125
+ };
1017
1126
  }
1018
- function getCoreLiteralValue(p) {
1019
- const def = p._def;
1020
- if (def.tag === "literal" && !def.caseInsensitive) return def.value;
1021
- if (def.tag === "transform") return getCoreLiteralValue(def.parser);
1022
- return null;
1127
+ function recoverScan(input, from, ctx, sync, expected) {
1128
+ let scanPos = from;
1129
+ while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1130
+ const span = { start: from, end: scanPos };
1131
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
1132
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1133
+ ctx._errors?.push(error);
1134
+ return { error, end: scanPos };
1023
1135
  }
1024
- function getCoreRegexDef(p) {
1025
- const def = p._def;
1026
- if (def.tag === "regex") return { source: def.source, flags: def.flags };
1027
- if (def.tag === "transform") return getCoreRegexDef(def.parser);
1028
- if (def.tag === "label") return getCoreRegexDef(def.parser);
1029
- return null;
1136
+ function matchesAt(sentinel, input, pos, ctx) {
1137
+ return probeAt(sentinel, input, pos, ctx);
1030
1138
  }
1031
- function applyTransforms(p, value, span) {
1032
- const def = p._def;
1033
- if (def.tag === "transform") {
1034
- const inner = applyTransforms(def.parser, value, span);
1035
- return def.fn(inner, span);
1036
- }
1037
- return value;
1139
+ function captureError(ctx, error) {
1140
+ if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
1038
1141
  }
1039
- function continuationFirstSet(lit, source, flags) {
1040
- const re = new RegExp(source, "y" + flags.replace(/[gy]/g, ""));
1041
- re.lastIndex = 0;
1042
- const base = re.exec(lit);
1043
- if (!base || base[0] !== lit) return null;
1044
- const contCodes = [];
1045
- for (let code = 1; code < 128; code++) {
1046
- re.lastIndex = 0;
1047
- const m = re.exec(lit + String.fromCharCode(code));
1048
- if (m && m[0].length > lit.length) contCodes.push(code);
1049
- }
1050
- if (contCodes.length === 0) return null;
1051
- return codesToFirstSet(contCodes);
1142
+
1143
+ // src/combinators/expect.ts
1144
+ function deriveExpected(c) {
1145
+ return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
1052
1146
  }
1053
- function codesToFirstSet(codes) {
1054
- codes.sort((a, b) => a - b);
1055
- const ranges = [];
1056
- let lo = codes[0], hi = codes[0];
1057
- for (let i = 1; i < codes.length; i++) {
1058
- if (codes[i] === hi + 1) {
1059
- hi = codes[i];
1060
- } else {
1061
- ranges.push({ lo, hi });
1062
- lo = hi = codes[i];
1147
+ function derive(c, seen) {
1148
+ const deriveExpected2 = (p) => derive(p, seen);
1149
+ const def = c._def;
1150
+ switch (def.tag) {
1151
+ case "literal":
1152
+ return [JSON.stringify(def.value)];
1153
+ case "regex":
1154
+ return [`/${def.source}/`];
1155
+ case "keywords":
1156
+ return def.words.map((w) => JSON.stringify(w));
1157
+ case "label":
1158
+ return [def.label];
1159
+ case "choice":
1160
+ return def.parsers.flatMap(deriveExpected2);
1161
+ case "dispatch":
1162
+ return deriveExpected2(def.selector);
1163
+ // Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
1164
+ // `optional(…)`/`many(…)` can match nothing, so the term after it is equally
1165
+ // able to fail first — deriving from term 0 alone named a token the parse
1166
+ // never actually required. A leading `not(…)` is zero-width and contributes
1167
+ // nothing (its own expectation is about what must NOT be here), but it is
1168
+ // nullable, so keep scanning past it. Stop at the first term that must match.
1169
+ case "sequence": {
1170
+ const out = [];
1171
+ for (const term of def.parsers) {
1172
+ if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
1173
+ if (!matchesEmpty(term)) break;
1174
+ }
1175
+ return out;
1063
1176
  }
1064
- }
1065
- ranges.push({ lo, hi });
1066
- return { kind: "ranges", ranges };
1067
- }
1068
- function inFirstSet(code, fs) {
1069
- if (fs.kind === "any") return true;
1070
- if (fs.kind === "empty") return false;
1071
- for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
1072
- return false;
1073
- }
1074
- function areDisjoint(sets) {
1075
- if (sets.some((s) => s.kind === "any")) return false;
1076
- for (let i = 0; i < sets.length; i++)
1077
- for (let j2 = i + 1; j2 < sets.length; j2++)
1078
- if (intersects(sets[i], sets[j2])) return false;
1079
- return true;
1080
- }
1081
- function buildAsciiDispatch(parsers) {
1082
- const table = Array(128).fill(-1);
1083
- for (let i = 0; i < parsers.length; i++) {
1084
- const fs = parsers[i]._meta.firstSet;
1085
- if (fs.kind !== "ranges") continue;
1086
- for (const { lo, hi } of fs.ranges) {
1087
- for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
1088
- table[code] = i;
1177
+ case "attempt":
1178
+ return deriveExpected2(def.parser);
1179
+ // Delegating wrappers whose first-token failure is their inner parser's — must
1180
+ // pass through so a start-failure (and the first-set-miss fast-path in attempt/
1181
+ // node, which reads this) names the real expected token, not the wrapper tag.
1182
+ case "field":
1183
+ case "withCtx":
1184
+ case "recover":
1185
+ return deriveExpected2(def.parser);
1186
+ case "skip":
1187
+ return deriveExpected2(def.main);
1188
+ case "expect":
1189
+ return def.expected;
1190
+ case "node":
1191
+ case "grammar":
1192
+ case "trivia":
1193
+ case "token":
1194
+ case "leaf":
1195
+ case "optional":
1196
+ case "many":
1197
+ case "oneOrMore":
1198
+ case "transform":
1199
+ case "not":
1200
+ case "peek":
1201
+ return deriveExpected2(def.parser);
1202
+ // A bare routed() can only fail as "there is no routed token here". With a
1203
+ // fallback, THAT is the branch that can fail, so its expectations are the useful
1204
+ // ones — the routed path either succeeds or is not taken.
1205
+ case "routed":
1206
+ return def.fallback === void 0 ? ["routed()"] : deriveExpected2(def.fallback);
1207
+ case "lazy": {
1208
+ const name = c._ruleName;
1209
+ if (seen.has(c)) return name ? [name] : [];
1210
+ seen.add(c);
1211
+ try {
1212
+ return deriveExpected2(def.thunk());
1213
+ } catch {
1214
+ return name ? [name] : [];
1215
+ } finally {
1216
+ seen.delete(c);
1089
1217
  }
1090
1218
  }
1219
+ default:
1220
+ return [];
1091
1221
  }
1092
- return table;
1093
- }
1094
-
1095
- // src/cst/trivia-kinds.ts
1096
- function unwrapTrivia(p) {
1097
- let cur = p;
1098
- while (cur._def.tag === "trivia") cur = cur._def.parser;
1099
- return cur;
1100
1222
  }
1101
- function peelLabel(p) {
1102
- if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
1103
- return null;
1104
- }
1105
- function analyzeLabeledTrivia(trivia2) {
1106
- let core = unwrapTrivia(trivia2);
1107
- let minRepeats = 1;
1108
- if (core._def.tag === "oneOrMore") {
1109
- core = core._def.parser;
1110
- minRepeats = 1;
1111
- } else if (core._def.tag === "many") {
1112
- core = core._def.parser;
1113
- minRepeats = 0;
1114
- }
1115
- const arms = [];
1116
- if (core._def.tag === "choice") {
1117
- for (let i = 0; i < core._def.parsers.length; i++) {
1118
- const peeled = peelLabel(core._def.parsers[i]);
1119
- if (!peeled) return null;
1120
- arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
1223
+ function expect(combinator, label2) {
1224
+ const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
1225
+ const meta = {
1226
+ firstSet: combinator._meta.firstSet,
1227
+ canMatchNewline: combinator._meta.canMatchNewline,
1228
+ isTrivia: false
1229
+ };
1230
+ return {
1231
+ _tag: "expect",
1232
+ _meta: meta,
1233
+ _def: { tag: "expect", parser: combinator, label: label2, expected },
1234
+ parse(input, pos, ctx) {
1235
+ const result = combinator.parse(input, pos, ctx);
1236
+ if (result.ok) return result;
1237
+ const span = { start: pos, end: pos };
1238
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1239
+ ctx._errors?.push(error);
1240
+ if (ctx._tolerant) captureError(ctx, error);
1241
+ return { ok: true, value: error, span: { start: pos, end: pos } };
1121
1242
  }
1122
- } else {
1123
- const peeled = peelLabel(core);
1124
- if (!peeled) return null;
1125
- arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
1126
- }
1127
- return { labels: arms.map((a) => a.label), arms, minRepeats };
1243
+ };
1244
+ }
1245
+
1246
+ // src/regex/classes.ts
1247
+ var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1248
+ var SPACE_RANGES = [
1249
+ [9, 13],
1250
+ [32, 32],
1251
+ [160, 160],
1252
+ [5760, 5760],
1253
+ [8192, 8202],
1254
+ [8232, 8232],
1255
+ [8233, 8233],
1256
+ [8239, 8239],
1257
+ [8287, 8287],
1258
+ [12288, 12288],
1259
+ [65279, 65279]
1260
+ ];
1261
+ function shorthandRanges(ch) {
1262
+ if (ch === "d") return [[48, 57]];
1263
+ if (ch === "s") return SPACE_RANGES;
1264
+ return [[48, 57], [65, 90], [97, 122], [95, 95]];
1128
1265
  }
1129
- function matchArmAt(input, pos, arm) {
1130
- const r = arm.parse(input, pos, { trackLines: false });
1131
- if (!r.ok || r.span.end <= pos) return null;
1132
- return { end: r.span.end };
1266
+ function readUnicodeEscape(body, i) {
1267
+ if (body[i] !== "\\" || body[i + 1] !== "u") return null;
1268
+ const hex = body.slice(i + 2, i + 6);
1269
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
1270
+ return { cp: Number.parseInt(hex, 16), next: i + 6 };
1133
1271
  }
1134
- function scanLabeledTriviaChunks(input, cur, spec) {
1135
- const chunks = [];
1136
- let pos = cur;
1137
- while (pos < input.length) {
1138
- let matched = null;
1139
- for (const arm of spec.arms) {
1140
- const m = matchArmAt(input, pos, arm.parser);
1141
- if (m) {
1142
- matched = { end: m.end, kindIndex: arm.kindIndex };
1143
- break;
1272
+ function parseClassRanges(body) {
1273
+ const ranges = [];
1274
+ let i = 0;
1275
+ const readAtom = () => {
1276
+ const ch = body[i];
1277
+ if (ch === void 0) return null;
1278
+ if (ch === "\\") {
1279
+ const uni = readUnicodeEscape(body, i);
1280
+ if (uni) {
1281
+ i = uni.next;
1282
+ return { cp: uni.cp };
1144
1283
  }
1284
+ const e = body[i + 1];
1285
+ if (e === void 0) return null;
1286
+ i += 2;
1287
+ if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1288
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
1289
+ if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1290
+ return { cp: e.codePointAt(0) };
1145
1291
  }
1146
- if (!matched) break;
1147
- chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
1148
- pos = matched.end;
1149
- }
1150
- if (chunks.length < spec.minRepeats) {
1151
- return { end: cur, chunks: [] };
1152
- }
1153
- return { end: pos, chunks };
1154
- }
1155
- function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
1156
- const chunks = [];
1157
- let pos = cur;
1158
- while (pos < input.length) {
1159
- const c = input.charCodeAt(pos);
1160
- if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {
1161
- const start = pos;
1162
- pos++;
1163
- while (pos < input.length) {
1164
- const c2 = input.charCodeAt(pos);
1165
- if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) pos++;
1166
- else break;
1167
- }
1168
- chunks.push({ start, end: pos, kindIndex: wsKind });
1292
+ i += ch.length;
1293
+ return { cp: ch.codePointAt(0) };
1294
+ };
1295
+ while (i < body.length) {
1296
+ const lo = readAtom();
1297
+ if (lo === null) return null;
1298
+ if ("set" in lo) {
1299
+ ranges.push(...lo.set);
1169
1300
  continue;
1170
1301
  }
1171
- if (c === 47 && input.charCodeAt(pos + 1) === 42) {
1172
- let j2 = pos + 2;
1173
- while (j2 + 1 < input.length && !(input.charCodeAt(j2) === 42 && input.charCodeAt(j2 + 1) === 47)) j2++;
1174
- if (j2 + 1 < input.length && input.charCodeAt(j2) === 42 && input.charCodeAt(j2 + 1) === 47) {
1175
- const start = pos;
1176
- pos = j2 + 2;
1177
- chunks.push({ start, end: pos, kindIndex: commentKind });
1178
- continue;
1179
- }
1180
- break;
1302
+ if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
1303
+ i += 1;
1304
+ const hi = readAtom();
1305
+ if (hi === null || "set" in hi) return null;
1306
+ ranges.push([lo.cp, hi.cp]);
1307
+ } else {
1308
+ ranges.push([lo.cp, lo.cp]);
1181
1309
  }
1182
- break;
1183
1310
  }
1184
- return { end: pos, chunks };
1311
+ return ranges.length ? ranges : null;
1185
1312
  }
1186
- function tryFastLabeledScan(input, cur, trivia2) {
1187
- const spec = analyzeLabeledTrivia(trivia2);
1188
- if (!spec || spec.arms.length !== 2) return null;
1189
- const wsArm = spec.arms.find((a) => {
1190
- const src = getCoreRegexDef(a.parser)?.source;
1191
- return src != null && !src.includes("\\*");
1192
- });
1193
- const commentArm = spec.arms.find((a) => {
1194
- const src = getCoreRegexDef(a.parser)?.source;
1195
- return src != null && src.includes("\\*");
1196
- });
1197
- if (!wsArm || !commentArm) return null;
1198
- const { chunks } = scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1199
- if (chunks.length < spec.minRepeats) return { end: cur, chunks: [] };
1200
- return scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1313
+
1314
+ // src/combinators/trivia-skip.ts
1315
+ var NOOP_COMMIT = () => {
1316
+ };
1317
+ var fastTriviaCache = /* @__PURE__ */ new WeakMap();
1318
+ function needsDeferredTriviaCommit(ctx) {
1319
+ return ctx._triviaLog !== void 0 || ctx._rootTriviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
1201
1320
  }
1202
- function recordTriviaChunks(ctx, chunks) {
1203
- const kinds = ctx.triviaKindLabels;
1204
- const mask = ctx._triviaCaptureMask;
1205
- for (const ch of chunks) {
1206
- pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1207
- if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
1208
- if (mask === void 0 || kinds === void 0 || (mask & 1 << ch.kindIndex) !== 0) {
1209
- pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1210
- }
1211
- }
1212
- }
1321
+ function saveTriviaMark(ctx) {
1322
+ const m = saveCstMark(ctx);
1323
+ return {
1324
+ raw: m.raw,
1325
+ tlog: m.tlog,
1326
+ leaves: m.leaves,
1327
+ fields: m.fields,
1328
+ errors: m.errors,
1329
+ log: ctx._triviaLog ? ctx._triviaLog.length : 0,
1330
+ rootLog: ctx._rootTriviaLog ? ctx._rootTriviaLog.length : 0
1331
+ };
1213
1332
  }
1214
- function triviaKindMask(labels, keep) {
1215
- if (!labels) return void 0;
1216
- let mask = 0;
1217
- for (const name of keep) {
1218
- const idx = labels.indexOf(name);
1219
- if (idx >= 0) mask |= 1 << idx;
1220
- }
1221
- return mask;
1333
+ function rollbackTrivia(ctx, mark) {
1334
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1335
+ if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1336
+ if (ctx._rootTriviaLog && ctx._rootTriviaLog.length !== mark.rootLog) ctx._rootTriviaLog.length = mark.rootLog;
1222
1337
  }
1223
-
1224
- // src/combinators/grammar.ts
1225
- function annotateResultLines(result, index) {
1226
- const annotated = { ...result, span: annotateSpan(result.span, index) };
1227
- const errors = annotated.errors;
1228
- if (errors) {
1229
- ;
1230
- annotated.errors = errors.map((error) => ({
1231
- ...error,
1232
- span: annotateSpan(error.span, index)
1233
- }));
1234
- }
1235
- return annotated;
1338
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1339
+ const probeCtx = {
1340
+ trackLines: ctx.trackLines,
1341
+ state: ctx.state,
1342
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1343
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1344
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1345
+ };
1346
+ const result = triviaP.parse(input, cur, probeCtx);
1347
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1348
+ return result;
1236
1349
  }
1237
- function createParseLineContext(input, pos) {
1238
- const lineIndex = createLineIndex();
1239
- if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
1240
- return { lineIndex, lineScannedTo: pos };
1350
+ function saveLookaheadMark(ctx) {
1351
+ return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1241
1352
  }
1242
- function parser(opts, root) {
1243
- const clearTrivia = opts.trivia === null;
1244
- return {
1245
- _tag: "grammar",
1246
- _meta: root._meta,
1247
- _def: {
1248
- tag: "grammar",
1249
- parser: root,
1250
- triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
1251
- clearTrivia,
1252
- ...opts.captureTrivia ? { captureTrivia: true } : {},
1253
- trackLines: opts.trackLines ?? false
1254
- },
1255
- parse(input, pos, _ctx) {
1256
- const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
1257
- const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
1258
- const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
1259
- const ctx = {
1260
- ..._ctx,
1261
- trackLines,
1262
- ...lineIndex ? { _lineIndex: lineIndex } : {},
1263
- ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
1264
- // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
1265
- ...clearTrivia ? {
1266
- trivia: void 0,
1267
- triviaKindLabels: void 0
1268
- } : opts.trivia != null ? {
1269
- trivia: opts.trivia,
1270
- ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
1271
- } : {},
1272
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
1273
- // Kind-filter for per-node capture. Resolve against this scope's trivia
1274
- // labels — this parser's own trivia if it declares one, else the INHERITED
1275
- // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
1276
- // trivia is inherited rather than re-declared here. No labels → undefined
1277
- // (capture all).
1278
- ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
1279
- };
1280
- const result = root.parse(input, pos ?? 0, ctx);
1281
- if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
1282
- _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
1283
- }
1284
- return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
1353
+ function rollbackLookahead(ctx, mark) {
1354
+ rollbackTrivia(ctx, mark);
1355
+ if (ctx._probe) ctx._probe.best = mark.probeBest;
1356
+ }
1357
+ function scanWithLabels(input, cur, ctx) {
1358
+ const triviaP = ctx.trivia;
1359
+ const spec = analyzeLabeledTrivia(triviaP);
1360
+ if (!spec) return { end: cur, commit: NOOP_COMMIT };
1361
+ const log = ctx._triviaLog;
1362
+ const rootLog = ctx._rootTriviaLog;
1363
+ const rootKinds = ctx._rootTriviaKindIndex;
1364
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1365
+ const mask = ctx._triviaCaptureMask;
1366
+ let fullRows;
1367
+ let rootRows;
1368
+ let cstRows;
1369
+ const visitedEnd = visitLabeledTrivia(input, cur, spec, ctx.state, (start, matchEnd, kindIndex) => {
1370
+ if (log !== void 0) (fullRows ??= []).push(start, matchEnd, kindIndex);
1371
+ const selectedKind = rootLog === void 0 || rootKinds === void 0 || ctx._rootTriviaCapture === false ? void 0 : rootKinds[spec.labels[kindIndex] ?? ""];
1372
+ if (selectedKind !== void 0) (rootRows ??= []).push(start, matchEnd, selectedKind);
1373
+ if (captureTl && (mask === void 0 || (mask & 1 << kindIndex) !== 0)) {
1374
+ (cstRows ??= []).push(start, matchEnd, kindIndex);
1285
1375
  }
1376
+ });
1377
+ if (visitedEnd !== void 0) {
1378
+ if (visitedEnd === cur) return { end: cur, commit: NOOP_COMMIT };
1379
+ return {
1380
+ end: visitedEnd,
1381
+ commit: () => {
1382
+ if (fullRows !== void 0 && log !== void 0) log.push(...fullRows);
1383
+ if (rootRows !== void 0 && rootLog !== void 0) {
1384
+ for (let i = 0; i < rootRows.length; i += 3) {
1385
+ rootLog.push(cur, visitedEnd, rootRows[i], rootRows[i + 1], rootRows[i + 2]);
1386
+ }
1387
+ }
1388
+ if (cstRows !== void 0) {
1389
+ for (let i = 0; i < cstRows.length; i += 3) {
1390
+ pushCstTriviaEntry(ctx, cstRows[i], cstRows[i + 1], cstRows[i + 2]);
1391
+ }
1392
+ }
1393
+ }
1394
+ };
1395
+ }
1396
+ const { end, chunks } = scanLabeledTriviaChunks(input, cur, spec, ctx.state);
1397
+ if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1398
+ return {
1399
+ end,
1400
+ commit: () => recordTriviaChunks(ctx, chunks)
1286
1401
  };
1287
1402
  }
1288
-
1289
- // src/cst/reflection.ts
1290
- var GRAMMAR_REFLECTION = /* @__PURE__ */ Symbol.for("parseman.grammarReflection");
1291
- function childrenOf(def) {
1292
- switch (def.tag) {
1293
- case "sequence":
1294
- case "choice":
1295
- return def.parsers;
1296
- case "dispatch":
1297
- return [
1298
- def.selector,
1299
- ...def.cases.map((c) => c.parser),
1300
- ...def.matchers ? def.matchers.map((c) => c.parser) : [],
1301
- ...def.otherwise ? [def.otherwise] : []
1302
- ];
1303
- case "many":
1304
- case "oneOrMore":
1305
- case "optional":
1306
- case "attempt":
1307
- case "transform":
1308
- case "trivia":
1309
- case "token":
1310
- case "leaf":
1311
- case "label":
1312
- case "field":
1313
- case "not":
1314
- case "peek":
1315
- case "node":
1316
- case "withCtx":
1317
- case "recover":
1318
- case "expect":
1319
- return [def.parser];
1320
- case "grammar":
1321
- return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
1322
- case "sepBy":
1323
- return [def.parser, def.separator];
1324
- case "skip":
1325
- return [def.main, def.skipped];
1326
- case "scanTo":
1327
- return [def.sentinel, ...def.skip];
1328
- case "literal":
1329
- case "regex":
1330
- case "keywords":
1331
- case "routed":
1332
- case "guard":
1333
- case "lazy":
1334
- case "unknown":
1335
- return [];
1403
+ function skipWithLabels(input, cur, ctx) {
1404
+ const spec = analyzeLabeledTrivia(ctx.trivia);
1405
+ if (spec) return scanLabeledTriviaEnd(input, cur, spec, ctx.state);
1406
+ const tr = parseTriviaNoCapture(ctx.trivia, input, cur, ctx);
1407
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1408
+ }
1409
+ function advanceTrivia(input, cur, ctx) {
1410
+ const triviaP = ctx.trivia;
1411
+ if (!triviaP) return cur;
1412
+ if (!ctx.trackLines) {
1413
+ const fast2 = fastTriviaScanner(triviaP);
1414
+ if (fast2) return fast2(input, cur);
1415
+ if (ctx.triviaKindLabels) return skipWithLabels(input, cur, ctx);
1416
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1417
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1336
1418
  }
1337
- }
1338
- function addNode(out, def) {
1339
- if (def.type === void 0) return;
1340
- const existing = out.get(def.type);
1341
- if (existing === void 0) {
1342
- out.set(def.type, def.tags === void 0 ? [] : [...def.tags]);
1343
- return;
1419
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1420
+ const fast = fastTriviaScanner(triviaP);
1421
+ if (fast) {
1422
+ const end = fast(input, cur);
1423
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1424
+ return end;
1344
1425
  }
1345
- if (def.tags === void 0) return;
1346
- for (const tag of def.tags) {
1347
- if (!existing.includes(tag)) existing.push(tag);
1426
+ if (ctx.triviaKindLabels) {
1427
+ const end = skipWithLabels(input, cur, ctx);
1428
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1429
+ return end;
1348
1430
  }
1431
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1432
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1349
1433
  }
1350
- function collectFromCombinator(parser2, out, seen, followLazy = true) {
1351
- if (seen.has(parser2)) return;
1352
- seen.add(parser2);
1353
- const def = parser2._def;
1354
- if (def.tag === "lazy") {
1355
- if (followLazy) {
1356
- try {
1357
- collectFromCombinator(def.thunk(), out, seen, followLazy);
1358
- } catch {
1359
- }
1434
+ function scanTrivia(input, cur, ctx) {
1435
+ const triviaP = ctx.trivia;
1436
+ if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1437
+ const log = ctx._triviaLog;
1438
+ const rootLog = ctx._rootTriviaLog;
1439
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1440
+ if (!ctx.trackLines) {
1441
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1442
+ if (fast2 && log === void 0 && !captureTl) {
1443
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1360
1444
  }
1361
- return;
1445
+ if (ctx.triviaKindLabels && (log !== void 0 || rootLog !== void 0 || captureTl)) {
1446
+ return scanWithLabels(input, cur, ctx);
1447
+ }
1448
+ if (ctx.triviaKindLabels) return { end: skipWithLabels(input, cur, ctx), commit: NOOP_COMMIT };
1449
+ if (log !== void 0 || rootLog !== void 0 || captureTl) {
1450
+ const tr3 = triviaP.parse(input, cur, {
1451
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1452
+ state: ctx.state
1453
+ });
1454
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1455
+ const end = tr3.span.end;
1456
+ return {
1457
+ end,
1458
+ commit: () => {
1459
+ pushTriviaLogEntry(ctx, cur, end);
1460
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1461
+ }
1462
+ };
1463
+ }
1464
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1465
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1362
1466
  }
1363
- if (def.tag === "node") addNode(out, def);
1364
- for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy);
1467
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1468
+ const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1469
+ if (fast && log === void 0 && !captureTl) {
1470
+ const end = fast(input, cur);
1471
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1472
+ return { end, commit: NOOP_COMMIT };
1473
+ }
1474
+ if (ctx.triviaKindLabels && (log !== void 0 || rootLog !== void 0 || captureTl)) {
1475
+ const scan = scanWithLabels(input, cur, ctx);
1476
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1477
+ return scan;
1478
+ }
1479
+ if (ctx.triviaKindLabels) {
1480
+ const end = skipWithLabels(input, cur, ctx);
1481
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1482
+ return { end, commit: NOOP_COMMIT };
1483
+ }
1484
+ if (log !== void 0 || rootLog !== void 0 || captureTl) {
1485
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1486
+ if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1487
+ const end = tr2.span.end;
1488
+ return {
1489
+ end,
1490
+ commit: () => {
1491
+ pushTriviaLogEntry(ctx, cur, end);
1492
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1493
+ }
1494
+ };
1495
+ }
1496
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1497
+ return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1365
1498
  }
1366
- function collectGrammarReflection(ruleMap, opts) {
1367
- const byType = /* @__PURE__ */ new Map();
1368
- const followLazy = opts?.followLazy !== false;
1369
- for (const [, rule] of ruleMap) {
1370
- if (!followLazy && rule._def.tag === "lazy") {
1371
- try {
1372
- const resolved = rule._def.thunk();
1373
- if (resolved._def.tag !== "lazy") collectFromCombinator(resolved, byType, /* @__PURE__ */ new Set(), false);
1374
- } catch {
1499
+ function consumeTrivia(input, cur, ctx) {
1500
+ if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1501
+ const scan = scanTrivia(input, cur, ctx);
1502
+ scan.commit();
1503
+ return scan.end;
1504
+ }
1505
+ function fastTriviaScanner(trivia2) {
1506
+ const cached = fastTriviaCache.get(trivia2);
1507
+ if (cached !== void 0) return cached;
1508
+ const scanner = buildFastTriviaScanner(trivia2);
1509
+ fastTriviaCache.set(trivia2, scanner);
1510
+ return scanner;
1511
+ }
1512
+ function buildFastTriviaScanner(trivia2) {
1513
+ const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1514
+ const direct = regexTriviaScanner(core);
1515
+ if (direct) return direct;
1516
+ const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1517
+ if (!repeat) return null;
1518
+ const one = regexTriviaScanner(repeat);
1519
+ if (one) return loopScanner([one]);
1520
+ if (repeat._def.tag !== "choice") return null;
1521
+ const arms = repeat._def.parsers.map(regexTriviaScanner);
1522
+ if (arms.some((s) => s === null)) return null;
1523
+ return loopScanner(arms);
1524
+ }
1525
+ function loopScanner(arms) {
1526
+ return (input, cur) => {
1527
+ let pos = cur;
1528
+ scan: while (pos < input.length) {
1529
+ for (const arm of arms) {
1530
+ const end = arm(input, pos);
1531
+ if (end > pos) {
1532
+ pos = end;
1533
+ continue scan;
1534
+ }
1375
1535
  }
1376
- continue;
1536
+ break;
1377
1537
  }
1378
- collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
1538
+ return pos;
1539
+ };
1540
+ }
1541
+ function regexTriviaScanner(parser2) {
1542
+ if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1543
+ const source = parser2._def.source;
1544
+ return classRunSource(source) ?? altStarSource(source);
1545
+ }
1546
+ function inRanges(cp, ranges) {
1547
+ for (let i = 0; i < ranges.length; i++) {
1548
+ const r = ranges[i];
1549
+ if (cp >= r[0] && cp <= r[1]) return true;
1379
1550
  }
1380
- return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
1551
+ return false;
1381
1552
  }
1382
- function attachGrammarReflection(grammar, reflection) {
1383
- Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true });
1384
- return grammar;
1553
+ function classScanner(classBody) {
1554
+ const ranges = parseClassRanges(classBody);
1555
+ if (!ranges) return null;
1556
+ return (input, cur) => {
1557
+ let pos = cur;
1558
+ while (pos < input.length && inRanges(input.charCodeAt(pos), ranges)) pos++;
1559
+ return pos;
1560
+ };
1385
1561
  }
1386
-
1387
- // src/combinators/parser.ts
1388
- var RULE_ORDER = "__parsemanRuleOrder";
1389
- function tagRule(r, key) {
1390
- ;
1391
- r._ruleName = key;
1392
- if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
1562
+ function untilLineBreakScanner(leaderCode) {
1563
+ return (input, cur) => {
1564
+ if (input.charCodeAt(cur) !== leaderCode) return cur;
1565
+ let pos = cur + 1;
1566
+ while (pos < input.length) {
1567
+ const cc = input.charCodeAt(pos);
1568
+ if (cc === 10 || cc === 13) break;
1569
+ pos++;
1570
+ }
1571
+ return pos;
1572
+ };
1393
1573
  }
1394
- function isDefinableRef(v) {
1395
- return !!v && typeof v === "object" && "_def" in v && v._def.tag === "lazy" && typeof v.define === "function";
1574
+ function classRunSource(source) {
1575
+ const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1576
+ return m ? classScanner(m[1]) : null;
1396
1577
  }
1397
- function ruleNameOf(r) {
1398
- return r._ruleName;
1578
+ function classifyTriviaArm(arm) {
1579
+ const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1580
+ if (cls) {
1581
+ const ranges = parseClassRanges(cls[1]);
1582
+ return ranges ? { kind: "class", ranges } : null;
1583
+ }
1584
+ const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1585
+ if (lc) {
1586
+ const marker = lc[1];
1587
+ return { kind: "untilLineBreak", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
1588
+ }
1589
+ return null;
1399
1590
  }
1400
- function isNamedRuleRefForAnotherRule(r, key) {
1401
- const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
1402
- return name !== void 0 && name !== key;
1591
+ function armScanner(arm) {
1592
+ if (arm.kind === "untilLineBreak") return untilLineBreakScanner(arm.code);
1593
+ const ranges = arm.ranges;
1594
+ return (input, cur) => {
1595
+ let pos = cur;
1596
+ while (pos < input.length && inRanges(input.charCodeAt(pos), ranges)) pos++;
1597
+ return pos;
1598
+ };
1403
1599
  }
1404
- function rules(a, b) {
1405
- const factory = typeof a === "function" ? a : b;
1406
- const options = typeof a === "function" ? b : a;
1407
- const cache = {};
1408
- const proxy = new Proxy(cache, {
1409
- get(target, key) {
1410
- if (typeof key !== "string") return void 0;
1411
- const record = target;
1412
- if (!(key in record)) {
1413
- const r = ref();
1414
- tagRule(r, key);
1415
- record[key] = r;
1600
+ function fusedTriviaScanner(ranges, leaders) {
1601
+ const c0 = leaders[0];
1602
+ const single = leaders.length === 1;
1603
+ return (input, cur) => {
1604
+ let pos = cur;
1605
+ const len = input.length;
1606
+ for (; ; ) {
1607
+ const c = input.charCodeAt(pos);
1608
+ if (pos < len && inRanges(c, ranges)) {
1609
+ pos++;
1610
+ continue;
1416
1611
  }
1417
- return record[key];
1418
- }
1419
- });
1420
- const definitions = factory(proxy);
1421
- for (const key of Object.keys(definitions)) {
1422
- const placeholder = cache[key];
1423
- const parser2 = definitions[key];
1424
- if (placeholder === parser2) {
1425
- throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
1426
- }
1427
- if (isDefinableRef(placeholder)) {
1428
- if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
1429
- placeholder.define(parser2);
1430
- placeholder._meta.firstSet = parser2._meta.firstSet;
1431
- placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
1432
- } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
1433
- const alias = ref();
1434
- tagRule(alias, key);
1435
- alias.define(parser2);
1436
- alias._meta.firstSet = parser2._meta.firstSet;
1437
- alias._meta.canMatchNewline = parser2._meta.canMatchNewline;
1438
- cache[key] = alias;
1439
- } else {
1440
- tagRule(parser2, key);
1441
- cache[key] = parser2;
1612
+ if (single ? c === c0 : leaders.includes(c)) {
1613
+ pos++;
1614
+ while (pos < len) {
1615
+ const cc = input.charCodeAt(pos);
1616
+ if (cc === 10 || cc === 13) break;
1617
+ pos++;
1618
+ }
1619
+ continue;
1620
+ }
1621
+ break;
1442
1622
  }
1443
- }
1444
- if (options?.trivia != null) {
1445
- for (const key of Object.keys(definitions)) {
1446
- const rule = cache[key];
1447
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
1623
+ return pos;
1624
+ };
1625
+ }
1626
+ function splitTopLevelAlts(body) {
1627
+ const arms = [];
1628
+ let inClass = false;
1629
+ let start = 0;
1630
+ for (let i = 0; i < body.length; i++) {
1631
+ const c = body[i];
1632
+ if (c === "\\") {
1633
+ i++;
1634
+ continue;
1448
1635
  }
1449
- }
1450
- if (options?.scanSkip != null) {
1451
- for (const key of Object.keys(definitions)) {
1452
- const rule = cache[key];
1453
- if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
1636
+ if (inClass) {
1637
+ if (c === "]") inClass = false;
1638
+ continue;
1454
1639
  }
1455
- }
1456
- if (options?.hostMode === "cst") {
1457
- for (const key of Object.keys(definitions)) {
1458
- const rule = cache[key];
1459
- if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
1640
+ if (c === "[") inClass = true;
1641
+ else if (c === "(") return null;
1642
+ else if (c === "|") {
1643
+ arms.push(body.slice(start, i));
1644
+ start = i + 1;
1460
1645
  }
1461
1646
  }
1462
- if (options?.trackLines === true) {
1463
- for (const key of Object.keys(definitions)) {
1464
- const rule = cache[key];
1465
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
1466
- }
1467
- for (const key of Object.keys(definitions)) {
1468
- const rule = cache[key];
1469
- if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
1470
- const wrapped = parser({ trackLines: true }, rule);
1471
- tagRule(wrapped, key);
1472
- wrapped._meta.grammarTrackLines = true;
1473
- cache[key] = wrapped;
1474
- }
1475
- }
1647
+ arms.push(body.slice(start));
1648
+ return arms;
1649
+ }
1650
+ function altStarSource(source) {
1651
+ const m = /^\(\?:(.*)\)[*+]$/.exec(source);
1652
+ if (!m) return null;
1653
+ const armSrcs = splitTopLevelAlts(m[1]);
1654
+ if (!armSrcs || armSrcs.length < 2) return null;
1655
+ const arms = [];
1656
+ for (const src of armSrcs) {
1657
+ const arm = classifyTriviaArm(src);
1658
+ if (!arm) return null;
1659
+ arms.push(arm);
1476
1660
  }
1477
- for (const key of Object.keys(definitions)) {
1478
- markUnusedValues(cache[key]);
1661
+ const ranges = [];
1662
+ const leaders = [];
1663
+ for (const arm of arms) {
1664
+ if (arm.kind === "class") ranges.push(...arm.ranges);
1665
+ else leaders.push(arm.code);
1479
1666
  }
1480
- Object.defineProperty(cache, RULE_ORDER, {
1481
- value: Object.keys(definitions),
1482
- enumerable: false,
1483
- configurable: true
1484
- });
1485
- attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
1486
- return cache;
1487
- }
1488
-
1489
- // src/analysis/duplication.ts
1490
- var WORD_RE_SRC = "-?[A-Za-z][A-Za-z0-9_-]*";
1491
- var WORDS_RE = new RegExp(`^${WORD_RE_SRC}(?:\\|${WORD_RE_SRC})*$`);
1492
-
1493
- // src/combinators/probe.ts
1494
- function failAt(ctx, expected, pos) {
1495
- const r = { ok: false, expected, span: { start: pos, end: pos } };
1496
- const probe = ctx._probe;
1497
- if (probe !== void 0 && pos <= probe.offset) {
1498
- const best = probe.best;
1499
- if (best === null || pos > best.span.start) {
1500
- probe.best = r;
1501
- } else if (pos === best.span.start) {
1502
- probe.best = { ...best, expected: [...best.expected, ...expected] };
1503
- }
1667
+ if (leaders.some((code) => inRanges(code, ranges))) {
1668
+ return loopScanner(arms.map(armScanner));
1504
1669
  }
1505
- return r;
1670
+ if (leaders.length === 0) return armScanner({ kind: "class", ranges });
1671
+ return fusedTriviaScanner(ranges, leaders);
1506
1672
  }
1507
1673
 
1508
- // src/combinators/literal.ts
1509
- function asciiFoldEq(a, b) {
1510
- if (a.length !== b.length) return false;
1511
- for (let i = 0; i < a.length; i++) {
1512
- let ca = a.charCodeAt(i);
1513
- let cb = b.charCodeAt(i);
1514
- if (ca >= 65 && ca <= 90) ca += 32;
1515
- if (cb >= 65 && cb <= 90) cb += 32;
1516
- if (ca !== cb) return false;
1517
- }
1518
- return true;
1519
- }
1520
- function literal(value, opts = {}) {
1521
- const caseInsensitive = opts.caseInsensitive ?? false;
1522
- const firstSet = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
1674
+ // src/combinators/choice.ts
1675
+ function choice(...args) {
1676
+ const parsers = args.map((a) => "gate" in a ? a.combinator : a);
1677
+ const gates = args.map((a) => "gate" in a ? a.gate : null);
1678
+ const hasGates = gates.some((g) => g !== null);
1679
+ const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
1680
+ let combined = { kind: "empty" };
1681
+ for (const p of parsers) combined = union(combined, p._meta.firstSet);
1523
1682
  const meta = {
1524
- firstSet,
1525
- canMatchNewline: value.includes("\n"),
1526
- isTrivia: false
1683
+ firstSet: combined,
1684
+ canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
1685
+ isTrivia: false,
1686
+ disjoint
1527
1687
  };
1528
- const canMatchNewline = meta.canMatchNewline;
1529
- if (caseInsensitive) {
1530
- const upper = value.toUpperCase();
1531
- const lower = value.toLowerCase();
1532
- const firstUpper = upper.codePointAt(0);
1533
- const firstLower = lower.codePointAt(0);
1534
- meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
1535
- { lo: firstLower, hi: firstLower },
1536
- { lo: firstUpper, hi: firstUpper }
1537
- ] } : firstSet;
1538
- }
1539
- const expected = [JSON.stringify(value)];
1540
- const parse = !caseInsensitive && value.length === 1 ? (() => {
1541
- const code = value.charCodeAt(0);
1542
- return function parse2(input, pos, ctx) {
1543
- if (input.charCodeAt(pos) === code) {
1544
- const span = { start: pos, end: pos + 1 };
1545
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1546
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
1547
- return { ok: true, value, span };
1548
- }
1549
- return failAt(ctx, expected, pos);
1550
- };
1551
- })() : function parse2(input, pos, ctx) {
1552
- const end = pos + value.length;
1553
- if (end > input.length) {
1554
- return failAt(ctx, expected, pos);
1555
- }
1556
- const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
1557
- if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
1558
- const span = { start: pos, end };
1559
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
1560
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
1561
- return { ok: true, value: matchedValue, span };
1688
+ const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
1689
+ const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
1690
+ let greedyLitMap = null;
1691
+ let sortedParsers = null;
1692
+ const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
1693
+ if (strategy?.tag === "greedyClassify") {
1694
+ greedyLitMap = /* @__PURE__ */ new Map();
1695
+ for (let i = 0; i < parsers.length; i++) {
1696
+ if (i === strategy.superIndex) continue;
1697
+ const litVal = getCoreLiteralValue(parsers[i]);
1698
+ if (litVal !== null) greedyLitMap.set(litVal, i);
1562
1699
  }
1563
- return failAt(ctx, expected, pos);
1564
- };
1700
+ } else if (strategy?.tag === "literalsLongestFirst") {
1701
+ sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
1702
+ }
1565
1703
  return {
1566
- _tag: "literal",
1704
+ _tag: "choice",
1567
1705
  _meta: meta,
1568
- _def: { tag: "literal", value, caseInsensitive },
1569
- parse
1570
- };
1571
- }
1572
-
1573
- // src/regex/classes.ts
1574
- var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1575
- var SPACE_RANGES = [
1576
- [9, 13],
1577
- [32, 32],
1578
- [160, 160],
1579
- [5760, 5760],
1580
- [8192, 8202],
1581
- [8232, 8232],
1582
- [8233, 8233],
1583
- [8239, 8239],
1584
- [8287, 8287],
1585
- [12288, 12288],
1586
- [65279, 65279]
1587
- ];
1588
- function shorthandRanges(ch) {
1589
- if (ch === "d") return [[48, 57]];
1590
- if (ch === "s") return SPACE_RANGES;
1591
- return [[48, 57], [65, 90], [97, 122], [95, 95]];
1592
- }
1593
- function readUnicodeEscape(body, i) {
1594
- if (body[i] !== "\\" || body[i + 1] !== "u") return null;
1595
- const hex = body.slice(i + 2, i + 6);
1596
- if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
1597
- return { cp: Number.parseInt(hex, 16), next: i + 6 };
1598
- }
1599
- function parseClassRanges(body) {
1600
- const ranges = [];
1601
- let i = 0;
1602
- const readAtom = () => {
1603
- const ch = body[i];
1604
- if (ch === void 0) return null;
1605
- if (ch === "\\") {
1606
- const uni = readUnicodeEscape(body, i);
1607
- if (uni) {
1608
- i = uni.next;
1609
- return { cp: uni.cp };
1706
+ _def: {
1707
+ tag: "choice",
1708
+ parsers,
1709
+ gates,
1710
+ disjoint,
1711
+ strategy: strategy ?? { tag: "firstMatch" },
1712
+ autoNot
1713
+ },
1714
+ parse(input, pos, ctx) {
1715
+ const expected = [];
1716
+ if (disjoint && pos < input.length) {
1717
+ const code = input.codePointAt(pos);
1718
+ let idx = code < 128 ? asciiDispatch[code] : -1;
1719
+ if (idx < 0) {
1720
+ for (let i = 0; i < parsers.length; i++) {
1721
+ if (inFirstSet(code, parsers[i]._meta.firstSet)) {
1722
+ idx = i;
1723
+ break;
1724
+ }
1725
+ }
1726
+ }
1727
+ if (idx >= 0) {
1728
+ const gate = gates[idx];
1729
+ if (gate && !gate(ctx.state)) {
1730
+ return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
1731
+ }
1732
+ const result = parsers[idx].parse(input, pos, ctx);
1733
+ if (result.ok) return result;
1734
+ expected.push(...result.expected);
1735
+ if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
1736
+ return { ok: false, expected, span: { start: pos, end: pos } };
1737
+ }
1738
+ return {
1739
+ ok: false,
1740
+ expected: parsers.flatMap((p) => {
1741
+ const r = p.parse(input, pos, ctx);
1742
+ return r.ok ? [] : r.expected;
1743
+ }),
1744
+ span: { start: pos, end: pos }
1745
+ };
1610
1746
  }
1611
- const e = body[i + 1];
1612
- if (e === void 0) return null;
1613
- i += 2;
1614
- if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1615
- if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
1616
- if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1617
- return { cp: e.codePointAt(0) };
1747
+ if (strategy?.tag === "greedyClassify") {
1748
+ const superResult = parsers[strategy.superIndex].parse(input, pos, ctx);
1749
+ if (!superResult.ok) return superResult;
1750
+ const end = superResult.span.end;
1751
+ const litIdx = greedyLitMap.get(input.slice(pos, end));
1752
+ if (litIdx !== void 0) {
1753
+ const litVal = getCoreLiteralValue(parsers[litIdx]);
1754
+ const value = applyTransforms(parsers[litIdx], litVal, { start: pos, end });
1755
+ return { ok: true, value, span: { start: pos, end } };
1756
+ }
1757
+ return superResult;
1758
+ }
1759
+ if (strategy?.tag === "literalsLongestFirst") {
1760
+ for (const p of sortedParsers) {
1761
+ const r = p.parse(input, pos, ctx);
1762
+ if (r.ok) return r;
1763
+ expected.push(...r.expected);
1764
+ }
1765
+ return { ok: false, expected, span: { start: pos, end: pos } };
1766
+ }
1767
+ for (let i = 0; i < parsers.length; i++) {
1768
+ if (gates[i] && !gates[i](ctx.state)) continue;
1769
+ const mark = saveTriviaMark(ctx);
1770
+ const result = parsers[i].parse(input, pos, ctx);
1771
+ if (!result.ok) {
1772
+ rollbackTrivia(ctx, mark);
1773
+ expected.push(...result.expected);
1774
+ if (result.committed) return { ok: false, expected, span: result.span, committed: true };
1775
+ continue;
1776
+ }
1777
+ const checks = autoNot[i];
1778
+ if (checks && autoNotFires(input, result.span.end, checks)) {
1779
+ rollbackTrivia(ctx, mark);
1780
+ continue;
1781
+ }
1782
+ return result;
1783
+ }
1784
+ return { ok: false, expected, span: { start: pos, end: pos } };
1618
1785
  }
1619
- i += ch.length;
1620
- return { cp: ch.codePointAt(0) };
1621
1786
  };
1622
- while (i < body.length) {
1623
- const lo = readAtom();
1624
- if (lo === null) return null;
1625
- if ("set" in lo) {
1626
- ranges.push(...lo.set);
1627
- continue;
1628
- }
1629
- if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
1630
- i += 1;
1631
- const hi = readAtom();
1632
- if (hi === null || "set" in hi) return null;
1633
- ranges.push([lo.cp, hi.cp]);
1634
- } else {
1635
- ranges.push([lo.cp, lo.cp]);
1636
- }
1637
- }
1638
- return ranges.length ? ranges : null;
1639
1787
  }
1640
-
1641
- // src/combinators/regex.ts
1642
- var firstSetAnalyzer = null;
1643
- var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
1644
- var SCAN_BAIL_AT = 64;
1645
- function inRanges(cp, ranges) {
1646
- for (let i = 0; i < ranges.length; i++) {
1647
- const [lo, hi] = ranges[i];
1648
- if (cp >= lo && cp <= hi) return true;
1788
+ function detectStrategy(parsers) {
1789
+ const regexIndices = [];
1790
+ const literalIndices = [];
1791
+ for (let i = 0; i < parsers.length; i++) {
1792
+ if (getCoreRegexDef(parsers[i]) !== null) regexIndices.push(i);
1793
+ else if (getCoreLiteralValue(parsers[i]) !== null) literalIndices.push(i);
1649
1794
  }
1650
- return false;
1795
+ if (regexIndices.length === 1 && literalIndices.length === parsers.length - 1 && literalIndices.length > 0) {
1796
+ const superIndex = regexIndices[0];
1797
+ const regexDef = getCoreRegexDef(parsers[superIndex]);
1798
+ const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
1799
+ const re = new RegExp(regexDef.source, flags);
1800
+ const allSubsumed = literalIndices.every((i) => {
1801
+ const litVal = getCoreLiteralValue(parsers[i]);
1802
+ re.lastIndex = 0;
1803
+ const m = re.exec(litVal);
1804
+ return m !== null && m[0] === litVal;
1805
+ });
1806
+ if (allSubsumed) return { tag: "greedyClassify", superIndex };
1807
+ }
1808
+ if (parsers.length === literalIndices.length) {
1809
+ const sortedIndices = [...literalIndices].sort(
1810
+ (a, b) => getCoreLiteralValue(parsers[b]).length - getCoreLiteralValue(parsers[a]).length
1811
+ );
1812
+ return { tag: "literalsLongestFirst", sortedIndices };
1813
+ }
1814
+ const shared = detectSharedPrefix(parsers);
1815
+ if (shared !== null) return shared;
1816
+ return { tag: "firstMatch" };
1651
1817
  }
1652
- function readClassRanges(body) {
1653
- if (body.startsWith("^")) return null;
1654
- return parseClassRanges(body);
1818
+ function bareLeadingTermKey(term) {
1819
+ const d = term._def;
1820
+ if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
1821
+ if (d.tag === "regex") return `R:${JSON.stringify([d.source, d.flags])}`;
1822
+ return null;
1655
1823
  }
1656
- function shortScanner(source, flags) {
1657
- if (/[imsuvy]/.test(flags)) return null;
1658
- let ranges = null;
1659
- let quant = "";
1660
- if (source[0] === "[") {
1661
- let end = 1;
1662
- while (end < source.length && source[end] !== "]") {
1663
- if (source[end] === "\\") end += 2;
1664
- else end++;
1665
- }
1666
- if (source[end] !== "]") return null;
1667
- ranges = readClassRanges(source.slice(1, end));
1668
- quant = source.slice(end + 1);
1669
- } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
1670
- ranges = shorthandRanges(source[1]);
1671
- quant = source.slice(2);
1824
+ function detectSharedPrefix(parsers) {
1825
+ if (parsers.length < 2) return null;
1826
+ let key = null;
1827
+ let prefix = null;
1828
+ const members = [];
1829
+ for (let i = 0; i < parsers.length; i++) {
1830
+ const term0 = leadingTermOfArm(parsers[i]);
1831
+ if (term0 === null) return null;
1832
+ const k = bareLeadingTermKey(term0);
1833
+ if (k === null) return null;
1834
+ if (key === null) {
1835
+ key = k;
1836
+ prefix = term0;
1837
+ } else if (k !== key) return null;
1838
+ members.push(i);
1672
1839
  }
1673
- if (!ranges || quant !== "+" && quant !== "*") return null;
1674
- const minOne = quant === "+";
1675
- return (input, pos) => {
1676
- let end = pos;
1677
- while (end < input.length && inRanges(input.charCodeAt(end), ranges)) {
1678
- end++;
1679
- if (end - pos >= SCAN_BAIL_AT) return void 0;
1680
- }
1681
- return minOne && end === pos ? null : end;
1682
- };
1840
+ if (prefix === null || members.length < 2) return null;
1841
+ return { tag: "sharedPrefix", prefix, members };
1683
1842
  }
1684
- function asciiCaseFold(fs) {
1685
- if (fs.kind !== "ranges") return fs;
1686
- const ranges = fs.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
1687
- const add = (lo, hi) => {
1688
- if (lo <= hi) ranges.push({ lo, hi });
1689
- };
1690
- for (const r of fs.ranges) {
1691
- const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
1692
- add(uLo + 32, uHi + 32);
1693
- const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
1694
- add(lLo - 32, lHi - 32);
1695
- }
1696
- ranges.sort((a, b) => a.lo - b.lo);
1697
- const merged = [];
1698
- for (const r of ranges) {
1699
- const last = merged[merged.length - 1];
1700
- if (last && r.lo <= last.hi + 1) last.hi = Math.max(last.hi, r.hi);
1701
- else merged.push({ lo: r.lo, hi: r.hi });
1843
+ function leadingTermOfArm(arm) {
1844
+ let d = arm._def;
1845
+ for (; ; ) {
1846
+ if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
1847
+ d = d.parser._def;
1848
+ continue;
1849
+ }
1850
+ break;
1702
1851
  }
1703
- return { kind: "ranges", ranges: merged };
1852
+ if (d.tag !== "sequence" || d.parsers.length < 2) return null;
1853
+ return d.parsers[0];
1704
1854
  }
1705
- function regex(pattern, flags = "") {
1706
- const source = typeof pattern === "string" ? pattern : pattern.source;
1707
- const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
1708
- const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
1709
- const scan = shortScanner(source, resolvedFlags);
1710
- const raw = (firstSetAnalyzer ?? permissiveFirstSet)(source);
1711
- const firstSet = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
1712
- const canMatchNewline = raw.canMatchNewline;
1713
- const meta = { firstSet, canMatchNewline, isTrivia: false };
1714
- return {
1715
- _tag: "regex",
1716
- _meta: meta,
1717
- _def: { tag: "regex", source, flags: resolvedFlags },
1718
- parse(input, pos, ctx) {
1719
- const scanEnd = scan?.(input, pos);
1720
- if (scanEnd !== void 0) {
1721
- if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
1722
- const value = input.slice(pos, scanEnd);
1723
- const span2 = { start: pos, end: scanEnd };
1724
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
1725
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
1726
- return { ok: true, value, span: span2 };
1855
+ function computeAutoNot(parsers) {
1856
+ return parsers.map((p, i) => {
1857
+ const litVal = getCoreLiteralValue(p);
1858
+ if (litVal === null) return null;
1859
+ const checks = [];
1860
+ for (let j2 = i + 1; j2 < parsers.length; j2++) {
1861
+ const other = parsers[j2];
1862
+ const otherLit = getCoreLiteralValue(other);
1863
+ if (otherLit !== null && otherLit.startsWith(litVal) && otherLit.length > litVal.length) {
1864
+ checks.push({ kind: "startsWith", value: otherLit.slice(litVal.length) });
1865
+ continue;
1727
1866
  }
1728
- anchored.lastIndex = pos;
1729
- const m = anchored.exec(input);
1730
- if (m === null) {
1731
- return failAt(ctx, [`/${source}/`], pos);
1867
+ const regexDef = getCoreRegexDef(other);
1868
+ if (regexDef !== null) {
1869
+ const contSet = continuationFirstSet(litVal, regexDef.source, regexDef.flags);
1870
+ if (contSet !== null) checks.push({ kind: "firstSet", set: contSet });
1732
1871
  }
1733
- const span = { start: pos, end: pos + m[0].length };
1734
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1735
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
1736
- return { ok: true, value: m[0], span };
1737
1872
  }
1738
- };
1873
+ return checks.length > 0 ? checks : null;
1874
+ });
1739
1875
  }
1740
-
1741
- // src/combinators/trivia-skip.ts
1742
- var NOOP_COMMIT = () => {
1743
- };
1744
- var fastTriviaCache = /* @__PURE__ */ new WeakMap();
1745
- function needsDeferredTriviaCommit(ctx) {
1746
- return ctx._triviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
1876
+ function autoNotFires(input, end, checks) {
1877
+ for (const check of checks) {
1878
+ if (check.kind === "firstSet") {
1879
+ const code = end < input.length ? input.codePointAt(end) ?? -1 : -1;
1880
+ if (inFirstSet(code, check.set)) return true;
1881
+ } else {
1882
+ if (input.startsWith(check.value, end)) return true;
1883
+ }
1884
+ }
1885
+ return false;
1747
1886
  }
1748
- function saveTriviaMark(ctx) {
1749
- const m = saveCstMark(ctx);
1750
- return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, errors: m.errors, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1887
+ function getCoreLiteralValue(p) {
1888
+ const def = p._def;
1889
+ if (def.tag === "literal" && !def.caseInsensitive) return def.value;
1890
+ if (def.tag === "transform") return getCoreLiteralValue(def.parser);
1891
+ return null;
1892
+ }
1893
+ function getCoreRegexDef(p) {
1894
+ const def = p._def;
1895
+ if (def.tag === "regex") return { source: def.source, flags: def.flags };
1896
+ if (def.tag === "transform") return getCoreRegexDef(def.parser);
1897
+ if (def.tag === "label") return getCoreRegexDef(def.parser);
1898
+ return null;
1751
1899
  }
1752
- function rollbackTrivia(ctx, mark) {
1753
- rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1754
- if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1900
+ function applyTransforms(p, value, span) {
1901
+ const def = p._def;
1902
+ if (def.tag === "transform") {
1903
+ const inner = applyTransforms(def.parser, value, span);
1904
+ return def.fn(inner, span);
1905
+ }
1906
+ return value;
1755
1907
  }
1756
- function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1757
- const probeCtx = {
1758
- trackLines: ctx.trackLines,
1759
- state: ctx.state,
1760
- ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1761
- ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1762
- ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1763
- };
1764
- const result = triviaP.parse(input, cur, probeCtx);
1765
- ctx._lineScannedTo = probeCtx._lineScannedTo;
1766
- return result;
1908
+ function continuationFirstSet(lit, source, flags) {
1909
+ const re = new RegExp(source, "y" + flags.replace(/[gy]/g, ""));
1910
+ re.lastIndex = 0;
1911
+ const base = re.exec(lit);
1912
+ if (!base || base[0] !== lit) return null;
1913
+ const contCodes = [];
1914
+ for (let code = 1; code < 128; code++) {
1915
+ re.lastIndex = 0;
1916
+ const m = re.exec(lit + String.fromCharCode(code));
1917
+ if (m && m[0].length > lit.length) contCodes.push(code);
1918
+ }
1919
+ if (contCodes.length === 0) return null;
1920
+ return codesToFirstSet(contCodes);
1767
1921
  }
1768
- function saveLookaheadMark(ctx) {
1769
- return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1922
+ function codesToFirstSet(codes) {
1923
+ codes.sort((a, b) => a - b);
1924
+ const ranges = [];
1925
+ let lo = codes[0], hi = codes[0];
1926
+ for (let i = 1; i < codes.length; i++) {
1927
+ if (codes[i] === hi + 1) {
1928
+ hi = codes[i];
1929
+ } else {
1930
+ ranges.push({ lo, hi });
1931
+ lo = hi = codes[i];
1932
+ }
1933
+ }
1934
+ ranges.push({ lo, hi });
1935
+ return { kind: "ranges", ranges };
1770
1936
  }
1771
- function rollbackLookahead(ctx, mark) {
1772
- rollbackTrivia(ctx, mark);
1773
- if (ctx._probe) ctx._probe.best = mark.probeBest;
1937
+ function inFirstSet(code, fs) {
1938
+ if (fs.kind === "any") return true;
1939
+ if (fs.kind === "empty") return false;
1940
+ for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
1941
+ return false;
1774
1942
  }
1775
- function scanWithLabels(input, cur, ctx) {
1776
- const triviaP = ctx.trivia;
1777
- const spec = analyzeLabeledTrivia(triviaP);
1778
- if (!spec) return { end: cur, commit: NOOP_COMMIT };
1779
- const fast = tryFastLabeledScan(input, cur, triviaP);
1780
- const { end, chunks } = fast ?? scanLabeledTriviaChunks(input, cur, spec);
1781
- if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1782
- return {
1783
- end,
1784
- commit: () => recordTriviaChunks(ctx, chunks)
1785
- };
1943
+ function areDisjoint(sets) {
1944
+ if (sets.some((s) => s.kind === "any")) return false;
1945
+ for (let i = 0; i < sets.length; i++)
1946
+ for (let j2 = i + 1; j2 < sets.length; j2++)
1947
+ if (intersects(sets[i], sets[j2])) return false;
1948
+ return true;
1786
1949
  }
1787
- function advanceTrivia(input, cur, ctx) {
1788
- const triviaP = ctx.trivia;
1789
- if (!triviaP) return cur;
1790
- if (!ctx.trackLines) {
1791
- const fast2 = fastTriviaScanner(triviaP);
1792
- if (fast2) return fast2(input, cur);
1793
- if (ctx.triviaKindLabels) {
1794
- const scan = scanWithLabels(input, cur, ctx);
1795
- return scan.end;
1950
+ function buildAsciiDispatch(parsers) {
1951
+ const table = Array(128).fill(-1);
1952
+ for (let i = 0; i < parsers.length; i++) {
1953
+ const fs = parsers[i]._meta.firstSet;
1954
+ if (fs.kind !== "ranges") continue;
1955
+ for (const { lo, hi } of fs.ranges) {
1956
+ for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
1957
+ table[code] = i;
1958
+ }
1796
1959
  }
1797
- const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1798
- return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1799
- }
1800
- const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1801
- const fast = fastTriviaScanner(triviaP);
1802
- if (fast) {
1803
- const end = fast(input, cur);
1804
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1805
- return end;
1806
- }
1807
- if (ctx.triviaKindLabels) {
1808
- const scan = scanWithLabels(input, cur, ctx);
1809
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1810
- return scan.end;
1811
1960
  }
1812
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1813
- return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1961
+ return table;
1814
1962
  }
1815
- function scanTrivia(input, cur, ctx) {
1816
- const triviaP = ctx.trivia;
1817
- if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1818
- const log = ctx._triviaLog;
1819
- const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1820
- if (!ctx.trackLines) {
1821
- const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1822
- if (fast2 && log === void 0 && !captureTl) {
1823
- return { end: fast2(input, cur), commit: NOOP_COMMIT };
1824
- }
1825
- if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1826
- return scanWithLabels(input, cur, ctx);
1827
- }
1828
- if (log !== void 0 || captureTl) {
1829
- const tr3 = triviaP.parse(input, cur, {
1830
- trackLines: log !== void 0 ? false : ctx.trackLines,
1831
- state: ctx.state
1832
- });
1833
- if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1834
- const end = tr3.span.end;
1835
- return {
1836
- end,
1837
- commit: () => {
1838
- pushTriviaLogEntry(ctx, cur, end);
1839
- if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1840
- }
1841
- };
1963
+
1964
+ // src/analysis/duplication.ts
1965
+ var WORD_RE_SRC = "-?[A-Za-z][A-Za-z0-9_-]*";
1966
+ var WORDS_RE = new RegExp(`^${WORD_RE_SRC}(?:\\|${WORD_RE_SRC})*$`);
1967
+
1968
+ // src/combinators/probe.ts
1969
+ function failAt(ctx, expected, pos) {
1970
+ const r = { ok: false, expected, span: { start: pos, end: pos } };
1971
+ const probe = ctx._probe;
1972
+ if (probe !== void 0 && pos <= probe.offset) {
1973
+ const best = probe.best;
1974
+ if (best === null || pos > best.span.start) {
1975
+ probe.best = r;
1976
+ } else if (pos === best.span.start) {
1977
+ probe.best = { ...best, expected: [...best.expected, ...expected] };
1842
1978
  }
1843
- const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1844
- return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1845
1979
  }
1846
- const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1847
- const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1848
- if (fast && log === void 0 && !captureTl) {
1849
- const end = fast(input, cur);
1850
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1851
- return { end, commit: NOOP_COMMIT };
1980
+ return r;
1981
+ }
1982
+
1983
+ // src/combinators/literal.ts
1984
+ function asciiFoldEq(a, b) {
1985
+ if (a.length !== b.length) return false;
1986
+ for (let i = 0; i < a.length; i++) {
1987
+ let ca = a.charCodeAt(i);
1988
+ let cb = b.charCodeAt(i);
1989
+ if (ca >= 65 && ca <= 90) ca += 32;
1990
+ if (cb >= 65 && cb <= 90) cb += 32;
1991
+ if (ca !== cb) return false;
1852
1992
  }
1853
- if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1854
- const scan = scanWithLabels(input, cur, ctx);
1855
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1856
- return scan;
1993
+ return true;
1994
+ }
1995
+ function literal(value, opts = {}) {
1996
+ const caseInsensitive = opts.caseInsensitive ?? false;
1997
+ const firstSet = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
1998
+ const meta = {
1999
+ firstSet,
2000
+ canMatchNewline: value.includes("\n"),
2001
+ isTrivia: false
2002
+ };
2003
+ const canMatchNewline = meta.canMatchNewline;
2004
+ if (caseInsensitive) {
2005
+ const upper = value.toUpperCase();
2006
+ const lower = value.toLowerCase();
2007
+ const firstUpper = upper.codePointAt(0);
2008
+ const firstLower = lower.codePointAt(0);
2009
+ meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
2010
+ { lo: firstLower, hi: firstLower },
2011
+ { lo: firstUpper, hi: firstUpper }
2012
+ ] } : firstSet;
1857
2013
  }
1858
- if (log !== void 0 || captureTl) {
1859
- const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1860
- if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1861
- const end = tr2.span.end;
1862
- return {
1863
- end,
1864
- commit: () => {
1865
- pushTriviaLogEntry(ctx, cur, end);
1866
- if (captureTl) pushCstTriviaEntry(ctx, cur, end);
2014
+ const expected = [JSON.stringify(value)];
2015
+ const parse = !caseInsensitive && value.length === 1 ? (() => {
2016
+ const code = value.charCodeAt(0);
2017
+ return function parse2(input, pos, ctx) {
2018
+ if (input.charCodeAt(pos) === code) {
2019
+ const span = { start: pos, end: pos + 1 };
2020
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
2021
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
2022
+ return { ok: true, value, span };
1867
2023
  }
2024
+ return failAt(ctx, expected, pos);
1868
2025
  };
1869
- }
1870
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1871
- return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1872
- }
1873
- function consumeTrivia(input, cur, ctx) {
1874
- if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1875
- const scan = scanTrivia(input, cur, ctx);
1876
- scan.commit();
1877
- return scan.end;
1878
- }
1879
- function fastTriviaScanner(trivia2) {
1880
- const cached = fastTriviaCache.get(trivia2);
1881
- if (cached !== void 0) return cached;
1882
- const scanner = buildFastTriviaScanner(trivia2);
1883
- fastTriviaCache.set(trivia2, scanner);
1884
- return scanner;
1885
- }
1886
- function buildFastTriviaScanner(trivia2) {
1887
- const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1888
- const direct = regexTriviaScanner(core);
1889
- if (direct) return direct;
1890
- const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1891
- if (!repeat) return null;
1892
- const one = regexTriviaScanner(repeat);
1893
- if (one) return loopScanner([one]);
1894
- if (repeat._def.tag !== "choice") return null;
1895
- const arms = repeat._def.parsers.map(regexTriviaScanner);
1896
- if (arms.some((s) => s === null)) return null;
1897
- return loopScanner(arms);
1898
- }
1899
- function loopScanner(arms) {
1900
- return (input, cur) => {
1901
- let pos = cur;
1902
- scan: while (pos < input.length) {
1903
- for (const arm of arms) {
1904
- const end = arm(input, pos);
1905
- if (end > pos) {
1906
- pos = end;
1907
- continue scan;
1908
- }
1909
- }
1910
- break;
2026
+ })() : function parse2(input, pos, ctx) {
2027
+ const end = pos + value.length;
2028
+ if (end > input.length) {
2029
+ return failAt(ctx, expected, pos);
2030
+ }
2031
+ const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
2032
+ if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
2033
+ const span = { start: pos, end };
2034
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
2035
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
2036
+ return { ok: true, value: matchedValue, span };
1911
2037
  }
1912
- return pos;
2038
+ return failAt(ctx, expected, pos);
2039
+ };
2040
+ return {
2041
+ _tag: "literal",
2042
+ _meta: meta,
2043
+ _def: { tag: "literal", value, caseInsensitive },
2044
+ parse
1913
2045
  };
1914
2046
  }
1915
- function regexTriviaScanner(parser2) {
1916
- if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1917
- const source = parser2._def.source;
1918
- return classRunSource(source) ?? altStarSource(source) ?? (blockCommentSource(source) ? scanBlockComment : null);
1919
- }
2047
+
2048
+ // src/combinators/regex.ts
2049
+ var firstSetAnalyzer = null;
2050
+ var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
2051
+ var SCAN_BAIL_AT = 64;
1920
2052
  function inRanges2(cp, ranges) {
1921
2053
  for (let i = 0; i < ranges.length; i++) {
1922
- const r = ranges[i];
1923
- if (cp >= r[0] && cp <= r[1]) return true;
2054
+ const [lo, hi] = ranges[i];
2055
+ if (cp >= lo && cp <= hi) return true;
1924
2056
  }
1925
2057
  return false;
1926
2058
  }
1927
- function classScanner(classBody) {
1928
- const ranges = parseClassRanges(classBody);
1929
- if (!ranges) return null;
1930
- return (input, cur) => {
1931
- let pos = cur;
1932
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1933
- return pos;
1934
- };
2059
+ function readClassRanges(body) {
2060
+ if (body.startsWith("^")) return null;
2061
+ return parseClassRanges(body);
1935
2062
  }
1936
- function lineCommentScanner(commentCode) {
1937
- return (input, cur) => {
1938
- if (input.charCodeAt(cur) !== commentCode) return cur;
1939
- let pos = cur + 1;
1940
- while (pos < input.length) {
1941
- const cc = input.charCodeAt(pos);
1942
- if (cc === 10 || cc === 13) break;
1943
- pos++;
2063
+ function shortScanner(source, flags) {
2064
+ if (/[imsuvy]/.test(flags)) return null;
2065
+ let ranges = null;
2066
+ let quant = "";
2067
+ if (source[0] === "[") {
2068
+ let end = 1;
2069
+ while (end < source.length && source[end] !== "]") {
2070
+ if (source[end] === "\\") end += 2;
2071
+ else end++;
1944
2072
  }
1945
- return pos;
2073
+ if (source[end] !== "]") return null;
2074
+ ranges = readClassRanges(source.slice(1, end));
2075
+ quant = source.slice(end + 1);
2076
+ } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
2077
+ ranges = shorthandRanges(source[1]);
2078
+ quant = source.slice(2);
2079
+ }
2080
+ if (!ranges || quant !== "+" && quant !== "*") return null;
2081
+ const minOne = quant === "+";
2082
+ return (input, pos) => {
2083
+ let end = pos;
2084
+ while (end < input.length && inRanges2(input.charCodeAt(end), ranges)) {
2085
+ end++;
2086
+ if (end - pos >= SCAN_BAIL_AT) return void 0;
2087
+ }
2088
+ return minOne && end === pos ? null : end;
1946
2089
  };
1947
2090
  }
1948
- function classRunSource(source) {
1949
- const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1950
- return m ? classScanner(m[1]) : null;
1951
- }
1952
- function classifyTriviaArm(arm) {
1953
- const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1954
- if (cls) {
1955
- const ranges = parseClassRanges(cls[1]);
1956
- return ranges ? { kind: "class", ranges } : null;
2091
+ function asciiCaseFold(fs) {
2092
+ if (fs.kind !== "ranges") return fs;
2093
+ const ranges = fs.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
2094
+ const add = (lo, hi) => {
2095
+ if (lo <= hi) ranges.push({ lo, hi });
2096
+ };
2097
+ for (const r of fs.ranges) {
2098
+ const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
2099
+ add(uLo + 32, uHi + 32);
2100
+ const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
2101
+ add(lLo - 32, lHi - 32);
1957
2102
  }
1958
- const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1959
- if (lc) {
1960
- const marker = lc[1];
1961
- return { kind: "comment", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
2103
+ ranges.sort((a, b) => a.lo - b.lo);
2104
+ const merged = [];
2105
+ for (const r of ranges) {
2106
+ const last = merged[merged.length - 1];
2107
+ if (last && r.lo <= last.hi + 1) last.hi = Math.max(last.hi, r.hi);
2108
+ else merged.push({ lo: r.lo, hi: r.hi });
1962
2109
  }
1963
- return null;
1964
- }
1965
- function armScanner(arm) {
1966
- if (arm.kind === "comment") return lineCommentScanner(arm.code);
1967
- const ranges = arm.ranges;
1968
- return (input, cur) => {
1969
- let pos = cur;
1970
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1971
- return pos;
1972
- };
2110
+ return { kind: "ranges", ranges: merged };
1973
2111
  }
1974
- function fusedTriviaScanner(ranges, commentCodes) {
1975
- const c0 = commentCodes[0];
1976
- const single = commentCodes.length === 1;
1977
- return (input, cur) => {
1978
- let pos = cur;
1979
- const len = input.length;
1980
- for (; ; ) {
1981
- const c = input.charCodeAt(pos);
1982
- if (pos < len && inRanges2(c, ranges)) {
1983
- pos++;
1984
- continue;
2112
+ function regex(pattern, flags = "") {
2113
+ const source = typeof pattern === "string" ? pattern : pattern.source;
2114
+ const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
2115
+ const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
2116
+ const scan = shortScanner(source, resolvedFlags);
2117
+ const raw = (firstSetAnalyzer ?? permissiveFirstSet)(source);
2118
+ const firstSet = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
2119
+ const canMatchNewline = raw.canMatchNewline;
2120
+ const meta = { firstSet, canMatchNewline, isTrivia: false };
2121
+ return {
2122
+ _tag: "regex",
2123
+ _meta: meta,
2124
+ _def: { tag: "regex", source, flags: resolvedFlags },
2125
+ parse(input, pos, ctx) {
2126
+ const scanEnd = scan?.(input, pos);
2127
+ if (scanEnd !== void 0) {
2128
+ if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
2129
+ const value = input.slice(pos, scanEnd);
2130
+ const span2 = { start: pos, end: scanEnd };
2131
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
2132
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
2133
+ return { ok: true, value, span: span2 };
1985
2134
  }
1986
- if (single ? c === c0 : commentCodes.includes(c)) {
1987
- pos++;
1988
- while (pos < len) {
1989
- const cc = input.charCodeAt(pos);
1990
- if (cc === 10 || cc === 13) break;
1991
- pos++;
1992
- }
1993
- continue;
2135
+ anchored.lastIndex = pos;
2136
+ const m = anchored.exec(input);
2137
+ if (m === null) {
2138
+ return failAt(ctx, [`/${source}/`], pos);
1994
2139
  }
1995
- break;
2140
+ const span = { start: pos, end: pos + m[0].length };
2141
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
2142
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
2143
+ return { ok: true, value: m[0], span };
1996
2144
  }
1997
- return pos;
1998
2145
  };
1999
2146
  }
2000
- function splitTopLevelAlts(body) {
2001
- const arms = [];
2002
- let inClass = false;
2003
- let start = 0;
2004
- for (let i = 0; i < body.length; i++) {
2005
- const c = body[i];
2006
- if (c === "\\") {
2007
- i++;
2008
- continue;
2009
- }
2010
- if (inClass) {
2011
- if (c === "]") inClass = false;
2012
- continue;
2013
- }
2014
- if (c === "[") inClass = true;
2015
- else if (c === "(") return null;
2016
- else if (c === "|") {
2017
- arms.push(body.slice(start, i));
2018
- start = i + 1;
2019
- }
2020
- }
2021
- arms.push(body.slice(start));
2022
- return arms;
2023
- }
2024
- function altStarSource(source) {
2025
- const m = /^\(\?:(.*)\)[*+]$/.exec(source);
2026
- if (!m) return null;
2027
- const armSrcs = splitTopLevelAlts(m[1]);
2028
- if (!armSrcs || armSrcs.length < 2) return null;
2029
- const arms = [];
2030
- for (const src of armSrcs) {
2031
- const arm = classifyTriviaArm(src);
2032
- if (!arm) return null;
2033
- arms.push(arm);
2034
- }
2035
- const ranges = [];
2036
- const commentCodes = [];
2037
- for (const arm of arms) {
2038
- if (arm.kind === "class") ranges.push(...arm.ranges);
2039
- else commentCodes.push(arm.code);
2040
- }
2041
- if (commentCodes.some((code) => inRanges2(code, ranges))) {
2042
- return loopScanner(arms.map(armScanner));
2043
- }
2044
- if (commentCodes.length === 0) return armScanner({ kind: "class", ranges });
2045
- return fusedTriviaScanner(ranges, commentCodes);
2046
- }
2047
- function blockCommentSource(source) {
2048
- return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
2049
- }
2050
- function scanBlockComment(input, cur) {
2051
- if (input.charCodeAt(cur) !== 47 || input.charCodeAt(cur + 1) !== 42) return cur;
2052
- const close = input.indexOf("*/", cur + 2);
2053
- return close === -1 ? cur : close + 2;
2054
- }
2055
2147
 
2056
2148
  // src/combinators/sequence.ts
2057
2149
  function sequence(...parsers) {
@@ -2467,6 +2559,25 @@ function trivia(combinator) {
2467
2559
  parse: combinator.parse.bind(combinator)
2468
2560
  };
2469
2561
  }
2562
+ function classifiedTrivia(arms) {
2563
+ const entries = Object.entries(arms);
2564
+ if (entries.length === 0) {
2565
+ throw new TypeError("classifiedTrivia() requires at least one named trivia arm.");
2566
+ }
2567
+ for (let i = 0; i < entries.length; i++) {
2568
+ const [name, arm] = entries[i];
2569
+ const first = arm._meta.firstSet;
2570
+ if (matchesEmpty(arm) || first.kind !== "ranges" || first.ranges.length === 0) {
2571
+ throw new TypeError(
2572
+ `classifiedTrivia(): ${JSON.stringify(name)} must be non-nullable with a concrete finite first set.`
2573
+ );
2574
+ }
2575
+ }
2576
+ const labeledArms = entries.map(([name, arm]) => label(name, arm));
2577
+ const result = trivia(oneOrMore(choice(...labeledArms)));
2578
+ result._meta.rootTriviaClassified = true;
2579
+ return result;
2580
+ }
2470
2581
  function label(name, combinator) {
2471
2582
  return {
2472
2583
  _tag: combinator._tag,
@@ -2560,8 +2671,48 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2560
2671
  };
2561
2672
  }
2562
2673
 
2674
+ // src/compiler/degradation.ts
2675
+ function formatDegradation(d) {
2676
+ return `[parseman] degraded [${d.code}] ${d.where}: ${d.subject} \u2014 ${d.fellBackTo}; otherwise ${d.otherwise}`;
2677
+ }
2678
+ var _sinks = [];
2679
+ function resolveDegradationLevel(explicit) {
2680
+ if (explicit !== void 0) return explicit;
2681
+ const env = typeof process !== "undefined" ? process.env?.PARSEMAN_DEGRADATION : void 0;
2682
+ if (env === "off" || env === "warn" || env === "error") return env;
2683
+ return "warn";
2684
+ }
2685
+ function degradationKey(d) {
2686
+ return JSON.stringify([d.code, d.where, d.subject]);
2687
+ }
2688
+ function recordDegradation(d) {
2689
+ const level = resolveDegradationLevel();
2690
+ if (level === "off") return;
2691
+ const key = degradationKey(d);
2692
+ const sink = _sinks[_sinks.length - 1];
2693
+ if (sink) {
2694
+ if (!sink.has(key)) sink.set(key, d);
2695
+ return;
2696
+ }
2697
+ if (_immediate.has(key)) return;
2698
+ _immediate.add(key);
2699
+ if (level === "error") throw new Error(`parseman: degraded compilation path
2700
+ ${formatDegradation(d)}`);
2701
+ console.warn(formatDegradation(d));
2702
+ }
2703
+ var _immediate = /* @__PURE__ */ new Set();
2704
+
2705
+ // src/cst/trivia-entries.ts
2706
+ var EMPTY_INDICES = Object.freeze([]);
2707
+ var EMPTY_GAPS = Object.freeze([]);
2708
+ var SELECTED_ROOT_STRIDE = 5;
2709
+
2563
2710
  // src/compiler/scannable-run.ts
2564
2711
  var META = new Set("()[]{}*+?|^$.".split(""));
2712
+ var SELECTED_ROOT_OWNED_RANGE_PLACEHOLDER = Array.from(
2713
+ { length: SELECTED_ROOT_STRIDE - 3 },
2714
+ () => "0"
2715
+ ).join(", ");
2565
2716
 
2566
2717
  // src/compiler/trivia-fast-path.ts
2567
2718
  var CAP_RECORD = [
@@ -2570,50 +2721,193 @@ var CAP_RECORD = [
2570
2721
  ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
2571
2722
  ` }`
2572
2723
  ].join("\n");
2724
+ var SELECTED_ROOT_FINISH = [
2725
+ ` if (_cap && _ctx._rootTriviaCapture !== false && _ctx._rootTriviaLog !== undefined && _ctx._rootTriviaLog.length !== _rootMark) {`,
2726
+ ` for (let _ri = _rootMark; _ri < _ctx._rootTriviaLog.length; _ri += ${SELECTED_ROOT_STRIDE}) { _ctx._rootTriviaLog[_ri] = _pos; _ctx._rootTriviaLog[_ri + 1] = _e }`,
2727
+ ` }`
2728
+ ].join("\n");
2573
2729
 
2574
2730
  // src/compiler/build-arity.ts
2575
2731
  function cstOutputHost(build) {
2576
2732
  return build?._parsemanCstOutput === true;
2577
2733
  }
2578
- var PARAM_LIST_RE = (
2579
- // (params) => ... | function name?(params) ... | single-ident arrow `x => ...`
2580
- /^(?:function\b[^(]*\(([^)]*)\)|\(([^)]*)\)\s*=>|([A-Za-z_$][\w$]*)\s*=>)/
2581
- );
2582
- var CONFIRMABLE_PARAM_RE = /^[A-Za-z_$][\w$]*\s*\??\s*(?::[^,=]+)?$/;
2734
+ var SINGLE_IDENT_ARROW_RE = /^[A-Za-z_$][\w$]*\s*=>/;
2735
+ var PARAM_LIST_OPEN_RE = /^(?:function\b[^(]*)?\(/;
2736
+ var NATIVE_CODE_RE = /\{\s*\[native code\]\s*\}/;
2737
+ var PARAM_HEAD_RE = /^[A-Za-z_$][\w$]*\s*\??\s*/;
2583
2738
  function confirmedBuildArity(src) {
2584
2739
  const s = src.trim();
2585
- const m = PARAM_LIST_RE.exec(s);
2586
- if (!m) return null;
2587
- if (m[3] !== void 0) return 1;
2588
- const inner = (m[1] ?? m[2] ?? "").trim();
2589
- if (inner === "") return 0;
2590
- const parts = inner.split(",");
2740
+ if (NATIVE_CODE_RE.test(s)) return null;
2741
+ if (/\barguments\b/.test(s)) return null;
2742
+ if (SINGLE_IDENT_ARROW_RE.test(s)) return 1;
2743
+ const inner = paramListText(s);
2744
+ if (inner === null) return null;
2745
+ if (inner.trim() === "") return 0;
2746
+ const parts = splitTopLevel(inner);
2747
+ if (parts === null) return null;
2591
2748
  for (const part of parts) {
2592
- if (!CONFIRMABLE_PARAM_RE.test(part.trim())) return null;
2749
+ if (!isConfirmableParam(part.trim())) return null;
2593
2750
  }
2594
- if (/\barguments\b/.test(s)) return null;
2595
2751
  return parts.length;
2596
2752
  }
2753
+ function paramListText(s) {
2754
+ const open = PARAM_LIST_OPEN_RE.exec(s);
2755
+ if (!open) return null;
2756
+ const start = open[0].length;
2757
+ const end = matchingParen(s, start);
2758
+ if (end === null) return null;
2759
+ if (!s.startsWith("function") && !/^\s*=>/.test(s.slice(end + 1))) return null;
2760
+ return s.slice(start, end);
2761
+ }
2762
+ function matchingParen(s, start) {
2763
+ let depth = 0;
2764
+ for (let i = start; i < s.length; i++) {
2765
+ const c = s[i];
2766
+ if (c === '"' || c === "'" || c === "`") {
2767
+ const close = skipString(s, i);
2768
+ if (close === null) return null;
2769
+ i = close;
2770
+ continue;
2771
+ }
2772
+ if (c === "(" || c === "[" || c === "{") depth++;
2773
+ else if (c === "]" || c === "}") {
2774
+ if (--depth < 0) return null;
2775
+ } else if (c === ")") {
2776
+ if (depth === 0) return i;
2777
+ depth--;
2778
+ }
2779
+ }
2780
+ return null;
2781
+ }
2782
+ function skipString(s, open) {
2783
+ const quote2 = s[open];
2784
+ for (let i = open + 1; i < s.length; i++) {
2785
+ if (s[i] === "\\") {
2786
+ i++;
2787
+ continue;
2788
+ }
2789
+ if (s[i] === quote2) return i;
2790
+ }
2791
+ return null;
2792
+ }
2793
+ function splitTopLevel(inner) {
2794
+ const parts = [];
2795
+ let depth = 0;
2796
+ let angle = 0;
2797
+ let last = 0;
2798
+ for (let i = 0; i < inner.length; i++) {
2799
+ const c = inner[i];
2800
+ if (c === "=" && inner[i + 1] === ">") {
2801
+ i++;
2802
+ continue;
2803
+ }
2804
+ if (c === '"' || c === "'" || c === "`") {
2805
+ const close = skipString(inner, i);
2806
+ if (close === null) return null;
2807
+ i = close;
2808
+ continue;
2809
+ }
2810
+ if (c === "(" || c === "[" || c === "{") depth++;
2811
+ else if (c === ")" || c === "]" || c === "}") {
2812
+ if (--depth < 0) return null;
2813
+ } else if (c === "<") angle++;
2814
+ else if (c === ">") {
2815
+ if (--angle < 0) return null;
2816
+ } else if (c === "," && depth === 0 && angle === 0) {
2817
+ parts.push(inner.slice(last, i));
2818
+ last = i + 1;
2819
+ }
2820
+ }
2821
+ if (depth !== 0 || angle !== 0) return null;
2822
+ parts.push(inner.slice(last));
2823
+ return parts;
2824
+ }
2825
+ function isConfirmableParam(part) {
2826
+ const head = PARAM_HEAD_RE.exec(part);
2827
+ if (!head) return false;
2828
+ const rest = part.slice(head[0].length);
2829
+ if (rest === "") return true;
2830
+ if (rest[0] !== ":") return false;
2831
+ return !hasDefaultAssign(rest.slice(1));
2832
+ }
2833
+ function hasDefaultAssign(annotation) {
2834
+ for (let i = 0; i < annotation.length; i++) {
2835
+ if (annotation[i] !== "=") continue;
2836
+ if (annotation[i + 1] === ">" || annotation[i + 1] === "=") {
2837
+ i++;
2838
+ continue;
2839
+ }
2840
+ const prev = annotation[i - 1];
2841
+ if (prev === "<" || prev === ">" || prev === "!" || prev === "=") continue;
2842
+ return true;
2843
+ }
2844
+ return false;
2845
+ }
2846
+ function buildAnalysisSrc(def) {
2847
+ return def.buildSigSrc ?? def.buildSrc ?? def.build.toString();
2848
+ }
2849
+ var RESOLVER_REASONS = {
2850
+ "rest-parameter": "its parameter list uses a rest parameter, so the declared arity is unbounded",
2851
+ "arguments": "its body references `arguments`",
2852
+ "reassigned": "the binding it names is reassigned, so the function read here is not decidable",
2853
+ "not-a-function": "the binding it names is not a function declaration",
2854
+ "unresolved-import": "its module could not be resolved or parsed",
2855
+ "not-found": "the name did not resolve to any binding",
2856
+ "computed": "it is a computed expression",
2857
+ "foreign-source": "it is named inside an imported factory, whose offsets the entry-file resolver cannot index"
2858
+ };
2859
+ function unconfirmableReason(def, src) {
2860
+ const declared = def.buildArityUnresolved;
2861
+ if (declared !== void 0 && RESOLVER_REASONS[declared]) return RESOLVER_REASONS[declared];
2862
+ const s = src.trim();
2863
+ const params = s.slice(0, s.indexOf(")") + 1);
2864
+ if (params.includes("...")) return RESOLVER_REASONS["rest-parameter"];
2865
+ if (/\barguments\b/.test(s)) return RESOLVER_REASONS["arguments"];
2866
+ if (/^[A-Za-z_$][\w$]*(\s*\.\s*[A-Za-z_$][\w$]*)?$/.test(s)) {
2867
+ return "it names a binding this build could not resolve to a function declaration";
2868
+ }
2869
+ return "its parameter list could not be parsed";
2870
+ }
2871
+ function confirmedArityForDef(def) {
2872
+ if (def.buildArity !== void 0) return def.buildArity;
2873
+ const src = buildAnalysisSrc(def);
2874
+ const arity = confirmedBuildArity(src);
2875
+ if (arity !== null) return arity;
2876
+ const subject = `build reducer \`${(def.buildSrc ?? "<runtime fn>").trim().split("\n")[0].slice(0, 60)}\``;
2877
+ recordDegradation({
2878
+ code: "build-arity-unconfirmed",
2879
+ severity: "warn",
2880
+ where: `node("${def.type ?? "<inferred>"}")`,
2881
+ subject,
2882
+ 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`,
2883
+ 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"
2884
+ });
2885
+ return null;
2886
+ }
2597
2887
  function buildReadsTrivia(def) {
2598
2888
  if (!def.build) return true;
2599
- const src = def.buildSrc ?? def.build.toString();
2600
- const arity = confirmedBuildArity(src);
2889
+ const arity = confirmedArityForDef(def);
2601
2890
  if (arity === null) return true;
2602
2891
  return arity >= 5;
2603
2892
  }
2604
2893
  function buildReadsState(def) {
2605
2894
  if (!def.build) return true;
2606
- const src = def.buildSrc ?? def.build.toString();
2607
- const arity = confirmedBuildArity(src);
2895
+ const arity = confirmedArityForDef(def);
2608
2896
  if (arity === null) return true;
2609
2897
  return arity >= 6;
2610
2898
  }
2611
2899
 
2900
+ // src/compiler/inline-build.ts
2901
+ var ANN = String.raw`\s*\??\s*(?::[^,)=]+)?`;
2902
+ var ANY_P = String.raw`\w+${ANN}`;
2903
+ var MK_BUILD_RE = new RegExp(
2904
+ 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*$`
2905
+ );
2906
+
2612
2907
  // src/compiler/fields.ts
2613
2908
  function buildReadsFields(def) {
2614
2909
  if (!def.build) return true;
2615
- const src = def.buildSrc ?? def.build.toString();
2616
- const arity = confirmedBuildArity(src);
2910
+ const arity = confirmedArityForDef(def);
2617
2911
  if (arity === null) return true;
2618
2912
  return arity >= 2;
2619
2913
  }
@@ -2864,17 +3158,21 @@ function when(key, parser2, opts = {}) {
2864
3158
  function otherwise(parser2) {
2865
3159
  return { kind: "otherwise", parser: parser2, usesRouted: parserUsesRouted(parser2) };
2866
3160
  }
2867
- function routed() {
3161
+ function routed(fallback) {
2868
3162
  return {
2869
3163
  _tag: "routed",
2870
- _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
2871
- _def: { tag: "routed" },
3164
+ _meta: {
3165
+ // The routed token is whatever the selector matched, so the first set stays
3166
+ // `any` even with a fallback — `any` already subsumes the fallback's.
3167
+ firstSet: { kind: "any" },
3168
+ canMatchNewline: fallback?._meta.canMatchNewline ?? false,
3169
+ isTrivia: false
3170
+ },
3171
+ _def: fallback === void 0 ? { tag: "routed" } : { tag: "routed", fallback },
2872
3172
  parse(_input, pos, ctx) {
2873
3173
  const item = ctx._routed;
2874
- if (item === void 0) {
2875
- return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
2876
- }
2877
- if (pos !== item.span.start) {
3174
+ if (item === void 0 || pos !== item.span.start) {
3175
+ if (fallback !== void 0) return fallback.parse(_input, pos, ctx);
2878
3176
  return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
2879
3177
  }
2880
3178
  if (cstCaptureActive(ctx)) {
@@ -3009,10 +3307,12 @@ function attempt(parser2) {
3009
3307
  }
3010
3308
  const mark = saveCstMark(ctx);
3011
3309
  const log = ctx._triviaLog?.length;
3310
+ const rootLog = ctx._rootTriviaLog?.length;
3012
3311
  const result = parser2.parse(input, pos, ctx);
3013
3312
  if (result.ok) return result;
3014
3313
  rollbackCstCapture(ctx, mark);
3015
3314
  if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
3315
+ if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
3016
3316
  if (result.committed) return result;
3017
3317
  return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
3018
3318
  }
@@ -3075,6 +3375,13 @@ function isCstChild(value) {
3075
3375
  function missingInferredType() {
3076
3376
  throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
3077
3377
  }
3378
+ function normalizeBuildArity(arity) {
3379
+ if (arity === void 0) return void 0;
3380
+ if (!Number.isInteger(arity) || arity < 0 || arity > 6) {
3381
+ throw new Error("node() buildArity must be an integer 0..6 (children, fields, span, rawChildren, triviaLog, state)");
3382
+ }
3383
+ return arity;
3384
+ }
3078
3385
  function normalizeProject(project) {
3079
3386
  if (project === void 0) return void 0;
3080
3387
  if (!Number.isInteger(project) || project < 0) {
@@ -3110,13 +3417,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3110
3417
  const captureTrivia = opts?.captureTrivia === true;
3111
3418
  const trailingTrivia = opts?.trailingTrivia === true;
3112
3419
  const tags = opts?.tags;
3420
+ const buildArity = normalizeBuildArity(opts?.buildArity);
3113
3421
  if (unwrap && collapse) {
3114
3422
  throw new Error("node() options cannot set both unwrap and collapse");
3115
3423
  }
3116
3424
  if (project !== void 0 && (unwrap || collapse)) {
3117
3425
  throw new Error("node() options cannot combine project with unwrap or collapse");
3118
3426
  }
3119
- 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;
3427
+ 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;
3120
3428
  const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
3121
3429
  const clonesState = build ? buildReadsState(def) : project === void 0;
3122
3430
  const hasOwnFields = parserHasOwnFields(combinator);
@@ -3162,7 +3470,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3162
3470
  const cstOutput = cstOutputHost(ctx.build);
3163
3471
  const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3164
3472
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3165
- 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 };
3473
+ 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 };
3166
3474
  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 };
3167
3475
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3168
3476
  pushCstChild(ctx, built, rawEntry);
@@ -3192,6 +3500,7 @@ function token(root) {
3192
3500
  const savedRaw = ctx._cstRawChildren;
3193
3501
  const savedTriviaLog = ctx._cstTriviaLog;
3194
3502
  const savedOuterTriviaLog = ctx._triviaLog;
3503
+ const savedRootTriviaLog = ctx._rootTriviaLog;
3195
3504
  const wasCapturing = cstCaptureActive(ctx);
3196
3505
  ctx.trivia = void 0;
3197
3506
  ctx.triviaKindLabels = void 0;
@@ -3201,6 +3510,7 @@ function token(root) {
3201
3510
  ctx._cstRawChildren = void 0;
3202
3511
  ctx._cstTriviaLog = void 0;
3203
3512
  delete ctx._triviaLog;
3513
+ delete ctx._rootTriviaLog;
3204
3514
  let result;
3205
3515
  try {
3206
3516
  result = root.parse(input, pos, ctx);
@@ -3214,6 +3524,8 @@ function token(root) {
3214
3524
  ctx._cstTriviaLog = savedTriviaLog;
3215
3525
  if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
3216
3526
  else ctx._triviaLog = savedOuterTriviaLog;
3527
+ if (savedRootTriviaLog === void 0) delete ctx._rootTriviaLog;
3528
+ else ctx._rootTriviaLog = savedRootTriviaLog;
3217
3529
  }
3218
3530
  if (!result.ok) return result;
3219
3531
  const span = result.span;
@@ -3313,7 +3625,7 @@ function evalRuleMapIR(ir) {
3313
3625
  l._def.fnSrc = src;
3314
3626
  return l;
3315
3627
  };
3316
- const _nd = (type, child, src, opts, staticError) => {
3628
+ const _nd = (type, child, src, opts, staticError, sigSrc) => {
3317
3629
  if (staticError !== void 0 && staticError.length > 0) {
3318
3630
  throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
3319
3631
  }
@@ -3322,6 +3634,7 @@ function evalRuleMapIR(ir) {
3322
3634
  }), opts);
3323
3635
  n._def.buildSrc = src;
3324
3636
  if (staticError !== void 0) n._def.buildStaticError = staticError;
3637
+ if (sigSrc !== void 0) n._def.buildSigSrc = sigSrc;
3325
3638
  return n;
3326
3639
  };
3327
3640
  const _gch = (items) => {
@@ -3393,6 +3706,7 @@ function evalRuleMapIR(ir) {
3393
3706
  "transform",
3394
3707
  "skip",
3395
3708
  "trivia",
3709
+ "classifiedTrivia",
3396
3710
  "label",
3397
3711
  "field",
3398
3712
  "expect",
@@ -3433,6 +3747,7 @@ function evalRuleMapIR(ir) {
3433
3747
  transform,
3434
3748
  skip,
3435
3749
  trivia,
3750
+ classifiedTrivia,
3436
3751
  label,
3437
3752
  field,
3438
3753
  expect,
@@ -3652,7 +3967,7 @@ var Builder = class {
3652
3967
  case "peek":
3653
3968
  return { kind: "peek", item: this.walk(def.parser) };
3654
3969
  case "routed":
3655
- return { kind: "empty" };
3970
+ return def.fallback === void 0 ? { kind: "empty" } : this.walk(def.fallback);
3656
3971
  // Transparent semantic wrappers — render the inner syntax.
3657
3972
  case "transform":
3658
3973
  case "token":