parseman 0.41.0 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) 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/regex.d.ts.map +1 -1
  6. package/dist/combinators/scanTo.d.ts.map +1 -1
  7. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  8. package/dist/compiler/codegen.d.ts +3 -0
  9. package/dist/compiler/codegen.d.ts.map +1 -1
  10. package/dist/compiler/inline-build.d.ts +1 -1
  11. package/dist/compiler/inline-build.d.ts.map +1 -1
  12. package/dist/compiler/line-index.d.ts +1 -15
  13. package/dist/compiler/line-index.d.ts.map +1 -1
  14. package/dist/cst/capture-buffer.d.ts.map +1 -1
  15. package/dist/index.cjs +364 -99
  16. package/dist/index.cjs.map +4 -4
  17. package/dist/index.d.ts +2 -2
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +364 -99
  20. package/dist/index.js.map +4 -4
  21. package/dist/language-service/index.cjs +48 -2
  22. package/dist/language-service/index.cjs.map +3 -3
  23. package/dist/language-service/index.js +48 -2
  24. package/dist/language-service/index.js.map +3 -3
  25. package/dist/line-index.d.ts +41 -0
  26. package/dist/line-index.d.ts.map +1 -0
  27. package/dist/plugin/index.cjs +334 -93
  28. package/dist/plugin/index.cjs.map +4 -4
  29. package/dist/plugin/index.js +334 -93
  30. package/dist/plugin/index.js.map +4 -4
  31. package/dist/recovery/scan.d.ts.map +1 -1
  32. package/dist/regex/first-set.d.ts.map +1 -1
  33. package/dist/run/index.cjs +47 -1
  34. package/dist/run/index.cjs.map +3 -3
  35. package/dist/run/index.d.ts +3 -2
  36. package/dist/run/index.d.ts.map +1 -1
  37. package/dist/run/index.js +47 -1
  38. package/dist/run/index.js.map +3 -3
  39. package/dist/spec/index.cjs +170 -50
  40. package/dist/spec/index.cjs.map +4 -4
  41. package/dist/spec/index.js +170 -50
  42. package/dist/spec/index.js.map +4 -4
  43. package/dist/types.d.ts +12 -0
  44. package/dist/types.d.ts.map +1 -1
  45. package/dist/version.d.ts +1 -1
  46. 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) {
@@ -3072,13 +3195,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3072
3195
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3073
3196
  const nodeType = def.type ?? missingInferredType();
3074
3197
  const cstOutput = cstOutputHost(ctx.build);
3198
+ const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3075
3199
  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 };
3200
+ 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 };
3201
+ 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
3202
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3079
3203
  pushCstChild(ctx, built, rawEntry);
3080
3204
  }
3081
- return { ok: true, value: built, span: r.span };
3205
+ return { ok: true, value: built, span };
3082
3206
  }
3083
3207
  };
3084
3208
  }
@@ -3149,38 +3273,24 @@ function field(name, combinator) {
3149
3273
  };
3150
3274
  }
3151
3275
 
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;
3276
+ // src/combinators/grammar.ts
3277
+ function annotateResultLines(result, index) {
3278
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
3279
+ const errors = annotated.errors;
3280
+ if (errors) {
3281
+ ;
3282
+ annotated.errors = errors.map((error) => ({
3283
+ ...error,
3284
+ span: annotateSpan(error.span, index)
3285
+ }));
3168
3286
  }
3169
- return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
3287
+ return annotated;
3170
3288
  }
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
- };
3289
+ function createParseLineContext(input, pos) {
3290
+ const lineIndex = createLineIndex();
3291
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
3292
+ return { lineIndex, lineScannedTo: pos };
3181
3293
  }
