parseman 0.40.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 (54) hide show
  1. package/README.md +7 -6
  2. package/dist/combinators/expect.d.ts.map +1 -1
  3. package/dist/combinators/grammar.d.ts.map +1 -1
  4. package/dist/combinators/literal.d.ts.map +1 -1
  5. package/dist/combinators/node.d.ts.map +1 -1
  6. package/dist/combinators/regex.d.ts.map +1 -1
  7. package/dist/combinators/scanTo.d.ts.map +1 -1
  8. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  9. package/dist/compiler/codegen.d.ts +3 -0
  10. package/dist/compiler/codegen.d.ts.map +1 -1
  11. package/dist/compiler/inline-build.d.ts +1 -1
  12. package/dist/compiler/inline-build.d.ts.map +1 -1
  13. package/dist/compiler/inline-callback.d.ts +9 -0
  14. package/dist/compiler/inline-callback.d.ts.map +1 -1
  15. package/dist/compiler/line-index.d.ts +1 -15
  16. package/dist/compiler/line-index.d.ts.map +1 -1
  17. package/dist/compiler/value-usage.d.ts.map +1 -1
  18. package/dist/cst/capture-buffer.d.ts.map +1 -1
  19. package/dist/cst/trivia-entries.d.ts +52 -0
  20. package/dist/cst/trivia-entries.d.ts.map +1 -1
  21. package/dist/functional/run.d.ts +11 -2
  22. package/dist/functional/run.d.ts.map +1 -1
  23. package/dist/index.cjs +634 -169
  24. package/dist/index.cjs.map +4 -4
  25. package/dist/index.d.ts +4 -4
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +634 -169
  28. package/dist/index.js.map +4 -4
  29. package/dist/language-service/index.cjs +203 -4
  30. package/dist/language-service/index.cjs.map +4 -4
  31. package/dist/language-service/index.js +203 -4
  32. package/dist/language-service/index.js.map +4 -4
  33. package/dist/line-index.d.ts +41 -0
  34. package/dist/line-index.d.ts.map +1 -0
  35. package/dist/plugin/index.cjs +454 -126
  36. package/dist/plugin/index.cjs.map +4 -4
  37. package/dist/plugin/index.js +454 -126
  38. package/dist/plugin/index.js.map +4 -4
  39. package/dist/recovery/scan.d.ts.map +1 -1
  40. package/dist/regex/first-set.d.ts.map +1 -1
  41. package/dist/run/index.cjs +202 -2
  42. package/dist/run/index.cjs.map +4 -4
  43. package/dist/run/index.d.ts +8 -4
  44. package/dist/run/index.d.ts.map +1 -1
  45. package/dist/run/index.js +202 -2
  46. package/dist/run/index.js.map +4 -4
  47. package/dist/spec/index.cjs +174 -52
  48. package/dist/spec/index.cjs.map +4 -4
  49. package/dist/spec/index.js +174 -52
  50. package/dist/spec/index.js.map +4 -4
  51. package/dist/types.d.ts +12 -0
  52. package/dist/types.d.ts.map +1 -1
  53. package/dist/version.d.ts +1 -1
  54. 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) {
@@ -2586,9 +2731,11 @@ function markUnusedValues(root) {
2586
2731
  visit(def.parser, consumed);
2587
2732
  visit(def.sentinel, false);
2588
2733
  return;
2734
+ case "token":
2735
+ visit(def.parser, false);
2736
+ return;
2589
2737
  case "label":
2590
2738
  case "trivia":
2591
- case "token":
2592
2739
  case "attempt":
2593
2740
  case "withCtx":
2594
2741
  case "expect":
@@ -3074,13 +3221,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3074
3221
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3075
3222
  const nodeType = def.type ?? missingInferredType();
3076
3223
  const cstOutput = cstOutputHost(ctx.build);
3224
+ const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3077
3225
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3078
- 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 };
3079
- 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 };
3080
3228
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3081
3229
  pushCstChild(ctx, built, rawEntry);
3082
3230
  }
3083
- return { ok: true, value: built, span: r.span };
3231
+ return { ok: true, value: built, span };
3084
3232
  }
3085
3233
  };
3086
3234
  }
@@ -3151,38 +3299,24 @@ function field(name, combinator) {
3151
3299
  };
3152
3300
  }
3153
3301
 
3154
- // src/compiler/line-index.ts
3155
- function buildLineIndex(input) {
3156
- const lineStarts = [0];
3157
- for (let i = 0; i < input.length; i++) {
3158
- if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
3159
- }
3160
- return { lineStarts };
3161
- }
3162
- function offsetToLineCol(index, offset) {
3163
- const { lineStarts } = index;
3164
- let lo = 0;
3165
- let hi = lineStarts.length - 1;
3166
- while (lo < hi) {
3167
- const mid = lo + hi + 1 >> 1;
3168
- if (lineStarts[mid] <= offset) lo = mid;
3169
- else hi = mid - 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
+ }));
3170
3312
  }
3171
- return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
3313
+ return annotated;
3172
3314
  }
3173
- function annotateSpan(span, index) {
3174
- const s = offsetToLineCol(index, span.start);
3175
- const e = offsetToLineCol(index, span.end);
3176
- return {
3177
- ...span,
3178
- startLine: s.line,
3179
- startColumn: s.col,
3180
- endLine: e.line,
3181
- endColumn: e.col
3182
- };
3315
+ function createParseLineContext(input, pos) {
3316
+ const lineIndex = createLineIndex();
3317
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
3318
+ return { lineIndex, lineScannedTo: pos };
3183
3319
  }
3184
-
3185
- // src/combinators/grammar.ts
3186
3320
  function parser(opts, root) {
3187
3321
  const clearTrivia = opts.trivia === null;
3188
3322
  return {
@@ -3198,9 +3332,13 @@ function parser(opts, root) {
3198
3332
  },
3199
3333
  parse(input, pos, _ctx) {
3200
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;
3201
3337
  const ctx = {
3202
3338
  ..._ctx,
3203
3339
  trackLines,
3340
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
3341
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
3204
3342
  // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
3205
3343
  ...clearTrivia ? {
3206
3344
  trivia: void 0,
@@ -3218,11 +3356,10 @@ function parser(opts, root) {
3218
3356
  ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
3219
3357
  };
3220
3358
  const result = root.parse(input, pos ?? 0, ctx);
3221
- if (trackLines) {
3222
- const idx = buildLineIndex(input);
3223
- 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);
3224
3361
  }
3225
- return result;
3362
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
3226
3363
  }
3227
3364
  };
3228
3365
  }
@@ -3236,25 +3373,25 @@ function parse(combinator, input, opts = {}) {
3236
3373
  markUnusedValues(combinator);
3237
3374
  }
3238
3375
  const trackLines = opts.trackLines ?? false;
3376
+ const lineContext = trackLines ? createParseLineContext(input, 0) : void 0;
3377
+ const lineIndex = lineContext?.lineIndex;
3239
3378
  const _errors = opts.recover ? [] : void 0;
3240
3379
  const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
3241
3380
  const grammarTrivia = combinator._meta.grammarTrivia;
3242
3381
  const grammarScanSkip = combinator._meta.grammarScanSkip;
3243
3382
  const ctx = {
3244
3383
  trackLines,
3384
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
3385
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
3245
3386
  ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
3246
3387
  ...grammarScanSkip !== void 0 ? { scanSkip: grammarScanSkip } : {},
3247
3388
  ..._errors !== void 0 ? { _errors } : {},
3248
3389
  ..._probe !== void 0 ? { _probe } : {}
3249
3390
  };
3250
3391
  const result = combinator.parse(input, 0, ctx);
3251
- if (!result.ok) return result;
3392
+ if (!result.ok) return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
3252
3393
  const withErrors = _errors !== void 0 ? { ...result, errors: _errors, furthestFail: _probe?.best ?? null } : result;
3253
- if (trackLines) {
3254
- const idx = buildLineIndex(input);
3255
- return { ...withErrors, span: annotateSpan(withErrors.span, idx) };
3256
- }
3257
- return withErrors;
3394
+ return lineIndex ? annotateResultLines(withErrors, normalizeLineIndex(lineIndex)) : withErrors;
3258
3395
  }
3259
3396
 
3260
3397
  // src/combinators/token.ts
@@ -3353,7 +3490,7 @@ function leaf(root, fn) {
3353
3490
  }
3354
3491
 
3355
3492
  // src/version.ts
3356
- var PARSEMAN_VERSION = "0.40.0";
3493
+ var PARSEMAN_VERSION = "0.42.0";
3357
3494
 
3358
3495
  // src/cst/host-mode.ts
3359
3496
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -5037,7 +5174,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5037
5174
  let cur = pos;
5038
5175
  const skip3 = resolveScanSkip(explicitSkip, raw, ctx);
5039
5176
  const probeCtx = {
5040
- trackLines: ctx.trackLines,
5177
+ trackLines: false,
5041
5178
  state: ctx.state,
5042
5179
  ...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
5043
5180
  };
@@ -5050,6 +5187,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5050
5187
  while (cur < input.length) {
5051
5188
  const s = sentinel.parse(input, cur, probeCtx);
5052
5189
  if (s.ok) {
5190
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
5053
5191
  emit2(cur);
5054
5192
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
5055
5193
  }
@@ -5065,6 +5203,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
5065
5203
  if (!advanced) cur++;
5066
5204
  }
5067
5205
  if (orEOF) {
5206
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
5068
5207
  emit2(cur);
5069
5208
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
5070
5209
  }
@@ -6297,7 +6436,7 @@ function scanBranchLabeled(shape, kindIndex, mint) {
6297
6436
  ` if (${m.end} > _e) {`,
6298
6437
  ` if (_cap) {`,
6299
6438
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
6300
- ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << kindIndex}))) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
6439
+ ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << kindIndex}))) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
6301
6440
  ` }`,
