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
@@ -317,6 +317,65 @@ function failAt(ctx, expected, pos) {
317
317
  return r;
318
318
  }
319
319
 
320
+ // src/line-index.ts
321
+ function createLineIndex() {
322
+ return { lineStarts: [0] };
323
+ }
324
+ function recordLineRange(index, input, start, end) {
325
+ const lineStarts = index.lineStarts;
326
+ for (let i = start; i < end; i++) {
327
+ if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
328
+ }
329
+ }
330
+ function recordLineRangeFromContext(ctx, input, _startHint, end) {
331
+ if (!ctx.trackLines) return;
332
+ const from = ctx._lineScannedTo ?? 0;
333
+ if (end <= from) return;
334
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
335
+ if (!index) return;
336
+ recordLineRange(index, input, from, end);
337
+ ctx._lineScannedTo = end;
338
+ }
339
+ function normalizeLineIndex(index) {
340
+ const starts = index.lineStarts;
341
+ starts.sort((a, b) => a - b);
342
+ let write = 0;
343
+ for (let read = 0; read < starts.length; read++) {
344
+ const value = starts[read];
345
+ if (write === 0 || starts[write - 1] !== value) starts[write++] = value;
346
+ }
347
+ starts.length = write;
348
+ if (starts.length === 0 || starts[0] !== 0) starts.unshift(0);
349
+ return index;
350
+ }
351
+ function offsetToLineCol(index, offset) {
352
+ const { lineStarts } = index;
353
+ let lo = 0;
354
+ let hi = lineStarts.length - 1;
355
+ while (lo < hi) {
356
+ const mid = lo + hi + 1 >> 1;
357
+ if (lineStarts[mid] <= offset) lo = mid;
358
+ else hi = mid - 1;
359
+ }
360
+ return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
361
+ }
362
+ function annotateSpan(span, index) {
363
+ const s = offsetToLineCol(index, span.start);
364
+ const e = offsetToLineCol(index, span.end);
365
+ return {
366
+ ...span,
367
+ startLine: s.line,
368
+ startColumn: s.col,
369
+ endLine: e.line,
370
+ endColumn: e.col
371
+ };
372
+ }
373
+ function annotateSpanFromLineContext(span, ctx) {
374
+ if (!ctx.trackLines) return span;
375
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
376
+ return index ? annotateSpan(span, index) : span;
377
+ }
378
+
320
379
  // src/cst/capture-buffer.ts
321
380
  var EMPTY = [];
322
381
  var EMPTY_TL = [];
@@ -358,7 +417,12 @@ function endCstNodeCapture(ctx, saved) {
358
417
  return materialized;
359
418
  }
360
419
  function pushCstLeaf(ctx, leaf2) {
361
- pushCstChild(ctx, leaf2, leaf2);
420
+ if (!ctx.trackLines) {
421
+ pushCstChild(ctx, leaf2, leaf2);
422
+ return;
423
+ }
424
+ const annotated = leaf2 && typeof leaf2 === "object" && leaf2.span ? { ...leaf2, span: annotateSpanFromLineContext(leaf2.span, ctx) } : leaf2;
425
+ pushCstChild(ctx, annotated, annotated);
362
426
  }
363
427
  function pushCstChild(ctx, built, rawEntry) {
364
428
  const b = ctx._cstBuf;
@@ -492,6 +556,7 @@ function literal(value, opts = {}) {
492
556
  canMatchNewline: value.includes("\n"),
493
557
  isTrivia: false
494
558
  };
559
+ const canMatchNewline = meta.canMatchNewline;
495
560
  if (caseInsensitive) {
496
561
  const upper = value.toUpperCase();
497
562
  const lower = value.toLowerCase();
@@ -508,6 +573,7 @@ function literal(value, opts = {}) {
508
573
  return function parse3(input, pos, ctx) {
509
574
  if (input.charCodeAt(pos) === code) {
510
575
  const span = { start: pos, end: pos + 1 };
576
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
511
577
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
512
578
  return { ok: true, value, span };
513
579
  }
@@ -521,6 +587,7 @@ function literal(value, opts = {}) {
521
587
  const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
522
588
  if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
523
589
  const span = { start: pos, end };
590
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
524
591
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
525
592
  return { ok: true, value: matchedValue, span };
526
593
  }
@@ -688,6 +755,7 @@ function regex(pattern, flags = "") {
688
755
  if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
689
756
  const value = input.slice(pos, scanEnd);
690
757
  const span2 = { start: pos, end: scanEnd };
758
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
691
759
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
692
760
  return { ok: true, value, span: span2 };
693
761
  }
@@ -697,6 +765,7 @@ function regex(pattern, flags = "") {
697
765
  return failAt(ctx, [`/${source}/`], pos);
698
766
  }
699
767
  const span = { start: pos, end: pos + m[0].length };
768
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
700
769
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
701
770
  return { ok: true, value: m[0], span };
702
771
  }
@@ -835,8 +904,8 @@ function parseRegex(src) {
835
904
  }
836
905
  if (src[k] !== "]") throw BAIL;
837
906
  i = k + 1;
838
- if (negated) return charNode(any(), true);
839
907
  const ranges = parseClassRanges(body);
908
+ if (negated) return charNode(any(), !ranges || !rangesHaveNewline(ranges));
840
909
  if (!ranges) return charNode(any(), true);
841
910
  return charNode(rangesToSet(ranges), rangesHaveNewline(ranges));
842
911
  };
@@ -932,7 +1001,7 @@ function firstSet(n) {
932
1001
  }
933
1002
  }
934
1003
  }
