parseman 0.42.1 → 0.44.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 (68) hide show
  1. package/dist/combinators/attempt.d.ts.map +1 -1
  2. package/dist/combinators/choice.d.ts.map +1 -1
  3. package/dist/combinators/grammar.d.ts +7 -0
  4. package/dist/combinators/grammar.d.ts.map +1 -1
  5. package/dist/combinators/map.d.ts +11 -0
  6. package/dist/combinators/map.d.ts.map +1 -1
  7. package/dist/combinators/node.d.ts +21 -7
  8. package/dist/combinators/node.d.ts.map +1 -1
  9. package/dist/combinators/parser.d.ts +17 -2
  10. package/dist/combinators/parser.d.ts.map +1 -1
  11. package/dist/combinators/token.d.ts +3 -2
  12. package/dist/combinators/token.d.ts.map +1 -1
  13. package/dist/combinators/trivia-skip.d.ts +1 -0
  14. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  15. package/dist/compiler/codegen.d.ts +4 -0
  16. package/dist/compiler/codegen.d.ts.map +1 -1
  17. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  18. package/dist/compiler/linker.d.ts +6 -1
  19. package/dist/compiler/linker.d.ts.map +1 -1
  20. package/dist/compiler/scannable-run.d.ts +1 -1
  21. package/dist/compiler/scannable-run.d.ts.map +1 -1
  22. package/dist/compiler/trivia-fast-path.d.ts +4 -3
  23. package/dist/compiler/trivia-fast-path.d.ts.map +1 -1
  24. package/dist/cst/reflection.d.ts +24 -0
  25. package/dist/cst/reflection.d.ts.map +1 -0
  26. package/dist/cst/trivia-entries.d.ts +15 -9
  27. package/dist/cst/trivia-entries.d.ts.map +1 -1
  28. package/dist/cst/trivia-kinds.d.ts +15 -11
  29. package/dist/cst/trivia-kinds.d.ts.map +1 -1
  30. package/dist/cst/types.d.ts +2 -0
  31. package/dist/cst/types.d.ts.map +1 -1
  32. package/dist/cst/walk.d.ts +27 -62
  33. package/dist/cst/walk.d.ts.map +1 -1
  34. package/dist/functional/run.d.ts +22 -16
  35. package/dist/functional/run.d.ts.map +1 -1
  36. package/dist/index.cjs +1651 -1207
  37. package/dist/index.cjs.map +4 -4
  38. package/dist/index.d.ts +6 -5
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +1651 -1207
  41. package/dist/index.js.map +4 -4
  42. package/dist/language-service/index.cjs +186 -135
  43. package/dist/language-service/index.cjs.map +4 -4
  44. package/dist/language-service/index.d.ts +1 -1
  45. package/dist/language-service/index.d.ts.map +1 -1
  46. package/dist/language-service/index.js +186 -135
  47. package/dist/language-service/index.js.map +4 -4
  48. package/dist/plugin/evaluator.d.ts +1 -0
  49. package/dist/plugin/evaluator.d.ts.map +1 -1
  50. package/dist/plugin/index.cjs +1525 -1048
  51. package/dist/plugin/index.cjs.map +4 -4
  52. package/dist/plugin/index.d.ts.map +1 -1
  53. package/dist/plugin/index.js +1525 -1048
  54. package/dist/plugin/index.js.map +4 -4
  55. package/dist/run/index.cjs +152 -102
  56. package/dist/run/index.cjs.map +3 -3
  57. package/dist/run/index.d.ts +1 -1
  58. package/dist/run/index.d.ts.map +1 -1
  59. package/dist/run/index.js +152 -102
  60. package/dist/run/index.js.map +3 -3
  61. package/dist/spec/index.cjs +1381 -1152
  62. package/dist/spec/index.cjs.map +4 -4
  63. package/dist/spec/index.js +1381 -1152
  64. package/dist/spec/index.js.map +4 -4
  65. package/dist/types.d.ts +27 -1
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/version.d.ts +1 -1
  68. package/package.json +1 -1
@@ -610,864 +610,631 @@ function pushTriviaLogEntry(ctx, start, end, kindIndex) {
610
610
  }
611
611
  }
612
612
 
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
- }
613
+ // src/cst/trivia-kinds.ts
614
+ var labeledTriviaSpecs = /* @__PURE__ */ new WeakMap();
615
+ function unwrapTrivia(p) {
616
+ let cur = p;
617
+ while (cur._def.tag === "trivia") cur = cur._def.parser;
618
+ return cur;
654
619
  }
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
- };
620
+ function peelLabel(p) {
621
+ if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
622
+ return null;
667
623
  }
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
- }
624
+ function analyzeLabeledTrivia(trivia2) {
625
+ const cached = labeledTriviaSpecs.get(trivia2);
626
+ if (cached !== void 0) return cached;
627
+ let core = unwrapTrivia(trivia2);
628
+ let minRepeats = 1;
629
+ if (core._def.tag === "oneOrMore") {
630
+ core = core._def.parser;
631
+ minRepeats = 1;
632
+ } else if (core._def.tag === "many") {
633
+ core = core._def.parser;
634
+ minRepeats = 0;
635
+ }
636
+ const arms = [];
637
+ if (core._def.tag === "choice") {
638
+ for (let i = 0; i < core._def.parsers.length; i++) {
639
+ const peeled = peelLabel(core._def.parsers[i]);
640
+ if (!peeled) {
641
+ labeledTriviaSpecs.set(trivia2, null);
642
+ return null;
681
643
  }
682
- return { ok: false, expected: [], span: { start: pos, end: pos } };
644
+ arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
645
+ }
646
+ } else {
647
+ const peeled = peelLabel(core);
648
+ if (!peeled) {
649
+ labeledTriviaSpecs.set(trivia2, null);
650
+ return null;
683
651
  }
652
+ arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
653
+ }
654
+ const spec = {
655
+ labels: arms.map((a) => a.label),
656
+ arms,
657
+ minRepeats
684
658
  };
659
+ labeledTriviaSpecs.set(trivia2, spec);
660
+ return spec;
685
661
  }
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);
662
+ function matchArmAt(input, pos, arm, state) {
663
+ if (!startsFirstSet(arm, input, pos)) return null;
664
+ const r = arm.parse(input, pos, { trackLines: false, state });
665
+ if (!r.ok || r.span.end <= pos) return null;
666
+ return { end: r.span.end };
697
667
  }
698
- function captureError(ctx, error) {
699
- if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
668
+ function scanLabeledTriviaChunks(input, cur, spec, state) {
669
+ const chunks = [];
670
+ let pos = cur;
671
+ while (pos < input.length) {
672
+ let matched = null;
673
+ for (const arm of spec.arms) {
674
+ const m = matchArmAt(input, pos, arm.parser, state);
675
+ if (m) {
676
+ matched = { end: m.end, kindIndex: arm.kindIndex };
677
+ break;
678
+ }
679
+ }
680
+ if (!matched) break;
681
+ chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
682
+ pos = matched.end;
683
+ }
684
+ if (chunks.length < spec.minRepeats) {
685
+ return { end: cur, chunks: [] };
686
+ }
687
+ return { end: pos, chunks };
700
688
  }
701
-
702
- // src/combinators/expect.ts
703
- function deriveExpected(c) {
704
- return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
689
+ function scanLabeledTriviaEnd(input, cur, spec, state) {
690
+ let pos = cur;
691
+ let count = 0;
692
+ while (pos < input.length) {
693
+ let matched = null;
694
+ for (const arm of spec.arms) {
695
+ matched = matchArmAt(input, pos, arm.parser, state);
696
+ if (matched) break;
697
+ }
698
+ if (!matched) break;
699
+ pos = matched.end;
700
+ count++;
701
+ }
702
+ return count < spec.minRepeats ? cur : pos;
705
703
  }
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;
704
+ function visitLabeledTrivia(input, cur, spec, state, visit) {
705
+ if (spec.minRepeats > 1) return void 0;
706
+ let pos = cur;
707
+ let count = 0;
708
+ while (pos < input.length) {
709
+ let matched = null;
710
+ for (const arm of spec.arms) {
711
+ const match = matchArmAt(input, pos, arm.parser, state);
712
+ if (match) {
713
+ matched = { end: match.end, kindIndex: arm.kindIndex };
714
+ break;
733
715
  }
734
- return out;
735
716
  }
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);
717
+ if (!matched) break;
718
+ visit(pos, matched.end, matched.kindIndex);
719
+ pos = matched.end;
720
+ count++;
721
+ }
722
+ return count < spec.minRepeats ? cur : pos;
723
+ }
724
+ function recordTriviaChunks(ctx, chunks) {
725
+ const kinds = ctx.triviaKindLabels;
726
+ const mask = ctx._triviaCaptureMask;
727
+ const rootLog = ctx._rootTriviaLog;
728
+ const rootKinds = ctx._rootTriviaKindIndex;
729
+ const rootMark = rootLog?.length ?? 0;
730
+ for (const ch of chunks) {
731
+ pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
732
+ const rootKindIndex = ctx._rootTriviaCapture === false || rootLog === void 0 || rootKinds === void 0 || kinds === void 0 ? -1 : rootKinds[kinds[ch.kindIndex] ?? ""] ?? -1;
733
+ if (rootKindIndex >= 0) {
734
+ rootLog.push(0, 0, ch.start, ch.end, rootKindIndex);
735
+ }
736
+ if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
737
+ if (mask === void 0 || kinds === void 0 || (mask & 1 << ch.kindIndex) !== 0) {
738
+ pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
773
739
  }
774
740
  }
775
- default:
776
- return [];
777
741
  }
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 } };
742
+ if (rootLog !== void 0 && rootLog.length !== rootMark && chunks.length > 0) {
743
+ const start = chunks[0].start;
744
+ const end = chunks[chunks.length - 1].end;
745
+ for (let offset = rootMark; offset < rootLog.length; offset += 5) {
746
+ rootLog[offset] = start;
747
+ rootLog[offset + 1] = end;
798
748
  }
799
- };
749
+ }
800
750
  }
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]);
751
+ function triviaKindMask(labels, keep) {
752
+ if (!labels) return void 0;
753
+ let mask = 0;
754
+ for (const name of keep) {
755
+ const idx = labels.indexOf(name);
756
+ if (idx >= 0) mask |= 1 << idx;
757
+ }
758
+ return mask;
759
+ }
760
+
761
+ // src/combinators/grammar.ts
762
+ function annotateResultLines(result, index) {
763
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
764
+ const errors = annotated.errors;
765
+ if (errors) {
766
+ ;
767
+ annotated.errors = errors.map((error) => ({
768
+ ...error,
769
+ span: annotateSpan(error.span, index)
770
+ }));
771
+ }
772
+ return annotated;
773
+ }
774
+ function createParseLineContext(input, pos) {
775
+ const lineIndex = createLineIndex();
776
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
777
+ return { lineIndex, lineScannedTo: pos };
778
+ }
779
+ function parser(opts, root) {
780
+ const clearTrivia = opts.trivia === null;
781
+ const opaqueRootCapture = opts.rootCapture === "opaque";
782
+ if (opaqueRootCapture && opts.trivia === void 0) {
783
+ throw new TypeError("parser({ rootCapture: 'opaque' }) requires an explicit trivia scope.");
830
784
  }
831
785
  return {
832
- _tag: "choice",
833
- _meta: meta,
786
+ _tag: "grammar",
787
+ _meta: {
788
+ ...root._meta,
789
+ // Trivia classification describes this grammar's own active trivia
790
+ // scope. A directly nested grammar may have classified trivia of its
791
+ // own, but that must not advertise classification for this wrapper.
792
+ triviaKindLabels: void 0,
793
+ rootTriviaClassified: void 0,
794
+ ...opts.trivia?._meta?.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {},
795
+ ...opts.trivia?._meta?.rootTriviaClassified ? { rootTriviaClassified: true } : {}
796
+ },
834
797
  _def: {
835
- tag: "choice",
836
- parsers,
837
- gates,
838
- disjoint,
839
- strategy: strategy ?? { tag: "firstMatch" },
840
- autoNot
798
+ tag: "grammar",
799
+ parser: root,
800
+ triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
801
+ clearTrivia,
802
+ ...opaqueRootCapture ? { rootCapture: "opaque" } : {},
803
+ ...opts.captureTrivia ? { captureTrivia: true } : {},
804
+ trackLines: opts.trackLines ?? false
841
805
  },
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 } };
806
+ parse(input, pos, _ctx) {
807
+ if (_ctx?._rootTriviaStrictScopes && opts.trivia !== void 0 && opts.trivia !== null && !opts.trivia._meta.rootTriviaClassified && !opaqueRootCapture) {
808
+ throw new TypeError(
809
+ "parser(): selected root trivia requires classifiedTrivia() for every local trivia scope, or rootCapture: 'opaque'."
810
+ );
894
811
  }
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;
812
+ const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
813
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
814
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
815
+ const ctx = {
816
+ ..._ctx,
817
+ trackLines,
818
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
819
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
820
+ // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
821
+ ...clearTrivia ? {
822
+ trivia: void 0,
823
+ triviaKindLabels: void 0
824
+ } : opts.trivia != null ? {
825
+ trivia: opts.trivia,
826
+ ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
827
+ } : {},
828
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
829
+ // Kind-filter for per-node capture. Resolve against this scope's trivia
830
+ // labels — this parser's own trivia if it declares one, else the INHERITED
831
+ // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
832
+ // trivia is inherited rather than re-declared here. No labels → undefined
833
+ // (capture all).
834
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {},
835
+ // This is a property of the local trivia scope, not a post-parse filter:
836
+ // nested recognition can still skip its trivia, but selected root rows
837
+ // must never be written for an explicitly opaque region.
838
+ ...opaqueRootCapture ? { _rootTriviaCapture: false } : {}
839
+ };
840
+ const result = root.parse(input, pos ?? 0, ctx);
841
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
842
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
914
843
  }
