parseman 0.41.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.
Files changed (51) hide show
  1. package/dist/combinators/expect.d.ts.map +1 -1
  2. package/dist/combinators/grammar.d.ts.map +1 -1
  3. package/dist/combinators/literal.d.ts.map +1 -1
  4. package/dist/combinators/node.d.ts.map +1 -1
  5. package/dist/combinators/parser.d.ts +6 -0
  6. package/dist/combinators/parser.d.ts.map +1 -1
  7. package/dist/combinators/regex.d.ts.map +1 -1
  8. package/dist/combinators/scanTo.d.ts.map +1 -1
  9. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  10. package/dist/compiler/codegen.d.ts +5 -0
  11. package/dist/compiler/codegen.d.ts.map +1 -1
  12. package/dist/compiler/inline-build.d.ts +1 -1
  13. package/dist/compiler/inline-build.d.ts.map +1 -1
  14. package/dist/compiler/line-index.d.ts +1 -15
  15. package/dist/compiler/line-index.d.ts.map +1 -1
  16. package/dist/compiler/linker.d.ts +1 -0
  17. package/dist/compiler/linker.d.ts.map +1 -1
  18. package/dist/cst/capture-buffer.d.ts.map +1 -1
  19. package/dist/index.cjs +483 -181
  20. package/dist/index.cjs.map +4 -4
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +483 -181
  24. package/dist/index.js.map +4 -4
  25. package/dist/language-service/index.cjs +48 -2
  26. package/dist/language-service/index.cjs.map +3 -3
  27. package/dist/language-service/index.js +48 -2
  28. package/dist/language-service/index.js.map +3 -3
  29. package/dist/line-index.d.ts +41 -0
  30. package/dist/line-index.d.ts.map +1 -0
  31. package/dist/plugin/index.cjs +646 -169
  32. package/dist/plugin/index.cjs.map +4 -4
  33. package/dist/plugin/index.d.ts.map +1 -1
  34. package/dist/plugin/index.js +646 -169
  35. package/dist/plugin/index.js.map +4 -4
  36. package/dist/recovery/scan.d.ts.map +1 -1
  37. package/dist/regex/first-set.d.ts.map +1 -1
  38. package/dist/run/index.cjs +47 -1
  39. package/dist/run/index.cjs.map +3 -3
  40. package/dist/run/index.d.ts +3 -2
  41. package/dist/run/index.d.ts.map +1 -1
  42. package/dist/run/index.js +47 -1
  43. package/dist/run/index.js.map +3 -3
  44. package/dist/spec/index.cjs +462 -327
  45. package/dist/spec/index.cjs.map +4 -4
  46. package/dist/spec/index.js +462 -327
  47. package/dist/spec/index.js.map +4 -4
  48. package/dist/types.d.ts +18 -0
  49. package/dist/types.d.ts.map +1 -1
  50. package/dist/version.d.ts +1 -1
  51. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -237,6 +237,87 @@ function failAt(ctx, expected, pos) {
237
237
  return r;
238
238
  }
239
239
 
240
+ // src/line-index.ts
241
+ function createLineIndex() {
242
+ return { lineStarts: [0] };
243
+ }
244
+ function buildLineIndex(input) {
245
+ const lineStarts = [0];
246
+ for (let i = 0; i < input.length; i++) {
247
+ if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
248
+ }
249
+ return { lineStarts };
250
+ }
251
+ function recordLineRange(index, input, start, end) {
252
+ const lineStarts = index.lineStarts;
253
+ for (let i = start; i < end; i++) {
254
+ if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
255
+ }
256
+ }
257
+ function recordLineRangeFromContext(ctx, input, _startHint, end) {
258
+ if (!ctx.trackLines) return;
259
+ const from = ctx._lineScannedTo ?? 0;
260
+ if (end <= from) return;
261
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
262
+ if (!index) return;
263
+ recordLineRange(index, input, from, end);
264
+ ctx._lineScannedTo = end;
265
+ }
266
+ function normalizeLineIndex(index) {
267
+ const starts = index.lineStarts;
268
+ starts.sort((a, b) => a - b);
269
+ let write = 0;
270
+ for (let read = 0; read < starts.length; read++) {
271
+ const value = starts[read];
272
+ if (write === 0 || starts[write - 1] !== value) starts[write++] = value;
273
+ }
274
+ starts.length = write;
275
+ if (starts.length === 0 || starts[0] !== 0) starts.unshift(0);
276
+ return index;
277
+ }
278
+ function offsetToLineCol(index, offset) {
279
+ const { lineStarts } = index;
280
+ let lo = 0;
281
+ let hi = lineStarts.length - 1;
282
+ while (lo < hi) {
283
+ const mid = lo + hi + 1 >> 1;
284
+ if (lineStarts[mid] <= offset) lo = mid;
285
+ else hi = mid - 1;
286
+ }
287
+ return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
288
+ }
289
+ function annotateSpan(span, index) {
290
+ const s = offsetToLineCol(index, span.start);
291
+ const e = offsetToLineCol(index, span.end);
292
+ return {
293
+ ...span,
294
+ startLine: s.line,
295
+ startColumn: s.col,
296
+ endLine: e.line,
297
+ endColumn: e.col
298
+ };
299
+ }
300
+ function annotateSpanFromLineContext(span, ctx) {
301
+ if (!ctx.trackLines) return span;
302
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
303
+ return index ? annotateSpan(span, index) : span;
304
+ }
305
+ function isSpan(value) {
306
+ return !!value && typeof value === "object" && typeof value.start === "number" && typeof value.end === "number";
307
+ }
308
+ function annotateTreeSpans(value, index, seen = /* @__PURE__ */ new WeakSet()) {
309
+ if (!value || typeof value !== "object") return value;
310
+ if (seen.has(value)) return value;
311
+ seen.add(value);
312
+ const obj = value;
313
+ if (isSpan(obj.span)) obj.span = annotateSpan(obj.span, index);
314
+ for (const key of Object.keys(obj)) {
315
+ const child = obj[key];
316
+ if (child && typeof child === "object") annotateTreeSpans(child, index, seen);
317
+ }
318
+ return value;
319
+ }
320
+
240
321
  // src/cst/capture-buffer.ts
241
322
  var EMPTY = [];
242
323
  var EMPTY_TL = [];
@@ -278,7 +359,12 @@ function endCstNodeCapture(ctx, saved) {
278
359
  return materialized;
279
360
  }
280
361
  function pushCstLeaf(ctx, leaf2) {
281
- pushCstChild(ctx, leaf2, leaf2);
362
+ if (!ctx.trackLines) {
363
+ pushCstChild(ctx, leaf2, leaf2);
364
+ return;
365
+ }
366
+ const annotated = leaf2 && typeof leaf2 === "object" && leaf2.span ? { ...leaf2, span: annotateSpanFromLineContext(leaf2.span, ctx) } : leaf2;
367
+ pushCstChild(ctx, annotated, annotated);
282
368
  }
