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
@@ -317,6 +317,65 @@ function failAt(ctx, expected, pos) {
317
317
  return r;
318
318
  }
319
319
 
320
+ // src/line-index.ts
321
+ function createLineIndex() {
322
+ return { lineStarts: [0] };
323
+ }
324
+ function recordLineRange(index, input, start, end) {
325
+ const lineStarts = index.lineStarts;
326
+ for (let i = start; i < end; i++) {
327
+ if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
328
+ }
329
+ }
330
+ function recordLineRangeFromContext(ctx, input, _startHint, end) {
331
+ if (!ctx.trackLines) return;
332
+ const from = ctx._lineScannedTo ?? 0;
333
+ if (end <= from) return;
334
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
335
+ if (!index) return;
336
+ recordLineRange(index, input, from, end);
337
+ ctx._lineScannedTo = end;
338
+ }
339
+ function normalizeLineIndex(index) {
340
+ const starts = index.lineStarts;
341
+ starts.sort((a, b) => a - b);
342
+ let write = 0;
343
+ for (let read = 0; read < starts.length; read++) {
344
+ const value = starts[read];
345
+ if (write === 0 || starts[write - 1] !== value) starts[write++] = value;
346
+ }
347
+ starts.length = write;
348
+ if (starts.length === 0 || starts[0] !== 0) starts.unshift(0);
349
+ return index;
350
+ }
351
+ function offsetToLineCol(index, offset) {
352
+ const { lineStarts } = index;
353
+ let lo = 0;
354
+ let hi = lineStarts.length - 1;
355
+ while (lo < hi) {
356
+ const mid = lo + hi + 1 >> 1;
357
+ if (lineStarts[mid] <= offset) lo = mid;
358
+ else hi = mid - 1;
359
+ }
360
+ return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
361
+ }
362
+ function annotateSpan(span, index) {
363
+ const s = offsetToLineCol(index, span.start);
364
+ const e = offsetToLineCol(index, span.end);
365
+ return {
366
+ ...span,
367
+ startLine: s.line,
368
+ startColumn: s.col,
369
+ endLine: e.line,
370
+ endColumn: e.col
371
+ };
372
+ }
373
+ function annotateSpanFromLineContext(span, ctx) {
374
+ if (!ctx.trackLines) return span;
375
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
376
+ return index ? annotateSpan(span, index) : span;
377
+ }
378
+
320
379
  // src/cst/capture-buffer.ts
321
380
  var EMPTY = [];
322
381
  var EMPTY_TL = [];
@@ -358,7 +417,12 @@ function endCstNodeCapture(ctx, saved) {
358
417
  return materialized;
359
418
  }
360
419
  function pushCstLeaf(ctx, leaf2) {
361
- pushCstChild(ctx, leaf2, leaf2);
420
+ if (!ctx.trackLines) {
421
+ pushCstChild(ctx, leaf2, leaf2);
422
+ return;
423
+ }
424
+ const annotated = leaf2 && typeof leaf2 === "object" && leaf2.span ? { ...leaf2, span: annotateSpanFromLineContext(leaf2.span, ctx) } : leaf2;
425
+ pushCstChild(ctx, annotated, annotated);
362
426
  }
363
427
  function pushCstChild(ctx, built, rawEntry) {
364
428
  const b = ctx._cstBuf;
@@ -492,6 +556,7 @@ function literal(value, opts = {}) {
492
556
  canMatchNewline: value.includes("\n"),
493
557
  isTrivia: false
494
558
  };
559
+ const canMatchNewline = meta.canMatchNewline;
495
560
  if (caseInsensitive) {
496
561
  const upper = value.toUpperCase();
497
562
  const lower = value.toLowerCase();
@@ -508,6 +573,7 @@ function literal(value, opts = {}) {
508
573
  return function parse3(input, pos, ctx) {
509
574
  if (input.charCodeAt(pos) === code) {
510
575
  const span = { start: pos, end: pos + 1 };
576
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
511
577
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
512
578
  return { ok: true, value, span };
513
579
  }
@@ -521,6 +587,7 @@ function literal(value, opts = {}) {
521
587
  const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
522
588
  if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
523
589
  const span = { start: pos, end };
590
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
524
591
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
525
592
  return { ok: true, value: matchedValue, span };
526
593
  }
@@ -688,6 +755,7 @@ function regex(pattern, flags = "") {
688
755
  if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
689
756
  const value = input.slice(pos, scanEnd);
690
757
  const span2 = { start: pos, end: scanEnd };
758
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
691
759
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
692
760
  return { ok: true, value, span: span2 };
693
761
  }
@@ -697,6 +765,7 @@ function regex(pattern, flags = "") {
697
765
  return failAt(ctx, [`/${source}/`], pos);
698
766
  }
699
767
  const span = { start: pos, end: pos + m[0].length };
768
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
700
769
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
701
770
  return { ok: true, value: m[0], span };
702
771
  }
@@ -835,8 +904,8 @@ function parseRegex(src) {
835
904
  }
836
905
  if (src[k] !== "]") throw BAIL;
837
906
  i = k + 1;
838
- if (negated) return charNode(any(), true);
839
907
  const ranges = parseClassRanges(body);
908
+ if (negated) return charNode(any(), !ranges || !rangesHaveNewline(ranges));
840
909
  if (!ranges) return charNode(any(), true);
841
910
  return charNode(rangesToSet(ranges), rangesHaveNewline(ranges));
842
911
  };
@@ -932,7 +1001,7 @@ function firstSet(n) {
932
1001
  }
933
1002
  }
934
1003
  }