3182
-
3183
- // src/combinators/grammar.ts
3184
3294
  function parser(opts, root) {
3185
3295
  const clearTrivia = opts.trivia === null;
3186
3296
  return {
@@ -3196,9 +3306,13 @@ function parser(opts, root) {
3196
3306
  },
3197
3307
  parse(input, pos, _ctx) {
3198
3308
  const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
3309
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
3310
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
3199
3311
  const ctx = {
3200
3312
  ..._ctx,
3201
3313
  trackLines,
3314
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
3315
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
3202
3316
  // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
3203
3317
  ...clearTrivia ? {
3204
3318
  trivia: void 0,
@@ -3216,11 +3330,10 @@ function parser(opts, root) {
3216
3330
  ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
3217
3331
  };
3218
3332
  const result = root.parse(input, pos ?? 0, ctx);
3219
- if (trackLines) {
3220
- const idx = buildLineIndex(input);
3221
- return { ...result, span: annotateSpan(result.span, idx) };
3333
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
3334
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
3222
3335
  }
3223
- return result;
3336
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
3224
3337
  }
3225
3338
  };
3226
3339
  }
@@ -3324,7 +3437,7 @@ function leaf(root, fn) {
3324
3437
  }
3325
3438
 
3326
3439
  // src/version.ts
3327
- var PARSEMAN_VERSION = "0.41.0";
3440
+ var PARSEMAN_VERSION = "0.42.0";
3328
3441
 
3329
3442
  // src/cst/host-mode.ts
3330
3443
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -5008,7 +5121,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5008
5121
  let cur = pos;
5009
5122
  const skip3 = resolveScanSkip(explicitSkip, raw, ctx);
5010
5123
  const probeCtx = {
5011
- trackLines: ctx.trackLines,
5124
+ trackLines: false,
5012
5125
  state: ctx.state,
5013
5126
  ...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
5014
5127
  };
@@ -5021,6 +5134,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5021
5134
  while (cur < input.length) {
5022
5135
  const s = sentinel.parse(input, cur, probeCtx);
5023
5136
  if (s.ok) {
5137
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
5024
5138
  emit2(cur);
5025
5139
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
5026
5140
  }
@@ -5036,6 +5150,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5036
5150
  if (!advanced) cur++;
5037
5151
  }
5038
5152
  if (orEOF) {
5153
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
5039
5154
  emit2(cur);
5040
5155
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
5041
5156
  }
@@ -6474,8 +6589,8 @@ function analyzeMkInlineBuild(def) {
6474
6589
  if (looseMkBuildRe(def.type).test(src)) return def.type;
6475
6590
  return null;
6476
6591
  }
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 }`;
6592
+ function emitInlineMkNodeExpr(type, chV, rawV, spanExpr, tlV) {
6593
+ return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: ${spanExpr}, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`;
6479
6594
  }
6480
6595
 
6481
6596
  // src/compiler/inline-callback.ts
@@ -6657,6 +6772,8 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
6657
6772
  }
6658
6773
  }
6659
6774
  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 }";
6775
+ 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 }";
6776
+ 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
6777
  var _loweringSink = null;
6661
6778
  function beginLoweringCapture() {
6662
6779
  _loweringSink = /* @__PURE__ */ new Set();
@@ -6805,14 +6922,46 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
6805
6922
  if (ctx.capAsTrivia) return [];
6806
6923
  const i = ind(ctx);
6807
6924
  const lf = v(ctx, "_lf");
6925
+ const spanExpr = emitSpanExpr(ctx, startExpr, endExpr);
6808
6926
  return [
6809
6927
  `${i}if (_ctx._cstLeaves || _ctx._cstRawChildren) {`,
6810
- `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } }`,
6928
+ `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: ${spanExpr} }`,
6811
6929
  `${i} if (_ctx._cstLeaves) _ctx._cstLeaves.push(${lf})`,
6812
6930
  `${i} if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(${lf})`,
6813
6931
  `${i}}`
6814
6932
  ];
6815
6933
  }
