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
@@ -282,6 +282,65 @@ function failAt(ctx, expected, pos) {
282
282
  return r;
283
283
  }
284
284
 
285
+ // src/line-index.ts
286
+ function createLineIndex() {
287
+ return { lineStarts: [0] };
288
+ }
289
+ function recordLineRange(index, input, start, end) {
290
+ const lineStarts = index.lineStarts;
291
+ for (let i = start; i < end; i++) {
292
+ if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
293
+ }
294
+ }
295
+ function recordLineRangeFromContext(ctx, input, _startHint, end) {
296
+ if (!ctx.trackLines) return;
297
+ const from = ctx._lineScannedTo ?? 0;
298
+ if (end <= from) return;
299
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
300
+ if (!index) return;
301
+ recordLineRange(index, input, from, end);
302
+ ctx._lineScannedTo = end;
303
+ }
304
+ function normalizeLineIndex(index) {
305
+ const starts = index.lineStarts;
306
+ starts.sort((a, b) => a - b);
307
+ let write = 0;
308
+ for (let read = 0; read < starts.length; read++) {
309
+ const value = starts[read];
310
+ if (write === 0 || starts[write - 1] !== value) starts[write++] = value;
311
+ }
312
+ starts.length = write;
313
+ if (starts.length === 0 || starts[0] !== 0) starts.unshift(0);
314
+ return index;
315
+ }
316
+ function offsetToLineCol(index, offset) {
317
+ const { lineStarts } = index;
318
+ let lo = 0;
319
+ let hi = lineStarts.length - 1;
320
+ while (lo < hi) {
321
+ const mid = lo + hi + 1 >> 1;
322
+ if (lineStarts[mid] <= offset) lo = mid;
323
+ else hi = mid - 1;
324
+ }
325
+ return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
326
+ }
327
+ function annotateSpan(span, index) {
328
+ const s = offsetToLineCol(index, span.start);
329
+ const e = offsetToLineCol(index, span.end);
330
+ return {
331
+ ...span,
332
+ startLine: s.line,
333
+ startColumn: s.col,
334
+ endLine: e.line,
335
+ endColumn: e.col
336
+ };
337
+ }
338
+ function annotateSpanFromLineContext(span, ctx) {
339
+ if (!ctx.trackLines) return span;
340
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
341
+ return index ? annotateSpan(span, index) : span;
342
+ }
343
+
285
344
  // src/cst/capture-buffer.ts
286
345
  var EMPTY = [];
287
346
  var EMPTY_TL = [];
@@ -323,7 +382,12 @@ function endCstNodeCapture(ctx, saved) {
323
382
  return materialized;
324
383
  }
325
384
  function pushCstLeaf(ctx, leaf2) {
326
- pushCstChild(ctx, leaf2, leaf2);
385
+ if (!ctx.trackLines) {
386
+ pushCstChild(ctx, leaf2, leaf2);
387
+ return;
388
+ }
389
+ const annotated = leaf2 && typeof leaf2 === "object" && leaf2.span ? { ...leaf2, span: annotateSpanFromLineContext(leaf2.span, ctx) } : leaf2;
390
+ pushCstChild(ctx, annotated, annotated);
327
391
  }
328
392
  function pushCstChild(ctx, built, rawEntry) {
329
393
  const b = ctx._cstBuf;
@@ -457,6 +521,7 @@ function literal(value, opts = {}) {
457
521
  canMatchNewline: value.includes("\n"),
458
522
  isTrivia: false
459
523
  };
524
+ const canMatchNewline = meta.canMatchNewline;
460
525
  if (caseInsensitive) {
461
526
  const upper = value.toUpperCase();
462
527
  const lower = value.toLowerCase();
@@ -473,6 +538,7 @@ function literal(value, opts = {}) {
473
538
  return function parse3(input, pos, ctx) {
474
539
  if (input.charCodeAt(pos) === code) {
475
540
  const span = { start: pos, end: pos + 1 };
541
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
476
542
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
477
543
  return { ok: true, value, span };
478
544
  }
@@ -486,6 +552,7 @@ function literal(value, opts = {}) {
486
552
  const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
487
553
  if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
488
554
  const span = { start: pos, end };
555
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
489
556
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
490
557
  return { ok: true, value: matchedValue, span };
491
558
  }
@@ -653,6 +720,7 @@ function regex(pattern, flags = "") {
653
720
  if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
654
721
  const value = input.slice(pos, scanEnd);
655
722
  const span2 = { start: pos, end: scanEnd };
723
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
656
724
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
657
725
  return { ok: true, value, span: span2 };
658
726
  }
@@ -662,6 +730,7 @@ function regex(pattern, flags = "") {
662
730
  return failAt(ctx, [`/${source}/`], pos);
663
731
  }
664
732
  const span = { start: pos, end: pos + m[0].length };
733
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
665
734
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
666
735
  return { ok: true, value: m[0], span };
667
736
  }
@@ -800,8 +869,8 @@ function parseRegex(src) {
800
869
  }
801
870
  if (src[k] !== "]") throw BAIL;
802
871
  i = k + 1;
803
- if (negated) return charNode(any(), true);
804
872
  const ranges = parseClassRanges(body);
873
+ if (negated) return charNode(any(), !ranges || !rangesHaveNewline(ranges));
805
874
  if (!ranges) return charNode(any(), true);
806
875
  return charNode(rangesToSet(ranges), rangesHaveNewline(ranges));
807
876
  };
@@ -897,7 +966,7 @@ function firstSet(n) {
897
966
  }
898
967
  }
899
968
  }
