parseman 0.41.0 → 0.42.1

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 (51) hide show
  1. package/dist/combinators/expect.d.ts.map +1 -1
  2. package/dist/combinators/grammar.d.ts.map +1 -1
  3. package/dist/combinators/literal.d.ts.map +1 -1
  4. package/dist/combinators/node.d.ts.map +1 -1
  5. package/dist/combinators/parser.d.ts +6 -0
  6. package/dist/combinators/parser.d.ts.map +1 -1
  7. package/dist/combinators/regex.d.ts.map +1 -1
  8. package/dist/combinators/scanTo.d.ts.map +1 -1
  9. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  10. package/dist/compiler/codegen.d.ts +5 -0
  11. package/dist/compiler/codegen.d.ts.map +1 -1
  12. package/dist/compiler/inline-build.d.ts +1 -1
  13. package/dist/compiler/inline-build.d.ts.map +1 -1
  14. package/dist/compiler/line-index.d.ts +1 -15
  15. package/dist/compiler/line-index.d.ts.map +1 -1
  16. package/dist/compiler/linker.d.ts +1 -0
  17. package/dist/compiler/linker.d.ts.map +1 -1
  18. package/dist/cst/capture-buffer.d.ts.map +1 -1
  19. package/dist/index.cjs +483 -181
  20. package/dist/index.cjs.map +4 -4
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +483 -181
  24. package/dist/index.js.map +4 -4
  25. package/dist/language-service/index.cjs +48 -2
  26. package/dist/language-service/index.cjs.map +3 -3
  27. package/dist/language-service/index.js +48 -2
  28. package/dist/language-service/index.js.map +3 -3
  29. package/dist/line-index.d.ts +41 -0
  30. package/dist/line-index.d.ts.map +1 -0
  31. package/dist/plugin/index.cjs +646 -169
  32. package/dist/plugin/index.cjs.map +4 -4
  33. package/dist/plugin/index.d.ts.map +1 -1
  34. package/dist/plugin/index.js +646 -169
  35. package/dist/plugin/index.js.map +4 -4
  36. package/dist/recovery/scan.d.ts.map +1 -1
  37. package/dist/regex/first-set.d.ts.map +1 -1
  38. package/dist/run/index.cjs +47 -1
  39. package/dist/run/index.cjs.map +3 -3
  40. package/dist/run/index.d.ts +3 -2
  41. package/dist/run/index.d.ts.map +1 -1
  42. package/dist/run/index.js +47 -1
  43. package/dist/run/index.js.map +3 -3
  44. package/dist/spec/index.cjs +462 -327
  45. package/dist/spec/index.cjs.map +4 -4
  46. package/dist/spec/index.js +462 -327
  47. package/dist/spec/index.js.map +4 -4
  48. package/dist/types.d.ts +18 -0
  49. package/dist/types.d.ts.map +1 -1
  50. package/dist/version.d.ts +1 -1
  51. package/package.json +1 -1
@@ -391,90 +391,63 @@ function markUnusedValues(root) {
391
391
  visit(resolveRoot(root), true);
392
392
  }
393
393
 
