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.
@@ -371,9 +371,9 @@ function asciiFoldEq(a, b) {
371
371
  }
372
372
  function literal(value, opts = {}) {
373
373
  const caseInsensitive = opts.caseInsensitive ?? false;
374
- const firstSet = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
374
+ const firstSet2 = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
375
375
  const meta = {
376
- firstSet,
376
+ firstSet: firstSet2,
377
377
  canMatchNewline: value.includes("\n"),
378
378
  isTrivia: false
379
379
  };
@@ -385,7 +385,7 @@ function literal(value, opts = {}) {
385
385
  meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
386
386
  { lo: firstLower, hi: firstLower },
387
387
  { lo: firstUpper, hi: firstUpper }
388
- ] } : firstSet;
388
+ ] } : firstSet2;
389
389
  }
390
390
  return {
391
391
  _tag: "literal",
@@ -408,68 +408,66 @@ function literal(value, opts = {}) {
408
408
  };
409
409
  }
410
410
 
411
- // src/combinators/regex.ts
412
- var firstSetAnalyzer = null;
413
- function registerRegexAnalyzer(analyzer) {
414
- firstSetAnalyzer = analyzer;
415
- }
416
- var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
417
- var SCAN_BAIL_AT = 64;
411
+ // src/regex/classes.ts
412
+ var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
418
413
  var SPACE_RANGES = [
419
414
  [9, 13],
420
415
  [32, 32],
421
416
  [160, 160],
422
417
  [5760, 5760],
423
418
  [8192, 8202],
424
- [8232, 8233],
419
+ [8232, 8232],
420
+ [8233, 8233],
425
421
  [8239, 8239],
426
422
  [8287, 8287],
427
423
  [12288, 12288],
428
424
  [65279, 65279]
429
425
  ];
430
- var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
431
426
  function shorthandRanges(ch) {
432
427
  if (ch === "d") return [[48, 57]];
433
428
  if (ch === "s") return SPACE_RANGES;
434
429
  return [[48, 57], [65, 90], [97, 122], [95, 95]];
435
430
  }