283
369
  function pushCstChild(ctx, built, rawEntry) {
284
370
  const b = ctx._cstBuf;
@@ -412,6 +498,7 @@ function literal(value, opts = {}) {
412
498
  canMatchNewline: value.includes("\n"),
413
499
  isTrivia: false
414
500
  };
501
+ const canMatchNewline = meta.canMatchNewline;
415
502
  if (caseInsensitive) {
416
503
  const upper = value.toUpperCase();
417
504
  const lower = value.toLowerCase();
@@ -428,6 +515,7 @@ function literal(value, opts = {}) {
428
515
  return function parse3(input, pos, ctx) {
429
516
  if (input.charCodeAt(pos) === code) {
430
517
  const span = { start: pos, end: pos + 1 };
518
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
431
519
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
432
520
  return { ok: true, value, span };
433
521
  }
@@ -441,6 +529,7 @@ function literal(value, opts = {}) {
441
529
  const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
442
530
  if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
443
531
  const span = { start: pos, end };
532
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
444
533
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
445
534
  return { ok: true, value: matchedValue, span };
446
535
  }
@@ -608,6 +697,7 @@ function regex(pattern, flags = "") {
608
697
  if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
609
698
  const value = input.slice(pos, scanEnd);
610
699
  const span2 = { start: pos, end: scanEnd };
700
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
611
701
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
612
702
  return { ok: true, value, span: span2 };
613
703
  }
@@ -617,6 +707,7 @@ function regex(pattern, flags = "") {
617
707
  return failAt(ctx, [`/${source}/`], pos);
618
708
  }
619
709
  const span = { start: pos, end: pos + m[0].length };
710
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
620
711
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
621
712
  return { ok: true, value: m[0], span };
622
713
  }
@@ -755,8 +846,8 @@ function parseRegex(src) {
755
846
  }
756
847
  if (src[k] !== "]") throw BAIL;
757
848
  i = k + 1;
758
- if (negated) return charNode(any(), true);
759
849
  const ranges = parseClassRanges(body);
850
+ if (negated) return charNode(any(), !ranges || !rangesHaveNewline(ranges));
760
851
  if (!ranges) return charNode(any(), true);
761
852
  return charNode(rangesToSet(ranges), rangesHaveNewline(ranges));
762
853
  };
@@ -852,7 +943,7 @@ function firstSet(n) {
852
943
  }
853
944
  }
854
945
  }
855
- function firstCanBeNewline(n) {
946
+ function canConsumeNewline(n) {
856
947
  switch (n.t) {
857
948
  case "char":
858
949
  return n.nl;
@@ -861,16 +952,11 @@ function firstCanBeNewline(n) {
861
952
  case "empty":
862
953
  return false;
863
954
  case "rep":
864
- return firstCanBeNewline(n.node);
955
+ return canConsumeNewline(n.node);
865
956
  case "alt":
866
- return n.arms.some(firstCanBeNewline);
867
- case "seq": {
868
- for (const part of n.parts) {
869
- if (firstCanBeNewline(part)) return true;
870
- if (!nullable(part)) break;
871
- }
872
- return false;
873
- }
957
+ return n.arms.some(canConsumeNewline);
958
+ case "seq":
959
+ return n.parts.some(canConsumeNewline);
874
960
  }
875
961
  }
876
962
  function firstSetFromRegex(source) {
@@ -880,8 +966,8 @@ function firstSetFromRegex(source) {
880
966
  } catch {
881
967
  return { firstSet: any(), canMatchNewline: true };
882
968
  }
883
- if (nullable(ast)) return { firstSet: any(), canMatchNewline: true };
884
- return { firstSet: firstSet(ast), canMatchNewline: firstCanBeNewline(ast) };
969
+ if (nullable(ast)) return { firstSet: any(), canMatchNewline: canConsumeNewline(ast) };
970
+ return { firstSet: firstSet(ast), canMatchNewline: canConsumeNewline(ast) };
885
971
  }
886
972
 
887
973
  // src/combinators/case-fold.ts
@@ -1043,7 +1129,9 @@ function firstSetSentinel(fs) {
1043
1129
  function recoverScan(input, from, ctx, sync, expected) {
1044
1130
  let scanPos = from;
1045
1131
  while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1046
- const error = { _tag: "parseError", span: { start: from, end: scanPos }, expected };
1132
+ const span = { start: from, end: scanPos };
1133
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
1134
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1047
1135
  ctx._errors?.push(error);
1048
1136
  return { error, end: scanPos };
1049
1137
  }
@@ -1146,7 +1234,8 @@ function expect(combinator, label2) {
1146
1234
  parse(input, pos, ctx) {
1147
1235
  const result = combinator.parse(input, pos, ctx);
1148
1236
  if (result.ok) return result;
1149
- const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
1237
+ const span = { start: pos, end: pos };
1238
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1150
1239
  ctx._errors?.push(error);
1151
1240
  if (ctx._tolerant) captureError(ctx, error);
1152
1241
  return { ok: true, value: error, span: { start: pos, end: pos } };
@@ -1594,6 +1683,18 @@ function rollbackTrivia(ctx, mark) {
1594
1683
  rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1595
1684
  if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1596
1685
  }
1686
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1687
+ const probeCtx = {
1688
+ trackLines: ctx.trackLines,
1689
+ state: ctx.state,
1690
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1691
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1692
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1693
+ };
1694
+ const result = triviaP.parse(input, cur, probeCtx);
1695
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1696
+ return result;
1697
+ }
1597
1698
  function saveLookaheadMark(ctx) {
1598
1699
  return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1599
1700
  }
@@ -1616,13 +1717,29 @@ function scanWithLabels(input, cur, ctx) {
1616
1717
  function advanceTrivia(input, cur, ctx) {
1617
1718
  const triviaP = ctx.trivia;
1618
1719
  if (!triviaP) return cur;
1720
+ if (!ctx.trackLines) {
1721
+ const fast2 = fastTriviaScanner(triviaP);
1722
+ if (fast2) return fast2(input, cur);
1723
+ if (ctx.triviaKindLabels) {
1724
+ const scan = scanWithLabels(input, cur, ctx);
1725
+ return scan.end;
1726
+ }
1727
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1728
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1729
+ }
1730
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1619
1731
  const fast = fastTriviaScanner(triviaP);
1620
- if (fast) return fast(input, cur);
1732
+ if (fast) {
1733
+ const end = fast(input, cur);
1734
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1735
+ return end;
1736
+ }
1621
1737
  if (ctx.triviaKindLabels) {
1622
1738
  const scan = scanWithLabels(input, cur, ctx);
1739
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1623
1740
  return scan.end;
1624
1741
  }
1625
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1742
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1626
1743
  return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1627
1744
  }
1628
1745
  function scanTrivia(input, cur, ctx) {
@@ -1630,18 +1747,46 @@ function scanTrivia(input, cur, ctx) {
1630
1747
  if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1631
1748
  const log = ctx._triviaLog;
1632
1749
  const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1750
+ if (!ctx.trackLines) {
1751
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1752
+ if (fast2 && log === void 0 && !captureTl) {
1753
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1754
+ }
1755
+ if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1756
+ return scanWithLabels(input, cur, ctx);
1757
+ }
1758
+ if (log !== void 0 || captureTl) {
1759
+ const tr3 = triviaP.parse(input, cur, {
1760
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1761
+ state: ctx.state
1762
+ });
1763
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1764
+ const end = tr3.span.end;
1765
+ return {
1766
+ end,
1767
+ commit: () => {
1768
+ pushTriviaLogEntry(ctx, cur, end);
1769
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1770
+ }
1771
+ };
1772
+ }
1773
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1774
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1775
+ }
1776
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1633
1777
  const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1634
1778
  if (fast && log === void 0 && !captureTl) {
1635
- return { end: fast(input, cur), commit: NOOP_COMMIT };
1779
+ const end = fast(input, cur);
1780
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1781
+ return { end, commit: NOOP_COMMIT };
1636
1782
  }
1637
1783
  if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1638
- return scanWithLabels(input, cur, ctx);
1784
+ const scan = scanWithLabels(input, cur, ctx);
1785
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1786
+ return scan;
1639
1787
  }
1640
1788
  if (log !== void 0 || captureTl) {
1641
- const tr2 = triviaP.parse(input, cur, {
1642
- trackLines: log !== void 0 ? false : ctx.trackLines,
1643
- state: ctx.state
1644
- });
1789
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1645
1790
  if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1646
1791
  const end = tr2.span.end;
1647
1792
  return {
@@ -1652,7 +1797,7 @@ function scanTrivia(input, cur, ctx) {
1652
1797
  }
1653
1798
  };
1654
1799
  }
1655
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1800
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1656
1801
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1657
1802
  }
1658
1803
  function consumeTrivia(input, cur, ctx) {
@@ -2623,6 +2768,101 @@ function markUnusedValues(root) {
2623
2768
  visit(resolveRoot(root), true);
2624
2769
  }
2625
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
+
2626
2866
  // src/combinators/parser.ts
2627
2867
  var RULE_ORDER = "__parsemanRuleOrder";
2628
2868
  function tagRule(r, key) {
@@ -2698,6 +2938,21 @@ function rules(a, b) {
2698
2938
  if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
2699
2939
  }
2700
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
+ }
2701
2956
  for (const key of Object.keys(definitions)) {
2702
2957
  markUnusedValues(cache[key]);
2703
2958
  }
@@ -3076,13 +3331,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3076
3331
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3077
3332
  const nodeType = def.type ?? missingInferredType();
3078
3333
  const cstOutput = cstOutputHost(ctx.build);
3334
+ const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3079
3335
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3080
- 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, r.span, rawChildren, triviaLog, hostState) : projectChild(children2, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, r.span, rawChildren, triviaLog, hostState) : build(children2, fields, r.span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, r.span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
3081
- 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: r.span };
3336
+ const built = unwrap && children2.length === 1 ? unwrapChild(children2[0]) : collapse && children2.length === 1 ? children2[0] : !build && project === void 0 && ctx.build?._parsemanCstCollapse && children2.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children2[0], children2, rawChildren) ? children2[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState) : projectChild(children2, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState) : build(children2, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
3337
+ 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 };
3082
3338
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3083
3339
  pushCstChild(ctx, built, rawEntry);
3084
3340
  }
3085
- return { ok: true, value: built, span: r.span };
3341
+ return { ok: true, value: built, span };
3086
3342
  }
3087
3343
  };