394
- // src/combinators/parser.ts
395
- var RULE_ORDER = "__parsemanRuleOrder";
396
- function tagRule(r, key) {
397
- ;
398
- r._ruleName = key;
399
- if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
400
- }
401
- function isDefinableRef(v) {
402
- return !!v && typeof v === "object" && "_def" in v && v._def.tag === "lazy" && typeof v.define === "function";
403
- }
404
- function ruleNameOf(r) {
405
- return r._ruleName;
394
+ // src/line-index.ts
395
+ function createLineIndex() {
396
+ return { lineStarts: [0] };
406
397
  }
407
- function isNamedRuleRefForAnotherRule(r, key) {
408
- const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
409
- return name !== void 0 && name !== key;
410
- }
411
- function rules(a, b) {
412
- const factory = typeof a === "function" ? a : b;
413
- const options = typeof a === "function" ? b : a;
414
- const cache = {};
415
- const proxy = new Proxy(cache, {
416
- get(target, key) {
417
- if (typeof key !== "string") return void 0;
418
- const record = target;
419
- if (!(key in record)) {
420
- const r = ref();
421
- tagRule(r, key);
422
- record[key] = r;
423
- }
424
- return record[key];
425
- }
426
- });
427
- const definitions = factory(proxy);
428
- for (const key of Object.keys(definitions)) {
429
- const placeholder = cache[key];
430
- const parser2 = definitions[key];
431
- if (placeholder === parser2) {
432
- throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
433
- }
434
- if (isDefinableRef(placeholder)) {
435
- if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
436
- placeholder.define(parser2);
437
- placeholder._meta.firstSet = parser2._meta.firstSet;
438
- placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
439
- } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
440
- const alias = ref();
441
- tagRule(alias, key);
442
- alias.define(parser2);
443
- alias._meta.firstSet = parser2._meta.firstSet;
444
- alias._meta.canMatchNewline = parser2._meta.canMatchNewline;
445
- cache[key] = alias;
446
- } else {
447
- tagRule(parser2, key);
448
- cache[key] = parser2;
449
- }
450
- }
451
- if (options?.trivia != null) {
452
- for (const key of Object.keys(definitions)) {
453
- const rule = cache[key];
454
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
455
- }
456
- }
457
- if (options?.scanSkip != null) {
458
- for (const key of Object.keys(definitions)) {
459
- const rule = cache[key];
460
- if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
461
- }
398
+ function recordLineRange(index, input, start, end) {
399
+ const lineStarts = index.lineStarts;
400
+ for (let i = start; i < end; i++) {
401
+ if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
462
402
  }
463
- if (options?.hostMode === "cst") {
464
- for (const key of Object.keys(definitions)) {
465
- const rule = cache[key];
466
- if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
467
- }
403
+ }
404
+ function recordLineRangeFromContext(ctx, input, _startHint, end) {
405
+ if (!ctx.trackLines) return;
406
+ const from = ctx._lineScannedTo ?? 0;
407
+ if (end <= from) return;
408
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
409
+ if (!index) return;
410
+ recordLineRange(index, input, from, end);
411
+ ctx._lineScannedTo = end;
412
+ }
413
+ function normalizeLineIndex(index) {
414
+ const starts = index.lineStarts;
415
+ starts.sort((a, b) => a - b);
416
+ let write = 0;
417
+ for (let read = 0; read < starts.length; read++) {
418
+ const value = starts[read];
419
+ if (write === 0 || starts[write - 1] !== value) starts[write++] = value;
468
420
  }
469
- for (const key of Object.keys(definitions)) {
470
- markUnusedValues(cache[key]);
421
+ starts.length = write;
422
+ if (starts.length === 0 || starts[0] !== 0) starts.unshift(0);
423
+ return index;
424
+ }
425
+ function offsetToLineCol(index, offset) {
426
+ const { lineStarts } = index;
427
+ let lo = 0;
428
+ let hi = lineStarts.length - 1;
429
+ while (lo < hi) {
430
+ const mid = lo + hi + 1 >> 1;
431
+ if (lineStarts[mid] <= offset) lo = mid;
432
+ else hi = mid - 1;
471
433
  }
472
- Object.defineProperty(cache, RULE_ORDER, {
473
- value: Object.keys(definitions),
474
- enumerable: false,
475
- configurable: true
476
- });
477
- return cache;
434
+ return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
435
+ }
436
+ function annotateSpan(span, index) {
437
+ const s = offsetToLineCol(index, span.start);
438
+ const e = offsetToLineCol(index, span.end);
439
+ return {
440
+ ...span,
441
+ startLine: s.line,
442
+ startColumn: s.col,
443
+ endLine: e.line,
444
+ endColumn: e.col
445
+ };
446
+ }
447
+ function annotateSpanFromLineContext(span, ctx) {
448
+ if (!ctx.trackLines) return span;
449
+ const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : void 0);
450
+ return index ? annotateSpan(span, index) : span;
478
451
  }
479
452
 
480
453
  // src/cst/capture-buffer.ts
@@ -518,7 +491,12 @@ function endCstNodeCapture(ctx, saved) {
518
491
  return materialized;
519
492
  }
520
493
  function pushCstLeaf(ctx, leaf2) {
521
- pushCstChild(ctx, leaf2, leaf2);
494
+ if (!ctx.trackLines) {
495
+ pushCstChild(ctx, leaf2, leaf2);
496
+ return;
497
+ }
498
+ const annotated = leaf2 && typeof leaf2 === "object" && leaf2.span ? { ...leaf2, span: annotateSpanFromLineContext(leaf2.span, ctx) } : leaf2;
499
+ pushCstChild(ctx, annotated, annotated);
522
500
  }
523
501
  function pushCstChild(ctx, built, rawEntry) {
524
502
  const b = ctx._cstBuf;
@@ -708,7 +686,9 @@ function firstSetSentinel(fs) {
708
686
  function recoverScan(input, from, ctx, sync, expected) {
709
687
  let scanPos = from;
710
688
  while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
711
- const error = { _tag: "parseError", span: { start: from, end: scanPos }, expected };
689
+ const span = { start: from, end: scanPos };
690
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
691
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
712
692
  ctx._errors?.push(error);
713
693
  return { error, end: scanPos };
714
694
  }
@@ -810,7 +790,8 @@ function expect(combinator, label2) {
810
790
  parse(input, pos, ctx) {
811
791
  const result = combinator.parse(input, pos, ctx);
812
792
  if (result.ok) return result;
813
- const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
793
+ const span = { start: pos, end: pos };
794
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
814
795
  ctx._errors?.push(error);
815
796
  if (ctx._tolerant) captureError(ctx, error);
816
797
  return { ok: true, value: error, span: { start: pos, end: pos } };
@@ -1081,34 +1062,329 @@ function codesToFirstSet(codes) {
1081
1062
  lo = hi = codes[i];
1082
1063
  }
1083
1064
  }
1084
- ranges.push({ lo, hi });
1085
- return { kind: "ranges", ranges };
1086
- }
1087
- function inFirstSet(code, fs) {
1088
- if (fs.kind === "any") return true;
1089
- if (fs.kind === "empty") return false;
1090
- for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
1091
- return false;
1092
- }
1093
- function areDisjoint(sets) {
1094
- if (sets.some((s) => s.kind === "any")) return false;
1095
- for (let i = 0; i < sets.length; i++)
1096
- for (let j2 = i + 1; j2 < sets.length; j2++)
1097
- if (intersects(sets[i], sets[j2])) return false;
1098
- return true;
1099
- }
1100
- function buildAsciiDispatch(parsers) {
1101
- const table = Array(128).fill(-1);
1102
- for (let i = 0; i < parsers.length; i++) {
1103
- const fs = parsers[i]._meta.firstSet;
1104
- if (fs.kind !== "ranges") continue;
1105
- for (const { lo, hi } of fs.ranges) {
1106
- for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
1107
- table[code] = i;
1065
+ ranges.push({ lo, hi });
1066
+ return { kind: "ranges", ranges };
1067
+ }
1068
+ function inFirstSet(code, fs) {
1069
+ if (fs.kind === "any") return true;
1070
+ if (fs.kind === "empty") return false;
1071
+ for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
1072
+ return false;
1073
+ }
1074
+ function areDisjoint(sets) {
1075
+ if (sets.some((s) => s.kind === "any")) return false;
1076
+ for (let i = 0; i < sets.length; i++)
1077
+ for (let j2 = i + 1; j2 < sets.length; j2++)
1078
+ if (intersects(sets[i], sets[j2])) return false;
1079
+ return true;
1080
+ }
1081
+ function buildAsciiDispatch(parsers) {
1082
+ const table = Array(128).fill(-1);
1083
+ for (let i = 0; i < parsers.length; i++) {
1084
+ const fs = parsers[i]._meta.firstSet;
1085
+ if (fs.kind !== "ranges") continue;
1086
+ for (const { lo, hi } of fs.ranges) {
1087
+ for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
1088
+ table[code] = i;
1089
+ }
1090
+ }
1091
+ }
1092
+ return table;
1093
+ }
1094
+
1095
+ // src/cst/trivia-kinds.ts
1096
+ function unwrapTrivia(p) {
1097
+ let cur = p;
1098
+ while (cur._def.tag === "trivia") cur = cur._def.parser;
1099
+ return cur;
1100
+ }
1101
+ function peelLabel(p) {
1102
+ if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
1103
+ return null;
1104
+ }
1105
+ function analyzeLabeledTrivia(trivia2) {
1106
+ let core = unwrapTrivia(trivia2);
1107
+ let minRepeats = 1;
1108
+ if (core._def.tag === "oneOrMore") {
1109
+ core = core._def.parser;
1110
+ minRepeats = 1;
1111
+ } else if (core._def.tag === "many") {
1112
+ core = core._def.parser;
1113
+ minRepeats = 0;
1114
+ }
1115
+ const arms = [];
1116
+ if (core._def.tag === "choice") {
1117
+ for (let i = 0; i < core._def.parsers.length; i++) {
1118
+ const peeled = peelLabel(core._def.parsers[i]);
1119
+ if (!peeled) return null;
1120
+ arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
1121
+ }
1122
+ } else {
1123
+ const peeled = peelLabel(core);
1124
+ if (!peeled) return null;
1125
+ arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
1126
+ }
1127
+ return { labels: arms.map((a) => a.label), arms, minRepeats };
1128
+ }
1129
+ function matchArmAt(input, pos, arm) {
1130
+ const r = arm.parse(input, pos, { trackLines: false });
1131
+ if (!r.ok || r.span.end <= pos) return null;
1132
+ return { end: r.span.end };
1133
+ }
1134
+ function scanLabeledTriviaChunks(input, cur, spec) {
1135
+ const chunks = [];
1136
+ let pos = cur;
1137
+ while (pos < input.length) {
1138
+ let matched = null;
1139
+ for (const arm of spec.arms) {
1140
+ const m = matchArmAt(input, pos, arm.parser);
1141
+ if (m) {
1142
+ matched = { end: m.end, kindIndex: arm.kindIndex };
1143
+ break;
1144
+ }
1145
+ }
1146
+ if (!matched) break;
1147
+ chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
1148
+ pos = matched.end;
1149
+ }
1150
+ if (chunks.length < spec.minRepeats) {
1151
+ return { end: cur, chunks: [] };
1152
+ }
1153
+ return { end: pos, chunks };
1154
+ }
1155
+ function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
1156
+ const chunks = [];
1157
+ let pos = cur;
1158
+ while (pos < input.length) {
1159
+ const c = input.charCodeAt(pos);
1160
+ if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {
1161
+ const start = pos;
1162
+ pos++;
1163
+ while (pos < input.length) {
1164
+ const c2 = input.charCodeAt(pos);
1165
+ if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) pos++;
1166
+ else break;
1167
+ }
1168
+ chunks.push({ start, end: pos, kindIndex: wsKind });
1169
+ continue;
1170
+ }
1171
+ if (c === 47 && input.charCodeAt(pos + 1) === 42) {
1172
+ let j2 = pos + 2;
1173
+ while (j2 + 1 < input.length && !(input.charCodeAt(j2) === 42 && input.charCodeAt(j2 + 1) === 47)) j2++;
1174
+ if (j2 + 1 < input.length && input.charCodeAt(j2) === 42 && input.charCodeAt(j2 + 1) === 47) {
1175
+ const start = pos;
1176
+ pos = j2 + 2;
1177
+ chunks.push({ start, end: pos, kindIndex: commentKind });
1178
+ continue;
1179
+ }
1180
+ break;
1181
+ }
1182
+ break;
1183
+ }
1184
+ return { end: pos, chunks };
1185
+ }
1186
+ function tryFastLabeledScan(input, cur, trivia2) {
1187
+ const spec = analyzeLabeledTrivia(trivia2);
1188
+ if (!spec || spec.arms.length !== 2) return null;
1189
+ const wsArm = spec.arms.find((a) => {
1190
+ const src = getCoreRegexDef(a.parser)?.source;
1191
+ return src != null && !src.includes("\\*");
1192
+ });
1193
+ const commentArm = spec.arms.find((a) => {
1194
+ const src = getCoreRegexDef(a.parser)?.source;
1195
+ return src != null && src.includes("\\*");
1196
+ });
1197
+ if (!wsArm || !commentArm) return null;
1198
+ const { chunks } = scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1199
+ if (chunks.length < spec.minRepeats) return { end: cur, chunks: [] };
1200
+ return scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1201
+ }
1202
+ function recordTriviaChunks(ctx, chunks) {
1203
+ const kinds = ctx.triviaKindLabels;
1204
+ const mask = ctx._triviaCaptureMask;
1205
+ for (const ch of chunks) {
1206
+ pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1207
+ if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
1208
+ if (mask === void 0 || kinds === void 0 || (mask & 1 << ch.kindIndex) !== 0) {
1209
+ pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1210
+ }
1211
+ }
1212
+ }
1213
+ }
1214
+ function triviaKindMask(labels, keep) {
1215
+ if (!labels) return void 0;
1216
+ let mask = 0;
1217
+ for (const name of keep) {
1218
+ const idx = labels.indexOf(name);
1219
+ if (idx >= 0) mask |= 1 << idx;
1220
+ }
1221
+ return mask;
1222
+ }
1223
+
1224
+ // src/combinators/grammar.ts
1225
+ function annotateResultLines(result, index) {
1226
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
1227
+ const errors = annotated.errors;
1228
+ if (errors) {
1229
+ ;
1230
+ annotated.errors = errors.map((error) => ({
1231
+ ...error,
1232
+ span: annotateSpan(error.span, index)
1233
+ }));
1234
+ }
1235
+ return annotated;
1236
+ }
1237
+ function createParseLineContext(input, pos) {
1238
+ const lineIndex = createLineIndex();
1239
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
1240
+ return { lineIndex, lineScannedTo: pos };
1241
+ }
1242
+ function parser(opts, root) {
1243
+ const clearTrivia = opts.trivia === null;
1244
+ return {
1245
+ _tag: "grammar",
1246
+ _meta: root._meta,
1247
+ _def: {
1248
+ tag: "grammar",
1249
+ parser: root,
1250
+ triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
1251
+ clearTrivia,
1252
+ ...opts.captureTrivia ? { captureTrivia: true } : {},
1253
+ trackLines: opts.trackLines ?? false
1254
+ },
1255
+ parse(input, pos, _ctx) {
1256
+ const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
1257
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
1258
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
1259
+ const ctx = {
1260
+ ..._ctx,
1261
+ trackLines,
1262
+ ...lineIndex ? { _lineIndex: lineIndex } : {},
1263
+ ...lineContext ? { _lineScannedTo: lineContext.lineScannedTo } : {},
1264
+ // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
1265
+ ...clearTrivia ? {
1266
+ trivia: void 0,
1267
+ triviaKindLabels: void 0
1268
+ } : opts.trivia != null ? {
1269
+ trivia: opts.trivia,
1270
+ ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
1271
+ } : {},
1272
+ ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
1273
+ // Kind-filter for per-node capture. Resolve against this scope's trivia
1274
+ // labels — this parser's own trivia if it declares one, else the INHERITED
1275
+ // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
1276
+ // trivia is inherited rather than re-declared here. No labels → undefined
1277
+ // (capture all).
1278
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
1279
+ };
1280
+ const result = root.parse(input, pos ?? 0, ctx);
1281
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
1282
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
1283
+ }
1284
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
1285
+ }
1286
+ };
1287
+ }
1288
+
1289
+ // src/combinators/parser.ts
1290
+ var RULE_ORDER = "__parsemanRuleOrder";
1291
+ function tagRule(r, key) {
1292
+ ;
1293
+ r._ruleName = key;
1294
+ if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
1295
+ }
1296
+ function isDefinableRef(v) {
1297
+ return !!v && typeof v === "object" && "_def" in v && v._def.tag === "lazy" && typeof v.define === "function";
1298
+ }
1299
+ function ruleNameOf(r) {
1300
+ return r._ruleName;
1301
+ }
1302
+ function isNamedRuleRefForAnotherRule(r, key) {
1303
+ const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
1304
+ return name !== void 0 && name !== key;
1305
+ }
1306
+ function rules(a, b) {
1307
+ const factory = typeof a === "function" ? a : b;
1308
+ const options = typeof a === "function" ? b : a;
1309
+ const cache = {};
1310
+ const proxy = new Proxy(cache, {
1311
+ get(target, key) {
1312
+ if (typeof key !== "string") return void 0;
1313
+ const record = target;
1314
+ if (!(key in record)) {
1315
+ const r = ref();
1316
+ tagRule(r, key);
1317
+ record[key] = r;
1318
+ }
1319
+ return record[key];
1320
+ }
1321
+ });
1322
+ const definitions = factory(proxy);
1323
+ for (const key of Object.keys(definitions)) {
1324
+ const placeholder = cache[key];
1325
+ const parser2 = definitions[key];
1326
+ if (placeholder === parser2) {
1327
+ throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
1328
+ }
1329
+ if (isDefinableRef(placeholder)) {
1330
+ if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
1331
+ placeholder.define(parser2);
1332
+ placeholder._meta.firstSet = parser2._meta.firstSet;
1333
+ placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
1334
+ } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
1335
+ const alias = ref();
1336
+ tagRule(alias, key);
1337
+ alias.define(parser2);
1338
+ alias._meta.firstSet = parser2._meta.firstSet;
1339
+ alias._meta.canMatchNewline = parser2._meta.canMatchNewline;
1340
+ cache[key] = alias;
1341
+ } else {
1342
+ tagRule(parser2, key);
1343
+ cache[key] = parser2;
1344
+ }
1345
+ }
1346
+ if (options?.trivia != null) {
1347
+ for (const key of Object.keys(definitions)) {
1348
+ const rule = cache[key];
1349
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
1350
+ }
1351
+ }
1352
+ if (options?.scanSkip != null) {
1353
+ for (const key of Object.keys(definitions)) {
1354
+ const rule = cache[key];
1355
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
1356
+ }
1357
+ }
1358
+ if (options?.hostMode === "cst") {
1359
+ for (const key of Object.keys(definitions)) {
1360
+ const rule = cache[key];
1361
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
1362
+ }
1363
+ }
1364
+ if (options?.trackLines === true) {
1365
+ for (const key of Object.keys(definitions)) {
1366
+ const rule = cache[key];
1367
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
1368
+ }
1369
+ for (const key of Object.keys(definitions)) {
1370
+ const rule = cache[key];
1371
+ if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
1372
+ const wrapped = parser({ trackLines: true }, rule);
1373
+ tagRule(wrapped, key);
1374
+ wrapped._meta.grammarTrackLines = true;
1375
+ cache[key] = wrapped;
1108
1376
  }
1109
1377
  }
1110
1378
  }
1111
- return table;
1379
+ for (const key of Object.keys(definitions)) {
1380
+ markUnusedValues(cache[key]);
1381
+ }
1382
+ Object.defineProperty(cache, RULE_ORDER, {
1383
+ value: Object.keys(definitions),
1384
+ enumerable: false,
1385
+ configurable: true
1386
+ });
1387
+ return cache;
1112
1388
  }