900
- function firstCanBeNewline(n) {
969
+ function canConsumeNewline(n) {
901
970
  switch (n.t) {
902
971
  case "char":
903
972
  return n.nl;
@@ -906,16 +975,11 @@ function firstCanBeNewline(n) {
906
975
  case "empty":
907
976
  return false;
908
977
  case "rep":
909
- return firstCanBeNewline(n.node);
978
+ return canConsumeNewline(n.node);
910
979
  case "alt":
911
- return n.arms.some(firstCanBeNewline);
912
- case "seq": {
913
- for (const part of n.parts) {
914
- if (firstCanBeNewline(part)) return true;
915
- if (!nullable(part)) break;
916
- }
917
- return false;
918
- }
980
+ return n.arms.some(canConsumeNewline);
981
+ case "seq":
982
+ return n.parts.some(canConsumeNewline);
919
983
  }
920
984
  }
921
985
  function firstSetFromRegex(source) {
@@ -925,8 +989,8 @@ function firstSetFromRegex(source) {
925
989
  } catch {
926
990
  return { firstSet: any(), canMatchNewline: true };
927
991
  }
928
- if (nullable(ast)) return { firstSet: any(), canMatchNewline: true };
929
- return { firstSet: firstSet(ast), canMatchNewline: firstCanBeNewline(ast) };
992
+ if (nullable(ast)) return { firstSet: any(), canMatchNewline: canConsumeNewline(ast) };
993
+ return { firstSet: firstSet(ast), canMatchNewline: canConsumeNewline(ast) };
930
994
  }
931
995
 
932
996
  // src/combinators/case-fold.ts
@@ -1083,7 +1147,9 @@ function firstSetSentinel(fs2) {
1083
1147
  function recoverScan(input, from, ctx, sync, expected) {
1084
1148
  let scanPos = from;
1085
1149
  while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1086
- const error = { _tag: "parseError", span: { start: from, end: scanPos }, expected };
1150
+ const span = { start: from, end: scanPos };
1151
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
1152
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1087
1153
  ctx._errors?.push(error);
1088
1154
  return { error, end: scanPos };
1089
1155
  }
@@ -1185,7 +1251,8 @@ function expect(combinator, label2) {
1185
1251
  parse(input, pos, ctx) {
1186
1252
  const result = combinator.parse(input, pos, ctx);
1187
1253
  if (result.ok) return result;
1188
- const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
1254
+ const span = { start: pos, end: pos };
1255
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1189
1256
  ctx._errors?.push(error);
1190
1257
  if (ctx._tolerant) captureError(ctx, error);
1191
1258
  return { ok: true, value: error, span: { start: pos, end: pos } };
@@ -1630,6 +1697,18 @@ function rollbackTrivia(ctx, mark) {
1630
1697
  rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1631
1698
  if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1632
1699
  }
1700
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1701
+ const probeCtx = {
1702
+ trackLines: ctx.trackLines,
1703
+ state: ctx.state,
1704
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1705
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1706
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1707
+ };
1708
+ const result = triviaP.parse(input, cur, probeCtx);
1709
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1710
+ return result;
1711
+ }
1633
1712
  function saveLookaheadMark(ctx) {
1634
1713
  return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1635
1714
  }
@@ -1652,13 +1731,29 @@ function scanWithLabels(input, cur, ctx) {
1652
1731
  function advanceTrivia(input, cur, ctx) {
1653
1732
  const triviaP = ctx.trivia;
1654
1733
  if (!triviaP) return cur;
1734
+ if (!ctx.trackLines) {
1735
+ const fast2 = fastTriviaScanner(triviaP);
1736
+ if (fast2) return fast2(input, cur);
1737
+ if (ctx.triviaKindLabels) {
1738
+ const scan = scanWithLabels(input, cur, ctx);
1739
+ return scan.end;
1740
+ }
1741
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1742
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1743
+ }
1744
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1655
1745
  const fast = fastTriviaScanner(triviaP);
1656
- if (fast) return fast(input, cur);
1746
+ if (fast) {
1747
+ const end = fast(input, cur);
1748
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1749
+ return end;
1750
+ }
1657
1751
  if (ctx.triviaKindLabels) {
1658
1752
  const scan = scanWithLabels(input, cur, ctx);
1753
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1659
1754
  return scan.end;
1660
1755
  }
1661
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1756
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1662
1757
  return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1663
1758
  }
1664
1759
  function scanTrivia(input, cur, ctx) {
@@ -1666,18 +1761,46 @@ function scanTrivia(input, cur, ctx) {
1666
1761
  if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1667
1762
  const log = ctx._triviaLog;
1668
1763
  const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1764
+ if (!ctx.trackLines) {
1765
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1766
+ if (fast2 && log === void 0 && !captureTl) {
1767
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1768
+ }
1769
+ if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1770
+ return scanWithLabels(input, cur, ctx);
1771
+ }
1772
+ if (log !== void 0 || captureTl) {
1773
+ const tr3 = triviaP.parse(input, cur, {
1774
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1775
+ state: ctx.state
1776
+ });
1777
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1778
+ const end = tr3.span.end;
1779
+ return {
1780
+ end,
1781
+ commit: () => {
1782
+ pushTriviaLogEntry(ctx, cur, end);
1783
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1784
+ }
1785
+ };
1786
+ }
1787
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1788
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1789
+ }
1790
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1669
1791
  const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1670
1792
  if (fast && log === void 0 && !captureTl) {
1671
- return { end: fast(input, cur), commit: NOOP_COMMIT };
1793
+ const end = fast(input, cur);
1794
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1795
+ return { end, commit: NOOP_COMMIT };
1672
1796
  }
1673
1797
  if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1674
- return scanWithLabels(input, cur, ctx);
1798
+ const scan = scanWithLabels(input, cur, ctx);
1799
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1800
+ return scan;
1675
1801
  }
1676
1802
  if (log !== void 0 || captureTl) {
1677
- const tr2 = triviaP.parse(input, cur, {
1678
- trackLines: log !== void 0 ? false : ctx.trackLines,
1679
- state: ctx.state
1680
- });
1803
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1681
1804
  if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1682
1805
  const end = tr2.span.end;
1683
1806
  return {
@@ -1688,7 +1811,7 @@ function scanTrivia(input, cur, ctx) {
1688
1811
  }
1689
1812
  };
1690
1813
  }
1691
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1814
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1692
1815
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1693
1816
  }
1694
1817
  function consumeTrivia(input, cur, ctx) {
@@ -2619,6 +2742,74 @@ function markUnusedValues(root) {
2619
2742
  visit(resolveRoot(root), true);
2620
2743
  }
2621
2744
 
2745
+ // src/combinators/grammar.ts
2746
+ function annotateResultLines(result, index) {
2747
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
2748
+ const errors = annotated.errors;
2749
+ if (errors) {
2750
+ ;
2751
+ annotated.errors = errors.map((error) => ({
2752
+ ...error,
2753
+ span: annotateSpan(error.span, index)
2754
+ }));
2755
+ }
2756
+ return annotated;
2757
+ }
2758
+ function createParseLineContext(input, pos) {
2759
+ const lineIndex = createLineIndex();
2760
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
2761
+ return { lineIndex, lineScannedTo: pos };
2762
+ }
2763
+ function parser(opts, root) {
2764
+ const clearTrivia = opts.trivia === null;
2765
+ return {
2766
+ _tag: "grammar",
2767
+ _meta: root._meta,
2768
+ _def: {
2769
+ tag: "grammar",
2770
+ parser: root,
2771
+ triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
2772
+ clearTrivia,
2773
+ ...opts.captureTrivia ? { captureTrivia: true } : {},
2774
+ trackLines: opts.trackLines ?? false
2775
+ },
2776
+ parse(input, pos, _ctx) {
2777
+ const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
2778
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
2779
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
2780
+ const ctx = {
2781
+ ..._ctx,
2782
+ trackLines,
2783
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
2784
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
2785
+ // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
2786
+ ...clearTrivia ? {
2787
+ trivia: void 0,
2788
+ triviaKindLabels: void 0
2789
+ } : opts.trivia != null ? {
2790
+ trivia: opts.trivia,
2791
+ ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
2792
+ } : {},
2793
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
2794
+ // Kind-filter for per-node capture. Resolve against this scope's trivia
2795
+ // labels — this parser's own trivia if it declares one, else the INHERITED
2796
+ // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
2797
+ // trivia is inherited rather than re-declared here. No labels → undefined
2798
+ // (capture all).
2799
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
2800
+ };
2801
+ const result = root.parse(input, pos ?? 0, ctx);
2802
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
2803
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
2804
+ }
2805
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
2806
+ }
2807
+ };
2808
+ }
2809
+ function noTrivia(root) {
2810
+ return parser({ trivia: null }, root);
2811
+ }
2812
+
2622
2813
  // src/combinators/parser.ts
2623
2814
  var RULE_ORDER = "__parsemanRuleOrder";
2624
2815
  function tagRule(r, key) {
@@ -2694,6 +2885,21 @@ function rules(a, b) {
2694
2885
  if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
2695
2886
  }
2696
2887
  }
2888
+ if (options?.trackLines === true) {
2889
+ for (const key of Object.keys(definitions)) {
2890
+ const rule = cache[key];
2891
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
2892
+ }
2893
+ for (const key of Object.keys(definitions)) {
2894
+ const rule = cache[key];
2895
+ if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
2896
+ const wrapped = parser({ trackLines: true }, rule);
2897
+ tagRule(wrapped, key);
2898
+ wrapped._meta.grammarTrackLines = true;
2899
+ cache[key] = wrapped;
2900
+ }
2901
+ }
2902
+ }
2697
2903
  for (const key of Object.keys(definitions)) {
2698
2904
  markUnusedValues(cache[key]);
2699
2905
  }
@@ -3072,13 +3278,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3072
3278
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3073
3279
  const nodeType = def.type ?? missingInferredType();
3074
3280
  const cstOutput = cstOutputHost(ctx.build);
3281
+ const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3075
3282
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3076
- 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 };
3077
- 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 };
3283
+ 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 };
3284
+ 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 };
3078
3285
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3079
3286
  pushCstChild(ctx, built, rawEntry);
3080
3287
  }
3081
- return { ok: true, value: built, span: r.span };
3288
+ return { ok: true, value: built, span };
3082
3289
  }
3083
3290
  };
3084
3291
  }
@@ -3149,85 +3356,6 @@ function field(name, combinator) {
3149
3356
  };
3150
3357
  }
3151
3358
 
3152
- // src/compiler/line-index.ts
3153
- function buildLineIndex(input) {
3154
- const lineStarts = [0];
3155
- for (let i = 0; i < input.length; i++) {
3156
- if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
3157
- }
3158
- return { lineStarts };
3159
- }
3160
- function offsetToLineCol(index, offset) {
3161
- const { lineStarts } = index;
3162
- let lo = 0;
3163
- let hi = lineStarts.length - 1;
3164
- while (lo < hi) {
3165
- const mid = lo + hi + 1 >> 1;
3166
- if (lineStarts[mid] <= offset) lo = mid;
3167
- else hi = mid - 1;
3168
- }
3169
- return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
3170
- }
3171
- function annotateSpan(span, index) {
3172
- const s = offsetToLineCol(index, span.start);
3173
- const e = offsetToLineCol(index, span.end);
3174
- return {
3175
- ...span,
3176
- startLine: s.line,
3177
- startColumn: s.col,
3178
- endLine: e.line,
3179
- endColumn: e.col
3180
- };
3181
- }
3182
-
3183
- // src/combinators/grammar.ts
3184
- function parser(opts, root) {
3185
- const clearTrivia = opts.trivia === null;
3186
- return {
3187
- _tag: "grammar",
3188
- _meta: root._meta,
3189
- _def: {
3190
- tag: "grammar",
3191
- parser: root,
3192
- triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
3193
- clearTrivia,
3194
- ...opts.captureTrivia ? { captureTrivia: true } : {},
3195
- trackLines: opts.trackLines ?? false
3196
- },
3197
- parse(input, pos, _ctx) {
3198
- const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
3199
- const ctx = {
3200
- ..._ctx,
3201
- trackLines,
3202
- // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
3203
- ...clearTrivia ? {
3204
- trivia: void 0,
3205
- triviaKindLabels: void 0
3206
- } : opts.trivia != null ? {
3207
- trivia: opts.trivia,
3208
- ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
3209
- } : {},
3210
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
3211
- // Kind-filter for per-node capture. Resolve against this scope's trivia
3212
- // labels — this parser's own trivia if it declares one, else the INHERITED
3213
- // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
3214
- // trivia is inherited rather than re-declared here. No labels → undefined
3215
- // (capture all).
3216
- ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
3217
- };
3218
- const result = root.parse(input, pos ?? 0, ctx);
3219
- if (trackLines) {
3220
- const idx = buildLineIndex(input);
3221
- return { ...result, span: annotateSpan(result.span, idx) };
3222
- }
3223
- return result;
3224
- }
3225
- };
3226
- }
3227
- function noTrivia(root) {
3228
- return parser({ trivia: null }, root);
3229
- }
3230
-
3231
3359
  // src/combinators/token.ts
3232
3360
  function token(root) {
3233
3361
  const meta = {
@@ -3324,7 +3452,7 @@ function leaf(root, fn) {
3324
3452
  }
3325
3453
 
3326
3454
  // src/version.ts
3327
- var PARSEMAN_VERSION = "0.41.0";
3455
+ var PARSEMAN_VERSION = "0.42.1";
3328
3456
 
3329
3457
  // src/cst/host-mode.ts
3330
3458
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -5008,7 +5136,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5008
5136
  let cur = pos;
5009
5137
  const skip3 = resolveScanSkip(explicitSkip, raw, ctx);
5010
5138
  const probeCtx = {
5011
- trackLines: ctx.trackLines,
5139
+ trackLines: false,
5012
5140
  state: ctx.state,
5013
5141
  ...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
5014
5142
  };
@@ -5021,6 +5149,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5021
5149
  while (cur < input.length) {
5022
5150
  const s = sentinel.parse(input, cur, probeCtx);
5023
5151
  if (s.ok) {
5152
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
5024
5153
  emit2(cur);
5025
5154
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
5026
5155
  }
@@ -5036,6 +5165,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5036
5165
  if (!advanced) cur++;
5037
5166
  }
5038
5167
  if (orEOF) {
5168
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
5039
5169
  emit2(cur);
5040
5170
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
5041
5171
  }
@@ -6474,8 +6604,8 @@ function analyzeMkInlineBuild(def) {
6474
6604
  if (looseMkBuildRe(def.type).test(src)) return def.type;
6475
6605
  return null;
6476
6606
  }
6477
- function emitInlineMkNodeExpr(type, chV, rawV, pos, endVar, tlV) {
6478
- return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: { start: ${pos}, end: ${endVar} }, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`;
6607
+ function emitInlineMkNodeExpr(type, chV, rawV, spanExpr, tlV) {
6608
+ return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: ${spanExpr}, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`;
6479
6609
  }
6480
6610
 
6481
6611
  // src/compiler/inline-callback.ts
@@ -6657,6 +6787,8 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
6657
6787
  }
6658
6788
  }