436
- function inRanges(cp, ranges) {
437
- for (let i = 0; i < ranges.length; i++) {
438
- const [lo, hi] = ranges[i];
439
- if (cp >= lo && cp <= hi) return true;
440
- }
441
- return false;
431
+ function readUnicodeEscape(body, i) {
432
+ if (body[i] !== "\\" || body[i + 1] !== "u") return null;
433
+ const hex = body.slice(i + 2, i + 6);
434
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
435
+ return { cp: Number.parseInt(hex, 16), next: i + 6 };
442
436
  }
443
- function readClassRanges(body) {
444
- if (body.startsWith("^")) return null;
437
+ function parseClassRanges(body) {
445
438
  const ranges = [];
446
439
  let i = 0;
447
440
  const readAtom = () => {
448
441
  const ch = body[i];
449
442
  if (ch === void 0) return null;
450
443
  if (ch === "\\") {
451
- const esc = body[i + 1];
452
- if (esc === void 0) return null;
444
+ const uni = readUnicodeEscape(body, i);
445
+ if (uni) {
446
+ i = uni.next;
447
+ return { cp: uni.cp };
448
+ }
449
+ const e = body[i + 1];
450
+ if (e === void 0) return null;
453
451
  i += 2;
454
- if (esc === "d" || esc === "w" || esc === "s") return { ranges: shorthandRanges(esc) };
455
- if (esc in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[esc] };
456
- if (esc >= "a" && esc <= "z" || esc >= "A" && esc <= "Z") return null;
457
- return { cp: esc.charCodeAt(0) };
452
+ if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
453
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
454
+ if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
455
+ return { cp: e.codePointAt(0) };
458
456
  }
459
- i++;
460
- return { cp: ch.charCodeAt(0) };
457
+ i += ch.length;
458
+ return { cp: ch.codePointAt(0) };
461
459
  };
462
460
  while (i < body.length) {
463
461
  const lo = readAtom();
464
- if (!lo) return null;
465
- if ("ranges" in lo) {
466
- ranges.push(...lo.ranges);
462
+ if (lo === null) return null;
463
+ if ("set" in lo) {
464
+ ranges.push(...lo.set);
467
465
  continue;
468
466
  }
469
467
  if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
470
- i++;
468
+ i += 1;
471
469
  const hi = readAtom();
472
- if (!hi || "ranges" in hi) return null;
470
+ if (hi === null || "set" in hi) return null;
473
471
  ranges.push([lo.cp, hi.cp]);
474
472
  } else {
475
473
  ranges.push([lo.cp, lo.cp]);
@@ -477,6 +475,25 @@ function readClassRanges(body) {
477
475
  }
478
476
  return ranges.length ? ranges : null;
479
477
  }
478
+
479
+ // src/combinators/regex.ts
480
+ var firstSetAnalyzer = null;
481
+ function registerRegexAnalyzer(analyzer) {
482
+ firstSetAnalyzer = analyzer;
483
+ }
484
+ var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
485
+ var SCAN_BAIL_AT = 64;
486
+ function inRanges(cp, ranges) {
487
+ for (let i = 0; i < ranges.length; i++) {
488
+ const [lo, hi] = ranges[i];
489
+ if (cp >= lo && cp <= hi) return true;
490
+ }
491
+ return false;
492
+ }
493
+ function readClassRanges(body) {
494
+ if (body.startsWith("^")) return null;
495
+ return parseClassRanges(body);
496
+ }
480
497
  function shortScanner(source, flags) {
481
498
  if (/[imsuvy]/.test(flags)) return null;
482
499
  let ranges = null;
@@ -510,8 +527,8 @@ function regex(pattern, flags = "") {
510
527
  const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
511
528
  const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
512
529
  const scan = shortScanner(source, resolvedFlags);
513
- const { firstSet, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
514
- const meta = { firstSet, canMatchNewline, isTrivia: false };
530
+ const { firstSet: firstSet2, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
531
+ const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
515
532
  return {
516
533
  _tag: "regex",
517
534
  _meta: meta,
@@ -539,111 +556,266 @@ function regex(pattern, flags = "") {
539
556
  };
540
557
  }
541
558
 
542
- // src/combinators/regex-analyze.ts
543
- import regexpTree from "regexp-tree";
544
- function firstSetFromRegex(pattern) {
545
- try {
546
- const ast = regexpTree.parse(`/${pattern}/`);
547
- return extractFirstSet(ast.body);
548
- } catch {
549
- return { firstSet: any(), canMatchNewline: true };
550
- }
559
+ // src/regex/first-set.ts
560
+ var ANY = { t: "any" };
561
+ var EMPTY2 = { t: "empty" };
562
+ function charNode(set, nl) {
563
+ return { t: "char", set, nl };
564
+ }
565
+ function rangesToSet(ranges) {
566
+ let fs2 = empty();
567
+ for (const [lo, hi] of ranges) fs2 = union(fs2, fromRange(lo, hi));
568
+ return fs2;
569
+ }
570
+ function rangesHaveNewline(ranges) {
571
+ return ranges.some(([lo, hi]) => lo <= 10 && 10 <= hi);
551
572
  }
552
- function canBeEmpty(node2) {
553
- if (!node2) return true;
554
- switch (node2.type) {
555
- case "Repetition": {
556
- const q = node2.quantifier;
557
- return q.kind === "*" || q.kind === "?" || q.kind === "Range" && (q.from ?? 1) === 0;
573
+ var BAIL = /* @__PURE__ */ Symbol("regex-bail");
574
+ function parseRegex(src) {
575
+ let i = 0;
576
+ const parseAlt = () => {
577
+ const arms = [parseSeq()];
578
+ while (src[i] === "|") {
579
+ i++;
580
+ arms.push(parseSeq());
558
581
  }
559
- case "Alternative": {
560
- const exprs = node2.expressions;
561
- return !exprs || exprs.length === 0 || exprs.every((e) => canBeEmpty(e));
582
+ return arms.length === 1 ? arms[0] : { t: "alt", arms };
583
+ };
584
+ const parseSeq = () => {
585
+ const parts = [];
586
+ while (i < src.length) {
587
+ const ch = src[i];
588
+ if (ch === "|" || ch === ")") break;
589
+ parts.push(parseQuantified());
590
+ }
591
+ if (parts.length === 0) return EMPTY2;
592
+ return parts.length === 1 ? parts[0] : { t: "seq", parts };
593
+ };
594
+ const parseQuantified = () => {
595
+ const atom = parseAtom();
596
+ const ch = src[i];
597
+ let min = null;
598
+ if (ch === "*") {
599
+ min = 0;
600
+ i++;
601
+ } else if (ch === "+") {
602
+ min = 1;
603
+ i++;
604
+ } else if (ch === "?") {
605
+ min = 0;
606
+ i++;
607
+ } else if (ch === "{") {
608
+ min = parseBraceQuantifier();
562
609
  }
563
- case "Group":
564
- return canBeEmpty(node2.expression);
565
- case "Disjunction":
566
- return canBeEmpty(node2.left) || canBeEmpty(node2.right);
567
- default:
568
- return false;
569
- }
570
- }
571
- function extractFirstSet(node2) {
572
- if (!node2) return { firstSet: empty(), canMatchNewline: false };
573
- switch (node2.type) {
574
- case "Disjunction": {
575
- const left = extractFirstSet(node2.left);
576
- const right = extractFirstSet(node2.right);
577
- return {
578
- firstSet: union(left.firstSet, right.firstSet),
579
- canMatchNewline: left.canMatchNewline || right.canMatchNewline
580
- };
610
+ if (min === null) return atom;
611
+ if (src[i] === "?") i++;
612
+ return { t: "rep", node: atom, min };
613
+ };
614
+ const parseBraceQuantifier = () => {
615
+ const close = src.indexOf("}", i);
616
+ if (close === -1) return null;
617
+ const m = /^(\d+)(?:,(\d*))?$/.exec(src.slice(i + 1, close));
618
+ if (!m) return null;
619
+ i = close + 1;
620
+ return Number.parseInt(m[1], 10);
621
+ };
622
+ const parseAtom = () => {
623
+ const ch = src[i];
624
+ switch (ch) {
625
+ case "(":
626
+ return parseGroup();
627
+ case "[":
628
+ return parseClass();
629
+ case ".":
630
+ i++;
631
+ return charNode(any(), false);
632
+ // `.` matches any char except line terminators
633
+ case "\\":
634
+ return parseEscape();
635
+ case "^":
636
+ case "$":
637
+ i++;
638
+ return EMPTY2;
639
+ // anchors are zero-width
640
+ case "{":
641
+ i++;
642
+ return charNode(fromRange(123, 123), false);
643
+ // a `{` with nothing to quantify is a literal brace
644
+ default:
645
+ i += ch.length;
646
+ return charNode(fromRange(ch.codePointAt(0), ch.codePointAt(0)), ch === "\n");
581
647
  }
582
- case "Alternative": {
583
- const exprs = node2.expressions;
584
- if (!exprs || exprs.length === 0) return { firstSet: empty(), canMatchNewline: false };
585
- let fs2 = empty();
586
- let canNL = false;
587
- for (const expr of exprs) {
588
- const r = extractFirstSet(expr);
589
- fs2 = union(fs2, r.firstSet);
590
- canNL = canNL || r.canMatchNewline;
591
- if (!canBeEmpty(expr)) break;
592
- }
593
- return { firstSet: fs2, canMatchNewline: canNL };
648
+ };
649
+ const parseGroup = () => {
650
+ i++;
651
+ let lookaround = false;
652
+ if (src[i] === "?") {
653
+ const c1 = src[i + 1];
654
+ if (c1 === ":") i += 2;
655
+ else if (c1 === "=" || c1 === "!") {
656
+ i += 2;
657
+ lookaround = true;
658
+ } else if (c1 === "<") {
659
+ const c2 = src[i + 2];
660
+ if (c2 === "=" || c2 === "!") {
661
+ i += 3;
662
+ lookaround = true;
663
+ } else {
664
+ const gt = src.indexOf(">", i);
665
+ if (gt === -1) throw BAIL;
666
+ i = gt + 1;
667
+ }
668
+ } else throw BAIL;
594
669
  }
595
- case "Char": {
596
- const kind = node2.kind;
597
- if (kind === "simple") {
598
- const code = node2.codePoint ?? node2.value.codePointAt(0) ?? 0;
599
- return { firstSet: fromRange(code, code), canMatchNewline: code === 10 };
600
- }
601
- if (kind === "meta") {
602
- const val = node2.value;
603
- if (val === ".") return { firstSet: any(), canMatchNewline: false };
604
- if (val === "d") return { firstSet: fromRange(48, 57), canMatchNewline: false };
605
- if (val === "D") return { firstSet: any(), canMatchNewline: true };
606
- if (val === "w") return {
607
- firstSet: union(
608
- union(fromRange(48, 57), fromRange(65, 90)),
609
- union(fromRange(97, 122), fromRange(95, 95))
610
- ),
611
- canMatchNewline: false
612
- };
613
- if (val === "s") return { firstSet: fromRange(9, 13), canMatchNewline: true };
614
- if (val === "n") return { firstSet: fromRange(10, 10), canMatchNewline: true };
670
+ const inner = parseAlt();
671
+ if (src[i] !== ")") throw BAIL;
672
+ i++;
673
+ return lookaround ? EMPTY2 : inner;
674
+ };
675
+ const parseClass = () => {
676
+ let k = i + 1;
677
+ const negated = src[k] === "^";
678
+ if (negated) k++;
679
+ let body = "";
680
+ while (k < src.length && src[k] !== "]") {
681
+ if (src[k] === "\\") {
682
+ body += src[k] + (src[k + 1] ?? "");
683
+ k += 2;
684
+ } else {
685
+ body += src[k];
686
+ k++;
615
687
  }
616
- return { firstSet: any(), canMatchNewline: true };
617
688
  }
618
- case "CharacterClass": {
619
- const expressions = node2.expressions;
689
+ if (src[k] !== "]") throw BAIL;
690
+ i = k + 1;
691
+ if (negated) return charNode(any(), true);
692
+ const ranges = parseClassRanges(body);
693
+ if (!ranges) return charNode(any(), true);
694
+ return charNode(rangesToSet(ranges), rangesHaveNewline(ranges));
695
+ };
696
+ const parseEscape = () => {
697
+ const e = src[i + 1];
698
+ if (e === void 0) throw BAIL;
699
+ if (e === "u" && /^[0-9a-fA-F]{4}$/.test(src.slice(i + 2, i + 6))) {
700
+ const cp = Number.parseInt(src.slice(i + 2, i + 6), 16);
701
+ i += 6;
702
+ return charNode(fromRange(cp, cp), cp === 10);
703
+ }
704
+ if (e === "x" && /^[0-9a-fA-F]{2}$/.test(src.slice(i + 2, i + 4))) {
705
+ const cp = Number.parseInt(src.slice(i + 2, i + 4), 16);
706
+ i += 4;
707
+ return charNode(fromRange(cp, cp), cp === 10);
708
+ }
709
+ i += 2;
710
+ switch (e) {
711
+ case "d":
712
+ return charNode(rangesToSet(shorthandRanges("d")), false);
713
+ case "w":
714
+ return charNode(rangesToSet(shorthandRanges("w")), false);
715
+ case "s":
716
+ return charNode(rangesToSet(shorthandRanges("s")), true);
717
+ case "D":
718
+ case "W":
719
+ case "S":
720
+ return charNode(any(), true);
721
+ // negated shorthand → widen
722
+ case "b":
723
+ case "B":
724
+ return EMPTY2;
725
+ // word boundary: zero-width
726
+ case "n":
727
+ return charNode(fromRange(10, 10), true);
728
+ case "r":
729
+ return charNode(fromRange(13, 13), false);
730
+ case "t":
731
+ return charNode(fromRange(9, 9), false);
732
+ case "f":
733
+ return charNode(fromRange(12, 12), false);
734
+ case "v":
735
+ return charNode(fromRange(11, 11), false);
736
+ case "0":
737
+ return charNode(fromRange(0, 0), false);
738
+ default:
739
+ if (e >= "1" && e <= "9") return ANY;
740
+ return charNode(fromRange(e.codePointAt(0), e.codePointAt(0)), e === "\n");
741
+ }
742
+ };
743
+ const node2 = parseAlt();
744
+ if (i < src.length) throw BAIL;
745
+ return node2;
746
+ }
747
+ function nullable(n) {
748
+ switch (n.t) {
749
+ case "char":
750
+ return false;
751
+ case "any":
752
+ return true;
753
+ case "empty":
754
+ return true;
755
+ case "seq":
756
+ return n.parts.every(nullable);
757
+ case "alt":
758
+ return n.arms.some(nullable);
759
+ case "rep":
760
+ return n.min === 0 || nullable(n.node);
761
+ }
762
+ }
763
+ function firstSet(n) {
764
+ switch (n.t) {
765
+ case "char":
766
+ return n.set;
767
+ case "any":
768
+ return any();
769
+ case "empty":
770
+ return empty();
771
+ case "rep":
772
+ return firstSet(n.node);
773
+ case "alt": {
774
+ let fs2 = empty();
775
+ for (const arm of n.arms) fs2 = union(fs2, firstSet(arm));
776
+ return fs2;
777
+ }
778
+ case "seq": {
620
779
  let fs2 = empty();
621
- let canNL = false;
622
- for (const expr of expressions) {
623
- if (expr.type === "ClassRange") {
624
- const from = expr.from.codePoint;
625
- const to = expr.to.codePoint;
626
- fs2 = union(fs2, fromRange(from, to));
627
- if (from <= 10 && 10 <= to) canNL = true;
628
- } else if (expr.type === "Char") {
629
- const r = extractFirstSet(expr);
630
- fs2 = union(fs2, r.firstSet);
631
- canNL = canNL || r.canMatchNewline;
632
- }
780
+ for (const part of n.parts) {
781
+ fs2 = union(fs2, firstSet(part));
782
+ if (!nullable(part)) break;
633
783
  }
634
- if (node2.negative) return { firstSet: any(), canMatchNewline: true };
635
- return { firstSet: fs2, canMatchNewline: canNL };
636
- }
637
- case "Repetition": {
638
- return extractFirstSet(node2.expression);
784
+ return fs2;
639
785
  }
640
- case "Group": {
641
- return extractFirstSet(node2.expression);
786
+ }
787
+ }
788
+ function firstCanBeNewline(n) {
789
+ switch (n.t) {
790
+ case "char":
791
+ return n.nl;
792
+ case "any":
793
+ return true;
794
+ case "empty":
795
+ return false;
796
+ case "rep":
797
+ return firstCanBeNewline(n.node);
798
+ case "alt":
799
+ return n.arms.some(firstCanBeNewline);
800
+ case "seq": {
801
+ for (const part of n.parts) {
802
+ if (firstCanBeNewline(part)) return true;
803
+ if (!nullable(part)) break;
804
+ }
805
+ return false;
642
806
  }
643
- default:
644
- return { firstSet: any(), canMatchNewline: true };
645
807
  }
646
808
  }
809
+ function firstSetFromRegex(source) {
810
+ let ast;
811
+ try {
812
+ ast = parseRegex(source);
813
+ } catch {
814
+ return { firstSet: any(), canMatchNewline: true };
815
+ }
816
+ if (nullable(ast)) return { firstSet: any(), canMatchNewline: true };
817
+ return { firstSet: firstSet(ast), canMatchNewline: firstCanBeNewline(ast) };
818
+ }
647
819
 
648
820
  // src/combinators/keywords.ts
649
821
  function word(str, boundary = "_0-9A-Za-z") {
@@ -658,19 +830,19 @@ function keywords(words, opts = {}) {
658
830
  const boundary = opts.boundary ? `(?![${opts.boundary}])` : "";
659
831
  const flags = opts.caseInsensitive ? "iuy" : "uy";
660
832
  const re = new RegExp(`(?:${alt})${boundary}`, flags);
661
- let firstSet = empty();
833
+ let firstSet2 = empty();
662
834
  for (const w of sorted) {
663
835
  if (w.length === 0) continue;
664
836
  const cp = w.codePointAt(0);
665
- firstSet = union(firstSet, fromChar(cp));
837
+ firstSet2 = union(firstSet2, fromChar(cp));
666
838
  if (opts.caseInsensitive) {
667
839
  const u = String.fromCodePoint(cp).toUpperCase().codePointAt(0);
668
840
  const l = String.fromCodePoint(cp).toLowerCase().codePointAt(0);
669
- if (u !== void 0) firstSet = union(firstSet, fromChar(u));
670
- if (l !== void 0) firstSet = union(firstSet, fromChar(l));
841
+ if (u !== void 0) firstSet2 = union(firstSet2, fromChar(u));
842
+ if (l !== void 0) firstSet2 = union(firstSet2, fromChar(l));
671
843
  }
672
844
  }
673
- const meta = { firstSet, canMatchNewline: false, isTrivia: false };
845
+ const meta = { firstSet: firstSet2, canMatchNewline: false, isTrivia: false };
674
846
  return {
675
847
  _tag: "keywords",
676
848
  _meta: meta,
@@ -1047,13 +1219,25 @@ function tryFastLabeledScan(input, cur, trivia2) {
1047
1219
  }
1048
1220
  function recordTriviaChunks(ctx, chunks) {
1049
1221
  const kinds = ctx.triviaKindLabels;
1222
+ const mask = ctx._triviaCaptureMask;
1050
1223
  for (const ch of chunks) {
1051
1224
  pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1052
1225
  if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
1053
- pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1226
+ if (mask === void 0 || kinds === void 0 || (mask & 1 << ch.kindIndex) !== 0) {
1227
+ pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1228
+ }
1054
1229
  }
1055
1230
  }
1056
1231
  }
1232
+ function triviaKindMask(labels, keep) {
1233
+ if (!labels) return void 0;
1234
+ let mask = 0;
1235
+ for (const name of keep) {
1236
+ const idx = labels.indexOf(name);
1237
+ if (idx >= 0) mask |= 1 << idx;
1238
+ }
1239
+ return mask;
1240
+ }
1057
1241
 
1058
1242
  // src/combinators/trivia-skip.ts
1059
1243
  var NOOP_COMMIT = () => {
@@ -1705,10 +1889,15 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
1705
1889
  const saved = beginCstNodeCapture(ctx);
1706
1890
  const savedFields = ctx._fields;
1707
1891
  ctx._fields = capturesFields ? [] : void 0;
1892
+ const savedMask = ctx._triviaCaptureMask;
1893
+ if (build === void 0 && ctx.build?._parsemanTriviaKinds !== void 0 && def.type !== void 0) {
1894
+ ctx._triviaCaptureMask = ctx.build._parsemanTriviaKinds(def.type);
1895
+ }
1708
1896
  if (!capturesTrivia) ctx.captureTrivia = false;
1709
1897
  const r = combinator.parse(input, pos, ctx);
1710
1898
  const fields = capturesFields ? buildFieldMap(ctx._fields) : void 0;
1711
1899
  ctx._fields = savedFields;
1900
+ ctx._triviaCaptureMask = savedMask;
1712
1901
  const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
1713
1902
  if (!r.ok) return r;
1714
1903
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
@@ -1846,7 +2035,13 @@ function parser(opts, root) {
1846
2035
  trivia: opts.trivia,
1847
2036
  ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
1848
2037
  } : {},
1849
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {}
2038
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
2039
+ // Kind-filter for per-node capture. Resolve against this scope's trivia
2040
+ // labels — this parser's own trivia if it declares one, else the INHERITED
2041
+ // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
2042
+ // trivia is inherited rather than re-declared here. No labels → undefined
2043
+ // (capture all).
2044
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
1850
2045
  };
1851
2046
  const result = root.parse(input, pos ?? 0, ctx);
1852
2047
  if (trackLines) {
@@ -1974,77 +2169,11 @@ function expect(combinator, label2) {
1974
2169
 
1975
2170
  // src/compiler/scannable-run.ts
1976
2171
  var BACKSLASH = 92;
1977
- var CLASS_ESCAPES2 = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1978
2172
  var META = new Set("()[]{}*+?|^$.".split(""));
1979
- var SPACE_RANGES2 = [
1980
- [9, 13],
1981
- [32, 32],
1982
- [160, 160],
1983
- [5760, 5760],
1984
- [8192, 8202],
1985
- [8232, 8232],
1986
- [8233, 8233],
1987
- [8239, 8239],
1988
- [8287, 8287],
1989
- [12288, 12288],
1990
- [65279, 65279]
1991
- ];
1992
- function shorthandRanges2(ch) {
1993
- if (ch === "d") return [[48, 57]];
1994
- if (ch === "s") return SPACE_RANGES2;
1995
- return [[48, 57], [65, 90], [97, 122], [95, 95]];
1996
- }
1997
- function readUnicodeEscape(body, i) {
1998
- if (body[i] !== "\\" || body[i + 1] !== "u") return null;
1999
- const hex = body.slice(i + 2, i + 6);
2000
- if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
2001
- return { cp: Number.parseInt(hex, 16), next: i + 6 };
2002
- }
2003
- function parseClassRanges(body) {
2004
- const ranges = [];
2005
- let i = 0;
2006
- const readAtom = () => {
2007
- const ch = body[i];
2008
- if (ch === void 0) return null;
2009
- if (ch === "\\") {
2010
- const uni = readUnicodeEscape(body, i);
2011
- if (uni) {
2012
- i = uni.next;
2013
- return { cp: uni.cp };
2014
- }
2015
- const e = body[i + 1];
2016
- if (e === void 0) return null;
2017
- i += 2;
2018
- if (e in CLASS_ESCAPES2) return { cp: CLASS_ESCAPES2[e] };
2019
- if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges2(e) };
2020
- if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
2021
- return { cp: e.codePointAt(0) };
2022
- }
2023
- i += ch.length;
2024
- return { cp: ch.codePointAt(0) };
2025
- };
2026
- while (i < body.length) {
2027
- const lo = readAtom();
2028
- if (lo === null) return null;
2029
- if ("set" in lo) {
2030
- ranges.push(...lo.set);
2031
- continue;
2032
- }
2033
- if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
2034
- i += 1;
2035
- const hi = readAtom();
2036
- if (hi === null || "set" in hi) return null;
2037
- ranges.push([lo.cp, hi.cp]);
2038
- } else {
2039
- ranges.push([lo.cp, lo.cp]);
2040
- }
2041
- }
2042
- return ranges.length ? ranges : null;
2043
- }
2044
2173
  function classToRanges(cls) {
2045
- if (cls === "\\d") return shorthandRanges2("d");
2046
- if (cls === "\\w") return shorthandRanges2("w");
2047
- if (cls === "\\s") return shorthandRanges2("s");
2174
+ if (cls === "\\d") return shorthandRanges("d");
2175
+ if (cls === "\\w") return shorthandRanges("w");
2176
+ if (cls === "\\s") return shorthandRanges("s");
2048
2177
  const body = cls.slice(1, -1);
2049
2178
  if (body.startsWith("^")) return null;
2050
2179
  return parseClassRanges(body);
@@ -2062,7 +2191,7 @@ function literalCodePoints(frag) {
2062
2191
  if (ch === "\\") {
2063
2192
  const e = frag[i + 1];
2064
2193
  if (e === void 0) return null;
2065
- out.push(e in CLASS_ESCAPES2 ? CLASS_ESCAPES2[e] : e.codePointAt(0));
2194
+ out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
2066
2195
  i += 2;
2067
2196
  continue;
2068
2197
  }
@@ -2155,7 +2284,7 @@ function parseSeqParts(source) {
2155
2284
  if (e === "d" || e === "w" || e === "s") {
2156
2285
  flush();
2157
2286
  i += 2;
2158
- i += runFrom(shorthandRanges2(e), false, source[i]);
2287
+ i += runFrom(shorthandRanges(e), false, source[i]);
2159
2288
  continue;
2160
2289
  }
2161
2290
  const uni = readUnicodeEscape(source, i);
@@ -2164,8 +2293,8 @@ function parseSeqParts(source) {
2164
2293
  cp2 = uni.cp;
2165
2294
  i = uni.next;
2166
2295
  } else if (e === void 0) return null;
2167
- else if (e in CLASS_ESCAPES2) {
2168
- cp2 = CLASS_ESCAPES2[e];
2296
+ else if (e in CLASS_ESCAPES) {
2297
+ cp2 = CLASS_ESCAPES[e];
2169
2298
  i += 2;
2170
2299
  } else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
2171
2300
  else {
@@ -2272,7 +2401,7 @@ function rangesSubset(sub, sup) {
2272
2401
  }
2273
2402
  function parseClassOperand(body) {
2274
2403
  if (body === "\\d" || body === "\\w" || body === "\\s") {
2275
- return { ranges: shorthandRanges2(body[1]), negated: false };
2404
+ return { ranges: shorthandRanges(body[1]), negated: false };
2276
2405
  }
2277
2406
  if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
2278
2407
  let inner = body.slice(1, -1);
@@ -2705,7 +2834,7 @@ function parseScanShapeCore(source) {
2705
2834
  }
2706
2835
  m = /^\\([dws])([+*])$/.exec(source);
2707
2836
  if (m) {
2708
- return { kind: "chars", ranges: shorthandRanges2(m[1]), minOne: m[2] === "+" };
2837
+ return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
2709
2838
  }
2710
2839
  m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
2711
2840
  if (m) {
@@ -3062,7 +3191,7 @@ function scanBranchLabeled(shape, kindIndex, mint) {
3062
3191
  ` if (${m.end} > _e) {`,
3063
3192
  ` if (_cap) {`,
3064
3193
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
3065
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
3194
+ ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << kindIndex}))) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
3066
3195
  ` }`,
3067
3196
  ` _e = ${m.end}`,
3068
3197
  ` continue`,
@@ -3178,7 +3307,7 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
3178
3307
  ` const _ce = _e + _m${i}[0].length`,
3179
3308
  ` if (_cap) {`,
3180
3309
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
3181
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3310
+ ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3182
3311
  ` }`,
3183
3312
  ` _e = _ce`,
3184
3313
  ` _matched = true`,
@@ -3216,7 +3345,7 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
3216
3345
  ` const _ce = _r${i}.span.end`,
3217
3346
  ` if (_cap) {`,
3218
3347
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
3219
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3348
+ ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3220
3349
  ` }`,
3221
3350
  ` _e = _ce`,
3222
3351
  ` _matched = true`,
@@ -4614,6 +4743,7 @@ function emitNode(def, ctx, pos) {
4614
4743
  const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
4615
4744
  if (!capturesTrivia) ctx.needsEmptyTl = true;
4616
4745
  const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
4746
+ const smk = structural ? v(ctx, "_smk") : null;
4617
4747
  if (structural) ctx.needsHostReads = true;
4618
4748
  const hostTriviaGate = `_ctx.build !== undefined && (_ctx.build._parsemanCaptureTrivia !== undefined ? _ctx.build._parsemanCaptureTrivia(${JSON.stringify(def.type)}) : (_ctx._pmCapTL ??= _hostReads(_ctx.build, 5)))`;
4619
4749
  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)` : ""}
@@ -4625,15 +4755,15 @@ ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capt
4625
4755
  const fObj = hasFields ? v(ctx, "_fields") : "undefined";
4626
4756
  const stmts = [
4627
4757
  allocStmt,
4628
- `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog${sf ? `, ${sf} = _ctx._fields` : ""}`,
4629
- `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}${sf ? `; _ctx._fields = ${fieldsOn} ? [] : undefined` : ""}`
4758
+ `${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` : ""}`,
4759
+ `${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` : ""}`
4630
4760
  ];
4631
4761
  const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
4632
4762
  stmts.push(...innerStmts);
4633
4763
  if (sf && fArr) {
4634
4764
  stmts.push(`${i}const ${fArr} = _ctx._fields`);
4635
4765
  }
4636
- stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${sf ? `; _ctx._fields = ${sf}` : ""}`);
4766
+ 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}` : ""}`);
4637
4767
  stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
4638
4768
  let stV = "undefined";
4639
4769
  if (structural) {