parseman 0.43.0 → 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 (52) 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/token.d.ts +3 -2
  8. package/dist/combinators/token.d.ts.map +1 -1
  9. package/dist/combinators/trivia-skip.d.ts +1 -0
  10. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  11. package/dist/compiler/codegen.d.ts.map +1 -1
  12. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  13. package/dist/compiler/scannable-run.d.ts +1 -1
  14. package/dist/compiler/scannable-run.d.ts.map +1 -1
  15. package/dist/compiler/trivia-fast-path.d.ts +4 -3
  16. package/dist/compiler/trivia-fast-path.d.ts.map +1 -1
  17. package/dist/cst/trivia-entries.d.ts +15 -9
  18. package/dist/cst/trivia-entries.d.ts.map +1 -1
  19. package/dist/cst/trivia-kinds.d.ts +15 -11
  20. package/dist/cst/trivia-kinds.d.ts.map +1 -1
  21. package/dist/functional/run.d.ts +22 -16
  22. package/dist/functional/run.d.ts.map +1 -1
  23. package/dist/index.cjs +1416 -1115
  24. package/dist/index.cjs.map +4 -4
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +1416 -1115
  28. package/dist/index.js.map +4 -4
  29. package/dist/language-service/index.cjs +161 -102
  30. package/dist/language-service/index.cjs.map +3 -3
  31. package/dist/language-service/index.js +161 -102
  32. package/dist/language-service/index.js.map +3 -3
  33. package/dist/plugin/evaluator.d.ts.map +1 -1
  34. package/dist/plugin/index.cjs +1217 -985
  35. package/dist/plugin/index.cjs.map +4 -4
  36. package/dist/plugin/index.d.ts.map +1 -1
  37. package/dist/plugin/index.js +1217 -985
  38. package/dist/plugin/index.js.map +4 -4
  39. package/dist/run/index.cjs +152 -102
  40. package/dist/run/index.cjs.map +3 -3
  41. package/dist/run/index.d.ts +1 -1
  42. package/dist/run/index.d.ts.map +1 -1
  43. package/dist/run/index.js +152 -102
  44. package/dist/run/index.js.map +3 -3
  45. package/dist/spec/index.cjs +1414 -1286
  46. package/dist/spec/index.cjs.map +4 -4
  47. package/dist/spec/index.js +1414 -1286
  48. package/dist/spec/index.js.map +4 -4
  49. package/dist/types.d.ts +25 -0
  50. package/dist/types.d.ts.map +1 -1
  51. package/dist/version.d.ts +1 -1
  52. package/package.json +1 -1
@@ -576,1448 +576,1535 @@ 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);
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 [];
891
862
  }
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 };
863
+ }
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;
904
870
  }
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 };
871
+ if (def.tags === void 0) return;
872
+ for (const tag of def.tags) {
873
+ if (!existing.includes(tag)) existing.push(tag);
910
874
  }
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
875
  }
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);
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 {
885
+ }
886
+ }
887
+ return;
936
888
  }
937
- if (prefix === null || members.length < 2) return null;
938
- return { tag: "sharedPrefix", prefix, members };
889
+ if (def.tag === "node") addNode(out, def);
890
+ for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy);
939
891
  }
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;
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
+ }
945
902
  continue;
946
903
  }
947
- break;
904
+ collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
948
905
  }
949
- if (d.tag !== "sequence" || d.parsers.length < 2) return null;
950
- return d.parsers[0];
906
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
951
907
  }
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 });
908
+ function attachGrammarReflection(grammar, reflection) {
909
+ Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true });
910
+ return grammar;
911
+ }
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;
919
+ }
920
+ function isDefinableRef(v) {
921
+ return !!v && typeof v === "object" && "_def" in v && v._def.tag === "lazy" && typeof v.define === "function";
922
+ }
923
+ function ruleNameOf(r) {
924
+ return r._ruleName;
925
+ }
926
+ function isNamedRuleRefForAnotherRule(r, key) {
927
+ const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
928
+ return name !== void 0 && name !== key;
929
+ }
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;
968
942
  }
943
+ return record[key];
969
944
  }
970
- return checks.length > 0 ? checks : null;
971
945
  });
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;
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;
978
965
  } else {
979
- if (input.startsWith(check.value, end)) return true;
966
+ tagRule(parser2, key);
967
+ cache[key] = parser2;
980
968
  }
981
969
  }
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;
996
- }
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);
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;
974
+ }
1002
975
  }
1003
- return value;
1004
- }
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);
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;
980
+ }
1015
981
  }
1016
- if (contCodes.length === 0) return null;
1017
- return codesToFirstSet(contCodes);
1018
- }
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];
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";
1029
986
  }
1030
987
  }
1031
- ranges.push({ lo, hi });
1032
- return { kind: "ranges", ranges };
1033
- }
1034
- function inFirstSet(code, fs) {
1035
- if (fs.kind === "any") return true;
1036
- if (fs.kind === "empty") return false;
1037
- for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
1038
- return false;
1039
- }
1040
- function areDisjoint(sets) {
1041
- if (sets.some((s) => s.kind === "any")) return false;
1042
- for (let i = 0; i < sets.length; i++)
1043
- for (let j2 = i + 1; j2 < sets.length; j2++)
1044
- if (intersects(sets[i], sets[j2])) return false;
1045
- return true;
1046
- }
1047
- function buildAsciiDispatch(parsers) {
1048
- const table = Array(128).fill(-1);
1049
- for (let i = 0; i < parsers.length; i++) {
1050
- const fs = parsers[i]._meta.firstSet;
1051
- if (fs.kind !== "ranges") continue;
1052
- for (const { lo, hi } of fs.ranges) {
1053
- for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
1054
- table[code] = i;
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;
992
+ }
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;
1055
1000
  }
1056
1001
  }
1057
1002
  }
1058
- return table;
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
1010
+ });
1011
+ attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
1012
+ return cache;
1059
1013
  }
