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
package/dist/index.js CHANGED
@@ -2863,6 +2863,126 @@ function parse(combinator, input, opts = {}) {
2863
2863
  return lineIndex ? annotateResultLines(withErrors, normalizeLineIndex(lineIndex)) : withErrors;
2864
2864
  }
2865
2865
 
2866
+ // src/cst/reflection.ts
2867
+ var GRAMMAR_REFLECTION = /* @__PURE__ */ Symbol.for("parseman.grammarReflection");
2868
+ function childrenOf(def) {
2869
+ switch (def.tag) {
2870
+ case "sequence":
2871
+ case "choice":
2872
+ return def.parsers;
2873
+ case "dispatch":
2874
+ return [
2875
+ def.selector,
2876
+ ...def.cases.map((c) => c.parser),
2877
+ ...def.matchers ? def.matchers.map((c) => c.parser) : [],
2878
+ ...def.otherwise ? [def.otherwise] : []
2879
+ ];
2880
+ case "many":
2881
+ case "oneOrMore":
2882
+ case "optional":
2883
+ case "attempt":
2884
+ case "transform":
2885
+ case "trivia":
2886
+ case "token":
2887
+ case "leaf":
2888
+ case "label":
2889
+ case "field":
2890
+ case "not":
2891
+ case "peek":
2892
+ case "node":
2893
+ case "withCtx":
2894
+ case "recover":
2895
+ case "expect":
2896
+ return [def.parser];
2897
+ case "grammar":
2898
+ return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
2899
+ case "sepBy":
2900
+ return [def.parser, def.separator];
2901
+ case "skip":
2902
+ return [def.main, def.skipped];
2903
+ case "scanTo":
2904
+ return [def.sentinel, ...def.skip];
2905
+ case "literal":
2906
+ case "regex":
2907
+ case "keywords":
2908
+ case "routed":
2909
+ case "guard":
2910
+ case "lazy":
2911
+ case "unknown":
2912
+ return [];
2913
+ }
2914
+ }
2915
+ function addNode(out, def) {
2916
+ if (def.type === void 0) return;
2917
+ const existing = out.get(def.type);
2918
+ if (existing === void 0) {
2919
+ out.set(def.type, def.tags === void 0 ? [] : [...def.tags]);
2920
+ return;
2921
+ }
2922
+ if (def.tags === void 0) return;
2923
+ for (const tag of def.tags) {
2924
+ if (!existing.includes(tag)) existing.push(tag);
2925
+ }
2926
+ }
2927
+ function collectFromCombinator(parser2, out, seen, followLazy = true) {
2928
+ if (seen.has(parser2)) return;
2929
+ seen.add(parser2);
2930
+ const def = parser2._def;
2931
+ if (def.tag === "lazy") {
2932
+ if (followLazy) {
2933
+ try {
2934
+ collectFromCombinator(def.thunk(), out, seen, followLazy);
2935
+ } catch {
2936
+ }
2937
+ }
2938
+ return;
2939
+ }
2940
+ if (def.tag === "node") addNode(out, def);
2941
+ for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy);
2942
+ }
2943
+ function collectGrammarReflection(ruleMap, opts) {
2944
+ const byType = /* @__PURE__ */ new Map();
2945
+ const followLazy = opts?.followLazy !== false;
2946
+ for (const [, rule] of ruleMap) {
2947
+ if (!followLazy && rule._def.tag === "lazy") {
2948
+ try {
2949
+ const resolved = rule._def.thunk();
2950
+ if (resolved._def.tag !== "lazy") collectFromCombinator(resolved, byType, /* @__PURE__ */ new Set(), false);
2951
+ } catch {
2952
+ }
2953
+ continue;
2954
+ }
2955
+ collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
2956
+ }
2957
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
2958
+ }
2959
+ function mergeGrammarReflections(reflections) {
2960
+ const byType = /* @__PURE__ */ new Map();
2961
+ for (const reflection of reflections) {
2962
+ for (const node2 of reflection.nodes) {
2963
+ const tags = byType.get(node2.type);
2964
+ if (tags === void 0) {
2965
+ byType.set(node2.type, [...node2.tags]);
2966
+ continue;
2967
+ }
2968
+ for (const tag of node2.tags) {
2969
+ if (!tags.includes(tag)) tags.push(tag);
2970
+ }
2971
+ }
2972
+ }
2973
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
2974
+ }
2975
+ function attachGrammarReflection(grammar, reflection) {
2976
+ Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true });
2977
+ return grammar;
2978
+ }
2979
+ function grammarReflectionOf(grammar) {
2980
+ return grammar[GRAMMAR_REFLECTION];
2981
+ }
2982
+ function grammarReflectionSource(reflection) {
2983
+ return `{ nodes: ${JSON.stringify(reflection.nodes)} }`;
2984
+ }
2985
+
2866
2986
  // src/combinators/parser.ts
