parseman 0.42.0 → 0.43.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.
Files changed (42) hide show
  1. package/dist/combinators/node.d.ts +21 -7
  2. package/dist/combinators/node.d.ts.map +1 -1
  3. package/dist/combinators/parser.d.ts +23 -2
  4. package/dist/combinators/parser.d.ts.map +1 -1
  5. package/dist/compiler/codegen.d.ts +6 -0
  6. package/dist/compiler/codegen.d.ts.map +1 -1
  7. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  8. package/dist/compiler/linker.d.ts +7 -1
  9. package/dist/compiler/linker.d.ts.map +1 -1
  10. package/dist/cst/reflection.d.ts +24 -0
  11. package/dist/cst/reflection.d.ts.map +1 -0
  12. package/dist/cst/types.d.ts +2 -0
  13. package/dist/cst/types.d.ts.map +1 -1
  14. package/dist/cst/walk.d.ts +27 -62
  15. package/dist/cst/walk.d.ts.map +1 -1
  16. package/dist/index.cjs +375 -195
  17. package/dist/index.cjs.map +4 -4
  18. package/dist/index.d.ts +4 -3
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +375 -195
  21. package/dist/index.js.map +4 -4
  22. package/dist/language-service/index.cjs +25 -33
  23. package/dist/language-service/index.cjs.map +4 -4
  24. package/dist/language-service/index.d.ts +1 -1
  25. package/dist/language-service/index.d.ts.map +1 -1
  26. package/dist/language-service/index.js +25 -33
  27. package/dist/language-service/index.js.map +4 -4
  28. package/dist/plugin/evaluator.d.ts +1 -0
  29. package/dist/plugin/evaluator.d.ts.map +1 -1
  30. package/dist/plugin/index.cjs +654 -173
  31. package/dist/plugin/index.cjs.map +4 -4
  32. package/dist/plugin/index.d.ts.map +1 -1
  33. package/dist/plugin/index.js +654 -173
  34. package/dist/plugin/index.js.map +4 -4
  35. package/dist/spec/index.cjs +429 -313
  36. package/dist/spec/index.cjs.map +4 -4
  37. package/dist/spec/index.js +429 -313
  38. package/dist/spec/index.js.map +4 -4
  39. package/dist/types.d.ts +8 -1
  40. package/dist/types.d.ts.map +1 -1
  41. package/dist/version.d.ts +1 -1
  42. package/package.json +1 -1
@@ -2742,6 +2742,191 @@ function markUnusedValues(root) {
2742
2742
  visit(resolveRoot(root), true);
2743
2743
  }
2744
2744
 
2745
+ // src/combinators/grammar.ts
2746
+ function annotateResultLines(result, index) {
2747
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
2748
+ const errors = annotated.errors;
2749
+ if (errors) {
2750
+ ;
2751
+ annotated.errors = errors.map((error) => ({
2752
+ ...error,
2753
+ span: annotateSpan(error.span, index)
2754
+ }));
2755
+ }
2756
+ return annotated;
2757
+ }
2758
+ function createParseLineContext(input, pos) {
2759
+ const lineIndex = createLineIndex();
2760
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
2761
+ return { lineIndex, lineScannedTo: pos };
2762
+ }
2763
+ function parser(opts, root) {
2764
+ const clearTrivia = opts.trivia === null;
2765
+ return {
2766
+ _tag: "grammar",
2767
+ _meta: root._meta,
2768
+ _def: {
2769
+ tag: "grammar",
2770
+ parser: root,
2771
+ triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
2772
+ clearTrivia,
2773
+ ...opts.captureTrivia ? { captureTrivia: true } : {},
2774
+ trackLines: opts.trackLines ?? false
2775
+ },
2776
+ parse(input, pos, _ctx) {
2777
+ const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
2778
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
2779
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
2780
+ const ctx = {
2781
+ ..._ctx,
2782
+ trackLines,
2783
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
2784
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
2785
+ // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
2786
+ ...clearTrivia ? {
2787
+ trivia: void 0,
2788
+ triviaKindLabels: void 0
2789
+ } : opts.trivia != null ? {
2790
+ trivia: opts.trivia,
2791
+ ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
2792
+ } : {},
2793
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
2794
+ // Kind-filter for per-node capture. Resolve against this scope's trivia
2795
+ // labels — this parser's own trivia if it declares one, else the INHERITED
2796
+ // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
2797
+ // trivia is inherited rather than re-declared here. No labels → undefined
2798
+ // (capture all).
2799
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
2800
+ };
2801
+ const result = root.parse(input, pos ?? 0, ctx);
2802
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
2803
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
2804
+ }
2805
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
2806
+ }
2807
+ };
2808
+ }
2809
+ function noTrivia(root) {
2810
+ return parser({ trivia: null }, root);
2811
+ }
2812
+
2813
+ // src/cst/reflection.ts
2814
+ var GRAMMAR_REFLECTION = /* @__PURE__ */ Symbol.for("parseman.grammarReflection");
2815
+ function childrenOf(def) {
2816
+ switch (def.tag) {
2817
+ case "sequence":
2818
+ case "choice":
2819
+ return def.parsers;
2820
+ case "dispatch":
2821
+ return [
2822
+ def.selector,
2823
+ ...def.cases.map((c) => c.parser),
2824
+ ...def.matchers ? def.matchers.map((c) => c.parser) : [],
2825
+ ...def.otherwise ? [def.otherwise] : []
2826
+ ];
2827
+ case "many":
2828
+ case "oneOrMore":
2829
+ case "optional":
2830
+ case "attempt":
2831
+ case "transform":
2832
+ case "trivia":
2833
+ case "token":
2834
+ case "leaf":
2835
+ case "label":
2836
+ case "field":
2837
+ case "not":
2838
+ case "peek":
2839
+ case "node":
2840
+ case "withCtx":
2841
+ case "recover":
2842
+ case "expect":
2843
+ return [def.parser];
2844
+ case "grammar":
2845
+ return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
2846
+ case "sepBy":
2847
+ return [def.parser, def.separator];
2848
+ case "skip":
2849
+ return [def.main, def.skipped];
2850
+ case "scanTo":
2851
+ return [def.sentinel, ...def.skip];
2852
+ case "literal":
2853
+ case "regex":
2854
+ case "keywords":
2855
+ case "routed":
2856
+ case "guard":
2857
+ case "lazy":
2858
+ case "unknown":
2859
+ return [];
2860
+ }
2861
+ }
2862
+ function addNode(out, def) {
2863
+ if (def.type === void 0) return;
2864
+ const existing = out.get(def.type);
2865
+ if (existing === void 0) {
2866
+ out.set(def.type, def.tags === void 0 ? [] : [...def.tags]);
2867
+ return;
2868
+ }
2869
+ if (def.tags === void 0) return;
2870
+ for (const tag of def.tags) {
2871
+ if (!existing.includes(tag)) existing.push(tag);
2872
+ }
2873
+ }
2874
+ function collectFromCombinator(parser2, out, seen, followLazy = true) {
2875
+ if (seen.has(parser2)) return;
2876
+ seen.add(parser2);
2877
+ const def = parser2._def;
2878
+ if (def.tag === "lazy") {
2879
+ if (followLazy) {
2880
+ try {
2881
+ collectFromCombinator(def.thunk(), out, seen, followLazy);
2882
+ } catch {
2883
+ }
2884
+ }
2885
+ return;
2886
+ }
2887
+ if (def.tag === "node") addNode(out, def);
2888
+ for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy);
2889
+ }
2890
+ function collectGrammarReflection(ruleMap, opts) {
2891
+ const byType = /* @__PURE__ */ new Map();
2892
+ const followLazy = opts?.followLazy !== false;
2893
+ for (const [, rule] of ruleMap) {
2894
+ if (!followLazy && rule._def.tag === "lazy") {
2895
+ try {
2896
+ const resolved = rule._def.thunk();
2897
+ if (resolved._def.tag !== "lazy") collectFromCombinator(resolved, byType, /* @__PURE__ */ new Set(), false);
2898
+ } catch {
2899
+ }
2900
+ continue;
2901
+ }
2902
+ collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
2903
+ }
2904
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
2905
+ }
2906
+ function mergeGrammarReflections(reflections) {
2907
+ const byType = /* @__PURE__ */ new Map();
2908
+ for (const reflection of reflections) {
2909
+ for (const node2 of reflection.nodes) {
2910
+ const tags = byType.get(node2.type);
2911
+ if (tags === void 0) {
2912
+ byType.set(node2.type, [...node2.tags]);
2913
+ continue;
2914
+ }
2915
+ for (const tag of node2.tags) {
2916
+ if (!tags.includes(tag)) tags.push(tag);
2917
+ }
2918
+ }
2919
+ }
2920
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
2921
+ }
2922
+ function attachGrammarReflection(grammar, reflection) {
2923
+ Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true });
2924
+ return grammar;
2925
+ }
2926
+ function grammarReflectionSource(reflection) {
2927
+ return `{ nodes: ${JSON.stringify(reflection.nodes)} }`;
2928
+ }
2929
+
2745
2930
  // src/combinators/parser.ts
2746
2931
  var RULE_ORDER = "__parsemanRuleOrder";
2747
2932
  function tagRule(r, key) {
@@ -2817,6 +3002,21 @@ function rules(a, b) {
2817
3002
  if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
2818
3003
  }
2819
3004
  }
3005
+ if (options?.trackLines === true) {
3006
+ for (const key of Object.keys(definitions)) {
3007
+ const rule = cache[key];
3008
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
3009
+ }
3010
+ for (const key of Object.keys(definitions)) {
3011
+ const rule = cache[key];
3012
+ if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
3013
+ const wrapped = parser({ trackLines: true }, rule);
3014
+ tagRule(wrapped, key);
3015
+ wrapped._meta.grammarTrackLines = true;
3016
+ cache[key] = wrapped;
3017
+ }
3018
+ }
3019
+ }
2820
3020
  for (const key of Object.keys(definitions)) {
2821
3021
  markUnusedValues(cache[key]);
2822
3022
  }
@@ -2825,6 +3025,7 @@ function rules(a, b) {
2825
3025
  enumerable: false,
2826
3026
  configurable: true
2827
3027
  });
3028
+ attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
2828
3029
  return cache;
2829
3030
  }
2830
3031
 
@@ -3145,13 +3346,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3145
3346
  const collapse = opts?.collapse === true;
3146
3347
  const captureTrivia = opts?.captureTrivia === true;
3147
3348
  const trailingTrivia = opts?.trailingTrivia === true;
3349
+ const tags = opts?.tags;
3148
3350
  if (unwrap && collapse) {
3149
3351
  throw new Error("node() options cannot set both unwrap and collapse");
3150
3352
  }
3151
3353
  if (project !== void 0 && (unwrap || collapse)) {
3152
3354
  throw new Error("node() options cannot combine project with unwrap or collapse");
3153
3355
  }
3154
- const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {} } : baseDef;
3356
+ const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia || tags !== void 0 && tags.length > 0 ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {}, ...tags !== void 0 && tags.length > 0 ? { tags } : {} } : baseDef;
3155
3357
  const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
3156
3358
  const clonesState = build ? buildReadsState(def) : project === void 0;
3157
3359
  const hasOwnFields = parserHasOwnFields(combinator);
@@ -3197,7 +3399,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3197
3399
  const cstOutput = cstOutputHost(ctx.build);
3198
3400
  const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3199
3401
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3200
- const built = unwrap && children2.length === 1 ? unwrapChild(children2[0]) : collapse && children2.length === 1 ? children2[0] : !build && project === void 0 && ctx.build?._parsemanCstCollapse && children2.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children2[0], children2, rawChildren) ? children2[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState) : projectChild(children2, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState) : build(children2, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
3402
+ const built = unwrap && children2.length === 1 ? unwrapChild(children2[0]) : collapse && children2.length === 1 ? children2[0] : !build && project === void 0 && ctx.build?._parsemanCstCollapse && children2.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children2[0], children2, rawChildren) ? children2[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState, tags) : projectChild(children2, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState, tags) : build(children2, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, st, tags) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
3201
3403
  const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span };
3202
3404
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3203
3405
  pushCstChild(ctx, built, rawEntry);