1113
1389
 
1114
1390
  // src/analysis/duplication.ts
@@ -1150,6 +1426,7 @@ function literal(value, opts = {}) {
1150
1426
  canMatchNewline: value.includes("\n"),
1151
1427
  isTrivia: false
1152
1428
  };
1429
+ const canMatchNewline = meta.canMatchNewline;
1153
1430
  if (caseInsensitive) {
1154
1431
  const upper = value.toUpperCase();
1155
1432
  const lower = value.toLowerCase();
@@ -1166,6 +1443,7 @@ function literal(value, opts = {}) {
1166
1443
  return function parse2(input, pos, ctx) {
1167
1444
  if (input.charCodeAt(pos) === code) {
1168
1445
  const span = { start: pos, end: pos + 1 };
1446
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1169
1447
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
1170
1448
  return { ok: true, value, span };
1171
1449
  }
@@ -1179,6 +1457,7 @@ function literal(value, opts = {}) {
1179
1457
  const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
1180
1458
  if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
1181
1459
  const span = { start: pos, end };
1460
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
1182
1461
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
1183
1462
  return { ok: true, value: matchedValue, span };
1184
1463
  }
@@ -1343,6 +1622,7 @@ function regex(pattern, flags = "") {
1343
1622
  if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
1344
1623
  const value = input.slice(pos, scanEnd);
1345
1624
  const span2 = { start: pos, end: scanEnd };
1625
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
1346
1626
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
1347
1627
  return { ok: true, value, span: span2 };
1348
1628
  }
@@ -1352,141 +1632,13 @@ function regex(pattern, flags = "") {
1352
1632
  return failAt(ctx, [`/${source}/`], pos);
1353
1633
  }
1354
1634
  const span = { start: pos, end: pos + m[0].length };
1635
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
1355
1636
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
1356
1637
  return { ok: true, value: m[0], span };
1357
1638
  }
1358
1639
  };
1359
1640
  }