1060
1014
 
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 });
1087
- }
1088
- } else {
1089
- const peeled = peelLabel(core);
1090
- if (!peeled) return null;
1091
- arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
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;
1092
1058
  }
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
1059
  }
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
- }
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);
1111
1070
  }
1112
- if (!matched) break;
1113
- chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
1114
- pos = matched.end;
1115
- }
1116
- if (chunks.length < spec.minRepeats) {
1117
- return { end: cur, chunks: [] };
1118
- }
1119
- return { end: pos, chunks };
1071
+ };
1120
1072
  }
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;
1136
- }
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;
1073
+ function firstSetSentinel(fs) {
1074
+ if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
1075
+ const ranges = fs.ranges;
1076
+ return {
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
+ }
1145
1086
  }
1146
- break;
1087
+ return { ok: false, expected: [], span: { start: pos, end: pos } };
1147
1088
  }
1148
- break;
1149
- }
1150
- return { end: pos, chunks };
1089
+ };
1151
1090
  }
1152
- function tryFastLabeledScan(input, cur, trivia2) {
1153
- const spec = analyzeLabeledTrivia(trivia2);
1154
- if (!spec || spec.arms.length !== 2) return null;
1155
- const wsArm = spec.arms.find((a) => {
1156
- const src = getCoreRegexDef(a.parser)?.source;
1157
- return src != null && !src.includes("\\*");
1158
- });
1159
- const commentArm = spec.arms.find((a) => {
1160
- const src = getCoreRegexDef(a.parser)?.source;
1161
- return src != null && src.includes("\\*");
1162
- });
1163
- if (!wsArm || !commentArm) return null;
1164
- const { chunks } = scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1165
- if (chunks.length < spec.minRepeats) return { end: cur, chunks: [] };
1166
- return scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
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 };
1167
1099
  }
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
- }
1100
+ function matchesAt(sentinel, input, pos, ctx) {
1101
+ return probeAt(sentinel, input, pos, ctx);
1179
1102
  }
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;
1103
+ function captureError(ctx, error) {
1104
+ if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
1188
1105
  }
1189
1106
 
1190
- // src/combinators/grammar.ts
1191
- function annotateResultLines(result, index) {
1192
- const annotated = { ...result, span: annotateSpan(result.span, index) };
1193
- const errors = annotated.errors;
1194
- if (errors) {
1195
- ;
1196
- annotated.errors = errors.map((error) => ({
1197
- ...error,
1198
- span: annotateSpan(error.span, index)
1199
- }));
1200
- }
1201
- return annotated;
1202
- }
1203
- function createParseLineContext(input, pos) {
1204
- const lineIndex = createLineIndex();
1205
- if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
1206
- return { lineIndex, lineScannedTo: pos };
1207
- }
1208
- function parser(opts, root) {
1209
- const clearTrivia = opts.trivia === null;
1210
- return {
1211
- _tag: "grammar",
1212
- _meta: root._meta,
1213
- _def: {
1214
- tag: "grammar",
1215
- parser: root,
1216
- triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
1217
- clearTrivia,
1218
- ...opts.captureTrivia ? { captureTrivia: true } : {},
1219
- trackLines: opts.trackLines ?? false
1220
- },
1221
- parse(input, pos, _ctx) {
1222
- const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
1223
- const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
1224
- const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
1225
- const ctx = {
1226
- ..._ctx,
1227
- trackLines,
1228
- ...lineIndex ? { _lineIndex: lineIndex } : {},
1229
- ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
1230
- // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
1231
- ...clearTrivia ? {
1232
- trivia: void 0,
1233
- triviaKindLabels: void 0
1234
- } : opts.trivia != null ? {
1235
- trivia: opts.trivia,
1236
- ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
1237
- } : {},
1238
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
1239
- // Kind-filter for per-node capture. Resolve against this scope's trivia
1240
- // labels — this parser's own trivia if it declares one, else the INHERITED
1241
- // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
1242
- // trivia is inherited rather than re-declared here. No labels → undefined
1243
- // (capture all).
1244
- ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
1245
- };
1246
- const result = root.parse(input, pos ?? 0, ctx);
1247
- if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
1248
- _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
1249
- }
1250
- return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
1251
- }
1252
- };
1107
+ // src/combinators/expect.ts
1108
+ function deriveExpected(c) {
1109
+ return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
1253
1110
  }
1254
-
1255
- // src/cst/reflection.ts
1256
- var GRAMMAR_REFLECTION = /* @__PURE__ */ Symbol.for("parseman.grammarReflection");
1257
- function childrenOf(def) {
1111
+ function derive(c, seen) {
1112
+ const deriveExpected2 = (p) => derive(p, seen);
1113
+ const def = c._def;
1258
1114
  switch (def.tag) {
1259
- case "sequence":
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];
1260
1123
  case "choice":
1261
- return def.parsers;
1124
+ return def.parsers.flatMap(deriveExpected2);
1262
1125
  case "dispatch":
1263
- return [
1264
- def.selector,
1265
- ...def.cases.map((c) => c.parser),
1266
- ...def.matchers ? def.matchers.map((c) => c.parser) : [],
1267
- ...def.otherwise ? [def.otherwise] : []
1268
- ];
1269
- case "many":
1270
- case "oneOrMore":
1271
- case "optional":
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;
1138
+ }
1139
+ return out;
1140
+ }
1272
1141
  case "attempt":
1273
- case "transform":
1274
- case "trivia":
1275
- case "token":
1276
- case "leaf":
1277
- case "label":
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.
1278
1146
  case "field":
1279
- case "not":
1280
- case "peek":
1281
- case "node":
1282
1147
  case "withCtx":
1283
1148
  case "recover":
1284
- case "expect":
1285
- return [def.parser];
1286
- case "grammar":
1287
- return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
1288
- case "sepBy":
1289
- return [def.parser, def.separator];
1149
+ return deriveExpected2(def.parser);
1290
1150
  case "skip":