6659
6789
  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 }";
6790
+ 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 }";
6791
+ 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] } }";
6660
6792
  var _loweringSink = null;
6661
6793
  function beginLoweringCapture() {
6662
6794
  _loweringSink = /* @__PURE__ */ new Set();
@@ -6688,13 +6820,16 @@ function reindentStmts(stmts, targetLevels) {
6688
6820
  const targetPrefix = " ".repeat(targetLevels);
6689
6821
  return stmts.map((s) => s.trim().length === 0 ? "" : targetPrefix + s.slice(minLeading));
6690
6822
  }
6691
- function failReturn(expected, posExpr) {
6823
+ function failReturn(ctx, expected, posExpr) {
6824
+ if (ctx.lineTracking) return `return { ok: false, expected: [${expected}], span: ${emitSpanExpr(ctx, posExpr, posExpr)} }`;
6692
6825
  return `return { ok: false, expected: [${expected}], span: { start: ${posExpr}, end: ${posExpr} } }`;
6693
6826
  }
6694
- function failReturnArr(expectedArr, posExpr) {
6827
+ function failReturnArr(ctx, expectedArr, posExpr) {
6828
+ if (ctx.lineTracking) return `return { ok: false, expected: ${expectedArr}, span: ${emitSpanExpr(ctx, posExpr, posExpr)} }`;
6695
6829
  return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
6696
6830
  }
6697
- function committedReturnArr(expectedArr, posExpr) {
6831
+ function committedReturnArr(ctx, expectedArr, posExpr) {
6832
+ if (ctx.lineTracking) return `return { ok: false, expected: [...${expectedArr}], span: ${emitSpanExpr(ctx, posExpr, posExpr)}, committed: true }`;
6698
6833
  return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} }, committed: true }`;
6699
6834
  }
6700
6835
  function hoistExpected(ctx, constArrSource) {
@@ -6719,7 +6854,7 @@ function failBody(ctx, expected, posExpr) {
6719
6854
  if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
6720
6855
  return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; ${trace}break ${ctx.failLabel} }`;
6721
6856
  }
6722
- return probe + trace + failReturn(expected, posExpr);
6857
+ return probe + trace + failReturn(ctx, expected, posExpr);
6723
6858
  }
6724
6859
  function failArrBody(ctx, expectedArr, posExpr, constant = true) {
6725
6860
  const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
@@ -6729,18 +6864,19 @@ function failArrBody(ctx, expectedArr, posExpr, constant = true) {
6729
6864
  const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
6730
6865
  return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; ${trace}break ${ctx.failLabel} }`;
6731
6866
  }
6732
- if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
6733
- return probe + trace + failReturnArr(expectedArr, posExpr);
6867
+ if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: ${ctx.lineTracking ? emitSpanExpr(ctx, posExpr, posExpr) : `{ start: ${posExpr}, end: ${posExpr} }`} }`;
6868
+ return probe + trace + failReturnArr(ctx, expectedArr, posExpr);
6734
6869
  }
6735
- function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
6736
- return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} }, ...(_ctx._fc ? { committed: true } : {}) }`;
6870
+ function resultFromRecorded(ctx, feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
6871
+ const spanExpr = ctx.lineTracking ? emitSpanExpr(ctx, feExpr, feExpr) : `{ start: ${feExpr}, end: ${feExpr} }`;
6872
+ return `return { ok: false, expected: [...${fxExpr}], span: ${spanExpr}, ...(_ctx._fc ? { committed: true } : {}) }`;
6737
6873
  }
6738
6874
  function committedFailBody(ctx, expectedArr = "_ctx._fx", posExpr = "_ctx._fe") {
6739
6875
  if (ctx.failLabel) {
6740
6876
  if (!ctx.recordFail) return `{ _ctx._fc = true; break ${ctx.failLabel} }`;
6741
6877
  return `{ _ctx._fc = true; _ctx._fe = ${posExpr}; _ctx._fx = ${expectedArr}; break ${ctx.failLabel} }`;
6742
6878
  }
6743
- return committedReturnArr(expectedArr, posExpr);
6879
+ return committedReturnArr(ctx, expectedArr, posExpr);
6744
6880
  }
6745
6881
  function propagateFailBody(ctx, srcCtx = "_ctx") {
6746
6882
  if (srcCtx !== "_ctx") {
@@ -6748,10 +6884,10 @@ function propagateFailBody(ctx, srcCtx = "_ctx") {
6748
6884
  if (!ctx.recordFail) return `break ${ctx.failLabel}`;
6749
6885
  return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; break ${ctx.failLabel} }`;
6750
6886
  }
6751
- return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; ${resultFromRecorded()} }`;
6887
+ return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; _ctx._fc = ${srcCtx}._fc; ${resultFromRecorded(ctx)} }`;
6752
6888
  }
6753
6889
  if (ctx.failLabel) return `break ${ctx.failLabel}`;
6754
- return resultFromRecorded();
6890
+ return resultFromRecorded(ctx);
6755
6891
  }
6756
6892
  function emitIfFail(ctx, cond, body) {
6757
6893
  return [
@@ -6805,14 +6941,46 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
6805
6941
  if (ctx.capAsTrivia) return [];
6806
6942
  const i = ind(ctx);
6807
6943
  const lf = v(ctx, "_lf");
6944
+ const spanExpr = emitSpanExpr(ctx, startExpr, endExpr);
6808
6945
  return [
6809
6946
  `${i}if (_ctx._cstLeaves || _ctx._cstRawChildren) {`,
6810
- `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } }`,
6947
+ `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: ${spanExpr} }`,
6811
6948
  `${i} if (_ctx._cstLeaves) _ctx._cstLeaves.push(${lf})`,
6812
6949
  `${i} if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(${lf})`,
6813
6950
  `${i}}`
6814
6951
  ];
6815
6952
  }
6953
+ function emitSpanExpr(ctx, startExpr, endExpr) {
6954
+ if (!ctx.lineTracking) return `{ start: ${startExpr}, end: ${endExpr} }`;
6955
+ ctx.needsLineSpan = true;
6956
+ return `_spanLines(_ctx, ${startExpr}, ${endExpr})`;
6957
+ }
6958
+ function emitLineTrack(ctx, startExpr, endExpr) {
6959
+ if (!ctx.lineTracking) return [];
6960
+ ctx.needsLineTrack = true;
6961
+ return [`${ind(ctx)}_trackLines(_ctx, input, ${startExpr}, ${endExpr})`];
6962
+ }
6963
+ function emitLiteralLineTrack(ctx, startExpr, value) {
6964
+ if (!ctx.lineTracking) return [];
6965
+ const lineDeltas = [];
6966
+ for (let i = 0; i < value.length; i++) {
6967
+ if (value.charCodeAt(i) === 10) lineDeltas.push(i + 1);
6968
+ }
6969
+ if (lineDeltas.length === 0) return [];
6970
+ const fromV = v(ctx, "_ltFrom");
6971
+ const endExpr = value.length === 0 ? startExpr : `${startExpr} + ${value.length}`;
6972
+ return [
6973
+ `${ind(ctx)}const ${fromV} = _ctx._lineScannedTo ?? 0`,
6974
+ `${ind(ctx)}if (${endExpr} > ${fromV}) {`,
6975
+ `${ind(ctx)} for (let i = ${fromV}; i < ${startExpr}; i++) if (input.charCodeAt(i) === 10) _ctx._lineStarts.push(i + 1)`,
6976
+ ...lineDeltas.map((delta) => {
6977
+ const lineStart = `${startExpr} + ${delta}`;
6978
+ return `${ind(ctx)} if (${lineStart} > ${fromV}) _ctx._lineStarts.push(${lineStart})`;
6979
+ }),
6980
+ `${ind(ctx)} _ctx._lineScannedTo = ${endExpr}`,
6981
+ `${ind(ctx)}}`
6982
+ ];
6983
+ }
6816
6984
  function ensureRegexDecl(ctx, source, flags) {
6817
6985
  const f = "y" + flags.replace(/[gy]/g, "");
6818
6986
  const key = `${source}/${f}`;
@@ -7197,6 +7365,7 @@ function emitLit(def, ctx, pos) {
7197
7365
  );
7198
7366
  }
7199
7367
  const endVar = len === 0 ? pos : `${pos} + ${len}`;
7368
+ stmts.push(...emitLiteralLineTrack(ctx, pos, value));
7200
7369
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7201
7370
  return { stmts, valueVar: vv, endVar };
7202
7371
  }
@@ -7276,7 +7445,7 @@ function emitKeywords(def, ctx, pos) {
7276
7445
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7277
7446
  return { stmts, valueVar: vv, endVar };
7278
7447
  }
7279
- function emitRegex(def, ctx, pos) {
7448
+ function emitRegex(def, ctx, pos, canMatchNewline = true) {
7280
7449
  const expectedStr = JSON.stringify(`/${def.source}/`);
7281
7450
  const shape = scanShapeFromRegex(def.source, def.flags);
7282
7451
  if (shape) {
@@ -7289,7 +7458,11 @@ function emitRegex(def, ctx, pos) {
7289
7458
  fresh: (prefix) => v(ctx, prefix)
7290
7459
  });
7291
7460
  if (scanned) {
7292
- const stmts2 = [...scanned.stmts, ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)];
7461
+ const stmts2 = [
7462
+ ...scanned.stmts,
7463
+ ...canMatchNewline ? emitLineTrack(ctx, pos, scanned.endVar) : [],
7464
+ ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)
7465
+ ];
7293
7466
  return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
7294
7467
  }