3088
3344
  }
@@ -3153,112 +3409,6 @@ function field(name, combinator) {
3153
3409
  };
3154
3410
  }
3155
3411
 
3156
- // src/compiler/line-index.ts
3157
- function buildLineIndex(input) {
3158
- const lineStarts = [0];
3159
- for (let i = 0; i < input.length; i++) {
3160
- if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
3161
- }
3162
- return { lineStarts };
3163
- }
3164
- function offsetToLineCol(index, offset) {
3165
- const { lineStarts } = index;
3166
- let lo = 0;
3167
- let hi = lineStarts.length - 1;
3168
- while (lo < hi) {
3169
- const mid = lo + hi + 1 >> 1;
3170
- if (lineStarts[mid] <= offset) lo = mid;
3171
- else hi = mid - 1;
3172
- }
3173
- return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
3174
- }
3175
- function annotateSpan(span, index) {
3176
- const s = offsetToLineCol(index, span.start);
3177
- const e = offsetToLineCol(index, span.end);
3178
- return {
3179
- ...span,
3180
- startLine: s.line,
3181
- startColumn: s.col,
3182
- endLine: e.line,
3183
- endColumn: e.col
3184
- };
3185
- }
3186
-
3187
- // src/combinators/grammar.ts
3188
- function parser(opts, root) {
3189
- const clearTrivia = opts.trivia === null;
3190
- return {
3191
- _tag: "grammar",
3192
- _meta: root._meta,
3193
- _def: {
3194
- tag: "grammar",
3195
- parser: root,
3196
- triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
3197
- clearTrivia,
3198
- ...opts.captureTrivia ? { captureTrivia: true } : {},
3199
- trackLines: opts.trackLines ?? false
3200
- },
3201
- parse(input, pos, _ctx) {
3202
- const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
3203
- const ctx = {
3204
- ..._ctx,
3205
- trackLines,
3206
- // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
3207
- ...clearTrivia ? {
3208
- trivia: void 0,
3209
- triviaKindLabels: void 0
3210
- } : opts.trivia != null ? {
3211
- trivia: opts.trivia,
3212
- ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
3213
- } : {},
3214
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
3215
- // Kind-filter for per-node capture. Resolve against this scope's trivia
3216
- // labels — this parser's own trivia if it declares one, else the INHERITED
3217
- // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
3218
- // trivia is inherited rather than re-declared here. No labels → undefined
3219
- // (capture all).
3220
- ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
3221
- };
3222
- const result = root.parse(input, pos ?? 0, ctx);
3223
- if (trackLines) {
3224
- const idx = buildLineIndex(input);
3225
- return { ...result, span: annotateSpan(result.span, idx) };
3226
- }
3227
- return result;
3228
- }
3229
- };
3230
- }
3231
- function noTrivia(root) {
3232
- return parser({ trivia: null }, root);
3233
- }
3234
- var _analyzed = /* @__PURE__ */ new WeakSet();
3235
- function parse(combinator, input, opts = {}) {
3236
- if (!_analyzed.has(combinator)) {
3237
- _analyzed.add(combinator);
3238
- markUnusedValues(combinator);
3239
- }
3240
- const trackLines = opts.trackLines ?? false;
3241
- const _errors = opts.recover ? [] : void 0;
3242
- const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
3243
- const grammarTrivia = combinator._meta.grammarTrivia;
3244
- const grammarScanSkip = combinator._meta.grammarScanSkip;
3245
- const ctx = {
3246
- trackLines,
3247
- ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
3248
- ...grammarScanSkip !== void 0 ? { scanSkip: grammarScanSkip } : {},
3249
- ..._errors !== void 0 ? { _errors } : {},
3250
- ..._probe !== void 0 ? { _probe } : {}
3251
- };
3252
- const result = combinator.parse(input, 0, ctx);
3253
- if (!result.ok) return result;
3254
- const withErrors = _errors !== void 0 ? { ...result, errors: _errors, furthestFail: _probe?.best ?? null } : result;
3255
- if (trackLines) {
3256
- const idx = buildLineIndex(input);
3257
- return { ...withErrors, span: annotateSpan(withErrors.span, idx) };
3258
- }
3259
- return withErrors;
3260
- }
3261
-
3262
3412
  // src/combinators/token.ts
