parseman 0.42.1 → 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 +17 -2
  4. package/dist/combinators/parser.d.ts.map +1 -1
  5. package/dist/compiler/codegen.d.ts +4 -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 +6 -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 +227 -84
  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 +227 -84
  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 +323 -78
  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 +323 -78
  34. package/dist/plugin/index.js.map +4 -4
  35. package/dist/spec/index.cjs +107 -6
  36. package/dist/spec/index.cjs.map +4 -4
  37. package/dist/spec/index.js +107 -6
  38. package/dist/spec/index.js.map +4 -4
  39. package/dist/types.d.ts +2 -1
  40. package/dist/types.d.ts.map +1 -1
  41. package/dist/version.d.ts +1 -1
  42. package/package.json +1 -1
@@ -2845,6 +2845,123 @@ function noTrivia(root) {
2845
2845
  return parser({ trivia: null }, root);
2846
2846
  }
2847
2847
 
2848
+ // src/cst/reflection.ts
2849
+ var GRAMMAR_REFLECTION = /* @__PURE__ */ Symbol.for("parseman.grammarReflection");
2850
+ function childrenOf(def) {
2851
+ switch (def.tag) {
2852
+ case "sequence":
2853
+ case "choice":
2854
+ return def.parsers;
2855
+ case "dispatch":
2856
+ return [
2857
+ def.selector,
2858
+ ...def.cases.map((c) => c.parser),
2859
+ ...def.matchers ? def.matchers.map((c) => c.parser) : [],
2860
+ ...def.otherwise ? [def.otherwise] : []
2861
+ ];
2862
+ case "many":
2863
+ case "oneOrMore":
2864
+ case "optional":
2865
+ case "attempt":
2866
+ case "transform":
2867
+ case "trivia":
2868
+ case "token":
2869
+ case "leaf":
2870
+ case "label":
2871
+ case "field":
2872
+ case "not":
2873
+ case "peek":
2874
+ case "node":
2875
+ case "withCtx":
2876
+ case "recover":
2877
+ case "expect":
2878
+ return [def.parser];
2879
+ case "grammar":
2880
+ return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
2881
+ case "sepBy":
2882
+ return [def.parser, def.separator];
2883
+ case "skip":
2884
+ return [def.main, def.skipped];
2885
+ case "scanTo":
2886
+ return [def.sentinel, ...def.skip];
2887
+ case "literal":
2888
+ case "regex":
2889
+ case "keywords":
2890
+ case "routed":
2891
+ case "guard":
2892
+ case "lazy":
2893
+ case "unknown":
2894
+ return [];
2895
+ }
2896
+ }
2897
+ function addNode(out, def) {
2898
+ if (def.type === void 0) return;
2899
+ const existing = out.get(def.type);
2900
+ if (existing === void 0) {
2901
+ out.set(def.type, def.tags === void 0 ? [] : [...def.tags]);
2902
+ return;
2903
+ }
2904
+ if (def.tags === void 0) return;
2905
+ for (const tag of def.tags) {
2906
+ if (!existing.includes(tag)) existing.push(tag);
2907
+ }
2908
+ }
2909
+ function collectFromCombinator(parser2, out, seen, followLazy = true) {
2910
+ if (seen.has(parser2)) return;
2911
+ seen.add(parser2);
2912
+ const def = parser2._def;
2913
+ if (def.tag === "lazy") {
2914
+ if (followLazy) {
2915
+ try {
2916
+ collectFromCombinator(def.thunk(), out, seen, followLazy);
2917
+ } catch {
2918
+ }
2919
+ }
2920
+ return;
2921
+ }
2922
+ if (def.tag === "node") addNode(out, def);
2923
+ for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy);
2924
+ }
2925
+ function collectGrammarReflection(ruleMap, opts) {
2926
+ const byType = /* @__PURE__ */ new Map();
2927
+ const followLazy = opts?.followLazy !== false;
2928
+ for (const [, rule] of ruleMap) {
2929
+ if (!followLazy && rule._def.tag === "lazy") {
2930
+ try {
2931
+ const resolved = rule._def.thunk();
2932
+ if (resolved._def.tag !== "lazy") collectFromCombinator(resolved, byType, /* @__PURE__ */ new Set(), false);
2933
+ } catch {
2934
+ }
2935
+ continue;
2936
+ }
2937
+ collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
2938
+ }
2939
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
2940
+ }
2941
+ function mergeGrammarReflections(reflections) {
2942
+ const byType = /* @__PURE__ */ new Map();
2943
+ for (const reflection of reflections) {
2944
+ for (const node2 of reflection.nodes) {
2945
+ const tags = byType.get(node2.type);
2946
+ if (tags === void 0) {
2947
+ byType.set(node2.type, [...node2.tags]);
2948
+ continue;
2949
+ }
2950
+ for (const tag of node2.tags) {
2951
+ if (!tags.includes(tag)) tags.push(tag);
2952
+ }
2953
+ }
2954
+ }
2955
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
2956
+ }
2957
+ function attachGrammarReflection(grammar, reflection) {
2958
+ Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true });
2959
+ return grammar;
2960
+ }
2961
+ function grammarReflectionSource(reflection) {
2962
+ return `{ nodes: ${JSON.stringify(reflection.nodes)} }`;
2963
+ }
2964
+
2848
2965
  // src/combinators/parser.ts
2849
2966
  var RULE_ORDER = "__parsemanRuleOrder";
2850
2967
  function tagRule(r, key) {
@@ -2943,6 +3060,7 @@ function rules(a, b) {
2943
3060
  enumerable: false,
2944
3061
  configurable: true
2945
3062
  });
3063
+ attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
2946
3064
  return cache;
2947
3065
  }
2948
3066
 
@@ -3263,13 +3381,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3263
3381
  const collapse = opts?.collapse === true;
3264
3382
  const captureTrivia = opts?.captureTrivia === true;
3265
3383
  const trailingTrivia = opts?.trailingTrivia === true;
3384
+ const tags = opts?.tags;
3266
3385
  if (unwrap && collapse) {
3267
3386
  throw new Error("node() options cannot set both unwrap and collapse");
3268
3387
  }
3269
3388
  if (project !== void 0 && (unwrap || collapse)) {
3270
3389
  throw new Error("node() options cannot combine project with unwrap or collapse");
3271
3390
  }