7295
7468
  }
@@ -7311,6 +7484,7 @@ function emitRegex(def, ctx, pos) {
7311
7484
  `${ind(ctx)}const ${vv} = ${mv}[0]`
7312
7485
  ];
7313
7486
  const endVar = `${pos} + ${vv}.length`;
7487
+ if (canMatchNewline) stmts.push(...emitLineTrack(ctx, pos, endVar));
7314
7488
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7315
7489
  return { stmts, valueVar: vv, endVar };
7316
7490
  }
@@ -7348,7 +7522,8 @@ function emitSeqValues(def, ctx, pos) {
7348
7522
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
7349
7523
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
7350
7524
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7351
- `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`
7525
+ `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`,
7526
+ ...emitLineTrack(ctx, curV, scanEndV)
7352
7527
  );
7353
7528
  const r2 = emit(def.parsers[i], ctx, scanEndV);
7354
7529
  stmts.push(...r2.stmts);
@@ -7366,7 +7541,8 @@ function emitSeqValues(def, ctx, pos) {
7366
7541
  const capArg = ctx.noHoist ? "0" : "_ctx._triviaLog !== undefined ? 2 : 0";
7367
7542
  stmts.push(
7368
7543
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7369
- `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`
7544
+ `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`,
7545
+ ...emitLineTrack(ctx, curV, scanEndV)
7370
7546
  );
7371
7547
  const r2 = emit(def.parsers[i], ctx, scanEndV);
7372
7548
  stmts.push(...r2.stmts);
@@ -8066,14 +8242,15 @@ function emitMany(def, ctx, pos) {
8066
8242
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8067
8243
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8068
8244
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8069
- `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`
8245
+ `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8246
+ ...emitLineTrack(ctx, curV, npV)
8070
8247
  );
8071
8248
  itemPos = npV;
8072
8249
  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}; `;
8073
8250
  } else {
8074
8251
  const trivFn = ensureTriviaFn(ctx);
8075
8252
  const npV = v(ctx, "_np");
8076
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`);
8253
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, npV));
8077
8254
  itemPos = npV;
8078
8255
  }
8079
8256
  }
@@ -8143,7 +8320,7 @@ function emitAttempt(p, def, ctx, pos) {
8143
8320
  if (!ctx.recovery && !ctx.coverage && needsFirstSetGuard(def.parser)) {
8144
8321
  const gcV = v(ctx, "_agc");
8145
8322
  const gExp = armStaticExpected(ctx, def.parser);
8146
- const gFail = ctx.failLabel ? `{ _ctx._fe = ${pos};${ctx.recordFail ? ` _ctx._fx = ${gExp};` : ""} break ${ctx.failLabel} }` : `{ _ctx._fe = ${pos}; ${failReturnArr(gExp, pos)} }`;
8323
+ const gFail = ctx.failLabel ? `{ _ctx._fe = ${pos};${ctx.recordFail ? ` _ctx._fx = ${gExp};` : ""} break ${ctx.failLabel} }` : `{ _ctx._fe = ${pos}; ${failReturnArr(ctx, gExp, pos)} }`;
8147
8324
  preGuard.push(
8148
8325
  `${ind(ctx)}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
8149
8326
  `${ind(ctx)}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
@@ -8250,7 +8427,8 @@ function emitSepBy(_p, def, ctx, pos) {
8250
8427
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8251
8428
  `${ind(ctx)}const ${markLv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8252
8429
  `${ind(ctx)}const ${markFld} = _ctx._fields ? _ctx._fields.length : 0`,
8253
- `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`
8430
+ `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8431
+ ...emitLineTrack(ctx, curV, spV)
8254
8432
  );
8255
8433
  sepAtPos = spV;
8256
8434
  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}; `;
@@ -8260,7 +8438,7 @@ function emitSepBy(_p, def, ctx, pos) {
8260
8438
  const post = postSepMarks();
8261
8439
  stmts.push(...post.decl);
8262
8440
  const npV = v(ctx, "_np");
8263
- stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
8441
+ stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`, ...emitLineTrack(ctx, sepEnd, npV));
8264
8442
  const next = emitFallible(def.parser, ctx, npV, true);
8265
8443
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8266
8444
  stmts.push(
@@ -8272,13 +8450,13 @@ function emitSepBy(_p, def, ctx, pos) {
8272
8450
  } else {
8273
8451
  const trivFn = ensureTriviaFn(ctx);
8274
8452
  const spV = v(ctx, "_sp");
8275
- stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
8453
+ stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, spV));
8276
8454
  sepAtPos = spV;
8277
8455
  const sep2 = emitFallible(def.separator, ctx, sepAtPos, true);
8278
8456
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep2;
8279
8457
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
8280
8458
  const npV = v(ctx, "_np");
8281
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
8459
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`, ...emitLineTrack(ctx, sepEnd, npV));
8282
8460
  const next = emitFallible(def.parser, ctx, npV, true);
8283
8461
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8284
8462
  stmts.push(
@@ -8365,6 +8543,7 @@ function emitScanTo(def, ctx, pos) {
8365
8543
  }
8366
8544
  const valV = v(ctx);
8367
8545
  stmts.push(`${ind(ctx)}const ${valV} = input.slice(${pos}, ${curV})`);
8546
+ stmts.push(...emitLineTrack(ctx, pos, curV));
8368
8547
  if (ctx.capturing) {
8369
8548
  const cap = reindentStmts(emitLeafCapture(ctx, valV, pos, curV), ctx.indent + 1);
8370
8549
  stmts.push(`${ind(ctx)}if (${curV} > ${pos}) {`, ...cap, `${ind(ctx)}}`);
@@ -8508,7 +8687,7 @@ function tokenNullableRegex(def) {
8508
8687
  }
8509
8688
  function emitToken(def, ctx, pos) {
8510
8689
  const lowered = tokenNullableRegex(def);
8511
- if (lowered) return emitRegex(lowered, ctx, pos);
8690
+ if (lowered) return emitRegex(lowered, ctx, pos, def.parser._meta.canMatchNewline);
8512
8691
  const savedTrivia = ctx.activeTrivia;
8513
8692
  const savedKindLabels = ctx.triviaKindLabels;
8514
8693
  ctx.activeTrivia = void 0;
@@ -8640,7 +8819,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8640
8819
  if (!ctx.recovery && !ctx.coverage && (capturesChildren || structural) && needsFirstSetGuard(def.parser)) {
8641
8820
  const gcV = v(ctx, "_ngc");
8642
8821
  const gExp = armStaticExpected(ctx, def.parser);
8643
- const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(gExp, pos);
8822
+ const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(ctx, gExp, pos);
8644
8823
  preGuard.push(
8645
8824
  `${i}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
8646
8825
  `${i}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
@@ -8658,7 +8837,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8658
8837
  stmts.push(...innerStmts);
8659
8838
  if (def.trailingTrivia === true && ctx.activeTrivia) {
8660
8839
  const triviaFn = ensureTriviaFn(ctx);
8661
- stmts.push(`${i}const ${endVar} = ${okVar} ? ${triviaFn}(input, ${innerEndVar}, _ctx, 1) : ${innerEndVar}`);
8840
+ stmts.push(`${i}const ${endVar} = ${okVar} ? ${triviaFn}(input, ${innerEndVar}, _ctx, 1) : ${innerEndVar}`, ...emitLineTrack(ctx, innerEndVar, endVar));
8662
8841
  }
8663
8842
  if (sf && fArr) {
8664
8843
  stmts.push(`${i}const ${fArr} = _ctx._fields`);
@@ -8698,8 +8877,11 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8698
8877
  );
8699
8878
  }
8700
8879
  const ndV = v(ctx, "_nd");
8701
- 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})`;
8702
- const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, { start: ${pos}, end: ${endVar} }, ${rawV}, ${tlV}, ${stV})`;
8880
+ const nodeSpanExpr = emitSpanExpr(ctx, pos, endVar);
8881
+ const nodeSpanV = ctx.lineTracking ? v(ctx, "_nspan") : nodeSpanExpr;
8882
+ if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
8883
+ 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})`;
8884
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
8703
8885
  const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
8704
8886
  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;
8705
8887
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
@@ -8709,7 +8891,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8709
8891
  `${i}const ${ndV} = (${profileRecognizer} || ${profileCapture}) ? undefined : (${finalExpr})`,
8710
8892
  `${i}if (!(${profileRecognizer})) {`,
8711
8893
  `${i} if (${sc}) ${sc}.push(${ndV})`,
8712
- `${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} } })`,
8894
+ `${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} })`,
8713
8895
  `${i}}`
8714
8896
  );
8715
8897
  return { stmts, valueVar: ndV, endVar };
@@ -8725,7 +8907,8 @@ function emitRuntimeFallback(parser2, ctx, pos) {
8725
8907
  `${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
8726
8908
  ...emitIfFail(ctx, `!${rv}.ok`, failStmt),
8727
8909
  `${ind(ctx)}const ${vv} = ${rv}.value`,
8728
- `${ind(ctx)}const ${ev} = ${rv}.span.end`
8910
+ `${ind(ctx)}const ${ev} = ${rv}.span.end`,
8911
+ ...parser2._meta.canMatchNewline ? emitLineTrack(ctx, pos, ev) : []
8729
8912
  ];
8730
8913
  return { stmts, valueVar: vv, endVar: ev };
8731
8914
  }