1360
1641
 
1361
- // src/cst/trivia-kinds.ts
1362
- function unwrapTrivia(p) {
1363
- let cur = p;
1364
- while (cur._def.tag === "trivia") cur = cur._def.parser;
1365
- return cur;
1366
- }
1367
- function peelLabel(p) {
1368
- if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
1369
- return null;
1370
- }
1371
- function analyzeLabeledTrivia(trivia2) {
1372
- let core = unwrapTrivia(trivia2);
1373
- let minRepeats = 1;
1374
- if (core._def.tag === "oneOrMore") {
1375
- core = core._def.parser;
1376
- minRepeats = 1;
1377
- } else if (core._def.tag === "many") {
1378
- core = core._def.parser;
1379
- minRepeats = 0;
1380
- }
1381
- const arms = [];
1382
- if (core._def.tag === "choice") {
1383
- for (let i = 0; i < core._def.parsers.length; i++) {
1384
- const peeled = peelLabel(core._def.parsers[i]);
1385
- if (!peeled) return null;
1386
- arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
1387
- }
1388
- } else {
1389
- const peeled = peelLabel(core);
1390
- if (!peeled) return null;
1391
- arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
1392
- }
1393
- return { labels: arms.map((a) => a.label), arms, minRepeats };
1394
- }
1395
- function matchArmAt(input, pos, arm) {
1396
- const r = arm.parse(input, pos, { trackLines: false });
1397
- if (!r.ok || r.span.end <= pos) return null;
1398
- return { end: r.span.end };
1399
- }
1400
- function scanLabeledTriviaChunks(input, cur, spec) {
1401
- const chunks = [];
1402
- let pos = cur;
1403
- while (pos < input.length) {
1404
- let matched = null;
1405
- for (const arm of spec.arms) {
1406
- const m = matchArmAt(input, pos, arm.parser);
1407
- if (m) {
1408
- matched = { end: m.end, kindIndex: arm.kindIndex };
1409
- break;
1410
- }
1411
- }
1412
- if (!matched) break;
1413
- chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
1414
- pos = matched.end;
1415
- }
1416
- if (chunks.length < spec.minRepeats) {
1417
- return { end: cur, chunks: [] };
1418
- }
1419
- return { end: pos, chunks };
1420
- }
1421
- function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
1422
- const chunks = [];
1423
- let pos = cur;
1424
- while (pos < input.length) {
1425
- const c = input.charCodeAt(pos);
1426
- if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {
1427
- const start = pos;
1428
- pos++;
1429
- while (pos < input.length) {
1430
- const c2 = input.charCodeAt(pos);
1431
- if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) pos++;
1432
- else break;
1433
- }
1434
- chunks.push({ start, end: pos, kindIndex: wsKind });
1435
- continue;
1436
- }
1437
- if (c === 47 && input.charCodeAt(pos + 1) === 42) {
1438
- let j2 = pos + 2;
1439
- while (j2 + 1 < input.length && !(input.charCodeAt(j2) === 42 && input.charCodeAt(j2 + 1) === 47)) j2++;
1440
- if (j2 + 1 < input.length && input.charCodeAt(j2) === 42 && input.charCodeAt(j2 + 1) === 47) {
1441
- const start = pos;
1442
- pos = j2 + 2;
1443
- chunks.push({ start, end: pos, kindIndex: commentKind });
1444
- continue;
1445
- }
1446
- break;
1447
- }
1448
- break;
1449
- }
1450
- return { end: pos, chunks };
1451
- }
1452
- function tryFastLabeledScan(input, cur, trivia2) {
1453
- const spec = analyzeLabeledTrivia(trivia2);
1454
- if (!spec || spec.arms.length !== 2) return null;
1455
- const wsArm = spec.arms.find((a) => {
1456
- const src = getCoreRegexDef(a.parser)?.source;
1457
- return src != null && !src.includes("\\*");
1458
- });
1459
- const commentArm = spec.arms.find((a) => {
1460
- const src = getCoreRegexDef(a.parser)?.source;
1461
- return src != null && src.includes("\\*");
1462
- });
1463
- if (!wsArm || !commentArm) return null;
1464
- const { chunks } = scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1465
- if (chunks.length < spec.minRepeats) return { end: cur, chunks: [] };
1466
- return scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1467
- }
1468
- function recordTriviaChunks(ctx, chunks) {
1469
- const kinds = ctx.triviaKindLabels;
1470
- const mask = ctx._triviaCaptureMask;
1471
- for (const ch of chunks) {
1472
- pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1473
- if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
1474
- if (mask === void 0 || kinds === void 0 || (mask & 1 << ch.kindIndex) !== 0) {
1475
- pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
1476
- }
1477
- }
1478
- }
1479
- }
1480
- function triviaKindMask(labels, keep) {
1481
- if (!labels) return void 0;
1482
- let mask = 0;
1483
- for (const name of keep) {
1484
- const idx = labels.indexOf(name);
1485
- if (idx >= 0) mask |= 1 << idx;
1486
- }
1487
- return mask;
1488
- }
1489
-
1490
1642
  // src/combinators/trivia-skip.ts
