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.
package/dist/index.cjs CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/index.ts
@@ -90,6 +80,7 @@ __export(index_exports, {
90
80
  transform: () => transform,
91
81
  trivia: () => trivia,
92
82
  triviaEntries: () => triviaEntries,
83
+ triviaKindMask: () => triviaKindMask,
93
84
  walk: () => walk,
94
85
  withCtx: () => withCtx,
95
86
  word: () => word
@@ -425,9 +416,9 @@ function asciiFoldEq(a, b) {
425
416
  }
426
417
  function literal(value, opts = {}) {
427
418
  const caseInsensitive = opts.caseInsensitive ?? false;
428
- const firstSet = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
419
+ const firstSet2 = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
429
420
  const meta = {
430
- firstSet,
421
+ firstSet: firstSet2,
431
422
  canMatchNewline: value.includes("\n"),
432
423
  isTrivia: false
433
424
  };
@@ -439,7 +430,7 @@ function literal(value, opts = {}) {
439
430
  meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
440
431
  { lo: firstLower, hi: firstLower },
441
432
  { lo: firstUpper, hi: firstUpper }
442
- ] } : firstSet;
433
+ ] } : firstSet2;
443
434
  }
444
435
  return {
445
436
  _tag: "literal",
@@ -462,68 +453,66 @@ function literal(value, opts = {}) {
462
453
  };
463
454
  }
464
455
 
465
- // src/combinators/regex.ts
466
- var firstSetAnalyzer = null;
467
- function registerRegexAnalyzer(analyzer) {
468
- firstSetAnalyzer = analyzer;
469
- }
470
- var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
471
- var SCAN_BAIL_AT = 64;
456
+ // src/regex/classes.ts
457
+ var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
472
458
  var SPACE_RANGES = [
473
459
  [9, 13],
474
460
  [32, 32],
475
461
  [160, 160],
476
462
  [5760, 5760],
477
463
  [8192, 8202],
478
- [8232, 8233],
464
+ [8232, 8232],
465
+ [8233, 8233],
479
466
  [8239, 8239],
480
467
  [8287, 8287],
481
468
  [12288, 12288],
482
469
  [65279, 65279]
483
470
  ];
484
- var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
485
471
  function shorthandRanges(ch) {
486
472
  if (ch === "d") return [[48, 57]];
487
473
  if (ch === "s") return SPACE_RANGES;
488
474
  return [[48, 57], [65, 90], [97, 122], [95, 95]];
489
475
  }