6302
6441
  ` _e = ${m.end}`,
6303
6442
  ` continue`,
@@ -6353,7 +6492,7 @@ function analyzeTriviaFastPath(trivia2) {
6353
6492
  var CAP_RECORD = [
6354
6493
  ` if (_cap && _e > _pos) {`,
6355
6494
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
6356
- ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
6495
+ ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
6357
6496
  ` }`
6358
6497
  ].join("\n");
6359
6498
  function composeFastLoop(shapes) {
@@ -6413,7 +6552,7 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
6413
6552
  ` const _ce = _e + _m${i}[0].length`,
6414
6553
  ` if (_cap) {`,
6415
6554
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
6416
- ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
6555
+ ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
6417
6556
  ` }`,
6418
6557
  ` _e = _ce`,
6419
6558
  ` _matched = true`,
@@ -6451,7 +6590,7 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
6451
6590
  ` const _ce = _r${i}.span.end`,
6452
6591
  ` if (_cap) {`,
6453
6592
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
6454
- ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
6593
+ ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
6455
6594
  ` }`,
6456
6595
  ` _e = _ce`,
6457
6596
  ` _matched = true`,
@@ -6503,8 +6642,8 @@ function analyzeMkInlineBuild(def) {
6503
6642
  if (looseMkBuildRe(def.type).test(src)) return def.type;
6504
6643
  return null;
6505
6644
  }
6506
- function emitInlineMkNodeExpr(type, chV, rawV, pos, endVar, tlV) {
6507
- 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 }`;
6508
6647
  }
6509
6648
 
6510
6649
  // src/compiler/inline-callback.ts
@@ -6537,6 +6676,9 @@ function transformFnSource(fn, fnSrc) {
6537
6676
  function replaceParam(body, param, valueVar) {
6538
6677
  return body.replace(new RegExp(`\\b${escapeRegExp2(param)}\\b`, "g"), valueVar);
6539
6678
  }
6679
+ function hasExplicitObjectKey(body, param) {
6680
+ return new RegExp(`\\b${escapeRegExp2(param)}\\b\\s*:`).test(body);
6681
+ }
6540
6682
  function stripForIdCheck(body) {
6541
6683
  return body.replace(/'[^'\\]*(?:\\.[^'\\]*)*'|"[^"\\]*(?:\\.[^"\\]*)*"/g, '""').replace(/\bas\s+const\b/g, "").replace(/\b[A-Za-z_$][\w$]*\s*:/g, ":");
6542
6684
  }
@@ -6579,10 +6721,19 @@ function tryInlineDestructureTransform(src, valueVars) {
6579
6721
  const allowed = new Set(valueVars);
6580
6722
  for (let i = 0; i < slots.length; i++) {
6581
6723
  const name = slots[i];
6582
- if (name) body = replaceParam(body, name, valueVars[i]);
6724
+ if (!name) continue;
6725
+ if (hasExplicitObjectKey(body, name)) return null;
6726
+ body = replaceParam(body, name, valueVars[i]);
6583
6727
  }
6584
6728
  return hasFreeIdentifiers(body, allowed) ? null : body;
6585
6729
  }
6730
+ function isDispatchTailOnlyTransform(src) {
6731
+ const m = src.match(/^\(\s*\[([^\]]*)\]\s*\)\s*=>\s*(.+)$/s);
6732
+ if (!m) return false;
6733
+ const slots = parseArrayDestructure(m[1], 2);
6734
+ const tail = slots[1];
6735
+ return tail !== null && m[2].trim() === tail;
6736
+ }
6586
6737
 
6587
6738
  // src/compiler/codegen.ts
6588
6739
  function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
@@ -6674,6 +6825,8 @@ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
6674
6825
  }
6675
6826
  }
6676
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] } }";
6677
6830
  var _loweringSink = null;
6678
6831
  function v(ctx, prefix = "_v") {
6679
6832
  return `${prefix}${ctx.vars++}`;
@@ -6814,14 +6967,46 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
6814
6967
  if (ctx.capAsTrivia) return [];
6815
6968
  const i = ind(ctx);
6816
6969
  const lf = v(ctx, "_lf");
6970
+ const spanExpr = emitSpanExpr(ctx, startExpr, endExpr);
6817
6971
  return [
6818
6972
  `${i}if (_ctx._cstLeaves || _ctx._cstRawChildren) {`,
6819
- `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } }`,
6973
+ `${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: ${spanExpr} }`,
6820
6974
  `${i} if (_ctx._cstLeaves) _ctx._cstLeaves.push(${lf})`,
6821
6975
  `${i} if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(${lf})`,
6822
6976
  `${i}}`
6823
6977
  ];
6824
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
+ }
6825
7010
  function ensureRegexDecl(ctx, source, flags) {
6826
7011
  const f = "y" + flags.replace(/[gy]/g, "");
6827
7012
  const key = `${source}/${f}`;
@@ -6833,6 +7018,17 @@ function ensureRegexDecl(ctx, source, flags) {
6833
7018
  }
6834
7019
  return rName;
6835
7020
  }
7021
+ function ensurePlainRegexDecl(ctx, source, flags) {
7022
+ const f = flags.replace(/[gy]/g, "");
7023
+ const key = `${source}/${f}`;
7024
+ let rName = ctx.regexMap.get(key);
7025
+ if (rName === void 0) {
7026
+ rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
7027
+ ctx.regexDecls.push(`const ${rName} = ${new RegExp(source, f).toString()}`);
7028
+ ctx.regexMap.set(key, rName);
7029
+ }
7030
+ return rName;
7031
+ }
6836
7032
  function ensureTriviaFn(ctx) {
6837
7033
  const trivia2 = ctx.activeTrivia;
6838
7034
  const existing = ctx.triviaFnNames.get(trivia2);
@@ -6898,7 +7094,7 @@ function ensureTriviaFn(ctx) {
6898
7094
  ` }`,
6899
7095
  ` if (_cap && _e > _pos) {`,
6900
7096
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
6901
- ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
7097
+ ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
6902
7098
  ` }`,
6903
7099
  ` return _e`,
6904
7100
  `}`
@@ -7195,6 +7391,7 @@ function emitLit(def, ctx, pos) {
7195
7391
  );
7196
7392
  }
7197
7393
  const endVar = len === 0 ? pos : `${pos} + ${len}`;
7394
+ stmts.push(...emitLiteralLineTrack(ctx, pos, value));
7198
7395
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7199
7396
  return { stmts, valueVar: vv, endVar };
7200
7397
  }
@@ -7274,7 +7471,7 @@ function emitKeywords(def, ctx, pos) {
7274
7471
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7275
7472
  return { stmts, valueVar: vv, endVar };
7276
7473
  }
7277
- function emitRegex(def, ctx, pos) {
7474
+ function emitRegex(def, ctx, pos, canMatchNewline = true) {
7278
7475
  const expectedStr = JSON.stringify(`/${def.source}/`);
7279
7476
  const shape = scanShapeFromRegex(def.source, def.flags);
7280
7477
  if (shape) {
@@ -7287,7 +7484,11 @@ function emitRegex(def, ctx, pos) {
7287
7484
  fresh: (prefix) => v(ctx, prefix)
7288
7485
  });
7289
7486
  if (scanned) {
7290
- 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
+ ];
7291
7492
  return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
7292
7493
  }
7293
7494
  }
@@ -7309,6 +7510,7 @@ function emitRegex(def, ctx, pos) {
7309
7510
  `${ind(ctx)}const ${vv} = ${mv}[0]`
7310
7511
  ];
7311
7512
  const endVar = `${pos} + ${vv}.length`;
7513
+ if (canMatchNewline) stmts.push(...emitLineTrack(ctx, pos, endVar));
7312
7514
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
7313
7515
  return { stmts, valueVar: vv, endVar };
7314
7516
  }
@@ -7346,7 +7548,8 @@ function emitSeqValues(def, ctx, pos) {
7346
7548
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
7347
7549
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
7348
7550
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7349
- `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`
7551
+ `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`,
7552
+ ...emitLineTrack(ctx, curV, scanEndV)
7350
7553
  );
7351
7554
  const r2 = emit(def.parsers[i], ctx, scanEndV);
7352
7555
  stmts.push(...r2.stmts);
@@ -7359,14 +7562,20 @@ function emitSeqValues(def, ctx, pos) {
7359
7562
  continue;
7360
7563
  } else {
7361
7564
  const trivFn = ensureTriviaFn(ctx);
7565
+ const markLog = v(ctx, "_mklg");
7362
7566
  const scanEndV = v(ctx, "_sne");
7363
- stmts.push(`${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx)`);
7567
+ const capArg = ctx.noHoist ? "0" : "_ctx._triviaLog !== undefined ? 2 : 0";
7568
+ stmts.push(
7569
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7570
+ `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`,
7571
+ ...emitLineTrack(ctx, curV, scanEndV)
7572
+ );
7364
7573
  const r2 = emit(def.parsers[i], ctx, scanEndV);
7365
7574
  stmts.push(...r2.stmts);
7366
7575
  const endAfterV = v(ctx, "_sea");
7367
7576
  stmts.push(
7368
7577
  `${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
7369
- `${ind(ctx)}if (${endAfterV} > ${scanEndV}) ${curV} = ${endAfterV}`
7578
+ `${ind(ctx)}if (${endAfterV} > ${scanEndV}) ${curV} = ${endAfterV}; else if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}`
7370
7579
  );
