parseman 0.51.0 → 0.51.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/analysis/diagnostics.js +48 -15
  2. package/dist/analysis/diagnostics.js.map +1 -1
  3. package/dist/cli/index.js +50 -17
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/combinators/token.d.ts +8 -8
  6. package/dist/combinators/token.d.ts.map +1 -1
  7. package/dist/coverage.d.ts.map +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +59 -21
  11. package/dist/index.js.map +1 -1
  12. package/dist/plugin/evaluator.d.ts.map +1 -1
  13. package/dist/plugin/index.js +59 -22
  14. package/dist/plugin/index.js.map +1 -1
  15. package/dist/spec/index.js +27 -5
  16. package/dist/spec/index.js.map +1 -1
  17. package/dist/table/assemble.d.ts.map +1 -1
  18. package/dist/table/emit-assembly.d.ts.map +1 -1
  19. package/dist/table/encode-baseline.d.ts.map +1 -1
  20. package/dist/table/exec-baseline.d.ts.map +1 -1
  21. package/dist/table/exec.d.ts.map +1 -1
  22. package/dist/table/index.js +373 -362
  23. package/dist/table/index.js.map +2 -2
  24. package/dist/table/ops.d.ts +2 -0
  25. package/dist/table/ops.d.ts.map +1 -1
  26. package/dist/types.d.ts +1 -0
  27. package/dist/types.d.ts.map +1 -1
  28. package/dist/version.d.ts +1 -1
  29. package/package.json +1 -1
  30. package/src/analysis/duplication.ts +1 -1
  31. package/src/analysis/rebuild.ts +2 -2
  32. package/src/combinators/token.ts +46 -3
  33. package/src/compiler/ir-serialize.ts +11 -7
  34. package/src/coverage.ts +2 -3
  35. package/src/index.ts +1 -1
  36. package/src/plugin/evaluator.ts +7 -4
  37. package/src/table/assemble.ts +8 -3
  38. package/src/table/child-slots.ts +2 -2
  39. package/src/table/emit-assembly.ts +5 -3
  40. package/src/table/encode-baseline.ts +3 -2
  41. package/src/table/encode.ts +3 -3
  42. package/src/table/exec-baseline.ts +30 -4
  43. package/src/table/exec.ts +8 -3
  44. package/src/table/ops.ts +3 -1
  45. package/src/table/scalar-terminal.ts +5 -5
  46. package/src/table/site-labels.ts +2 -2
  47. package/src/types.ts +1 -1
  48. package/src/version.ts +1 -1
@@ -3434,6 +3434,271 @@ function ref() {
3434
3434
  return slot;
3435
3435
  }
3436
3436
 