1491
1643
  var NOOP_COMMIT = () => {
1492
1644
  };
@@ -1502,6 +1654,18 @@ function rollbackTrivia(ctx, mark) {
1502
1654
  rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1503
1655
  if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1504
1656
  }
1657
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1658
+ const probeCtx = {
1659
+ trackLines: ctx.trackLines,
1660
+ state: ctx.state,
1661
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1662
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1663
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1664
+ };
1665
+ const result = triviaP.parse(input, cur, probeCtx);
1666
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1667
+ return result;
1668
+ }
1505
1669
  function saveLookaheadMark(ctx) {
1506
1670
  return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1507
1671
  }
@@ -1524,13 +1688,29 @@ function scanWithLabels(input, cur, ctx) {
1524
1688
  function advanceTrivia(input, cur, ctx) {
1525
1689
  const triviaP = ctx.trivia;
1526
1690
  if (!triviaP) return cur;
1691
+ if (!ctx.trackLines) {
1692
+ const fast2 = fastTriviaScanner(triviaP);
1693
+ if (fast2) return fast2(input, cur);
1694
+ if (ctx.triviaKindLabels) {
1695
+ const scan = scanWithLabels(input, cur, ctx);
1696
+ return scan.end;
1697
+ }
1698
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1699
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1700
+ }
1701
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1527
1702
  const fast = fastTriviaScanner(triviaP);
1528
- if (fast) return fast(input, cur);
1703
+ if (fast) {
1704
+ const end = fast(input, cur);
1705
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1706
+ return end;
1707
+ }
1529
1708
  if (ctx.triviaKindLabels) {
1530
1709
  const scan = scanWithLabels(input, cur, ctx);
1710
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1531
1711
  return scan.end;
1532
1712
  }
1533
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1713
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1534
1714
  return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1535
1715
  }
1536
1716
  function scanTrivia(input, cur, ctx) {
@@ -1538,18 +1718,46 @@ function scanTrivia(input, cur, ctx) {
1538
1718
  if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1539
1719
  const log = ctx._triviaLog;
1540
1720
  const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1721
+ if (!ctx.trackLines) {
1722
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1723
+ if (fast2 && log === void 0 && !captureTl) {
1724
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1725
+ }
1726
+ if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1727
+ return scanWithLabels(input, cur, ctx);
1728
+ }
1729
+ if (log !== void 0 || captureTl) {
1730
+ const tr3 = triviaP.parse(input, cur, {
1731
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1732
+ state: ctx.state
1733
+ });
1734
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1735
+ const end = tr3.span.end;
1736
+ return {
1737
+ end,
1738
+ commit: () => {
1739
+ pushTriviaLogEntry(ctx, cur, end);
1740
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1741
+ }
1742
+ };
1743
+ }
1744
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1745
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1746
+ }
1747
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1541
1748
  const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1542
1749
  if (fast && log === void 0 && !captureTl) {
1543
- return { end: fast(input, cur), commit: NOOP_COMMIT };
1750
+ const end = fast(input, cur);
1751
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1752
+ return { end, commit: NOOP_COMMIT };
1544
1753
  }
1545
1754
  if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1546
- return scanWithLabels(input, cur, ctx);
1755
+ const scan = scanWithLabels(input, cur, ctx);
1756
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1757
+ return scan;
1547
1758
  }
1548
1759
  if (log !== void 0 || captureTl) {
1549
- const tr2 = triviaP.parse(input, cur, {
1550
- trackLines: log !== void 0 ? false : ctx.trackLines,
1551
- state: ctx.state
1552
- });
1760
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1553
1761
  if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1554
1762
  const end = tr2.span.end;
1555
1763
  return {
@@ -1560,7 +1768,7 @@ function scanTrivia(input, cur, ctx) {
1560
1768
  }
1561
1769
  };
1562
1770
  }
1563
- const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1771
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1564
1772
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1565
1773
  }