1291
- return [def.main, def.skipped];
1292
- case "scanTo":
1293
- return [def.sentinel, ...def.skip];
1294
- case "literal":
1295
- case "regex":
1296
- case "keywords":
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);
1297
1166
  case "routed":
1298
- case "guard":
1299
- case "lazy":
1300
- case "unknown":
1301
- return [];
1302
- }
1303
- }
1304
- function addNode(out, def) {
1305
- if (def.type === void 0) return;
1306
- const existing = out.get(def.type);
1307
- if (existing === void 0) {
1308
- out.set(def.type, def.tags === void 0 ? [] : [...def.tags]);
1309
- return;
1310
- }
1311
- if (def.tags === void 0) return;
1312
- for (const tag of def.tags) {
1313
- if (!existing.includes(tag)) existing.push(tag);
1314
- }
1315
- }
1316
- function collectFromCombinator(parser2, out, seen, followLazy = true) {
1317
- if (seen.has(parser2)) return;
1318
- seen.add(parser2);
1319
- const def = parser2._def;
1320
- if (def.tag === "lazy") {
1321
- if (followLazy) {
1167
+ return ["routed()"];
1168
+ case "lazy": {
1169
+ const name = c._ruleName;
1170
+ if (seen.has(c)) return name ? [name] : [];
1171
+ seen.add(c);
1322
1172
  try {
1323
- collectFromCombinator(def.thunk(), out, seen, followLazy);
1173
+ return deriveExpected2(def.thunk());
1324
1174
  } catch {
1175
+ return name ? [name] : [];
1176
+ } finally {
1177
+ seen.delete(c);
1325
1178
  }
1326
1179
  }
1327
- return;
1180
+ default:
1181
+ return [];
1328
1182
  }
1329
- if (def.tag === "node") addNode(out, def);
1330
- for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy);
1331
1183
  }
1332
- function collectGrammarReflection(ruleMap, opts) {
1333
- const byType = /* @__PURE__ */ new Map();
1334
- const followLazy = opts?.followLazy !== false;
1335
- for (const [, rule] of ruleMap) {
1336
- if (!followLazy && rule._def.tag === "lazy") {
1337
- try {
1338
- const resolved = rule._def.thunk();
1339
- if (resolved._def.tag !== "lazy") collectFromCombinator(resolved, byType, /* @__PURE__ */ new Set(), false);
1340
- } catch {
1341
- }
1342
- continue;
1184
+ function expect(combinator, label2) {
1185
+ const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
1186
+ const meta = {
1187
+ firstSet: combinator._meta.firstSet,
1188
+ canMatchNewline: combinator._meta.canMatchNewline,
1189
+ isTrivia: false
1190
+ };
1191
+ return {
1192
+ _tag: "expect",
1193
+ _meta: meta,
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 } };
1343
1203
  }
1344
- collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
1345
- }
1346
- return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
1347
- }
1348
- function attachGrammarReflection(grammar, reflection) {
1349
- Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true });
1350
- return grammar;
1204
+ };
1351
1205
  }
1352
1206
 
