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
package/dist/index.js CHANGED
@@ -237,6 +237,87 @@ function failAt(ctx, expected, pos) {
237
237
  return r;
238
238
  }
239
239
 
240
+ // src/line-index.ts
241
+ function createLineIndex() {
242
+ return { lineStarts: [0] };
243
+ }
244
+ function buildLineIndex(input) {
245
+ const lineStarts = [0];
246
+ for (let i = 0; i < input.length; i++) {
247
+ if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
248
+ }
249
+ return { lineStarts };
250
+ }
251
+ function recordLineRange(index, input, start, end) {
252
+ const lineStarts = index.lineStarts;
253
+ for (let i = start; i < end; i++) {
254
+ if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
255
+ }
256
+ }
257
+ function recordLineRangeFromContext(ctx, input, _startHint, end) {
258
+ if (!ctx.trackLines) return;
259
+ const from = ctx._lineScannedTo ?? 0;
260
+ if (end <= from) return;
261
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
262
+ if (!index) return;
263
+ recordLineRange(index, input, from, end);
264
+ ctx._lineScannedTo = end;
265
+ }
266
+ function normalizeLineIndex(index) {
267
+ const starts = index.lineStarts;
268
+ starts.sort((a, b) => a - b);
269
+ let write = 0;
270
+ for (let read = 0; read < starts.length; read++) {
271
+ const value = starts[read];
272
+ if (write === 0 || starts[write - 1] !== value) starts[write++] = value;
273
+ }
274
+ starts.length = write;
275
+ if (starts.length === 0 || starts[0] !== 0) starts.unshift(0);
276
+ return index;
277
+ }
278
+ function offsetToLineCol(index, offset) {
279
+ const { lineStarts } = index;
280
+ let lo = 0;
281
+ let hi = lineStarts.length - 1;
282
+ while (lo < hi) {
283
+ const mid = lo + hi + 1 >> 1;
284
+ if (lineStarts[mid] <= offset) lo = mid;
285
+ else hi = mid - 1;
286
+ }
287
+ return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
288
+ }
289
+ function annotateSpan(span, index) {
290
+ const s = offsetToLineCol(index, span.start);
291
+ const e = offsetToLineCol(index, span.end);
292
+ return {
293
+ ...span,
294
+ startLine: s.line,
295
+ startColumn: s.col,
296
+ endLine: e.line,
297
+ endColumn: e.col
298
+ };
299
+ }
300
+ function annotateSpanFromLineContext(span, ctx) {
301
+ if (!ctx.trackLines) return span;
302
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
303
+ return index ? annotateSpan(span, index) : span;
304
+ }
305
+ function isSpan(value) {
306
+ return !!value && typeof value === "object" && typeof value.start === "number" && typeof value.end === "number";
307
+ }
308
+ function annotateTreeSpans(value, index, seen = /* @__PURE__ */ new WeakSet()) {
309
+ if (!value || typeof value !== "object") return value;
310
+ if (seen.has(value)) return value;
311
+ seen.add(value);
312
+ const obj = value;
313
+ if (isSpan(obj.span)) obj.span = annotateSpan(obj.span, index);
314
+ for (const key of Object.keys(obj)) {
315
+ const child = obj[key];
316
+ if (child && typeof child === "object") annotateTreeSpans(child, index, seen);
317
+ }
318
+ return value;
319
+ }
320
+
240
321
  // src/cst/capture-buffer.ts
241
322
  var EMPTY = [];
242
323
  var EMPTY_TL = [];
@@ -278,7 +359,12 @@ function endCstNodeCapture(ctx, saved) {
278
359
  return materialized;
279
360
  }
280
361
  function pushCstLeaf(ctx, leaf2) {
281
- pushCstChild(ctx, leaf2, leaf2);
362
+ if (!ctx.trackLines) {
363
+ pushCstChild(ctx, leaf2, leaf2);
364
+ return;
365
+ }
366
+ const annotated = leaf2 && typeof leaf2 === "object" && leaf2.span ? { ...leaf2, span: annotateSpanFromLineContext(leaf2.span, ctx) } : leaf2;
367
+ pushCstChild(ctx, annotated, annotated);
282
368
  }
283
369
  function pushCstChild(ctx, built, rawEntry) {
284
370
  const b = ctx._cstBuf;
@@ -412,6 +498,7 @@ function literal(value, opts = {}) {
412
498
  canMatchNewline: value.includes("\n"),
413
499
  isTrivia: false
414
500
  };
501
+ const canMatchNewline = meta.canMatchNewline;
415
502
  if (caseInsensitive) {
416
503
  const upper = value.toUpperCase();
417
504
  const lower = value.toLowerCase();
@@ -428,6 +515,7 @@ function literal(value, opts = {}) {
428
515
  return function parse3(input, pos, ctx) {
429
516
  if (input.charCodeAt(pos) === code) {
430
517
  const span = { start: pos, end: pos + 1 };
518
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
431
519
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
432
520
  return { ok: true, value, span };
433
521
  }
@@ -441,6 +529,7 @@ function literal(value, opts = {}) {
441
529
  const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
442
530
  if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
443
531
  const span = { start: pos, end };
532
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
444
533
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
445
534
  return { ok: true, value: matchedValue, span };
446
535
  }
@@ -608,6 +697,7 @@ function regex(pattern, flags = "") {
608
697
  if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
609
698
  const value = input.slice(pos, scanEnd);
610
699
  const span2 = { start: pos, end: scanEnd };
700
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
611
701
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
612
702
  return { ok: true, value, span: span2 };
613
703
  }
@@ -617,6 +707,7 @@ function regex(pattern, flags = "") {
617
707
  return failAt(ctx, [`/${source}/`], pos);
618
708
  }
619
709
  const span = { start: pos, end: pos + m[0].length };
710
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
620
711
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
621
712
  return { ok: true, value: m[0], span };
622
713
  }
@@ -755,8 +846,8 @@ function parseRegex(src) {
755
846
  }
756
847
  if (src[k] !== "]") throw BAIL;
757
848
  i = k + 1;
758
- if (negated) return charNode(any(), true);
759
849
  const ranges = parseClassRanges(body);
850
+ if (negated) return charNode(any(), !ranges || !rangesHaveNewline(ranges));
760
851
  if (!ranges) return charNode(any(), true);
761
852
  return charNode(rangesToSet(ranges), rangesHaveNewline(ranges));
762
853
  };
@@ -852,7 +943,7 @@ function firstSet(n) {
852
943
  }
853
944
  }
854
945
  }
