parseman 0.42.0 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/combinators/node.d.ts +21 -7
  2. package/dist/combinators/node.d.ts.map +1 -1
  3. package/dist/combinators/parser.d.ts +23 -2
  4. package/dist/combinators/parser.d.ts.map +1 -1
  5. package/dist/compiler/codegen.d.ts +6 -0
  6. package/dist/compiler/codegen.d.ts.map +1 -1
  7. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  8. package/dist/compiler/linker.d.ts +7 -1
  9. package/dist/compiler/linker.d.ts.map +1 -1
  10. package/dist/cst/reflection.d.ts +24 -0
  11. package/dist/cst/reflection.d.ts.map +1 -0
  12. package/dist/cst/types.d.ts +2 -0
  13. package/dist/cst/types.d.ts.map +1 -1
  14. package/dist/cst/walk.d.ts +27 -62
  15. package/dist/cst/walk.d.ts.map +1 -1
  16. package/dist/index.cjs +375 -195
  17. package/dist/index.cjs.map +4 -4
  18. package/dist/index.d.ts +4 -3
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +375 -195
  21. package/dist/index.js.map +4 -4
  22. package/dist/language-service/index.cjs +25 -33
  23. package/dist/language-service/index.cjs.map +4 -4
  24. package/dist/language-service/index.d.ts +1 -1
  25. package/dist/language-service/index.d.ts.map +1 -1
  26. package/dist/language-service/index.js +25 -33
  27. package/dist/language-service/index.js.map +4 -4
  28. package/dist/plugin/evaluator.d.ts +1 -0
  29. package/dist/plugin/evaluator.d.ts.map +1 -1
  30. package/dist/plugin/index.cjs +654 -173
  31. package/dist/plugin/index.cjs.map +4 -4
  32. package/dist/plugin/index.d.ts.map +1 -1
  33. package/dist/plugin/index.js +654 -173
  34. package/dist/plugin/index.js.map +4 -4
  35. package/dist/spec/index.cjs +429 -313
  36. package/dist/spec/index.cjs.map +4 -4
  37. package/dist/spec/index.js +429 -313
  38. package/dist/spec/index.js.map +4 -4
  39. package/dist/types.d.ts +8 -1
  40. package/dist/types.d.ts.map +1 -1
  41. package/dist/version.d.ts +1 -1
  42. package/package.json +1 -1
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
@@ -2898,6 +2897,221 @@ function markUnusedValues(root) {
2898
2897
  visit(resolveRoot(root), true);
2899
2898
  }
2900
2899
 
2900
+ // src/combinators/grammar.ts
2901
+ function annotateResultLines(result, index) {
2902
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
2903
+ const errors = annotated.errors;
2904
+ if (errors) {
2905
+ ;
2906
+ annotated.errors = errors.map((error) => ({
2907
+ ...error,
2908
+ span: annotateSpan(error.span, index)
2909
+ }));
2910
+ }
2911
+ return annotated;
2912
+ }
2913
+ function createParseLineContext(input, pos) {
2914
+ const lineIndex = createLineIndex();
2915
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
2916
+ return { lineIndex, lineScannedTo: pos };
2917
+ }
2918
+ function parser(opts, root) {
2919
+ const clearTrivia = opts.trivia === null;
2920
+ return {
2921
+ _tag: "grammar",
2922
+ _meta: root._meta,
2923
+ _def: {
2924
+ tag: "grammar",
2925
+ parser: root,
2926
+ triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
2927
+ clearTrivia,
2928
+ ...opts.captureTrivia ? { captureTrivia: true } : {},
2929
+ trackLines: opts.trackLines ?? false
2930
+ },
2931
+ parse(input, pos, _ctx) {
2932
+ const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
2933
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
2934
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
2935
+ const ctx = {
2936
+ ..._ctx,
2937
+ trackLines,
2938
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
2939
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
2940
+ // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
2941
+ ...clearTrivia ? {
2942
+ trivia: void 0,
2943
+ triviaKindLabels: void 0
2944
+ } : opts.trivia != null ? {
2945
+ trivia: opts.trivia,
2946
+ ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
2947
+ } : {},
2948
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
2949
+ // Kind-filter for per-node capture. Resolve against this scope's trivia
2950
+ // labels — this parser's own trivia if it declares one, else the INHERITED
2951
+ // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
2952
+ // trivia is inherited rather than re-declared here. No labels → undefined
2953
+ // (capture all).
2954
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
2955
+ };
2956
+ const result = root.parse(input, pos ?? 0, ctx);
2957
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
2958
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
2959
+ }
2960
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
2961
+ }
2962
+ };
2963
+ }
2964
+ function noTrivia(root) {
2965
+ return parser({ trivia: null }, root);
2966
+ }
2967
+ var _analyzed = /* @__PURE__ */ new WeakSet();
2968
+ function parse(combinator, input, opts = {}) {
2969
+ if (!_analyzed.has(combinator)) {
2970
+ _analyzed.add(combinator);
2971
+ markUnusedValues(combinator);
2972
+ }
2973
+ const trackLines = opts.trackLines ?? combinator._meta.grammarTrackLines ?? false;
2974
+ const lineContext = trackLines ? createParseLineContext(input, 0) : void 0;
2975
+ const lineIndex = lineContext?.lineIndex;
2976
+ const _errors = opts.recover ? [] : void 0;
2977
+ const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
2978
+ const grammarTrivia = combinator._meta.grammarTrivia;
2979
+ const grammarScanSkip = combinator._meta.grammarScanSkip;
2980
+ const ctx = {
2981
+ trackLines,
2982
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
2983
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
2984
+ ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
2985
+ ...grammarScanSkip !== void 0 ? { scanSkip: grammarScanSkip } : {},
2986
+ ..._errors !== void 0 ? { _errors } : {},
2987
+ ..._probe !== void 0 ? { _probe } : {}
2988
+ };
2989
+ const result = combinator.parse(input, 0, ctx);
2990
+ if (!result.ok) return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
2991
+ const withErrors = _errors !== void 0 ? { ...result, errors: _errors, furthestFail: _probe?.best ?? null } : result;
2992
+ return lineIndex ? annotateResultLines(withErrors, normalizeLineIndex(lineIndex)) : withErrors;
2993
+ }
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
+
2901
3115
  // src/combinators/parser.ts