3437
+ // src/compiler/degradation.ts
3438
+ function formatDegradation(d) {
3439
+ return `[parseman] degraded [${d.code}] ${d.where}: ${d.subject} \u2014 ${d.fellBackTo}; otherwise ${d.otherwise}`;
3440
+ }
3441
+ var DETAIL_CAP = 8;
3442
+ function formatDegradations(list) {
3443
+ const byCode = /* @__PURE__ */ new Map();
3444
+ for (const d of list) {
3445
+ const bucket = byCode.get(d.code);
3446
+ if (bucket) bucket.push(d);
3447
+ else byCode.set(d.code, [d]);
3448
+ }
3449
+ const out = [];
3450
+ for (const [code, items] of byCode) {
3451
+ for (const d of items.slice(0, DETAIL_CAP)) out.push(formatDegradation(d));
3452
+ if (items.length > DETAIL_CAP) {
3453
+ out.push(
3454
+ `[parseman] degraded [${code}] +${items.length - DETAIL_CAP} more site(s) not listed (${items.length} total). Set PARSEMAN_DEGRADATION=error to fail the build on these.`
3455
+ );
3456
+ }
3457
+ }
3458
+ return out;
3459
+ }
3460
+ var _sinks = [];
3461
+ function beginDegradationCapture() {
3462
+ _sinks.push(/* @__PURE__ */ new Map());
3463
+ }
3464
+ function degradationCaptureDepth() {
3465
+ return _sinks.length;
3466
+ }
3467
+ function unwindDegradationCapture(depth) {
3468
+ const found = [];
3469
+ while (_sinks.length > depth) {
3470
+ const sink = _sinks.pop();
3471
+ found.unshift(...sink.values());
3472
+ }
3473
+ return found;
3474
+ }
3475
+ function resolveDegradationLevel(explicit) {
3476
+ if (explicit !== void 0) return explicit;
3477
+ const env = typeof process !== "undefined" ? process.env?.PARSEMAN_DEGRADATION : void 0;
3478
+ if (env === "off" || env === "warn" || env === "error") return env;
3479
+ return "warn";
3480
+ }
3481
+ function degradationKey(d) {
3482
+ return JSON.stringify([d.code, d.where, d.subject]);
3483
+ }
3484
+ function recordDegradation(d) {
3485
+ const level = resolveDegradationLevel();
3486
+ if (level === "off") return;
3487
+ const key = degradationKey(d);
3488
+ const sink = _sinks[_sinks.length - 1];
3489
+ if (sink) {
3490
+ if (!sink.has(key)) sink.set(key, d);
3491
+ return;
3492
+ }
3493
+ if (_immediate.has(key)) return;
3494
+ _immediate.add(key);
3495
+ if (level === "error") throw new Error(`parseman: degraded compilation path
3496
+ ${formatDegradation(d)}`);
3497
+ console.warn(formatDegradation(d));
3498
+ }
3499
+ var _immediate = /* @__PURE__ */ new Set();
3500
+ function beginCompileDegradationDrain() {
3501
+ if (resolveDegradationLevel() === "off" || _sinks.length > 0) return () => {
3502
+ };
3503
+ const depth = degradationCaptureDepth();
3504
+ beginDegradationCapture();
3505
+ return (report) => {
3506
+ const found = unwindDegradationCapture(depth);
3507
+ if (!report || found.length === 0) return;
3508
+ const fresh = found.filter((d) => {
3509
+ const k = degradationKey(d);
3510
+ if (_immediate.has(k)) return false;
3511
+ _immediate.add(k);
3512
+ return true;
3513
+ });
3514
+ if (fresh.length === 0) return;
3515
+ const lines = formatDegradations(fresh);
3516
+ if (resolveDegradationLevel() === "error") {
3517
+ throw new Error(`parseman: ${fresh.length} degraded compilation path(s)
3518
+ ${lines.join("\n")}`);
3519
+ }
3520
+ for (const l of lines) console.warn(l);
3521
+ };
3522
+ }
3523
+
3524
+ // src/compiler/build-arity.ts
3525
+ function cstOutputHost(build) {
3526
+ return build?._parsemanCstOutput === true;
3527
+ }
3528
+ var SINGLE_IDENT_ARROW_RE = /^[A-Za-z_$][\w$]*\s*=>/;
3529
+ var PARAM_LIST_OPEN_RE = /^(?:function\b[^(]*)?\(/;
3530
+ var NATIVE_CODE_RE = /\{\s*\[native code\]\s*\}/;
3531
+ var PARAM_HEAD_RE = /^[A-Za-z_$][\w$]*\s*\??\s*/;
3532
+ function confirmedBuildArity(src) {
3533
+ const s = src.trim();
3534
+ if (NATIVE_CODE_RE.test(s)) return null;
3535
+ if (/\barguments\b/.test(s)) return null;
3536
+ if (SINGLE_IDENT_ARROW_RE.test(s)) return 1;
3537
+ const inner = paramListText(s);
3538
+ if (inner === null) return null;
3539
+ if (inner.trim() === "") return 0;
3540
+ const parts = splitTopLevel(inner);
3541
+ if (parts === null) return null;
3542
+ for (const part of parts) {
3543
+ if (!isConfirmableParam(part.trim())) return null;
3544
+ }
3545
+ return parts.length;
3546
+ }
3547
+ function paramListText(s) {
3548
+ const open = PARAM_LIST_OPEN_RE.exec(s);
3549
+ if (!open) return null;
3550
+ const start = open[0].length;
3551
+ const end = matchingParen(s, start);
3552
+ if (end === null) return null;
3553
+ if (!s.startsWith("function") && !/^\s*=>/.test(s.slice(end + 1))) return null;
3554
+ return s.slice(start, end);
3555
+ }
3556
+ function matchingParen(s, start) {
3557
+ let depth = 0;
3558
+ for (let i = start; i < s.length; i++) {
3559
+ const c = s[i];
3560
+ if (c === '"' || c === "'" || c === "`") {
3561
+ const close = skipString(s, i);
3562
+ if (close === null) return null;
3563
+ i = close;
3564
+ continue;
3565
+ }
3566
+ if (c === "(" || c === "[" || c === "{") depth++;
3567
+ else if (c === "]" || c === "}") {
3568
+ if (--depth < 0) return null;
3569
+ } else if (c === ")") {
3570
+ if (depth === 0) return i;
3571
+ depth--;
3572
+ }
3573
+ }
3574
+ return null;
3575
+ }
3576
+ function skipString(s, open) {
3577
+ const quote = s[open];
3578
+ for (let i = open + 1; i < s.length; i++) {
3579
+ if (s[i] === "\\") {
3580
+ i++;
3581
+ continue;
3582
+ }
3583
+ if (s[i] === quote) return i;
3584
+ }
3585
+ return null;
3586
+ }
3587
+ function splitTopLevel(inner) {
3588
+ const parts = [];
3589
+ let depth = 0;
3590
+ let angle = 0;
3591
+ let last = 0;
3592
+ for (let i = 0; i < inner.length; i++) {
3593
+ const c = inner[i];
3594
+ if (c === "=" && inner[i + 1] === ">") {
3595
+ i++;
3596
+ continue;
3597
+ }
3598
+ if (c === '"' || c === "'" || c === "`") {
3599
+ const close = skipString(inner, i);
3600
+ if (close === null) return null;
3601
+ i = close;
3602
+ continue;
3603
+ }
3604
+ if (c === "(" || c === "[" || c === "{") depth++;
3605
+ else if (c === ")" || c === "]" || c === "}") {
3606
+ if (--depth < 0) return null;
3607
+ } else if (c === "<") angle++;
3608
+ else if (c === ">") {
3609
+ if (--angle < 0) return null;
3610
+ } else if (c === "," && depth === 0 && angle === 0) {
3611
+ parts.push(inner.slice(last, i));
3612
+ last = i + 1;
3613
+ }
3614
+ }
3615
+ if (depth !== 0 || angle !== 0) return null;
3616
+ parts.push(inner.slice(last));
3617
+ return parts;
3618
+ }
3619
+ function isConfirmableParam(part) {
3620
+ const head = PARAM_HEAD_RE.exec(part);
3621
+ if (!head) return false;
3622
+ const rest = part.slice(head[0].length);
3623
+ if (rest === "") return true;
3624
+ if (rest[0] !== ":") return false;
3625
+ return !hasDefaultAssign(rest.slice(1));
3626
+ }
3627
+ function hasDefaultAssign(annotation) {
3628
+ for (let i = 0; i < annotation.length; i++) {
3629
+ if (annotation[i] !== "=") continue;
3630
+ if (annotation[i + 1] === ">" || annotation[i + 1] === "=") {
3631
+ i++;
3632
+ continue;
3633
+ }
3634
+ const prev = annotation[i - 1];
3635
+ if (prev === "<" || prev === ">" || prev === "!" || prev === "=") continue;
3636
+ return true;
3637
+ }
3638
+ return false;
3639
+ }
3640
+ function buildAnalysisSrc(def) {
3641
+ return def.buildSigSrc ?? def.buildSrc ?? def.build.toString();
3642
+ }
3643
+ var RESOLVER_REASONS = {
3644
+ "rest-parameter": "its parameter list uses a rest parameter, so the declared arity is unbounded",
3645
+ "arguments": "its body references `arguments`",
3646
+ "reassigned": "the binding it names is reassigned, so the function read here is not decidable",
3647
+ "not-a-function": "the binding it names is not a function declaration",
3648
+ "unresolved-import": "its module could not be resolved or parsed",
3649
+ "not-found": "the name did not resolve to any binding",
3650
+ "computed": "it is a computed expression",
3651
+ "foreign-source": "it is named in a module the resolver was never told about, so its offsets index no scope tree the resolver holds",
3652
+ "ambiguous-source": "two distinct registered modules hold byte-identical text, so that text no longer names the module its offsets index"
3653
+ };
3654
+ function unconfirmableReason(def, src) {
3655
+ const declared = def.buildArityUnresolved;
3656
+ if (declared !== void 0 && RESOLVER_REASONS[declared]) return RESOLVER_REASONS[declared];
3657
+ const s = src.trim();
3658
+ const params = s.slice(0, s.indexOf(")") + 1);
3659
+ if (params.includes("...")) return RESOLVER_REASONS["rest-parameter"];
3660
+ if (/\barguments\b/.test(s)) return RESOLVER_REASONS["arguments"];
3661
+ if (/^[A-Za-z_$][\w$]*(\s*\.\s*[A-Za-z_$][\w$]*)?$/.test(s)) {
3662
+ return "it names a binding this build could not resolve to a function declaration";
3663
+ }
3664
+ return "its parameter list could not be parsed";
3665
+ }
3666
+ function confirmedArityForDef(def) {
3667
+ if (def.buildArity !== void 0) return def.buildArity;
3668
+ const src = buildAnalysisSrc(def);
3669
+ const arity = confirmedBuildArity(src);
3670
+ if (arity !== null) return arity;
3671
+ const subject = `build reducer \`${(def.buildSrc ?? "<runtime fn>").trim().split("\n")[0].slice(0, 60)}\``;
3672
+ recordDegradation({
3673
+ code: "build-arity-unconfirmed",
3674
+ severity: "warn",
3675
+ where: `node("${def.type ?? "<inferred>"}")`,
3676
+ subject,
3677
+ fellBackTo: `could not confirm its declared arity \u2014 ${unconfirmableReason(def, src)} \u2014 so this node captures children, fields and raw children, logs trivia, and clones \`_ctx.state\` on every match`,
3678
+ otherwise: "only the tiers the reducer actually declares would be captured (arity >= 1 children, >= 2 fields, >= 4 raw, >= 5 trivia, >= 6 state). Declare it with `node(..., { buildArity: n })` if it cannot be derived"
3679
+ });
3680
+ return null;
3681
+ }
3682
+ function buildReadsRaw(def) {
3683
+ if (!def.build) return true;
3684
+ if (def.buildRawUnused === true) return false;
3685
+ const arity = confirmedArityForDef(def);
3686
+ if (arity === null) return true;
3687
+ return arity >= 4;
3688
+ }
3689
+ function buildReadsTrivia(def) {
3690
+ if (!def.build) return true;
3691
+ const arity = confirmedArityForDef(def);
3692
+ if (arity === null) return true;
3693
+ return arity >= 5;
3694
+ }
3695
+ function buildReadsState(def) {
3696
+ if (!def.build) return true;
3697
+ const arity = confirmedArityForDef(def);
3698
+ if (arity === null) return true;
3699
+ return arity >= 6;
3700
+ }
3701
+
3437
3702
  // src/combinators/token.ts
3438
3703
  function token(root) {
3439
3704
  const meta = {
@@ -3632,369 +3897,104 @@ function scanTo(sentinel, { skip, raw = false, orEOF = false, recoverAt, stopAt
3632
3897
  const recover = recoverAt.parse(input, cur, probeCtx);
3633
3898
  const errors = ctx._errors;
3634
3899
  if (errors !== void 0 && errorCount >= 0 && errors.length > errorCount) {
3635
- errors.length = errorCount;
3636
- }
3637
- if (recover.ok) {
3638
- if (recover.span.end <= cur) {
3639
- throw new TypeError("scanTo recoverAt must consume input");
3640
- }
3641
- if (recovery < 0) {
3642
- recovery = cur;
3643
- recoveryErrorCount = errorCount;
3644
- }
3645
- if (firstSentinel >= 0 && (sentinelCount & 1) === 1) {
3646
- return succeed(recoveredEnd());
3647
- }
3648
- cur = recover.span.end;
3649
- continue;
3650
- }
3651
- }
3652
- cur++;
3653
- }
3654
- if (recovery >= 0) return succeed(recoveredEnd());
3655
- if (orEOF) {
3656
- return succeed(cur);
3657
- }
3658
- const sentDef = sentinel._def;
3659
- const expected = sentDef.tag === "literal" ? [JSON.stringify(sentDef.value)] : ["sentinel"];
3660
- return { ok: false, expected, span: { start: pos, end: cur } };
3661
- }
3662
- };
3663
- }
3664
- function markSpec(outer, open, close, ownSkip, strict, raw) {
3665
- ;
3666
- outer._balancedSpec = { open, close, ownSkip, strict, raw };
3667
- return outer;
3668
- }
3669
- function balanced(open, close, options = {}) {
3670
- const ownSkip = options.skip ?? [];
3671
- const strict = options.strict ?? false;
3672
- const combi = buildBalancedInterior(open, close, ownSkip, strict);
3673
- if (options.raw) return markSpec(token(combi), open, close, ownSkip, strict, true);
3674
- const eagerParse = combi.parse.bind(combi);
3675
- const cache = /* @__PURE__ */ new Map();
3676
- combi._balancedAmbient = { open, close, ownSkip, strict };
3677
- combi.parse = (input, pos, ctx) => {
3678
- const amb = ctx.scanSkip;
3679
- if (!amb || amb.length === 0) return eagerParse(input, pos, ctx);
3680
- let interior = cache.get(amb);
3681
- if (!interior) {
3682
- interior = buildBalancedInterior(open, close, [...amb, ...ownSkip], strict);
3683
- cache.set(amb, interior);
3684
- }
3685
- return interior.parse(input, pos, ctx);
3686
- };
3687
- return markSpec(token(combi), open, close, ownSkip, strict, false);
3688
- }
3689
- function buildBalancedInterior(open, close, skips, strict = false) {
3690
- const self = ref();
3691
- const stop = /* @__PURE__ */ new Set([open, close]);
3692
- let bounded = true;
3693
- for (const sk of skips) {
3694
- const cs = firstSetClassChars(sk);
3695
- if (cs === null) {
3696
- bounded = false;
3697
- break;
3698
- }
3699
- for (const ch2 of cs) stop.add(ch2);
3700
- }
3701
- const cls = [...stop].map(escapeClassChar).join("");
3702
- const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
3703
- const inner = many(choice(self, ...skips, content));
3704
- const closer = strict ? literal(close) : expect(literal(close));
3705
- const combi = transform(
3706
- sequence(literal(open), inner, closer),
3707
- // parts: strings (content/self) or arrays (a sequence-shaped skip) or a
3708
- // ParseError (recovered close). `c` is the close string or a ParseError.
3709
- ([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
3710
- );
3711
- if (combi._def.tag === "transform") {
3712
- combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
3713
- combi._def.recognitionOnly = true;
3714
- }
3715
- ;
3716
- combi._balanced = { open, close };
3717
- self.define(combi);
3718
- return combi;
3719
- }
3720
- function firstSetClassChars(c) {
3721
- const fs = c._meta.firstSet;
3722
- if (fs.kind === "empty") return [];
3723
- if (fs.kind !== "ranges") return null;
3724
- const out = [];
3725
- for (const { lo, hi } of fs.ranges) {
3726
- if (hi - lo > 8) return null;
3727
- for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
3728
- }
3729
- return out;
3730
- }
3731
- function escapeClassChar(ch2) {
3732
- return ch2.replace(/[\\\]^-]/g, "\\$&");
3733
- }
3734
-
3735
- // src/compiler/degradation.ts
3736
- function formatDegradation(d) {
3737
- return `[parseman] degraded [${d.code}] ${d.where}: ${d.subject} \u2014 ${d.fellBackTo}; otherwise ${d.otherwise}`;
3738
- }
3739
- var DETAIL_CAP = 8;
3740
- function formatDegradations(list) {
3741
- const byCode = /* @__PURE__ */ new Map();
3742
- for (const d of list) {
3743
- const bucket = byCode.get(d.code);
3744
- if (bucket) bucket.push(d);
3745
- else byCode.set(d.code, [d]);
3746
- }
3747
- const out = [];
3748
- for (const [code, items] of byCode) {
3749
- for (const d of items.slice(0, DETAIL_CAP)) out.push(formatDegradation(d));
3750
- if (items.length > DETAIL_CAP) {
3751
- out.push(
3752
- `[parseman] degraded [${code}] +${items.length - DETAIL_CAP} more site(s) not listed (${items.length} total). Set PARSEMAN_DEGRADATION=error to fail the build on these.`
3753
- );
3754
- }
3755
- }
3756
- return out;
3757
- }
3758
- var _sinks = [];
3759
- function beginDegradationCapture() {
3760
- _sinks.push(/* @__PURE__ */ new Map());
3761
- }
3762
- function degradationCaptureDepth() {
3763
- return _sinks.length;
3764
- }
3765
- function unwindDegradationCapture(depth) {
3766
- const found = [];
3767
- while (_sinks.length > depth) {
3768
- const sink = _sinks.pop();
3769
- found.unshift(...sink.values());
3770
- }
3771
- return found;
3772
- }
3773
- function resolveDegradationLevel(explicit) {
3774
- if (explicit !== void 0) return explicit;
3775
- const env = typeof process !== "undefined" ? process.env?.PARSEMAN_DEGRADATION : void 0;
3776
- if (env === "off" || env === "warn" || env === "error") return env;
3777
- return "warn";
3778
- }
3779
- function degradationKey(d) {
3780
- return JSON.stringify([d.code, d.where, d.subject]);
3781
- }
3782
- function recordDegradation(d) {
3783
- const level = resolveDegradationLevel();
3784
- if (level === "off") return;
3785
- const key = degradationKey(d);
3786
- const sink = _sinks[_sinks.length - 1];
3787
- if (sink) {
3788
- if (!sink.has(key)) sink.set(key, d);
3789
- return;
3790
- }
3791
- if (_immediate.has(key)) return;
3792
- _immediate.add(key);
3793
- if (level === "error") throw new Error(`parseman: degraded compilation path
3794
- ${formatDegradation(d)}`);
3795
- console.warn(formatDegradation(d));
3796
- }
3797
- var _immediate = /* @__PURE__ */ new Set();
3798
- function beginCompileDegradationDrain() {
3799
- if (resolveDegradationLevel() === "off" || _sinks.length > 0) return () => {
3800
- };
3801
- const depth = degradationCaptureDepth();
3802
- beginDegradationCapture();
3803
- return (report) => {
3804
- const found = unwindDegradationCapture(depth);
3805
- if (!report || found.length === 0) return;
3806
- const fresh = found.filter((d) => {
3807
- const k = degradationKey(d);
3808
- if (_immediate.has(k)) return false;
3809
- _immediate.add(k);
3810
- return true;
3811
- });
3812
- if (fresh.length === 0) return;
3813
- const lines = formatDegradations(fresh);
3814
- if (resolveDegradationLevel() === "error") {
3815
- throw new Error(`parseman: ${fresh.length} degraded compilation path(s)
3816
- ${lines.join("\n")}`);
3900
+ errors.length = errorCount;
3901
+ }
3902
+ if (recover.ok) {
3903
+ if (recover.span.end <= cur) {
3904
+ throw new TypeError("scanTo recoverAt must consume input");
3905
+ }
3906
+ if (recovery < 0) {
3907
+ recovery = cur;
3908
+ recoveryErrorCount = errorCount;
3909
+ }
3910
+ if (firstSentinel >= 0 && (sentinelCount & 1) === 1) {
3911
+ return succeed(recoveredEnd());
3912
+ }
3913
+ cur = recover.span.end;
3914
+ continue;
3915
+ }
3916
+ }
3917
+ cur++;
3918
+ }
3919
+ if (recovery >= 0) return succeed(recoveredEnd());
3920
+ if (orEOF) {
3921
+ return succeed(cur);
3922
+ }
3923
+ const sentDef = sentinel._def;
3924
+ const expected = sentDef.tag === "literal" ? [JSON.stringify(sentDef.value)] : ["sentinel"];
3925
+ return { ok: false, expected, span: { start: pos, end: cur } };
3817
3926
  }
3818
- for (const l of lines) console.warn(l);
3819
3927
  };