1353
- // src/combinators/parser.ts
1354
- var RULE_ORDER = "__parsemanRuleOrder";
1355
- function tagRule(r, key) {
1356
- ;
1357
- r._ruleName = key;
1358
- if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
1359
- }
1360
- function isDefinableRef(v) {
1361
- return !!v && typeof v === "object" && "_def" in v && v._def.tag === "lazy" && typeof v.define === "function";
1362
- }
1363
- function ruleNameOf(r) {
1364
- return r._ruleName;
1207
+ // src/regex/classes.ts
1208
+ var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1209
+ var SPACE_RANGES = [
1210
+ [9, 13],
1211
+ [32, 32],
1212
+ [160, 160],
1213
+ [5760, 5760],
1214
+ [8192, 8202],
1215
+ [8232, 8232],
1216
+ [8233, 8233],
1217
+ [8239, 8239],
1218
+ [8287, 8287],
1219
+ [12288, 12288],
1220
+ [65279, 65279]
1221
+ ];
1222
+ function shorthandRanges(ch) {
1223
+ if (ch === "d") return [[48, 57]];
1224
+ if (ch === "s") return SPACE_RANGES;
1225
+ return [[48, 57], [65, 90], [97, 122], [95, 95]];
1365
1226
  }
1366
- function isNamedRuleRefForAnotherRule(r, key) {
1367
- const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
1368
- return name !== void 0 && name !== key;
1227
+ function readUnicodeEscape(body, i) {
1228
+ if (body[i] !== "\\" || body[i + 1] !== "u") return null;
1229
+ const hex = body.slice(i + 2, i + 6);
1230
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
1231
+ return { cp: Number.parseInt(hex, 16), next: i + 6 };
1369
1232
  }
1370
- function rules(a, b) {
1371
- const factory = typeof a === "function" ? a : b;
1372
- const options = typeof a === "function" ? b : a;
1373
- const cache = {};
1374
- const proxy = new Proxy(cache, {
1375
- get(target, key) {
1376
- if (typeof key !== "string") return void 0;
1377
- const record = target;
1378
- if (!(key in record)) {
1379
- const r = ref();
1380
- tagRule(r, key);
1381
- record[key] = r;
1233
+ function parseClassRanges(body) {
1234
+ const ranges = [];
1235
+ let i = 0;
1236
+ const readAtom = () => {
1237
+ const ch = body[i];
1238
+ if (ch === void 0) return null;
1239
+ if (ch === "\\") {
1240
+ const uni = readUnicodeEscape(body, i);
1241
+ if (uni) {
1242
+ i = uni.next;
1243
+ return { cp: uni.cp };
1382
1244
  }
1383
- return record[key];
1245
+ const e = body[i + 1];
1246
+ if (e === void 0) return null;
1247
+ i += 2;
1248
+ if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1249
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
1250
+ if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1251
+ return { cp: e.codePointAt(0) };
1384
1252
  }
1385
- });
1386
- const definitions = factory(proxy);
1387
- for (const key of Object.keys(definitions)) {
1388
- const placeholder = cache[key];
1389
- const parser2 = definitions[key];
1390
- if (placeholder === parser2) {
1391
- throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
1253
+ i += ch.length;
1254
+ return { cp: ch.codePointAt(0) };
1255
+ };
1256
+ while (i < body.length) {
1257
+ const lo = readAtom();
1258
+ if (lo === null) return null;
1259
+ if ("set" in lo) {
1260
+ ranges.push(...lo.set);
1261
+ continue;
1392
1262
  }
1393
- if (isDefinableRef(placeholder)) {
1394
- if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
1395
- placeholder.define(parser2);
1396
- placeholder._meta.firstSet = parser2._meta.firstSet;
1397
- placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
1398
- } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
1399
- const alias = ref();
1400
- tagRule(alias, key);
1401
- alias.define(parser2);
1402
- alias._meta.firstSet = parser2._meta.firstSet;
1403
- alias._meta.canMatchNewline = parser2._meta.canMatchNewline;
1404
- cache[key] = alias;
1263
+ if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
1264
+ i += 1;
1265
+ const hi = readAtom();
1266
+ if (hi === null || "set" in hi) return null;
1267
+ ranges.push([lo.cp, hi.cp]);
1405
1268
  } else {
1406
- tagRule(parser2, key);
1407
- cache[key] = parser2;
1408
- }
1409
- }
1410
- if (options?.trivia != null) {
1411
- for (const key of Object.keys(definitions)) {
1412
- const rule = cache[key];
1413
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
1414
- }
1415
- }
1416
- if (options?.scanSkip != null) {
1417
- for (const key of Object.keys(definitions)) {
1418
- const rule = cache[key];
1419
- if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
1420
- }
1421
- }
1422
- if (options?.hostMode === "cst") {
1423
- for (const key of Object.keys(definitions)) {
1424
- const rule = cache[key];
1425
- if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
1426
- }
1427
- }
1428
- if (options?.trackLines === true) {
1429
- for (const key of Object.keys(definitions)) {
1430
- const rule = cache[key];
1431
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
1432
- }
1433
- for (const key of Object.keys(definitions)) {
1434
- const rule = cache[key];
1435
- if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
1436
- const wrapped = parser({ trackLines: true }, rule);
1437
- tagRule(wrapped, key);
1438
- wrapped._meta.grammarTrackLines = true;
1439
- cache[key] = wrapped;
1440
- }
1269
+ ranges.push([lo.cp, lo.cp]);
1441
1270
  }
1442
1271
  }
1443
- for (const key of Object.keys(definitions)) {
1444
- markUnusedValues(cache[key]);
1445
- }
1446
- Object.defineProperty(cache, RULE_ORDER, {
1447
- value: Object.keys(definitions),
1448
- enumerable: false,
1449
- configurable: true
1450
- });
1451
- attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
1452
- return cache;
1272
+ return ranges.length ? ranges : null;
1453
1273
  }
1454
1274
 
1455
- // src/analysis/duplication.ts
1456
- var WORD_RE_SRC = "-?[A-Za-z][A-Za-z0-9_-]*";
1457
- var WORDS_RE = new RegExp(`^${WORD_RE_SRC}(?:\\|${WORD_RE_SRC})*$`);
1458
-
1459
- // src/combinators/probe.ts
1460
- function failAt(ctx, expected, pos) {
1461
- const r = { ok: false, expected, span: { start: pos, end: pos } };
1462
- const probe = ctx._probe;
1463
- if (probe !== void 0 && pos <= probe.offset) {
1464
- const best = probe.best;
1465
- if (best === null || pos > best.span.start) {
1466
- probe.best = r;
1467
- } else if (pos === best.span.start) {
1468
- probe.best = { ...best, expected: [...best.expected, ...expected] };
1469
- }
1470
- }
1471
- return r;
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;
1472
1281
  }
1473
-
1474
- // src/combinators/literal.ts
1475
- function asciiFoldEq(a, b) {
1476
- if (a.length !== b.length) return false;
1477
- for (let i = 0; i < a.length; i++) {
1478
- let ca = a.charCodeAt(i);
1479
- let cb = b.charCodeAt(i);
1480
- if (ca >= 65 && ca <= 90) ca += 32;
1481
- if (cb >= 65 && cb <= 90) cb += 32;
1482
- if (ca !== cb) return false;
1483
- }
1484
- return true;
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
+ };
1485
1293
  }
1486
- function literal(value, opts = {}) {
1487
- const caseInsensitive = opts.caseInsensitive ?? false;
1488
- const firstSet = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
1489
- const meta = {
1490
- firstSet,
1491
- canMatchNewline: value.includes("\n"),
1492
- isTrivia: false
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 } : {}
1493
1306
  };
1494
- const canMatchNewline = meta.canMatchNewline;
1495
- if (caseInsensitive) {
1496
- const upper = value.toUpperCase();
1497
- const lower = value.toLowerCase();
1498
- const firstUpper = upper.codePointAt(0);
1499
- const firstLower = lower.codePointAt(0);
1500
- meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
1501
- { lo: firstLower, hi: firstLower },
1502
- { lo: firstUpper, hi: firstUpper }
1503
- ] } : firstSet;
1504
- }
1505
- const expected = [JSON.stringify(value)];
1506
- const parse = !caseInsensitive && value.length === 1 ? (() => {
1507
- const code = value.charCodeAt(0);
1508
- return function parse2(input, pos, ctx) {
1509
- if (input.charCodeAt(pos) === code) {
1510
- const span = { start: pos, end: pos + 1 };
1511
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1512
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
1513
- return { ok: true, value, span };
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);
1336
+ }
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
+ }
1514
1354
  }
1515
- return failAt(ctx, expected, pos);
1516
1355
  };
1517
- })() : function parse2(input, pos, ctx) {
1518
- const end = pos + value.length;
1519
- if (end > input.length) {
1520
- return failAt(ctx, expected, pos);
1521
- }
1522
- const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
1523
- if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
1524
- const span = { start: pos, end };
1525
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
1526
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
1527
- return { ok: true, value: matchedValue, span };
1528
- }
1529
- return failAt(ctx, expected, pos);
1530
- };
1356
+ }
1357
+ const { end, chunks } = scanLabeledTriviaChunks(input, cur, spec, ctx.state);
1358
+ if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1531
1359
  return {
1532
- _tag: "literal",
1533
- _meta: meta,
1534
- _def: { tag: "literal", value, caseInsensitive },
1535
- parse
1360
+ end,
1361
+ commit: () => recordTriviaChunks(ctx, chunks)
1536
1362
  };
