parseman 0.43.0 → 0.44.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 (52) hide show
  1. package/dist/combinators/attempt.d.ts.map +1 -1
  2. package/dist/combinators/choice.d.ts.map +1 -1
  3. package/dist/combinators/grammar.d.ts +7 -0
  4. package/dist/combinators/grammar.d.ts.map +1 -1
  5. package/dist/combinators/map.d.ts +11 -0
  6. package/dist/combinators/map.d.ts.map +1 -1
  7. package/dist/combinators/token.d.ts +3 -2
  8. package/dist/combinators/token.d.ts.map +1 -1
  9. package/dist/combinators/trivia-skip.d.ts +1 -0
  10. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  11. package/dist/compiler/codegen.d.ts.map +1 -1
  12. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  13. package/dist/compiler/scannable-run.d.ts +1 -1
  14. package/dist/compiler/scannable-run.d.ts.map +1 -1
  15. package/dist/compiler/trivia-fast-path.d.ts +4 -3
  16. package/dist/compiler/trivia-fast-path.d.ts.map +1 -1
  17. package/dist/cst/trivia-entries.d.ts +15 -9
  18. package/dist/cst/trivia-entries.d.ts.map +1 -1
  19. package/dist/cst/trivia-kinds.d.ts +15 -11
  20. package/dist/cst/trivia-kinds.d.ts.map +1 -1
  21. package/dist/functional/run.d.ts +22 -16
  22. package/dist/functional/run.d.ts.map +1 -1
  23. package/dist/index.cjs +1416 -1115
  24. package/dist/index.cjs.map +4 -4
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +1416 -1115
  28. package/dist/index.js.map +4 -4
  29. package/dist/language-service/index.cjs +161 -102
  30. package/dist/language-service/index.cjs.map +3 -3
  31. package/dist/language-service/index.js +161 -102
  32. package/dist/language-service/index.js.map +3 -3
  33. package/dist/plugin/evaluator.d.ts.map +1 -1
  34. package/dist/plugin/index.cjs +1217 -985
  35. package/dist/plugin/index.cjs.map +4 -4
  36. package/dist/plugin/index.d.ts.map +1 -1
  37. package/dist/plugin/index.js +1217 -985
  38. package/dist/plugin/index.js.map +4 -4
  39. package/dist/run/index.cjs +152 -102
  40. package/dist/run/index.cjs.map +3 -3
  41. package/dist/run/index.d.ts +1 -1
  42. package/dist/run/index.d.ts.map +1 -1
  43. package/dist/run/index.js +152 -102
  44. package/dist/run/index.js.map +3 -3
  45. package/dist/spec/index.cjs +1414 -1286
  46. package/dist/spec/index.cjs.map +4 -4
  47. package/dist/spec/index.js +1414 -1286
  48. package/dist/spec/index.js.map +4 -4
  49. package/dist/types.d.ts +25 -0
  50. package/dist/types.d.ts.map +1 -1
  51. package/dist/version.d.ts +1 -1
  52. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1053,1035 +1053,1099 @@ function keywords(words, opts = {}) {
1053
1053
  };
1054
1054
  }
1055
1055
 