935
- function firstCanBeNewline(n) {
1004
+ function canConsumeNewline(n) {
936
1005
  switch (n.t) {
937
1006
  case "char":
938
1007
  return n.nl;
@@ -941,16 +1010,11 @@ function firstCanBeNewline(n) {
941
1010
  case "empty":
942
1011
  return false;
943
1012
  case "rep":
944
- return firstCanBeNewline(n.node);
1013
+ return canConsumeNewline(n.node);
945
1014
  case "alt":
946
- return n.arms.some(firstCanBeNewline);
947
- case "seq": {
948
- for (const part of n.parts) {
949
- if (firstCanBeNewline(part)) return true;
950
- if (!nullable(part)) break;
951
- }
952
- return false;
953
- }
1015
+ return n.arms.some(canConsumeNewline);
1016
+ case "seq":
1017
+ return n.parts.some(canConsumeNewline);
954
1018
  }
955
1019
  }
956
1020
  function firstSetFromRegex(source) {
@@ -960,8 +1024,8 @@ function firstSetFromRegex(source) {
960
1024
  } catch {
961
1025
  return { firstSet: any(), canMatchNewline: true };
962
1026
  }
963
- if (nullable(ast)) return { firstSet: any(), canMatchNewline: true };
964
- return { firstSet: firstSet(ast), canMatchNewline: firstCanBeNewline(ast) };
1027
+ if (nullable(ast)) return { firstSet: any(), canMatchNewline: canConsumeNewline(ast) };
1028
+ return { firstSet: firstSet(ast), canMatchNewline: canConsumeNewline(ast) };
965
1029
  }
966
1030
 
967
1031
  // src/combinators/case-fold.ts
@@ -1118,7 +1182,9 @@ function firstSetSentinel(fs2) {
1118
1182
  function recoverScan(input, from, ctx, sync, expected) {
1119
1183
  let scanPos = from;
1120
1184
  while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1121
- const error = { _tag: "parseError", span: { start: from, end: scanPos }, expected };
1185
+ const span = { start: from, end: scanPos };
1186
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
1187
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1122
1188
  ctx._errors?.push(error);
1123
1189
  return { error, end: scanPos };
1124
1190
  }
@@ -1220,7 +1286,8 @@ function expect(combinator, label2) {
1220
1286
  parse(input, pos, ctx) {
1221
1287
  const result = combinator.parse(input, pos, ctx);
1222
1288
  if (result.ok) return result;
1223
- const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
1289
+ const span = { start: pos, end: pos };
1290
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1224
1291
  ctx._errors?.push(error);
1225
1292
  if (ctx._tolerant) captureError(ctx, error);
1226
1293
  return { ok: true, value: error, span: { start: pos, end: pos } };
@@ -1665,6 +1732,18 @@ function rollbackTrivia(ctx, mark) {
1665
1732
  rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1666
1733
  if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1667
1734
  }
1735
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1736
+ const probeCtx = {
1737
+ trackLines: ctx.trackLines,
1738
+ state: ctx.state,
1739
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1740
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1741
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1742
+ };
1743
+ const result = triviaP.parse(input, cur, probeCtx);
1744
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1745
+ return result;
1746
+ }
1668
1747
  function saveLookaheadMark(ctx) {
1669
1748
  return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1670
1749
  }
@@ -1687,13 +1766,29 @@ function scanWithLabels(input, cur, ctx) {
1687
1766
  function advanceTrivia(input, cur, ctx) {
1688
1767
  const triviaP = ctx.trivia;
1689
1768
  if (!triviaP) return cur;
1769
+ if (!ctx.trackLines) {
1770
+ const fast2 = fastTriviaScanner(triviaP);
1771
+ if (fast2) return fast2(input, cur);
1772
+ if (ctx.triviaKindLabels) {
1773
+ const scan = scanWithLabels(input, cur, ctx);
1774
+ return scan.end;
1775
+ }
1776
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1777
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1778
+ }
1779
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1690
1780
  const fast = fastTriviaScanner(triviaP);
1691
- if (fast) return fast(input, cur);
1781
+ if (fast) {
1782
+ const end = fast(input, cur);
1783
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1784
+ return end;
1785
+ }
1692
1786
  if (ctx.triviaKindLabels) {
1693
1787
  const scan = scanWithLabels(input, cur, ctx);
1788
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1694
1789
  return scan.end;
1695
1790
  }
1696
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1791
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1697
1792
  return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1698
1793
  }
1699
1794
  function scanTrivia(input, cur, ctx) {
@@ -1701,18 +1796,46 @@ function scanTrivia(input, cur, ctx) {
1701
1796
  if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1702
1797
  const log = ctx._triviaLog;
1703
1798
  const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1799
+ if (!ctx.trackLines) {
1800
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1801
+ if (fast2 && log === void 0 && !captureTl) {
1802
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1803
+ }
1804
+ if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1805
+ return scanWithLabels(input, cur, ctx);
1806
+ }
1807
+ if (log !== void 0 || captureTl) {
1808
+ const tr3 = triviaP.parse(input, cur, {
1809
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1810
+ state: ctx.state
1811
+ });
1812
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1813
+ const end = tr3.span.end;
1814
+ return {
1815
+ end,
1816
+ commit: () => {
1817
+ pushTriviaLogEntry(ctx, cur, end);
1818
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1819
+ }
1820
+ };
1821
+ }
1822
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1823
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1824
+ }
1825
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1704
1826
  const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1705
1827
  if (fast && log === void 0 && !captureTl) {
1706
- return { end: fast(input, cur), commit: NOOP_COMMIT };
1828
+ const end = fast(input, cur);
1829
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1830
+ return { end, commit: NOOP_COMMIT };
1707
1831
  }
1708
1832
  if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1709
- return scanWithLabels(input, cur, ctx);
1833
+ const scan = scanWithLabels(input, cur, ctx);
1834
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1835
+ return scan;
1710
1836
  }
1711
1837
  if (log !== void 0 || captureTl) {
1712
- const tr2 = triviaP.parse(input, cur, {
1713
- trackLines: log !== void 0 ? false : ctx.trackLines,
1714
- state: ctx.state
1715
- });
1838
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1716
1839
  if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1717
1840
  const end = tr2.span.end;
1718
1841
  return {
@@ -1723,7 +1846,7 @@ function scanTrivia(input, cur, ctx) {
1723
1846
  }
1724
1847
  };
1725
1848
  }
1726
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1849
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1727
1850
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1728
1851
  }
1729
1852
  function consumeTrivia(input, cur, ctx) {
@@ -2654,6 +2777,74 @@ function markUnusedValues(root) {
2654
2777
  visit(resolveRoot(root), true);
2655
2778
  }
2656
2779
 
2780
+ // src/combinators/grammar.ts
2781
+ function annotateResultLines(result, index) {
2782
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
2783
+ const errors = annotated.errors;
2784
+ if (errors) {
2785
+ ;
2786
+ annotated.errors = errors.map((error) => ({
2787
+ ...error,
2788
+ span: annotateSpan(error.span, index)
2789
+ }));
2790
+ }
2791
+ return annotated;
2792
+ }
2793
+ function createParseLineContext(input, pos) {
2794
+ const lineIndex = createLineIndex();
2795
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
2796
+ return { lineIndex, lineScannedTo: pos };
2797
+ }
2798
+ function parser(opts, root) {
2799
+ const clearTrivia = opts.trivia === null;
2800
+ return {
2801
+ _tag: "grammar",
2802
+ _meta: root._meta,
2803
+ _def: {
2804
+ tag: "grammar",
2805
+ parser: root,
2806
+ triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
2807
+ clearTrivia,
2808
+ ...opts.captureTrivia ? { captureTrivia: true } : {},
2809
+ trackLines: opts.trackLines ?? false
2810
+ },
2811
+ parse(input, pos, _ctx) {
2812
+ const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
2813
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
2814
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
2815
+ const ctx = {
2816
+ ..._ctx,
2817
+ trackLines,
2818
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
2819
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
2820
+ // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
2821
+ ...clearTrivia ? {
2822
+ trivia: void 0,
2823
+ triviaKindLabels: void 0
2824
+ } : opts.trivia != null ? {
2825
+ trivia: opts.trivia,
2826
+ ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
2827
+ } : {},
2828
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
2829
+ // Kind-filter for per-node capture. Resolve against this scope's trivia
2830
+ // labels — this parser's own trivia if it declares one, else the INHERITED
2831
+ // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
2832
+ // trivia is inherited rather than re-declared here. No labels → undefined
2833
+ // (capture all).
2834
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
2835
+ };
2836
+ const result = root.parse(input, pos ?? 0, ctx);
2837
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
2838
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
2839
+ }
2840
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
2841
+ }
2842
+ };
2843
+ }
2844
+ function noTrivia(root) {
2845
+ return parser({ trivia: null }, root);
2846
+ }
2847
+
2657
2848
  // src/combinators/parser.ts
2658
2849
  var RULE_ORDER = "__parsemanRuleOrder";
2659
2850
  function tagRule(r, key) {
@@ -2729,6 +2920,21 @@ function rules(a, b) {
2729
2920
  if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
2730
2921
  }
2731
2922
  }
2923
+ if (options?.trackLines === true) {
2924
+ for (const key of Object.keys(definitions)) {
2925
+ const rule = cache[key];
2926
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
2927
+ }
2928
+ for (const key of Object.keys(definitions)) {
2929
+ const rule = cache[key];
2930
+ if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
2931
+ const wrapped = parser({ trackLines: true }, rule);
2932
+ tagRule(wrapped, key);
2933
+ wrapped._meta.grammarTrackLines = true;
2934
+ cache[key] = wrapped;
2935
+ }
2936
+ }
2937
+ }
2732
2938
  for (const key of Object.keys(definitions)) {
2733
2939
  markUnusedValues(cache[key]);
2734
2940
  }
@@ -3107,13 +3313,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3107
3313
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3108
3314
  const nodeType = def.type ?? missingInferredType();
3109
3315
  const cstOutput = cstOutputHost(ctx.build);
3316
+ const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3110
3317
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3111
- 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 };
3112
- 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 };
3318
+ 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 };
3319
+ 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 };
3113
3320
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3114
3321
  pushCstChild(ctx, built, rawEntry);
3115
3322
  }
3116
- return { ok: true, value: built, span: r.span };
3323
+ return { ok: true, value: built, span };
3117
3324
  }
3118
3325
  };
3119
3326
  }
@@ -3184,85 +3391,6 @@ function field(name, combinator) {
3184
3391
  };
3185
3392
  }
3186
3393
 
3187
- // src/compiler/line-index.ts
3188
- function buildLineIndex(input) {
3189
- const lineStarts = [0];
3190
- for (let i = 0; i < input.length; i++) {
3191
- if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
3192
- }
3193
- return { lineStarts };
3194
- }
3195
- function offsetToLineCol(index, offset) {
3196
- const { lineStarts } = index;
3197
- let lo = 0;
3198
- let hi = lineStarts.length - 1;
3199
- while (lo < hi) {
3200
- const mid = lo + hi + 1 >> 1;
3201
- if (lineStarts[mid] <= offset) lo = mid;
3202
- else hi = mid - 1;
3203
- }
3204
- return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
3205
- }
3206
- function annotateSpan(span, index) {
3207
- const s = offsetToLineCol(index, span.start);
3208
- const e = offsetToLineCol(index, span.end);
3209
- return {
3210
- ...span,
3211
- startLine: s.line,
3212
- startColumn: s.col,
3213
- endLine: e.line,
3214
- endColumn: e.col
3215
- };
3216
- }
3217
-
3218
- // src/combinators/grammar.ts
3219
- function parser(opts, root) {
3220
- const clearTrivia = opts.trivia === null;
3221
- return {
3222
- _tag: "grammar",
3223
- _meta: root._meta,
3224
- _def: {
3225
- tag: "grammar",
3226
- parser: root,
3227
- triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
3228
- clearTrivia,
3229
- ...opts.captureTrivia ? { captureTrivia: true } : {},
3230
- trackLines: opts.trackLines ?? false
3231
- },
3232
- parse(input, pos, _ctx) {
3233
- const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
3234
- const ctx = {
3235
- ..._ctx,
3236
- trackLines,
3237
- // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
3238
- ...clearTrivia ? {
3239
- trivia: void 0,
3240
- triviaKindLabels: void 0
3241
- } : opts.trivia != null ? {
3242
- trivia: opts.trivia,
3243
- ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
3244
- } : {},
3245
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
3246
- // Kind-filter for per-node capture. Resolve against this scope's trivia
3247
- // labels — this parser's own trivia if it declares one, else the INHERITED
3248
- // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
3249
- // trivia is inherited rather than re-declared here. No labels → undefined
3250
- // (capture all).
3251
- ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
3252
- };
3253
- const result = root.parse(input, pos ?? 0, ctx);
3254
- if (trackLines) {
3255
- const idx = buildLineIndex(input);
3256
- return { ...result, span: annotateSpan(result.span, idx) };
3257
- }
3258
- return result;
3259
- }
3260
- };
3261
- }
3262
- function noTrivia(root) {
3263
- return parser({ trivia: null }, root);
3264
- }
3265
-
3266
3394
  // src/combinators/token.ts
3267
3395
  function token(root) {
3268
3396
  const meta = {
@@ -3359,7 +3487,7 @@ function leaf(root, fn) {
3359
3487
  }
3360
3488
 
3361
3489
  // src/version.ts
3362
- var PARSEMAN_VERSION = "0.41.0";
3490
+ var PARSEMAN_VERSION = "0.42.1";
3363
3491
 
3364
3492
  // src/cst/host-mode.ts
3365
3493
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -5043,7 +5171,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5043
5171
  let cur = pos;
5044
5172
  const skip3 = resolveScanSkip(explicitSkip, raw, ctx);
5045
5173
  const probeCtx = {
5046
- trackLines: ctx.trackLines,
5174
+ trackLines: false,
5047
5175
  state: ctx.state,
5048
5176
  ...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
5049
5177
  };
@@ -5056,6 +5184,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5056
5184
  while (cur < input.length) {
5057
5185
  const s = sentinel.parse(input, cur, probeCtx);
5058
5186
  if (s.ok) {
5187
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
5059
5188
  emit2(cur);
5060
5189
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
5061
5190
  }
@@ -5071,6 +5200,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5071
5200
  if (!advanced) cur++;
5072
5201
  }
5073
5202
  if (orEOF) {
5203
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
5074
5204
  emit2(cur);
5075
5205
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
5076
5206
  }
@@ -6509,8 +6639,8 @@ function analyzeMkInlineBuild(def) {
6509
6639
  if (looseMkBuildRe(def.type).test(src)) return def.type;
6510
6640
  return null;
6511
6641
  }
6512
- function emitInlineMkNodeExpr(type, chV, rawV, pos, endVar, tlV) {
6513
- return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: { start: ${pos}, end: ${endVar} }, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`;
6642
+ function emitInlineMkNodeExpr(type, chV, rawV, spanExpr, tlV) {
6643
+ return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: ${spanExpr}, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`;
6514
6644
  }
6515
6645
 
6516
6646
  // src/compiler/inline-callback.ts
@@ -6692,6 +6822,8 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
6692
6822
  }
6693
6823
  }