3272
- 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;
3391
+ 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;
3273
3392
  const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
3274
3393
  const clonesState = build ? buildReadsState(def) : project === void 0;
3275
3394
  const hasOwnFields = parserHasOwnFields(combinator);
@@ -3315,7 +3434,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3315
3434
  const cstOutput = cstOutputHost(ctx.build);
3316
3435
  const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3317
3436
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3318
- 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 };
3437
+ 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 };
3319
3438
  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 };
3320
3439
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3321
3440
  pushCstChild(ctx, built, rawEntry);
@@ -3487,7 +3606,7 @@ function leaf(root, fn) {
3487
3606
  }
3488
3607
 
3489
3608
  // src/version.ts
3490
- var PARSEMAN_VERSION = "0.42.1";
3609
+ var PARSEMAN_VERSION = "0.43.0";
3491
3610
 
3492
3611
  // src/cst/host-mode.ts
3493
3612
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -3553,7 +3672,7 @@ var SUGGESTIONS = {
3553
3672
  function classifyBroadArm(arm, resolve2) {
3554
3673
  const seen = /* @__PURE__ */ new Set();
3555
3674
  const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
3556
- const walk2 = (p) => {
3675
+ const walk = (p) => {
3557
3676
  if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
3558
3677
  seen.add(p);
3559
3678
  const d = p._def;
@@ -3567,7 +3686,7 @@ function classifyBroadArm(arm, resolve2) {
3567
3686
  // peek() CARRIES its body's first-set, so it is only broad when the body is
3568
3687
  // (or the body is nullable) — walk in for the real cause.
3569
3688
  case "peek":
3570
- return walk2(d.parser);
3689
+ return walk(d.parser);
3571
3690
  case "scanTo":
3572
3691
  return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
3573
3692
  // `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
@@ -3581,7 +3700,7 @@ function classifyBroadArm(arm, resolve2) {
3581
3700
  case "attempt":
3582
3701
  case "withCtx":
3583
3702
  case "recover":
3584
- return walk2(d.parser);
3703
+ return walk(d.parser);
3585
3704
  case "lazy": {
3586
3705
  const name = p._ruleName;
3587
3706
  let target;
@@ -3593,13 +3712,13 @@ function classifyBroadArm(arm, resolve2) {
3593
3712
  return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
3594
3713
  target = bound;
3595
3714
  }
3596
- const inner = walk2(target);
3715
+ const inner = walk(target);
3597
3716
  if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
3598
3717
  return inner;
3599
3718
  }
3600
3719
  case "optional":
3601
3720
  case "many": {
3602
- const inner = walk2(d.parser);
3721
+ const inner = walk(d.parser);
3603
3722
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3604
3723
  }
3605
3724
  case "oneOrMore":
@@ -3612,9 +3731,9 @@ function classifyBroadArm(arm, resolve2) {
3612
3731
  case "node":
3613
3732
  case "grammar":
3614
3733
  case "expect":
3615
- return walk2(d.parser);
3734
+ return walk(d.parser);
3616
3735
  case "skip":
3617
- return walk2(d.main);
3736
+ return walk(d.main);
3618
3737
  case "sequence": {
3619
3738
  let sawNullablePrefix = false;
3620
3739
  for (const t of d.parsers) {
@@ -3623,7 +3742,7 @@ function classifyBroadArm(arm, resolve2) {
3623
3742
  continue;
3624
3743
  }
3625
3744
  if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve2))) {
3626
- const inner = walk2(t);
3745
+ const inner = walk(t);
3627
3746
  if (sawNullablePrefix)
3628
3747
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3629
3748
  return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
@@ -3638,18 +3757,18 @@ function classifyBroadArm(arm, resolve2) {
3638
3757
  }
3639
3758
  case "choice": {
3640
3759
  for (const a of d.parsers) {
3641
- const r = walk2(a);
3760
+ const r = walk(a);
3642
3761
  if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
3643
3762
  }
3644
3763
  return null;
3645
3764
  }
3646
3765
  case "dispatch":
3647
- return walk2(d.selector);
3766
+ return walk(d.selector);
3648
3767
  default:
3649
3768
  return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
3650
3769
  }
3651
3770
  };
3652
- return walk2(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3771
+ return walk(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3653
3772
  }
3654
3773
  function peelToLeading(arm) {
3655
3774
  let d = arm._def;
@@ -3851,7 +3970,7 @@ function formatGatingWarnings(report) {
3851
3970
 
3852
3971
  // src/analysis/duplication.ts
3853
3972
  var siteToString = (s) => s.path === "" ? s.rule : `${s.rule} \u203A ${s.path}`;
3854
- function childrenOf(d) {
3973
+ function childrenOf2(d) {
3855
3974
  switch (d.tag) {
3856
3975
  case "sequence":
3857
3976
  case "choice":
@@ -3985,7 +4104,7 @@ function render(p, depth = 3) {
3985
4104
  break;
3986
4105
  }
3987
4106
  if (depth <= 0) return `${d.tag}(\u2026)`;
3988
- const kids = childrenOf(d).map((k) => render(k, depth - 1));
4107
+ const kids = childrenOf2(d).map((k) => render(k, depth - 1));
3989
4108
  switch (d.tag) {
3990
4109
  case "sequence":
3991
4110
  return `sequence(${kids.join(", ")})`;
@@ -4180,7 +4299,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4180
4299
  if (hit !== void 0) return hit;
4181
4300
  const d = p._def;
4182
4301
  shapeCache.set(p, interner.hole);
4183
- const kids = childrenOf(d);
4302
+ const kids = childrenOf2(d);
4184
4303
  const id = interner.of(`${payloadKey(p, d)}|${kids.map(shapeOf).join(",")}`);
4185
4304
  shapeCache.set(p, id);
4186
4305
  return id;
@@ -4190,7 +4309,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4190
4309
  if (hit !== void 0) return hit;
4191
4310
  sizeCache.set(p, 1);
4192
4311
  let n = 1;
4193
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4312
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4194
4313
  sizeCache.set(p, n);
4195
4314
  return n;
4196
4315
  };
@@ -4203,7 +4322,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4203
4322
  visited.set(p, { p, d, site: here, shape: shapeOf(p), size: sizeOf(p), enclosingNode, enclosingReducer });
4204
4323
  const inner = d.tag === "node" ? d.type ?? "(anonymous)" : enclosingNode;
4205
4324
  const innerReducer = enclosingReducer || d.tag === "node" && (d.build !== void 0 || d.buildSrc !== void 0) || d.tag === "transform" || d.tag === "leaf";
4206
- const kids = childrenOf(d);
4325
+ const kids = childrenOf2(d);
4207
4326
  kids.forEach((k, i) => {
4208
4327
  const seg = segment(d, i);
4209
4328
  visit(k, here.rule, here.path === "" ? seg : `${here.path} \u203A ${seg}`, inner, innerReducer);
@@ -4263,7 +4382,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4263
4382
  function markSubtree(p, into) {
4264
4383
  if (into.has(p)) return;
4265
4384
  into.add(p);
4266
- for (const k of childrenOf(p._def)) markSubtree(k, into);
4385
+ for (const k of childrenOf2(p._def)) markSubtree(k, into);
4267
4386
  }
4268
4387
  var stripMembers = (f) => {
4269
4388
  const { _members, ...rest } = f;
@@ -4303,7 +4422,7 @@ function findNearDuplicates(visited, interner, shapeOf, minSize) {
4303
4422
  if (hit !== void 0) return hit;
4304
4423
  holeCache.set(p, []);
4305
4424
  const d = p._def;
4306
- const kids = childrenOf(d);
4425
+ const kids = childrenOf2(d);
4307
4426
  const kidShapes = kids.map(shapeOf);
4308
4427
  const payload = payloadKey(p, d);
4309
4428
  const out2 = [];
@@ -4364,7 +4483,7 @@ function findDivergentSlot(members, shapeOf) {
4364
4483
  let cur = [...members];
4365
4484
  for (let guard2 = 0; guard2 < 64; guard2++) {
4366
4485
  const d0 = cur[0]._def;
4367
- const kidLists = cur.map((c) => childrenOf(c._def));
4486
+ const kidLists = cur.map((c) => childrenOf2(c._def));
4368
4487
  const arity = kidLists[0].length;
4369
4488
  if (kidLists.some((k) => k.length !== arity) || arity === 0) break;
4370
4489
  const diff = [];
@@ -4377,7 +4496,7 @@ function findDivergentSlot(members, shapeOf) {
4377
4496
  path2.push(i);
4378
4497
  defs.push(d0);
4379
4498
  cur = kidLists.map((k) => k[i]);
4380
- const nextArity = childrenOf(cur[0]._def).length;
4499
+ const nextArity = childrenOf2(cur[0]._def).length;
4381
4500
  const samePayload = cur.every((c) => payloadKey(c, c._def) === payloadKey(cur[0], cur[0]._def));
4382
4501
  if (!samePayload || nextArity === 0) break;
4383
4502
  }
@@ -4385,7 +4504,7 @@ function findDivergentSlot(members, shapeOf) {
4385
4504
  let slotSize = 0;
4386
4505
  const sizeOf = (p) => {
4387
4506
  let n = 1;
4388
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4507
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4389
4508
  return n;
4390
4509
  };
4391
4510
  for (const c of cur) slotSize = Math.max(slotSize, sizeOf(c));
@@ -4394,7 +4513,7 @@ function findDivergentSlot(members, shapeOf) {
4394
4513
  function renderWithHole(p, path2, depth = 0) {
4395
4514
  if (depth === path2.length) return "\u2039slot\u203A";
4396
4515
  const d = p._def;
4397
- const kids = childrenOf(d);
4516
+ const kids = childrenOf2(d);
4398
4517
  const idx = path2[depth];
4399
4518
  const parts = kids.map((k, i) => i === idx ? renderWithHole(k, path2, depth + 1) : render(k, 2));
4400
4519
  switch (d.tag) {
@@ -4852,7 +4971,7 @@ function capturesPositionally(p, seen = /* @__PURE__ */ new Set()) {
4852
4971
  seen.add(p);
4853
4972
  const d = p._def;
4854
4973
  if (d.tag === "field" || d.tag === "label") return true;
4855
- for (const k of childrenOf(d)) if (capturesPositionally(k, seen)) return true;
4974
+ for (const k of childrenOf2(d)) if (capturesPositionally(k, seen)) return true;
4856
4975
  return false;
4857
4976
  }
4858
4977
  function detectHandRolledSepBy(terms, shapeOf) {
@@ -4958,7 +5077,7 @@ function nestedNodeTypes(root, out, seen, isRoot) {
4958
5077
  }
4959
5078
  return;
4960
5079
  }
4961
- for (const k of childrenOf(d)) nestedNodeTypes(k, out, seen, false);
5080
+ for (const k of childrenOf2(d)) nestedNodeTypes(k, out, seen, false);
4962
5081
  }
4963
5082
  var MAX_LOST_TYPES = 8;
4964
5083
  function findStructureLoss(visited, resolve2) {
@@ -8153,7 +8272,7 @@ function routedNeedsContextBridge(p, ctx, seen = /* @__PURE__ */ new Set()) {
8153
8272
  const d = p._def;
8154
8273
  if (d.tag === "dispatch") return false;
8155
8274
  if (d.tag === "withCtx") return parserUsesRouted2(d.parser);
8156
- return childrenOf2(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8275
+ return childrenOf3(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8157
8276
  }
8158
8277
  function armReplayInScope(arm, ctx) {
8159
8278
  let p = arm;
@@ -8916,7 +9035,8 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8916
9035
  const nodeSpanV = ctx.lineTracking ? v(ctx, "_nspan") : nodeSpanExpr;
8917
9036
  if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
8918
9037
  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})`;
8919
- const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
9038
+ const hostBuildArgs = `${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV}${def.tags !== void 0 && def.tags.length > 0 ? `, ${JSON.stringify(def.tags)}` : ""}`;
9039
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${hostBuildArgs})`;
8920
9040
  const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
8921
9041
  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;
8922
9042
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
@@ -9379,7 +9499,7 @@ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9379
9499
  return false;
9380
9500
  }
9381
9501
  }
9382
- return childrenOf2(d).some((child) => hasLineTrackingDef(child, seen));
9502
+ return childrenOf3(d).some((child) => hasLineTrackingDef(child, seen));
9383
9503
  }
9384
9504
  function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9385
9505
  if (seen.has(p)) return false;
@@ -9398,7 +9518,7 @@ function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9398
9518
  case "dispatch":
9399
9519
  return false;
9400
9520
  default:
9401
- return childrenOf2(d).some((child) => parserUsesRouted2(child, seen));
9521
+ return childrenOf3(d).some((child) => parserUsesRouted2(child, seen));
9402
9522
  }
9403
9523
  }
9404
9524
  function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
@@ -9413,9 +9533,9 @@ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
9413
9533
  return false;
9414
9534
  }
9415
9535
  }
9416
- return childrenOf2(d).some((child) => hasDirectBuildDef(child, seen));
9536
+ return childrenOf3(d).some((child) => hasDirectBuildDef(child, seen));
9417
9537
  }
9418
- function childrenOf2(def) {
9538
+ function childrenOf3(def) {
9419
9539
  switch (def.tag) {
9420
9540
  case "sequence":
9421
9541
  case "choice":
@@ -9490,7 +9610,7 @@ function subtreeSizes(roots) {
9490
9610
  const def = p._def;
9491
9611
  let s = 1;
9492
9612
  if (def.tag !== "lazy") {
9493
- for (const child of childrenOf2(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
9613
+ for (const child of childrenOf3(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
9494
9614
  }
9495
9615
  sizes.set(p, s);
9496
9616
  return s;
@@ -9511,7 +9631,7 @@ function subtreeSizes(roots) {
9511
9631
  return;
9512
9632
  }
9513
9633
  sz(p);
9514
- for (const child of childrenOf2(def)) visit(child);
9634
+ for (const child of childrenOf3(def)) visit(child);
9515
9635
  };
9516
9636
  for (const root of roots) visit(root);
9517
9637
  return sizes;
@@ -9521,7 +9641,7 @@ function analyzeLazyUsageMulti(roots) {
9521
9641
  const recursive = /* @__PURE__ */ new Set();
9522
9642
  const descended = /* @__PURE__ */ new Set();
9523
9643
  const active = /* @__PURE__ */ new Set();
9524
- function walk2(p) {
9644
+ function walk(p) {
9525
9645
  const def = p._def;
9526
9646
  if (def.tag === "lazy") {
9527
9647
  counts.set(p, (counts.get(p) ?? 0) + 1);
@@ -9538,17 +9658,17 @@ function analyzeLazyUsageMulti(roots) {
9538
9658
  return;
9539
9659
  }
9540
9660
  active.add(p);
9541
- walk2(resolved);
9661
+ walk(resolved);
9542
9662
  active.delete(p);
9543
9663
  return;
9544
9664
  }
9545
9665
  if (isHoistableTag(def.tag)) {
9546
9666
  counts.set(p, (counts.get(p) ?? 0) + 1);
9547
9667
  }
9548
- for (const child of childrenOf2(def)) walk2(child);
9668
+ for (const child of childrenOf3(def)) walk(child);
9549
9669
  }
9550
9670
  const rootsArr = [...roots];
9551
- for (const root of rootsArr) walk2(root);
9671
+ for (const root of rootsArr) walk(root);
9552
9672
  return { counts, recursive, sizes: subtreeSizes(rootsArr) };
9553
9673
  }
9554
9674
  function ruleDependencies(ruleMap) {
@@ -9558,7 +9678,7 @@ function ruleDependencies(ruleMap) {
9558
9678
  for (const [name, comb] of ruleMap) {
9559
9679
  const found = /* @__PURE__ */ new Set();
9560
9680
  const seen = /* @__PURE__ */ new Set();
9561
- const walk2 = (p, isRoot) => {
9681
+ const walk = (p, isRoot) => {
9562
9682
  const def = p._def;
9563
9683
  const boundary = def.tag === "lazy" ? nameOf.get(p) ?? p._ruleName : nameOf.get(p);
9564
9684
  if (!isRoot && boundary !== void 0) {
@@ -9574,12 +9694,12 @@ function ruleDependencies(ruleMap) {
9574
9694
  } catch {
9575
9695
  return;
9576
9696
  }
9577
- walk2(resolved, false);
9697
+ walk(resolved, false);
9578
9698
  return;
9579
9699
  }
9580
- for (const child of childrenOf2(def)) walk2(child, false);
9700
+ for (const child of childrenOf3(def)) walk(child, false);
9581
9701
  };
9582
- walk2(comb, true);
9702
+ walk(comb, true);
9583
9703
  deps.set(name, [...found]);
9584
9704
  }
9585
9705
  return deps;
@@ -10044,6 +10164,7 @@ function compileRuleMap(ruleMap, opts) {
10044
10164
  // host check passes vacuously — see `withHostMode` in the plugin.
10045
10165
  hostMode: ctx.hostMode ?? "ast",
10046
10166
  hostBranchElided: !!ctx.hostBranchElided,
10167
+ reflection: collectGrammarReflection(ruleMap),
10047
10168
  ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions }
10048
10169
  };
10049
10170
  }
@@ -10066,6 +10187,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10066
10187
  return false;
10067
10188
  }
10068
10189
  });
10190
+ const nodeMeta = new Map(ruleMap.map(([name, rule]) => [name, collectGrammarReflection([[name, rule]], { followLazy: false })]));
10069
10191
  const ctx = {
10070
10192
  vars: 0,
10071
10193
  indent: 1,
@@ -10141,7 +10263,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10141
10263
  scanExternal(resolved);
10142
10264
  return;
10143
10265
  }
10144
- for (const child of childrenOf2(def)) scanExternal(child);
10266
+ for (const child of childrenOf3(def)) scanExternal(child);
10145
10267
  };
10146
10268
  for (const [, rule] of ruleMap) scanExternal(rule);
10147
10269
  }
@@ -10263,6 +10385,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10263
10385
  hostBranchElided: !!ctx.hostBranchElided,
10264
10386
  hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
10265
10387
  isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs),
10388
+ nodeMeta,
10266
10389
  mfFns: mfSrcs ? [] : ctx.mapFns,
10267
10390
  buildFns: buildSrcs ? [] : ctx.buildFns
10268
10391
  };
@@ -10283,7 +10406,7 @@ function hasExternalRuleRef(ruleMap) {
10283
10406
  if (resolved === void 0) return p._ruleName !== void 0;
10284
10407
  return visit(resolved);
10285
10408
  }
10286
- return childrenOf2(def).some(visit);
10409
+ return childrenOf3(def).some(visit);
10287
10410
  };
10288
10411
  return ruleMap.some(([, rule]) => visit(rule));
10289
10412
  }
@@ -10305,7 +10428,7 @@ function hasSemanticReduction(roots, externalRefs) {
10305
10428
  return true;
10306
10429
  }
10307
10430
  }
10308
- return childrenOf2(def).some(visit);
10431
+ return childrenOf3(def).some(visit);
10309
10432
  };
10310
10433
  return roots.some(visit);
10311
10434
  }
@@ -10378,7 +10501,7 @@ function withCtx(extra, combinator) {
10378
10501
  }
10379
10502
 
10380
10503
  // src/compiler/ir-serialize.ts
10381
- function childrenOf3(def) {
10504
+ function childrenOf4(def) {
10382
10505
  switch (def.tag) {
10383
10506
  case "sequence":
10384
10507
  case "choice":
@@ -10442,6 +10565,10 @@ function projectOpt(project) {
10442
10565
  if (project === void 0) return void 0;
10443
10566
  return `project: ${project}`;
10444
10567
  }
10568
+ function tagsOpt(tags) {
10569
+ if (tags === void 0 || tags.length === 0) return void 0;
10570
+ return `tags: ${JSON.stringify(tags)}`;
10571
+ }
10445
10572
  function matcherExpr(entry) {
10446
10573
  switch (entry.kind) {
10447
10574
  case "startsWith":
@@ -10655,7 +10782,7 @@ var Serializer = class {
10655
10782
  if (target) this.assertNoRuleRef(target, seen);
10656
10783
  return;
10657
10784
  }
10658
- for (const child of childrenOf3(c._def)) this.assertNoRuleRef(child, seen);
10785
+ for (const child of childrenOf4(c._def)) this.assertNoRuleRef(child, seen);
10659
10786
  }
10660
10787
  /** A `rules()` map wraps each rule value in a named `lazy` proxy; the real body is
10661
10788
  * its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
@@ -10719,7 +10846,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10719
10846
  this.counts.set(c, (this.counts.get(c) ?? 0) + 1);
10720
10847
  if ((this.counts.get(c) ?? 0) > 1) return;
10721
10848
  active.add(c);
10722
- for (const child of childrenOf3(def)) this.analyze(child, active);
10849
+ for (const child of childrenOf4(def)) this.analyze(child, active);
10723
10850
  active.delete(c);
10724
10851
  }
10725
10852
  /** Reference `c`: a const var if it has one (emitting its decl first), else inline.
@@ -10836,12 +10963,14 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10836
10963
  case "node": {
10837
10964
  if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
10838
10965
  const projectEntry = projectOpt(def.project);
10966
+ const tagEntry = tagsOpt(def.tags);
10839
10967
  const optEntries = [
10840
10968
  ...def.unwrap ? ["unwrap: true"] : [],
10841
10969
  ...def.collapse ? ["collapse: true"] : [],
10842
10970
  ...projectEntry === void 0 ? [] : [projectEntry],
10843
10971
  ...def.captureTrivia ? ["captureTrivia: true"] : [],
10844
- ...def.trailingTrivia ? ["trailingTrivia: true"] : []
10972
+ ...def.trailingTrivia ? ["trailingTrivia: true"] : [],
10973
+ ...tagEntry === void 0 ? [] : [tagEntry]
10845
10974
  ];
10846
10975
  const opts = optEntries.length > 0 ? `, { ${optEntries.join(", ")} }` : "";
10847
10976
  if (def.type === void 0) {
@@ -10879,15 +11008,16 @@ function normalizeCstCollapse(collapse) {
10879
11008
  }
10880
11009
  return typeof collapse === "function" ? collapse : void 0;
10881
11010
  }
10882
- function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
10883
- return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children2] };
11011
+ function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state, tags) {
11012
+ 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] };
10884
11013
  }
10885
- function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
11014
+ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state, _tags) {
10886
11015
  if (typeof typeOrOptions === "string") {
10887
11016
  return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
10888
11017
  }
10889
11018
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
10890
- const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2);
11019
+ const materializeTags = typeOrOptions?.tags === true;
11020
+ const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2, tags) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2, materializeTags ? tags : void 0);
10891
11021
  host._parsemanCstOutput = true;
10892
11022
  if (collapse) host._parsemanCstCollapse = collapse;
10893
11023
  return host;
@@ -10908,7 +11038,7 @@ function pickPieces(pieces, names) {
10908
11038
  };
10909
11039
  for (const n of names) visit(n);
10910
11040
  const filt = (m, pc) => new Map([...m].filter(([k]) => keep.has(k) && winner.get(k) === pc));
10911
- 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);
11041
+ 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);
10912
11042
  }
10913
11043
  function fusedBody(pieces) {
10914
11044
  for (const p of pieces) {
@@ -10946,6 +11076,7 @@ function fusedBody(pieces) {
10946
11076
  ...[...winner].map(([k, p]) => p.ruleFns.get(k))
10947
11077
  ];
10948
11078
  const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
11079
+ const reflection = mergeGrammarReflections([...winner].map(([k, p]) => p.nodeMeta.get(k) ?? { nodes: [] }));
10949
11080
  const { mode, elided } = hostModeOfPieces(pieces);
10950
11081
  const rawBody = [
10951
11082
  ...lines,
@@ -10954,6 +11085,7 @@ function fusedBody(pieces) {
10954
11085
  "}",
10955
11086
  `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_MODE.description)}), { value: ${JSON.stringify(mode)}, enumerable: false })`,
10956
11087
  `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_ELIDED.description)}), { value: ${JSON.stringify(elided)}, enumerable: false })`,
11088
+ `Object.defineProperty(_map, Symbol.for('parseman.grammarReflection'), { value: ${grammarReflectionSource(reflection)}, enumerable: false })`,
10957
11089
  // …and on each rule FUNCTION, because `run(map.Rule, …)` is handed the rule, not the
10958
11090
  // map, and would otherwise have nothing to check against. Done once at fuse time,
10959
11091
  // before any rule has been called, so it costs nothing per parse.
@@ -11244,19 +11376,19 @@ function directBuilderUnsupportedBindings(src) {
11244
11376
  // src/plugin/evaluator.ts
11245
11377
  function stripTsFromSource(node2, code) {
11246
11378
  const cuts = [];
11247
- const walk2 = (n) => {
11379
+ const walk = (n) => {
11248
11380
  if (!n || typeof n !== "object") return;
11249
11381
  const rec = n;
11250
11382
  if (typeof rec.type === "string" && rec.type.startsWith("TS") && typeof rec.start === "number" && typeof rec.end === "number") {
11251
11383
  const ex = rec.expression;
11252
11384
  if ((rec.type === "TSAsExpression" || rec.type === "TSSatisfiesExpression" || rec.type === "TSNonNullExpression" || rec.type === "TSInstantiationExpression") && ex && typeof ex.end === "number") {
11253
11385
  cuts.push([ex.end, rec.end]);
11254
- walk2(rec.expression);
11386
+ walk(rec.expression);
11255
11387
  return;
11256
11388
  }
11257
11389
  if (rec.type === "TSTypeAssertion" && ex && typeof ex.start === "number") {
11258
11390
  cuts.push([rec.start, ex.start]);
11259
- walk2(rec.expression);
11391
+ walk(rec.expression);
11260
11392
  return;
11261
11393
  }
11262
11394
  cuts.push([rec.start, rec.end]);
@@ -11266,11 +11398,11 @@ function stripTsFromSource(node2, code) {
11266
11398
  if (key === "type" || key === "start" || key === "end") continue;
11267
11399
  const v2 = rec[key];
11268
11400
  if (Array.isArray(v2)) {
11269
- for (const item of v2) walk2(item);
11270
- } else if (v2 && typeof v2 === "object") walk2(v2);
11401
+ for (const item of v2) walk(item);
11402
+ } else if (v2 && typeof v2 === "object") walk(v2);
11271
11403
  }
11272
11404
  };
11273
- walk2(node2);
11405
+ walk(node2);
11274
11406
  const start = node2.start;
11275
11407
  const end = node2.end;
11276
11408
  if (cuts.length === 0) return code.slice(start, end);
@@ -11449,10 +11581,38 @@ function scopeGet(scope, name, mfs) {
11449
11581
  if (isCombinator(entry)) return entry;
11450
11582
  return null;
11451
11583
  }
11584
+ function unwrapStaticExpr(expr) {
11585
+ let cur = expr;
11586
+ while (cur.type === "TSAsExpression" || cur.type === "TSSatisfiesExpression" || cur.type === "TSNonNullExpression" || cur.type === "TSTypeAssertion" || cur.type === "TSInstantiationExpression" || cur.type === "ParenthesizedExpression") {
11587
+ if (!cur.expression) break;
11588
+ cur = cur.expression;
11589
+ }
11590
+ return cur;
11591
+ }
11452
11592
  function staticLiteralValue(expr) {
11453
- const val = expr;
11593
+ const val = unwrapStaticExpr(expr);
11454
11594
  return val.type === "Literal" || val.type === "BooleanLiteral" || val.type === "NumericLiteral" ? val.value : void 0;
11455
11595
  }
11596
+ function staticStringArray(expr, scope) {
11597
+ const id = unwrapStaticExpr(expr);
11598
+ if (id.type === "Identifier" && scope !== void 0 && id.name !== void 0) {
11599
+ const scoped = scope.get(id.name);
11600
+ const value = isStaticValueEntry(scoped) ? scoped.value : scoped;
11601
+ return Array.isArray(value) && value.every((v2) => typeof v2 === "string") ? value : void 0;
11602
+ }
11603
+ const arr = id;
11604
+ if (arr.type !== "ArrayExpression" || !Array.isArray(arr.elements)) return void 0;
11605
+ const out = [];
11606
+ for (const el of arr.elements) {
11607
+ if (!el || el.type === "SpreadElement") return void 0;
11608
+ const value = staticLiteralValue(el);
11609
+ if (typeof value !== "string") return void 0;
11610
+ out.push(value);
11611
+ }
11612
+ return out;
11613
+ }
11614
+ var STATIC_NODE_OPTIONS_FAILED = /* @__PURE__ */ Symbol("parseman.staticNodeOptions.failed");
11615
+ var STATIC_NODE_OPTIONS_NOT_OPTIONS = /* @__PURE__ */ Symbol("parseman.staticNodeOptions.notOptions");
11456
11616
  function staticNodeProject(expr) {
11457
11617
  const literalValue = staticLiteralValue(expr);
11458
11618
  if (typeof literalValue === "number" && Number.isInteger(literalValue) && literalValue >= 0) {
@@ -11461,23 +11621,60 @@ function staticNodeProject(expr) {
11461
11621
  if (typeof literalValue === "number" || literalValue !== void 0) return { ok: false };
11462
11622
  return { ok: false };
11463
11623
  }
11464
- function staticNodeOptions(expr) {
11465
- if (expr.type !== "ObjectExpression") return void 0;
11624
+ function scopedStaticValue(expr, scope) {
11625
+ const unwrapped = unwrapStaticExpr(expr);
11626
+ if (unwrapped.type !== "Identifier" || unwrapped.name === "undefined") return { found: false };
11627
+ if (!scope.has(unwrapped.name)) return { found: false };
11628
+ const scoped = scope.get(unwrapped.name);
11629
+ const value = isStaticValueEntry(scoped) ? scoped.value : scoped;
11630
+ return { found: true, value };
11631
+ }
11632
+ function staticNodeOptionsFromValue(value) {
11633
+ if (value === null || typeof value !== "object" || Array.isArray(value) || isCombinator(value)) return STATIC_NODE_OPTIONS_NOT_OPTIONS;
11634
+ const opts = {};
11635
+ const rec = value;
11636
+ for (const name of Object.keys(rec)) {
11637
+ const v2 = rec[name];
11638
+ if (name === "unwrap" || name === "collapse" || name === "captureTrivia" || name === "trailingTrivia") {
11639
+ if (v2 === true) opts[name] = true;
11640
+ else if (v2 !== false && v2 !== void 0) return STATIC_NODE_OPTIONS_FAILED;
11641
+ } else if (name === "project") {
11642
+ if (typeof v2 !== "number" || !Number.isInteger(v2) || v2 < 0) return STATIC_NODE_OPTIONS_FAILED;
11643
+ opts.project = v2;
11644
+ } else if (name === "tags") {
11645
+ if (!Array.isArray(v2) || !v2.every((item) => typeof item === "string")) return STATIC_NODE_OPTIONS_FAILED;
11646
+ opts.tags = v2;
11647
+ }
11648
+ }
11649
+ return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 ? opts : void 0;
11650
+ }
11651
+ function staticNodeOptions(expr, scope) {
11652
+ const unwrapped = unwrapStaticExpr(expr);
11653
+ const scoped = scopedStaticValue(unwrapped, scope);
11654
+ if (scoped.found) return staticNodeOptionsFromValue(scoped.value);
11655
+ if (unwrapped.type !== "ObjectExpression") return STATIC_NODE_OPTIONS_NOT_OPTIONS;
11466
11656
  const opts = {};
11467
- for (const prop of expr.properties) {
11657
+ for (const prop of unwrapped.properties) {
11658
+ if (prop.type !== "Property") return STATIC_NODE_OPTIONS_FAILED;
11468
11659
  const p = prop;
11469
- if (p.computed) continue;
11660
+ if (p.computed) return STATIC_NODE_OPTIONS_FAILED;
11470
11661
  const key = p.key;
11471
11662
  const name = key.type === "Identifier" ? key.name : key.type === "Literal" ? String(key.value) : void 0;
11472
11663
  if (name === "unwrap" || name === "collapse" || name === "captureTrivia" || name === "trailingTrivia") {
11473
- if (staticLiteralValue(p.value) === true) opts[name] = true;
11664
+ const value = staticLiteralValue(p.value);
11665
+ if (value === true) opts[name] = true;
11666
+ else if (value !== false && value !== void 0) return STATIC_NODE_OPTIONS_FAILED;
11474
11667
  } else if (name === "project") {
11475
11668
  const project = staticNodeProject(p.value);
11476
- if (project?.ok === false) return { project: -1 };
11669
+ if (project?.ok === false) return STATIC_NODE_OPTIONS_FAILED;
11477
11670
  if (project !== void 0) opts.project = project.value;
11671
+ } else if (name === "tags") {
11672
+ const tags = staticStringArray(p.value, scope);
11673
+ if (tags === void 0) return STATIC_NODE_OPTIONS_FAILED;
11674
+ opts.tags = tags;
11478
11675
  }
11479
11676
  }
11480
- return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia ? opts : void 0;
11677
+ return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 ? opts : void 0;
11481
11678
  }
11482
11679
  function exprToCombi(node2, scope, code, mfs) {
11483
11680
  if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
@@ -11552,10 +11749,23 @@ function exprToCombi(node2, scope, code, mfs) {
11552
11749
  const inner = anyValue(parserArg, scope, code, mfs);
11553
11750
  if (!isCombinator(inner)) return null;
11554
11751
  const be = buildArg;
11555
- const hasBuild = be !== void 0 && be.type !== "SpreadElement" && be.type !== "ObjectExpression" && !(be.type === "Identifier" && be.name === "undefined");
11752
+ const buildExpr = be === void 0 || be.type === "SpreadElement" ? void 0 : unwrapStaticExpr(be);
11753
+ const buildArgOptions = be !== void 0 && be.type !== "SpreadElement" ? staticNodeOptions(buildArg, scope) : STATIC_NODE_OPTIONS_NOT_OPTIONS;
11754
+ if (buildArgOptions === STATIC_NODE_OPTIONS_FAILED) return null;
11755
+ const buildArgIsOptions = buildArgOptions !== STATIC_NODE_OPTIONS_NOT_OPTIONS;
11756
+ const scopedBuild = buildExpr === void 0 ? { found: false } : scopedStaticValue(buildExpr, scope);
11757
+ const absentBuild = buildExpr?.type === "Identifier" && buildExpr.name === "undefined" || buildExpr !== void 0 && staticLiteralValue(buildExpr) === null || scopedBuild.found && (scopedBuild.value === void 0 || scopedBuild.value === null);
11758
+ const hasBuild = be !== void 0 && be.type !== "SpreadElement" && !buildArgIsOptions && !absentBuild;
11556
11759
  const buildSrc = hasBuild ? stripTsFromSource(be, code) : void 0;
11557
- const optionsArg = hasBuild ? optsArg : be?.type === "ObjectExpression" ? buildArg : optsArg;
11558
- const opts = optionsArg !== void 0 && optionsArg.type !== "SpreadElement" ? staticNodeOptions(optionsArg) : void 0;
11760
+ let opts;
11761
+ if (buildArgIsOptions) {
11762
+ opts = buildArgOptions;
11763
+ } else if (optsArg !== void 0) {
11764
+ if (optsArg.type === "SpreadElement") return null;
11765
+ const optsResult = staticNodeOptions(optsArg, scope);
11766
+ if (optsResult === STATIC_NODE_OPTIONS_FAILED || optsResult === STATIC_NODE_OPTIONS_NOT_OPTIONS) return null;
11767
+ opts = optsResult;
11768
+ }
11559
11769
  try {
11560
11770
  const combi = explicitType !== void 0 ? node(explicitType, inner, hasBuild ? () => null : void 0, opts) : node(inner, hasBuild ? () => null : void 0, opts);
11561
11771
  if (combi._def.tag === "node" && buildSrc !== void 0) {
@@ -11778,6 +11988,10 @@ function exprToCombi(node2, scope, code, mfs) {
11778
11988
  }
11779
11989
  }
11780
11990
  function anyValue(node2, scope, code, mfs) {
11991
+ if (node2.type === "TSAsExpression" || node2.type === "TSSatisfiesExpression" || node2.type === "TSNonNullExpression" || node2.type === "TSTypeAssertion" || node2.type === "TSInstantiationExpression" || node2.type === "ParenthesizedExpression") {
11992
+ const inner = node2.expression;
11993
+ return inner ? anyValue(inner, scope, code, mfs) : null;
11994
+ }
11781
11995
  if (node2.type === "Literal") {
11782
11996
  if ("regex" in node2 && node2.regex !== null && node2.regex !== void 0) {
11783
11997
  return new RegExp(node2.regex.pattern, node2.regex.flags);
@@ -11901,6 +12115,9 @@ function collectRuleKeys(retObj) {
11901
12115
  }
11902
12116
  return out;
11903
12117
  }
12118
+ function evaluateStaticValue(node2, scope, code) {
12119
+ return anyValue(node2, scope, code, []);
12120
+ }
11904
12121
  function flattenRuleEntries(retObj, scope, code) {
11905
12122
  const out = [];
11906
12123
  for (const prop of retObj.properties) {
@@ -12066,6 +12283,18 @@ function referencesAny(node2, names, scope) {
12066
12283
  // src/plugin/index.ts
12067
12284
  var import_node_crypto = require("node:crypto");
12068
12285
  var PARSEMAN_MODULE = "parseman";
12286
+ function unwrapStaticExpression(expr) {
12287
+ let cur = expr;
12288
+ while (cur.type === "TSAsExpression" || cur.type === "TSSatisfiesExpression" || cur.type === "TSNonNullExpression" || cur.type === "TSTypeAssertion" || cur.type === "TSInstantiationExpression" || cur.type === "ParenthesizedExpression") {
12289
+ if (!cur.expression) break;
12290
+ cur = cur.expression;
12291
+ }
12292
+ return cur;
12293
+ }
12294
+ function isStaticNullishExpression(expr) {
12295
+ const unwrapped = unwrapStaticExpression(expr);
12296
+ return unwrapped.type === "Identifier" && unwrapped.name === "undefined" || unwrapped.type === "Literal" && unwrapped.value === null;
12297
+ }
12069
12298
  var _compiledResolver = null;
12070
12299
  function getCompiledResolver() {
12071
12300
  return _compiledResolver ??= new import_oxc_resolver.ResolverFactory({
@@ -12548,6 +12777,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12548
12777
  ruleMap: evaluated.ruleMap,
12549
12778
  hostMode: compiled?.hostMode ?? "ast",
12550
12779
  hostBranchElided: compiled?.hostBranchElided ?? false,
12780
+ reflection: compiled?.reflection ?? { nodes: [] },
12551
12781
  ...compiled?.coverageDefinitions ? { coverageDefinitions: compiled.coverageDefinitions } : {},
12552
12782
  ...evaluated.trivia ? { trivia: evaluated.trivia } : {},
12553
12783
  ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {},
@@ -12568,7 +12798,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12568
12798
  const mapLit = (m, stripVals = false) => `new Map([${[...m].map(
12569
12799
  ([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(stripVals && typeof v2 === "string" ? stripIndent(v2) : v2)}]`
12570
12800
  ).join(", ")}])`;
12571
- 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: [] }`;
12801
+ 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: [] }`;
12572
12802
  };
12573
12803
  const isSpread = (it) => "__spreadLocal" in it;
12574
12804
  const isIR = (it) => "ir" in it && !("ruleFns" in it);
@@ -12577,6 +12807,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12577
12807
  const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
12578
12808
  const withLeafMarker = (grammarExpr) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.leafComposed'), { value: true, enumerable: false })`;
12579
12809
  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})`;
12810
+ const withGrammarReflection = (grammarExpr, reflection) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.grammarReflection'), { value: ${grammarReflectionSource(reflection)}, enumerable: false })`;
12580
12811
  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 })`;
12581
12812
  const emittedCoverageDefinitions = (source) => {
12582
12813
  const ids = /* @__PURE__ */ new Set();
@@ -12972,7 +13203,14 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12972
13203
  const kind = vd.kind ?? "const";
12973
13204
  if (d.id.type === "Identifier") {
12974
13205
  const varName = d.id.name;
12975
- if (!referencesAny(init, allNames, scope)) continue;
13206
+ if (!referencesAny(init, allNames, scope)) {
13207
+ const staticValue = evaluateStaticValue(init, scope, code);
13208
+ if (staticValue !== null && staticValue !== void 0 || isStaticNullishExpression(init)) {
13209
+ ;
13210
+ scope.set(varName, staticValue);
13211
+ }
13212
+ continue;
13213
+ }
12976
13214
  if (refNames.has(varName)) {
12977
13215
  const refEntry = scope.get(varName);
12978
13216
  const refCombi = refEntry?.combi ?? null;
@@ -13012,6 +13250,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
13012
13250
  );
13013
13251
  if (compiledRules.replacement !== null) {
13014
13252
  replacement = withHostMode(replacement, compiledRules.hostMode, compiledRules.hostBranchElided);
13253
+ replacement = withGrammarReflection(replacement, compiledRules.reflection);
13015
13254
  }
13016
13255
  if (exportPrefix && pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
13017
13256
  const ir = serializeRuleMap([...compiledRules.ruleMap], compiledRules.scanSkip);
@@ -13068,6 +13307,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
13068
13307
  scope.set(varName, combiArray);
13069
13308
  continue;
13070
13309
  }
13310
+ const staticValue = evaluateStaticValue(init, scope, code);
13311
+ if (staticValue !== null && staticValue !== void 0 || isStaticNullishExpression(init)) {
13312
+ ;
13313
+ scope.set(varName, staticValue);
13314
+ continue;
13315
+ }
13071
13316
  if (factoryDecls.has(varName)) continue;
13072
13317
  warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
13073
13318
  continue;
@@ -13143,7 +13388,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
13143
13388
  const replacementRanges = runtimeComposeFallback ? [] : replacements.map(({ start, end }) => ({ start, end }));
13144
13389
  const isInsideReplacement = (start, end) => replacementRanges.some((r) => start >= r.start && end <= r.end);
13145
13390
  const importedBindingStillReferenced = (local) => {
13146
- const walk2 = (node2, parent, parentKey) => {
13391
+ const walk = (node2, parent, parentKey) => {
13147
13392
  if (!node2 || typeof node2 !== "object") return false;
13148
13393
  const rec = node2;
13149
13394
  if (rec.type === "ImportDeclaration") return false;
@@ -13158,14 +13403,14 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
13158
13403
  if (key === "type" || key === "start" || key === "end") continue;
13159
13404
  const value = rec[key];
13160
13405
  if (Array.isArray(value)) {
13161
- if (value.some((child) => walk2(child, rec, key))) return true;
13162
- } else if (walk2(value, rec, key)) {
13406
+ if (value.some((child) => walk(child, rec, key))) return true;
13407
+ } else if (walk(value, rec, key)) {
13163
13408
  return true;
13164
13409
  }
13165
13410
  }
13166
13411
  return false;
13167
13412
  };
13168
- return body.some((stmt) => walk2(stmt));
13413
+ return body.some((stmt) => walk(stmt));
13169
13414
  };
13170
13415
  for (const imp of ordinaryImports) {
13171
13416
  if (!runtimeComposeFallback && imp.specifiers.length > 0 && imp.specifiers.every(