855
- function firstCanBeNewline(n) {
946
+ function canConsumeNewline(n) {
856
947
  switch (n.t) {
857
948
  case "char":
858
949
  return n.nl;
@@ -861,16 +952,11 @@ function firstCanBeNewline(n) {
861
952
  case "empty":
862
953
  return false;
863
954
  case "rep":
864
- return firstCanBeNewline(n.node);
955
+ return canConsumeNewline(n.node);
865
956
  case "alt":
866
- return n.arms.some(firstCanBeNewline);
867
- case "seq": {
868
- for (const part of n.parts) {
869
- if (firstCanBeNewline(part)) return true;
870
- if (!nullable(part)) break;
871
- }
872
- return false;
873
- }
957
+ return n.arms.some(canConsumeNewline);
958
+ case "seq":
959
+ return n.parts.some(canConsumeNewline);
874
960
  }
875
961
  }
876
962
  function firstSetFromRegex(source) {
@@ -880,8 +966,8 @@ function firstSetFromRegex(source) {
880
966
  } catch {
881
967
  return { firstSet: any(), canMatchNewline: true };
882
968
  }
883
- if (nullable(ast)) return { firstSet: any(), canMatchNewline: true };
884
- return { firstSet: firstSet(ast), canMatchNewline: firstCanBeNewline(ast) };
969
+ if (nullable(ast)) return { firstSet: any(), canMatchNewline: canConsumeNewline(ast) };
970
+ return { firstSet: firstSet(ast), canMatchNewline: canConsumeNewline(ast) };
885
971
  }
886
972
 
887
973
  // src/combinators/case-fold.ts
@@ -1043,7 +1129,9 @@ function firstSetSentinel(fs) {
1043
1129
  function recoverScan(input, from, ctx, sync, expected) {
1044
1130
  let scanPos = from;
1045
1131
  while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1046
- const error = { _tag: "parseError", span: { start: from, end: scanPos }, expected };
1132
+ const span = { start: from, end: scanPos };
1133
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
1134
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1047
1135
  ctx._errors?.push(error);
1048
1136
  return { error, end: scanPos };
1049
1137
  }
@@ -1146,7 +1234,8 @@ function expect(combinator, label2) {
1146
1234
  parse(input, pos, ctx) {
1147
1235
  const result = combinator.parse(input, pos, ctx);
1148
1236
  if (result.ok) return result;
1149
- const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
1237
+ const span = { start: pos, end: pos };
1238
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1150
1239
  ctx._errors?.push(error);
1151
1240
  if (ctx._tolerant) captureError(ctx, error);
1152
1241
  return { ok: true, value: error, span: { start: pos, end: pos } };
@@ -1594,6 +1683,18 @@ function rollbackTrivia(ctx, mark) {
1594
1683
  rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1595
1684
  if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1596
1685
  }
1686
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1687
+ const probeCtx = {
1688
+ trackLines: ctx.trackLines,
1689
+ state: ctx.state,
1690
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1691
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1692
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1693
+ };
1694
+ const result = triviaP.parse(input, cur, probeCtx);
1695
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1696
+ return result;
1697
+ }
1597
1698
  function saveLookaheadMark(ctx) {
1598
1699
  return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1599
1700
  }
@@ -1616,13 +1717,29 @@ function scanWithLabels(input, cur, ctx) {
1616
1717
  function advanceTrivia(input, cur, ctx) {
1617
1718
  const triviaP = ctx.trivia;
1618
1719
  if (!triviaP) return cur;
1720
+ if (!ctx.trackLines) {
1721
+ const fast2 = fastTriviaScanner(triviaP);
1722
+ if (fast2) return fast2(input, cur);
1723
+ if (ctx.triviaKindLabels) {
1724
+ const scan = scanWithLabels(input, cur, ctx);
1725
+ return scan.end;
1726
+ }
1727
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1728
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1729
+ }
1730
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1619
1731
  const fast = fastTriviaScanner(triviaP);
1620
- if (fast) return fast(input, cur);
1732
+ if (fast) {
1733
+ const end = fast(input, cur);
1734
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1735
+ return end;
1736
+ }
1621
1737
  if (ctx.triviaKindLabels) {
1622
1738
  const scan = scanWithLabels(input, cur, ctx);
1739
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1623
1740
  return scan.end;
1624
1741
  }
1625
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1742
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1626
1743
  return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1627
1744
  }
1628
1745
  function scanTrivia(input, cur, ctx) {
@@ -1630,18 +1747,46 @@ function scanTrivia(input, cur, ctx) {
1630
1747
  if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1631
1748
  const log = ctx._triviaLog;
1632
1749
  const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1750
+ if (!ctx.trackLines) {
1751
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1752
+ if (fast2 && log === void 0 && !captureTl) {
1753
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1754
+ }
1755
+ if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1756
+ return scanWithLabels(input, cur, ctx);
1757
+ }
1758
+ if (log !== void 0 || captureTl) {
1759
+ const tr3 = triviaP.parse(input, cur, {
1760
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1761
+ state: ctx.state
1762
+ });
1763
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1764
+ const end = tr3.span.end;
1765
+ return {
1766
+ end,
1767
+ commit: () => {
1768
+ pushTriviaLogEntry(ctx, cur, end);
1769
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1770
+ }
1771
+ };
1772
+ }
1773
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1774
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1775
+ }
1776
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1633
1777
  const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1634
1778
  if (fast && log === void 0 && !captureTl) {
1635
- return { end: fast(input, cur), commit: NOOP_COMMIT };
1779
+ const end = fast(input, cur);
1780
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1781
+ return { end, commit: NOOP_COMMIT };
1636
1782
  }
1637
1783
  if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1638
- return scanWithLabels(input, cur, ctx);
1784
+ const scan = scanWithLabels(input, cur, ctx);
1785
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1786
+ return scan;
1639
1787
  }
1640
1788
  if (log !== void 0 || captureTl) {
1641
- const tr2 = triviaP.parse(input, cur, {
1642
- trackLines: log !== void 0 ? false : ctx.trackLines,
1643
- state: ctx.state
1644
- });
1789
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1645
1790
  if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1646
1791
  const end = tr2.span.end;
1647
1792
  return {
@@ -1652,7 +1797,7 @@ function scanTrivia(input, cur, ctx) {
1652
1797
  }
1653
1798
  };
1654
1799
  }
1655
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1800
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1656
1801
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1657
1802
  }
1658
1803
  function consumeTrivia(input, cur, ctx) {
@@ -3076,13 +3221,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3076
3221
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3077
3222
  const nodeType = def.type ?? missingInferredType();
3078
3223
  const cstOutput = cstOutputHost(ctx.build);
3224
+ const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3079
3225
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3080
- const built = unwrap && children2.length === 1 ? unwrapChild(children2[0]) : collapse && children2.length === 1 ? children2[0] : !build && project === void 0 && ctx.build?._parsemanCstCollapse && children2.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children2[0], children2, rawChildren) ? children2[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, r.span, rawChildren, triviaLog, hostState) : projectChild(children2, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, r.span, rawChildren, triviaLog, hostState) : build(children2, fields, r.span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, r.span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
3081
- const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span: r.span };
3226
+ 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 };
3227
+ const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span };
3082
3228
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3083
3229
  pushCstChild(ctx, built, rawEntry);
3084
3230
  }
3085
- return { ok: true, value: built, span: r.span };
3231
+ return { ok: true, value: built, span };
3086
3232
  }