6694
6824
  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 }";
6825
+ 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 }";
6826
+ 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] } }";
6695
6827
  var _loweringSink = null;
6696
6828
  function beginLoweringCapture() {
6697
6829
  _loweringSink = /* @__PURE__ */ new Set();
@@ -6723,13 +6855,16 @@ function reindentStmts(stmts, targetLevels) {
6723
6855
  const targetPrefix = " ".repeat(targetLevels);
6724
6856
  return stmts.map((s) => s.trim().length === 0 ? "" : targetPrefix + s.slice(minLeading));
6725
6857
  }
6726
- function failReturn(expected, posExpr) {
6858
+ function failReturn(ctx, expected, posExpr) {
6859
+ if (ctx.lineTracking) return `return { ok: false, expected: [${expected}], span: ${emitSpanExpr(ctx, posExpr, posExpr)} }`;
6727
6860
  return `return { ok: false, expected: [${expected}], span: { start: ${posExpr}, end: ${posExpr} } }`;
6728
6861
  }
6729
- function failReturnArr(expectedArr, posExpr) {
6862
+ function failReturnArr(ctx, expectedArr, posExpr) {
6863
+ if (ctx.lineTracking) return `return { ok: false, expected: ${expectedArr}, span: ${emitSpanExpr(ctx, posExpr, posExpr)} }`;
6730
6864
  return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
6731
6865
  }
6732
- function committedReturnArr(expectedArr, posExpr) {
6866
+ function committedReturnArr(ctx, expectedArr, posExpr) {
6867
+ if (ctx.lineTracking) return `return { ok: false, expected: [...${expectedArr}], span: ${emitSpanExpr(ctx, posExpr, posExpr)}, committed: true }`;
6733
6868
  return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} }, committed: true }`;
6734
6869
  }
6735
6870
  function hoistExpected(ctx, constArrSource) {
@@ -6754,7 +6889,7 @@ function failBody(ctx, expected, posExpr) {
6754
6889
  if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
6755
6890
  return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; ${trace}break ${ctx.failLabel} }`;
6756
6891
  }
6757
- return probe + trace + failReturn(expected, posExpr);
6892
+ return probe + trace + failReturn(ctx, expected, posExpr);
6758
6893
  }
6759
6894
  function failArrBody(ctx, expectedArr, posExpr, constant = true) {
6760
6895
  const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
@@ -6764,18 +6899,19 @@ function failArrBody(ctx, expectedArr, posExpr, constant = true) {
6764
6899
  const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
6765
6900
  return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; ${trace}break ${ctx.failLabel} }`;
6766
6901
  }
6767
- if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
6768
- return probe + trace + failReturnArr(expectedArr, posExpr);
6902
+ if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: ${ctx.lineTracking ? emitSpanExpr(ctx, posExpr, posExpr) : `{ start: ${posExpr}, end: ${posExpr} }`} }`;
6903
+ return probe + trace + failReturnArr(ctx, expectedArr, posExpr);
6769
6904
  }
6770
- function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
6771
- return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} }, ...(_ctx._fc ? { committed: true } : {}) }`;
6905
+ function resultFromRecorded(ctx, feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
6906
+ const spanExpr = ctx.lineTracking ? emitSpanExpr(ctx, feExpr, feExpr) : `{ start: ${feExpr}, end: ${feExpr} }`;
6907
+ return `return { ok: false, expected: [...${fxExpr}], span: ${spanExpr}, ...(_ctx._fc ? { committed: true } : {}) }`;
6772
6908
  }
6773
6909
  function committedFailBody(ctx, expectedArr = "_ctx._fx", posExpr = "_ctx._fe") {
6774
6910
  if (ctx.failLabel) {
6775
6911
  if (!ctx.recordFail) return `{ _ctx._fc = true; break ${ctx.failLabel} }`;
6776
6912
  return `{ _ctx._fc = true; _ctx._fe = ${posExpr}; _ctx._fx = ${expectedArr}; break ${ctx.failLabel} }`;
6777
6913
  }
6778
- return committedReturnArr(expectedArr, posExpr);
6914
+ return committedReturnArr(ctx, expectedArr, posExpr);
6779
6915
  }
6780
6916
  function propagateFailBody(ctx, srcCtx = "_ctx") {
6781
6917
  if (srcCtx !== "_ctx") {
@@ -6783,10 +6919,10 @@ function propagateFailBody(ctx, srcCtx = "_ctx") {
6783
6919
  if (!ctx.recordFail) return `break ${ctx.failLabel}`;
6784
6920
  return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; break ${ctx.failLabel} }`;
6785
6921
  }
6786
- return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; ${resultFromRecorded()} }`;
6922
+ return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; ${resultFromRecorded(ctx)} }`;
6787
6923
  }
6788
6924
  if (ctx.failLabel) return `break ${ctx.failLabel}`;
6789
- return resultFromRecorded();
6925
+ return resultFromRecorded(ctx);
6790
6926
  }
6791
6927
  function emitIfFail(ctx, cond, body) {
6792
6928
  return [
@@ -6840,14 +6976,46 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
6840
6976
  if (ctx.capAsTrivia) return [];
6841
6977
  const i = ind(ctx);
6842
6978
  const lf = v(ctx, "_lf");
6979
+ const spanExpr = emitSpanExpr(ctx, startExpr, endExpr);
6843
6980
  return [
6844
6981
  `${i}if (_ctx._cstLeaves || _ctx._cstRawChildren) {`,
6845
- `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } }`,
6982
+ `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: ${spanExpr} }`,
6846
6983
  `${i} if (_ctx._cstLeaves) _ctx._cstLeaves.push(${lf})`,
6847
6984
  `${i} if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(${lf})`,
6848
6985
  `${i}}`
6849
6986
  ];
6850
6987
  }
6988
+ function emitSpanExpr(ctx, startExpr, endExpr) {
6989
+ if (!ctx.lineTracking) return `{ start: ${startExpr}, end: ${endExpr} }`;
6990
+ ctx.needsLineSpan = true;
6991
+ return `_spanLines(_ctx, ${startExpr}, ${endExpr})`;
6992
+ }
6993
+ function emitLineTrack(ctx, startExpr, endExpr) {
6994
+ if (!ctx.lineTracking) return [];
6995
+ ctx.needsLineTrack = true;
6996
+ return [`${ind(ctx)}_trackLines(_ctx, input, ${startExpr}, ${endExpr})`];
6997
+ }
6998
+ function emitLiteralLineTrack(ctx, startExpr, value) {
6999
+ if (!ctx.lineTracking) return [];
7000
+ const lineDeltas = [];
7001
+ for (let i = 0; i < value.length; i++) {
7002
+ if (value.charCodeAt(i) === 10) lineDeltas.push(i + 1);
7003
+ }
7004
+ if (lineDeltas.length === 0) return [];
7005
+ const fromV = v(ctx, "_ltFrom");
7006
+ const endExpr = value.length === 0 ? startExpr : `${startExpr} + ${value.length}`;
7007
+ return [
7008
+ `${ind(ctx)}const ${fromV} = _ctx._lineScannedTo ?? 0`,
7009
+ `${ind(ctx)}if (${endExpr} > ${fromV}) {`,
7010
+ `${ind(ctx)} for (let i = ${fromV}; i < ${startExpr}; i++) if (input.charCodeAt(i) === 10) _ctx._lineStarts.push(i + 1)`,
7011
+ ...lineDeltas.map((delta) => {
7012
+ const lineStart = `${startExpr} + ${delta}`;
7013
+ return `${ind(ctx)} if (${lineStart} > ${fromV}) _ctx._lineStarts.push(${lineStart})`;
7014
+ }),
7015
+ `${ind(ctx)} _ctx._lineScannedTo = ${endExpr}`,
7016
+ `${ind(ctx)}}`
7017
+ ];
7018
+ }
6851
7019
  function ensureRegexDecl(ctx, source, flags) {
6852
7020
  const f = "y" + flags.replace(/[gy]/g, "");
6853
7021
  const key = `${source}/${f}`;
@@ -7232,6 +7400,7 @@ function emitLit(def, ctx, pos) {
7232
7400
  );
7233
7401
  }
7234
7402
  const endVar = len === 0 ? pos : `${pos} + ${len}`;
7403
+ stmts.push(...emitLiteralLineTrack(ctx, pos, value));
7235
7404
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7236
7405
  return { stmts, valueVar: vv, endVar };
7237
7406
  }
@@ -7311,7 +7480,7 @@ function emitKeywords(def, ctx, pos) {
7311
7480
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7312
7481
  return { stmts, valueVar: vv, endVar };
7313
7482
  }
7314
- function emitRegex(def, ctx, pos) {
7483
+ function emitRegex(def, ctx, pos, canMatchNewline = true) {
7315
7484
  const expectedStr = JSON.stringify(`/${def.source}/`);
7316
7485
  const shape = scanShapeFromRegex(def.source, def.flags);
7317
7486
  if (shape) {
@@ -7324,7 +7493,11 @@ function emitRegex(def, ctx, pos) {
7324
7493
  fresh: (prefix) => v(ctx, prefix)
7325
7494
  });
7326
7495
  if (scanned) {
7327
- const stmts2 = [...scanned.stmts, ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)];
7496
+ const stmts2 = [
7497
+ ...scanned.stmts,
7498
+ ...canMatchNewline ? emitLineTrack(ctx, pos, scanned.endVar) : [],
7499
+ ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)
7500
+ ];
7328
7501
  return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
7329
7502
  }
7330
7503
  }