@@ -3273,74 +3475,6 @@ function field(name, combinator) {
3273
3475
  };
3274
3476
  }
3275
3477
 
3276
- // src/combinators/grammar.ts
3277
- function annotateResultLines(result, index) {
3278
- const annotated = { ...result, span: annotateSpan(result.span, index) };
3279
- const errors = annotated.errors;
3280
- if (errors) {
3281
- ;
3282
- annotated.errors = errors.map((error) => ({
3283
- ...error,
3284
- span: annotateSpan(error.span, index)
3285
- }));
3286
- }
3287
- return annotated;
3288
- }
3289
- function createParseLineContext(input, pos) {
3290
- const lineIndex = createLineIndex();
3291
- if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
3292
- return { lineIndex, lineScannedTo: pos };
3293
- }
3294
- function parser(opts, root) {
3295
- const clearTrivia = opts.trivia === null;
3296
- return {
3297
- _tag: "grammar",
3298
- _meta: root._meta,
3299
- _def: {
3300
- tag: "grammar",
3301
- parser: root,
3302
- triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
3303
- clearTrivia,
3304
- ...opts.captureTrivia ? { captureTrivia: true } : {},
3305
- trackLines: opts.trackLines ?? false
3306
- },
3307
- parse(input, pos, _ctx) {
3308
- const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
3309
- const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
3310
- const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
3311
- const ctx = {
3312
- ..._ctx,
3313
- trackLines,
3314
- ...lineIndex ? { _lineIndex: lineIndex } : {},
3315
- ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
3316
- // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
3317
- ...clearTrivia ? {
3318
- trivia: void 0,
3319
- triviaKindLabels: void 0
3320
- } : opts.trivia != null ? {
3321
- trivia: opts.trivia,
3322
- ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
3323
- } : {},
3324
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
3325
- // Kind-filter for per-node capture. Resolve against this scope's trivia
3326
- // labels — this parser's own trivia if it declares one, else the INHERITED
3327
- // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
3328
- // trivia is inherited rather than re-declared here. No labels → undefined
3329
- // (capture all).
3330
- ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
3331
- };
3332
- const result = root.parse(input, pos ?? 0, ctx);
3333
- if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
3334
- _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
3335
- }
3336
- return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
3337
- }
3338
- };
3339
- }
3340
- function noTrivia(root) {
3341
- return parser({ trivia: null }, root);
3342
- }
3343
-
3344
3478
  // src/combinators/token.ts