3087
3233
  };
3088
3234
  }
@@ -3153,38 +3299,24 @@ function field(name, combinator) {
3153
3299
  };
3154
3300
  }
3155
3301
 
3156
- // src/compiler/line-index.ts
3157
- function buildLineIndex(input) {
3158
- const lineStarts = [0];
3159
- for (let i = 0; i < input.length; i++) {
3160
- if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
3302
+ // src/combinators/grammar.ts
3303
+ function annotateResultLines(result, index) {
3304
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
3305
+ const errors = annotated.errors;
3306
+ if (errors) {
3307
+ ;
3308
+ annotated.errors = errors.map((error) => ({
3309
+ ...error,
3310
+ span: annotateSpan(error.span, index)
3311
+ }));
3161
3312
  }
3162
- return { lineStarts };
3313
+ return annotated;
3163
3314
  }
3164
- function offsetToLineCol(index, offset) {
3165
- const { lineStarts } = index;
3166
- let lo = 0;
3167
- let hi = lineStarts.length - 1;
3168
- while (lo < hi) {
3169
- const mid = lo + hi + 1 >> 1;
3170
- if (lineStarts[mid] <= offset) lo = mid;
3171
- else hi = mid - 1;
3172
- }
3173
- return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
3315
+ function createParseLineContext(input, pos) {
3316
+ const lineIndex = createLineIndex();
3317
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
3318
+ return { lineIndex, lineScannedTo: pos };
3174
3319
  }
3175
- function annotateSpan(span, index) {
3176
- const s = offsetToLineCol(index, span.start);
3177
- const e = offsetToLineCol(index, span.end);
3178
- return {
3179
- ...span,
3180
- startLine: s.line,
3181
- startColumn: s.col,
3182
- endLine: e.line,
3183
- endColumn: e.col
3184
- };
3185
- }
3186
-
3187
- // src/combinators/grammar.ts
3188
3320
  function parser(opts, root) {
3189
3321
  const clearTrivia = opts.trivia === null;
3190
3322
  return {
@@ -3200,9 +3332,13 @@ function parser(opts, root) {
3200
3332
  },
3201
3333
  parse(input, pos, _ctx) {
3202
3334
  const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
3335
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
3336
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
3203
3337
  const ctx = {
3204
3338
  ..._ctx,
3205
3339
  trackLines,
3340
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
3341
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
3206
3342
  // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
3207
3343
  ...clearTrivia ? {
3208
3344
  trivia: void 0,
@@ -3220,11 +3356,10 @@ function parser(opts, root) {
3220
3356
  ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
3221
3357
  };
3222
3358
  const result = root.parse(input, pos ?? 0, ctx);
3223
- if (trackLines) {
3224
- const idx = buildLineIndex(input);
3225
- return { ...result, span: annotateSpan(result.span, idx) };
3359
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
3360
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
3226
3361
  }
3227
- return result;
3362
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
3228
3363
  }
3229
3364
  };
3230
3365
  }
@@ -3238,25 +3373,25 @@ function parse(combinator, input, opts = {}) {
3238
3373
  markUnusedValues(combinator);
3239
3374
  }
3240
3375
  const trackLines = opts.trackLines ?? false;
3376
+ const lineContext = trackLines ? createParseLineContext(input, 0) : void 0;
3377
+ const lineIndex = lineContext?.lineIndex;
3241
3378
  const _errors = opts.recover ? [] : void 0;
3242
3379
  const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
3243
3380
  const grammarTrivia = combinator._meta.grammarTrivia;
3244
3381
  const grammarScanSkip = combinator._meta.grammarScanSkip;
3245
3382
  const ctx = {
3246
3383
  trackLines,
3384
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
3385
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
3247
3386
  ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
3248
3387
  ...grammarScanSkip !== void 0 ? { scanSkip: grammarScanSkip } : {},
3249
3388
  ..._errors !== void 0 ? { _errors } : {},
3250
3389
  ..._probe !== void 0 ? { _probe } : {}
3251
3390
  };
3252
3391
  const result = combinator.parse(input, 0, ctx);
3253
- if (!result.ok) return result;
3392
+ if (!result.ok) return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
3254
3393
  const withErrors = _errors !== void 0 ? { ...result, errors: _errors, furthestFail: _probe?.best ?? null } : result;
3255
- if (trackLines) {
3256
- const idx = buildLineIndex(input);
3257
- return { ...withErrors, span: annotateSpan(withErrors.span, idx) };
3258
- }
3259
- return withErrors;
3394
+ return lineIndex ? annotateResultLines(withErrors, normalizeLineIndex(lineIndex)) : withErrors;
3260
3395
  }
3261
3396
 
3262
3397
  // src/combinators/token.ts
@@ -3355,7 +3490,7 @@ function leaf(root, fn) {
3355
3490
  }
3356
3491
 
3357
3492
  // src/version.ts
3358
- var PARSEMAN_VERSION = "0.41.0";
3493
+ var PARSEMAN_VERSION = "0.42.0";
3359
3494
 
3360
3495
  // src/cst/host-mode.ts
3361
3496
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -5039,7 +5174,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5039
5174
  let cur = pos;
5040
5175
  const skip3 = resolveScanSkip(explicitSkip, raw, ctx);
5041
5176
  const probeCtx = {
5042
- trackLines: ctx.trackLines,
5177
+ trackLines: false,
5043
5178
  state: ctx.state,
5044
5179
  ...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
5045
5180
  };
@@ -5052,6 +5187,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5052
5187
  while (cur < input.length) {
5053
5188
  const s = sentinel.parse(input, cur, probeCtx);
5054
5189
  if (s.ok) {
5190
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
5055
5191
  emit2(cur);
5056
5192
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
5057
5193
  }
@@ -5067,6 +5203,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5067
5203
  if (!advanced) cur++;
5068
5204
  }
5069
5205
  if (orEOF) {
5206
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
5070
5207
  emit2(cur);
5071
5208
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
5072
5209
  }
@@ -6505,8 +6642,8 @@ function analyzeMkInlineBuild(def) {
6505
6642
  if (looseMkBuildRe(def.type).test(src)) return def.type;
6506
6643
  return null;
6507
6644
  }
6508
- function emitInlineMkNodeExpr(type, chV, rawV, pos, endVar, tlV) {
6509
- return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: { start: ${pos}, end: ${endVar} }, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`;
6645
+ function emitInlineMkNodeExpr(type, chV, rawV, spanExpr, tlV) {
6646
+ return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: ${spanExpr}, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`;
6510
6647
  }
6511
6648
 
6512
6649
  // src/compiler/inline-callback.ts
@@ -6688,6 +6825,8 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
6688
6825
  }
6689
6826
  }
6690
6827
  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 }";
6828
+ 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 }";
6829
+ var LINE_SPAN_DECL = "const _lineCol = (_ctx, offset) => { const starts = _ctx._lineStarts; let lo = 0, hi = starts.length - 1; while (lo < hi) { const mid = (lo + hi + 1) >> 1; if (starts[mid] <= offset) lo = mid; else hi = mid - 1 } return [lo + 1, offset - starts[lo] + 1] }\nconst _spanLines = (_ctx, start, end) => { const s = _lineCol(_ctx, start), e = _lineCol(_ctx, end); return { start, end, startLine: s[0], startColumn: s[1], endLine: e[0], endColumn: e[1] } }";
6691
6830
  var _loweringSink = null;