@@ -7346,6 +7519,7 @@ function emitRegex(def, ctx, pos) {
7346
7519
  `${ind(ctx)}const ${vv} = ${mv}[0]`
7347
7520
  ];
7348
7521
  const endVar = `${pos} + ${vv}.length`;
7522
+ if (canMatchNewline) stmts.push(...emitLineTrack(ctx, pos, endVar));
7349
7523
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7350
7524
  return { stmts, valueVar: vv, endVar };
7351
7525
  }
@@ -7383,7 +7557,8 @@ function emitSeqValues(def, ctx, pos) {
7383
7557
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
7384
7558
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
7385
7559
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7386
- `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`
7560
+ `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`,
7561
+ ...emitLineTrack(ctx, curV, scanEndV)
7387
7562
  );
7388
7563
  const r2 = emit(def.parsers[i], ctx, scanEndV);
7389
7564
  stmts.push(...r2.stmts);
@@ -7401,7 +7576,8 @@ function emitSeqValues(def, ctx, pos) {
7401
7576
  const capArg = ctx.noHoist ? "0" : "_ctx._triviaLog !== undefined ? 2 : 0";
7402
7577
  stmts.push(
7403
7578
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7404
- `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`
7579
+ `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`,
7580
+ ...emitLineTrack(ctx, curV, scanEndV)
7405
7581
  );
7406
7582
  const r2 = emit(def.parsers[i], ctx, scanEndV);
7407
7583
  stmts.push(...r2.stmts);
@@ -8101,14 +8277,15 @@ function emitMany(def, ctx, pos) {
8101
8277
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8102
8278
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8103
8279
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8104
- `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`
8280
+ `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8281
+ ...emitLineTrack(ctx, curV, npV)
8105
8282
  );
8106
8283
  itemPos = npV;
8107
8284
  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}; `;
8108
8285
  } else {
8109
8286
  const trivFn = ensureTriviaFn(ctx);
8110
8287
  const npV = v(ctx, "_np");
8111
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`);
8288
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, npV));
8112
8289
  itemPos = npV;
8113
8290
  }
8114
8291
  }
@@ -8178,7 +8355,7 @@ function emitAttempt(p, def, ctx, pos) {
8178
8355
  if (!ctx.recovery && !ctx.coverage && needsFirstSetGuard(def.parser)) {
8179
8356
  const gcV = v(ctx, "_agc");
8180
8357
  const gExp = armStaticExpected(ctx, def.parser);
8181
- const gFail = ctx.failLabel ? `{ _ctx._fe = ${pos};${ctx.recordFail ? ` _ctx._fx = ${gExp};` : ""} break ${ctx.failLabel} }` : `{ _ctx._fe = ${pos}; ${failReturnArr(gExp, pos)} }`;
8358
+ const gFail = ctx.failLabel ? `{ _ctx._fe = ${pos};${ctx.recordFail ? ` _ctx._fx = ${gExp};` : ""} break ${ctx.failLabel} }` : `{ _ctx._fe = ${pos}; ${failReturnArr(ctx, gExp, pos)} }`;
8182
8359
  preGuard.push(
8183
8360
  `${ind(ctx)}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
8184
8361
  `${ind(ctx)}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
@@ -8285,7 +8462,8 @@ function emitSepBy(_p, def, ctx, pos) {
8285
8462
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8286
8463
  `${ind(ctx)}const ${markLv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8287
8464
  `${ind(ctx)}const ${markFld} = _ctx._fields ? _ctx._fields.length : 0`,
8288
- `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`
8465
+ `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8466
+ ...emitLineTrack(ctx, curV, spV)
8289
8467
  );
8290
8468
  sepAtPos = spV;
8291
8469
  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}; `;
@@ -8295,7 +8473,7 @@ function emitSepBy(_p, def, ctx, pos) {
8295
8473
  const post = postSepMarks();
8296
8474
  stmts.push(...post.decl);
8297
8475
  const npV = v(ctx, "_np");
8298
- stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
8476
+ stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`, ...emitLineTrack(ctx, sepEnd, npV));
8299
8477
  const next = emitFallible(def.parser, ctx, npV, true);
8300
8478
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8301
8479
  stmts.push(
@@ -8307,13 +8485,13 @@ function emitSepBy(_p, def, ctx, pos) {
8307
8485
  } else {
8308
8486
  const trivFn = ensureTriviaFn(ctx);
8309
8487
  const spV = v(ctx, "_sp");
8310
- stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
8488
+ stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, spV));
8311
8489
  sepAtPos = spV;
8312
8490
  const sep2 = emitFallible(def.separator, ctx, sepAtPos, true);
8313
8491
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep2;
8314
8492
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
8315
8493
  const npV = v(ctx, "_np");
8316
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
8494
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`, ...emitLineTrack(ctx, sepEnd, npV));
8317
8495
  const next = emitFallible(def.parser, ctx, npV, true);
8318
8496
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8319
8497
  stmts.push(
@@ -8400,6 +8578,7 @@ function emitScanTo(def, ctx, pos) {
8400
8578
  }
8401
8579
  const valV = v(ctx);
8402
8580
  stmts.push(`${ind(ctx)}const ${valV} = input.slice(${pos}, ${curV})`);
8581
+ stmts.push(...emitLineTrack(ctx, pos, curV));
8403
8582
  if (ctx.capturing) {
8404
8583
  const cap = reindentStmts(emitLeafCapture(ctx, valV, pos, curV), ctx.indent + 1);
8405
8584
  stmts.push(`${ind(ctx)}if (${curV} > ${pos}) {`, ...cap, `${ind(ctx)}}`);
@@ -8543,7 +8722,7 @@ function tokenNullableRegex(def) {
8543
8722
  }
8544
8723
  function emitToken(def, ctx, pos) {
8545
8724
  const lowered = tokenNullableRegex(def);
8546
- if (lowered) return emitRegex(lowered, ctx, pos);
8725
+ if (lowered) return emitRegex(lowered, ctx, pos, def.parser._meta.canMatchNewline);
8547
8726
  const savedTrivia = ctx.activeTrivia;
8548
8727
  const savedKindLabels = ctx.triviaKindLabels;
8549
8728
  ctx.activeTrivia = void 0;
@@ -8675,7 +8854,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8675
8854
  if (!ctx.recovery && !ctx.coverage && (capturesChildren || structural) && needsFirstSetGuard(def.parser)) {
8676
8855
  const gcV = v(ctx, "_ngc");
8677
8856
  const gExp = armStaticExpected(ctx, def.parser);
8678
- const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(gExp, pos);
8857
+ const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(ctx, gExp, pos);
8679
8858
  preGuard.push(
8680
8859
  `${i}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
8681
8860
  `${i}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
@@ -8693,7 +8872,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8693
8872
  stmts.push(...innerStmts);
8694
8873
  if (def.trailingTrivia === true && ctx.activeTrivia) {
8695
8874
  const triviaFn = ensureTriviaFn(ctx);
8696
- stmts.push(`${i}const ${endVar} = ${okVar} ? ${triviaFn}(input, ${innerEndVar}, _ctx, 1) : ${innerEndVar}`);
8875
+ stmts.push(`${i}const ${endVar} = ${okVar} ? ${triviaFn}(input, ${innerEndVar}, _ctx, 1) : ${innerEndVar}`, ...emitLineTrack(ctx, innerEndVar, endVar));
8697
8876
  }
8698
8877
  if (sf && fArr) {
8699
8878
  stmts.push(`${i}const ${fArr} = _ctx._fields`);
@@ -8733,8 +8912,11 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8733
8912
  );
8734
8913
  }
8735
8914
  const ndV = v(ctx, "_nd");
8736
- 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})`;
8737
- const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, { start: ${pos}, end: ${endVar} }, ${rawV}, ${tlV}, ${stV})`;
8915
+ const nodeSpanExpr = emitSpanExpr(ctx, pos, endVar);
8916
+ const nodeSpanV = ctx.lineTracking ? v(ctx, "_nspan") : nodeSpanExpr;
8917
+ if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
8918
+ 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})`;
8919
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
8738
8920
  const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
8739
8921
  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;
8740
8922
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
@@ -8744,7 +8926,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8744
8926
  `${i}const ${ndV} = (${profileRecognizer} || ${profileCapture}) ? undefined : (${finalExpr})`,
8745
8927
  `${i}if (!(${profileRecognizer})) {`,
8746
8928
  `${i} if (${sc}) ${sc}.push(${ndV})`,
8747
- `${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} } })`,
8929
+ `${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} })`,
8748
8930
  `${i}}`
8749
8931
  );
8750
8932
  return { stmts, valueVar: ndV, endVar };
@@ -8760,7 +8942,8 @@ function emitRuntimeFallback(parser2, ctx, pos) {
8760
8942
  `${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
8761
8943
  ...emitIfFail(ctx, `!${rv}.ok`, failStmt),
8762
8944
  `${ind(ctx)}const ${vv} = ${rv}.value`,
8763
- `${ind(ctx)}const ${ev} = ${rv}.span.end`
8945
+ `${ind(ctx)}const ${ev} = ${rv}.span.end`,
8946
+ ...parser2._meta.canMatchNewline ? emitLineTrack(ctx, pos, ev) : []
8764
8947
  ];
8765
8948
  return { stmts, valueVar: vv, endVar: ev };
8766
8949
  }