1566
1774
  function consumeTrivia(input, cur, ctx) {
@@ -2213,7 +2421,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2213
2421
  let cur = pos;
2214
2422
  const skip3 = resolveScanSkip(explicitSkip, raw, ctx);
2215
2423
  const probeCtx = {
2216
- trackLines: ctx.trackLines,
2424
+ trackLines: false,
2217
2425
  state: ctx.state,
2218
2426
  ...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
2219
2427
  };
@@ -2226,6 +2434,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2226
2434
  while (cur < input.length) {
2227
2435
  const s = sentinel.parse(input, cur, probeCtx);
2228
2436
  if (s.ok) {
2437
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
2229
2438
  emit(cur);
2230
2439
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
2231
2440
  }
@@ -2241,6 +2450,7 @@ function scanTo(sentinel, { skip: skip2, raw = false, orEOF = false } = {}) {
2241
2450
  if (!advanced) cur++;
2242
2451
  }
2243
2452
  if (orEOF) {
2453
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
2244
2454
  emit(cur);
2245
2455
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
2246
2456
  }
@@ -2850,89 +3060,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2850
3060
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
2851
3061
  const nodeType = def.type ?? missingInferredType();
2852
3062
  const cstOutput = cstOutputHost(ctx.build);
3063
+ const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
2853
3064
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
2854
- 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 };
2855
- 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 };
3065
+ 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 };
3066
+ 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 };
2856
3067
  if (saved.buf !== void 0 || saved.ch !== void 0) {
2857
3068
  pushCstChild(ctx, built, rawEntry);
2858
3069
  }