6692
6831
  function v(ctx, prefix = "_v") {
6693
6832
  return `${prefix}${ctx.vars++}`;
@@ -6828,14 +6967,46 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
6828
6967
  if (ctx.capAsTrivia) return [];
6829
6968
  const i = ind(ctx);
6830
6969
  const lf = v(ctx, "_lf");
6970
+ const spanExpr = emitSpanExpr(ctx, startExpr, endExpr);
6831
6971
  return [
6832
6972
  `${i}if (_ctx._cstLeaves || _ctx._cstRawChildren) {`,
6833
- `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } }`,
6973
+ `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: ${spanExpr} }`,
6834
6974
  `${i} if (_ctx._cstLeaves) _ctx._cstLeaves.push(${lf})`,
6835
6975
  `${i} if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(${lf})`,
6836
6976
  `${i}}`
6837
6977
  ];
6838
6978
  }
6979
+ function emitSpanExpr(ctx, startExpr, endExpr) {
6980
+ if (!ctx.lineTracking) return `{ start: ${startExpr}, end: ${endExpr} }`;
6981
+ ctx.needsLineSpan = true;
6982
+ return `_spanLines(_ctx, ${startExpr}, ${endExpr})`;
6983
+ }
6984
+ function emitLineTrack(ctx, startExpr, endExpr) {
6985
+ if (!ctx.lineTracking) return [];
6986
+ ctx.needsLineTrack = true;
6987
+ return [`${ind(ctx)}_trackLines(_ctx, input, ${startExpr}, ${endExpr})`];
6988
+ }
6989
+ function emitLiteralLineTrack(ctx, startExpr, value) {
6990
+ if (!ctx.lineTracking) return [];
6991
+ const lineDeltas = [];
6992
+ for (let i = 0; i < value.length; i++) {
6993
+ if (value.charCodeAt(i) === 10) lineDeltas.push(i + 1);
6994
+ }
6995
+ if (lineDeltas.length === 0) return [];
6996
+ const fromV = v(ctx, "_ltFrom");
6997
+ const endExpr = value.length === 0 ? startExpr : `${startExpr} + ${value.length}`;
6998
+ return [
6999
+ `${ind(ctx)}const ${fromV} = _ctx._lineScannedTo ?? 0`,
7000
+ `${ind(ctx)}if (${endExpr} > ${fromV}) {`,
7001
+ `${ind(ctx)} for (let i = ${fromV}; i < ${startExpr}; i++) if (input.charCodeAt(i) === 10) _ctx._lineStarts.push(i + 1)`,
7002
+ ...lineDeltas.map((delta) => {
7003
+ const lineStart = `${startExpr} + ${delta}`;
7004
+ return `${ind(ctx)} if (${lineStart} > ${fromV}) _ctx._lineStarts.push(${lineStart})`;
7005
+ }),
7006
+ `${ind(ctx)} _ctx._lineScannedTo = ${endExpr}`,
7007
+ `${ind(ctx)}}`
7008
+ ];
7009
+ }
6839
7010
  function ensureRegexDecl(ctx, source, flags) {
6840
7011
  const f = "y" + flags.replace(/[gy]/g, "");
6841
7012
  const key = `${source}/${f}`;
@@ -7220,6 +7391,7 @@ function emitLit(def, ctx, pos) {
7220
7391
  );
7221
7392
  }
