parseman 0.42.0 → 0.42.1

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.
package/dist/index.js CHANGED
@@ -2768,6 +2768,101 @@ function markUnusedValues(root) {
2768
2768
  visit(resolveRoot(root), true);
2769
2769
  }
2770
2770
 
2771
+ // src/combinators/grammar.ts
2772
+ function annotateResultLines(result, index) {
2773
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
2774
+ const errors = annotated.errors;
2775
+ if (errors) {
2776
+ ;
2777
+ annotated.errors = errors.map((error) => ({
2778
+ ...error,
2779
+ span: annotateSpan(error.span, index)
2780
+ }));
2781
+ }
2782
+ return annotated;
2783
+ }
2784
+ function createParseLineContext(input, pos) {
2785
+ const lineIndex = createLineIndex();
2786
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
2787
+ return { lineIndex, lineScannedTo: pos };
2788
+ }
2789
+ function parser(opts, root) {
2790
+ const clearTrivia = opts.trivia === null;
2791
+ return {
2792
+ _tag: "grammar",
2793
+ _meta: root._meta,
2794
+ _def: {
2795
+ tag: "grammar",
2796
+ parser: root,
2797
+ triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
2798
+ clearTrivia,
2799
+ ...opts.captureTrivia ? { captureTrivia: true } : {},
2800
+ trackLines: opts.trackLines ?? false
2801
+ },
2802
+ parse(input, pos, _ctx) {
2803
+ const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
2804
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
2805
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
2806
+ const ctx = {
2807
+ ..._ctx,
2808
+ trackLines,
2809
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
2810
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
2811
+ // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
2812
+ ...clearTrivia ? {
2813
+ trivia: void 0,
2814
+ triviaKindLabels: void 0
2815
+ } : opts.trivia != null ? {
2816
+ trivia: opts.trivia,
2817
+ ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
2818
+ } : {},
2819
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
2820
+ // Kind-filter for per-node capture. Resolve against this scope's trivia
2821
+ // labels — this parser's own trivia if it declares one, else the INHERITED
2822
+ // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
2823
+ // trivia is inherited rather than re-declared here. No labels → undefined
2824
+ // (capture all).
2825
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
2826
+ };
2827
+ const result = root.parse(input, pos ?? 0, ctx);
2828
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
2829
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
2830
+ }
2831
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
2832
+ }
2833
+ };
2834
+ }
2835
+ function noTrivia(root) {
2836
+ return parser({ trivia: null }, root);
2837
+ }
2838
+ var _analyzed = /* @__PURE__ */ new WeakSet();
2839
+ function parse(combinator, input, opts = {}) {
2840
+ if (!_analyzed.has(combinator)) {
2841
+ _analyzed.add(combinator);
2842
+ markUnusedValues(combinator);
2843
+ }
2844
+ const trackLines = opts.trackLines ?? combinator._meta.grammarTrackLines ?? false;
2845
+ const lineContext = trackLines ? createParseLineContext(input, 0) : void 0;
2846
+ const lineIndex = lineContext?.lineIndex;
2847
+ const _errors = opts.recover ? [] : void 0;
2848
+ const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
2849
+ const grammarTrivia = combinator._meta.grammarTrivia;
2850
+ const grammarScanSkip = combinator._meta.grammarScanSkip;
2851
+ const ctx = {
2852
+ trackLines,
2853
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
2854
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
2855
+ ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
2856
+ ...grammarScanSkip !== void 0 ? { scanSkip: grammarScanSkip } : {},
2857
+ ..._errors !== void 0 ? { _errors } : {},
2858
+ ..._probe !== void 0 ? { _probe } : {}
2859
+ };
2860
+ const result = combinator.parse(input, 0, ctx);
2861
+ if (!result.ok) return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
2862
+ const withErrors = _errors !== void 0 ? { ...result, errors: _errors, furthestFail: _probe?.best ?? null } : result;
2863
+ return lineIndex ? annotateResultLines(withErrors, normalizeLineIndex(lineIndex)) : withErrors;
2864
+ }
2865
+
2771
2866
  // src/combinators/parser.ts
2772
2867
  var RULE_ORDER = "__parsemanRuleOrder";
2773
2868
  function tagRule(r, key) {
@@ -2843,6 +2938,21 @@ function rules(a, b) {
2843
2938
  if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
2844
2939
  }
2845
2940
  }
