parseman 0.19.0 → 0.21.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.
@@ -406,9 +406,9 @@ function asciiFoldEq(a, b) {
406
406
  }
407
407
  function literal(value, opts = {}) {
408
408
  const caseInsensitive = opts.caseInsensitive ?? false;
409
- const firstSet = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
409
+ const firstSet2 = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
410
410
  const meta = {
411
- firstSet,
411
+ firstSet: firstSet2,
412
412
  canMatchNewline: value.includes("\n"),
413
413
  isTrivia: false
414
414
  };
@@ -420,7 +420,7 @@ function literal(value, opts = {}) {
420
420
  meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
421
421
  { lo: firstLower, hi: firstLower },
422
422
  { lo: firstUpper, hi: firstUpper }
423
- ] } : firstSet;
423
+ ] } : firstSet2;
424
424
  }
425
425
  return {
426
426
  _tag: "literal",
@@ -443,68 +443,66 @@ function literal(value, opts = {}) {
443
443
  };
444
444
  }
445
445
 
446
- // src/combinators/regex.ts
447
- var firstSetAnalyzer = null;
448
- function registerRegexAnalyzer(analyzer) {
449
- firstSetAnalyzer = analyzer;
450
- }
451
- var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
452
- var SCAN_BAIL_AT = 64;
446
+ // src/regex/classes.ts
447
+ var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
453
448
  var SPACE_RANGES = [
454
449
  [9, 13],
455
450
  [32, 32],
456
451
  [160, 160],
457
452
  [5760, 5760],
458
453
  [8192, 8202],
459
- [8232, 8233],
454
+ [8232, 8232],
455
+ [8233, 8233],
460
456
  [8239, 8239],
461
457
  [8287, 8287],
462
458
  [12288, 12288],
463
459
  [65279, 65279]
464
460
  ];
465
- var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
466
461
  function shorthandRanges(ch) {
467
462
  if (ch === "d") return [[48, 57]];
468
463
  if (ch === "s") return SPACE_RANGES;
469
464
  return [[48, 57], [65, 90], [97, 122], [95, 95]];
470
465
  }