@@ -8791,7 +8974,8 @@ function emitRecover(def, ctx, pos) {
8791
8974
  `${ind0} if (${sentOk}) break`,
8792
8975
  `${ind0} ${scanV}++`,
8793
8976
  `${ind0} }`,
8794
- `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(deriveExpected(def.parser))} }`,
8977
+ ...emitLineTrack(ctx, pos, scanV).map((s) => `${ind0} ${s.trim()}`),
8978
+ `${ind0} const ${errV} = { _tag: 'parseError', span: ${emitSpanExpr(ctx, pos, scanV)}, expected: ${JSON.stringify(deriveExpected(def.parser))} }`,
8795
8979
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
8796
8980
  `${ind0} ${valVar} = ${errV}`,
8797
8981
  `${ind0} ${endVar} = ${scanV}`,
@@ -8807,7 +8991,7 @@ function emitExpect(def, ctx, pos) {
8807
8991
  const stmts = [
8808
8992
  ...innerStmts,
8809
8993
  `${ind0}if (!${okVar}) {`,
8810
- `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${pos} }, expected: ${JSON.stringify(def.expected)} }`,
8994
+ `${ind0} const ${errV} = { _tag: 'parseError', span: ${emitSpanExpr(ctx, pos, pos)}, expected: ${JSON.stringify(def.expected)} }`,
8811
8995
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
8812
8996
  // Embed the missing-token error as a `parseError` CST child, mirroring the
8813
8997
  // interpreter (expect.ts) and the list-recovery emit sites — guarded on
@@ -8897,7 +9081,7 @@ function emitDispatch(p, ctx, pos) {
8897
9081
  case "literal":
8898
9082
  return emitLit(def, ctx, pos);
8899
9083
  case "regex":
8900
- return emitRegex(def, ctx, pos);
9084
+ return emitRegex(def, ctx, pos, p._meta.canMatchNewline);
8901
9085
  case "keywords":
8902
9086
  return emitKeywords(def, ctx, pos);
8903
9087
  case "sequence":
@@ -9147,6 +9331,21 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
9147
9331
  return false;
9148
9332
  }
9149
9333
  }
9334
+ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9335
+ if (seen.has(p)) return false;
9336
+ seen.add(p);
9337
+ const d = p._def;
9338
+ if (p._meta.grammarTrackLines) return true;
9339
+ if (d.tag === "grammar" && d.trackLines) return true;
9340
+ if (d.tag === "lazy") {
9341
+ try {
9342
+ return hasLineTrackingDef(d.thunk(), seen);
9343
+ } catch {
9344
+ return false;
9345
+ }
9346
+ }
9347
+ return childrenOf2(d).some((child) => hasLineTrackingDef(child, seen));
9348
+ }
9150
9349
  function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9151
9350
  if (seen.has(p)) return false;
9152
9351
  seen.add(p);
@@ -9494,6 +9693,7 @@ function compile(combinator, mapFnSources, opts) {
9494
9693
  const grammarTrivia = combinator._meta.grammarTrivia;
9495
9694
  const grammarScanSkip = combinator._meta.grammarScanSkip;
9496
9695
  const grammarHostMode = combinator._meta.grammarHostMode;
9696
+ const grammarTrackLines = combinator._meta.grammarTrackLines;
9497
9697
  const ctx = {
9498
9698
  vars: 0,
9499
9699
  indent: 1,
@@ -9514,6 +9714,7 @@ function compile(combinator, mapFnSources, opts) {
9514
9714
  capturing: hasNodeDef(combinator),
9515
9715
  recovery: opts?.recovery ?? false,
9516
9716
  hostMode: opts?.hostMode ?? grammarHostMode ?? "ast",
9717
+ lineTracking: opts?.trackLines === true || grammarTrackLines === true || hasLineTrackingDef(combinator),
9517
9718
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
9518
9719
  lazyUsage: analyzeLazyUsage(combinator),
9519
9720
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
@@ -9537,9 +9738,13 @@ function compile(combinator, mapFnSources, opts) {
9537
9738
  const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
9538
9739
  const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
9539
9740
  const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
9741
+ const lineTrackDecls = ctx.needsLineTrack ? [LINE_TRACK_DECL] : [];
9742
+ const lineSpanDecls = ctx.needsLineSpan ? [LINE_SPAN_DECL] : [];
9540
9743
  const source = [
9541
9744
  ...emptyTlDecls,
9542
9745
  ...hostReadsDecls,
9746
+ ...lineTrackDecls,
9747
+ ...lineSpanDecls,
9543
9748
  ...ctx.regexDecls,
9544
9749
  ...ctx.expectedDecls,
9545
9750
  "",
@@ -9552,6 +9757,8 @@ function compile(combinator, mapFnSources, opts) {
9552
9757
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
9553
9758
  ...emptyTlDecls,
9554
9759
  ...hostReadsDecls,
9760
+ ...lineTrackDecls,
9761
+ ...lineSpanDecls,
9555
9762
  ...ctx.regexDecls,
9556
9763
  ...ctx.expectedDecls,
9557
9764
  ...namedPrelude,
@@ -9570,6 +9777,25 @@ function compile(combinator, mapFnSources, opts) {
9570
9777
  trackLines: false,
9571
9778
  ...ctx.triviaKindLabels ? { triviaKindLabels: ctx.triviaKindLabels } : {}
9572
9779
  };
9780
+ const annotateWithTrackedLines = (result, lineStarts) => {
9781
+ const index = normalizeLineIndex({ lineStarts });
9782
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
9783
+ const errors = annotated.errors;
9784
+ if (errors) {
9785
+ ;
9786
+ annotated.errors = errors.map((error) => ({
9787
+ ...error,
9788
+ span: annotateSpan(error.span, index)
9789
+ }));
9790
+ }
9791
+ return annotated;
9792
+ };
9793
+ const trackedCtx = (base, input, pos) => {
9794
+ if (!ctx.lineTracking) return base;
9795
+ const lineIndex = { lineStarts: [0] };
9796
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
9797
+ return { ...base, trackLines: true, _lineStarts: lineIndex.lineStarts, _lineScannedTo: pos };
9798
+ };
9573
9799
  const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
9574
9800
  const effectiveSources = mapFnSources ?? derivedSrcs;
9575
9801
  const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
@@ -9577,17 +9803,46 @@ function compile(combinator, mapFnSources, opts) {
9577
9803
  const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
9578
9804
  const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
9579
9805
  const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources, coverageRootRuleId) : null;
9580
- return {
9806
+ const compiledMeta = {
9581
9807
  source,
9582
9808
  inlineExpression,
9583
9809
  ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions },
9584
- ...gatingReport === void 0 ? {} : { gating: gatingReport },
9810
+ ...gatingReport === void 0 ? {} : { gating: gatingReport }
9811
+ };
9812
+ if (!ctx.lineTracking) {
9813
+ return {
9814
+ ...compiledMeta,
9815
+ parse(input, pos = 0) {
9816
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
9817
+ },
9818
+ parseWithContext(input, parseCtx, pos = 0) {
9819
+ assertHostModeCompatible(ctx.hostMode ?? "ast", parseCtx.build, !!ctx.hostBranchElided);
9820
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9821
+ },
9822
+ // Note: collects expect() errors via _errors. Unlike interpreter
9823
+ // parse({recover:true}) it does NOT populate furthestFail — the compiled path
9824
+ // inlines failures for throughput and deliberately skips _probe bookkeeping.
9825
+ // Callers wanting a furthest-position diagnostic detect unconsumed input
9826
+ // (span.end < input.length) instead, which is mode-agnostic.
9827
+ parseWithErrors(input, pos = 0) {
9828
+ const errors = [];
9829
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
9830
+ return { ...result, errors };
9831
+ }
9832
+ };
9833
+ }
9834
+ return {
9835
+ ...compiledMeta,
9585
9836
  parse(input, pos = 0) {
9586
- return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
9837
+ const parseCtx = trackedCtx(defaultCtx, input, pos);
9838
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9839
+ return annotateWithTrackedLines(result, parseCtx._lineStarts);
9587
9840
  },
9588
9841
  parseWithContext(input, parseCtx, pos = 0) {
9589
9842
  assertHostModeCompatible(ctx.hostMode ?? "ast", parseCtx.build, !!ctx.hostBranchElided);
9590
- return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9843
+ const activeCtx = trackedCtx(parseCtx, input, pos);
9844
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, activeCtx);
9845
+ return activeCtx.trackLines ? annotateWithTrackedLines(result, activeCtx._lineStarts) : result;
9591
9846
  },
9592
9847
  // Note: collects expect() errors via _errors. Unlike interpreter
9593
9848
  // parse({recover:true}) it does NOT populate furthestFail — the compiled path
@@ -9596,8 +9851,9 @@ function compile(combinator, mapFnSources, opts) {
9596
9851
  // (span.end < input.length) instead, which is mode-agnostic.
9597
9852
  parseWithErrors(input, pos = 0) {
9598
9853
  const errors = [];
9599
- const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
9600
- return { ...result, errors };
9854
+ const parseCtx = trackedCtx({ ...defaultCtx, _errors: errors }, input, pos);
9855
+ const result = { ...fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx), errors };
9856
+ return annotateWithTrackedLines(result, parseCtx._lineStarts);
9601
9857
  }
9602
9858
  };
9603
9859
  }
@@ -9622,6 +9878,7 @@ function compileRuleMap(ruleMap, opts) {
9622
9878
  const grammarTrivia = opts?.trivia;
9623
9879
  const grammarScanSkip = opts?.scanSkip ?? ruleMap.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
9624
9880
  const grammarHostMode = opts?.hostMode ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
9881
+ const grammarTrackLines = opts?.trackLines === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
9625
9882
  const ctx = {
9626
9883
  vars: 0,
9627
9884
  indent: 1,
@@ -9642,6 +9899,7 @@ function compileRuleMap(ruleMap, opts) {
9642
9899
  capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
9643
9900
  recovery: opts?.recovery ?? false,
9644
9901
  hostMode: grammarHostMode ?? "ast",
9902
+ lineTracking: grammarTrackLines,
9645
9903
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(ruleMap.map(([, rule]) => rule), coverageWinners) } } : {},
9646
9904
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
9647
9905
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
@@ -9675,12 +9933,18 @@ function compileRuleMap(ruleMap, opts) {
9675
9933
  const mfCovered = ctx.mapFns.length === 0 || derivedSrcs !== void 0;
9676
9934
  const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
9677
9935
  if (!canInline) return null;
9936
+ if (ctx.lineTracking) {
9937
+ ctx.needsLineTrack = true;
9938
+ ctx.needsLineSpan = true;
9939
+ }
9678
9940
  const mfDecl = derivedSrcs?.length ? ` const ${mfRef(ctx)} = [${derivedSrcs.join(", ")}]` : "";
9679
9941
  const buildDecl = buildSources?.length ? ` const ${buildRef(ctx)} = [${buildSources.join(", ")}]` : "";
9680
9942
  const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
9681
9943
  const hoistedDecls = [
9682
9944
  ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
9683
9945
  ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "",
9946
+ ctx.needsLineTrack ? ` ${LINE_TRACK_DECL}` : "",
9947
+ ctx.needsLineSpan ? ` ${LINE_SPAN_DECL}` : "",
9684
9948
  ...ctx.regexDecls.map((d) => ` ${d}`),
9685
9949
  ...ctx.expectedDecls.map((d) => ` ${d}`),
9686
9950
  mfDecl,
@@ -9702,15 +9966,23 @@ function compileRuleMap(ruleMap, opts) {
9702
9966
  };
9703
9967
  const entryFnText = (r, rule) => {
9704
9968
  const ruleId = entryRuleId(rule);
9969
+ const linePreamble = ctx.lineTracking ? [
9970
+ ` if (_ctx._lineStarts === undefined) _ctx = { ..._ctx, trackLines: true, _lineStarts: [0], _lineScannedTo: 0 }`,
9971
+ ` else _ctx.trackLines = true`,
9972
+ ` if (_pos > (_ctx._lineScannedTo ?? 0)) _trackLines(_ctx, input, 0, _pos)`
9973
+ ] : [];
9974
+ const resultSpan = ctx.lineTracking ? `_spanLines(_ctx, _pos, ${r.endVar})` : `{ start: _pos, end: ${r.endVar} }`;
9705
9975
  const body = ruleId === void 0 ? [
9976
+ ...linePreamble,
9706
9977
  ` let pos = _pos`,
9707
9978
  ...r.stmts,
9708
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
9979
+ ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`
9709
9980
  ] : [
9981
+ ...linePreamble,
9710
9982
  ` const _coverageResult = (() => {`,
9711
9983
  ` let pos = _pos`,
9712
9984
  ...r.stmts,
9713
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
9985
+ ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`,
9714
9986
  ` })()`,
9715
9987
  ` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
9716
9988
  ` return _coverageResult`
@@ -9748,6 +10020,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
9748
10020
  const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
9749
10021
  const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
9750
10022
  const grammarHostMode = opts?.hostMode ?? ruleMapArg.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
10023
+ const grammarTrackLines = opts?.trackLines === true || ruleMapArg.some(([, r]) => r._meta.grammarTrackLines === true);
9751
10024
  const ruleMap = ruleMapArg.filter(([, val]) => {
9752
10025
  const d = val._def;
9753
10026
  if (d.tag !== "lazy") return true;
@@ -9782,6 +10055,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
9782
10055
  capturing: opts?.captureTerminals === true || ruleMap.some(([, rule]) => hasNodeDef(rule)),
9783
10056
  recovery: opts?.recovery ?? false,
9784
10057
  hostMode: grammarHostMode ?? "ast",
10058
+ lineTracking: grammarTrackLines,
9785
10059
  ...opts?.coverage ? { coverage: { plan: opts.coverage } } : {},
9786
10060
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
9787
10061
  ns,
@@ -9878,6 +10152,10 @@ function compileLinkable(ruleMapArg, ns, opts) {
9878
10152
  perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
9879
10153
  }
9880
10154
  if (ctx.runtimeParsers.length !== 0) return null;
10155
+ if (ctx.lineTracking) {
10156
+ ctx.needsLineTrack = true;
10157
+ ctx.needsLineSpan = true;
10158
+ }
9881
10159
  const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
9882
10160
  const buildSrcs = ctx.buildFns.length > 0 && ctx.buildSrcs.every((s) => s !== null) ? ctx.buildSrcs : null;
9883
10161
  const ruleFns = /* @__PURE__ */ new Map();
@@ -9891,6 +10169,8 @@ function compileLinkable(ruleMapArg, ns, opts) {
9891
10169
  const mfDecl = ctx.mapFns.length === 0 ? "" : mfSrcs ? `const ${mfRef(ctx)} = [${mfSrcs.join(", ")}]` : `const ${mfRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "mf")}]`;
9892
10170
  const buildDecl = ctx.buildFns.length === 0 ? "" : buildSrcs ? `const ${buildRef(ctx)} = [${buildSrcs.join(", ")}]` : `const ${buildRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "build")}]`;
9893
10171
  const prelude = [
10172
+ ctx.needsLineTrack ? LINE_TRACK_DECL : "",
10173
+ ctx.needsLineSpan ? LINE_SPAN_DECL : "",
9894
10174
  ...ctx.regexDecls,
9895
10175
  ...ctx.expectedDecls,
9896
10176
  mfDecl,
@@ -9899,11 +10179,18 @@ function compileLinkable(ruleMapArg, ns, opts) {
9899
10179
  ].filter(Boolean);
9900
10180
  const wrappers = /* @__PURE__ */ new Map();
9901
10181
  for (const { key, rule, r } of perEntry) {
10182
+ const linePreamble = ctx.lineTracking ? [
10183
+ ` if (_ctx._lineStarts === undefined) _ctx = { ..._ctx, trackLines: true, _lineStarts: [0], _lineScannedTo: 0 }`,
10184
+ ` else _ctx.trackLines = true`,
10185
+ ` if (_pos > (_ctx._lineScannedTo ?? 0)) _trackLines(_ctx, input, 0, _pos)`
10186
+ ] : [];
10187
+ const resultSpan = ctx.lineTracking ? `_spanLines(_ctx, _pos, ${r.endVar})` : `{ start: _pos, end: ${r.endVar} }`;
9902
10188
  wrappers.set(key, publicRuleWrapperSource(rule, [
9903
10189
  `function(input, _pos, _ctx) {`,
10190
+ ...linePreamble,
9904
10191
  ` let pos = _pos`,
9905
10192
  ...r.stmts,
9906
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
10193
+ ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`,
9907
10194
  `}`
9908
10195
  ].join("\n"), ctx.triviaKindLabels));
9909
10196
  }
@@ -10010,12 +10297,16 @@ function buildInlineExpression(ctx, r, mapFnSources, buildSources, coverageRootR
10010
10297
  const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
10011
10298
  const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
10012
10299
  const hostReadsDecl = ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "";
10013
- const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl;
10300
+ const lineTrackDecl = ctx.needsLineTrack ? ` ${LINE_TRACK_DECL}` : "";
10301
+ const lineSpanDecl = ctx.needsLineSpan ? ` ${LINE_SPAN_DECL}` : "";
10302
+ const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl || !!lineTrackDecl || !!lineSpanDecl;
10014
10303
  if (!needsWrapper) return innerFn;
10015
10304
  const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
10016
10305
  const hoistedDecls = [
10017
10306
  emptyTlDecl,
10018
10307
  hostReadsDecl,
10308
+ lineTrackDecl,
10309
+ lineSpanDecl,
10019
10310
  ...ctx.regexDecls.map((d) => ` ${d}`),
10020
10311
  ...ctx.expectedDecls.map((d) => ` ${d}`),
10021
10312
  mfDecl,
@@ -10554,7 +10845,7 @@ function normalizeCstCollapse(collapse) {
10554
10845
  return typeof collapse === "function" ? collapse : void 0;
10555
10846
  }
10556
10847
  function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
10557
- return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children2] };
10848
+ return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children2] };
10558
10849
  }
10559
10850
  function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
10560
10851
  if (typeof typeOrOptions === "string") {
@@ -10733,7 +11024,8 @@ function materializePiece(p, trivia2, captureTerminals = false, hostMode) {
10733
11024
  const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, {
10734
11025
  ...trivia2 ? { trivia: trivia2 } : {},
10735
11026
  ...captureTerminals ? { captureTerminals: true } : {},
10736
- ...hostMode ? { hostMode } : {}
11027
+ ...hostMode ? { hostMode } : {},
11028
+ ...p.trackLines ? { trackLines: true } : {}
10737
11029
  });
10738
11030
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
10739
11031
  return pieces;
@@ -11980,6 +12272,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
11980
12272
  const macroImports = [];
11981
12273
  const allNames = /* @__PURE__ */ new Set();
11982
12274
  const importBindings = /* @__PURE__ */ new Map();
12275
+ const ordinaryImports = [];
11983
12276
  for (const stmt of body) {
11984
12277
  if (stmt.type !== "ImportDeclaration") continue;
11985
12278
  const s = stmt;
@@ -11988,12 +12281,17 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
11988
12281
  return key === "type" && a.value.value === "macro";
11989
12282
  });
11990
12283
  if (!isMacro) {
12284
+ const specifiers = [];
11991
12285
  for (const spec of s.specifiers) {
11992
12286
  if (spec.type === "ImportSpecifier") {
11993
12287
  const imported = spec.imported.name ?? spec.local.name;
11994
12288
  importBindings.set(spec.local.name, { source: s.source.value, imported });
12289
+ specifiers.push({ type: spec.type, local: spec.local.name, start: spec.start, end: spec.end });
12290
+ } else {
12291
+ specifiers.push({ type: spec.type, local: spec.local.name, start: spec.start, end: spec.end });
11995
12292
  }
11996
12293
  }
12294
+ ordinaryImports.push({ start: s.start, end: s.end, specifiers });
11997
12295
  continue;
11998
12296
  }
11999
12297
  const names = /* @__PURE__ */ new Set();
@@ -12004,6 +12302,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12004
12302
  for (const n of names) allNames.add(n);
12005
12303
  }
12006
12304
  if (macroImports.length === 0) return null;
12305
+ const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
12007
12306
  const factoryDecls = /* @__PURE__ */ new Map();
12008
12307
  for (const stmt of body) {
12009
12308
  const decl = stmt.type === "ExportNamedDeclaration" ? stmt.declaration : stmt;
@@ -12021,6 +12320,101 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12021
12320
  }
12022
12321
  }