2941
+ if (options?.trackLines === true) {
2942
+ for (const key of Object.keys(definitions)) {
2943
+ const rule = cache[key];
2944
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
2945
+ }
2946
+ for (const key of Object.keys(definitions)) {
2947
+ const rule = cache[key];
2948
+ if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
2949
+ const wrapped = parser({ trackLines: true }, rule);
2950
+ tagRule(wrapped, key);
2951
+ wrapped._meta.grammarTrackLines = true;
2952
+ cache[key] = wrapped;
2953
+ }
2954
+ }
2955
+ }
2846
2956
  for (const key of Object.keys(definitions)) {
2847
2957
  markUnusedValues(cache[key]);
2848
2958
  }
@@ -3299,101 +3409,6 @@ function field(name, combinator) {
3299
3409
  };
3300
3410
  }
3301
3411
 
3302
- // src/combinators/grammar.ts
3303
- function annotateResultLines(result, index) {
3304
- const annotated = { ...result, span: annotateSpan(result.span, index) };
3305
- const errors = annotated.errors;
3306
- if (errors) {
3307
- ;
3308
- annotated.errors = errors.map((error) => ({
3309
- ...error,
3310
- span: annotateSpan(error.span, index)
3311
- }));
3312
- }
3313
- return annotated;
3314
- }
3315
- function createParseLineContext(input, pos) {
3316
- const lineIndex = createLineIndex();
3317
- if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
3318
- return { lineIndex, lineScannedTo: pos };
3319
- }
3320
- function parser(opts, root) {
3321
- const clearTrivia = opts.trivia === null;
3322
- return {
3323
- _tag: "grammar",
3324
- _meta: root._meta,
3325
- _def: {
3326
- tag: "grammar",
3327
- parser: root,
3328
- triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
3329
- clearTrivia,
3330
- ...opts.captureTrivia ? { captureTrivia: true } : {},
3331
- trackLines: opts.trackLines ?? false
3332
- },
3333
- parse(input, pos, _ctx) {
3334
- const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
3335
- const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
3336
- const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
3337
- const ctx = {
3338
- ..._ctx,
3339
- trackLines,
3340
- ...lineIndex ? { _lineIndex: lineIndex } : {},
3341
- ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
3342
- // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
3343
- ...clearTrivia ? {
3344
- trivia: void 0,
3345
- triviaKindLabels: void 0
3346
- } : opts.trivia != null ? {
3347
- trivia: opts.trivia,
3348
- ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
3349
- } : {},
3350
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
3351
- // Kind-filter for per-node capture. Resolve against this scope's trivia
3352
- // labels — this parser's own trivia if it declares one, else the INHERITED
3353
- // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
3354
- // trivia is inherited rather than re-declared here. No labels → undefined
3355
- // (capture all).
3356
- ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
3357
- };
3358
- const result = root.parse(input, pos ?? 0, ctx);
3359
- if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
3360
- _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
3361
- }
3362
- return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
3363
- }
3364
- };
3365
- }
3366
- function noTrivia(root) {
3367
- return parser({ trivia: null }, root);
3368
- }
3369
- var _analyzed = /* @__PURE__ */ new WeakSet();
3370
- function parse(combinator, input, opts = {}) {
3371
- if (!_analyzed.has(combinator)) {
3372
- _analyzed.add(combinator);
3373
- markUnusedValues(combinator);
3374
- }
3375
- const trackLines = opts.trackLines ?? false;
3376
- const lineContext = trackLines ? createParseLineContext(input, 0) : void 0;
3377
- const lineIndex = lineContext?.lineIndex;
3378
- const _errors = opts.recover ? [] : void 0;
3379
- const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
3380
- const grammarTrivia = combinator._meta.grammarTrivia;
3381
- const grammarScanSkip = combinator._meta.grammarScanSkip;
3382
- const ctx = {
3383
- trackLines,
3384
- ...lineIndex ? { _lineIndex: lineIndex } : {},
3385
- ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
3386
- ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
3387
- ...grammarScanSkip !== void 0 ? { scanSkip: grammarScanSkip } : {},
3388
- ..._errors !== void 0 ? { _errors } : {},
3389
- ..._probe !== void 0 ? { _probe } : {}
3390
- };
3391
- const result = combinator.parse(input, 0, ctx);
3392
- if (!result.ok) return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
3393
- const withErrors = _errors !== void 0 ? { ...result, errors: _errors, furthestFail: _probe?.best ?? null } : result;
3394
- return lineIndex ? annotateResultLines(withErrors, normalizeLineIndex(lineIndex)) : withErrors;
3395
- }
3396
-
3397
3412
  // src/combinators/token.ts