471
- function inRanges(cp, ranges) {
472
- for (let i = 0; i < ranges.length; i++) {
473
- const [lo, hi] = ranges[i];
474
- if (cp >= lo && cp <= hi) return true;
475
- }
476
- return false;
466
+ function readUnicodeEscape(body, i) {
467
+ if (body[i] !== "\\" || body[i + 1] !== "u") return null;
468
+ const hex = body.slice(i + 2, i + 6);
469
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
470
+ return { cp: Number.parseInt(hex, 16), next: i + 6 };
477
471
  }
478
- function readClassRanges(body) {
479
- if (body.startsWith("^")) return null;
472
+ function parseClassRanges(body) {
480
473
  const ranges = [];
481
474
  let i = 0;
482
475
  const readAtom = () => {
483
476
  const ch = body[i];
484
477
  if (ch === void 0) return null;
485
478
  if (ch === "\\") {
486
- const esc = body[i + 1];
487
- if (esc === void 0) return null;
479
+ const uni = readUnicodeEscape(body, i);
480
+ if (uni) {
481
+ i = uni.next;
482
+ return { cp: uni.cp };
483
+ }
484
+ const e = body[i + 1];
485
+ if (e === void 0) return null;
488
486
  i += 2;
489
- if (esc === "d" || esc === "w" || esc === "s") return { ranges: shorthandRanges(esc) };
490
- if (esc in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[esc] };
491
- if (esc >= "a" && esc <= "z" || esc >= "A" && esc <= "Z") return null;
492
- return { cp: esc.charCodeAt(0) };
487
+ if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
488
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
489
+ if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
490
+ return { cp: e.codePointAt(0) };
493
491
  }
494
- i++;
495
- return { cp: ch.charCodeAt(0) };
492
+ i += ch.length;
493
+ return { cp: ch.codePointAt(0) };
496
494
  };
497
495
  while (i < body.length) {
498
496
  const lo = readAtom();
499
- if (!lo) return null;
500
- if ("ranges" in lo) {
501
- ranges.push(...lo.ranges);
497
+ if (lo === null) return null;
498
+ if ("set" in lo) {
499
+ ranges.push(...lo.set);
502
500
  continue;
503
501
  }
504
502
  if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
505
- i++;
503
+ i += 1;
506
504
  const hi = readAtom();
507
- if (!hi || "ranges" in hi) return null;
505
+ if (hi === null || "set" in hi) return null;
508
506
  ranges.push([lo.cp, hi.cp]);
509
507
  } else {
510
508
  ranges.push([lo.cp, lo.cp]);
@@ -512,6 +510,25 @@ function readClassRanges(body) {
512
510
  }
513
511
  return ranges.length ? ranges : null;
514
512
  }
513
+
514
+ // src/combinators/regex.ts
515
+ var firstSetAnalyzer = null;
516
+ function registerRegexAnalyzer(analyzer) {
517
+ firstSetAnalyzer = analyzer;
518
+ }
519
+ var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
520
+ var SCAN_BAIL_AT = 64;
521
+ function inRanges(cp, ranges) {
522
+ for (let i = 0; i < ranges.length; i++) {
523
+ const [lo, hi] = ranges[i];
524
+ if (cp >= lo && cp <= hi) return true;
525
+ }
526
+ return false;
527
+ }
528
+ function readClassRanges(body) {
529
+ if (body.startsWith("^")) return null;
530
+ return parseClassRanges(body);
531
+ }
515
532
  function shortScanner(source, flags) {
516
533
  if (/[imsuvy]/.test(flags)) return null;
517
534
  let ranges = null;
@@ -545,8 +562,8 @@ function regex(pattern, flags = "") {
545
562
  const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
546
563
  const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
547
564
  const scan = shortScanner(source, resolvedFlags);
548
- const { firstSet, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
549
- const meta = { firstSet, canMatchNewline, isTrivia: false };
565
+ const { firstSet: firstSet2, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
566
+ const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
550
567
  return {
551
568
  _tag: "regex",
552
569
  _meta: meta,
@@ -574,111 +591,266 @@ function regex(pattern, flags = "") {
574
591
  };
575
592
  }
576
593
 
577
- // src/combinators/regex-analyze.ts
578
- var import_regexp_tree = __toESM(require("regexp-tree"), 1);
579
- function firstSetFromRegex(pattern) {
580
- try {
581
- const ast = import_regexp_tree.default.parse(`/${pattern}/`);
582
- return extractFirstSet(ast.body);
583
- } catch {
584
- return { firstSet: any(), canMatchNewline: true };
585
- }
594
+ // src/regex/first-set.ts
595
+ var ANY = { t: "any" };
596
+ var EMPTY2 = { t: "empty" };
597
+ function charNode(set, nl) {
598
+ return { t: "char", set, nl };
599
+ }
600
+ function rangesToSet(ranges) {
601
+ let fs2 = empty();
602
+ for (const [lo, hi] of ranges) fs2 = union(fs2, fromRange(lo, hi));
603
+ return fs2;
604
+ }
605
+ function rangesHaveNewline(ranges) {
606
+ return ranges.some(([lo, hi]) => lo <= 10 && 10 <= hi);
586
607
  }
587
- function canBeEmpty(node2) {
588
- if (!node2) return true;
589
- switch (node2.type) {
590
- case "Repetition": {
591
- const q = node2.quantifier;
592
- return q.kind === "*" || q.kind === "?" || q.kind === "Range" && (q.from ?? 1) === 0;
608
+ var BAIL = /* @__PURE__ */ Symbol("regex-bail");
609
+ function parseRegex(src) {
610
+ let i = 0;
611
+ const parseAlt = () => {
612
+ const arms = [parseSeq()];
613
+ while (src[i] === "|") {
614
+ i++;
615
+ arms.push(parseSeq());
593
616
  }
594
- case "Alternative": {
595
- const exprs = node2.expressions;
596
- return !exprs || exprs.length === 0 || exprs.every((e) => canBeEmpty(e));
617
+ return arms.length === 1 ? arms[0] : { t: "alt", arms };
618
+ };
619
+ const parseSeq = () => {
620
+ const parts = [];
621
+ while (i < src.length) {
622
+ const ch = src[i];
623
+ if (ch === "|" || ch === ")") break;
624
+ parts.push(parseQuantified());
625
+ }
626
+ if (parts.length === 0) return EMPTY2;
627
+ return parts.length === 1 ? parts[0] : { t: "seq", parts };
628
+ };
629
+ const parseQuantified = () => {
630
+ const atom = parseAtom();
631
+ const ch = src[i];
632
+ let min = null;
633
+ if (ch === "*") {
634
+ min = 0;
635
+ i++;
636
+ } else if (ch === "+") {
637
+ min = 1;
638
+ i++;
639
+ } else if (ch === "?") {
640
+ min = 0;
641
+ i++;
642
+ } else if (ch === "{") {
643
+ min = parseBraceQuantifier();
597
644
  }
598
- case "Group":
599
- return canBeEmpty(node2.expression);
600
- case "Disjunction":
601
- return canBeEmpty(node2.left) || canBeEmpty(node2.right);
602
- default:
603
- return false;
604
- }
605
- }
606
- function extractFirstSet(node2) {
607
- if (!node2) return { firstSet: empty(), canMatchNewline: false };
608
- switch (node2.type) {
609
- case "Disjunction": {
610
- const left = extractFirstSet(node2.left);
611
- const right = extractFirstSet(node2.right);
612
- return {
613
- firstSet: union(left.firstSet, right.firstSet),
614
- canMatchNewline: left.canMatchNewline || right.canMatchNewline
615
- };
645
+ if (min === null) return atom;
646
+ if (src[i] === "?") i++;
647
+ return { t: "rep", node: atom, min };
648
+ };
649
+ const parseBraceQuantifier = () => {
650
+ const close = src.indexOf("}", i);
651
+ if (close === -1) return null;
652
+ const m = /^(\d+)(?:,(\d*))?$/.exec(src.slice(i + 1, close));
653
+ if (!m) return null;
654
+ i = close + 1;
655
+ return Number.parseInt(m[1], 10);
656
+ };
657
+ const parseAtom = () => {
658
+ const ch = src[i];
659
+ switch (ch) {
660
+ case "(":
661
+ return parseGroup();
662
+ case "[":
663
+ return parseClass();
664
+ case ".":
665
+ i++;
666
+ return charNode(any(), false);
667
+ // `.` matches any char except line terminators
668
+ case "\\":
669
+ return parseEscape();
670
+ case "^":
671
+ case "$":
672
+ i++;
673
+ return EMPTY2;
674
+ // anchors are zero-width
675
+ case "{":
676
+ i++;
677
+ return charNode(fromRange(123, 123), false);
678
+ // a `{` with nothing to quantify is a literal brace
679
+ default:
680
+ i += ch.length;
681
+ return charNode(fromRange(ch.codePointAt(0), ch.codePointAt(0)), ch === "\n");
616
682
  }
617
- case "Alternative": {
618
- const exprs = node2.expressions;
619
- if (!exprs || exprs.length === 0) return { firstSet: empty(), canMatchNewline: false };
620
- let fs2 = empty();
621
- let canNL = false;
622
- for (const expr of exprs) {
623
- const r = extractFirstSet(expr);
624
- fs2 = union(fs2, r.firstSet);
625
- canNL = canNL || r.canMatchNewline;
626
- if (!canBeEmpty(expr)) break;
627
- }
628
- return { firstSet: fs2, canMatchNewline: canNL };
683
+ };
684
+ const parseGroup = () => {
685
+ i++;
686
+ let lookaround = false;
687
+ if (src[i] === "?") {
688
+ const c1 = src[i + 1];
689
+ if (c1 === ":") i += 2;
690
+ else if (c1 === "=" || c1 === "!") {
691
+ i += 2;
692
+ lookaround = true;
693
+ } else if (c1 === "<") {
694
+ const c2 = src[i + 2];
695
+ if (c2 === "=" || c2 === "!") {
696
+ i += 3;
697
+ lookaround = true;
698
+ } else {
699
+ const gt = src.indexOf(">", i);
700
+ if (gt === -1) throw BAIL;
701
+ i = gt + 1;
702
+ }
703
+ } else throw BAIL;
629
704
  }
630
- case "Char": {
631
- const kind = node2.kind;
632
- if (kind === "simple") {
633
- const code = node2.codePoint ?? node2.value.codePointAt(0) ?? 0;
634
- return { firstSet: fromRange(code, code), canMatchNewline: code === 10 };
635
- }
636
- if (kind === "meta") {
637
- const val = node2.value;
638
- if (val === ".") return { firstSet: any(), canMatchNewline: false };
639
- if (val === "d") return { firstSet: fromRange(48, 57), canMatchNewline: false };
640
- if (val === "D") return { firstSet: any(), canMatchNewline: true };
641
- if (val === "w") return {
642
- firstSet: union(
643
- union(fromRange(48, 57), fromRange(65, 90)),
644
- union(fromRange(97, 122), fromRange(95, 95))
645
- ),
646
- canMatchNewline: false
647
- };
648
- if (val === "s") return { firstSet: fromRange(9, 13), canMatchNewline: true };
649
- if (val === "n") return { firstSet: fromRange(10, 10), canMatchNewline: true };
705
+ const inner = parseAlt();
706
+ if (src[i] !== ")") throw BAIL;
707
+ i++;
708
+ return lookaround ? EMPTY2 : inner;
709
+ };
710
+ const parseClass = () => {
711
+ let k = i + 1;
712
+ const negated = src[k] === "^";
713
+ if (negated) k++;
714
+ let body = "";
715
+ while (k < src.length && src[k] !== "]") {
716
+ if (src[k] === "\\") {
717
+ body += src[k] + (src[k + 1] ?? "");
718
+ k += 2;
719
+ } else {
720
+ body += src[k];
721
+ k++;
650
722
  }
651
- return { firstSet: any(), canMatchNewline: true };
652
723
  }
653
- case "CharacterClass": {
654
- const expressions = node2.expressions;
724
+ if (src[k] !== "]") throw BAIL;
725
+ i = k + 1;
726
+ if (negated) return charNode(any(), true);
727
+ const ranges = parseClassRanges(body);
728
+ if (!ranges) return charNode(any(), true);
729
+ return charNode(rangesToSet(ranges), rangesHaveNewline(ranges));
730
+ };
731
+ const parseEscape = () => {
732
+ const e = src[i + 1];
733
+ if (e === void 0) throw BAIL;
734
+ if (e === "u" && /^[0-9a-fA-F]{4}$/.test(src.slice(i + 2, i + 6))) {
735
+ const cp = Number.parseInt(src.slice(i + 2, i + 6), 16);
736
+ i += 6;
737
+ return charNode(fromRange(cp, cp), cp === 10);
738
+ }
739
+ if (e === "x" && /^[0-9a-fA-F]{2}$/.test(src.slice(i + 2, i + 4))) {
740
+ const cp = Number.parseInt(src.slice(i + 2, i + 4), 16);
741
+ i += 4;
742
+ return charNode(fromRange(cp, cp), cp === 10);
743
+ }
744
+ i += 2;
745
+ switch (e) {
746
+ case "d":
747
+ return charNode(rangesToSet(shorthandRanges("d")), false);
748
+ case "w":
749
+ return charNode(rangesToSet(shorthandRanges("w")), false);
750
+ case "s":
751
+ return charNode(rangesToSet(shorthandRanges("s")), true);
752
+ case "D":
753
+ case "W":
754
+ case "S":
755
+ return charNode(any(), true);
756
+ // negated shorthand → widen
757
+ case "b":
758
+ case "B":
759
+ return EMPTY2;
760
+ // word boundary: zero-width
761
+ case "n":
762
+ return charNode(fromRange(10, 10), true);
763
+ case "r":
764
+ return charNode(fromRange(13, 13), false);
765
+ case "t":
766
+ return charNode(fromRange(9, 9), false);
767
+ case "f":
768
+ return charNode(fromRange(12, 12), false);
769
+ case "v":
770
+ return charNode(fromRange(11, 11), false);
771
+ case "0":
772
+ return charNode(fromRange(0, 0), false);
773
+ default:
774
+ if (e >= "1" && e <= "9") return ANY;
775
+ return charNode(fromRange(e.codePointAt(0), e.codePointAt(0)), e === "\n");
776
+ }
777
+ };
778
+ const node2 = parseAlt();
779
+ if (i < src.length) throw BAIL;
780
+ return node2;
781
+ }
782
+ function nullable(n) {
783
+ switch (n.t) {
784
+ case "char":
785
+ return false;
786
+ case "any":
787
+ return true;
788
+ case "empty":
789
+ return true;
790
+ case "seq":
791
+ return n.parts.every(nullable);
792
+ case "alt":
793
+ return n.arms.some(nullable);
794
+ case "rep":
795
+ return n.min === 0 || nullable(n.node);
796
+ }
797
+ }
798
+ function firstSet(n) {
799
+ switch (n.t) {
800
+ case "char":
801
+ return n.set;
802
+ case "any":
803
+ return any();
804
+ case "empty":
805
+ return empty();
806
+ case "rep":
807
+ return firstSet(n.node);
808
+ case "alt": {
809
+ let fs2 = empty();
810
+ for (const arm of n.arms) fs2 = union(fs2, firstSet(arm));
811
+ return fs2;
812
+ }
813
+ case "seq": {
655
814
  let fs2 = empty();
656
- let canNL = false;
657
- for (const expr of expressions) {
658
- if (expr.type === "ClassRange") {
659
- const from = expr.from.codePoint;
660
- const to = expr.to.codePoint;
661
- fs2 = union(fs2, fromRange(from, to));
662
- if (from <= 10 && 10 <= to) canNL = true;
663
- } else if (expr.type === "Char") {
664
- const r = extractFirstSet(expr);
665
- fs2 = union(fs2, r.firstSet);
666
- canNL = canNL || r.canMatchNewline;
667
- }
815
+ for (const part of n.parts) {
816
+ fs2 = union(fs2, firstSet(part));
817
+ if (!nullable(part)) break;
668
818
  }
669
- if (node2.negative) return { firstSet: any(), canMatchNewline: true };
670
- return { firstSet: fs2, canMatchNewline: canNL };
671
- }
672
- case "Repetition": {
673
- return extractFirstSet(node2.expression);
819
+ return fs2;
674
820
  }
675
- case "Group": {
676
- return extractFirstSet(node2.expression);
821
+ }
822
+ }
823
+ function firstCanBeNewline(n) {
824
+ switch (n.t) {
825
+ case "char":
826
+ return n.nl;
827
+ case "any":
828
+ return true;
829
+ case "empty":
830
+ return false;
831
+ case "rep":
832
+ return firstCanBeNewline(n.node);
833
+ case "alt":
834
+ return n.arms.some(firstCanBeNewline);
835
+ case "seq": {
836
+ for (const part of n.parts) {
837
+ if (firstCanBeNewline(part)) return true;
838
+ if (!nullable(part)) break;
839
+ }
840
+ return false;
677
841
  }
678
- default:
679
- return { firstSet: any(), canMatchNewline: true };
680
842
  }
681
843
  }
844
+ function firstSetFromRegex(source) {
845
+ let ast;
846
+ try {
847
+ ast = parseRegex(source);
848
+ } catch {
849
+ return { firstSet: any(), canMatchNewline: true };
850
+ }
851
+ if (nullable(ast)) return { firstSet: any(), canMatchNewline: true };
852
+ return { firstSet: firstSet(ast), canMatchNewline: firstCanBeNewline(ast) };
853
+ }
682
854
 
683
855
  // src/combinators/keywords.ts
684
856
  function word(str, boundary = "_0-9A-Za-z") {
@@ -693,19 +865,19 @@ function keywords(words, opts = {}) {
693
865
  const boundary = opts.boundary ? `(?![${opts.boundary}])` : "";
694
866
  const flags = opts.caseInsensitive ? "iuy" : "uy";
695
867
  const re = new RegExp(`(?:${alt})${boundary}`, flags);
696
- let firstSet = empty();
868
+ let firstSet2 = empty();
697
869
  for (const w of sorted) {
698
870
  if (w.length === 0) continue;
699
871
  const cp = w.codePointAt(0);
700
- firstSet = union(firstSet, fromChar(cp));
872
+ firstSet2 = union(firstSet2, fromChar(cp));
701
873
  if (opts.caseInsensitive) {
702
874
  const u = String.fromCodePoint(cp).toUpperCase().codePointAt(0);
703
875
  const l = String.fromCodePoint(cp).toLowerCase().codePointAt(0);
704
- if (u !== void 0) firstSet = union(firstSet, fromChar(u));
705
- if (l !== void 0) firstSet = union(firstSet, fromChar(l));
876
+ if (u !== void 0) firstSet2 = union(firstSet2, fromChar(u));
877
+ if (l !== void 0) firstSet2 = union(firstSet2, fromChar(l));
706
878
  }
707
879
  }
708
- const meta = { firstSet, canMatchNewline: false, isTrivia: false };
880
+ const meta = { firstSet: firstSet2, canMatchNewline: false, isTrivia: false };
709
881
  return {
710
882
  _tag: "keywords",
711
883
  _meta: meta,
@@ -1082,13 +1254,25 @@ function tryFastLabeledScan(input, cur, trivia2) {
1082
1254
  }
1083
1255
  function recordTriviaChunks(ctx, chunks) {
1084
1256
  const kinds = ctx.triviaKindLabels;
1257
+ const mask = ctx._triviaCaptureMask;
1085
1258
  for (const ch of chunks) {
1086
1259
  pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1087
1260
  if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
1088
- pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1261
+ if (mask === void 0 || kinds === void 0 || (mask & 1 << ch.kindIndex) !== 0) {
1262
+ pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1263
+ }
1089
1264
  }
1090
1265
  }
1091
1266
  }
1267
+ function triviaKindMask(labels, keep) {
1268
+ if (!labels) return void 0;
1269
+ let mask = 0;
1270
+ for (const name of keep) {
1271
+ const idx = labels.indexOf(name);
1272
+ if (idx >= 0) mask |= 1 << idx;
1273
+ }
1274
+ return mask;
1275
+ }
1092
1276
 
1093
1277
  // src/combinators/trivia-skip.ts
1094
1278
  var NOOP_COMMIT = () => {
@@ -1740,10 +1924,15 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
1740
1924
  const saved = beginCstNodeCapture(ctx);
1741
1925
  const savedFields = ctx._fields;
1742
1926
  ctx._fields = capturesFields ? [] : void 0;
1927
+ const savedMask = ctx._triviaCaptureMask;
1928
+ if (build === void 0 && ctx.build?._parsemanTriviaKinds !== void 0 && def.type !== void 0) {
1929
+ ctx._triviaCaptureMask = ctx.build._parsemanTriviaKinds(def.type);
1930
+ }
1743
1931
  if (!capturesTrivia) ctx.captureTrivia = false;
1744
1932
  const r = combinator.parse(input, pos, ctx);
1745
1933
  const fields = capturesFields ? buildFieldMap(ctx._fields) : void 0;
1746
1934
  ctx._fields = savedFields;
1935
+ ctx._triviaCaptureMask = savedMask;
1747
1936
  const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
1748
1937
  if (!r.ok) return r;
1749
1938
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
@@ -1881,7 +2070,13 @@ function parser(opts, root) {
1881
2070
  trivia: opts.trivia,
1882
2071
  ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
1883
2072
  } : {},
1884
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {}
2073
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
2074
+ // Kind-filter for per-node capture. Resolve against this scope's trivia
2075
+ // labels — this parser's own trivia if it declares one, else the INHERITED
2076
+ // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
2077
+ // trivia is inherited rather than re-declared here. No labels → undefined
2078
+ // (capture all).
2079
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
1885
2080
  };
1886
2081
  const result = root.parse(input, pos ?? 0, ctx);
1887
2082
  if (trackLines) {
@@ -2009,77 +2204,11 @@ function expect(combinator, label2) {
2009
2204
 
2010
2205
  // src/compiler/scannable-run.ts
2011
2206
  var BACKSLASH = 92;
2012
- var CLASS_ESCAPES2 = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
2013
2207
  var META = new Set("()[]{}*+?|^$.".split(""));
2014
- var SPACE_RANGES2 = [
2015
- [9, 13],
2016
- [32, 32],
2017
- [160, 160],
2018
- [5760, 5760],
2019
- [8192, 8202],
2020
- [8232, 8232],
2021
- [8233, 8233],
2022
- [8239, 8239],
2023
- [8287, 8287],
2024
- [12288, 12288],
2025
- [65279, 65279]
2026
- ];
2027
- function shorthandRanges2(ch) {
2028
- if (ch === "d") return [[48, 57]];
2029
- if (ch === "s") return SPACE_RANGES2;
2030
- return [[48, 57], [65, 90], [97, 122], [95, 95]];
2031
- }
2032
- function readUnicodeEscape(body, i) {
2033
- if (body[i] !== "\\" || body[i + 1] !== "u") return null;
2034
- const hex = body.slice(i + 2, i + 6);
2035
- if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
2036
- return { cp: Number.parseInt(hex, 16), next: i + 6 };
2037
- }
2038
- function parseClassRanges(body) {
2039
- const ranges = [];
2040
- let i = 0;
2041
- const readAtom = () => {
2042
- const ch = body[i];
2043
- if (ch === void 0) return null;
2044
- if (ch === "\\") {
2045
- const uni = readUnicodeEscape(body, i);
2046
- if (uni) {
2047
- i = uni.next;
2048
- return { cp: uni.cp };
2049
- }
2050
- const e = body[i + 1];
2051
- if (e === void 0) return null;
2052
- i += 2;
2053
- if (e in CLASS_ESCAPES2) return { cp: CLASS_ESCAPES2[e] };
2054
- if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges2(e) };
2055
- if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
2056
- return { cp: e.codePointAt(0) };
2057
- }
2058
- i += ch.length;
2059
- return { cp: ch.codePointAt(0) };
2060
- };
2061
- while (i < body.length) {
2062
- const lo = readAtom();
2063
- if (lo === null) return null;
2064
- if ("set" in lo) {
2065
- ranges.push(...lo.set);
2066
- continue;
2067
- }
2068
- if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
2069
- i += 1;
2070
- const hi = readAtom();
2071
- if (hi === null || "set" in hi) return null;
2072
- ranges.push([lo.cp, hi.cp]);
2073
- } else {
2074
- ranges.push([lo.cp, lo.cp]);
2075
- }
2076
- }
2077
- return ranges.length ? ranges : null;
2078
- }
2079
2208
  function classToRanges(cls) {
2080
- if (cls === "\\d") return shorthandRanges2("d");
2081
- if (cls === "\\w") return shorthandRanges2("w");
2082
- if (cls === "\\s") return shorthandRanges2("s");
2209
+ if (cls === "\\d") return shorthandRanges("d");
2210
+ if (cls === "\\w") return shorthandRanges("w");
2211
+ if (cls === "\\s") return shorthandRanges("s");
2083
2212
  const body = cls.slice(1, -1);
2084
2213
  if (body.startsWith("^")) return null;
2085
2214
  return parseClassRanges(body);
@@ -2097,7 +2226,7 @@ function literalCodePoints(frag) {
2097
2226
  if (ch === "\\") {
2098
2227
  const e = frag[i + 1];
2099
2228
  if (e === void 0) return null;
2100
- out.push(e in CLASS_ESCAPES2 ? CLASS_ESCAPES2[e] : e.codePointAt(0));
2229
+ out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
2101
2230
  i += 2;
2102
2231
  continue;
2103
2232
  }
@@ -2190,7 +2319,7 @@ function parseSeqParts(source) {
2190
2319
  if (e === "d" || e === "w" || e === "s") {
2191
2320
  flush();
2192
2321
  i += 2;
2193
- i += runFrom(shorthandRanges2(e), false, source[i]);
2322
+ i += runFrom(shorthandRanges(e), false, source[i]);
2194
2323
  continue;
2195
2324
  }
2196
2325
  const uni = readUnicodeEscape(source, i);
@@ -2199,8 +2328,8 @@ function parseSeqParts(source) {
2199
2328
  cp2 = uni.cp;
2200
2329
  i = uni.next;
2201
2330
  } else if (e === void 0) return null;
2202
- else if (e in CLASS_ESCAPES2) {
2203
- cp2 = CLASS_ESCAPES2[e];
2331
+ else if (e in CLASS_ESCAPES) {
2332
+ cp2 = CLASS_ESCAPES[e];
2204
2333
  i += 2;
2205
2334
  } else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
2206
2335
  else {
@@ -2307,7 +2436,7 @@ function rangesSubset(sub, sup) {
2307
2436
  }
2308
2437
  function parseClassOperand(body) {
2309
2438
  if (body === "\\d" || body === "\\w" || body === "\\s") {
2310
- return { ranges: shorthandRanges2(body[1]), negated: false };
2439
+ return { ranges: shorthandRanges(body[1]), negated: false };
2311
2440
  }
2312
2441
  if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
2313
2442
  let inner = body.slice(1, -1);
@@ -2740,7 +2869,7 @@ function parseScanShapeCore(source) {
2740
2869
  }
2741
2870
  m = /^\\([dws])([+*])$/.exec(source);
2742
2871
  if (m) {
2743
- return { kind: "chars", ranges: shorthandRanges2(m[1]), minOne: m[2] === "+" };
2872
+ return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
2744
2873
  }
2745
2874
  m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
2746
2875
  if (m) {
@@ -3097,7 +3226,7 @@ function scanBranchLabeled(shape, kindIndex, mint) {
3097
3226
  ` if (${m.end} > _e) {`,
3098
3227
  ` if (_cap) {`,
3099
3228
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
3100
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
3229
+ ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << kindIndex}))) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
3101
3230
  ` }`,
3102
3231
  ` _e = ${m.end}`,
3103
3232
  ` continue`,
@@ -3213,7 +3342,7 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
3213
3342
  ` const _ce = _e + _m${i}[0].length`,
3214
3343
  ` if (_cap) {`,
3215
3344
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
3216
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3345
+ ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3217
3346
  ` }`,
3218
3347
  ` _e = _ce`,
3219
3348
  ` _matched = true`,
@@ -3251,7 +3380,7 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
3251
3380
  ` const _ce = _r${i}.span.end`,
3252
3381
  ` if (_cap) {`,
3253
3382
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
3254
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3383
+ ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3255
3384
  ` }`,
3256
3385
  ` _e = _ce`,
3257
3386
  ` _matched = true`,
@@ -4649,6 +4778,7 @@ function emitNode(def, ctx, pos) {
4649
4778
  const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
4650
4779
  if (!capturesTrivia) ctx.needsEmptyTl = true;
4651
4780
  const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
4781
+ const smk = structural ? v(ctx, "_smk") : null;
4652
4782
  if (structural) ctx.needsHostReads = true;
4653
4783
  const hostTriviaGate = `_ctx.build !== undefined && (_ctx.build._parsemanCaptureTrivia !== undefined ? _ctx.build._parsemanCaptureTrivia(${JSON.stringify(def.type)}) : (_ctx._pmCapTL ??= _hostReads(_ctx.build, 5)))`;
4654
4784
  const allocStmt = structural ? `${i}const ${capTLv} = ${hostTriviaGate}, ${capSTv} = _ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 6))${capFv ? `, ${capFv} = _ctx.build !== undefined && _hostReads(_ctx.build, 2)` : ""}
@@ -4660,15 +4790,15 @@ ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capt
4660
4790
  const fObj = hasFields ? v(ctx, "_fields") : "undefined";
4661
4791
  const stmts = [
4662
4792
  allocStmt,
4663
- `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog${sf ? `, ${sf} = _ctx._fields` : ""}`,
4664
- `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}${sf ? `; _ctx._fields = ${fieldsOn} ? [] : undefined` : ""}`
4793
+ `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog${smk ? `, ${smk} = _ctx._triviaCaptureMask` : ""}${sf ? `, ${sf} = _ctx._fields` : ""}`,
4794
+ `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}${smk ? `; _ctx._triviaCaptureMask = ${capTLv} && _ctx.build._parsemanTriviaKinds !== undefined ? _ctx.build._parsemanTriviaKinds(${JSON.stringify(def.type)}) : ${smk}` : ""}${sf ? `; _ctx._fields = ${fieldsOn} ? [] : undefined` : ""}`
4665
4795
  ];
4666
4796
  const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
4667
4797
  stmts.push(...innerStmts);
4668
4798
  if (sf && fArr) {
4669
4799
  stmts.push(`${i}const ${fArr} = _ctx._fields`);
4670
4800
  }
4671
- stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${sf ? `; _ctx._fields = ${sf}` : ""}`);
4801
+ stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${smk ? `; _ctx._triviaCaptureMask = ${smk}` : ""}${sf ? `; _ctx._fields = ${sf}` : ""}`);
4672
4802
  stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
4673
4803
  let stV = "undefined";
4674
4804
  if (structural) {