3345
3479
  function token(root) {
3346
3480
  const meta = {
@@ -3437,7 +3571,7 @@ function leaf(root, fn) {
3437
3571
  }
3438
3572
 
3439
3573
  // src/version.ts
3440
- var PARSEMAN_VERSION = "0.42.0";
3574
+ var PARSEMAN_VERSION = "0.43.0";
3441
3575
 
3442
3576
  // src/cst/host-mode.ts
3443
3577
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -3503,7 +3637,7 @@ var SUGGESTIONS = {
3503
3637
  function classifyBroadArm(arm, resolve2) {
3504
3638
  const seen = /* @__PURE__ */ new Set();
3505
3639
  const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
3506
- const walk2 = (p) => {
3640
+ const walk = (p) => {
3507
3641
  if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
3508
3642
  seen.add(p);
3509
3643
  const d = p._def;
@@ -3517,7 +3651,7 @@ function classifyBroadArm(arm, resolve2) {
3517
3651
  // peek() CARRIES its body's first-set, so it is only broad when the body is
3518
3652
  // (or the body is nullable) — walk in for the real cause.
3519
3653
  case "peek":
3520
- return walk2(d.parser);
3654
+ return walk(d.parser);
3521
3655
  case "scanTo":
3522
3656
  return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
3523
3657
  // `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
@@ -3531,7 +3665,7 @@ function classifyBroadArm(arm, resolve2) {
3531
3665
  case "attempt":
3532
3666
  case "withCtx":
3533
3667
  case "recover":
3534
- return walk2(d.parser);
3668
+ return walk(d.parser);
3535
3669
  case "lazy": {
3536
3670
  const name = p._ruleName;
3537
3671
  let target;
@@ -3543,13 +3677,13 @@ function classifyBroadArm(arm, resolve2) {
3543
3677
  return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
3544
3678
  target = bound;
3545
3679
  }
3546
- const inner = walk2(target);
3680
+ const inner = walk(target);
3547
3681
  if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
3548
3682
  return inner;
3549
3683
  }
3550
3684
  case "optional":
3551
3685
  case "many": {
3552
- const inner = walk2(d.parser);
3686
+ const inner = walk(d.parser);
3553
3687
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3554
3688
  }
3555
3689
  case "oneOrMore":
@@ -3562,9 +3696,9 @@ function classifyBroadArm(arm, resolve2) {
3562
3696
  case "node":
3563
3697
  case "grammar":
3564
3698
  case "expect":
3565
- return walk2(d.parser);
3699
+ return walk(d.parser);
3566
3700
  case "skip":
3567
- return walk2(d.main);
3701
+ return walk(d.main);
3568
3702
  case "sequence": {
3569
3703
  let sawNullablePrefix = false;
3570
3704
  for (const t of d.parsers) {
@@ -3573,7 +3707,7 @@ function classifyBroadArm(arm, resolve2) {
3573
3707
  continue;
3574
3708
  }
3575
3709
  if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve2))) {
3576
- const inner = walk2(t);
3710
+ const inner = walk(t);
3577
3711
  if (sawNullablePrefix)
3578
3712
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3579
3713
  return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
@@ -3588,18 +3722,18 @@ function classifyBroadArm(arm, resolve2) {
3588
3722
  }
3589
3723
  case "choice": {
3590
3724
  for (const a of d.parsers) {
3591
- const r = walk2(a);
3725
+ const r = walk(a);
3592
3726
  if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
3593
3727
  }
3594
3728
  return null;
3595
3729
  }
3596
3730
  case "dispatch":
3597
- return walk2(d.selector);
3731
+ return walk(d.selector);
3598
3732
  default:
3599
3733
  return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
3600
3734
  }
3601
3735
  };
3602
- return walk2(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3736
+ return walk(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3603
3737
  }
3604
3738
  function peelToLeading(arm) {
3605
3739
  let d = arm._def;
@@ -3801,7 +3935,7 @@ function formatGatingWarnings(report) {
3801
3935
 
3802
3936
  // src/analysis/duplication.ts
3803
3937
  var siteToString = (s) => s.path === "" ? s.rule : `${s.rule} \u203A ${s.path}`;
3804
- function childrenOf(d) {
3938
+ function childrenOf2(d) {
3805
3939
  switch (d.tag) {
3806
3940
  case "sequence":
3807
3941
  case "choice":
@@ -3935,7 +4069,7 @@ function render(p, depth = 3) {
3935
4069
  break;
3936
4070
  }
3937
4071
  if (depth <= 0) return `${d.tag}(\u2026)`;
3938
- const kids = childrenOf(d).map((k) => render(k, depth - 1));
4072
+ const kids = childrenOf2(d).map((k) => render(k, depth - 1));
3939
4073
  switch (d.tag) {
3940
4074
  case "sequence":
3941
4075
  return `sequence(${kids.join(", ")})`;
@@ -4130,7 +4264,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4130
4264
  if (hit !== void 0) return hit;
4131
4265
  const d = p._def;
4132
4266
  shapeCache.set(p, interner.hole);
4133
- const kids = childrenOf(d);
4267
+ const kids = childrenOf2(d);
4134
4268
  const id = interner.of(`${payloadKey(p, d)}|${kids.map(shapeOf).join(",")}`);
4135
4269
  shapeCache.set(p, id);
4136
4270
  return id;
@@ -4140,7 +4274,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4140
4274
  if (hit !== void 0) return hit;
4141
4275
  sizeCache.set(p, 1);
4142
4276
  let n = 1;
4143
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4277
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4144
4278
  sizeCache.set(p, n);
4145
4279
  return n;
4146
4280
  };
@@ -4153,7 +4287,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4153
4287
  visited.set(p, { p, d, site: here, shape: shapeOf(p), size: sizeOf(p), enclosingNode, enclosingReducer });
4154
4288
  const inner = d.tag === "node" ? d.type ?? "(anonymous)" : enclosingNode;
4155
4289
  const innerReducer = enclosingReducer || d.tag === "node" && (d.build !== void 0 || d.buildSrc !== void 0) || d.tag === "transform" || d.tag === "leaf";
4156
- const kids = childrenOf(d);
4290
+ const kids = childrenOf2(d);
4157
4291
  kids.forEach((k, i) => {
4158
4292
  const seg = segment(d, i);
4159
4293
  visit(k, here.rule, here.path === "" ? seg : `${here.path} \u203A ${seg}`, inner, innerReducer);
@@ -4213,7 +4347,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4213
4347
  function markSubtree(p, into) {
4214
4348
  if (into.has(p)) return;
4215
4349
  into.add(p);
4216
- for (const k of childrenOf(p._def)) markSubtree(k, into);
4350
+ for (const k of childrenOf2(p._def)) markSubtree(k, into);
4217
4351
  }
4218
4352
  var stripMembers = (f) => {
4219
4353
  const { _members, ...rest } = f;
@@ -4253,7 +4387,7 @@ function findNearDuplicates(visited, interner, shapeOf, minSize) {
4253
4387
  if (hit !== void 0) return hit;
4254
4388
  holeCache.set(p, []);
4255
4389
  const d = p._def;
4256
- const kids = childrenOf(d);
4390
+ const kids = childrenOf2(d);
4257
4391
  const kidShapes = kids.map(shapeOf);
4258
4392
  const payload = payloadKey(p, d);
4259
4393
  const out2 = [];
@@ -4314,7 +4448,7 @@ function findDivergentSlot(members, shapeOf) {
4314
4448
  let cur = [...members];
4315
4449
  for (let guard2 = 0; guard2 < 64; guard2++) {
4316
4450
  const d0 = cur[0]._def;
4317
- const kidLists = cur.map((c) => childrenOf(c._def));
4451
+ const kidLists = cur.map((c) => childrenOf2(c._def));
4318
4452
  const arity = kidLists[0].length;
4319
4453
  if (kidLists.some((k) => k.length !== arity) || arity === 0) break;
4320
4454
  const diff = [];
@@ -4327,7 +4461,7 @@ function findDivergentSlot(members, shapeOf) {
4327
4461
  path2.push(i);
4328
4462
  defs.push(d0);
4329
4463
  cur = kidLists.map((k) => k[i]);
4330
- const nextArity = childrenOf(cur[0]._def).length;
4464
+ const nextArity = childrenOf2(cur[0]._def).length;
4331
4465
  const samePayload = cur.every((c) => payloadKey(c, c._def) === payloadKey(cur[0], cur[0]._def));
4332
4466
  if (!samePayload || nextArity === 0) break;
4333
4467
  }
@@ -4335,7 +4469,7 @@ function findDivergentSlot(members, shapeOf) {
4335
4469
  let slotSize = 0;
4336
4470
  const sizeOf = (p) => {
4337
4471
  let n = 1;
4338
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4472
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4339
4473
  return n;
4340
4474
  };
4341
4475
  for (const c of cur) slotSize = Math.max(slotSize, sizeOf(c));
@@ -4344,7 +4478,7 @@ function findDivergentSlot(members, shapeOf) {
4344
4478
  function renderWithHole(p, path2, depth = 0) {
4345
4479
  if (depth === path2.length) return "\u2039slot\u203A";
4346
4480
  const d = p._def;
4347
- const kids = childrenOf(d);
4481
+ const kids = childrenOf2(d);
4348
4482
  const idx = path2[depth];
4349
4483
  const parts = kids.map((k, i) => i === idx ? renderWithHole(k, path2, depth + 1) : render(k, 2));
4350
4484
  switch (d.tag) {
@@ -4802,7 +4936,7 @@ function capturesPositionally(p, seen = /* @__PURE__ */ new Set()) {
4802
4936
  seen.add(p);
4803
4937
  const d = p._def;
4804
4938
  if (d.tag === "field" || d.tag === "label") return true;
4805
- for (const k of childrenOf(d)) if (capturesPositionally(k, seen)) return true;
4939
+ for (const k of childrenOf2(d)) if (capturesPositionally(k, seen)) return true;
4806
4940
  return false;
4807
4941
  }
4808
4942
  function detectHandRolledSepBy(terms, shapeOf) {
@@ -4908,7 +5042,7 @@ function nestedNodeTypes(root, out, seen, isRoot) {
4908
5042
  }
4909
5043
  return;
4910
5044
  }
4911
- for (const k of childrenOf(d)) nestedNodeTypes(k, out, seen, false);
5045
+ for (const k of childrenOf2(d)) nestedNodeTypes(k, out, seen, false);
4912
5046
  }
4913
5047
  var MAX_LOST_TYPES = 8;
4914
5048
  function findStructureLoss(visited, resolve2) {
@@ -6805,13 +6939,16 @@ function reindentStmts(stmts, targetLevels) {
6805
6939
  const targetPrefix = " ".repeat(targetLevels);
6806
6940
  return stmts.map((s) => s.trim().length === 0 ? "" : targetPrefix + s.slice(minLeading));
6807
6941
  }
6808
- function failReturn(expected, posExpr) {
6942
+ function failReturn(ctx, expected, posExpr) {
6943
+ if (ctx.lineTracking) return `return { ok: false, expected: [${expected}], span: ${emitSpanExpr(ctx, posExpr, posExpr)} }`;
6809
6944
  return `return { ok: false, expected: [${expected}], span: { start: ${posExpr}, end: ${posExpr} } }`;
6810
6945
  }
6811
- function failReturnArr(expectedArr, posExpr) {
6946
+ function failReturnArr(ctx, expectedArr, posExpr) {
6947
+ if (ctx.lineTracking) return `return { ok: false, expected: ${expectedArr}, span: ${emitSpanExpr(ctx, posExpr, posExpr)} }`;
6812
6948
  return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
6813
6949
  }
6814
- function committedReturnArr(expectedArr, posExpr) {
6950
+ function committedReturnArr(ctx, expectedArr, posExpr) {
6951
+ if (ctx.lineTracking) return `return { ok: false, expected: [...${expectedArr}], span: ${emitSpanExpr(ctx, posExpr, posExpr)}, committed: true }`;
6815
6952
  return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} }, committed: true }`;
6816
6953
  }
6817
6954
  function hoistExpected(ctx, constArrSource) {
@@ -6836,7 +6973,7 @@ function failBody(ctx, expected, posExpr) {
6836
6973
  if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
6837
6974
  return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; ${trace}break ${ctx.failLabel} }`;
6838
6975
  }
6839
- return probe + trace + failReturn(expected, posExpr);
6976
+ return probe + trace + failReturn(ctx, expected, posExpr);
6840
6977
  }
6841
6978
  function failArrBody(ctx, expectedArr, posExpr, constant = true) {
6842
6979
  const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
@@ -6846,18 +6983,19 @@ function failArrBody(ctx, expectedArr, posExpr, constant = true) {
6846
6983
  const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
6847
6984
  return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; ${trace}break ${ctx.failLabel} }`;
6848
6985
  }
6849
- if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
6850
- return probe + trace + failReturnArr(expectedArr, posExpr);
6986
+ if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: ${ctx.lineTracking ? emitSpanExpr(ctx, posExpr, posExpr) : `{ start: ${posExpr}, end: ${posExpr} }`} }`;
6987
+ return probe + trace + failReturnArr(ctx, expectedArr, posExpr);
6851
6988
  }
6852
- function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
6853
- return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} }, ...(_ctx._fc ? { committed: true } : {}) }`;
6989
+ function resultFromRecorded(ctx, feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
6990
+ const spanExpr = ctx.lineTracking ? emitSpanExpr(ctx, feExpr, feExpr) : `{ start: ${feExpr}, end: ${feExpr} }`;
6991
+ return `return { ok: false, expected: [...${fxExpr}], span: ${spanExpr}, ...(_ctx._fc ? { committed: true } : {}) }`;
6854
6992
  }
6855
6993
  function committedFailBody(ctx, expectedArr = "_ctx._fx", posExpr = "_ctx._fe") {
6856
6994
  if (ctx.failLabel) {
6857
6995
  if (!ctx.recordFail) return `{ _ctx._fc = true; break ${ctx.failLabel} }`;
6858
6996
  return `{ _ctx._fc = true; _ctx._fe = ${posExpr}; _ctx._fx = ${expectedArr}; break ${ctx.failLabel} }`;
6859
6997
  }
6860
- return committedReturnArr(expectedArr, posExpr);
6998
+ return committedReturnArr(ctx, expectedArr, posExpr);
6861
6999
  }
6862
7000
  function propagateFailBody(ctx, srcCtx = "_ctx") {
6863
7001
  if (srcCtx !== "_ctx") {
@@ -6865,10 +7003,10 @@ function propagateFailBody(ctx, srcCtx = "_ctx") {
6865
7003
  if (!ctx.recordFail) return `break ${ctx.failLabel}`;
6866
7004
  return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; break ${ctx.failLabel} }`;
6867
7005
  }
6868
- return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; ${resultFromRecorded()} }`;
7006
+ return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; ${resultFromRecorded(ctx)} }`;
6869
7007
  }
6870
7008
  if (ctx.failLabel) return `break ${ctx.failLabel}`;
6871
- return resultFromRecorded();
7009
+ return resultFromRecorded(ctx);
6872
7010
  }
6873
7011
  function emitIfFail(ctx, cond, body) {
6874
7012
  return [
@@ -8099,7 +8237,7 @@ function routedNeedsContextBridge(p, ctx, seen = /* @__PURE__ */ new Set()) {
8099
8237
  const d = p._def;
8100
8238
  if (d.tag === "dispatch") return false;
8101
8239
  if (d.tag === "withCtx") return parserUsesRouted2(d.parser);
8102
- return childrenOf2(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8240
+ return childrenOf3(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8103
8241
  }
8104
8242
  function armReplayInScope(arm, ctx) {
8105
8243
  let p = arm;
@@ -8301,7 +8439,7 @@ function emitAttempt(p, def, ctx, pos) {
8301
8439
  if (!ctx.recovery && !ctx.coverage && needsFirstSetGuard(def.parser)) {
8302
8440
  const gcV = v(ctx, "_agc");
8303
8441
  const gExp = armStaticExpected(ctx, def.parser);
8304
- const gFail = ctx.failLabel ? `{ _ctx._fe = ${pos};${ctx.recordFail ? ` _ctx._fx = ${gExp};` : ""} break ${ctx.failLabel} }` : `{ _ctx._fe = ${pos}; ${failReturnArr(gExp, pos)} }`;
8442
+ const gFail = ctx.failLabel ? `{ _ctx._fe = ${pos};${ctx.recordFail ? ` _ctx._fx = ${gExp};` : ""} break ${ctx.failLabel} }` : `{ _ctx._fe = ${pos}; ${failReturnArr(ctx, gExp, pos)} }`;
8305
8443
  preGuard.push(
8306
8444
  `${ind(ctx)}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
8307
8445
  `${ind(ctx)}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
@@ -8800,7 +8938,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8800
8938
  if (!ctx.recovery && !ctx.coverage && (capturesChildren || structural) && needsFirstSetGuard(def.parser)) {
8801
8939
  const gcV = v(ctx, "_ngc");
8802
8940
  const gExp = armStaticExpected(ctx, def.parser);
8803
- const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(gExp, pos);
8941
+ const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(ctx, gExp, pos);
8804
8942
  preGuard.push(
8805
8943
  `${i}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
8806
8944
  `${i}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
@@ -8862,7 +9000,8 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8862
9000
  const nodeSpanV = ctx.lineTracking ? v(ctx, "_nspan") : nodeSpanExpr;
8863
9001
  if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
8864
9002
  const buildExpr = structural ? `{ _tag: 'node', type: ${JSON.stringify(def.type)}, span: ${nodeSpanV}, state: ${stV} ?? null, children: ${chV} }` : mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, nodeSpanV, tlV) : def.project !== void 0 ? emitNodeProjectExpr(def.type, def.project, chV) : `${buildRef(ctx)}[${buildIdx}](${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
8865
- const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
9003
+ const hostBuildArgs = `${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV}${def.tags !== void 0 && def.tags.length > 0 ? `, ${JSON.stringify(def.tags)}` : ""}`;
9004
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${hostBuildArgs})`;
8866
9005
  const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
8867
9006
  const hostCollapseExpr = structural ? `_ctx.build !== undefined && _ctx.build._parsemanCstCollapse !== undefined && ${chV}.length === 1 && ${rawV}.length === 1 && _ctx.build._parsemanCstCollapse(${JSON.stringify(def.type)}, ${chV}[0], ${chV}, ${rawV}) ? ${chV}[0] : (${ndExpr})` : ndExpr;
8868
9007
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
@@ -9316,6 +9455,7 @@ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9316
9455
  if (seen.has(p)) return false;
9317
9456
  seen.add(p);
9318
9457
  const d = p._def;
9458
+ if (p._meta.grammarTrackLines) return true;
9319
9459
  if (d.tag === "grammar" && d.trackLines) return true;
9320
9460
  if (d.tag === "lazy") {
9321
9461
  try {
@@ -9324,7 +9464,7 @@ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9324
9464
  return false;
9325
9465
  }
9326
9466
  }
9327
- return childrenOf2(d).some((child) => hasLineTrackingDef(child, seen));
9467
+ return childrenOf3(d).some((child) => hasLineTrackingDef(child, seen));
9328
9468
  }
9329
9469
  function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9330
9470
  if (seen.has(p)) return false;
@@ -9343,7 +9483,7 @@ function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9343
9483
  case "dispatch":
9344
9484
  return false;
9345
9485
  default:
9346
- return childrenOf2(d).some((child) => parserUsesRouted2(child, seen));
9486
+ return childrenOf3(d).some((child) => parserUsesRouted2(child, seen));
9347
9487
  }
9348
9488
  }
9349
9489
  function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
@@ -9358,9 +9498,9 @@ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
9358
9498
  return false;
9359
9499
  }
9360
9500
  }
9361
- return childrenOf2(d).some((child) => hasDirectBuildDef(child, seen));
9501
+ return childrenOf3(d).some((child) => hasDirectBuildDef(child, seen));
9362
9502
  }
9363
- function childrenOf2(def) {
9503
+ function childrenOf3(def) {
9364
9504
  switch (def.tag) {
9365
9505
  case "sequence":
9366
9506
  case "choice":
@@ -9435,7 +9575,7 @@ function subtreeSizes(roots) {
9435
9575
  const def = p._def;
9436
9576
  let s = 1;
9437
9577
  if (def.tag !== "lazy") {
9438
- for (const child of childrenOf2(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
9578
+ for (const child of childrenOf3(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
9439
9579
  }
9440
9580
  sizes.set(p, s);
9441
9581
  return s;
@@ -9456,7 +9596,7 @@ function subtreeSizes(roots) {
9456
9596
  return;
9457
9597
  }
9458
9598
  sz(p);
9459
- for (const child of childrenOf2(def)) visit(child);
9599
+ for (const child of childrenOf3(def)) visit(child);
9460
9600
  };
9461
9601
  for (const root of roots) visit(root);
9462
9602
  return sizes;
@@ -9466,7 +9606,7 @@ function analyzeLazyUsageMulti(roots) {
9466
9606
  const recursive = /* @__PURE__ */ new Set();
9467
9607
  const descended = /* @__PURE__ */ new Set();
9468
9608
  const active = /* @__PURE__ */ new Set();
9469
- function walk2(p) {
9609
+ function walk(p) {
9470
9610
  const def = p._def;
9471
9611
  if (def.tag === "lazy") {
9472
9612
  counts.set(p, (counts.get(p) ?? 0) + 1);
@@ -9483,17 +9623,17 @@ function analyzeLazyUsageMulti(roots) {
9483
9623
  return;
9484
9624
  }
9485
9625
  active.add(p);
9486
- walk2(resolved);
9626
+ walk(resolved);
9487
9627
  active.delete(p);
9488
9628
  return;
9489
9629
  }
9490
9630
  if (isHoistableTag(def.tag)) {
9491
9631
  counts.set(p, (counts.get(p) ?? 0) + 1);
9492
9632
  }
9493
- for (const child of childrenOf2(def)) walk2(child);
9633
+ for (const child of childrenOf3(def)) walk(child);
9494
9634
  }
9495
9635
  const rootsArr = [...roots];
9496
- for (const root of rootsArr) walk2(root);
9636
+ for (const root of rootsArr) walk(root);
9497
9637
  return { counts, recursive, sizes: subtreeSizes(rootsArr) };
9498
9638
  }
9499
9639
  function ruleDependencies(ruleMap) {
@@ -9503,7 +9643,7 @@ function ruleDependencies(ruleMap) {
9503
9643
  for (const [name, comb] of ruleMap) {
9504
9644
  const found = /* @__PURE__ */ new Set();
9505
9645
  const seen = /* @__PURE__ */ new Set();
9506
- const walk2 = (p, isRoot) => {
9646
+ const walk = (p, isRoot) => {
9507
9647
  const def = p._def;
9508
9648
  const boundary = def.tag === "lazy" ? nameOf.get(p) ?? p._ruleName : nameOf.get(p);
9509
9649
  if (!isRoot && boundary !== void 0) {
@@ -9519,12 +9659,12 @@ function ruleDependencies(ruleMap) {
9519
9659
  } catch {
9520
9660
  return;
9521
9661
  }
9522
- walk2(resolved, false);
9662
+ walk(resolved, false);
9523
9663
  return;
9524
9664
  }
9525
- for (const child of childrenOf2(def)) walk2(child, false);
9665
+ for (const child of childrenOf3(def)) walk(child, false);
9526
9666
  };
9527
- walk2(comb, true);
9667
+ walk(comb, true);
9528
9668
  deps.set(name, [...found]);
9529
9669
  }
9530
9670
  return deps;
@@ -9673,6 +9813,7 @@ function compile(combinator, mapFnSources, opts) {
9673
9813
  const grammarTrivia = combinator._meta.grammarTrivia;
9674
9814
  const grammarScanSkip = combinator._meta.grammarScanSkip;
9675
9815
  const grammarHostMode = combinator._meta.grammarHostMode;
9816
+ const grammarTrackLines = combinator._meta.grammarTrackLines;
9676
9817
  const ctx = {
9677
9818
  vars: 0,
9678
9819
  indent: 1,
@@ -9693,7 +9834,7 @@ function compile(combinator, mapFnSources, opts) {
9693
9834
  capturing: hasNodeDef(combinator),
9694
9835
  recovery: opts?.recovery ?? false,
9695
9836
  hostMode: opts?.hostMode ?? grammarHostMode ?? "ast",
9696
- lineTracking: opts?.trackLines === true || hasLineTrackingDef(combinator),
9837
+ lineTracking: opts?.trackLines === true || grammarTrackLines === true || hasLineTrackingDef(combinator),
9697
9838
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
9698
9839
  lazyUsage: analyzeLazyUsage(combinator),
9699
9840
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
@@ -9857,6 +9998,7 @@ function compileRuleMap(ruleMap, opts) {
9857
9998
  const grammarTrivia = opts?.trivia;
9858
9999
  const grammarScanSkip = opts?.scanSkip ?? ruleMap.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
9859
10000
  const grammarHostMode = opts?.hostMode ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
10001
+ const grammarTrackLines = opts?.trackLines === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
9860
10002
  const ctx = {
9861
10003
  vars: 0,
9862
10004
  indent: 1,
@@ -9877,6 +10019,7 @@ function compileRuleMap(ruleMap, opts) {
9877
10019
  capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
9878
10020
  recovery: opts?.recovery ?? false,
9879
10021
  hostMode: grammarHostMode ?? "ast",
10022
+ lineTracking: grammarTrackLines,
9880
10023
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(ruleMap.map(([, rule]) => rule), coverageWinners) } } : {},
9881
10024
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
9882
10025
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
@@ -9910,6 +10053,10 @@ function compileRuleMap(ruleMap, opts) {
9910
10053
  const mfCovered = ctx.mapFns.length === 0 || derivedSrcs !== void 0;
9911
10054
  const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
9912
10055
  if (!canInline) return null;
10056
+ if (ctx.lineTracking) {
10057
+ ctx.needsLineTrack = true;
10058
+ ctx.needsLineSpan = true;
10059
+ }
9913
10060
  const mfDecl = derivedSrcs?.length ? ` const ${mfRef(ctx)} = [${derivedSrcs.join(", ")}]` : "";
9914
10061
  const buildDecl = buildSources?.length ? ` const ${buildRef(ctx)} = [${buildSources.join(", ")}]` : "";
9915
10062
  const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
@@ -9939,15 +10086,23 @@ function compileRuleMap(ruleMap, opts) {
9939
10086
  };
9940
10087
  const entryFnText = (r, rule) => {
9941
10088
  const ruleId = entryRuleId(rule);
10089
+ const linePreamble = ctx.lineTracking ? [
10090
+ ` if (_ctx._lineStarts === undefined) _ctx = { ..._ctx, trackLines: true, _lineStarts: [0], _lineScannedTo: 0 }`,
10091
+ ` else _ctx.trackLines = true`,
10092
+ ` if (_pos > (_ctx._lineScannedTo ?? 0)) _trackLines(_ctx, input, 0, _pos)`
10093
+ ] : [];
10094
+ const resultSpan = ctx.lineTracking ? `_spanLines(_ctx, _pos, ${r.endVar})` : `{ start: _pos, end: ${r.endVar} }`;
9942
10095
  const body = ruleId === void 0 ? [
10096
+ ...linePreamble,
9943
10097
  ` let pos = _pos`,
9944
10098
  ...r.stmts,
9945
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
10099
+ ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`
9946
10100
  ] : [
10101
+ ...linePreamble,
9947
10102
  ` const _coverageResult = (() => {`,
9948
10103
  ` let pos = _pos`,
9949
10104
  ...r.stmts,
9950
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
10105
+ ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`,
9951
10106
  ` })()`,
9952
10107
  ` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
9953
10108
  ` return _coverageResult`
@@ -9974,6 +10129,7 @@ function compileRuleMap(ruleMap, opts) {
9974
10129
  // host check passes vacuously — see `withHostMode` in the plugin.
9975
10130
  hostMode: ctx.hostMode ?? "ast",
9976
10131
  hostBranchElided: !!ctx.hostBranchElided,
10132
+ reflection: collectGrammarReflection(ruleMap),
9977
10133
  ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions }
9978
10134
  };
9979
10135
  }
@@ -9985,6 +10141,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
9985
10141
  const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
9986
10142
  const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
9987
10143
  const grammarHostMode = opts?.hostMode ?? ruleMapArg.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
10144
+ const grammarTrackLines = opts?.trackLines === true || ruleMapArg.some(([, r]) => r._meta.grammarTrackLines === true);
9988
10145
  const ruleMap = ruleMapArg.filter(([, val]) => {
9989
10146
  const d = val._def;
9990
10147
  if (d.tag !== "lazy") return true;
@@ -9995,6 +10152,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
9995
10152
  return false;
9996
10153
  }
9997
10154
  });
10155
+ const nodeMeta = new Map(ruleMap.map(([name, rule]) => [name, collectGrammarReflection([[name, rule]], { followLazy: false })]));
9998
10156
  const ctx = {
9999
10157
  vars: 0,
10000
10158
  indent: 1,
@@ -10019,6 +10177,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10019
10177
  capturing: opts?.captureTerminals === true || ruleMap.some(([, rule]) => hasNodeDef(rule)),
10020
10178
  recovery: opts?.recovery ?? false,
10021
10179
  hostMode: grammarHostMode ?? "ast",
10180
+ lineTracking: grammarTrackLines,
10022
10181
  ...opts?.coverage ? { coverage: { plan: opts.coverage } } : {},
10023
10182
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
10024
10183
  ns,
@@ -10069,7 +10228,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10069
10228
  scanExternal(resolved);
10070
10229
  return;
10071
10230
  }
10072
- for (const child of childrenOf2(def)) scanExternal(child);
10231
+ for (const child of childrenOf3(def)) scanExternal(child);
10073
10232
  };
10074
10233
  for (const [, rule] of ruleMap) scanExternal(rule);
10075
10234
  }
@@ -10115,6 +10274,10 @@ function compileLinkable(ruleMapArg, ns, opts) {
10115
10274
  perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
10116
10275
  }
10117
10276
  if (ctx.runtimeParsers.length !== 0) return null;
10277
+ if (ctx.lineTracking) {
10278
+ ctx.needsLineTrack = true;
10279
+ ctx.needsLineSpan = true;
10280
+ }
10118
10281
  const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
10119
10282
  const buildSrcs = ctx.buildFns.length > 0 && ctx.buildSrcs.every((s) => s !== null) ? ctx.buildSrcs : null;
10120
10283
  const ruleFns = /* @__PURE__ */ new Map();
@@ -10128,6 +10291,8 @@ function compileLinkable(ruleMapArg, ns, opts) {
10128
10291
  const mfDecl = ctx.mapFns.length === 0 ? "" : mfSrcs ? `const ${mfRef(ctx)} = [${mfSrcs.join(", ")}]` : `const ${mfRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "mf")}]`;
10129
10292
  const buildDecl = ctx.buildFns.length === 0 ? "" : buildSrcs ? `const ${buildRef(ctx)} = [${buildSrcs.join(", ")}]` : `const ${buildRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "build")}]`;
10130
10293
  const prelude = [
10294
+ ctx.needsLineTrack ? LINE_TRACK_DECL : "",
10295
+ ctx.needsLineSpan ? LINE_SPAN_DECL : "",
10131
10296
  ...ctx.regexDecls,
10132
10297
  ...ctx.expectedDecls,
10133
10298
  mfDecl,
@@ -10136,11 +10301,18 @@ function compileLinkable(ruleMapArg, ns, opts) {
10136
10301
  ].filter(Boolean);
10137
10302
  const wrappers = /* @__PURE__ */ new Map();
10138
10303
  for (const { key, rule, r } of perEntry) {
10304
+ const linePreamble = ctx.lineTracking ? [
10305
+ ` if (_ctx._lineStarts === undefined) _ctx = { ..._ctx, trackLines: true, _lineStarts: [0], _lineScannedTo: 0 }`,
10306
+ ` else _ctx.trackLines = true`,
10307
+ ` if (_pos > (_ctx._lineScannedTo ?? 0)) _trackLines(_ctx, input, 0, _pos)`
10308
+ ] : [];
10309
+ const resultSpan = ctx.lineTracking ? `_spanLines(_ctx, _pos, ${r.endVar})` : `{ start: _pos, end: ${r.endVar} }`;
10139
10310
  wrappers.set(key, publicRuleWrapperSource(rule, [
10140
10311
  `function(input, _pos, _ctx) {`,
10312
+ ...linePreamble,
10141
10313
  ` let pos = _pos`,
10142
10314
  ...r.stmts,
10143
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
10315
+ ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`,
10144
10316
  `}`
10145
10317
  ].join("\n"), ctx.triviaKindLabels));
10146
10318
  }
@@ -10178,6 +10350,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10178
10350
  hostBranchElided: !!ctx.hostBranchElided,
10179
10351
  hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
10180
10352
  isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs),
10353
+ nodeMeta,
10181
10354
  mfFns: mfSrcs ? [] : ctx.mapFns,
10182
10355
  buildFns: buildSrcs ? [] : ctx.buildFns
10183
10356
  };
@@ -10198,7 +10371,7 @@ function hasExternalRuleRef(ruleMap) {
10198
10371
  if (resolved === void 0) return p._ruleName !== void 0;
10199
10372
  return visit(resolved);
10200
10373
  }
10201
- return childrenOf2(def).some(visit);
10374
+ return childrenOf3(def).some(visit);
10202
10375
  };
10203
10376
  return ruleMap.some(([, rule]) => visit(rule));
10204
10377
  }
@@ -10220,7 +10393,7 @@ function hasSemanticReduction(roots, externalRefs) {
10220
10393
  return true;
10221
10394
  }
10222
10395
  }
10223
- return childrenOf2(def).some(visit);
10396
+ return childrenOf3(def).some(visit);
10224
10397
  };
10225
10398
  return roots.some(visit);
10226
10399
  }
@@ -10293,7 +10466,7 @@ function withCtx(extra, combinator) {
10293
10466
  }
10294
10467
 
10295
10468
  // src/compiler/ir-serialize.ts
10296
- function childrenOf3(def) {
10469
+ function childrenOf4(def) {
10297
10470
  switch (def.tag) {
10298
10471
  case "sequence":
10299
10472
  case "choice":
@@ -10357,6 +10530,10 @@ function projectOpt(project) {
10357
10530
  if (project === void 0) return void 0;
10358
10531
  return `project: ${project}`;
10359
10532
  }
10533
+ function tagsOpt(tags) {
10534
+ if (tags === void 0 || tags.length === 0) return void 0;
10535
+ return `tags: ${JSON.stringify(tags)}`;
10536
+ }
10360
10537
  function matcherExpr(entry) {
10361
10538
  switch (entry.kind) {
10362
10539
  case "startsWith":
@@ -10570,7 +10747,7 @@ var Serializer = class {
10570
10747
  if (target) this.assertNoRuleRef(target, seen);
10571
10748
  return;
10572
10749
  }
10573
- for (const child of childrenOf3(c._def)) this.assertNoRuleRef(child, seen);
10750
+ for (const child of childrenOf4(c._def)) this.assertNoRuleRef(child, seen);
10574
10751
  }
10575
10752
  /** A `rules()` map wraps each rule value in a named `lazy` proxy; the real body is
10576
10753
  * its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
@@ -10634,7 +10811,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10634
10811
  this.counts.set(c, (this.counts.get(c) ?? 0) + 1);
10635
10812
  if ((this.counts.get(c) ?? 0) > 1) return;
10636
10813
  active.add(c);
10637
- for (const child of childrenOf3(def)) this.analyze(child, active);
10814
+ for (const child of childrenOf4(def)) this.analyze(child, active);
10638
10815
  active.delete(c);
10639
10816
  }
10640
10817
  /** Reference `c`: a const var if it has one (emitting its decl first), else inline.
@@ -10751,12 +10928,14 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10751
10928
  case "node": {
10752
10929
  if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
10753
10930
  const projectEntry = projectOpt(def.project);
10931
+ const tagEntry = tagsOpt(def.tags);
10754
10932
  const optEntries = [
10755
10933
  ...def.unwrap ? ["unwrap: true"] : [],
10756
10934
  ...def.collapse ? ["collapse: true"] : [],
10757
10935
  ...projectEntry === void 0 ? [] : [projectEntry],
10758
10936
  ...def.captureTrivia ? ["captureTrivia: true"] : [],
10759
- ...def.trailingTrivia ? ["trailingTrivia: true"] : []
10937
+ ...def.trailingTrivia ? ["trailingTrivia: true"] : [],
10938
+ ...tagEntry === void 0 ? [] : [tagEntry]
10760
10939
  ];
10761
10940
  const opts = optEntries.length > 0 ? `, { ${optEntries.join(", ")} }` : "";
10762
10941
  if (def.type === void 0) {
@@ -10794,15 +10973,16 @@ function normalizeCstCollapse(collapse) {
10794
10973
  }
10795
10974
  return typeof collapse === "function" ? collapse : void 0;
10796
10975
  }
10797
- function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
10798
- return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children2] };
10976
+ function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state, tags) {
10977
+ return tags !== void 0 && tags.length > 0 ? { _tag: "node", type, tags, span: { ...span }, state: state ?? null, children: [...children2] } : { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children2] };
10799
10978
  }
10800
- function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
10979
+ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state, _tags) {
10801
10980
  if (typeof typeOrOptions === "string") {
10802
10981
  return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
10803
10982
  }
10804
10983
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
10805
- const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2);
10984
+ const materializeTags = typeOrOptions?.tags === true;
10985
+ const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2, tags) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2, materializeTags ? tags : void 0);
10806
10986
  host._parsemanCstOutput = true;
10807
10987
  if (collapse) host._parsemanCstCollapse = collapse;
10808
10988
  return host;
@@ -10823,7 +11003,7 @@ function pickPieces(pieces, names) {
10823
11003
  };
10824
11004
  for (const n of names) visit(n);
10825
11005
  const filt = (m, pc) => new Map([...m].filter(([k]) => keep.has(k) && winner.get(k) === pc));
10826
- return pieces.map((pc) => ({ ...pc, keys: pc.keys.filter((k) => keep.has(k) && winner.get(k) === pc), ruleFns: filt(pc.ruleFns, pc), wrappers: filt(pc.wrappers, pc), deps: filt(pc.deps, pc) })).filter((pc) => pc.keys.length > 0);
11006
+ return pieces.map((pc) => ({ ...pc, keys: pc.keys.filter((k) => keep.has(k) && winner.get(k) === pc), ruleFns: filt(pc.ruleFns, pc), wrappers: filt(pc.wrappers, pc), deps: filt(pc.deps, pc), nodeMeta: filt(pc.nodeMeta, pc) })).filter((pc) => pc.keys.length > 0);
10827
11007
  }
10828
11008
  function fusedBody(pieces) {
10829
11009
  for (const p of pieces) {
@@ -10861,6 +11041,7 @@ function fusedBody(pieces) {
10861
11041
  ...[...winner].map(([k, p]) => p.ruleFns.get(k))
10862
11042
  ];
10863
11043
  const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
11044
+ const reflection = mergeGrammarReflections([...winner].map(([k, p]) => p.nodeMeta.get(k) ?? { nodes: [] }));
10864
11045
  const { mode, elided } = hostModeOfPieces(pieces);
10865
11046
  const rawBody = [
10866
11047
  ...lines,
@@ -10869,6 +11050,7 @@ function fusedBody(pieces) {
10869
11050
  "}",
10870
11051
  `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_MODE.description)}), { value: ${JSON.stringify(mode)}, enumerable: false })`,
10871
11052
  `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_ELIDED.description)}), { value: ${JSON.stringify(elided)}, enumerable: false })`,
11053
+ `Object.defineProperty(_map, Symbol.for('parseman.grammarReflection'), { value: ${grammarReflectionSource(reflection)}, enumerable: false })`,
10872
11054
  // …and on each rule FUNCTION, because `run(map.Rule, …)` is handed the rule, not the
10873
11055
  // map, and would otherwise have nothing to check against. Done once at fuse time,
10874
11056
  // before any rule has been called, so it costs nothing per parse.
@@ -10974,7 +11156,8 @@ function materializePiece(p, trivia2, captureTerminals = false, hostMode) {
10974
11156
  const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, {
10975
11157
  ...trivia2 ? { trivia: trivia2 } : {},
10976
11158
  ...captureTerminals ? { captureTerminals: true } : {},
10977
- ...hostMode ? { hostMode } : {}
11159
+ ...hostMode ? { hostMode } : {},
11160
+ ...p.trackLines ? { trackLines: true } : {}
10978
11161
  });
10979
11162
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
10980
11163
  return pieces;
@@ -11158,19 +11341,19 @@ function directBuilderUnsupportedBindings(src) {
11158
11341
  // src/plugin/evaluator.ts
11159
11342
  function stripTsFromSource(node2, code) {
11160
11343
  const cuts = [];
11161
- const walk2 = (n) => {
11344
+ const walk = (n) => {
11162
11345
  if (!n || typeof n !== "object") return;
11163
11346
  const rec = n;
11164
11347
  if (typeof rec.type === "string" && rec.type.startsWith("TS") && typeof rec.start === "number" && typeof rec.end === "number") {
11165
11348
  const ex = rec.expression;
11166
11349
  if ((rec.type === "TSAsExpression" || rec.type === "TSSatisfiesExpression" || rec.type === "TSNonNullExpression" || rec.type === "TSInstantiationExpression") && ex && typeof ex.end === "number") {
11167
11350
  cuts.push([ex.end, rec.end]);
11168
- walk2(rec.expression);
11351
+ walk(rec.expression);
11169
11352
  return;
11170
11353
  }
11171
11354
  if (rec.type === "TSTypeAssertion" && ex && typeof ex.start === "number") {
11172
11355
  cuts.push([rec.start, ex.start]);
11173
- walk2(rec.expression);
11356
+ walk(rec.expression);
11174
11357
  return;
11175
11358
  }
11176
11359
  cuts.push([rec.start, rec.end]);
@@ -11180,11 +11363,11 @@ function stripTsFromSource(node2, code) {
11180
11363
  if (key === "type" || key === "start" || key === "end") continue;
11181
11364
  const v2 = rec[key];
11182
11365
  if (Array.isArray(v2)) {
11183
- for (const item of v2) walk2(item);
11184
- } else if (v2 && typeof v2 === "object") walk2(v2);
11366
+ for (const item of v2) walk(item);
11367
+ } else if (v2 && typeof v2 === "object") walk(v2);
11185
11368
  }
11186
11369
  };
11187
- walk2(node2);
11370
+ walk(node2);
11188
11371
  const start = node2.start;
11189
11372
  const end = node2.end;
11190
11373
  if (cuts.length === 0) return code.slice(start, end);
@@ -11363,10 +11546,38 @@ function scopeGet(scope, name, mfs) {
11363
11546
  if (isCombinator(entry)) return entry;
11364
11547
  return null;
11365
11548
  }
11549
+ function unwrapStaticExpr(expr) {
11550
+ let cur = expr;
11551
+ while (cur.type === "TSAsExpression" || cur.type === "TSSatisfiesExpression" || cur.type === "TSNonNullExpression" || cur.type === "TSTypeAssertion" || cur.type === "TSInstantiationExpression" || cur.type === "ParenthesizedExpression") {
11552
+ if (!cur.expression) break;
11553
+ cur = cur.expression;
11554
+ }
11555
+ return cur;
11556
+ }
11366
11557
  function staticLiteralValue(expr) {
11367
- const val = expr;
11558
+ const val = unwrapStaticExpr(expr);
11368
11559
  return val.type === "Literal" || val.type === "BooleanLiteral" || val.type === "NumericLiteral" ? val.value : void 0;
11369
11560
  }
11561
+ function staticStringArray(expr, scope) {
11562
+ const id = unwrapStaticExpr(expr);
11563
+ if (id.type === "Identifier" && scope !== void 0 && id.name !== void 0) {
11564
+ const scoped = scope.get(id.name);
11565
+ const value = isStaticValueEntry(scoped) ? scoped.value : scoped;
11566
+ return Array.isArray(value) && value.every((v2) => typeof v2 === "string") ? value : void 0;
11567
+ }
11568
+ const arr = id;
11569
+ if (arr.type !== "ArrayExpression" || !Array.isArray(arr.elements)) return void 0;
11570
+ const out = [];
11571
+ for (const el of arr.elements) {
11572
+ if (!el || el.type === "SpreadElement") return void 0;
11573
+ const value = staticLiteralValue(el);
11574
+ if (typeof value !== "string") return void 0;
11575
+ out.push(value);
11576
+ }
11577
+ return out;
11578
+ }
11579
+ var STATIC_NODE_OPTIONS_FAILED = /* @__PURE__ */ Symbol("parseman.staticNodeOptions.failed");
11580
+ var STATIC_NODE_OPTIONS_NOT_OPTIONS = /* @__PURE__ */ Symbol("parseman.staticNodeOptions.notOptions");
11370
11581
  function staticNodeProject(expr) {
11371
11582
  const literalValue = staticLiteralValue(expr);
11372
11583
  if (typeof literalValue === "number" && Number.isInteger(literalValue) && literalValue >= 0) {
@@ -11375,23 +11586,60 @@ function staticNodeProject(expr) {
11375
11586
  if (typeof literalValue === "number" || literalValue !== void 0) return { ok: false };
11376
11587
  return { ok: false };
11377
11588
  }
11378
- function staticNodeOptions(expr) {
11379
- if (expr.type !== "ObjectExpression") return void 0;
11589
+ function scopedStaticValue(expr, scope) {
11590
+ const unwrapped = unwrapStaticExpr(expr);
11591
+ if (unwrapped.type !== "Identifier" || unwrapped.name === "undefined") return { found: false };
11592
+ if (!scope.has(unwrapped.name)) return { found: false };
11593
+ const scoped = scope.get(unwrapped.name);
11594
+ const value = isStaticValueEntry(scoped) ? scoped.value : scoped;
11595
+ return { found: true, value };
11596
+ }
11597
+ function staticNodeOptionsFromValue(value) {
11598
+ if (value === null || typeof value !== "object" || Array.isArray(value) || isCombinator(value)) return STATIC_NODE_OPTIONS_NOT_OPTIONS;
11599
+ const opts = {};
11600
+ const rec = value;
11601
+ for (const name of Object.keys(rec)) {
11602
+ const v2 = rec[name];
11603
+ if (name === "unwrap" || name === "collapse" || name === "captureTrivia" || name === "trailingTrivia") {
11604
+ if (v2 === true) opts[name] = true;
11605
+ else if (v2 !== false && v2 !== void 0) return STATIC_NODE_OPTIONS_FAILED;
11606
+ } else if (name === "project") {
11607
+ if (typeof v2 !== "number" || !Number.isInteger(v2) || v2 < 0) return STATIC_NODE_OPTIONS_FAILED;
11608
+ opts.project = v2;
11609
+ } else if (name === "tags") {
11610
+ if (!Array.isArray(v2) || !v2.every((item) => typeof item === "string")) return STATIC_NODE_OPTIONS_FAILED;
11611
+ opts.tags = v2;
11612
+ }
11613
+ }
11614
+ return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 ? opts : void 0;
11615
+ }
11616
+ function staticNodeOptions(expr, scope) {
11617
+ const unwrapped = unwrapStaticExpr(expr);
11618
+ const scoped = scopedStaticValue(unwrapped, scope);
11619
+ if (scoped.found) return staticNodeOptionsFromValue(scoped.value);
11620
+ if (unwrapped.type !== "ObjectExpression") return STATIC_NODE_OPTIONS_NOT_OPTIONS;
11380
11621
  const opts = {};
11381
- for (const prop of expr.properties) {
11622
+ for (const prop of unwrapped.properties) {
11623
+ if (prop.type !== "Property") return STATIC_NODE_OPTIONS_FAILED;
11382
11624
  const p = prop;
11383
- if (p.computed) continue;
11625
+ if (p.computed) return STATIC_NODE_OPTIONS_FAILED;
11384
11626
  const key = p.key;
11385
11627
  const name = key.type === "Identifier" ? key.name : key.type === "Literal" ? String(key.value) : void 0;
11386
11628
  if (name === "unwrap" || name === "collapse" || name === "captureTrivia" || name === "trailingTrivia") {
11387
- if (staticLiteralValue(p.value) === true) opts[name] = true;
11629
+ const value = staticLiteralValue(p.value);
11630
+ if (value === true) opts[name] = true;
11631
+ else if (value !== false && value !== void 0) return STATIC_NODE_OPTIONS_FAILED;
11388
11632
  } else if (name === "project") {
11389
11633
  const project = staticNodeProject(p.value);
11390
- if (project?.ok === false) return { project: -1 };
11634
+ if (project?.ok === false) return STATIC_NODE_OPTIONS_FAILED;
11391
11635
  if (project !== void 0) opts.project = project.value;
11636
+ } else if (name === "tags") {
11637
+ const tags = staticStringArray(p.value, scope);
11638
+ if (tags === void 0) return STATIC_NODE_OPTIONS_FAILED;
11639
+ opts.tags = tags;
11392
11640
  }
11393
11641
  }
11394
- return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia ? opts : void 0;
11642
+ return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 ? opts : void 0;
11395
11643
  }
11396
11644
  function exprToCombi(node2, scope, code, mfs) {
11397
11645
  if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
@@ -11466,10 +11714,23 @@ function exprToCombi(node2, scope, code, mfs) {
11466
11714
  const inner = anyValue(parserArg, scope, code, mfs);
11467
11715
  if (!isCombinator(inner)) return null;
11468
11716
  const be = buildArg;
11469
- const hasBuild = be !== void 0 && be.type !== "SpreadElement" && be.type !== "ObjectExpression" && !(be.type === "Identifier" && be.name === "undefined");
11717
+ const buildExpr = be === void 0 || be.type === "SpreadElement" ? void 0 : unwrapStaticExpr(be);
11718
+ const buildArgOptions = be !== void 0 && be.type !== "SpreadElement" ? staticNodeOptions(buildArg, scope) : STATIC_NODE_OPTIONS_NOT_OPTIONS;
11719
+ if (buildArgOptions === STATIC_NODE_OPTIONS_FAILED) return null;
11720
+ const buildArgIsOptions = buildArgOptions !== STATIC_NODE_OPTIONS_NOT_OPTIONS;
11721
+ const scopedBuild = buildExpr === void 0 ? { found: false } : scopedStaticValue(buildExpr, scope);
11722
+ const absentBuild = buildExpr?.type === "Identifier" && buildExpr.name === "undefined" || buildExpr !== void 0 && staticLiteralValue(buildExpr) === null || scopedBuild.found && (scopedBuild.value === void 0 || scopedBuild.value === null);
11723
+ const hasBuild = be !== void 0 && be.type !== "SpreadElement" && !buildArgIsOptions && !absentBuild;
11470
11724
  const buildSrc = hasBuild ? stripTsFromSource(be, code) : void 0;
11471
- const optionsArg = hasBuild ? optsArg : be?.type === "ObjectExpression" ? buildArg : optsArg;
11472
- const opts = optionsArg !== void 0 && optionsArg.type !== "SpreadElement" ? staticNodeOptions(optionsArg) : void 0;
11725
+ let opts;
11726
+ if (buildArgIsOptions) {
11727
+ opts = buildArgOptions;
11728
+ } else if (optsArg !== void 0) {
11729
+ if (optsArg.type === "SpreadElement") return null;
11730
+ const optsResult = staticNodeOptions(optsArg, scope);
11731
+ if (optsResult === STATIC_NODE_OPTIONS_FAILED || optsResult === STATIC_NODE_OPTIONS_NOT_OPTIONS) return null;
11732
+ opts = optsResult;
11733
+ }
11473
11734
  try {
11474
11735
  const combi = explicitType !== void 0 ? node(explicitType, inner, hasBuild ? () => null : void 0, opts) : node(inner, hasBuild ? () => null : void 0, opts);
11475
11736
  if (combi._def.tag === "node" && buildSrc !== void 0) {
@@ -11692,6 +11953,10 @@ function exprToCombi(node2, scope, code, mfs) {
11692
11953
  }
11693
11954
  }
11694
11955
  function anyValue(node2, scope, code, mfs) {
11956
+ if (node2.type === "TSAsExpression" || node2.type === "TSSatisfiesExpression" || node2.type === "TSNonNullExpression" || node2.type === "TSTypeAssertion" || node2.type === "TSInstantiationExpression" || node2.type === "ParenthesizedExpression") {
11957
+ const inner = node2.expression;
11958
+ return inner ? anyValue(inner, scope, code, mfs) : null;
11959
+ }
11695
11960
  if (node2.type === "Literal") {
11696
11961
  if ("regex" in node2 && node2.regex !== null && node2.regex !== void 0) {
11697
11962
  return new RegExp(node2.regex.pattern, node2.regex.flags);
@@ -11815,6 +12080,9 @@ function collectRuleKeys(retObj) {
11815
12080
  }
11816
12081
  return out;
11817
12082
  }
12083
+ function evaluateStaticValue(node2, scope, code) {
12084
+ return anyValue(node2, scope, code, []);
12085
+ }
11818
12086
  function flattenRuleEntries(retObj, scope, code) {
11819
12087
  const out = [];
11820
12088
  for (const prop of retObj.properties) {
@@ -11980,6 +12248,18 @@ function referencesAny(node2, names, scope) {
11980
12248
  // src/plugin/index.ts
11981
12249
  import { createHash } from "node:crypto";
11982
12250
  var PARSEMAN_MODULE = "parseman";
12251
+ function unwrapStaticExpression(expr) {
12252
+ let cur = expr;
12253
+ while (cur.type === "TSAsExpression" || cur.type === "TSSatisfiesExpression" || cur.type === "TSNonNullExpression" || cur.type === "TSTypeAssertion" || cur.type === "TSInstantiationExpression" || cur.type === "ParenthesizedExpression") {
12254
+ if (!cur.expression) break;
12255
+ cur = cur.expression;
12256
+ }
12257
+ return cur;
12258
+ }
12259
+ function isStaticNullishExpression(expr) {
12260
+ const unwrapped = unwrapStaticExpression(expr);
12261
+ return unwrapped.type === "Identifier" && unwrapped.name === "undefined" || unwrapped.type === "Literal" && unwrapped.value === null;
12262
+ }
11983
12263
  var _compiledResolver = null;
11984
12264
  function getCompiledResolver() {
11985
12265
  return _compiledResolver ??= new ResolverFactory({
@@ -12221,6 +12501,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12221
12501
  const macroImports = [];
12222
12502
  const allNames = /* @__PURE__ */ new Set();
12223
12503
  const importBindings = /* @__PURE__ */ new Map();
12504
+ const ordinaryImports = [];
12224
12505
  for (const stmt of body) {
12225
12506
  if (stmt.type !== "ImportDeclaration") continue;
12226
12507
  const s = stmt;
@@ -12229,12 +12510,17 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12229
12510
  return key === "type" && a.value.value === "macro";
12230
12511
  });
12231
12512
  if (!isMacro) {
12513
+ const specifiers = [];
12232
12514
  for (const spec of s.specifiers) {
12233
12515
  if (spec.type === "ImportSpecifier") {
12234
12516
  const imported = spec.imported.name ?? spec.local.name;
12235
12517
  importBindings.set(spec.local.name, { source: s.source.value, imported });
12518
+ specifiers.push({ type: spec.type, local: spec.local.name, start: spec.start, end: spec.end });
12519
+ } else {
12520
+ specifiers.push({ type: spec.type, local: spec.local.name, start: spec.start, end: spec.end });
12236
12521
  }
12237
12522
  }
12523
+ ordinaryImports.push({ start: s.start, end: s.end, specifiers });
12238
12524
  continue;
12239
12525
  }
12240
12526
  const names = /* @__PURE__ */ new Set();
@@ -12245,6 +12531,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12245
12531
  for (const n of names) allNames.add(n);
12246
12532
  }
12247
12533
  if (macroImports.length === 0) return null;
12534
+ const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
12248
12535
  const factoryDecls = /* @__PURE__ */ new Map();
12249
12536
  for (const stmt of body) {
12250
12537
  const decl = stmt.type === "ExportNamedDeclaration" ? stmt.declaration : stmt;
@@ -12262,6 +12549,101 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12262
12549
  }
12263
12550
  }
12264
12551
  }
12552
+ const topLevelFunction = (moduleBody, name) => {
12553
+ for (const st of moduleBody) {
12554
+ const decl = st.type === "ExportNamedDeclaration" ? st.declaration : st;
12555
+ const idn = decl?.type === "FunctionDeclaration" ? decl.id : void 0;
12556
+ if (idn?.name === name) return decl ?? null;
12557
+ }
12558
+ return null;
12559
+ };
12560
+ const isParsemanMacroImport = (stmt) => {
12561
+ if (stmt.type !== "ImportDeclaration") return false;
12562
+ const s = stmt;
12563
+ return moduleAliases.has(s.source.value) && s.attributes.some((a) => {
12564
+ const key = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
12565
+ return key === "type" && a.value.value === "macro";
12566
+ });
12567
+ };
12568
+ const sourceScopeUntil = (moduleBody, source, until) => {
12569
+ const out = /* @__PURE__ */ new Map();
12570
+ for (const stmt of moduleBody) {
12571
+ if (stmt.type === "ImportDeclaration") {
12572
+ if (!isParsemanMacroImport(stmt)) return null;
12573
+ continue;
12574
+ }
12575
+ if (stmt.type === "FunctionDeclaration") continue;
12576
+ if (stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "FunctionDeclaration") continue;
12577
+ if (stmt.type === "ExportNamedDeclaration" && !stmt.declaration) {
12578
+ if (stmt.source) return null;
12579
+ continue;
12580
+ }
12581
+ const vd = unwrapVd(stmt);
12582
+ if (!vd || vd.kind !== "const") return null;
12583
+ for (const decl of vd.declarations) {
12584
+ const d = decl;
12585
+ const idn = d.id;
12586
+ const init = d.init;
12587
+ if (idn.type !== "Identifier" && idn.type !== "BindingIdentifier" || !idn.name || !init) return null;
12588
+ if (init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression") continue;
12589
+ const mapFnSources = [];
12590
+ const combi = evaluateExpr(init, out, source, mapFnSources);
12591
+ if (combi) {
12592
+ if (stmt.start < until) out.set(idn.name, { combi, mfSrcs: mapFnSources });
12593
+ continue;
12594
+ }
12595
+ const wordFactory = evaluateWordFactory(init, out, source);
12596
+ if (wordFactory) {
12597
+ if (stmt.start < until) {
12598
+ ;
12599
+ out.set(idn.name, wordFactory);
12600
+ }
12601
+ continue;
12602
+ }
12603
+ const whenFactory = evaluateWhenFactory(init, out, source);
12604
+ if (whenFactory) {
12605
+ if (stmt.start < until) {
12606
+ ;
12607
+ out.set(idn.name, whenFactory);
12608
+ }
12609
+ continue;
12610
+ }
12611
+ const combiArray = evaluateCombinatorArray(init, out, source);
12612
+ if (combiArray) {
12613
+ if (stmt.start < until) {
12614
+ ;
12615
+ out.set(idn.name, combiArray);
12616
+ }
12617
+ continue;
12618
+ }
12619
+ return null;
12620
+ }
12621
+ continue;
12622
+ }
12623
+ return out;
12624
+ };
12625
+ const usedImportedFactories = /* @__PURE__ */ new Set();
12626
+ const markUsedImportedFactories = (names) => {
12627
+ for (const name of names ?? []) usedImportedFactories.add(name);
12628
+ };
12629
+ for (const [localName, binding] of importBindings) {
12630
+ const file = resolvePrivateSourceModule(id, binding.source);
12631
+ if (!file) continue;
12632
+ const mod = parseModuleCached(file);
12633
+ if (!mod) continue;
12634
+ const localFor = exportLocalName(mod.body, binding.imported);
12635
+ const init = localFor ? topLevelInit(mod.body, localFor) ?? topLevelFunction(mod.body, localFor) : null;
12636
+ if (!init || init.type !== "ArrowFunctionExpression" && init.type !== "FunctionExpression" && init.type !== "FunctionDeclaration") continue;
12637
+ const factoryScope = sourceScopeUntil(mod.body, mod.src, init.start);
12638
+ if (!factoryScope) continue;
12639
+ factoryDecls.set(localName, {
12640
+ fn: init,
12641
+ declaredAt: -1,
12642
+ code: mod.src,
12643
+ scope: factoryScope,
12644
+ imported: true
12645
+ });
12646
+ }
12265
12647
  const scope = /* @__PURE__ */ new Map();
12266
12648
  const replacements = [];
12267
12649
  const warnings = [];
@@ -12291,6 +12673,9 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12291
12673
  }
12292
12674
  const namedFactory = factoryArgRaw.type === "Identifier" ? factoryDecls.get(factoryArgRaw.name) : void 0;
12293
12675
  const factoryArg = namedFactory !== void 0 && namedFactory.declaredAt <= init.start ? namedFactory.fn : factoryArgRaw;
12676
+ const factoryScope = namedFactory?.scope ?? scope;
12677
+ const factoryCode = namedFactory?.code ?? code;
12678
+ const importedFactory = namedFactory?.imported === true && factoryArg === namedFactory.fn ? factoryArgRaw.name : void 0;
12294
12679
  const optionsArg = optionsFirst ? arg0 : arg1;
12295
12680
  const optionValue = (name) => optionsArg?.type === "ObjectExpression" ? (optionsArg.properties ?? []).find(
12296
12681
  (p) => p.key?.name === name
@@ -12301,7 +12686,13 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12301
12686
  warn(init.start, `${label2}: rules({ hostMode }) must be the literal 'ast' or 'cst'`);
12302
12687
  return null;
12303
12688
  }
12304
- const ruleMap = evaluateParserFactory(factoryArg, scope, code, []);
12689
+ const trackLinesValue = optionValue("trackLines");
12690
+ const gTrackLines = trackLinesValue?.type === "Literal" ? trackLinesValue.value : void 0;
12691
+ if (trackLinesValue !== void 0 && typeof gTrackLines !== "boolean") {
12692
+ warn(init.start, `${label2}: rules({ trackLines }) must be a boolean literal`);
12693
+ return null;
12694
+ }
12695
+ const ruleMap = evaluateParserFactory(factoryArg, factoryScope, factoryCode, []);
12305
12696
  if (!ruleMap) {
12306
12697
  warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
12307
12698
  return null;
@@ -12326,20 +12717,37 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12326
12717
  }
12327
12718
  }
12328
12719
  }
12329
- return { ruleMap, ...gTrivia ? { trivia: gTrivia } : {}, ...gScanSkip ? { scanSkip: gScanSkip } : {} };
12720
+ if (gTrackLines === true) {
12721
+ for (const rule of ruleMap.values()) {
12722
+ if (rule && !rule._meta.isTrivia) {
12723
+ ;
12724
+ rule._meta.grammarTrackLines = true;
12725
+ }
12726
+ }
12727
+ }
12728
+ return {
12729
+ ruleMap,
12730
+ ...gTrivia ? { trivia: gTrivia } : {},
12731
+ ...gScanSkip ? { scanSkip: gScanSkip } : {},
12732
+ ...gTrackLines === true ? { trackLines: true } : {},
12733
+ ...importedFactory ? { importedFactory } : {}
12734
+ };
12330
12735
  };
12331
12736
  const compileRulesFactory = (init, label2) => {
12332
12737
  const evaluated = evaluateRulesFactory(init, label2);
12333
12738
  if (!evaluated) return null;
12334
- const compiled = compileRuleMap([...evaluated.ruleMap], { ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}, recovery, coverage: grammarCoverage });
12739
+ const compiled = compileRuleMap([...evaluated.ruleMap], { ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}, ...evaluated.trackLines ? { trackLines: true } : {}, recovery, coverage: grammarCoverage });
12335
12740
  return {
12336
12741
  replacement: compiled?.replacement ?? null,
12337
12742
  ruleMap: evaluated.ruleMap,
12338
12743
  hostMode: compiled?.hostMode ?? "ast",
12339
12744
  hostBranchElided: compiled?.hostBranchElided ?? false,
12745
+ reflection: compiled?.reflection ?? { nodes: [] },
12340
12746
  ...compiled?.coverageDefinitions ? { coverageDefinitions: compiled.coverageDefinitions } : {},
12341
12747
  ...evaluated.trivia ? { trivia: evaluated.trivia } : {},
12342
- ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}
12748
+ ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {},
12749
+ ...evaluated.trackLines ? { trackLines: true } : {},
12750
+ ...evaluated.importedFactory ? { importedFactory: evaluated.importedFactory } : {}
12343
12751
  };
12344
12752
  };
12345
12753
  const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
@@ -12355,15 +12763,16 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12355
12763
  const mapLit = (m, stripVals = false) => `new Map([${[...m].map(
12356
12764
  ([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(stripVals && typeof v2 === "string" ? stripIndent(v2) : v2)}]`
12357
12765
  ).join(", ")}])`;
12358
- return `{ v: ${JSON.stringify(p.v)}, ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude.map(stripIndent))}, ruleFns: ${mapLit(p.ruleFns, true)}, wrappers: ${mapLit(p.wrappers, true)}, firstSets: ${mapLit(p.firstSets)}, nullable: ${p.nullable ? mapLit(p.nullable) : "new Map()"}, firstSetRecipes: ${p.firstSetRecipes ? mapLit(p.firstSetRecipes) : "new Map()"}, deps: ${mapLit(p.deps)}, hostMode: ${JSON.stringify(p.hostMode ?? "ast")}, hostBranchElided: ${p.hostBranchElided === true}, needsEmptyTl: ${p.needsEmptyTl}, needsHostReads: ${p.needsHostReads}, hasDirectBuilders: ${p.hasDirectBuilders === true}, isRecognitionOnly: ${p.isRecognitionOnly === true}, mfFns: [], buildFns: [] }`;
12766
+ return `{ v: ${JSON.stringify(p.v)}, ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude.map(stripIndent))}, ruleFns: ${mapLit(p.ruleFns, true)}, wrappers: ${mapLit(p.wrappers, true)}, firstSets: ${mapLit(p.firstSets)}, nullable: ${p.nullable ? mapLit(p.nullable) : "new Map()"}, firstSetRecipes: ${p.firstSetRecipes ? mapLit(p.firstSetRecipes) : "new Map()"}, deps: ${mapLit(p.deps)}, nodeMeta: ${mapLit(p.nodeMeta)}, hostMode: ${JSON.stringify(p.hostMode ?? "ast")}, hostBranchElided: ${p.hostBranchElided === true}, needsEmptyTl: ${p.needsEmptyTl}, needsHostReads: ${p.needsHostReads}, hasDirectBuilders: ${p.hasDirectBuilders === true}, isRecognitionOnly: ${p.isRecognitionOnly === true}, mfFns: [], buildFns: [] }`;
12359
12767
  };
12360
12768
  const isSpread = (it) => "__spreadLocal" in it;
12361
12769
  const isIR = (it) => "ir" in it && !("ruleFns" in it);
12362
- const serializeItem = (it) => isSpread(it) ? `...(${it.__spreadLocal}[Symbol.for('parseman.composedPieces')] ?? [])` : isIR(it) ? `{ ns: ${JSON.stringify(it.ns)}, ir: ${JSON.stringify(it.ir)} }` : serializePieces(it);
12770
+ const serializeItem = (it) => isSpread(it) ? `...(${it.__spreadLocal}[Symbol.for('parseman.composedPieces')] ?? [])` : isIR(it) ? `{ ns: ${JSON.stringify(it.ns)}, ir: ${JSON.stringify(it.ir)}${it.trackLines ? ", trackLines: true" : ""} }` : serializePieces(it);
12363
12771
  const serializeList = (list) => `[${list.map(serializeItem).join(", ")}]`;
12364
12772
  const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
12365
12773
  const withLeafMarker = (grammarExpr) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.leafComposed'), { value: true, enumerable: false })`;
12366
12774
  const withHostMode = (grammarExpr, mode, elided) => `/* @__PURE__ */ (m => { for (const k of Object.keys(m)) { Object.defineProperty(m[k], Symbol.for('parseman.fusedHostMode'), { value: ${JSON.stringify(mode)}, enumerable: false }); Object.defineProperty(m[k], Symbol.for('parseman.fusedHostElided'), { value: ${JSON.stringify(elided)}, enumerable: false }) } Object.defineProperty(m, Symbol.for('parseman.fusedHostMode'), { value: ${JSON.stringify(mode)}, enumerable: false }); Object.defineProperty(m, Symbol.for('parseman.fusedHostElided'), { value: ${JSON.stringify(elided)}, enumerable: false }); return m })(${grammarExpr})`;
12775
+ const withGrammarReflection = (grammarExpr, reflection) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.grammarReflection'), { value: ${grammarReflectionSource(reflection)}, enumerable: false })`;
12367
12776
  const withCoverageDefinitions = (grammarExpr, definitions) => !grammarCoverage ? grammarExpr : `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.grammarCoverageDefinitions'), { value: Object.freeze(${JSON.stringify(definitions)}.map(Object.freeze)), enumerable: false })`;
12368
12777
  const emittedCoverageDefinitions = (source) => {
12369
12778
  const ids = /* @__PURE__ */ new Set();
@@ -12490,10 +12899,11 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12490
12899
  const localCarried = (rm, label2, composing, scanSkip) => {
12491
12900
  const entries = [...rm];
12492
12901
  const ns = nsFor(label2);
12902
+ const trackLines = entries.some(([, rule]) => rule?._meta?.grammarTrackLines === true);
12493
12903
  const ir = serializeRuleMap(entries, scanSkip);
12494
- if (ir) return { carried: [{ ns, ir }] };
12904
+ if (ir) return { carried: [{ ns, ir, ...trackLines ? { trackLines: true } : {} }] };
12495
12905
  warn(0, `${label2}: rule map could not be serialized to IR; carrying FULL pieces instead. The artifact is correct but larger, and a downstream compose cannot re-lower it. Re-run with PARSEMAN_IR_DEBUG=1 to print the exact combinator that blocked serialization.`);
12496
- const p = compileLinkable(entries, ns, { ...composing ? { trivia: composing } : {}, ...scanSkip ? { scanSkip } : {}, recovery });
12906
+ const p = compileLinkable(entries, ns, { ...composing ? { trivia: composing } : {}, ...scanSkip ? { scanSkip } : {}, ...trackLines ? { trackLines: true } : {}, recovery });
12497
12907
  return p ? { carried: [p] } : null;
12498
12908
  };
12499
12909
  const argPieces = (arg, label2, composing) => {
@@ -12511,7 +12921,10 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12511
12921
  const innerArg = argPieces(inner, `${label2}_pick`);
12512
12922
  if (!innerArg) return null;
12513
12923
  try {
12514
- return { carried: pickPieces(materializeCarried(innerArg.carried, ownTrivia(inner)), names) };
12924
+ return {
12925
+ carried: pickPieces(materializeCarried(innerArg.carried, ownTrivia(inner)), names),
12926
+ ...innerArg.importedFactories ? { importedFactories: innerArg.importedFactories } : {}
12927
+ };
12515
12928
  } catch (e) {
12516
12929
  warn(arg.start, `pick(): ${e.message}`);
12517
12930
  return null;
@@ -12519,7 +12932,8 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12519
12932
  }
12520
12933
  if (isRulesCall(arg)) {
12521
12934
  const evaluated = evaluateRulesFactory(arg, label2);
12522
- return evaluated ? localCarried(evaluated.ruleMap, label2, composing, evaluated.scanSkip) : null;
12935
+ const carried = evaluated ? localCarried(evaluated.ruleMap, label2, composing, evaluated.scanSkip) : null;
12936
+ return carried ? { ...carried, ...evaluated?.importedFactory ? { importedFactories: [evaluated.importedFactory] } : {} } : null;
12523
12937
  }
12524
12938
  if (arg.type === "Identifier") {
12525
12939
  const name = arg.name;
@@ -12591,6 +13005,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12591
13005
  }
12592
13006
  const composing = composingTrivia(elements);
12593
13007
  const carried = [];
13008
+ const importedFactories = [];
12594
13009
  for (let i = 0; i < elements.length; i++) {
12595
13010
  const r = argPieces(elements[i], `compose${init.start}_${i}`, composing);
12596
13011
  if (!r) {
@@ -12598,6 +13013,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12598
13013
  return null;
12599
13014
  }
12600
13015
  carried.push(...r.carried);
13016
+ importedFactories.push(...r.importedFactories ?? []);
12601
13017
  }
12602
13018
  const detailed = once(() => carriedRuleMapsDetailed(carried));
12603
13019
  runFusedGatingDiagnostic(
@@ -12607,7 +13023,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12607
13023
  );
12608
13024
  const pieces = materializeCarried(carried, composing, false, cHostMode);
12609
13025
  try {
12610
- return { replacement: emitFusedSource(pieces), carried, ...composing ? { trivia: composing } : {} };
13026
+ return {
13027
+ replacement: emitFusedSource(pieces),
13028
+ carried,
13029
+ ...composing ? { trivia: composing } : {},
13030
+ ...importedFactories.length ? { importedFactories } : {}
13031
+ };
12611
13032
  } catch (e) {
12612
13033
  warn(init.start, `compose(): ${e.message}; falling back to runtime`);
12613
13034
  return null;
@@ -12628,10 +13049,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12628
13049
  const localArg = elements[elements.length - 1];
12629
13050
  let localRules = null;
12630
13051
  let localScanSkip;
13052
+ const importedFactories = [];
12631
13053
  if (isRulesCall(localArg)) {
12632
13054
  const evaluated = evaluateRulesFactory(localArg, `composeLeaf${init.start}`);
12633
13055
  localRules = evaluated?.ruleMap ?? null;
12634
13056
  localScanSkip = evaluated?.scanSkip;
13057
+ if (evaluated?.importedFactory) importedFactories.push(evaluated.importedFactory);
12635
13058
  } else if (localArg.type === "Identifier") {
12636
13059
  const name = localArg.name;
12637
13060
  localRules = localRuleMaps.get(name) ?? null;
@@ -12650,6 +13073,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12650
13073
  return null;
12651
13074
  }
12652
13075
  carried.push(...r.carried);
13076
+ importedFactories.push(...r.importedFactories ?? []);
12653
13077
  }
12654
13078
  try {
12655
13079
  const localNs = nsFor(`composeLeaf${init.start}`);
@@ -12675,13 +13099,15 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12675
13099
  return null;
12676
13100
  }
12677
13101
  const replacement = withLeafMarker(emitFusedSource(grammarCoverage ? recognitionPieces : [...recognitionPieces, plainLocalPiece]));
12678
- return { replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)) };
13102
+ return {
13103
+ replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)),
13104
+ ...importedFactories.length ? { importedFactories } : {}
13105
+ };
12679
13106
  } catch (e) {
12680
13107
  warn(init.start, `composeLeaf(): ${e.message}`);
12681
13108
  return null;
12682
13109
  }
12683
13110
  };
12684
- const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
12685
13111
  const refNames = /* @__PURE__ */ new Set();
12686
13112
  for (const stmt of body) {
12687
13113
  const innerVd = unwrapVd(stmt);
@@ -12742,7 +13168,14 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12742
13168
  const kind = vd.kind ?? "const";
12743
13169
  if (d.id.type === "Identifier") {
12744
13170
  const varName = d.id.name;
12745
- if (!referencesAny(init, allNames, scope)) continue;
13171
+ if (!referencesAny(init, allNames, scope)) {
13172
+ const staticValue = evaluateStaticValue(init, scope, code);
13173
+ if (staticValue !== null && staticValue !== void 0 || isStaticNullishExpression(init)) {
13174
+ ;
13175
+ scope.set(varName, staticValue);
13176
+ }
13177
+ continue;
13178
+ }
12746
13179
  if (refNames.has(varName)) {
12747
13180
  const refEntry = scope.get(varName);
12748
13181
  const refCombi = refEntry?.combi ?? null;
@@ -12765,6 +13198,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12765
13198
  const ns = nsFor(varName);
12766
13199
  const pieces = exportPrefix || compiledRules.replacement === null ? compileLinkable([...compiledRules.ruleMap], ns, {
12767
13200
  ...compiledRules.scanSkip ? { scanSkip: compiledRules.scanSkip } : {},
13201
+ ...compiledRules.trackLines ? { trackLines: true } : {},
12768
13202
  recovery
12769
13203
  }) : null;
12770
13204
  if (compiledRules.replacement === null) {
@@ -12781,12 +13215,16 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12781
13215
  );
12782
13216
  if (compiledRules.replacement !== null) {
12783
13217
  replacement = withHostMode(replacement, compiledRules.hostMode, compiledRules.hostBranchElided);
13218
+ replacement = withGrammarReflection(replacement, compiledRules.reflection);
12784
13219
  }
12785
13220
  if (exportPrefix && pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
12786
13221
  const ir = serializeRuleMap([...compiledRules.ruleMap], compiledRules.scanSkip);
12787
- replacement = withCarriedPieces(replacement, [ir ? { ns, ir } : pieces]);
13222
+ replacement = withCarriedPieces(replacement, [ir ? { ns, ir, ...compiledRules.trackLines ? { trackLines: true } : {} } : pieces]);
12788
13223
  }
12789
13224
  replacements.push({ start: init.start, end: init.end, replacement });
13225
+ if (compiledRules.replacement !== null) {
13226
+ markUsedImportedFactories(compiledRules.importedFactory ? [compiledRules.importedFactory] : void 0);
13227
+ }
12790
13228
  continue;
12791
13229
  }
12792
13230
  if (isComposeCall(init)) {
@@ -12799,6 +13237,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12799
13237
  if (fused.trivia) localComposedTrivia.set(varName, fused.trivia);
12800
13238
  const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
12801
13239
  replacements.push({ start: init.start, end: init.end, replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)) });
13240
+ markUsedImportedFactories(fused.importedFactories);
12802
13241
  continue;
12803
13242
  }
12804
13243
  if (isComposeLeafCall(init)) {
@@ -12807,6 +13246,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12807
13246
  throw new Error(`${id}:${lineOf(init.start)} \u2014 composeLeaf() must macro-fuse; runtime composition is forbidden`);
12808
13247
  }
12809
13248
  replacements.push({ start: init.start, end: init.end, replacement: fused.replacement });
13249
+ markUsedImportedFactories(fused.importedFactories);
12810
13250
  continue;
12811
13251
  }
12812
13252
  const mapFnSources = [];
@@ -12832,6 +13272,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12832
13272
  scope.set(varName, combiArray);
12833
13273
  continue;
12834
13274
  }
13275
+ const staticValue = evaluateStaticValue(init, scope, code);
13276
+ if (staticValue !== null && staticValue !== void 0 || isStaticNullishExpression(init)) {
13277
+ ;
13278
+ scope.set(varName, staticValue);
13279
+ continue;
13280
+ }
12835
13281
  if (factoryDecls.has(varName)) continue;
12836
13282
  warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
12837
13283
  continue;
@@ -12895,6 +13341,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12895
13341
  end: stmtEnd,
12896
13342
  replacement: lines.join("\n")
12897
13343
  });
13344
+ markUsedImportedFactories(compiledRules.importedFactory ? [compiledRules.importedFactory] : void 0);
12898
13345
  }
12899
13346
  }
12900
13347
  }
@@ -12903,6 +13350,40 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12903
13350
  imp.fullyResolved = !anyUnresolved && !keepMacroImport;
12904
13351
  }
12905
13352
  const ms = new MagicString(code);
13353
+ const replacementRanges = runtimeComposeFallback ? [] : replacements.map(({ start, end }) => ({ start, end }));
13354
+ const isInsideReplacement = (start, end) => replacementRanges.some((r) => start >= r.start && end <= r.end);
13355
+ const importedBindingStillReferenced = (local) => {
13356
+ const walk = (node2, parent, parentKey) => {
13357
+ if (!node2 || typeof node2 !== "object") return false;
13358
+ const rec = node2;
13359
+ if (rec.type === "ImportDeclaration") return false;
13360
+ if (typeof rec.start === "number" && typeof rec.end === "number" && isInsideReplacement(rec.start, rec.end)) return false;
13361
+ if ((rec.type === "Identifier" || rec.type === "BindingIdentifier") && rec.name === local) {
13362
+ if (parentKey === "key" && (parent?.type === "ObjectProperty" || parent?.type === "Property" || parent?.type === "PropertyDefinition") && parent.computed !== true) return false;
13363
+ if (parentKey === "property" && parent?.type === "StaticMemberExpression") return false;
13364
+ if (parentKey === "property" && parent?.type === "MemberExpression" && parent.computed !== true) return false;
13365
+ return true;
13366
+ }
13367
+ for (const key of Object.keys(rec)) {
13368
+ if (key === "type" || key === "start" || key === "end") continue;
13369
+ const value = rec[key];
13370
+ if (Array.isArray(value)) {
13371
+ if (value.some((child) => walk(child, rec, key))) return true;
13372
+ } else if (walk(value, rec, key)) {
13373
+ return true;
13374
+ }
13375
+ }
13376
+ return false;
13377
+ };
13378
+ return body.some((stmt) => walk(stmt));
13379
+ };
13380
+ for (const imp of ordinaryImports) {
13381
+ if (!runtimeComposeFallback && imp.specifiers.length > 0 && imp.specifiers.every(
13382
+ (spec) => spec.type === "ImportSpecifier" && usedImportedFactories.has(spec.local) && !importedBindingStillReferenced(spec.local)
13383
+ )) {
13384
+ ms.remove(imp.start, imp.end);
13385
+ }
13386
+ }
12906
13387
  if (!anyUnresolved) {
12907
13388
  for (const { start, end } of defineRemovals) ms.remove(start, end);
12908
13389
  }