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.cjs CHANGED
@@ -121,7 +121,6 @@ __export(index_exports, {
121
121
  trivia: () => trivia,
122
122
  triviaEntries: () => triviaEntries,
123
123
  triviaKindMask: () => triviaKindMask,
124
- walk: () => walk,
125
124
  when: () => when,
126
125
  withCtx: () => withCtx,
127
126
  word: () => word
@@ -2993,6 +2992,126 @@ function parse(combinator, input, opts = {}) {
2993
2992
  return lineIndex ? annotateResultLines(withErrors, normalizeLineIndex(lineIndex)) : withErrors;
2994
2993
  }
2995
2994
 
2995
+ // src/cst/reflection.ts
2996
+ var GRAMMAR_REFLECTION = /* @__PURE__ */ Symbol.for("parseman.grammarReflection");
2997
+ function childrenOf(def) {
2998
+ switch (def.tag) {
2999
+ case "sequence":
3000
+ case "choice":
3001
+ return def.parsers;
3002
+ case "dispatch":
3003
+ return [
3004
+ def.selector,
3005
+ ...def.cases.map((c) => c.parser),
3006
+ ...def.matchers ? def.matchers.map((c) => c.parser) : [],
3007
+ ...def.otherwise ? [def.otherwise] : []
3008
+ ];
3009
+ case "many":
3010
+ case "oneOrMore":
3011
+ case "optional":
3012
+ case "attempt":
3013
+ case "transform":
3014
+ case "trivia":
3015
+ case "token":
3016
+ case "leaf":
3017
+ case "label":
3018
+ case "field":
3019
+ case "not":
3020
+ case "peek":
3021
+ case "node":
3022
+ case "withCtx":
3023
+ case "recover":
3024
+ case "expect":
3025
+ return [def.parser];
3026
+ case "grammar":
3027
+ return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
3028
+ case "sepBy":
3029
+ return [def.parser, def.separator];
3030
+ case "skip":
3031
+ return [def.main, def.skipped];
3032
+ case "scanTo":
3033
+ return [def.sentinel, ...def.skip];
3034
+ case "literal":
3035
+ case "regex":
3036
+ case "keywords":
3037
+ case "routed":
3038
+ case "guard":
3039
+ case "lazy":
3040
+ case "unknown":
3041
+ return [];
3042
+ }
3043
+ }
3044
+ function addNode(out, def) {
3045
+ if (def.type === void 0) return;
3046
+ const existing = out.get(def.type);
3047
+ if (existing === void 0) {
3048
+ out.set(def.type, def.tags === void 0 ? [] : [...def.tags]);
3049
+ return;
3050
+ }
3051
+ if (def.tags === void 0) return;
3052
+ for (const tag of def.tags) {
3053
+ if (!existing.includes(tag)) existing.push(tag);
3054
+ }
3055
+ }
3056
+ function collectFromCombinator(parser2, out, seen, followLazy = true) {
3057
+ if (seen.has(parser2)) return;
3058
+ seen.add(parser2);
3059
+ const def = parser2._def;
3060
+ if (def.tag === "lazy") {
3061
+ if (followLazy) {
3062
+ try {
3063
+ collectFromCombinator(def.thunk(), out, seen, followLazy);
3064
+ } catch {
3065
+ }
3066
+ }
3067
+ return;
3068
+ }
3069
+ if (def.tag === "node") addNode(out, def);
3070
+ for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy);
3071
+ }
3072
+ function collectGrammarReflection(ruleMap, opts) {
3073
+ const byType = /* @__PURE__ */ new Map();
3074
+ const followLazy = opts?.followLazy !== false;
3075
+ for (const [, rule] of ruleMap) {
3076
+ if (!followLazy && rule._def.tag === "lazy") {
3077
+ try {
3078
+ const resolved = rule._def.thunk();
3079
+ if (resolved._def.tag !== "lazy") collectFromCombinator(resolved, byType, /* @__PURE__ */ new Set(), false);
3080
+ } catch {
3081
+ }
3082
+ continue;
3083
+ }
3084
+ collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
3085
+ }
3086
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
3087
+ }
3088
+ function mergeGrammarReflections(reflections) {
3089
+ const byType = /* @__PURE__ */ new Map();
3090
+ for (const reflection of reflections) {
3091
+ for (const node2 of reflection.nodes) {
3092
+ const tags = byType.get(node2.type);
3093
+ if (tags === void 0) {
3094
+ byType.set(node2.type, [...node2.tags]);
3095
+ continue;
3096
+ }
3097
+ for (const tag of node2.tags) {
3098
+ if (!tags.includes(tag)) tags.push(tag);
3099
+ }
3100
+ }
3101
+ }
3102
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
3103
+ }
3104
+ function attachGrammarReflection(grammar, reflection) {
3105
+ Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true });
3106
+ return grammar;
3107
+ }
3108
+ function grammarReflectionOf(grammar) {
3109
+ return grammar[GRAMMAR_REFLECTION];
3110
+ }
3111
+ function grammarReflectionSource(reflection) {
3112
+ return `{ nodes: ${JSON.stringify(reflection.nodes)} }`;
3113
+ }
3114
+
2996
3115
  // src/combinators/parser.ts
