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
@@ -576,864 +576,631 @@ function pushTriviaLogEntry(ctx, start, end, kindIndex) {
576
576
  }
577
577
  }
578
578
 
579
- // src/recovery/scan.ts
580
- function probeAt(sentinel, input, pos, ctx) {
581
- const s = {
582
- _probe: ctx._probe,
583
- _errors: ctx._errors,
584
- _cstLeaves: ctx._cstLeaves,
585
- _cstChildren: ctx._cstChildren,
586
- _cstRawChildren: ctx._cstRawChildren,
587
- _cstTriviaLog: ctx._cstTriviaLog,
588
- _triviaLog: ctx._triviaLog,
589
- _fields: ctx._fields,
590
- _cstBuf: ctx._cstBuf,
591
- _tolerant: ctx._tolerant,
592
- _sync: ctx._sync
593
- };
594
- ctx._probe = void 0;
595
- ctx._errors = void 0;
596
- ctx._cstLeaves = void 0;
597
- ctx._cstChildren = void 0;
598
- ctx._cstRawChildren = void 0;
599
- ctx._cstTriviaLog = void 0;
600
- ctx._triviaLog = void 0;
601
- ctx._fields = void 0;
602
- ctx._cstBuf = void 0;
603
- ctx._tolerant = void 0;
604
- ctx._sync = void 0;
605
- try {
606
- return sentinel.parse(input, pos, ctx).ok;
607
- } finally {
608
- ctx._probe = s._probe;
609
- ctx._errors = s._errors;
610
- ctx._cstLeaves = s._cstLeaves;
611
- ctx._cstChildren = s._cstChildren;
612
- ctx._cstRawChildren = s._cstRawChildren;
613
- ctx._cstTriviaLog = s._cstTriviaLog;
614
- ctx._triviaLog = s._triviaLog;
615
- ctx._fields = s._fields;
616
- ctx._cstBuf = s._cstBuf;
617
- ctx._tolerant = s._tolerant;
618
- ctx._sync = s._sync;
619
- }
579
+ // src/cst/trivia-kinds.ts
580
+ var labeledTriviaSpecs = /* @__PURE__ */ new WeakMap();
581
+ function unwrapTrivia(p) {
582
+ let cur = p;
583
+ while (cur._def.tag === "trivia") cur = cur._def.parser;
584
+ return cur;
620
585
  }
621
- function orSentinel(a, b) {
622
- if (b === void 0) return a;
623
- return {
624
- _tag: "orSentinel",
625
- _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
626
- _def: { tag: "unknown" },
627
- parse(input, pos, ctx) {
628
- const ra = a.parse(input, pos, ctx);
629
- if (ra.ok) return ra;
630
- return b.parse(input, pos, ctx);
631
- }
632
- };
586
+ function peelLabel(p) {
587
+ if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
588
+ return null;
633
589
  }
634
- function firstSetSentinel(fs) {
635
- if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
636
- const ranges = fs.ranges;
637
- return {
638
- _tag: "firstSetSentinel",
639
- _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },
640
- _def: { tag: "unknown" },
641
- parse(input, pos) {
642
- const code = pos < input.length ? input.codePointAt(pos) : void 0;
643
- if (code !== void 0) {
644
- for (const r of ranges) {
645
- if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
646
- }
590
+ function analyzeLabeledTrivia(trivia2) {
591
+ const cached = labeledTriviaSpecs.get(trivia2);
592
+ if (cached !== void 0) return cached;
593
+ let core = unwrapTrivia(trivia2);
594
+ let minRepeats = 1;
595
+ if (core._def.tag === "oneOrMore") {
596
+ core = core._def.parser;
597
+ minRepeats = 1;
598
+ } else if (core._def.tag === "many") {
599
+ core = core._def.parser;
600
+ minRepeats = 0;
601
+ }
602
+ const arms = [];
603
+ if (core._def.tag === "choice") {
604
+ for (let i = 0; i < core._def.parsers.length; i++) {
605
+ const peeled = peelLabel(core._def.parsers[i]);
606
+ if (!peeled) {
607
+ labeledTriviaSpecs.set(trivia2, null);
608
+ return null;
647
609
  }
648
- return { ok: false, expected: [], span: { start: pos, end: pos } };
610
+ arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
611
+ }
612
+ } else {
613
+ const peeled = peelLabel(core);
614
+ if (!peeled) {
615
+ labeledTriviaSpecs.set(trivia2, null);
616
+ return null;
649
617
  }
618
+ arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
619
+ }
620
+ const spec = {
621
+ labels: arms.map((a) => a.label),
622
+ arms,
623
+ minRepeats
650
624
  };
625
+ labeledTriviaSpecs.set(trivia2, spec);
626
+ return spec;
651
627
  }
652
- function recoverScan(input, from, ctx, sync, expected) {
653
- let scanPos = from;
654
- while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
655
- const span = { start: from, end: scanPos };
656
- if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
657
- const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
658
- ctx._errors?.push(error);
659
- return { error, end: scanPos };
660
- }
661
- function matchesAt(sentinel, input, pos, ctx) {
662
- return probeAt(sentinel, input, pos, ctx);
628
+ function matchArmAt(input, pos, arm, state) {
629
+ if (!startsFirstSet(arm, input, pos)) return null;
630
+ const r = arm.parse(input, pos, { trackLines: false, state });
631
+ if (!r.ok || r.span.end <= pos) return null;
632
+ return { end: r.span.end };
663
633
  }
664
- function captureError(ctx, error) {
665
- if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
634
+ function scanLabeledTriviaChunks(input, cur, spec, state) {
635
+ const chunks = [];
636
+ let pos = cur;
637
+ while (pos < input.length) {
638
+ let matched = null;
639
+ for (const arm of spec.arms) {
640
+ const m = matchArmAt(input, pos, arm.parser, state);
641
+ if (m) {
642
+ matched = { end: m.end, kindIndex: arm.kindIndex };
643
+ break;
644
+ }
645
+ }
646
+ if (!matched) break;
647
+ chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
648
+ pos = matched.end;
649
+ }
650
+ if (chunks.length < spec.minRepeats) {
651
+ return { end: cur, chunks: [] };
652
+ }
653
+ return { end: pos, chunks };
666
654
  }
667
-
668
- // src/combinators/expect.ts
669
- function deriveExpected(c) {
670
- return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
655
+ function scanLabeledTriviaEnd(input, cur, spec, state) {
656
+ let pos = cur;
657
+ let count = 0;
658
+ while (pos < input.length) {
659
+ let matched = null;
660
+ for (const arm of spec.arms) {
661
+ matched = matchArmAt(input, pos, arm.parser, state);
662
+ if (matched) break;
663
+ }
664
+ if (!matched) break;
665
+ pos = matched.end;
666
+ count++;
667
+ }
668
+ return count < spec.minRepeats ? cur : pos;
671
669
  }
672
- function derive(c, seen) {
673
- const deriveExpected2 = (p) => derive(p, seen);
674
- const def = c._def;
675
- switch (def.tag) {
676
- case "literal":
677
- return [JSON.stringify(def.value)];
678
- case "regex":
679
- return [`/${def.source}/`];
680
- case "keywords":
681
- return def.words.map((w) => JSON.stringify(w));
682
- case "label":
683
- return [def.label];
684
- case "choice":
685
- return def.parsers.flatMap(deriveExpected2);
686
- case "dispatch":
687
- return deriveExpected2(def.selector);
688
- // Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
689
- // `optional(…)`/`many(…)` can match nothing, so the term after it is equally
690
- // able to fail first — deriving from term 0 alone named a token the parse
691
- // never actually required. A leading `not(…)` is zero-width and contributes
692
- // nothing (its own expectation is about what must NOT be here), but it is
693
- // nullable, so keep scanning past it. Stop at the first term that must match.
694
- case "sequence": {
695
- const out = [];
696
- for (const term of def.parsers) {
697
- if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
698
- if (!matchesEmpty(term)) break;
670
+ function visitLabeledTrivia(input, cur, spec, state, visit) {
671
+ if (spec.minRepeats > 1) return void 0;
672
+ let pos = cur;
673
+ let count = 0;
674
+ while (pos < input.length) {
675
+ let matched = null;
676
+ for (const arm of spec.arms) {
677
+ const match = matchArmAt(input, pos, arm.parser, state);
678
+ if (match) {
679
+ matched = { end: match.end, kindIndex: arm.kindIndex };
680
+ break;
699
681
  }
700
- return out;
701
682
  }
702
- case "attempt":
703
- return deriveExpected2(def.parser);
704
- // Delegating wrappers whose first-token failure is their inner parser's — must
705
- // pass through so a start-failure (and the first-set-miss fast-path in attempt/
706
- // node, which reads this) names the real expected token, not the wrapper tag.
707
- case "field":
708
- case "withCtx":
709
- case "recover":
710
- return deriveExpected2(def.parser);
711
- case "skip":
712
- return deriveExpected2(def.main);
713
- case "expect":
714
- return def.expected;
715
- case "node":
716
- case "grammar":
717
- case "trivia":
718
- case "token":
719
- case "leaf":
720
- case "optional":
721
- case "many":
722
- case "oneOrMore":
723
- case "transform":
724
- case "not":
725
- case "peek":
726
- return deriveExpected2(def.parser);
727
- case "routed":
728
- return ["routed()"];
729
- case "lazy": {
730
- const name = c._ruleName;
731
- if (seen.has(c)) return name ? [name] : [];
732
- seen.add(c);
733
- try {
734
- return deriveExpected2(def.thunk());
735
- } catch {
736
- return name ? [name] : [];
737
- } finally {
738
- seen.delete(c);
683
+ if (!matched) break;
684
+ visit(pos, matched.end, matched.kindIndex);
685
+ pos = matched.end;
686
+ count++;
687
+ }
688
+ return count < spec.minRepeats ? cur : pos;
689
+ }
690
+ function recordTriviaChunks(ctx, chunks) {
691
+ const kinds = ctx.triviaKindLabels;
692
+ const mask = ctx._triviaCaptureMask;
693
+ const rootLog = ctx._rootTriviaLog;
694
+ const rootKinds = ctx._rootTriviaKindIndex;
695
+ const rootMark = rootLog?.length ?? 0;
696
+ for (const ch of chunks) {
697
+ pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
698
+ const rootKindIndex = ctx._rootTriviaCapture === false || rootLog === void 0 || rootKinds === void 0 || kinds === void 0 ? -1 : rootKinds[kinds[ch.kindIndex] ?? ""] ?? -1;
699
+ if (rootKindIndex >= 0) {
700
+ rootLog.push(0, 0, ch.start, ch.end, rootKindIndex);
701
+ }
702
+ if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
703
+ if (mask === void 0 || kinds === void 0 || (mask & 1 << ch.kindIndex) !== 0) {
704
+ pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
739
705
  }
740
706
  }
741
- default:
742
- return [];
743
707
  }
744
- }
745
- function expect(combinator, label2) {
746
- const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
747
- const meta = {
748
- firstSet: combinator._meta.firstSet,
749
- canMatchNewline: combinator._meta.canMatchNewline,
750
- isTrivia: false
751
- };
752
- return {
753
- _tag: "expect",
754
- _meta: meta,
755
- _def: { tag: "expect", parser: combinator, label: label2, expected },
756
- parse(input, pos, ctx) {
757
- const result = combinator.parse(input, pos, ctx);
758
- if (result.ok) return result;
759
- const span = { start: pos, end: pos };
760
- const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
761
- ctx._errors?.push(error);
762
- if (ctx._tolerant) captureError(ctx, error);
763
- return { ok: true, value: error, span: { start: pos, end: pos } };
708
+ if (rootLog !== void 0 && rootLog.length !== rootMark && chunks.length > 0) {
709
+ const start = chunks[0].start;
710
+ const end = chunks[chunks.length - 1].end;
711
+ for (let offset = rootMark; offset < rootLog.length; offset += 5) {
712
+ rootLog[offset] = start;
713
+ rootLog[offset + 1] = end;
764
714
  }
765
- };
715
+ }
766
716
  }
767
-
768
- // src/combinators/choice.ts
769
- function choice(...args) {
770
- const parsers = args.map((a) => "gate" in a ? a.combinator : a);
771
- const gates = args.map((a) => "gate" in a ? a.gate : null);
772
- const hasGates = gates.some((g) => g !== null);
773
- const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
774
- let combined = { kind: "empty" };
775
- for (const p of parsers) combined = union(combined, p._meta.firstSet);
776
- const meta = {
777
- firstSet: combined,
778
- canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
779
- isTrivia: false,
780
- disjoint
781
- };
782
- const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
783
- const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
784
- let greedyLitMap = null;
785
- let sortedParsers = null;
786
- const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
787
- if (strategy?.tag === "greedyClassify") {
788
- greedyLitMap = /* @__PURE__ */ new Map();
789
- for (let i = 0; i < parsers.length; i++) {
790
- if (i === strategy.superIndex) continue;
791
- const litVal = getCoreLiteralValue(parsers[i]);
792
- if (litVal !== null) greedyLitMap.set(litVal, i);
793
- }
794
- } else if (strategy?.tag === "literalsLongestFirst") {
795
- sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
717
+ function triviaKindMask(labels, keep) {
718
+ if (!labels) return void 0;
719
+ let mask = 0;
720
+ for (const name of keep) {
721
+ const idx = labels.indexOf(name);
722
+ if (idx >= 0) mask |= 1 << idx;
723
+ }
724
+ return mask;
725
+ }
726
+
727
+ // src/combinators/grammar.ts
728
+ function annotateResultLines(result, index) {
729
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
730
+ const errors = annotated.errors;
731
+ if (errors) {
732
+ ;
733
+ annotated.errors = errors.map((error) => ({
734
+ ...error,
735
+ span: annotateSpan(error.span, index)
736
+ }));
737
+ }
738
+ return annotated;
739
+ }
740
+ function createParseLineContext(input, pos) {
741
+ const lineIndex = createLineIndex();
742
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
743
+ return { lineIndex, lineScannedTo: pos };
744
+ }
745
+ function parser(opts, root) {
746
+ const clearTrivia = opts.trivia === null;
747
+ const opaqueRootCapture = opts.rootCapture === "opaque";
748
+ if (opaqueRootCapture && opts.trivia === void 0) {
749
+ throw new TypeError("parser({ rootCapture: 'opaque' }) requires an explicit trivia scope.");
796
750
  }
797
751
  return {
798
- _tag: "choice",
799
- _meta: meta,
752
+ _tag: "grammar",
753
+ _meta: {
754
+ ...root._meta,
755
+ // Trivia classification describes this grammar's own active trivia
756
+ // scope. A directly nested grammar may have classified trivia of its
757
+ // own, but that must not advertise classification for this wrapper.
758
+ triviaKindLabels: void 0,
759
+ rootTriviaClassified: void 0,
760
+ ...opts.trivia?._meta?.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {},
761
+ ...opts.trivia?._meta?.rootTriviaClassified ? { rootTriviaClassified: true } : {}
762
+ },
800
763
  _def: {
801
- tag: "choice",
802
- parsers,
803
- gates,
804
- disjoint,
805
- strategy: strategy ?? { tag: "firstMatch" },
806
- autoNot
764
+ tag: "grammar",
765
+ parser: root,
766
+ triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
767
+ clearTrivia,
768
+ ...opaqueRootCapture ? { rootCapture: "opaque" } : {},
769
+ ...opts.captureTrivia ? { captureTrivia: true } : {},
770
+ trackLines: opts.trackLines ?? false
807
771
  },
808
- parse(input, pos, ctx) {
809
- const expected = [];
810
- if (disjoint && pos < input.length) {
811
- const code = input.codePointAt(pos);
812
- let idx = code < 128 ? asciiDispatch[code] : -1;
813
- if (idx < 0) {
814
- for (let i = 0; i < parsers.length; i++) {
815
- if (inFirstSet(code, parsers[i]._meta.firstSet)) {
816
- idx = i;
817
- break;
818
- }
819
- }
820
- }
821
- if (idx >= 0) {
822
- const gate = gates[idx];
823
- if (gate && !gate(ctx.state)) {
824
- return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
825
- }
826
- const result = parsers[idx].parse(input, pos, ctx);
827
- if (result.ok) return result;
828
- expected.push(...result.expected);
829
- if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
830
- return { ok: false, expected, span: { start: pos, end: pos } };
831
- }
832
- return {
833
- ok: false,
834
- expected: parsers.flatMap((p) => {
835
- const r = p.parse(input, pos, ctx);
836
- return r.ok ? [] : r.expected;
837
- }),
838
- span: { start: pos, end: pos }
839
- };
840
- }
841
- if (strategy?.tag === "greedyClassify") {
842
- const superResult = parsers[strategy.superIndex].parse(input, pos, ctx);
843
- if (!superResult.ok) return superResult;
844
- const end = superResult.span.end;
845
- const litIdx = greedyLitMap.get(input.slice(pos, end));
846
- if (litIdx !== void 0) {
847
- const litVal = getCoreLiteralValue(parsers[litIdx]);
848
- const value = applyTransforms(parsers[litIdx], litVal, { start: pos, end });
849
- return { ok: true, value, span: { start: pos, end } };
850
- }
851
- return superResult;
852
- }
853
- if (strategy?.tag === "literalsLongestFirst") {
854
- for (const p of sortedParsers) {
855
- const r = p.parse(input, pos, ctx);
856
- if (r.ok) return r;
857
- expected.push(...r.expected);
858
- }
859
- return { ok: false, expected, span: { start: pos, end: pos } };
772
+ parse(input, pos, _ctx) {
773
+ if (_ctx?._rootTriviaStrictScopes && opts.trivia !== void 0 && opts.trivia !== null && !opts.trivia._meta.rootTriviaClassified && !opaqueRootCapture) {
774
+ throw new TypeError(
775
+ "parser(): selected root trivia requires classifiedTrivia() for every local trivia scope, or rootCapture: 'opaque'."
776
+ );
860
777
  }
861
- for (let i = 0; i < parsers.length; i++) {
862
- if (gates[i] && !gates[i](ctx.state)) continue;
863
- const mark = saveCstMark(ctx);
864
- const logLen = ctx._triviaLog?.length;
865
- const result = parsers[i].parse(input, pos, ctx);
866
- if (!result.ok) {
867
- rollbackCstCapture(ctx, mark);
868
- if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
869
- expected.push(...result.expected);
870
- if (result.committed) return { ok: false, expected, span: result.span, committed: true };
871
- continue;
872
- }
873
- const checks = autoNot[i];
874
- if (checks && autoNotFires(input, result.span.end, checks)) {
875
- rollbackCstCapture(ctx, mark);
876
- if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
877
- continue;
878
- }
879
- return result;
778
+ const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
779
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
780
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
781
+ const ctx = {
782
+ ..._ctx,
783
+ trackLines,
784
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
785
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
786
+ // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
787
+ ...clearTrivia ? {
788
+ trivia: void 0,
789
+ triviaKindLabels: void 0
790
+ } : opts.trivia != null ? {
791
+ trivia: opts.trivia,
792
+ ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
793
+ } : {},
794
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
795
+ // Kind-filter for per-node capture. Resolve against this scope's trivia
796
+ // labels — this parser's own trivia if it declares one, else the INHERITED
797
+ // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
798
+ // trivia is inherited rather than re-declared here. No labels → undefined
799
+ // (capture all).
800
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {},
801
+ // This is a property of the local trivia scope, not a post-parse filter:
802
+ // nested recognition can still skip its trivia, but selected root rows
803
+ // must never be written for an explicitly opaque region.
804
+ ...opaqueRootCapture ? { _rootTriviaCapture: false } : {}
805
+ };
806
+ const result = root.parse(input, pos ?? 0, ctx);
807
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
808
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
880
809
  }
881
- return { ok: false, expected, span: { start: pos, end: pos } };
810
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
882
811
  }
883
812
  };
884
813
  }
885
- function detectStrategy(parsers) {
886
- const regexIndices = [];
887
- const literalIndices = [];
888
- for (let i = 0; i < parsers.length; i++) {
889
- if (getCoreRegexDef(parsers[i]) !== null) regexIndices.push(i);
890
- else if (getCoreLiteralValue(parsers[i]) !== null) literalIndices.push(i);
891
- }
892
- if (regexIndices.length === 1 && literalIndices.length === parsers.length - 1 && literalIndices.length > 0) {
893
- const superIndex = regexIndices[0];
894
- const regexDef = getCoreRegexDef(parsers[superIndex]);
895
- const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
896
- const re = new RegExp(regexDef.source, flags);
897
- const allSubsumed = literalIndices.every((i) => {
898
- const litVal = getCoreLiteralValue(parsers[i]);
899
- re.lastIndex = 0;
900
- const m = re.exec(litVal);
901
- return m !== null && m[0] === litVal;
902
- });
903
- if (allSubsumed) return { tag: "greedyClassify", superIndex };
904
- }
905
- if (parsers.length === literalIndices.length) {
906
- const sortedIndices = [...literalIndices].sort(
907
- (a, b) => getCoreLiteralValue(parsers[b]).length - getCoreLiteralValue(parsers[a]).length
908
- );
909
- return { tag: "literalsLongestFirst", sortedIndices };
814
+
815
+ // src/cst/reflection.ts
816
+ var GRAMMAR_REFLECTION = /* @__PURE__ */ Symbol.for("parseman.grammarReflection");
817
+ function childrenOf(def) {
818
+ switch (def.tag) {
819
+ case "sequence":
820
+ case "choice":
821
+ return def.parsers;
822
+ case "dispatch":
823
+ return [
824
+ def.selector,
825
+ ...def.cases.map((c) => c.parser),
826
+ ...def.matchers ? def.matchers.map((c) => c.parser) : [],
827
+ ...def.otherwise ? [def.otherwise] : []
828
+ ];
829
+ case "many":
830
+ case "oneOrMore":
831
+ case "optional":
832
+ case "attempt":
833
+ case "transform":
834
+ case "trivia":
835
+ case "token":
836
+ case "leaf":
837
+ case "label":
838
+ case "field":
839
+ case "not":
840
+ case "peek":
841
+ case "node":
842
+ case "withCtx":
843
+ case "recover":
844
+ case "expect":
845
+ return [def.parser];
846
+ case "grammar":
847
+ return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
848
+ case "sepBy":
849
+ return [def.parser, def.separator];
850
+ case "skip":
851
+ return [def.main, def.skipped];
852
+ case "scanTo":
853
+ return [def.sentinel, ...def.skip];
854
+ case "literal":
855
+ case "regex":
856
+ case "keywords":
857
+ case "routed":
858
+ case "guard":
859
+ case "lazy":
860
+ case "unknown":
861
+ return [];
910
862
  }
911
- const shared = detectSharedPrefix(parsers);
912
- if (shared !== null) return shared;
913
- return { tag: "firstMatch" };
914
- }
915
- function bareLeadingTermKey(term) {
916
- const d = term._def;
917
- if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
918
- if (d.tag === "regex") return `R:${d.source}\0${d.flags}`;
919
- return null;
920
863
  }
921
- function detectSharedPrefix(parsers) {
922
- if (parsers.length < 2) return null;
923
- let key = null;
924
- let prefix = null;
925
- const members = [];
926
- for (let i = 0; i < parsers.length; i++) {
927
- const term0 = leadingTermOfArm(parsers[i]);
928
- if (term0 === null) return null;
929
- const k = bareLeadingTermKey(term0);
930
- if (k === null) return null;
931
- if (key === null) {
932
- key = k;
933
- prefix = term0;
934
- } else if (k !== key) return null;
935
- members.push(i);
864
+ function addNode(out, def) {
865
+ if (def.type === void 0) return;
866
+ const existing = out.get(def.type);
867
+ if (existing === void 0) {
868
+ out.set(def.type, def.tags === void 0 ? [] : [...def.tags]);
869
+ return;
936
870
  }
937
- if (prefix === null || members.length < 2) return null;
938
- return { tag: "sharedPrefix", prefix, members };
939
- }
940
- function leadingTermOfArm(arm) {
941
- let d = arm._def;
942
- for (; ; ) {
943
- if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
944
- d = d.parser._def;
945
- continue;
946
- }
947
- break;
871
+ if (def.tags === void 0) return;
872
+ for (const tag of def.tags) {
873
+ if (!existing.includes(tag)) existing.push(tag);
948
874
  }
949
- if (d.tag !== "sequence" || d.parsers.length < 2) return null;
950
- return d.parsers[0];
951
875
  }
952
- function computeAutoNot(parsers) {
953
- return parsers.map((p, i) => {
954
- const litVal = getCoreLiteralValue(p);
955
- if (litVal === null) return null;
956
- const checks = [];
957
- for (let j2 = i + 1; j2 < parsers.length; j2++) {
958
- const other = parsers[j2];
959
- const otherLit = getCoreLiteralValue(other);
960
- if (otherLit !== null && otherLit.startsWith(litVal) && otherLit.length > litVal.length) {
961
- checks.push({ kind: "startsWith", value: otherLit.slice(litVal.length) });
962
- continue;
963
- }
964
- const regexDef = getCoreRegexDef(other);
965
- if (regexDef !== null) {
966
- const contSet = continuationFirstSet(litVal, regexDef.source, regexDef.flags);
967
- if (contSet !== null) checks.push({ kind: "firstSet", set: contSet });
876
+ function collectFromCombinator(parser2, out, seen, followLazy = true) {
877
+ if (seen.has(parser2)) return;
878
+ seen.add(parser2);
879
+ const def = parser2._def;
880
+ if (def.tag === "lazy") {
881
+ if (followLazy) {
882
+ try {
883
+ collectFromCombinator(def.thunk(), out, seen, followLazy);
884
+ } catch {
968
885
  }
969
886
  }
970
- return checks.length > 0 ? checks : null;
971
- });
972
- }
973
- function autoNotFires(input, end, checks) {
974
- for (const check of checks) {
975
- if (check.kind === "firstSet") {
976
- const code = end < input.length ? input.codePointAt(end) ?? -1 : -1;
977
- if (inFirstSet(code, check.set)) return true;
978
- } else {
979
- if (input.startsWith(check.value, end)) return true;
887
+ return;
888
+ }
889
+ if (def.tag === "node") addNode(out, def);
890
+ for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy);
891
+ }
892
+ function collectGrammarReflection(ruleMap, opts) {
893
+ const byType = /* @__PURE__ */ new Map();
894
+ const followLazy = opts?.followLazy !== false;
895
+ for (const [, rule] of ruleMap) {
896
+ if (!followLazy && rule._def.tag === "lazy") {
897
+ try {
898
+ const resolved = rule._def.thunk();
899
+ if (resolved._def.tag !== "lazy") collectFromCombinator(resolved, byType, /* @__PURE__ */ new Set(), false);
900
+ } catch {
901
+ }
902
+ continue;
980
903
  }
904
+ collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
981
905
  }
982
- return false;
983
- }
984
- function getCoreLiteralValue(p) {
985
- const def = p._def;
986
- if (def.tag === "literal" && !def.caseInsensitive) return def.value;
987
- if (def.tag === "transform") return getCoreLiteralValue(def.parser);
988
- return null;
989
- }
990
- function getCoreRegexDef(p) {
991
- const def = p._def;
992
- if (def.tag === "regex") return { source: def.source, flags: def.flags };
993
- if (def.tag === "transform") return getCoreRegexDef(def.parser);
994
- if (def.tag === "label") return getCoreRegexDef(def.parser);
995
- return null;
906
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
996
907
  }
997
- function applyTransforms(p, value, span) {
998
- const def = p._def;
999
- if (def.tag === "transform") {
1000
- const inner = applyTransforms(def.parser, value, span);
1001
- return def.fn(inner, span);
1002
- }
1003
- return value;
908
+ function attachGrammarReflection(grammar, reflection) {
909
+ Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true });
910
+ return grammar;
1004
911
  }
1005
- function continuationFirstSet(lit, source, flags) {
1006
- const re = new RegExp(source, "y" + flags.replace(/[gy]/g, ""));
1007
- re.lastIndex = 0;
1008
- const base = re.exec(lit);
1009
- if (!base || base[0] !== lit) return null;
1010
- const contCodes = [];
1011
- for (let code = 1; code < 128; code++) {
1012
- re.lastIndex = 0;
1013
- const m = re.exec(lit + String.fromCharCode(code));
1014
- if (m && m[0].length > lit.length) contCodes.push(code);
1015
- }
1016
- if (contCodes.length === 0) return null;
1017
- return codesToFirstSet(contCodes);
912
+
913
+ // src/combinators/parser.ts
914
+ var RULE_ORDER = "__parsemanRuleOrder";
915
+ function tagRule(r, key) {
916
+ ;
917
+ r._ruleName = key;
918
+ if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
1018
919
  }
1019
- function codesToFirstSet(codes) {
1020
- codes.sort((a, b) => a - b);
1021
- const ranges = [];
1022
- let lo = codes[0], hi = codes[0];
1023
- for (let i = 1; i < codes.length; i++) {
1024
- if (codes[i] === hi + 1) {
1025
- hi = codes[i];
1026
- } else {
1027
- ranges.push({ lo, hi });
1028
- lo = hi = codes[i];
1029
- }
1030
- }
1031
- ranges.push({ lo, hi });
1032
- return { kind: "ranges", ranges };
920
+ function isDefinableRef(v) {
921
+ return !!v && typeof v === "object" && "_def" in v && v._def.tag === "lazy" && typeof v.define === "function";
1033
922
  }
1034
- function inFirstSet(code, fs) {
1035
- if (fs.kind === "any") return true;
1036
- if (fs.kind === "empty") return false;
1037
- for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
1038
- return false;
923
+ function ruleNameOf(r) {
924
+ return r._ruleName;
1039
925
  }
1040
- function areDisjoint(sets) {
1041
- if (sets.some((s) => s.kind === "any")) return false;
1042
- for (let i = 0; i < sets.length; i++)
1043
- for (let j2 = i + 1; j2 < sets.length; j2++)
1044
- if (intersects(sets[i], sets[j2])) return false;
1045
- return true;
926
+ function isNamedRuleRefForAnotherRule(r, key) {
927
+ const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
928
+ return name !== void 0 && name !== key;
1046
929
  }
1047
- function buildAsciiDispatch(parsers) {
1048
- const table = Array(128).fill(-1);
1049
- for (let i = 0; i < parsers.length; i++) {
1050
- const fs = parsers[i]._meta.firstSet;
1051
- if (fs.kind !== "ranges") continue;
1052
- for (const { lo, hi } of fs.ranges) {
1053
- for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
1054
- table[code] = i;
930
+ function rules(a, b) {
931
+ const factory = typeof a === "function" ? a : b;
932
+ const options = typeof a === "function" ? b : a;
933
+ const cache = {};
934
+ const proxy = new Proxy(cache, {
935
+ get(target, key) {
936
+ if (typeof key !== "string") return void 0;
937
+ const record = target;
938
+ if (!(key in record)) {
939
+ const r = ref();
940
+ tagRule(r, key);
941
+ record[key] = r;
1055
942
  }
943
+ return record[key];
944
+ }
945
+ });
946
+ const definitions = factory(proxy);
947
+ for (const key of Object.keys(definitions)) {
948
+ const placeholder = cache[key];
949
+ const parser2 = definitions[key];
950
+ if (placeholder === parser2) {
951
+ throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
952
+ }
953
+ if (isDefinableRef(placeholder)) {
954
+ if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
955
+ placeholder.define(parser2);
956
+ placeholder._meta.firstSet = parser2._meta.firstSet;
957
+ placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
958
+ } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
959
+ const alias = ref();
960
+ tagRule(alias, key);
961
+ alias.define(parser2);
962
+ alias._meta.firstSet = parser2._meta.firstSet;
963
+ alias._meta.canMatchNewline = parser2._meta.canMatchNewline;
964
+ cache[key] = alias;
965
+ } else {
966
+ tagRule(parser2, key);
967
+ cache[key] = parser2;
1056
968
  }
1057
969
  }
1058
- return table;
1059
- }
1060
-
1061
- // src/cst/trivia-kinds.ts
1062
- function unwrapTrivia(p) {
1063
- let cur = p;
1064
- while (cur._def.tag === "trivia") cur = cur._def.parser;
1065
- return cur;
1066
- }
1067
- function peelLabel(p) {
1068
- if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
1069
- return null;
1070
- }
1071
- function analyzeLabeledTrivia(trivia2) {
1072
- let core = unwrapTrivia(trivia2);
1073
- let minRepeats = 1;
1074
- if (core._def.tag === "oneOrMore") {
1075
- core = core._def.parser;
1076
- minRepeats = 1;
1077
- } else if (core._def.tag === "many") {
1078
- core = core._def.parser;
1079
- minRepeats = 0;
1080
- }
1081
- const arms = [];
1082
- if (core._def.tag === "choice") {
1083
- for (let i = 0; i < core._def.parsers.length; i++) {
1084
- const peeled = peelLabel(core._def.parsers[i]);
1085
- if (!peeled) return null;
1086
- arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
970
+ if (options?.trivia != null) {
971
+ for (const key of Object.keys(definitions)) {
972
+ const rule = cache[key];
973
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
1087
974
  }
1088
- } else {
1089
- const peeled = peelLabel(core);
1090
- if (!peeled) return null;
1091
- arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
1092
975
  }
1093
- return { labels: arms.map((a) => a.label), arms, minRepeats };
1094
- }
1095
- function matchArmAt(input, pos, arm) {
1096
- const r = arm.parse(input, pos, { trackLines: false });
1097
- if (!r.ok || r.span.end <= pos) return null;
1098
- return { end: r.span.end };
1099
- }
1100
- function scanLabeledTriviaChunks(input, cur, spec) {
1101
- const chunks = [];
1102
- let pos = cur;
1103
- while (pos < input.length) {
1104
- let matched = null;
1105
- for (const arm of spec.arms) {
1106
- const m = matchArmAt(input, pos, arm.parser);
1107
- if (m) {
1108
- matched = { end: m.end, kindIndex: arm.kindIndex };
1109
- break;
1110
- }
976
+ if (options?.scanSkip != null) {
977
+ for (const key of Object.keys(definitions)) {
978
+ const rule = cache[key];
979
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
1111
980
  }
1112
- if (!matched) break;
1113
- chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
1114
- pos = matched.end;
1115
981
  }
1116
- if (chunks.length < spec.minRepeats) {
1117
- return { end: cur, chunks: [] };
982
+ if (options?.hostMode === "cst") {
983
+ for (const key of Object.keys(definitions)) {
984
+ const rule = cache[key];
985
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
986
+ }
1118
987
  }
1119
- return { end: pos, chunks };
1120
- }
1121
- function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
1122
- const chunks = [];
1123
- let pos = cur;
1124
- while (pos < input.length) {
1125
- const c = input.charCodeAt(pos);
1126
- if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {
1127
- const start = pos;
1128
- pos++;
1129
- while (pos < input.length) {
1130
- const c2 = input.charCodeAt(pos);
1131
- if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) pos++;
1132
- else break;
1133
- }
1134
- chunks.push({ start, end: pos, kindIndex: wsKind });
1135
- continue;
988
+ if (options?.trackLines === true) {
989
+ for (const key of Object.keys(definitions)) {
990
+ const rule = cache[key];
991
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
1136
992
  }
1137
- if (c === 47 && input.charCodeAt(pos + 1) === 42) {
1138
- let j2 = pos + 2;
1139
- while (j2 + 1 < input.length && !(input.charCodeAt(j2) === 42 && input.charCodeAt(j2 + 1) === 47)) j2++;
1140
- if (j2 + 1 < input.length && input.charCodeAt(j2) === 42 && input.charCodeAt(j2 + 1) === 47) {
1141
- const start = pos;
1142
- pos = j2 + 2;
1143
- chunks.push({ start, end: pos, kindIndex: commentKind });
1144
- continue;
993
+ for (const key of Object.keys(definitions)) {
994
+ const rule = cache[key];
995
+ if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
996
+ const wrapped = parser({ trackLines: true }, rule);
997
+ tagRule(wrapped, key);
998
+ wrapped._meta.grammarTrackLines = true;
999
+ cache[key] = wrapped;
1145
1000
  }
1146
- break;
1147
1001
  }
1148
- break;
1149
1002
  }
1150
- return { end: pos, chunks };
1151
- }
1152
- function tryFastLabeledScan(input, cur, trivia2) {
1153
- const spec = analyzeLabeledTrivia(trivia2);
1154
- if (!spec || spec.arms.length !== 2) return null;
1155
- const wsArm = spec.arms.find((a) => {
1156
- const src = getCoreRegexDef(a.parser)?.source;
1157
- return src != null && !src.includes("\\*");
1158
- });
1159
- const commentArm = spec.arms.find((a) => {
1160
- const src = getCoreRegexDef(a.parser)?.source;
1161
- return src != null && src.includes("\\*");
1003
+ for (const key of Object.keys(definitions)) {
1004
+ markUnusedValues(cache[key]);
1005
+ }
1006
+ Object.defineProperty(cache, RULE_ORDER, {
1007
+ value: Object.keys(definitions),
1008
+ enumerable: false,
1009
+ configurable: true
1162
1010
  });
1163
- if (!wsArm || !commentArm) return null;
1164
- const { chunks } = scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1165
- if (chunks.length < spec.minRepeats) return { end: cur, chunks: [] };
1166
- return scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1167
- }
1168
- function recordTriviaChunks(ctx, chunks) {
1169
- const kinds = ctx.triviaKindLabels;
1170
- const mask = ctx._triviaCaptureMask;
1171
- for (const ch of chunks) {
1172
- pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1173
- if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
1174
- if (mask === void 0 || kinds === void 0 || (mask & 1 << ch.kindIndex) !== 0) {
1175
- pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1176
- }
1177
- }
1178
- }
1179
- }
1180
- function triviaKindMask(labels, keep) {
1181
- if (!labels) return void 0;
1182
- let mask = 0;
1183
- for (const name of keep) {
1184
- const idx = labels.indexOf(name);
1185
- if (idx >= 0) mask |= 1 << idx;
1186
- }
1187
- return mask;
1011
+ attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
1012
+ return cache;
1188
1013
  }
1189
1014
 
1190
- // src/combinators/grammar.ts
1191
- function annotateResultLines(result, index) {
1192
- const annotated = { ...result, span: annotateSpan(result.span, index) };
1193
- const errors = annotated.errors;
1194
- if (errors) {
1195
- ;
1196
- annotated.errors = errors.map((error) => ({
1197
- ...error,
1198
- span: annotateSpan(error.span, index)
1199
- }));
1015
+ // src/recovery/scan.ts
1016
+ function probeAt(sentinel, input, pos, ctx) {
1017
+ const s = {
1018
+ _probe: ctx._probe,
1019
+ _errors: ctx._errors,
1020
+ _cstLeaves: ctx._cstLeaves,
1021
+ _cstChildren: ctx._cstChildren,
1022
+ _cstRawChildren: ctx._cstRawChildren,
1023
+ _cstTriviaLog: ctx._cstTriviaLog,
1024
+ _triviaLog: ctx._triviaLog,
1025
+ _rootTriviaLog: ctx._rootTriviaLog,
1026
+ _fields: ctx._fields,
1027
+ _cstBuf: ctx._cstBuf,
1028
+ _tolerant: ctx._tolerant,
1029
+ _sync: ctx._sync
1030
+ };
1031
+ ctx._probe = void 0;
1032
+ ctx._errors = void 0;
1033
+ ctx._cstLeaves = void 0;
1034
+ ctx._cstChildren = void 0;
1035
+ ctx._cstRawChildren = void 0;
1036
+ ctx._cstTriviaLog = void 0;
1037
+ ctx._triviaLog = void 0;
1038
+ ctx._rootTriviaLog = void 0;
1039
+ ctx._fields = void 0;
1040
+ ctx._cstBuf = void 0;
1041
+ ctx._tolerant = void 0;
1042
+ ctx._sync = void 0;
1043
+ try {
1044
+ return sentinel.parse(input, pos, ctx).ok;
1045
+ } finally {
1046
+ ctx._probe = s._probe;
1047
+ ctx._errors = s._errors;
1048
+ ctx._cstLeaves = s._cstLeaves;
1049
+ ctx._cstChildren = s._cstChildren;
1050
+ ctx._cstRawChildren = s._cstRawChildren;
1051
+ ctx._cstTriviaLog = s._cstTriviaLog;
1052
+ ctx._triviaLog = s._triviaLog;
1053
+ ctx._rootTriviaLog = s._rootTriviaLog;
1054
+ ctx._fields = s._fields;
1055
+ ctx._cstBuf = s._cstBuf;
1056
+ ctx._tolerant = s._tolerant;
1057
+ ctx._sync = s._sync;
1200
1058
  }
1201
- return annotated;
1202
1059
  }
1203
- function createParseLineContext(input, pos) {
1204
- const lineIndex = createLineIndex();
1205
- if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
1206
- return { lineIndex, lineScannedTo: pos };
1060
+ function orSentinel(a, b) {
1061
+ if (b === void 0) return a;
1062
+ return {
1063
+ _tag: "orSentinel",
1064
+ _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
1065
+ _def: { tag: "unknown" },
1066
+ parse(input, pos, ctx) {
1067
+ const ra = a.parse(input, pos, ctx);
1068
+ if (ra.ok) return ra;
1069
+ return b.parse(input, pos, ctx);
1070
+ }
1071
+ };
1207
1072
  }
1208
- function parser(opts, root) {
1209
- const clearTrivia = opts.trivia === null;
1073
+ function firstSetSentinel(fs) {
1074
+ if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
1075
+ const ranges = fs.ranges;
1210
1076
  return {
1211
- _tag: "grammar",
1212
- _meta: root._meta,
1213
- _def: {
1214
- tag: "grammar",
1215
- parser: root,
1216
- triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
1217
- clearTrivia,
1218
- ...opts.captureTrivia ? { captureTrivia: true } : {},
1219
- trackLines: opts.trackLines ?? false
1220
- },
1221
- parse(input, pos, _ctx) {
1222
- const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
1223
- const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
1224
- const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
1225
- const ctx = {
1226
- ..._ctx,
1227
- trackLines,
1228
- ...lineIndex ? { _lineIndex: lineIndex } : {},
1229
- ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
1230
- // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
1231
- ...clearTrivia ? {
1232
- trivia: void 0,
1233
- triviaKindLabels: void 0
1234
- } : opts.trivia != null ? {
1235
- trivia: opts.trivia,
1236
- ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
1237
- } : {},
1238
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
1239
- // Kind-filter for per-node capture. Resolve against this scope's trivia
1240
- // labels — this parser's own trivia if it declares one, else the INHERITED
1241
- // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
1242
- // trivia is inherited rather than re-declared here. No labels → undefined
1243
- // (capture all).
1244
- ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
1245
- };
1246
- const result = root.parse(input, pos ?? 0, ctx);
1247
- if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
1248
- _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
1077
+ _tag: "firstSetSentinel",
1078
+ _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },
1079
+ _def: { tag: "unknown" },
1080
+ parse(input, pos) {
1081
+ const code = pos < input.length ? input.codePointAt(pos) : void 0;
1082
+ if (code !== void 0) {
1083
+ for (const r of ranges) {
1084
+ if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
1085
+ }
1249
1086
  }
1250
- return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
1087
+ return { ok: false, expected: [], span: { start: pos, end: pos } };
1251
1088
  }
1252
1089
  };
1253
1090
  }
1254
-
1255
- // src/combinators/parser.ts
1256
- var RULE_ORDER = "__parsemanRuleOrder";
1257
- function tagRule(r, key) {
1258
- ;
1259
- r._ruleName = key;
1260
- if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
1091
+ function recoverScan(input, from, ctx, sync, expected) {
1092
+ let scanPos = from;
1093
+ while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1094
+ const span = { start: from, end: scanPos };
1095
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
1096
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1097
+ ctx._errors?.push(error);
1098
+ return { error, end: scanPos };
1261
1099
  }
1262
- function isDefinableRef(v) {
1263
- return !!v && typeof v === "object" && "_def" in v && v._def.tag === "lazy" && typeof v.define === "function";
1100
+ function matchesAt(sentinel, input, pos, ctx) {
1101
+ return probeAt(sentinel, input, pos, ctx);
1264
1102
  }
1265
- function ruleNameOf(r) {
1266
- return r._ruleName;
1103
+ function captureError(ctx, error) {
1104
+ if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
1267
1105
  }
1268
- function isNamedRuleRefForAnotherRule(r, key) {
1269
- const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
1270
- return name !== void 0 && name !== key;
1106
+
1107
+ // src/combinators/expect.ts
1108
+ function deriveExpected(c) {
1109
+ return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
1271
1110
  }
1272
- function rules(a, b) {
1273
- const factory = typeof a === "function" ? a : b;
1274
- const options = typeof a === "function" ? b : a;
1275
- const cache = {};
1276
- const proxy = new Proxy(cache, {
1277
- get(target, key) {
1278
- if (typeof key !== "string") return void 0;
1279
- const record = target;
1280
- if (!(key in record)) {
1281
- const r = ref();
1282
- tagRule(r, key);
1283
- record[key] = r;
1111
+ function derive(c, seen) {
1112
+ const deriveExpected2 = (p) => derive(p, seen);
1113
+ const def = c._def;
1114
+ switch (def.tag) {
1115
+ case "literal":
1116
+ return [JSON.stringify(def.value)];
1117
+ case "regex":
1118
+ return [`/${def.source}/`];
1119
+ case "keywords":
1120
+ return def.words.map((w) => JSON.stringify(w));
1121
+ case "label":
1122
+ return [def.label];
1123
+ case "choice":
1124
+ return def.parsers.flatMap(deriveExpected2);
1125
+ case "dispatch":
1126
+ return deriveExpected2(def.selector);
1127
+ // Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
1128
+ // `optional(…)`/`many(…)` can match nothing, so the term after it is equally
1129
+ // able to fail first — deriving from term 0 alone named a token the parse
1130
+ // never actually required. A leading `not(…)` is zero-width and contributes
1131
+ // nothing (its own expectation is about what must NOT be here), but it is
1132
+ // nullable, so keep scanning past it. Stop at the first term that must match.
1133
+ case "sequence": {
1134
+ const out = [];
1135
+ for (const term of def.parsers) {
1136
+ if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
1137
+ if (!matchesEmpty(term)) break;
1284
1138
  }
1285
- return record[key];
1139
+ return out;
1286
1140
  }
1287
- });
1288
- const definitions = factory(proxy);
1289
- for (const key of Object.keys(definitions)) {
1290
- const placeholder = cache[key];
1291
- const parser2 = definitions[key];
1292
- if (placeholder === parser2) {
1293
- throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
1141
+ case "attempt":
1142
+ return deriveExpected2(def.parser);
1143
+ // Delegating wrappers whose first-token failure is their inner parser's — must
1144
+ // pass through so a start-failure (and the first-set-miss fast-path in attempt/
1145
+ // node, which reads this) names the real expected token, not the wrapper tag.
1146
+ case "field":
1147
+ case "withCtx":
1148
+ case "recover":
1149
+ return deriveExpected2(def.parser);
1150
+ case "skip":
1151
+ return deriveExpected2(def.main);
1152
+ case "expect":
1153
+ return def.expected;
1154
+ case "node":
1155
+ case "grammar":
1156
+ case "trivia":
1157
+ case "token":
1158
+ case "leaf":
1159
+ case "optional":
1160
+ case "many":
1161
+ case "oneOrMore":
1162
+ case "transform":
1163
+ case "not":
1164
+ case "peek":
1165
+ return deriveExpected2(def.parser);
1166
+ case "routed":
1167
+ return ["routed()"];
1168
+ case "lazy": {
1169
+ const name = c._ruleName;
1170
+ if (seen.has(c)) return name ? [name] : [];
1171
+ seen.add(c);
1172
+ try {
1173
+ return deriveExpected2(def.thunk());
1174
+ } catch {
1175
+ return name ? [name] : [];
1176
+ } finally {
1177
+ seen.delete(c);
1178
+ }
1294
1179
  }
1295
- if (isDefinableRef(placeholder)) {
1296
- if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
1297
- placeholder.define(parser2);
1298
- placeholder._meta.firstSet = parser2._meta.firstSet;
1299
- placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
1300
- } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
1301
- const alias = ref();
1302
- tagRule(alias, key);
1303
- alias.define(parser2);
1304
- alias._meta.firstSet = parser2._meta.firstSet;
1305
- alias._meta.canMatchNewline = parser2._meta.canMatchNewline;
1306
- cache[key] = alias;
1307
- } else {
1308
- tagRule(parser2, key);
1309
- cache[key] = parser2;
1310
- }
1311
- }
1312
- if (options?.trivia != null) {
1313
- for (const key of Object.keys(definitions)) {
1314
- const rule = cache[key];
1315
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
1316
- }
1317
- }
1318
- if (options?.scanSkip != null) {
1319
- for (const key of Object.keys(definitions)) {
1320
- const rule = cache[key];
1321
- if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
1322
- }
1323
- }
1324
- if (options?.hostMode === "cst") {
1325
- for (const key of Object.keys(definitions)) {
1326
- const rule = cache[key];
1327
- if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
1328
- }
1329
- }
1330
- if (options?.trackLines === true) {
1331
- for (const key of Object.keys(definitions)) {
1332
- const rule = cache[key];
1333
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
1334
- }
1335
- for (const key of Object.keys(definitions)) {
1336
- const rule = cache[key];
1337
- if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
1338
- const wrapped = parser({ trackLines: true }, rule);
1339
- tagRule(wrapped, key);
1340
- wrapped._meta.grammarTrackLines = true;
1341
- cache[key] = wrapped;
1342
- }
1343
- }
1344
- }
1345
- for (const key of Object.keys(definitions)) {
1346
- markUnusedValues(cache[key]);
1347
- }
1348
- Object.defineProperty(cache, RULE_ORDER, {
1349
- value: Object.keys(definitions),
1350
- enumerable: false,
1351
- configurable: true
1352
- });
1353
- return cache;
1354
- }
1355
-
1356
- // src/analysis/duplication.ts
1357
- var WORD_RE_SRC = "-?[A-Za-z][A-Za-z0-9_-]*";
1358
- var WORDS_RE = new RegExp(`^${WORD_RE_SRC}(?:\\|${WORD_RE_SRC})*$`);
1359
-
1360
- // src/combinators/probe.ts
1361
- function failAt(ctx, expected, pos) {
1362
- const r = { ok: false, expected, span: { start: pos, end: pos } };
1363
- const probe = ctx._probe;
1364
- if (probe !== void 0 && pos <= probe.offset) {
1365
- const best = probe.best;
1366
- if (best === null || pos > best.span.start) {
1367
- probe.best = r;
1368
- } else if (pos === best.span.start) {
1369
- probe.best = { ...best, expected: [...best.expected, ...expected] };
1370
- }
1371
- }
1372
- return r;
1373
- }
1374
-
1375
- // src/combinators/literal.ts
1376
- function asciiFoldEq(a, b) {
1377
- if (a.length !== b.length) return false;
1378
- for (let i = 0; i < a.length; i++) {
1379
- let ca = a.charCodeAt(i);
1380
- let cb = b.charCodeAt(i);
1381
- if (ca >= 65 && ca <= 90) ca += 32;
1382
- if (cb >= 65 && cb <= 90) cb += 32;
1383
- if (ca !== cb) return false;
1180
+ default:
1181
+ return [];
1384
1182
  }
1385
- return true;
1386
1183
  }
1387
- function literal(value, opts = {}) {
1388
- const caseInsensitive = opts.caseInsensitive ?? false;
1389
- const firstSet = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
1184
+ function expect(combinator, label2) {
1185
+ const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
1390
1186
  const meta = {
1391
- firstSet,
1392
- canMatchNewline: value.includes("\n"),
1187
+ firstSet: combinator._meta.firstSet,
1188
+ canMatchNewline: combinator._meta.canMatchNewline,
1393
1189
  isTrivia: false
1394
1190
  };
1395
- const canMatchNewline = meta.canMatchNewline;
1396
- if (caseInsensitive) {
1397
- const upper = value.toUpperCase();
1398
- const lower = value.toLowerCase();
1399
- const firstUpper = upper.codePointAt(0);
1400
- const firstLower = lower.codePointAt(0);
1401
- meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
1402
- { lo: firstLower, hi: firstLower },
1403
- { lo: firstUpper, hi: firstUpper }
1404
- ] } : firstSet;
1405
- }
1406
- const expected = [JSON.stringify(value)];
1407
- const parse = !caseInsensitive && value.length === 1 ? (() => {
1408
- const code = value.charCodeAt(0);
1409
- return function parse2(input, pos, ctx) {
1410
- if (input.charCodeAt(pos) === code) {
1411
- const span = { start: pos, end: pos + 1 };
1412
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1413
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
1414
- return { ok: true, value, span };
1415
- }
1416
- return failAt(ctx, expected, pos);
1417
- };
1418
- })() : function parse2(input, pos, ctx) {
1419
- const end = pos + value.length;
1420
- if (end > input.length) {
1421
- return failAt(ctx, expected, pos);
1422
- }
1423
- const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
1424
- if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
1425
- const span = { start: pos, end };
1426
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
1427
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
1428
- return { ok: true, value: matchedValue, span };
1429
- }
1430
- return failAt(ctx, expected, pos);
1431
- };
1432
1191
  return {
1433
- _tag: "literal",
1192
+ _tag: "expect",
1434
1193
  _meta: meta,
1435
- _def: { tag: "literal", value, caseInsensitive },
1436
- parse
1194
+ _def: { tag: "expect", parser: combinator, label: label2, expected },
1195
+ parse(input, pos, ctx) {
1196
+ const result = combinator.parse(input, pos, ctx);
1197
+ if (result.ok) return result;
1198
+ const span = { start: pos, end: pos };
1199
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1200
+ ctx._errors?.push(error);
1201
+ if (ctx._tolerant) captureError(ctx, error);
1202
+ return { ok: true, value: error, span: { start: pos, end: pos } };
1203
+ }
1437
1204
  };
1438
1205
  }
1439
1206
 
@@ -1505,420 +1272,839 @@ function parseClassRanges(body) {
1505
1272
  return ranges.length ? ranges : null;
1506
1273
  }
1507
1274
 
1508
- // src/combinators/regex.ts
1509
- var firstSetAnalyzer = null;
1510
- var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
1511
- var SCAN_BAIL_AT = 64;
1512
- function inRanges(cp, ranges) {
1513
- for (let i = 0; i < ranges.length; i++) {
1514
- const [lo, hi] = ranges[i];
1515
- if (cp >= lo && cp <= hi) return true;
1516
- }
1517
- return false;
1275
+ // src/combinators/trivia-skip.ts
1276
+ var NOOP_COMMIT = () => {
1277
+ };
1278
+ var fastTriviaCache = /* @__PURE__ */ new WeakMap();
1279
+ function needsDeferredTriviaCommit(ctx) {
1280
+ return ctx._triviaLog !== void 0 || ctx._rootTriviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
1518
1281
  }
1519
- function readClassRanges(body) {
1520
- if (body.startsWith("^")) return null;
1521
- return parseClassRanges(body);
1282
+ function saveTriviaMark(ctx) {
1283
+ const m = saveCstMark(ctx);
1284
+ return {
1285
+ raw: m.raw,
1286
+ tlog: m.tlog,
1287
+ leaves: m.leaves,
1288
+ fields: m.fields,
1289
+ errors: m.errors,
1290
+ log: ctx._triviaLog ? ctx._triviaLog.length : 0,
1291
+ rootLog: ctx._rootTriviaLog ? ctx._rootTriviaLog.length : 0
1292
+ };
1522
1293
  }
1523
- function shortScanner(source, flags) {
1524
- if (/[imsuvy]/.test(flags)) return null;
1525
- let ranges = null;
1526
- let quant = "";
1527
- if (source[0] === "[") {
1528
- let end = 1;
1529
- while (end < source.length && source[end] !== "]") {
1530
- if (source[end] === "\\") end += 2;
1531
- else end++;
1294
+ function rollbackTrivia(ctx, mark) {
1295
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1296
+ if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1297
+ if (ctx._rootTriviaLog && ctx._rootTriviaLog.length !== mark.rootLog) ctx._rootTriviaLog.length = mark.rootLog;
1298
+ }
1299
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1300
+ const probeCtx = {
1301
+ trackLines: ctx.trackLines,
1302
+ state: ctx.state,
1303
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1304
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1305
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1306
+ };
1307
+ const result = triviaP.parse(input, cur, probeCtx);
1308
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1309
+ return result;
1310
+ }
1311
+ function saveLookaheadMark(ctx) {
1312
+ return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1313
+ }
1314
+ function rollbackLookahead(ctx, mark) {
1315
+ rollbackTrivia(ctx, mark);
1316
+ if (ctx._probe) ctx._probe.best = mark.probeBest;
1317
+ }
1318
+ function scanWithLabels(input, cur, ctx) {
1319
+ const triviaP = ctx.trivia;
1320
+ const spec = analyzeLabeledTrivia(triviaP);
1321
+ if (!spec) return { end: cur, commit: NOOP_COMMIT };
1322
+ const log = ctx._triviaLog;
1323
+ const rootLog = ctx._rootTriviaLog;
1324
+ const rootKinds = ctx._rootTriviaKindIndex;
1325
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1326
+ const mask = ctx._triviaCaptureMask;
1327
+ let fullRows;
1328
+ let rootRows;
1329
+ let cstRows;
1330
+ const visitedEnd = visitLabeledTrivia(input, cur, spec, ctx.state, (start, matchEnd, kindIndex) => {
1331
+ if (log !== void 0) (fullRows ??= []).push(start, matchEnd, kindIndex);
1332
+ const selectedKind = rootLog === void 0 || rootKinds === void 0 || ctx._rootTriviaCapture === false ? void 0 : rootKinds[spec.labels[kindIndex] ?? ""];
1333
+ if (selectedKind !== void 0) (rootRows ??= []).push(start, matchEnd, selectedKind);
1334
+ if (captureTl && (mask === void 0 || (mask & 1 << kindIndex) !== 0)) {
1335
+ (cstRows ??= []).push(start, matchEnd, kindIndex);
1532
1336
  }
1533
- if (source[end] !== "]") return null;
1534
- ranges = readClassRanges(source.slice(1, end));
1535
- quant = source.slice(end + 1);
1536
- } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
1537
- ranges = shorthandRanges(source[1]);
1538
- quant = source.slice(2);
1337
+ });
1338
+ if (visitedEnd !== void 0) {
1339
+ if (visitedEnd === cur) return { end: cur, commit: NOOP_COMMIT };
1340
+ return {
1341
+ end: visitedEnd,
1342
+ commit: () => {
1343
+ if (fullRows !== void 0 && log !== void 0) log.push(...fullRows);
1344
+ if (rootRows !== void 0 && rootLog !== void 0) {
1345
+ for (let i = 0; i < rootRows.length; i += 3) {
1346
+ rootLog.push(cur, visitedEnd, rootRows[i], rootRows[i + 1], rootRows[i + 2]);
1347
+ }
1348
+ }
1349
+ if (cstRows !== void 0) {
1350
+ for (let i = 0; i < cstRows.length; i += 3) {
1351
+ pushCstTriviaEntry(ctx, cstRows[i], cstRows[i + 1], cstRows[i + 2]);
1352
+ }
1353
+ }
1354
+ }
1355
+ };
1539
1356
  }
1540
- if (!ranges || quant !== "+" && quant !== "*") return null;
1541
- const minOne = quant === "+";
1542
- return (input, pos) => {
1543
- let end = pos;
1544
- while (end < input.length && inRanges(input.charCodeAt(end), ranges)) {
1545
- end++;
1546
- if (end - pos >= SCAN_BAIL_AT) return void 0;
1547
- }
1548
- return minOne && end === pos ? null : end;
1357
+ const { end, chunks } = scanLabeledTriviaChunks(input, cur, spec, ctx.state);
1358
+ if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1359
+ return {
1360
+ end,
1361
+ commit: () => recordTriviaChunks(ctx, chunks)
1549
1362
  };
1550
1363
  }
1551
- function asciiCaseFold(fs) {
1552
- if (fs.kind !== "ranges") return fs;
1553
- const ranges = fs.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
1554
- const add = (lo, hi) => {
1555
- if (lo <= hi) ranges.push({ lo, hi });
1556
- };
1557
- for (const r of fs.ranges) {
1558
- const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
1559
- add(uLo + 32, uHi + 32);
1560
- const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
1561
- add(lLo - 32, lHi - 32);
1562
- }
1563
- ranges.sort((a, b) => a.lo - b.lo);
1564
- const merged = [];
1565
- for (const r of ranges) {
1566
- const last = merged[merged.length - 1];
1567
- if (last && r.lo <= last.hi + 1) last.hi = Math.max(last.hi, r.hi);
1568
- else merged.push({ lo: r.lo, hi: r.hi });
1569
- }
1570
- return { kind: "ranges", ranges: merged };
1364
+ function skipWithLabels(input, cur, ctx) {
1365
+ const spec = analyzeLabeledTrivia(ctx.trivia);
1366
+ if (spec) return scanLabeledTriviaEnd(input, cur, spec, ctx.state);
1367
+ const tr = parseTriviaNoCapture(ctx.trivia, input, cur, ctx);
1368
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1571
1369
  }
1572
- function regex(pattern, flags = "") {
1573
- const source = typeof pattern === "string" ? pattern : pattern.source;
1574
- const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
1575
- const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
1576
- const scan = shortScanner(source, resolvedFlags);
1577
- const raw = (firstSetAnalyzer ?? permissiveFirstSet)(source);
1578
- const firstSet = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
1579
- const canMatchNewline = raw.canMatchNewline;
1580
- const meta = { firstSet, canMatchNewline, isTrivia: false };
1370
+ function advanceTrivia(input, cur, ctx) {
1371
+ const triviaP = ctx.trivia;
1372
+ if (!triviaP) return cur;
1373
+ if (!ctx.trackLines) {
1374
+ const fast2 = fastTriviaScanner(triviaP);
1375
+ if (fast2) return fast2(input, cur);
1376
+ if (ctx.triviaKindLabels) return skipWithLabels(input, cur, ctx);
1377
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1378
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1379
+ }
1380
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1381
+ const fast = fastTriviaScanner(triviaP);
1382
+ if (fast) {
1383
+ const end = fast(input, cur);
1384
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1385
+ return end;
1386
+ }
1387
+ if (ctx.triviaKindLabels) {
1388
+ const end = skipWithLabels(input, cur, ctx);
1389
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1390
+ return end;
1391
+ }
1392
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1393
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1394
+ }
1395
+ function scanTrivia(input, cur, ctx) {
1396
+ const triviaP = ctx.trivia;
1397
+ if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1398
+ const log = ctx._triviaLog;
1399
+ const rootLog = ctx._rootTriviaLog;
1400
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1401
+ if (!ctx.trackLines) {
1402
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1403
+ if (fast2 && log === void 0 && !captureTl) {
1404
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1405
+ }
1406
+ if (ctx.triviaKindLabels && (log !== void 0 || rootLog !== void 0 || captureTl)) {
1407
+ return scanWithLabels(input, cur, ctx);
1408
+ }
1409
+ if (ctx.triviaKindLabels) return { end: skipWithLabels(input, cur, ctx), commit: NOOP_COMMIT };
1410
+ if (log !== void 0 || rootLog !== void 0 || captureTl) {
1411
+ const tr3 = triviaP.parse(input, cur, {
1412
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1413
+ state: ctx.state
1414
+ });
1415
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1416
+ const end = tr3.span.end;
1417
+ return {
1418
+ end,
1419
+ commit: () => {
1420
+ pushTriviaLogEntry(ctx, cur, end);
1421
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1422
+ }
1423
+ };
1424
+ }
1425
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1426
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1427
+ }
1428
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1429
+ const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1430
+ if (fast && log === void 0 && !captureTl) {
1431
+ const end = fast(input, cur);
1432
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1433
+ return { end, commit: NOOP_COMMIT };
1434
+ }
1435
+ if (ctx.triviaKindLabels && (log !== void 0 || rootLog !== void 0 || captureTl)) {
1436
+ const scan = scanWithLabels(input, cur, ctx);
1437
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1438
+ return scan;
1439
+ }
1440
+ if (ctx.triviaKindLabels) {
1441
+ const end = skipWithLabels(input, cur, ctx);
1442
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1443
+ return { end, commit: NOOP_COMMIT };
1444
+ }
1445
+ if (log !== void 0 || rootLog !== void 0 || captureTl) {
1446
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1447
+ if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1448
+ const end = tr2.span.end;
1449
+ return {
1450
+ end,
1451
+ commit: () => {
1452
+ pushTriviaLogEntry(ctx, cur, end);
1453
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1454
+ }
1455
+ };
1456
+ }
1457
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1458
+ return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1459
+ }
1460
+ function consumeTrivia(input, cur, ctx) {
1461
+ if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1462
+ const scan = scanTrivia(input, cur, ctx);
1463
+ scan.commit();
1464
+ return scan.end;
1465
+ }
1466
+ function fastTriviaScanner(trivia2) {
1467
+ const cached = fastTriviaCache.get(trivia2);
1468
+ if (cached !== void 0) return cached;
1469
+ const scanner = buildFastTriviaScanner(trivia2);
1470
+ fastTriviaCache.set(trivia2, scanner);
1471
+ return scanner;
1472
+ }
1473
+ function buildFastTriviaScanner(trivia2) {
1474
+ const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1475
+ const direct = regexTriviaScanner(core);
1476
+ if (direct) return direct;
1477
+ const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1478
+ if (!repeat) return null;
1479
+ const one = regexTriviaScanner(repeat);
1480
+ if (one) return loopScanner([one]);
1481
+ if (repeat._def.tag !== "choice") return null;
1482
+ const arms = repeat._def.parsers.map(regexTriviaScanner);
1483
+ if (arms.some((s) => s === null)) return null;
1484
+ return loopScanner(arms);
1485
+ }
1486
+ function loopScanner(arms) {
1487
+ return (input, cur) => {
1488
+ let pos = cur;
1489
+ scan: while (pos < input.length) {
1490
+ for (const arm of arms) {
1491
+ const end = arm(input, pos);
1492
+ if (end > pos) {
1493
+ pos = end;
1494
+ continue scan;
1495
+ }
1496
+ }
1497
+ break;
1498
+ }
1499
+ return pos;
1500
+ };
1501
+ }
1502
+ function regexTriviaScanner(parser2) {
1503
+ if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1504
+ const source = parser2._def.source;
1505
+ return classRunSource(source) ?? altStarSource(source);
1506
+ }
1507
+ function inRanges(cp, ranges) {
1508
+ for (let i = 0; i < ranges.length; i++) {
1509
+ const r = ranges[i];
1510
+ if (cp >= r[0] && cp <= r[1]) return true;
1511
+ }
1512
+ return false;
1513
+ }
1514
+ function classScanner(classBody) {
1515
+ const ranges = parseClassRanges(classBody);
1516
+ if (!ranges) return null;
1517
+ return (input, cur) => {
1518
+ let pos = cur;
1519
+ while (pos < input.length && inRanges(input.charCodeAt(pos), ranges)) pos++;
1520
+ return pos;
1521
+ };
1522
+ }
1523
+ function untilLineBreakScanner(leaderCode) {
1524
+ return (input, cur) => {
1525
+ if (input.charCodeAt(cur) !== leaderCode) return cur;
1526
+ let pos = cur + 1;
1527
+ while (pos < input.length) {
1528
+ const cc = input.charCodeAt(pos);
1529
+ if (cc === 10 || cc === 13) break;
1530
+ pos++;
1531
+ }
1532
+ return pos;
1533
+ };
1534
+ }
1535
+ function classRunSource(source) {
1536
+ const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1537
+ return m ? classScanner(m[1]) : null;
1538
+ }
1539
+ function classifyTriviaArm(arm) {
1540
+ const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1541
+ if (cls) {
1542
+ const ranges = parseClassRanges(cls[1]);
1543
+ return ranges ? { kind: "class", ranges } : null;
1544
+ }
1545
+ const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1546
+ if (lc) {
1547
+ const marker = lc[1];
1548
+ return { kind: "untilLineBreak", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
1549
+ }
1550
+ return null;
1551
+ }
1552
+ function armScanner(arm) {
1553
+ if (arm.kind === "untilLineBreak") return untilLineBreakScanner(arm.code);
1554
+ const ranges = arm.ranges;
1555
+ return (input, cur) => {
1556
+ let pos = cur;
1557
+ while (pos < input.length && inRanges(input.charCodeAt(pos), ranges)) pos++;
1558
+ return pos;
1559
+ };
1560
+ }
1561
+ function fusedTriviaScanner(ranges, leaders) {
1562
+ const c0 = leaders[0];
1563
+ const single = leaders.length === 1;
1564
+ return (input, cur) => {
1565
+ let pos = cur;
1566
+ const len = input.length;
1567
+ for (; ; ) {
1568
+ const c = input.charCodeAt(pos);
1569
+ if (pos < len && inRanges(c, ranges)) {
1570
+ pos++;
1571
+ continue;
1572
+ }
1573
+ if (single ? c === c0 : leaders.includes(c)) {
1574
+ pos++;
1575
+ while (pos < len) {
1576
+ const cc = input.charCodeAt(pos);
1577
+ if (cc === 10 || cc === 13) break;
1578
+ pos++;
1579
+ }
1580
+ continue;
1581
+ }
1582
+ break;
1583
+ }
1584
+ return pos;
1585
+ };
1586
+ }
1587
+ function splitTopLevelAlts(body) {
1588
+ const arms = [];
1589
+ let inClass = false;
1590
+ let start = 0;
1591
+ for (let i = 0; i < body.length; i++) {
1592
+ const c = body[i];
1593
+ if (c === "\\") {
1594
+ i++;
1595
+ continue;
1596
+ }
1597
+ if (inClass) {
1598
+ if (c === "]") inClass = false;
1599
+ continue;
1600
+ }
1601
+ if (c === "[") inClass = true;
1602
+ else if (c === "(") return null;
1603
+ else if (c === "|") {
1604
+ arms.push(body.slice(start, i));
1605
+ start = i + 1;
1606
+ }
1607
+ }
1608
+ arms.push(body.slice(start));
1609
+ return arms;
1610
+ }
1611
+ function altStarSource(source) {
1612
+ const m = /^\(\?:(.*)\)[*+]$/.exec(source);
1613
+ if (!m) return null;
1614
+ const armSrcs = splitTopLevelAlts(m[1]);
1615
+ if (!armSrcs || armSrcs.length < 2) return null;
1616
+ const arms = [];
1617
+ for (const src of armSrcs) {
1618
+ const arm = classifyTriviaArm(src);
1619
+ if (!arm) return null;
1620
+ arms.push(arm);
1621
+ }
1622
+ const ranges = [];
1623
+ const leaders = [];
1624
+ for (const arm of arms) {
1625
+ if (arm.kind === "class") ranges.push(...arm.ranges);
1626
+ else leaders.push(arm.code);
1627
+ }
1628
+ if (leaders.some((code) => inRanges(code, ranges))) {
1629
+ return loopScanner(arms.map(armScanner));
1630
+ }
1631
+ if (leaders.length === 0) return armScanner({ kind: "class", ranges });
1632
+ return fusedTriviaScanner(ranges, leaders);
1633
+ }
1634
+
1635
+ // src/combinators/choice.ts
1636
+ function choice(...args) {
1637
+ const parsers = args.map((a) => "gate" in a ? a.combinator : a);
1638
+ const gates = args.map((a) => "gate" in a ? a.gate : null);
1639
+ const hasGates = gates.some((g) => g !== null);
1640
+ const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
1641
+ let combined = { kind: "empty" };
1642
+ for (const p of parsers) combined = union(combined, p._meta.firstSet);
1643
+ const meta = {
1644
+ firstSet: combined,
1645
+ canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
1646
+ isTrivia: false,
1647
+ disjoint
1648
+ };
1649
+ const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
1650
+ const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
1651
+ let greedyLitMap = null;
1652
+ let sortedParsers = null;
1653
+ const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
1654
+ if (strategy?.tag === "greedyClassify") {
1655
+ greedyLitMap = /* @__PURE__ */ new Map();
1656
+ for (let i = 0; i < parsers.length; i++) {
1657
+ if (i === strategy.superIndex) continue;
1658
+ const litVal = getCoreLiteralValue(parsers[i]);
1659
+ if (litVal !== null) greedyLitMap.set(litVal, i);
1660
+ }
1661
+ } else if (strategy?.tag === "literalsLongestFirst") {
1662
+ sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
1663
+ }
1581
1664
  return {
1582
- _tag: "regex",
1665
+ _tag: "choice",
1583
1666
  _meta: meta,
1584
- _def: { tag: "regex", source, flags: resolvedFlags },
1667
+ _def: {
1668
+ tag: "choice",
1669
+ parsers,
1670
+ gates,
1671
+ disjoint,
1672
+ strategy: strategy ?? { tag: "firstMatch" },
1673
+ autoNot
1674
+ },
1585
1675
  parse(input, pos, ctx) {
1586
- const scanEnd = scan?.(input, pos);
1587
- if (scanEnd !== void 0) {
1588
- if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
1589
- const value = input.slice(pos, scanEnd);
1590
- const span2 = { start: pos, end: scanEnd };
1591
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
1592
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
1593
- return { ok: true, value, span: span2 };
1676
+ const expected = [];
1677
+ if (disjoint && pos < input.length) {
1678
+ const code = input.codePointAt(pos);
1679
+ let idx = code < 128 ? asciiDispatch[code] : -1;
1680
+ if (idx < 0) {
1681
+ for (let i = 0; i < parsers.length; i++) {
1682
+ if (inFirstSet(code, parsers[i]._meta.firstSet)) {
1683
+ idx = i;
1684
+ break;
1685
+ }
1686
+ }
1687
+ }
1688
+ if (idx >= 0) {
1689
+ const gate = gates[idx];
1690
+ if (gate && !gate(ctx.state)) {
1691
+ return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
1692
+ }
1693
+ const result = parsers[idx].parse(input, pos, ctx);
1694
+ if (result.ok) return result;
1695
+ expected.push(...result.expected);
1696
+ if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
1697
+ return { ok: false, expected, span: { start: pos, end: pos } };
1698
+ }
1699
+ return {
1700
+ ok: false,
1701
+ expected: parsers.flatMap((p) => {
1702
+ const r = p.parse(input, pos, ctx);
1703
+ return r.ok ? [] : r.expected;
1704
+ }),
1705
+ span: { start: pos, end: pos }
1706
+ };
1707
+ }
1708
+ if (strategy?.tag === "greedyClassify") {
1709
+ const superResult = parsers[strategy.superIndex].parse(input, pos, ctx);
1710
+ if (!superResult.ok) return superResult;
1711
+ const end = superResult.span.end;
1712
+ const litIdx = greedyLitMap.get(input.slice(pos, end));
1713
+ if (litIdx !== void 0) {
1714
+ const litVal = getCoreLiteralValue(parsers[litIdx]);
1715
+ const value = applyTransforms(parsers[litIdx], litVal, { start: pos, end });
1716
+ return { ok: true, value, span: { start: pos, end } };
1717
+ }
1718
+ return superResult;
1719
+ }
1720
+ if (strategy?.tag === "literalsLongestFirst") {
1721
+ for (const p of sortedParsers) {
1722
+ const r = p.parse(input, pos, ctx);
1723
+ if (r.ok) return r;
1724
+ expected.push(...r.expected);
1725
+ }
1726
+ return { ok: false, expected, span: { start: pos, end: pos } };
1727
+ }
1728
+ for (let i = 0; i < parsers.length; i++) {
1729
+ if (gates[i] && !gates[i](ctx.state)) continue;
1730
+ const mark = saveTriviaMark(ctx);
1731
+ const result = parsers[i].parse(input, pos, ctx);
1732
+ if (!result.ok) {
1733
+ rollbackTrivia(ctx, mark);
1734
+ expected.push(...result.expected);
1735
+ if (result.committed) return { ok: false, expected, span: result.span, committed: true };
1736
+ continue;
1737
+ }
1738
+ const checks = autoNot[i];
1739
+ if (checks && autoNotFires(input, result.span.end, checks)) {
1740
+ rollbackTrivia(ctx, mark);
1741
+ continue;
1742
+ }
1743
+ return result;
1744
+ }
1745
+ return { ok: false, expected, span: { start: pos, end: pos } };
1746
+ }
1747
+ };
1748
+ }
1749
+ function detectStrategy(parsers) {
1750
+ const regexIndices = [];
1751
+ const literalIndices = [];
1752
+ for (let i = 0; i < parsers.length; i++) {
1753
+ if (getCoreRegexDef(parsers[i]) !== null) regexIndices.push(i);
1754
+ else if (getCoreLiteralValue(parsers[i]) !== null) literalIndices.push(i);
1755
+ }
1756
+ if (regexIndices.length === 1 && literalIndices.length === parsers.length - 1 && literalIndices.length > 0) {
1757
+ const superIndex = regexIndices[0];
1758
+ const regexDef = getCoreRegexDef(parsers[superIndex]);
1759
+ const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
1760
+ const re = new RegExp(regexDef.source, flags);
1761
+ const allSubsumed = literalIndices.every((i) => {
1762
+ const litVal = getCoreLiteralValue(parsers[i]);
1763
+ re.lastIndex = 0;
1764
+ const m = re.exec(litVal);
1765
+ return m !== null && m[0] === litVal;
1766
+ });
1767
+ if (allSubsumed) return { tag: "greedyClassify", superIndex };
1768
+ }
1769
+ if (parsers.length === literalIndices.length) {
1770
+ const sortedIndices = [...literalIndices].sort(
1771
+ (a, b) => getCoreLiteralValue(parsers[b]).length - getCoreLiteralValue(parsers[a]).length
1772
+ );
1773
+ return { tag: "literalsLongestFirst", sortedIndices };
1774
+ }
1775
+ const shared = detectSharedPrefix(parsers);
1776
+ if (shared !== null) return shared;
1777
+ return { tag: "firstMatch" };
1778
+ }
1779
+ function bareLeadingTermKey(term) {
1780
+ const d = term._def;
1781
+ if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
1782
+ if (d.tag === "regex") return `R:${d.source}\0${d.flags}`;
1783
+ return null;
1784
+ }
1785
+ function detectSharedPrefix(parsers) {
1786
+ if (parsers.length < 2) return null;
1787
+ let key = null;
1788
+ let prefix = null;
1789
+ const members = [];
1790
+ for (let i = 0; i < parsers.length; i++) {
1791
+ const term0 = leadingTermOfArm(parsers[i]);
1792
+ if (term0 === null) return null;
1793
+ const k = bareLeadingTermKey(term0);
1794
+ if (k === null) return null;
1795
+ if (key === null) {
1796
+ key = k;
1797
+ prefix = term0;
1798
+ } else if (k !== key) return null;
1799
+ members.push(i);
1800
+ }
1801
+ if (prefix === null || members.length < 2) return null;
1802
+ return { tag: "sharedPrefix", prefix, members };
1803
+ }
1804
+ function leadingTermOfArm(arm) {
1805
+ let d = arm._def;
1806
+ for (; ; ) {
1807
+ if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
1808
+ d = d.parser._def;
1809
+ continue;
1810
+ }
1811
+ break;
1812
+ }
1813
+ if (d.tag !== "sequence" || d.parsers.length < 2) return null;
1814
+ return d.parsers[0];
1815
+ }
1816
+ function computeAutoNot(parsers) {
1817
+ return parsers.map((p, i) => {
1818
+ const litVal = getCoreLiteralValue(p);
1819
+ if (litVal === null) return null;
1820
+ const checks = [];
1821
+ for (let j2 = i + 1; j2 < parsers.length; j2++) {
1822
+ const other = parsers[j2];
1823
+ const otherLit = getCoreLiteralValue(other);
1824
+ if (otherLit !== null && otherLit.startsWith(litVal) && otherLit.length > litVal.length) {
1825
+ checks.push({ kind: "startsWith", value: otherLit.slice(litVal.length) });
1826
+ continue;
1594
1827
  }
1595
- anchored.lastIndex = pos;
1596
- const m = anchored.exec(input);
1597
- if (m === null) {
1598
- return failAt(ctx, [`/${source}/`], pos);
1828
+ const regexDef = getCoreRegexDef(other);
1829
+ if (regexDef !== null) {
1830
+ const contSet = continuationFirstSet(litVal, regexDef.source, regexDef.flags);
1831
+ if (contSet !== null) checks.push({ kind: "firstSet", set: contSet });
1599
1832
  }
1600
- const span = { start: pos, end: pos + m[0].length };
1601
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1602
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
1603
- return { ok: true, value: m[0], span };
1604
1833
  }
1605
- };
1834
+ return checks.length > 0 ? checks : null;
1835
+ });
1606
1836
  }
1607
-
1608
- // src/combinators/trivia-skip.ts
1609
- var NOOP_COMMIT = () => {
1610
- };
1611
- var fastTriviaCache = /* @__PURE__ */ new WeakMap();
1612
- function needsDeferredTriviaCommit(ctx) {
1613
- return ctx._triviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
1837
+ function autoNotFires(input, end, checks) {
1838
+ for (const check of checks) {
1839
+ if (check.kind === "firstSet") {
1840
+ const code = end < input.length ? input.codePointAt(end) ?? -1 : -1;
1841
+ if (inFirstSet(code, check.set)) return true;
1842
+ } else {
1843
+ if (input.startsWith(check.value, end)) return true;
1844
+ }
1845
+ }
1846
+ return false;
1614
1847
  }
1615
- function saveTriviaMark(ctx) {
1616
- const m = saveCstMark(ctx);
1617
- return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, errors: m.errors, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1848
+ function getCoreLiteralValue(p) {
1849
+ const def = p._def;
1850
+ if (def.tag === "literal" && !def.caseInsensitive) return def.value;
1851
+ if (def.tag === "transform") return getCoreLiteralValue(def.parser);
1852
+ return null;
1618
1853
  }
1619
- function rollbackTrivia(ctx, mark) {
1620
- rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1621
- if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1854
+ function getCoreRegexDef(p) {
1855
+ const def = p._def;
1856
+ if (def.tag === "regex") return { source: def.source, flags: def.flags };
1857
+ if (def.tag === "transform") return getCoreRegexDef(def.parser);
1858
+ if (def.tag === "label") return getCoreRegexDef(def.parser);
1859
+ return null;
1622
1860
  }
1623
- function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1624
- const probeCtx = {
1625
- trackLines: ctx.trackLines,
1626
- state: ctx.state,
1627
- ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1628
- ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1629
- ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1630
- };
1631
- const result = triviaP.parse(input, cur, probeCtx);
1632
- ctx._lineScannedTo = probeCtx._lineScannedTo;
1633
- return result;
1861
+ function applyTransforms(p, value, span) {
1862
+ const def = p._def;
1863
+ if (def.tag === "transform") {
1864
+ const inner = applyTransforms(def.parser, value, span);
1865
+ return def.fn(inner, span);
1866
+ }
1867
+ return value;
1634
1868
  }
1635
- function saveLookaheadMark(ctx) {
1636
- return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1869
+ function continuationFirstSet(lit, source, flags) {
1870
+ const re = new RegExp(source, "y" + flags.replace(/[gy]/g, ""));
1871
+ re.lastIndex = 0;
1872
+ const base = re.exec(lit);
1873
+ if (!base || base[0] !== lit) return null;
1874
+ const contCodes = [];
1875
+ for (let code = 1; code < 128; code++) {
1876
+ re.lastIndex = 0;
1877
+ const m = re.exec(lit + String.fromCharCode(code));
1878
+ if (m && m[0].length > lit.length) contCodes.push(code);
1879
+ }
1880
+ if (contCodes.length === 0) return null;
1881
+ return codesToFirstSet(contCodes);
1637
1882
  }
1638
- function rollbackLookahead(ctx, mark) {
1639
- rollbackTrivia(ctx, mark);
1640
- if (ctx._probe) ctx._probe.best = mark.probeBest;
1883
+ function codesToFirstSet(codes) {
1884
+ codes.sort((a, b) => a - b);
1885
+ const ranges = [];
1886
+ let lo = codes[0], hi = codes[0];
1887
+ for (let i = 1; i < codes.length; i++) {
1888
+ if (codes[i] === hi + 1) {
1889
+ hi = codes[i];
1890
+ } else {
1891
+ ranges.push({ lo, hi });
1892
+ lo = hi = codes[i];
1893
+ }
1894
+ }
1895
+ ranges.push({ lo, hi });
1896
+ return { kind: "ranges", ranges };
1641
1897
  }
1642
- function scanWithLabels(input, cur, ctx) {
1643
- const triviaP = ctx.trivia;
1644
- const spec = analyzeLabeledTrivia(triviaP);
1645
- if (!spec) return { end: cur, commit: NOOP_COMMIT };
1646
- const fast = tryFastLabeledScan(input, cur, triviaP);
1647
- const { end, chunks } = fast ?? scanLabeledTriviaChunks(input, cur, spec);
1648
- if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1649
- return {
1650
- end,
1651
- commit: () => recordTriviaChunks(ctx, chunks)
1652
- };
1898
+ function inFirstSet(code, fs) {
1899
+ if (fs.kind === "any") return true;
1900
+ if (fs.kind === "empty") return false;
1901
+ for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
1902
+ return false;
1653
1903
  }
1654
- function advanceTrivia(input, cur, ctx) {
1655
- const triviaP = ctx.trivia;
1656
- if (!triviaP) return cur;
1657
- if (!ctx.trackLines) {
1658
- const fast2 = fastTriviaScanner(triviaP);
1659
- if (fast2) return fast2(input, cur);
1660
- if (ctx.triviaKindLabels) {
1661
- const scan = scanWithLabels(input, cur, ctx);
1662
- return scan.end;
1904
+ function areDisjoint(sets) {
1905
+ if (sets.some((s) => s.kind === "any")) return false;
1906
+ for (let i = 0; i < sets.length; i++)
1907
+ for (let j2 = i + 1; j2 < sets.length; j2++)
1908
+ if (intersects(sets[i], sets[j2])) return false;
1909
+ return true;
1910
+ }
1911
+ function buildAsciiDispatch(parsers) {
1912
+ const table = Array(128).fill(-1);
1913
+ for (let i = 0; i < parsers.length; i++) {
1914
+ const fs = parsers[i]._meta.firstSet;
1915
+ if (fs.kind !== "ranges") continue;
1916
+ for (const { lo, hi } of fs.ranges) {
1917
+ for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
1918
+ table[code] = i;
1919
+ }
1663
1920
  }
1664
- const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1665
- return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1666
- }
1667
- const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1668
- const fast = fastTriviaScanner(triviaP);
1669
- if (fast) {
1670
- const end = fast(input, cur);
1671
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1672
- return end;
1673
- }
1674
- if (ctx.triviaKindLabels) {
1675
- const scan = scanWithLabels(input, cur, ctx);
1676
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1677
- return scan.end;
1678
1921
  }
1679
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1680
- return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1922
+ return table;
1681
1923
  }
1682
- function scanTrivia(input, cur, ctx) {
1683
- const triviaP = ctx.trivia;
1684
- if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1685
- const log = ctx._triviaLog;
1686
- const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1687
- if (!ctx.trackLines) {
1688
- const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1689
- if (fast2 && log === void 0 && !captureTl) {
1690
- return { end: fast2(input, cur), commit: NOOP_COMMIT };
1691
- }
1692
- if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1693
- return scanWithLabels(input, cur, ctx);
1694
- }
1695
- if (log !== void 0 || captureTl) {
1696
- const tr3 = triviaP.parse(input, cur, {
1697
- trackLines: log !== void 0 ? false : ctx.trackLines,
1698
- state: ctx.state
1699
- });
1700
- if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1701
- const end = tr3.span.end;
1702
- return {
1703
- end,
1704
- commit: () => {
1705
- pushTriviaLogEntry(ctx, cur, end);
1706
- if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1707
- }
1708
- };
1924
+
1925
+ // src/analysis/duplication.ts
1926
+ var WORD_RE_SRC = "-?[A-Za-z][A-Za-z0-9_-]*";
1927
+ var WORDS_RE = new RegExp(`^${WORD_RE_SRC}(?:\\|${WORD_RE_SRC})*$`);
1928
+
1929
+ // src/combinators/probe.ts
1930
+ function failAt(ctx, expected, pos) {
1931
+ const r = { ok: false, expected, span: { start: pos, end: pos } };
1932
+ const probe = ctx._probe;
1933
+ if (probe !== void 0 && pos <= probe.offset) {
1934
+ const best = probe.best;
1935
+ if (best === null || pos > best.span.start) {
1936
+ probe.best = r;
1937
+ } else if (pos === best.span.start) {
1938
+ probe.best = { ...best, expected: [...best.expected, ...expected] };
1709
1939
  }
1710
- const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1711
- return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1712
- }
1713
- const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1714
- const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1715
- if (fast && log === void 0 && !captureTl) {
1716
- const end = fast(input, cur);
1717
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1718
- return { end, commit: NOOP_COMMIT };
1719
- }
1720
- if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1721
- const scan = scanWithLabels(input, cur, ctx);
1722
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1723
- return scan;
1724
- }
1725
- if (log !== void 0 || captureTl) {
1726
- const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1727
- if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1728
- const end = tr2.span.end;
1729
- return {
1730
- end,
1731
- commit: () => {
1732
- pushTriviaLogEntry(ctx, cur, end);
1733
- if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1734
- }
1735
- };
1736
1940
  }
1737
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1738
- return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1739
- }
1740
- function consumeTrivia(input, cur, ctx) {
1741
- if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1742
- const scan = scanTrivia(input, cur, ctx);
1743
- scan.commit();
1744
- return scan.end;
1745
- }
1746
- function fastTriviaScanner(trivia2) {
1747
- const cached = fastTriviaCache.get(trivia2);
1748
- if (cached !== void 0) return cached;
1749
- const scanner = buildFastTriviaScanner(trivia2);
1750
- fastTriviaCache.set(trivia2, scanner);
1751
- return scanner;
1941
+ return r;
1752
1942
  }
1753
- function buildFastTriviaScanner(trivia2) {
1754
- const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1755
- const direct = regexTriviaScanner(core);
1756
- if (direct) return direct;
1757
- const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1758
- if (!repeat) return null;
1759
- const one = regexTriviaScanner(repeat);
1760
- if (one) return loopScanner([one]);
1761
- if (repeat._def.tag !== "choice") return null;
1762
- const arms = repeat._def.parsers.map(regexTriviaScanner);
1763
- if (arms.some((s) => s === null)) return null;
1764
- return loopScanner(arms);
1943
+
1944
+ // src/combinators/literal.ts
1945
+ function asciiFoldEq(a, b) {
1946
+ if (a.length !== b.length) return false;
1947
+ for (let i = 0; i < a.length; i++) {
1948
+ let ca = a.charCodeAt(i);
1949
+ let cb = b.charCodeAt(i);
1950
+ if (ca >= 65 && ca <= 90) ca += 32;
1951
+ if (cb >= 65 && cb <= 90) cb += 32;
1952
+ if (ca !== cb) return false;
1953
+ }
1954
+ return true;
1765
1955
  }
1766
- function loopScanner(arms) {
1767
- return (input, cur) => {
1768
- let pos = cur;
1769
- scan: while (pos < input.length) {
1770
- for (const arm of arms) {
1771
- const end = arm(input, pos);
1772
- if (end > pos) {
1773
- pos = end;
1774
- continue scan;
1775
- }
1956
+ function literal(value, opts = {}) {
1957
+ const caseInsensitive = opts.caseInsensitive ?? false;
1958
+ const firstSet = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
1959
+ const meta = {
1960
+ firstSet,
1961
+ canMatchNewline: value.includes("\n"),
1962
+ isTrivia: false
1963
+ };
1964
+ const canMatchNewline = meta.canMatchNewline;
1965
+ if (caseInsensitive) {
1966
+ const upper = value.toUpperCase();
1967
+ const lower = value.toLowerCase();
1968
+ const firstUpper = upper.codePointAt(0);
1969
+ const firstLower = lower.codePointAt(0);
1970
+ meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
1971
+ { lo: firstLower, hi: firstLower },
1972
+ { lo: firstUpper, hi: firstUpper }
1973
+ ] } : firstSet;
1974
+ }
1975
+ const expected = [JSON.stringify(value)];
1976
+ const parse = !caseInsensitive && value.length === 1 ? (() => {
1977
+ const code = value.charCodeAt(0);
1978
+ return function parse2(input, pos, ctx) {
1979
+ if (input.charCodeAt(pos) === code) {
1980
+ const span = { start: pos, end: pos + 1 };
1981
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1982
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
1983
+ return { ok: true, value, span };
1776
1984
  }
1777
- break;
1985
+ return failAt(ctx, expected, pos);
1986
+ };
1987
+ })() : function parse2(input, pos, ctx) {
1988
+ const end = pos + value.length;
1989
+ if (end > input.length) {
1990
+ return failAt(ctx, expected, pos);
1778
1991
  }
1779
- return pos;
1992
+ const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
1993
+ if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
1994
+ const span = { start: pos, end };
1995
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
1996
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
1997
+ return { ok: true, value: matchedValue, span };
1998
+ }
1999
+ return failAt(ctx, expected, pos);
2000
+ };
2001
+ return {
2002
+ _tag: "literal",
2003
+ _meta: meta,
2004
+ _def: { tag: "literal", value, caseInsensitive },
2005
+ parse
1780
2006
  };
1781
2007
  }
1782
- function regexTriviaScanner(parser2) {
1783
- if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1784
- const source = parser2._def.source;
1785
- return classRunSource(source) ?? altStarSource(source) ?? (blockCommentSource(source) ? scanBlockComment : null);
1786
- }
2008
+
2009
+ // src/combinators/regex.ts
2010
+ var firstSetAnalyzer = null;
2011
+ var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
2012
+ var SCAN_BAIL_AT = 64;
1787
2013
  function inRanges2(cp, ranges) {
1788
2014
  for (let i = 0; i < ranges.length; i++) {
1789
- const r = ranges[i];
1790
- if (cp >= r[0] && cp <= r[1]) return true;
2015
+ const [lo, hi] = ranges[i];
2016
+ if (cp >= lo && cp <= hi) return true;
1791
2017
  }
1792
2018
  return false;
1793
2019
  }
1794
- function classScanner(classBody) {
1795
- const ranges = parseClassRanges(classBody);
1796
- if (!ranges) return null;
1797
- return (input, cur) => {
1798
- let pos = cur;
1799
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1800
- return pos;
1801
- };
2020
+ function readClassRanges(body) {
2021
+ if (body.startsWith("^")) return null;
2022
+ return parseClassRanges(body);
1802
2023
  }
1803
- function lineCommentScanner(commentCode) {
1804
- return (input, cur) => {
1805
- if (input.charCodeAt(cur) !== commentCode) return cur;
1806
- let pos = cur + 1;
1807
- while (pos < input.length) {
1808
- const cc = input.charCodeAt(pos);
1809
- if (cc === 10 || cc === 13) break;
1810
- pos++;
2024
+ function shortScanner(source, flags) {
2025
+ if (/[imsuvy]/.test(flags)) return null;
2026
+ let ranges = null;
2027
+ let quant = "";
2028
+ if (source[0] === "[") {
2029
+ let end = 1;
2030
+ while (end < source.length && source[end] !== "]") {
2031
+ if (source[end] === "\\") end += 2;
2032
+ else end++;
1811
2033
  }
1812
- return pos;
2034
+ if (source[end] !== "]") return null;
2035
+ ranges = readClassRanges(source.slice(1, end));
2036
+ quant = source.slice(end + 1);
2037
+ } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
2038
+ ranges = shorthandRanges(source[1]);
2039
+ quant = source.slice(2);
2040
+ }
2041
+ if (!ranges || quant !== "+" && quant !== "*") return null;
2042
+ const minOne = quant === "+";
2043
+ return (input, pos) => {
2044
+ let end = pos;
2045
+ while (end < input.length && inRanges2(input.charCodeAt(end), ranges)) {
2046
+ end++;
2047
+ if (end - pos >= SCAN_BAIL_AT) return void 0;
2048
+ }
2049
+ return minOne && end === pos ? null : end;
1813
2050
  };
1814
2051
  }
1815
- function classRunSource(source) {
1816
- const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1817
- return m ? classScanner(m[1]) : null;
1818
- }
1819
- function classifyTriviaArm(arm) {
1820
- const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1821
- if (cls) {
1822
- const ranges = parseClassRanges(cls[1]);
1823
- return ranges ? { kind: "class", ranges } : null;
2052
+ function asciiCaseFold(fs) {
2053
+ if (fs.kind !== "ranges") return fs;
2054
+ const ranges = fs.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
2055
+ const add = (lo, hi) => {
2056
+ if (lo <= hi) ranges.push({ lo, hi });
2057
+ };
2058
+ for (const r of fs.ranges) {
2059
+ const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
2060
+ add(uLo + 32, uHi + 32);
2061
+ const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
2062
+ add(lLo - 32, lHi - 32);
1824
2063
  }
1825
- const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1826
- if (lc) {
1827
- const marker = lc[1];
1828
- return { kind: "comment", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
2064
+ ranges.sort((a, b) => a.lo - b.lo);
2065
+ const merged = [];
2066
+ for (const r of ranges) {
2067
+ const last = merged[merged.length - 1];
2068
+ if (last && r.lo <= last.hi + 1) last.hi = Math.max(last.hi, r.hi);
2069
+ else merged.push({ lo: r.lo, hi: r.hi });
1829
2070
  }
1830
- return null;
1831
- }
1832
- function armScanner(arm) {
1833
- if (arm.kind === "comment") return lineCommentScanner(arm.code);
1834
- const ranges = arm.ranges;
1835
- return (input, cur) => {
1836
- let pos = cur;
1837
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1838
- return pos;
1839
- };
2071
+ return { kind: "ranges", ranges: merged };
1840
2072
  }
1841
- function fusedTriviaScanner(ranges, commentCodes) {
1842
- const c0 = commentCodes[0];
1843
- const single = commentCodes.length === 1;
1844
- return (input, cur) => {
1845
- let pos = cur;
1846
- const len = input.length;
1847
- for (; ; ) {
1848
- const c = input.charCodeAt(pos);
1849
- if (pos < len && inRanges2(c, ranges)) {
1850
- pos++;
1851
- continue;
2073
+ function regex(pattern, flags = "") {
2074
+ const source = typeof pattern === "string" ? pattern : pattern.source;
2075
+ const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
2076
+ const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
2077
+ const scan = shortScanner(source, resolvedFlags);
2078
+ const raw = (firstSetAnalyzer ?? permissiveFirstSet)(source);
2079
+ const firstSet = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
2080
+ const canMatchNewline = raw.canMatchNewline;
2081
+ const meta = { firstSet, canMatchNewline, isTrivia: false };
2082
+ return {
2083
+ _tag: "regex",
2084
+ _meta: meta,
2085
+ _def: { tag: "regex", source, flags: resolvedFlags },
2086
+ parse(input, pos, ctx) {
2087
+ const scanEnd = scan?.(input, pos);
2088
+ if (scanEnd !== void 0) {
2089
+ if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
2090
+ const value = input.slice(pos, scanEnd);
2091
+ const span2 = { start: pos, end: scanEnd };
2092
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
2093
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
2094
+ return { ok: true, value, span: span2 };
1852
2095
  }
1853
- if (single ? c === c0 : commentCodes.includes(c)) {
1854
- pos++;
1855
- while (pos < len) {
1856
- const cc = input.charCodeAt(pos);
1857
- if (cc === 10 || cc === 13) break;
1858
- pos++;
1859
- }
1860
- continue;
2096
+ anchored.lastIndex = pos;
2097
+ const m = anchored.exec(input);
2098
+ if (m === null) {
2099
+ return failAt(ctx, [`/${source}/`], pos);
1861
2100
  }
1862
- break;
2101
+ const span = { start: pos, end: pos + m[0].length };
2102
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
2103
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
2104
+ return { ok: true, value: m[0], span };
1863
2105
  }
1864
- return pos;
1865
2106
  };
1866
2107
  }
1867
- function splitTopLevelAlts(body) {
1868
- const arms = [];
1869
- let inClass = false;
1870
- let start = 0;
1871
- for (let i = 0; i < body.length; i++) {
1872
- const c = body[i];
1873
- if (c === "\\") {
1874
- i++;
1875
- continue;
1876
- }
1877
- if (inClass) {
1878
- if (c === "]") inClass = false;
1879
- continue;
1880
- }
1881
- if (c === "[") inClass = true;
1882
- else if (c === "(") return null;
1883
- else if (c === "|") {
1884
- arms.push(body.slice(start, i));
1885
- start = i + 1;
1886
- }
1887
- }
1888
- arms.push(body.slice(start));
1889
- return arms;
1890
- }
1891
- function altStarSource(source) {
1892
- const m = /^\(\?:(.*)\)[*+]$/.exec(source);
1893
- if (!m) return null;
1894
- const armSrcs = splitTopLevelAlts(m[1]);
1895
- if (!armSrcs || armSrcs.length < 2) return null;
1896
- const arms = [];
1897
- for (const src of armSrcs) {
1898
- const arm = classifyTriviaArm(src);
1899
- if (!arm) return null;
1900
- arms.push(arm);
1901
- }
1902
- const ranges = [];
1903
- const commentCodes = [];
1904
- for (const arm of arms) {
1905
- if (arm.kind === "class") ranges.push(...arm.ranges);
1906
- else commentCodes.push(arm.code);
1907
- }
1908
- if (commentCodes.some((code) => inRanges2(code, ranges))) {
1909
- return loopScanner(arms.map(armScanner));
1910
- }
1911
- if (commentCodes.length === 0) return armScanner({ kind: "class", ranges });
1912
- return fusedTriviaScanner(ranges, commentCodes);
1913
- }
1914
- function blockCommentSource(source) {
1915
- return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
1916
- }
1917
- function scanBlockComment(input, cur) {
1918
- if (input.charCodeAt(cur) !== 47 || input.charCodeAt(cur + 1) !== 42) return cur;
1919
- const close = input.indexOf("*/", cur + 2);
1920
- return close === -1 ? cur : close + 2;
1921
- }
1922
2108
 
1923
2109
  // src/combinators/sequence.ts
1924
2110
  function sequence(...parsers) {
@@ -2334,6 +2520,25 @@ function trivia(combinator) {
2334
2520
  parse: combinator.parse.bind(combinator)
2335
2521
  };
2336
2522
  }
2523
+ function classifiedTrivia(arms) {
2524
+ const entries = Object.entries(arms);
2525
+ if (entries.length === 0) {
2526
+ throw new TypeError("classifiedTrivia() requires at least one named trivia arm.");
2527
+ }
2528
+ for (let i = 0; i < entries.length; i++) {
2529
+ const [name, arm] = entries[i];
2530
+ const first = arm._meta.firstSet;
2531
+ if (matchesEmpty(arm) || first.kind !== "ranges" || first.ranges.length === 0) {
2532
+ throw new TypeError(
2533
+ `classifiedTrivia(): ${JSON.stringify(name)} must be non-nullable with a concrete finite first set.`
2534
+ );
2535
+ }
2536
+ }
2537
+ const labeledArms = entries.map(([name, arm]) => label(name, arm));
2538
+ const result = trivia(oneOrMore(choice(...labeledArms)));
2539
+ result._meta.rootTriviaClassified = true;
2540
+ return result;
2541
+ }
2337
2542
  function label(name, combinator) {
2338
2543
  return {
2339
2544
  _tag: combinator._tag,
@@ -2427,8 +2632,17 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2427
2632
  };
2428
2633
  }
2429
2634
 
2635
+ // src/cst/trivia-entries.ts
2636
+ var EMPTY_INDICES = Object.freeze([]);
2637
+ var EMPTY_GAPS = Object.freeze([]);
2638
+ var SELECTED_ROOT_STRIDE = 5;
2639
+
2430
2640
  // src/compiler/scannable-run.ts
2431
2641
  var META = new Set("()[]{}*+?|^$.".split(""));
2642
+ var SELECTED_ROOT_OWNED_RANGE_PLACEHOLDER = Array.from(
2643
+ { length: SELECTED_ROOT_STRIDE - 3 },
2644
+ () => "0"
2645
+ ).join(", ");
2432
2646
 
2433
2647
  // src/compiler/trivia-fast-path.ts
2434
2648
  var CAP_RECORD = [
@@ -2437,6 +2651,11 @@ var CAP_RECORD = [
2437
2651
  ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
2438
2652
  ` }`
2439
2653
  ].join("\n");
2654
+ var SELECTED_ROOT_FINISH = [
2655
+ ` if (_cap && _ctx._rootTriviaCapture !== false && _ctx._rootTriviaLog !== undefined && _ctx._rootTriviaLog.length !== _rootMark) {`,
2656
+ ` for (let _ri = _rootMark; _ri < _ctx._rootTriviaLog.length; _ri += ${SELECTED_ROOT_STRIDE}) { _ctx._rootTriviaLog[_ri] = _pos; _ctx._rootTriviaLog[_ri + 1] = _e }`,
2657
+ ` }`
2658
+ ].join("\n");
2440
2659
 
2441
2660
  // src/compiler/build-arity.ts
2442
2661
  function cstOutputHost(build) {
@@ -2876,10 +3095,12 @@ function attempt(parser2) {
2876
3095
  }
2877
3096
  const mark = saveCstMark(ctx);
2878
3097
  const log = ctx._triviaLog?.length;
3098
+ const rootLog = ctx._rootTriviaLog?.length;
2879
3099
  const result = parser2.parse(input, pos, ctx);
2880
3100
  if (result.ok) return result;
2881
3101
  rollbackCstCapture(ctx, mark);
2882
3102
  if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
3103
+ if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
2883
3104
  if (result.committed) return result;
2884
3105
  return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
2885
3106
  }
@@ -2976,13 +3197,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2976
3197
  const collapse = opts?.collapse === true;
2977
3198
  const captureTrivia = opts?.captureTrivia === true;
2978
3199
  const trailingTrivia = opts?.trailingTrivia === true;
3200
+ const tags = opts?.tags;
2979
3201
  if (unwrap && collapse) {
2980
3202
  throw new Error("node() options cannot set both unwrap and collapse");
2981
3203
  }
2982
3204
  if (project !== void 0 && (unwrap || collapse)) {
2983
3205
  throw new Error("node() options cannot combine project with unwrap or collapse");
2984
3206
  }
2985
- 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;
3207
+ 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;
2986
3208
  const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
2987
3209
  const clonesState = build ? buildReadsState(def) : project === void 0;
2988
3210
  const hasOwnFields = parserHasOwnFields(combinator);
@@ -3028,7 +3250,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3028
3250
  const cstOutput = cstOutputHost(ctx.build);
3029
3251
  const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3030
3252
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3031
- 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 };
3253
+ 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 };
3032
3254
  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 };
3033
3255
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3034
3256
  pushCstChild(ctx, built, rawEntry);
@@ -3058,6 +3280,7 @@ function token(root) {
3058
3280
  const savedRaw = ctx._cstRawChildren;
3059
3281
  const savedTriviaLog = ctx._cstTriviaLog;
3060
3282
  const savedOuterTriviaLog = ctx._triviaLog;
3283
+ const savedRootTriviaLog = ctx._rootTriviaLog;
3061
3284
  const wasCapturing = cstCaptureActive(ctx);
3062
3285
  ctx.trivia = void 0;
3063
3286
  ctx.triviaKindLabels = void 0;
@@ -3067,6 +3290,7 @@ function token(root) {
3067
3290
  ctx._cstRawChildren = void 0;
3068
3291
  ctx._cstTriviaLog = void 0;
3069
3292
  delete ctx._triviaLog;
3293
+ delete ctx._rootTriviaLog;
3070
3294
  let result;
3071
3295
  try {
3072
3296
  result = root.parse(input, pos, ctx);
@@ -3080,6 +3304,8 @@ function token(root) {
3080
3304
  ctx._cstTriviaLog = savedTriviaLog;
3081
3305
  if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
3082
3306
  else ctx._triviaLog = savedOuterTriviaLog;
3307
+ if (savedRootTriviaLog === void 0) delete ctx._rootTriviaLog;
3308
+ else ctx._rootTriviaLog = savedRootTriviaLog;
3083
3309
  }
3084
3310
  if (!result.ok) return result;
3085
3311
  const span = result.span;
@@ -3259,6 +3485,7 @@ function evalRuleMapIR(ir) {
3259
3485
  "transform",
3260
3486
  "skip",
3261
3487
  "trivia",
3488
+ "classifiedTrivia",
3262
3489
  "label",
3263
3490
  "field",
3264
3491
  "expect",
@@ -3299,6 +3526,7 @@ function evalRuleMapIR(ir) {
3299
3526
  transform,
3300
3527
  skip,
3301
3528
  trivia,
3529
+ classifiedTrivia,
3302
3530
  label,
3303
3531
  field,
3304
3532
  expect,
@@ -3320,15 +3548,16 @@ function normalizeCstCollapse(collapse) {
3320
3548
  }
3321
3549
  return typeof collapse === "function" ? collapse : void 0;
3322
3550
  }
3323
- function buildCstNode(type, children, _fields, span, _rawChildren, _triviaLog, state) {
3324
- return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children] };
3551
+ function buildCstNode(type, children, _fields, span, _rawChildren, _triviaLog, state, tags) {
3552
+ 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] };
3325
3553
  }
3326
- function cstBuildHost(typeOrOptions, children, _fields, span, rawChildren, triviaLog, state) {
3554
+ function cstBuildHost(typeOrOptions, children, _fields, span, rawChildren, triviaLog, state, _tags) {
3327
3555
  if (typeof typeOrOptions === "string") {
3328
3556
  return buildCstNode(typeOrOptions, children ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
3329
3557
  }
3330
3558
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
3331
- const host = (type, children2, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children2 ?? [], fields, span2, rawChildren2, triviaLog2, state2);
3559
+ const materializeTags = typeOrOptions?.tags === true;
3560
+ const host = (type, children2, fields, span2, rawChildren2, triviaLog2, state2, tags) => buildCstNode(type, children2 ?? [], fields, span2, rawChildren2, triviaLog2, state2, materializeTags ? tags : void 0);
3332
3561
  host._parsemanCstOutput = true;
3333
3562
  if (collapse) host._parsemanCstCollapse = collapse;
3334
3563
  return host;