915
- return { ok: false, expected, span: { start: pos, end: pos } };
844
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
916
845
  }
917
846
  };
918
847
  }
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);
925
- }
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 };
938
- }
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 };
848
+
849
+ // src/cst/reflection.ts
850
+ var GRAMMAR_REFLECTION = /* @__PURE__ */ Symbol.for("parseman.grammarReflection");
851
+ function childrenOf(def) {
852
+ switch (def.tag) {
853
+ case "sequence":
854
+ case "choice":
855
+ return def.parsers;
856
+ case "dispatch":
857
+ return [
858
+ def.selector,
859
+ ...def.cases.map((c) => c.parser),
860
+ ...def.matchers ? def.matchers.map((c) => c.parser) : [],
861
+ ...def.otherwise ? [def.otherwise] : []
862
+ ];
863
+ case "many":
864
+ case "oneOrMore":
865
+ case "optional":
866
+ case "attempt":
867
+ case "transform":
868
+ case "trivia":
869
+ case "token":
870
+ case "leaf":
871
+ case "label":
872
+ case "field":
873
+ case "not":
874
+ case "peek":
875
+ case "node":
876
+ case "withCtx":
877
+ case "recover":
878
+ case "expect":
879
+ return [def.parser];
880
+ case "grammar":
881
+ return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
882
+ case "sepBy":
883
+ return [def.parser, def.separator];
884
+ case "skip":
885
+ return [def.main, def.skipped];
886
+ case "scanTo":
887
+ return [def.sentinel, ...def.skip];
888
+ case "literal":
889
+ case "regex":
890
+ case "keywords":
891
+ case "routed":
892
+ case "guard":
893
+ case "lazy":
894
+ case "unknown":
895
+ return [];
944
896
  }
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
897
  }
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);
898
+ function addNode(out, def) {
899
+ if (def.type === void 0) return;
900
+ const existing = out.get(def.type);
901
+ if (existing === void 0) {
902
+ out.set(def.type, def.tags === void 0 ? [] : [...def.tags]);
903
+ return;
970
904
  }
971
- if (prefix === null || members.length < 2) return null;
972
- return { tag: "sharedPrefix", prefix, members };
973
- }
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;
979
- continue;
980
- }
981
- break;
905
+ if (def.tags === void 0) return;
906
+ for (const tag of def.tags) {
907
+ if (!existing.includes(tag)) existing.push(tag);
982
908
  }
983
- if (d.tag !== "sequence" || d.parsers.length < 2) return null;
984
- return d.parsers[0];
985
909
  }
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;
997
- }
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 });
910
+ function collectFromCombinator(parser2, out, seen, followLazy = true) {
911
+ if (seen.has(parser2)) return;
912
+ seen.add(parser2);
913
+ const def = parser2._def;
914
+ if (def.tag === "lazy") {
915
+ if (followLazy) {
916
+ try {
917
+ collectFromCombinator(def.thunk(), out, seen, followLazy);
918
+ } catch {
1002
919
  }
1003
920
  }
1004
- return checks.length > 0 ? checks : null;
1005
- });
1006
- }
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;
921
+ return;
922
+ }
923
+ if (def.tag === "node") addNode(out, def);
924
+ for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy);
925
+ }
926
+ function collectGrammarReflection(ruleMap, opts) {
927
+ const byType = /* @__PURE__ */ new Map();
928
+ const followLazy = opts?.followLazy !== false;
929
+ for (const [, rule] of ruleMap) {
930
+ if (!followLazy && rule._def.tag === "lazy") {
931
+ try {
932
+ const resolved = rule._def.thunk();
933
+ if (resolved._def.tag !== "lazy") collectFromCombinator(resolved, byType, /* @__PURE__ */ new Set(), false);
934
+ } catch {
935
+ }
936
+ continue;
1014
937
  }
938
+ collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
1015
939
  }
1016
- return false;
1017
- }
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;
1023
- }
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;
940
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
1030
941
  }
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;
942
+ function attachGrammarReflection(grammar, reflection) {
943
+ Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true });
944
+ return grammar;
1038
945
  }
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);
946
+
947
+ // src/combinators/parser.ts
948
+ var RULE_ORDER = "__parsemanRuleOrder";
949
+ function tagRule(r, key) {
950
+ ;
951
+ r._ruleName = key;
952
+ if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
1052
953
  }
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];
1063
- }
1064
- }
1065
- ranges.push({ lo, hi });
1066
- return { kind: "ranges", ranges };
954
+ function isDefinableRef(v) {
955
+ return !!v && typeof v === "object" && "_def" in v && v._def.tag === "lazy" && typeof v.define === "function";
1067
956
  }
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;
957
+ function ruleNameOf(r) {
958
+ return r._ruleName;
1073
959
  }
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;
960
+ function isNamedRuleRefForAnotherRule(r, key) {
961
+ const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
962
+ return name !== void 0 && name !== key;
1080
963
  }
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;
964
+ function rules(a, b) {
965
+ const factory = typeof a === "function" ? a : b;
966
+ const options = typeof a === "function" ? b : a;
967
+ const cache = {};
968
+ const proxy = new Proxy(cache, {
969
+ get(target, key) {
970
+ if (typeof key !== "string") return void 0;
971
+ const record = target;
972
+ if (!(key in record)) {
973
+ const r = ref();
974
+ tagRule(r, key);
975
+ record[key] = r;
1089
976
  }
977
+ return record[key];
978
+ }
979
+ });
980
+ const definitions = factory(proxy);
981
+ for (const key of Object.keys(definitions)) {
982
+ const placeholder = cache[key];
983
+ const parser2 = definitions[key];
984
+ if (placeholder === parser2) {
985
+ throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
986
+ }
987
+ if (isDefinableRef(placeholder)) {
988
+ if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
989
+ placeholder.define(parser2);
990
+ placeholder._meta.firstSet = parser2._meta.firstSet;
991
+ placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
992
+ } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
993
+ const alias = ref();
994
+ tagRule(alias, key);
995
+ alias.define(parser2);
996
+ alias._meta.firstSet = parser2._meta.firstSet;
997
+ alias._meta.canMatchNewline = parser2._meta.canMatchNewline;
998
+ cache[key] = alias;
999
+ } else {
1000
+ tagRule(parser2, key);
1001
+ cache[key] = parser2;
1090
1002
  }
1091
1003
  }
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
- }
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 });
1004
+ if (options?.trivia != null) {
1005
+ for (const key of Object.keys(definitions)) {
1006
+ const rule = cache[key];
1007
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
1121
1008
  }
1122
- } else {
1123
- const peeled = peelLabel(core);
1124
- if (!peeled) return null;
1125
- arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
1126
1009
  }
1127
- return { labels: arms.map((a) => a.label), arms, minRepeats };
1128
- }
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 };
1133
- }
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;
1144
- }
1010
+ if (options?.scanSkip != null) {
1011
+ for (const key of Object.keys(definitions)) {
1012
+ const rule = cache[key];
1013
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
1145
1014
  }
1146
- if (!matched) break;
1147
- chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
1148
- pos = matched.end;
1149
1015
  }
1150
- if (chunks.length < spec.minRepeats) {
1151
- return { end: cur, chunks: [] };
1016
+ if (options?.hostMode === "cst") {
1017
+ for (const key of Object.keys(definitions)) {
1018
+ const rule = cache[key];
1019
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
1020
+ }
1152
1021
  }
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 });
1169
- continue;
1022
+ if (options?.trackLines === true) {
1023
+ for (const key of Object.keys(definitions)) {
1024
+ const rule = cache[key];
1025
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
1170
1026
  }
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;
1027
+ for (const key of Object.keys(definitions)) {
1028
+ const rule = cache[key];
1029
+ if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
1030
+ const wrapped = parser({ trackLines: true }, rule);
1031
+ tagRule(wrapped, key);
1032
+ wrapped._meta.grammarTrackLines = true;
1033
+ cache[key] = wrapped;
1179
1034
  }
1180
- break;
1181
1035
  }
1182
- break;
1183
1036
  }
1184
- return { end: pos, chunks };
1185
- }
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("\\*");
1037
+ for (const key of Object.keys(definitions)) {
1038
+ markUnusedValues(cache[key]);
1039
+ }
1040
+ Object.defineProperty(cache, RULE_ORDER, {
1041
+ value: Object.keys(definitions),
1042
+ enumerable: false,
1043
+ configurable: true
1196
1044
  });
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);
1201
- }
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
- }
1213
- }
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;
1045
+ attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
1046
+ return cache;
1222
1047
  }
1223
1048
 
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
- }));
1049
+ // src/recovery/scan.ts
1050
+ function probeAt(sentinel, input, pos, ctx) {
1051
+ const s = {
1052
+ _probe: ctx._probe,
1053
+ _errors: ctx._errors,
1054
+ _cstLeaves: ctx._cstLeaves,
1055
+ _cstChildren: ctx._cstChildren,
1056
+ _cstRawChildren: ctx._cstRawChildren,
1057
+ _cstTriviaLog: ctx._cstTriviaLog,
1058
+ _triviaLog: ctx._triviaLog,
1059
+ _rootTriviaLog: ctx._rootTriviaLog,
1060
+ _fields: ctx._fields,
1061
+ _cstBuf: ctx._cstBuf,
1062
+ _tolerant: ctx._tolerant,
1063
+ _sync: ctx._sync
1064
+ };
1065
+ ctx._probe = void 0;
1066
+ ctx._errors = void 0;
1067
+ ctx._cstLeaves = void 0;
1068
+ ctx._cstChildren = void 0;
1069
+ ctx._cstRawChildren = void 0;
1070
+ ctx._cstTriviaLog = void 0;
1071
+ ctx._triviaLog = void 0;
1072
+ ctx._rootTriviaLog = void 0;
1073
+ ctx._fields = void 0;
1074
+ ctx._cstBuf = void 0;
1075
+ ctx._tolerant = void 0;
1076
+ ctx._sync = void 0;
1077
+ try {
1078
+ return sentinel.parse(input, pos, ctx).ok;
1079
+ } finally {
1080
+ ctx._probe = s._probe;
1081
+ ctx._errors = s._errors;
1082
+ ctx._cstLeaves = s._cstLeaves;
1083
+ ctx._cstChildren = s._cstChildren;
1084
+ ctx._cstRawChildren = s._cstRawChildren;
1085
+ ctx._cstTriviaLog = s._cstTriviaLog;
1086
+ ctx._triviaLog = s._triviaLog;
1087
+ ctx._rootTriviaLog = s._rootTriviaLog;
1088
+ ctx._fields = s._fields;
1089
+ ctx._cstBuf = s._cstBuf;
1090
+ ctx._tolerant = s._tolerant;
1091
+ ctx._sync = s._sync;
1234
1092
  }
1235
- return annotated;
1236
1093
  }