@@ -8826,7 +9009,8 @@ function emitRecover(def, ctx, pos) {
8826
9009
  `${ind0} if (${sentOk}) break`,
8827
9010
  `${ind0} ${scanV}++`,
8828
9011
  `${ind0} }`,
8829
- `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(deriveExpected(def.parser))} }`,
9012
+ ...emitLineTrack(ctx, pos, scanV).map((s) => `${ind0} ${s.trim()}`),
9013
+ `${ind0} const ${errV} = { _tag: 'parseError', span: ${emitSpanExpr(ctx, pos, scanV)}, expected: ${JSON.stringify(deriveExpected(def.parser))} }`,
8830
9014
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
8831
9015
  `${ind0} ${valVar} = ${errV}`,
8832
9016
  `${ind0} ${endVar} = ${scanV}`,
@@ -8842,7 +9026,7 @@ function emitExpect(def, ctx, pos) {
8842
9026
  const stmts = [
8843
9027
  ...innerStmts,
8844
9028
  `${ind0}if (!${okVar}) {`,
8845
- `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${pos} }, expected: ${JSON.stringify(def.expected)} }`,
9029
+ `${ind0} const ${errV} = { _tag: 'parseError', span: ${emitSpanExpr(ctx, pos, pos)}, expected: ${JSON.stringify(def.expected)} }`,
8846
9030
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
8847
9031
  // Embed the missing-token error as a `parseError` CST child, mirroring the
8848
9032
  // interpreter (expect.ts) and the list-recovery emit sites — guarded on
@@ -8932,7 +9116,7 @@ function emitDispatch(p, ctx, pos) {
8932
9116
  case "literal":
8933
9117
  return emitLit(def, ctx, pos);
8934
9118
  case "regex":
8935
- return emitRegex(def, ctx, pos);
9119
+ return emitRegex(def, ctx, pos, p._meta.canMatchNewline);
8936
9120
  case "keywords":
8937
9121
  return emitKeywords(def, ctx, pos);
8938
9122
  case "sequence":
@@ -9182,6 +9366,21 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
9182
9366
  return false;
9183
9367
  }
9184
9368
  }
9369
+ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9370
+ if (seen.has(p)) return false;
9371
+ seen.add(p);
9372
+ const d = p._def;
9373
+ if (p._meta.grammarTrackLines) return true;
9374
+ if (d.tag === "grammar" && d.trackLines) return true;
9375
+ if (d.tag === "lazy") {
9376
+ try {
9377
+ return hasLineTrackingDef(d.thunk(), seen);
9378
+ } catch {
9379
+ return false;
9380
+ }
9381
+ }
9382
+ return childrenOf2(d).some((child) => hasLineTrackingDef(child, seen));
9383
+ }
9185
9384
  function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9186
9385
  if (seen.has(p)) return false;
9187
9386
  seen.add(p);
@@ -9529,6 +9728,7 @@ function compile(combinator, mapFnSources, opts) {
9529
9728
  const grammarTrivia = combinator._meta.grammarTrivia;
9530
9729
  const grammarScanSkip = combinator._meta.grammarScanSkip;
9531
9730
  const grammarHostMode = combinator._meta.grammarHostMode;
9731
+ const grammarTrackLines = combinator._meta.grammarTrackLines;
9532
9732
  const ctx = {
9533
9733
  vars: 0,
9534
9734
  indent: 1,
@@ -9549,6 +9749,7 @@ function compile(combinator, mapFnSources, opts) {
9549
9749
  capturing: hasNodeDef(combinator),
9550
9750
  recovery: opts?.recovery ?? false,
9551
9751
  hostMode: opts?.hostMode ?? grammarHostMode ?? "ast",
9752
+ lineTracking: opts?.trackLines === true || grammarTrackLines === true || hasLineTrackingDef(combinator),
9552
9753
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
9553
9754
  lazyUsage: analyzeLazyUsage(combinator),
9554
9755
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
@@ -9572,9 +9773,13 @@ function compile(combinator, mapFnSources, opts) {
9572
9773
  const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
9573
9774
  const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
9574
9775
  const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
9776
+ const lineTrackDecls = ctx.needsLineTrack ? [LINE_TRACK_DECL] : [];
9777
+ const lineSpanDecls = ctx.needsLineSpan ? [LINE_SPAN_DECL] : [];
9575
9778
  const source = [
9576
9779
  ...emptyTlDecls,
9577
9780
  ...hostReadsDecls,
9781
+ ...lineTrackDecls,
9782
+ ...lineSpanDecls,
9578
9783
  ...ctx.regexDecls,
9579
9784
  ...ctx.expectedDecls,
9580
9785
  "",
@@ -9587,6 +9792,8 @@ function compile(combinator, mapFnSources, opts) {
9587
9792
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
9588
9793
  ...emptyTlDecls,
9589
9794
  ...hostReadsDecls,
9795
+ ...lineTrackDecls,
9796
+ ...lineSpanDecls,
9590
9797
  ...ctx.regexDecls,
9591
9798
  ...ctx.expectedDecls,
9592
9799
  ...namedPrelude,
@@ -9605,6 +9812,25 @@ function compile(combinator, mapFnSources, opts) {
9605
9812
  trackLines: false,
9606
9813
  ...ctx.triviaKindLabels ? { triviaKindLabels: ctx.triviaKindLabels } : {}
9607
9814
  };
9815
+ const annotateWithTrackedLines = (result, lineStarts) => {
9816
+ const index = normalizeLineIndex({ lineStarts });
9817
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
9818
+ const errors = annotated.errors;
9819
+ if (errors) {
9820
+ ;
9821
+ annotated.errors = errors.map((error) => ({
9822
+ ...error,
9823
+ span: annotateSpan(error.span, index)
9824
+ }));
9825
+ }
9826
+ return annotated;
9827
+ };
9828
+ const trackedCtx = (base, input, pos) => {
9829
+ if (!ctx.lineTracking) return base;
9830
+ const lineIndex = { lineStarts: [0] };
9831
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
9832
+ return { ...base, trackLines: true, _lineStarts: lineIndex.lineStarts, _lineScannedTo: pos };
9833
+ };
9608
9834
  const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
9609
9835
  const effectiveSources = mapFnSources ?? derivedSrcs;
9610
9836
  const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
@@ -9612,17 +9838,46 @@ function compile(combinator, mapFnSources, opts) {
9612
9838
  const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
9613
9839
  const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
9614
9840
  const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources, coverageRootRuleId) : null;
9615
- return {
9841
+ const compiledMeta = {
9616
9842
  source,
9617
9843
  inlineExpression,
9618
9844
  ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions },
9619
- ...gatingReport === void 0 ? {} : { gating: gatingReport },
9845
+ ...gatingReport === void 0 ? {} : { gating: gatingReport }
9846
+ };
9847
+ if (!ctx.lineTracking) {
9848
+ return {
9849
+ ...compiledMeta,
9850
+ parse(input, pos = 0) {
9851
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
9852
+ },
9853
+ parseWithContext(input, parseCtx, pos = 0) {
9854
+ assertHostModeCompatible(ctx.hostMode ?? "ast", parseCtx.build, !!ctx.hostBranchElided);
9855
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9856
+ },
9857
+ // Note: collects expect() errors via _errors. Unlike interpreter
9858
+ // parse({recover:true}) it does NOT populate furthestFail — the compiled path
9859
+ // inlines failures for throughput and deliberately skips _probe bookkeeping.
9860
+ // Callers wanting a furthest-position diagnostic detect unconsumed input
9861
+ // (span.end < input.length) instead, which is mode-agnostic.
9862
+ parseWithErrors(input, pos = 0) {
9863
+ const errors = [];
9864
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
9865
+ return { ...result, errors };
9866
+ }
9867
+ };
9868
+ }
9869
+ return {
9870
+ ...compiledMeta,
9620
9871
  parse(input, pos = 0) {
9621
- return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
9872
+ const parseCtx = trackedCtx(defaultCtx, input, pos);
9873
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9874
+ return annotateWithTrackedLines(result, parseCtx._lineStarts);
9622
9875
  },
9623
9876
  parseWithContext(input, parseCtx, pos = 0) {
9624
9877
  assertHostModeCompatible(ctx.hostMode ?? "ast", parseCtx.build, !!ctx.hostBranchElided);
9625
- return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9878
+ const activeCtx = trackedCtx(parseCtx, input, pos);
9879
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, activeCtx);
9880
+ return activeCtx.trackLines ? annotateWithTrackedLines(result, activeCtx._lineStarts) : result;
9626
9881
  },
9627
9882
  // Note: collects expect() errors via _errors. Unlike interpreter
9628
9883
  // parse({recover:true}) it does NOT populate furthestFail — the compiled path
@@ -9631,8 +9886,9 @@ function compile(combinator, mapFnSources, opts) {
9631
9886
  // (span.end < input.length) instead, which is mode-agnostic.
9632
9887
  parseWithErrors(input, pos = 0) {
9633
9888
  const errors = [];
9634
- const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
9635
- return { ...result, errors };
9889
+ const parseCtx = trackedCtx({ ...defaultCtx, _errors: errors }, input, pos);
9890
+ const result = { ...fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx), errors };
9891
+ return annotateWithTrackedLines(result, parseCtx._lineStarts);
9636
9892
  }
9637
9893
  };
9638
9894
  }
@@ -9657,6 +9913,7 @@ function compileRuleMap(ruleMap, opts) {
9657
9913
  const grammarTrivia = opts?.trivia;
9658
9914
  const grammarScanSkip = opts?.scanSkip ?? ruleMap.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
9659
9915
  const grammarHostMode = opts?.hostMode ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
9916
+ const grammarTrackLines = opts?.trackLines === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
9660
9917
  const ctx = {
9661
9918
  vars: 0,
9662
9919
  indent: 1,
@@ -9677,6 +9934,7 @@ function compileRuleMap(ruleMap, opts) {
9677
9934
  capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
9678
9935
  recovery: opts?.recovery ?? false,
9679
9936
  hostMode: grammarHostMode ?? "ast",
9937
+ lineTracking: grammarTrackLines,
9680
9938
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(ruleMap.map(([, rule]) => rule), coverageWinners) } } : {},
9681
9939
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
9682
9940
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
@@ -9710,12 +9968,18 @@ function compileRuleMap(ruleMap, opts) {
9710
9968
  const mfCovered = ctx.mapFns.length === 0 || derivedSrcs !== void 0;
9711
9969
  const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
9712
9970
  if (!canInline) return null;
9971
+ if (ctx.lineTracking) {
9972
+ ctx.needsLineTrack = true;
9973
+ ctx.needsLineSpan = true;
9974
+ }
9713
9975
  const mfDecl = derivedSrcs?.length ? ` const ${mfRef(ctx)} = [${derivedSrcs.join(", ")}]` : "";
9714
9976
  const buildDecl = buildSources?.length ? ` const ${buildRef(ctx)} = [${buildSources.join(", ")}]` : "";
9715
9977
  const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
9716
9978
  const hoistedDecls = [
9717
9979
  ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
9718
9980
  ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "",
9981
+ ctx.needsLineTrack ? ` ${LINE_TRACK_DECL}` : "",
9982
+ ctx.needsLineSpan ? ` ${LINE_SPAN_DECL}` : "",
9719
9983
  ...ctx.regexDecls.map((d) => ` ${d}`),
9720
9984
  ...ctx.expectedDecls.map((d) => ` ${d}`),
9721
9985
  mfDecl,
@@ -9737,15 +10001,23 @@ function compileRuleMap(ruleMap, opts) {
9737
10001
  };
9738
10002
  const entryFnText = (r, rule) => {
9739
10003
  const ruleId = entryRuleId(rule);
10004
+ const linePreamble = ctx.lineTracking ? [
10005
+ ` if (_ctx._lineStarts === undefined) _ctx = { ..._ctx, trackLines: true, _lineStarts: [0], _lineScannedTo: 0 }`,
10006
+ ` else _ctx.trackLines = true`,
10007
+ ` if (_pos > (_ctx._lineScannedTo ?? 0)) _trackLines(_ctx, input, 0, _pos)`
10008
+ ] : [];
10009
+ const resultSpan = ctx.lineTracking ? `_spanLines(_ctx, _pos, ${r.endVar})` : `{ start: _pos, end: ${r.endVar} }`;
9740
10010
  const body = ruleId === void 0 ? [
10011
+ ...linePreamble,
9741
10012
  ` let pos = _pos`,
9742
10013
  ...r.stmts,
9743
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
10014
+ ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`
9744
10015
  ] : [
10016
+ ...linePreamble,
9745
10017
  ` const _coverageResult = (() => {`,
9746
10018
  ` let pos = _pos`,
9747
10019
  ...r.stmts,
9748
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
10020
+ ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`,
9749
10021
  ` })()`,
9750
10022
  ` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
9751
10023
  ` return _coverageResult`
@@ -9783,6 +10055,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
9783
10055
  const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
9784
10056
  const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
9785
10057
  const grammarHostMode = opts?.hostMode ?? ruleMapArg.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
10058
+ const grammarTrackLines = opts?.trackLines === true || ruleMapArg.some(([, r]) => r._meta.grammarTrackLines === true);
9786
10059
  const ruleMap = ruleMapArg.filter(([, val]) => {
9787
10060
  const d = val._def;
9788
10061
  if (d.tag !== "lazy") return true;
@@ -9817,6 +10090,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
9817
10090
  capturing: opts?.captureTerminals === true || ruleMap.some(([, rule]) => hasNodeDef(rule)),
9818
10091
  recovery: opts?.recovery ?? false,
9819
10092
  hostMode: grammarHostMode ?? "ast",
10093
+ lineTracking: grammarTrackLines,
9820
10094
  ...opts?.coverage ? { coverage: { plan: opts.coverage } } : {},
9821
10095
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
9822
10096
  ns,
@@ -9913,6 +10187,10 @@ function compileLinkable(ruleMapArg, ns, opts) {
9913
10187
  perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
9914
10188
  }
9915
10189
  if (ctx.runtimeParsers.length !== 0) return null;
10190
+ if (ctx.lineTracking) {
10191
+ ctx.needsLineTrack = true;
10192
+ ctx.needsLineSpan = true;
10193
+ }
9916
10194
  const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
9917
10195
  const buildSrcs = ctx.buildFns.length > 0 && ctx.buildSrcs.every((s) => s !== null) ? ctx.buildSrcs : null;
9918
10196
  const ruleFns = /* @__PURE__ */ new Map();
@@ -9926,6 +10204,8 @@ function compileLinkable(ruleMapArg, ns, opts) {
9926
10204
  const mfDecl = ctx.mapFns.length === 0 ? "" : mfSrcs ? `const ${mfRef(ctx)} = [${mfSrcs.join(", ")}]` : `const ${mfRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "mf")}]`;
9927
10205
  const buildDecl = ctx.buildFns.length === 0 ? "" : buildSrcs ? `const ${buildRef(ctx)} = [${buildSrcs.join(", ")}]` : `const ${buildRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "build")}]`;
9928
10206
  const prelude = [
10207
+ ctx.needsLineTrack ? LINE_TRACK_DECL : "",
10208
+ ctx.needsLineSpan ? LINE_SPAN_DECL : "",
9929
10209
  ...ctx.regexDecls,
9930
10210
  ...ctx.expectedDecls,
9931
10211
  mfDecl,
@@ -9934,11 +10214,18 @@ function compileLinkable(ruleMapArg, ns, opts) {
9934
10214
  ].filter(Boolean);
9935
10215
  const wrappers = /* @__PURE__ */ new Map();
9936
10216
  for (const { key, rule, r } of perEntry) {
10217
+ const linePreamble = ctx.lineTracking ? [
10218
+ ` if (_ctx._lineStarts === undefined) _ctx = { ..._ctx, trackLines: true, _lineStarts: [0], _lineScannedTo: 0 }`,
10219
+ ` else _ctx.trackLines = true`,
10220
+ ` if (_pos > (_ctx._lineScannedTo ?? 0)) _trackLines(_ctx, input, 0, _pos)`
10221
+ ] : [];
10222
+ const resultSpan = ctx.lineTracking ? `_spanLines(_ctx, _pos, ${r.endVar})` : `{ start: _pos, end: ${r.endVar} }`;
9937
10223
  wrappers.set(key, publicRuleWrapperSource(rule, [
9938
10224
  `function(input, _pos, _ctx) {`,
10225
+ ...linePreamble,
9939
10226
  ` let pos = _pos`,
9940
10227
  ...r.stmts,
9941
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
10228
+ ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`,
9942
10229
  `}`
9943
10230
  ].join("\n"), ctx.triviaKindLabels));
9944
10231
  }
@@ -10045,12 +10332,16 @@ function buildInlineExpression(ctx, r, mapFnSources, buildSources, coverageRootR
10045
10332
  const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
10046
10333
  const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
10047
10334
  const hostReadsDecl = ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "";
10048
- const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl;
10335
+ const lineTrackDecl = ctx.needsLineTrack ? ` ${LINE_TRACK_DECL}` : "";
10336
+ const lineSpanDecl = ctx.needsLineSpan ? ` ${LINE_SPAN_DECL}` : "";
10337
+ const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl || !!lineTrackDecl || !!lineSpanDecl;
10049
10338
  if (!needsWrapper) return innerFn;
10050
10339
  const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
10051
10340
  const hoistedDecls = [
10052
10341
  emptyTlDecl,
10053
10342
  hostReadsDecl,
10343
+ lineTrackDecl,
10344
+ lineSpanDecl,
10054
10345
  ...ctx.regexDecls.map((d) => ` ${d}`),
10055
10346
  ...ctx.expectedDecls.map((d) => ` ${d}`),
10056
10347
  mfDecl,
@@ -10589,7 +10880,7 @@ function normalizeCstCollapse(collapse) {
10589
10880
  return typeof collapse === "function" ? collapse : void 0;
10590
10881
  }
10591
10882
  function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
10592
- return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children2] };
10883
+ return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children2] };
10593
10884
  }
10594
10885
  function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
10595
10886
  if (typeof typeOrOptions === "string") {
@@ -10768,7 +11059,8 @@ function materializePiece(p, trivia2, captureTerminals = false, hostMode) {
10768
11059
  const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, {
10769
11060
  ...trivia2 ? { trivia: trivia2 } : {},
10770
11061
  ...captureTerminals ? { captureTerminals: true } : {},
10771
- ...hostMode ? { hostMode } : {}
11062
+ ...hostMode ? { hostMode } : {},
11063
+ ...p.trackLines ? { trackLines: true } : {}
10772
11064
  });
10773
11065
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
10774
11066
  return pieces;
@@ -12015,6 +12307,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12015
12307
  const macroImports = [];
12016
12308
  const allNames = /* @__PURE__ */ new Set();
12017
12309
  const importBindings = /* @__PURE__ */ new Map();
12310
+ const ordinaryImports = [];
12018
12311
  for (const stmt of body) {
12019
12312
  if (stmt.type !== "ImportDeclaration") continue;
12020
12313
  const s = stmt;
@@ -12023,12 +12316,17 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12023
12316
  return key === "type" && a.value.value === "macro";
12024
12317
  });
12025
12318
  if (!isMacro) {
12319
+ const specifiers = [];
12026
12320
  for (const spec of s.specifiers) {
12027
12321
  if (spec.type === "ImportSpecifier") {
12028
12322
  const imported = spec.imported.name ?? spec.local.name;
12029
12323
  importBindings.set(spec.local.name, { source: s.source.value, imported });
12324
+ specifiers.push({ type: spec.type, local: spec.local.name, start: spec.start, end: spec.end });
12325
+ } else {
12326
+ specifiers.push({ type: spec.type, local: spec.local.name, start: spec.start, end: spec.end });
12030
12327
  }
12031
12328
  }
12329
+ ordinaryImports.push({ start: s.start, end: s.end, specifiers });
12032
12330
  continue;
12033
12331
  }
12034
12332
  const names = /* @__PURE__ */ new Set();
@@ -12039,6 +12337,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12039
12337
  for (const n of names) allNames.add(n);
12040
12338
  }
12041
12339
  if (macroImports.length === 0) return null;
12340
+ const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
12042
12341
  const factoryDecls = /* @__PURE__ */ new Map();
12043
12342
  for (const stmt of body) {
12044
12343
  const decl = stmt.type === "ExportNamedDeclaration" ? stmt.declaration : stmt;
@@ -12056,6 +12355,101 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12056
12355
  }
12057
12356
  }
