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
@@ -320,9 +320,11 @@ function markUnusedValues(root) {
320
320
  visit(def.parser, consumed);
321
321
  visit(def.sentinel, false);
322
322
  return;
323
+ case "token":
324
+ visit(def.parser, false);
325
+ return;
323
326
  case "label":
324
327
  case "trivia":
325
- case "token":
326
328
  case "attempt":
327
329
  case "withCtx":
328
330
  case "expect":
@@ -441,6 +443,65 @@ function rules(a, b) {
441
443
  return cache;
442
444
  }
443
445
 
446
+ // src/line-index.ts
447
+ function createLineIndex() {
448
+ return { lineStarts: [0] };
449
+ }
450
+ function recordLineRange(index, input, start, end) {
451
+ const lineStarts = index.lineStarts;
452
+ for (let i = start; i < end; i++) {
453
+ if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
454
+ }
455
+ }
456
+ function recordLineRangeFromContext(ctx, input, _startHint, end) {
457
+ if (!ctx.trackLines) return;
458
+ const from = ctx._lineScannedTo ?? 0;
459
+ if (end <= from) return;
460
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
461
+ if (!index) return;
462
+ recordLineRange(index, input, from, end);
463
+ ctx._lineScannedTo = end;
464
+ }
465
+ function normalizeLineIndex(index) {
466
+ const starts = index.lineStarts;
467
+ starts.sort((a, b) => a - b);
468
+ let write = 0;
469
+ for (let read = 0; read < starts.length; read++) {
470
+ const value = starts[read];
471
+ if (write === 0 || starts[write - 1] !== value) starts[write++] = value;
472
+ }
473
+ starts.length = write;
474
+ if (starts.length === 0 || starts[0] !== 0) starts.unshift(0);
475
+ return index;
476
+ }
477
+ function offsetToLineCol(index, offset) {
478
+ const { lineStarts } = index;
479
+ let lo = 0;
480
+ let hi = lineStarts.length - 1;
481
+ while (lo < hi) {
482
+ const mid = lo + hi + 1 >> 1;
483
+ if (lineStarts[mid] <= offset) lo = mid;
484
+ else hi = mid - 1;
485
+ }
486
+ return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
487
+ }
488
+ function annotateSpan(span, index) {
489
+ const s = offsetToLineCol(index, span.start);
490
+ const e = offsetToLineCol(index, span.end);
491
+ return {
492
+ ...span,
493
+ startLine: s.line,
494
+ startColumn: s.col,
495
+ endLine: e.line,
496
+ endColumn: e.col
497
+ };
498
+ }
499
+ function annotateSpanFromLineContext(span, ctx) {
500
+ if (!ctx.trackLines) return span;
501
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
502
+ return index ? annotateSpan(span, index) : span;
503
+ }
504
+
444
505
  // src/cst/capture-buffer.ts
445
506
  var EMPTY = [];
446
507
  var EMPTY_TL = [];
@@ -482,7 +543,12 @@ function endCstNodeCapture(ctx, saved) {
482
543
  return materialized;
483
544
  }
484
545
  function pushCstLeaf(ctx, leaf2) {
485
- pushCstChild(ctx, leaf2, leaf2);
546
+ if (!ctx.trackLines) {
547
+ pushCstChild(ctx, leaf2, leaf2);
548
+ return;
549
+ }
550
+ const annotated = leaf2 && typeof leaf2 === "object" && leaf2.span ? { ...leaf2, span: annotateSpanFromLineContext(leaf2.span, ctx) } : leaf2;
551
+ pushCstChild(ctx, annotated, annotated);
486
552
  }
487
553
  function pushCstChild(ctx, built, rawEntry) {
488
554
  const b = ctx._cstBuf;
@@ -672,7 +738,9 @@ function firstSetSentinel(fs) {
672
738
  function recoverScan(input, from, ctx, sync, expected) {
673
739
  let scanPos = from;
674
740
  while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
675
- const error = { _tag: "parseError", span: { start: from, end: scanPos }, expected };
741
+ const span = { start: from, end: scanPos };
742
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
743
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
676
744
  ctx._errors?.push(error);
677
745
  return { error, end: scanPos };
678
746
  }
@@ -774,7 +842,8 @@ function expect(combinator, label2) {
774
842
  parse(input, pos, ctx) {
775
843
  const result = combinator.parse(input, pos, ctx);
776
844
  if (result.ok) return result;
777
- const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
845
+ const span = { start: pos, end: pos };
846
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
778
847
  ctx._errors?.push(error);
779
848
  if (ctx._tolerant) captureError(ctx, error);
780
849
  return { ok: true, value: error, span: { start: pos, end: pos } };
@@ -1114,6 +1183,7 @@ function literal(value, opts = {}) {
1114
1183
  canMatchNewline: value.includes("\n"),
1115
1184
  isTrivia: false
1116
1185
  };
1186
+ const canMatchNewline = meta.canMatchNewline;
1117
1187
  if (caseInsensitive) {
1118
1188
  const upper = value.toUpperCase();
1119
1189
  const lower = value.toLowerCase();
@@ -1130,6 +1200,7 @@ function literal(value, opts = {}) {
1130
1200
  return function parse2(input, pos, ctx) {
1131
1201
  if (input.charCodeAt(pos) === code) {
1132
1202
  const span = { start: pos, end: pos + 1 };
1203
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1133
1204
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
1134
1205
  return { ok: true, value, span };
1135
1206
  }
@@ -1143,6 +1214,7 @@ function literal(value, opts = {}) {
1143
1214
  const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
1144
1215
  if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
1145
1216
  const span = { start: pos, end };
1217
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
1146
1218
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
1147
1219
  return { ok: true, value: matchedValue, span };
1148
1220
  }
@@ -1307,6 +1379,7 @@ function regex(pattern, flags = "") {
1307
1379
  if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
1308
1380
  const value = input.slice(pos, scanEnd);
1309
1381
  const span2 = { start: pos, end: scanEnd };
1382
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
1310
1383
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
1311
1384
  return { ok: true, value, span: span2 };
1312
1385
  }
@@ -1316,6 +1389,7 @@ function regex(pattern, flags = "") {
1316
1389
  return failAt(ctx, [`/${source}/`], pos);
1317
1390
  }
1318
1391
  const span = { start: pos, end: pos + m[0].length };
1392
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1319
1393
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
1320
1394
  return { ok: true, value: m[0], span };
1321
1395
  }
@@ -1466,6 +1540,18 @@ function rollbackTrivia(ctx, mark) {
1466
1540
  rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1467
1541
  if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1468
1542
  }
1543
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1544
+ const probeCtx = {
1545
+ trackLines: ctx.trackLines,
1546
+ state: ctx.state,
1547
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1548
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1549
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1550
+ };
1551
+ const result = triviaP.parse(input, cur, probeCtx);
1552
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1553
+ return result;
1554
+ }
1469
1555
  function saveLookaheadMark(ctx) {
1470
1556
  return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1471
1557
  }
@@ -1488,13 +1574,29 @@ function scanWithLabels(input, cur, ctx) {
1488
1574
  function advanceTrivia(input, cur, ctx) {
1489
1575
  const triviaP = ctx.trivia;
1490
1576
  if (!triviaP) return cur;
1577
+ if (!ctx.trackLines) {
1578
+ const fast2 = fastTriviaScanner(triviaP);
1579
+ if (fast2) return fast2(input, cur);
1580
+ if (ctx.triviaKindLabels) {
1581
+ const scan = scanWithLabels(input, cur, ctx);
1582
+ return scan.end;
1583
+ }
1584
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1585
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1586
+ }
1587
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1491
1588
  const fast = fastTriviaScanner(triviaP);
1492
- if (fast) return fast(input, cur);
1589
+ if (fast) {
1590
+ const end = fast(input, cur);
1591
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1592
+ return end;
1593
+ }
1493
1594
  if (ctx.triviaKindLabels) {
1494
1595
  const scan = scanWithLabels(input, cur, ctx);
1596
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1495
1597
  return scan.end;
1496
1598
  }
1497
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1599
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1498
1600
  return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1499
1601
  }
1500
1602
  function scanTrivia(input, cur, ctx) {
@@ -1502,18 +1604,46 @@ function scanTrivia(input, cur, ctx) {
1502
1604
  if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1503
1605
  const log = ctx._triviaLog;
1504
1606
  const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1607
+ if (!ctx.trackLines) {
1608
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1609
+ if (fast2 && log === void 0 && !captureTl) {
1610
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1611
+ }
1612
+ if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1613
+ return scanWithLabels(input, cur, ctx);
1614
+ }
1615
+ if (log !== void 0 || captureTl) {
1616
+ const tr3 = triviaP.parse(input, cur, {
1617
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1618
+ state: ctx.state
1619
+ });
1620
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1621
+ const end = tr3.span.end;
1622
+ return {
1623
+ end,
1624
+ commit: () => {
1625
+ pushTriviaLogEntry(ctx, cur, end);
1626
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1627
+ }
1628
+ };
1629
+ }
1630
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1631
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1632
+ }
1633
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1505
1634
  const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1506
1635
  if (fast && log === void 0 && !captureTl) {
1507
- return { end: fast(input, cur), commit: NOOP_COMMIT };
1636
+ const end = fast(input, cur);
1637
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1638
+ return { end, commit: NOOP_COMMIT };
1508
1639
  }
1509
1640
  if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1510
- return scanWithLabels(input, cur, ctx);
1641
+ const scan = scanWithLabels(input, cur, ctx);
1642
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1643
+ return scan;
1511
1644
  }
1512
1645
  if (log !== void 0 || captureTl) {
1513
- const tr2 = triviaP.parse(input, cur, {
1514
- trackLines: log !== void 0 ? false : ctx.trackLines,
1515
- state: ctx.state
1516
- });
1646
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1517
1647
  if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1518
1648
  const end = tr2.span.end;
1519
1649
  return {
@@ -1524,7 +1654,7 @@ function scanTrivia(input, cur, ctx) {
1524
1654
  }
1525
1655
  };
1526
1656
  }
1527
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1657
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1528
1658
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1529
1659
  }
1530
1660
  function consumeTrivia(input, cur, ctx) {
@@ -2177,7 +2307,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2177
2307
  let cur = pos;
2178
2308
  const skip3 = resolveScanSkip(explicitSkip, raw, ctx);
2179
2309
  const probeCtx = {
2180
- trackLines: ctx.trackLines,
2310
+ trackLines: false,
2181
2311
  state: ctx.state,
2182
2312
  ...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
2183
2313
  };
@@ -2190,6 +2320,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2190
2320
  while (cur < input.length) {
2191
2321
  const s = sentinel.parse(input, cur, probeCtx);
2192
2322
  if (s.ok) {
2323
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
2193
2324
  emit(cur);
2194
2325
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
2195
2326
  }
@@ -2205,6 +2336,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2205
2336
  if (!advanced) cur++;
2206
2337
  }
2207
2338
  if (orEOF) {
2339
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
2208
2340
  emit(cur);
2209
2341
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
2210
2342
  }
@@ -2222,7 +2354,7 @@ var META = new Set("()[]{}*+?|^$.".split(""));
2222
2354
  var CAP_RECORD = [
2223
2355
  ` if (_cap && _e > _pos) {`,
2224
2356
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
2225
- ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
2357
+ ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
2226
2358
  ` }`
2227
2359
  ].join("\n");
2228
2360
 
@@ -2814,49 +2946,36 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2814
2946
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
2815
2947
  const nodeType = def.type ?? missingInferredType();
2816
2948
  const cstOutput = cstOutputHost(ctx.build);
2949
+ const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
2817
2950
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
2818
- const built = unwrap && children.length === 1 ? unwrapChild(children[0]) : collapse && children.length === 1 ? children[0] : !build && project === void 0 && ctx.build?._parsemanCstCollapse && children.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children[0], children, rawChildren) ? children[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, r.span, rawChildren, triviaLog, hostState) : projectChild(children, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, r.span, rawChildren, triviaLog, hostState) : build(children, fields, r.span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children, fields, r.span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: { start: r.span.start, end: r.span.end }, state: st ?? null, children };
2819
- 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 };
2951
+ const built = unwrap && children.length === 1 ? unwrapChild(children[0]) : collapse && children.length === 1 ? children[0] : !build && project === void 0 && ctx.build?._parsemanCstCollapse && children.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children[0], children, rawChildren) ? children[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, span, rawChildren, triviaLog, hostState) : projectChild(children, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, span, rawChildren, triviaLog, hostState) : build(children, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children, 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 };
2952
+ 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 };
2820
2953
  if (saved.buf !== void 0 || saved.ch !== void 0) {
2821
2954
  pushCstChild(ctx, built, rawEntry);
2822
2955
  }
2823
- return { ok: true, value: built, span: r.span };
2956
+ return { ok: true, value: built, span };
2824
2957
  }
2825
2958
  };
2826
2959
  }