2902
3116
  var RULE_ORDER = "__parsemanRuleOrder";
2903
3117
  function tagRule(r, key) {
@@ -2973,6 +3187,21 @@ function rules(a, b) {
2973
3187
  if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
2974
3188
  }
2975
3189
  }
3190
+ if (options?.trackLines === true) {
3191
+ for (const key of Object.keys(definitions)) {
3192
+ const rule = cache[key];
3193
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
3194
+ }
3195
+ for (const key of Object.keys(definitions)) {
3196
+ const rule = cache[key];
3197
+ if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
3198
+ const wrapped = parser({ trackLines: true }, rule);
3199
+ tagRule(wrapped, key);
3200
+ wrapped._meta.grammarTrackLines = true;
3201
+ cache[key] = wrapped;
3202
+ }
3203
+ }
3204
+ }
2976
3205
  for (const key of Object.keys(definitions)) {
2977
3206
  markUnusedValues(cache[key]);
2978
3207
  }
@@ -2981,6 +3210,7 @@ function rules(a, b) {
2981
3210
  enumerable: false,
2982
3211
  configurable: true
2983
3212
  });
3213
+ attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
2984
3214
  return cache;
2985
3215
  }
2986
3216
 
@@ -3301,13 +3531,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3301
3531
  const collapse = opts?.collapse === true;
3302
3532
  const captureTrivia = opts?.captureTrivia === true;
3303
3533
  const trailingTrivia = opts?.trailingTrivia === true;
3534
+ const tags = opts?.tags;
3304
3535
  if (unwrap && collapse) {
3305
3536
  throw new Error("node() options cannot set both unwrap and collapse");
3306
3537
  }
3307
3538
  if (project !== void 0 && (unwrap || collapse)) {
3308
3539
  throw new Error("node() options cannot combine project with unwrap or collapse");
3309
3540
  }
3310
- 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;
3311
3542
  const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
3312
3543
  const clonesState = build ? buildReadsState(def) : project === void 0;
3313
3544
  const hasOwnFields = parserHasOwnFields(combinator);
@@ -3353,7 +3584,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3353
3584
  const cstOutput = cstOutputHost(ctx.build);
3354
3585
  const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3355
3586
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3356
- 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 };
3357
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 };
3358
3589
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3359
3590
  pushCstChild(ctx, built, rawEntry);
@@ -3429,101 +3660,6 @@ function field(name, combinator) {
3429
3660
  };
3430
3661
  }
3431
3662
 
3432
- // src/combinators/grammar.ts
3433
- function annotateResultLines(result, index) {
3434
- const annotated = { ...result, span: annotateSpan(result.span, index) };
3435
- const errors = annotated.errors;
3436
- if (errors) {
3437
- ;
3438
- annotated.errors = errors.map((error) => ({
3439
- ...error,
3440
- span: annotateSpan(error.span, index)
3441
- }));
3442
- }
3443
- return annotated;
3444
- }
3445
- function createParseLineContext(input, pos) {
3446
- const lineIndex = createLineIndex();
3447
- if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
3448
- return { lineIndex, lineScannedTo: pos };
3449
- }
3450
- function parser(opts, root) {
3451
- const clearTrivia = opts.trivia === null;
3452
- return {
3453
- _tag: "grammar",
3454
- _meta: root._meta,
3455
- _def: {
3456
- tag: "grammar",
3457
- parser: root,
3458
- triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
3459
- clearTrivia,
3460
- ...opts.captureTrivia ? { captureTrivia: true } : {},
3461
- trackLines: opts.trackLines ?? false
3462
- },
3463
- parse(input, pos, _ctx) {
3464
- const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
3465
- const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
3466
- const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
3467
- const ctx = {
3468
- ..._ctx,
3469
- trackLines,
3470
- ...lineIndex ? { _lineIndex: lineIndex } : {},
3471
- ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
3472
- // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
3473
- ...clearTrivia ? {
3474
- trivia: void 0,
3475
- triviaKindLabels: void 0
3476
- } : opts.trivia != null ? {
3477
- trivia: opts.trivia,
3478
- ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
3479
- } : {},
3480
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
3481
- // Kind-filter for per-node capture. Resolve against this scope's trivia
3482
- // labels — this parser's own trivia if it declares one, else the INHERITED
3483
- // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
3484
- // trivia is inherited rather than re-declared here. No labels → undefined
3485
- // (capture all).
3486
- ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
3487
- };
3488
- const result = root.parse(input, pos ?? 0, ctx);
3489
- if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
3490
- _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
3491
- }
3492
- return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
3493
- }
3494
- };
3495
- }
3496
- function noTrivia(root) {
3497
- return parser({ trivia: null }, root);
3498
- }
3499
- var _analyzed = /* @__PURE__ */ new WeakSet();
3500
- function parse(combinator, input, opts = {}) {
3501
- if (!_analyzed.has(combinator)) {
3502
- _analyzed.add(combinator);
3503
- markUnusedValues(combinator);
3504
- }
3505
- const trackLines = opts.trackLines ?? false;
3506
- const lineContext = trackLines ? createParseLineContext(input, 0) : void 0;
3507
- const lineIndex = lineContext?.lineIndex;
3508
- const _errors = opts.recover ? [] : void 0;
3509
- const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
3510
- const grammarTrivia = combinator._meta.grammarTrivia;
3511
- const grammarScanSkip = combinator._meta.grammarScanSkip;
3512
- const ctx = {
3513
- trackLines,
3514
- ...lineIndex ? { _lineIndex: lineIndex } : {},
3515
- ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
3516
- ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
3517
- ...grammarScanSkip !== void 0 ? { scanSkip: grammarScanSkip } : {},
3518
- ..._errors !== void 0 ? { _errors } : {},
3519
- ..._probe !== void 0 ? { _probe } : {}
3520
- };
3521
- const result = combinator.parse(input, 0, ctx);
3522
- if (!result.ok) return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
3523
- const withErrors = _errors !== void 0 ? { ...result, errors: _errors, furthestFail: _probe?.best ?? null } : result;
3524
- return lineIndex ? annotateResultLines(withErrors, normalizeLineIndex(lineIndex)) : withErrors;
3525
- }
3526
-
3527
3663
  // src/combinators/token.ts