12058
12357
  }
12358
+ const topLevelFunction = (moduleBody, name) => {
12359
+ for (const st of moduleBody) {
12360
+ const decl = st.type === "ExportNamedDeclaration" ? st.declaration : st;
12361
+ const idn = decl?.type === "FunctionDeclaration" ? decl.id : void 0;
12362
+ if (idn?.name === name) return decl ?? null;
12363
+ }
12364
+ return null;
12365
+ };
12366
+ const isParsemanMacroImport = (stmt) => {
12367
+ if (stmt.type !== "ImportDeclaration") return false;
12368
+ const s = stmt;
12369
+ return moduleAliases.has(s.source.value) && s.attributes.some((a) => {
12370
+ const key = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
12371
+ return key === "type" && a.value.value === "macro";
12372
+ });
12373
+ };
12374
+ const sourceScopeUntil = (moduleBody, source, until) => {
12375
+ const out = /* @__PURE__ */ new Map();
12376
+ for (const stmt of moduleBody) {
12377
+ if (stmt.type === "ImportDeclaration") {
12378
+ if (!isParsemanMacroImport(stmt)) return null;
12379
+ continue;
12380
+ }
12381
+ if (stmt.type === "FunctionDeclaration") continue;
12382
+ if (stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "FunctionDeclaration") continue;
12383
+ if (stmt.type === "ExportNamedDeclaration" && !stmt.declaration) {
12384
+ if (stmt.source) return null;
12385
+ continue;
12386
+ }
12387
+ const vd = unwrapVd(stmt);
12388
+ if (!vd || vd.kind !== "const") return null;
12389
+ for (const decl of vd.declarations) {
12390
+ const d = decl;
12391
+ const idn = d.id;
12392
+ const init = d.init;
12393
+ if (idn.type !== "Identifier" && idn.type !== "BindingIdentifier" || !idn.name || !init) return null;
12394
+ if (init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression") continue;
12395
+ const mapFnSources = [];
12396
+ const combi = evaluateExpr(init, out, source, mapFnSources);
12397
+ if (combi) {
12398
+ if (stmt.start < until) out.set(idn.name, { combi, mfSrcs: mapFnSources });
12399
+ continue;
12400
+ }
12401
+ const wordFactory = evaluateWordFactory(init, out, source);
12402
+ if (wordFactory) {
12403
+ if (stmt.start < until) {
12404
+ ;
12405
+ out.set(idn.name, wordFactory);
12406
+ }
12407
+ continue;
12408
+ }
12409
+ const whenFactory = evaluateWhenFactory(init, out, source);
12410
+ if (whenFactory) {
12411
+ if (stmt.start < until) {
12412
+ ;
12413
+ out.set(idn.name, whenFactory);
12414
+ }
12415
+ continue;
12416
+ }
12417
+ const combiArray = evaluateCombinatorArray(init, out, source);
12418
+ if (combiArray) {
12419
+ if (stmt.start < until) {
12420
+ ;
12421
+ out.set(idn.name, combiArray);
12422
+ }
12423
+ continue;
12424
+ }
12425
+ return null;
12426
+ }
12427
+ continue;
12428
+ }
12429
+ return out;
12430
+ };
12431
+ const usedImportedFactories = /* @__PURE__ */ new Set();
12432
+ const markUsedImportedFactories = (names) => {
12433
+ for (const name of names ?? []) usedImportedFactories.add(name);
12434
+ };
12435
+ for (const [localName, binding] of importBindings) {
12436
+ const file = resolvePrivateSourceModule(id, binding.source);
12437
+ if (!file) continue;
12438
+ const mod = parseModuleCached(file);
12439
+ if (!mod) continue;
12440
+ const localFor = exportLocalName(mod.body, binding.imported);
12441
+ const init = localFor ? topLevelInit(mod.body, localFor) ?? topLevelFunction(mod.body, localFor) : null;
12442
+ if (!init || init.type !== "ArrowFunctionExpression" && init.type !== "FunctionExpression" && init.type !== "FunctionDeclaration") continue;
12443
+ const factoryScope = sourceScopeUntil(mod.body, mod.src, init.start);
12444
+ if (!factoryScope) continue;
12445
+ factoryDecls.set(localName, {
12446
+ fn: init,
12447
+ declaredAt: -1,
12448
+ code: mod.src,
12449
+ scope: factoryScope,
12450
+ imported: true
12451
+ });
12452
+ }
12059
12453
  const scope = /* @__PURE__ */ new Map();
12060
12454
  const replacements = [];
12061
12455
  const warnings = [];
@@ -12085,6 +12479,9 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12085
12479
  }