6934
+ function emitSpanExpr(ctx, startExpr, endExpr) {
6935
+ if (!ctx.lineTracking) return `{ start: ${startExpr}, end: ${endExpr} }`;
6936
+ ctx.needsLineSpan = true;
6937
+ return `_spanLines(_ctx, ${startExpr}, ${endExpr})`;
6938
+ }
6939
+ function emitLineTrack(ctx, startExpr, endExpr) {
6940
+ if (!ctx.lineTracking) return [];
6941
+ ctx.needsLineTrack = true;
6942
+ return [`${ind(ctx)}_trackLines(_ctx, input, ${startExpr}, ${endExpr})`];
6943
+ }
6944
+ function emitLiteralLineTrack(ctx, startExpr, value) {
6945
+ if (!ctx.lineTracking) return [];
6946
+ const lineDeltas = [];
6947
+ for (let i = 0; i < value.length; i++) {
6948
+ if (value.charCodeAt(i) === 10) lineDeltas.push(i + 1);
6949
+ }
6950
+ if (lineDeltas.length === 0) return [];
6951
+ const fromV = v(ctx, "_ltFrom");
6952
+ const endExpr = value.length === 0 ? startExpr : `${startExpr} + ${value.length}`;
6953
+ return [
6954
+ `${ind(ctx)}const ${fromV} = _ctx._lineScannedTo ?? 0`,
6955
+ `${ind(ctx)}if (${endExpr} > ${fromV}) {`,
6956
+ `${ind(ctx)} for (let i = ${fromV}; i < ${startExpr}; i++) if (input.charCodeAt(i) === 10) _ctx._lineStarts.push(i + 1)`,
6957
+ ...lineDeltas.map((delta) => {
6958
+ const lineStart = `${startExpr} + ${delta}`;
6959
+ return `${ind(ctx)} if (${lineStart} > ${fromV}) _ctx._lineStarts.push(${lineStart})`;
6960
+ }),
6961
+ `${ind(ctx)} _ctx._lineScannedTo = ${endExpr}`,
6962
+ `${ind(ctx)}}`
6963
+ ];
6964
+ }
6816
6965
  function ensureRegexDecl(ctx, source, flags) {
6817
6966
  const f = "y" + flags.replace(/[gy]/g, "");
6818
6967
  const key = `${source}/${f}`;
@@ -7197,6 +7346,7 @@ function emitLit(def, ctx, pos) {
7197
7346
  );
7198
7347
  }
7199
7348
  const endVar = len === 0 ? pos : `${pos} + ${len}`;
7349
+ stmts.push(...emitLiteralLineTrack(ctx, pos, value));
7200
7350
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7201
7351
  return { stmts, valueVar: vv, endVar };
7202
7352
  }
@@ -7276,7 +7426,7 @@ function emitKeywords(def, ctx, pos) {
7276
7426
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7277
7427
  return { stmts, valueVar: vv, endVar };
7278
7428
  }
7279
- function emitRegex(def, ctx, pos) {
7429
+ function emitRegex(def, ctx, pos, canMatchNewline = true) {
7280
7430
  const expectedStr = JSON.stringify(`/${def.source}/`);
7281
7431
  const shape = scanShapeFromRegex(def.source, def.flags);
7282
7432
  if (shape) {
@@ -7289,7 +7439,11 @@ function emitRegex(def, ctx, pos) {
7289
7439
  fresh: (prefix) => v(ctx, prefix)
7290
7440
  });
7291
7441
  if (scanned) {
7292
- const stmts2 = [...scanned.stmts, ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)];
7442
+ const stmts2 = [
7443
+ ...scanned.stmts,
7444
+ ...canMatchNewline ? emitLineTrack(ctx, pos, scanned.endVar) : [],
7445
+ ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)
7446
+ ];
7293
7447
  return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
7294
7448
  }
7295
7449
  }
@@ -7311,6 +7465,7 @@ function emitRegex(def, ctx, pos) {
7311
7465
  `${ind(ctx)}const ${vv} = ${mv}[0]`
7312
7466
  ];
7313
7467
  const endVar = `${pos} + ${vv}.length`;
7468
+ if (canMatchNewline) stmts.push(...emitLineTrack(ctx, pos, endVar));
7314
7469
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7315
7470
  return { stmts, valueVar: vv, endVar };
7316
7471
  }
@@ -7348,7 +7503,8 @@ function emitSeqValues(def, ctx, pos) {
7348
7503
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
7349
7504
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
7350
7505
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7351
- `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`
7506
+ `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`,
7507
+ ...emitLineTrack(ctx, curV, scanEndV)
7352
7508
  );
7353
7509
  const r2 = emit(def.parsers[i], ctx, scanEndV);
7354
7510
  stmts.push(...r2.stmts);