2867
2987
  var RULE_ORDER = "__parsemanRuleOrder";
2868
2988
  function tagRule(r, key) {
@@ -2961,6 +3081,7 @@ function rules(a, b) {
2961
3081
  enumerable: false,
2962
3082
  configurable: true
2963
3083
  });
3084
+ attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
2964
3085
  return cache;
2965
3086
  }
2966
3087
 
@@ -3281,13 +3402,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3281
3402
  const collapse = opts?.collapse === true;
3282
3403
  const captureTrivia = opts?.captureTrivia === true;
3283
3404
  const trailingTrivia = opts?.trailingTrivia === true;
3405
+ const tags = opts?.tags;
3284
3406
  if (unwrap && collapse) {
3285
3407
  throw new Error("node() options cannot set both unwrap and collapse");
3286
3408
  }
3287
3409
  if (project !== void 0 && (unwrap || collapse)) {
3288
3410
  throw new Error("node() options cannot combine project with unwrap or collapse");
3289
3411
  }
3290
- 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;
3412
+ 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;
3291
3413
  const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
3292
3414
  const clonesState = build ? buildReadsState(def) : project === void 0;
3293
3415
  const hasOwnFields = parserHasOwnFields(combinator);
@@ -3333,7 +3455,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3333
3455
  const cstOutput = cstOutputHost(ctx.build);
3334
3456
  const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3335
3457
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3336
- 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 };
3458
+ 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 };
3337
3459
  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 };
3338
3460
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3339
3461
  pushCstChild(ctx, built, rawEntry);
@@ -3505,7 +3627,7 @@ function leaf(root, fn) {
3505
3627
  }
3506
3628
 
3507
3629
  // src/version.ts
3508
- var PARSEMAN_VERSION = "0.42.1";
3630
+ var PARSEMAN_VERSION = "0.43.0";
3509
3631
 
3510
3632
  // src/cst/host-mode.ts