12086
12480
  const namedFactory = factoryArgRaw.type === "Identifier" ? factoryDecls.get(factoryArgRaw.name) : void 0;
12087
12481
  const factoryArg = namedFactory !== void 0 && namedFactory.declaredAt <= init.start ? namedFactory.fn : factoryArgRaw;
12482
+ const factoryScope = namedFactory?.scope ?? scope;
12483
+ const factoryCode = namedFactory?.code ?? code;
12484
+ const importedFactory = namedFactory?.imported === true && factoryArg === namedFactory.fn ? factoryArgRaw.name : void 0;
12088
12485
  const optionsArg = optionsFirst ? arg0 : arg1;
12089
12486
  const optionValue = (name) => optionsArg?.type === "ObjectExpression" ? (optionsArg.properties ?? []).find(
12090
12487
  (p) => p.key?.name === name
@@ -12095,7 +12492,13 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12095
12492
  warn(init.start, `${label2}: rules({ hostMode }) must be the literal 'ast' or 'cst'`);
12096
12493
  return null;
12097
12494
  }
12098
- const ruleMap = evaluateParserFactory(factoryArg, scope, code, []);
12495
+ const trackLinesValue = optionValue("trackLines");
12496
+ const gTrackLines = trackLinesValue?.type === "Literal" ? trackLinesValue.value : void 0;
12497
+ if (trackLinesValue !== void 0 && typeof gTrackLines !== "boolean") {
12498
+ warn(init.start, `${label2}: rules({ trackLines }) must be a boolean literal`);
12499
+ return null;
12500
+ }
12501
+ const ruleMap = evaluateParserFactory(factoryArg, factoryScope, factoryCode, []);
12099
12502
  if (!ruleMap) {
12100
12503
  warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
12101
12504
  return null;
@@ -12120,12 +12523,26 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12120
12523
  }
12121
12524
  }
12122
12525
  }
12123
- return { ruleMap, ...gTrivia ? { trivia: gTrivia } : {}, ...gScanSkip ? { scanSkip: gScanSkip } : {} };
12526
+ if (gTrackLines === true) {
12527
+ for (const rule of ruleMap.values()) {
12528
+ if (rule && !rule._meta.isTrivia) {
12529
+ ;
12530
+ rule._meta.grammarTrackLines = true;
12531
+ }
12532
+ }
12533
+ }
12534
+ return {
12535
+ ruleMap,
12536
+ ...gTrivia ? { trivia: gTrivia } : {},
12537
+ ...gScanSkip ? { scanSkip: gScanSkip } : {},
12538
+ ...gTrackLines === true ? { trackLines: true } : {},
12539
+ ...importedFactory ? { importedFactory } : {}
12540
+ };
12124
12541
  };
12125
12542
  const compileRulesFactory = (init, label2) => {
12126
12543
  const evaluated = evaluateRulesFactory(init, label2);
12127
12544
  if (!evaluated) return null;
12128
- const compiled = compileRuleMap([...evaluated.ruleMap], { ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}, recovery, coverage: grammarCoverage });
12545
+ const compiled = compileRuleMap([...evaluated.ruleMap], { ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}, ...evaluated.trackLines ? { trackLines: true } : {}, recovery, coverage: grammarCoverage });
12129
12546
  return {
12130
12547
  replacement: compiled?.replacement ?? null,
12131
12548
  ruleMap: evaluated.ruleMap,
@@ -12133,7 +12550,9 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12133
12550
  hostBranchElided: compiled?.hostBranchElided ?? false,
12134
12551
  ...compiled?.coverageDefinitions ? { coverageDefinitions: compiled.coverageDefinitions } : {},
12135
12552
  ...evaluated.trivia ? { trivia: evaluated.trivia } : {},
12136
- ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}
12553
+ ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {},
12554
+ ...evaluated.trackLines ? { trackLines: true } : {},
12555
+ ...evaluated.importedFactory ? { importedFactory: evaluated.importedFactory } : {}
12137
12556
  };
12138
12557
  };
12139
12558
  const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
@@ -12153,7 +12572,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12153
12572
  };
12154
12573
  const isSpread = (it) => "__spreadLocal" in it;
12155
12574
  const isIR = (it) => "ir" in it && !("ruleFns" in it);
12156
- const serializeItem = (it) => isSpread(it) ? `...(${it.__spreadLocal}[Symbol.for('parseman.composedPieces')] ?? [])` : isIR(it) ? `{ ns: ${JSON.stringify(it.ns)}, ir: ${JSON.stringify(it.ir)} }` : serializePieces(it);
12575
+ const serializeItem = (it) => isSpread(it) ? `...(${it.__spreadLocal}[Symbol.for('parseman.composedPieces')] ?? [])` : isIR(it) ? `{ ns: ${JSON.stringify(it.ns)}, ir: ${JSON.stringify(it.ir)}${it.trackLines ? ", trackLines: true" : ""} }` : serializePieces(it);
12157
12576
  const serializeList = (list) => `[${list.map(serializeItem).join(", ")}]`;
12158
12577
  const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
12159
12578
  const withLeafMarker = (grammarExpr) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.leafComposed'), { value: true, enumerable: false })`;
@@ -12284,10 +12703,11 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12284
12703
  const localCarried = (rm, label2, composing, scanSkip) => {
12285
12704
  const entries = [...rm];
12286
12705
  const ns = nsFor(label2);
12706
+ const trackLines = entries.some(([, rule]) => rule?._meta?.grammarTrackLines === true);
12287
12707
  const ir = serializeRuleMap(entries, scanSkip);
12288
- if (ir) return { carried: [{ ns, ir }] };
12708
+ if (ir) return { carried: [{ ns, ir, ...trackLines ? { trackLines: true } : {} }] };
12289
12709
  warn(0, `${label2}: rule map could not be serialized to IR; carrying FULL pieces instead. The artifact is correct but larger, and a downstream compose cannot re-lower it. Re-run with PARSEMAN_IR_DEBUG=1 to print the exact combinator that blocked serialization.`);
12290
- const p = compileLinkable(entries, ns, { ...composing ? { trivia: composing } : {}, ...scanSkip ? { scanSkip } : {}, recovery });
12710
+ const p = compileLinkable(entries, ns, { ...composing ? { trivia: composing } : {}, ...scanSkip ? { scanSkip } : {}, ...trackLines ? { trackLines: true } : {}, recovery });
12291
12711
  return p ? { carried: [p] } : null;
12292
12712
  };
