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.js CHANGED
@@ -327,9 +327,9 @@ function asciiFoldEq(a, b) {
327
327
  }
328
328
  function literal(value, opts = {}) {
329
329
  const caseInsensitive = opts.caseInsensitive ?? false;
330
- const firstSet = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
330
+ const firstSet2 = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
331
331
  const meta = {
332
- firstSet,
332
+ firstSet: firstSet2,
333
333
  canMatchNewline: value.includes("\n"),
334
334
  isTrivia: false
335
335
  };
@@ -341,7 +341,7 @@ function literal(value, opts = {}) {
341
341
  meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
342
342
  { lo: firstLower, hi: firstLower },
343
343
  { lo: firstUpper, hi: firstUpper }
344
- ] } : firstSet;
344
+ ] } : firstSet2;
345
345
  }
346
346
  return {
347
347
  _tag: "literal",
@@ -364,68 +364,66 @@ function literal(value, opts = {}) {
364
364
  };
365
365
  }
366
366
 
367
- // src/combinators/regex.ts
368
- var firstSetAnalyzer = null;
369
- function registerRegexAnalyzer(analyzer) {
370
- firstSetAnalyzer = analyzer;
371
- }
372
- var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
373
- var SCAN_BAIL_AT = 64;
367
+ // src/regex/classes.ts
368
+ var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
374
369
  var SPACE_RANGES = [
375
370
  [9, 13],
376
371
  [32, 32],
377
372
  [160, 160],
378
373
  [5760, 5760],
379
374
  [8192, 8202],
380
- [8232, 8233],
375
+ [8232, 8232],
376
+ [8233, 8233],
381
377
  [8239, 8239],
382
378
  [8287, 8287],
383
379
  [12288, 12288],
384
380
  [65279, 65279]
385
381
  ];
386
- var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
387
382
  function shorthandRanges(ch) {
388
383
  if (ch === "d") return [[48, 57]];
389
384
  if (ch === "s") return SPACE_RANGES;
390
385
  return [[48, 57], [65, 90], [97, 122], [95, 95]];
391
386
  }