3263
3413
  function token(root) {
3264
3414
  const meta = {
@@ -3355,7 +3505,7 @@ function leaf(root, fn) {
3355
3505
  }
3356
3506
 
3357
3507
  // src/version.ts
3358
- var PARSEMAN_VERSION = "0.41.0";
3508
+ var PARSEMAN_VERSION = "0.42.1";
3359
3509
 
3360
3510
  // src/cst/host-mode.ts
3361
3511
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -5039,7 +5189,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5039
5189
  let cur = pos;
5040
5190
  const skip3 = resolveScanSkip(explicitSkip, raw, ctx);
5041
5191
  const probeCtx = {
5042
- trackLines: ctx.trackLines,
5192
+ trackLines: false,
5043
5193
  state: ctx.state,
5044
5194
  ...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
5045
5195
  };
@@ -5052,6 +5202,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5052
5202
  while (cur < input.length) {
5053
5203
  const s = sentinel.parse(input, cur, probeCtx);
5054
5204
  if (s.ok) {
5205
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
5055
5206
  emit2(cur);
5056
5207
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
5057
5208
  }
@@ -5067,6 +5218,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5067
5218
  if (!advanced) cur++;
5068
5219
  }
5069
5220
  if (orEOF) {
5221
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
5070
5222
  emit2(cur);
5071
5223
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
5072
5224
  }
@@ -6505,8 +6657,8 @@ function analyzeMkInlineBuild(def) {
6505
6657
  if (looseMkBuildRe(def.type).test(src)) return def.type;
6506
6658
  return null;
6507
6659
  }
6508
- function emitInlineMkNodeExpr(type, chV, rawV, pos, endVar, tlV) {
6509
- return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: { start: ${pos}, end: ${endVar} }, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`;
6660
+ function emitInlineMkNodeExpr(type, chV, rawV, spanExpr, tlV) {
6661
+ return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: ${spanExpr}, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`;
6510
6662
  }
6511
6663
 
6512
6664
  // src/compiler/inline-callback.ts
@@ -6688,6 +6840,8 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
6688
6840
  }
6689
6841
  }
6690
6842
  var HOST_READS_DECL = "const _hostReads = (b, n) => { if (b === undefined) return false; let s; try { s = Function.prototype.toString.call(b) } catch (e) { return true } if (/\\barguments\\b/.test(s)) return true; const m = /^[^(]*\\(([\\s\\S]*?)\\)/.exec(s); if (m && /\\.\\.\\.|=/.test(m[1])) return true; return b.length > n }";
6843
+ var LINE_TRACK_DECL = "const _trackLines = (_ctx, input, start, end) => { const from = _ctx._lineScannedTo ?? 0; if (end <= from) return; for (let i = from; i < end; i++) if (input.charCodeAt(i) === 10) _ctx._lineStarts.push(i + 1); _ctx._lineScannedTo = end }";
6844
+ var LINE_SPAN_DECL = "const _lineCol = (_ctx, offset) => { const starts = _ctx._lineStarts; let lo = 0, hi = starts.length - 1; while (lo < hi) { const mid = (lo + hi + 1) >> 1; if (starts[mid] <= offset) lo = mid; else hi = mid - 1 } return [lo + 1, offset - starts[lo] + 1] }\nconst _spanLines = (_ctx, start, end) => { const s = _lineCol(_ctx, start), e = _lineCol(_ctx, end); return { start, end, startLine: s[0], startColumn: s[1], endLine: e[0], endColumn: e[1] } }";
6691
6845
  var _loweringSink = null;
6692
6846
  function v(ctx, prefix = "_v") {
6693
6847
  return `${prefix}${ctx.vars++}`;
@@ -6711,13 +6865,16 @@ function reindentStmts(stmts, targetLevels) {
6711
6865
  const targetPrefix = " ".repeat(targetLevels);
6712
6866
  return stmts.map((s) => s.trim().length === 0 ? "" : targetPrefix + s.slice(minLeading));
6713
6867
  }
6714
- 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)} }`;
6715
6870
  return `return { ok: false, expected: [${expected}], span: { start: ${posExpr}, end: ${posExpr} } }`;
6716
6871
  }
6717
- 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)} }`;
6718
6874
  return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
6719
6875
  }
6720
- 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 }`;
6721
6878
  return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} }, committed: true }`;
6722
6879
  }
6723
6880
  function hoistExpected(ctx, constArrSource) {
@@ -6742,7 +6899,7 @@ function failBody(ctx, expected, posExpr) {
6742
6899
  if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
6743
6900
  return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; ${trace}break ${ctx.failLabel} }`;
6744
6901
  }
6745
- return probe + trace + failReturn(expected, posExpr);
6902
+ return probe + trace + failReturn(ctx, expected, posExpr);
6746
6903
  }
6747
6904
  function failArrBody(ctx, expectedArr, posExpr, constant = true) {
6748
6905
  const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
@@ -6752,18 +6909,19 @@ function failArrBody(ctx, expectedArr, posExpr, constant = true) {
6752
6909
  const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
6753
6910
  return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; ${trace}break ${ctx.failLabel} }`;
6754
6911
  }
6755
- if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
6756
- 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);
6757
6914
  }
6758
- function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
6759
- 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 } : {}) }`;
6760
6918
  }
6761
6919
  function committedFailBody(ctx, expectedArr = "_ctx._fx", posExpr = "_ctx._fe") {
6762
6920
  if (ctx.failLabel) {
6763
6921
  if (!ctx.recordFail) return `{ _ctx._fc = true; break ${ctx.failLabel} }`;
6764
6922
  return `{ _ctx._fc = true; _ctx._fe = ${posExpr}; _ctx._fx = ${expectedArr}; break ${ctx.failLabel} }`;
6765
6923
  }
6766
- return committedReturnArr(expectedArr, posExpr);
6924
+ return committedReturnArr(ctx, expectedArr, posExpr);
6767
6925
  }
6768
6926
  function propagateFailBody(ctx, srcCtx = "_ctx") {
6769
6927
  if (srcCtx !== "_ctx") {
@@ -6771,10 +6929,10 @@ function propagateFailBody(ctx, srcCtx = "_ctx") {
6771
6929
  if (!ctx.recordFail) return `break ${ctx.failLabel}`;
6772
6930
  return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; break ${ctx.failLabel} }`;
6773
6931
  }