3820
3928
  }
3821
-
3822
- // src/compiler/build-arity.ts
3823
- function cstOutputHost(build) {
3824
- return build?._parsemanCstOutput === true;
3825
- }
3826
- var SINGLE_IDENT_ARROW_RE = /^[A-Za-z_$][\w$]*\s*=>/;
3827
- var PARAM_LIST_OPEN_RE = /^(?:function\b[^(]*)?\(/;
3828
- var NATIVE_CODE_RE = /\{\s*\[native code\]\s*\}/;
3829
- var PARAM_HEAD_RE = /^[A-Za-z_$][\w$]*\s*\??\s*/;
3830
- function confirmedBuildArity(src) {
3831
- const s = src.trim();
3832
- if (NATIVE_CODE_RE.test(s)) return null;
3833
- if (/\barguments\b/.test(s)) return null;
3834
- if (SINGLE_IDENT_ARROW_RE.test(s)) return 1;
3835
- const inner = paramListText(s);
3836
- if (inner === null) return null;
3837
- if (inner.trim() === "") return 0;
3838
- const parts = splitTopLevel(inner);
3839
- if (parts === null) return null;
3840
- for (const part of parts) {
3841
- if (!isConfirmableParam(part.trim())) return null;
3842
- }
3843
- return parts.length;
3844
- }
3845
- function paramListText(s) {
3846
- const open = PARAM_LIST_OPEN_RE.exec(s);
3847
- if (!open) return null;
3848
- const start = open[0].length;
3849
- const end = matchingParen(s, start);
3850
- if (end === null) return null;
3851
- if (!s.startsWith("function") && !/^\s*=>/.test(s.slice(end + 1))) return null;
3852
- return s.slice(start, end);
3853
- }
3854
- function matchingParen(s, start) {
3855
- let depth = 0;
3856
- for (let i = start; i < s.length; i++) {
3857
- const c = s[i];
3858
- if (c === '"' || c === "'" || c === "`") {
3859
- const close = skipString(s, i);
3860
- if (close === null) return null;
3861
- i = close;
3862
- continue;
3863
- }
3864
- if (c === "(" || c === "[" || c === "{") depth++;
3865
- else if (c === "]" || c === "}") {
3866
- if (--depth < 0) return null;
3867
- } else if (c === ")") {
3868
- if (depth === 0) return i;
3869
- depth--;
3870
- }
3871
- }
3872
- return null;
3929
+ function markSpec(outer, open, close, ownSkip, strict, raw) {
3930
+ ;
3931
+ outer._balancedSpec = { open, close, ownSkip, strict, raw };
3932
+ return outer;
3873
3933
  }
3874
- function skipString(s, open) {
3875
- const quote = s[open];
3876
- for (let i = open + 1; i < s.length; i++) {
3877
- if (s[i] === "\\") {
3878
- i++;
3879
- continue;
3934
+ function balanced(open, close, options = {}) {
3935
+ const ownSkip = options.skip ?? [];
3936
+ const strict = options.strict ?? false;
3937
+ const combi = buildBalancedInterior(open, close, ownSkip, strict);
3938
+ if (options.raw) return markSpec(token(combi), open, close, ownSkip, strict, true);
3939
+ const eagerParse = combi.parse.bind(combi);
3940
+ const cache = /* @__PURE__ */ new Map();
3941
+ combi._balancedAmbient = { open, close, ownSkip, strict };
3942
+ combi.parse = (input, pos, ctx) => {
3943
+ const amb = ctx.scanSkip;
3944
+ if (!amb || amb.length === 0) return eagerParse(input, pos, ctx);
3945
+ let interior = cache.get(amb);
3946
+ if (!interior) {
3947
+ interior = buildBalancedInterior(open, close, [...amb, ...ownSkip], strict);
3948
+ cache.set(amb, interior);
3880
3949
  }
3881
- if (s[i] === quote) return i;
3882
- }
3883
- return null;
3950
+ return interior.parse(input, pos, ctx);
3951
+ };
3952
+ return markSpec(token(combi), open, close, ownSkip, strict, false);
3884
3953
  }
3885
- function splitTopLevel(inner) {
3886
- const parts = [];
3887
- let depth = 0;
3888
- let angle = 0;
3889
- let last = 0;
3890
- for (let i = 0; i < inner.length; i++) {
3891
- const c = inner[i];
3892
- if (c === "=" && inner[i + 1] === ">") {
3893
- i++;
3894
- continue;
3895
- }
3896
- if (c === '"' || c === "'" || c === "`") {
3897
- const close = skipString(inner, i);
3898
- if (close === null) return null;
3899
- i = close;
3900
- continue;
3901
- }
3902
- if (c === "(" || c === "[" || c === "{") depth++;
3903
- else if (c === ")" || c === "]" || c === "}") {
3904
- if (--depth < 0) return null;
3905
- } else if (c === "<") angle++;
3906
- else if (c === ">") {
3907
- if (--angle < 0) return null;
3908
- } else if (c === "," && depth === 0 && angle === 0) {
3909
- parts.push(inner.slice(last, i));
3910
- last = i + 1;
3954
+ function buildBalancedInterior(open, close, skips, strict = false) {
3955
+ const self = ref();
3956
+ const stop = /* @__PURE__ */ new Set([open, close]);
3957
+ let bounded = true;
3958
+ for (const sk of skips) {
3959
+ const cs = firstSetClassChars(sk);
3960
+ if (cs === null) {
3961
+ bounded = false;
3962
+ break;
3911
3963
  }
3964
+ for (const ch2 of cs) stop.add(ch2);
3912
3965
  }
3913
- if (depth !== 0 || angle !== 0) return null;
3914
- parts.push(inner.slice(last));
3915
- return parts;
3916
- }
3917
- function isConfirmableParam(part) {
3918
- const head = PARAM_HEAD_RE.exec(part);
3919
- if (!head) return false;
3920
- const rest = part.slice(head[0].length);
3921
- if (rest === "") return true;
3922
- if (rest[0] !== ":") return false;
3923
- return !hasDefaultAssign(rest.slice(1));
3924
- }
3925
- function hasDefaultAssign(annotation) {
3926
- for (let i = 0; i < annotation.length; i++) {
3927
- if (annotation[i] !== "=") continue;
3928
- if (annotation[i + 1] === ">" || annotation[i + 1] === "=") {
3929
- i++;
3930
- continue;
3931
- }
3932
- const prev = annotation[i - 1];
3933
- if (prev === "<" || prev === ">" || prev === "!" || prev === "=") continue;
3934
- return true;
3966
+ const cls = [...stop].map(escapeClassChar).join("");
3967
+ const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
3968
+ const inner = many(choice(self, ...skips, content));
3969
+ const closer = strict ? literal(close) : expect(literal(close));
3970
+ const combi = transform(
3971
+ sequence(literal(open), inner, closer),
3972
+ // parts: strings (content/self) or arrays (a sequence-shaped skip) or a
3973
+ // ParseError (recovered close). `c` is the close string or a ParseError.
3974
+ ([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
3975
+ );
3976
+ if (combi._def.tag === "transform") {
3977
+ combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
3978
+ combi._def.recognitionOnly = true;
3935
3979
  }
3936
- return false;
3937
- }
3938
- function buildAnalysisSrc(def) {
3939
- return def.buildSigSrc ?? def.buildSrc ?? def.build.toString();
3980
+ ;
3981
+ combi._balanced = { open, close };
3982
+ self.define(combi);
3983
+ return combi;
3940
3984
  }
3941
- var RESOLVER_REASONS = {
3942
- "rest-parameter": "its parameter list uses a rest parameter, so the declared arity is unbounded",
3943
- "arguments": "its body references `arguments`",
3944
- "reassigned": "the binding it names is reassigned, so the function read here is not decidable",
3945
- "not-a-function": "the binding it names is not a function declaration",
3946
- "unresolved-import": "its module could not be resolved or parsed",
3947
- "not-found": "the name did not resolve to any binding",
3948
- "computed": "it is a computed expression",
3949
- "foreign-source": "it is named in a module the resolver was never told about, so its offsets index no scope tree the resolver holds",
3950
- "ambiguous-source": "two distinct registered modules hold byte-identical text, so that text no longer names the module its offsets index"
3951
- };
3952
- function unconfirmableReason(def, src) {
3953
- const declared = def.buildArityUnresolved;
3954
- if (declared !== void 0 && RESOLVER_REASONS[declared]) return RESOLVER_REASONS[declared];
3955
- const s = src.trim();
3956
- const params = s.slice(0, s.indexOf(")") + 1);
3957
- if (params.includes("...")) return RESOLVER_REASONS["rest-parameter"];
3958
- if (/\barguments\b/.test(s)) return RESOLVER_REASONS["arguments"];
3959
- if (/^[A-Za-z_$][\w$]*(\s*\.\s*[A-Za-z_$][\w$]*)?$/.test(s)) {
3960
- return "it names a binding this build could not resolve to a function declaration";
3985
+ function firstSetClassChars(c) {
3986
+ const fs = c._meta.firstSet;
3987
+ if (fs.kind === "empty") return [];
3988
+ if (fs.kind !== "ranges") return null;
3989
+ const out = [];
3990
+ for (const { lo, hi } of fs.ranges) {
3991
+ if (hi - lo > 8) return null;
3992
+ for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
3961
3993
  }
3962
- return "its parameter list could not be parsed";
3963
- }
3964
- function confirmedArityForDef(def) {
3965
- if (def.buildArity !== void 0) return def.buildArity;
3966
- const src = buildAnalysisSrc(def);
3967
- const arity = confirmedBuildArity(src);
3968
- if (arity !== null) return arity;
3969
- const subject = `build reducer \`${(def.buildSrc ?? "<runtime fn>").trim().split("\n")[0].slice(0, 60)}\``;
3970
- recordDegradation({
3971
- code: "build-arity-unconfirmed",
3972
- severity: "warn",
3973
- where: `node("${def.type ?? "<inferred>"}")`,
3974
- subject,
3975
- fellBackTo: `could not confirm its declared arity \u2014 ${unconfirmableReason(def, src)} \u2014 so this node captures children, fields and raw children, logs trivia, and clones \`_ctx.state\` on every match`,
3976
- otherwise: "only the tiers the reducer actually declares would be captured (arity >= 1 children, >= 2 fields, >= 4 raw, >= 5 trivia, >= 6 state). Declare it with `node(..., { buildArity: n })` if it cannot be derived"
3977
- });
3978
- return null;
3979
- }
3980
- function buildReadsRaw(def) {
3981
- if (!def.build) return true;
3982
- if (def.buildRawUnused === true) return false;
3983
- const arity = confirmedArityForDef(def);
3984
- if (arity === null) return true;
3985
- return arity >= 4;
3986
- }
3987
- function buildReadsTrivia(def) {
3988
- if (!def.build) return true;
3989
- const arity = confirmedArityForDef(def);
3990
- if (arity === null) return true;
3991
- return arity >= 5;
3994
+ return out;
3992
3995
  }
3993
- function buildReadsState(def) {
3994
- if (!def.build) return true;
3995
- const arity = confirmedArityForDef(def);
3996
- if (arity === null) return true;
3997
- return arity >= 6;
3996
+ function escapeClassChar(ch2) {
3997
+ return ch2.replace(/[\\\]^-]/g, "\\$&");
3998
3998
  }
3999
3999
 
4000
4000
  // src/compiler/fields.ts
@@ -4181,6 +4181,7 @@ var OP_COV = 40;
4181
4181
  var OP_SCOPE_PLAIN = 41;
4182
4182
  var OP_LEX_BODY = 42;
4183
4183
  var OP_LEX_PROGRAM = 43;
4184
+ var OP_SOURCE_LEAF = 44;
4184
4185
  var OP_DISPATCH = 25;
4185
4186
  var OP_ROUTED = 26;
4186
4187
  var OP_NAMES = {
@@ -4199,6 +4200,7 @@ var OP_NAMES = {
4199
4200
  [OP_NOT]: "NOT",
4200
4201
  [OP_PEEK]: "PEEK",
4201
4202
  [OP_LEAF]: "LEAF",
4203
+ [OP_SOURCE_LEAF]: "SOURCE_LEAF",
4202
4204
  [OP_EMPTY]: "EMPTY",
4203
4205
  [OP_LIT_TRACK]: "LIT_TRACK",
4204
4206
  [OP_RX_TRACK]: "RX_TRACK",
@@ -6047,6 +6049,7 @@ function childSlots(code, ip, out) {
6047
6049
  case OP_NODE_TRACK:
6048
6050
  case OP_FIELD:
6049
6051
  case OP_LEAF:
6052
+ case OP_SOURCE_LEAF:
6050
6053
  case OP_ARMGATE:
6051
6054
  out.push(code[ip + 2]);
6052
6055
  return true;
@@ -6155,7 +6158,7 @@ function transfer(code, ip, at, hostCst) {
6155
6158
  if (at.tri === TRI_NONE && !at.buf) return at;
6156
6159
  return { tri: TRI_NONE, buf: false, raw: RAW_UNKNOWN, cap: at.cap };
6157
6160
  }
6158
- if (op === OP_LEAF) {
6161
+ if (op === OP_LEAF || op === OP_SOURCE_LEAF) {
6159
6162
  if (!at.buf) return at;
6160
6163
  return { tri: at.tri, buf: false, raw: RAW_UNKNOWN, cap: at.cap };
6161
6164
  }
@@ -6242,7 +6245,7 @@ function leadingScalarTerminal(code, ip, minDepth = 2, throughAttempt = true, th
6242
6245
  depth++;
6243
6246
  continue;
6244
6247
  }
6245
- if (op === OP_GATE || op === OP_SCOPE || op === OP_SCOPE_CAP || op === OP_SCOPE_PLAIN || op === OP_XFORM || op === OP_NODE || op === OP_FIELD || op === OP_LEAF) {
6248
+ if (op === OP_GATE || op === OP_SCOPE || op === OP_SCOPE_CAP || op === OP_SCOPE_PLAIN || op === OP_XFORM || op === OP_NODE || op === OP_FIELD || (op === OP_LEAF || op === OP_SOURCE_LEAF)) {
6246
6249
  at = code[at + 2];
6247
6250
  depth++;
6248
6251
  continue;
@@ -6275,7 +6278,7 @@ function leadingScalarSequence(code, ip) {
6275
6278
  at2 = code[at2 + 1];
6276
6279
  continue;
6277
6280
  }
6278
- if (op === OP_GATE || op === OP_SCOPE_PLAIN || op === OP_XFORM || op === OP_NODE || op === OP_FIELD || op === OP_LEAF) {
6281
+ if (op === OP_GATE || op === OP_SCOPE_PLAIN || op === OP_XFORM || op === OP_NODE || op === OP_FIELD || (op === OP_LEAF || op === OP_SOURCE_LEAF)) {
6279
6282
  at2 = code[at2 + 2];
6280
6283
  continue;
6281
6284
  }
@@ -6308,7 +6311,7 @@ function leadingScalarSequence(code, ip) {
6308
6311
  at = code[at + 2];
6309
6312
  continue;
6310
6313
  }
6311
- if (op === OP_GATE || op === OP_XFORM || op === OP_NODE || op === OP_FIELD || op === OP_LEAF) {
6314
+ if (op === OP_GATE || op === OP_XFORM || op === OP_NODE || op === OP_FIELD || (op === OP_LEAF || op === OP_SOURCE_LEAF)) {
6312
6315
  at = code[at + 2];
6313
6316
  continue;
6314
6317
  }
@@ -6338,7 +6341,7 @@ function leadingLiteralFamily(code, constants, ip, minDepth = 2, limit = 4) {
6338
6341
  if (op === OP_RULE || op === OP_LABEL || op === OP_TOKEN || op === OP_ATTEMPT || op === OP_PEEK) {
6339
6342
  return walk(code[at + 1], depth + 1);
6340
6343
  }
6341
- if (op === OP_GATE || op === OP_SCOPE || op === OP_SCOPE_CAP || op === OP_SCOPE_PLAIN || op === OP_XFORM || op === OP_NODE || op === OP_FIELD || op === OP_LEAF) {
6344
+ if (op === OP_GATE || op === OP_SCOPE || op === OP_SCOPE_CAP || op === OP_SCOPE_PLAIN || op === OP_XFORM || op === OP_NODE || op === OP_FIELD || (op === OP_LEAF || op === OP_SOURCE_LEAF)) {
6342
6345
  return walk(code[at + 2], depth + 1);
6343
6346
  }
6344
6347
  if (op === OP_SEQ || op === OP_SEQV) {
@@ -7224,8 +7227,10 @@ ${rootCap ? "ctx._rootTriviaCapture=sR\n" : ""}return v
7224
7227
  }
7225
7228
  /* ── boundaries ──────────────────────────────────────────────────────── */
7226
7229
  case OP_TOKEN:
7227
- case OP_LEAF: {
7230
+ case OP_LEAF:
7231
+ case OP_SOURCE_LEAF: {
7228
7232
  const isToken = op === OP_TOKEN;
7233
+ const capturesSource = isToken || op === OP_SOURCE_LEAF && hostCst;
7229
7234
  const fn = isToken ? void 0 : fnRef(code[ip + 1]);
7230
7235
  const child = link(isToken ? code[ip + 1] : code[ip + 2]);
7231
7236
  const wasCap = L.buf ? "true" : "(ctx._cstBuf!==undefined||ctx._cstLeaves!==undefined)";
@@ -7258,7 +7263,7 @@ ctx._triviaLog=sOtl
7258
7263
  if(v===FAIL)return FAIL
7259
7264
  const e=EC.e
7260
7265
  const out=${isToken ? "input.slice(pos,e)" : `${fn}(v,{start:pos,end:e})`}
7261
- if(wasCap)pushCstLeaf(ctx,{_tag:'leaf',value:out,span:{start:pos,end:e}})
7266
+ if(wasCap)pushCstLeaf(ctx,{_tag:'leaf',value:${capturesSource ? "input.slice(pos,e)" : "out"},span:{start:pos,end:e}})
7262
7267
  EC.e=e
7263
7268
  return out
7264
7269
  }`;
@@ -9691,7 +9696,8 @@ function assemble(t, prog, cfg) {
9691
9696
  return value;
9692
9697
  };
9693
9698
  }
9694
- case OP_LEAF: {
9699
+ case OP_LEAF:
9700
+ case OP_SOURCE_LEAF: {
9695
9701
  const fn = fns[code[ip + 1]];
9696
9702
  const child = link(code[ip + 2]);
9697
9703
  return (input, pos, ctx) => {
@@ -9719,7 +9725,11 @@ function assemble(t, prog, cfg) {
9719
9725
  if (v === FAIL) return FAIL;
9720
9726
  const end = EC.e;
9721
9727
  const out = fn(v, { start: pos, end });
9722
- if (wasCapturing) pushCstLeaf(ctx, { _tag: "leaf", value: out, span: { start: pos, end } });
9728
+ if (wasCapturing) pushCstLeaf(ctx, {
9729
+ _tag: "leaf",
9730
+ value: op === OP_SOURCE_LEAF && hostCst ? input.slice(pos, end) : out,
9731
+ span: { start: pos, end }
9732
+ });
9723
9733
  EC.e = end;
9724
9734
  return out;
9725
9735
  };
@@ -13136,7 +13146,7 @@ var Encoder = class {
13136
13146
  }
13137
13147
  case "leaf": {
13138
13148
  const child = this.node(d.parser).ip;
13139
- return this.emit(OP_LEAF, this.fn(d.fn, d.fnSrc ?? null), child);
13149
+ return this.emit(d.cstValue === "source" ? OP_SOURCE_LEAF : OP_LEAF, this.fn(d.fn, d.fnSrc ?? null), child);
13140
13150
  }
13141
13151
  case "node": {
13142
13152
  if (d.type === void 0) missingInferredType();
@@ -13361,6 +13371,7 @@ var Encoder = class {
13361
13371
  case OP_WITHCTX:
13362
13372
  case OP_XFORM:
13363
13373
  case OP_LEAF:
13374
+ case OP_SOURCE_LEAF:
13364
13375
  case OP_NODE:
13365
13376
  case OP_NODE_TRACK:
13366
13377
  return [ip + 2];
@@ -13853,7 +13864,7 @@ function payloadKey(p, d) {
13853
13864
  case "transform":
13854
13865
  return `transform\0${fnKey(d.fnSrc, d.fn)}\0${d.recognitionOnly === true}`;
13855
13866
  case "leaf":
13856
- return `leaf\0${fnKey(d.fnSrc, d.fn)}`;
13867
+ return `leaf\0${d.cstValue ?? "value"}\0${fnKey(d.fnSrc, d.fn)}`;
13857
13868
  case "many":
13858
13869
  case "oneOrMore":
13859
13870
  return `${d.tag}\0${d.min}\0${d.max ?? ""}`;