1537
1363
  }
1538
-
1539
- // src/regex/classes.ts
1540
- var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1541
- var SPACE_RANGES = [
1542
- [9, 13],
1543
- [32, 32],
1544
- [160, 160],
1545
- [5760, 5760],
1546
- [8192, 8202],
1547
- [8232, 8232],
1548
- [8233, 8233],
1549
- [8239, 8239],
1550
- [8287, 8287],
1551
- [12288, 12288],
1552
- [65279, 65279]
1553
- ];
1554
- function shorthandRanges(ch) {
1555
- if (ch === "d") return [[48, 57]];
1556
- if (ch === "s") return SPACE_RANGES;
1557
- return [[48, 57], [65, 90], [97, 122], [95, 95]];
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;
1558
1369
  }
1559
- function readUnicodeEscape(body, i) {
1560
- if (body[i] !== "\\" || body[i + 1] !== "u") return null;
1561
- const hex = body.slice(i + 2, i + 6);
1562
- if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
1563
- return { cp: Number.parseInt(hex, 16), next: i + 6 };
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;
1564
1394
  }
1565
- function parseClassRanges(body) {
1566
- const ranges = [];
1567
- let i = 0;
1568
- const readAtom = () => {
1569
- const ch = body[i];
1570
- if (ch === void 0) return null;
1571
- if (ch === "\\") {
1572
- const uni = readUnicodeEscape(body, i);
1573
- if (uni) {
1574
- i = uni.next;
1575
- return { cp: uni.cp };
1576
- }
1577
- const e = body[i + 1];
1578
- if (e === void 0) return null;
1579
- i += 2;
1580
- if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1581
- if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
1582
- if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1583
- return { cp: e.codePointAt(0) };
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 };
1584
1405
  }
1585
- i += ch.length;
1586
- return { cp: ch.codePointAt(0) };
1587
- };
1588
- while (i < body.length) {
1589
- const lo = readAtom();
1590
- if (lo === null) return null;
1591
- if ("set" in lo) {
1592
- ranges.push(...lo.set);
1593
- continue;
1406
+ if (ctx.triviaKindLabels && (log !== void 0 || rootLog !== void 0 || captureTl)) {
1407
+ return scanWithLabels(input, cur, ctx);
1594
1408
  }
1595
- if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
1596
- i += 1;
1597
- const hi = readAtom();
1598
- if (hi === null || "set" in hi) return null;
1599
- ranges.push([lo.cp, hi.cp]);
1600
- } else {
1601
- ranges.push([lo.cp, lo.cp]);
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
+ };
1602
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 };
1603
1427
  }
1604
- return ranges.length ? ranges : null;
1605
- }
1606
-
1607
- // src/combinators/regex.ts
1608
- var firstSetAnalyzer = null;
1609
- var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
1610
- var SCAN_BAIL_AT = 64;
1611
- function inRanges(cp, ranges) {
1612
- for (let i = 0; i < ranges.length; i++) {
1613
- const [lo, hi] = ranges[i];
1614
- if (cp >= lo && cp <= hi) return true;
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 };
1615
1434
  }
1616
- return false;
1617
- }
1618
- function readClassRanges(body) {
1619
- if (body.startsWith("^")) return null;
1620
- return parseClassRanges(body);
1621
- }
1622
- function shortScanner(source, flags) {
1623
- if (/[imsuvy]/.test(flags)) return null;
1624
- let ranges = null;
1625
- let quant = "";
1626
- if (source[0] === "[") {
1627
- let end = 1;
1628
- while (end < source.length && source[end] !== "]") {
1629
- if (source[end] === "\\") end += 2;
1630
- else end++;
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;
1631
1498
  }
1632
- if (source[end] !== "]") return null;
1633
- ranges = readClassRanges(source.slice(1, end));
1634
- quant = source.slice(end + 1);
1635
- } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
1636
- ranges = shorthandRanges(source[1]);
1637
- quant = source.slice(2);
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;
1638
1511
  }
1639
- if (!ranges || quant !== "+" && quant !== "*") return null;
1640
- const minOne = quant === "+";
1641
- return (input, pos) => {
1642
- let end = pos;
1643
- while (end < input.length && inRanges(input.charCodeAt(end), ranges)) {
1644
- end++;
1645
- if (end - pos >= SCAN_BAIL_AT) return void 0;
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++;
1646
1531
  }
1647
- return minOne && end === pos ? null : end;
1532
+ return pos;
1648
1533
  };
1649
1534
  }
1650
- function asciiCaseFold(fs) {
1651
- if (fs.kind !== "ranges") return fs;
1652
- const ranges = fs.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
1653
- const add = (lo, hi) => {
1654
- if (lo <= hi) ranges.push({ lo, hi });
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;
1655
1559
  };
1656
- for (const r of fs.ranges) {
1657
- const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
1658
- add(uLo + 32, uHi + 32);
1659
- const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
1660
- add(lLo - 32, lHi - 32);
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
+ }
1661
1607
  }