7222
7393
  const endVar = len === 0 ? pos : `${pos} + ${len}`;
7394
+ stmts.push(...emitLiteralLineTrack(ctx, pos, value));
7223
7395
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7224
7396
  return { stmts, valueVar: vv, endVar };
7225
7397
  }
@@ -7299,7 +7471,7 @@ function emitKeywords(def, ctx, pos) {
7299
7471
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7300
7472
  return { stmts, valueVar: vv, endVar };
7301
7473
  }
7302
- function emitRegex(def, ctx, pos) {
7474
+ function emitRegex(def, ctx, pos, canMatchNewline = true) {
7303
7475
  const expectedStr = JSON.stringify(`/${def.source}/`);
7304
7476
  const shape = scanShapeFromRegex(def.source, def.flags);
7305
7477
  if (shape) {
@@ -7312,7 +7484,11 @@ function emitRegex(def, ctx, pos) {
7312
7484
  fresh: (prefix) => v(ctx, prefix)
7313
7485
  });
7314
7486
  if (scanned) {
7315
- const stmts2 = [...scanned.stmts, ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)];
7487
+ const stmts2 = [
7488
+ ...scanned.stmts,
7489
+ ...canMatchNewline ? emitLineTrack(ctx, pos, scanned.endVar) : [],
7490
+ ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)
7491
+ ];
7316
7492
  return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
7317
7493
  }
7318
7494
  }
@@ -7334,6 +7510,7 @@ function emitRegex(def, ctx, pos) {
7334
7510
  `${ind(ctx)}const ${vv} = ${mv}[0]`
7335
7511
  ];
7336
7512
  const endVar = `${pos} + ${vv}.length`;
7513
+ if (canMatchNewline) stmts.push(...emitLineTrack(ctx, pos, endVar));
7337
7514
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7338
7515
  return { stmts, valueVar: vv, endVar };
7339
7516
  }
@@ -7371,7 +7548,8 @@ function emitSeqValues(def, ctx, pos) {
7371
7548
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
7372
7549
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
7373
7550
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7374
- `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`
7551
+ `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`,
7552
+ ...emitLineTrack(ctx, curV, scanEndV)
7375
7553
  );
7376
7554
  const r2 = emit(def.parsers[i], ctx, scanEndV);
7377
7555
  stmts.push(...r2.stmts);
@@ -7389,7 +7567,8 @@ function emitSeqValues(def, ctx, pos) {
7389
7567
  const capArg = ctx.noHoist ? "0" : "_ctx._triviaLog !== undefined ? 2 : 0";
7390
7568
  stmts.push(
7391
7569
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7392
- `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`
7570
+ `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`,
7571
+ ...emitLineTrack(ctx, curV, scanEndV)
7393
7572
  );
7394
7573
  const r2 = emit(def.parsers[i], ctx, scanEndV);
7395
7574
  stmts.push(...r2.stmts);
@@ -8089,14 +8268,15 @@ function emitMany(def, ctx, pos) {
8089
8268
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8090
8269
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8091
8270
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8092
- `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`
8271
+ `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8272
+ ...emitLineTrack(ctx, curV, npV)
8093
8273
  );
8094
8274
  itemPos = npV;
8095
8275
  rollback = `if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markV}) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${markTl}) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}; `;
8096
8276
  } else {
8097
8277
  const trivFn = ensureTriviaFn(ctx);
8098
8278
  const npV = v(ctx, "_np");
8099
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`);
8279
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, npV));
8100
8280
  itemPos = npV;
8101
8281
  }
8102
8282
  }
@@ -8273,7 +8453,8 @@ function emitSepBy(_p, def, ctx, pos) {
8273
8453
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8274
8454
  `${ind(ctx)}const ${markLv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8275
8455
  `${ind(ctx)}const ${markFld} = _ctx._fields ? _ctx._fields.length : 0`,
8276
- `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`
8456
+ `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8457
+ ...emitLineTrack(ctx, curV, spV)
8277
8458
  );
8278
8459
  sepAtPos = spV;
8279
8460
  const rollbackToSep = `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${markLv}) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markV}) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${markTl}) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}; if (_ctx._fields && _ctx._fields.length !== ${markFld}) _ctx._fields.length = ${markFld}; `;