2859
- return { ok: true, value: built, span: r.span };
2860
- }
2861
- };
2862
- }
2863
-
2864
- // src/compiler/line-index.ts
2865
- function buildLineIndex(input) {
2866
- const lineStarts = [0];
2867
- for (let i = 0; i < input.length; i++) {
2868
- if (input.charCodeAt(i) === 10) lineStarts.push(i + 1);
2869
- }
2870
- return { lineStarts };
2871
- }
2872
- function offsetToLineCol(index, offset) {
2873
- const { lineStarts } = index;
2874
- let lo = 0;
2875
- let hi = lineStarts.length - 1;
2876
- while (lo < hi) {
2877
- const mid = lo + hi + 1 >> 1;
2878
- if (lineStarts[mid] <= offset) lo = mid;
2879
- else hi = mid - 1;
2880
- }
2881
- return { line: lo + 1, col: offset - lineStarts[lo] + 1 };
2882
- }
2883
- function annotateSpan(span, index) {
2884
- const s = offsetToLineCol(index, span.start);
2885
- const e = offsetToLineCol(index, span.end);
2886
- return {
2887
- ...span,
2888
- startLine: s.line,
2889
- startColumn: s.col,
2890
- endLine: e.line,
2891
- endColumn: e.col
2892
- };
2893
- }
2894
-
2895
- // src/combinators/grammar.ts
2896
- function parser(opts, root) {
2897
- const clearTrivia = opts.trivia === null;
2898
- return {
2899
- _tag: "grammar",
2900
- _meta: root._meta,
2901
- _def: {
2902
- tag: "grammar",
2903
- parser: root,
2904
- triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
2905
- clearTrivia,
2906
- ...opts.captureTrivia ? { captureTrivia: true } : {},
2907
- trackLines: opts.trackLines ?? false
2908
- },
2909
- parse(input, pos, _ctx) {
2910
- const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
2911
- const ctx = {
2912
- ..._ctx,
2913
- trackLines,
2914
- // trivia: null clears (contiguous terms); a Combinator sets; undefined inherits.
2915
- ...clearTrivia ? {
2916
- trivia: void 0,
2917
- triviaKindLabels: void 0
2918
- } : opts.trivia != null ? {
2919
- trivia: opts.trivia,
2920
- ...opts.trivia._meta.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {}
2921
- } : {},
2922
- ...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {},
2923
- // Kind-filter for per-node capture. Resolve against this scope's trivia
2924
- // labels — this parser's own trivia if it declares one, else the INHERITED
2925
- // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
2926
- // trivia is inherited rather than re-declared here. No labels → undefined
2927
- // (capture all).
2928
- ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
2929
- };
2930
- const result = root.parse(input, pos ?? 0, ctx);
2931
- if (trackLines) {
2932
- const idx = buildLineIndex(input);
2933
- return { ...result, span: annotateSpan(result.span, idx) };
2934
- }
2935
- return result;
3070
+ return { ok: true, value: built, span };
2936
3071
  }
2937
3072
  };
2938
3073
  }
@@ -3220,7 +3355,7 @@ function normalizeCstCollapse(collapse) {
3220
3355
  return typeof collapse === "function" ? collapse : void 0;
3221
3356
  }
3222
3357
  function buildCstNode(type, children, _fields, span, _rawChildren, _triviaLog, state) {
3223
- return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children] };
3358
+ return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children] };
3224
3359
  }
3225
3360
  function cstBuildHost(typeOrOptions, children, _fields, span, rawChildren, triviaLog, state) {
3226
3361
  if (typeof typeOrOptions === "string") {