@@ -7366,7 +7522,8 @@ function emitSeqValues(def, ctx, pos) {
7366
7522
  const capArg = ctx.noHoist ? "0" : "_ctx._triviaLog !== undefined ? 2 : 0";
7367
7523
  stmts.push(
7368
7524
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7369
- `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`
7525
+ `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`,
7526
+ ...emitLineTrack(ctx, curV, scanEndV)
7370
7527
  );
7371
7528
  const r2 = emit(def.parsers[i], ctx, scanEndV);
7372
7529
  stmts.push(...r2.stmts);
@@ -8066,14 +8223,15 @@ function emitMany(def, ctx, pos) {
8066
8223
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8067
8224
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8068
8225
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8069
- `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`
8226
+ `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8227
+ ...emitLineTrack(ctx, curV, npV)
8070
8228
  );
8071
8229
  itemPos = npV;
8072
8230
  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
8231
  } else {
8074
8232
  const trivFn = ensureTriviaFn(ctx);
8075
8233
  const npV = v(ctx, "_np");
8076
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`);
8234
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, npV));
8077
8235
  itemPos = npV;
8078
8236
  }
8079
8237
  }
@@ -8250,7 +8408,8 @@ function emitSepBy(_p, def, ctx, pos) {
8250
8408
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8251
8409
  `${ind(ctx)}const ${markLv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8252
8410
  `${ind(ctx)}const ${markFld} = _ctx._fields ? _ctx._fields.length : 0`,
8253
- `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`
8411
+ `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8412
+ ...emitLineTrack(ctx, curV, spV)
8254
8413
  );
8255
8414
  sepAtPos = spV;
8256
8415
  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 +8419,7 @@ function emitSepBy(_p, def, ctx, pos) {
8260
8419
  const post = postSepMarks();
8261
8420
  stmts.push(...post.decl);
8262
8421
  const npV = v(ctx, "_np");
8263
- stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
8422
+ stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`, ...emitLineTrack(ctx, sepEnd, npV));
8264
8423
  const next = emitFallible(def.parser, ctx, npV, true);
8265
8424
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8266
8425
  stmts.push(
@@ -8272,13 +8431,13 @@ function emitSepBy(_p, def, ctx, pos) {
8272
8431
  } else {
8273
8432
  const trivFn = ensureTriviaFn(ctx);
8274
8433
  const spV = v(ctx, "_sp");
8275
- stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
8434
+ stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, spV));
8276
8435
  sepAtPos = spV;
8277
8436
  const sep2 = emitFallible(def.separator, ctx, sepAtPos, true);
8278
8437
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep2;
8279
8438
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
8280
8439
  const npV = v(ctx, "_np");
8281
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
8440
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`, ...emitLineTrack(ctx, sepEnd, npV));
8282
8441
  const next = emitFallible(def.parser, ctx, npV, true);
8283
8442
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8284
8443
  stmts.push(
@@ -8365,6 +8524,7 @@ function emitScanTo(def, ctx, pos) {
8365
8524
  }
8366
8525
  const valV = v(ctx);
8367
8526
  stmts.push(`${ind(ctx)}const ${valV} = input.slice(${pos}, ${curV})`);
8527
+ stmts.push(...emitLineTrack(ctx, pos, curV));
8368
8528
  if (ctx.capturing) {
8369
8529
  const cap = reindentStmts(emitLeafCapture(ctx, valV, pos, curV), ctx.indent + 1);
8370
8530
  stmts.push(`${ind(ctx)}if (${curV} > ${pos}) {`, ...cap, `${ind(ctx)}}`);
@@ -8508,7 +8668,7 @@ function tokenNullableRegex(def) {
8508
8668
  }
8509
8669
  function emitToken(def, ctx, pos) {
8510
8670
  const lowered = tokenNullableRegex(def);
8511
- if (lowered) return emitRegex(lowered, ctx, pos);
8671
+ if (lowered) return emitRegex(lowered, ctx, pos, def.parser._meta.canMatchNewline);
8512
8672
  const savedTrivia = ctx.activeTrivia;
8513
8673
  const savedKindLabels = ctx.triviaKindLabels;
8514
8674
  ctx.activeTrivia = void 0;
@@ -8658,7 +8818,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8658
8818
  stmts.push(...innerStmts);
8659
8819
  if (def.trailingTrivia === true && ctx.activeTrivia) {
8660
8820
  const triviaFn = ensureTriviaFn(ctx);
8661
- stmts.push(`${i}const ${endVar} = ${okVar} ? ${triviaFn}(input, ${innerEndVar}, _ctx, 1) : ${innerEndVar}`);
8821
+ stmts.push(`${i}const ${endVar} = ${okVar} ? ${triviaFn}(input, ${innerEndVar}, _ctx, 1) : ${innerEndVar}`, ...emitLineTrack(ctx, innerEndVar, endVar));
8662
8822
  }
8663
8823
  if (sf && fArr) {
8664
8824
  stmts.push(`${i}const ${fArr} = _ctx._fields`);
@@ -8698,8 +8858,11 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8698
8858
  );
8699
8859
  }
8700
8860
  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})`;
8861
+ const nodeSpanExpr = emitSpanExpr(ctx, pos, endVar);
8862
+ const nodeSpanV = ctx.lineTracking ? v(ctx, "_nspan") : nodeSpanExpr;
8863
+ if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
8864
+ 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})`;
8865
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
8703
8866
  const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