@@ -8283,7 +8464,7 @@ function emitSepBy(_p, def, ctx, pos) {
8283
8464
  const post = postSepMarks();
8284
8465
  stmts.push(...post.decl);
8285
8466
  const npV = v(ctx, "_np");
8286
- stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
8467
+ stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`, ...emitLineTrack(ctx, sepEnd, npV));
8287
8468
  const next = emitFallible(def.parser, ctx, npV, true);
8288
8469
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8289
8470
  stmts.push(
@@ -8295,13 +8476,13 @@ function emitSepBy(_p, def, ctx, pos) {
8295
8476
  } else {
8296
8477
  const trivFn = ensureTriviaFn(ctx);
8297
8478
  const spV = v(ctx, "_sp");
8298
- stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
8479
+ stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, spV));
8299
8480
  sepAtPos = spV;
8300
8481
  const sep = emitFallible(def.separator, ctx, sepAtPos, true);
8301
8482
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep;
8302
8483
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
8303
8484
  const npV = v(ctx, "_np");
8304
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
8485
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`, ...emitLineTrack(ctx, sepEnd, npV));
8305
8486
  const next = emitFallible(def.parser, ctx, npV, true);
8306
8487
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8307
8488
  stmts.push(
@@ -8388,6 +8569,7 @@ function emitScanTo(def, ctx, pos) {
8388
8569
  }
8389
8570
  const valV = v(ctx);
8390
8571
  stmts.push(`${ind(ctx)}const ${valV} = input.slice(${pos}, ${curV})`);
8572
+ stmts.push(...emitLineTrack(ctx, pos, curV));
8391
8573
  if (ctx.capturing) {
8392
8574
  const cap = reindentStmts(emitLeafCapture(ctx, valV, pos, curV), ctx.indent + 1);
8393
8575
  stmts.push(`${ind(ctx)}if (${curV} > ${pos}) {`, ...cap, `${ind(ctx)}}`);
@@ -8531,7 +8713,7 @@ function tokenNullableRegex(def) {
8531
8713
  }
8532
8714
  function emitToken(def, ctx, pos) {
8533
8715
  const lowered = tokenNullableRegex(def);
8534
- if (lowered) return emitRegex(lowered, ctx, pos);
8716
+ if (lowered) return emitRegex(lowered, ctx, pos, def.parser._meta.canMatchNewline);
8535
8717
  const savedTrivia = ctx.activeTrivia;
8536
8718
  const savedKindLabels = ctx.triviaKindLabels;
8537
8719
  ctx.activeTrivia = void 0;
@@ -8681,7 +8863,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8681
8863
  stmts.push(...innerStmts);
8682
8864
  if (def.trailingTrivia === true && ctx.activeTrivia) {
8683
8865
  const triviaFn = ensureTriviaFn(ctx);
8684
- stmts.push(`${i}const ${endVar} = ${okVar} ? ${triviaFn}(input, ${innerEndVar}, _ctx, 1) : ${innerEndVar}`);
8866
+ stmts.push(`${i}const ${endVar} = ${okVar} ? ${triviaFn}(input, ${innerEndVar}, _ctx, 1) : ${innerEndVar}`, ...emitLineTrack(ctx, innerEndVar, endVar));
8685
8867
  }
8686
8868
  if (sf && fArr) {
8687
8869
  stmts.push(`${i}const ${fArr} = _ctx._fields`);
@@ -8721,8 +8903,11 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8721
8903
  );
8722
8904
  }
8723
8905
  const ndV = v(ctx, "_nd");
8724
- const buildExpr = structural ? `{ _tag: 'node', type: ${JSON.stringify(def.type)}, span: { start: ${pos}, end: ${endVar} }, state: ${stV} ?? null, children: ${chV} }` : mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : def.project !== void 0 ? emitNodeProjectExpr(def.type, def.project, chV) : `${buildRef(ctx)}[${buildIdx}](${chV}, ${fObj}, { start: ${pos}, end: ${endVar} }, ${rawV}, ${tlV}, ${stV})`;
8725
- const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, { start: ${pos}, end: ${endVar} }, ${rawV}, ${tlV}, ${stV})`;
8906
+ const nodeSpanExpr = emitSpanExpr(ctx, pos, endVar);
8907
+ const nodeSpanV = ctx.lineTracking ? v(ctx, "_nspan") : nodeSpanExpr;
8908
+ if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
8909
+ 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})`;
8910
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
8726
8911
  const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
8727
8912
  const hostCollapseExpr = structural ? `_ctx.build !== undefined && _ctx.build._parsemanCstCollapse !== undefined && ${chV}.length === 1 && ${rawV}.length === 1 && _ctx.build._parsemanCstCollapse(${JSON.stringify(def.type)}, ${chV}[0], ${chV}, ${rawV}) ? ${chV}[0] : (${ndExpr})` : ndExpr;
8728
8913
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
@@ -8732,7 +8917,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8732
8917
  `${i}const ${ndV} = (${profileRecognizer} || ${profileCapture}) ? undefined : (${finalExpr})`,
8733
8918
  `${i}if (!(${profileRecognizer})) {`,
8734
8919
  `${i} if (${sc}) ${sc}.push(${ndV})`,
8735
- `${i} if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && (${ndV}._tag === 'node' || ${ndV}._tag === 'leaf' || ${ndV}._tag === 'parseError')) ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : (typeof ${ndV} === 'object' && ${ndV} !== null ? input.slice(${pos}, ${endVar}) : ''), span: { start: ${pos}, end: ${endVar} } })`,
8920
+ `${i} if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && (${ndV}._tag === 'node' || ${ndV}._tag === 'leaf' || ${ndV}._tag === 'parseError')) ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : (typeof ${ndV} === 'object' && ${ndV} !== null ? input.slice(${pos}, ${endVar}) : ''), span: ${nodeSpanV} })`,
8736
8921
  `${i}}`
8737
8922
  );
8738
8923
  return { stmts, valueVar: ndV, endVar };