935
- function firstCanBeNewline(n) {
1004
+ function canConsumeNewline(n) {
936
1005
  switch (n.t) {
937
1006
  case "char":
938
1007
  return n.nl;
@@ -941,16 +1010,11 @@ function firstCanBeNewline(n) {
941
1010
  case "empty":
942
1011
  return false;
943
1012
  case "rep":
944
- return firstCanBeNewline(n.node);
1013
+ return canConsumeNewline(n.node);
945
1014
  case "alt":
946
- return n.arms.some(firstCanBeNewline);
947
- case "seq": {
948
- for (const part of n.parts) {
949
- if (firstCanBeNewline(part)) return true;
950
- if (!nullable(part)) break;
951
- }
952
- return false;
953
- }
1015
+ return n.arms.some(canConsumeNewline);
1016
+ case "seq":
1017
+ return n.parts.some(canConsumeNewline);
954
1018
  }
955
1019
  }
956
1020
  function firstSetFromRegex(source) {
@@ -960,8 +1024,8 @@ function firstSetFromRegex(source) {
960
1024
  } catch {
961
1025
  return { firstSet: any(), canMatchNewline: true };
962
1026
  }
963
- if (nullable(ast)) return { firstSet: any(), canMatchNewline: true };
964
- return { firstSet: firstSet(ast), canMatchNewline: firstCanBeNewline(ast) };
1027
+ if (nullable(ast)) return { firstSet: any(), canMatchNewline: canConsumeNewline(ast) };
1028
+ return { firstSet: firstSet(ast), canMatchNewline: canConsumeNewline(ast) };
965
1029
  }
966
1030
 
967
1031
  // src/combinators/case-fold.ts
@@ -1118,7 +1182,9 @@ function firstSetSentinel(fs2) {
1118
1182
  function recoverScan(input, from, ctx, sync, expected) {
1119
1183
  let scanPos = from;
1120
1184
  while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1121
- const error = { _tag: "parseError", span: { start: from, end: scanPos }, expected };
1185
+ const span = { start: from, end: scanPos };
1186
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
1187
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1122
1188
  ctx._errors?.push(error);
1123
1189
  return { error, end: scanPos };
1124
1190
  }
@@ -1220,7 +1286,8 @@ function expect(combinator, label2) {
1220
1286
  parse(input, pos, ctx) {
1221
1287
  const result = combinator.parse(input, pos, ctx);
1222
1288
  if (result.ok) return result;
1223
- const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
1289
+ const span = { start: pos, end: pos };
1290
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1224
1291
  ctx._errors?.push(error);
1225
1292
  if (ctx._tolerant) captureError(ctx, error);
1226
1293
  return { ok: true, value: error, span: { start: pos, end: pos } };
@@ -1665,6 +1732,18 @@ function rollbackTrivia(ctx, mark) {
1665
1732
  rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1666
1733
  if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1667
1734
  }
1735
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1736
+ const probeCtx = {
1737
+ trackLines: ctx.trackLines,
1738
+ state: ctx.state,
1739
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1740
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1741
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1742
+ };
1743
+ const result = triviaP.parse(input, cur, probeCtx);
1744
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1745
+ return result;
1746
+ }
1668
1747
  function saveLookaheadMark(ctx) {
1669
1748
  return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1670
1749
  }
@@ -1687,13 +1766,29 @@ function scanWithLabels(input, cur, ctx) {
1687
1766
  function advanceTrivia(input, cur, ctx) {
1688
1767
  const triviaP = ctx.trivia;
1689
1768
  if (!triviaP) return cur;
1769
+ if (!ctx.trackLines) {
1770
+ const fast2 = fastTriviaScanner(triviaP);
1771
+ if (fast2) return fast2(input, cur);
1772
+ if (ctx.triviaKindLabels) {
1773
+ const scan = scanWithLabels(input, cur, ctx);
1774
+ return scan.end;
1775
+ }
1776
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1777
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1778
+ }
1779
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1690
1780
  const fast = fastTriviaScanner(triviaP);
1691
- if (fast) return fast(input, cur);
1781
+ if (fast) {
1782
+ const end = fast(input, cur);
1783
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1784
+ return end;
1785
+ }
1692
1786
  if (ctx.triviaKindLabels) {
1693
1787
  const scan = scanWithLabels(input, cur, ctx);
1788
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1694
1789
  return scan.end;
1695
1790
  }
1696
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1791
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1697
1792
  return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1698
1793
  }
1699
1794
  function scanTrivia(input, cur, ctx) {
@@ -1701,18 +1796,46 @@ function scanTrivia(input, cur, ctx) {
1701
1796
  if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1702
1797
  const log = ctx._triviaLog;
1703
1798
  const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1799
+ if (!ctx.trackLines) {
1800
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1801
+ if (fast2 && log === void 0 && !captureTl) {
1802
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1803
+ }
1804
+ if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1805
+ return scanWithLabels(input, cur, ctx);
1806
+ }
1807
+ if (log !== void 0 || captureTl) {
1808
+ const tr3 = triviaP.parse(input, cur, {
1809
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1810
+ state: ctx.state
1811
+ });
1812
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1813
+ const end = tr3.span.end;
1814
+ return {
1815
+ end,
1816
+ commit: () => {
1817
+ pushTriviaLogEntry(ctx, cur, end);
1818
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1819
+ }
1820
+ };
1821
+ }
1822
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1823
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1824
+ }
1825
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1704
1826
  const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1705
1827
  if (fast && log === void 0 && !captureTl) {
1706
- return { end: fast(input, cur), commit: NOOP_COMMIT };
1828
+ const end = fast(input, cur);
1829
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1830
+ return { end, commit: NOOP_COMMIT };
1707
1831
  }
1708
1832
  if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1709
- return scanWithLabels(input, cur, ctx);
1833
+ const scan = scanWithLabels(input, cur, ctx);
1834
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1835
+ return scan;
1710
1836
  }
1711
1837
  if (log !== void 0 || captureTl) {
1712
- const tr2 = triviaP.parse(input, cur, {
1713
- trackLines: log !== void 0 ? false : ctx.trackLines,
1714
- state: ctx.state
1715
- });
1838
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1716
1839
  if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1717
1840
  const end = tr2.span.end;
1718
1841
  return {
@@ -1723,7 +1846,7 @@ function scanTrivia(input, cur, ctx) {
1723
1846
  }
1724
1847
  };
1725
1848
  }
1726
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1849
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1727
1850
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1728
1851
  }
1729
1852
  function consumeTrivia(input, cur, ctx) {
@@ -3107,13 +3230,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3107
3230
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3108
3231
  const nodeType = def.type ?? missingInferredType();
3109
3232
  const cstOutput = cstOutputHost(ctx.build);
3233
+ const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3110
3234
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3111
- const built = unwrap && children2.length === 1 ? unwrapChild(children2[0]) : collapse && children2.length === 1 ? children2[0] : !build && project === void 0 && ctx.build?._parsemanCstCollapse && children2.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children2[0], children2, rawChildren) ? children2[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, r.span, rawChildren, triviaLog, hostState) : projectChild(children2, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, r.span, rawChildren, triviaLog, hostState) : build(children2, fields, r.span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, r.span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
3112
- const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span: r.span };
3235
+ 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 };
3236
+ const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span };
3113
3237
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3114
3238
  pushCstChild(ctx, built, rawEntry);
3115
3239
  }
3116
- return { ok: true, value: built, span: r.span };
3240
+ return { ok: true, value: built, span };
3117
3241
  }