12023
12322
  }
12323
+ const topLevelFunction = (moduleBody, name) => {
12324
+ for (const st of moduleBody) {
12325
+ const decl = st.type === "ExportNamedDeclaration" ? st.declaration : st;
12326
+ const idn = decl?.type === "FunctionDeclaration" ? decl.id : void 0;
12327
+ if (idn?.name === name) return decl ?? null;
12328
+ }
12329
+ return null;
12330
+ };
12331
+ const isParsemanMacroImport = (stmt) => {
12332
+ if (stmt.type !== "ImportDeclaration") return false;
12333
+ const s = stmt;
12334
+ return moduleAliases.has(s.source.value) && s.attributes.some((a) => {
12335
+ const key = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
12336
+ return key === "type" && a.value.value === "macro";
12337
+ });
12338
+ };
12339
+ const sourceScopeUntil = (moduleBody, source, until) => {
12340
+ const out = /* @__PURE__ */ new Map();
12341
+ for (const stmt of moduleBody) {
12342
+ if (stmt.type === "ImportDeclaration") {
12343
+ if (!isParsemanMacroImport(stmt)) return null;
12344
+ continue;
12345
+ }
12346
+ if (stmt.type === "FunctionDeclaration") continue;
12347
+ if (stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "FunctionDeclaration") continue;
12348
+ if (stmt.type === "ExportNamedDeclaration" && !stmt.declaration) {
12349
+ if (stmt.source) return null;
12350
+ continue;
12351
+ }
12352
+ const vd = unwrapVd(stmt);
12353
+ if (!vd || vd.kind !== "const") return null;
12354
+ for (const decl of vd.declarations) {
12355
+ const d = decl;
12356
+ const idn = d.id;
12357
+ const init = d.init;
12358
+ if (idn.type !== "Identifier" && idn.type !== "BindingIdentifier" || !idn.name || !init) return null;
12359
+ if (init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression") continue;
12360
+ const mapFnSources = [];
12361
+ const combi = evaluateExpr(init, out, source, mapFnSources);
12362
+ if (combi) {
12363
+ if (stmt.start < until) out.set(idn.name, { combi, mfSrcs: mapFnSources });
12364
+ continue;
12365
+ }
12366
+ const wordFactory = evaluateWordFactory(init, out, source);
12367
+ if (wordFactory) {
12368
+ if (stmt.start < until) {
12369
+ ;
12370
+ out.set(idn.name, wordFactory);
12371
+ }
12372
+ continue;
12373
+ }
12374
+ const whenFactory = evaluateWhenFactory(init, out, source);
12375
+ if (whenFactory) {
12376
+ if (stmt.start < until) {
12377
+ ;
12378
+ out.set(idn.name, whenFactory);
12379
+ }
12380
+ continue;
12381
+ }
12382
+ const combiArray = evaluateCombinatorArray(init, out, source);
12383
+ if (combiArray) {
12384
+ if (stmt.start < until) {
12385
+ ;
12386
+ out.set(idn.name, combiArray);
12387
+ }
12388
+ continue;
12389
+ }
12390
+ return null;
12391
+ }
12392
+ continue;
12393
+ }
12394
+ return out;
12395
+ };
12396
+ const usedImportedFactories = /* @__PURE__ */ new Set();
12397
+ const markUsedImportedFactories = (names) => {
12398
+ for (const name of names ?? []) usedImportedFactories.add(name);
12399
+ };
12400
+ for (const [localName, binding] of importBindings) {
12401
+ const file = resolvePrivateSourceModule(id, binding.source);
12402
+ if (!file) continue;
12403
+ const mod = parseModuleCached(file);
12404
+ if (!mod) continue;
12405
+ const localFor = exportLocalName(mod.body, binding.imported);
12406
+ const init = localFor ? topLevelInit(mod.body, localFor) ?? topLevelFunction(mod.body, localFor) : null;
12407
+ if (!init || init.type !== "ArrowFunctionExpression" && init.type !== "FunctionExpression" && init.type !== "FunctionDeclaration") continue;
12408
+ const factoryScope = sourceScopeUntil(mod.body, mod.src, init.start);
12409
+ if (!factoryScope) continue;
12410
+ factoryDecls.set(localName, {
12411
+ fn: init,
12412
+ declaredAt: -1,
12413
+ code: mod.src,
12414
+ scope: factoryScope,
12415
+ imported: true
12416
+ });
12417
+ }
12024
12418
  const scope = /* @__PURE__ */ new Map();
12025
12419
  const replacements = [];
12026
12420
  const warnings = [];
@@ -12050,6 +12444,9 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12050
12444
  }