3511
3633
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -3571,7 +3693,7 @@ var SUGGESTIONS = {
3571
3693
  function classifyBroadArm(arm, resolve) {
3572
3694
  const seen = /* @__PURE__ */ new Set();
3573
3695
  const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
3574
- const walk2 = (p) => {
3696
+ const walk = (p) => {
3575
3697
  if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
3576
3698
  seen.add(p);
3577
3699
  const d = p._def;
@@ -3585,7 +3707,7 @@ function classifyBroadArm(arm, resolve) {
3585
3707
  // peek() CARRIES its body's first-set, so it is only broad when the body is
3586
3708
  // (or the body is nullable) — walk in for the real cause.
3587
3709
  case "peek":
3588
- return walk2(d.parser);
3710
+ return walk(d.parser);
3589
3711
  case "scanTo":
3590
3712
  return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
3591
3713
  // `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
@@ -3599,7 +3721,7 @@ function classifyBroadArm(arm, resolve) {
3599
3721
  case "attempt":
3600
3722
  case "withCtx":
3601
3723
  case "recover":
3602
- return walk2(d.parser);
3724
+ return walk(d.parser);
3603
3725
  case "lazy": {
3604
3726
  const name = p._ruleName;
3605
3727
  let target;
@@ -3611,13 +3733,13 @@ function classifyBroadArm(arm, resolve) {
3611
3733
  return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
3612
3734
  target = bound;
3613
3735
  }
3614
- const inner = walk2(target);
3736
+ const inner = walk(target);
3615
3737
  if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
3616
3738
  return inner;
3617
3739
  }
3618
3740
  case "optional":
3619
3741
  case "many": {
3620
- const inner = walk2(d.parser);
3742
+ const inner = walk(d.parser);
3621
3743
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3622
3744
  }
3623
3745
  case "oneOrMore":
@@ -3630,9 +3752,9 @@ function classifyBroadArm(arm, resolve) {
3630
3752
  case "node":
3631
3753
  case "grammar":
3632
3754
  case "expect":
3633
- return walk2(d.parser);
3755
+ return walk(d.parser);
3634
3756
  case "skip":
3635
- return walk2(d.main);
3757
+ return walk(d.main);
3636
3758
  case "sequence": {
3637
3759
  let sawNullablePrefix = false;
3638
3760
  for (const t of d.parsers) {
@@ -3641,7 +3763,7 @@ function classifyBroadArm(arm, resolve) {
3641
3763
  continue;
3642
3764
  }
3643
3765
  if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve))) {
3644
- const inner = walk2(t);
3766
+ const inner = walk(t);
3645
3767
  if (sawNullablePrefix)
3646
3768
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3647
3769
  return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
@@ -3656,18 +3778,18 @@ function classifyBroadArm(arm, resolve) {
3656
3778
  }
3657
3779
  case "choice": {
3658
3780
  for (const a of d.parsers) {
3659
- const r = walk2(a);
3781
+ const r = walk(a);
3660
3782
  if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
3661
3783
  }
3662
3784
  return null;
3663
3785
  }
3664
3786
  case "dispatch":
3665
- return walk2(d.selector);
3787
+ return walk(d.selector);
3666
3788
  default:
3667
3789
  return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
3668
3790
  }
3669
3791
  };
3670
- return walk2(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3792
+ return walk(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3671
3793
  }
3672
3794
  function peelToLeading(arm) {
3673
3795
  let d = arm._def;
@@ -3869,7 +3991,7 @@ function formatGatingWarnings(report) {
3869
3991
 
3870
3992
  // src/analysis/duplication.ts
3871
3993
  var siteToString = (s) => s.path === "" ? s.rule : `${s.rule} \u203A ${s.path}`;
3872
- function childrenOf(d) {
3994
+ function childrenOf2(d) {
3873
3995
  switch (d.tag) {
3874
3996
  case "sequence":
3875
3997
  case "choice":
@@ -4003,7 +4125,7 @@ function render(p, depth = 3) {
4003
4125
  break;
4004
4126
  }
4005
4127
  if (depth <= 0) return `${d.tag}(\u2026)`;
4006
- const kids = childrenOf(d).map((k) => render(k, depth - 1));
4128
+ const kids = childrenOf2(d).map((k) => render(k, depth - 1));
4007
4129
  switch (d.tag) {
4008
4130
  case "sequence":
4009
4131
  return `sequence(${kids.join(", ")})`;
@@ -4198,7 +4320,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4198
4320
  if (hit !== void 0) return hit;
4199
4321
  const d = p._def;
4200
4322
  shapeCache.set(p, interner.hole);
4201
- const kids = childrenOf(d);
4323
+ const kids = childrenOf2(d);
4202
4324
  const id = interner.of(`${payloadKey(p, d)}|${kids.map(shapeOf).join(",")}`);
4203
4325
  shapeCache.set(p, id);
4204
4326
  return id;
@@ -4208,7 +4330,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4208
4330
  if (hit !== void 0) return hit;
4209
4331
  sizeCache.set(p, 1);
4210
4332
  let n = 1;
4211
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4333
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4212
4334
  sizeCache.set(p, n);
4213
4335
  return n;
4214
4336
  };
@@ -4221,7 +4343,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4221
4343
  visited.set(p, { p, d, site: here, shape: shapeOf(p), size: sizeOf(p), enclosingNode, enclosingReducer });
4222
4344
  const inner = d.tag === "node" ? d.type ?? "(anonymous)" : enclosingNode;
4223
4345
  const innerReducer = enclosingReducer || d.tag === "node" && (d.build !== void 0 || d.buildSrc !== void 0) || d.tag === "transform" || d.tag === "leaf";
4224
- const kids = childrenOf(d);
4346
+ const kids = childrenOf2(d);
4225
4347
  kids.forEach((k, i) => {
4226
4348
  const seg = segment(d, i);
4227
4349
  visit(k, here.rule, here.path === "" ? seg : `${here.path} \u203A ${seg}`, inner, innerReducer);
@@ -4281,7 +4403,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4281
4403
  function markSubtree(p, into) {
4282
4404
  if (into.has(p)) return;
4283
4405
  into.add(p);
4284
- for (const k of childrenOf(p._def)) markSubtree(k, into);
4406
+ for (const k of childrenOf2(p._def)) markSubtree(k, into);
4285
4407
  }
4286
4408
  var stripMembers = (f) => {
4287
4409
  const { _members, ...rest } = f;
@@ -4321,7 +4443,7 @@ function findNearDuplicates(visited, interner, shapeOf, minSize) {
4321
4443
  if (hit !== void 0) return hit;
4322
4444
  holeCache.set(p, []);
4323
4445
  const d = p._def;
4324
- const kids = childrenOf(d);
4446
+ const kids = childrenOf2(d);
4325
4447
  const kidShapes = kids.map(shapeOf);
4326
4448
  const payload = payloadKey(p, d);
4327
4449
  const out2 = [];
@@ -4382,7 +4504,7 @@ function findDivergentSlot(members, shapeOf) {
4382
4504
  let cur = [...members];
4383
4505
  for (let guard2 = 0; guard2 < 64; guard2++) {
4384
4506
  const d0 = cur[0]._def;
4385
- const kidLists = cur.map((c) => childrenOf(c._def));
4507
+ const kidLists = cur.map((c) => childrenOf2(c._def));
4386
4508
  const arity = kidLists[0].length;
4387
4509
  if (kidLists.some((k) => k.length !== arity) || arity === 0) break;
4388
4510
  const diff = [];
@@ -4395,7 +4517,7 @@ function findDivergentSlot(members, shapeOf) {
4395
4517
  path.push(i);
4396
4518
  defs.push(d0);
4397
4519
  cur = kidLists.map((k) => k[i]);
4398
- const nextArity = childrenOf(cur[0]._def).length;
4520
+ const nextArity = childrenOf2(cur[0]._def).length;
4399
4521
  const samePayload = cur.every((c) => payloadKey(c, c._def) === payloadKey(cur[0], cur[0]._def));
4400
4522
  if (!samePayload || nextArity === 0) break;
4401
4523
  }
@@ -4403,7 +4525,7 @@ function findDivergentSlot(members, shapeOf) {
4403
4525
  let slotSize = 0;
4404
4526
  const sizeOf = (p) => {
4405
4527
  let n = 1;
4406
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4528
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4407
4529
  return n;
4408
4530
  };
4409
4531
  for (const c of cur) slotSize = Math.max(slotSize, sizeOf(c));
@@ -4412,7 +4534,7 @@ function findDivergentSlot(members, shapeOf) {
4412
4534
  function renderWithHole(p, path, depth = 0) {
4413
4535
  if (depth === path.length) return "\u2039slot\u203A";
4414
4536
  const d = p._def;
4415
- const kids = childrenOf(d);
4537
+ const kids = childrenOf2(d);
4416
4538
  const idx = path[depth];
4417
4539
  const parts = kids.map((k, i) => i === idx ? renderWithHole(k, path, depth + 1) : render(k, 2));
4418
4540
  switch (d.tag) {
@@ -4870,7 +4992,7 @@ function capturesPositionally(p, seen = /* @__PURE__ */ new Set()) {
4870
4992
  seen.add(p);
4871
4993
  const d = p._def;
4872
4994
  if (d.tag === "field" || d.tag === "label") return true;
4873
- for (const k of childrenOf(d)) if (capturesPositionally(k, seen)) return true;
4995
+ for (const k of childrenOf2(d)) if (capturesPositionally(k, seen)) return true;
4874
4996
  return false;
4875
4997
  }
4876
4998
  function detectHandRolledSepBy(terms, shapeOf) {
@@ -4976,7 +5098,7 @@ function nestedNodeTypes(root, out, seen, isRoot) {
4976
5098
  }
4977
5099
  return;
4978
5100
  }
4979
- for (const k of childrenOf(d)) nestedNodeTypes(k, out, seen, false);
5101
+ for (const k of childrenOf2(d)) nestedNodeTypes(k, out, seen, false);
4980
5102
  }
4981
5103
  var MAX_LOST_TYPES = 8;
4982
5104
  function findStructureLoss(visited, resolve) {
@@ -8163,7 +8285,7 @@ function routedNeedsContextBridge(p, ctx, seen = /* @__PURE__ */ new Set()) {
8163
8285
  const d = p._def;
8164
8286
  if (d.tag === "dispatch") return false;
8165
8287
  if (d.tag === "withCtx") return parserUsesRouted2(d.parser);
8166
- return childrenOf2(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8288
+ return childrenOf3(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8167
8289
  }
8168
8290
  function armReplayInScope(arm, ctx) {
8169
8291
  let p = arm;
@@ -8926,7 +9048,8 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8926
9048
  const nodeSpanV = ctx.lineTracking ? v(ctx, "_nspan") : nodeSpanExpr;
8927
9049
  if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
8928
9050
  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})`;
8929
- const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
9051
+ const hostBuildArgs = `${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV}${def.tags !== void 0 && def.tags.length > 0 ? `, ${JSON.stringify(def.tags)}` : ""}`;
9052
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${hostBuildArgs})`;
8930
9053
  const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
8931
9054
  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;
8932
9055
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
@@ -9389,7 +9512,7 @@ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9389
9512
  return false;
9390
9513
  }
9391
9514
  }
9392
- return childrenOf2(d).some((child) => hasLineTrackingDef(child, seen));
9515
+ return childrenOf3(d).some((child) => hasLineTrackingDef(child, seen));
9393
9516
  }
9394
9517
  function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9395
9518
  if (seen.has(p)) return false;
@@ -9408,7 +9531,7 @@ function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9408
9531
  case "dispatch":
9409
9532
  return false;
9410
9533
  default:
9411
- return childrenOf2(d).some((child) => parserUsesRouted2(child, seen));
9534
+ return childrenOf3(d).some((child) => parserUsesRouted2(child, seen));
9412
9535
  }
9413
9536
  }
9414
9537
  function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
@@ -9423,9 +9546,9 @@ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
9423
9546
  return false;
9424
9547
  }
9425
9548
  }
9426
- return childrenOf2(d).some((child) => hasDirectBuildDef(child, seen));
9549
+ return childrenOf3(d).some((child) => hasDirectBuildDef(child, seen));
9427
9550
  }
9428
- function childrenOf2(def) {
9551
+ function childrenOf3(def) {
9429
9552
  switch (def.tag) {
9430
9553
  case "sequence":
9431
9554
  case "choice":
@@ -9500,7 +9623,7 @@ function subtreeSizes(roots) {
9500
9623
  const def = p._def;
9501
9624
  let s = 1;
9502
9625
  if (def.tag !== "lazy") {
9503
- for (const child of childrenOf2(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
9626
+ for (const child of childrenOf3(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
9504
9627
  }
9505
9628
  sizes.set(p, s);
9506
9629
  return s;
@@ -9521,7 +9644,7 @@ function subtreeSizes(roots) {
9521
9644
  return;
9522
9645
  }
9523
9646
  sz(p);
9524
- for (const child of childrenOf2(def)) visit(child);
9647
+ for (const child of childrenOf3(def)) visit(child);
9525
9648
  };
9526
9649
  for (const root of roots) visit(root);
9527
9650
  return sizes;
@@ -9531,7 +9654,7 @@ function analyzeLazyUsageMulti(roots) {
9531
9654
  const recursive = /* @__PURE__ */ new Set();
9532
9655
  const descended = /* @__PURE__ */ new Set();
9533
9656
  const active = /* @__PURE__ */ new Set();
9534
- function walk2(p) {
9657
+ function walk(p) {
9535
9658
  const def = p._def;
9536
9659
  if (def.tag === "lazy") {
9537
9660
  counts.set(p, (counts.get(p) ?? 0) + 1);
@@ -9548,17 +9671,17 @@ function analyzeLazyUsageMulti(roots) {
9548
9671
  return;
9549
9672
  }
9550
9673
  active.add(p);
9551
- walk2(resolved);
9674
+ walk(resolved);
9552
9675
  active.delete(p);
9553
9676
  return;
9554
9677
  }
9555
9678
  if (isHoistableTag(def.tag)) {
9556
9679
  counts.set(p, (counts.get(p) ?? 0) + 1);
9557
9680
  }
9558
- for (const child of childrenOf2(def)) walk2(child);
9681
+ for (const child of childrenOf3(def)) walk(child);
9559
9682
  }
9560
9683
  const rootsArr = [...roots];
9561
- for (const root of rootsArr) walk2(root);
9684
+ for (const root of rootsArr) walk(root);
9562
9685
  return { counts, recursive, sizes: subtreeSizes(rootsArr) };
9563
9686
  }
9564
9687
  function ruleDependencies(ruleMap) {
@@ -9568,7 +9691,7 @@ function ruleDependencies(ruleMap) {
9568
9691
  for (const [name, comb] of ruleMap) {
9569
9692
  const found = /* @__PURE__ */ new Set();
9570
9693
  const seen = /* @__PURE__ */ new Set();
9571
- const walk2 = (p, isRoot) => {
9694
+ const walk = (p, isRoot) => {
9572
9695
  const def = p._def;
9573
9696
  const boundary = def.tag === "lazy" ? nameOf.get(p) ?? p._ruleName : nameOf.get(p);
9574
9697
  if (!isRoot && boundary !== void 0) {
@@ -9584,12 +9707,12 @@ function ruleDependencies(ruleMap) {
9584
9707
  } catch {
9585
9708
  return;
9586
9709
  }
9587
- walk2(resolved, false);
9710
+ walk(resolved, false);
9588
9711
  return;
9589
9712
  }
9590
- for (const child of childrenOf2(def)) walk2(child, false);
9713
+ for (const child of childrenOf3(def)) walk(child, false);
9591
9714
  };
9592
- walk2(comb, true);
9715
+ walk(comb, true);
9593
9716
  deps.set(name, [...found]);
9594
9717
  }
9595
9718
  return deps;
@@ -9934,6 +10057,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
9934
10057
  return false;
9935
10058
  }
9936
10059
  });
10060
+ const nodeMeta = new Map(ruleMap.map(([name, rule]) => [name, collectGrammarReflection([[name, rule]], { followLazy: false })]));
9937
10061
  const ctx = {
9938
10062
  vars: 0,
9939
10063
  indent: 1,
@@ -10009,7 +10133,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10009
10133
  scanExternal(resolved);
10010
10134
  return;
10011
10135
  }
10012
- for (const child of childrenOf2(def)) scanExternal(child);
10136
+ for (const child of childrenOf3(def)) scanExternal(child);
10013
10137
  };
10014
10138
  for (const [, rule] of ruleMap) scanExternal(rule);
10015
10139
  }
@@ -10131,6 +10255,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10131
10255
  hostBranchElided: !!ctx.hostBranchElided,
10132
10256
  hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
10133
10257
  isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs),
10258
+ nodeMeta,
10134
10259
  mfFns: mfSrcs ? [] : ctx.mapFns,
10135
10260
  buildFns: buildSrcs ? [] : ctx.buildFns
10136
10261
  };
@@ -10151,7 +10276,7 @@ function hasExternalRuleRef(ruleMap) {
10151
10276
  if (resolved === void 0) return p._ruleName !== void 0;
10152
10277
  return visit(resolved);
10153
10278
  }
10154
- return childrenOf2(def).some(visit);
10279
+ return childrenOf3(def).some(visit);
10155
10280
  };
10156
10281
  return ruleMap.some(([, rule]) => visit(rule));
10157
10282
  }
@@ -10173,7 +10298,7 @@ function hasSemanticReduction(roots, externalRefs) {
10173
10298
  return true;
10174
10299
  }
10175
10300
  }
10176
- return childrenOf2(def).some(visit);
10301
+ return childrenOf3(def).some(visit);
10177
10302
  };
10178
10303
  return roots.some(visit);
10179
10304
  }
@@ -10246,7 +10371,7 @@ function withCtx(extra, combinator) {
10246
10371
  }
10247
10372
 
10248
10373
  // src/compiler/ir-serialize.ts
10249
- function childrenOf3(def) {
10374
+ function childrenOf4(def) {
10250
10375
  switch (def.tag) {
10251
10376
  case "sequence":
10252
10377
  case "choice":
@@ -10310,6 +10435,10 @@ function projectOpt(project) {
10310
10435
  if (project === void 0) return void 0;
10311
10436
  return `project: ${project}`;
10312
10437
  }
10438
+ function tagsOpt(tags) {
10439
+ if (tags === void 0 || tags.length === 0) return void 0;
10440
+ return `tags: ${JSON.stringify(tags)}`;
10441
+ }
10313
10442
  function matcherExpr(entry) {
10314
10443
  switch (entry.kind) {
10315
10444
  case "startsWith":
@@ -10523,7 +10652,7 @@ var Serializer = class {
10523
10652
  if (target) this.assertNoRuleRef(target, seen);
10524
10653
  return;
10525
10654
  }
10526
- for (const child of childrenOf3(c._def)) this.assertNoRuleRef(child, seen);
10655
+ for (const child of childrenOf4(c._def)) this.assertNoRuleRef(child, seen);
10527
10656
  }
10528
10657
  /** A `rules()` map wraps each rule value in a named `lazy` proxy; the real body is
10529
10658
  * its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
@@ -10587,7 +10716,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10587
10716
  this.counts.set(c, (this.counts.get(c) ?? 0) + 1);
10588
10717
  if ((this.counts.get(c) ?? 0) > 1) return;
10589
10718
  active.add(c);
10590
- for (const child of childrenOf3(def)) this.analyze(child, active);
10719
+ for (const child of childrenOf4(def)) this.analyze(child, active);
10591
10720
  active.delete(c);
10592
10721
  }
10593
10722
  /** Reference `c`: a const var if it has one (emitting its decl first), else inline.
@@ -10704,12 +10833,14 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10704
10833
  case "node": {
10705
10834
  if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
10706
10835
  const projectEntry = projectOpt(def.project);
10836
+ const tagEntry = tagsOpt(def.tags);
10707
10837
  const optEntries = [
10708
10838
  ...def.unwrap ? ["unwrap: true"] : [],
10709
10839
  ...def.collapse ? ["collapse: true"] : [],
10710
10840
  ...projectEntry === void 0 ? [] : [projectEntry],
10711
10841
  ...def.captureTrivia ? ["captureTrivia: true"] : [],
10712
- ...def.trailingTrivia ? ["trailingTrivia: true"] : []
10842
+ ...def.trailingTrivia ? ["trailingTrivia: true"] : [],
10843
+ ...tagEntry === void 0 ? [] : [tagEntry]
10713
10844
  ];
10714
10845
  const opts = optEntries.length > 0 ? `, { ${optEntries.join(", ")} }` : "";
10715
10846
  if (def.type === void 0) {
@@ -10757,15 +10888,16 @@ function normalizeCstCollapse(collapse) {
10757
10888
  }
10758
10889
  return typeof collapse === "function" ? collapse : void 0;
10759
10890
  }
10760
- function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
10761
- return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children2] };
10891
+ function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state, tags) {
10892
+ 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] };
10762
10893
  }
10763
- function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
10894
+ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state, _tags) {
10764
10895
  if (typeof typeOrOptions === "string") {
10765
10896
  return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
10766
10897
  }
10767
10898
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
10768
- const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2);
10899
+ const materializeTags = typeOrOptions?.tags === true;
10900
+ const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2, tags) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2, materializeTags ? tags : void 0);
10769
10901
  host._parsemanCstOutput = true;
10770
10902
  if (collapse) host._parsemanCstCollapse = collapse;
10771
10903
  return host;
@@ -10807,6 +10939,7 @@ function fusedBody(pieces) {
10807
10939
  ...[...winner].map(([k, p]) => p.ruleFns.get(k))
10808
10940
  ];
10809
10941
  const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
10942
+ const reflection = mergeGrammarReflections([...winner].map(([k, p]) => p.nodeMeta.get(k) ?? { nodes: [] }));
10810
10943
  const { mode, elided } = hostModeOfPieces(pieces);
10811
10944
  const rawBody = [
10812
10945
  ...lines,
@@ -10815,6 +10948,7 @@ function fusedBody(pieces) {
10815
10948
  "}",
10816
10949
  `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_MODE.description)}), { value: ${JSON.stringify(mode)}, enumerable: false })`,
10817
10950
  `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_ELIDED.description)}), { value: ${JSON.stringify(elided)}, enumerable: false })`,
10951
+ `Object.defineProperty(_map, Symbol.for('parseman.grammarReflection'), { value: ${grammarReflectionSource(reflection)}, enumerable: false })`,
10818
10952
  // …and on each rule FUNCTION, because `run(map.Rule, …)` is handed the rule, not the
10819
10953
  // map, and would otherwise have nothing to check against. Done once at fuse time,
10820
10954
  // before any rule has been called, so it costs nothing per parse.
@@ -12243,38 +12377,48 @@ function buildTriviaIndex(root, input = "", opts) {
12243
12377
  }
12244
12378
 
12245
12379
  // src/cst/walk.ts
12246
- function walk(root, visitor, ctx = void 0) {
12247
- const go = (node2, parent) => {
12248
- const descend = visitor.enter ? visitor.enter(node2, parent, ctx) : void 0;
12249
- const children2 = node2.children;
12250
- if (descend !== false && Array.isArray(children2)) {
12251
- for (const child of children2) go(child, node2);
12252
- }
12253
- visitor.leave?.(node2, parent, ctx);
12254
- };
12255
- go(root, null);
12256
- }
12257
- function createVisitor(handlers) {
12258
- const api = {
12259
- visit(node2) {
12260
- const handler = node2.type !== void 0 ? handlers[node2.type] : void 0;
12261
- if (handler) return handler(node2, api);
12262
- api.visitChildren(node2);
12263
- return void 0;
12264
- },
12265
- visitChildren(node2) {
12266
- const out = [];
12267
- const children2 = node2.children;
12268
- if (Array.isArray(children2)) {
12269
- for (const child of children2) {
12270
- const r = api.visit(child);
12271
- if (r !== void 0) out.push(r);
12380
+ function isCombinator(value) {
12381
+ return !!value && typeof value === "object" && "_def" in value && "_meta" in value && typeof value.parse === "function";
12382
+ }
12383
+ function reflectionFor(grammar) {
12384
+ const stamped = grammarReflectionOf(grammar);
12385
+ if (stamped) return stamped;
12386
+ const entries = Object.entries(grammar).filter((entry) => isCombinator(entry[1]));
12387
+ return collectGrammarReflection(entries);
12388
+ }
12389
+ function createVisitor(grammar, spec) {
12390
+ const reflection = reflectionFor(grammar);
12391
+ const typeHandlers = spec.type;
12392
+ const tagSpec = spec.tag;
12393
+ const tagHandlersByType = /* @__PURE__ */ new Map();
12394
+ if (tagSpec !== void 0) {
12395
+ for (const node2 of reflection.nodes) {
12396
+ const handlers = [];
12397
+ for (const tag of node2.tags) {
12398
+ const handler = tagSpec[tag];
12399
+ if (handler) handlers.push(handler);
12400
+ }
12401
+ if (handlers.length > 0) tagHandlersByType.set(node2.type, handlers);
12402
+ }
12403
+ }
12404
+ return (root, ctx = void 0) => {
12405
+ const go = (node2, parent) => {
12406
+ const descend = spec.enter ? spec.enter(node2, parent, ctx) : void 0;
12407
+ if (node2._tag === "node" && node2.type !== void 0) {
12408
+ typeHandlers?.[node2.type]?.(node2, parent, ctx);
12409
+ const tagHandlers = tagHandlersByType.get(node2.type);
12410
+ if (tagHandlers !== void 0) {
12411
+ for (const handler of tagHandlers) handler(node2, parent, ctx);
12272
12412
  }
12273
12413
  }
12274
- return out;
12275
- }
12414
+ const children2 = node2.children;
12415
+ if (descend !== false && Array.isArray(children2)) {
12416
+ for (const child of children2) go(child, node2);
12417
+ }
12418
+ spec.leave?.(node2, parent, ctx);
12419
+ };
12420
+ go(root, null);
12276
12421
  };
12277
- return (node2) => api.visit(node2);
12278
12422
  }
12279
12423
 
12280
12424
  // src/cst/offset-model.ts
@@ -12571,7 +12715,6 @@ export {
12571
12715
  trivia,
12572
12716
  triviaEntries,
12573
12717
  triviaKindMask,
12574
- walk,
12575
12718
  when,
12576
12719
  withCtx,
12577
12720
  word