7371
7580
  valueVars.push(r2.valueVar);
7372
7581
  continue;
@@ -7596,19 +7805,20 @@ function mayRecordRecoveryError(p, recovery, seen = /* @__PURE__ */ new Set()) {
7596
7805
  return false;
7597
7806
  }
7598
7807
  }
7599
- function emitDispatchCombinator(parser2, def, ctx, pos) {
7808
+ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7600
7809
  if (parserUsesRouted2(def.selector)) {
7601
7810
  throw new Error("parseman: routed() can only appear inside a dispatch() branch");
7602
7811
  }
7603
7812
  const coverageIds = ctx.coverage?.plan.dispatches.get(parser2) ?? [];
7604
7813
  const branchUsesRouted2 = (entry) => entry.usesRouted === true || parserUsesRouted2(entry.parser);
7605
7814
  const hasRoutedBranch = def.cases.some(branchUsesRouted2) || (def.matchers ? def.matchers.some(branchUsesRouted2) : false) || (def.otherwise ? def.otherwiseUsesRouted === true || parserUsesRouted2(def.otherwise) : false);
7606
- const selectorLeafMark = hasRoutedBranch ? v(ctx, "_dsl") : null;
7607
- const selectorRawMark = hasRoutedBranch ? v(ctx, "_dsr") : null;
7608
- const selectorTriviaMark = hasRoutedBranch ? v(ctx, "_dst") : null;
7609
- const selectorLogMark = hasRoutedBranch ? v(ctx, "_dsg") : null;
7610
- const selectorFieldMark = hasRoutedBranch ? v(ctx, "_dsf") : null;
7611
- const selectorErrorMark = hasRoutedBranch ? v(ctx, "_dse") : null;
7815
+ const selectorNeedsRollback = hasRoutedBranch && (ctx.capturing === true && capturesLeaf(def.selector) || ctx.activeTrivia !== void 0 && parserHasTriviaSite(def.selector) || parserEnablesTriviaCapture(def.selector) || parserHasOwnFields(def.selector) || mayRecordRecoveryError(def.selector, !!ctx.recovery));
7816
+ const selectorLeafMark = selectorNeedsRollback ? v(ctx, "_dsl") : null;
7817
+ const selectorRawMark = selectorNeedsRollback ? v(ctx, "_dsr") : null;
7818
+ const selectorTriviaMark = selectorNeedsRollback ? v(ctx, "_dst") : null;
7819
+ const selectorLogMark = selectorNeedsRollback ? v(ctx, "_dsg") : null;
7820
+ const selectorFieldMark = selectorNeedsRollback ? v(ctx, "_dsf") : null;
7821
+ const selectorErrorMark = selectorNeedsRollback ? v(ctx, "_dse") : null;
7612
7822
  const selector = emit(def.selector, ctx, pos);
7613
7823
  const outV = v(ctx, "_dval");
7614
7824
  const outE = v(ctx, "_dend");
@@ -7633,13 +7843,14 @@ function emitDispatchCombinator(parser2, def, ctx, pos) {
7633
7843
  stmts.push(head);
7634
7844
  ctx.indent++;
7635
7845
  const usesRouted = branchUsesRouted2(entry);
7636
- const routedV = usesRouted ? v(ctx, "_drt") : null;
7846
+ const needsRoutedContext = usesRouted && routedNeedsContextBridge(parser3, ctx);
7847
+ const routedV = needsRoutedContext ? v(ctx, "_drt") : null;
7637
7848
  const mayError = mayRecordRecoveryError(parser3, !!ctx.recovery);
7638
7849
  const errMark = mayError ? v(ctx, "_derr") : null;
7639
7850
  const logMark = ctx.activeTrivia ? v(ctx, "_dlog") : null;
7640
- if (routedV) {
7851
+ if (usesRouted) {
7641
7852
  stmts.push(
7642
- `${ind(ctx)}const ${routedV} = _ctx._routed; _ctx._routed = { value: ${selector.valueVar}, span: { start: ${pos}, end: ${selector.endVar} } }`,
7853
+ ...routedV ? [`${ind(ctx)}const ${routedV} = _ctx._routed; _ctx._routed = { value: ${selector.valueVar}, span: { start: ${pos}, end: ${selector.endVar} } }`] : [],
7643
7854
  ...selectorLeafMark ? [
7644
7855
  `${ind(ctx)}if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${selectorLeafMark}) _ctx._cstLeaves.length = ${selectorLeafMark}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${selectorRawMark}) _ctx._cstRawChildren.length = ${selectorRawMark}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${selectorTriviaMark}) _ctx._cstTriviaLog.length = ${selectorTriviaMark}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${selectorLogMark}) _ctx._triviaLog.length = ${selectorLogMark}; if (_ctx._fields && _ctx._fields.length !== ${selectorFieldMark}) _ctx._fields.length = ${selectorFieldMark}; if (_ctx._errors && _ctx._errors.length !== ${selectorErrorMark}) _ctx._errors.length = ${selectorErrorMark}`
7645
7856
  ] : []
@@ -7647,7 +7858,17 @@ function emitDispatchCombinator(parser2, def, ctx, pos) {
7647
7858
  }
7648
7859
  if (errMark) stmts.push(`${ind(ctx)}const ${errMark} = _ctx._errors?.length ?? 0`);
7649
7860
  if (logMark) stmts.push(`${ind(ctx)}const ${logMark} = _ctx._triviaLog?.length ?? 0`);
7861
+ const savedRoutedLocal = ctx.routedLocal;
7862
+ const savedNoHoist = ctx.noHoist;
7863
+ if (usesRouted && !needsRoutedContext) {
7864
+ ctx.routedLocal = { valueVar: selector.valueVar, startVar: pos, endVar: selector.endVar };
7865
+ ctx.noHoist = true;
7866
+ } else if (usesRouted) {
7867
+ ctx.routedLocal = void 0;
7868
+ }
7650
7869
  const tail = emitTailFallible(parser3, ctx, usesRouted ? pos : selector.endVar);
7870
+ ctx.routedLocal = savedRoutedLocal;
7871
+ ctx.noHoist = savedNoHoist;
7651
7872
  stmts.push(...coverageAttempt(ctx, coverageId, pos));
7652
7873
  const errorRollback = errMark ? `if (_ctx._errors && _ctx._errors.length !== ${errMark}) _ctx._errors.length = ${errMark}; ` : "";
7653
7874
  const logRollback = logMark ? `if (_ctx._triviaLog && _ctx._triviaLog.length !== ${logMark}) _ctx._triviaLog.length = ${logMark}; ` : "";
@@ -7657,7 +7878,7 @@ function emitDispatchCombinator(parser2, def, ctx, pos) {
7657
7878
  ...tail.stmts,
7658
7879
  `${ind(ctx)}if (!${tail.okVar}) { ${routedRollback}${errorRollback}${logRollback}${coverageFailure}${committedFailBody(ctx)} }`,
7659
7880
  ...routedV ? [`${ind(ctx)}_ctx._routed = ${routedV}`] : [],
7660
- `${ind(ctx)}${outV} = [${selector.valueVar}, ${tail.valVar}]`,
7881
+ `${ind(ctx)}${outV} = ${valueMode === "tail" ? tail.valVar : `[${selector.valueVar}, ${tail.valVar}]`}`,
7661
7882
  `${ind(ctx)}${outE} = ${tail.endVar}`,
7662
7883
  ...coverageHit(ctx, coverageId, pos, tail.endVar)
7663
7884
  );
@@ -7672,7 +7893,7 @@ function emitDispatchCombinator(parser2, def, ctx, pos) {
7672
7893
  }
7673
7894
  const emitMatcherTails = () => {
7674
7895
  for (const matcher of def.matchers ?? []) {
7675
- emitSelectedTail(matcher, wroteBranch ? "else if" : "if", emitDispatchMatcherCondition(keyV, matcher), coverageIds[coverageIndex++]);
7896
+ emitSelectedTail(matcher, wroteBranch ? "else if" : "if", emitDispatchMatcherCondition(ctx, keyV, matcher), coverageIds[coverageIndex++]);
7676
7897
  wroteBranch = true;
7677
7898
  }
7678
7899
  };
@@ -7697,10 +7918,10 @@ function emitDispatchKeyCondition(valueVar, key, caseInsensitive) {
7697
7918
  }
7698
7919
  return parts.join(" && ");
7699
7920
  }
7700
- function emitDispatchMatcherCondition(valueVar, matcher) {
7921
+ function emitDispatchMatcherCondition(ctx, valueVar, matcher) {
7701
7922
  if (matcher.kind === "matches") {
7702
7923
  const flags = matcher.caseInsensitive && !matcher.flags?.includes("i") ? `${matcher.flags ?? ""}i` : matcher.flags ?? "";
7703
- return `${new RegExp(matcher.value, flags).toString()}.test(${valueVar})`;
7924
+ return `${ensurePlainRegexDecl(ctx, matcher.value, flags)}.test(${valueVar})`;
7704
7925
  }
7705
7926
  const text = matcher.value;
7706
7927
  const parts = [`${valueVar}.length >= ${text.length}`];
@@ -7903,6 +8124,28 @@ function emitsAsNamedFn(p, ctx) {
7903
8124
  const usage = ctx.lazyUsage;
7904
8125
  return !!usage && !ctx.noHoist && !ctx.capAsTrivia && isHoistableTag(p._def.tag) && (usage.counts.get(p) ?? 0) > 1 && (usage.sizes.get(p) ?? 0) >= HOIST_MIN_SUBTREE;
7905
8126
  }
8127
+ function routedNeedsContextBridge(p, ctx, seen = /* @__PURE__ */ new Set()) {
8128
+ if (seen.has(p)) return false;
8129
+ seen.add(p);
8130
+ if (p._def.tag === "lazy") {
8131
+ if (ctx.ruleNames?.has(p) || ctx.namedParsers.has(p)) return parserUsesRouted2(p);
8132
+ const usage = ctx.lazyUsage;
8133
+ if (usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
8134
+ try {
8135
+ return routedNeedsContextBridge(p._def.thunk(), ctx, seen);
8136
+ } catch {
8137
+ return parserUsesRouted2(p);
8138
+ }
8139
+ }
8140
+ return parserUsesRouted2(p);
8141
+ }
8142
+ if (ctx.ruleNames?.has(p)) return parserUsesRouted2(p);
8143
+ if (ctx.namedParsers.has(p)) return parserUsesRouted2(p);
8144
+ const d = p._def;
8145
+ if (d.tag === "dispatch") return false;
8146
+ if (d.tag === "withCtx") return parserUsesRouted2(d.parser);
8147
+ return childrenOf2(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8148
+ }
7906
8149
  function armReplayInScope(arm, ctx) {
7907
8150
  let p = arm;
7908
8151
  for (; ; ) {
@@ -8025,14 +8268,15 @@ function emitMany(def, ctx, pos) {
8025
8268
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8026
8269
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8027
8270
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8028
- `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`
8271
+ `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8272
+ ...emitLineTrack(ctx, curV, npV)
8029
8273
  );
8030
8274
  itemPos = npV;
8031
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}; `;
8032
8276
  } else {
8033
8277
  const trivFn = ensureTriviaFn(ctx);
8034
8278
  const npV = v(ctx, "_np");
8035
- 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));
8036
8280
  itemPos = npV;
8037
8281
  }
8038
8282
  }
@@ -8153,15 +8397,16 @@ function emitSepBy(_p, def, ctx, pos) {
8153
8397
  const itemFailBreak = (sepEndVar, sepRb, postSepRb) => def.trailing === void 0 ? `${sepRb}break` : `${postSepRb}${curV} = ${sepEndVar}; break`;
8154
8398
  const postSepMarks = () => {
8155
8399
  if (def.trailing === void 0 || !ctx.capturing) return { decl: [], rb: "" };
8156
- const lv = v(ctx, "_tlv"), rw = v(ctx, "_trw"), tl = v(ctx, "_ttl"), lg = v(ctx, "_tlg");
8400
+ const lv = v(ctx, "_tlv"), rw = v(ctx, "_trw"), tl = v(ctx, "_ttl"), lg = v(ctx, "_tlg"), fl = v(ctx, "_tlf");
8157
8401
  return {
8158
8402
  decl: [
8159
8403
  `${ind(ctx)}const ${lv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8160
8404
  `${ind(ctx)}const ${rw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8161
8405
  `${ind(ctx)}const ${tl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8162
- `${ind(ctx)}const ${lg} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`
8406
+ `${ind(ctx)}const ${lg} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8407
+ `${ind(ctx)}const ${fl} = _ctx._fields ? _ctx._fields.length : 0`
8163
8408
  ],
8164
- rb: `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${lv}) _ctx._cstLeaves.length = ${lv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${rw}) _ctx._cstRawChildren.length = ${rw}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${tl}) _ctx._cstTriviaLog.length = ${tl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${lg}) _ctx._triviaLog.length = ${lg}; `
8409
+ rb: `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${lv}) _ctx._cstLeaves.length = ${lv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${rw}) _ctx._cstRawChildren.length = ${rw}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${tl}) _ctx._cstTriviaLog.length = ${tl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${lg}) _ctx._triviaLog.length = ${lg}; if (_ctx._fields && _ctx._fields.length !== ${fl}) _ctx._fields.length = ${fl}; `
8165
8410
  };
8166
8411
  };
8167
8412
  if (rec) {
@@ -8200,23 +8445,26 @@ function emitSepBy(_p, def, ctx, pos) {
8200
8445
  const markTl = v(ctx, "_mktl");
8201
8446
  const markLog = v(ctx, "_mklg");
8202
8447
  const markLv = v(ctx, "_mklv");
8448
+ const markFld = v(ctx, "_mkf");
8203
8449
  const spV = v(ctx, "_sp");
8204
8450
  stmts.push(
8205
8451
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8206
8452
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8207
8453
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8208
8454
  `${ind(ctx)}const ${markLv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8209
- `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`
8455
+ `${ind(ctx)}const ${markFld} = _ctx._fields ? _ctx._fields.length : 0`,
8456
+ `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8457
+ ...emitLineTrack(ctx, curV, spV)
8210
8458
  );
8211
8459
  sepAtPos = spV;
8212
- 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}; `;
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}; `;
8213
8461
  const sep = emitFallible(def.separator, ctx, sepAtPos, true);
8214
8462
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep;
8215
8463
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}${sep.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
8216
8464
  const post = postSepMarks();
8217
8465
  stmts.push(...post.decl);
8218
8466
  const npV = v(ctx, "_np");
8219
- 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));
8220
8468
  const next = emitFallible(def.parser, ctx, npV, true);
8221
8469
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8222
8470
  stmts.push(
@@ -8228,13 +8476,13 @@ function emitSepBy(_p, def, ctx, pos) {
8228
8476
  } else {
8229
8477
  const trivFn = ensureTriviaFn(ctx);
8230
8478
  const spV = v(ctx, "_sp");
8231
- 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));
8232
8480
  sepAtPos = spV;
8233
8481
  const sep = emitFallible(def.separator, ctx, sepAtPos, true);
8234
8482
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep;
8235
8483
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
8236
8484
  const npV = v(ctx, "_np");
8237
- 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));
8238
8486
  const next = emitFallible(def.parser, ctx, npV, true);
8239
8487
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8240
8488
  stmts.push(
@@ -8247,16 +8495,18 @@ function emitSepBy(_p, def, ctx, pos) {
8247
8495
  } else {
8248
8496
  const markLv = ctx.capturing ? v(ctx, "_mklv") : null;
8249
8497
  const markRw = ctx.capturing ? v(ctx, "_mkrw") : null;
8498
+ const markFld = ctx.capturing ? v(ctx, "_mkf") : null;
8250
8499
  if (markLv) {
8251
8500
  stmts.push(
8252
8501
  `${ind(ctx)}const ${markLv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8253
- `${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
8502
+ `${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8503
+ `${ind(ctx)}const ${markFld} = _ctx._fields ? _ctx._fields.length : 0`
8254
8504
  );
8255
8505
  }
8256
8506
  const sep = emitFallible(def.separator, ctx, sepAtPos, true);
8257
8507
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep;
8258
8508
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
8259
- const nextRb = markLv ? `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${markLv}) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markRw}) _ctx._cstRawChildren.length = ${markRw}; ` : "";
8509
+ const nextRb = markLv ? `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${markLv}) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${markRw}) _ctx._cstRawChildren.length = ${markRw}; if (_ctx._fields && _ctx._fields.length !== ${markFld}) _ctx._fields.length = ${markFld}; ` : "";
8260
8510
  const post = postSepMarks();
8261
8511
  stmts.push(...post.decl);
8262
8512
  const next = emitFallible(def.parser, ctx, sepEnd, true);
@@ -8319,6 +8569,7 @@ function emitScanTo(def, ctx, pos) {
8319
8569
  }
8320
8570
  const valV = v(ctx);
8321
8571
  stmts.push(`${ind(ctx)}const ${valV} = input.slice(${pos}, ${curV})`);
8572
+ stmts.push(...emitLineTrack(ctx, pos, curV));
8322
8573
  if (ctx.capturing) {
8323
8574
  const cap = reindentStmts(emitLeafCapture(ctx, valV, pos, curV), ctx.indent + 1);
8324
8575
  stmts.push(`${ind(ctx)}if (${curV} > ${pos}) {`, ...cap, `${ind(ctx)}}`);
@@ -8326,7 +8577,7 @@ function emitScanTo(def, ctx, pos) {
8326
8577
  return { stmts, valueVar: valV, endVar: curV };
8327
8578
  }
8328
8579
  function emitNot(def, ctx, pos) {
8329
- const sinksLive = !!ctx.capturing || !!ctx.recovery;
8580
+ const sinksLive = !!ctx.capturing || !!ctx.recovery || !!ctx.activeTrivia;
8330
8581
  const leaves = sinksLive ? v(ctx, "_ntl") : null;
8331
8582
  const raw = sinksLive ? v(ctx, "_ntr") : null;
8332
8583
  const tl = sinksLive ? v(ctx, "_ntt") : null;
@@ -8365,6 +8616,17 @@ function emitPeek(def, ctx, pos) {
8365
8616
  };
8366
8617
  }
8367
8618
  function emitRouted(ctx, pos) {
8619
+ const local = ctx.routedLocal;
8620
+ if (local !== void 0) {
8621
+ return {
8622
+ stmts: [
8623
+ ...emitIfFail(ctx, `${local.startVar} !== ${pos}`, failBody(ctx, '"routed()"', pos)),
8624
+ ...emitLeafCapture(ctx, local.valueVar, local.startVar, local.endVar)
8625
+ ],
8626
+ valueVar: local.valueVar,
8627
+ endVar: local.endVar
8628
+ };
8629
+ }
8368
8630
  const item = v(ctx, "_rt");
8369
8631
  return {
8370
8632
  stmts: [
@@ -8451,7 +8713,7 @@ function tokenNullableRegex(def) {
8451
8713
  }
8452
8714
  function emitToken(def, ctx, pos) {
8453
8715
  const lowered = tokenNullableRegex(def);
8454
- if (lowered) return emitRegex(lowered, ctx, pos);
8716
+ if (lowered) return emitRegex(lowered, ctx, pos, def.parser._meta.canMatchNewline);
8455
8717
  const savedTrivia = ctx.activeTrivia;
8456
8718
  const savedKindLabels = ctx.triviaKindLabels;
8457
8719
  ctx.activeTrivia = void 0;
@@ -8601,7 +8863,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8601
8863
  stmts.push(...innerStmts);
8602
8864
  if (def.trailingTrivia === true && ctx.activeTrivia) {
8603
8865
  const triviaFn = ensureTriviaFn(ctx);
8604
- 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));
8605
8867
  }
8606
8868
  if (sf && fArr) {
8607
8869
  stmts.push(`${i}const ${fArr} = _ctx._fields`);
@@ -8641,8 +8903,11 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8641
8903
  );
8642
8904
  }
8643
8905
  const ndV = v(ctx, "_nd");
8644
- 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})`;
8645
- 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})`;
8646
8911
  const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
8647
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;
8648
8913
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
@@ -8652,7 +8917,7 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8652
8917
  `${i}const ${ndV} = (${profileRecognizer} || ${profileCapture}) ? undefined : (${finalExpr})`,
8653
8918
  `${i}if (!(${profileRecognizer})) {`,
8654
8919
  `${i} if (${sc}) ${sc}.push(${ndV})`,
8655
- `${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} })`,
8656
8921
  `${i}}`
8657
8922
  );
8658
8923
  return { stmts, valueVar: ndV, endVar };
@@ -8668,7 +8933,8 @@ function emitRuntimeFallback(parser2, ctx, pos) {
8668
8933
  `${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
8669
8934
  ...emitIfFail(ctx, `!${rv}.ok`, failStmt),
8670
8935
  `${ind(ctx)}const ${vv} = ${rv}.value`,
8671
- `${ind(ctx)}const ${ev} = ${rv}.span.end`
8936
+ `${ind(ctx)}const ${ev} = ${rv}.span.end`,
8937
+ ...parser2._meta.canMatchNewline ? emitLineTrack(ctx, pos, ev) : []
8672
8938
  ];
8673
8939
  return { stmts, valueVar: vv, endVar: ev };
8674
8940
  }