1662
- ranges.sort((a, b) => a.lo - b.lo);
1663
- const merged = [];
1664
- for (const r of ranges) {
1665
- const last = merged[merged.length - 1];
1666
- if (last && r.lo <= last.hi + 1) last.hi = Math.max(last.hi, r.hi);
1667
- else merged.push({ lo: r.lo, hi: r.hi });
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);
1668
1621
  }
1669
- return { kind: "ranges", ranges: merged };
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);
1670
1633
  }
1671
- function regex(pattern, flags = "") {
1672
- const source = typeof pattern === "string" ? pattern : pattern.source;
1673
- const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
1674
- const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
1675
- const scan = shortScanner(source, resolvedFlags);
1676
- const raw = (firstSetAnalyzer ?? permissiveFirstSet)(source);
1677
- const firstSet = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
1678
- const canMatchNewline = raw.canMatchNewline;
1679
- const meta = { firstSet, canMatchNewline, isTrivia: false };
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
+ }
1680
1664
  return {
1681
- _tag: "regex",
1665
+ _tag: "choice",
1682
1666
  _meta: meta,
1683
- _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
+ },
1684
1675
  parse(input, pos, ctx) {
1685
- const scanEnd = scan?.(input, pos);
1686
- if (scanEnd !== void 0) {
1687
- if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
1688
- const value = input.slice(pos, scanEnd);
1689
- const span2 = { start: pos, end: scanEnd };
1690
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
1691
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
1692
- return { ok: true, value, span: span2 };
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;
1693
1827
  }
1694
- anchored.lastIndex = pos;
1695
- const m = anchored.exec(input);
1696
- if (m === null) {
1697
- 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 });
1698
1832
  }
1699
- const span = { start: pos, end: pos + m[0].length };
1700
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1701
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
1702
- return { ok: true, value: m[0], span };
1703
1833
  }
1704
- };
1834
+ return checks.length > 0 ? checks : null;
1835
+ });
1705
1836
  }
1706
-
1707
- // src/combinators/trivia-skip.ts
1708
- var NOOP_COMMIT = () => {
1709
- };
1710
- var fastTriviaCache = /* @__PURE__ */ new WeakMap();
1711
- function needsDeferredTriviaCommit(ctx) {
1712
- return ctx._triviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
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;
1713
1847
  }
1714
- function saveTriviaMark(ctx) {
1715
- const m = saveCstMark(ctx);
1716
- return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, errors: m.errors, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
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;
1717
1853
  }
1718
- function rollbackTrivia(ctx, mark) {
1719
- rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1720
- if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
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;
1721
1860
  }
1722
- function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1723
- const probeCtx = {
1724
- trackLines: ctx.trackLines,
1725
- state: ctx.state,
1726
- ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1727
- ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1728
- ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1729
- };
1730
- const result = triviaP.parse(input, cur, probeCtx);
1731
- ctx._lineScannedTo = probeCtx._lineScannedTo;
1732
- return result;
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;
1733
1868
  }
1734
- function saveLookaheadMark(ctx) {
1735
- 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);
1736
1882
  }
1737
- function rollbackLookahead(ctx, mark) {
1738
- rollbackTrivia(ctx, mark);
1739
- 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 };
1740
1897
  }
1741
- function scanWithLabels(input, cur, ctx) {
1742
- const triviaP = ctx.trivia;
1743
- const spec = analyzeLabeledTrivia(triviaP);
1744
- if (!spec) return { end: cur, commit: NOOP_COMMIT };
1745
- const fast = tryFastLabeledScan(input, cur, triviaP);
1746
- const { end, chunks } = fast ?? scanLabeledTriviaChunks(input, cur, spec);
1747
- if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1748
- return {
1749
- end,
1750
- commit: () => recordTriviaChunks(ctx, chunks)
1751
- };
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;
1752
1903
  }
1753
- function advanceTrivia(input, cur, ctx) {
1754
- const triviaP = ctx.trivia;
1755
- if (!triviaP) return cur;
1756
- if (!ctx.trackLines) {
1757
- const fast2 = fastTriviaScanner(triviaP);
1758
- if (fast2) return fast2(input, cur);
1759
- if (ctx.triviaKindLabels) {
1760
- const scan = scanWithLabels(input, cur, ctx);
1761
- return scan.end;
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
+ }
1762
1920
  }
1763
- const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1764
- return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1765
- }
1766
- const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1767
- const fast = fastTriviaScanner(triviaP);
1768
- if (fast) {
1769
- const end = fast(input, cur);
1770
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1771
- return end;
1772
- }
1773
- if (ctx.triviaKindLabels) {
1774
- const scan = scanWithLabels(input, cur, ctx);
1775
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1776
- return scan.end;
1777
1921
  }
1778
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1779
- return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1922
+ return table;
1780
1923
  }
1781
- function scanTrivia(input, cur, ctx) {
1782
- const triviaP = ctx.trivia;
1783
- if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1784
- const log = ctx._triviaLog;
1785
- const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1786
- if (!ctx.trackLines) {
1787
- const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1788
- if (fast2 && log === void 0 && !captureTl) {
1789
- return { end: fast2(input, cur), commit: NOOP_COMMIT };
1790
- }
1791
- if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1792
- return scanWithLabels(input, cur, ctx);
1793
- }
1794
- if (log !== void 0 || captureTl) {
1795
- const tr3 = triviaP.parse(input, cur, {
1796
- trackLines: log !== void 0 ? false : ctx.trackLines,
1797
- state: ctx.state
1798
- });
1799
- if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1800
- const end = tr3.span.end;
1801
- return {
1802
- end,
1803
- commit: () => {
1804
- pushTriviaLogEntry(ctx, cur, end);
1805
- if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1806
- }
1807
- };
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] };
1808
1939
  }