1237
- function createParseLineContext(input, pos) {
1238
- const lineIndex = createLineIndex();
1239
- if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
1240
- return { lineIndex, lineScannedTo: pos };
1094
+ function orSentinel(a, b) {
1095
+ if (b === void 0) return a;
1096
+ return {
1097
+ _tag: "orSentinel",
1098
+ _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
1099
+ _def: { tag: "unknown" },
1100
+ parse(input, pos, ctx) {
1101
+ const ra = a.parse(input, pos, ctx);
1102
+ if (ra.ok) return ra;
1103
+ return b.parse(input, pos, ctx);
1104
+ }
1105
+ };
1241
1106
  }
1242
- function parser(opts, root) {
1243
- const clearTrivia = opts.trivia === null;
1107
+ function firstSetSentinel(fs) {
1108
+ if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
1109
+ const ranges = fs.ranges;
1244
1110
  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);
1111
+ _tag: "firstSetSentinel",
1112
+ _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },
1113
+ _def: { tag: "unknown" },
1114
+ parse(input, pos) {
1115
+ const code = pos < input.length ? input.codePointAt(pos) : void 0;
1116
+ if (code !== void 0) {
1117
+ for (const r of ranges) {
1118
+ if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
1119
+ }
1283
1120
  }
1284
- return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
1121
+ return { ok: false, expected: [], span: { start: pos, end: pos } };
1285
1122
  }
1286
1123
  };
1287
1124
  }