3398
3413
  function token(root) {
3399
3414
  const meta = {
@@ -3490,7 +3505,7 @@ function leaf(root, fn) {
3490
3505
  }
3491
3506
 
3492
3507
  // src/version.ts
3493
- var PARSEMAN_VERSION = "0.42.0";
3508
+ var PARSEMAN_VERSION = "0.42.1";
3494
3509
 
3495
3510
  // src/cst/host-mode.ts
3496
3511
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -6850,13 +6865,16 @@ function reindentStmts(stmts, targetLevels) {
6850
6865
  const targetPrefix = " ".repeat(targetLevels);
6851
6866
  return stmts.map((s) => s.trim().length === 0 ? "" : targetPrefix + s.slice(minLeading));
6852
6867
  }
6853
- function failReturn(expected, posExpr) {
6868
+ function failReturn(ctx, expected, posExpr) {
6869
+ if (ctx.lineTracking) return `return { ok: false, expected: [${expected}], span: ${emitSpanExpr(ctx, posExpr, posExpr)} }`;
6854
6870
  return `return { ok: false, expected: [${expected}], span: { start: ${posExpr}, end: ${posExpr} } }`;
6855
6871
  }
6856
- function failReturnArr(expectedArr, posExpr) {
6872
+ function failReturnArr(ctx, expectedArr, posExpr) {
6873
+ if (ctx.lineTracking) return `return { ok: false, expected: ${expectedArr}, span: ${emitSpanExpr(ctx, posExpr, posExpr)} }`;
6857
6874
  return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
6858
6875
  }
6859
- function committedReturnArr(expectedArr, posExpr) {
6876
+ function committedReturnArr(ctx, expectedArr, posExpr) {
6877
+ if (ctx.lineTracking) return `return { ok: false, expected: [...${expectedArr}], span: ${emitSpanExpr(ctx, posExpr, posExpr)}, committed: true }`;
6860
6878
  return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} }, committed: true }`;
6861
6879
  }
6862
6880
  function hoistExpected(ctx, constArrSource) {
@@ -6881,7 +6899,7 @@ function failBody(ctx, expected, posExpr) {
6881
6899
  if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
6882
6900
  return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; ${trace}break ${ctx.failLabel} }`;
6883
6901
  }
6884
- return probe + trace + failReturn(expected, posExpr);
6902
+ return probe + trace + failReturn(ctx, expected, posExpr);
6885
6903
  }
6886
6904
  function failArrBody(ctx, expectedArr, posExpr, constant = true) {
6887
6905
  const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
@@ -6891,18 +6909,19 @@ function failArrBody(ctx, expectedArr, posExpr, constant = true) {
6891
6909
  const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
6892
6910
  return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; ${trace}break ${ctx.failLabel} }`;
6893
6911
  }
6894
- if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
6895
- return probe + trace + failReturnArr(expectedArr, posExpr);
6912
+ if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: ${ctx.lineTracking ? emitSpanExpr(ctx, posExpr, posExpr) : `{ start: ${posExpr}, end: ${posExpr} }`} }`;
6913
+ return probe + trace + failReturnArr(ctx, expectedArr, posExpr);
6896
6914
  }
6897
- function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
6898
- return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} }, ...(_ctx._fc ? { committed: true } : {}) }`;
6915
+ function resultFromRecorded(ctx, feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
6916
+ const spanExpr = ctx.lineTracking ? emitSpanExpr(ctx, feExpr, feExpr) : `{ start: ${feExpr}, end: ${feExpr} }`;
6917
+ return `return { ok: false, expected: [...${fxExpr}], span: ${spanExpr}, ...(_ctx._fc ? { committed: true } : {}) }`;
6899
6918
  }
6900
6919
  function committedFailBody(ctx, expectedArr = "_ctx._fx", posExpr = "_ctx._fe") {
6901
6920
  if (ctx.failLabel) {
6902
6921
  if (!ctx.recordFail) return `{ _ctx._fc = true; break ${ctx.failLabel} }`;
6903
6922
  return `{ _ctx._fc = true; _ctx._fe = ${posExpr}; _ctx._fx = ${expectedArr}; break ${ctx.failLabel} }`;
6904
6923
  }
6905
- return committedReturnArr(expectedArr, posExpr);
6924
+ return committedReturnArr(ctx, expectedArr, posExpr);
6906
6925
  }
6907
6926
  function propagateFailBody(ctx, srcCtx = "_ctx") {
6908
6927
  if (srcCtx !== "_ctx") {
@@ -6910,10 +6929,10 @@ function propagateFailBody(ctx, srcCtx = "_ctx") {
6910
6929
  if (!ctx.recordFail) return `break ${ctx.failLabel}`;
6911
6930
  return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; break ${ctx.failLabel} }`;