12051
12445
  const namedFactory = factoryArgRaw.type === "Identifier" ? factoryDecls.get(factoryArgRaw.name) : void 0;
12052
12446
  const factoryArg = namedFactory !== void 0 && namedFactory.declaredAt <= init.start ? namedFactory.fn : factoryArgRaw;
12447
+ const factoryScope = namedFactory?.scope ?? scope;
12448
+ const factoryCode = namedFactory?.code ?? code;
12449
+ const importedFactory = namedFactory?.imported === true && factoryArg === namedFactory.fn ? factoryArgRaw.name : void 0;
12053
12450
  const optionsArg = optionsFirst ? arg0 : arg1;
12054
12451
  const optionValue = (name) => optionsArg?.type === "ObjectExpression" ? (optionsArg.properties ?? []).find(
12055
12452
  (p) => p.key?.name === name
@@ -12060,7 +12457,13 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12060
12457
  warn(init.start, `${label2}: rules({ hostMode }) must be the literal 'ast' or 'cst'`);
12061
12458
  return null;
12062
12459
  }
12063
- const ruleMap = evaluateParserFactory(factoryArg, scope, code, []);
12460
+ const trackLinesValue = optionValue("trackLines");
12461
+ const gTrackLines = trackLinesValue?.type === "Literal" ? trackLinesValue.value : void 0;
12462
+ if (trackLinesValue !== void 0 && typeof gTrackLines !== "boolean") {
12463
+ warn(init.start, `${label2}: rules({ trackLines }) must be a boolean literal`);
12464
+ return null;
12465
+ }
12466
+ const ruleMap = evaluateParserFactory(factoryArg, factoryScope, factoryCode, []);
12064
12467
  if (!ruleMap) {
12065
12468
  warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
12066
12469
  return null;
@@ -12085,12 +12488,26 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12085
12488
  }
12086
12489
  }
12087
12490
  }
12088
- return { ruleMap, ...gTrivia ? { trivia: gTrivia } : {}, ...gScanSkip ? { scanSkip: gScanSkip } : {} };
12491
+ if (gTrackLines === true) {
12492
+ for (const rule of ruleMap.values()) {
12493
+ if (rule && !rule._meta.isTrivia) {
12494
+ ;
12495
+ rule._meta.grammarTrackLines = true;
12496
+ }
12497
+ }
12498
+ }
12499
+ return {
12500
+ ruleMap,
12501
+ ...gTrivia ? { trivia: gTrivia } : {},
12502
+ ...gScanSkip ? { scanSkip: gScanSkip } : {},
12503
+ ...gTrackLines === true ? { trackLines: true } : {},
12504
+ ...importedFactory ? { importedFactory } : {}
12505
+ };
12089
12506
  };
12090
12507
  const compileRulesFactory = (init, label2) => {
12091
12508
  const evaluated = evaluateRulesFactory(init, label2);
12092
12509
  if (!evaluated) return null;
12093
- const compiled = compileRuleMap([...evaluated.ruleMap], { ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}, recovery, coverage: grammarCoverage });
12510
+ const compiled = compileRuleMap([...evaluated.ruleMap], { ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}, ...evaluated.trackLines ? { trackLines: true } : {}, recovery, coverage: grammarCoverage });
12094
12511
  return {
12095
12512
  replacement: compiled?.replacement ?? null,
12096
12513
  ruleMap: evaluated.ruleMap,
@@ -12098,7 +12515,9 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12098
12515
  hostBranchElided: compiled?.hostBranchElided ?? false,
12099
12516
  ...compiled?.coverageDefinitions ? { coverageDefinitions: compiled.coverageDefinitions } : {},
12100
12517
  ...evaluated.trivia ? { trivia: evaluated.trivia } : {},
12101
- ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}
12518
+ ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {},
12519
+ ...evaluated.trackLines ? { trackLines: true } : {},
12520
+ ...evaluated.importedFactory ? { importedFactory: evaluated.importedFactory } : {}
12102
12521
  };
12103
12522
  };
12104
12523
  const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
@@ -12118,7 +12537,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12118
12537
  };
12119
12538
  const isSpread = (it) => "__spreadLocal" in it;
12120
12539
  const isIR = (it) => "ir" in it && !("ruleFns" in it);
12121
- 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);
12540
+ 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);
12122
12541
  const serializeList = (list) => `[${list.map(serializeItem).join(", ")}]`;
12123
12542
  const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
12124
12543
  const withLeafMarker = (grammarExpr) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.leafComposed'), { value: true, enumerable: false })`;