3118
3242
  };
3119
3243
  }
@@ -3184,38 +3308,24 @@ function field(name, combinator) {
3184
3308
  };
3185
3309
  }
3186
3310
 
3187
- // src/compiler/line-index.ts
3188
- function buildLineIndex(input) {
3189
- const lineStarts = [0];
3190
- for (let i = 0; i < input.length; i++) {
3191
- if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
3192
- }
3193
- return { lineStarts };
3194
- }
3195
- function offsetToLineCol(index, offset) {
3196
- const { lineStarts } = index;
3197
- let lo = 0;
3198
- let hi = lineStarts.length - 1;
3199
- while (lo < hi) {
3200
- const mid = lo + hi + 1 >> 1;
3201
- if (lineStarts[mid] <= offset) lo = mid;
3202
- else hi = mid - 1;
3311
+ // src/combinators/grammar.ts
3312
+ function annotateResultLines(result, index) {
3313
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
3314
+ const errors = annotated.errors;
3315
+ if (errors) {
3316
+ ;
3317
+ annotated.errors = errors.map((error) => ({
3318
+ ...error,
3319
+ span: annotateSpan(error.span, index)
3320
+ }));
3203
3321
  }
3204
- return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
3322
+ return annotated;
3205
3323
  }
3206
- function annotateSpan(span, index) {
3207
- const s = offsetToLineCol(index, span.start);
3208
- const e = offsetToLineCol(index, span.end);
3209
- return {
3210
- ...span,
3211
- startLine: s.line,
3212
- startColumn: s.col,
3213
- endLine: e.line,
3214
- endColumn: e.col
3215
- };
3324
+ function createParseLineContext(input, pos) {
3325
+ const lineIndex = createLineIndex();
3326
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
3327
+ return { lineIndex, lineScannedTo: pos };
3216
3328
  }
3217
-
3218
- // src/combinators/grammar.ts
3219
3329
  function parser(opts, root) {
3220
3330
  const clearTrivia = opts.trivia === null;
3221
3331
  return {
@@ -3231,9 +3341,13 @@ function parser(opts, root) {
3231
3341
  },
3232
3342
  parse(input, pos, _ctx) {
3233
3343
  const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
3344
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
3345
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
3234
3346
  const ctx = {
3235
3347
  ..._ctx,
3236
3348
  trackLines,
3349
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
3350
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
3237
3351
  // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
3238
3352
  ...clearTrivia ? {
3239
3353
  trivia: void 0,
@@ -3251,11 +3365,10 @@ function parser(opts, root) {
3251
3365
  ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
3252
3366
  };
3253
3367
  const result = root.parse(input, pos ?? 0, ctx);
3254
- if (trackLines) {
3255
- const idx = buildLineIndex(input);
3256
- return { ...result, span: annotateSpan(result.span, idx) };
3368
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
3369
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
3257
3370
  }
3258
- return result;
3371
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
3259
3372
  }
3260
3373
  };
3261
3374
  }
@@ -3359,7 +3472,7 @@ function leaf(root, fn) {
3359
3472
  }
3360
3473
 
3361
3474
  // src/version.ts
3362
- var PARSEMAN_VERSION = "0.41.0";
3475
+ var PARSEMAN_VERSION = "0.42.0";
3363
3476
 
3364
3477
  // src/cst/host-mode.ts
3365
3478
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -5043,7 +5156,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5043
5156
  let cur = pos;
5044
5157
  const skip3 = resolveScanSkip(explicitSkip, raw, ctx);
5045
5158
  const probeCtx = {
5046
- trackLines: ctx.trackLines,
5159
+ trackLines: false,
5047
5160
  state: ctx.state,
5048
5161
  ...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
5049
5162
  };
@@ -5056,6 +5169,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5056
5169
  while (cur < input.length) {
5057
5170
  const s = sentinel.parse(input, cur, probeCtx);
5058
5171
  if (s.ok) {
5172
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
5059
5173
  emit2(cur);
5060
5174
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
5061
5175
  }
@@ -5071,6 +5185,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5071
5185
  if (!advanced) cur++;
5072
5186
  }
5073
5187
  if (orEOF) {
5188
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
5074
5189
  emit2(cur);
5075
5190
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
5076
5191
  }
@@ -6509,8 +6624,8 @@ function analyzeMkInlineBuild(def) {
6509
6624
  if (looseMkBuildRe(def.type).test(src)) return def.type;
6510
6625
  return null;
6511
6626
  }
6512
- function emitInlineMkNodeExpr(type, chV, rawV, pos, endVar, tlV) {
6513
- return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: { start: ${pos}, end: ${endVar} }, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`;
6627
+ function emitInlineMkNodeExpr(type, chV, rawV, spanExpr, tlV) {
6628
+ return `{ _tag: 'node', type: ${JSON.stringify(type)}, span: ${spanExpr}, children: ${chV}, rawCount: ${rawV}.length, localTriviaLen: ${tlV}.length }`;
6514
6629
  }
6515
6630
 
6516
6631
  // src/compiler/inline-callback.ts
@@ -6692,6 +6807,8 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
6692
6807
  }
6693
6808
  }
6694
6809
  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 }";
6810
+ 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 }";
6811
+ var LINE_SPAN_DECL = "const _lineCol = (_ctx, offset) => { const starts = _ctx._lineStarts; let lo = 0, hi = starts.length - 1; while (lo < hi) { const mid = (lo + hi + 1) >> 1; if (starts[mid] <= offset) lo = mid; else hi = mid - 1 } return [lo + 1, offset - starts[lo] + 1] }\nconst _spanLines = (_ctx, start, end) => { const s = _lineCol(_ctx, start), e = _lineCol(_ctx, end); return { start, end, startLine: s[0], startColumn: s[1], endLine: e[0], endColumn: e[1] } }";
6695
6812
  var _loweringSink = null;
6696
6813
  function beginLoweringCapture() {
6697
6814
  _loweringSink = /* @__PURE__ */ new Set();
@@ -6840,14 +6957,46 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
6840
6957
  if (ctx.capAsTrivia) return [];
6841
6958
  const i = ind(ctx);
6842
6959
  const lf = v(ctx, "_lf");
6960
+ const spanExpr = emitSpanExpr(ctx, startExpr, endExpr);
6843
6961
  return [
6844
6962
  `${i}if (_ctx._cstLeaves || _ctx._cstRawChildren) {`,
6845
- `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } }`,
6963
+ `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: ${spanExpr} }`,
6846
6964
  `${i} if (_ctx._cstLeaves) _ctx._cstLeaves.push(${lf})`,
6847
6965
  `${i} if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(${lf})`,
6848
6966
  `${i}}`
6849
6967
  ];
6850
6968
  }
6969
+ function emitSpanExpr(ctx, startExpr, endExpr) {
6970
+ if (!ctx.lineTracking) return `{ start: ${startExpr}, end: ${endExpr} }`;
6971
+ ctx.needsLineSpan = true;
6972
+ return `_spanLines(_ctx, ${startExpr}, ${endExpr})`;
6973
+ }
6974
+ function emitLineTrack(ctx, startExpr, endExpr) {
6975
+ if (!ctx.lineTracking) return [];
6976
+ ctx.needsLineTrack = true;
6977
+ return [`${ind(ctx)}_trackLines(_ctx, input, ${startExpr}, ${endExpr})`];
6978
+ }
6979
+ function emitLiteralLineTrack(ctx, startExpr, value) {
6980
+ if (!ctx.lineTracking) return [];
6981
+ const lineDeltas = [];
6982
+ for (let i = 0; i < value.length; i++) {
6983
+ if (value.charCodeAt(i) === 10) lineDeltas.push(i + 1);
6984
+ }
6985
+ if (lineDeltas.length === 0) return [];
6986
+ const fromV = v(ctx, "_ltFrom");
6987
+ const endExpr = value.length === 0 ? startExpr : `${startExpr} + ${value.length}`;
6988
+ return [
6989
+ `${ind(ctx)}const ${fromV} = _ctx._lineScannedTo ?? 0`,
6990
+ `${ind(ctx)}if (${endExpr} > ${fromV}) {`,
6991
+ `${ind(ctx)} for (let i = ${fromV}; i < ${startExpr}; i++) if (input.charCodeAt(i) === 10) _ctx._lineStarts.push(i + 1)`,
6992
+ ...lineDeltas.map((delta) => {
6993
+ const lineStart = `${startExpr} + ${delta}`;
6994
+ return `${ind(ctx)} if (${lineStart} > ${fromV}) _ctx._lineStarts.push(${lineStart})`;
6995
+ }),
6996
+ `${ind(ctx)} _ctx._lineScannedTo = ${endExpr}`,
6997
+ `${ind(ctx)}}`
6998
+ ];
6999
+ }
6851
7000
  function ensureRegexDecl(ctx, source, flags) {
6852
7001
  const f = "y" + flags.replace(/[gy]/g, "");
6853
7002
  const key = `${source}/${f}`;
@@ -7232,6 +7381,7 @@ function emitLit(def, ctx, pos) {
7232
7381
  );
7233
7382
  }
7234
7383
  const endVar = len === 0 ? pos : `${pos} + ${len}`;
7384
+ stmts.push(...emitLiteralLineTrack(ctx, pos, value));
7235
7385
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7236
7386
  return { stmts, valueVar: vv, endVar };
7237
7387
  }
@@ -7311,7 +7461,7 @@ function emitKeywords(def, ctx, pos) {
7311
7461
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7312
7462
  return { stmts, valueVar: vv, endVar };
7313
7463
  }
7314
- function emitRegex(def, ctx, pos) {
7464
+ function emitRegex(def, ctx, pos, canMatchNewline = true) {
7315
7465
  const expectedStr = JSON.stringify(`/${def.source}/`);
7316
7466
  const shape = scanShapeFromRegex(def.source, def.flags);
7317
7467
  if (shape) {
@@ -7324,7 +7474,11 @@ function emitRegex(def, ctx, pos) {
7324
7474
  fresh: (prefix) => v(ctx, prefix)
7325
7475
  });
7326
7476
  if (scanned) {
7327
- const stmts2 = [...scanned.stmts, ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)];
7477
+ const stmts2 = [
7478
+ ...scanned.stmts,
7479
+ ...canMatchNewline ? emitLineTrack(ctx, pos, scanned.endVar) : [],
7480
+ ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)
7481
+ ];
7328
7482
  return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
7329
7483
  }
7330
7484
  }
@@ -7346,6 +7500,7 @@ function emitRegex(def, ctx, pos) {
7346
7500
  `${ind(ctx)}const ${vv} = ${mv}[0]`
7347
7501
  ];
7348
7502
  const endVar = `${pos} + ${vv}.length`;
7503
+ if (canMatchNewline) stmts.push(...emitLineTrack(ctx, pos, endVar));
7349
7504
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7350
7505
  return { stmts, valueVar: vv, endVar };
7351
7506
  }
@@ -7383,7 +7538,8 @@ function emitSeqValues(def, ctx, pos) {
7383
7538
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
7384
7539
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
7385
7540
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7386
- `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`
7541
+ `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`,
7542
+ ...emitLineTrack(ctx, curV, scanEndV)
7387
7543
  );
7388
7544
  const r2 = emit(def.parsers[i], ctx, scanEndV);
7389
7545
  stmts.push(...r2.stmts);
@@ -7401,7 +7557,8 @@ function emitSeqValues(def, ctx, pos) {
7401
7557
  const capArg = ctx.noHoist ? "0" : "_ctx._triviaLog !== undefined ? 2 : 0";
7402
7558
  stmts.push(
7403
7559
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7404
- `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`
7560
+ `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`,
7561
+ ...emitLineTrack(ctx, curV, scanEndV)
7405
7562
  );
7406
7563
  const r2 = emit(def.parsers[i], ctx, scanEndV);
7407
7564
  stmts.push(...r2.stmts);
@@ -8101,14 +8258,15 @@ function emitMany(def, ctx, pos) {
8101
8258
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8102
8259
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8103
8260
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8104
- `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`
8261
+ `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8262
+ ...emitLineTrack(ctx, curV, npV)
8105
8263
  );
8106
8264
  itemPos = npV;
8107
8265
  rollback = `if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markV}) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${markTl}) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}; `;
8108
8266
  } else {
8109
8267
  const trivFn = ensureTriviaFn(ctx);
8110
8268
  const npV = v(ctx, "_np");
8111
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`);
8269
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, npV));
8112
8270
  itemPos = npV;
8113
8271
  }
8114
8272
  }
@@ -8285,7 +8443,8 @@ function emitSepBy(_p, def, ctx, pos) {
8285
8443
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8286
8444
  `${ind(ctx)}const ${markLv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8287
8445
  `${ind(ctx)}const ${markFld} = _ctx._fields ? _ctx._fields.length : 0`,
8288
- `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`
8446
+ `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8447
+ ...emitLineTrack(ctx, curV, spV)
8289
8448
  );
8290
8449
  sepAtPos = spV;
8291
8450
  const rollbackToSep = `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${markLv}) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markV}) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${markTl}) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}; if (_ctx._fields && _ctx._fields.length !== ${markFld}) _ctx._fields.length = ${markFld}; `;
@@ -8295,7 +8454,7 @@ function emitSepBy(_p, def, ctx, pos) {
8295
8454
  const post = postSepMarks();
8296
8455
  stmts.push(...post.decl);
8297
8456
  const npV = v(ctx, "_np");
8298
- stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
8457
+ stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`, ...emitLineTrack(ctx, sepEnd, npV));
8299
8458
  const next = emitFallible(def.parser, ctx, npV, true);