@@ -8734,7 +9000,8 @@ function emitRecover(def, ctx, pos) {
8734
9000
  `${ind0} if (${sentOk}) break`,
8735
9001
  `${ind0} ${scanV}++`,
8736
9002
  `${ind0} }`,
8737
- `${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))} }`,
8738
9005
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
8739
9006
  `${ind0} ${valVar} = ${errV}`,
8740
9007
  `${ind0} ${endVar} = ${scanV}`,
@@ -8750,7 +9017,7 @@ function emitExpect(def, ctx, pos) {
8750
9017
  const stmts = [
8751
9018
  ...innerStmts,
8752
9019
  `${ind0}if (!${okVar}) {`,
8753
- `${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)} }`,
8754
9021
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
8755
9022
  // Embed the missing-token error as a `parseError` CST child, mirroring the
8756
9023
  // interpreter (expect.ts) and the list-recovery emit sites — guarded on
@@ -8840,7 +9107,7 @@ function emitDispatch(p, ctx, pos) {
8840
9107
  case "literal":
8841
9108
  return emitLit(def, ctx, pos);
8842
9109
  case "regex":
8843
- return emitRegex(def, ctx, pos);
9110
+ return emitRegex(def, ctx, pos, p._meta.canMatchNewline);
8844
9111
  case "keywords":
8845
9112
  return emitKeywords(def, ctx, pos);
8846
9113
  case "sequence":
@@ -8860,6 +9127,9 @@ function emitDispatch(p, ctx, pos) {
8860
9127
  return emitSepBy(p, def, ctx, pos);
8861
9128
  case "transform": {
8862
9129
  const fnSrc = transformFnSource(def.fn, def.fnSrc);
9130
+ if (fnSrc && def.parser._def.tag === "dispatch" && isDispatchTailOnlyTransform(fnSrc)) {
9131
+ return emitDispatchCombinator(def.parser, def.parser._def, ctx, pos, "tail");
9132
+ }
8863
9133
  if (fnSrc && def.parser._def.tag === "sequence") {
8864
9134
  const seqR = emitSeqValues(def.parser._def, ctx, pos);
8865
9135
  const inlined = tryInlineDestructureTransform(fnSrc, seqR.valueVars);
@@ -9087,6 +9357,20 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
9087
9357
  return false;
9088
9358
  }
9089
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
+ }
9090
9374
  function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9091
9375
  if (seen.has(p)) return false;
9092
9376
  seen.add(p);
@@ -9454,6 +9738,7 @@ function compile(combinator, mapFnSources, opts) {
9454
9738
  capturing: hasNodeDef(combinator),
9455
9739
  recovery: opts?.recovery ?? false,
9456
9740
  hostMode: opts?.hostMode ?? grammarHostMode ?? "ast",
9741
+ lineTracking: opts?.trackLines === true || hasLineTrackingDef(combinator),
9457
9742
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
9458
9743
  lazyUsage: analyzeLazyUsage(combinator),
9459
9744
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
@@ -9477,9 +9762,13 @@ function compile(combinator, mapFnSources, opts) {
9477
9762
  const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
9478
9763
  const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
9479
9764
  const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
9765
+ const lineTrackDecls = ctx.needsLineTrack ? [LINE_TRACK_DECL] : [];
9766
+ const lineSpanDecls = ctx.needsLineSpan ? [LINE_SPAN_DECL] : [];
9480
9767
  const source = [
9481
9768
  ...emptyTlDecls,
9482
9769
  ...hostReadsDecls,
9770
+ ...lineTrackDecls,
9771
+ ...lineSpanDecls,
9483
9772
  ...ctx.regexDecls,
9484
9773
  ...ctx.expectedDecls,
9485
9774
  "",
@@ -9492,6 +9781,8 @@ function compile(combinator, mapFnSources, opts) {
9492
9781
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
9493
9782
  ...emptyTlDecls,
9494
9783
  ...hostReadsDecls,
9784
+ ...lineTrackDecls,
9785
+ ...lineSpanDecls,
9495
9786
  ...ctx.regexDecls,
9496
9787
  ...ctx.expectedDecls,
9497
9788
  ...namedPrelude,
@@ -9510,6 +9801,25 @@ function compile(combinator, mapFnSources, opts) {
9510
9801
  trackLines: false,
9511
9802
  ...ctx.triviaKindLabels ? { triviaKindLabels: ctx.triviaKindLabels } : {}
9512
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
+ };
9513
9823
  const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
9514
9824
  const effectiveSources = mapFnSources ?? derivedSrcs;
9515
9825
  const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
@@ -9517,17 +9827,46 @@ function compile(combinator, mapFnSources, opts) {
9517
9827
  const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
9518
9828
  const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
9519
9829
  const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources, coverageRootRuleId) : null;
9520
- return {
9830
+ const compiledMeta = {
9521
9831
  source,
9522
9832
  inlineExpression,
9523
9833
  ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions },
9524
- ...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,
9525
9860
  parse(input, pos = 0) {
9526
- 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);
9527
9864
  },
9528
9865
  parseWithContext(input, parseCtx, pos = 0) {
9529
9866
  assertHostModeCompatible(ctx.hostMode ?? "ast", parseCtx.build, !!ctx.hostBranchElided);
9530
- 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;
9531
9870
  },
9532
9871
  // Note: collects expect() errors via _errors. Unlike interpreter
9533
9872
  // parse({recover:true}) it does NOT populate furthestFail — the compiled path
@@ -9536,8 +9875,9 @@ function compile(combinator, mapFnSources, opts) {
9536
9875
  // (span.end < input.length) instead, which is mode-agnostic.
9537
9876
  parseWithErrors(input, pos = 0) {
9538
9877
  const errors = [];
9539
- const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
9540
- 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);
9541
9881
  }
9542
9882
  };
9543
9883
  }
@@ -9549,8 +9889,8 @@ function assertRuleName(name) {
9549
9889
  );
9550
9890
  }
9551
9891
  }
9552
- function publicRuleWrapperSource(rule, fnSource) {
9553
- const labels = rule._meta.triviaKindLabels;
9892
+ function publicRuleWrapperSource(rule, fnSource, ambientTriviaKindLabels) {
9893
+ const labels = rule._meta.triviaKindLabels ?? ambientTriviaKindLabels;
9554
9894
  if (labels === void 0) return fnSource;
9555
9895
  return `Object.assign(${fnSource}, { _meta: { triviaKindLabels: ${JSON.stringify(labels)} } })`;
9556
9896
  }
@@ -9719,7 +10059,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
9719
10059
  ...r.stmts,
9720
10060
  ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
9721
10061
  `}`
9722
- ].join("\n")));
10062
+ ].join("\n"), ctx.triviaKindLabels));
9723
10063
  }