@@ -8748,7 +8933,8 @@ function emitRuntimeFallback(parser2, ctx, pos) {
8748
8933
  `${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
8749
8934
  ...emitIfFail(ctx, `!${rv}.ok`, failStmt),
8750
8935
  `${ind(ctx)}const ${vv} = ${rv}.value`,
8751
- `${ind(ctx)}const ${ev} = ${rv}.span.end`
8936
+ `${ind(ctx)}const ${ev} = ${rv}.span.end`,
8937
+ ...parser2._meta.canMatchNewline ? emitLineTrack(ctx, pos, ev) : []
8752
8938
  ];
8753
8939
  return { stmts, valueVar: vv, endVar: ev };
8754
8940
  }
@@ -8814,7 +9000,8 @@ function emitRecover(def, ctx, pos) {
8814
9000
  `${ind0} if (${sentOk}) break`,
8815
9001
  `${ind0} ${scanV}++`,
8816
9002
  `${ind0} }`,
8817
- `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(deriveExpected(def.parser))} }`,
9003
+ ...emitLineTrack(ctx, pos, scanV).map((s) => `${ind0} ${s.trim()}`),
9004
+ `${ind0} const ${errV} = { _tag: 'parseError', span: ${emitSpanExpr(ctx, pos, scanV)}, expected: ${JSON.stringify(deriveExpected(def.parser))} }`,
8818
9005
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
8819
9006
  `${ind0} ${valVar} = ${errV}`,
8820
9007
  `${ind0} ${endVar} = ${scanV}`,
@@ -8830,7 +9017,7 @@ function emitExpect(def, ctx, pos) {
8830
9017
  const stmts = [
8831
9018
  ...innerStmts,
8832
9019
  `${ind0}if (!${okVar}) {`,
8833
- `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${pos} }, expected: ${JSON.stringify(def.expected)} }`,
9020
+ `${ind0} const ${errV} = { _tag: 'parseError', span: ${emitSpanExpr(ctx, pos, pos)}, expected: ${JSON.stringify(def.expected)} }`,
8834
9021
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
8835
9022
  // Embed the missing-token error as a `parseError` CST child, mirroring the
8836
9023
  // interpreter (expect.ts) and the list-recovery emit sites — guarded on
@@ -8920,7 +9107,7 @@ function emitDispatch(p, ctx, pos) {
8920
9107
  case "literal":
8921
9108
  return emitLit(def, ctx, pos);
8922
9109
  case "regex":
8923
- return emitRegex(def, ctx, pos);
9110
+ return emitRegex(def, ctx, pos, p._meta.canMatchNewline);
8924
9111
  case "keywords":
8925
9112
  return emitKeywords(def, ctx, pos);
8926
9113
  case "sequence":
@@ -9170,6 +9357,20 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
9170
9357
  return false;
9171
9358
  }
9172
9359
  }
9360
+ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9361
+ if (seen.has(p)) return false;
9362
+ seen.add(p);
9363
+ const d = p._def;
9364
+ if (d.tag === "grammar" && d.trackLines) return true;
9365
+ if (d.tag === "lazy") {
9366
+ try {
9367
+ return hasLineTrackingDef(d.thunk(), seen);
9368
+ } catch {
9369
+ return false;
9370
+ }
9371
+ }
9372
+ return childrenOf2(d).some((child) => hasLineTrackingDef(child, seen));
9373
+ }
9173
9374
  function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9174
9375
  if (seen.has(p)) return false;
9175
9376
  seen.add(p);
@@ -9537,6 +9738,7 @@ function compile(combinator, mapFnSources, opts) {
9537
9738
  capturing: hasNodeDef(combinator),
9538
9739
  recovery: opts?.recovery ?? false,
9539
9740
  hostMode: opts?.hostMode ?? grammarHostMode ?? "ast",
9741
+ lineTracking: opts?.trackLines === true || hasLineTrackingDef(combinator),
9540
9742
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
9541
9743
  lazyUsage: analyzeLazyUsage(combinator),
9542
9744
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
@@ -9560,9 +9762,13 @@ function compile(combinator, mapFnSources, opts) {
9560
9762
  const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
9561
9763
  const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
9562
9764
  const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
9765
+ const lineTrackDecls = ctx.needsLineTrack ? [LINE_TRACK_DECL] : [];
9766
+ const lineSpanDecls = ctx.needsLineSpan ? [LINE_SPAN_DECL] : [];
9563
9767
  const source = [
9564
9768
  ...emptyTlDecls,
9565
9769
  ...hostReadsDecls,
9770
+ ...lineTrackDecls,
9771
+ ...lineSpanDecls,
9566
9772
  ...ctx.regexDecls,
9567
9773
  ...ctx.expectedDecls,
9568
9774
  "",
@@ -9575,6 +9781,8 @@ function compile(combinator, mapFnSources, opts) {
9575
9781
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
9576
9782
  ...emptyTlDecls,
9577
9783
  ...hostReadsDecls,
9784
+ ...lineTrackDecls,
9785
+ ...lineSpanDecls,
9578
9786
  ...ctx.regexDecls,
9579
9787
  ...ctx.expectedDecls,
9580
9788
  ...namedPrelude,
@@ -9593,6 +9801,25 @@ function compile(combinator, mapFnSources, opts) {
9593
9801
  trackLines: false,
9594
9802
  ...ctx.triviaKindLabels ? { triviaKindLabels: ctx.triviaKindLabels } : {}
9595
9803
  };
9804
+ const annotateWithTrackedLines = (result, lineStarts) => {
9805
+ const index = normalizeLineIndex({ lineStarts });
9806
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
9807
+ const errors = annotated.errors;
9808
+ if (errors) {
9809
+ ;
9810
+ annotated.errors = errors.map((error) => ({
9811
+ ...error,
9812
+ span: annotateSpan(error.span, index)
9813
+ }));
9814
+ }
9815
+ return annotated;
9816
+ };
9817
+ const trackedCtx = (base, input, pos) => {
9818
+ if (!ctx.lineTracking) return base;
9819
+ const lineIndex = { lineStarts: [0] };
9820
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
9821
+ return { ...base, trackLines: true, _lineStarts: lineIndex.lineStarts, _lineScannedTo: pos };
9822
+ };
9596
9823
  const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
9597
9824
  const effectiveSources = mapFnSources ?? derivedSrcs;
9598
9825
  const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
@@ -9600,17 +9827,46 @@ function compile(combinator, mapFnSources, opts) {
9600
9827
  const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
9601
9828
  const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
9602
9829
  const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources, coverageRootRuleId) : null;
9603
- return {
9830
+ const compiledMeta = {
9604
9831
  source,
9605
9832
  inlineExpression,
9606
9833
  ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions },
9607
- ...gatingReport === void 0 ? {} : { gating: gatingReport },
9834
+ ...gatingReport === void 0 ? {} : { gating: gatingReport }
9835
+ };
9836
+ if (!ctx.lineTracking) {
9837
+ return {
9838
+ ...compiledMeta,
9839
+ parse(input, pos = 0) {
9840
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
9841
+ },
9842
+ parseWithContext(input, parseCtx, pos = 0) {
9843
+ assertHostModeCompatible(ctx.hostMode ?? "ast", parseCtx.build, !!ctx.hostBranchElided);
9844
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9845
+ },
9846
+ // Note: collects expect() errors via _errors. Unlike interpreter
9847
+ // parse({recover:true}) it does NOT populate furthestFail — the compiled path
9848
+ // inlines failures for throughput and deliberately skips _probe bookkeeping.
9849
+ // Callers wanting a furthest-position diagnostic detect unconsumed input
9850
+ // (span.end < input.length) instead, which is mode-agnostic.
9851
+ parseWithErrors(input, pos = 0) {
9852
+ const errors = [];
9853
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
9854
+ return { ...result, errors };
9855
+ }
9856
+ };
9857
+ }
9858
+ return {
9859
+ ...compiledMeta,
9608
9860
  parse(input, pos = 0) {
9609
- return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
9861
+ const parseCtx = trackedCtx(defaultCtx, input, pos);
9862
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9863
+ return annotateWithTrackedLines(result, parseCtx._lineStarts);
9610
9864
  },
9611
9865
  parseWithContext(input, parseCtx, pos = 0) {
9612
9866
  assertHostModeCompatible(ctx.hostMode ?? "ast", parseCtx.build, !!ctx.hostBranchElided);
9613
- return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9867
+ const activeCtx = trackedCtx(parseCtx, input, pos);
9868
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, activeCtx);
9869
+ return activeCtx.trackLines ? annotateWithTrackedLines(result, activeCtx._lineStarts) : result;
9614
9870
  },
9615
9871
  // Note: collects expect() errors via _errors. Unlike interpreter
9616
9872
  // parse({recover:true}) it does NOT populate furthestFail — the compiled path
@@ -9619,8 +9875,9 @@ function compile(combinator, mapFnSources, opts) {
9619
9875
  // (span.end < input.length) instead, which is mode-agnostic.
9620
9876
  parseWithErrors(input, pos = 0) {
9621
9877
  const errors = [];
9622
- const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
9623
- return { ...result, errors };
9878
+ const parseCtx = trackedCtx({ ...defaultCtx, _errors: errors }, input, pos);
9879
+ const result = { ...fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx), errors };
9880
+ return annotateWithTrackedLines(result, parseCtx._lineStarts);
9624
9881
  }
9625
9882
  };
9626
9883
  }
@@ -9907,12 +10164,16 @@ function buildInlineExpression(ctx, r, mapFnSources, buildSources, coverageRootR
9907
10164
  const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
9908
10165
  const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
9909
10166
  const hostReadsDecl = ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "";
9910
- const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl;
10167
+ const lineTrackDecl = ctx.needsLineTrack ? ` ${LINE_TRACK_DECL}` : "";
10168
+ const lineSpanDecl = ctx.needsLineSpan ? ` ${LINE_SPAN_DECL}` : "";
10169
+ const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl || !!lineTrackDecl || !!lineSpanDecl;
9911
10170
  if (!needsWrapper) return innerFn;
9912
10171
  const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
9913
10172
  const hoistedDecls = [
9914
10173
  emptyTlDecl,
9915
10174
  hostReadsDecl,
10175
+ lineTrackDecl,
10176
+ lineSpanDecl,
9916
10177
  ...ctx.regexDecls.map((d) => ` ${d}`),
9917
10178
  ...ctx.expectedDecls.map((d) => ` ${d}`),
9918
10179
  mfDecl,
@@ -10461,7 +10722,7 @@ function normalizeCstCollapse(collapse) {
10461
10722
  return typeof collapse === "function" ? collapse : void 0;
10462
10723
  }
10463
10724
  function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
10464
- return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children2] };
10725
+ return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children2] };
10465
10726
  }
10466
10727
  function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
10467
10728
  if (typeof typeOrOptions === "string") {
@@ -12185,6 +12446,7 @@ export {
12185
12446
  analyzeGatingRules,
12186
12447
  analyzeGrammarGating,
12187
12448
  annotateSpan,
12449
+ annotateTreeSpans,
12188
12450
  applyEdit,
12189
12451
  attempt,
12190
12452
  balanced,
@@ -12206,6 +12468,7 @@ export {
12206
12468
  createGrammarCoverageCollector,
12207
12469
  createGrammarInstrumentationContext,
12208
12470
  createGrammarTraceSink,
12471
+ createLineIndex,
12209
12472
  createVisitor,
12210
12473
  cstBuildHost,
12211
12474
  dispatch,
@@ -12239,6 +12502,7 @@ export {
12239
12502
  matches,
12240
12503
  noTrivia,
12241
12504
  node,
12505
+ normalizeLineIndex,
12242
12506
  not,
12243
12507
  offsetToLineCol,
12244
12508
  oneOrMore,
@@ -12249,6 +12513,7 @@ export {
12249
12513
  parseDoc,
12250
12514
  parser,
12251
12515
  peek,
12516
+ recordLineRange,
12252
12517
  ref,
12253
12518
  regex,
12254
12519
  relativize,