490
- function inRanges(cp, ranges) {
491
- for (let i = 0; i < ranges.length; i++) {
492
- const [lo, hi] = ranges[i];
493
- if (cp >= lo && cp <= hi) return true;
494
- }
495
- return false;
476
+ function readUnicodeEscape(body, i) {
477
+ if (body[i] !== "\\" || body[i + 1] !== "u") return null;
478
+ const hex = body.slice(i + 2, i + 6);
479
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
480
+ return { cp: Number.parseInt(hex, 16), next: i + 6 };
496
481
  }
497
- function readClassRanges(body) {
498
- if (body.startsWith("^")) return null;
482
+ function parseClassRanges(body) {
499
483
  const ranges = [];
500
484
  let i = 0;
501
485
  const readAtom = () => {
502
486
  const ch = body[i];
503
487
  if (ch === void 0) return null;
504
488
  if (ch === "\\") {
505
- const esc = body[i + 1];
506
- if (esc === void 0) return null;
489
+ const uni = readUnicodeEscape(body, i);
490
+ if (uni) {
491
+ i = uni.next;
492
+ return { cp: uni.cp };
493
+ }
494
+ const e = body[i + 1];
495
+ if (e === void 0) return null;
507
496
  i += 2;
508
- if (esc === "d" || esc === "w" || esc === "s") return { ranges: shorthandRanges(esc) };
509
- if (esc in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[esc] };
510
- if (esc >= "a" && esc <= "z" || esc >= "A" && esc <= "Z") return null;
511
- return { cp: esc.charCodeAt(0) };
497
+ if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
498
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
499
+ if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
500
+ return { cp: e.codePointAt(0) };
512
501
  }
513
- i++;
514
- return { cp: ch.charCodeAt(0) };
502
+ i += ch.length;
503
+ return { cp: ch.codePointAt(0) };
515
504
  };
516
505
  while (i < body.length) {
517
506
  const lo = readAtom();
518
- if (!lo) return null;
519
- if ("ranges" in lo) {
520
- ranges.push(...lo.ranges);
507
+ if (lo === null) return null;
508
+ if ("set" in lo) {
509
+ ranges.push(...lo.set);
521
510
  continue;
522
511
  }
523
512
  if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
524
- i++;
513
+ i += 1;
525
514
  const hi = readAtom();
526
- if (!hi || "ranges" in hi) return null;
515
+ if (hi === null || "set" in hi) return null;
527
516
  ranges.push([lo.cp, hi.cp]);
528
517
  } else {
529
518
  ranges.push([lo.cp, lo.cp]);
@@ -531,6 +520,25 @@ function readClassRanges(body) {
531
520
  }
532
521
  return ranges.length ? ranges : null;
533
522
  }
523
+
524
+ // src/combinators/regex.ts
525
+ var firstSetAnalyzer = null;
526
+ function registerRegexAnalyzer(analyzer) {
527
+ firstSetAnalyzer = analyzer;
528
+ }
529
+ var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
530
+ var SCAN_BAIL_AT = 64;
531
+ function inRanges(cp, ranges) {
532
+ for (let i = 0; i < ranges.length; i++) {
533
+ const [lo, hi] = ranges[i];
534
+ if (cp >= lo && cp <= hi) return true;
535
+ }
536
+ return false;
537
+ }
538
+ function readClassRanges(body) {
539
+ if (body.startsWith("^")) return null;
540
+ return parseClassRanges(body);
541
+ }
534
542
  function shortScanner(source, flags) {
535
543
  if (/[imsuvy]/.test(flags)) return null;
536
544
  let ranges = null;
@@ -564,8 +572,8 @@ function regex(pattern, flags = "") {
564
572
  const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
565
573
  const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
566
574
  const scan = shortScanner(source, resolvedFlags);
567
- const { firstSet, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
568
- const meta = { firstSet, canMatchNewline, isTrivia: false };
575
+ const { firstSet: firstSet2, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
576
+ const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
569
577
  return {
570
578
  _tag: "regex",
571
579
  _meta: meta,
@@ -593,111 +601,266 @@ function regex(pattern, flags = "") {
593
601
  };
594
602
  }
595
603
 
596
- // src/combinators/regex-analyze.ts
597
- var import_regexp_tree = __toESM(require("regexp-tree"), 1);
598
- function firstSetFromRegex(pattern) {
599
- try {
600
- const ast = import_regexp_tree.default.parse(`/${pattern}/`);
601
- return extractFirstSet(ast.body);
602
- } catch {
603
- return { firstSet: any(), canMatchNewline: true };
604
- }
604
+ // src/regex/first-set.ts
605
+ var ANY = { t: "any" };
606
+ var EMPTY2 = { t: "empty" };
607
+ function charNode(set, nl) {
608
+ return { t: "char", set, nl };
609
+ }
610
+ function rangesToSet(ranges) {
611
+ let fs = empty();
612
+ for (const [lo, hi] of ranges) fs = union(fs, fromRange(lo, hi));
613
+ return fs;
614
+ }
615
+ function rangesHaveNewline(ranges) {
616
+ return ranges.some(([lo, hi]) => lo <= 10 && 10 <= hi);
605
617
  }
606
- function canBeEmpty(node2) {
607
- if (!node2) return true;
608
- switch (node2.type) {
609
- case "Repetition": {
610
- const q = node2.quantifier;
611
- return q.kind === "*" || q.kind === "?" || q.kind === "Range" && (q.from ?? 1) === 0;
618
+ var BAIL = /* @__PURE__ */ Symbol("regex-bail");
619
+ function parseRegex(src) {
620
+ let i = 0;
621
+ const parseAlt = () => {
622
+ const arms = [parseSeq()];
623
+ while (src[i] === "|") {
624
+ i++;
625
+ arms.push(parseSeq());
612
626
  }
613
- case "Alternative": {
614
- const exprs = node2.expressions;
615
- return !exprs || exprs.length === 0 || exprs.every((e) => canBeEmpty(e));
627
+ return arms.length === 1 ? arms[0] : { t: "alt", arms };
628
+ };
629
+ const parseSeq = () => {
630
+ const parts = [];
631
+ while (i < src.length) {
632
+ const ch = src[i];
633
+ if (ch === "|" || ch === ")") break;
634
+ parts.push(parseQuantified());
635
+ }
636
+ if (parts.length === 0) return EMPTY2;
637
+ return parts.length === 1 ? parts[0] : { t: "seq", parts };
638
+ };
639
+ const parseQuantified = () => {
640
+ const atom = parseAtom();
641
+ const ch = src[i];
642
+ let min = null;
643
+ if (ch === "*") {
644
+ min = 0;
645
+ i++;
646
+ } else if (ch === "+") {
647
+ min = 1;
648
+ i++;
649
+ } else if (ch === "?") {
650
+ min = 0;
651
+ i++;
652
+ } else if (ch === "{") {
653
+ min = parseBraceQuantifier();
616
654
  }
617
- case "Group":
618
- return canBeEmpty(node2.expression);
619
- case "Disjunction":
620
- return canBeEmpty(node2.left) || canBeEmpty(node2.right);
621
- default:
622
- return false;
623
- }
624
- }
625
- function extractFirstSet(node2) {
626
- if (!node2) return { firstSet: empty(), canMatchNewline: false };
627
- switch (node2.type) {
628
- case "Disjunction": {
629
- const left = extractFirstSet(node2.left);
630
- const right = extractFirstSet(node2.right);
631
- return {
632
- firstSet: union(left.firstSet, right.firstSet),
633
- canMatchNewline: left.canMatchNewline || right.canMatchNewline
634
- };
655
+ if (min === null) return atom;
656
+ if (src[i] === "?") i++;
657
+ return { t: "rep", node: atom, min };
658
+ };
659
+ const parseBraceQuantifier = () => {
660
+ const close = src.indexOf("}", i);
661
+ if (close === -1) return null;
662
+ const m = /^(\d+)(?:,(\d*))?$/.exec(src.slice(i + 1, close));
663
+ if (!m) return null;
664
+ i = close + 1;
665
+ return Number.parseInt(m[1], 10);
666
+ };
667
+ const parseAtom = () => {
668
+ const ch = src[i];
669
+ switch (ch) {
670
+ case "(":
671
+ return parseGroup();
672
+ case "[":
673
+ return parseClass();
674
+ case ".":
675
+ i++;
676
+ return charNode(any(), false);
677
+ // `.` matches any char except line terminators
678
+ case "\\":
679
+ return parseEscape();
680
+ case "^":
681
+ case "$":
682
+ i++;
683
+ return EMPTY2;
684
+ // anchors are zero-width
685
+ case "{":
686
+ i++;
687
+ return charNode(fromRange(123, 123), false);
688
+ // a `{` with nothing to quantify is a literal brace
689
+ default:
690
+ i += ch.length;
691
+ return charNode(fromRange(ch.codePointAt(0), ch.codePointAt(0)), ch === "\n");
635
692
  }
636
- case "Alternative": {
637
- const exprs = node2.expressions;
638
- if (!exprs || exprs.length === 0) return { firstSet: empty(), canMatchNewline: false };
639
- let fs = empty();
640
- let canNL = false;
641
- for (const expr of exprs) {
642
- const r = extractFirstSet(expr);
643
- fs = union(fs, r.firstSet);
644
- canNL = canNL || r.canMatchNewline;
645
- if (!canBeEmpty(expr)) break;
646
- }
647
- return { firstSet: fs, canMatchNewline: canNL };
693
+ };
694
+ const parseGroup = () => {
695
+ i++;
696
+ let lookaround = false;
697
+ if (src[i] === "?") {
698
+ const c1 = src[i + 1];
699
+ if (c1 === ":") i += 2;
700
+ else if (c1 === "=" || c1 === "!") {
701
+ i += 2;
702
+ lookaround = true;
703
+ } else if (c1 === "<") {
704
+ const c2 = src[i + 2];
705
+ if (c2 === "=" || c2 === "!") {
706
+ i += 3;
707
+ lookaround = true;
708
+ } else {
709
+ const gt = src.indexOf(">", i);
710
+ if (gt === -1) throw BAIL;
711
+ i = gt + 1;
712
+ }
713
+ } else throw BAIL;
648
714
  }
649
- case "Char": {
650
- const kind = node2.kind;
651
- if (kind === "simple") {
652
- const code = node2.codePoint ?? node2.value.codePointAt(0) ?? 0;
653
- return { firstSet: fromRange(code, code), canMatchNewline: code === 10 };
654
- }
655
- if (kind === "meta") {
656
- const val = node2.value;
657
- if (val === ".") return { firstSet: any(), canMatchNewline: false };
658
- if (val === "d") return { firstSet: fromRange(48, 57), canMatchNewline: false };
659
- if (val === "D") return { firstSet: any(), canMatchNewline: true };
660
- if (val === "w") return {
661
- firstSet: union(
662
- union(fromRange(48, 57), fromRange(65, 90)),
663
- union(fromRange(97, 122), fromRange(95, 95))
664
- ),
665
- canMatchNewline: false
666
- };
667
- if (val === "s") return { firstSet: fromRange(9, 13), canMatchNewline: true };
668
- if (val === "n") return { firstSet: fromRange(10, 10), canMatchNewline: true };
715
+ const inner = parseAlt();
716
+ if (src[i] !== ")") throw BAIL;
717
+ i++;
718
+ return lookaround ? EMPTY2 : inner;
719
+ };
720
+ const parseClass = () => {
721
+ let k = i + 1;
722
+ const negated = src[k] === "^";
723
+ if (negated) k++;
724
+ let body = "";
725
+ while (k < src.length && src[k] !== "]") {
726
+ if (src[k] === "\\") {
727
+ body += src[k] + (src[k + 1] ?? "");
728
+ k += 2;
729
+ } else {
730
+ body += src[k];
731
+ k++;
669
732
  }
670
- return { firstSet: any(), canMatchNewline: true };
671
733
  }
672
- case "CharacterClass": {
673
- const expressions = node2.expressions;
734
+ if (src[k] !== "]") throw BAIL;
735
+ i = k + 1;
736
+ if (negated) return charNode(any(), true);
737
+ const ranges = parseClassRanges(body);
738
+ if (!ranges) return charNode(any(), true);
739
+ return charNode(rangesToSet(ranges), rangesHaveNewline(ranges));
740
+ };
741
+ const parseEscape = () => {
742
+ const e = src[i + 1];
743
+ if (e === void 0) throw BAIL;
744
+ if (e === "u" && /^[0-9a-fA-F]{4}$/.test(src.slice(i + 2, i + 6))) {
745
+ const cp = Number.parseInt(src.slice(i + 2, i + 6), 16);
746
+ i += 6;
747
+ return charNode(fromRange(cp, cp), cp === 10);
748
+ }
749
+ if (e === "x" && /^[0-9a-fA-F]{2}$/.test(src.slice(i + 2, i + 4))) {
750
+ const cp = Number.parseInt(src.slice(i + 2, i + 4), 16);
751
+ i += 4;
752
+ return charNode(fromRange(cp, cp), cp === 10);
753
+ }
754
+ i += 2;
755
+ switch (e) {
756
+ case "d":
757
+ return charNode(rangesToSet(shorthandRanges("d")), false);
758
+ case "w":
759
+ return charNode(rangesToSet(shorthandRanges("w")), false);
760
+ case "s":
761
+ return charNode(rangesToSet(shorthandRanges("s")), true);
762
+ case "D":
763
+ case "W":
764
+ case "S":
765
+ return charNode(any(), true);
766
+ // negated shorthand → widen
767
+ case "b":
768
+ case "B":
769
+ return EMPTY2;
770
+ // word boundary: zero-width
771
+ case "n":
772
+ return charNode(fromRange(10, 10), true);
773
+ case "r":
774
+ return charNode(fromRange(13, 13), false);
775
+ case "t":
776
+ return charNode(fromRange(9, 9), false);
777
+ case "f":
778
+ return charNode(fromRange(12, 12), false);
779
+ case "v":
780
+ return charNode(fromRange(11, 11), false);
781
+ case "0":
782
+ return charNode(fromRange(0, 0), false);
783
+ default:
784
+ if (e >= "1" && e <= "9") return ANY;
785
+ return charNode(fromRange(e.codePointAt(0), e.codePointAt(0)), e === "\n");
786
+ }
787
+ };
788
+ const node2 = parseAlt();
789
+ if (i < src.length) throw BAIL;
790
+ return node2;
791
+ }
792
+ function nullable(n) {
793
+ switch (n.t) {
794
+ case "char":
795
+ return false;
796
+ case "any":
797
+ return true;
798
+ case "empty":
799
+ return true;
800
+ case "seq":
801
+ return n.parts.every(nullable);
802
+ case "alt":
803
+ return n.arms.some(nullable);
804
+ case "rep":
805
+ return n.min === 0 || nullable(n.node);
806
+ }
807
+ }
808
+ function firstSet(n) {
809
+ switch (n.t) {
810
+ case "char":
811
+ return n.set;
812
+ case "any":
813
+ return any();
814
+ case "empty":
815
+ return empty();
816
+ case "rep":
817
+ return firstSet(n.node);
818
+ case "alt": {
819
+ let fs = empty();
820
+ for (const arm of n.arms) fs = union(fs, firstSet(arm));
821
+ return fs;
822
+ }
823
+ case "seq": {
674
824
  let fs = empty();
675
- let canNL = false;
676
- for (const expr of expressions) {
677
- if (expr.type === "ClassRange") {
678
- const from = expr.from.codePoint;
679
- const to = expr.to.codePoint;
680
- fs = union(fs, fromRange(from, to));
681
- if (from <= 10 && 10 <= to) canNL = true;
682
- } else if (expr.type === "Char") {
683
- const r = extractFirstSet(expr);
684
- fs = union(fs, r.firstSet);
685
- canNL = canNL || r.canMatchNewline;
686
- }
825
+ for (const part of n.parts) {
826
+ fs = union(fs, firstSet(part));
827
+ if (!nullable(part)) break;
687
828
  }
688
- if (node2.negative) return { firstSet: any(), canMatchNewline: true };
689
- return { firstSet: fs, canMatchNewline: canNL };
690
- }
691
- case "Repetition": {
692
- return extractFirstSet(node2.expression);
829
+ return fs;
693
830
  }
694
- case "Group": {
695
- return extractFirstSet(node2.expression);
831
+ }
832
+ }
833
+ function firstCanBeNewline(n) {
834
+ switch (n.t) {
835
+ case "char":
836
+ return n.nl;
837
+ case "any":
838
+ return true;
839
+ case "empty":
840
+ return false;
841
+ case "rep":
842
+ return firstCanBeNewline(n.node);
843
+ case "alt":
844
+ return n.arms.some(firstCanBeNewline);
845
+ case "seq": {
846
+ for (const part of n.parts) {
847
+ if (firstCanBeNewline(part)) return true;
848
+ if (!nullable(part)) break;
849
+ }
850
+ return false;
696
851
  }
697
- default:
698
- return { firstSet: any(), canMatchNewline: true };
699
852
  }
700
853
  }
854
+ function firstSetFromRegex(source) {
855
+ let ast;
856
+ try {
857
+ ast = parseRegex(source);
858
+ } catch {
859
+ return { firstSet: any(), canMatchNewline: true };
860
+ }
861
+ if (nullable(ast)) return { firstSet: any(), canMatchNewline: true };
862
+ return { firstSet: firstSet(ast), canMatchNewline: firstCanBeNewline(ast) };
863
+ }
701
864
 
702
865
  // src/combinators/keywords.ts
703
866
  function word(str, boundary = "_0-9A-Za-z") {
@@ -715,19 +878,19 @@ function keywords(words, opts = {}) {
715
878
  const boundary = opts.boundary ? `(?![${opts.boundary}])` : "";
716
879
  const flags = opts.caseInsensitive ? "iuy" : "uy";
717
880
  const re = new RegExp(`(?:${alt})${boundary}`, flags);
718
- let firstSet = empty();
881
+ let firstSet2 = empty();
719
882
  for (const w of sorted) {
720
883
  if (w.length === 0) continue;
721
884
  const cp = w.codePointAt(0);
722
- firstSet = union(firstSet, fromChar(cp));
885
+ firstSet2 = union(firstSet2, fromChar(cp));
723
886
  if (opts.caseInsensitive) {
724
887
  const u = String.fromCodePoint(cp).toUpperCase().codePointAt(0);
725
888
  const l = String.fromCodePoint(cp).toLowerCase().codePointAt(0);
726
- if (u !== void 0) firstSet = union(firstSet, fromChar(u));
727
- if (l !== void 0) firstSet = union(firstSet, fromChar(l));
889
+ if (u !== void 0) firstSet2 = union(firstSet2, fromChar(u));
890
+ if (l !== void 0) firstSet2 = union(firstSet2, fromChar(l));
728
891
  }
729
892
  }
730
- const meta = { firstSet, canMatchNewline: false, isTrivia: false };
893
+ const meta = { firstSet: firstSet2, canMatchNewline: false, isTrivia: false };
731
894
  return {
732
895
  _tag: "keywords",
733
896
  _meta: meta,
@@ -1104,13 +1267,25 @@ function tryFastLabeledScan(input, cur, trivia2) {
1104
1267
  }
1105
1268
  function recordTriviaChunks(ctx, chunks) {
1106
1269
  const kinds = ctx.triviaKindLabels;
1270
+ const mask = ctx._triviaCaptureMask;
1107
1271
  for (const ch of chunks) {
1108
1272
  pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1109
1273
  if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
1110
- pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1274
+ if (mask === void 0 || kinds === void 0 || (mask & 1 << ch.kindIndex) !== 0) {
1275
+ pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1276
+ }
1111
1277
  }
1112
1278
  }
1113
1279
  }
1280
+ function triviaKindMask(labels, keep) {
1281
+ if (!labels) return void 0;
1282
+ let mask = 0;
1283
+ for (const name of keep) {
1284
+ const idx = labels.indexOf(name);
1285
+ if (idx >= 0) mask |= 1 << idx;
1286
+ }
1287
+ return mask;
1288
+ }
1114
1289
 
1115
1290
  // src/combinators/trivia-skip.ts
1116
1291
  var NOOP_COMMIT = () => {
@@ -1799,10 +1974,15 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
1799
1974
  const saved = beginCstNodeCapture(ctx);
1800
1975
  const savedFields = ctx._fields;
1801
1976
  ctx._fields = capturesFields ? [] : void 0;
1977
+ const savedMask = ctx._triviaCaptureMask;
1978
+ if (build === void 0 && ctx.build?._parsemanTriviaKinds !== void 0 && def.type !== void 0) {
1979
+ ctx._triviaCaptureMask = ctx.build._parsemanTriviaKinds(def.type);
1980
+ }
1802
1981
  if (!capturesTrivia) ctx.captureTrivia = false;
1803
1982
  const r = combinator.parse(input, pos, ctx);
1804
1983
  const fields = capturesFields ? buildFieldMap(ctx._fields) : void 0;
1805
1984
  ctx._fields = savedFields;
1985
+ ctx._triviaCaptureMask = savedMask;
1806
1986
  const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
1807
1987
  if (!r.ok) return r;
1808
1988
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
@@ -1940,7 +2120,13 @@ function parser(opts, root) {
1940
2120
  trivia: opts.trivia,
1941
2121
  ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
1942
2122
  } : {},
1943
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {}
2123
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
2124
+ // Kind-filter for per-node capture. Resolve against this scope's trivia
2125
+ // labels — this parser's own trivia if it declares one, else the INHERITED
2126
+ // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
2127
+ // trivia is inherited rather than re-declared here. No labels → undefined
2128
+ // (capture all).
2129
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
1944
2130
  };
1945
2131
  const result = root.parse(input, pos ?? 0, ctx);
1946
2132
  if (trackLines) {
@@ -2091,77 +2277,11 @@ function expect(combinator, label2) {
2091
2277
 
2092
2278
  // src/compiler/scannable-run.ts
2093
2279
  var BACKSLASH = 92;
2094
- var CLASS_ESCAPES2 = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
2095
2280
  var META = new Set("()[]{}*+?|^$.".split(""));
2096
- var SPACE_RANGES2 = [
2097
- [9, 13],
2098
- [32, 32],
2099
- [160, 160],
2100
- [5760, 5760],
2101
- [8192, 8202],
2102
- [8232, 8232],
2103
- [8233, 8233],
2104
- [8239, 8239],
2105
- [8287, 8287],
2106
- [12288, 12288],
2107
- [65279, 65279]
2108
- ];
2109
- function shorthandRanges2(ch) {
2110
- if (ch === "d") return [[48, 57]];
2111
- if (ch === "s") return SPACE_RANGES2;
2112
- return [[48, 57], [65, 90], [97, 122], [95, 95]];
2113
- }
2114
- function readUnicodeEscape(body, i) {
2115
- if (body[i] !== "\\" || body[i + 1] !== "u") return null;
2116
- const hex = body.slice(i + 2, i + 6);
2117
- if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
2118
- return { cp: Number.parseInt(hex, 16), next: i + 6 };
2119
- }
2120
- function parseClassRanges(body) {
2121
- const ranges = [];
2122
- let i = 0;
2123
- const readAtom = () => {
2124
- const ch = body[i];
2125
- if (ch === void 0) return null;
2126
- if (ch === "\\") {
2127
- const uni = readUnicodeEscape(body, i);
2128
- if (uni) {
2129
- i = uni.next;
2130
- return { cp: uni.cp };
2131
- }
2132
- const e = body[i + 1];
2133
- if (e === void 0) return null;
2134
- i += 2;
2135
- if (e in CLASS_ESCAPES2) return { cp: CLASS_ESCAPES2[e] };
2136
- if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges2(e) };
2137
- if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
2138
- return { cp: e.codePointAt(0) };
2139
- }
2140
- i += ch.length;
2141
- return { cp: ch.codePointAt(0) };
2142
- };
2143
- while (i < body.length) {
2144
- const lo = readAtom();
2145
- if (lo === null) return null;
2146
- if ("set" in lo) {
2147
- ranges.push(...lo.set);
2148
- continue;
2149
- }
2150
- if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
2151
- i += 1;
2152
- const hi = readAtom();
2153
- if (hi === null || "set" in hi) return null;
2154
- ranges.push([lo.cp, hi.cp]);
2155
- } else {
2156
- ranges.push([lo.cp, lo.cp]);
2157
- }
2158
- }
2159
- return ranges.length ? ranges : null;
2160
- }
2161
2281
  function classToRanges(cls) {
2162
- if (cls === "\\d") return shorthandRanges2("d");
2163
- if (cls === "\\w") return shorthandRanges2("w");
2164
- if (cls === "\\s") return shorthandRanges2("s");
2282
+ if (cls === "\\d") return shorthandRanges("d");
2283
+ if (cls === "\\w") return shorthandRanges("w");
2284
+ if (cls === "\\s") return shorthandRanges("s");
2165
2285
  const body = cls.slice(1, -1);
2166
2286
  if (body.startsWith("^")) return null;
2167
2287
  return parseClassRanges(body);
@@ -2179,7 +2299,7 @@ function literalCodePoints(frag) {
2179
2299
  if (ch === "\\") {
2180
2300
  const e = frag[i + 1];
2181
2301
  if (e === void 0) return null;
2182
- out.push(e in CLASS_ESCAPES2 ? CLASS_ESCAPES2[e] : e.codePointAt(0));
2302
+ out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
2183
2303
  i += 2;
2184
2304
  continue;
2185
2305
  }
@@ -2272,7 +2392,7 @@ function parseSeqParts(source) {
2272
2392
  if (e === "d" || e === "w" || e === "s") {
2273
2393
  flush();
2274
2394
  i += 2;
2275
- i += runFrom(shorthandRanges2(e), false, source[i]);
2395
+ i += runFrom(shorthandRanges(e), false, source[i]);
2276
2396
  continue;
2277
2397
  }
2278
2398
  const uni = readUnicodeEscape(source, i);
@@ -2281,8 +2401,8 @@ function parseSeqParts(source) {
2281
2401
  cp2 = uni.cp;
2282
2402
  i = uni.next;
2283
2403
  } else if (e === void 0) return null;
2284
- else if (e in CLASS_ESCAPES2) {
2285
- cp2 = CLASS_ESCAPES2[e];
2404
+ else if (e in CLASS_ESCAPES) {
2405
+ cp2 = CLASS_ESCAPES[e];
2286
2406
  i += 2;
2287
2407
  } else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
2288
2408
  else {
@@ -2389,7 +2509,7 @@ function rangesSubset(sub, sup) {
2389
2509
  }
2390
2510
  function parseClassOperand(body) {
2391
2511
  if (body === "\\d" || body === "\\w" || body === "\\s") {
2392
- return { ranges: shorthandRanges2(body[1]), negated: false };
2512
+ return { ranges: shorthandRanges(body[1]), negated: false };
2393
2513
  }
2394
2514
  if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
2395
2515
  let inner = body.slice(1, -1);
@@ -2822,7 +2942,7 @@ function parseScanShapeCore(source) {
2822
2942
  }
2823
2943
  m = /^\\([dws])([+*])$/.exec(source);
2824
2944
  if (m) {
2825
- return { kind: "chars", ranges: shorthandRanges2(m[1]), minOne: m[2] === "+" };
2945
+ return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
2826
2946
  }
2827
2947
  m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
2828
2948
  if (m) {
@@ -3179,7 +3299,7 @@ function scanBranchLabeled(shape, kindIndex, mint) {
3179
3299
  ` if (${m.end} > _e) {`,
3180
3300
  ` if (_cap) {`,
3181
3301
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
3182
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
3302
+ ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << kindIndex}))) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
3183
3303
  ` }`,
3184
3304
  ` _e = ${m.end}`,
3185
3305
  ` continue`,
@@ -3295,7 +3415,7 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
3295
3415
  ` const _ce = _e + _m${i}[0].length`,
3296
3416
  ` if (_cap) {`,
3297
3417
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
3298
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3418
+ ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3299
3419
  ` }`,
3300
3420
  ` _e = _ce`,
3301
3421
  ` _matched = true`,
@@ -3333,7 +3453,7 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
3333
3453
  ` const _ce = _r${i}.span.end`,
3334
3454
  ` if (_cap) {`,
3335
3455
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
3336
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3456
+ ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3337
3457
  ` }`,
3338
3458
  ` _e = _ce`,
3339
3459
  ` _matched = true`,
@@ -4723,6 +4843,7 @@ function emitNode(def, ctx, pos) {
4723
4843
  const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
4724
4844
  if (!capturesTrivia) ctx.needsEmptyTl = true;
4725
4845
  const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
4846
+ const smk = structural ? v(ctx, "_smk") : null;
4726
4847
  if (structural) ctx.needsHostReads = true;
4727
4848
  const hostTriviaGate = `_ctx.build !== undefined && (_ctx.build._parsemanCaptureTrivia !== undefined ? _ctx.build._parsemanCaptureTrivia(${JSON.stringify(def.type)}) : (_ctx._pmCapTL ??= _hostReads(_ctx.build, 5)))`;
4728
4849
  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)` : ""}
@@ -4734,15 +4855,15 @@ ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capt
4734
4855
  const fObj = hasFields ? v(ctx, "_fields") : "undefined";
4735
4856
  const stmts = [
4736
4857
  allocStmt,
4737
- `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog${sf ? `, ${sf} = _ctx._fields` : ""}`,
4738
- `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}${sf ? `; _ctx._fields = ${fieldsOn} ? [] : undefined` : ""}`
4858
+ `${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` : ""}`,
4859
+ `${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` : ""}`
4739
4860
  ];
4740
4861
  const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
4741
4862
  stmts.push(...innerStmts);
4742
4863
  if (sf && fArr) {
4743
4864
  stmts.push(`${i}const ${fArr} = _ctx._fields`);
4744
4865
  }
4745
- stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${sf ? `; _ctx._fields = ${sf}` : ""}`);
4866
+ 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}` : ""}`);
4746
4867
  stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
4747
4868
  let stV = "undefined";
4748
4869
  if (structural) {
@@ -6176,7 +6297,8 @@ function run(entry, input, options = {}) {
6176
6297
  _triviaLog: triviaLog,
6177
6298
  _errors: errors,
6178
6299
  build: options.build,
6179
- state: options.state
6300
+ state: options.state,
6301
+ ...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {}
6180
6302
  };
6181
6303
  const r = invoke(entry, input, 0, ctx);
6182
6304
  let unconsumedFrom = null;