392
- function inRanges(cp, ranges) {
393
- for (let i = 0; i < ranges.length; i++) {
394
- const [lo, hi] = ranges[i];
395
- if (cp >= lo && cp <= hi) return true;
396
- }
397
- return false;
387
+ function readUnicodeEscape(body, i) {
388
+ if (body[i] !== "\\" || body[i + 1] !== "u") return null;
389
+ const hex = body.slice(i + 2, i + 6);
390
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
391
+ return { cp: Number.parseInt(hex, 16), next: i + 6 };
398
392
  }
399
- function readClassRanges(body) {
400
- if (body.startsWith("^")) return null;
393
+ function parseClassRanges(body) {
401
394
  const ranges = [];
402
395
  let i = 0;
403
396
  const readAtom = () => {
404
397
  const ch = body[i];
405
398
  if (ch === void 0) return null;
406
399
  if (ch === "\\") {
407
- const esc = body[i + 1];
408
- if (esc === void 0) return null;
400
+ const uni = readUnicodeEscape(body, i);
401
+ if (uni) {
402
+ i = uni.next;
403
+ return { cp: uni.cp };
404
+ }
405
+ const e = body[i + 1];
406
+ if (e === void 0) return null;
409
407
  i += 2;
410
- if (esc === "d" || esc === "w" || esc === "s") return { ranges: shorthandRanges(esc) };
411
- if (esc in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[esc] };
412
- if (esc >= "a" && esc <= "z" || esc >= "A" && esc <= "Z") return null;
413
- return { cp: esc.charCodeAt(0) };
408
+ if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
409
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
410
+ if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
411
+ return { cp: e.codePointAt(0) };
414
412
  }
415
- i++;
416
- return { cp: ch.charCodeAt(0) };
413
+ i += ch.length;
414
+ return { cp: ch.codePointAt(0) };
417
415
  };
418
416
  while (i < body.length) {
419
417
  const lo = readAtom();
420
- if (!lo) return null;
421
- if ("ranges" in lo) {
422
- ranges.push(...lo.ranges);
418
+ if (lo === null) return null;
419
+ if ("set" in lo) {
420
+ ranges.push(...lo.set);
423
421
  continue;
424
422
  }
425
423
  if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
426
- i++;
424
+ i += 1;
427
425
  const hi = readAtom();
428
- if (!hi || "ranges" in hi) return null;
426
+ if (hi === null || "set" in hi) return null;
429
427
  ranges.push([lo.cp, hi.cp]);
430
428
  } else {
431
429
  ranges.push([lo.cp, lo.cp]);
@@ -433,6 +431,25 @@ function readClassRanges(body) {
433
431
  }
434
432
  return ranges.length ? ranges : null;
435
433
  }
434
+
435
+ // src/combinators/regex.ts
436
+ var firstSetAnalyzer = null;
437
+ function registerRegexAnalyzer(analyzer) {
438
+ firstSetAnalyzer = analyzer;
439
+ }
440
+ var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
441
+ var SCAN_BAIL_AT = 64;
442
+ function inRanges(cp, ranges) {
443
+ for (let i = 0; i < ranges.length; i++) {
444
+ const [lo, hi] = ranges[i];
445
+ if (cp >= lo && cp <= hi) return true;
446
+ }
447
+ return false;
448
+ }
449
+ function readClassRanges(body) {
450
+ if (body.startsWith("^")) return null;
451
+ return parseClassRanges(body);
452
+ }
436
453
  function shortScanner(source, flags) {
437
454
  if (/[imsuvy]/.test(flags)) return null;
438
455
  let ranges = null;
@@ -466,8 +483,8 @@ function regex(pattern, flags = "") {
466
483
  const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
467
484
  const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
468
485
  const scan = shortScanner(source, resolvedFlags);
469
- const { firstSet, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
470
- const meta = { firstSet, canMatchNewline, isTrivia: false };
486
+ const { firstSet: firstSet2, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
487
+ const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
471
488
  return {
472
489
  _tag: "regex",
473
490
  _meta: meta,
@@ -495,111 +512,266 @@ function regex(pattern, flags = "") {
495
512
  };
496
513
  }
497
514
 
498
- // src/combinators/regex-analyze.ts
499
- import regexpTree from "regexp-tree";
500
- function firstSetFromRegex(pattern) {
501
- try {
502
- const ast = regexpTree.parse(`/${pattern}/`);
503
- return extractFirstSet(ast.body);
504
- } catch {
505
- return { firstSet: any(), canMatchNewline: true };
506
- }
515
+ // src/regex/first-set.ts
516
+ var ANY = { t: "any" };
517
+ var EMPTY2 = { t: "empty" };
518
+ function charNode(set, nl) {
519
+ return { t: "char", set, nl };
520
+ }
521
+ function rangesToSet(ranges) {
522
+ let fs = empty();
523
+ for (const [lo, hi] of ranges) fs = union(fs, fromRange(lo, hi));
524
+ return fs;
525
+ }
526
+ function rangesHaveNewline(ranges) {
527
+ return ranges.some(([lo, hi]) => lo <= 10 && 10 <= hi);
507
528
  }
508
- function canBeEmpty(node2) {
509
- if (!node2) return true;
510
- switch (node2.type) {
511
- case "Repetition": {
512
- const q = node2.quantifier;
513
- return q.kind === "*" || q.kind === "?" || q.kind === "Range" && (q.from ?? 1) === 0;
529
+ var BAIL = /* @__PURE__ */ Symbol("regex-bail");
530
+ function parseRegex(src) {
531
+ let i = 0;
532
+ const parseAlt = () => {
533
+ const arms = [parseSeq()];
534
+ while (src[i] === "|") {
535
+ i++;
536
+ arms.push(parseSeq());
514
537
  }
515
- case "Alternative": {
516
- const exprs = node2.expressions;
517
- return !exprs || exprs.length === 0 || exprs.every((e) => canBeEmpty(e));
538
+ return arms.length === 1 ? arms[0] : { t: "alt", arms };
539
+ };
540
+ const parseSeq = () => {
541
+ const parts = [];
542
+ while (i < src.length) {
543
+ const ch = src[i];
544
+ if (ch === "|" || ch === ")") break;
545
+ parts.push(parseQuantified());
546
+ }
547
+ if (parts.length === 0) return EMPTY2;
548
+ return parts.length === 1 ? parts[0] : { t: "seq", parts };
549
+ };
550
+ const parseQuantified = () => {
551
+ const atom = parseAtom();
552
+ const ch = src[i];
553
+ let min = null;
554
+ if (ch === "*") {
555
+ min = 0;
556
+ i++;
557
+ } else if (ch === "+") {
558
+ min = 1;
559
+ i++;
560
+ } else if (ch === "?") {
561
+ min = 0;
562
+ i++;
563
+ } else if (ch === "{") {
564
+ min = parseBraceQuantifier();
518
565
  }
519
- case "Group":
520
- return canBeEmpty(node2.expression);
521
- case "Disjunction":
522
- return canBeEmpty(node2.left) || canBeEmpty(node2.right);
523
- default:
524
- return false;
525
- }
526
- }
527
- function extractFirstSet(node2) {
528
- if (!node2) return { firstSet: empty(), canMatchNewline: false };
529
- switch (node2.type) {
530
- case "Disjunction": {
531
- const left = extractFirstSet(node2.left);
532
- const right = extractFirstSet(node2.right);
533
- return {
534
- firstSet: union(left.firstSet, right.firstSet),
535
- canMatchNewline: left.canMatchNewline || right.canMatchNewline
536
- };
566
+ if (min === null) return atom;
567
+ if (src[i] === "?") i++;
568
+ return { t: "rep", node: atom, min };
569
+ };
570
+ const parseBraceQuantifier = () => {
571
+ const close = src.indexOf("}", i);
572
+ if (close === -1) return null;
573
+ const m = /^(\d+)(?:,(\d*))?$/.exec(src.slice(i + 1, close));
574
+ if (!m) return null;
575
+ i = close + 1;
576
+ return Number.parseInt(m[1], 10);
577
+ };
578
+ const parseAtom = () => {
579
+ const ch = src[i];
580
+ switch (ch) {
581
+ case "(":
582
+ return parseGroup();
583
+ case "[":
584
+ return parseClass();
585
+ case ".":
586
+ i++;
587
+ return charNode(any(), false);
588
+ // `.` matches any char except line terminators
589
+ case "\\":
590
+ return parseEscape();
591
+ case "^":
592
+ case "$":
593
+ i++;
594
+ return EMPTY2;
595
+ // anchors are zero-width
596
+ case "{":
597
+ i++;
598
+ return charNode(fromRange(123, 123), false);
599
+ // a `{` with nothing to quantify is a literal brace
600
+ default:
601
+ i += ch.length;
602
+ return charNode(fromRange(ch.codePointAt(0), ch.codePointAt(0)), ch === "\n");
537
603
  }
538
- case "Alternative": {
539
- const exprs = node2.expressions;
540
- if (!exprs || exprs.length === 0) return { firstSet: empty(), canMatchNewline: false };
541
- let fs = empty();
542
- let canNL = false;
543
- for (const expr of exprs) {
544
- const r = extractFirstSet(expr);
545
- fs = union(fs, r.firstSet);
546
- canNL = canNL || r.canMatchNewline;
547
- if (!canBeEmpty(expr)) break;
548
- }
549
- return { firstSet: fs, canMatchNewline: canNL };
604
+ };
605
+ const parseGroup = () => {
606
+ i++;
607
+ let lookaround = false;
608
+ if (src[i] === "?") {
609
+ const c1 = src[i + 1];
610
+ if (c1 === ":") i += 2;
611
+ else if (c1 === "=" || c1 === "!") {
612
+ i += 2;
613
+ lookaround = true;
614
+ } else if (c1 === "<") {
615
+ const c2 = src[i + 2];
616
+ if (c2 === "=" || c2 === "!") {
617
+ i += 3;
618
+ lookaround = true;
619
+ } else {
620
+ const gt = src.indexOf(">", i);
621
+ if (gt === -1) throw BAIL;
622
+ i = gt + 1;
623
+ }
624
+ } else throw BAIL;
550
625
  }
551
- case "Char": {
552
- const kind = node2.kind;
553
- if (kind === "simple") {
554
- const code = node2.codePoint ?? node2.value.codePointAt(0) ?? 0;
555
- return { firstSet: fromRange(code, code), canMatchNewline: code === 10 };
556
- }
557
- if (kind === "meta") {
558
- const val = node2.value;
559
- if (val === ".") return { firstSet: any(), canMatchNewline: false };
560
- if (val === "d") return { firstSet: fromRange(48, 57), canMatchNewline: false };
561
- if (val === "D") return { firstSet: any(), canMatchNewline: true };
562
- if (val === "w") return {
563
- firstSet: union(
564
- union(fromRange(48, 57), fromRange(65, 90)),
565
- union(fromRange(97, 122), fromRange(95, 95))
566
- ),
567
- canMatchNewline: false
568
- };
569
- if (val === "s") return { firstSet: fromRange(9, 13), canMatchNewline: true };
570
- if (val === "n") return { firstSet: fromRange(10, 10), canMatchNewline: true };
626
+ const inner = parseAlt();
627
+ if (src[i] !== ")") throw BAIL;
628
+ i++;
629
+ return lookaround ? EMPTY2 : inner;
630
+ };
631
+ const parseClass = () => {
632
+ let k = i + 1;
633
+ const negated = src[k] === "^";
634
+ if (negated) k++;
635
+ let body = "";
636
+ while (k < src.length && src[k] !== "]") {
637
+ if (src[k] === "\\") {
638
+ body += src[k] + (src[k + 1] ?? "");
639
+ k += 2;
640
+ } else {
641
+ body += src[k];
642
+ k++;
571
643
  }
572
- return { firstSet: any(), canMatchNewline: true };
573
644
  }
574
- case "CharacterClass": {
575
- const expressions = node2.expressions;
645
+ if (src[k] !== "]") throw BAIL;
646
+ i = k + 1;
647
+ if (negated) return charNode(any(), true);
648
+ const ranges = parseClassRanges(body);
649
+ if (!ranges) return charNode(any(), true);
650
+ return charNode(rangesToSet(ranges), rangesHaveNewline(ranges));
651
+ };
652
+ const parseEscape = () => {
653
+ const e = src[i + 1];
654
+ if (e === void 0) throw BAIL;
655
+ if (e === "u" && /^[0-9a-fA-F]{4}$/.test(src.slice(i + 2, i + 6))) {
656
+ const cp = Number.parseInt(src.slice(i + 2, i + 6), 16);
657
+ i += 6;
658
+ return charNode(fromRange(cp, cp), cp === 10);
659
+ }
660
+ if (e === "x" && /^[0-9a-fA-F]{2}$/.test(src.slice(i + 2, i + 4))) {
661
+ const cp = Number.parseInt(src.slice(i + 2, i + 4), 16);
662
+ i += 4;
663
+ return charNode(fromRange(cp, cp), cp === 10);
664
+ }
665
+ i += 2;
666
+ switch (e) {
667
+ case "d":
668
+ return charNode(rangesToSet(shorthandRanges("d")), false);
669
+ case "w":
670
+ return charNode(rangesToSet(shorthandRanges("w")), false);
671
+ case "s":
672
+ return charNode(rangesToSet(shorthandRanges("s")), true);
673
+ case "D":
674
+ case "W":
675
+ case "S":
676
+ return charNode(any(), true);
677
+ // negated shorthand → widen
678
+ case "b":
679
+ case "B":
680
+ return EMPTY2;
681
+ // word boundary: zero-width
682
+ case "n":
683
+ return charNode(fromRange(10, 10), true);
684
+ case "r":
685
+ return charNode(fromRange(13, 13), false);
686
+ case "t":
687
+ return charNode(fromRange(9, 9), false);
688
+ case "f":
689
+ return charNode(fromRange(12, 12), false);
690
+ case "v":
691
+ return charNode(fromRange(11, 11), false);
692
+ case "0":
693
+ return charNode(fromRange(0, 0), false);
694
+ default:
695
+ if (e >= "1" && e <= "9") return ANY;
696
+ return charNode(fromRange(e.codePointAt(0), e.codePointAt(0)), e === "\n");
697
+ }
698
+ };
699
+ const node2 = parseAlt();
700
+ if (i < src.length) throw BAIL;
701
+ return node2;
702
+ }
703
+ function nullable(n) {
704
+ switch (n.t) {
705
+ case "char":
706
+ return false;
707
+ case "any":
708
+ return true;
709
+ case "empty":
710
+ return true;
711
+ case "seq":
712
+ return n.parts.every(nullable);
713
+ case "alt":
714
+ return n.arms.some(nullable);
715
+ case "rep":
716
+ return n.min === 0 || nullable(n.node);
717
+ }
718
+ }
719
+ function firstSet(n) {
720
+ switch (n.t) {
721
+ case "char":
722
+ return n.set;
723
+ case "any":
724
+ return any();
725
+ case "empty":
726
+ return empty();
727
+ case "rep":
728
+ return firstSet(n.node);
729
+ case "alt": {
730
+ let fs = empty();
731
+ for (const arm of n.arms) fs = union(fs, firstSet(arm));
732
+ return fs;
733
+ }
734
+ case "seq": {
576
735
  let fs = empty();
577
- let canNL = false;
578
- for (const expr of expressions) {
579
- if (expr.type === "ClassRange") {
580
- const from = expr.from.codePoint;
581
- const to = expr.to.codePoint;
582
- fs = union(fs, fromRange(from, to));
583
- if (from <= 10 && 10 <= to) canNL = true;
584
- } else if (expr.type === "Char") {
585
- const r = extractFirstSet(expr);
586
- fs = union(fs, r.firstSet);
587
- canNL = canNL || r.canMatchNewline;
588
- }
736
+ for (const part of n.parts) {
737
+ fs = union(fs, firstSet(part));
738
+ if (!nullable(part)) break;
589
739
  }
590
- if (node2.negative) return { firstSet: any(), canMatchNewline: true };
591
- return { firstSet: fs, canMatchNewline: canNL };
592
- }
593
- case "Repetition": {
594
- return extractFirstSet(node2.expression);
740
+ return fs;
595
741
  }
596
- case "Group": {
597
- return extractFirstSet(node2.expression);
742
+ }
743
+ }
744
+ function firstCanBeNewline(n) {
745
+ switch (n.t) {
746
+ case "char":
747
+ return n.nl;
748
+ case "any":
749
+ return true;
750
+ case "empty":
751
+ return false;
752
+ case "rep":
753
+ return firstCanBeNewline(n.node);
754
+ case "alt":
755
+ return n.arms.some(firstCanBeNewline);
756
+ case "seq": {
757
+ for (const part of n.parts) {
758
+ if (firstCanBeNewline(part)) return true;
759
+ if (!nullable(part)) break;
760
+ }
761
+ return false;
598
762
  }
599
- default:
600
- return { firstSet: any(), canMatchNewline: true };
601
763
  }
602
764
  }
765
+ function firstSetFromRegex(source) {
766
+ let ast;
767
+ try {
768
+ ast = parseRegex(source);
769
+ } catch {
770
+ return { firstSet: any(), canMatchNewline: true };
771
+ }
772
+ if (nullable(ast)) return { firstSet: any(), canMatchNewline: true };
773
+ return { firstSet: firstSet(ast), canMatchNewline: firstCanBeNewline(ast) };
774
+ }
603
775
 
604
776
  // src/combinators/keywords.ts
605
777
  function word(str, boundary = "_0-9A-Za-z") {
@@ -617,19 +789,19 @@ function keywords(words, opts = {}) {
617
789
  const boundary = opts.boundary ? `(?![${opts.boundary}])` : "";
618
790
  const flags = opts.caseInsensitive ? "iuy" : "uy";
619
791
  const re = new RegExp(`(?:${alt})${boundary}`, flags);
620
- let firstSet = empty();
792
+ let firstSet2 = empty();
621
793
  for (const w of sorted) {
622
794
  if (w.length === 0) continue;
623
795
  const cp = w.codePointAt(0);
624
- firstSet = union(firstSet, fromChar(cp));
796
+ firstSet2 = union(firstSet2, fromChar(cp));
625
797
  if (opts.caseInsensitive) {
626
798
  const u = String.fromCodePoint(cp).toUpperCase().codePointAt(0);
627
799
  const l = String.fromCodePoint(cp).toLowerCase().codePointAt(0);
628
- if (u !== void 0) firstSet = union(firstSet, fromChar(u));
629
- if (l !== void 0) firstSet = union(firstSet, fromChar(l));
800
+ if (u !== void 0) firstSet2 = union(firstSet2, fromChar(u));
801
+ if (l !== void 0) firstSet2 = union(firstSet2, fromChar(l));
630
802
  }
631
803
  }
632
- const meta = { firstSet, canMatchNewline: false, isTrivia: false };
804
+ const meta = { firstSet: firstSet2, canMatchNewline: false, isTrivia: false };
633
805
  return {
634
806
  _tag: "keywords",
635
807
  _meta: meta,
@@ -1006,13 +1178,25 @@ function tryFastLabeledScan(input, cur, trivia2) {
1006
1178
  }
1007
1179
  function recordTriviaChunks(ctx, chunks) {
1008
1180
  const kinds = ctx.triviaKindLabels;
1181
+ const mask = ctx._triviaCaptureMask;
1009
1182
  for (const ch of chunks) {
1010
1183
  pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1011
1184
  if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
1012
- pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1185
+ if (mask === void 0 || kinds === void 0 || (mask & 1 << ch.kindIndex) !== 0) {
1186
+ pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1187
+ }
1013
1188
  }
1014
1189
  }
1015
1190
  }
1191
+ function triviaKindMask(labels, keep) {
1192
+ if (!labels) return void 0;
1193
+ let mask = 0;
1194
+ for (const name of keep) {
1195
+ const idx = labels.indexOf(name);
1196
+ if (idx >= 0) mask |= 1 << idx;
1197
+ }
1198
+ return mask;
1199
+ }
1016
1200
 
1017
1201
  // src/combinators/trivia-skip.ts
1018
1202
  var NOOP_COMMIT = () => {
@@ -1701,10 +1885,15 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
1701
1885
  const saved = beginCstNodeCapture(ctx);
1702
1886
  const savedFields = ctx._fields;
1703
1887
  ctx._fields = capturesFields ? [] : void 0;
1888
+ const savedMask = ctx._triviaCaptureMask;
1889
+ if (build === void 0 && ctx.build?._parsemanTriviaKinds !== void 0 && def.type !== void 0) {
1890
+ ctx._triviaCaptureMask = ctx.build._parsemanTriviaKinds(def.type);
1891
+ }
1704
1892
  if (!capturesTrivia) ctx.captureTrivia = false;
1705
1893
  const r = combinator.parse(input, pos, ctx);
1706
1894
  const fields = capturesFields ? buildFieldMap(ctx._fields) : void 0;
1707
1895
  ctx._fields = savedFields;
1896
+ ctx._triviaCaptureMask = savedMask;
1708
1897
  const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
1709
1898
  if (!r.ok) return r;
1710
1899
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
@@ -1842,7 +2031,13 @@ function parser(opts, root) {
1842
2031
  trivia: opts.trivia,
1843
2032
  ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
1844
2033
  } : {},
1845
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {}
2034
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
2035
+ // Kind-filter for per-node capture. Resolve against this scope's trivia
2036
+ // labels — this parser's own trivia if it declares one, else the INHERITED
2037
+ // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
2038
+ // trivia is inherited rather than re-declared here. No labels → undefined
2039
+ // (capture all).
2040
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
1846
2041
  };
1847
2042
  const result = root.parse(input, pos ?? 0, ctx);
1848
2043
  if (trackLines) {
@@ -1993,77 +2188,11 @@ function expect(combinator, label2) {
1993
2188
 
1994
2189
  // src/compiler/scannable-run.ts
1995
2190
  var BACKSLASH = 92;
1996
- var CLASS_ESCAPES2 = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1997
2191
  var META = new Set("()[]{}*+?|^$.".split(""));
1998
- var SPACE_RANGES2 = [
1999
- [9, 13],
2000
- [32, 32],
2001
- [160, 160],
2002
- [5760, 5760],
2003
- [8192, 8202],
2004
- [8232, 8232],
2005
- [8233, 8233],
2006
- [8239, 8239],
2007
- [8287, 8287],
2008
- [12288, 12288],
2009
- [65279, 65279]
2010
- ];
2011
- function shorthandRanges2(ch) {
2012
- if (ch === "d") return [[48, 57]];
2013
- if (ch === "s") return SPACE_RANGES2;
2014
- return [[48, 57], [65, 90], [97, 122], [95, 95]];
2015
- }
2016
- function readUnicodeEscape(body, i) {
2017
- if (body[i] !== "\\" || body[i + 1] !== "u") return null;
2018
- const hex = body.slice(i + 2, i + 6);
2019
- if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
2020
- return { cp: Number.parseInt(hex, 16), next: i + 6 };
2021
- }
2022
- function parseClassRanges(body) {
2023
- const ranges = [];
2024
- let i = 0;
2025
- const readAtom = () => {
2026
- const ch = body[i];
2027
- if (ch === void 0) return null;
2028
- if (ch === "\\") {
2029
- const uni = readUnicodeEscape(body, i);
2030
- if (uni) {
2031
- i = uni.next;
2032
- return { cp: uni.cp };
2033
- }
2034
- const e = body[i + 1];
2035
- if (e === void 0) return null;
2036
- i += 2;
2037
- if (e in CLASS_ESCAPES2) return { cp: CLASS_ESCAPES2[e] };
2038
- if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges2(e) };
2039
- if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
2040
- return { cp: e.codePointAt(0) };
2041
- }
2042
- i += ch.length;
2043
- return { cp: ch.codePointAt(0) };
2044
- };
2045
- while (i < body.length) {
2046
- const lo = readAtom();
2047
- if (lo === null) return null;
2048
- if ("set" in lo) {
2049
- ranges.push(...lo.set);
2050
- continue;
2051
- }
2052
- if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
2053
- i += 1;
2054
- const hi = readAtom();
2055
- if (hi === null || "set" in hi) return null;
2056
- ranges.push([lo.cp, hi.cp]);
2057
- } else {
2058
- ranges.push([lo.cp, lo.cp]);
2059
- }
2060
- }
2061
- return ranges.length ? ranges : null;
2062
- }
2063
2192
  function classToRanges(cls) {
2064
- if (cls === "\\d") return shorthandRanges2("d");
2065
- if (cls === "\\w") return shorthandRanges2("w");
2066
- if (cls === "\\s") return shorthandRanges2("s");
2193
+ if (cls === "\\d") return shorthandRanges("d");
2194
+ if (cls === "\\w") return shorthandRanges("w");
2195
+ if (cls === "\\s") return shorthandRanges("s");
2067
2196
  const body = cls.slice(1, -1);
2068
2197
  if (body.startsWith("^")) return null;
2069
2198
  return parseClassRanges(body);
@@ -2081,7 +2210,7 @@ function literalCodePoints(frag) {
2081
2210
  if (ch === "\\") {
2082
2211
  const e = frag[i + 1];
2083
2212
  if (e === void 0) return null;
2084
- out.push(e in CLASS_ESCAPES2 ? CLASS_ESCAPES2[e] : e.codePointAt(0));
2213
+ out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
2085
2214
  i += 2;
2086
2215
  continue;
2087
2216
  }
@@ -2174,7 +2303,7 @@ function parseSeqParts(source) {
2174
2303
  if (e === "d" || e === "w" || e === "s") {
2175
2304
  flush();
2176
2305
  i += 2;
2177
- i += runFrom(shorthandRanges2(e), false, source[i]);
2306
+ i += runFrom(shorthandRanges(e), false, source[i]);
2178
2307
  continue;
2179
2308
  }
2180
2309
  const uni = readUnicodeEscape(source, i);
@@ -2183,8 +2312,8 @@ function parseSeqParts(source) {
2183
2312
  cp2 = uni.cp;
2184
2313
  i = uni.next;
2185
2314
  } else if (e === void 0) return null;
2186
- else if (e in CLASS_ESCAPES2) {
2187
- cp2 = CLASS_ESCAPES2[e];
2315
+ else if (e in CLASS_ESCAPES) {
2316
+ cp2 = CLASS_ESCAPES[e];
2188
2317
  i += 2;
2189
2318
  } else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
2190
2319
  else {
@@ -2291,7 +2420,7 @@ function rangesSubset(sub, sup) {
2291
2420
  }
2292
2421
  function parseClassOperand(body) {
2293
2422
  if (body === "\\d" || body === "\\w" || body === "\\s") {
2294
- return { ranges: shorthandRanges2(body[1]), negated: false };
2423
+ return { ranges: shorthandRanges(body[1]), negated: false };
2295
2424
  }
2296
2425
  if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
2297
2426
  let inner = body.slice(1, -1);
@@ -2724,7 +2853,7 @@ function parseScanShapeCore(source) {
2724
2853
  }
2725
2854
  m = /^\\([dws])([+*])$/.exec(source);
2726
2855
  if (m) {
2727
- return { kind: "chars", ranges: shorthandRanges2(m[1]), minOne: m[2] === "+" };
2856
+ return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
2728
2857
  }
2729
2858
  m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
2730
2859
  if (m) {
@@ -3081,7 +3210,7 @@ function scanBranchLabeled(shape, kindIndex, mint) {
3081
3210
  ` if (${m.end} > _e) {`,
3082
3211
  ` if (_cap) {`,
3083
3212
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
3084
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
3213
+ ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << kindIndex}))) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
3085
3214
  ` }`,
3086
3215
  ` _e = ${m.end}`,
3087
3216
  ` continue`,
@@ -3197,7 +3326,7 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
3197
3326
  ` const _ce = _e + _m${i}[0].length`,
3198
3327
  ` if (_cap) {`,
3199
3328
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
3200
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3329
+ ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3201
3330
  ` }`,
3202
3331
  ` _e = _ce`,
3203
3332
  ` _matched = true`,
@@ -3235,7 +3364,7 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
3235
3364
  ` const _ce = _r${i}.span.end`,
3236
3365
  ` if (_cap) {`,
3237
3366
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
3238
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3367
+ ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3239
3368
  ` }`,
3240
3369
  ` _e = _ce`,
3241
3370
  ` _matched = true`,
@@ -4625,6 +4754,7 @@ function emitNode(def, ctx, pos) {
4625
4754
  const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
4626
4755
  if (!capturesTrivia) ctx.needsEmptyTl = true;
4627
4756
  const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
4757
+ const smk = structural ? v(ctx, "_smk") : null;
4628
4758
  if (structural) ctx.needsHostReads = true;
4629
4759
  const hostTriviaGate = `_ctx.build !== undefined && (_ctx.build._parsemanCaptureTrivia !== undefined ? _ctx.build._parsemanCaptureTrivia(${JSON.stringify(def.type)}) : (_ctx._pmCapTL ??= _hostReads(_ctx.build, 5)))`;
4630
4760
  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)` : ""}
@@ -4636,15 +4766,15 @@ ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capt
4636
4766
  const fObj = hasFields ? v(ctx, "_fields") : "undefined";
4637
4767
  const stmts = [
4638
4768
  allocStmt,
4639
- `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog${sf ? `, ${sf} = _ctx._fields` : ""}`,
4640
- `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}${sf ? `; _ctx._fields = ${fieldsOn} ? [] : undefined` : ""}`
4769
+ `${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` : ""}`,
4770
+ `${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` : ""}`
4641
4771
  ];
4642
4772
  const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
4643
4773
  stmts.push(...innerStmts);
4644
4774
  if (sf && fArr) {
4645
4775
  stmts.push(`${i}const ${fArr} = _ctx._fields`);
4646
4776
  }
4647
- stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${sf ? `; _ctx._fields = ${sf}` : ""}`);
4777
+ 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}` : ""}`);
4648
4778
  stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
4649
4779
  let stV = "undefined";
4650
4780
  if (structural) {
@@ -6078,7 +6208,8 @@ function run(entry, input, options = {}) {
6078
6208
  _triviaLog: triviaLog,
6079
6209
  _errors: errors,
6080
6210
  build: options.build,
6081
- state: options.state
6211
+ state: options.state,
6212
+ ...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {}
6082
6213
  };
6083
6214
  const r = invoke(entry, input, 0, ctx);
6084
6215
  let unconsumedFrom = null;
@@ -6555,6 +6686,7 @@ export {
6555
6686
  transform,
6556
6687
  trivia,
6557
6688
  triviaEntries,
6689
+ triviaKindMask,
6558
6690
  walk,
6559
6691
  withCtx,
6560
6692
  word