8704
8867
  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
8868
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
@@ -8709,7 +8872,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8709
8872
  `${i}const ${ndV} = (${profileRecognizer} || ${profileCapture}) ? undefined : (${finalExpr})`,
8710
8873
  `${i}if (!(${profileRecognizer})) {`,
8711
8874
  `${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} } })`,
8875
+ `${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
8876
  `${i}}`
8714
8877
  );
8715
8878
  return { stmts, valueVar: ndV, endVar };
@@ -8725,7 +8888,8 @@ function emitRuntimeFallback(parser2, ctx, pos) {
8725
8888
  `${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
8726
8889
  ...emitIfFail(ctx, `!${rv}.ok`, failStmt),
8727
8890
  `${ind(ctx)}const ${vv} = ${rv}.value`,
8728
- `${ind(ctx)}const ${ev} = ${rv}.span.end`
8891
+ `${ind(ctx)}const ${ev} = ${rv}.span.end`,
8892
+ ...parser2._meta.canMatchNewline ? emitLineTrack(ctx, pos, ev) : []
8729
8893
  ];
8730
8894
  return { stmts, valueVar: vv, endVar: ev };
8731
8895
  }
@@ -8791,7 +8955,8 @@ function emitRecover(def, ctx, pos) {
8791
8955
  `${ind0} if (${sentOk}) break`,
8792
8956
  `${ind0} ${scanV}++`,
8793
8957
  `${ind0} }`,
8794
- `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(deriveExpected(def.parser))} }`,
8958
+ ...emitLineTrack(ctx, pos, scanV).map((s) => `${ind0} ${s.trim()}`),
8959
+ `${ind0} const ${errV} = { _tag: 'parseError', span: ${emitSpanExpr(ctx, pos, scanV)}, expected: ${JSON.stringify(deriveExpected(def.parser))} }`,
8795
8960
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
8796
8961
  `${ind0} ${valVar} = ${errV}`,
8797
8962
  `${ind0} ${endVar} = ${scanV}`,
@@ -8807,7 +8972,7 @@ function emitExpect(def, ctx, pos) {
8807
8972
  const stmts = [
8808
8973
  ...innerStmts,
8809
8974
  `${ind0}if (!${okVar}) {`,
8810
- `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${pos} }, expected: ${JSON.stringify(def.expected)} }`,
8975
+ `${ind0} const ${errV} = { _tag: 'parseError', span: ${emitSpanExpr(ctx, pos, pos)}, expected: ${JSON.stringify(def.expected)} }`,
8811
8976
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
8812
8977
  // Embed the missing-token error as a `parseError` CST child, mirroring the
8813
8978
  // interpreter (expect.ts) and the list-recovery emit sites — guarded on
@@ -8897,7 +9062,7 @@ function emitDispatch(p, ctx, pos) {
8897
9062
  case "literal":
8898
9063
  return emitLit(def, ctx, pos);
8899
9064
  case "regex":
8900
- return emitRegex(def, ctx, pos);
9065
+ return emitRegex(def, ctx, pos, p._meta.canMatchNewline);
8901
9066
  case "keywords":
8902
9067
  return emitKeywords(def, ctx, pos);
8903
9068
  case "sequence":
@@ -9147,6 +9312,20 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
9147
9312
  return false;
9148
9313
  }
9149
9314
  }
9315
+ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9316
+ if (seen.has(p)) return false;
9317
+ seen.add(p);
9318
+ const d = p._def;
9319
+ if (d.tag === "grammar" && d.trackLines) return true;
9320
+ if (d.tag === "lazy") {
9321
+ try {
9322
+ return hasLineTrackingDef(d.thunk(), seen);
9323
+ } catch {
9324
+ return false;
9325
+ }
9326
+ }
9327
+ return childrenOf2(d).some((child) => hasLineTrackingDef(child, seen));
9328
+ }
9150
9329
  function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9151
9330
  if (seen.has(p)) return false;
9152
9331
  seen.add(p);
@@ -9514,6 +9693,7 @@ function compile(combinator, mapFnSources, opts) {
9514
9693
  capturing: hasNodeDef(combinator),
9515
9694
  recovery: opts?.recovery ?? false,
9516
9695
  hostMode: opts?.hostMode ?? grammarHostMode ?? "ast",
9696
+ lineTracking: opts?.trackLines === true || hasLineTrackingDef(combinator),
9517
9697
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
9518
9698
  lazyUsage: analyzeLazyUsage(combinator),
9519
9699
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
@@ -9537,9 +9717,13 @@ function compile(combinator, mapFnSources, opts) {
9537
9717
  const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
9538
9718
  const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
9539
9719
  const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
9720
+ const lineTrackDecls = ctx.needsLineTrack ? [LINE_TRACK_DECL] : [];
9721
+ const lineSpanDecls = ctx.needsLineSpan ? [LINE_SPAN_DECL] : [];
9540
9722
  const source = [
9541
9723
  ...emptyTlDecls,
9542
9724
  ...hostReadsDecls,
9725
+ ...lineTrackDecls,
9726
+ ...lineSpanDecls,
9543
9727
  ...ctx.regexDecls,
9544
9728
  ...ctx.expectedDecls,
9545
9729
  "",
@@ -9552,6 +9736,8 @@ function compile(combinator, mapFnSources, opts) {
9552
9736
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
9553
9737
  ...emptyTlDecls,
9554
9738
  ...hostReadsDecls,
9739
+ ...lineTrackDecls,
9740
+ ...lineSpanDecls,
9555
9741
  ...ctx.regexDecls,
9556
9742
  ...ctx.expectedDecls,
9557
9743
  ...namedPrelude,
@@ -9570,6 +9756,25 @@ function compile(combinator, mapFnSources, opts) {
9570
9756
  trackLines: false,
9571
9757
  ...ctx.triviaKindLabels ? { triviaKindLabels: ctx.triviaKindLabels } : {}
9572
9758
  };
9759
+ const annotateWithTrackedLines = (result, lineStarts) => {
9760
+ const index = normalizeLineIndex({ lineStarts });
9761
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
9762
+ const errors = annotated.errors;
9763
+ if (errors) {
9764
+ ;
9765
+ annotated.errors = errors.map((error) => ({
9766
+ ...error,
9767
+ span: annotateSpan(error.span, index)
9768
+ }));
9769
+ }
9770
+ return annotated;
9771
+ };
9772
+ const trackedCtx = (base, input, pos) => {
9773
+ if (!ctx.lineTracking) return base;
9774
+ const lineIndex = { lineStarts: [0] };
9775
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
9776
+ return { ...base, trackLines: true, _lineStarts: lineIndex.lineStarts, _lineScannedTo: pos };
9777
+ };
9573
9778
  const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
9574
9779
  const effectiveSources = mapFnSources ?? derivedSrcs;
9575
9780
  const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
@@ -9577,17 +9782,46 @@ function compile(combinator, mapFnSources, opts) {
9577
9782
  const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
9578
9783
  const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
9579
9784
  const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources, coverageRootRuleId) : null;
9580
- return {
9785
+ const compiledMeta = {
9581
9786
  source,
9582
9787
  inlineExpression,
9583
9788
  ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions },
9584
- ...gatingReport === void 0 ? {} : { gating: gatingReport },
9789
+ ...gatingReport === void 0 ? {} : { gating: gatingReport }
9790
+ };
9791
+ if (!ctx.lineTracking) {
9792
+ return {
9793
+ ...compiledMeta,
9794
+ parse(input, pos = 0) {
9795
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
9796
+ },
9797
+ parseWithContext(input, parseCtx, pos = 0) {
9798
+ assertHostModeCompatible(ctx.hostMode ?? "ast", parseCtx.build, !!ctx.hostBranchElided);
9799
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9800
+ },
9801
+ // Note: collects expect() errors via _errors. Unlike interpreter
9802
+ // parse({recover:true}) it does NOT populate furthestFail — the compiled path
9803
+ // inlines failures for throughput and deliberately skips _probe bookkeeping.
9804
+ // Callers wanting a furthest-position diagnostic detect unconsumed input
9805
+ // (span.end < input.length) instead, which is mode-agnostic.
9806
+ parseWithErrors(input, pos = 0) {
9807
+ const errors = [];
9808
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
9809
+ return { ...result, errors };
9810
+ }
9811
+ };
9812
+ }
9813
+ return {
9814
+ ...compiledMeta,
9585
9815
  parse(input, pos = 0) {
9586
- return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
9816
+ const parseCtx = trackedCtx(defaultCtx, input, pos);
9817
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9818
+ return annotateWithTrackedLines(result, parseCtx._lineStarts);
9587
9819
  },
9588
9820
  parseWithContext(input, parseCtx, pos = 0) {
9589
9821
  assertHostModeCompatible(ctx.hostMode ?? "ast", parseCtx.build, !!ctx.hostBranchElided);
9590
- return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9822
+ const activeCtx = trackedCtx(parseCtx, input, pos);
9823
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, activeCtx);
9824
+ return activeCtx.trackLines ? annotateWithTrackedLines(result, activeCtx._lineStarts) : result;
9591
9825
  },
9592
9826
  // Note: collects expect() errors via _errors. Unlike interpreter
9593
9827
  // parse({recover:true}) it does NOT populate furthestFail — the compiled path
@@ -9596,8 +9830,9 @@ function compile(combinator, mapFnSources, opts) {
9596
9830
  // (span.end < input.length) instead, which is mode-agnostic.
9597
9831
  parseWithErrors(input, pos = 0) {
9598
9832
  const errors = [];
9599
- const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
9600
- return { ...result, errors };
9833
+ const parseCtx = trackedCtx({ ...defaultCtx, _errors: errors }, input, pos);
9834
+ const result = { ...fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx), errors };
9835
+ return annotateWithTrackedLines(result, parseCtx._lineStarts);
9601
9836
  }
9602
9837
  };
9603
9838
  }
@@ -9681,6 +9916,8 @@ function compileRuleMap(ruleMap, opts) {
9681
9916
  const hoistedDecls = [
9682
9917
  ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
9683
9918
  ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "",
9919
+ ctx.needsLineTrack ? ` ${LINE_TRACK_DECL}` : "",
9920
+ ctx.needsLineSpan ? ` ${LINE_SPAN_DECL}` : "",
9684
9921
  ...ctx.regexDecls.map((d) => ` ${d}`),
9685
9922
  ...ctx.expectedDecls.map((d) => ` ${d}`),
9686
9923
  mfDecl,
@@ -10010,12 +10247,16 @@ function buildInlineExpression(ctx, r, mapFnSources, buildSources, coverageRootR
10010
10247
  const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
10011
10248
  const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
10012
10249
  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;
10250
+ const lineTrackDecl = ctx.needsLineTrack ? ` ${LINE_TRACK_DECL}` : "";
10251
+ const lineSpanDecl = ctx.needsLineSpan ? ` ${LINE_SPAN_DECL}` : "";
10252
+ const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl || !!lineTrackDecl || !!lineSpanDecl;
10014
10253
  if (!needsWrapper) return innerFn;
10015
10254
  const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
10016
10255
  const hoistedDecls = [
10017
10256
  emptyTlDecl,
10018
10257
  hostReadsDecl,
10258
+ lineTrackDecl,
10259
+ lineSpanDecl,
10019
10260
  ...ctx.regexDecls.map((d) => ` ${d}`),
10020
10261
  ...ctx.expectedDecls.map((d) => ` ${d}`),
10021
10262
  mfDecl,
@@ -10554,7 +10795,7 @@ function normalizeCstCollapse(collapse) {
10554
10795
  return typeof collapse === "function" ? collapse : void 0;
10555
10796
  }
10556
10797
  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] };
10798
+ return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children2] };
10558
10799
  }
10559
10800
  function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
10560
10801
  if (typeof typeOrOptions === "string") {