@@ -12249,10 +12668,11 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12249
12668
  const localCarried = (rm, label2, composing, scanSkip) => {
12250
12669
  const entries = [...rm];
12251
12670
  const ns = nsFor(label2);
12671
+ const trackLines = entries.some(([, rule]) => rule?._meta?.grammarTrackLines === true);
12252
12672
  const ir = serializeRuleMap(entries, scanSkip);
12253
- if (ir) return { carried: [{ ns, ir }] };
12673
+ if (ir) return { carried: [{ ns, ir, ...trackLines ? { trackLines: true } : {} }] };
12254
12674
  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.`);
12255
- const p = compileLinkable(entries, ns, { ...composing ? { trivia: composing } : {}, ...scanSkip ? { scanSkip } : {}, recovery });
12675
+ const p = compileLinkable(entries, ns, { ...composing ? { trivia: composing } : {}, ...scanSkip ? { scanSkip } : {}, ...trackLines ? { trackLines: true } : {}, recovery });
12256
12676
  return p ? { carried: [p] } : null;
12257
12677
  };
12258
12678
  const argPieces = (arg, label2, composing) => {
@@ -12270,7 +12690,10 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12270
12690
  const innerArg = argPieces(inner, `${label2}_pick`);
12271
12691
  if (!innerArg) return null;
12272
12692
  try {
12273
- return { carried: pickPieces(materializeCarried(innerArg.carried, ownTrivia(inner)), names) };
12693
+ return {
12694
+ carried: pickPieces(materializeCarried(innerArg.carried, ownTrivia(inner)), names),
12695
+ ...innerArg.importedFactories ? { importedFactories: innerArg.importedFactories } : {}
12696
+ };
12274
12697
  } catch (e) {
12275
12698
  warn(arg.start, `pick(): ${e.message}`);
12276
12699
  return null;
@@ -12278,7 +12701,8 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12278
12701
  }
12279
12702
  if (isRulesCall(arg)) {
12280
12703
  const evaluated = evaluateRulesFactory(arg, label2);
12281
- return evaluated ? localCarried(evaluated.ruleMap, label2, composing, evaluated.scanSkip) : null;
12704
+ const carried = evaluated ? localCarried(evaluated.ruleMap, label2, composing, evaluated.scanSkip) : null;
12705
+ return carried ? { ...carried, ...evaluated?.importedFactory ? { importedFactories: [evaluated.importedFactory] } : {} } : null;
12282
12706
  }
12283
12707
  if (arg.type === "Identifier") {
12284
12708
  const name = arg.name;
@@ -12350,6 +12774,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12350
12774
  }
12351
12775
  const composing = composingTrivia(elements);
12352
12776
  const carried = [];
12777
+ const importedFactories = [];
12353
12778
  for (let i = 0; i < elements.length; i++) {
12354
12779
  const r = argPieces(elements[i], `compose${init.start}_${i}`, composing);
12355
12780
  if (!r) {
@@ -12357,6 +12782,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12357
12782
  return null;
12358
12783
  }
12359
12784
  carried.push(...r.carried);
12785
+ importedFactories.push(...r.importedFactories ?? []);
12360
12786
  }
12361
12787
  const detailed = once(() => carriedRuleMapsDetailed(carried));
12362
12788
  runFusedGatingDiagnostic(
@@ -12366,7 +12792,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12366
12792
  );
12367
12793
  const pieces = materializeCarried(carried, composing, false, cHostMode);
12368
12794
  try {
12369
- return { replacement: emitFusedSource(pieces), carried, ...composing ? { trivia: composing } : {} };
12795
+ return {
12796
+ replacement: emitFusedSource(pieces),
12797
+ carried,
12798
+ ...composing ? { trivia: composing } : {},
12799
+ ...importedFactories.length ? { importedFactories } : {}
12800
+ };
12370
12801
  } catch (e) {
12371
12802
  warn(init.start, `compose(): ${e.message}; falling back to runtime`);
12372
12803
  return null;
@@ -12387,10 +12818,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12387
12818
  const localArg = elements[elements.length - 1];
12388
12819
  let localRules = null;
12389
12820
  let localScanSkip;
12821
+ const importedFactories = [];
12390
12822
  if (isRulesCall(localArg)) {
12391
12823
  const evaluated = evaluateRulesFactory(localArg, `composeLeaf${init.start}`);
12392
12824
  localRules = evaluated?.ruleMap ?? null;
12393
12825
  localScanSkip = evaluated?.scanSkip;
12826
+ if (evaluated?.importedFactory) importedFactories.push(evaluated.importedFactory);
12394
12827
  } else if (localArg.type === "Identifier") {
12395
12828
  const name = localArg.name;
12396
12829
  localRules = localRuleMaps.get(name) ?? null;
@@ -12409,6 +12842,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12409
12842
  return null;
12410
12843
  }
12411
12844
  carried.push(...r.carried);
12845
+ importedFactories.push(...r.importedFactories ?? []);
12412
12846
  }
12413
12847
  try {
12414
12848
  const localNs = nsFor(`composeLeaf${init.start}`);
@@ -12434,13 +12868,15 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12434
12868
  return null;
12435
12869
  }
12436
12870
  const replacement = withLeafMarker(emitFusedSource(grammarCoverage ? recognitionPieces : [...recognitionPieces, plainLocalPiece]));
12437
- return { replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)) };
12871
+ return {
12872
+ replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)),
12873
+ ...importedFactories.length ? { importedFactories } : {}
12874
+ };
12438
12875
  } catch (e) {
12439
12876
  warn(init.start, `composeLeaf(): ${e.message}`);
12440
12877
  return null;
12441
12878
  }
12442
12879
  };
12443
- const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
12444
12880
  const refNames = /* @__PURE__ */ new Set();
12445
12881
  for (const stmt of body) {
12446
12882
  const innerVd = unwrapVd(stmt);
@@ -12524,6 +12960,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12524
12960
  const ns = nsFor(varName);
12525
12961
  const pieces = exportPrefix || compiledRules.replacement === null ? compileLinkable([...compiledRules.ruleMap], ns, {
12526
12962
  ...compiledRules.scanSkip ? { scanSkip: compiledRules.scanSkip } : {},
12963
+ ...compiledRules.trackLines ? { trackLines: true } : {},
12527
12964
  recovery
12528
12965
  }) : null;
12529
12966
  if (compiledRules.replacement === null) {
@@ -12543,9 +12980,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12543
12980
  }
12544
12981
  if (exportPrefix && pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
12545
12982
  const ir = serializeRuleMap([...compiledRules.ruleMap], compiledRules.scanSkip);
12546
- replacement = withCarriedPieces(replacement, [ir ? { ns, ir } : pieces]);
12983
+ replacement = withCarriedPieces(replacement, [ir ? { ns, ir, ...compiledRules.trackLines ? { trackLines: true } : {} } : pieces]);
12547
12984
  }
12548
12985
  replacements.push({ start: init.start, end: init.end, replacement });
12986
+ if (compiledRules.replacement !== null) {
12987
+ markUsedImportedFactories(compiledRules.importedFactory ? [compiledRules.importedFactory] : void 0);
12988
+ }
12549
12989
  continue;
12550
12990
  }
12551
12991
  if (isComposeCall(init)) {
@@ -12558,6 +12998,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12558
12998
  if (fused.trivia) localComposedTrivia.set(varName, fused.trivia);
12559
12999
  const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
12560
13000
  replacements.push({ start: init.start, end: init.end, replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)) });
13001
+ markUsedImportedFactories(fused.importedFactories);
12561
13002
  continue;
12562
13003
  }
12563
13004
  if (isComposeLeafCall(init)) {
@@ -12566,6 +13007,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12566
13007
  throw new Error(`${id}:${lineOf(init.start)} \u2014 composeLeaf() must macro-fuse; runtime composition is forbidden`);
12567
13008
  }
12568
13009
  replacements.push({ start: init.start, end: init.end, replacement: fused.replacement });
13010
+ markUsedImportedFactories(fused.importedFactories);
12569
13011
  continue;
12570
13012
  }
12571
13013
  const mapFnSources = [];
@@ -12654,6 +13096,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12654
13096
  end: stmtEnd,
12655
13097
  replacement: lines.join("\n")
12656
13098
  });
13099
+ markUsedImportedFactories(compiledRules.importedFactory ? [compiledRules.importedFactory] : void 0);
12657
13100
  }
12658
13101
  }
12659
13102
  }
@@ -12662,6 +13105,40 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12662
13105
  imp.fullyResolved = !anyUnresolved && !keepMacroImport;
12663
13106
  }
12664
13107
  const ms = new MagicString(code);
13108
+ const replacementRanges = runtimeComposeFallback ? [] : replacements.map(({ start, end }) => ({ start, end }));
13109
+ const isInsideReplacement = (start, end) => replacementRanges.some((r) => start >= r.start && end <= r.end);
13110
+ const importedBindingStillReferenced = (local) => {
13111
+ const walk2 = (node2, parent, parentKey) => {
13112
+ if (!node2 || typeof node2 !== "object") return false;
13113
+ const rec = node2;
13114
+ if (rec.type === "ImportDeclaration") return false;
13115
+ if (typeof rec.start === "number" && typeof rec.end === "number" && isInsideReplacement(rec.start, rec.end)) return false;
13116
+ if ((rec.type === "Identifier" || rec.type === "BindingIdentifier") && rec.name === local) {
13117
+ if (parentKey === "key" && (parent?.type === "ObjectProperty" || parent?.type === "Property" || parent?.type === "PropertyDefinition") && parent.computed !== true) return false;
13118
+ if (parentKey === "property" && parent?.type === "StaticMemberExpression") return false;
13119
+ if (parentKey === "property" && parent?.type === "MemberExpression" && parent.computed !== true) return false;
13120
+ return true;
13121
+ }
13122
+ for (const key of Object.keys(rec)) {
13123
+ if (key === "type" || key === "start" || key === "end") continue;
13124
+ const value = rec[key];
13125
+ if (Array.isArray(value)) {
13126
+ if (value.some((child) => walk2(child, rec, key))) return true;
13127
+ } else if (walk2(value, rec, key)) {
13128
+ return true;
13129
+ }
13130
+ }
13131
+ return false;
13132
+ };
13133
+ return body.some((stmt) => walk2(stmt));
13134
+ };
13135
+ for (const imp of ordinaryImports) {
13136
+ if (!runtimeComposeFallback && imp.specifiers.length > 0 && imp.specifiers.every(
13137
+ (spec) => spec.type === "ImportSpecifier" && usedImportedFactories.has(spec.local) && !importedBindingStillReferenced(spec.local)
13138
+ )) {
13139
+ ms.remove(imp.start, imp.end);
13140
+ }
13141
+ }
12665
13142
  if (!anyUnresolved) {
12666
13143
  for (const { start, end } of defineRemovals) ms.remove(start, end);
12667
13144
  }