6774
- 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)} }`;
6775
6933
  }
6776
6934
  if (ctx.failLabel) return `break ${ctx.failLabel}`;
6777
- return resultFromRecorded();
6935
+ return resultFromRecorded(ctx);
6778
6936
  }
6779
6937
  function emitIfFail(ctx, cond, body) {
6780
6938
  return [
@@ -6828,14 +6986,46 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
6828
6986
  if (ctx.capAsTrivia) return [];
6829
6987
  const i = ind(ctx);
6830
6988
  const lf = v(ctx, "_lf");
6989
+ const spanExpr = emitSpanExpr(ctx, startExpr, endExpr);
6831
6990
  return [
6832
6991
  `${i}if (_ctx._cstLeaves || _ctx._cstRawChildren) {`,
6833
- `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } }`,
6992
+ `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: ${spanExpr} }`,
6834
6993
  `${i} if (_ctx._cstLeaves) _ctx._cstLeaves.push(${lf})`,
6835
6994
  `${i} if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(${lf})`,
6836
6995
  `${i}}`
6837
6996
  ];
6838
6997
  }
6998
+ function emitSpanExpr(ctx, startExpr, endExpr) {
6999
+ if (!ctx.lineTracking) return `{ start: ${startExpr}, end: ${endExpr} }`;
7000
+ ctx.needsLineSpan = true;
7001
+ return `_spanLines(_ctx, ${startExpr}, ${endExpr})`;
7002
+ }
7003
+ function emitLineTrack(ctx, startExpr, endExpr) {
7004
+ if (!ctx.lineTracking) return [];
7005
+ ctx.needsLineTrack = true;
7006
+ return [`${ind(ctx)}_trackLines(_ctx, input, ${startExpr}, ${endExpr})`];
7007
+ }
7008
+ function emitLiteralLineTrack(ctx, startExpr, value) {
7009
+ if (!ctx.lineTracking) return [];
7010
+ const lineDeltas = [];
7011
+ for (let i = 0; i < value.length; i++) {
7012
+ if (value.charCodeAt(i) === 10) lineDeltas.push(i + 1);
7013
+ }
7014
+ if (lineDeltas.length === 0) return [];
7015
+ const fromV = v(ctx, "_ltFrom");
7016
+ const endExpr = value.length === 0 ? startExpr : `${startExpr} + ${value.length}`;
7017
+ return [
7018
+ `${ind(ctx)}const ${fromV} = _ctx._lineScannedTo ?? 0`,
7019
+ `${ind(ctx)}if (${endExpr} > ${fromV}) {`,
7020
+ `${ind(ctx)} for (let i = ${fromV}; i < ${startExpr}; i++) if (input.charCodeAt(i) === 10) _ctx._lineStarts.push(i + 1)`,
7021
+ ...lineDeltas.map((delta) => {
7022
+ const lineStart = `${startExpr} + ${delta}`;
7023
+ return `${ind(ctx)} if (${lineStart} > ${fromV}) _ctx._lineStarts.push(${lineStart})`;
7024
+ }),
7025
+ `${ind(ctx)} _ctx._lineScannedTo = ${endExpr}`,
7026
+ `${ind(ctx)}}`
7027
+ ];
7028
+ }
6839
7029
  function ensureRegexDecl(ctx, source, flags) {
6840
7030
  const f = "y" + flags.replace(/[gy]/g, "");
6841
7031
  const key = `${source}/${f}`;
@@ -7220,6 +7410,7 @@ function emitLit(def, ctx, pos) {
7220
7410
  );
7221
7411
  }
7222
7412
  const endVar = len === 0 ? pos : `${pos} + ${len}`;
7413
+ stmts.push(...emitLiteralLineTrack(ctx, pos, value));
7223
7414
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7224
7415
  return { stmts, valueVar: vv, endVar };
7225
7416
  }
@@ -7299,7 +7490,7 @@ function emitKeywords(def, ctx, pos) {
7299
7490
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7300
7491
  return { stmts, valueVar: vv, endVar };
7301
7492
  }
7302
- function emitRegex(def, ctx, pos) {
7493
+ function emitRegex(def, ctx, pos, canMatchNewline = true) {
7303
7494
  const expectedStr = JSON.stringify(`/${def.source}/`);
7304
7495
  const shape = scanShapeFromRegex(def.source, def.flags);
7305
7496
  if (shape) {
@@ -7312,7 +7503,11 @@ function emitRegex(def, ctx, pos) {
7312
7503
  fresh: (prefix) => v(ctx, prefix)
7313
7504
  });
7314
7505
  if (scanned) {
7315
- const stmts2 = [...scanned.stmts, ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)];
7506
+ const stmts2 = [
7507
+ ...scanned.stmts,
7508
+ ...canMatchNewline ? emitLineTrack(ctx, pos, scanned.endVar) : [],
7509
+ ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)
7510
+ ];
7316
7511
  return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
7317
7512
  }
7318
7513
  }
@@ -7334,6 +7529,7 @@ function emitRegex(def, ctx, pos) {
7334
7529
  `${ind(ctx)}const ${vv} = ${mv}[0]`
7335
7530
  ];
7336
7531
  const endVar = `${pos} + ${vv}.length`;
7532
+ if (canMatchNewline) stmts.push(...emitLineTrack(ctx, pos, endVar));
7337
7533
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7338
7534
  return { stmts, valueVar: vv, endVar };
7339
7535
  }
@@ -7371,7 +7567,8 @@ function emitSeqValues(def, ctx, pos) {
7371
7567
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
7372
7568
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
7373
7569
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7374
- `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`
7570
+ `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`,
7571
+ ...emitLineTrack(ctx, curV, scanEndV)
7375
7572
  );
7376
7573
  const r2 = emit(def.parsers[i], ctx, scanEndV);
7377
7574
  stmts.push(...r2.stmts);
@@ -7389,7 +7586,8 @@ function emitSeqValues(def, ctx, pos) {
7389
7586
  const capArg = ctx.noHoist ? "0" : "_ctx._triviaLog !== undefined ? 2 : 0";
7390
7587
  stmts.push(
7391
7588
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7392
- `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`
7589
+ `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`,
7590
+ ...emitLineTrack(ctx, curV, scanEndV)
7393
7591
  );
7394
7592
  const r2 = emit(def.parsers[i], ctx, scanEndV);
7395
7593
  stmts.push(...r2.stmts);
@@ -8089,14 +8287,15 @@ function emitMany(def, ctx, pos) {
8089
8287
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8090
8288
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8091
8289
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8092
- `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`
8290
+ `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8291
+ ...emitLineTrack(ctx, curV, npV)
8093
8292
  );
8094
8293
  itemPos = npV;
8095
8294
  rollback = `if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markV}) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${markTl}) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}; `;