6912
6931
  }
6913
- return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; ${resultFromRecorded()} }`;
6932
+ return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; ${resultFromRecorded(ctx)} }`;
6914
6933
  }
6915
6934
  if (ctx.failLabel) return `break ${ctx.failLabel}`;
6916
- return resultFromRecorded();
6935
+ return resultFromRecorded(ctx);
6917
6936
  }
6918
6937
  function emitIfFail(ctx, cond, body) {
6919
6938
  return [
@@ -8346,7 +8365,7 @@ function emitAttempt(p, def, ctx, pos) {
8346
8365
  if (!ctx.recovery && !ctx.coverage && needsFirstSetGuard(def.parser)) {
8347
8366
  const gcV = v(ctx, "_agc");
8348
8367
  const gExp = armStaticExpected(ctx, def.parser);
8349
- const gFail = ctx.failLabel ? `{ _ctx._fe = ${pos};${ctx.recordFail ? ` _ctx._fx = ${gExp};` : ""} break ${ctx.failLabel} }` : `{ _ctx._fe = ${pos}; ${failReturnArr(gExp, pos)} }`;
8368
+ const gFail = ctx.failLabel ? `{ _ctx._fe = ${pos};${ctx.recordFail ? ` _ctx._fx = ${gExp};` : ""} break ${ctx.failLabel} }` : `{ _ctx._fe = ${pos}; ${failReturnArr(ctx, gExp, pos)} }`;
8350
8369
  preGuard.push(
8351
8370
  `${ind(ctx)}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
8352
8371
  `${ind(ctx)}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
@@ -8845,7 +8864,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8845
8864
  if (!ctx.recovery && !ctx.coverage && (capturesChildren || structural) && needsFirstSetGuard(def.parser)) {
8846
8865
  const gcV = v(ctx, "_ngc");
8847
8866
  const gExp = armStaticExpected(ctx, def.parser);
8848
- const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(gExp, pos);
8867
+ const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(ctx, gExp, pos);
8849
8868
  preGuard.push(
8850
8869
  `${i}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
8851
8870
  `${i}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
@@ -9361,6 +9380,7 @@ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9361
9380
  if (seen.has(p)) return false;
9362
9381
  seen.add(p);
9363
9382
  const d = p._def;
9383
+ if (p._meta.grammarTrackLines) return true;
9364
9384
  if (d.tag === "grammar" && d.trackLines) return true;
9365
9385
  if (d.tag === "lazy") {
9366
9386
  try {
@@ -9718,6 +9738,7 @@ function compile(combinator, mapFnSources, opts) {
9718
9738
  const grammarTrivia = combinator._meta.grammarTrivia;
9719
9739
  const grammarScanSkip = combinator._meta.grammarScanSkip;
9720
9740
  const grammarHostMode = combinator._meta.grammarHostMode;
9741
+ const grammarTrackLines = combinator._meta.grammarTrackLines;
9721
9742
  const ctx = {
9722
9743
  vars: 0,
9723
9744
  indent: 1,
@@ -9738,7 +9759,7 @@ function compile(combinator, mapFnSources, opts) {
9738
9759
  capturing: hasNodeDef(combinator),
9739
9760
  recovery: opts?.recovery ?? false,
9740
9761
  hostMode: opts?.hostMode ?? grammarHostMode ?? "ast",
9741
- lineTracking: opts?.trackLines === true || hasLineTrackingDef(combinator),
9762
+ lineTracking: opts?.trackLines === true || grammarTrackLines === true || hasLineTrackingDef(combinator),
9742
9763
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
9743
9764
  lazyUsage: analyzeLazyUsage(combinator),
9744
9765
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
@@ -9902,6 +9923,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
9902
9923
  const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
9903
9924
  const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
9904
9925
  const grammarHostMode = opts?.hostMode ?? ruleMapArg.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
9926
+ const grammarTrackLines = opts?.trackLines === true || ruleMapArg.some(([, r]) => r._meta.grammarTrackLines === true);
9905
9927
  const ruleMap = ruleMapArg.filter(([, val]) => {
9906
9928
  const d = val._def;
9907
9929
  if (d.tag !== "lazy") return true;
@@ -9936,6 +9958,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
9936
9958
  capturing: opts?.captureTerminals === true || ruleMap.some(([, rule]) => hasNodeDef(rule)),
9937
9959
  recovery: opts?.recovery ?? false,
9938
9960
  hostMode: grammarHostMode ?? "ast",
9961
+ lineTracking: grammarTrackLines,
9939
9962
  ...opts?.coverage ? { coverage: { plan: opts.coverage } } : {},
9940
9963
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
9941
9964
  ns,
@@ -10032,6 +10055,10 @@ function compileLinkable(ruleMapArg, ns, opts) {
10032
10055
  perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
10033
10056
  }
10034
10057
  if (ctx.runtimeParsers.length !== 0) return null;
10058
+ if (ctx.lineTracking) {
10059
+ ctx.needsLineTrack = true;
10060
+ ctx.needsLineSpan = true;
10061
+ }
10035
10062
  const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
10036
10063
  const buildSrcs = ctx.buildFns.length > 0 && ctx.buildSrcs.every((s) => s !== null) ? ctx.buildSrcs : null;
10037
10064
  const ruleFns = /* @__PURE__ */ new Map();
@@ -10045,6 +10072,8 @@ function compileLinkable(ruleMapArg, ns, opts) {
10045
10072
  const mfDecl = ctx.mapFns.length === 0 ? "" : mfSrcs ? `const ${mfRef(ctx)} = [${mfSrcs.join(", ")}]` : `const ${mfRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "mf")}]`;
10046
10073
  const buildDecl = ctx.buildFns.length === 0 ? "" : buildSrcs ? `const ${buildRef(ctx)} = [${buildSrcs.join(", ")}]` : `const ${buildRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "build")}]`;
10047
10074
  const prelude = [
10075
+ ctx.needsLineTrack ? LINE_TRACK_DECL : "",
10076
+ ctx.needsLineSpan ? LINE_SPAN_DECL : "",
10048
10077
  ...ctx.regexDecls,
10049
10078
  ...ctx.expectedDecls,
10050
10079
  mfDecl,
@@ -10053,11 +10082,18 @@ function compileLinkable(ruleMapArg, ns, opts) {
10053
10082
  ].filter(Boolean);
10054
10083
  const wrappers = /* @__PURE__ */ new Map();
10055
10084
  for (const { key, rule, r } of perEntry) {
10085
+ const linePreamble = ctx.lineTracking ? [
10086
+ ` if (_ctx._lineStarts === undefined) _ctx = { ..._ctx, trackLines: true, _lineStarts: [0], _lineScannedTo: 0 }`,
10087
+ ` else _ctx.trackLines = true`,
10088
+ ` if (_pos > (_ctx._lineScannedTo ?? 0)) _trackLines(_ctx, input, 0, _pos)`
10089
+ ] : [];
10090
+ const resultSpan = ctx.lineTracking ? `_spanLines(_ctx, _pos, ${r.endVar})` : `{ start: _pos, end: ${r.endVar} }`;
10056
10091
  wrappers.set(key, publicRuleWrapperSource(rule, [
10057
10092
  `function(input, _pos, _ctx) {`,
10093
+ ...linePreamble,
10058
10094
  ` let pos = _pos`,
10059
10095
  ...r.stmts,
10060
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
10096
+ ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`,
10061
10097
  `}`
10062
10098
  ].join("\n"), ctx.triviaKindLabels));
10063
10099
  }
@@ -10945,7 +10981,8 @@ function materializePiece(p, trivia2, captureTerminals = false, hostMode) {
10945
10981
  const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, {
10946
10982
  ...trivia2 ? { trivia: trivia2 } : {},
10947
10983
  ...captureTerminals ? { captureTerminals: true } : {},
10948
- ...hostMode ? { hostMode } : {}
10984
+ ...hostMode ? { hostMode } : {},
10985
+ ...p.trackLines ? { trackLines: true } : {}
10949
10986
  });
10950
10987
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
10951
10988
  return pieces;