12293
12713
  const argPieces = (arg, label2, composing) => {
@@ -12305,7 +12725,10 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12305
12725
  const innerArg = argPieces(inner, `${label2}_pick`);
12306
12726
  if (!innerArg) return null;
12307
12727
  try {
12308
- return { carried: pickPieces(materializeCarried(innerArg.carried, ownTrivia(inner)), names) };
12728
+ return {
12729
+ carried: pickPieces(materializeCarried(innerArg.carried, ownTrivia(inner)), names),
12730
+ ...innerArg.importedFactories ? { importedFactories: innerArg.importedFactories } : {}
12731
+ };
12309
12732
  } catch (e) {
12310
12733
  warn(arg.start, `pick(): ${e.message}`);
12311
12734
  return null;
@@ -12313,7 +12736,8 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12313
12736
  }
12314
12737
  if (isRulesCall(arg)) {
12315
12738
  const evaluated = evaluateRulesFactory(arg, label2);
12316
- return evaluated ? localCarried(evaluated.ruleMap, label2, composing, evaluated.scanSkip) : null;
12739
+ const carried = evaluated ? localCarried(evaluated.ruleMap, label2, composing, evaluated.scanSkip) : null;
12740
+ return carried ? { ...carried, ...evaluated?.importedFactory ? { importedFactories: [evaluated.importedFactory] } : {} } : null;
12317
12741
  }
12318
12742
  if (arg.type === "Identifier") {
12319
12743
  const name = arg.name;
@@ -12385,6 +12809,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12385
12809
  }
12386
12810
  const composing = composingTrivia(elements);
12387
12811
  const carried = [];
12812
+ const importedFactories = [];
12388
12813
  for (let i = 0; i < elements.length; i++) {
12389
12814
  const r = argPieces(elements[i], `compose${init.start}_${i}`, composing);
12390
12815
  if (!r) {
@@ -12392,6 +12817,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12392
12817
  return null;
12393
12818
  }
12394
12819
  carried.push(...r.carried);
12820
+ importedFactories.push(...r.importedFactories ?? []);
12395
12821
  }
12396
12822
  const detailed = once(() => carriedRuleMapsDetailed(carried));
12397
12823
  runFusedGatingDiagnostic(
@@ -12401,7 +12827,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12401
12827
  );
12402
12828
  const pieces = materializeCarried(carried, composing, false, cHostMode);
12403
12829
  try {
12404
- return { replacement: emitFusedSource(pieces), carried, ...composing ? { trivia: composing } : {} };
12830
+ return {
12831
+ replacement: emitFusedSource(pieces),
12832
+ carried,
12833
+ ...composing ? { trivia: composing } : {},
12834
+ ...importedFactories.length ? { importedFactories } : {}
12835
+ };
12405
12836
  } catch (e) {
12406
12837
  warn(init.start, `compose(): ${e.message}; falling back to runtime`);
12407
12838
  return null;
@@ -12422,10 +12853,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12422
12853
  const localArg = elements[elements.length - 1];
12423
12854
  let localRules = null;
12424
12855
  let localScanSkip;
12856
+ const importedFactories = [];
12425
12857
  if (isRulesCall(localArg)) {
12426
12858
  const evaluated = evaluateRulesFactory(localArg, `composeLeaf${init.start}`);
12427
12859
  localRules = evaluated?.ruleMap ?? null;
12428
12860
  localScanSkip = evaluated?.scanSkip;
12861
+ if (evaluated?.importedFactory) importedFactories.push(evaluated.importedFactory);
12429
12862
  } else if (localArg.type === "Identifier") {
12430
12863
  const name = localArg.name;
12431
12864
  localRules = localRuleMaps.get(name) ?? null;
@@ -12444,6 +12877,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12444
12877
  return null;
12445
12878
  }
12446
12879
  carried.push(...r.carried);
12880
+ importedFactories.push(...r.importedFactories ?? []);
12447
12881
  }
12448
12882
  try {
12449
12883
  const localNs = nsFor(`composeLeaf${init.start}`);
@@ -12469,13 +12903,15 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12469
12903
  return null;
12470
12904
  }
12471
12905
  const replacement = withLeafMarker(emitFusedSource(grammarCoverage ? recognitionPieces : [...recognitionPieces, plainLocalPiece]));
12472
- return { replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)) };
12906
+ return {
12907
+ replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)),
12908
+ ...importedFactories.length ? { importedFactories } : {}
12909
+ };
12473
12910
  } catch (e) {
12474
12911
  warn(init.start, `composeLeaf(): ${e.message}`);
12475
12912
  return null;
12476
12913
  }
12477
12914
  };
12478
- const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
12479
12915
  const refNames = /* @__PURE__ */ new Set();
12480
12916
  for (const stmt of body) {
12481
12917
  const innerVd = unwrapVd(stmt);
@@ -12559,6 +12995,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12559
12995
  const ns = nsFor(varName);
12560
12996
  const pieces = exportPrefix || compiledRules.replacement === null ? compileLinkable([...compiledRules.ruleMap], ns, {
12561
12997
  ...compiledRules.scanSkip ? { scanSkip: compiledRules.scanSkip } : {},
12998
+ ...compiledRules.trackLines ? { trackLines: true } : {},
12562
12999
  recovery
12563
13000
  }) : null;
12564
13001
  if (compiledRules.replacement === null) {
@@ -12578,9 +13015,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12578
13015
  }
12579
13016
  if (exportPrefix && pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
12580
13017
  const ir = serializeRuleMap([...compiledRules.ruleMap], compiledRules.scanSkip);
12581
- replacement = withCarriedPieces(replacement, [ir ? { ns, ir } : pieces]);
13018
+ replacement = withCarriedPieces(replacement, [ir ? { ns, ir, ...compiledRules.trackLines ? { trackLines: true } : {} } : pieces]);
12582
13019
  }
12583
13020
  replacements.push({ start: init.start, end: init.end, replacement });
13021
+ if (compiledRules.replacement !== null) {
13022
+ markUsedImportedFactories(compiledRules.importedFactory ? [compiledRules.importedFactory] : void 0);
13023
+ }
12584
13024
  continue;
12585
13025
  }
12586
13026
  if (isComposeCall(init)) {
@@ -12593,6 +13033,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12593
13033
  if (fused.trivia) localComposedTrivia.set(varName, fused.trivia);
12594
13034
  const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
12595
13035
  replacements.push({ start: init.start, end: init.end, replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)) });
13036
+ markUsedImportedFactories(fused.importedFactories);
12596
13037
  continue;
12597
13038
  }
12598
13039
  if (isComposeLeafCall(init)) {
@@ -12601,6 +13042,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12601
13042
  throw new Error(`${id}:${lineOf(init.start)} \u2014 composeLeaf() must macro-fuse; runtime composition is forbidden`);
12602
13043
  }
12603
13044
  replacements.push({ start: init.start, end: init.end, replacement: fused.replacement });
13045
+ markUsedImportedFactories(fused.importedFactories);
12604
13046
  continue;
12605
13047
  }
12606
13048
  const mapFnSources = [];
@@ -12689,6 +13131,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12689
13131
  end: stmtEnd,
12690
13132
  replacement: lines.join("\n")
12691
13133
  });
13134
+ markUsedImportedFactories(compiledRules.importedFactory ? [compiledRules.importedFactory] : void 0);
12692
13135
  }
12693
13136
  }
12694
13137
  }
@@ -12697,6 +13140,40 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12697
13140
  imp.fullyResolved = !anyUnresolved && !keepMacroImport;
12698
13141
  }
12699
13142
  const ms = new import_magic_string.default(code);
13143
+ const replacementRanges = runtimeComposeFallback ? [] : replacements.map(({ start, end }) => ({ start, end }));
13144
+ const isInsideReplacement = (start, end) => replacementRanges.some((r) => start >= r.start && end <= r.end);
13145
+ const importedBindingStillReferenced = (local) => {
13146
+ const walk2 = (node2, parent, parentKey) => {
13147
+ if (!node2 || typeof node2 !== "object") return false;
13148
+ const rec = node2;
13149
+ if (rec.type === "ImportDeclaration") return false;
13150
+ if (typeof rec.start === "number" && typeof rec.end === "number" && isInsideReplacement(rec.start, rec.end)) return false;
13151
+ if ((rec.type === "Identifier" || rec.type === "BindingIdentifier") && rec.name === local) {
13152
+ if (parentKey === "key" && (parent?.type === "ObjectProperty" || parent?.type === "Property" || parent?.type === "PropertyDefinition") && parent.computed !== true) return false;
13153
+ if (parentKey === "property" && parent?.type === "StaticMemberExpression") return false;
13154
+ if (parentKey === "property" && parent?.type === "MemberExpression" && parent.computed !== true) return false;
13155
+ return true;
13156
+ }
13157
+ for (const key of Object.keys(rec)) {
13158
+ if (key === "type" || key === "start" || key === "end") continue;
13159
+ const value = rec[key];
13160
+ if (Array.isArray(value)) {
13161
+ if (value.some((child) => walk2(child, rec, key))) return true;
13162
+ } else if (walk2(value, rec, key)) {
13163
+ return true;
13164
+ }
13165
+ }
13166
+ return false;
13167
+ };
13168
+ return body.some((stmt) => walk2(stmt));
13169
+ };
13170
+ for (const imp of ordinaryImports) {
13171
+ if (!runtimeComposeFallback && imp.specifiers.length > 0 && imp.specifiers.every(
13172
+ (spec) => spec.type === "ImportSpecifier" && usedImportedFactories.has(spec.local) && !importedBindingStillReferenced(spec.local)
13173
+ )) {
13174
+ ms.remove(imp.start, imp.end);
13175
+ }
13176
+ }
12700
13177
  if (!anyUnresolved) {
12701
13178
  for (const { start, end } of defineRemovals) ms.remove(start, end);
12702
13179
  }