8096
8295
  } else {
8097
8296
  const trivFn = ensureTriviaFn(ctx);
8098
8297
  const npV = v(ctx, "_np");
8099
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`);
8298
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, npV));
8100
8299
  itemPos = npV;
8101
8300
  }
8102
8301
  }
@@ -8166,7 +8365,7 @@ function emitAttempt(p, def, ctx, pos) {
8166
8365
  if (!ctx.recovery && !ctx.coverage && needsFirstSetGuard(def.parser)) {
8167
8366
  const gcV = v(ctx, "_agc");
8168
8367
  const gExp = armStaticExpected(ctx, def.parser);
8169
- 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)} }`;
8170
8369
  preGuard.push(
8171
8370
  `${ind(ctx)}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
8172
8371
  `${ind(ctx)}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
@@ -8273,7 +8472,8 @@ function emitSepBy(_p, def, ctx, pos) {
8273
8472
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8274
8473
  `${ind(ctx)}const ${markLv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8275
8474
  `${ind(ctx)}const ${markFld} = _ctx._fields ? _ctx._fields.length : 0`,
8276
- `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`
8475
+ `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8476
+ ...emitLineTrack(ctx, curV, spV)
8277
8477
  );
8278
8478
  sepAtPos = spV;
8279
8479
  const rollbackToSep = `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${markLv}) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markV}) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${markTl}) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}; if (_ctx._fields && _ctx._fields.length !== ${markFld}) _ctx._fields.length = ${markFld}; `;
@@ -8283,7 +8483,7 @@ function emitSepBy(_p, def, ctx, pos) {
8283
8483
  const post = postSepMarks();
8284
8484
  stmts.push(...post.decl);
8285
8485
  const npV = v(ctx, "_np");
8286
- stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
8486
+ stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`, ...emitLineTrack(ctx, sepEnd, npV));
8287
8487
  const next = emitFallible(def.parser, ctx, npV, true);
8288
8488
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8289
8489
  stmts.push(
@@ -8295,13 +8495,13 @@ function emitSepBy(_p, def, ctx, pos) {
8295
8495
  } else {
8296
8496
  const trivFn = ensureTriviaFn(ctx);
8297
8497
  const spV = v(ctx, "_sp");
8298
- stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
8498
+ stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, spV));
8299
8499
  sepAtPos = spV;
8300
8500
  const sep = emitFallible(def.separator, ctx, sepAtPos, true);
8301
8501
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep;
8302
8502
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
8303
8503
  const npV = v(ctx, "_np");
8304
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
8504
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`, ...emitLineTrack(ctx, sepEnd, npV));
8305
8505
  const next = emitFallible(def.parser, ctx, npV, true);
8306
8506
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8307
8507
  stmts.push(
@@ -8388,6 +8588,7 @@ function emitScanTo(def, ctx, pos) {
8388
8588
  }
8389
8589
  const valV = v(ctx);
8390
8590
  stmts.push(`${ind(ctx)}const ${valV} = input.slice(${pos}, ${curV})`);
8591
+ stmts.push(...emitLineTrack(ctx, pos, curV));
8391
8592
  if (ctx.capturing) {
8392
8593
  const cap = reindentStmts(emitLeafCapture(ctx, valV, pos, curV), ctx.indent + 1);
8393
8594
  stmts.push(`${ind(ctx)}if (${curV} > ${pos}) {`, ...cap, `${ind(ctx)}}`);
@@ -8531,7 +8732,7 @@ function tokenNullableRegex(def) {
8531
8732
  }
8532
8733
  function emitToken(def, ctx, pos) {
8533
8734
  const lowered = tokenNullableRegex(def);
8534
- if (lowered) return emitRegex(lowered, ctx, pos);
8735
+ if (lowered) return emitRegex(lowered, ctx, pos, def.parser._meta.canMatchNewline);
8535
8736
  const savedTrivia = ctx.activeTrivia;
8536
8737
  const savedKindLabels = ctx.triviaKindLabels;
8537
8738
  ctx.activeTrivia = void 0;
@@ -8663,7 +8864,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8663
8864
  if (!ctx.recovery && !ctx.coverage && (capturesChildren || structural) && needsFirstSetGuard(def.parser)) {
8664
8865
  const gcV = v(ctx, "_ngc");
8665
8866
  const gExp = armStaticExpected(ctx, def.parser);
8666
- 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);
8667
8868
  preGuard.push(
8668
8869
  `${i}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
8669
8870
  `${i}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
@@ -8681,7 +8882,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8681
8882
  stmts.push(...innerStmts);
8682
8883
  if (def.trailingTrivia === true && ctx.activeTrivia) {
8683
8884
  const triviaFn = ensureTriviaFn(ctx);
8684
- stmts.push(`${i}const ${endVar} = ${okVar} ? ${triviaFn}(input, ${innerEndVar}, _ctx, 1) : ${innerEndVar}`);
8885
+ stmts.push(`${i}const ${endVar} = ${okVar} ? ${triviaFn}(input, ${innerEndVar}, _ctx, 1) : ${innerEndVar}`, ...emitLineTrack(ctx, innerEndVar, endVar));
8685
8886
  }
8686
8887
  if (sf && fArr) {
8687
8888
  stmts.push(`${i}const ${fArr} = _ctx._fields`);
@@ -8721,8 +8922,11 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8721
8922
  );
8722
8923
  }
8723
8924
  const ndV = v(ctx, "_nd");
8724
- const buildExpr = structural ? `{ _tag: 'node', type: ${JSON.stringify(def.type)}, span: { start: ${pos}, end: ${endVar} }, state: ${stV} ?? null, children: ${chV} }` : mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : def.project !== void 0 ? emitNodeProjectExpr(def.type, def.project, chV) : `${buildRef(ctx)}[${buildIdx}](${chV}, ${fObj}, { start: ${pos}, end: ${endVar} }, ${rawV}, ${tlV}, ${stV})`;
8725
- const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, { start: ${pos}, end: ${endVar} }, ${rawV}, ${tlV}, ${stV})`;
8925
+ const nodeSpanExpr = emitSpanExpr(ctx, pos, endVar);
8926
+ const nodeSpanV = ctx.lineTracking ? v(ctx, "_nspan") : nodeSpanExpr;
8927
+ if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
8928
+ const buildExpr = structural ? `{ _tag: 'node', type: ${JSON.stringify(def.type)}, span: ${nodeSpanV}, state: ${stV} ?? null, children: ${chV} }` : mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, nodeSpanV, tlV) : def.project !== void 0 ? emitNodeProjectExpr(def.type, def.project, chV) : `${buildRef(ctx)}[${buildIdx}](${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
8929
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
8726
8930
  const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
8727
8931
  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;
8728
8932
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
@@ -8732,7 +8936,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8732
8936
  `${i}const ${ndV} = (${profileRecognizer} || ${profileCapture}) ? undefined : (${finalExpr})`,
8733
8937
  `${i}if (!(${profileRecognizer})) {`,
8734
8938
  `${i} if (${sc}) ${sc}.push(${ndV})`,
8735
- `${i} if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && (${ndV}._tag === 'node' || ${ndV}._tag === 'leaf' || ${ndV}._tag === 'parseError')) ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : (typeof ${ndV} === 'object' && ${ndV} !== null ? input.slice(${pos}, ${endVar}) : ''), span: { start: ${pos}, end: ${endVar} } })`,
8939
+ `${i} if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && (${ndV}._tag === 'node' || ${ndV}._tag === 'leaf' || ${ndV}._tag === 'parseError')) ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : (typeof ${ndV} === 'object' && ${ndV} !== null ? input.slice(${pos}, ${endVar}) : ''), span: ${nodeSpanV} })`,
8736
8940
  `${i}}`
8737
8941
  );
8738
8942
  return { stmts, valueVar: ndV, endVar };
@@ -8748,7 +8952,8 @@ function emitRuntimeFallback(parser2, ctx, pos) {
8748
8952
  `${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
8749
8953
  ...emitIfFail(ctx, `!${rv}.ok`, failStmt),
8750
8954
  `${ind(ctx)}const ${vv} = ${rv}.value`,
8751
- `${ind(ctx)}const ${ev} = ${rv}.span.end`
8955
+ `${ind(ctx)}const ${ev} = ${rv}.span.end`,
8956
+ ...parser2._meta.canMatchNewline ? emitLineTrack(ctx, pos, ev) : []
8752
8957
  ];
8753
8958
  return { stmts, valueVar: vv, endVar: ev };
8754
8959
  }
@@ -8814,7 +9019,8 @@ function emitRecover(def, ctx, pos) {
8814
9019
  `${ind0} if (${sentOk}) break`,
8815
9020
  `${ind0} ${scanV}++`,
8816
9021
  `${ind0} }`,
8817
- `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(deriveExpected(def.parser))} }`,
9022
+ ...emitLineTrack(ctx, pos, scanV).map((s) => `${ind0} ${s.trim()}`),
9023
+ `${ind0} const ${errV} = { _tag: 'parseError', span: ${emitSpanExpr(ctx, pos, scanV)}, expected: ${JSON.stringify(deriveExpected(def.parser))} }`,
8818
9024
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
8819
9025
  `${ind0} ${valVar} = ${errV}`,
8820
9026
  `${ind0} ${endVar} = ${scanV}`,
@@ -8830,7 +9036,7 @@ function emitExpect(def, ctx, pos) {
8830
9036
  const stmts = [
8831
9037
  ...innerStmts,
8832
9038
  `${ind0}if (!${okVar}) {`,
8833
- `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${pos} }, expected: ${JSON.stringify(def.expected)} }`,
9039
+ `${ind0} const ${errV} = { _tag: 'parseError', span: ${emitSpanExpr(ctx, pos, pos)}, expected: ${JSON.stringify(def.expected)} }`,
8834
9040
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
8835
9041
  // Embed the missing-token error as a `parseError` CST child, mirroring the
8836
9042
  // interpreter (expect.ts) and the list-recovery emit sites — guarded on
@@ -8920,7 +9126,7 @@ function emitDispatch(p, ctx, pos) {
8920
9126
  case "literal":
8921
9127
  return emitLit(def, ctx, pos);
8922
9128
  case "regex":
8923
- return emitRegex(def, ctx, pos);
9129
+ return emitRegex(def, ctx, pos, p._meta.canMatchNewline);
8924
9130
  case "keywords":
8925
9131
  return emitKeywords(def, ctx, pos);
8926
9132
  case "sequence":
@@ -9170,6 +9376,21 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
9170
9376
  return false;
9171
9377
  }
9172
9378
  }
9379
+ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9380
+ if (seen.has(p)) return false;
9381
+ seen.add(p);
9382
+ const d = p._def;
9383
+ if (p._meta.grammarTrackLines) return true;
9384
+ if (d.tag === "grammar" && d.trackLines) return true;
9385
+ if (d.tag === "lazy") {
9386
+ try {
9387
+ return hasLineTrackingDef(d.thunk(), seen);
9388
+ } catch {
9389
+ return false;
9390
+ }
9391
+ }
9392
+ return childrenOf2(d).some((child) => hasLineTrackingDef(child, seen));
9393
+ }
9173
9394
  function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9174
9395
  if (seen.has(p)) return false;
9175
9396
  seen.add(p);
@@ -9517,6 +9738,7 @@ function compile(combinator, mapFnSources, opts) {
9517
9738
  const grammarTrivia = combinator._meta.grammarTrivia;
9518
9739
  const grammarScanSkip = combinator._meta.grammarScanSkip;
9519
9740
  const grammarHostMode = combinator._meta.grammarHostMode;
9741
+ const grammarTrackLines = combinator._meta.grammarTrackLines;
9520
9742
  const ctx = {
9521
9743
  vars: 0,
9522
9744
  indent: 1,
@@ -9537,6 +9759,7 @@ function compile(combinator, mapFnSources, opts) {
9537
9759
  capturing: hasNodeDef(combinator),
9538
9760
  recovery: opts?.recovery ?? false,
9539
9761
  hostMode: opts?.hostMode ?? grammarHostMode ?? "ast",
9762
+ lineTracking: opts?.trackLines === true || grammarTrackLines === true || hasLineTrackingDef(combinator),
9540
9763
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
9541
9764
  lazyUsage: analyzeLazyUsage(combinator),
9542
9765
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
@@ -9560,9 +9783,13 @@ function compile(combinator, mapFnSources, opts) {
9560
9783
  const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
9561
9784
  const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
9562
9785
  const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
9786
+ const lineTrackDecls = ctx.needsLineTrack ? [LINE_TRACK_DECL] : [];
9787
+ const lineSpanDecls = ctx.needsLineSpan ? [LINE_SPAN_DECL] : [];
9563
9788
  const source = [
9564
9789
  ...emptyTlDecls,
9565
9790
  ...hostReadsDecls,
9791
+ ...lineTrackDecls,
9792
+ ...lineSpanDecls,
9566
9793
  ...ctx.regexDecls,
9567
9794
  ...ctx.expectedDecls,
9568
9795
  "",
@@ -9575,6 +9802,8 @@ function compile(combinator, mapFnSources, opts) {
9575
9802
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
9576
9803
  ...emptyTlDecls,
9577
9804
  ...hostReadsDecls,
9805
+ ...lineTrackDecls,
9806
+ ...lineSpanDecls,
9578
9807
  ...ctx.regexDecls,
9579
9808
  ...ctx.expectedDecls,
9580
9809
  ...namedPrelude,
@@ -9593,6 +9822,25 @@ function compile(combinator, mapFnSources, opts) {
9593
9822
  trackLines: false,
9594
9823
  ...ctx.triviaKindLabels ? { triviaKindLabels: ctx.triviaKindLabels } : {}
9595
9824
  };
9825
+ const annotateWithTrackedLines = (result, lineStarts) => {
9826
+ const index = normalizeLineIndex({ lineStarts });
9827
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
9828
+ const errors = annotated.errors;
9829
+ if (errors) {
9830
+ ;
9831
+ annotated.errors = errors.map((error) => ({
9832
+ ...error,
9833
+ span: annotateSpan(error.span, index)
9834
+ }));
9835
+ }
9836
+ return annotated;
9837
+ };
9838
+ const trackedCtx = (base, input, pos) => {
9839
+ if (!ctx.lineTracking) return base;
9840
+ const lineIndex = { lineStarts: [0] };
9841
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
9842
+ return { ...base, trackLines: true, _lineStarts: lineIndex.lineStarts, _lineScannedTo: pos };
9843
+ };
9596
9844
  const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
9597
9845
  const effectiveSources = mapFnSources ?? derivedSrcs;
9598
9846
  const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
@@ -9600,17 +9848,46 @@ function compile(combinator, mapFnSources, opts) {
9600
9848
  const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
9601
9849
  const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
9602
9850
  const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources, coverageRootRuleId) : null;
9603
- return {
9851
+ const compiledMeta = {
9604
9852
  source,
9605
9853
  inlineExpression,
9606
9854
  ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions },
9607
- ...gatingReport === void 0 ? {} : { gating: gatingReport },
9855
+ ...gatingReport === void 0 ? {} : { gating: gatingReport }
9856
+ };
9857
+ if (!ctx.lineTracking) {
9858
+ return {
9859
+ ...compiledMeta,
9860
+ parse(input, pos = 0) {
9861
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
9862
+ },
9863
+ parseWithContext(input, parseCtx, pos = 0) {
9864
+ assertHostModeCompatible(ctx.hostMode ?? "ast", parseCtx.build, !!ctx.hostBranchElided);
9865
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9866
+ },
9867
+ // Note: collects expect() errors via _errors. Unlike interpreter
9868
+ // parse({recover:true}) it does NOT populate furthestFail — the compiled path
9869
+ // inlines failures for throughput and deliberately skips _probe bookkeeping.
9870
+ // Callers wanting a furthest-position diagnostic detect unconsumed input
9871
+ // (span.end < input.length) instead, which is mode-agnostic.
9872
+ parseWithErrors(input, pos = 0) {
9873
+ const errors = [];
9874
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
9875
+ return { ...result, errors };
9876
+ }
9877
+ };
9878
+ }
9879
+ return {
9880
+ ...compiledMeta,
9608
9881
  parse(input, pos = 0) {
9609
- return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
9882
+ const parseCtx = trackedCtx(defaultCtx, input, pos);
9883
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9884
+ return annotateWithTrackedLines(result, parseCtx._lineStarts);
9610
9885
  },
9611
9886
  parseWithContext(input, parseCtx, pos = 0) {
9612
9887
  assertHostModeCompatible(ctx.hostMode ?? "ast", parseCtx.build, !!ctx.hostBranchElided);
9613
- return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9888
+ const activeCtx = trackedCtx(parseCtx, input, pos);
9889
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, activeCtx);
9890
+ return activeCtx.trackLines ? annotateWithTrackedLines(result, activeCtx._lineStarts) : result;
9614
9891
  },
9615
9892
  // Note: collects expect() errors via _errors. Unlike interpreter
9616
9893
  // parse({recover:true}) it does NOT populate furthestFail — the compiled path
@@ -9619,8 +9896,9 @@ function compile(combinator, mapFnSources, opts) {
9619
9896
  // (span.end < input.length) instead, which is mode-agnostic.
9620
9897
  parseWithErrors(input, pos = 0) {
9621
9898
  const errors = [];
9622
- const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
9623
- return { ...result, errors };
9899
+ const parseCtx = trackedCtx({ ...defaultCtx, _errors: errors }, input, pos);
9900
+ const result = { ...fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx), errors };
9901
+ return annotateWithTrackedLines(result, parseCtx._lineStarts);
9624
9902
  }
9625
9903
  };
9626
9904
  }
@@ -9645,6 +9923,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
9645
9923
  const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
9646
9924
  const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
9647
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);
9648
9927
  const ruleMap = ruleMapArg.filter(([, val]) => {
9649
9928
  const d = val._def;
9650
9929
  if (d.tag !== "lazy") return true;
@@ -9679,6 +9958,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
9679
9958
  capturing: opts?.captureTerminals === true || ruleMap.some(([, rule]) => hasNodeDef(rule)),
9680
9959
  recovery: opts?.recovery ?? false,
9681
9960
  hostMode: grammarHostMode ?? "ast",
9961
+ lineTracking: grammarTrackLines,
9682
9962
  ...opts?.coverage ? { coverage: { plan: opts.coverage } } : {},
9683
9963
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
9684
9964
  ns,
@@ -9775,6 +10055,10 @@ function compileLinkable(ruleMapArg, ns, opts) {
9775
10055
  perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
9776
10056
  }
9777
10057
  if (ctx.runtimeParsers.length !== 0) return null;
10058
+ if (ctx.lineTracking) {
10059
+ ctx.needsLineTrack = true;
10060
+ ctx.needsLineSpan = true;
10061
+ }
9778
10062
  const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
9779
10063
  const buildSrcs = ctx.buildFns.length > 0 && ctx.buildSrcs.every((s) => s !== null) ? ctx.buildSrcs : null;
9780
10064
  const ruleFns = /* @__PURE__ */ new Map();
@@ -9788,6 +10072,8 @@ function compileLinkable(ruleMapArg, ns, opts) {
9788
10072
  const mfDecl = ctx.mapFns.length === 0 ? "" : mfSrcs ? `const ${mfRef(ctx)} = [${mfSrcs.join(", ")}]` : `const ${mfRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "mf")}]`;
9789
10073
  const buildDecl = ctx.buildFns.length === 0 ? "" : buildSrcs ? `const ${buildRef(ctx)} = [${buildSrcs.join(", ")}]` : `const ${buildRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "build")}]`;
9790
10074
  const prelude = [
10075
+ ctx.needsLineTrack ? LINE_TRACK_DECL : "",
10076
+ ctx.needsLineSpan ? LINE_SPAN_DECL : "",
9791
10077
  ...ctx.regexDecls,
9792
10078
  ...ctx.expectedDecls,
9793
10079
  mfDecl,
@@ -9796,11 +10082,18 @@ function compileLinkable(ruleMapArg, ns, opts) {
9796
10082
  ].filter(Boolean);
9797
10083
  const wrappers = /* @__PURE__ */ new Map();
9798
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} }`;
9799
10091
  wrappers.set(key, publicRuleWrapperSource(rule, [
9800
10092
  `function(input, _pos, _ctx) {`,
10093
+ ...linePreamble,
9801
10094
  ` let pos = _pos`,
9802
10095
  ...r.stmts,
9803
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
10096
+ ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`,
9804
10097
  `}`
9805
10098
  ].join("\n"), ctx.triviaKindLabels));
9806
10099
  }
@@ -9907,12 +10200,16 @@ function buildInlineExpression(ctx, r, mapFnSources, buildSources, coverageRootR
9907
10200
  const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
9908
10201
  const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
9909
10202
  const hostReadsDecl = ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "";
9910
- const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl;
10203
+ const lineTrackDecl = ctx.needsLineTrack ? ` ${LINE_TRACK_DECL}` : "";
10204
+ const lineSpanDecl = ctx.needsLineSpan ? ` ${LINE_SPAN_DECL}` : "";
10205
+ const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl || !!lineTrackDecl || !!lineSpanDecl;
9911
10206
  if (!needsWrapper) return innerFn;
9912
10207
  const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
9913
10208
  const hoistedDecls = [
9914
10209
  emptyTlDecl,
9915
10210
  hostReadsDecl,
10211
+ lineTrackDecl,
10212
+ lineSpanDecl,
9916
10213
  ...ctx.regexDecls.map((d) => ` ${d}`),
9917
10214
  ...ctx.expectedDecls.map((d) => ` ${d}`),
9918
10215
  mfDecl,
@@ -10461,7 +10758,7 @@ function normalizeCstCollapse(collapse) {
10461
10758
  return typeof collapse === "function" ? collapse : void 0;
10462
10759
  }
10463
10760
  function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
10464
- return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children2] };
10761
+ return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children2] };
10465
10762
  }
10466
10763
  function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
10467
10764
  if (typeof typeOrOptions === "string") {
@@ -10684,7 +10981,8 @@ function materializePiece(p, trivia2, captureTerminals = false, hostMode) {
10684
10981
  const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, {
10685
10982
  ...trivia2 ? { trivia: trivia2 } : {},
10686
10983
  ...captureTerminals ? { captureTerminals: true } : {},
10687
- ...hostMode ? { hostMode } : {}
10984
+ ...hostMode ? { hostMode } : {},
10985
+ ...p.trackLines ? { trackLines: true } : {}
10688
10986
  });
10689
10987
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
10690
10988
  return pieces;
@@ -12185,6 +12483,7 @@ export {
12185
12483
  analyzeGatingRules,
12186
12484
  analyzeGrammarGating,
12187
12485
  annotateSpan,
12486
+ annotateTreeSpans,
12188
12487
  applyEdit,
12189
12488
  attempt,
12190
12489
  balanced,
@@ -12206,6 +12505,7 @@ export {
12206
12505
  createGrammarCoverageCollector,
12207
12506
  createGrammarInstrumentationContext,
12208
12507
  createGrammarTraceSink,
12508
+ createLineIndex,
12209
12509
  createVisitor,
12210
12510
  cstBuildHost,
12211
12511
  dispatch,
@@ -12239,6 +12539,7 @@ export {
12239
12539
  matches,
12240
12540
  noTrivia,
12241
12541
  node,
12542
+ normalizeLineIndex,
12242
12543
  not,
12243
12544
  offsetToLineCol,
12244
12545
  oneOrMore,
@@ -12249,6 +12550,7 @@ export {
12249
12550
  parseDoc,
12250
12551
  parser,
12251
12552
  peek,
12553
+ recordLineRange,
12252
12554
  ref,
12253
12555
  regex,
12254
12556
  relativize,