8300
8459
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8301
8460
  stmts.push(
@@ -8307,13 +8466,13 @@ function emitSepBy(_p, def, ctx, pos) {
8307
8466
  } else {
8308
8467
  const trivFn = ensureTriviaFn(ctx);
8309
8468
  const spV = v(ctx, "_sp");
8310
- stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
8469
+ stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, spV));
8311
8470
  sepAtPos = spV;
8312
8471
  const sep2 = emitFallible(def.separator, ctx, sepAtPos, true);
8313
8472
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep2;
8314
8473
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
8315
8474
  const npV = v(ctx, "_np");
8316
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
8475
+ stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`, ...emitLineTrack(ctx, sepEnd, npV));
8317
8476
  const next = emitFallible(def.parser, ctx, npV, true);
8318
8477
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8319
8478
  stmts.push(
@@ -8400,6 +8559,7 @@ function emitScanTo(def, ctx, pos) {
8400
8559
  }
8401
8560
  const valV = v(ctx);
8402
8561
  stmts.push(`${ind(ctx)}const ${valV} = input.slice(${pos}, ${curV})`);
8562
+ stmts.push(...emitLineTrack(ctx, pos, curV));
8403
8563
  if (ctx.capturing) {
8404
8564
  const cap = reindentStmts(emitLeafCapture(ctx, valV, pos, curV), ctx.indent + 1);
8405
8565
  stmts.push(`${ind(ctx)}if (${curV} > ${pos}) {`, ...cap, `${ind(ctx)}}`);
@@ -8543,7 +8703,7 @@ function tokenNullableRegex(def) {
8543
8703
  }
8544
8704
  function emitToken(def, ctx, pos) {
8545
8705
  const lowered = tokenNullableRegex(def);
8546
- if (lowered) return emitRegex(lowered, ctx, pos);
8706
+ if (lowered) return emitRegex(lowered, ctx, pos, def.parser._meta.canMatchNewline);
8547
8707
  const savedTrivia = ctx.activeTrivia;
8548
8708
  const savedKindLabels = ctx.triviaKindLabels;
8549
8709
  ctx.activeTrivia = void 0;
@@ -8693,7 +8853,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8693
8853
  stmts.push(...innerStmts);
8694
8854
  if (def.trailingTrivia === true && ctx.activeTrivia) {
8695
8855
  const triviaFn = ensureTriviaFn(ctx);
8696
- stmts.push(`${i}const ${endVar} = ${okVar} ? ${triviaFn}(input, ${innerEndVar}, _ctx, 1) : ${innerEndVar}`);
8856
+ stmts.push(`${i}const ${endVar} = ${okVar} ? ${triviaFn}(input, ${innerEndVar}, _ctx, 1) : ${innerEndVar}`, ...emitLineTrack(ctx, innerEndVar, endVar));
8697
8857
  }
8698
8858
  if (sf && fArr) {
8699
8859
  stmts.push(`${i}const ${fArr} = _ctx._fields`);
@@ -8733,8 +8893,11 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8733
8893
  );
8734
8894
  }
8735
8895
  const ndV = v(ctx, "_nd");
8736
- const buildExpr = structural ? `{ _tag: 'node', type: ${JSON.stringify(def.type)}, span: { start: ${pos}, end: ${endVar} }, state: ${stV} ?? null, children: ${chV} }` : mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : def.project !== void 0 ? emitNodeProjectExpr(def.type, def.project, chV) : `${buildRef(ctx)}[${buildIdx}](${chV}, ${fObj}, { start: ${pos}, end: ${endVar} }, ${rawV}, ${tlV}, ${stV})`;
8737
- const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, { start: ${pos}, end: ${endVar} }, ${rawV}, ${tlV}, ${stV})`;
8896
+ const nodeSpanExpr = emitSpanExpr(ctx, pos, endVar);
8897
+ const nodeSpanV = ctx.lineTracking ? v(ctx, "_nspan") : nodeSpanExpr;
8898
+ if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
8899
+ 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})`;
8900
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
8738
8901
  const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
8739
8902
  const hostCollapseExpr = structural ? `_ctx.build !== undefined && _ctx.build._parsemanCstCollapse !== undefined && ${chV}.length === 1 && ${rawV}.length === 1 && _ctx.build._parsemanCstCollapse(${JSON.stringify(def.type)}, ${chV}[0], ${chV}, ${rawV}) ? ${chV}[0] : (${ndExpr})` : ndExpr;
8740
8903
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
@@ -8744,7 +8907,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8744
8907
  `${i}const ${ndV} = (${profileRecognizer} || ${profileCapture}) ? undefined : (${finalExpr})`,
8745
8908
  `${i}if (!(${profileRecognizer})) {`,
8746
8909
  `${i} if (${sc}) ${sc}.push(${ndV})`,
8747
- `${i} if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && (${ndV}._tag === 'node' || ${ndV}._tag === 'leaf' || ${ndV}._tag === 'parseError')) ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : (typeof ${ndV} === 'object' && ${ndV} !== null ? input.slice(${pos}, ${endVar}) : ''), span: { start: ${pos}, end: ${endVar} } })`,
8910
+ `${i} if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && (${ndV}._tag === 'node' || ${ndV}._tag === 'leaf' || ${ndV}._tag === 'parseError')) ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : (typeof ${ndV} === 'object' && ${ndV} !== null ? input.slice(${pos}, ${endVar}) : ''), span: ${nodeSpanV} })`,
8748
8911
  `${i}}`
8749
8912
  );
8750
8913
  return { stmts, valueVar: ndV, endVar };
@@ -8760,7 +8923,8 @@ function emitRuntimeFallback(parser2, ctx, pos) {
8760
8923
  `${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
8761
8924
  ...emitIfFail(ctx, `!${rv}.ok`, failStmt),
8762
8925
  `${ind(ctx)}const ${vv} = ${rv}.value`,
8763
- `${ind(ctx)}const ${ev} = ${rv}.span.end`
8926
+ `${ind(ctx)}const ${ev} = ${rv}.span.end`,
8927
+ ...parser2._meta.canMatchNewline ? emitLineTrack(ctx, pos, ev) : []
8764
8928
  ];