1056
- // src/recovery/scan.ts
1057
- function probeAt(sentinel, input, pos, ctx) {
1058
- const s = {
1059
- _probe: ctx._probe,
1060
- _errors: ctx._errors,
1061
- _cstLeaves: ctx._cstLeaves,
1062
- _cstChildren: ctx._cstChildren,
1063
- _cstRawChildren: ctx._cstRawChildren,
1064
- _cstTriviaLog: ctx._cstTriviaLog,
1065
- _triviaLog: ctx._triviaLog,
1066
- _fields: ctx._fields,
1067
- _cstBuf: ctx._cstBuf,
1068
- _tolerant: ctx._tolerant,
1069
- _sync: ctx._sync
1070
- };
1071
- ctx._probe = void 0;
1072
- ctx._errors = void 0;
1073
- ctx._cstLeaves = void 0;
1074
- ctx._cstChildren = void 0;
1075
- ctx._cstRawChildren = void 0;
1076
- ctx._cstTriviaLog = void 0;
1077
- ctx._triviaLog = void 0;
1078
- ctx._fields = void 0;
1079
- ctx._cstBuf = void 0;
1080
- ctx._tolerant = void 0;
1081
- ctx._sync = void 0;
1082
- try {
1083
- return sentinel.parse(input, pos, ctx).ok;
1084
- } finally {
1085
- ctx._probe = s._probe;
1086
- ctx._errors = s._errors;
1087
- ctx._cstLeaves = s._cstLeaves;
1088
- ctx._cstChildren = s._cstChildren;
1089
- ctx._cstRawChildren = s._cstRawChildren;
1090
- ctx._cstTriviaLog = s._cstTriviaLog;
1091
- ctx._triviaLog = s._triviaLog;
1092
- ctx._fields = s._fields;
1093
- ctx._cstBuf = s._cstBuf;
1094
- ctx._tolerant = s._tolerant;
1095
- ctx._sync = s._sync;
1096
- }
1056
+ // src/cst/trivia-kinds.ts
1057
+ var labeledTriviaSpecs = /* @__PURE__ */ new WeakMap();
1058
+ function unwrapTrivia(p) {
1059
+ let cur = p;
1060
+ while (cur._def.tag === "trivia") cur = cur._def.parser;
1061
+ return cur;
1097
1062
  }
1098
- function orSentinel(a, b) {
1099
- if (b === void 0) return a;
1100
- return {
1101
- _tag: "orSentinel",
1102
- _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
1103
- _def: { tag: "unknown" },
1104
- parse(input, pos, ctx) {
1105
- const ra = a.parse(input, pos, ctx);
1106
- if (ra.ok) return ra;
1107
- return b.parse(input, pos, ctx);
1108
- }
1109
- };
1063
+ function peelLabel(p) {
1064
+ if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
1065
+ return null;
1110
1066
  }
1111
- function firstSetSentinel(fs) {
1112
- if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
1113
- const ranges = fs.ranges;
1114
- return {
1115
- _tag: "firstSetSentinel",
1116
- _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },
1117
- _def: { tag: "unknown" },
1118
- parse(input, pos) {
1119
- const code = pos < input.length ? input.codePointAt(pos) : void 0;
1120
- if (code !== void 0) {
1121
- for (const r of ranges) {
1122
- if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
1123
- }
1067
+ function analyzeLabeledTrivia(trivia2) {
1068
+ const cached = labeledTriviaSpecs.get(trivia2);
1069
+ if (cached !== void 0) return cached;
1070
+ let core = unwrapTrivia(trivia2);
1071
+ let minRepeats = 1;
1072
+ if (core._def.tag === "oneOrMore") {
1073
+ core = core._def.parser;
1074
+ minRepeats = 1;
1075
+ } else if (core._def.tag === "many") {
1076
+ core = core._def.parser;
1077
+ minRepeats = 0;
1078
+ }
1079
+ const arms = [];
1080
+ if (core._def.tag === "choice") {
1081
+ for (let i = 0; i < core._def.parsers.length; i++) {
1082
+ const peeled = peelLabel(core._def.parsers[i]);
1083
+ if (!peeled) {
1084
+ labeledTriviaSpecs.set(trivia2, null);
1085
+ return null;
1124
1086
  }
1125
- return { ok: false, expected: [], span: { start: pos, end: pos } };
1087
+ arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
1088
+ }
1089
+ } else {
1090
+ const peeled = peelLabel(core);
1091
+ if (!peeled) {
1092
+ labeledTriviaSpecs.set(trivia2, null);
1093
+ return null;
1126
1094
  }
1095
+ arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
1096
+ }
1097
+ const spec = {
1098
+ labels: arms.map((a) => a.label),
1099
+ arms,
1100
+ minRepeats
1127
1101
  };
1102
+ labeledTriviaSpecs.set(trivia2, spec);
1103
+ return spec;
1128
1104
  }
1129
- function recoverScan(input, from, ctx, sync, expected) {
1130
- let scanPos = from;
1131
- while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
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 };
1135
- ctx._errors?.push(error);
1136
- return { error, end: scanPos };
1137
- }
1138
- function matchesAt(sentinel, input, pos, ctx) {
1139
- return probeAt(sentinel, input, pos, ctx);
1105
+ function matchArmAt(input, pos, arm, state) {
1106
+ if (!startsFirstSet(arm, input, pos)) return null;
1107
+ const r = arm.parse(input, pos, { trackLines: false, state });
1108
+ if (!r.ok || r.span.end <= pos) return null;
1109
+ return { end: r.span.end };
1140
1110
  }
1141
- function captureError(ctx, error) {
1142
- if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
1111
+ function scanLabeledTriviaChunks(input, cur, spec, state) {
1112
+ const chunks = [];
1113
+ let pos = cur;
1114
+ while (pos < input.length) {
1115
+ let matched = null;
1116
+ for (const arm of spec.arms) {
1117
+ const m = matchArmAt(input, pos, arm.parser, state);
1118
+ if (m) {
1119
+ matched = { end: m.end, kindIndex: arm.kindIndex };
1120
+ break;
1121
+ }
1122
+ }
1123
+ if (!matched) break;
1124
+ chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
1125
+ pos = matched.end;
1126
+ }
1127
+ if (chunks.length < spec.minRepeats) {
1128
+ return { end: cur, chunks: [] };
1129
+ }
1130
+ return { end: pos, chunks };
1143
1131
  }
1144
- var REC = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError };
1145
-
1146
- // src/combinators/expect.ts
1147
- function deriveExpected(c) {
1148
- return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
1132
+ function scanLabeledTriviaEnd(input, cur, spec, state) {
1133
+ let pos = cur;
1134
+ let count = 0;
1135
+ while (pos < input.length) {
1136
+ let matched = null;
1137
+ for (const arm of spec.arms) {
1138
+ matched = matchArmAt(input, pos, arm.parser, state);
1139
+ if (matched) break;
1140
+ }
1141
+ if (!matched) break;
1142
+ pos = matched.end;
1143
+ count++;
1144
+ }
1145
+ return count < spec.minRepeats ? cur : pos;
1149
1146
  }
1150
- function derive(c, seen) {
1151
- const deriveExpected2 = (p) => derive(p, seen);
1152
- const def = c._def;
1153
- switch (def.tag) {
1154
- case "literal":
1155
- return [JSON.stringify(def.value)];
1156
- case "regex":
1157
- return [`/${def.source}/`];
1158
- case "keywords":
1159
- return def.words.map((w) => JSON.stringify(w));
1160
- case "label":
1161
- return [def.label];
1162
- case "choice":
1163
- return def.parsers.flatMap(deriveExpected2);
1164
- case "dispatch":
1165
- return deriveExpected2(def.selector);
1166
- // Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
1167
- // `optional(…)`/`many(…)` can match nothing, so the term after it is equally
1168
- // able to fail first — deriving from term 0 alone named a token the parse
1169
- // never actually required. A leading `not(…)` is zero-width and contributes
1170
- // nothing (its own expectation is about what must NOT be here), but it is
1171
- // nullable, so keep scanning past it. Stop at the first term that must match.
1172
- case "sequence": {
1173
- const out = [];
1174
- for (const term of def.parsers) {
1175
- if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
1176
- if (!matchesEmpty(term)) break;
1147
+ function visitLabeledTrivia(input, cur, spec, state, visit) {
1148
+ if (spec.minRepeats > 1) return void 0;
1149
+ let pos = cur;
1150
+ let count = 0;
1151
+ while (pos < input.length) {
1152
+ let matched = null;
1153
+ for (const arm of spec.arms) {
1154
+ const match = matchArmAt(input, pos, arm.parser, state);
1155
+ if (match) {
1156
+ matched = { end: match.end, kindIndex: arm.kindIndex };
1157
+ break;
1177
1158
  }
1178
- return out;
1179
1159
  }
1180
- case "attempt":
1181
- return deriveExpected2(def.parser);
1182
- // Delegating wrappers whose first-token failure is their inner parser's — must
1183
- // pass through so a start-failure (and the first-set-miss fast-path in attempt/
1184
- // node, which reads this) names the real expected token, not the wrapper tag.
1185
- case "field":
1186
- case "withCtx":
1187
- case "recover":
1188
- return deriveExpected2(def.parser);
1189
- case "skip":
1190
- return deriveExpected2(def.main);
1191
- case "expect":
1192
- return def.expected;
1193
- case "node":
1194
- case "grammar":
1195
- case "trivia":
1196
- case "token":
1197
- case "leaf":
1198
- case "optional":
1199
- case "many":
1200
- case "oneOrMore":
1201
- case "transform":
1202
- case "not":
1203
- case "peek":
1204
- return deriveExpected2(def.parser);
1205
- case "routed":
1206
- return ["routed()"];
1207
- case "lazy": {
1208
- const name = c._ruleName;
1209
- if (seen.has(c)) return name ? [name] : [];
1210
- seen.add(c);
1211
- try {
1212
- return deriveExpected2(def.thunk());
1213
- } catch {
1214
- return name ? [name] : [];
1215
- } finally {
1216
- seen.delete(c);
1160
+ if (!matched) break;
1161
+ visit(pos, matched.end, matched.kindIndex);
1162
+ pos = matched.end;
1163
+ count++;
1164
+ }
1165
+ return count < spec.minRepeats ? cur : pos;
1166
+ }
1167
+ function recordTriviaChunks(ctx, chunks) {
1168
+ const kinds = ctx.triviaKindLabels;
1169
+ const mask = ctx._triviaCaptureMask;
1170
+ const rootLog = ctx._rootTriviaLog;
1171
+ const rootKinds = ctx._rootTriviaKindIndex;
1172
+ const rootMark = rootLog?.length ?? 0;
1173
+ for (const ch2 of chunks) {
1174
+ pushTriviaLogEntry(ctx, ch2.start, ch2.end, kinds ? ch2.kindIndex : void 0);
1175
+ const rootKindIndex = ctx._rootTriviaCapture === false || rootLog === void 0 || rootKinds === void 0 || kinds === void 0 ? -1 : rootKinds[kinds[ch2.kindIndex] ?? ""] ?? -1;
1176
+ if (rootKindIndex >= 0) {
1177
+ rootLog.push(0, 0, ch2.start, ch2.end, rootKindIndex);
1178
+ }
1179
+ if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
1180
+ if (mask === void 0 || kinds === void 0 || (mask & 1 << ch2.kindIndex) !== 0) {
1181
+ pushCstTriviaEntry(ctx, ch2.start, ch2.end, kinds ? ch2.kindIndex : void 0);
1217
1182
  }
1218
1183
  }
1219
- default:
1220
- return [];
1221
1184
  }
1222
- }
1223
- function expect(combinator, label2) {
1224
- const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
1225
- const meta = {
1226
- firstSet: combinator._meta.firstSet,
1227
- canMatchNewline: combinator._meta.canMatchNewline,
1228
- isTrivia: false
1229
- };
1230
- return {
1231
- _tag: "expect",
1232
- _meta: meta,
1233
- _def: { tag: "expect", parser: combinator, label: label2, expected },
1234
- parse(input, pos, ctx) {
1235
- const result = combinator.parse(input, pos, ctx);
1236
- if (result.ok) return result;
1237
- const span = { start: pos, end: pos };
1238
- const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1239
- ctx._errors?.push(error);
1240
- if (ctx._tolerant) captureError(ctx, error);
1241
- return { ok: true, value: error, span: { start: pos, end: pos } };
1185
+ if (rootLog !== void 0 && rootLog.length !== rootMark && chunks.length > 0) {
1186
+ const start = chunks[0].start;
1187
+ const end = chunks[chunks.length - 1].end;
1188
+ for (let offset = rootMark; offset < rootLog.length; offset += 5) {
1189
+ rootLog[offset] = start;
1190
+ rootLog[offset + 1] = end;
1242
1191
  }
1243
- };
1244
- }
1245
- function isParseError(value) {
1246
- return typeof value === "object" && value !== null && value._tag === "parseError";
1192
+ }
1247
1193
  }
1248
-
1249
- // src/combinators/choice.ts
1250
- function choice(...args) {
1251
- const parsers = args.map((a) => "gate" in a ? a.combinator : a);
1252
- const gates = args.map((a) => "gate" in a ? a.gate : null);
1253
- const hasGates = gates.some((g) => g !== null);
1254
- const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
1255
- let combined = { kind: "empty" };
1256
- for (const p of parsers) combined = union(combined, p._meta.firstSet);
1257
- const meta = {
1258
- firstSet: combined,
1259
- canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
1260
- isTrivia: false,
1261
- disjoint
1262
- };
1263
- const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
1264
- const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
1265
- let greedyLitMap = null;
1266
- let sortedParsers = null;
1267
- const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
1268
- if (strategy?.tag === "greedyClassify") {
1269
- greedyLitMap = /* @__PURE__ */ new Map();
1270
- for (let i = 0; i < parsers.length; i++) {
1271
- if (i === strategy.superIndex) continue;
1272
- const litVal = getCoreLiteralValue(parsers[i]);
1273
- if (litVal !== null) greedyLitMap.set(litVal, i);
1274
- }
1275
- } else if (strategy?.tag === "literalsLongestFirst") {
1276
- sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
1194
+ function triviaKindMask(labels, keep) {
1195
+ if (!labels) return void 0;
1196
+ let mask = 0;
1197
+ for (const name of keep) {
1198
+ const idx = labels.indexOf(name);
1199
+ if (idx >= 0) mask |= 1 << idx;
1277
1200
  }
1201
+ return mask;
1202
+ }
1203
+
1204
+ // src/combinators/trivia-skip.ts
1205
+ var NOOP_COMMIT = () => {
1206
+ };
1207
+ var fastTriviaCache = /* @__PURE__ */ new WeakMap();
1208
+ function needsDeferredTriviaCommit(ctx) {
1209
+ return ctx._triviaLog !== void 0 || ctx._rootTriviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
1210
+ }
1211
+ function saveTriviaMark(ctx) {
1212
+ const m = saveCstMark(ctx);
1278
1213
  return {
1279
- _tag: "choice",
1280
- _meta: meta,
1281
- _def: {
1282
- tag: "choice",
1283
- parsers,
1284
- gates,
1285
- disjoint,
1286
- strategy: strategy ?? { tag: "firstMatch" },
1287
- autoNot
1288
- },
1289
- parse(input, pos, ctx) {
1290
- const expected = [];
1291
- if (disjoint && pos < input.length) {
1292
- const code = input.codePointAt(pos);
1293
- let idx = code < 128 ? asciiDispatch[code] : -1;
1294
- if (idx < 0) {
1295
- for (let i = 0; i < parsers.length; i++) {
1296
- if (inFirstSet(code, parsers[i]._meta.firstSet)) {
1297
- idx = i;
1298
- break;
1299
- }
1214
+ raw: m.raw,
1215
+ tlog: m.tlog,
1216
+ leaves: m.leaves,
1217
+ fields: m.fields,
1218
+ errors: m.errors,
1219
+ log: ctx._triviaLog ? ctx._triviaLog.length : 0,
1220
+ rootLog: ctx._rootTriviaLog ? ctx._rootTriviaLog.length : 0
1221
+ };
1222
+ }
1223
+ function rollbackTrivia(ctx, mark) {
1224
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1225
+ if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1226
+ if (ctx._rootTriviaLog && ctx._rootTriviaLog.length !== mark.rootLog) ctx._rootTriviaLog.length = mark.rootLog;
1227
+ }
1228
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1229
+ const probeCtx = {
1230
+ trackLines: ctx.trackLines,
1231
+ state: ctx.state,
1232
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1233
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1234
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1235
+ };
1236
+ const result = triviaP.parse(input, cur, probeCtx);
1237
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1238
+ return result;
1239
+ }
1240
+ function saveLookaheadMark(ctx) {
1241
+ return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1242
+ }
1243
+ function rollbackLookahead(ctx, mark) {
1244
+ rollbackTrivia(ctx, mark);
1245
+ if (ctx._probe) ctx._probe.best = mark.probeBest;
1246
+ }
1247
+ function scanWithLabels(input, cur, ctx) {
1248
+ const triviaP = ctx.trivia;
1249
+ const spec = analyzeLabeledTrivia(triviaP);
1250
+ if (!spec) return { end: cur, commit: NOOP_COMMIT };
1251
+ const log = ctx._triviaLog;
1252
+ const rootLog = ctx._rootTriviaLog;
1253
+ const rootKinds = ctx._rootTriviaKindIndex;
1254
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1255
+ const mask = ctx._triviaCaptureMask;
1256
+ let fullRows;
1257
+ let rootRows;
1258
+ let cstRows;
1259
+ const visitedEnd = visitLabeledTrivia(input, cur, spec, ctx.state, (start, matchEnd, kindIndex) => {
1260
+ if (log !== void 0) (fullRows ??= []).push(start, matchEnd, kindIndex);
1261
+ const selectedKind = rootLog === void 0 || rootKinds === void 0 || ctx._rootTriviaCapture === false ? void 0 : rootKinds[spec.labels[kindIndex] ?? ""];
1262
+ if (selectedKind !== void 0) (rootRows ??= []).push(start, matchEnd, selectedKind);
1263
+ if (captureTl && (mask === void 0 || (mask & 1 << kindIndex) !== 0)) {
1264
+ (cstRows ??= []).push(start, matchEnd, kindIndex);
1265
+ }
1266
+ });
1267
+ if (visitedEnd !== void 0) {
1268
+ if (visitedEnd === cur) return { end: cur, commit: NOOP_COMMIT };
1269
+ return {
1270
+ end: visitedEnd,
1271
+ commit: () => {
1272
+ if (fullRows !== void 0 && log !== void 0) log.push(...fullRows);
1273
+ if (rootRows !== void 0 && rootLog !== void 0) {
1274
+ for (let i = 0; i < rootRows.length; i += 3) {
1275
+ rootLog.push(cur, visitedEnd, rootRows[i], rootRows[i + 1], rootRows[i + 2]);
1300
1276
  }
1301
1277
  }
1302
- if (idx >= 0) {
1303
- const gate2 = gates[idx];
1304
- if (gate2 && !gate2(ctx.state)) {
1305
- return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
1278
+ if (cstRows !== void 0) {
1279
+ for (let i = 0; i < cstRows.length; i += 3) {
1280
+ pushCstTriviaEntry(ctx, cstRows[i], cstRows[i + 1], cstRows[i + 2]);
1306
1281
  }
1307
- const result = parsers[idx].parse(input, pos, ctx);
1308
- if (result.ok) return result;
1309
- expected.push(...result.expected);
1310
- if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
1311
- return { ok: false, expected, span: { start: pos, end: pos } };
1312
- }
1313
- return {
1314
- ok: false,
1315
- expected: parsers.flatMap((p) => {
1316
- const r = p.parse(input, pos, ctx);
1317
- return r.ok ? [] : r.expected;
1318
- }),
1319
- span: { start: pos, end: pos }
1320
- };
1321
- }
1322
- if (strategy?.tag === "greedyClassify") {
1323
- const superResult = parsers[strategy.superIndex].parse(input, pos, ctx);
1324
- if (!superResult.ok) return superResult;
1325
- const end = superResult.span.end;
1326
- const litIdx = greedyLitMap.get(input.slice(pos, end));
1327
- if (litIdx !== void 0) {
1328
- const litVal = getCoreLiteralValue(parsers[litIdx]);
1329
- const value = applyTransforms(parsers[litIdx], litVal, { start: pos, end });
1330
- return { ok: true, value, span: { start: pos, end } };
1331
- }
1332
- return superResult;
1333
- }
1334
- if (strategy?.tag === "literalsLongestFirst") {
1335
- for (const p of sortedParsers) {
1336
- const r = p.parse(input, pos, ctx);
1337
- if (r.ok) return r;
1338
- expected.push(...r.expected);
1339
- }
1340
- return { ok: false, expected, span: { start: pos, end: pos } };
1341
- }
1342
- for (let i = 0; i < parsers.length; i++) {
1343
- if (gates[i] && !gates[i](ctx.state)) continue;
1344
- const mark = saveCstMark(ctx);
1345
- const logLen = ctx._triviaLog?.length;
1346
- const result = parsers[i].parse(input, pos, ctx);
1347
- if (!result.ok) {
1348
- rollbackCstCapture(ctx, mark);
1349
- if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
1350
- expected.push(...result.expected);
1351
- if (result.committed) return { ok: false, expected, span: result.span, committed: true };
1352
- continue;
1353
- }
1354
- const checks = autoNot[i];
1355
- if (checks && autoNotFires(input, result.span.end, checks)) {
1356
- rollbackCstCapture(ctx, mark);
1357
- if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
1358
- continue;
1359
1282
  }
1360
- return result;
1361
1283
  }
1362
- return { ok: false, expected, span: { start: pos, end: pos } };
1363
- }
1284
+ };
1285
+ }
1286
+ const { end, chunks } = scanLabeledTriviaChunks(input, cur, spec, ctx.state);
1287
+ if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1288
+ return {
1289
+ end,
1290
+ commit: () => recordTriviaChunks(ctx, chunks)
1364
1291
  };
1365
1292
  }
1366
- function detectStrategy(parsers) {
1367
- const regexIndices = [];
1368
- const literalIndices = [];
1369
- for (let i = 0; i < parsers.length; i++) {
1370
- if (getCoreRegexDef(parsers[i]) !== null) regexIndices.push(i);
1371
- else if (getCoreLiteralValue(parsers[i]) !== null) literalIndices.push(i);
1372
- }
1373
- if (regexIndices.length === 1 && literalIndices.length === parsers.length - 1 && literalIndices.length > 0) {
1374
- const superIndex = regexIndices[0];
1375
- const regexDef = getCoreRegexDef(parsers[superIndex]);
1376
- const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
1377
- const re = new RegExp(regexDef.source, flags);
1378
- const allSubsumed = literalIndices.every((i) => {
1379
- const litVal = getCoreLiteralValue(parsers[i]);
1380
- re.lastIndex = 0;
1381
- const m = re.exec(litVal);
1382
- return m !== null && m[0] === litVal;
1383
- });
1384
- if (allSubsumed) return { tag: "greedyClassify", superIndex };
1293
+ function skipWithLabels(input, cur, ctx) {
1294
+ const spec = analyzeLabeledTrivia(ctx.trivia);
1295
+ if (spec) return scanLabeledTriviaEnd(input, cur, spec, ctx.state);
1296
+ const tr = parseTriviaNoCapture(ctx.trivia, input, cur, ctx);
1297
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1298
+ }
1299
+ function advanceTrivia(input, cur, ctx) {
1300
+ const triviaP = ctx.trivia;
1301
+ if (!triviaP) return cur;
1302
+ if (!ctx.trackLines) {
1303
+ const fast2 = fastTriviaScanner(triviaP);
1304
+ if (fast2) return fast2(input, cur);
1305
+ if (ctx.triviaKindLabels) return skipWithLabels(input, cur, ctx);
1306
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1307
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1385
1308
  }
1386
- if (parsers.length === literalIndices.length) {
1387
- const sortedIndices = [...literalIndices].sort(
1388
- (a, b) => getCoreLiteralValue(parsers[b]).length - getCoreLiteralValue(parsers[a]).length
1389
- );
1390
- return { tag: "literalsLongestFirst", sortedIndices };
1309
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1310
+ const fast = fastTriviaScanner(triviaP);
1311
+ if (fast) {
1312
+ const end = fast(input, cur);
1313
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1314
+ return end;
1391
1315
  }
1392
- const shared = detectSharedPrefix(parsers);
1393
- if (shared !== null) return shared;
1394
- return { tag: "firstMatch" };
1395
- }
1396
- function bareLeadingTermKey(term) {
1397
- const d = term._def;
1398
- if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
1399
- if (d.tag === "regex") return `R:${d.source}\0${d.flags}`;
1400
- return null;
1401
- }
1402
- function detectSharedPrefix(parsers) {
1403
- if (parsers.length < 2) return null;
1404
- let key = null;
1405
- let prefix = null;
1406
- const members = [];
1407
- for (let i = 0; i < parsers.length; i++) {
1408
- const term0 = leadingTermOfArm(parsers[i]);
1409
- if (term0 === null) return null;
1410
- const k = bareLeadingTermKey(term0);
1411
- if (k === null) return null;
1412
- if (key === null) {
1413
- key = k;
1414
- prefix = term0;
1415
- } else if (k !== key) return null;
1416
- members.push(i);
1316
+ if (ctx.triviaKindLabels) {
1317
+ const end = skipWithLabels(input, cur, ctx);
1318
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1319
+ return end;
1417
1320
  }
1418
- if (prefix === null || members.length < 2) return null;
1419
- return { tag: "sharedPrefix", prefix, members };
1321
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1322
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1420
1323
  }
1421
- function leadingTermOfArm(arm) {
1422
- let d = arm._def;
1423
- for (; ; ) {
1424
- if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
1425
- d = d.parser._def;
1426
- continue;
1324
+ function scanTrivia(input, cur, ctx) {
1325
+ const triviaP = ctx.trivia;
1326
+ if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1327
+ const log = ctx._triviaLog;
1328
+ const rootLog = ctx._rootTriviaLog;
1329
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1330
+ if (!ctx.trackLines) {
1331
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1332
+ if (fast2 && log === void 0 && !captureTl) {
1333
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1427
1334
  }
1428
- break;
1429
- }
1430
- if (d.tag !== "sequence" || d.parsers.length < 2) return null;
1431
- return d.parsers[0];
1432
- }
1433
- function computeAutoNot(parsers) {
1434
- return parsers.map((p, i) => {
1435
- const litVal = getCoreLiteralValue(p);
1436
- if (litVal === null) return null;
1437
- const checks = [];
1438
- for (let j = i + 1; j < parsers.length; j++) {
1439
- const other = parsers[j];
1440
- const otherLit = getCoreLiteralValue(other);
1441
- if (otherLit !== null && otherLit.startsWith(litVal) && otherLit.length > litVal.length) {
1442
- checks.push({ kind: "startsWith", value: otherLit.slice(litVal.length) });
1443
- continue;
1444
- }
1445
- const regexDef = getCoreRegexDef(other);
1446
- if (regexDef !== null) {
1447
- const contSet = continuationFirstSet(litVal, regexDef.source, regexDef.flags);
1448
- if (contSet !== null) checks.push({ kind: "firstSet", set: contSet });
1449
- }
1335
+ if (ctx.triviaKindLabels && (log !== void 0 || rootLog !== void 0 || captureTl)) {
1336
+ return scanWithLabels(input, cur, ctx);
1450
1337
  }
1451
- return checks.length > 0 ? checks : null;
1452
- });
1453
- }
1454
- function autoNotFires(input, end, checks) {
1455
- for (const check of checks) {
1456
- if (check.kind === "firstSet") {
1457
- const code = end < input.length ? input.codePointAt(end) ?? -1 : -1;
1458
- if (inFirstSet(code, check.set)) return true;
1459
- } else {
1460
- if (input.startsWith(check.value, end)) return true;
1338
+ if (ctx.triviaKindLabels) return { end: skipWithLabels(input, cur, ctx), commit: NOOP_COMMIT };
1339
+ if (log !== void 0 || rootLog !== void 0 || captureTl) {
1340
+ const tr3 = triviaP.parse(input, cur, {
1341
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1342
+ state: ctx.state
1343
+ });
1344
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1345
+ const end = tr3.span.end;
1346
+ return {
1347
+ end,
1348
+ commit: () => {
1349
+ pushTriviaLogEntry(ctx, cur, end);
1350
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1351
+ }
1352
+ };
1461
1353
  }
1354
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1355
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1462
1356
  }
1463
- return false;
1464
- }
1465
- function getCoreLiteralValue(p) {
1466
- const def = p._def;
1467
- if (def.tag === "literal" && !def.caseInsensitive) return def.value;
1468
- if (def.tag === "transform") return getCoreLiteralValue(def.parser);
1469
- return null;
1470
- }
1471
- function getCoreRegexDef(p) {
1472
- const def = p._def;
1473
- if (def.tag === "regex") return { source: def.source, flags: def.flags };
1474
- if (def.tag === "transform") return getCoreRegexDef(def.parser);
1475
- if (def.tag === "label") return getCoreRegexDef(def.parser);
1476
- return null;
1477
- }
1478
- function applyTransforms(p, value, span) {
1479
- const def = p._def;
1480
- if (def.tag === "transform") {
1481
- const inner = applyTransforms(def.parser, value, span);
1482
- return def.fn(inner, span);
1357
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1358
+ const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1359
+ if (fast && log === void 0 && !captureTl) {
1360
+ const end = fast(input, cur);
1361
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1362
+ return { end, commit: NOOP_COMMIT };
1483
1363
  }
1484
- return value;
1485
- }
1486
- function continuationFirstSet(lit, source, flags) {
1487
- const re = new RegExp(source, "y" + flags.replace(/[gy]/g, ""));
1488
- re.lastIndex = 0;
1489
- const base = re.exec(lit);
1490
- if (!base || base[0] !== lit) return null;
1491
- const contCodes = [];
1492
- for (let code = 1; code < 128; code++) {
1493
- re.lastIndex = 0;
1494
- const m = re.exec(lit + String.fromCharCode(code));
1495
- if (m && m[0].length > lit.length) contCodes.push(code);
1364
+ if (ctx.triviaKindLabels && (log !== void 0 || rootLog !== void 0 || captureTl)) {
1365
+ const scan = scanWithLabels(input, cur, ctx);
1366
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1367
+ return scan;
1496
1368
  }
1497
- if (contCodes.length === 0) return null;
1498
- return codesToFirstSet(contCodes);
1499
- }
1500
- function codesToFirstSet(codes) {
1501
- codes.sort((a, b) => a - b);
1502
- const ranges = [];
1503
- let lo = codes[0], hi = codes[0];
1504
- for (let i = 1; i < codes.length; i++) {
1505
- if (codes[i] === hi + 1) {
1506
- hi = codes[i];
1507
- } else {
1508
- ranges.push({ lo, hi });
1509
- lo = hi = codes[i];
1510
- }
1369
+ if (ctx.triviaKindLabels) {
1370
+ const end = skipWithLabels(input, cur, ctx);
1371
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1372
+ return { end, commit: NOOP_COMMIT };
1511
1373
  }
1512
- ranges.push({ lo, hi });
1513
- return { kind: "ranges", ranges };
1374
+ if (log !== void 0 || rootLog !== void 0 || captureTl) {
1375
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1376
+ if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1377
+ const end = tr2.span.end;
1378
+ return {
1379
+ end,
1380
+ commit: () => {
1381
+ pushTriviaLogEntry(ctx, cur, end);
1382
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1383
+ }
1384
+ };
1385
+ }
1386
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1387
+ return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1514
1388
  }
1515
- function inFirstSet(code, fs) {
1516
- if (fs.kind === "any") return true;
1517
- if (fs.kind === "empty") return false;
1518
- for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
1519
- return false;
1389
+ function consumeTrivia(input, cur, ctx) {
1390
+ if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1391
+ const scan = scanTrivia(input, cur, ctx);
1392
+ scan.commit();
1393
+ return scan.end;
1520
1394
  }
1521
- function areDisjoint(sets) {
1522
- if (sets.some((s) => s.kind === "any")) return false;
1523
- for (let i = 0; i < sets.length; i++)
1524
- for (let j = i + 1; j < sets.length; j++)
1525
- if (intersects(sets[i], sets[j])) return false;
1526
- return true;
1395
+ function fastTriviaScanner(trivia2) {
1396
+ const cached = fastTriviaCache.get(trivia2);
1397
+ if (cached !== void 0) return cached;
1398
+ const scanner = buildFastTriviaScanner(trivia2);
1399
+ fastTriviaCache.set(trivia2, scanner);
1400
+ return scanner;
1527
1401
  }
1528
- function buildAsciiDispatch(parsers) {
1529
- const table = Array(128).fill(-1);
1530
- for (let i = 0; i < parsers.length; i++) {
1531
- const fs = parsers[i]._meta.firstSet;
1532
- if (fs.kind !== "ranges") continue;
1533
- for (const { lo, hi } of fs.ranges) {
1534
- for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
1535
- table[code] = i;
1402
+ function buildFastTriviaScanner(trivia2) {
1403
+ const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1404
+ const direct = regexTriviaScanner(core);
1405
+ if (direct) return direct;
1406
+ const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1407
+ if (!repeat) return null;
1408
+ const one = regexTriviaScanner(repeat);
1409
+ if (one) return loopScanner([one]);
1410
+ if (repeat._def.tag !== "choice") return null;
1411
+ const arms = repeat._def.parsers.map(regexTriviaScanner);
1412
+ if (arms.some((s) => s === null)) return null;
1413
+ return loopScanner(arms);
1414
+ }
1415
+ function loopScanner(arms) {
1416
+ return (input, cur) => {
1417
+ let pos = cur;
1418
+ scan: while (pos < input.length) {
1419
+ for (const arm of arms) {
1420
+ const end = arm(input, pos);
1421
+ if (end > pos) {
1422
+ pos = end;
1423
+ continue scan;
1424
+ }
1536
1425
  }
1426
+ break;
1537
1427
  }
1538
- }
1539
- return table;
1540
- }
1541
-
1542
- // src/cst/trivia-kinds.ts
1543
- function unwrapTrivia(p) {
1544
- let cur = p;
1545
- while (cur._def.tag === "trivia") cur = cur._def.parser;
1546
- return cur;
1428
+ return pos;
1429
+ };
1547
1430
  }
1548
- function peelLabel(p) {
1549
- if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
1550
- return null;
1431
+ function regexTriviaScanner(parser2) {
1432
+ if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1433
+ const source = parser2._def.source;
1434
+ return classRunSource(source) ?? altStarSource(source);
1551
1435
  }
1552
- function analyzeLabeledTrivia(trivia2) {
1553
- let core = unwrapTrivia(trivia2);
1554
- let minRepeats = 1;
1555
- if (core._def.tag === "oneOrMore") {
1556
- core = core._def.parser;
1557
- minRepeats = 1;
1558
- } else if (core._def.tag === "many") {
1559
- core = core._def.parser;
1560
- minRepeats = 0;
1561
- }
1562
- const arms = [];
1563
- if (core._def.tag === "choice") {
1564
- for (let i = 0; i < core._def.parsers.length; i++) {
1565
- const peeled = peelLabel(core._def.parsers[i]);
1566
- if (!peeled) return null;
1567
- arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
1568
- }
1569
- } else {
1570
- const peeled = peelLabel(core);
1571
- if (!peeled) return null;
1572
- arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
1436
+ function inRanges2(cp, ranges) {
1437
+ for (let i = 0; i < ranges.length; i++) {
1438
+ const r = ranges[i];
1439
+ if (cp >= r[0] && cp <= r[1]) return true;
1573
1440
  }
1574
- return { labels: arms.map((a) => a.label), arms, minRepeats };
1441
+ return false;
1575
1442
  }
1576
- function matchArmAt(input, pos, arm) {
1577
- const r = arm.parse(input, pos, { trackLines: false });
1578
- if (!r.ok || r.span.end <= pos) return null;
1579
- return { end: r.span.end };
1443
+ function classScanner(classBody) {
1444
+ const ranges = parseClassRanges(classBody);
1445
+ if (!ranges) return null;
1446
+ return (input, cur) => {
1447
+ let pos = cur;
1448
+ while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1449
+ return pos;
1450
+ };
1580
1451
  }
1581
- function scanLabeledTriviaChunks(input, cur, spec) {
1582
- const chunks = [];
1583
- let pos = cur;
1584
- while (pos < input.length) {
1585
- let matched = null;
1586
- for (const arm of spec.arms) {
1587
- const m = matchArmAt(input, pos, arm.parser);
1588
- if (m) {
1589
- matched = { end: m.end, kindIndex: arm.kindIndex };
1590
- break;
1591
- }
1452
+ function untilLineBreakScanner(leaderCode) {
1453
+ return (input, cur) => {
1454
+ if (input.charCodeAt(cur) !== leaderCode) return cur;
1455
+ let pos = cur + 1;
1456
+ while (pos < input.length) {
1457
+ const cc = input.charCodeAt(pos);
1458
+ if (cc === 10 || cc === 13) break;
1459
+ pos++;
1592
1460
  }
1593
- if (!matched) break;
1594
- chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
1595
- pos = matched.end;
1461
+ return pos;
1462
+ };
1463
+ }
1464
+ function classRunSource(source) {
1465
+ const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1466
+ return m ? classScanner(m[1]) : null;
1467
+ }
1468
+ function classifyTriviaArm(arm) {
1469
+ const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1470
+ if (cls) {
1471
+ const ranges = parseClassRanges(cls[1]);
1472
+ return ranges ? { kind: "class", ranges } : null;
1596
1473
  }
1597
- if (chunks.length < spec.minRepeats) {
1598
- return { end: cur, chunks: [] };
1474
+ const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1475
+ if (lc) {
1476
+ const marker = lc[1];
1477
+ return { kind: "untilLineBreak", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
1599
1478
  }
1600
- return { end: pos, chunks };
1479
+ return null;
1601
1480
  }
1602
- function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
1603
- const chunks = [];
1604
- let pos = cur;
1605
- while (pos < input.length) {
1606
- const c = input.charCodeAt(pos);
1607
- if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {
1608
- const start = pos;
1609
- pos++;
1610
- while (pos < input.length) {
1611
- const c2 = input.charCodeAt(pos);
1612
- if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) pos++;
1613
- else break;
1481
+ function armScanner(arm) {
1482
+ if (arm.kind === "untilLineBreak") return untilLineBreakScanner(arm.code);
1483
+ const ranges = arm.ranges;
1484
+ return (input, cur) => {
1485
+ let pos = cur;
1486
+ while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1487
+ return pos;
1488
+ };
1489
+ }
1490
+ function fusedTriviaScanner(ranges, leaders) {
1491
+ const c0 = leaders[0];
1492
+ const single = leaders.length === 1;
1493
+ return (input, cur) => {
1494
+ let pos = cur;
1495
+ const len = input.length;
1496
+ for (; ; ) {
1497
+ const c = input.charCodeAt(pos);
1498
+ if (pos < len && inRanges2(c, ranges)) {
1499
+ pos++;
1500
+ continue;
1614
1501
  }
1615
- chunks.push({ start, end: pos, kindIndex: wsKind });
1616
- continue;
1617
- }
1618
- if (c === 47 && input.charCodeAt(pos + 1) === 42) {
1619
- let j = pos + 2;
1620
- while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++;
1621
- if (j + 1 < input.length && input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47) {
1622
- const start = pos;
1623
- pos = j + 2;
1624
- chunks.push({ start, end: pos, kindIndex: commentKind });
1502
+ if (single ? c === c0 : leaders.includes(c)) {
1503
+ pos++;
1504
+ while (pos < len) {
1505
+ const cc = input.charCodeAt(pos);
1506
+ if (cc === 10 || cc === 13) break;
1507
+ pos++;
1508
+ }
1625
1509
  continue;
1626
1510
  }
1627
1511
  break;
1628
1512
  }
1629
- break;
1630
- }
1631
- return { end: pos, chunks };
1632
- }
1633
- function tryFastLabeledScan(input, cur, trivia2) {
1634
- const spec = analyzeLabeledTrivia(trivia2);
1635
- if (!spec || spec.arms.length !== 2) return null;
1636
- const wsArm = spec.arms.find((a) => {
1637
- const src = getCoreRegexDef(a.parser)?.source;
1638
- return src != null && !src.includes("\\*");
1639
- });
1640
- const commentArm = spec.arms.find((a) => {
1641
- const src = getCoreRegexDef(a.parser)?.source;
1642
- return src != null && src.includes("\\*");
1643
- });
1644
- if (!wsArm || !commentArm) return null;
1645
- const { chunks } = scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1646
- if (chunks.length < spec.minRepeats) return { end: cur, chunks: [] };
1647
- return scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1513
+ return pos;
1514
+ };
1648
1515
  }
1649
- function recordTriviaChunks(ctx, chunks) {
1650
- const kinds = ctx.triviaKindLabels;
1651
- const mask = ctx._triviaCaptureMask;
1652
- for (const ch2 of chunks) {
1653
- pushTriviaLogEntry(ctx, ch2.start, ch2.end, kinds ? ch2.kindIndex : void 0);
1654
- if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
1655
- if (mask === void 0 || kinds === void 0 || (mask & 1 << ch2.kindIndex) !== 0) {
1656
- pushCstTriviaEntry(ctx, ch2.start, ch2.end, kinds ? ch2.kindIndex : void 0);
1657
- }
1516
+ function splitTopLevelAlts(body) {
1517
+ const arms = [];
1518
+ let inClass = false;
1519
+ let start = 0;
1520
+ for (let i = 0; i < body.length; i++) {
1521
+ const c = body[i];
1522
+ if (c === "\\") {
1523
+ i++;
1524
+ continue;
1525
+ }
1526
+ if (inClass) {
1527
+ if (c === "]") inClass = false;
1528
+ continue;
1529
+ }
1530
+ if (c === "[") inClass = true;
1531
+ else if (c === "(") return null;
1532
+ else if (c === "|") {
1533
+ arms.push(body.slice(start, i));
1534
+ start = i + 1;
1658
1535
  }
1659
1536
  }
1537
+ arms.push(body.slice(start));
1538
+ return arms;
1660
1539
  }
1661
- function triviaKindMask(labels, keep) {
1662
- if (!labels) return void 0;
1663
- let mask = 0;
1664
- for (const name of keep) {
1665
- const idx = labels.indexOf(name);
1666
- if (idx >= 0) mask |= 1 << idx;
1540
+ function altStarSource(source) {
1541
+ const m = /^\(\?:(.*)\)[*+]$/.exec(source);
1542
+ if (!m) return null;
1543
+ const armSrcs = splitTopLevelAlts(m[1]);
1544
+ if (!armSrcs || armSrcs.length < 2) return null;
1545
+ const arms = [];
1546
+ for (const src of armSrcs) {
1547
+ const arm = classifyTriviaArm(src);
1548
+ if (!arm) return null;
1549
+ arms.push(arm);
1667
1550
  }
1668
- return mask;
1551
+ const ranges = [];
1552
+ const leaders = [];
1553
+ for (const arm of arms) {
1554
+ if (arm.kind === "class") ranges.push(...arm.ranges);
1555
+ else leaders.push(arm.code);
1556
+ }
1557
+ if (leaders.some((code) => inRanges2(code, ranges))) {
1558
+ return loopScanner(arms.map(armScanner));
1559
+ }
1560
+ if (leaders.length === 0) return armScanner({ kind: "class", ranges });
1561
+ return fusedTriviaScanner(ranges, leaders);
1669
1562
  }
1670
1563
 
1671
- // src/combinators/trivia-skip.ts
1672
- var NOOP_COMMIT = () => {
1673
- };
1674
- var fastTriviaCache = /* @__PURE__ */ new WeakMap();
1675
- function needsDeferredTriviaCommit(ctx) {
1676
- return ctx._triviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
1677
- }
1678
- function saveTriviaMark(ctx) {
1679
- const m = saveCstMark(ctx);
1680
- return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, errors: m.errors, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1681
- }
1682
- function rollbackTrivia(ctx, mark) {
1683
- rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1684
- if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
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 } : {}
1564
+ // src/recovery/scan.ts
1565
+ function probeAt(sentinel, input, pos, ctx) {
1566
+ const s = {
1567
+ _probe: ctx._probe,
1568
+ _errors: ctx._errors,
1569
+ _cstLeaves: ctx._cstLeaves,
1570
+ _cstChildren: ctx._cstChildren,
1571
+ _cstRawChildren: ctx._cstRawChildren,
1572
+ _cstTriviaLog: ctx._cstTriviaLog,
1573
+ _triviaLog: ctx._triviaLog,
1574
+ _rootTriviaLog: ctx._rootTriviaLog,
1575
+ _fields: ctx._fields,
1576
+ _cstBuf: ctx._cstBuf,
1577
+ _tolerant: ctx._tolerant,
1578
+ _sync: ctx._sync
1693
1579
  };
1694
- const result = triviaP.parse(input, cur, probeCtx);
1695
- ctx._lineScannedTo = probeCtx._lineScannedTo;
1696
- return result;
1697
- }
1698
- function saveLookaheadMark(ctx) {
1699
- return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1700
- }
1701
- function rollbackLookahead(ctx, mark) {
1702
- rollbackTrivia(ctx, mark);
1703
- if (ctx._probe) ctx._probe.best = mark.probeBest;
1580
+ ctx._probe = void 0;
1581
+ ctx._errors = void 0;
1582
+ ctx._cstLeaves = void 0;
1583
+ ctx._cstChildren = void 0;
1584
+ ctx._cstRawChildren = void 0;
1585
+ ctx._cstTriviaLog = void 0;
1586
+ ctx._triviaLog = void 0;
1587
+ ctx._rootTriviaLog = void 0;
1588
+ ctx._fields = void 0;
1589
+ ctx._cstBuf = void 0;
1590
+ ctx._tolerant = void 0;
1591
+ ctx._sync = void 0;
1592
+ try {
1593
+ return sentinel.parse(input, pos, ctx).ok;
1594
+ } finally {
1595
+ ctx._probe = s._probe;
1596
+ ctx._errors = s._errors;
1597
+ ctx._cstLeaves = s._cstLeaves;
1598
+ ctx._cstChildren = s._cstChildren;
1599
+ ctx._cstRawChildren = s._cstRawChildren;
1600
+ ctx._cstTriviaLog = s._cstTriviaLog;
1601
+ ctx._triviaLog = s._triviaLog;
1602
+ ctx._rootTriviaLog = s._rootTriviaLog;
1603
+ ctx._fields = s._fields;
1604
+ ctx._cstBuf = s._cstBuf;
1605
+ ctx._tolerant = s._tolerant;
1606
+ ctx._sync = s._sync;
1607
+ }
1704
1608
  }
1705
- function scanWithLabels(input, cur, ctx) {
1706
- const triviaP = ctx.trivia;
1707
- const spec = analyzeLabeledTrivia(triviaP);
1708
- if (!spec) return { end: cur, commit: NOOP_COMMIT };
1709
- const fast = tryFastLabeledScan(input, cur, triviaP);
1710
- const { end, chunks } = fast ?? scanLabeledTriviaChunks(input, cur, spec);
1711
- if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1609
+ function orSentinel(a, b) {
1610
+ if (b === void 0) return a;
1712
1611
  return {
1713
- end,
1714
- commit: () => recordTriviaChunks(ctx, chunks)
1715
- };
1716
- }
1717
- function advanceTrivia(input, cur, ctx) {
1718
- const triviaP = ctx.trivia;
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;
1612
+ _tag: "orSentinel",
1613
+ _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
1614
+ _def: { tag: "unknown" },
1615
+ parse(input, pos, ctx) {
1616
+ const ra = a.parse(input, pos, ctx);
1617
+ if (ra.ok) return ra;
1618
+ return b.parse(input, pos, ctx);
1726
1619
  }
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;
1731
- const fast = fastTriviaScanner(triviaP);
1732
- if (fast) {
1733
- const end = fast(input, cur);
1734
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1735
- return end;
1736
- }
1737
- if (ctx.triviaKindLabels) {
1738
- const scan = scanWithLabels(input, cur, ctx);
1739
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1740
- return scan.end;
1741
- }
1742
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1743
- return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1620
+ };
1744
1621
  }
1745
- function scanTrivia(input, cur, ctx) {
1746
- const triviaP = ctx.trivia;
1747
- if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1748
- const log = ctx._triviaLog;
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);
1622
+ function firstSetSentinel(fs) {
1623
+ if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
1624
+ const ranges = fs.ranges;
1625
+ return {
1626
+ _tag: "firstSetSentinel",
1627
+ _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },
1628
+ _def: { tag: "unknown" },
1629
+ parse(input, pos) {
1630
+ const code = pos < input.length ? input.codePointAt(pos) : void 0;
1631
+ if (code !== void 0) {
1632
+ for (const r of ranges) {
1633
+ if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
1770
1634
  }
1771
- };
1635
+ }
1636
+ return { ok: false, expected: [], span: { start: pos, end: pos } };
1772
1637
  }
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;
1777
- const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1778
- if (fast && log === void 0 && !captureTl) {
1779
- const end = fast(input, cur);
1780
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1781
- return { end, commit: NOOP_COMMIT };
1782
- }
1783
- if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1784
- const scan = scanWithLabels(input, cur, ctx);
1785
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1786
- return scan;
1787
- }
1788
- if (log !== void 0 || captureTl) {
1789
- const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1790
- if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1791
- const end = tr2.span.end;
1792
- return {
1793
- end,
1794
- commit: () => {
1795
- pushTriviaLogEntry(ctx, cur, end);
1796
- if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1797
- }
1798
- };
1799
- }
1800
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1801
- return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1638
+ };
1802
1639
  }
1803
- function consumeTrivia(input, cur, ctx) {
1804
- if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1805
- const scan = scanTrivia(input, cur, ctx);
1806
- scan.commit();
1807
- return scan.end;
1640
+ function recoverScan(input, from, ctx, sync, expected) {
1641
+ let scanPos = from;
1642
+ while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1643
+ const span = { start: from, end: scanPos };
1644
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
1645
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1646
+ ctx._errors?.push(error);
1647
+ return { error, end: scanPos };
1808
1648
  }
1809
- function fastTriviaScanner(trivia2) {
1810
- const cached = fastTriviaCache.get(trivia2);
1811
- if (cached !== void 0) return cached;
1812
- const scanner = buildFastTriviaScanner(trivia2);
1813
- fastTriviaCache.set(trivia2, scanner);
1814
- return scanner;
1649
+ function matchesAt(sentinel, input, pos, ctx) {
1650
+ return probeAt(sentinel, input, pos, ctx);
1815
1651
  }
1816
- function buildFastTriviaScanner(trivia2) {
1817
- const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1818
- const direct = regexTriviaScanner(core);
1819
- if (direct) return direct;
1820
- const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1821
- if (!repeat) return null;
1822
- const one = regexTriviaScanner(repeat);
1823
- if (one) return loopScanner([one]);
1824
- if (repeat._def.tag !== "choice") return null;
1825
- const arms = repeat._def.parsers.map(regexTriviaScanner);
1826
- if (arms.some((s) => s === null)) return null;
1827
- return loopScanner(arms);
1652
+ function captureError(ctx, error) {
1653
+ if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
1828
1654
  }
1829
- function loopScanner(arms) {
1830
- return (input, cur) => {
1831
- let pos = cur;
1832
- scan: while (pos < input.length) {
1833
- for (const arm of arms) {
1834
- const end = arm(input, pos);
1835
- if (end > pos) {
1836
- pos = end;
1837
- continue scan;
1655
+ var REC = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError };
1656
+
1657
+ // src/combinators/sequence.ts
1658
+ function sequence(...parsers) {
1659
+ const meta = {
1660
+ // Union through the nullable prefix — a leading optional()/many() lets a later
1661
+ // term's first char start the sequence. Just `parsers[0]` under-approximates.
1662
+ firstSet: sequenceFirstSet(parsers),
1663
+ canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
1664
+ isTrivia: false
1665
+ };
1666
+ const def = { tag: "sequence", parsers };
1667
+ let followSentinels;
1668
+ function parseTolerant(input, pos, ctx) {
1669
+ followSentinels ??= parsers.map((_, i) => {
1670
+ const fs = parsers.slice(i + 1).reduce(
1671
+ (acc, p) => union(acc, firstSetOf(p)),
1672
+ { kind: "empty" }
1673
+ );
1674
+ return firstSetSentinel(fs);
1675
+ });
1676
+ const values = def.valueUnused ? void 0 : [];
1677
+ let cur = pos;
1678
+ const inheritedSync = ctx._sync;
1679
+ try {
1680
+ for (let i = 0; i < parsers.length; i++) {
1681
+ ctx._sync = followSentinels[i] ?? inheritedSync;
1682
+ if (ctx.trivia && i > 0) {
1683
+ const mark = saveTriviaMark(ctx);
1684
+ let scanEnd;
1685
+ if (needsDeferredTriviaCommit(ctx)) {
1686
+ const scan = scanTrivia(input, cur, ctx);
1687
+ scan.commit();
1688
+ scanEnd = scan.end;
1689
+ } else {
1690
+ scanEnd = advanceTrivia(input, cur, ctx);
1691
+ }
1692
+ const result2 = parsers[i].parse(input, scanEnd, ctx);
1693
+ if (!result2.ok) return result2;
1694
+ if (result2.span.end > scanEnd) cur = result2.span.end;
1695
+ else rollbackTrivia(ctx, mark);
1696
+ if (values !== void 0) values.push(result2.value);
1697
+ continue;
1838
1698
  }
1699
+ const result = parsers[i].parse(input, cur, ctx);
1700
+ if (!result.ok) return result;
1701
+ if (values !== void 0) values.push(result.value);
1702
+ cur = result.span.end;
1839
1703
  }
1840
- break;
1704
+ } finally {
1705
+ ctx._sync = inheritedSync;
1706
+ }
1707
+ return {
1708
+ ok: true,
1709
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1710
+ value: values ?? void 0,
1711
+ span: { start: pos, end: cur }
1712
+ };
1713
+ }
1714
+ return {
1715
+ _tag: "sequence",
1716
+ _meta: meta,
1717
+ _def: def,
1718
+ parse(input, pos, ctx) {
1719
+ if (ctx._tolerant) return parseTolerant(input, pos, ctx);
1720
+ const values = def.valueUnused ? void 0 : [];
1721
+ let cur = pos;
1722
+ for (let i = 0; i < parsers.length; i++) {
1723
+ if (ctx.trivia && i > 0) {
1724
+ let scanEnd;
1725
+ let mark = saveTriviaMark(ctx);
1726
+ if (needsDeferredTriviaCommit(ctx)) {
1727
+ const scan = scanTrivia(input, cur, ctx);
1728
+ scan.commit();
1729
+ scanEnd = scan.end;
1730
+ } else {
1731
+ scanEnd = advanceTrivia(input, cur, ctx);
1732
+ }
1733
+ const result2 = parsers[i].parse(input, scanEnd, ctx);
1734
+ if (!result2.ok) return result2;
1735
+ if (result2.span.end > scanEnd) {
1736
+ cur = result2.span.end;
1737
+ } else {
1738
+ rollbackTrivia(ctx, mark);
1739
+ }
1740
+ if (values !== void 0) values.push(result2.value);
1741
+ continue;
1742
+ }
1743
+ const result = parsers[i].parse(input, cur, ctx);
1744
+ if (!result.ok) return result;
1745
+ if (values !== void 0) values.push(result.value);
1746
+ cur = result.span.end;
1747
+ }
1748
+ return {
1749
+ ok: true,
1750
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1751
+ value: values ?? void 0,
1752
+ span: { start: pos, end: cur }
1753
+ };
1841
1754
  }
1842
- return pos;
1843
1755
  };
1844
1756
  }
1845
- function regexTriviaScanner(parser2) {
1846
- if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1847
- const source = parser2._def.source;
1848
- return classRunSource(source) ?? altStarSource(source) ?? (blockCommentSource(source) ? scanBlockComment : null);
1757
+
1758
+ // src/combinators/expect.ts
1759
+ function deriveExpected(c) {
1760
+ return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
1849
1761
  }
1850
- function inRanges2(cp, ranges) {
1851
- for (let i = 0; i < ranges.length; i++) {
1852
- const r = ranges[i];
1853
- if (cp >= r[0] && cp <= r[1]) return true;
1762
+ function derive(c, seen) {
1763
+ const deriveExpected2 = (p) => derive(p, seen);
1764
+ const def = c._def;
1765
+ switch (def.tag) {
1766
+ case "literal":
1767
+ return [JSON.stringify(def.value)];
1768
+ case "regex":
1769
+ return [`/${def.source}/`];
1770
+ case "keywords":
1771
+ return def.words.map((w) => JSON.stringify(w));
1772
+ case "label":
1773
+ return [def.label];
1774
+ case "choice":
1775
+ return def.parsers.flatMap(deriveExpected2);
1776
+ case "dispatch":
1777
+ return deriveExpected2(def.selector);
1778
+ // Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
1779
+ // `optional(…)`/`many(…)` can match nothing, so the term after it is equally
1780
+ // able to fail first — deriving from term 0 alone named a token the parse
1781
+ // never actually required. A leading `not(…)` is zero-width and contributes
1782
+ // nothing (its own expectation is about what must NOT be here), but it is
1783
+ // nullable, so keep scanning past it. Stop at the first term that must match.
1784
+ case "sequence": {
1785
+ const out = [];
1786
+ for (const term of def.parsers) {
1787
+ if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
1788
+ if (!matchesEmpty(term)) break;
1789
+ }
1790
+ return out;
1791
+ }
1792
+ case "attempt":
1793
+ return deriveExpected2(def.parser);
1794
+ // Delegating wrappers whose first-token failure is their inner parser's — must
1795
+ // pass through so a start-failure (and the first-set-miss fast-path in attempt/
1796
+ // node, which reads this) names the real expected token, not the wrapper tag.
1797
+ case "field":
1798
+ case "withCtx":
1799
+ case "recover":
1800
+ return deriveExpected2(def.parser);
1801
+ case "skip":
1802
+ return deriveExpected2(def.main);
1803
+ case "expect":
1804
+ return def.expected;
1805
+ case "node":
1806
+ case "grammar":
1807
+ case "trivia":
1808
+ case "token":
1809
+ case "leaf":
1810
+ case "optional":
1811
+ case "many":
1812
+ case "oneOrMore":
1813
+ case "transform":
1814
+ case "not":
1815
+ case "peek":
1816
+ return deriveExpected2(def.parser);
1817
+ case "routed":
1818
+ return ["routed()"];
1819
+ case "lazy": {
1820
+ const name = c._ruleName;
1821
+ if (seen.has(c)) return name ? [name] : [];
1822
+ seen.add(c);
1823
+ try {
1824
+ return deriveExpected2(def.thunk());
1825
+ } catch {
1826
+ return name ? [name] : [];
1827
+ } finally {
1828
+ seen.delete(c);
1829
+ }
1830
+ }
1831
+ default:
1832
+ return [];
1854
1833
  }
1855
- return false;
1856
1834
  }
1857
- function classScanner(classBody) {
1858
- const ranges = parseClassRanges(classBody);
1859
- if (!ranges) return null;
1860
- return (input, cur) => {
1861
- let pos = cur;
1862
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1863
- return pos;
1835
+ function expect(combinator, label2) {
1836
+ const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
1837
+ const meta = {
1838
+ firstSet: combinator._meta.firstSet,
1839
+ canMatchNewline: combinator._meta.canMatchNewline,
1840
+ isTrivia: false
1864
1841
  };
1865
- }
1866
- function lineCommentScanner(commentCode) {
1867
- return (input, cur) => {
1868
- if (input.charCodeAt(cur) !== commentCode) return cur;
1869
- let pos = cur + 1;
1870
- while (pos < input.length) {
1871
- const cc = input.charCodeAt(pos);
1872
- if (cc === 10 || cc === 13) break;
1873
- pos++;
1842
+ return {
1843
+ _tag: "expect",
1844
+ _meta: meta,
1845
+ _def: { tag: "expect", parser: combinator, label: label2, expected },
1846
+ parse(input, pos, ctx) {
1847
+ const result = combinator.parse(input, pos, ctx);
1848
+ if (result.ok) return result;
1849
+ const span = { start: pos, end: pos };
1850
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1851
+ ctx._errors?.push(error);
1852
+ if (ctx._tolerant) captureError(ctx, error);
1853
+ return { ok: true, value: error, span: { start: pos, end: pos } };
1874
1854
  }
1875
- return pos;
1876
1855
  };
1877
1856
  }
1878
- function classRunSource(source) {
1879
- const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1880
- return m ? classScanner(m[1]) : null;
1857
+ function isParseError(value) {
1858
+ return typeof value === "object" && value !== null && value._tag === "parseError";
1881
1859
  }
1882
- function classifyTriviaArm(arm) {
1883
- const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1884
- if (cls) {
1885
- const ranges = parseClassRanges(cls[1]);
1886
- return ranges ? { kind: "class", ranges } : null;
1860
+
1861
+ // src/combinators/choice.ts
1862
+ function choice(...args) {
1863
+ const parsers = args.map((a) => "gate" in a ? a.combinator : a);
1864
+ const gates = args.map((a) => "gate" in a ? a.gate : null);
1865
+ const hasGates = gates.some((g) => g !== null);
1866
+ const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
1867
+ let combined = { kind: "empty" };
1868
+ for (const p of parsers) combined = union(combined, p._meta.firstSet);
1869
+ const meta = {
1870
+ firstSet: combined,
1871
+ canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
1872
+ isTrivia: false,
1873
+ disjoint
1874
+ };
1875
+ const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
1876
+ const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
1877
+ let greedyLitMap = null;
1878
+ let sortedParsers = null;
1879
+ const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
1880
+ if (strategy?.tag === "greedyClassify") {
1881
+ greedyLitMap = /* @__PURE__ */ new Map();
1882
+ for (let i = 0; i < parsers.length; i++) {
1883
+ if (i === strategy.superIndex) continue;
1884
+ const litVal = getCoreLiteralValue(parsers[i]);
1885
+ if (litVal !== null) greedyLitMap.set(litVal, i);
1886
+ }
1887
+ } else if (strategy?.tag === "literalsLongestFirst") {
1888
+ sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
1887
1889
  }
1888
- const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1889
- if (lc) {
1890
- const marker = lc[1];
1891
- return { kind: "comment", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
1890
+ return {
1891
+ _tag: "choice",
1892
+ _meta: meta,
1893
+ _def: {
1894
+ tag: "choice",
1895
+ parsers,
1896
+ gates,
1897
+ disjoint,
1898
+ strategy: strategy ?? { tag: "firstMatch" },
1899
+ autoNot
1900
+ },
1901
+ parse(input, pos, ctx) {
1902
+ const expected = [];
1903
+ if (disjoint && pos < input.length) {
1904
+ const code = input.codePointAt(pos);
1905
+ let idx = code < 128 ? asciiDispatch[code] : -1;
1906
+ if (idx < 0) {
1907
+ for (let i = 0; i < parsers.length; i++) {
1908
+ if (inFirstSet(code, parsers[i]._meta.firstSet)) {
1909
+ idx = i;
1910
+ break;
1911
+ }
1912
+ }
1913
+ }
1914
+ if (idx >= 0) {
1915
+ const gate2 = gates[idx];
1916
+ if (gate2 && !gate2(ctx.state)) {
1917
+ return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
1918
+ }
1919
+ const result = parsers[idx].parse(input, pos, ctx);
1920
+ if (result.ok) return result;
1921
+ expected.push(...result.expected);
1922
+ if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
1923
+ return { ok: false, expected, span: { start: pos, end: pos } };
1924
+ }
1925
+ return {
1926
+ ok: false,
1927
+ expected: parsers.flatMap((p) => {
1928
+ const r = p.parse(input, pos, ctx);
1929
+ return r.ok ? [] : r.expected;
1930
+ }),
1931
+ span: { start: pos, end: pos }
1932
+ };
1933
+ }
1934
+ if (strategy?.tag === "greedyClassify") {
1935
+ const superResult = parsers[strategy.superIndex].parse(input, pos, ctx);
1936
+ if (!superResult.ok) return superResult;
1937
+ const end = superResult.span.end;
1938
+ const litIdx = greedyLitMap.get(input.slice(pos, end));
1939
+ if (litIdx !== void 0) {
1940
+ const litVal = getCoreLiteralValue(parsers[litIdx]);
1941
+ const value = applyTransforms(parsers[litIdx], litVal, { start: pos, end });
1942
+ return { ok: true, value, span: { start: pos, end } };
1943
+ }
1944
+ return superResult;
1945
+ }
1946
+ if (strategy?.tag === "literalsLongestFirst") {
1947
+ for (const p of sortedParsers) {
1948
+ const r = p.parse(input, pos, ctx);
1949
+ if (r.ok) return r;
1950
+ expected.push(...r.expected);
1951
+ }
1952
+ return { ok: false, expected, span: { start: pos, end: pos } };
1953
+ }
1954
+ for (let i = 0; i < parsers.length; i++) {
1955
+ if (gates[i] && !gates[i](ctx.state)) continue;
1956
+ const mark = saveTriviaMark(ctx);
1957
+ const result = parsers[i].parse(input, pos, ctx);
1958
+ if (!result.ok) {
1959
+ rollbackTrivia(ctx, mark);
1960
+ expected.push(...result.expected);
1961
+ if (result.committed) return { ok: false, expected, span: result.span, committed: true };
1962
+ continue;
1963
+ }
1964
+ const checks = autoNot[i];
1965
+ if (checks && autoNotFires(input, result.span.end, checks)) {
1966
+ rollbackTrivia(ctx, mark);
1967
+ continue;
1968
+ }
1969
+ return result;
1970
+ }
1971
+ return { ok: false, expected, span: { start: pos, end: pos } };
1972
+ }
1973
+ };
1974
+ }
1975
+ function detectStrategy(parsers) {
1976
+ const regexIndices = [];
1977
+ const literalIndices = [];
1978
+ for (let i = 0; i < parsers.length; i++) {
1979
+ if (getCoreRegexDef(parsers[i]) !== null) regexIndices.push(i);
1980
+ else if (getCoreLiteralValue(parsers[i]) !== null) literalIndices.push(i);
1981
+ }
1982
+ if (regexIndices.length === 1 && literalIndices.length === parsers.length - 1 && literalIndices.length > 0) {
1983
+ const superIndex = regexIndices[0];
1984
+ const regexDef = getCoreRegexDef(parsers[superIndex]);
1985
+ const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
1986
+ const re = new RegExp(regexDef.source, flags);
1987
+ const allSubsumed = literalIndices.every((i) => {
1988
+ const litVal = getCoreLiteralValue(parsers[i]);
1989
+ re.lastIndex = 0;
1990
+ const m = re.exec(litVal);
1991
+ return m !== null && m[0] === litVal;
1992
+ });
1993
+ if (allSubsumed) return { tag: "greedyClassify", superIndex };
1994
+ }
1995
+ if (parsers.length === literalIndices.length) {
1996
+ const sortedIndices = [...literalIndices].sort(
1997
+ (a, b) => getCoreLiteralValue(parsers[b]).length - getCoreLiteralValue(parsers[a]).length
1998
+ );
1999
+ return { tag: "literalsLongestFirst", sortedIndices };
1892
2000
  }
2001
+ const shared = detectSharedPrefix(parsers);
2002
+ if (shared !== null) return shared;
2003
+ return { tag: "firstMatch" };
2004
+ }
2005
+ function bareLeadingTermKey(term) {
2006
+ const d = term._def;
2007
+ if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
2008
+ if (d.tag === "regex") return `R:${d.source}\0${d.flags}`;
1893
2009
  return null;
1894
2010
  }
1895
- function armScanner(arm) {
1896
- if (arm.kind === "comment") return lineCommentScanner(arm.code);
1897
- const ranges = arm.ranges;
1898
- return (input, cur) => {
1899
- let pos = cur;
1900
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1901
- return pos;
1902
- };
2011
+ function detectSharedPrefix(parsers) {
2012
+ if (parsers.length < 2) return null;
2013
+ let key = null;
2014
+ let prefix = null;
2015
+ const members = [];
2016
+ for (let i = 0; i < parsers.length; i++) {
2017
+ const term0 = leadingTermOfArm(parsers[i]);
2018
+ if (term0 === null) return null;
2019
+ const k = bareLeadingTermKey(term0);
2020
+ if (k === null) return null;
2021
+ if (key === null) {
2022
+ key = k;
2023
+ prefix = term0;
2024
+ } else if (k !== key) return null;
2025
+ members.push(i);
2026
+ }
2027
+ if (prefix === null || members.length < 2) return null;
2028
+ return { tag: "sharedPrefix", prefix, members };
1903
2029
  }
1904
- function fusedTriviaScanner(ranges, commentCodes) {
1905
- const c0 = commentCodes[0];
1906
- const single = commentCodes.length === 1;
1907
- return (input, cur) => {
1908
- let pos = cur;
1909
- const len = input.length;
1910
- for (; ; ) {
1911
- const c = input.charCodeAt(pos);
1912
- if (pos < len && inRanges2(c, ranges)) {
1913
- pos++;
2030
+ function leadingTermOfArm(arm) {
2031
+ let d = arm._def;
2032
+ for (; ; ) {
2033
+ if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
2034
+ d = d.parser._def;
2035
+ continue;
2036
+ }
2037
+ break;
2038
+ }
2039
+ if (d.tag !== "sequence" || d.parsers.length < 2) return null;
2040
+ return d.parsers[0];
2041
+ }
2042
+ function computeAutoNot(parsers) {
2043
+ return parsers.map((p, i) => {
2044
+ const litVal = getCoreLiteralValue(p);
2045
+ if (litVal === null) return null;
2046
+ const checks = [];
2047
+ for (let j = i + 1; j < parsers.length; j++) {
2048
+ const other = parsers[j];
2049
+ const otherLit = getCoreLiteralValue(other);
2050
+ if (otherLit !== null && otherLit.startsWith(litVal) && otherLit.length > litVal.length) {
2051
+ checks.push({ kind: "startsWith", value: otherLit.slice(litVal.length) });
1914
2052
  continue;
1915
2053
  }
1916
- if (single ? c === c0 : commentCodes.includes(c)) {
1917
- pos++;
1918
- while (pos < len) {
1919
- const cc = input.charCodeAt(pos);
1920
- if (cc === 10 || cc === 13) break;
1921
- pos++;
1922
- }
1923
- continue;
2054
+ const regexDef = getCoreRegexDef(other);
2055
+ if (regexDef !== null) {
2056
+ const contSet = continuationFirstSet(litVal, regexDef.source, regexDef.flags);
2057
+ if (contSet !== null) checks.push({ kind: "firstSet", set: contSet });
1924
2058
  }
1925
- break;
1926
2059
  }
1927
- return pos;
1928
- };
2060
+ return checks.length > 0 ? checks : null;
2061
+ });
1929
2062
  }
1930
- function splitTopLevelAlts(body) {
1931
- const arms = [];
1932
- let inClass = false;
1933
- let start = 0;
1934
- for (let i = 0; i < body.length; i++) {
1935
- const c = body[i];
1936
- if (c === "\\") {
1937
- i++;
1938
- continue;
1939
- }
1940
- if (inClass) {
1941
- if (c === "]") inClass = false;
1942
- continue;
1943
- }
1944
- if (c === "[") inClass = true;
1945
- else if (c === "(") return null;
1946
- else if (c === "|") {
1947
- arms.push(body.slice(start, i));
1948
- start = i + 1;
2063
+ function autoNotFires(input, end, checks) {
2064
+ for (const check of checks) {
2065
+ if (check.kind === "firstSet") {
2066
+ const code = end < input.length ? input.codePointAt(end) ?? -1 : -1;
2067
+ if (inFirstSet(code, check.set)) return true;
2068
+ } else {
2069
+ if (input.startsWith(check.value, end)) return true;
1949
2070
  }
1950
2071
  }
1951
- arms.push(body.slice(start));
1952
- return arms;
2072
+ return false;
1953
2073
  }
1954
- function altStarSource(source) {
1955
- const m = /^\(\?:(.*)\)[*+]$/.exec(source);
1956
- if (!m) return null;
1957
- const armSrcs = splitTopLevelAlts(m[1]);
1958
- if (!armSrcs || armSrcs.length < 2) return null;
1959
- const arms = [];
1960
- for (const src of armSrcs) {
1961
- const arm = classifyTriviaArm(src);
1962
- if (!arm) return null;
1963
- arms.push(arm);
1964
- }
1965
- const ranges = [];
1966
- const commentCodes = [];
1967
- for (const arm of arms) {
1968
- if (arm.kind === "class") ranges.push(...arm.ranges);
1969
- else commentCodes.push(arm.code);
1970
- }
1971
- if (commentCodes.some((code) => inRanges2(code, ranges))) {
1972
- return loopScanner(arms.map(armScanner));
1973
- }
1974
- if (commentCodes.length === 0) return armScanner({ kind: "class", ranges });
1975
- return fusedTriviaScanner(ranges, commentCodes);
2074
+ function getCoreLiteralValue(p) {
2075
+ const def = p._def;
2076
+ if (def.tag === "literal" && !def.caseInsensitive) return def.value;
2077
+ if (def.tag === "transform") return getCoreLiteralValue(def.parser);
2078
+ return null;
2079
+ }
2080
+ function getCoreRegexDef(p) {
2081
+ const def = p._def;
2082
+ if (def.tag === "regex") return { source: def.source, flags: def.flags };
2083
+ if (def.tag === "transform") return getCoreRegexDef(def.parser);
2084
+ if (def.tag === "label") return getCoreRegexDef(def.parser);
2085
+ return null;
1976
2086
  }
1977
- function blockCommentSource(source) {
1978
- return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
2087
+ function applyTransforms(p, value, span) {
2088
+ const def = p._def;
2089
+ if (def.tag === "transform") {
2090
+ const inner = applyTransforms(def.parser, value, span);
2091
+ return def.fn(inner, span);
2092
+ }
2093
+ return value;
1979
2094
  }
1980
- function scanBlockComment(input, cur) {
1981
- if (input.charCodeAt(cur) !== 47 || input.charCodeAt(cur + 1) !== 42) return cur;
1982
- const close = input.indexOf("*/", cur + 2);
1983
- return close === -1 ? cur : close + 2;
2095
+ function continuationFirstSet(lit, source, flags) {
2096
+ const re = new RegExp(source, "y" + flags.replace(/[gy]/g, ""));
2097
+ re.lastIndex = 0;
2098
+ const base = re.exec(lit);
2099
+ if (!base || base[0] !== lit) return null;
2100
+ const contCodes = [];
2101
+ for (let code = 1; code < 128; code++) {
2102
+ re.lastIndex = 0;
2103
+ const m = re.exec(lit + String.fromCharCode(code));
2104
+ if (m && m[0].length > lit.length) contCodes.push(code);
2105
+ }
2106
+ if (contCodes.length === 0) return null;
2107
+ return codesToFirstSet(contCodes);
1984
2108
  }
1985
-
1986
- // src/combinators/sequence.ts
1987
- function sequence(...parsers) {
1988
- const meta = {
1989
- // Union through the nullable prefix — a leading optional()/many() lets a later
1990
- // term's first char start the sequence. Just `parsers[0]` under-approximates.
1991
- firstSet: sequenceFirstSet(parsers),
1992
- canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
1993
- isTrivia: false
1994
- };
1995
- const def = { tag: "sequence", parsers };
1996
- let followSentinels;
1997
- function parseTolerant(input, pos, ctx) {
1998
- followSentinels ??= parsers.map((_, i) => {
1999
- const fs = parsers.slice(i + 1).reduce(
2000
- (acc, p) => union(acc, firstSetOf(p)),
2001
- { kind: "empty" }
2002
- );
2003
- return firstSetSentinel(fs);
2004
- });
2005
- const values = def.valueUnused ? void 0 : [];
2006
- let cur = pos;
2007
- const inheritedSync = ctx._sync;
2008
- try {
2009
- for (let i = 0; i < parsers.length; i++) {
2010
- ctx._sync = followSentinels[i] ?? inheritedSync;
2011
- if (ctx.trivia && i > 0) {
2012
- const mark = saveTriviaMark(ctx);
2013
- let scanEnd;
2014
- if (needsDeferredTriviaCommit(ctx)) {
2015
- const scan = scanTrivia(input, cur, ctx);
2016
- scan.commit();
2017
- scanEnd = scan.end;
2018
- } else {
2019
- scanEnd = advanceTrivia(input, cur, ctx);
2020
- }
2021
- const result2 = parsers[i].parse(input, scanEnd, ctx);
2022
- if (!result2.ok) return result2;
2023
- if (result2.span.end > scanEnd) cur = result2.span.end;
2024
- else rollbackTrivia(ctx, mark);
2025
- if (values !== void 0) values.push(result2.value);
2026
- continue;
2027
- }
2028
- const result = parsers[i].parse(input, cur, ctx);
2029
- if (!result.ok) return result;
2030
- if (values !== void 0) values.push(result.value);
2031
- cur = result.span.end;
2032
- }
2033
- } finally {
2034
- ctx._sync = inheritedSync;
2109
+ function codesToFirstSet(codes) {
2110
+ codes.sort((a, b) => a - b);
2111
+ const ranges = [];
2112
+ let lo = codes[0], hi = codes[0];
2113
+ for (let i = 1; i < codes.length; i++) {
2114
+ if (codes[i] === hi + 1) {
2115
+ hi = codes[i];
2116
+ } else {
2117
+ ranges.push({ lo, hi });
2118
+ lo = hi = codes[i];
2035
2119
  }
2036
- return {
2037
- ok: true,
2038
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2039
- value: values ?? void 0,
2040
- span: { start: pos, end: cur }
2041
- };
2042
2120
  }
2043
- return {
2044
- _tag: "sequence",
2045
- _meta: meta,
2046
- _def: def,
2047
- parse(input, pos, ctx) {
2048
- if (ctx._tolerant) return parseTolerant(input, pos, ctx);
2049
- const values = def.valueUnused ? void 0 : [];
2050
- let cur = pos;
2051
- for (let i = 0; i < parsers.length; i++) {
2052
- if (ctx.trivia && i > 0) {
2053
- let scanEnd;
2054
- let mark = saveTriviaMark(ctx);
2055
- if (needsDeferredTriviaCommit(ctx)) {
2056
- const scan = scanTrivia(input, cur, ctx);
2057
- scan.commit();
2058
- scanEnd = scan.end;
2059
- } else {
2060
- scanEnd = advanceTrivia(input, cur, ctx);
2061
- }
2062
- const result2 = parsers[i].parse(input, scanEnd, ctx);
2063
- if (!result2.ok) return result2;
2064
- if (result2.span.end > scanEnd) {
2065
- cur = result2.span.end;
2066
- } else {
2067
- rollbackTrivia(ctx, mark);
2068
- }
2069
- if (values !== void 0) values.push(result2.value);
2070
- continue;
2071
- }
2072
- const result = parsers[i].parse(input, cur, ctx);
2073
- if (!result.ok) return result;
2074
- if (values !== void 0) values.push(result.value);
2075
- cur = result.span.end;
2121
+ ranges.push({ lo, hi });
2122
+ return { kind: "ranges", ranges };
2123
+ }
2124
+ function inFirstSet(code, fs) {
2125
+ if (fs.kind === "any") return true;
2126
+ if (fs.kind === "empty") return false;
2127
+ for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
2128
+ return false;
2129
+ }
2130
+ function areDisjoint(sets) {
2131
+ if (sets.some((s) => s.kind === "any")) return false;
2132
+ for (let i = 0; i < sets.length; i++)
2133
+ for (let j = i + 1; j < sets.length; j++)
2134
+ if (intersects(sets[i], sets[j])) return false;
2135
+ return true;
2136
+ }
2137
+ function buildAsciiDispatch(parsers) {
2138
+ const table = Array(128).fill(-1);
2139
+ for (let i = 0; i < parsers.length; i++) {
2140
+ const fs = parsers[i]._meta.firstSet;
2141
+ if (fs.kind !== "ranges") continue;
2142
+ for (const { lo, hi } of fs.ranges) {
2143
+ for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
2144
+ table[code] = i;
2076
2145
  }
2077
- return {
2078
- ok: true,
2079
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2080
- value: values ?? void 0,
2081
- span: { start: pos, end: cur }
2082
- };
2083
2146
  }
2084
- };
2147
+ }
2148
+ return table;
2085
2149
  }
2086
2150
 
2087
2151
  // src/combinators/dispatch.ts
@@ -2344,10 +2408,12 @@ function attempt(parser2) {
2344
2408
  }
2345
2409
  const mark = saveCstMark(ctx);
2346
2410
  const log = ctx._triviaLog?.length;
2411
+ const rootLog = ctx._rootTriviaLog?.length;
2347
2412
  const result = parser2.parse(input, pos, ctx);
2348
2413
  if (result.ok) return result;
2349
2414
  rollbackCstCapture(ctx, mark);
2350
2415
  if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
2416
+ if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
2351
2417
  if (result.committed) return result;
2352
2418
  return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
2353
2419
  }
@@ -2788,18 +2854,37 @@ function createParseLineContext(input, pos) {
2788
2854
  }
2789
2855
  function parser(opts, root) {
2790
2856
  const clearTrivia = opts.trivia === null;
2857
+ const opaqueRootCapture = opts.rootCapture === "opaque";
2858
+ if (opaqueRootCapture && opts.trivia === void 0) {
2859
+ throw new TypeError("parser({ rootCapture: 'opaque' }) requires an explicit trivia scope.");
2860
+ }
2791
2861
  return {
2792
2862
  _tag: "grammar",
2793
- _meta: root._meta,
2863
+ _meta: {
2864
+ ...root._meta,
2865
+ // Trivia classification describes this grammar's own active trivia
2866
+ // scope. A directly nested grammar may have classified trivia of its
2867
+ // own, but that must not advertise classification for this wrapper.
2868
+ triviaKindLabels: void 0,
2869
+ rootTriviaClassified: void 0,
2870
+ ...opts.trivia?._meta?.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {},
2871
+ ...opts.trivia?._meta?.rootTriviaClassified ? { rootTriviaClassified: true } : {}
2872
+ },
2794
2873
  _def: {
2795
2874
  tag: "grammar",
2796
2875
  parser: root,
2797
2876
  triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
2798
2877
  clearTrivia,
2878
+ ...opaqueRootCapture ? { rootCapture: "opaque" } : {},
2799
2879
  ...opts.captureTrivia ? { captureTrivia: true } : {},
2800
2880
  trackLines: opts.trackLines ?? false
2801
2881
  },
2802
2882
  parse(input, pos, _ctx) {
2883
+ if (_ctx?._rootTriviaStrictScopes && opts.trivia !== void 0 && opts.trivia !== null && !opts.trivia._meta.rootTriviaClassified && !opaqueRootCapture) {
2884
+ throw new TypeError(
2885
+ "parser(): selected root trivia requires classifiedTrivia() for every local trivia scope, or rootCapture: 'opaque'."
2886
+ );
2887
+ }
2803
2888
  const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
2804
2889
  const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
2805
2890
  const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
@@ -2822,7 +2907,11 @@ function parser(opts, root) {
2822
2907
  // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
2823
2908
  // trivia is inherited rather than re-declared here. No labels → undefined
2824
2909
  // (capture all).
2825
- ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
2910
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {},
2911
+ // This is a property of the local trivia scope, not a post-parse filter:
2912
+ // nested recognition can still skip its trivia, but selected root rows
2913
+ // must never be written for an explicitly opaque region.
2914
+ ...opaqueRootCapture ? { _rootTriviaCapture: false } : {}
2826
2915
  };
2827
2916
  const result = root.parse(input, pos ?? 0, ctx);
2828
2917
  if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
@@ -3505,6 +3594,25 @@ function trivia(combinator) {
3505
3594
  parse: combinator.parse.bind(combinator)
3506
3595
  };
3507
3596
  }
3597
+ function classifiedTrivia(arms) {
3598
+ const entries = Object.entries(arms);
3599
+ if (entries.length === 0) {
3600
+ throw new TypeError("classifiedTrivia() requires at least one named trivia arm.");
3601
+ }
3602
+ for (let i = 0; i < entries.length; i++) {
3603
+ const [name, arm] = entries[i];
3604
+ const first = arm._meta.firstSet;
3605
+ if (matchesEmpty(arm) || first.kind !== "ranges" || first.ranges.length === 0) {
3606
+ throw new TypeError(
3607
+ `classifiedTrivia(): ${JSON.stringify(name)} must be non-nullable with a concrete finite first set.`
3608
+ );
3609
+ }
3610
+ }
3611
+ const labeledArms = entries.map(([name, arm]) => label(name, arm));
3612
+ const result = trivia(oneOrMore(choice(...labeledArms)));
3613
+ result._meta.rootTriviaClassified = true;
3614
+ return result;
3615
+ }
3508
3616
  function label(name, combinator) {
3509
3617
  return {
3510
3618
  _tag: combinator._tag,
@@ -3551,6 +3659,7 @@ function token(root) {
3551
3659
  const savedRaw = ctx._cstRawChildren;
3552
3660
  const savedTriviaLog = ctx._cstTriviaLog;
3553
3661
  const savedOuterTriviaLog = ctx._triviaLog;
3662
+ const savedRootTriviaLog = ctx._rootTriviaLog;
3554
3663
  const wasCapturing = cstCaptureActive(ctx);
3555
3664
  ctx.trivia = void 0;
3556
3665
  ctx.triviaKindLabels = void 0;
@@ -3560,6 +3669,7 @@ function token(root) {
3560
3669
  ctx._cstRawChildren = void 0;
3561
3670
  ctx._cstTriviaLog = void 0;
3562
3671
  delete ctx._triviaLog;
3672
+ delete ctx._rootTriviaLog;
3563
3673
  let result;
3564
3674
  try {
3565
3675
  result = root.parse(input, pos, ctx);
@@ -3573,6 +3683,8 @@ function token(root) {
3573
3683
  ctx._cstTriviaLog = savedTriviaLog;
3574
3684
  if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
3575
3685
  else ctx._triviaLog = savedOuterTriviaLog;
3686
+ if (savedRootTriviaLog === void 0) delete ctx._rootTriviaLog;
3687
+ else ctx._rootTriviaLog = savedRootTriviaLog;
3576
3688
  }
3577
3689
  if (!result.ok) return result;
3578
3690
  const span = result.span;
@@ -3627,7 +3739,7 @@ function leaf(root, fn) {
3627
3739
  }
3628
3740
 
3629
3741
  // src/version.ts
3630
- var PARSEMAN_VERSION = "0.43.0";
3742
+ var PARSEMAN_VERSION = "0.44.0";
3631
3743
 
3632
3744
  // src/cst/host-mode.ts
3633
3745
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -5525,9 +5637,196 @@ function buildGrammarPlan(entry, winners) {
5525
5637
  return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, dispatches, attempts, labels, rules: rules2 };
5526
5638
  }
5527
5639
 
5640
+ // src/cst/trivia-entries.ts
5641
+ var EMPTY_INDICES = Object.freeze([]);
5642
+ var EMPTY_GAPS = Object.freeze([]);
5643
+ var SELECTED_ROOT_STRIDE = 5;
5644
+ function entryOffset(i, stride) {
5645
+ return i * stride;
5646
+ }
5647
+ function triviaEntries(log, labels, opts) {
5648
+ const baseStride = opts?.nodeLog ? 3 : 2;
5649
+ const stride = labels ? baseStride + 1 : baseStride;
5650
+ const length = Math.floor(log.length / stride);
5651
+ return {
5652
+ length,
5653
+ labels,
5654
+ stride,
5655
+ start(i) {
5656
+ return log[entryOffset(i, stride)];
5657
+ },
5658
+ end(i) {
5659
+ return log[entryOffset(i, stride) + 1];
5660
+ },
5661
+ insertIndex(i) {
5662
+ return opts?.nodeLog ? log[entryOffset(i, stride) + 2] : void 0;
5663
+ },
5664
+ kindIndex(i) {
5665
+ if (!labels) return void 0;
5666
+ return log[entryOffset(i, stride) + baseStride];
5667
+ },
5668
+ kind(i) {
5669
+ const ki = labels ? log[entryOffset(i, stride) + baseStride] : void 0;
5670
+ return ki !== void 0 ? labels[ki] : void 0;
5671
+ },
5672
+ text(i, input) {
5673
+ const s = log[entryOffset(i, stride)];
5674
+ const e = log[entryOffset(i, stride) + 1];
5675
+ return input.slice(s, e);
5676
+ }
5677
+ };
5678
+ }
5679
+ function buildRootTriviaIndex(log, labels) {
5680
+ const length = Math.floor(log.length / SELECTED_ROOT_STRIDE);
5681
+ const markerOffset = (i) => i * SELECTED_ROOT_STRIDE;
5682
+ const ownedRangeStart = (i) => log[markerOffset(i)];
5683
+ const ownedRangeEnd = (i) => log[markerOffset(i) + 1];
5684
+ const markerStart = (i) => log[markerOffset(i) + 2];
5685
+ const markerEnd = (i) => log[markerOffset(i) + 3];
5686
+ const markerKind = (i) => log[markerOffset(i) + 4];
5687
+ const entries = {
5688
+ length,
5689
+ labels,
5690
+ stride: SELECTED_ROOT_STRIDE,
5691
+ start: markerStart,
5692
+ end: markerEnd,
5693
+ insertIndex: () => void 0,
5694
+ kindIndex: markerKind,
5695
+ kind(i) {
5696
+ return labels[markerKind(i)];
5697
+ },
5698
+ text(i, input) {
5699
+ return input.slice(markerStart(i), markerEnd(i));
5700
+ }
5701
+ };
5702
+ const lowerBound = (offset, by) => {
5703
+ let lo = 0;
5704
+ let hi = length;
5705
+ while (lo < hi) {
5706
+ const mid = lo + hi >>> 1;
5707
+ if (by(mid) < offset) lo = mid + 1;
5708
+ else hi = mid;
5709
+ }
5710
+ return lo;
5711
+ };
5712
+ const gapRowsAt = (offset, direction) => {
5713
+ const edge = direction === "before" ? ownedRangeEnd : ownedRangeStart;
5714
+ let first = lowerBound(offset, edge);
5715
+ if (first === length || edge(first) !== offset) return void 0;
5716
+ const start = ownedRangeStart(first);
5717
+ const end = ownedRangeEnd(first);
5718
+ while (first > 0 && ownedRangeStart(first - 1) === start && ownedRangeEnd(first - 1) === end) first--;
5719
+ let last = first + 1;
5720
+ while (last < length && ownedRangeStart(last) === start && ownedRangeEnd(last) === end) last++;
5721
+ return [first, last];
5722
+ };
5723
+ const indices = (first, last) => {
5724
+ const out = [];
5725
+ for (let i = first; i < last; i++) out.push(i);
5726
+ return out;
5727
+ };
5728
+ const makeGap = (first, last) => {
5729
+ const start = ownedRangeStart(first);
5730
+ const end = ownedRangeEnd(first);
5731
+ let entryIndices;
5732
+ return {
5733
+ start,
5734
+ end,
5735
+ get entryIndices() {
5736
+ return entryIndices ??= indices(first, last);
5737
+ },
5738
+ hasKind(kind) {
5739
+ for (let i = first; i < last; i++) {
5740
+ if (labels[markerKind(i)] === kind) return true;
5741
+ }
5742
+ return false;
5743
+ },
5744
+ text(input) {
5745
+ return input.slice(start, end);
5746
+ }
5747
+ };
5748
+ };
5749
+ let maps;
5750
+ const getMaps = () => {
5751
+ if (maps !== void 0) return maps;
5752
+ const before = /* @__PURE__ */ new Map();
5753
+ const after = /* @__PURE__ */ new Map();
5754
+ const beforeGaps = /* @__PURE__ */ new Map();
5755
+ const afterGaps = /* @__PURE__ */ new Map();
5756
+ const gaps = [];
5757
+ for (let first = 0; first < length; ) {
5758
+ let last = first + 1;
5759
+ while (last < length && ownedRangeStart(last) === ownedRangeStart(first) && ownedRangeEnd(last) === ownedRangeEnd(first)) last++;
5760
+ const gap = makeGap(first, last);
5761
+ gaps.push(gap);
5762
+ afterGaps.set(gap.start, gap);
5763
+ beforeGaps.set(gap.end, gap);
5764
+ after.set(gap.start, [...gap.entryIndices]);
5765
+ before.set(gap.end, [...gap.entryIndices]);
5766
+ first = last;
5767
+ }
5768
+ maps = { before, after, beforeGaps, afterGaps, gaps };
5769
+ return maps;
5770
+ };
5771
+ return {
5772
+ entries,
5773
+ labels,
5774
+ get before() {
5775
+ return getMaps().before;
5776
+ },
5777
+ get after() {
5778
+ return getMaps().after;
5779
+ },
5780
+ entryIndicesBefore(offset) {
5781
+ const rows = gapRowsAt(offset, "before");
5782
+ return rows ? indices(rows[0], rows[1]) : EMPTY_INDICES;
5783
+ },
5784
+ entryIndicesAfter(offset) {
5785
+ const rows = gapRowsAt(offset, "after");
5786
+ return rows ? indices(rows[0], rows[1]) : EMPTY_INDICES;
5787
+ },
5788
+ gapBefore(offset) {
5789
+ const rows = gapRowsAt(offset, "before");
5790
+ return rows ? makeGap(rows[0], rows[1]) : void 0;
5791
+ },
5792
+ gapAfter(offset) {
5793
+ const rows = gapRowsAt(offset, "after");
5794
+ return rows ? makeGap(rows[0], rows[1]) : void 0;
5795
+ },
5796
+ gaps() {
5797
+ return getMaps().gaps;
5798
+ },
5799
+ gapsWithKind(kind) {
5800
+ const kinds = typeof kind === "string" ? [kind] : kind;
5801
+ const matches2 = [];
5802
+ for (let first = 0; first < length; ) {
5803
+ let last = first + 1;
5804
+ while (last < length && ownedRangeStart(last) === ownedRangeStart(first) && ownedRangeEnd(last) === ownedRangeEnd(first)) last++;
5805
+ let matched = false;
5806
+ for (let i = first; i < last && !matched; i++) {
5807
+ const label2 = labels[markerKind(i)];
5808
+ for (const kind2 of kinds) {
5809
+ if (label2 === kind2) {
5810
+ matched = true;
5811
+ break;
5812
+ }
5813
+ }
5814
+ }
5815
+ if (matched) matches2.push(makeGap(first, last));
5816
+ first = last;
5817
+ }
5818
+ return matches2.length === 0 ? EMPTY_GAPS : matches2;
5819
+ }
5820
+ };
5821
+ }
5822
+
5528
5823
  // src/compiler/scannable-run.ts
5529
5824
  var BACKSLASH = 92;
5530
5825
  var META = new Set("()[]{}*+?|^$.".split(""));
5826
+ var SELECTED_ROOT_OWNED_RANGE_PLACEHOLDER = Array.from(
5827
+ { length: SELECTED_ROOT_STRIDE - 3 },
5828
+ () => "0"
5829
+ ).join(", ");
5531
5830
  function classToRanges(cls) {
5532
5831
  if (cls === "\\d") return shorthandRanges("d");
5533
5832
  if (cls === "\\w") return shorthandRanges("w");
@@ -6566,13 +6865,15 @@ function scanBranch(shape, mint) {
6566
6865
  const m = emitShapeMatch(shape, "_e", mint, " ", "c");
6567
6866
  return [...m.setup, ` if (${m.end} > _e) { _e = ${m.end}; continue }`].join("\n");
6568
6867
  }
6569
- function scanBranchLabeled(shape, kindIndex, mint) {
6868
+ function scanBranchLabeled(shape, kindIndex, label2, mint) {
6570
6869
  const m = emitShapeMatch(shape, "_e", mint, " ", "c");
6571
6870
  return [
6572
6871
  ...m.setup,
6573
6872
  ` if (${m.end} > _e) {`,
6574
6873
  ` if (_cap) {`,
6575
6874
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
6875
+ ` const _rk = _ctx._rootTriviaCapture === false || _ctx._rootTriviaLog === undefined ? -1 : (_ctx._rootTriviaKindIndex?.[${JSON.stringify(label2)}] ?? -1)`,
6876
+ ` if (_rk >= 0) _ctx._rootTriviaLog.push(${SELECTED_ROOT_OWNED_RANGE_PLACEHOLDER}, _e, ${m.end}, _rk)`,
6576
6877
  ` 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})`,
6577
6878
  ` }`,
6578
6879
  ` _e = ${m.end}`,
@@ -6632,6 +6933,11 @@ var CAP_RECORD = [
6632
6933
  ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
6633
6934
  ` }`
6634
6935
  ].join("\n");
6936
+ var SELECTED_ROOT_FINISH = [
6937
+ ` if (_cap && _ctx._rootTriviaCapture !== false && _ctx._rootTriviaLog !== undefined && _ctx._rootTriviaLog.length !== _rootMark) {`,
6938
+ ` for (let _ri = _rootMark; _ri < _ctx._rootTriviaLog.length; _ri += ${SELECTED_ROOT_STRIDE}) { _ctx._rootTriviaLog[_ri] = _pos; _ctx._rootTriviaLog[_ri + 1] = _e }`,
6939
+ ` }`
6940
+ ].join("\n");
6635
6941
  function composeFastLoop(shapes) {
6636
6942
  const mint = makeMint();
6637
6943
  return [
@@ -6660,7 +6966,7 @@ function analyzeLabeledScannableRun(trivia2) {
6660
6966
  const d = getCoreRegexDef(arm.parser);
6661
6967
  const shape = d ? scanShapeFromRegex(d.source, d.flags) : null;
6662
6968
  if (!shape) return null;
6663
- out.push({ shape, kindIndex: arm.kindIndex });
6969
+ out.push({ shape, kindIndex: arm.kindIndex, label: arm.label });
6664
6970
  }
6665
6971
  return out;
6666
6972
  }
@@ -6669,11 +6975,13 @@ function buildLabeledScannableTriviaFnDecl(fnName, arms) {
6669
6975
  return [
6670
6976
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
6671
6977
  ` let _e = _pos`,
6978
+ ` const _rootMark = _cap && _ctx._rootTriviaLog !== undefined ? _ctx._rootTriviaLog.length : 0`,
6672
6979
  ` while (_e < input.length) {`,
6673
6980
  ` const c = input.charCodeAt(_e)`,
6674
- ...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, mint)),
6981
+ ...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, a.label, mint)),
6675
6982
  ` break`,
6676
6983
  ` }`,
6984
+ SELECTED_ROOT_FINISH,
6677
6985
  ` return _e`,
6678
6986
  `}`
6679
6987
  ].join("\n");
@@ -6689,6 +6997,8 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
6689
6997
  ` const _ce = _e + _m${i}[0].length`,
6690
6998
  ` if (_cap) {`,
6691
6999
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
7000
+ ` const _rk${i} = _ctx._rootTriviaCapture === false || _ctx._rootTriviaLog === undefined ? -1 : (_ctx._rootTriviaKindIndex?.[${JSON.stringify(arm.label)}] ?? -1)`,
7001
+ ` if (_rk${i} >= 0) _ctx._rootTriviaLog.push(0, 0, _e, _ce, _rk${i})`,
6692
7002
  ` 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})`,
6693
7003
  ` }`,
6694
7004
  ` _e = _ce`,
@@ -6700,11 +7010,13 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
6700
7010
  return [
6701
7011
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
6702
7012
  ` let _e = _pos`,
7013
+ ` const _rootMark = _cap && _ctx._rootTriviaLog !== undefined ? _ctx._rootTriviaLog.length : 0`,
6703
7014
  ` while (_e < input.length) {`,
6704
7015
  ` let _matched = false`,
6705
7016
  tryArms,
6706
7017
  ` if (!_matched) break`,
6707
7018
  ` }`,
7019
+ SELECTED_ROOT_FINISH,
6708
7020
  ` return _e`,
6709
7021
  `}`
6710
7022
  ].join("\n");
@@ -6727,6 +7039,8 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
6727
7039
  ` const _ce = _r${i}.span.end`,
6728
7040
  ` if (_cap) {`,
6729
7041
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
7042
+ ` const _rk${i} = _ctx._rootTriviaCapture === false || _ctx._rootTriviaLog === undefined ? -1 : (_ctx._rootTriviaKindIndex?.[${JSON.stringify(arm.label)}] ?? -1)`,
7043
+ ` if (_rk${i} >= 0) _ctx._rootTriviaLog.push(0, 0, _e, _ce, _rk${i})`,
6730
7044
  ` 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})`,
6731
7045
  ` }`,
6732
7046
  ` _e = _ce`,
@@ -6738,11 +7052,13 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
6738
7052
  return [
6739
7053
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
6740
7054
  ` let _e = _pos`,
7055
+ ` const _rootMark = _cap && _ctx._rootTriviaLog !== undefined ? _ctx._rootTriviaLog.length : 0`,
6741
7056
  ` while (_e < input.length) {`,
6742
7057
  ` let _matched = false`,
6743
7058
  tryArms,
6744
7059
  ` if (!_matched) break`,
6745
7060
  ` }`,
7061
+ SELECTED_ROOT_FINISH,
6746
7062
  ` return _e`,
6747
7063
  `}`
6748
7064
  ].join("\n");
@@ -6965,6 +7281,9 @@ var HOST_READS_DECL = "const _hostReads = (b, n) => { if (b === undefined) retur
6965
7281
  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 }";
6966
7282
  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] } }";
6967
7283
  var _loweringSink = null;
7284
+ function hasSelectedRootTrivia(ctx) {
7285
+ return ctx.rootTriviaClassified === true;
7286
+ }
6968
7287
  function v(ctx, prefix = "_v") {
6969
7288
  return `${prefix}${ctx.vars++}`;
6970
7289
  }
@@ -7274,10 +7593,12 @@ function planDisjointDispatch(parsers) {
7274
7593
  }
7275
7594
  return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
7276
7595
  }
7277
- function captureRestoreBody(mL, mR, mTl, mLg, mF = null) {
7596
+ function captureRestoreBody(ctx, mL, mR, mTl, mLg, mF = null, mRootLg = null) {
7278
7597
  const fieldRestore = mF ? `; if (_ctx._fields && _ctx._fields.length !== ${mF}) _ctx._fields.length = ${mF}` : "";
7279
7598
  const base = `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${mL}) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${mR}) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${mTl}) _ctx._cstTriviaLog.length = ${mTl}${fieldRestore}`;
7280
- return mLg ? `${base}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${mLg}) _ctx._triviaLog.length = ${mLg}` : base;
7599
+ const logRestore = mLg ? `; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${mLg}) _ctx._triviaLog.length = ${mLg}` : "";
7600
+ const rootLogRestore = hasSelectedRootTrivia(ctx) && mRootLg ? `; if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${mRootLg}) _ctx._rootTriviaLog.length = ${mRootLg}` : "";
7601
+ return `${base}${logRestore}${rootLogRestore}`;
7281
7602
  }
7282
7603
  function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
7283
7604
  if (seen.has(p)) return false;
@@ -7487,7 +7808,7 @@ function emitFallible(inner, ctx, pos, swallow = false) {
7487
7808
  `${ind0} ${valV} = ${r.valueVar}; ${endV} = ${r.endVar}; ${okV} = true`,
7488
7809
  `${ind0}}`,
7489
7810
  ...mL ? [
7490
- `${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null, mF)} }`
7811
+ `${ind0}if (!${okV}) { ${captureRestoreBody(ctx, mL, mR, mTl, null, mF)} }`
7491
7812
  ] : []
7492
7813
  ];
7493
7814
  return { stmts, okVar: okV, valVar: valV, endVar: endV, mayCommit };
@@ -7684,11 +8005,13 @@ function emitSeqValues(def, ctx, pos) {
7684
8005
  const markV = v(ctx, "_mk");
7685
8006
  const markTl = v(ctx, "_mktl");
7686
8007
  const markLog = v(ctx, "_mklg");
8008
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
7687
8009
  const scanEndV = v(ctx, "_sne");
7688
8010
  stmts.push(
7689
8011
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
7690
8012
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
7691
8013
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8014
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
7692
8015
  `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`,
7693
8016
  ...emitLineTrack(ctx, curV, scanEndV)
7694
8017
  );
@@ -7697,17 +8020,19 @@ function emitSeqValues(def, ctx, pos) {
7697
8020
  const endAfterV = v(ctx, "_sea");
7698
8021
  stmts.push(
7699
8022
  `${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
7700
- `${ind(ctx)}if (${endAfterV} > ${scanEndV}) { ${curV} = ${endAfterV} } else { 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}; }`
8023
+ `${ind(ctx)}if (${endAfterV} > ${scanEndV}) { ${curV} = ${endAfterV} } else { 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};${markRootLog ? ` if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${markRootLog}) _ctx._rootTriviaLog.length = ${markRootLog};` : ""} }`
7701
8024
  );
7702
8025
  valueVars.push(r2.valueVar);
7703
8026
  continue;
7704
8027
  } else {
7705
8028
  const trivFn = ensureTriviaFn(ctx);
7706
8029
  const markLog = v(ctx, "_mklg");
8030
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
7707
8031
  const scanEndV = v(ctx, "_sne");
7708
- const capArg = ctx.noHoist ? "0" : "_ctx._triviaLog !== undefined ? 2 : 0";
8032
+ const capArg = ctx.noHoist ? "0" : hasSelectedRootTrivia(ctx) ? "(_ctx._triviaLog !== undefined || _ctx._rootTriviaLog !== undefined) ? 2 : 0" : "_ctx._triviaLog !== undefined ? 2 : 0";
7709
8033
  stmts.push(
7710
8034
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8035
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
7711
8036
  `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`,
7712
8037
  ...emitLineTrack(ctx, curV, scanEndV)
7713
8038
  );
@@ -7716,7 +8041,7 @@ function emitSeqValues(def, ctx, pos) {
7716
8041
  const endAfterV = v(ctx, "_sea");
7717
8042
  stmts.push(
7718
8043
  `${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
7719
- `${ind(ctx)}if (${endAfterV} > ${scanEndV}) ${curV} = ${endAfterV}; else if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}`
8044
+ `${ind(ctx)}if (${endAfterV} > ${scanEndV}) ${curV} = ${endAfterV}; else { if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog};${markRootLog ? ` if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${markRootLog}) _ctx._rootTriviaLog.length = ${markRootLog};` : ""} }`
7720
8045
  );
7721
8046
  valueVars.push(r2.valueVar);
7722
8047
  continue;
@@ -7958,6 +8283,7 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7958
8283
  const selectorRawMark = selectorNeedsRollback ? v(ctx, "_dsr") : null;
7959
8284
  const selectorTriviaMark = selectorNeedsRollback ? v(ctx, "_dst") : null;
7960
8285
  const selectorLogMark = selectorNeedsRollback ? v(ctx, "_dsg") : null;
8286
+ const selectorRootLogMark = selectorNeedsRollback && hasSelectedRootTrivia(ctx) ? v(ctx, "_dsrg") : null;
7961
8287
  const selectorFieldMark = selectorNeedsRollback ? v(ctx, "_dsf") : null;
7962
8288
  const selectorErrorMark = selectorNeedsRollback ? v(ctx, "_dse") : null;
7963
8289
  const selector = emit(def.selector, ctx, pos);
@@ -7970,6 +8296,7 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7970
8296
  `${ind(ctx)}const ${selectorRawMark} = _ctx._cstRawChildren?.length ?? 0`,
7971
8297
  `${ind(ctx)}const ${selectorTriviaMark} = _ctx._cstTriviaLog?.length ?? 0`,
7972
8298
  `${ind(ctx)}const ${selectorLogMark} = _ctx._triviaLog?.length ?? 0`,
8299
+ ...selectorRootLogMark ? [`${ind(ctx)}const ${selectorRootLogMark} = _ctx._rootTriviaLog?.length ?? 0`] : [],
7973
8300
  `${ind(ctx)}const ${selectorFieldMark} = _ctx._fields?.length ?? 0`,
7974
8301
  `${ind(ctx)}const ${selectorErrorMark} = _ctx._errors?.length ?? 0`
7975
8302
  ] : [],
@@ -7989,16 +8316,18 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7989
8316
  const mayError = mayRecordRecoveryError(parser3, !!ctx.recovery);
7990
8317
  const errMark = mayError ? v(ctx, "_derr") : null;
7991
8318
  const logMark = ctx.activeTrivia ? v(ctx, "_dlog") : null;
8319
+ const rootLogMark = ctx.activeTrivia && hasSelectedRootTrivia(ctx) ? v(ctx, "_drlog") : null;
7992
8320
  if (usesRouted) {
7993
8321
  stmts.push(
7994
8322
  ...routedV ? [`${ind(ctx)}const ${routedV} = _ctx._routed; _ctx._routed = { value: ${selector.valueVar}, span: { start: ${pos}, end: ${selector.endVar} } }`] : [],
7995
8323
  ...selectorLeafMark ? [
7996
- `${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}`
8324
+ `${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};${selectorRootLogMark ? ` if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${selectorRootLogMark}) _ctx._rootTriviaLog.length = ${selectorRootLogMark};` : ""} if (_ctx._fields && _ctx._fields.length !== ${selectorFieldMark}) _ctx._fields.length = ${selectorFieldMark}; if (_ctx._errors && _ctx._errors.length !== ${selectorErrorMark}) _ctx._errors.length = ${selectorErrorMark}`
7997
8325
  ] : []
7998
8326
  );
7999
8327
  }
8000
8328
  if (errMark) stmts.push(`${ind(ctx)}const ${errMark} = _ctx._errors?.length ?? 0`);
8001
8329
  if (logMark) stmts.push(`${ind(ctx)}const ${logMark} = _ctx._triviaLog?.length ?? 0`);
8330
+ if (rootLogMark) stmts.push(`${ind(ctx)}const ${rootLogMark} = _ctx._rootTriviaLog?.length ?? 0`);
8002
8331
  const savedRoutedLocal = ctx.routedLocal;
8003
8332
  const savedNoHoist = ctx.noHoist;
8004
8333
  if (usesRouted && !needsRoutedContext) {
@@ -8013,11 +8342,12 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
8013
8342
  stmts.push(...coverageAttempt(ctx, coverageId, pos));
8014
8343
  const errorRollback = errMark ? `if (_ctx._errors && _ctx._errors.length !== ${errMark}) _ctx._errors.length = ${errMark}; ` : "";
8015
8344
  const logRollback = logMark ? `if (_ctx._triviaLog && _ctx._triviaLog.length !== ${logMark}) _ctx._triviaLog.length = ${logMark}; ` : "";
8345
+ const rootLogRollback = rootLogMark ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${rootLogMark}) _ctx._rootTriviaLog.length = ${rootLogMark}; ` : "";
8016
8346
  const routedRollback = routedV ? `_ctx._routed = ${routedV}; ` : "";
8017
8347
  const coverageFailure = coverageId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageId)}, phase: 'failure', offset: _ctx._fe }); `;
8018
8348
  stmts.push(
8019
8349
  ...tail.stmts,
8020
- `${ind(ctx)}if (!${tail.okVar}) { ${routedRollback}${errorRollback}${logRollback}${coverageFailure}${committedFailBody(ctx)} }`,
8350
+ `${ind(ctx)}if (!${tail.okVar}) { ${routedRollback}${errorRollback}${logRollback}${rootLogRollback}${coverageFailure}${committedFailBody(ctx)} }`,
8021
8351
  ...routedV ? [`${ind(ctx)}_ctx._routed = ${routedV}`] : [],
8022
8352
  `${ind(ctx)}${outV} = ${valueMode === "tail" ? tail.valVar : `[${selector.valueVar}, ${tail.valVar}]`}`,
8023
8353
  `${ind(ctx)}${outE} = ${tail.endVar}`,
@@ -8198,11 +8528,13 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
8198
8528
  const markRaw = armNeedsRollback ? v(ctx, "_cmr") : null;
8199
8529
  const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
8200
8530
  const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
8531
+ const markRootLog = ctx.activeTrivia && hasSelectedRootTrivia(ctx) ? v(ctx, "_cmlrg") : null;
8201
8532
  const markFields = armNeedsFieldRollback ? v(ctx, "_cmf") : null;
8202
8533
  const markErrors = armMayRecordError ? v(ctx, "_cme") : null;
8203
- const captureRollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog, markFields) : "";
8534
+ const captureRollback = markLeaves ? captureRestoreBody(ctx, markLeaves, markRaw, markTl, markLog, markFields, markRootLog) : "";
8535
+ const rootRollback = markRootLog && !markLeaves ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${markRootLog}) _ctx._rootTriviaLog.length = ${markRootLog}` : "";
8204
8536
  const errorRollback = markErrors ? `if (_ctx._errors && _ctx._errors.length !== ${markErrors}) _ctx._errors.length = ${markErrors}` : "";
8205
- const rollback = [captureRollback, errorRollback].filter(Boolean).join("; ");
8537
+ const rollback = [captureRollback, rootRollback, errorRollback].filter(Boolean).join("; ");
8206
8538
  const failSlot = atStart ? staticFx : "_ctx._fx";
8207
8539
  stmts.push(`${ind0}if (${skipCond}) {`);
8208
8540
  if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
@@ -8217,6 +8549,7 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
8217
8549
  ...markFields ? [`${ind(ctx)}const ${markFields} = _ctx._fields?.length ?? 0`] : []
8218
8550
  );
8219
8551
  }
8552
+ if (markRootLog) stmts.push(`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog?.length ?? 0`);
8220
8553
  if (markErrors) stmts.push(`${ind(ctx)}const ${markErrors} = _ctx._errors?.length ?? 0`);
8221
8554
  const arm = emitFallible(p, ctx, pos, atStart);
8222
8555
  const { stmts: armStmts, okVar, valVar, endVar } = arm;
@@ -8404,21 +8737,29 @@ function emitMany(def, ctx, pos) {
8404
8737
  const markV = v(ctx, "_mk");
8405
8738
  const markTl = v(ctx, "_mktl");
8406
8739
  const markLog = v(ctx, "_mklg");
8740
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8407
8741
  const npV = v(ctx, "_np");
8408
8742
  stmts.push(
8409
8743
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8410
8744
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8411
8745
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8746
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8412
8747
  `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8413
8748
  ...emitLineTrack(ctx, curV, npV)
8414
8749
  );
8415
8750
  itemPos = npV;
8416
- 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}; `;
8751
+ 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};${markRootLog ? ` if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${markRootLog}) _ctx._rootTriviaLog.length = ${markRootLog};` : ""} `;
8417
8752
  } else {
8418
8753
  const trivFn = ensureTriviaFn(ctx);
8754
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8419
8755
  const npV = v(ctx, "_np");
8420
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, npV));
8756
+ stmts.push(
8757
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8758
+ `${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx, ${hasSelectedRootTrivia(ctx) ? "_ctx._rootTriviaLog !== undefined ? 2 : 0" : "0"})`,
8759
+ ...emitLineTrack(ctx, curV, npV)
8760
+ );
8421
8761
  itemPos = npV;
8762
+ rollback = markRootLog ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${markRootLog}) _ctx._rootTriviaLog.length = ${markRootLog}; ` : "";
8422
8763
  }
8423
8764
  }
8424
8765
  if (!ctx.recovery && !ctx.coverage && !failsAtStart(def.parser) && needsFirstSetGuard(def.parser)) {
@@ -8478,9 +8819,10 @@ function emitAttempt(p, def, ctx, pos) {
8478
8819
  const raw = v(ctx, "_atr");
8479
8820
  const trivia2 = v(ctx, "_att");
8480
8821
  const log = v(ctx, "_atg");
8822
+ const rootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_atrg") : null;
8481
8823
  const fields = v(ctx, "_atf");
8482
8824
  const errors = v(ctx, "_ate");
8483
- const rollback = `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${leaves}) _ctx._cstLeaves.length = ${leaves}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${raw}) _ctx._cstRawChildren.length = ${raw}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${trivia2}) _ctx._cstTriviaLog.length = ${trivia2}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${log}) _ctx._triviaLog.length = ${log}; if (_ctx._fields && _ctx._fields.length !== ${fields}) _ctx._fields.length = ${fields}; if (_ctx._errors && _ctx._errors.length !== ${errors}) _ctx._errors.length = ${errors}`;
8825
+ const rollback = `if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${leaves}) _ctx._cstLeaves.length = ${leaves}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${raw}) _ctx._cstRawChildren.length = ${raw}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${trivia2}) _ctx._cstTriviaLog.length = ${trivia2}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${log}) _ctx._triviaLog.length = ${log};${rootLog ? ` if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${rootLog}) _ctx._rootTriviaLog.length = ${rootLog};` : ""} if (_ctx._fields && _ctx._fields.length !== ${fields}) _ctx._fields.length = ${fields}; if (_ctx._errors && _ctx._errors.length !== ${errors}) _ctx._errors.length = ${errors}`;
8484
8826
  const traceId = ctx.coverage?.plan.attempts.get(p);
8485
8827
  const traceRollback = traceId === void 0 ? "" : ` _ctx._grammarTrace?.write({ id: ${JSON.stringify(traceId)}, phase: 'rollback', offset: ${pos} });`;
8486
8828
  const preGuard = [];
@@ -8496,7 +8838,7 @@ function emitAttempt(p, def, ctx, pos) {
8496
8838
  return {
8497
8839
  stmts: [
8498
8840
  ...preGuard,
8499
- `${ind(ctx)}const ${leaves} = _ctx._cstLeaves?.length ?? 0, ${raw} = _ctx._cstRawChildren?.length ?? 0, ${trivia2} = _ctx._cstTriviaLog?.length ?? 0, ${log} = _ctx._triviaLog?.length ?? 0, ${fields} = _ctx._fields?.length ?? 0, ${errors} = _ctx._errors?.length ?? 0`,
8841
+ `${ind(ctx)}const ${leaves} = _ctx._cstLeaves?.length ?? 0, ${raw} = _ctx._cstRawChildren?.length ?? 0, ${trivia2} = _ctx._cstTriviaLog?.length ?? 0, ${log} = _ctx._triviaLog?.length ?? 0${rootLog ? `, ${rootLog} = _ctx._rootTriviaLog?.length ?? 0` : ""}, ${fields} = _ctx._fields?.length ?? 0, ${errors} = _ctx._errors?.length ?? 0`,
8500
8842
  ...inner.stmts,
8501
8843
  ...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback}${inner.mayCommit ? ` if (_ctx._fc) ${committedFailBody(ctx)};` : ""} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
8502
8844
  ],
@@ -8538,16 +8880,17 @@ function emitSepBy(_p, def, ctx, pos) {
8538
8880
  const itemFailBreak = (sepEndVar, sepRb, postSepRb) => def.trailing === void 0 ? `${sepRb}break` : `${postSepRb}${curV} = ${sepEndVar}; break`;
8539
8881
  const postSepMarks = () => {
8540
8882
  if (def.trailing === void 0 || !ctx.capturing) return { decl: [], rb: "" };
8541
- const lv = v(ctx, "_tlv"), rw = v(ctx, "_trw"), tl = v(ctx, "_ttl"), lg = v(ctx, "_tlg"), fl = v(ctx, "_tlf");
8883
+ const lv = v(ctx, "_tlv"), rw = v(ctx, "_trw"), tl = v(ctx, "_ttl"), lg = v(ctx, "_tlg"), rlg = hasSelectedRootTrivia(ctx) ? v(ctx, "_trlg") : null, fl = v(ctx, "_tlf");
8542
8884
  return {
8543
8885
  decl: [
8544
8886
  `${ind(ctx)}const ${lv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8545
8887
  `${ind(ctx)}const ${rw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8546
8888
  `${ind(ctx)}const ${tl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8547
8889
  `${ind(ctx)}const ${lg} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8890
+ ...rlg ? [`${ind(ctx)}const ${rlg} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8548
8891
  `${ind(ctx)}const ${fl} = _ctx._fields ? _ctx._fields.length : 0`
8549
8892
  ],
8550
- 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}; `
8893
+ 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};${rlg ? ` if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${rlg}) _ctx._rootTriviaLog.length = ${rlg};` : ""} if (_ctx._fields && _ctx._fields.length !== ${fl}) _ctx._fields.length = ${fl}; `
8551
8894
  };
8552
8895
  };
8553
8896
  if (rec) {
@@ -8585,6 +8928,7 @@ function emitSepBy(_p, def, ctx, pos) {
8585
8928
  const markV = v(ctx, "_mk");
8586
8929
  const markTl = v(ctx, "_mktl");
8587
8930
  const markLog = v(ctx, "_mklg");
8931
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8588
8932
  const markLv = v(ctx, "_mklv");
8589
8933
  const markFld = v(ctx, "_mkf");
8590
8934
  const spV = v(ctx, "_sp");
@@ -8592,13 +8936,14 @@ function emitSepBy(_p, def, ctx, pos) {
8592
8936
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8593
8937
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8594
8938
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8939
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8595
8940
  `${ind(ctx)}const ${markLv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8596
8941
  `${ind(ctx)}const ${markFld} = _ctx._fields ? _ctx._fields.length : 0`,
8597
8942
  `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8598
8943
  ...emitLineTrack(ctx, curV, spV)
8599
8944
  );
8600
8945
  sepAtPos = spV;
8601
- 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}; `;
8946
+ 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};${markRootLog ? ` if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${markRootLog}) _ctx._rootTriviaLog.length = ${markRootLog};` : ""} if (_ctx._fields && _ctx._fields.length !== ${markFld}) _ctx._fields.length = ${markFld}; `;
8602
8947
  const sep = emitFallible(def.separator, ctx, sepAtPos, true);
8603
8948
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep;
8604
8949
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}${sep.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
@@ -8616,19 +8961,30 @@ function emitSepBy(_p, def, ctx, pos) {
8616
8961
  );
8617
8962
  } else {
8618
8963
  const trivFn = ensureTriviaFn(ctx);
8964
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8619
8965
  const spV = v(ctx, "_sp");
8620
- stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, spV));
8966
+ stmts.push(
8967
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8968
+ `${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx, ${hasSelectedRootTrivia(ctx) ? "_ctx._rootTriviaLog !== undefined ? 2 : 0" : "0"})`,
8969
+ ...emitLineTrack(ctx, curV, spV)
8970
+ );
8621
8971
  sepAtPos = spV;
8622
8972
  const sep = emitFallible(def.separator, ctx, sepAtPos, true);
8623
8973
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep;
8624
- stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
8974
+ const rollbackToSep = markRootLog ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${markRootLog}) _ctx._rootTriviaLog.length = ${markRootLog}; ` : "";
8975
+ stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}${sep.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
8976
+ const postRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_psrlg") : null;
8625
8977
  const npV = v(ctx, "_np");
8626
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`, ...emitLineTrack(ctx, sepEnd, npV));
8978
+ stmts.push(
8979
+ ...postRootLog ? [`${ind(ctx)}const ${postRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8980
+ `${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx, ${hasSelectedRootTrivia(ctx) ? "_ctx._rootTriviaLog !== undefined ? 2 : 0" : "0"})`,
8981
+ ...emitLineTrack(ctx, sepEnd, npV)
8982
+ );
8627
8983
  const next = emitFallible(def.parser, ctx, npV, true);
8628
8984
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8629
8985
  stmts.push(
8630
8986
  ...nextStmts,
8631
- ...failItem(nextOk, next.mayCommit, npV, itemFailBreak(sepEnd, "", "")),
8987
+ ...failItem(nextOk, next.mayCommit, npV, itemFailBreak(sepEnd, rollbackToSep, postRootLog ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${postRootLog}) _ctx._rootTriviaLog.length = ${postRootLog}; ` : "")),
8632
8988
  `${ind(ctx)}${arrV}.push(${nextVal})`,
8633
8989
  `${ind(ctx)}${curV} = ${nextEnd}`
8634
8990
  );
@@ -8723,6 +9079,7 @@ function emitNot(def, ctx, pos) {
8723
9079
  const raw = sinksLive ? v(ctx, "_ntr") : null;
8724
9080
  const tl = sinksLive ? v(ctx, "_ntt") : null;
8725
9081
  const log = sinksLive ? v(ctx, "_ntg") : null;
9082
+ const rootLog = sinksLive && hasSelectedRootTrivia(ctx) ? v(ctx, "_ntrg") : null;
8726
9083
  const fields = sinksLive ? v(ctx, "_ntf") : null;
8727
9084
  const errors = sinksLive ? v(ctx, "_nte") : null;
8728
9085
  const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
@@ -8730,11 +9087,11 @@ function emitNot(def, ctx, pos) {
8730
9087
  return {
8731
9088
  stmts: [
8732
9089
  ...sinksLive ? [
8733
- `${ind(ctx)}const ${leaves} = _ctx._cstLeaves?.length ?? 0, ${raw} = _ctx._cstRawChildren?.length ?? 0, ${tl} = _ctx._cstTriviaLog?.length ?? 0, ${log} = _ctx._triviaLog?.length ?? 0, ${fields} = _ctx._fields?.length ?? 0, ${errors} = _ctx._errors?.length ?? 0`
9090
+ `${ind(ctx)}const ${leaves} = _ctx._cstLeaves?.length ?? 0, ${raw} = _ctx._cstRawChildren?.length ?? 0, ${tl} = _ctx._cstTriviaLog?.length ?? 0, ${log} = _ctx._triviaLog?.length ?? 0${rootLog ? `, ${rootLog} = _ctx._rootTriviaLog?.length ?? 0` : ""}, ${fields} = _ctx._fields?.length ?? 0, ${errors} = _ctx._errors?.length ?? 0`
8734
9091
  ] : [],
8735
9092
  ...stmts,
8736
9093
  ...sinksLive ? [
8737
- `${ind(ctx)}if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${leaves}) _ctx._cstLeaves.length = ${leaves}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${raw}) _ctx._cstRawChildren.length = ${raw}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${tl}) _ctx._cstTriviaLog.length = ${tl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${log}) _ctx._triviaLog.length = ${log}; if (_ctx._fields && _ctx._fields.length !== ${fields}) _ctx._fields.length = ${fields}; if (_ctx._errors && _ctx._errors.length !== ${errors}) _ctx._errors.length = ${errors}`
9094
+ `${ind(ctx)}if (_ctx._cstLeaves && _ctx._cstLeaves.length !== ${leaves}) _ctx._cstLeaves.length = ${leaves}; if (_ctx._cstRawChildren && _ctx._cstRawChildren.length !== ${raw}) _ctx._cstRawChildren.length = ${raw}; if (_ctx._cstTriviaLog && _ctx._cstTriviaLog.length !== ${tl}) _ctx._cstTriviaLog.length = ${tl}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${log}) _ctx._triviaLog.length = ${log};${rootLog ? ` if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${rootLog}) _ctx._rootTriviaLog.length = ${rootLog};` : ""} if (_ctx._fields && _ctx._fields.length !== ${fields}) _ctx._fields.length = ${fields}; if (_ctx._errors && _ctx._errors.length !== ${errors}) _ctx._errors.length = ${errors}`
8738
9095
  ] : [],
8739
9096
  ...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
8740
9097
  ],
@@ -8870,16 +9227,17 @@ function emitToken(def, ctx, pos) {
8870
9227
  const sk = v(ctx, "_tokKinds");
8871
9228
  const stl = v(ctx, "_tokTl");
8872
9229
  const sol = v(ctx, "_tokLog");
9230
+ const srl = hasSelectedRootTrivia(ctx) ? v(ctx, "_tokRootLog") : null;
8873
9231
  const sb = v(ctx, "_tokBuf");
8874
9232
  const valV = v(ctx, "_tok");
8875
9233
  return {
8876
9234
  stmts: [
8877
- `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${sv} = _ctx.trivia, ${sk} = _ctx.triviaKindLabels, ${stl} = _ctx._cstTriviaLog, ${sol} = _ctx._triviaLog, ${sb} = _ctx._cstBuf`,
8878
- `${i}_ctx.trivia = undefined; _ctx.triviaKindLabels = undefined; _ctx._cstChildren = undefined; _ctx._cstLeaves = undefined; _ctx._cstRawChildren = undefined; _ctx._cstTriviaLog = undefined; _ctx._triviaLog = undefined; _ctx._cstBuf = undefined`,
9235
+ `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${sv} = _ctx.trivia, ${sk} = _ctx.triviaKindLabels, ${stl} = _ctx._cstTriviaLog, ${sol} = _ctx._triviaLog${srl ? `, ${srl} = _ctx._rootTriviaLog` : ""}, ${sb} = _ctx._cstBuf`,
9236
+ `${i}_ctx.trivia = undefined; _ctx.triviaKindLabels = undefined; _ctx._cstChildren = undefined; _ctx._cstLeaves = undefined; _ctx._cstRawChildren = undefined; _ctx._cstTriviaLog = undefined; _ctx._triviaLog = undefined;${srl ? " _ctx._rootTriviaLog = undefined;" : ""} _ctx._cstBuf = undefined`,
8879
9237
  `${i}try {`,
8880
9238
  ...reindentStmts(inner.stmts, ctx.indent + 1).map((stmt) => stmt.replace(/^(\s*)(?:const|let)\s+/, "$1var ")),
8881
9239
  `${i}} finally {`,
8882
- `${i} _ctx.trivia = ${sv}; _ctx.triviaKindLabels = ${sk}; _ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx._cstTriviaLog = ${stl}; _ctx._triviaLog = ${sol}; _ctx._cstBuf = ${sb}`,
9240
+ `${i} _ctx.trivia = ${sv}; _ctx.triviaKindLabels = ${sk}; _ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx._cstTriviaLog = ${stl}; _ctx._triviaLog = ${sol};${srl ? ` _ctx._rootTriviaLog = ${srl};` : ""} _ctx._cstBuf = ${sb}`,
8883
9241
  `${i}}`,
8884
9242
  ...emitIfFail(ctx, `!${inner.okVar}`, propagateFailBody(ctx)),
8885
9243
  `${i}const ${valV} = input.slice(${pos}, ${inner.endVar})`,
@@ -9358,6 +9716,8 @@ function emitDispatch(p, ctx, pos) {
9358
9716
  case "grammar": {
9359
9717
  const savedTrivia = ctx.activeTrivia;
9360
9718
  const savedKindLabels = ctx.triviaKindLabels;
9719
+ const opaqueRootCapture = hasSelectedRootTrivia(ctx) && def.rootCapture === "opaque";
9720
+ const strictScopeCheck = hasSelectedRootTrivia(ctx) && def.triviaParser !== void 0 && !def.triviaParser._meta.rootTriviaClassified && !opaqueRootCapture ? `${ind(ctx)}if (_ctx._rootTriviaStrictScopes) throw new TypeError(${JSON.stringify("parser(): selected root trivia requires classifiedTrivia() for every local trivia scope, or rootCapture: 'opaque'.")})` : void 0;
9361
9721
  if (def.clearTrivia) {
9362
9722
  ctx.activeTrivia = void 0;
9363
9723
  ctx.triviaKindLabels = void 0;
@@ -9368,13 +9728,38 @@ function emitDispatch(p, ctx, pos) {
9368
9728
  }
9369
9729
  }
9370
9730
  const savedCapture = def.captureTrivia ? v(ctx, "_gcap") : null;
9731
+ const savedRootCapture = opaqueRootCapture ? v(ctx, "_grtc") : null;
9732
+ const opaqueValue = opaqueRootCapture ? v(ctx, "_grtv") : null;
9733
+ const opaqueEnd = opaqueRootCapture ? v(ctx, "_grte") : null;
9734
+ if (opaqueRootCapture) ctx.indent++;
9371
9735
  const r = emit(def.parser, ctx, pos);
9736
+ if (opaqueRootCapture) ctx.indent--;
9372
9737
  ctx.activeTrivia = savedTrivia;
9373
9738
  ctx.triviaKindLabels = savedKindLabels;
9374
- if (!savedCapture) return r;
9739
+ if (!savedCapture && !opaqueRootCapture) return strictScopeCheck === void 0 ? r : {
9740
+ ...r,
9741
+ stmts: [strictScopeCheck, ...r.stmts]
9742
+ };
9743
+ if (opaqueRootCapture) {
9744
+ return {
9745
+ stmts: [
9746
+ ...strictScopeCheck === void 0 ? [] : [strictScopeCheck],
9747
+ `${ind(ctx)}let ${opaqueValue}, ${opaqueEnd}`,
9748
+ `${ind(ctx)}${savedCapture ? `const ${savedCapture} = _ctx.captureTrivia; ` : ""}const ${savedRootCapture} = _ctx._rootTriviaCapture; _ctx._rootTriviaCapture = false`,
9749
+ `${ind(ctx)}try {`,
9750
+ ...savedCapture ? [`${ind(ctx)} _ctx.captureTrivia = true`] : [],
9751
+ ...r.stmts,
9752
+ `${ind(ctx)} ${opaqueValue} = ${r.valueVar}; ${opaqueEnd} = ${r.endVar}`,
9753
+ `${ind(ctx)}} finally { ${savedCapture ? `_ctx.captureTrivia = ${savedCapture}; ` : ""}_ctx._rootTriviaCapture = ${savedRootCapture} }`
9754
+ ],
9755
+ valueVar: opaqueValue,
9756
+ endVar: opaqueEnd
9757
+ };
9758
+ }
9375
9759
  return {
9376
9760
  ...r,
9377
9761
  stmts: [
9762
+ ...strictScopeCheck === void 0 ? [] : [strictScopeCheck],
9378
9763
  `${ind(ctx)}const ${savedCapture} = _ctx.captureTrivia; _ctx.captureTrivia = true`,
9379
9764
  ...r.stmts,
9380
9765
  `${ind(ctx)}_ctx.captureTrivia = ${savedCapture}`
@@ -9859,6 +10244,7 @@ function compile(combinator, mapFnSources, opts) {
9859
10244
  runDuplicationDiagnostic(combinator, opts?.duplication);
9860
10245
  markUnusedValues(combinator);
9861
10246
  const grammarTrivia = combinator._meta.grammarTrivia;
10247
+ const rootTriviaClassified = grammarTrivia?._meta.rootTriviaClassified === true || combinator._meta.rootTriviaClassified === true;
9862
10248
  const grammarScanSkip = combinator._meta.grammarScanSkip;
9863
10249
  const grammarHostMode = combinator._meta.grammarHostMode;
9864
10250
  const grammarTrackLines = combinator._meta.grammarTrackLines;
@@ -9885,7 +10271,11 @@ function compile(combinator, mapFnSources, opts) {
9885
10271
  lineTracking: opts?.trackLines === true || grammarTrackLines === true || hasLineTrackingDef(combinator),
9886
10272
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
9887
10273
  lazyUsage: analyzeLazyUsage(combinator),
9888
- ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
10274
+ ...rootTriviaClassified ? { rootTriviaClassified: true } : {},
10275
+ ...grammarTrivia ? {
10276
+ activeTrivia: grammarTrivia,
10277
+ triviaKindLabels: grammarTrivia._meta.triviaKindLabels
10278
+ } : {},
9889
10279
  ...grammarScanSkip ? { activeScanSkip: grammarScanSkip } : {}
9890
10280
  };
9891
10281
  const r = emit(combinator, ctx, "_pos");
@@ -10033,10 +10423,11 @@ function assertRuleName(name) {
10033
10423
  );
10034
10424
  }
10035
10425
  }
10036
- function publicRuleWrapperSource(rule, fnSource, ambientTriviaKindLabels) {
10037
- const labels = rule._meta.triviaKindLabels ?? ambientTriviaKindLabels;
10038
- if (labels === void 0) return fnSource;
10039
- return `Object.assign(${fnSource}, { _meta: { triviaKindLabels: ${JSON.stringify(labels)} } })`;
10426
+ function publicRuleWrapperSource(rule, fnSource, ambientTriviaKindLabels, ambientRootTriviaClassified) {
10427
+ const labels = ambientTriviaKindLabels ?? rule._meta.triviaKindLabels;
10428
+ const classified = ambientRootTriviaClassified ?? rule._meta.rootTriviaClassified;
10429
+ if (labels === void 0 && classified === void 0) return fnSource;
10430
+ return `Object.assign(${fnSource}, { _meta: {${labels === void 0 ? "" : ` triviaKindLabels: ${JSON.stringify(labels)},`}${classified === void 0 ? "" : " rootTriviaClassified: true,"} } })`;
10040
10431
  }
10041
10432
  function compileLinkable(ruleMapArg, ns, opts) {
10042
10433
  if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
@@ -10044,6 +10435,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10044
10435
  if (opts?.duplication !== void 0) runDuplicationDiagnosticRules(ruleMapArg, opts.duplication);
10045
10436
  for (const [, rule] of ruleMapArg) markUnusedValues(rule);
10046
10437
  const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
10438
+ const rootTriviaClassified = grammarTrivia?._meta.rootTriviaClassified === true || ruleMapArg.some(([, rule]) => rule._meta.rootTriviaClassified === true);
10047
10439
  const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
10048
10440
  const grammarHostMode = opts?.hostMode ?? ruleMapArg.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
10049
10441
  const grammarTrackLines = opts?.trackLines === true || ruleMapArg.some(([, r]) => r._meta.grammarTrackLines === true);
@@ -10087,7 +10479,11 @@ function compileLinkable(ruleMapArg, ns, opts) {
10087
10479
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
10088
10480
  ns,
10089
10481
  deferFirstSetRefs: true,
10090
- ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
10482
+ ...rootTriviaClassified ? { rootTriviaClassified: true } : {},
10483
+ ...grammarTrivia ? {
10484
+ activeTrivia: grammarTrivia,
10485
+ triviaKindLabels: grammarTrivia._meta.triviaKindLabels
10486
+ } : {},
10091
10487
  ...grammarScanSkip ? { activeScanSkip: grammarScanSkip } : {}
10092
10488
  };
10093
10489
  const seen = /* @__PURE__ */ new Map();
@@ -10219,7 +10615,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10219
10615
  ...r.stmts,
10220
10616
  ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`,
10221
10617
  `}`
10222
- ].join("\n"), ctx.triviaKindLabels));
10618
+ ].join("\n"), ctx.triviaKindLabels, ctx.rootTriviaClassified));
10223
10619
  }
10224
10620
  const firstSets = /* @__PURE__ */ new Map();
10225
10621
  const firstSetRecipes = /* @__PURE__ */ new Map();
@@ -10568,6 +10964,7 @@ function evalRuleMapIR(ir) {
10568
10964
  "transform",
10569
10965
  "skip",
10570
10966
  "trivia",
10967
+ "classifiedTrivia",
10571
10968
  "label",
10572
10969
  "field",
10573
10970
  "expect",
@@ -10608,6 +11005,7 @@ function evalRuleMapIR(ir) {
10608
11005
  transform,
10609
11006
  skip,
10610
11007
  trivia,
11008
+ classifiedTrivia,
10611
11009
  label,
10612
11010
  field,
10613
11011
  expect,
@@ -10854,8 +11252,11 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10854
11252
  return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
10855
11253
  }
10856
11254
  case "grammar": {
10857
- const trivia2 = def.clearTrivia ? "null" : def.triviaParser ? kid(def.triviaParser) : "undefined";
10858
- return `parser({ trivia: ${trivia2}${def.captureTrivia ? ", captureTrivia: true" : ""}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
11255
+ const classified = def.triviaParser?._meta.rootTriviaClassified === true;
11256
+ const spec = classified && def.triviaParser ? analyzeLabeledTrivia(def.triviaParser) : null;
11257
+ if (classified && !spec) throw new Unserializable("classified trivia without labeled arms");
11258
+ const trivia2 = def.clearTrivia ? "null" : spec ? `classifiedTrivia({ ${spec.arms.map((arm) => `${JSON.stringify(arm.label)}: ${kid(arm.parser)}`).join(", ")} })` : def.triviaParser ? kid(def.triviaParser) : "undefined";
11259
+ return `parser({ trivia: ${trivia2}${def.captureTrivia ? ", captureTrivia: true" : ""}${def.rootCapture ? `, rootCapture: ${JSON.stringify(def.rootCapture)}` : ""}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
10859
11260
  }
10860
11261
  case "withCtx": {
10861
11262
  if (def.extraSrc === void 0) throw new Unserializable("withCtx without extraSrc");
@@ -11752,157 +12153,26 @@ function parseDoc(registry, rootRule, input, opts = {}) {
11752
12153
  return new ParseDocImpl(registry, rootRule, opts, { abs: null }, [fail], null, input);
11753
12154
  }
11754
12155
 
11755
- // src/cst/trivia-entries.ts
11756
- var EMPTY_INDICES = Object.freeze([]);
11757
- var EMPTY_GAPS = Object.freeze([]);
11758
- function entryOffset(i, stride) {
11759
- return i * stride;
11760
- }
11761
- function triviaEntries(log, labels, opts) {
11762
- const baseStride = opts?.nodeLog ? 3 : 2;
11763
- const stride = labels ? baseStride + 1 : baseStride;
11764
- const length = Math.floor(log.length / stride);
11765
- return {
11766
- length,
11767
- labels,
11768
- stride,
11769
- start(i) {
11770
- return log[entryOffset(i, stride)];
11771
- },
11772
- end(i) {
11773
- return log[entryOffset(i, stride) + 1];
11774
- },
11775
- insertIndex(i) {
11776
- return opts?.nodeLog ? log[entryOffset(i, stride) + 2] : void 0;
11777
- },
11778
- kindIndex(i) {
11779
- if (!labels) return void 0;
11780
- return log[entryOffset(i, stride) + baseStride];
11781
- },
11782
- kind(i) {
11783
- const ki = labels ? log[entryOffset(i, stride) + baseStride] : void 0;
11784
- return ki !== void 0 ? labels[ki] : void 0;
11785
- },
11786
- text(i, input) {
11787
- const s = log[entryOffset(i, stride)];
11788
- const e = log[entryOffset(i, stride) + 1];
11789
- return input.slice(s, e);
11790
- }
11791
- };
11792
- }
11793
- function appendEntryRange(map, key, first, last) {
11794
- let indices = map.get(key);
11795
- if (!indices) {
11796
- indices = [];
11797
- map.set(key, indices);
11798
- }
11799
- for (let i = first; i <= last; i++) indices.push(i);
11800
- }
11801
- function buildGap(entries, start, end, first, last) {
11802
- const entryIndices = [];
11803
- for (let i = first; i <= last; i++) entryIndices.push(i);
11804
- return {
11805
- start,
11806
- end,
11807
- entryIndices,
11808
- hasKind(kind) {
11809
- for (const i of entryIndices) {
11810
- if (entries.kind(i) === kind) return true;
11811
- }
11812
- return false;
11813
- },
11814
- text(input) {
11815
- return input.slice(start, end);
11816
- }
11817
- };
11818
- }
11819
- function buildRootMaps(entries) {
11820
- const before = /* @__PURE__ */ new Map();
11821
- const after = /* @__PURE__ */ new Map();
11822
- const beforeGaps = /* @__PURE__ */ new Map();
11823
- const afterGaps = /* @__PURE__ */ new Map();
11824
- const gaps = [];
11825
- if (entries.length === 0) return { before, after, beforeGaps, afterGaps, gaps };
11826
- const appendGap = (first2, last, start2, end2) => {
11827
- const gap = buildGap(entries, start2, end2, first2, last);
11828
- gaps.push(gap);
11829
- afterGaps.set(start2, gap);
11830
- beforeGaps.set(end2, gap);
11831
- appendEntryRange(after, start2, first2, last);
11832
- appendEntryRange(before, end2, first2, last);
11833
- };
11834
- let first = 0;
11835
- let start = entries.start(0);
11836
- let end = entries.end(0);
11837
- for (let i = 1; i < entries.length; i++) {
11838
- const nextStart = entries.start(i);
11839
- const nextEnd = entries.end(i);
11840
- if (nextStart === end) {
11841
- end = nextEnd;
11842
- continue;
11843
- }
11844
- appendGap(first, i - 1, start, end);
11845
- first = i;
11846
- start = nextStart;
11847
- end = nextEnd;
11848
- }
11849
- appendGap(first, entries.length - 1, start, end);
11850
- return { before, after, beforeGaps, afterGaps, gaps };
11851
- }
11852
- function buildRootTriviaIndex(log, labels) {
11853
- const entries = triviaEntries(log, labels);
11854
- let maps;
11855
- const getMaps = () => {
11856
- maps ??= buildRootMaps(entries);
11857
- return maps;
11858
- };
11859
- return {
11860
- entries,
11861
- labels,
11862
- get before() {
11863
- return getMaps().before;
11864
- },
11865
- get after() {
11866
- return getMaps().after;
11867
- },
11868
- entryIndicesBefore(offset) {
11869
- return getMaps().before.get(offset) ?? EMPTY_INDICES;
11870
- },
11871
- entryIndicesAfter(offset) {
11872
- return getMaps().after.get(offset) ?? EMPTY_INDICES;
11873
- },
11874
- gapBefore(offset) {
11875
- return getMaps().beforeGaps.get(offset);
11876
- },
11877
- gapAfter(offset) {
11878
- return getMaps().afterGaps.get(offset);
11879
- },
11880
- gaps() {
11881
- return getMaps().gaps;
11882
- },
11883
- gapsWithKind(kind) {
11884
- const kinds = typeof kind === "string" ? [kind] : kind;
11885
- const matches2 = [];
11886
- for (const gap of getMaps().gaps) {
11887
- for (const k of kinds) {
11888
- if (gap.hasKind(k)) {
11889
- matches2.push(gap);
11890
- break;
11891
- }
11892
- }
11893
- }
11894
- return matches2.length === 0 ? EMPTY_GAPS : matches2;
11895
- }
11896
- };
11897
- }
11898
-
11899
12156
  // src/functional/run.ts
11900
12157
  var invoke = (r, input, pos, ctx) => typeof r === "function" ? r(input, pos, ctx) : r.parse(input, pos, ctx);
11901
12158
  function triviaKindLabelsFromRunnable(r) {
11902
12159
  if (!r) return void 0;
11903
12160
  if (typeof r === "function") return r._meta?.triviaKindLabels;
11904
12161
  const grammarTrivia = r._meta.grammarTrivia;
11905
- return r._meta.triviaKindLabels ?? grammarTrivia?._meta.triviaKindLabels ?? (r._def.tag === "grammar" ? r._def.triviaParser?._meta.triviaKindLabels : void 0);
12162
+ return grammarTrivia?._meta.triviaKindLabels ?? r._meta.triviaKindLabels ?? (r._def.tag === "grammar" ? r._def.triviaParser?._meta.triviaKindLabels : void 0);
12163
+ }
12164
+ function rootTriviaClassifiedFromRunnable(r) {
12165
+ if (!r) return false;
12166
+ if (typeof r === "function") return r._meta?.rootTriviaClassified === true;
12167
+ const grammarTrivia = r._meta.grammarTrivia;
12168
+ return grammarTrivia?._meta.rootTriviaClassified === true || r._meta.rootTriviaClassified === true;
12169
+ }
12170
+ function makeSelectedRootLabelIndex(labels) {
12171
+ const index = /* @__PURE__ */ Object.create(null);
12172
+ for (let i = 0; i < labels.length; i++) {
12173
+ if (index[labels[i]] === void 0) index[labels[i]] = i;
12174
+ }
12175
+ return index;
11906
12176
  }
11907
12177
  function runOnce(entry, input, options, phase, profileState) {
11908
12178
  if (typeof entry !== "function" && typeof entry?.parse !== "function") {
@@ -11910,13 +12180,31 @@ function runOnce(entry, input, options, phase, profileState) {
11910
12180
  `run(): start production is ${entry === null ? "null" : typeof entry}, not a rule \u2014 the requested grammar rule does not exist (check the rule name).`
11911
12181
  );
11912
12182
  }
11913
- const triviaLog = [];
12183
+ const rootTriviaSelection = options.rootTrivia?.select;
12184
+ const captureRootTrivia = rootTriviaSelection !== void 0 && rootTriviaSelection.length > 0;
12185
+ const selectedRootLog = captureRootTrivia ? [] : void 0;
12186
+ const selectedRootLabelIndex = captureRootTrivia ? makeSelectedRootLabelIndex(rootTriviaSelection) : void 0;
11914
12187
  const errors = [];
11915
12188
  const profile = profileState ?? (phase === void 0 ? void 0 : { phase, nodes: 0, childSlots: 0, rawSlots: 0, triviaSlots: 0, fieldSlots: 0, hostCalls: 0 });
11916
12189
  const skipGlobalSinks = phase === "recognizer" || phase === "capture";
11917
12190
  const grammarTrivia = typeof entry !== "function" ? entry._meta.grammarTrivia : void 0;
11918
12191
  const grammarScanSkip = typeof entry !== "function" ? entry._meta.grammarScanSkip : void 0;
11919
12192
  const triviaKindLabels = triviaKindLabelsFromRunnable(entry) ?? triviaKindLabelsFromRunnable(options.trivia);
12193
+ if (captureRootTrivia && triviaKindLabels === void 0) {
12194
+ throw new TypeError("run(): rootTrivia.select requires labeled grammar trivia.");
12195
+ }
12196
+ if (captureRootTrivia) {
12197
+ for (const label2 of rootTriviaSelection) {
12198
+ if (!triviaKindLabels.includes(label2)) {
12199
+ throw new TypeError(`run(): rootTrivia.select contains unknown trivia label ${JSON.stringify(label2)}.`);
12200
+ }
12201
+ }
12202
+ }
12203
+ if (captureRootTrivia && !rootTriviaClassifiedFromRunnable(entry) && !rootTriviaClassifiedFromRunnable(options.trivia)) {
12204
+ throw new TypeError(
12205
+ "run(): rootTrivia.select requires classifiedTrivia() at the root."
12206
+ );
12207
+ }
11920
12208
  const stamped = entry;
11921
12209
  assertHostModeCompatible(
11922
12210
  typeof entry === "function" ? stamped[FUSED_HOST_MODE] ?? "ast" : entry._meta.grammarHostMode ?? "ast",
@@ -11925,7 +12213,15 @@ function runOnce(entry, input, options, phase, profileState) {
11925
12213
  );
11926
12214
  const ctx = {
11927
12215
  trackLines: false,
11928
- ...skipGlobalSinks ? { _pmProfile: profile } : { _triviaLog: triviaLog, _errors: errors, ...profile === void 0 ? {} : { _pmProfile: profile } },
12216
+ ...skipGlobalSinks ? { _pmProfile: profile } : {
12217
+ ...captureRootTrivia ? {
12218
+ _rootTriviaLog: selectedRootLog,
12219
+ _rootTriviaKindIndex: selectedRootLabelIndex,
12220
+ _rootTriviaStrictScopes: true
12221
+ } : {},
12222
+ _errors: errors,
12223
+ ...profile === void 0 ? {} : { _pmProfile: profile }
12224
+ },
11929
12225
  build: options.build,
11930
12226
  state: options.state,
11931
12227
  ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...triviaKindLabels ? { triviaKindLabels } : {} } : triviaKindLabels ? { triviaKindLabels } : {},
@@ -11950,9 +12246,13 @@ function runOnce(entry, input, options, phase, profileState) {
11950
12246
  span: r.span ?? { start: 0, end: 0 },
11951
12247
  expected: r.ok ? [] : r.expected ?? [],
11952
12248
  errors,
11953
- triviaLog,
11954
- ...triviaKindLabels === void 0 ? {} : { triviaKindLabels },
11955
- triviaMap: buildRootTriviaIndex(triviaLog, triviaKindLabels),
12249
+ ...selectedRootLog !== void 0 && selectedRootLog.length > 0 ? {
12250
+ rootTrivia: {
12251
+ rows: selectedRootLog,
12252
+ select: rootTriviaSelection,
12253
+ index: buildRootTriviaIndex(selectedRootLog, rootTriviaSelection)
12254
+ }
12255
+ } : {},
11956
12256
  unconsumedFrom
11957
12257
  };
11958
12258
  }
@@ -12638,6 +12938,7 @@ export {
12638
12938
  buildTriviaIndex,
12639
12939
  charClassMembers,
12640
12940
  choice,
12941
+ classifiedTrivia,
12641
12942
  collectLeafSlots,
12642
12943
  commentsIn,
12643
12944
  compile,