2997
3116
  var RULE_ORDER = "__parsemanRuleOrder";
2998
3117
  function tagRule(r, key) {
@@ -3091,6 +3210,7 @@ function rules(a, b) {
3091
3210
  enumerable: false,
3092
3211
  configurable: true
3093
3212
  });
3213
+ attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
3094
3214
  return cache;
3095
3215
  }
3096
3216
 
@@ -3411,13 +3531,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3411
3531
  const collapse = opts?.collapse === true;
3412
3532
  const captureTrivia = opts?.captureTrivia === true;
3413
3533
  const trailingTrivia = opts?.trailingTrivia === true;
3534
+ const tags = opts?.tags;
3414
3535
  if (unwrap && collapse) {
3415
3536
  throw new Error("node() options cannot set both unwrap and collapse");
3416
3537
  }
3417
3538
  if (project !== void 0 && (unwrap || collapse)) {
3418
3539
  throw new Error("node() options cannot combine project with unwrap or collapse");
3419
3540
  }
3420
- 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;
3541
+ 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;
3421
3542
  const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
3422
3543
  const clonesState = build ? buildReadsState(def) : project === void 0;
3423
3544
  const hasOwnFields = parserHasOwnFields(combinator);
@@ -3463,7 +3584,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3463
3584
  const cstOutput = cstOutputHost(ctx.build);
3464
3585
  const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3465
3586
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3466
- 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 };
3587
+ 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 };
3467
3588
  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 };
3468
3589
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3469
3590
  pushCstChild(ctx, built, rawEntry);
@@ -3635,7 +3756,7 @@ function leaf(root, fn) {
3635
3756
  }
3636
3757
 
3637
3758
  // src/version.ts
3638
- var PARSEMAN_VERSION = "0.42.1";
3759
+ var PARSEMAN_VERSION = "0.43.0";
3639
3760
 
3640
3761
  // src/cst/host-mode.ts