8765
8929
  return { stmts, valueVar: vv, endVar: ev };
8766
8930
  }
@@ -8826,7 +8990,8 @@ function emitRecover(def, ctx, pos) {
8826
8990
  `${ind0} if (${sentOk}) break`,
8827
8991
  `${ind0} ${scanV}++`,
8828
8992
  `${ind0} }`,
8829
- `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(deriveExpected(def.parser))} }`,
8993
+ ...emitLineTrack(ctx, pos, scanV).map((s) => `${ind0} ${s.trim()}`),
8994
+ `${ind0} const ${errV} = { _tag: 'parseError', span: ${emitSpanExpr(ctx, pos, scanV)}, expected: ${JSON.stringify(deriveExpected(def.parser))} }`,
8830
8995
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
8831
8996
  `${ind0} ${valVar} = ${errV}`,
8832
8997
  `${ind0} ${endVar} = ${scanV}`,
@@ -8842,7 +9007,7 @@ function emitExpect(def, ctx, pos) {
8842
9007
  const stmts = [
8843
9008
  ...innerStmts,
8844
9009
  `${ind0}if (!${okVar}) {`,
8845
- `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${pos} }, expected: ${JSON.stringify(def.expected)} }`,
9010
+ `${ind0} const ${errV} = { _tag: 'parseError', span: ${emitSpanExpr(ctx, pos, pos)}, expected: ${JSON.stringify(def.expected)} }`,
8846
9011
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
8847
9012
  // Embed the missing-token error as a `parseError` CST child, mirroring the
8848
9013
  // interpreter (expect.ts) and the list-recovery emit sites — guarded on
@@ -8932,7 +9097,7 @@ function emitDispatch(p, ctx, pos) {
8932
9097
  case "literal":
8933
9098
  return emitLit(def, ctx, pos);
8934
9099
  case "regex":
8935
- return emitRegex(def, ctx, pos);
9100
+ return emitRegex(def, ctx, pos, p._meta.canMatchNewline);
8936
9101
  case "keywords":
8937
9102
  return emitKeywords(def, ctx, pos);
8938
9103
  case "sequence":
@@ -9182,6 +9347,20 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
9182
9347
  return false;
9183
9348
  }
9184
9349
  }
9350
+ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9351
+ if (seen.has(p)) return false;
9352
+ seen.add(p);
9353
+ const d = p._def;
9354
+ if (d.tag === "grammar" && d.trackLines) return true;
9355
+ if (d.tag === "lazy") {
9356
+ try {
9357
+ return hasLineTrackingDef(d.thunk(), seen);
9358
+ } catch {
9359
+ return false;
9360
+ }
9361
+ }
9362
+ return childrenOf2(d).some((child) => hasLineTrackingDef(child, seen));
9363
+ }
9185
9364
  function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9186
9365
  if (seen.has(p)) return false;
9187
9366
  seen.add(p);
@@ -9549,6 +9728,7 @@ function compile(combinator, mapFnSources, opts) {
9549
9728
  capturing: hasNodeDef(combinator),
9550
9729
  recovery: opts?.recovery ?? false,
9551
9730
  hostMode: opts?.hostMode ?? grammarHostMode ?? "ast",
9731
+ lineTracking: opts?.trackLines === true || hasLineTrackingDef(combinator),
9552
9732
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
9553
9733
  lazyUsage: analyzeLazyUsage(combinator),
9554
9734
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
@@ -9572,9 +9752,13 @@ function compile(combinator, mapFnSources, opts) {
9572
9752
  const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
9573
9753
  const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
9574
9754
  const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
9755
+ const lineTrackDecls = ctx.needsLineTrack ? [LINE_TRACK_DECL] : [];
9756
+ const lineSpanDecls = ctx.needsLineSpan ? [LINE_SPAN_DECL] : [];
9575
9757
  const source = [
9576
9758
  ...emptyTlDecls,
9577
9759
  ...hostReadsDecls,
9760
+ ...lineTrackDecls,
9761
+ ...lineSpanDecls,
9578
9762
  ...ctx.regexDecls,
9579
9763
  ...ctx.expectedDecls,
9580
9764
  "",
@@ -9587,6 +9771,8 @@ function compile(combinator, mapFnSources, opts) {
9587
9771
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
9588
9772
  ...emptyTlDecls,
9589
9773
  ...hostReadsDecls,
9774
+ ...lineTrackDecls,
9775
+ ...lineSpanDecls,
9590
9776
  ...ctx.regexDecls,
9591
9777
  ...ctx.expectedDecls,
9592
9778
  ...namedPrelude,
@@ -9605,6 +9791,25 @@ function compile(combinator, mapFnSources, opts) {
9605
9791
  trackLines: false,
9606
9792
  ...ctx.triviaKindLabels ? { triviaKindLabels: ctx.triviaKindLabels } : {}
9607
9793
  };
9794
+ const annotateWithTrackedLines = (result, lineStarts) => {
9795
+ const index = normalizeLineIndex({ lineStarts });
9796
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
9797
+ const errors = annotated.errors;
9798
+ if (errors) {
9799
+ ;
9800
+ annotated.errors = errors.map((error) => ({
9801
+ ...error,
9802
+ span: annotateSpan(error.span, index)
9803
+ }));
9804
+ }
9805
+ return annotated;
9806
+ };
9807
+ const trackedCtx = (base, input, pos) => {
9808
+ if (!ctx.lineTracking) return base;
9809
+ const lineIndex = { lineStarts: [0] };
9810
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
9811
+ return { ...base, trackLines: true, _lineStarts: lineIndex.lineStarts, _lineScannedTo: pos };
9812
+ };
9608
9813
  const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
9609
9814
  const effectiveSources = mapFnSources ?? derivedSrcs;
9610
9815
  const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
@@ -9612,17 +9817,46 @@ function compile(combinator, mapFnSources, opts) {
9612
9817
  const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
9613
9818
  const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
9614
9819
  const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources, coverageRootRuleId) : null;
9615
- return {
9820
+ const compiledMeta = {
9616
9821
  source,
9617
9822
  inlineExpression,
9618
9823
  ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions },
9619
- ...gatingReport === void 0 ? {} : { gating: gatingReport },
9824
+ ...gatingReport === void 0 ? {} : { gating: gatingReport }
9825
+ };
9826
+ if (!ctx.lineTracking) {
9827
+ return {
9828
+ ...compiledMeta,
9829
+ parse(input, pos = 0) {
9830
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
9831
+ },
9832
+ parseWithContext(input, parseCtx, pos = 0) {
9833
+ assertHostModeCompatible(ctx.hostMode ?? "ast", parseCtx.build, !!ctx.hostBranchElided);
9834
+ return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9835
+ },
9836
+ // Note: collects expect() errors via _errors. Unlike interpreter
9837
+ // parse({recover:true}) it does NOT populate furthestFail — the compiled path
9838
+ // inlines failures for throughput and deliberately skips _probe bookkeeping.
9839
+ // Callers wanting a furthest-position diagnostic detect unconsumed input
9840
+ // (span.end < input.length) instead, which is mode-agnostic.
9841
+ parseWithErrors(input, pos = 0) {
9842
+ const errors = [];
9843
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
9844
+ return { ...result, errors };
9845
+ }
9846
+ };
9847
+ }
9848
+ return {
9849
+ ...compiledMeta,
9620
9850
  parse(input, pos = 0) {
9621
- return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
9851
+ const parseCtx = trackedCtx(defaultCtx, input, pos);
9852
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9853
+ return annotateWithTrackedLines(result, parseCtx._lineStarts);
9622
9854
  },
9623
9855
  parseWithContext(input, parseCtx, pos = 0) {
9624
9856
  assertHostModeCompatible(ctx.hostMode ?? "ast", parseCtx.build, !!ctx.hostBranchElided);
9625
- return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
9857
+ const activeCtx = trackedCtx(parseCtx, input, pos);
9858
+ const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, activeCtx);
9859
+ return activeCtx.trackLines ? annotateWithTrackedLines(result, activeCtx._lineStarts) : result;
9626
9860
  },
9627
9861
  // Note: collects expect() errors via _errors. Unlike interpreter
9628
9862
  // parse({recover:true}) it does NOT populate furthestFail — the compiled path
@@ -9631,8 +9865,9 @@ function compile(combinator, mapFnSources, opts) {
9631
9865
  // (span.end < input.length) instead, which is mode-agnostic.
9632
9866
  parseWithErrors(input, pos = 0) {
9633
9867
  const errors = [];
9634
- const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
9635
- return { ...result, errors };
9868
+ const parseCtx = trackedCtx({ ...defaultCtx, _errors: errors }, input, pos);
9869
+ const result = { ...fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx), errors };
9870
+ return annotateWithTrackedLines(result, parseCtx._lineStarts);
9636
9871
  }
9637
9872
  };
9638
9873
  }
@@ -9716,6 +9951,8 @@ function compileRuleMap(ruleMap, opts) {
9716
9951
  const hoistedDecls = [
9717
9952
  ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
9718
9953
  ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "",
9954
+ ctx.needsLineTrack ? ` ${LINE_TRACK_DECL}` : "",
9955
+ ctx.needsLineSpan ? ` ${LINE_SPAN_DECL}` : "",
9719
9956
  ...ctx.regexDecls.map((d) => ` ${d}`),
9720
9957
  ...ctx.expectedDecls.map((d) => ` ${d}`),
9721
9958
  mfDecl,
@@ -10045,12 +10282,16 @@ function buildInlineExpression(ctx, r, mapFnSources, buildSources, coverageRootR
10045
10282
  const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
10046
10283
  const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
10047
10284
  const hostReadsDecl = ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "";
10048
- const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl;
10285
+ const lineTrackDecl = ctx.needsLineTrack ? ` ${LINE_TRACK_DECL}` : "";
10286
+ const lineSpanDecl = ctx.needsLineSpan ? ` ${LINE_SPAN_DECL}` : "";
10287
+ const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl || !!hostReadsDecl || !!lineTrackDecl || !!lineSpanDecl;
10049
10288
  if (!needsWrapper) return innerFn;
10050
10289
  const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
10051
10290
  const hoistedDecls = [
10052
10291
  emptyTlDecl,
10053
10292
  hostReadsDecl,
10293
+ lineTrackDecl,
10294
+ lineSpanDecl,
10054
10295
  ...ctx.regexDecls.map((d) => ` ${d}`),
10055
10296
  ...ctx.expectedDecls.map((d) => ` ${d}`),
10056
10297
  mfDecl,
@@ -10589,7 +10830,7 @@ function normalizeCstCollapse(collapse) {
10589
10830
  return typeof collapse === "function" ? collapse : void 0;
10590
10831
  }
10591
10832
  function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
10592
- return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children2] };
10833
+ return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children2] };
10593
10834
  }
10594
10835
  function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
10595
10836
  if (typeof typeOrOptions === "string") {