1809
- const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1810
- return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1811
- }
1812
- const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1813
- const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1814
- if (fast && log === void 0 && !captureTl) {
1815
- const end = fast(input, cur);
1816
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1817
- return { end, commit: NOOP_COMMIT };
1818
- }
1819
- if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1820
- const scan = scanWithLabels(input, cur, ctx);
1821
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1822
- return scan;
1823
- }
1824
- if (log !== void 0 || captureTl) {
1825
- const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1826
- if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1827
- const end = tr2.span.end;
1828
- return {
1829
- end,
1830
- commit: () => {
1831
- pushTriviaLogEntry(ctx, cur, end);
1832
- if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1833
- }
1834
- };
1835
1940
  }
1836
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1837
- return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1838
- }
1839
- function consumeTrivia(input, cur, ctx) {
1840
- if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1841
- const scan = scanTrivia(input, cur, ctx);
1842
- scan.commit();
1843
- return scan.end;
1844
- }
1845
- function fastTriviaScanner(trivia2) {
1846
- const cached = fastTriviaCache.get(trivia2);
1847
- if (cached !== void 0) return cached;
1848
- const scanner = buildFastTriviaScanner(trivia2);
1849
- fastTriviaCache.set(trivia2, scanner);
1850
- return scanner;
1941
+ return r;
1851
1942
  }
1852
- function buildFastTriviaScanner(trivia2) {
1853
- const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1854
- const direct = regexTriviaScanner(core);
1855
- if (direct) return direct;
1856
- const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1857
- if (!repeat) return null;
1858
- const one = regexTriviaScanner(repeat);
1859
- if (one) return loopScanner([one]);
1860
- if (repeat._def.tag !== "choice") return null;
1861
- const arms = repeat._def.parsers.map(regexTriviaScanner);
1862
- if (arms.some((s) => s === null)) return null;
1863
- return loopScanner(arms);
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;
1864
1955
  }
1865
- function loopScanner(arms) {
1866
- return (input, cur) => {
1867
- let pos = cur;
1868
- scan: while (pos < input.length) {
1869
- for (const arm of arms) {
1870
- const end = arm(input, pos);
1871
- if (end > pos) {
1872
- pos = end;
1873
- continue scan;
1874
- }
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 };
1875
1984
  }
1876
- 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);
1877
1991
  }
1878
- 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
1879
2006
  };
1880
2007
  }
1881
- function regexTriviaScanner(parser2) {
1882
- if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1883
- const source = parser2._def.source;
1884
- return classRunSource(source) ?? altStarSource(source) ?? (blockCommentSource(source) ? scanBlockComment : null);
1885
- }
2008
+
2009
+ // src/combinators/regex.ts
2010
+ var firstSetAnalyzer = null;
2011
+ var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
2012
+ var SCAN_BAIL_AT = 64;
1886
2013
  function inRanges2(cp, ranges) {
1887
2014
  for (let i = 0; i < ranges.length; i++) {
1888
- const r = ranges[i];
1889
- if (cp >= r[0] && cp <= r[1]) return true;
2015
+ const [lo, hi] = ranges[i];
2016
+ if (cp >= lo && cp <= hi) return true;
1890
2017
  }
1891
2018
  return false;
1892
2019
  }
1893
- function classScanner(classBody) {
1894
- const ranges = parseClassRanges(classBody);
1895
- if (!ranges) return null;
1896
- return (input, cur) => {
1897
- let pos = cur;
1898
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1899
- return pos;
1900
- };
2020
+ function readClassRanges(body) {
2021
+ if (body.startsWith("^")) return null;
2022
+ return parseClassRanges(body);
1901
2023
  }
1902
- function lineCommentScanner(commentCode) {
1903
- return (input, cur) => {
1904
- if (input.charCodeAt(cur) !== commentCode) return cur;
1905
- let pos = cur + 1;
1906
- while (pos < input.length) {
1907
- const cc = input.charCodeAt(pos);
1908
- if (cc === 10 || cc === 13) break;
1909
- pos++;
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++;
1910
2033
  }
1911
- 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;
1912
2050
  };
1913
2051
  }
1914
- function classRunSource(source) {
1915
- const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1916
- return m ? classScanner(m[1]) : null;
1917
- }
1918
- function classifyTriviaArm(arm) {
1919
- const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1920
- if (cls) {
1921
- const ranges = parseClassRanges(cls[1]);
1922
- return ranges ? { kind: "class", ranges } : null;
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);
1923
2063
  }
1924
- const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1925
- if (lc) {
1926
- const marker = lc[1];
1927
- return { kind: "comment", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
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 });
1928
2070
  }
1929
- return null;
1930
- }
1931
- function armScanner(arm) {
1932
- if (arm.kind === "comment") return lineCommentScanner(arm.code);
1933
- const ranges = arm.ranges;
1934
- return (input, cur) => {
1935
- let pos = cur;
1936
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1937
- return pos;
1938
- };
2071
+ return { kind: "ranges", ranges: merged };
1939
2072
  }
1940
- function fusedTriviaScanner(ranges, commentCodes) {
1941
- const c0 = commentCodes[0];
1942
- const single = commentCodes.length === 1;
1943
- return (input, cur) => {
1944
- let pos = cur;
1945
- const len = input.length;
1946
- for (; ; ) {
1947
- const c = input.charCodeAt(pos);
1948
- if (pos < len && inRanges2(c, ranges)) {
1949
- pos++;
1950
- continue;
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 };
1951
2095
  }
1952
- if (single ? c === c0 : commentCodes.includes(c)) {
1953
- pos++;
1954
- while (pos < len) {
1955
- const cc = input.charCodeAt(pos);
1956
- if (cc === 10 || cc === 13) break;
1957
- pos++;
1958
- }
1959
- continue;
2096
+ anchored.lastIndex = pos;
2097
+ const m = anchored.exec(input);
2098
+ if (m === null) {
2099
+ return failAt(ctx, [`/${source}/`], pos);
1960
2100
  }
1961
- 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 };
1962
2105
  }
1963
- return pos;
1964
2106
  };