3528
3664
  function token(root) {
3529
3665
  const meta = {
@@ -3620,7 +3756,7 @@ function leaf(root, fn) {
3620
3756
  }
3621
3757
 
3622
3758
  // src/version.ts
3623
- var PARSEMAN_VERSION = "0.42.0";
3759
+ var PARSEMAN_VERSION = "0.43.0";
3624
3760
 
3625
3761
  // src/cst/host-mode.ts
3626
3762
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -3686,7 +3822,7 @@ var SUGGESTIONS = {
3686
3822
  function classifyBroadArm(arm, resolve) {
3687
3823
  const seen = /* @__PURE__ */ new Set();
3688
3824
  const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
3689
- const walk2 = (p) => {
3825
+ const walk = (p) => {
3690
3826
  if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
3691
3827
  seen.add(p);
3692
3828
  const d = p._def;
@@ -3700,7 +3836,7 @@ function classifyBroadArm(arm, resolve) {
3700
3836
  // peek() CARRIES its body's first-set, so it is only broad when the body is
3701
3837
  // (or the body is nullable) — walk in for the real cause.
3702
3838
  case "peek":
3703
- return walk2(d.parser);
3839
+ return walk(d.parser);
3704
3840
  case "scanTo":
3705
3841
  return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
3706
3842
  // `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
@@ -3714,7 +3850,7 @@ function classifyBroadArm(arm, resolve) {
3714
3850
  case "attempt":
3715
3851
  case "withCtx":
3716
3852
  case "recover":
3717
- return walk2(d.parser);
3853
+ return walk(d.parser);
3718
3854
  case "lazy": {
3719
3855
  const name = p._ruleName;
3720
3856
  let target;
@@ -3726,13 +3862,13 @@ function classifyBroadArm(arm, resolve) {
3726
3862
  return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
3727
3863
  target = bound;
3728
3864
  }
3729
- const inner = walk2(target);
3865
+ const inner = walk(target);
3730
3866
  if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
3731
3867
  return inner;
3732
3868
  }
3733
3869
  case "optional":
3734
3870
  case "many": {
3735
- const inner = walk2(d.parser);
3871
+ const inner = walk(d.parser);
3736
3872
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3737
3873
  }
3738
3874
  case "oneOrMore":
@@ -3745,9 +3881,9 @@ function classifyBroadArm(arm, resolve) {
3745
3881
  case "node":
3746
3882
  case "grammar":
3747
3883
  case "expect":
3748
- return walk2(d.parser);
3884
+ return walk(d.parser);
3749
3885
  case "skip":
3750
- return walk2(d.main);
3886
+ return walk(d.main);
3751
3887
  case "sequence": {
3752
3888
  let sawNullablePrefix = false;
3753
3889
  for (const t of d.parsers) {
@@ -3756,7 +3892,7 @@ function classifyBroadArm(arm, resolve) {
3756
3892
  continue;
3757
3893
  }
3758
3894
  if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve))) {
3759
- const inner = walk2(t);
3895
+ const inner = walk(t);
3760
3896
  if (sawNullablePrefix)
3761
3897
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3762
3898
  return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
@@ -3771,18 +3907,18 @@ function classifyBroadArm(arm, resolve) {
3771
3907
  }
3772
3908
  case "choice": {
3773
3909
  for (const a of d.parsers) {
3774
- const r = walk2(a);
3910
+ const r = walk(a);
3775
3911
  if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
3776
3912
  }
3777
3913
  return null;
3778
3914
  }
3779
3915
  case "dispatch":
3780
- return walk2(d.selector);
3916
+ return walk(d.selector);
3781
3917
  default:
3782
3918
  return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
3783
3919
  }
3784
3920
  };
3785
- return walk2(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3921
+ return walk(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3786
3922
  }
3787
3923
  function peelToLeading(arm) {
3788
3924
  let d = arm._def;
@@ -3984,7 +4120,7 @@ function formatGatingWarnings(report) {
3984
4120
 
3985
4121
  // src/analysis/duplication.ts
3986
4122
  var siteToString = (s) => s.path === "" ? s.rule : `${s.rule} \u203A ${s.path}`;
3987
- function childrenOf(d) {
4123
+ function childrenOf2(d) {
3988
4124
  switch (d.tag) {
3989
4125
  case "sequence":
3990
4126
  case "choice":
@@ -4118,7 +4254,7 @@ function render(p, depth = 3) {
4118
4254
  break;
4119
4255
  }
4120
4256
  if (depth <= 0) return `${d.tag}(\u2026)`;
4121
- const kids = childrenOf(d).map((k) => render(k, depth - 1));
4257
+ const kids = childrenOf2(d).map((k) => render(k, depth - 1));
4122
4258
  switch (d.tag) {
4123
4259
  case "sequence":
4124
4260
  return `sequence(${kids.join(", ")})`;
@@ -4313,7 +4449,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4313
4449
  if (hit !== void 0) return hit;
4314
4450
  const d = p._def;
4315
4451
  shapeCache.set(p, interner.hole);
4316
- const kids = childrenOf(d);
4452
+ const kids = childrenOf2(d);
4317
4453
  const id = interner.of(`${payloadKey(p, d)}|${kids.map(shapeOf).join(",")}`);
4318
4454
  shapeCache.set(p, id);
4319
4455
  return id;
@@ -4323,7 +4459,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4323
4459
  if (hit !== void 0) return hit;
4324
4460
  sizeCache.set(p, 1);
4325
4461
  let n = 1;
4326
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4462
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4327
4463
  sizeCache.set(p, n);
4328
4464
  return n;
4329
4465
  };
@@ -4336,7 +4472,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4336
4472
  visited.set(p, { p, d, site: here, shape: shapeOf(p), size: sizeOf(p), enclosingNode, enclosingReducer });
4337
4473
  const inner = d.tag === "node" ? d.type ?? "(anonymous)" : enclosingNode;
4338
4474
  const innerReducer = enclosingReducer || d.tag === "node" && (d.build !== void 0 || d.buildSrc !== void 0) || d.tag === "transform" || d.tag === "leaf";
4339
- const kids = childrenOf(d);
4475
+ const kids = childrenOf2(d);
4340
4476
  kids.forEach((k, i) => {
4341
4477
  const seg = segment(d, i);
4342
4478
  visit(k, here.rule, here.path === "" ? seg : `${here.path} \u203A ${seg}`, inner, innerReducer);
@@ -4396,7 +4532,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4396
4532
  function markSubtree(p, into) {
4397
4533
  if (into.has(p)) return;
4398
4534
  into.add(p);
4399
- for (const k of childrenOf(p._def)) markSubtree(k, into);
4535
+ for (const k of childrenOf2(p._def)) markSubtree(k, into);
4400
4536
  }
4401
4537
  var stripMembers = (f) => {
4402
4538
  const { _members, ...rest } = f;
@@ -4436,7 +4572,7 @@ function findNearDuplicates(visited, interner, shapeOf, minSize) {
4436
4572
  if (hit !== void 0) return hit;
4437
4573
  holeCache.set(p, []);
4438
4574
  const d = p._def;
4439
- const kids = childrenOf(d);
4575
+ const kids = childrenOf2(d);
4440
4576
  const kidShapes = kids.map(shapeOf);
4441
4577
  const payload = payloadKey(p, d);
4442
4578
  const out2 = [];
@@ -4497,7 +4633,7 @@ function findDivergentSlot(members, shapeOf) {
4497
4633
  let cur = [...members];
4498
4634
  for (let guard2 = 0; guard2 < 64; guard2++) {
4499
4635
  const d0 = cur[0]._def;
4500
- const kidLists = cur.map((c) => childrenOf(c._def));
4636
+ const kidLists = cur.map((c) => childrenOf2(c._def));
4501
4637
  const arity = kidLists[0].length;
4502
4638
  if (kidLists.some((k) => k.length !== arity) || arity === 0) break;
4503
4639
  const diff = [];
@@ -4510,7 +4646,7 @@ function findDivergentSlot(members, shapeOf) {
4510
4646
  path.push(i);
4511
4647
  defs.push(d0);
4512
4648
  cur = kidLists.map((k) => k[i]);
4513
- const nextArity = childrenOf(cur[0]._def).length;
4649
+ const nextArity = childrenOf2(cur[0]._def).length;
4514
4650
  const samePayload = cur.every((c) => payloadKey(c, c._def) === payloadKey(cur[0], cur[0]._def));
4515
4651
  if (!samePayload || nextArity === 0) break;
4516
4652
  }
@@ -4518,7 +4654,7 @@ function findDivergentSlot(members, shapeOf) {
4518
4654
  let slotSize = 0;
4519
4655
  const sizeOf = (p) => {
4520
4656
  let n = 1;
4521
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4657
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4522
4658
  return n;
4523
4659
  };
4524
4660
  for (const c of cur) slotSize = Math.max(slotSize, sizeOf(c));
@@ -4527,7 +4663,7 @@ function findDivergentSlot(members, shapeOf) {
4527
4663
  function renderWithHole(p, path, depth = 0) {
4528
4664
  if (depth === path.length) return "\u2039slot\u203A";
4529
4665
  const d = p._def;
4530
- const kids = childrenOf(d);
4666
+ const kids = childrenOf2(d);
4531
4667
  const idx = path[depth];
4532
4668
  const parts = kids.map((k, i) => i === idx ? renderWithHole(k, path, depth + 1) : render(k, 2));
4533
4669
  switch (d.tag) {
@@ -4985,7 +5121,7 @@ function capturesPositionally(p, seen = /* @__PURE__ */ new Set()) {
4985
5121
  seen.add(p);
4986
5122
  const d = p._def;
4987
5123
  if (d.tag === "field" || d.tag === "label") return true;
4988
- 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;
4989
5125
  return false;
4990
5126
  }
4991
5127
  function detectHandRolledSepBy(terms, shapeOf) {
@@ -5091,7 +5227,7 @@ function nestedNodeTypes(root, out, seen, isRoot) {
5091
5227
  }
5092
5228
  return;
5093
5229
  }
5094
- for (const k of childrenOf(d)) nestedNodeTypes(k, out, seen, false);
5230
+ for (const k of childrenOf2(d)) nestedNodeTypes(k, out, seen, false);
5095
5231
  }
5096
5232
  var MAX_LOST_TYPES = 8;
5097
5233
  function findStructureLoss(visited, resolve) {
@@ -6980,13 +7116,16 @@ function reindentStmts(stmts, targetLevels) {
6980
7116
  const targetPrefix = " ".repeat(targetLevels);
6981
7117
  return stmts.map((s) => s.trim().length === 0 ? "" : targetPrefix + s.slice(minLeading));
6982
7118
  }
6983
- function failReturn(expected, posExpr) {
7119
+ function failReturn(ctx, expected, posExpr) {
7120
+ if (ctx.lineTracking) return `return { ok: false, expected: [${expected}], span: ${emitSpanExpr(ctx, posExpr, posExpr)} }`;
6984
7121
  return `return { ok: false, expected: [${expected}], span: { start: ${posExpr}, end: ${posExpr} } }`;
6985
7122
  }
6986
- function failReturnArr(expectedArr, posExpr) {
7123
+ function failReturnArr(ctx, expectedArr, posExpr) {
7124
+ if (ctx.lineTracking) return `return { ok: false, expected: ${expectedArr}, span: ${emitSpanExpr(ctx, posExpr, posExpr)} }`;
6987
7125
  return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
6988
7126
  }
6989
- function committedReturnArr(expectedArr, posExpr) {
7127
+ function committedReturnArr(ctx, expectedArr, posExpr) {
7128
+ if (ctx.lineTracking) return `return { ok: false, expected: [...${expectedArr}], span: ${emitSpanExpr(ctx, posExpr, posExpr)}, committed: true }`;
6990
7129
  return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} }, committed: true }`;
6991
7130
  }
6992
7131
  function hoistExpected(ctx, constArrSource) {
@@ -7011,7 +7150,7 @@ function failBody(ctx, expected, posExpr) {
7011
7150
  if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
7012
7151
  return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; ${trace}break ${ctx.failLabel} }`;
7013
7152
  }
7014
- return probe + trace + failReturn(expected, posExpr);
7153
+ return probe + trace + failReturn(ctx, expected, posExpr);
7015
7154
  }
7016
7155
  function failArrBody(ctx, expectedArr, posExpr, constant = true) {
7017
7156
  const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
@@ -7021,18 +7160,19 @@ function failArrBody(ctx, expectedArr, posExpr, constant = true) {
7021
7160
  const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
7022
7161
  return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; ${trace}break ${ctx.failLabel} }`;
7023
7162
  }
7024
- if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
7025
- return probe + trace + failReturnArr(expectedArr, posExpr);
7163
+ if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: ${ctx.lineTracking ? emitSpanExpr(ctx, posExpr, posExpr) : `{ start: ${posExpr}, end: ${posExpr} }`} }`;
7164
+ return probe + trace + failReturnArr(ctx, expectedArr, posExpr);
7026
7165
  }
7027
- function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
7028
- return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} }, ...(_ctx._fc ? { committed: true } : {}) }`;
7166
+ function resultFromRecorded(ctx, feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
7167
+ const spanExpr = ctx.lineTracking ? emitSpanExpr(ctx, feExpr, feExpr) : `{ start: ${feExpr}, end: ${feExpr} }`;
7168
+ return `return { ok: false, expected: [...${fxExpr}], span: ${spanExpr}, ...(_ctx._fc ? { committed: true } : {}) }`;
7029
7169
  }
7030
7170
  function committedFailBody(ctx, expectedArr = "_ctx._fx", posExpr = "_ctx._fe") {
7031
7171
  if (ctx.failLabel) {
7032
7172
  if (!ctx.recordFail) return `{ _ctx._fc = true; break ${ctx.failLabel} }`;
7033
7173
  return `{ _ctx._fc = true; _ctx._fe = ${posExpr}; _ctx._fx = ${expectedArr}; break ${ctx.failLabel} }`;
7034
7174
  }
7035
- return committedReturnArr(expectedArr, posExpr);
7175
+ return committedReturnArr(ctx, expectedArr, posExpr);
7036
7176
  }
7037
7177
  function propagateFailBody(ctx, srcCtx = "_ctx") {
7038
7178
  if (srcCtx !== "_ctx") {
@@ -7040,10 +7180,10 @@ function propagateFailBody(ctx, srcCtx = "_ctx") {
7040
7180
  if (!ctx.recordFail) return `break ${ctx.failLabel}`;
7041
7181
  return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; break ${ctx.failLabel} }`;
7042
7182
  }
7043
- return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; ${resultFromRecorded()} }`;
7183
+ return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; ${resultFromRecorded(ctx)} }`;
7044
7184
  }
7045
7185
  if (ctx.failLabel) return `break ${ctx.failLabel}`;
7046
- return resultFromRecorded();
7186
+ return resultFromRecorded(ctx);
7047
7187
  }
7048
7188
  function emitIfFail(ctx, cond, body) {
7049
7189
  return [
@@ -8274,7 +8414,7 @@ function routedNeedsContextBridge(p, ctx, seen = /* @__PURE__ */ new Set()) {
8274
8414
  const d = p._def;
8275
8415
  if (d.tag === "dispatch") return false;
8276
8416
  if (d.tag === "withCtx") return parserUsesRouted2(d.parser);
8277
- return childrenOf2(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8417
+ return childrenOf3(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8278
8418
  }
8279
8419
  function armReplayInScope(arm, ctx) {
8280
8420
  let p = arm;
@@ -8476,7 +8616,7 @@ function emitAttempt(p, def, ctx, pos) {
8476
8616
  if (!ctx.recovery && !ctx.coverage && needsFirstSetGuard(def.parser)) {
8477
8617
  const gcV = v(ctx, "_agc");
8478
8618
  const gExp = armStaticExpected(ctx, def.parser);
8479
- const gFail = ctx.failLabel ? `{ _ctx._fe = ${pos};${ctx.recordFail ? ` _ctx._fx = ${gExp};` : ""} break ${ctx.failLabel} }` : `{ _ctx._fe = ${pos}; ${failReturnArr(gExp, pos)} }`;
8619
+ const gFail = ctx.failLabel ? `{ _ctx._fe = ${pos};${ctx.recordFail ? ` _ctx._fx = ${gExp};` : ""} break ${ctx.failLabel} }` : `{ _ctx._fe = ${pos}; ${failReturnArr(ctx, gExp, pos)} }`;
8480
8620
  preGuard.push(
8481
8621
  `${ind(ctx)}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
8482
8622
  `${ind(ctx)}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
@@ -8975,7 +9115,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8975
9115
  if (!ctx.recovery && !ctx.coverage && (capturesChildren || structural) && needsFirstSetGuard(def.parser)) {
8976
9116
  const gcV = v(ctx, "_ngc");
8977
9117
  const gExp = armStaticExpected(ctx, def.parser);
8978
- const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(gExp, pos);
9118
+ const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(ctx, gExp, pos);
8979
9119
  preGuard.push(
8980
9120
  `${i}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
8981
9121
  `${i}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
@@ -9037,7 +9177,8 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
9037
9177
  const nodeSpanV = ctx.lineTracking ? v(ctx, "_nspan") : nodeSpanExpr;
9038
9178
  if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
9039
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})`;
9040
- 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})`;
9041
9182
  const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
9042
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;
9043
9184
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
@@ -9491,6 +9632,7 @@ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9491
9632
  if (seen.has(p)) return false;
9492
9633
  seen.add(p);
9493
9634
  const d = p._def;
9635
+ if (p._meta.grammarTrackLines) return true;
9494
9636
  if (d.tag === "grammar" && d.trackLines) return true;
9495
9637
  if (d.tag === "lazy") {
9496
9638
  try {
@@ -9499,7 +9641,7 @@ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9499
9641
  return false;
9500
9642
  }
9501
9643
  }
9502
- return childrenOf2(d).some((child) => hasLineTrackingDef(child, seen));
9644
+ return childrenOf3(d).some((child) => hasLineTrackingDef(child, seen));
9503
9645
  }
9504
9646
  function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9505
9647
  if (seen.has(p)) return false;
@@ -9518,7 +9660,7 @@ function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9518
9660
  case "dispatch":
9519
9661
  return false;
9520
9662
  default:
9521
- return childrenOf2(d).some((child) => parserUsesRouted2(child, seen));
9663
+ return childrenOf3(d).some((child) => parserUsesRouted2(child, seen));
9522
9664
  }
9523
9665
  }
9524
9666
  function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
@@ -9533,9 +9675,9 @@ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
9533
9675
  return false;
9534
9676
  }
9535
9677
  }
9536
- return childrenOf2(d).some((child) => hasDirectBuildDef(child, seen));
9678
+ return childrenOf3(d).some((child) => hasDirectBuildDef(child, seen));
9537
9679
  }
9538
- function childrenOf2(def) {
9680
+ function childrenOf3(def) {
9539
9681
  switch (def.tag) {
9540
9682
  case "sequence":
9541
9683
  case "choice":
@@ -9610,7 +9752,7 @@ function subtreeSizes(roots) {
9610
9752
  const def = p._def;
9611
9753
  let s = 1;
9612
9754
  if (def.tag !== "lazy") {
9613
- 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);
9614
9756
  }
9615
9757
  sizes.set(p, s);
9616
9758
  return s;
@@ -9631,7 +9773,7 @@ function subtreeSizes(roots) {
9631
9773
  return;
9632
9774
  }
9633
9775
  sz(p);
9634
- for (const child of childrenOf2(def)) visit(child);
9776
+ for (const child of childrenOf3(def)) visit(child);
9635
9777
  };
9636
9778
  for (const root of roots) visit(root);
9637
9779
  return sizes;
@@ -9641,7 +9783,7 @@ function analyzeLazyUsageMulti(roots) {
9641
9783
  const recursive = /* @__PURE__ */ new Set();
9642
9784
  const descended = /* @__PURE__ */ new Set();
9643
9785
  const active = /* @__PURE__ */ new Set();
9644
- function walk2(p) {
9786
+ function walk(p) {
9645
9787
  const def = p._def;
9646
9788
  if (def.tag === "lazy") {
9647
9789
  counts.set(p, (counts.get(p) ?? 0) + 1);
@@ -9658,17 +9800,17 @@ function analyzeLazyUsageMulti(roots) {
9658
9800
  return;
9659
9801
  }
9660
9802
  active.add(p);
9661
- walk2(resolved);
9803
+ walk(resolved);
9662
9804
  active.delete(p);
9663
9805
  return;
9664
9806
  }
9665
9807
  if (isHoistableTag(def.tag)) {
9666
9808
  counts.set(p, (counts.get(p) ?? 0) + 1);
9667
9809
  }
9668
- for (const child of childrenOf2(def)) walk2(child);
9810
+ for (const child of childrenOf3(def)) walk(child);
9669
9811
  }
9670
9812
  const rootsArr = [...roots];
9671
- for (const root of rootsArr) walk2(root);
9813
+ for (const root of rootsArr) walk(root);
9672
9814
  return { counts, recursive, sizes: subtreeSizes(rootsArr) };
9673
9815
  }
9674
9816
  function ruleDependencies(ruleMap) {
@@ -9678,7 +9820,7 @@ function ruleDependencies(ruleMap) {
9678
9820
  for (const [name, comb] of ruleMap) {
9679
9821
  const found = /* @__PURE__ */ new Set();
9680
9822
  const seen = /* @__PURE__ */ new Set();
9681
- const walk2 = (p, isRoot) => {
9823
+ const walk = (p, isRoot) => {
9682
9824
  const def = p._def;
9683
9825
  const boundary = def.tag === "lazy" ? nameOf.get(p) ?? p._ruleName : nameOf.get(p);
9684
9826
  if (!isRoot && boundary !== void 0) {
@@ -9694,12 +9836,12 @@ function ruleDependencies(ruleMap) {
9694
9836
  } catch {
9695
9837
  return;
9696
9838
  }
9697
- walk2(resolved, false);
9839
+ walk(resolved, false);
9698
9840
  return;
9699
9841
  }
9700
- for (const child of childrenOf2(def)) walk2(child, false);
9842
+ for (const child of childrenOf3(def)) walk(child, false);
9701
9843
  };
9702
- walk2(comb, true);
9844
+ walk(comb, true);
9703
9845
  deps.set(name, [...found]);
9704
9846
  }
9705
9847
  return deps;
@@ -9848,6 +9990,7 @@ function compile(combinator, mapFnSources, opts) {
9848
9990
  const grammarTrivia = combinator._meta.grammarTrivia;
9849
9991
  const grammarScanSkip = combinator._meta.grammarScanSkip;
9850
9992
  const grammarHostMode = combinator._meta.grammarHostMode;
9993
+ const grammarTrackLines = combinator._meta.grammarTrackLines;
9851
9994
  const ctx = {
9852
9995
  vars: 0,
9853
9996
  indent: 1,
@@ -9868,7 +10011,7 @@ function compile(combinator, mapFnSources, opts) {
9868
10011
  capturing: hasNodeDef(combinator),
9869
10012
  recovery: opts?.recovery ?? false,
9870
10013
  hostMode: opts?.hostMode ?? grammarHostMode ?? "ast",
9871
- lineTracking: opts?.trackLines === true || hasLineTrackingDef(combinator),
10014
+ lineTracking: opts?.trackLines === true || grammarTrackLines === true || hasLineTrackingDef(combinator),
9872
10015
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
9873
10016
  lazyUsage: analyzeLazyUsage(combinator),
9874
10017
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
@@ -10032,6 +10175,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10032
10175
  const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
10033
10176
  const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
10034
10177
  const grammarHostMode = opts?.hostMode ?? ruleMapArg.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
10178
+ const grammarTrackLines = opts?.trackLines === true || ruleMapArg.some(([, r]) => r._meta.grammarTrackLines === true);
10035
10179
  const ruleMap = ruleMapArg.filter(([, val]) => {
10036
10180
  const d = val._def;
10037
10181
  if (d.tag !== "lazy") return true;
@@ -10042,6 +10186,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10042
10186
  return false;
10043
10187
  }
10044
10188
  });
10189
+ const nodeMeta = new Map(ruleMap.map(([name, rule]) => [name, collectGrammarReflection([[name, rule]], { followLazy: false })]));
10045
10190
  const ctx = {
10046
10191
  vars: 0,
10047
10192
  indent: 1,
@@ -10066,6 +10211,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10066
10211
  capturing: opts?.captureTerminals === true || ruleMap.some(([, rule]) => hasNodeDef(rule)),
10067
10212
  recovery: opts?.recovery ?? false,
10068
10213
  hostMode: grammarHostMode ?? "ast",
10214
+ lineTracking: grammarTrackLines,
10069
10215
  ...opts?.coverage ? { coverage: { plan: opts.coverage } } : {},
10070
10216
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
10071
10217
  ns,
@@ -10116,7 +10262,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10116
10262
  scanExternal(resolved);
10117
10263
  return;
10118
10264
  }
10119
- for (const child of childrenOf2(def)) scanExternal(child);
10265
+ for (const child of childrenOf3(def)) scanExternal(child);
10120
10266
  };
10121
10267
  for (const [, rule] of ruleMap) scanExternal(rule);
10122
10268
  }
@@ -10162,6 +10308,10 @@ function compileLinkable(ruleMapArg, ns, opts) {
10162
10308
  perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
10163
10309
  }
10164
10310
  if (ctx.runtimeParsers.length !== 0) return null;
10311
+ if (ctx.lineTracking) {
10312
+ ctx.needsLineTrack = true;
10313
+ ctx.needsLineSpan = true;
10314
+ }
10165
10315
  const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
10166
10316
  const buildSrcs = ctx.buildFns.length > 0 && ctx.buildSrcs.every((s) => s !== null) ? ctx.buildSrcs : null;
10167
10317
  const ruleFns = /* @__PURE__ */ new Map();
@@ -10175,6 +10325,8 @@ function compileLinkable(ruleMapArg, ns, opts) {
10175
10325
  const mfDecl = ctx.mapFns.length === 0 ? "" : mfSrcs ? `const ${mfRef(ctx)} = [${mfSrcs.join(", ")}]` : `const ${mfRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "mf")}]`;
10176
10326
  const buildDecl = ctx.buildFns.length === 0 ? "" : buildSrcs ? `const ${buildRef(ctx)} = [${buildSrcs.join(", ")}]` : `const ${buildRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "build")}]`;
10177
10327
  const prelude = [
10328
+ ctx.needsLineTrack ? LINE_TRACK_DECL : "",
10329
+ ctx.needsLineSpan ? LINE_SPAN_DECL : "",
10178
10330
  ...ctx.regexDecls,
10179
10331
  ...ctx.expectedDecls,
10180
10332
  mfDecl,
@@ -10183,11 +10335,18 @@ function compileLinkable(ruleMapArg, ns, opts) {
10183
10335
  ].filter(Boolean);
10184
10336
  const wrappers = /* @__PURE__ */ new Map();
10185
10337
  for (const { key, rule, r } of perEntry) {
10338
+ const linePreamble = ctx.lineTracking ? [
10339
+ ` if (_ctx._lineStarts === undefined) _ctx = { ..._ctx, trackLines: true, _lineStarts: [0], _lineScannedTo: 0 }`,
10340
+ ` else _ctx.trackLines = true`,
10341
+ ` if (_pos > (_ctx._lineScannedTo ?? 0)) _trackLines(_ctx, input, 0, _pos)`
10342
+ ] : [];
10343
+ const resultSpan = ctx.lineTracking ? `_spanLines(_ctx, _pos, ${r.endVar})` : `{ start: _pos, end: ${r.endVar} }`;
10186
10344
  wrappers.set(key, publicRuleWrapperSource(rule, [
10187
10345
  `function(input, _pos, _ctx) {`,
10346
+ ...linePreamble,
10188
10347
  ` let pos = _pos`,
10189
10348
  ...r.stmts,
10190
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
10349
+ ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`,
10191
10350
  `}`
10192
10351
  ].join("\n"), ctx.triviaKindLabels));
10193
10352
  }
@@ -10225,6 +10384,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10225
10384
  hostBranchElided: !!ctx.hostBranchElided,
10226
10385
  hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
10227
10386
  isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs),
10387
+ nodeMeta,
10228
10388
  mfFns: mfSrcs ? [] : ctx.mapFns,
10229
10389
  buildFns: buildSrcs ? [] : ctx.buildFns
10230
10390
  };
@@ -10245,7 +10405,7 @@ function hasExternalRuleRef(ruleMap) {
10245
10405
  if (resolved === void 0) return p._ruleName !== void 0;
10246
10406
  return visit(resolved);
10247
10407
  }
10248
- return childrenOf2(def).some(visit);
10408
+ return childrenOf3(def).some(visit);
10249
10409
  };
10250
10410
  return ruleMap.some(([, rule]) => visit(rule));
10251
10411
  }
@@ -10267,7 +10427,7 @@ function hasSemanticReduction(roots, externalRefs) {
10267
10427
  return true;
10268
10428
  }
10269
10429
  }
10270
- return childrenOf2(def).some(visit);
10430
+ return childrenOf3(def).some(visit);
10271
10431
  };
10272
10432
  return roots.some(visit);
10273
10433
  }
@@ -10340,7 +10500,7 @@ function withCtx(extra, combinator) {
10340
10500
  }
10341
10501
 
10342
10502
  // src/compiler/ir-serialize.ts
10343
- function childrenOf3(def) {
10503
+ function childrenOf4(def) {
10344
10504
  switch (def.tag) {
10345
10505
  case "sequence":
10346
10506
  case "choice":
@@ -10404,6 +10564,10 @@ function projectOpt(project) {
10404
10564
  if (project === void 0) return void 0;
10405
10565
  return `project: ${project}`;
10406
10566
  }
10567
+ function tagsOpt(tags) {
10568
+ if (tags === void 0 || tags.length === 0) return void 0;
10569
+ return `tags: ${JSON.stringify(tags)}`;
10570
+ }
10407
10571
  function matcherExpr(entry) {
10408
10572
  switch (entry.kind) {
10409
10573
  case "startsWith":
@@ -10617,7 +10781,7 @@ var Serializer = class {
10617
10781
  if (target) this.assertNoRuleRef(target, seen);
10618
10782
  return;
10619
10783
  }
10620
- for (const child of childrenOf3(c._def)) this.assertNoRuleRef(child, seen);
10784
+ for (const child of childrenOf4(c._def)) this.assertNoRuleRef(child, seen);
10621
10785
  }
10622
10786
  /** A `rules()` map wraps each rule value in a named `lazy` proxy; the real body is
10623
10787
  * its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
@@ -10681,7 +10845,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10681
10845
  this.counts.set(c, (this.counts.get(c) ?? 0) + 1);
10682
10846
  if ((this.counts.get(c) ?? 0) > 1) return;
10683
10847
  active.add(c);
10684
- for (const child of childrenOf3(def)) this.analyze(child, active);
10848
+ for (const child of childrenOf4(def)) this.analyze(child, active);
10685
10849
  active.delete(c);
10686
10850
  }
10687
10851
  /** Reference `c`: a const var if it has one (emitting its decl first), else inline.
@@ -10798,12 +10962,14 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10798
10962
  case "node": {
10799
10963
  if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
10800
10964
  const projectEntry = projectOpt(def.project);
10965
+ const tagEntry = tagsOpt(def.tags);
10801
10966
  const optEntries = [
10802
10967
  ...def.unwrap ? ["unwrap: true"] : [],
10803
10968
  ...def.collapse ? ["collapse: true"] : [],
10804
10969
  ...projectEntry === void 0 ? [] : [projectEntry],
10805
10970
  ...def.captureTrivia ? ["captureTrivia: true"] : [],
10806
- ...def.trailingTrivia ? ["trailingTrivia: true"] : []
10971
+ ...def.trailingTrivia ? ["trailingTrivia: true"] : [],
10972
+ ...tagEntry === void 0 ? [] : [tagEntry]
10807
10973
  ];
10808
10974
  const opts = optEntries.length > 0 ? `, { ${optEntries.join(", ")} }` : "";
10809
10975
  if (def.type === void 0) {
@@ -10851,15 +11017,16 @@ function normalizeCstCollapse(collapse) {
10851
11017
  }
10852
11018
  return typeof collapse === "function" ? collapse : void 0;
10853
11019
  }
10854
- function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
10855
- 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] };
10856
11022
  }
10857
- function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
11023
+ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state, _tags) {
10858
11024
  if (typeof typeOrOptions === "string") {
10859
11025
  return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
10860
11026
  }
10861
11027
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
10862
- 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);
10863
11030
  host._parsemanCstOutput = true;
10864
11031
  if (collapse) host._parsemanCstCollapse = collapse;
10865
11032
  return host;
@@ -10901,6 +11068,7 @@ function fusedBody(pieces) {
10901
11068
  ...[...winner].map(([k, p]) => p.ruleFns.get(k))
10902
11069
  ];
10903
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: [] }));
10904
11072
  const { mode, elided } = hostModeOfPieces(pieces);
10905
11073
  const rawBody = [
10906
11074
  ...lines,
@@ -10909,6 +11077,7 @@ function fusedBody(pieces) {
10909
11077
  "}",
10910
11078
  `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_MODE.description)}), { value: ${JSON.stringify(mode)}, enumerable: false })`,
10911
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 })`,
10912
11081
  // …and on each rule FUNCTION, because `run(map.Rule, …)` is handed the rule, not the
10913
11082
  // map, and would otherwise have nothing to check against. Done once at fuse time,
10914
11083
  // before any rule has been called, so it costs nothing per parse.
@@ -11075,7 +11244,8 @@ function materializePiece(p, trivia2, captureTerminals = false, hostMode) {
11075
11244
  const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, {
11076
11245
  ...trivia2 ? { trivia: trivia2 } : {},
11077
11246
  ...captureTerminals ? { captureTerminals: true } : {},
11078
- ...hostMode ? { hostMode } : {}
11247
+ ...hostMode ? { hostMode } : {},
11248
+ ...p.trackLines ? { trackLines: true } : {}
11079
11249
  });
11080
11250
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
11081
11251
  return pieces;
@@ -12336,38 +12506,48 @@ function buildTriviaIndex(root, input = "", opts) {
12336
12506
  }
12337
12507
 
12338
12508
  // src/cst/walk.ts
12339
- function walk(root, visitor, ctx = void 0) {
12340
- const go = (node2, parent) => {
12341
- const descend = visitor.enter ? visitor.enter(node2, parent, ctx) : void 0;
12342
- const children2 = node2.children;
12343
- if (descend !== false && Array.isArray(children2)) {
12344
- for (const child of children2) go(child, node2);
12345
- }
12346
- visitor.leave?.(node2, parent, ctx);
12347
- };
12348
- go(root, null);
12349
- }
12350
- function createVisitor(handlers) {
12351
- const api = {
12352
- visit(node2) {
12353
- const handler = node2.type !== void 0 ? handlers[node2.type] : void 0;
12354
- if (handler) return handler(node2, api);
12355
- api.visitChildren(node2);
12356
- return void 0;
12357
- },
12358
- visitChildren(node2) {
12359
- const out = [];
12360
- const children2 = node2.children;
12361
- if (Array.isArray(children2)) {
12362
- for (const child of children2) {
12363
- const r = api.visit(child);
12364
- 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);
12365
12541
  }
12366
12542
  }
12367
- return out;
12368
- }
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);
12369
12550
  };
12370
- return (node2) => api.visit(node2);
12371
12551
  }
12372
12552
 
12373
12553
  // src/cst/offset-model.ts