9724
10064
  const firstSets = /* @__PURE__ */ new Map();
9725
10065
  const firstSetRecipes = /* @__PURE__ */ new Map();
@@ -9824,12 +10164,16 @@ function buildInlineExpression(ctx, r, mapFnSources, buildSources, coverageRootR
9824
10164
  const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
9825
10165
  const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
9826
10166
  const hostReadsDecl = ctx.needsHostReads ? ` ${HOST_READS_DECL}` : "";
9827
- 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;
9828
10170
  if (!needsWrapper) return innerFn;
9829
10171
  const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
9830
10172
  const hoistedDecls = [
9831
10173
  emptyTlDecl,
9832
10174
  hostReadsDecl,
10175
+ lineTrackDecl,
10176
+ lineSpanDecl,
9833
10177
  ...ctx.regexDecls.map((d) => ` ${d}`),
9834
10178
  ...ctx.expectedDecls.map((d) => ` ${d}`),
9835
10179
  mfDecl,
@@ -10378,7 +10722,7 @@ function normalizeCstCollapse(collapse) {
10378
10722
  return typeof collapse === "function" ? collapse : void 0;
10379
10723
  }
10380
10724
  function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
10381
- 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] };
10382
10726
  }
10383
10727
  function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
10384
10728
  if (typeof typeOrOptions === "string") {
@@ -11237,8 +11581,158 @@ function parseDoc(registry, rootRule, input, opts = {}) {
11237
11581
  return new ParseDocImpl(registry, rootRule, opts, { abs: null }, [fail], null, input);
11238
11582
  }
11239
11583
 
11584
+ // src/cst/trivia-entries.ts
11585
+ var EMPTY_INDICES = Object.freeze([]);
11586
+ var EMPTY_GAPS = Object.freeze([]);
11587
+ function entryOffset(i, stride) {
11588
+ return i * stride;
11589
+ }
11590
+ function triviaEntries(log, labels, opts) {
11591
+ const baseStride = opts?.nodeLog ? 3 : 2;
11592
+ const stride = labels ? baseStride + 1 : baseStride;
11593
+ const length = Math.floor(log.length / stride);
11594
+ return {
11595
+ length,
11596
+ labels,
11597
+ stride,
11598
+ start(i) {
11599
+ return log[entryOffset(i, stride)];
11600
+ },
11601
+ end(i) {
11602
+ return log[entryOffset(i, stride) + 1];
11603
+ },
11604
+ insertIndex(i) {
11605
+ return opts?.nodeLog ? log[entryOffset(i, stride) + 2] : void 0;
11606
+ },
11607
+ kindIndex(i) {
11608
+ if (!labels) return void 0;
11609
+ return log[entryOffset(i, stride) + baseStride];
11610
+ },
11611
+ kind(i) {
11612
+ const ki = labels ? log[entryOffset(i, stride) + baseStride] : void 0;
11613
+ return ki !== void 0 ? labels[ki] : void 0;
11614
+ },
11615
+ text(i, input) {
11616
+ const s = log[entryOffset(i, stride)];
11617
+ const e = log[entryOffset(i, stride) + 1];
11618
+ return input.slice(s, e);
11619
+ }
11620
+ };
11621
+ }
11622
+ function appendEntryRange(map, key, first, last) {
11623
+ let indices = map.get(key);
11624
+ if (!indices) {
11625
+ indices = [];
11626
+ map.set(key, indices);
11627
+ }
11628
+ for (let i = first; i <= last; i++) indices.push(i);
11629
+ }
11630
+ function buildGap(entries, start, end, first, last) {
11631
+ const entryIndices = [];
11632
+ for (let i = first; i <= last; i++) entryIndices.push(i);
11633
+ return {
11634
+ start,
11635
+ end,
11636
+ entryIndices,
11637
+ hasKind(kind) {
11638
+ for (const i of entryIndices) {
11639
+ if (entries.kind(i) === kind) return true;
11640
+ }
11641
+ return false;
11642
+ },
11643
+ text(input) {
11644
+ return input.slice(start, end);
11645
+ }
11646
+ };
11647
+ }
11648
+ function buildRootMaps(entries) {
11649
+ const before = /* @__PURE__ */ new Map();
11650
+ const after = /* @__PURE__ */ new Map();
11651
+ const beforeGaps = /* @__PURE__ */ new Map();
11652
+ const afterGaps = /* @__PURE__ */ new Map();
11653
+ const gaps = [];
11654
+ if (entries.length === 0) return { before, after, beforeGaps, afterGaps, gaps };
11655
+ const appendGap = (first2, last, start2, end2) => {
11656
+ const gap = buildGap(entries, start2, end2, first2, last);
11657
+ gaps.push(gap);
11658
+ afterGaps.set(start2, gap);
11659
+ beforeGaps.set(end2, gap);
11660
+ appendEntryRange(after, start2, first2, last);
11661
+ appendEntryRange(before, end2, first2, last);
11662
+ };
11663
+ let first = 0;
11664
+ let start = entries.start(0);
11665
+ let end = entries.end(0);
11666
+ for (let i = 1; i < entries.length; i++) {
11667
+ const nextStart = entries.start(i);
11668
+ const nextEnd = entries.end(i);
11669
+ if (nextStart === end) {
11670
+ end = nextEnd;
11671
+ continue;
11672
+ }
11673
+ appendGap(first, i - 1, start, end);
11674
+ first = i;
11675
+ start = nextStart;
11676
+ end = nextEnd;
11677
+ }
11678
+ appendGap(first, entries.length - 1, start, end);
11679
+ return { before, after, beforeGaps, afterGaps, gaps };
11680
+ }
11681
+ function buildRootTriviaIndex(log, labels) {
11682
+ const entries = triviaEntries(log, labels);
11683
+ let maps;
11684
+ const getMaps = () => {
11685
+ maps ??= buildRootMaps(entries);
11686
+ return maps;
11687
+ };
11688
+ return {
11689
+ entries,
11690
+ labels,
11691
+ get before() {
11692
+ return getMaps().before;
11693
+ },
11694
+ get after() {
11695
+ return getMaps().after;
11696
+ },
11697
+ entryIndicesBefore(offset) {
11698
+ return getMaps().before.get(offset) ?? EMPTY_INDICES;
11699
+ },
11700
+ entryIndicesAfter(offset) {
11701
+ return getMaps().after.get(offset) ?? EMPTY_INDICES;
11702
+ },
11703
+ gapBefore(offset) {
11704
+ return getMaps().beforeGaps.get(offset);
11705
+ },
11706
+ gapAfter(offset) {
11707
+ return getMaps().afterGaps.get(offset);
11708
+ },
11709
+ gaps() {
11710
+ return getMaps().gaps;
11711
+ },
11712
+ gapsWithKind(kind) {
11713
+ const kinds = typeof kind === "string" ? [kind] : kind;
11714
+ const matches2 = [];
11715
+ for (const gap of getMaps().gaps) {
11716
+ for (const k of kinds) {
11717
+ if (gap.hasKind(k)) {
11718
+ matches2.push(gap);
11719
+ break;
11720
+ }
11721
+ }
11722
+ }
11723
+ return matches2.length === 0 ? EMPTY_GAPS : matches2;
11724
+ }
11725
+ };
11726
+ }
11727
+
11240
11728
  // src/functional/run.ts
11241
11729
  var invoke = (r, input, pos, ctx) => typeof r === "function" ? r(input, pos, ctx) : r.parse(input, pos, ctx);
11730
+ function triviaKindLabelsFromRunnable(r) {
11731
+ if (!r) return void 0;
11732
+ if (typeof r === "function") return r._meta?.triviaKindLabels;
11733
+ const grammarTrivia = r._meta.grammarTrivia;
11734
+ return r._meta.triviaKindLabels ?? grammarTrivia?._meta.triviaKindLabels ?? (r._def.tag === "grammar" ? r._def.triviaParser?._meta.triviaKindLabels : void 0);
11735
+ }
11242
11736
  function runOnce(entry, input, options, phase, profileState) {
11243
11737
  if (typeof entry !== "function" && typeof entry?.parse !== "function") {
11244
11738
  throw new TypeError(
@@ -11251,6 +11745,7 @@ function runOnce(entry, input, options, phase, profileState) {
11251
11745
  const skipGlobalSinks = phase === "recognizer" || phase === "capture";
11252
11746
  const grammarTrivia = typeof entry !== "function" ? entry._meta.grammarTrivia : void 0;
11253
11747
  const grammarScanSkip = typeof entry !== "function" ? entry._meta.grammarScanSkip : void 0;
11748
+ const triviaKindLabels = triviaKindLabelsFromRunnable(entry) ?? triviaKindLabelsFromRunnable(options.trivia);
11254
11749
  const stamped = entry;
11255
11750
  assertHostModeCompatible(
11256
11751
  typeof entry === "function" ? stamped[FUSED_HOST_MODE] ?? "ast" : entry._meta.grammarHostMode ?? "ast",
@@ -11262,7 +11757,7 @@ function runOnce(entry, input, options, phase, profileState) {
11262
11757
  ...skipGlobalSinks ? { _pmProfile: profile } : { _triviaLog: triviaLog, _errors: errors, ...profile === void 0 ? {} : { _pmProfile: profile } },
11263
11758
  build: options.build,
11264
11759
  state: options.state,
11265
- ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
11760
+ ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...triviaKindLabels ? { triviaKindLabels } : {} } : triviaKindLabels ? { triviaKindLabels } : {},
11266
11761
  ...grammarScanSkip !== void 0 ? { scanSkip: grammarScanSkip } : {},
11267
11762
  ...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {},
11268
11763
  ...options.tolerant ? { _tolerant: true, _rec: REC } : {},
@@ -11285,6 +11780,8 @@ function runOnce(entry, input, options, phase, profileState) {
11285
11780
  expected: r.ok ? [] : r.expected ?? [],
11286
11781
  errors,
11287
11782
  triviaLog,
11783
+ ...triviaKindLabels === void 0 ? {} : { triviaKindLabels },
11784
+ triviaMap: buildRootTriviaIndex(triviaLog, triviaKindLabels),
11288
11785
  unconsumedFrom
11289
11786
  };
11290
11787
  }
@@ -11743,43 +12240,6 @@ function createVisitor(handlers) {
11743
12240
  return (node2) => api.visit(node2);
11744
12241
  }
11745
12242
 
11746
- // src/cst/trivia-entries.ts
11747
- function entryOffset(i, stride) {
11748
- return i * stride;
11749
- }
11750
- function triviaEntries(log, labels, opts) {
11751
- const baseStride = opts?.nodeLog ? 3 : 2;
11752
- const stride = labels ? baseStride + 1 : baseStride;
11753
- const length = Math.floor(log.length / stride);
11754
- return {
11755
- length,
11756
- labels,
11757
- stride,
11758
- start(i) {
11759
- return log[entryOffset(i, stride)];
11760
- },
11761
- end(i) {
11762
- return log[entryOffset(i, stride) + 1];
11763
- },
11764
- insertIndex(i) {
11765
- return opts?.nodeLog ? log[entryOffset(i, stride) + 2] : void 0;
11766
- },
11767
- kindIndex(i) {
11768
- if (!labels) return void 0;
11769
- return log[entryOffset(i, stride) + baseStride];
11770
- },
11771
- kind(i) {
11772
- const ki = labels ? log[entryOffset(i, stride) + baseStride] : void 0;
11773
- return ki !== void 0 ? labels[ki] : void 0;
11774
- },
11775
- text(i, input) {
11776
- const s = log[entryOffset(i, stride)];
11777
- const e = log[entryOffset(i, stride) + 1];
11778
- return input.slice(s, e);
11779
- }
11780
- };
11781
- }
11782
-
11783
12243
  // src/cst/offset-model.ts
11784
12244
  var { isFinite } = Number;
11785
12245
  function floorIndex(arr, len, x) {
@@ -11986,12 +12446,14 @@ export {
11986
12446
  analyzeGatingRules,
11987
12447
  analyzeGrammarGating,
11988
12448
  annotateSpan,
12449
+ annotateTreeSpans,
11989
12450
  applyEdit,
11990
12451
  attempt,
11991
12452
  balanced,
11992
12453
  blankLinesIn,
11993
12454
  buildLineIndex,
11994
12455
  buildOffsetIndex,
12456
+ buildRootTriviaIndex,
11995
12457
  buildTriviaIndex,
11996
12458
  charClassMembers,
11997
12459
  choice,
@@ -12006,6 +12468,7 @@ export {
12006
12468
  createGrammarCoverageCollector,
12007
12469
  createGrammarInstrumentationContext,
12008
12470
  createGrammarTraceSink,
12471
+ createLineIndex,
12009
12472
  createVisitor,
12010
12473
  cstBuildHost,
12011
12474
  dispatch,
@@ -12039,6 +12502,7 @@ export {
12039
12502
  matches,
12040
12503
  noTrivia,
12041
12504
  node,
12505
+ normalizeLineIndex,
12042
12506
  not,
12043
12507
  offsetToLineCol,
12044
12508
  oneOrMore,
@@ -12049,6 +12513,7 @@ export {
12049
12513
  parseDoc,
12050
12514
  parser,
12051
12515
  peek,
12516
+ recordLineRange,
12052
12517
  ref,
12053
12518
  regex,
12054
12519
  relativize,