1965
2107
  }
1966
- function splitTopLevelAlts(body) {
1967
- const arms = [];
1968
- let inClass = false;
1969
- let start = 0;
1970
- for (let i = 0; i < body.length; i++) {
1971
- const c = body[i];
1972
- if (c === "\\") {
1973
- i++;
1974
- continue;
1975
- }
1976
- if (inClass) {
1977
- if (c === "]") inClass = false;
1978
- continue;
1979
- }
1980
- if (c === "[") inClass = true;
1981
- else if (c === "(") return null;
1982
- else if (c === "|") {
1983
- arms.push(body.slice(start, i));
1984
- start = i + 1;
1985
- }
1986
- }
1987
- arms.push(body.slice(start));
1988
- return arms;
1989
- }
1990
- function altStarSource(source) {
1991
- const m = /^\(\?:(.*)\)[*+]$/.exec(source);
1992
- if (!m) return null;
1993
- const armSrcs = splitTopLevelAlts(m[1]);
1994
- if (!armSrcs || armSrcs.length < 2) return null;
1995
- const arms = [];
1996
- for (const src of armSrcs) {
1997
- const arm = classifyTriviaArm(src);
1998
- if (!arm) return null;
1999
- arms.push(arm);
2000
- }
2001
- const ranges = [];
2002
- const commentCodes = [];
2003
- for (const arm of arms) {
2004
- if (arm.kind === "class") ranges.push(...arm.ranges);
2005
- else commentCodes.push(arm.code);
2006
- }
2007
- if (commentCodes.some((code) => inRanges2(code, ranges))) {
2008
- return loopScanner(arms.map(armScanner));
2009
- }
2010
- if (commentCodes.length === 0) return armScanner({ kind: "class", ranges });
2011
- return fusedTriviaScanner(ranges, commentCodes);
2012
- }
2013
- function blockCommentSource(source) {
2014
- return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
2015
- }
2016
- function scanBlockComment(input, cur) {
2017
- if (input.charCodeAt(cur) !== 47 || input.charCodeAt(cur + 1) !== 42) return cur;
2018
- const close = input.indexOf("*/", cur + 2);
2019
- return close === -1 ? cur : close + 2;
2020
- }
2021
2108
 
2022
2109
  // src/combinators/sequence.ts
2023
2110
  function sequence(...parsers) {
@@ -2433,6 +2520,25 @@ function trivia(combinator) {
2433
2520
  parse: combinator.parse.bind(combinator)
2434
2521
  };
2435
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
+ }
2436
2542
  function label(name, combinator) {
2437
2543
  return {
2438
2544
  _tag: combinator._tag,
@@ -2526,8 +2632,17 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2526
2632
  };
2527
2633
  }
2528
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
+
2529
2640
  // src/compiler/scannable-run.ts
2530
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(", ");
2531
2646
 
2532
2647
  // src/compiler/trivia-fast-path.ts
2533
2648
  var CAP_RECORD = [
@@ -2536,6 +2651,11 @@ var CAP_RECORD = [
2536
2651
  ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
2537
2652
  ` }`
2538
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");
2539
2659
 
2540
2660
  // src/compiler/build-arity.ts
2541
2661
  function cstOutputHost(build) {
@@ -2975,10 +3095,12 @@ function attempt(parser2) {
2975
3095
  }
2976
3096
  const mark = saveCstMark(ctx);
2977
3097
  const log = ctx._triviaLog?.length;
3098
+ const rootLog = ctx._rootTriviaLog?.length;
2978
3099
  const result = parser2.parse(input, pos, ctx);
2979
3100
  if (result.ok) return result;
2980
3101
  rollbackCstCapture(ctx, mark);
2981
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;
2982
3104
  if (result.committed) return result;
2983
3105
  return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
2984
3106
  }
@@ -3158,6 +3280,7 @@ function token(root) {
3158
3280
  const savedRaw = ctx._cstRawChildren;
3159
3281
  const savedTriviaLog = ctx._cstTriviaLog;
3160
3282
  const savedOuterTriviaLog = ctx._triviaLog;
3283
+ const savedRootTriviaLog = ctx._rootTriviaLog;
3161
3284
  const wasCapturing = cstCaptureActive(ctx);
3162
3285
  ctx.trivia = void 0;
3163
3286
  ctx.triviaKindLabels = void 0;
@@ -3167,6 +3290,7 @@ function token(root) {
3167
3290
  ctx._cstRawChildren = void 0;
3168
3291
  ctx._cstTriviaLog = void 0;
3169
3292
  delete ctx._triviaLog;
3293
+ delete ctx._rootTriviaLog;
3170
3294
  let result;
3171
3295
  try {
3172
3296
  result = root.parse(input, pos, ctx);
@@ -3180,6 +3304,8 @@ function token(root) {
3180
3304
  ctx._cstTriviaLog = savedTriviaLog;
3181
3305
  if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
3182
3306
  else ctx._triviaLog = savedOuterTriviaLog;
3307
+ if (savedRootTriviaLog === void 0) delete ctx._rootTriviaLog;
3308
+ else ctx._rootTriviaLog = savedRootTriviaLog;
3183
3309
  }
3184
3310
  if (!result.ok) return result;
3185
3311
  const span = result.span;
@@ -3359,6 +3485,7 @@ function evalRuleMapIR(ir) {
3359
3485
  "transform",
3360
3486
  "skip",
3361
3487
  "trivia",
3488
+ "classifiedTrivia",
3362
3489
  "label",
3363
3490
  "field",
3364
3491
  "expect",
@@ -3399,6 +3526,7 @@ function evalRuleMapIR(ir) {
3399
3526
  transform,
3400
3527
  skip,
3401
3528
  trivia,
3529
+ classifiedTrivia,
3402
3530
  label,
3403
3531
  field,
3404
3532
  expect,