1288
-
1289
- // src/combinators/parser.ts
1290
- var RULE_ORDER = "__parsemanRuleOrder";
1291
- function tagRule(r, key) {
1292
- ;
1293
- r._ruleName = key;
1294
- if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
1125
+ function recoverScan(input, from, ctx, sync, expected) {
1126
+ let scanPos = from;
1127
+ while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1128
+ const span = { start: from, end: scanPos };
1129
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
1130
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1131
+ ctx._errors?.push(error);
1132
+ return { error, end: scanPos };
1295
1133
  }
1296
- function isDefinableRef(v) {
1297
- return !!v && typeof v === "object" && "_def" in v && v._def.tag === "lazy" && typeof v.define === "function";
1134
+ function matchesAt(sentinel, input, pos, ctx) {
1135
+ return probeAt(sentinel, input, pos, ctx);
1298
1136
  }
1299
- function ruleNameOf(r) {
1300
- return r._ruleName;
1137
+ function captureError(ctx, error) {
1138
+ if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
1301
1139
  }
1302
- function isNamedRuleRefForAnotherRule(r, key) {
1303
- const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
1304
- return name !== void 0 && name !== key;
1140
+
1141
+ // src/combinators/expect.ts
1142
+ function deriveExpected(c) {
1143
+ return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
1305
1144
  }
1306
- function rules(a, b) {
1307
- const factory = typeof a === "function" ? a : b;
1308
- const options = typeof a === "function" ? b : a;
1309
- const cache = {};
1310
- const proxy = new Proxy(cache, {
1311
- get(target, key) {
1312
- if (typeof key !== "string") return void 0;
1313
- const record = target;
1314
- if (!(key in record)) {
1315
- const r = ref();
1316
- tagRule(r, key);
1317
- record[key] = r;
1145
+ function derive(c, seen) {
1146
+ const deriveExpected2 = (p) => derive(p, seen);
1147
+ const def = c._def;
1148
+ switch (def.tag) {
1149
+ case "literal":
1150
+ return [JSON.stringify(def.value)];
1151
+ case "regex":
1152
+ return [`/${def.source}/`];
1153
+ case "keywords":
1154
+ return def.words.map((w) => JSON.stringify(w));
1155
+ case "label":
1156
+ return [def.label];
1157
+ case "choice":
1158
+ return def.parsers.flatMap(deriveExpected2);
1159
+ case "dispatch":
1160
+ return deriveExpected2(def.selector);
1161
+ // Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
1162
+ // `optional(…)`/`many(…)` can match nothing, so the term after it is equally
1163
+ // able to fail first — deriving from term 0 alone named a token the parse
1164
+ // never actually required. A leading `not(…)` is zero-width and contributes
1165
+ // nothing (its own expectation is about what must NOT be here), but it is
1166
+ // nullable, so keep scanning past it. Stop at the first term that must match.
1167
+ case "sequence": {
1168
+ const out = [];
1169
+ for (const term of def.parsers) {
1170
+ if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
1171
+ if (!matchesEmpty(term)) break;
1318
1172
  }
1319
- return record[key];
1173
+ return out;
1320
1174
  }
1321
- });
1322
- const definitions = factory(proxy);
1323
- for (const key of Object.keys(definitions)) {
1324
- const placeholder = cache[key];
1325
- const parser2 = definitions[key];
1326
- if (placeholder === parser2) {
1327
- throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
1175
+ case "attempt":
1176
+ return deriveExpected2(def.parser);
1177
+ // Delegating wrappers whose first-token failure is their inner parser's — must
1178
+ // pass through so a start-failure (and the first-set-miss fast-path in attempt/
1179
+ // node, which reads this) names the real expected token, not the wrapper tag.
1180
+ case "field":
1181
+ case "withCtx":
1182
+ case "recover":
1183
+ return deriveExpected2(def.parser);
1184
+ case "skip":
1185
+ return deriveExpected2(def.main);
1186
+ case "expect":
1187
+ return def.expected;
1188
+ case "node":
1189
+ case "grammar":
1190
+ case "trivia":
1191
+ case "token":
1192
+ case "leaf":
1193
+ case "optional":
1194
+ case "many":
1195
+ case "oneOrMore":
1196
+ case "transform":
1197
+ case "not":
1198
+ case "peek":
1199
+ return deriveExpected2(def.parser);
1200
+ case "routed":
1201
+ return ["routed()"];
1202
+ case "lazy": {
1203
+ const name = c._ruleName;
1204
+ if (seen.has(c)) return name ? [name] : [];
1205
+ seen.add(c);
1206
+ try {
1207
+ return deriveExpected2(def.thunk());
1208
+ } catch {
1209
+ return name ? [name] : [];
1210
+ } finally {
1211
+ seen.delete(c);
1212
+ }
1328
1213
  }
1329
- if (isDefinableRef(placeholder)) {
1330
- if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
1331
- placeholder.define(parser2);
1332
- placeholder._meta.firstSet = parser2._meta.firstSet;
1333
- placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
1334
- } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
1335
- const alias = ref();
1336
- tagRule(alias, key);
1337
- alias.define(parser2);
1338
- alias._meta.firstSet = parser2._meta.firstSet;
1339
- alias._meta.canMatchNewline = parser2._meta.canMatchNewline;
1340
- cache[key] = alias;
1341
- } else {
1342
- tagRule(parser2, key);
1343
- cache[key] = parser2;
1344
- }
1345
- }
1346
- if (options?.trivia != null) {
1347
- for (const key of Object.keys(definitions)) {
1348
- const rule = cache[key];
1349
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
1350
- }
1351
- }
1352
- if (options?.scanSkip != null) {
1353
- for (const key of Object.keys(definitions)) {
1354
- const rule = cache[key];
1355
- if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
1356
- }
1357
- }
1358
- if (options?.hostMode === "cst") {
1359
- for (const key of Object.keys(definitions)) {
1360
- const rule = cache[key];
1361
- if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
1362
- }
1363
- }
1364
- if (options?.trackLines === true) {
1365
- for (const key of Object.keys(definitions)) {
1366
- const rule = cache[key];
1367
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
1368
- }
1369
- for (const key of Object.keys(definitions)) {
1370
- const rule = cache[key];
1371
- if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
1372
- const wrapped = parser({ trackLines: true }, rule);
1373
- tagRule(wrapped, key);
1374
- wrapped._meta.grammarTrackLines = true;
1375
- cache[key] = wrapped;
1376
- }
1377
- }
1378
- }
1379
- for (const key of Object.keys(definitions)) {
1380
- markUnusedValues(cache[key]);
1381
- }
1382
- Object.defineProperty(cache, RULE_ORDER, {
1383
- value: Object.keys(definitions),
1384
- enumerable: false,
1385
- configurable: true
1386
- });
1387
- return cache;
1388
- }
1389
-
1390
- // src/analysis/duplication.ts
1391
- var WORD_RE_SRC = "-?[A-Za-z][A-Za-z0-9_-]*";
1392
- var WORDS_RE = new RegExp(`^${WORD_RE_SRC}(?:\\|${WORD_RE_SRC})*$`);
1393
-
1394
- // src/combinators/probe.ts
1395
- function failAt(ctx, expected, pos) {
1396
- const r = { ok: false, expected, span: { start: pos, end: pos } };
1397
- const probe = ctx._probe;
1398
- if (probe !== void 0 && pos <= probe.offset) {
1399
- const best = probe.best;
1400
- if (best === null || pos > best.span.start) {
1401
- probe.best = r;
1402
- } else if (pos === best.span.start) {
1403
- probe.best = { ...best, expected: [...best.expected, ...expected] };
1404
- }
1405
- }
1406
- return r;
1407
- }
1408
-
1409
- // src/combinators/literal.ts
1410
- function asciiFoldEq(a, b) {
1411
- if (a.length !== b.length) return false;
1412
- for (let i = 0; i < a.length; i++) {
1413
- let ca = a.charCodeAt(i);
1414
- let cb = b.charCodeAt(i);
1415
- if (ca >= 65 && ca <= 90) ca += 32;
1416
- if (cb >= 65 && cb <= 90) cb += 32;
1417
- if (ca !== cb) return false;
1214
+ default:
1215
+ return [];
1418
1216
  }
1419
- return true;
1420
1217
  }
1421
- function literal(value, opts = {}) {
1422
- const caseInsensitive = opts.caseInsensitive ?? false;
1423
- const firstSet = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
1218
+ function expect(combinator, label2) {
1219
+ const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
1424
1220
  const meta = {
1425
- firstSet,
1426
- canMatchNewline: value.includes("\n"),
1221
+ firstSet: combinator._meta.firstSet,
1222
+ canMatchNewline: combinator._meta.canMatchNewline,
1427
1223
  isTrivia: false
1428
1224
  };
1429
- const canMatchNewline = meta.canMatchNewline;
1430
- if (caseInsensitive) {
1431
- const upper = value.toUpperCase();
1432
- const lower = value.toLowerCase();
1433
- const firstUpper = upper.codePointAt(0);
1434
- const firstLower = lower.codePointAt(0);
1435
- meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
1436
- { lo: firstLower, hi: firstLower },
1437
- { lo: firstUpper, hi: firstUpper }
1438
- ] } : firstSet;
1439
- }
1440
- const expected = [JSON.stringify(value)];
1441
- const parse = !caseInsensitive && value.length === 1 ? (() => {
1442
- const code = value.charCodeAt(0);
1443
- return function parse2(input, pos, ctx) {
1444
- if (input.charCodeAt(pos) === code) {
1445
- const span = { start: pos, end: pos + 1 };
1446
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1447
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
1448
- return { ok: true, value, span };
1449
- }
1450
- return failAt(ctx, expected, pos);
1451
- };
1452
- })() : function parse2(input, pos, ctx) {
1453
- const end = pos + value.length;
1454
- if (end > input.length) {
1455
- return failAt(ctx, expected, pos);
1456
- }
1457
- const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
1458
- if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
1459
- const span = { start: pos, end };
1460
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
1461
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
1462
- return { ok: true, value: matchedValue, span };
1463
- }
1464
- return failAt(ctx, expected, pos);
1465
- };
1466
1225
  return {
1467
- _tag: "literal",
1226
+ _tag: "expect",
1468
1227
  _meta: meta,
1469
- _def: { tag: "literal", value, caseInsensitive },
1470
- parse
1228
+ _def: { tag: "expect", parser: combinator, label: label2, expected },
1229
+ parse(input, pos, ctx) {
1230
+ const result = combinator.parse(input, pos, ctx);
1231
+ if (result.ok) return result;
1232
+ const span = { start: pos, end: pos };
1233
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1234
+ ctx._errors?.push(error);
1235
+ if (ctx._tolerant) captureError(ctx, error);
1236
+ return { ok: true, value: error, span: { start: pos, end: pos } };
1237
+ }
1471
1238
  };
1472
1239
  }
1473
1240
 
@@ -1539,420 +1306,839 @@ function parseClassRanges(body) {
1539
1306
  return ranges.length ? ranges : null;
1540
1307
  }
1541
1308
 
1542
- // src/combinators/regex.ts
1543
- var firstSetAnalyzer = null;
1544
- var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
1545
- var SCAN_BAIL_AT = 64;
1546
- function inRanges(cp, ranges) {
1547
- for (let i = 0; i < ranges.length; i++) {
1548
- const [lo, hi] = ranges[i];
1549
- if (cp >= lo && cp <= hi) return true;
1550
- }
1551
- return false;
1309
+ // src/combinators/trivia-skip.ts
1310
+ var NOOP_COMMIT = () => {
1311
+ };
1312
+ var fastTriviaCache = /* @__PURE__ */ new WeakMap();
1313
+ function needsDeferredTriviaCommit(ctx) {
1314
+ return ctx._triviaLog !== void 0 || ctx._rootTriviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
1552
1315
  }
1553
- function readClassRanges(body) {
1554
- if (body.startsWith("^")) return null;
1555
- return parseClassRanges(body);
1316
+ function saveTriviaMark(ctx) {
1317
+ const m = saveCstMark(ctx);
1318
+ return {
1319
+ raw: m.raw,
1320
+ tlog: m.tlog,
1321
+ leaves: m.leaves,
1322
+ fields: m.fields,
1323
+ errors: m.errors,
1324
+ log: ctx._triviaLog ? ctx._triviaLog.length : 0,
1325
+ rootLog: ctx._rootTriviaLog ? ctx._rootTriviaLog.length : 0
1326
+ };
1556
1327
  }
1557
- function shortScanner(source, flags) {
1558
- if (/[imsuvy]/.test(flags)) return null;
1559
- let ranges = null;
1560
- let quant = "";
1561
- if (source[0] === "[") {
1562
- let end = 1;
1563
- while (end < source.length && source[end] !== "]") {
1564
- if (source[end] === "\\") end += 2;
1565
- else end++;
1328
+ function rollbackTrivia(ctx, mark) {
1329
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1330
+ if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1331
+ if (ctx._rootTriviaLog && ctx._rootTriviaLog.length !== mark.rootLog) ctx._rootTriviaLog.length = mark.rootLog;
1332
+ }
1333
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1334
+ const probeCtx = {
1335
+ trackLines: ctx.trackLines,
1336
+ state: ctx.state,
1337
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1338
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1339
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1340
+ };
1341
+ const result = triviaP.parse(input, cur, probeCtx);
1342
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1343
+ return result;
1344
+ }
1345
+ function saveLookaheadMark(ctx) {
1346
+ return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1347
+ }
1348
+ function rollbackLookahead(ctx, mark) {
1349
+ rollbackTrivia(ctx, mark);
1350
+ if (ctx._probe) ctx._probe.best = mark.probeBest;
1351
+ }
1352
+ function scanWithLabels(input, cur, ctx) {
1353
+ const triviaP = ctx.trivia;
1354
+ const spec = analyzeLabeledTrivia(triviaP);
1355
+ if (!spec) return { end: cur, commit: NOOP_COMMIT };
1356
+ const log = ctx._triviaLog;
1357
+ const rootLog = ctx._rootTriviaLog;
1358
+ const rootKinds = ctx._rootTriviaKindIndex;
1359
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1360
+ const mask = ctx._triviaCaptureMask;
1361
+ let fullRows;
1362
+ let rootRows;
1363
+ let cstRows;
1364
+ const visitedEnd = visitLabeledTrivia(input, cur, spec, ctx.state, (start, matchEnd, kindIndex) => {
1365
+ if (log !== void 0) (fullRows ??= []).push(start, matchEnd, kindIndex);
1366
+ const selectedKind = rootLog === void 0 || rootKinds === void 0 || ctx._rootTriviaCapture === false ? void 0 : rootKinds[spec.labels[kindIndex] ?? ""];
1367
+ if (selectedKind !== void 0) (rootRows ??= []).push(start, matchEnd, selectedKind);
1368
+ if (captureTl && (mask === void 0 || (mask & 1 << kindIndex) !== 0)) {
1369
+ (cstRows ??= []).push(start, matchEnd, kindIndex);
1566
1370
  }
1567
- if (source[end] !== "]") return null;
1568
- ranges = readClassRanges(source.slice(1, end));
1569
- quant = source.slice(end + 1);
1570
- } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
1571
- ranges = shorthandRanges(source[1]);
1572
- quant = source.slice(2);
1371
+ });
1372
+ if (visitedEnd !== void 0) {
1373
+ if (visitedEnd === cur) return { end: cur, commit: NOOP_COMMIT };
1374
+ return {
1375
+ end: visitedEnd,
1376
+ commit: () => {
1377
+ if (fullRows !== void 0 && log !== void 0) log.push(...fullRows);
1378
+ if (rootRows !== void 0 && rootLog !== void 0) {
1379
+ for (let i = 0; i < rootRows.length; i += 3) {
1380
+ rootLog.push(cur, visitedEnd, rootRows[i], rootRows[i + 1], rootRows[i + 2]);
1381
+ }
1382
+ }
1383
+ if (cstRows !== void 0) {
1384
+ for (let i = 0; i < cstRows.length; i += 3) {
1385
+ pushCstTriviaEntry(ctx, cstRows[i], cstRows[i + 1], cstRows[i + 2]);
1386
+ }
1387
+ }
1388
+ }
1389
+ };
1573
1390
  }
1574
- if (!ranges || quant !== "+" && quant !== "*") return null;
1575
- const minOne = quant === "+";
1576
- return (input, pos) => {
1577
- let end = pos;
1578
- while (end < input.length && inRanges(input.charCodeAt(end), ranges)) {
1579
- end++;
1580
- if (end - pos >= SCAN_BAIL_AT) return void 0;
1581
- }
1582
- return minOne && end === pos ? null : end;
1391
+ const { end, chunks } = scanLabeledTriviaChunks(input, cur, spec, ctx.state);
1392
+ if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1393
+ return {
1394
+ end,
1395
+ commit: () => recordTriviaChunks(ctx, chunks)
1583
1396
  };
1584
1397
  }
1585
- function asciiCaseFold(fs) {
1586
- if (fs.kind !== "ranges") return fs;
1587
- const ranges = fs.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
1588
- const add = (lo, hi) => {
1589
- if (lo <= hi) ranges.push({ lo, hi });
1590
- };
1591
- for (const r of fs.ranges) {
1592
- const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
1593
- add(uLo + 32, uHi + 32);
1594
- const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
1595
- add(lLo - 32, lHi - 32);
1596
- }
1597
- ranges.sort((a, b) => a.lo - b.lo);
1598
- const merged = [];
1599
- for (const r of ranges) {
1600
- const last = merged[merged.length - 1];
1601
- if (last && r.lo <= last.hi + 1) last.hi = Math.max(last.hi, r.hi);
1602
- else merged.push({ lo: r.lo, hi: r.hi });
1603
- }
1604
- return { kind: "ranges", ranges: merged };
1398
+ function skipWithLabels(input, cur, ctx) {
1399
+ const spec = analyzeLabeledTrivia(ctx.trivia);
1400
+ if (spec) return scanLabeledTriviaEnd(input, cur, spec, ctx.state);
1401
+ const tr = parseTriviaNoCapture(ctx.trivia, input, cur, ctx);
1402
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1605
1403
  }
1606
- function regex(pattern, flags = "") {
1607
- const source = typeof pattern === "string" ? pattern : pattern.source;
1608
- const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
1609
- const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
1610
- const scan = shortScanner(source, resolvedFlags);
1611
- const raw = (firstSetAnalyzer ?? permissiveFirstSet)(source);
1612
- const firstSet = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
1613
- const canMatchNewline = raw.canMatchNewline;
1614
- const meta = { firstSet, canMatchNewline, isTrivia: false };
1404
+ function advanceTrivia(input, cur, ctx) {
1405
+ const triviaP = ctx.trivia;
1406
+ if (!triviaP) return cur;
1407
+ if (!ctx.trackLines) {
1408
+ const fast2 = fastTriviaScanner(triviaP);
1409
+ if (fast2) return fast2(input, cur);
1410
+ if (ctx.triviaKindLabels) return skipWithLabels(input, cur, ctx);
1411
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1412
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1413
+ }
1414
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1415
+ const fast = fastTriviaScanner(triviaP);
1416
+ if (fast) {
1417
+ const end = fast(input, cur);
1418
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1419
+ return end;
1420
+ }
1421
+ if (ctx.triviaKindLabels) {
1422
+ const end = skipWithLabels(input, cur, ctx);
1423
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1424
+ return end;
1425
+ }
1426
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1427
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1428
+ }
1429
+ function scanTrivia(input, cur, ctx) {
1430
+ const triviaP = ctx.trivia;
1431
+ if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1432
+ const log = ctx._triviaLog;
1433
+ const rootLog = ctx._rootTriviaLog;
1434
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1435
+ if (!ctx.trackLines) {
1436
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1437
+ if (fast2 && log === void 0 && !captureTl) {
1438
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1439
+ }
1440
+ if (ctx.triviaKindLabels && (log !== void 0 || rootLog !== void 0 || captureTl)) {
1441
+ return scanWithLabels(input, cur, ctx);
1442
+ }
1443
+ if (ctx.triviaKindLabels) return { end: skipWithLabels(input, cur, ctx), commit: NOOP_COMMIT };
1444
+ if (log !== void 0 || rootLog !== void 0 || captureTl) {
1445
+ const tr3 = triviaP.parse(input, cur, {
1446
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1447
+ state: ctx.state
1448
+ });
1449
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1450
+ const end = tr3.span.end;
1451
+ return {
1452
+ end,
1453
+ commit: () => {
1454
+ pushTriviaLogEntry(ctx, cur, end);
1455
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1456
+ }
1457
+ };
1458
+ }
1459
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1460
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1461
+ }
1462
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1463
+ const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1464
+ if (fast && log === void 0 && !captureTl) {
1465
+ const end = fast(input, cur);
1466
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1467
+ return { end, commit: NOOP_COMMIT };
1468
+ }
1469
+ if (ctx.triviaKindLabels && (log !== void 0 || rootLog !== void 0 || captureTl)) {
1470
+ const scan = scanWithLabels(input, cur, ctx);
1471
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1472
+ return scan;
1473
+ }
1474
+ if (ctx.triviaKindLabels) {
1475
+ const end = skipWithLabels(input, cur, ctx);
1476
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1477
+ return { end, commit: NOOP_COMMIT };
1478
+ }
1479
+ if (log !== void 0 || rootLog !== void 0 || captureTl) {
1480
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1481
+ if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1482
+ const end = tr2.span.end;
1483
+ return {
1484
+ end,
1485
+ commit: () => {
1486
+ pushTriviaLogEntry(ctx, cur, end);
1487
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1488
+ }
1489
+ };
1490
+ }
1491
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1492
+ return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1493
+ }
1494
+ function consumeTrivia(input, cur, ctx) {
1495
+ if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1496
+ const scan = scanTrivia(input, cur, ctx);
1497
+ scan.commit();
1498
+ return scan.end;
1499
+ }
1500
+ function fastTriviaScanner(trivia2) {
1501
+ const cached = fastTriviaCache.get(trivia2);
1502
+ if (cached !== void 0) return cached;
1503
+ const scanner = buildFastTriviaScanner(trivia2);
1504
+ fastTriviaCache.set(trivia2, scanner);
1505
+ return scanner;
1506
+ }
1507
+ function buildFastTriviaScanner(trivia2) {
1508
+ const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1509
+ const direct = regexTriviaScanner(core);
1510
+ if (direct) return direct;
1511
+ const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1512
+ if (!repeat) return null;
1513
+ const one = regexTriviaScanner(repeat);
1514
+ if (one) return loopScanner([one]);
1515
+ if (repeat._def.tag !== "choice") return null;
1516
+ const arms = repeat._def.parsers.map(regexTriviaScanner);
1517
+ if (arms.some((s) => s === null)) return null;
1518
+ return loopScanner(arms);
1519
+ }
1520
+ function loopScanner(arms) {
1521
+ return (input, cur) => {
1522
+ let pos = cur;
1523
+ scan: while (pos < input.length) {
1524
+ for (const arm of arms) {
1525
+ const end = arm(input, pos);
1526
+ if (end > pos) {
1527
+ pos = end;
1528
+ continue scan;
1529
+ }
1530
+ }
1531
+ break;
1532
+ }
1533
+ return pos;
1534
+ };
1535
+ }
1536
+ function regexTriviaScanner(parser2) {
1537
+ if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1538
+ const source = parser2._def.source;
1539
+ return classRunSource(source) ?? altStarSource(source);
1540
+ }
1541
+ function inRanges(cp, ranges) {
1542
+ for (let i = 0; i < ranges.length; i++) {
1543
+ const r = ranges[i];
1544
+ if (cp >= r[0] && cp <= r[1]) return true;
1545
+ }
1546
+ return false;
1547
+ }
1548
+ function classScanner(classBody) {
1549
+ const ranges = parseClassRanges(classBody);
1550
+ if (!ranges) return null;
1551
+ return (input, cur) => {
1552
+ let pos = cur;
1553
+ while (pos < input.length && inRanges(input.charCodeAt(pos), ranges)) pos++;
1554
+ return pos;
1555
+ };
1556
+ }
1557
+ function untilLineBreakScanner(leaderCode) {
1558
+ return (input, cur) => {
1559
+ if (input.charCodeAt(cur) !== leaderCode) return cur;
1560
+ let pos = cur + 1;
1561
+ while (pos < input.length) {
1562
+ const cc = input.charCodeAt(pos);
1563
+ if (cc === 10 || cc === 13) break;
1564
+ pos++;
1565
+ }
1566
+ return pos;
1567
+ };
1568
+ }
1569
+ function classRunSource(source) {
1570
+ const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1571
+ return m ? classScanner(m[1]) : null;
1572
+ }
1573
+ function classifyTriviaArm(arm) {
1574
+ const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1575
+ if (cls) {
1576
+ const ranges = parseClassRanges(cls[1]);
1577
+ return ranges ? { kind: "class", ranges } : null;
1578
+ }
1579
+ const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1580
+ if (lc) {
1581
+ const marker = lc[1];
1582
+ return { kind: "untilLineBreak", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
1583
+ }
1584
+ return null;
1585
+ }
1586
+ function armScanner(arm) {
1587
+ if (arm.kind === "untilLineBreak") return untilLineBreakScanner(arm.code);
1588
+ const ranges = arm.ranges;
1589
+ return (input, cur) => {
1590
+ let pos = cur;
1591
+ while (pos < input.length && inRanges(input.charCodeAt(pos), ranges)) pos++;
1592
+ return pos;
1593
+ };
1594
+ }
1595
+ function fusedTriviaScanner(ranges, leaders) {
1596
+ const c0 = leaders[0];
1597
+ const single = leaders.length === 1;
1598
+ return (input, cur) => {
1599
+ let pos = cur;
1600
+ const len = input.length;
1601
+ for (; ; ) {
1602
+ const c = input.charCodeAt(pos);
1603
+ if (pos < len && inRanges(c, ranges)) {
1604
+ pos++;
1605
+ continue;
1606
+ }
1607
+ if (single ? c === c0 : leaders.includes(c)) {
1608
+ pos++;
1609
+ while (pos < len) {
1610
+ const cc = input.charCodeAt(pos);
1611
+ if (cc === 10 || cc === 13) break;
1612
+ pos++;
1613
+ }
1614
+ continue;
1615
+ }
1616
+ break;
1617
+ }
1618
+ return pos;
1619
+ };
1620
+ }
1621
+ function splitTopLevelAlts(body) {
1622
+ const arms = [];
1623
+ let inClass = false;
1624
+ let start = 0;
1625
+ for (let i = 0; i < body.length; i++) {
1626
+ const c = body[i];
1627
+ if (c === "\\") {
1628
+ i++;
1629
+ continue;
1630
+ }
1631
+ if (inClass) {
1632
+ if (c === "]") inClass = false;
1633
+ continue;
1634
+ }
1635
+ if (c === "[") inClass = true;
1636
+ else if (c === "(") return null;
1637
+ else if (c === "|") {
1638
+ arms.push(body.slice(start, i));
1639
+ start = i + 1;
1640
+ }
1641
+ }
1642
+ arms.push(body.slice(start));
1643
+ return arms;
1644
+ }
1645
+ function altStarSource(source) {
1646
+ const m = /^\(\?:(.*)\)[*+]$/.exec(source);
1647
+ if (!m) return null;
1648
+ const armSrcs = splitTopLevelAlts(m[1]);
1649
+ if (!armSrcs || armSrcs.length < 2) return null;
1650
+ const arms = [];
1651
+ for (const src of armSrcs) {
1652
+ const arm = classifyTriviaArm(src);
1653
+ if (!arm) return null;
1654
+ arms.push(arm);
1655
+ }
1656
+ const ranges = [];
1657
+ const leaders = [];
1658
+ for (const arm of arms) {
1659
+ if (arm.kind === "class") ranges.push(...arm.ranges);
1660
+ else leaders.push(arm.code);
1661
+ }
1662
+ if (leaders.some((code) => inRanges(code, ranges))) {
1663
+ return loopScanner(arms.map(armScanner));
1664
+ }
1665
+ if (leaders.length === 0) return armScanner({ kind: "class", ranges });
1666
+ return fusedTriviaScanner(ranges, leaders);
1667
+ }
1668
+
1669
+ // src/combinators/choice.ts
1670
+ function choice(...args) {
1671
+ const parsers = args.map((a) => "gate" in a ? a.combinator : a);
1672
+ const gates = args.map((a) => "gate" in a ? a.gate : null);
1673
+ const hasGates = gates.some((g) => g !== null);
1674
+ const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
1675
+ let combined = { kind: "empty" };
1676
+ for (const p of parsers) combined = union(combined, p._meta.firstSet);
1677
+ const meta = {
1678
+ firstSet: combined,
1679
+ canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
1680
+ isTrivia: false,
1681
+ disjoint
1682
+ };
1683
+ const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
1684
+ const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
1685
+ let greedyLitMap = null;
1686
+ let sortedParsers = null;
1687
+ const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
1688
+ if (strategy?.tag === "greedyClassify") {
1689
+ greedyLitMap = /* @__PURE__ */ new Map();
1690
+ for (let i = 0; i < parsers.length; i++) {
1691
+ if (i === strategy.superIndex) continue;
1692
+ const litVal = getCoreLiteralValue(parsers[i]);
1693
+ if (litVal !== null) greedyLitMap.set(litVal, i);
1694
+ }
1695
+ } else if (strategy?.tag === "literalsLongestFirst") {
1696
+ sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
1697
+ }
1615
1698
  return {
1616
- _tag: "regex",
1699
+ _tag: "choice",
1617
1700
  _meta: meta,
1618
- _def: { tag: "regex", source, flags: resolvedFlags },
1701
+ _def: {
1702
+ tag: "choice",
1703
+ parsers,
1704
+ gates,
1705
+ disjoint,
1706
+ strategy: strategy ?? { tag: "firstMatch" },
1707
+ autoNot
1708
+ },
1619
1709
  parse(input, pos, ctx) {
1620
- const scanEnd = scan?.(input, pos);
1621
- if (scanEnd !== void 0) {
1622
- if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
1623
- const value = input.slice(pos, scanEnd);
1624
- const span2 = { start: pos, end: scanEnd };
1625
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
1626
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
1627
- return { ok: true, value, span: span2 };
1710
+ const expected = [];
1711
+ if (disjoint && pos < input.length) {
1712
+ const code = input.codePointAt(pos);
1713
+ let idx = code < 128 ? asciiDispatch[code] : -1;
1714
+ if (idx < 0) {
1715
+ for (let i = 0; i < parsers.length; i++) {
1716
+ if (inFirstSet(code, parsers[i]._meta.firstSet)) {
1717
+ idx = i;
1718
+ break;
1719
+ }
1720
+ }
1721
+ }
1722
+ if (idx >= 0) {
1723
+ const gate = gates[idx];
1724
+ if (gate && !gate(ctx.state)) {
1725
+ return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
1726
+ }
1727
+ const result = parsers[idx].parse(input, pos, ctx);
1728
+ if (result.ok) return result;
1729
+ expected.push(...result.expected);
1730
+ if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
1731
+ return { ok: false, expected, span: { start: pos, end: pos } };
1732
+ }
1733
+ return {
1734
+ ok: false,
1735
+ expected: parsers.flatMap((p) => {
1736
+ const r = p.parse(input, pos, ctx);
1737
+ return r.ok ? [] : r.expected;
1738
+ }),
1739
+ span: { start: pos, end: pos }
1740
+ };
1741
+ }
1742
+ if (strategy?.tag === "greedyClassify") {
1743
+ const superResult = parsers[strategy.superIndex].parse(input, pos, ctx);
1744
+ if (!superResult.ok) return superResult;
1745
+ const end = superResult.span.end;
1746
+ const litIdx = greedyLitMap.get(input.slice(pos, end));
1747
+ if (litIdx !== void 0) {
1748
+ const litVal = getCoreLiteralValue(parsers[litIdx]);
1749
+ const value = applyTransforms(parsers[litIdx], litVal, { start: pos, end });
1750
+ return { ok: true, value, span: { start: pos, end } };
1751
+ }
1752
+ return superResult;
1753
+ }
1754
+ if (strategy?.tag === "literalsLongestFirst") {
1755
+ for (const p of sortedParsers) {
1756
+ const r = p.parse(input, pos, ctx);
1757
+ if (r.ok) return r;
1758
+ expected.push(...r.expected);
1759
+ }
1760
+ return { ok: false, expected, span: { start: pos, end: pos } };
1761
+ }
1762
+ for (let i = 0; i < parsers.length; i++) {
1763
+ if (gates[i] && !gates[i](ctx.state)) continue;
1764
+ const mark = saveTriviaMark(ctx);
1765
+ const result = parsers[i].parse(input, pos, ctx);
1766
+ if (!result.ok) {
1767
+ rollbackTrivia(ctx, mark);
1768
+ expected.push(...result.expected);
1769
+ if (result.committed) return { ok: false, expected, span: result.span, committed: true };
1770
+ continue;
1771
+ }
1772
+ const checks = autoNot[i];
1773
+ if (checks && autoNotFires(input, result.span.end, checks)) {
1774
+ rollbackTrivia(ctx, mark);
1775
+ continue;
1776
+ }
1777
+ return result;
1778
+ }
1779
+ return { ok: false, expected, span: { start: pos, end: pos } };
1780
+ }
1781
+ };
1782
+ }
1783
+ function detectStrategy(parsers) {
1784
+ const regexIndices = [];
1785
+ const literalIndices = [];
1786
+ for (let i = 0; i < parsers.length; i++) {
1787
+ if (getCoreRegexDef(parsers[i]) !== null) regexIndices.push(i);
1788
+ else if (getCoreLiteralValue(parsers[i]) !== null) literalIndices.push(i);
1789
+ }
1790
+ if (regexIndices.length === 1 && literalIndices.length === parsers.length - 1 && literalIndices.length > 0) {
1791
+ const superIndex = regexIndices[0];
1792
+ const regexDef = getCoreRegexDef(parsers[superIndex]);
1793
+ const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
1794
+ const re = new RegExp(regexDef.source, flags);
1795
+ const allSubsumed = literalIndices.every((i) => {
1796
+ const litVal = getCoreLiteralValue(parsers[i]);
1797
+ re.lastIndex = 0;
1798
+ const m = re.exec(litVal);
1799
+ return m !== null && m[0] === litVal;
1800
+ });
1801
+ if (allSubsumed) return { tag: "greedyClassify", superIndex };
1802
+ }
1803
+ if (parsers.length === literalIndices.length) {
1804
+ const sortedIndices = [...literalIndices].sort(
1805
+ (a, b) => getCoreLiteralValue(parsers[b]).length - getCoreLiteralValue(parsers[a]).length
1806
+ );
1807
+ return { tag: "literalsLongestFirst", sortedIndices };
1808
+ }
1809
+ const shared = detectSharedPrefix(parsers);
1810
+ if (shared !== null) return shared;
1811
+ return { tag: "firstMatch" };
1812
+ }
1813
+ function bareLeadingTermKey(term) {
1814
+ const d = term._def;
1815
+ if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
1816
+ if (d.tag === "regex") return `R:${d.source}\0${d.flags}`;
1817
+ return null;
1818
+ }
1819
+ function detectSharedPrefix(parsers) {
1820
+ if (parsers.length < 2) return null;
1821
+ let key = null;
1822
+ let prefix = null;
1823
+ const members = [];
1824
+ for (let i = 0; i < parsers.length; i++) {
1825
+ const term0 = leadingTermOfArm(parsers[i]);
1826
+ if (term0 === null) return null;
1827
+ const k = bareLeadingTermKey(term0);
1828
+ if (k === null) return null;
1829
+ if (key === null) {
1830
+ key = k;
1831
+ prefix = term0;
1832
+ } else if (k !== key) return null;
1833
+ members.push(i);
1834
+ }
1835
+ if (prefix === null || members.length < 2) return null;
1836
+ return { tag: "sharedPrefix", prefix, members };
1837
+ }
1838
+ function leadingTermOfArm(arm) {
1839
+ let d = arm._def;
1840
+ for (; ; ) {
1841
+ if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
1842
+ d = d.parser._def;
1843
+ continue;
1844
+ }
1845
+ break;
1846
+ }
1847
+ if (d.tag !== "sequence" || d.parsers.length < 2) return null;
1848
+ return d.parsers[0];
1849
+ }
1850
+ function computeAutoNot(parsers) {
1851
+ return parsers.map((p, i) => {
1852
+ const litVal = getCoreLiteralValue(p);
1853
+ if (litVal === null) return null;
1854
+ const checks = [];
1855
+ for (let j2 = i + 1; j2 < parsers.length; j2++) {
1856
+ const other = parsers[j2];
1857
+ const otherLit = getCoreLiteralValue(other);
1858
+ if (otherLit !== null && otherLit.startsWith(litVal) && otherLit.length > litVal.length) {
1859
+ checks.push({ kind: "startsWith", value: otherLit.slice(litVal.length) });
1860
+ continue;
1628
1861
  }
1629
- anchored.lastIndex = pos;
1630
- const m = anchored.exec(input);
1631
- if (m === null) {
1632
- return failAt(ctx, [`/${source}/`], pos);
1862
+ const regexDef = getCoreRegexDef(other);
1863
+ if (regexDef !== null) {
1864
+ const contSet = continuationFirstSet(litVal, regexDef.source, regexDef.flags);
1865
+ if (contSet !== null) checks.push({ kind: "firstSet", set: contSet });
1633
1866
  }
1634
- const span = { start: pos, end: pos + m[0].length };
1635
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1636
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
1637
- return { ok: true, value: m[0], span };
1638
1867
  }
1639
- };
1868
+ return checks.length > 0 ? checks : null;
1869
+ });
1640
1870
  }
1641
-
1642
- // src/combinators/trivia-skip.ts
1643
- var NOOP_COMMIT = () => {
1644
- };
1645
- var fastTriviaCache = /* @__PURE__ */ new WeakMap();
1646
- function needsDeferredTriviaCommit(ctx) {
1647
- return ctx._triviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
1871
+ function autoNotFires(input, end, checks) {
1872
+ for (const check of checks) {
1873
+ if (check.kind === "firstSet") {
1874
+ const code = end < input.length ? input.codePointAt(end) ?? -1 : -1;
1875
+ if (inFirstSet(code, check.set)) return true;
1876
+ } else {
1877
+ if (input.startsWith(check.value, end)) return true;
1878
+ }
1879
+ }
1880
+ return false;
1648
1881
  }
1649
- function saveTriviaMark(ctx) {
1650
- const m = saveCstMark(ctx);
1651
- return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, errors: m.errors, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1882
+ function getCoreLiteralValue(p) {
1883
+ const def = p._def;
1884
+ if (def.tag === "literal" && !def.caseInsensitive) return def.value;
1885
+ if (def.tag === "transform") return getCoreLiteralValue(def.parser);
1886
+ return null;
1652
1887
  }
1653
- function rollbackTrivia(ctx, mark) {
1654
- rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1655
- if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1888
+ function getCoreRegexDef(p) {
1889
+ const def = p._def;
1890
+ if (def.tag === "regex") return { source: def.source, flags: def.flags };
1891
+ if (def.tag === "transform") return getCoreRegexDef(def.parser);
1892
+ if (def.tag === "label") return getCoreRegexDef(def.parser);
1893
+ return null;
1656
1894
  }
1657
- function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1658
- const probeCtx = {
1659
- trackLines: ctx.trackLines,
1660
- state: ctx.state,
1661
- ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1662
- ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1663
- ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1664
- };
1665
- const result = triviaP.parse(input, cur, probeCtx);
1666
- ctx._lineScannedTo = probeCtx._lineScannedTo;
1667
- return result;
1895
+ function applyTransforms(p, value, span) {
1896
+ const def = p._def;
1897
+ if (def.tag === "transform") {
1898
+ const inner = applyTransforms(def.parser, value, span);
1899
+ return def.fn(inner, span);
1900
+ }
1901
+ return value;
1668
1902
  }
1669
- function saveLookaheadMark(ctx) {
1670
- return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1903
+ function continuationFirstSet(lit, source, flags) {
1904
+ const re = new RegExp(source, "y" + flags.replace(/[gy]/g, ""));
1905
+ re.lastIndex = 0;
1906
+ const base = re.exec(lit);
1907
+ if (!base || base[0] !== lit) return null;
1908
+ const contCodes = [];
1909
+ for (let code = 1; code < 128; code++) {
1910
+ re.lastIndex = 0;
1911
+ const m = re.exec(lit + String.fromCharCode(code));
1912
+ if (m && m[0].length > lit.length) contCodes.push(code);
1913
+ }
1914
+ if (contCodes.length === 0) return null;
1915
+ return codesToFirstSet(contCodes);
1671
1916
  }
1672
- function rollbackLookahead(ctx, mark) {
1673
- rollbackTrivia(ctx, mark);
1674
- if (ctx._probe) ctx._probe.best = mark.probeBest;
1917
+ function codesToFirstSet(codes) {
1918
+ codes.sort((a, b) => a - b);
1919
+ const ranges = [];
1920
+ let lo = codes[0], hi = codes[0];
1921
+ for (let i = 1; i < codes.length; i++) {
1922
+ if (codes[i] === hi + 1) {
1923
+ hi = codes[i];
1924
+ } else {
1925
+ ranges.push({ lo, hi });
1926
+ lo = hi = codes[i];
1927
+ }
1928
+ }
1929
+ ranges.push({ lo, hi });
1930
+ return { kind: "ranges", ranges };
1675
1931
  }
1676
- function scanWithLabels(input, cur, ctx) {
1677
- const triviaP = ctx.trivia;
1678
- const spec = analyzeLabeledTrivia(triviaP);
1679
- if (!spec) return { end: cur, commit: NOOP_COMMIT };
1680
- const fast = tryFastLabeledScan(input, cur, triviaP);
1681
- const { end, chunks } = fast ?? scanLabeledTriviaChunks(input, cur, spec);
1682
- if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1683
- return {
1684
- end,
1685
- commit: () => recordTriviaChunks(ctx, chunks)
1686
- };
1932
+ function inFirstSet(code, fs) {
1933
+ if (fs.kind === "any") return true;
1934
+ if (fs.kind === "empty") return false;
1935
+ for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
1936
+ return false;
1687
1937
  }
1688
- function advanceTrivia(input, cur, ctx) {
1689
- const triviaP = ctx.trivia;
1690
- if (!triviaP) return cur;
1691
- if (!ctx.trackLines) {
1692
- const fast2 = fastTriviaScanner(triviaP);
1693
- if (fast2) return fast2(input, cur);
1694
- if (ctx.triviaKindLabels) {
1695
- const scan = scanWithLabels(input, cur, ctx);
1696
- return scan.end;
1938
+ function areDisjoint(sets) {
1939
+ if (sets.some((s) => s.kind === "any")) return false;
1940
+ for (let i = 0; i < sets.length; i++)
1941
+ for (let j2 = i + 1; j2 < sets.length; j2++)
1942
+ if (intersects(sets[i], sets[j2])) return false;
1943
+ return true;
1944
+ }
1945
+ function buildAsciiDispatch(parsers) {
1946
+ const table = Array(128).fill(-1);
1947
+ for (let i = 0; i < parsers.length; i++) {
1948
+ const fs = parsers[i]._meta.firstSet;
1949
+ if (fs.kind !== "ranges") continue;
1950
+ for (const { lo, hi } of fs.ranges) {
1951
+ for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
1952
+ table[code] = i;
1953
+ }
1697
1954
  }
1698
- const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1699
- return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1700
- }
1701
- const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1702
- const fast = fastTriviaScanner(triviaP);
1703
- if (fast) {
1704
- const end = fast(input, cur);
1705
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1706
- return end;
1707
- }
1708
- if (ctx.triviaKindLabels) {
1709
- const scan = scanWithLabels(input, cur, ctx);
1710
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1711
- return scan.end;
1712
1955
  }
1713
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1714
- return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1956
+ return table;
1715
1957
  }
1716
- function scanTrivia(input, cur, ctx) {
1717
- const triviaP = ctx.trivia;
1718
- if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1719
- const log = ctx._triviaLog;
1720
- const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1721
- if (!ctx.trackLines) {
1722
- const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1723
- if (fast2 && log === void 0 && !captureTl) {
1724
- return { end: fast2(input, cur), commit: NOOP_COMMIT };
1725
- }
1726
- if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1727
- return scanWithLabels(input, cur, ctx);
1728
- }
1729
- if (log !== void 0 || captureTl) {
1730
- const tr3 = triviaP.parse(input, cur, {
1731
- trackLines: log !== void 0 ? false : ctx.trackLines,
1732
- state: ctx.state
1733
- });
1734
- if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1735
- const end = tr3.span.end;
1736
- return {
1737
- end,
1738
- commit: () => {
1739
- pushTriviaLogEntry(ctx, cur, end);
1740
- if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1741
- }
1742
- };
1958
+
1959
+ // src/analysis/duplication.ts
1960
+ var WORD_RE_SRC = "-?[A-Za-z][A-Za-z0-9_-]*";
1961
+ var WORDS_RE = new RegExp(`^${WORD_RE_SRC}(?:\\|${WORD_RE_SRC})*$`);
1962
+
1963
+ // src/combinators/probe.ts
1964
+ function failAt(ctx, expected, pos) {
1965
+ const r = { ok: false, expected, span: { start: pos, end: pos } };
1966
+ const probe = ctx._probe;
1967
+ if (probe !== void 0 && pos <= probe.offset) {
1968
+ const best = probe.best;
1969
+ if (best === null || pos > best.span.start) {
1970
+ probe.best = r;
1971
+ } else if (pos === best.span.start) {
1972
+ probe.best = { ...best, expected: [...best.expected, ...expected] };
1743
1973
  }
1744
- const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1745
- return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1746
- }
1747
- const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1748
- const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1749
- if (fast && log === void 0 && !captureTl) {
1750
- const end = fast(input, cur);
1751
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1752
- return { end, commit: NOOP_COMMIT };
1753
- }
1754
- if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1755
- const scan = scanWithLabels(input, cur, ctx);
1756
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1757
- return scan;
1758
- }
1759
- if (log !== void 0 || captureTl) {
1760
- const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1761
- if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1762
- const end = tr2.span.end;
1763
- return {
1764
- end,
1765
- commit: () => {
1766
- pushTriviaLogEntry(ctx, cur, end);
1767
- if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1768
- }
1769
- };
1770
1974
  }
1771
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1772
- return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1773
- }
1774
- function consumeTrivia(input, cur, ctx) {
1775
- if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1776
- const scan = scanTrivia(input, cur, ctx);
1777
- scan.commit();
1778
- return scan.end;
1779
- }
1780
- function fastTriviaScanner(trivia2) {
1781
- const cached = fastTriviaCache.get(trivia2);
1782
- if (cached !== void 0) return cached;
1783
- const scanner = buildFastTriviaScanner(trivia2);
1784
- fastTriviaCache.set(trivia2, scanner);
1785
- return scanner;
1975
+ return r;
1786
1976
  }
1787
- function buildFastTriviaScanner(trivia2) {
1788
- const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1789
- const direct = regexTriviaScanner(core);
1790
- if (direct) return direct;
1791
- const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1792
- if (!repeat) return null;
1793
- const one = regexTriviaScanner(repeat);
1794
- if (one) return loopScanner([one]);
1795
- if (repeat._def.tag !== "choice") return null;
1796
- const arms = repeat._def.parsers.map(regexTriviaScanner);
1797
- if (arms.some((s) => s === null)) return null;
1798
- return loopScanner(arms);
1977
+
1978
+ // src/combinators/literal.ts
1979
+ function asciiFoldEq(a, b) {
1980
+ if (a.length !== b.length) return false;
1981
+ for (let i = 0; i < a.length; i++) {
1982
+ let ca = a.charCodeAt(i);
1983
+ let cb = b.charCodeAt(i);
1984
+ if (ca >= 65 && ca <= 90) ca += 32;
1985
+ if (cb >= 65 && cb <= 90) cb += 32;
1986
+ if (ca !== cb) return false;
1987
+ }
1988
+ return true;
1799
1989
  }
1800
- function loopScanner(arms) {
1801
- return (input, cur) => {
1802
- let pos = cur;
1803
- scan: while (pos < input.length) {
1804
- for (const arm of arms) {
1805
- const end = arm(input, pos);
1806
- if (end > pos) {
1807
- pos = end;
1808
- continue scan;
1809
- }
1990
+ function literal(value, opts = {}) {
1991
+ const caseInsensitive = opts.caseInsensitive ?? false;
1992
+ const firstSet = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
1993
+ const meta = {
1994
+ firstSet,
1995
+ canMatchNewline: value.includes("\n"),
1996
+ isTrivia: false
1997
+ };
1998
+ const canMatchNewline = meta.canMatchNewline;
1999
+ if (caseInsensitive) {
2000
+ const upper = value.toUpperCase();
2001
+ const lower = value.toLowerCase();
2002
+ const firstUpper = upper.codePointAt(0);
2003
+ const firstLower = lower.codePointAt(0);
2004
+ meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
2005
+ { lo: firstLower, hi: firstLower },
2006
+ { lo: firstUpper, hi: firstUpper }
2007
+ ] } : firstSet;
2008
+ }
2009
+ const expected = [JSON.stringify(value)];
2010
+ const parse = !caseInsensitive && value.length === 1 ? (() => {
2011
+ const code = value.charCodeAt(0);
2012
+ return function parse2(input, pos, ctx) {
2013
+ if (input.charCodeAt(pos) === code) {
2014
+ const span = { start: pos, end: pos + 1 };
2015
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
2016
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
2017
+ return { ok: true, value, span };
1810
2018
  }
1811
- break;
2019
+ return failAt(ctx, expected, pos);
2020
+ };
2021
+ })() : function parse2(input, pos, ctx) {
2022
+ const end = pos + value.length;
2023
+ if (end > input.length) {
2024
+ return failAt(ctx, expected, pos);
1812
2025
  }
1813
- return pos;
2026
+ const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
2027
+ if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
2028
+ const span = { start: pos, end };
2029
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
2030
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
2031
+ return { ok: true, value: matchedValue, span };
2032
+ }
2033
+ return failAt(ctx, expected, pos);
2034
+ };
2035
+ return {
2036
+ _tag: "literal",
2037
+ _meta: meta,
2038
+ _def: { tag: "literal", value, caseInsensitive },
2039
+ parse
1814
2040
  };
1815
2041
  }
1816
- function regexTriviaScanner(parser2) {
1817
- if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1818
- const source = parser2._def.source;
1819
- return classRunSource(source) ?? altStarSource(source) ?? (blockCommentSource(source) ? scanBlockComment : null);
1820
- }
2042
+
2043
+ // src/combinators/regex.ts
2044
+ var firstSetAnalyzer = null;
2045
+ var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
2046
+ var SCAN_BAIL_AT = 64;
1821
2047
  function inRanges2(cp, ranges) {
1822
2048
  for (let i = 0; i < ranges.length; i++) {
1823
- const r = ranges[i];
1824
- if (cp >= r[0] && cp <= r[1]) return true;
2049
+ const [lo, hi] = ranges[i];
2050
+ if (cp >= lo && cp <= hi) return true;
1825
2051
  }
1826
2052
  return false;
1827
2053
  }
1828
- function classScanner(classBody) {
1829
- const ranges = parseClassRanges(classBody);
1830
- if (!ranges) return null;
1831
- return (input, cur) => {
1832
- let pos = cur;
1833
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1834
- return pos;
1835
- };
2054
+ function readClassRanges(body) {
2055
+ if (body.startsWith("^")) return null;
2056
+ return parseClassRanges(body);
1836
2057
  }
1837
- function lineCommentScanner(commentCode) {
1838
- return (input, cur) => {
1839
- if (input.charCodeAt(cur) !== commentCode) return cur;
1840
- let pos = cur + 1;
1841
- while (pos < input.length) {
1842
- const cc = input.charCodeAt(pos);
1843
- if (cc === 10 || cc === 13) break;
1844
- pos++;
2058
+ function shortScanner(source, flags) {
2059
+ if (/[imsuvy]/.test(flags)) return null;
2060
+ let ranges = null;
2061
+ let quant = "";
2062
+ if (source[0] === "[") {
2063
+ let end = 1;
2064
+ while (end < source.length && source[end] !== "]") {
2065
+ if (source[end] === "\\") end += 2;
2066
+ else end++;
1845
2067
  }
1846
- return pos;
2068
+ if (source[end] !== "]") return null;
2069
+ ranges = readClassRanges(source.slice(1, end));
2070
+ quant = source.slice(end + 1);
2071
+ } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
2072
+ ranges = shorthandRanges(source[1]);
2073
+ quant = source.slice(2);
2074
+ }
2075
+ if (!ranges || quant !== "+" && quant !== "*") return null;
2076
+ const minOne = quant === "+";
2077
+ return (input, pos) => {
2078
+ let end = pos;
2079
+ while (end < input.length && inRanges2(input.charCodeAt(end), ranges)) {
2080
+ end++;
2081
+ if (end - pos >= SCAN_BAIL_AT) return void 0;
2082
+ }
2083
+ return minOne && end === pos ? null : end;
1847
2084
  };
1848
2085
  }
1849
- function classRunSource(source) {
1850
- const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1851
- return m ? classScanner(m[1]) : null;
1852
- }
1853
- function classifyTriviaArm(arm) {
1854
- const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1855
- if (cls) {
1856
- const ranges = parseClassRanges(cls[1]);
1857
- return ranges ? { kind: "class", ranges } : null;
2086
+ function asciiCaseFold(fs) {
2087
+ if (fs.kind !== "ranges") return fs;
2088
+ const ranges = fs.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
2089
+ const add = (lo, hi) => {
2090
+ if (lo <= hi) ranges.push({ lo, hi });
2091
+ };
2092
+ for (const r of fs.ranges) {
2093
+ const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
2094
+ add(uLo + 32, uHi + 32);
2095
+ const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
2096
+ add(lLo - 32, lHi - 32);
1858
2097
  }
1859
- const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1860
- if (lc) {
1861
- const marker = lc[1];
1862
- return { kind: "comment", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
2098
+ ranges.sort((a, b) => a.lo - b.lo);
2099
+ const merged = [];
2100
+ for (const r of ranges) {
2101
+ const last = merged[merged.length - 1];
2102
+ if (last && r.lo <= last.hi + 1) last.hi = Math.max(last.hi, r.hi);
2103
+ else merged.push({ lo: r.lo, hi: r.hi });
1863
2104
  }
1864
- return null;
1865
- }
1866
- function armScanner(arm) {
1867
- if (arm.kind === "comment") return lineCommentScanner(arm.code);
1868
- const ranges = arm.ranges;
1869
- return (input, cur) => {
1870
- let pos = cur;
1871
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1872
- return pos;
1873
- };
2105
+ return { kind: "ranges", ranges: merged };
1874
2106
  }
1875
- function fusedTriviaScanner(ranges, commentCodes) {
1876
- const c0 = commentCodes[0];
1877
- const single = commentCodes.length === 1;
1878
- return (input, cur) => {
1879
- let pos = cur;
1880
- const len = input.length;
1881
- for (; ; ) {
1882
- const c = input.charCodeAt(pos);
1883
- if (pos < len && inRanges2(c, ranges)) {
1884
- pos++;
1885
- continue;
2107
+ function regex(pattern, flags = "") {
2108
+ const source = typeof pattern === "string" ? pattern : pattern.source;
2109
+ const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
2110
+ const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
2111
+ const scan = shortScanner(source, resolvedFlags);
2112
+ const raw = (firstSetAnalyzer ?? permissiveFirstSet)(source);
2113
+ const firstSet = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
2114
+ const canMatchNewline = raw.canMatchNewline;
2115
+ const meta = { firstSet, canMatchNewline, isTrivia: false };
2116
+ return {
2117
+ _tag: "regex",
2118
+ _meta: meta,
2119
+ _def: { tag: "regex", source, flags: resolvedFlags },
2120
+ parse(input, pos, ctx) {
2121
+ const scanEnd = scan?.(input, pos);
2122
+ if (scanEnd !== void 0) {
2123
+ if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
2124
+ const value = input.slice(pos, scanEnd);
2125
+ const span2 = { start: pos, end: scanEnd };
2126
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
2127
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
2128
+ return { ok: true, value, span: span2 };
1886
2129
  }
1887
- if (single ? c === c0 : commentCodes.includes(c)) {
1888
- pos++;
1889
- while (pos < len) {
1890
- const cc = input.charCodeAt(pos);
1891
- if (cc === 10 || cc === 13) break;
1892
- pos++;
1893
- }
1894
- continue;
2130
+ anchored.lastIndex = pos;
2131
+ const m = anchored.exec(input);
2132
+ if (m === null) {
2133
+ return failAt(ctx, [`/${source}/`], pos);
1895
2134
  }
1896
- break;
2135
+ const span = { start: pos, end: pos + m[0].length };
2136
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
2137
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
2138
+ return { ok: true, value: m[0], span };
1897
2139
  }
1898
- return pos;
1899
2140
  };
1900
2141
  }
1901
- function splitTopLevelAlts(body) {
1902
- const arms = [];
1903
- let inClass = false;
1904
- let start = 0;
1905
- for (let i = 0; i < body.length; i++) {
1906
- const c = body[i];
1907
- if (c === "\\") {
1908
- i++;
1909
- continue;
1910
- }
1911
- if (inClass) {
1912
- if (c === "]") inClass = false;
1913
- continue;
1914
- }
1915
- if (c === "[") inClass = true;
1916
- else if (c === "(") return null;
1917
- else if (c === "|") {
1918
- arms.push(body.slice(start, i));
1919
- start = i + 1;
1920
- }
1921
- }
1922
- arms.push(body.slice(start));
1923
- return arms;
1924
- }
1925
- function altStarSource(source) {
1926
- const m = /^\(\?:(.*)\)[*+]$/.exec(source);
1927
- if (!m) return null;
1928
- const armSrcs = splitTopLevelAlts(m[1]);
1929
- if (!armSrcs || armSrcs.length < 2) return null;
1930
- const arms = [];
1931
- for (const src of armSrcs) {
1932
- const arm = classifyTriviaArm(src);
1933
- if (!arm) return null;
1934
- arms.push(arm);
1935
- }
1936
- const ranges = [];
1937
- const commentCodes = [];
1938
- for (const arm of arms) {
1939
- if (arm.kind === "class") ranges.push(...arm.ranges);
1940
- else commentCodes.push(arm.code);
1941
- }
1942
- if (commentCodes.some((code) => inRanges2(code, ranges))) {
1943
- return loopScanner(arms.map(armScanner));
1944
- }
1945
- if (commentCodes.length === 0) return armScanner({ kind: "class", ranges });
1946
- return fusedTriviaScanner(ranges, commentCodes);
1947
- }
1948
- function blockCommentSource(source) {
1949
- return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
1950
- }
1951
- function scanBlockComment(input, cur) {
1952
- if (input.charCodeAt(cur) !== 47 || input.charCodeAt(cur + 1) !== 42) return cur;
1953
- const close = input.indexOf("*/", cur + 2);
1954
- return close === -1 ? cur : close + 2;
1955
- }
1956
2142
 
1957
2143
  // src/combinators/sequence.ts
1958
2144
  function sequence(...parsers) {
@@ -2368,6 +2554,25 @@ function trivia(combinator) {
2368
2554
  parse: combinator.parse.bind(combinator)
2369
2555
  };
2370
2556
  }
2557
+ function classifiedTrivia(arms) {
2558
+ const entries = Object.entries(arms);
2559
+ if (entries.length === 0) {
2560
+ throw new TypeError("classifiedTrivia() requires at least one named trivia arm.");
2561
+ }
2562
+ for (let i = 0; i < entries.length; i++) {
2563
+ const [name, arm] = entries[i];
2564
+ const first = arm._meta.firstSet;
2565
+ if (matchesEmpty(arm) || first.kind !== "ranges" || first.ranges.length === 0) {
2566
+ throw new TypeError(
2567
+ `classifiedTrivia(): ${JSON.stringify(name)} must be non-nullable with a concrete finite first set.`
2568
+ );
2569
+ }
2570
+ }
2571
+ const labeledArms = entries.map(([name, arm]) => label(name, arm));
2572
+ const result = trivia(oneOrMore(choice(...labeledArms)));
2573
+ result._meta.rootTriviaClassified = true;
2574
+ return result;
2575
+ }
2371
2576
  function label(name, combinator) {
2372
2577
  return {
2373
2578
  _tag: combinator._tag,
@@ -2461,8 +2666,17 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2461
2666
  };
2462
2667
  }
2463
2668
 
2669
+ // src/cst/trivia-entries.ts
2670
+ var EMPTY_INDICES = Object.freeze([]);
2671
+ var EMPTY_GAPS = Object.freeze([]);
2672
+ var SELECTED_ROOT_STRIDE = 5;
2673
+
2464
2674
  // src/compiler/scannable-run.ts
2465
2675
  var META = new Set("()[]{}*+?|^$.".split(""));
2676
+ var SELECTED_ROOT_OWNED_RANGE_PLACEHOLDER = Array.from(
2677
+ { length: SELECTED_ROOT_STRIDE - 3 },
2678
+ () => "0"
2679
+ ).join(", ");
2466
2680
 
2467
2681
  // src/compiler/trivia-fast-path.ts
2468
2682
  var CAP_RECORD = [
@@ -2471,6 +2685,11 @@ var CAP_RECORD = [
2471
2685
  ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
2472
2686
  ` }`
2473
2687
  ].join("\n");
2688
+ var SELECTED_ROOT_FINISH = [
2689
+ ` if (_cap && _ctx._rootTriviaCapture !== false && _ctx._rootTriviaLog !== undefined && _ctx._rootTriviaLog.length !== _rootMark) {`,
2690
+ ` for (let _ri = _rootMark; _ri < _ctx._rootTriviaLog.length; _ri += ${SELECTED_ROOT_STRIDE}) { _ctx._rootTriviaLog[_ri] = _pos; _ctx._rootTriviaLog[_ri + 1] = _e }`,
2691
+ ` }`
2692
+ ].join("\n");
2474
2693
 
2475
2694
  // src/compiler/build-arity.ts
2476
2695
  function cstOutputHost(build) {
@@ -2910,10 +3129,12 @@ function attempt(parser2) {
2910
3129
  }
2911
3130
  const mark = saveCstMark(ctx);
2912
3131
  const log = ctx._triviaLog?.length;
3132
+ const rootLog = ctx._rootTriviaLog?.length;
2913
3133
  const result = parser2.parse(input, pos, ctx);
2914
3134
  if (result.ok) return result;
2915
3135
  rollbackCstCapture(ctx, mark);
2916
3136
  if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
3137
+ if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
2917
3138
  if (result.committed) return result;
2918
3139
  return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
2919
3140
  }
@@ -3010,13 +3231,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3010
3231
  const collapse = opts?.collapse === true;
3011
3232
  const captureTrivia = opts?.captureTrivia === true;
3012
3233
  const trailingTrivia = opts?.trailingTrivia === true;
3234
+ const tags = opts?.tags;
3013
3235
  if (unwrap && collapse) {
3014
3236
  throw new Error("node() options cannot set both unwrap and collapse");
3015
3237
  }
3016
3238
  if (project !== void 0 && (unwrap || collapse)) {
3017
3239
  throw new Error("node() options cannot combine project with unwrap or collapse");
3018
3240
  }
3019
- const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {} } : baseDef;
3241
+ 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;
3020
3242
  const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
3021
3243
  const clonesState = build ? buildReadsState(def) : project === void 0;
3022
3244
  const hasOwnFields = parserHasOwnFields(combinator);
@@ -3062,7 +3284,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3062
3284
  const cstOutput = cstOutputHost(ctx.build);
3063
3285
  const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3064
3286
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3065
- 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) : projectChild(children, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, span, rawChildren, triviaLog, hostState) : build(children, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children, fields, span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children };
3287
+ 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 };
3066
3288
  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 };
3067
3289
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3068
3290
  pushCstChild(ctx, built, rawEntry);
@@ -3092,6 +3314,7 @@ function token(root) {
3092
3314
  const savedRaw = ctx._cstRawChildren;
3093
3315
  const savedTriviaLog = ctx._cstTriviaLog;
3094
3316
  const savedOuterTriviaLog = ctx._triviaLog;
3317
+ const savedRootTriviaLog = ctx._rootTriviaLog;
3095
3318
  const wasCapturing = cstCaptureActive(ctx);
3096
3319
  ctx.trivia = void 0;
3097
3320
  ctx.triviaKindLabels = void 0;
@@ -3101,6 +3324,7 @@ function token(root) {
3101
3324
  ctx._cstRawChildren = void 0;
3102
3325
  ctx._cstTriviaLog = void 0;
3103
3326
  delete ctx._triviaLog;
3327
+ delete ctx._rootTriviaLog;
3104
3328
  let result;
3105
3329
  try {
3106
3330
  result = root.parse(input, pos, ctx);
@@ -3114,6 +3338,8 @@ function token(root) {
3114
3338
  ctx._cstTriviaLog = savedTriviaLog;
3115
3339
  if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
3116
3340
  else ctx._triviaLog = savedOuterTriviaLog;
3341
+ if (savedRootTriviaLog === void 0) delete ctx._rootTriviaLog;
3342
+ else ctx._rootTriviaLog = savedRootTriviaLog;
3117
3343
  }
3118
3344
  if (!result.ok) return result;
3119
3345
  const span = result.span;
@@ -3293,6 +3519,7 @@ function evalRuleMapIR(ir) {
3293
3519
  "transform",
3294
3520
  "skip",
3295
3521
  "trivia",
3522
+ "classifiedTrivia",
3296
3523
  "label",
3297
3524
  "field",
3298
3525
  "expect",
@@ -3333,6 +3560,7 @@ function evalRuleMapIR(ir) {
3333
3560
  transform,
3334
3561
  skip,
3335
3562
  trivia,
3563
+ classifiedTrivia,
3336
3564
  label,
3337
3565
  field,
3338
3566
  expect,
@@ -3354,15 +3582,16 @@ function normalizeCstCollapse(collapse) {
3354
3582
  }
3355
3583
  return typeof collapse === "function" ? collapse : void 0;
3356
3584
  }
3357
- function buildCstNode(type, children, _fields, span, _rawChildren, _triviaLog, state) {
3358
- return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children] };
3585
+ function buildCstNode(type, children, _fields, span, _rawChildren, _triviaLog, state, tags) {
3586
+ return tags !== void 0 && tags.length > 0 ? { _tag: "node", type, tags, span: { ...span }, state: state ?? null, children: [...children] } : { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children] };
3359
3587
  }
3360
- function cstBuildHost(typeOrOptions, children, _fields, span, rawChildren, triviaLog, state) {
3588
+ function cstBuildHost(typeOrOptions, children, _fields, span, rawChildren, triviaLog, state, _tags) {
3361
3589
  if (typeof typeOrOptions === "string") {
3362
3590
  return buildCstNode(typeOrOptions, children ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
3363
3591
  }
3364
3592
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
3365
- const host = (type, children2, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children2 ?? [], fields, span2, rawChildren2, triviaLog2, state2);
3593
+ const materializeTags = typeOrOptions?.tags === true;
3594
+ const host = (type, children2, fields, span2, rawChildren2, triviaLog2, state2, tags) => buildCstNode(type, children2 ?? [], fields, span2, rawChildren2, triviaLog2, state2, materializeTags ? tags : void 0);
3366
3595
  host._parsemanCstOutput = true;
3367
3596
  if (collapse) host._parsemanCstCollapse = collapse;
3368
3597
  return host;