3641
3762
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -3701,7 +3822,7 @@ var SUGGESTIONS = {
3701
3822
  function classifyBroadArm(arm, resolve) {
3702
3823
  const seen = /* @__PURE__ */ new Set();
3703
3824
  const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
3704
- const walk2 = (p) => {
3825
+ const walk = (p) => {
3705
3826
  if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
3706
3827
  seen.add(p);
3707
3828
  const d = p._def;
@@ -3715,7 +3836,7 @@ function classifyBroadArm(arm, resolve) {
3715
3836
  // peek() CARRIES its body's first-set, so it is only broad when the body is
3716
3837
  // (or the body is nullable) — walk in for the real cause.
3717
3838
  case "peek":
3718
- return walk2(d.parser);
3839
+ return walk(d.parser);
3719
3840
  case "scanTo":
3720
3841
  return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
3721
3842
  // `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
@@ -3729,7 +3850,7 @@ function classifyBroadArm(arm, resolve) {
3729
3850
  case "attempt":
3730
3851
  case "withCtx":
3731
3852
  case "recover":
3732
- return walk2(d.parser);
3853
+ return walk(d.parser);
3733
3854
  case "lazy": {
3734
3855
  const name = p._ruleName;
3735
3856
  let target;
@@ -3741,13 +3862,13 @@ function classifyBroadArm(arm, resolve) {
3741
3862
  return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
3742
3863
  target = bound;
3743
3864
  }
3744
- const inner = walk2(target);
3865
+ const inner = walk(target);
3745
3866
  if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
3746
3867
  return inner;
3747
3868
  }
3748
3869
  case "optional":
3749
3870
  case "many": {
3750
- const inner = walk2(d.parser);
3871
+ const inner = walk(d.parser);
3751
3872
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3752
3873
  }
3753
3874
  case "oneOrMore":
@@ -3760,9 +3881,9 @@ function classifyBroadArm(arm, resolve) {
3760
3881
  case "node":
3761
3882
  case "grammar":
3762
3883
  case "expect":
3763
- return walk2(d.parser);
3884
+ return walk(d.parser);
3764
3885
  case "skip":
3765
- return walk2(d.main);
3886
+ return walk(d.main);
3766
3887
  case "sequence": {
3767
3888
  let sawNullablePrefix = false;
3768
3889
  for (const t of d.parsers) {
@@ -3771,7 +3892,7 @@ function classifyBroadArm(arm, resolve) {
3771
3892
  continue;
3772
3893
  }
3773
3894
  if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve))) {
3774
- const inner = walk2(t);
3895
+ const inner = walk(t);
3775
3896
  if (sawNullablePrefix)
3776
3897
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3777
3898
  return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
@@ -3786,18 +3907,18 @@ function classifyBroadArm(arm, resolve) {
3786
3907
  }
3787
3908
  case "choice": {
3788
3909
  for (const a of d.parsers) {
3789
- const r = walk2(a);
3910
+ const r = walk(a);
3790
3911
  if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
3791
3912
  }
3792
3913
  return null;
3793
3914
  }
3794
3915
  case "dispatch":
3795
- return walk2(d.selector);
3916
+ return walk(d.selector);
3796
3917
  default:
3797
3918
  return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
3798
3919
  }
3799
3920
  };
3800
- return walk2(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3921
+ return walk(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3801
3922
  }
3802
3923
  function peelToLeading(arm) {
3803
3924
  let d = arm._def;
@@ -3999,7 +4120,7 @@ function formatGatingWarnings(report) {
3999
4120
 
4000
4121
  // src/analysis/duplication.ts
4001
4122
  var siteToString = (s) => s.path === "" ? s.rule : `${s.rule} \u203A ${s.path}`;
4002
- function childrenOf(d) {
4123
+ function childrenOf2(d) {
4003
4124
  switch (d.tag) {
4004
4125
  case "sequence":
4005
4126
  case "choice":
@@ -4133,7 +4254,7 @@ function render(p, depth = 3) {
4133
4254
  break;
4134
4255
  }
4135
4256
  if (depth <= 0) return `${d.tag}(\u2026)`;
4136
- const kids = childrenOf(d).map((k) => render(k, depth - 1));
4257
+ const kids = childrenOf2(d).map((k) => render(k, depth - 1));
4137
4258
  switch (d.tag) {
4138
4259
  case "sequence":
4139
4260
  return `sequence(${kids.join(", ")})`;
@@ -4328,7 +4449,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4328
4449
  if (hit !== void 0) return hit;
4329
4450
  const d = p._def;
4330
4451
  shapeCache.set(p, interner.hole);
4331
- const kids = childrenOf(d);
4452
+ const kids = childrenOf2(d);
4332
4453
  const id = interner.of(`${payloadKey(p, d)}|${kids.map(shapeOf).join(",")}`);
4333
4454
  shapeCache.set(p, id);
4334
4455
  return id;
@@ -4338,7 +4459,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4338
4459
  if (hit !== void 0) return hit;
4339
4460
  sizeCache.set(p, 1);
4340
4461
  let n = 1;
4341
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4462
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4342
4463
  sizeCache.set(p, n);
4343
4464
  return n;
4344
4465
  };
@@ -4351,7 +4472,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4351
4472
  visited.set(p, { p, d, site: here, shape: shapeOf(p), size: sizeOf(p), enclosingNode, enclosingReducer });
4352
4473
  const inner = d.tag === "node" ? d.type ?? "(anonymous)" : enclosingNode;
4353
4474
  const innerReducer = enclosingReducer || d.tag === "node" && (d.build !== void 0 || d.buildSrc !== void 0) || d.tag === "transform" || d.tag === "leaf";
4354
- const kids = childrenOf(d);
4475
+ const kids = childrenOf2(d);
4355
4476
  kids.forEach((k, i) => {
4356
4477
  const seg = segment(d, i);
4357
4478
  visit(k, here.rule, here.path === "" ? seg : `${here.path} \u203A ${seg}`, inner, innerReducer);
@@ -4411,7 +4532,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4411
4532
  function markSubtree(p, into) {
4412
4533
  if (into.has(p)) return;
4413
4534
  into.add(p);
4414
- for (const k of childrenOf(p._def)) markSubtree(k, into);
4535
+ for (const k of childrenOf2(p._def)) markSubtree(k, into);
4415
4536
  }
4416
4537
  var stripMembers = (f) => {
4417
4538
  const { _members, ...rest } = f;
@@ -4451,7 +4572,7 @@ function findNearDuplicates(visited, interner, shapeOf, minSize) {
4451
4572
  if (hit !== void 0) return hit;
4452
4573
  holeCache.set(p, []);
4453
4574
  const d = p._def;
4454
- const kids = childrenOf(d);
4575
+ const kids = childrenOf2(d);
4455
4576
  const kidShapes = kids.map(shapeOf);
4456
4577
  const payload = payloadKey(p, d);
4457
4578
  const out2 = [];
@@ -4512,7 +4633,7 @@ function findDivergentSlot(members, shapeOf) {
4512
4633
  let cur = [...members];
4513
4634
  for (let guard2 = 0; guard2 < 64; guard2++) {
4514
4635
  const d0 = cur[0]._def;
4515
- const kidLists = cur.map((c) => childrenOf(c._def));
4636
+ const kidLists = cur.map((c) => childrenOf2(c._def));
4516
4637
  const arity = kidLists[0].length;
4517
4638
  if (kidLists.some((k) => k.length !== arity) || arity === 0) break;
4518
4639
  const diff = [];
@@ -4525,7 +4646,7 @@ function findDivergentSlot(members, shapeOf) {
4525
4646
  path.push(i);
4526
4647
  defs.push(d0);
4527
4648
  cur = kidLists.map((k) => k[i]);
4528
- const nextArity = childrenOf(cur[0]._def).length;
4649
+ const nextArity = childrenOf2(cur[0]._def).length;
4529
4650
  const samePayload = cur.every((c) => payloadKey(c, c._def) === payloadKey(cur[0], cur[0]._def));
4530
4651
  if (!samePayload || nextArity === 0) break;
4531
4652
  }
@@ -4533,7 +4654,7 @@ function findDivergentSlot(members, shapeOf) {
4533
4654
  let slotSize = 0;
4534
4655
  const sizeOf = (p) => {
4535
4656
  let n = 1;
4536
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4657
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4537
4658
  return n;
4538
4659
  };
4539
4660
  for (const c of cur) slotSize = Math.max(slotSize, sizeOf(c));
@@ -4542,7 +4663,7 @@ function findDivergentSlot(members, shapeOf) {
4542
4663
  function renderWithHole(p, path, depth = 0) {
4543
4664
  if (depth === path.length) return "\u2039slot\u203A";
4544
4665
  const d = p._def;
4545
- const kids = childrenOf(d);
4666
+ const kids = childrenOf2(d);
4546
4667
  const idx = path[depth];
4547
4668
  const parts = kids.map((k, i) => i === idx ? renderWithHole(k, path, depth + 1) : render(k, 2));
4548
4669
  switch (d.tag) {
@@ -5000,7 +5121,7 @@ function capturesPositionally(p, seen = /* @__PURE__ */ new Set()) {
5000
5121
  seen.add(p);
5001
5122
  const d = p._def;
5002
5123
  if (d.tag === "field" || d.tag === "label") return true;
5003
- for (const k of childrenOf(d)) if (capturesPositionally(k, seen)) return true;
5124
+ for (const k of childrenOf2(d)) if (capturesPositionally(k, seen)) return true;
5004
5125
  return false;
5005
5126
  }
5006
5127
  function detectHandRolledSepBy(terms, shapeOf) {
@@ -5106,7 +5227,7 @@ function nestedNodeTypes(root, out, seen, isRoot) {
5106
5227
  }
5107
5228
  return;
5108
5229
  }
5109
- for (const k of childrenOf(d)) nestedNodeTypes(k, out, seen, false);
5230
+ for (const k of childrenOf2(d)) nestedNodeTypes(k, out, seen, false);
5110
5231
  }
5111
5232
  var MAX_LOST_TYPES = 8;
5112
5233
  function findStructureLoss(visited, resolve) {
@@ -8293,7 +8414,7 @@ function routedNeedsContextBridge(p, ctx, seen = /* @__PURE__ */ new Set()) {
8293
8414
  const d = p._def;
8294
8415
  if (d.tag === "dispatch") return false;
8295
8416
  if (d.tag === "withCtx") return parserUsesRouted2(d.parser);
8296
- return childrenOf2(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8417
+ return childrenOf3(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8297
8418
  }
8298
8419
  function armReplayInScope(arm, ctx) {
8299
8420
  let p = arm;
@@ -9056,7 +9177,8 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
9056
9177
  const nodeSpanV = ctx.lineTracking ? v(ctx, "_nspan") : nodeSpanExpr;
9057
9178
  if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
9058
9179
  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})`;
9059
- const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
9180
+ const hostBuildArgs = `${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV}${def.tags !== void 0 && def.tags.length > 0 ? `, ${JSON.stringify(def.tags)}` : ""}`;
9181
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${hostBuildArgs})`;
9060
9182
  const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
9061
9183
  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;
9062
9184
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
@@ -9519,7 +9641,7 @@ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9519
9641
  return false;
9520
9642
  }
9521
9643
  }
9522
- return childrenOf2(d).some((child) => hasLineTrackingDef(child, seen));
9644
+ return childrenOf3(d).some((child) => hasLineTrackingDef(child, seen));
9523
9645
  }
9524
9646
  function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9525
9647
  if (seen.has(p)) return false;
@@ -9538,7 +9660,7 @@ function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9538
9660
  case "dispatch":
9539
9661
  return false;
9540
9662
  default:
9541
- return childrenOf2(d).some((child) => parserUsesRouted2(child, seen));
9663
+ return childrenOf3(d).some((child) => parserUsesRouted2(child, seen));
9542
9664
  }
9543
9665
  }
9544
9666
  function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
@@ -9553,9 +9675,9 @@ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
9553
9675
  return false;
9554
9676
  }
9555
9677
  }
9556
- return childrenOf2(d).some((child) => hasDirectBuildDef(child, seen));
9678
+ return childrenOf3(d).some((child) => hasDirectBuildDef(child, seen));
9557
9679
  }
9558
- function childrenOf2(def) {
9680
+ function childrenOf3(def) {
9559
9681
  switch (def.tag) {
9560
9682
  case "sequence":
9561
9683
  case "choice":
@@ -9630,7 +9752,7 @@ function subtreeSizes(roots) {
9630
9752
  const def = p._def;
9631
9753
  let s = 1;
9632
9754
  if (def.tag !== "lazy") {
9633
- for (const child of childrenOf2(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
9755
+ for (const child of childrenOf3(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
9634
9756
  }
9635
9757
  sizes.set(p, s);
9636
9758
  return s;
@@ -9651,7 +9773,7 @@ function subtreeSizes(roots) {
9651
9773
  return;
9652
9774
  }
9653
9775
  sz(p);
9654
- for (const child of childrenOf2(def)) visit(child);
9776
+ for (const child of childrenOf3(def)) visit(child);
9655
9777
  };
9656
9778
  for (const root of roots) visit(root);
9657
9779
  return sizes;
@@ -9661,7 +9783,7 @@ function analyzeLazyUsageMulti(roots) {
9661
9783
  const recursive = /* @__PURE__ */ new Set();
9662
9784
  const descended = /* @__PURE__ */ new Set();
9663
9785
  const active = /* @__PURE__ */ new Set();
9664
- function walk2(p) {
9786
+ function walk(p) {
9665
9787
  const def = p._def;
9666
9788
  if (def.tag === "lazy") {
9667
9789
  counts.set(p, (counts.get(p) ?? 0) + 1);
@@ -9678,17 +9800,17 @@ function analyzeLazyUsageMulti(roots) {
9678
9800
  return;
9679
9801
  }
9680
9802
  active.add(p);
9681
- walk2(resolved);
9803
+ walk(resolved);
9682
9804
  active.delete(p);
9683
9805
  return;
9684
9806
  }
9685
9807
  if (isHoistableTag(def.tag)) {
9686
9808
  counts.set(p, (counts.get(p) ?? 0) + 1);
9687
9809
  }
9688
- for (const child of childrenOf2(def)) walk2(child);
9810
+ for (const child of childrenOf3(def)) walk(child);
9689
9811
  }
9690
9812
  const rootsArr = [...roots];
9691
- for (const root of rootsArr) walk2(root);
9813
+ for (const root of rootsArr) walk(root);
9692
9814
  return { counts, recursive, sizes: subtreeSizes(rootsArr) };
9693
9815
  }
9694
9816
  function ruleDependencies(ruleMap) {
@@ -9698,7 +9820,7 @@ function ruleDependencies(ruleMap) {
9698
9820
  for (const [name, comb] of ruleMap) {
9699
9821
  const found = /* @__PURE__ */ new Set();
9700
9822
  const seen = /* @__PURE__ */ new Set();
9701
- const walk2 = (p, isRoot) => {
9823
+ const walk = (p, isRoot) => {
9702
9824
  const def = p._def;
9703
9825
  const boundary = def.tag === "lazy" ? nameOf.get(p) ?? p._ruleName : nameOf.get(p);
9704
9826
  if (!isRoot && boundary !== void 0) {
@@ -9714,12 +9836,12 @@ function ruleDependencies(ruleMap) {
9714
9836
  } catch {
9715
9837
  return;
9716
9838
  }
9717
- walk2(resolved, false);
9839
+ walk(resolved, false);
9718
9840
  return;
9719
9841
  }
9720
- for (const child of childrenOf2(def)) walk2(child, false);
9842
+ for (const child of childrenOf3(def)) walk(child, false);
9721
9843
  };
9722
- walk2(comb, true);
9844
+ walk(comb, true);
9723
9845
  deps.set(name, [...found]);
9724
9846
  }
9725
9847
  return deps;
@@ -10064,6 +10186,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10064
10186
  return false;
10065
10187
  }
10066
10188
  });
10189
+ const nodeMeta = new Map(ruleMap.map(([name, rule]) => [name, collectGrammarReflection([[name, rule]], { followLazy: false })]));
10067
10190
  const ctx = {
10068
10191
  vars: 0,
10069
10192
  indent: 1,
@@ -10139,7 +10262,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10139
10262
  scanExternal(resolved);
10140
10263
  return;
10141
10264
  }
10142
- for (const child of childrenOf2(def)) scanExternal(child);
10265
+ for (const child of childrenOf3(def)) scanExternal(child);
10143
10266
  };
10144
10267
  for (const [, rule] of ruleMap) scanExternal(rule);
10145
10268
  }
@@ -10261,6 +10384,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10261
10384
  hostBranchElided: !!ctx.hostBranchElided,
10262
10385
  hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
10263
10386
  isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs),
10387
+ nodeMeta,
10264
10388
  mfFns: mfSrcs ? [] : ctx.mapFns,
10265
10389
  buildFns: buildSrcs ? [] : ctx.buildFns
10266
10390
  };
@@ -10281,7 +10405,7 @@ function hasExternalRuleRef(ruleMap) {
10281
10405
  if (resolved === void 0) return p._ruleName !== void 0;
10282
10406
  return visit(resolved);
10283
10407
  }
10284
- return childrenOf2(def).some(visit);
10408
+ return childrenOf3(def).some(visit);
10285
10409
  };
10286
10410
  return ruleMap.some(([, rule]) => visit(rule));
10287
10411
  }
@@ -10303,7 +10427,7 @@ function hasSemanticReduction(roots, externalRefs) {
10303
10427
  return true;
10304
10428
  }
10305
10429
  }
10306
- return childrenOf2(def).some(visit);
10430
+ return childrenOf3(def).some(visit);
10307
10431
  };
10308
10432
  return roots.some(visit);
10309
10433
  }
@@ -10376,7 +10500,7 @@ function withCtx(extra, combinator) {
10376
10500
  }
10377
10501
 
10378
10502
  // src/compiler/ir-serialize.ts
10379
- function childrenOf3(def) {
10503
+ function childrenOf4(def) {
10380
10504
  switch (def.tag) {
10381
10505
  case "sequence":
10382
10506
  case "choice":
@@ -10440,6 +10564,10 @@ function projectOpt(project) {
10440
10564
  if (project === void 0) return void 0;
10441
10565
  return `project: ${project}`;
10442
10566
  }
10567
+ function tagsOpt(tags) {
10568
+ if (tags === void 0 || tags.length === 0) return void 0;
10569
+ return `tags: ${JSON.stringify(tags)}`;
10570
+ }
10443
10571
  function matcherExpr(entry) {
10444
10572
  switch (entry.kind) {
10445
10573
  case "startsWith":
@@ -10653,7 +10781,7 @@ var Serializer = class {
10653
10781
  if (target) this.assertNoRuleRef(target, seen);
10654
10782
  return;
10655
10783
  }
10656
- for (const child of childrenOf3(c._def)) this.assertNoRuleRef(child, seen);
10784
+ for (const child of childrenOf4(c._def)) this.assertNoRuleRef(child, seen);
10657
10785
  }
10658
10786
  /** A `rules()` map wraps each rule value in a named `lazy` proxy; the real body is
10659
10787
  * its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
@@ -10717,7 +10845,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10717
10845
  this.counts.set(c, (this.counts.get(c) ?? 0) + 1);
10718
10846
  if ((this.counts.get(c) ?? 0) > 1) return;
10719
10847
  active.add(c);
10720
- for (const child of childrenOf3(def)) this.analyze(child, active);
10848
+ for (const child of childrenOf4(def)) this.analyze(child, active);
10721
10849
  active.delete(c);
10722
10850
  }
10723
10851
  /** Reference `c`: a const var if it has one (emitting its decl first), else inline.
@@ -10834,12 +10962,14 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10834
10962
  case "node": {
10835
10963
  if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
10836
10964
  const projectEntry = projectOpt(def.project);
10965
+ const tagEntry = tagsOpt(def.tags);
10837
10966
  const optEntries = [
10838
10967
  ...def.unwrap ? ["unwrap: true"] : [],
10839
10968
  ...def.collapse ? ["collapse: true"] : [],
10840
10969
  ...projectEntry === void 0 ? [] : [projectEntry],
10841
10970
  ...def.captureTrivia ? ["captureTrivia: true"] : [],
10842
- ...def.trailingTrivia ? ["trailingTrivia: true"] : []
10971
+ ...def.trailingTrivia ? ["trailingTrivia: true"] : [],
10972
+ ...tagEntry === void 0 ? [] : [tagEntry]
10843
10973
  ];
10844
10974
  const opts = optEntries.length > 0 ? `, { ${optEntries.join(", ")} }` : "";
10845
10975
  if (def.type === void 0) {
@@ -10887,15 +11017,16 @@ function normalizeCstCollapse(collapse) {
10887
11017
  }
10888
11018
  return typeof collapse === "function" ? collapse : void 0;
10889
11019
  }
10890
- function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
10891
- return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children2] };
11020
+ function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state, tags) {
11021
+ 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] };
10892
11022
  }
10893
- function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
11023
+ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state, _tags) {
10894
11024
  if (typeof typeOrOptions === "string") {
10895
11025
  return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
10896
11026
  }
10897
11027
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
10898
- const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2);
11028
+ const materializeTags = typeOrOptions?.tags === true;
11029
+ const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2, tags) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2, materializeTags ? tags : void 0);
10899
11030
  host._parsemanCstOutput = true;
10900
11031
  if (collapse) host._parsemanCstCollapse = collapse;
10901
11032
  return host;
@@ -10937,6 +11068,7 @@ function fusedBody(pieces) {
10937
11068
  ...[...winner].map(([k, p]) => p.ruleFns.get(k))
10938
11069
  ];
10939
11070
  const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
11071
+ const reflection = mergeGrammarReflections([...winner].map(([k, p]) => p.nodeMeta.get(k) ?? { nodes: [] }));
10940
11072
  const { mode, elided } = hostModeOfPieces(pieces);
10941
11073
  const rawBody = [
10942
11074
  ...lines,
@@ -10945,6 +11077,7 @@ function fusedBody(pieces) {
10945
11077
  "}",
10946
11078
  `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_MODE.description)}), { value: ${JSON.stringify(mode)}, enumerable: false })`,
10947
11079
  `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_ELIDED.description)}), { value: ${JSON.stringify(elided)}, enumerable: false })`,
11080
+ `Object.defineProperty(_map, Symbol.for('parseman.grammarReflection'), { value: ${grammarReflectionSource(reflection)}, enumerable: false })`,
10948
11081
  // …and on each rule FUNCTION, because `run(map.Rule, …)` is handed the rule, not the
10949
11082
  // map, and would otherwise have nothing to check against. Done once at fuse time,
10950
11083
  // before any rule has been called, so it costs nothing per parse.
@@ -12373,38 +12506,48 @@ function buildTriviaIndex(root, input = "", opts) {
12373
12506
  }
12374
12507
 
12375
12508
  // src/cst/walk.ts
12376
- function walk(root, visitor, ctx = void 0) {
12377
- const go = (node2, parent) => {
12378
- const descend = visitor.enter ? visitor.enter(node2, parent, ctx) : void 0;
12379
- const children2 = node2.children;
12380
- if (descend !== false && Array.isArray(children2)) {
12381
- for (const child of children2) go(child, node2);
12382
- }
12383
- visitor.leave?.(node2, parent, ctx);
12384
- };
12385
- go(root, null);
12386
- }
12387
- function createVisitor(handlers) {
12388
- const api = {
12389
- visit(node2) {
12390
- const handler = node2.type !== void 0 ? handlers[node2.type] : void 0;
12391
- if (handler) return handler(node2, api);
12392
- api.visitChildren(node2);
12393
- return void 0;
12394
- },
12395
- visitChildren(node2) {
12396
- const out = [];
12397
- const children2 = node2.children;
12398
- if (Array.isArray(children2)) {
12399
- for (const child of children2) {
12400
- const r = api.visit(child);
12401
- if (r !== void 0) out.push(r);
12509
+ function isCombinator(value) {
12510
+ return !!value && typeof value === "object" && "_def" in value && "_meta" in value && typeof value.parse === "function";
12511
+ }
12512
+ function reflectionFor(grammar) {
12513
+ const stamped = grammarReflectionOf(grammar);
12514
+ if (stamped) return stamped;
12515
+ const entries = Object.entries(grammar).filter((entry) => isCombinator(entry[1]));
12516
+ return collectGrammarReflection(entries);
12517
+ }
12518
+ function createVisitor(grammar, spec) {
12519
+ const reflection = reflectionFor(grammar);
12520
+ const typeHandlers = spec.type;
12521
+ const tagSpec = spec.tag;
12522
+ const tagHandlersByType = /* @__PURE__ */ new Map();
12523
+ if (tagSpec !== void 0) {
12524
+ for (const node2 of reflection.nodes) {
12525
+ const handlers = [];
12526
+ for (const tag of node2.tags) {
12527
+ const handler = tagSpec[tag];
12528
+ if (handler) handlers.push(handler);
12529
+ }
12530
+ if (handlers.length > 0) tagHandlersByType.set(node2.type, handlers);
12531
+ }
12532
+ }
12533
+ return (root, ctx = void 0) => {
12534
+ const go = (node2, parent) => {
12535
+ const descend = spec.enter ? spec.enter(node2, parent, ctx) : void 0;
12536
+ if (node2._tag === "node" && node2.type !== void 0) {
12537
+ typeHandlers?.[node2.type]?.(node2, parent, ctx);
12538
+ const tagHandlers = tagHandlersByType.get(node2.type);
12539
+ if (tagHandlers !== void 0) {
12540
+ for (const handler of tagHandlers) handler(node2, parent, ctx);
12402
12541
  }
12403
12542
  }
12404
- return out;
12405
- }
12543
+ const children2 = node2.children;
12544
+ if (descend !== false && Array.isArray(children2)) {
12545
+ for (const child of children2) go(child, node2);
12546
+ }
12547
+ spec.leave?.(node2, parent, ctx);
12548
+ };
12549
+ go(root, null);
12406
12550
  };
12407
- return (node2) => api.visit(node2);
12408
12551
  }
12409
12552
 
12410
12553
  // src/cst/offset-model.ts