2827
2960
 
2828
- // src/compiler/line-index.ts
2829
- function buildLineIndex(input) {
2830
- const lineStarts = [0];
2831
- for (let i = 0; i < input.length; i++) {
2832
- if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
2833
- }
2834
- return { lineStarts };
2835
- }
2836
- function offsetToLineCol(index, offset) {
2837
- const { lineStarts } = index;
2838
- let lo = 0;
2839
- let hi = lineStarts.length - 1;
2840
- while (lo < hi) {
2841
- const mid = lo + hi + 1 >> 1;
2842
- if (lineStarts[mid] <= offset) lo = mid;
2843
- else hi = mid - 1;
2961
+ // src/combinators/grammar.ts
2962
+ function annotateResultLines(result, index) {
2963
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
2964
+ const errors = annotated.errors;
2965
+ if (errors) {
2966
+ ;
2967
+ annotated.errors = errors.map((error) => ({
2968
+ ...error,
2969
+ span: annotateSpan(error.span, index)
2970
+ }));
2844
2971
  }
2845
- return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
2972
+ return annotated;
2846
2973
  }
2847
- function annotateSpan(span, index) {
2848
- const s = offsetToLineCol(index, span.start);
2849
- const e = offsetToLineCol(index, span.end);
2850
- return {
2851
- ...span,
2852
- startLine: s.line,
2853
- startColumn: s.col,
2854
- endLine: e.line,
2855
- endColumn: e.col
2856
- };
2974
+ function createParseLineContext(input, pos) {
2975
+ const lineIndex = createLineIndex();
2976
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
2977
+ return { lineIndex, lineScannedTo: pos };
2857
2978
  }
2858
-
2859
- // src/combinators/grammar.ts
2860
2979
  function parser(opts, root) {
2861
2980
  const clearTrivia = opts.trivia === null;
2862
2981
  return {
@@ -2872,9 +2991,13 @@ function parser(opts, root) {
2872
2991
  },
2873
2992
  parse(input, pos, _ctx) {
2874
2993
  const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
2994
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
2995
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
2875
2996
  const ctx = {
2876
2997
  ..._ctx,
2877
2998
  trackLines,
2999
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
3000
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
2878
3001
  // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
2879
3002
  ...clearTrivia ? {
2880
3003
  trivia: void 0,
@@ -2892,11 +3015,10 @@ function parser(opts, root) {
2892
3015
  ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
2893
3016
  };
2894
3017
  const result = root.parse(input, pos ?? 0, ctx);
2895
- if (trackLines) {
2896
- const idx = buildLineIndex(input);
2897
- return { ...result, span: annotateSpan(result.span, idx) };
3018
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
3019
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
2898
3020
  }
2899
- return result;
3021
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
2900
3022
  }
2901
3023
  };
2902
3024
  }
@@ -3184,7 +3306,7 @@ function normalizeCstCollapse(collapse) {
3184
3306
  return typeof collapse === "function" ? collapse : void 0;
3185
3307
  }
3186
3308
  function buildCstNode(type, children, _fields, span, _rawChildren, _triviaLog, state) {
3187
- return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children] };
3309
+ return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children] };
3188
3310
  }
3189
3311
  function cstBuildHost(typeOrOptions, children, _fields, span, rawChildren, triviaLog, state) {
3190
3312
  if (typeof typeOrOptions === "string") {