parseman 0.42.1 → 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 (68) 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/node.d.ts +21 -7
  8. package/dist/combinators/node.d.ts.map +1 -1
  9. package/dist/combinators/parser.d.ts +17 -2
  10. package/dist/combinators/parser.d.ts.map +1 -1
  11. package/dist/combinators/token.d.ts +3 -2
  12. package/dist/combinators/token.d.ts.map +1 -1
  13. package/dist/combinators/trivia-skip.d.ts +1 -0
  14. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  15. package/dist/compiler/codegen.d.ts +4 -0
  16. package/dist/compiler/codegen.d.ts.map +1 -1
  17. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  18. package/dist/compiler/linker.d.ts +6 -1
  19. package/dist/compiler/linker.d.ts.map +1 -1
  20. package/dist/compiler/scannable-run.d.ts +1 -1
  21. package/dist/compiler/scannable-run.d.ts.map +1 -1
  22. package/dist/compiler/trivia-fast-path.d.ts +4 -3
  23. package/dist/compiler/trivia-fast-path.d.ts.map +1 -1
  24. package/dist/cst/reflection.d.ts +24 -0
  25. package/dist/cst/reflection.d.ts.map +1 -0
  26. package/dist/cst/trivia-entries.d.ts +15 -9
  27. package/dist/cst/trivia-entries.d.ts.map +1 -1
  28. package/dist/cst/trivia-kinds.d.ts +15 -11
  29. package/dist/cst/trivia-kinds.d.ts.map +1 -1
  30. package/dist/cst/types.d.ts +2 -0
  31. package/dist/cst/types.d.ts.map +1 -1
  32. package/dist/cst/walk.d.ts +27 -62
  33. package/dist/cst/walk.d.ts.map +1 -1
  34. package/dist/functional/run.d.ts +22 -16
  35. package/dist/functional/run.d.ts.map +1 -1
  36. package/dist/index.cjs +1651 -1207
  37. package/dist/index.cjs.map +4 -4
  38. package/dist/index.d.ts +6 -5
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +1651 -1207
  41. package/dist/index.js.map +4 -4
  42. package/dist/language-service/index.cjs +186 -135
  43. package/dist/language-service/index.cjs.map +4 -4
  44. package/dist/language-service/index.d.ts +1 -1
  45. package/dist/language-service/index.d.ts.map +1 -1
  46. package/dist/language-service/index.js +186 -135
  47. package/dist/language-service/index.js.map +4 -4
  48. package/dist/plugin/evaluator.d.ts +1 -0
  49. package/dist/plugin/evaluator.d.ts.map +1 -1
  50. package/dist/plugin/index.cjs +1525 -1048
  51. package/dist/plugin/index.cjs.map +4 -4
  52. package/dist/plugin/index.d.ts.map +1 -1
  53. package/dist/plugin/index.js +1525 -1048
  54. package/dist/plugin/index.js.map +4 -4
  55. package/dist/run/index.cjs +152 -102
  56. package/dist/run/index.cjs.map +3 -3
  57. package/dist/run/index.d.ts +1 -1
  58. package/dist/run/index.d.ts.map +1 -1
  59. package/dist/run/index.js +152 -102
  60. package/dist/run/index.js.map +3 -3
  61. package/dist/spec/index.cjs +1381 -1152
  62. package/dist/spec/index.cjs.map +4 -4
  63. package/dist/spec/index.js +1381 -1152
  64. package/dist/spec/index.js.map +4 -4
  65. package/dist/types.d.ts +27 -1
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/version.d.ts +1 -1
  68. 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)
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);
1619
+ }
1715
1620
  };
1716
1621
  }
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;
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 } };
1634
+ }
1635
+ }
1636
+ return { ok: false, expected: [], span: { start: pos, end: pos } };
1726
1637
  }
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;
1638
+ };
1744
1639
  }
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);
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 };
1648
+ }
1649
+ function matchesAt(sentinel, input, pos, ctx) {
1650
+ return probeAt(sentinel, input, pos, ctx);
1651
+ }
1652
+ function captureError(ctx, error) {
1653
+ if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
1654
+ }
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;
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;
1703
+ }
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
+ };
1754
+ }
1755
+ };
1756
+ }
1757
+
1758
+ // src/combinators/expect.ts
1759
+ function deriveExpected(c) {
1760
+ return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
1761
+ }
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 [];
1833
+ }
1834
+ }
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
1841
+ };
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 } };
1854
+ }
1855
+ };
1856
+ }
1857
+ function isParseError(value) {
1858
+ return typeof value === "object" && value !== null && value._tag === "parseError";
1859
+ }
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]);
1889
+ }
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 } };
1770
1943
  }
1771
- };
1772
- }
1773
- const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1774
- return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1775
- }
1776
- const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
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);
1944
+ return superResult;
1797
1945
  }
1798
- };
1799
- }
1800
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1801
- return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1802
- }
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;
1808
- }
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;
1815
- }
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);
1828
- }
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;
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);
1838
1951
  }
1952
+ return { ok: false, expected, span: { start: pos, end: pos } };
1839
1953
  }
1840
- break;
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 } };
1841
1972
  }
1842
- return pos;
1843
1973
  };
1844
1974
  }
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);
1849
- }
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;
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);
1854
1981
  }
1855
- return false;
1856
- }
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;
1864
- };
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++;
1874
- }
1875
- return pos;
1876
- };
1877
- }
1878
- function classRunSource(source) {
1879
- const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1880
- return m ? classScanner(m[1]) : null;
1881
- }
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;
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 };
1887
1994
  }
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) };
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
  }
1893
- return null;
2001
+ const shared = detectSharedPrefix(parsers);
2002
+ if (shared !== null) return shared;
2003
+ return { tag: "firstMatch" };
1894
2004
  }
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
- };
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}`;
2009
+ return null;
1903
2010
  }
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++;
1914
- continue;
1915
- }
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;
1924
- }
1925
- break;
1926
- }
1927
- return pos;
1928
- };
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 };
1929
2029
  }
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++;
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;
1938
2035
  continue;
1939
2036
  }
1940
- if (inClass) {
1941
- if (c === "]") inClass = false;
1942
- continue;
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) });
2052
+ continue;
2053
+ }
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 });
2058
+ }
1943
2059
  }
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;
2060
+ return checks.length > 0 ? checks : null;
2061
+ });
2062
+ }
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);
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;
2086
+ }
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);
1964
2092
  }
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);
2093
+ return value;
2094
+ }
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);
1970
2105
  }
1971
- if (commentCodes.some((code) => inRanges2(code, ranges))) {
1972
- return loopScanner(arms.map(armScanner));
2106
+ if (contCodes.length === 0) return null;
2107
+ return codesToFirstSet(contCodes);
2108
+ }
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];
2119
+ }
1973
2120
  }
1974
- if (commentCodes.length === 0) return armScanner({ kind: "class", ranges });
1975
- return fusedTriviaScanner(ranges, commentCodes);
2121
+ ranges.push({ lo, hi });
2122
+ return { kind: "ranges", ranges };
1976
2123
  }
1977
- function blockCommentSource(source) {
1978
- return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
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;
1979
2129
  }
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;
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;
1984
2136
  }
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;
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;
2032
2145
  }
2033
- } finally {
2034
- ctx._sync = inheritedSync;
2035
2146
  }
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
2147
  }
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;
2076
- }
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
- }
2084
- };
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) {
@@ -2863,6 +2952,126 @@ function parse(combinator, input, opts = {}) {
2863
2952
  return lineIndex ? annotateResultLines(withErrors, normalizeLineIndex(lineIndex)) : withErrors;
2864
2953
  }
2865
2954
 
2955
+ // src/cst/reflection.ts
2956
+ var GRAMMAR_REFLECTION = /* @__PURE__ */ Symbol.for("parseman.grammarReflection");
2957
+ function childrenOf(def) {
2958
+ switch (def.tag) {
2959
+ case "sequence":
2960
+ case "choice":
2961
+ return def.parsers;
2962
+ case "dispatch":
2963
+ return [
2964
+ def.selector,
2965
+ ...def.cases.map((c) => c.parser),
2966
+ ...def.matchers ? def.matchers.map((c) => c.parser) : [],
2967
+ ...def.otherwise ? [def.otherwise] : []
2968
+ ];
2969
+ case "many":
2970
+ case "oneOrMore":
2971
+ case "optional":
2972
+ case "attempt":
2973
+ case "transform":
2974
+ case "trivia":
2975
+ case "token":
2976
+ case "leaf":
2977
+ case "label":
2978
+ case "field":
2979
+ case "not":
2980
+ case "peek":
2981
+ case "node":
2982
+ case "withCtx":
2983
+ case "recover":
2984
+ case "expect":
2985
+ return [def.parser];
2986
+ case "grammar":
2987
+ return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
2988
+ case "sepBy":
2989
+ return [def.parser, def.separator];
2990
+ case "skip":
2991
+ return [def.main, def.skipped];
2992
+ case "scanTo":
2993
+ return [def.sentinel, ...def.skip];
2994
+ case "literal":
2995
+ case "regex":
2996
+ case "keywords":
2997
+ case "routed":
2998
+ case "guard":
2999
+ case "lazy":
3000
+ case "unknown":
3001
+ return [];
3002
+ }
3003
+ }
3004
+ function addNode(out, def) {
3005
+ if (def.type === void 0) return;
3006
+ const existing = out.get(def.type);
3007
+ if (existing === void 0) {
3008
+ out.set(def.type, def.tags === void 0 ? [] : [...def.tags]);
3009
+ return;
3010
+ }
3011
+ if (def.tags === void 0) return;
3012
+ for (const tag of def.tags) {
3013
+ if (!existing.includes(tag)) existing.push(tag);
3014
+ }
3015
+ }
3016
+ function collectFromCombinator(parser2, out, seen, followLazy = true) {
3017
+ if (seen.has(parser2)) return;
3018
+ seen.add(parser2);
3019
+ const def = parser2._def;
3020
+ if (def.tag === "lazy") {
3021
+ if (followLazy) {
3022
+ try {
3023
+ collectFromCombinator(def.thunk(), out, seen, followLazy);
3024
+ } catch {
3025
+ }
3026
+ }
3027
+ return;
3028
+ }
3029
+ if (def.tag === "node") addNode(out, def);
3030
+ for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy);
3031
+ }
3032
+ function collectGrammarReflection(ruleMap, opts) {
3033
+ const byType = /* @__PURE__ */ new Map();
3034
+ const followLazy = opts?.followLazy !== false;
3035
+ for (const [, rule] of ruleMap) {
3036
+ if (!followLazy && rule._def.tag === "lazy") {
3037
+ try {
3038
+ const resolved = rule._def.thunk();
3039
+ if (resolved._def.tag !== "lazy") collectFromCombinator(resolved, byType, /* @__PURE__ */ new Set(), false);
3040
+ } catch {
3041
+ }
3042
+ continue;
3043
+ }
3044
+ collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
3045
+ }
3046
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
3047
+ }
3048
+ function mergeGrammarReflections(reflections) {
3049
+ const byType = /* @__PURE__ */ new Map();
3050
+ for (const reflection of reflections) {
3051
+ for (const node2 of reflection.nodes) {
3052
+ const tags = byType.get(node2.type);
3053
+ if (tags === void 0) {
3054
+ byType.set(node2.type, [...node2.tags]);
3055
+ continue;
3056
+ }
3057
+ for (const tag of node2.tags) {
3058
+ if (!tags.includes(tag)) tags.push(tag);
3059
+ }
3060
+ }
3061
+ }
3062
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
3063
+ }
3064
+ function attachGrammarReflection(grammar, reflection) {
3065
+ Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true });
3066
+ return grammar;
3067
+ }
3068
+ function grammarReflectionOf(grammar) {
3069
+ return grammar[GRAMMAR_REFLECTION];
3070
+ }
3071
+ function grammarReflectionSource(reflection) {
3072
+ return `{ nodes: ${JSON.stringify(reflection.nodes)} }`;
3073
+ }
3074
+
2866
3075
  // src/combinators/parser.ts
2867
3076
  var RULE_ORDER = "__parsemanRuleOrder";
2868
3077
  function tagRule(r, key) {
@@ -2961,6 +3170,7 @@ function rules(a, b) {
2961
3170
  enumerable: false,
2962
3171
  configurable: true
2963
3172
  });
3173
+ attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
2964
3174
  return cache;
2965
3175
  }
2966
3176
 
@@ -3281,13 +3491,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3281
3491
  const collapse = opts?.collapse === true;
3282
3492
  const captureTrivia = opts?.captureTrivia === true;
3283
3493
  const trailingTrivia = opts?.trailingTrivia === true;
3494
+ const tags = opts?.tags;
3284
3495
  if (unwrap && collapse) {
3285
3496
  throw new Error("node() options cannot set both unwrap and collapse");
3286
3497
  }
3287
3498
  if (project !== void 0 && (unwrap || collapse)) {
3288
3499
  throw new Error("node() options cannot combine project with unwrap or collapse");
3289
3500
  }
3290
- const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {} } : baseDef;
3501
+ const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia || tags !== void 0 && tags.length > 0 ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {}, ...tags !== void 0 && tags.length > 0 ? { tags } : {} } : baseDef;
3291
3502
  const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
3292
3503
  const clonesState = build ? buildReadsState(def) : project === void 0;
3293
3504
  const hasOwnFields = parserHasOwnFields(combinator);
@@ -3333,7 +3544,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3333
3544
  const cstOutput = cstOutputHost(ctx.build);
3334
3545
  const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3335
3546
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3336
- const built = unwrap && children2.length === 1 ? unwrapChild(children2[0]) : collapse && children2.length === 1 ? children2[0] : !build && project === void 0 && ctx.build?._parsemanCstCollapse && children2.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children2[0], children2, rawChildren) ? children2[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState) : projectChild(children2, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState) : build(children2, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
3547
+ const built = unwrap && children2.length === 1 ? unwrapChild(children2[0]) : collapse && children2.length === 1 ? children2[0] : !build && project === void 0 && ctx.build?._parsemanCstCollapse && children2.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children2[0], children2, rawChildren) ? children2[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState, tags) : projectChild(children2, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState, tags) : build(children2, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, st, tags) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
3337
3548
  const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span };
3338
3549
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3339
3550
  pushCstChild(ctx, built, rawEntry);
@@ -3383,6 +3594,25 @@ function trivia(combinator) {
3383
3594
  parse: combinator.parse.bind(combinator)
3384
3595
  };
3385
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
+ }
3386
3616
  function label(name, combinator) {
3387
3617
  return {
3388
3618
  _tag: combinator._tag,
@@ -3429,6 +3659,7 @@ function token(root) {
3429
3659
  const savedRaw = ctx._cstRawChildren;
3430
3660
  const savedTriviaLog = ctx._cstTriviaLog;
3431
3661
  const savedOuterTriviaLog = ctx._triviaLog;
3662
+ const savedRootTriviaLog = ctx._rootTriviaLog;
3432
3663
  const wasCapturing = cstCaptureActive(ctx);
3433
3664
  ctx.trivia = void 0;
3434
3665
  ctx.triviaKindLabels = void 0;
@@ -3438,6 +3669,7 @@ function token(root) {
3438
3669
  ctx._cstRawChildren = void 0;
3439
3670
  ctx._cstTriviaLog = void 0;
3440
3671
  delete ctx._triviaLog;
3672
+ delete ctx._rootTriviaLog;
3441
3673
  let result;
3442
3674
  try {
3443
3675
  result = root.parse(input, pos, ctx);
@@ -3451,6 +3683,8 @@ function token(root) {
3451
3683
  ctx._cstTriviaLog = savedTriviaLog;
3452
3684
  if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
3453
3685
  else ctx._triviaLog = savedOuterTriviaLog;
3686
+ if (savedRootTriviaLog === void 0) delete ctx._rootTriviaLog;
3687
+ else ctx._rootTriviaLog = savedRootTriviaLog;
3454
3688
  }
3455
3689
  if (!result.ok) return result;
3456
3690
  const span = result.span;
@@ -3505,7 +3739,7 @@ function leaf(root, fn) {
3505
3739
  }
3506
3740
 
3507
3741
  // src/version.ts
3508
- var PARSEMAN_VERSION = "0.42.1";
3742
+ var PARSEMAN_VERSION = "0.44.0";
3509
3743
 
3510
3744
  // src/cst/host-mode.ts
3511
3745
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -3571,7 +3805,7 @@ var SUGGESTIONS = {
3571
3805
  function classifyBroadArm(arm, resolve) {
3572
3806
  const seen = /* @__PURE__ */ new Set();
3573
3807
  const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
3574
- const walk2 = (p) => {
3808
+ const walk = (p) => {
3575
3809
  if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
3576
3810
  seen.add(p);
3577
3811
  const d = p._def;
@@ -3585,7 +3819,7 @@ function classifyBroadArm(arm, resolve) {
3585
3819
  // peek() CARRIES its body's first-set, so it is only broad when the body is
3586
3820
  // (or the body is nullable) — walk in for the real cause.
3587
3821
  case "peek":
3588
- return walk2(d.parser);
3822
+ return walk(d.parser);
3589
3823
  case "scanTo":
3590
3824
  return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
3591
3825
  // `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
@@ -3599,7 +3833,7 @@ function classifyBroadArm(arm, resolve) {
3599
3833
  case "attempt":
3600
3834
  case "withCtx":
3601
3835
  case "recover":
3602
- return walk2(d.parser);
3836
+ return walk(d.parser);
3603
3837
  case "lazy": {
3604
3838
  const name = p._ruleName;
3605
3839
  let target;
@@ -3611,13 +3845,13 @@ function classifyBroadArm(arm, resolve) {
3611
3845
  return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
3612
3846
  target = bound;
3613
3847
  }
3614
- const inner = walk2(target);
3848
+ const inner = walk(target);
3615
3849
  if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
3616
3850
  return inner;
3617
3851
  }
3618
3852
  case "optional":
3619
3853
  case "many": {
3620
- const inner = walk2(d.parser);
3854
+ const inner = walk(d.parser);
3621
3855
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3622
3856
  }
3623
3857
  case "oneOrMore":
@@ -3630,9 +3864,9 @@ function classifyBroadArm(arm, resolve) {
3630
3864
  case "node":
3631
3865
  case "grammar":
3632
3866
  case "expect":
3633
- return walk2(d.parser);
3867
+ return walk(d.parser);
3634
3868
  case "skip":
3635
- return walk2(d.main);
3869
+ return walk(d.main);
3636
3870
  case "sequence": {
3637
3871
  let sawNullablePrefix = false;
3638
3872
  for (const t of d.parsers) {
@@ -3641,7 +3875,7 @@ function classifyBroadArm(arm, resolve) {
3641
3875
  continue;
3642
3876
  }
3643
3877
  if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve))) {
3644
- const inner = walk2(t);
3878
+ const inner = walk(t);
3645
3879
  if (sawNullablePrefix)
3646
3880
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3647
3881
  return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
@@ -3656,18 +3890,18 @@ function classifyBroadArm(arm, resolve) {
3656
3890
  }
3657
3891
  case "choice": {
3658
3892
  for (const a of d.parsers) {
3659
- const r = walk2(a);
3893
+ const r = walk(a);
3660
3894
  if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
3661
3895
  }
3662
3896
  return null;
3663
3897
  }
3664
3898
  case "dispatch":
3665
- return walk2(d.selector);
3899
+ return walk(d.selector);
3666
3900
  default:
3667
3901
  return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
3668
3902
  }
3669
3903
  };
3670
- return walk2(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3904
+ return walk(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3671
3905
  }
3672
3906
  function peelToLeading(arm) {
3673
3907
  let d = arm._def;
@@ -3869,7 +4103,7 @@ function formatGatingWarnings(report) {
3869
4103
 
3870
4104
  // src/analysis/duplication.ts
3871
4105
  var siteToString = (s) => s.path === "" ? s.rule : `${s.rule} \u203A ${s.path}`;
3872
- function childrenOf(d) {
4106
+ function childrenOf2(d) {
3873
4107
  switch (d.tag) {
3874
4108
  case "sequence":
3875
4109
  case "choice":
@@ -4003,7 +4237,7 @@ function render(p, depth = 3) {
4003
4237
  break;
4004
4238
  }
4005
4239
  if (depth <= 0) return `${d.tag}(\u2026)`;
4006
- const kids = childrenOf(d).map((k) => render(k, depth - 1));
4240
+ const kids = childrenOf2(d).map((k) => render(k, depth - 1));
4007
4241
  switch (d.tag) {
4008
4242
  case "sequence":
4009
4243
  return `sequence(${kids.join(", ")})`;
@@ -4198,7 +4432,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4198
4432
  if (hit !== void 0) return hit;
4199
4433
  const d = p._def;
4200
4434
  shapeCache.set(p, interner.hole);
4201
- const kids = childrenOf(d);
4435
+ const kids = childrenOf2(d);
4202
4436
  const id = interner.of(`${payloadKey(p, d)}|${kids.map(shapeOf).join(",")}`);
4203
4437
  shapeCache.set(p, id);
4204
4438
  return id;
@@ -4208,7 +4442,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4208
4442
  if (hit !== void 0) return hit;
4209
4443
  sizeCache.set(p, 1);
4210
4444
  let n = 1;
4211
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4445
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4212
4446
  sizeCache.set(p, n);
4213
4447
  return n;
4214
4448
  };
@@ -4221,7 +4455,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4221
4455
  visited.set(p, { p, d, site: here, shape: shapeOf(p), size: sizeOf(p), enclosingNode, enclosingReducer });
4222
4456
  const inner = d.tag === "node" ? d.type ?? "(anonymous)" : enclosingNode;
4223
4457
  const innerReducer = enclosingReducer || d.tag === "node" && (d.build !== void 0 || d.buildSrc !== void 0) || d.tag === "transform" || d.tag === "leaf";
4224
- const kids = childrenOf(d);
4458
+ const kids = childrenOf2(d);
4225
4459
  kids.forEach((k, i) => {
4226
4460
  const seg = segment(d, i);
4227
4461
  visit(k, here.rule, here.path === "" ? seg : `${here.path} \u203A ${seg}`, inner, innerReducer);
@@ -4281,7 +4515,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4281
4515
  function markSubtree(p, into) {
4282
4516
  if (into.has(p)) return;
4283
4517
  into.add(p);
4284
- for (const k of childrenOf(p._def)) markSubtree(k, into);
4518
+ for (const k of childrenOf2(p._def)) markSubtree(k, into);
4285
4519
  }
4286
4520
  var stripMembers = (f) => {
4287
4521
  const { _members, ...rest } = f;
@@ -4321,7 +4555,7 @@ function findNearDuplicates(visited, interner, shapeOf, minSize) {
4321
4555
  if (hit !== void 0) return hit;
4322
4556
  holeCache.set(p, []);
4323
4557
  const d = p._def;
4324
- const kids = childrenOf(d);
4558
+ const kids = childrenOf2(d);
4325
4559
  const kidShapes = kids.map(shapeOf);
4326
4560
  const payload = payloadKey(p, d);
4327
4561
  const out2 = [];
@@ -4382,7 +4616,7 @@ function findDivergentSlot(members, shapeOf) {
4382
4616
  let cur = [...members];
4383
4617
  for (let guard2 = 0; guard2 < 64; guard2++) {
4384
4618
  const d0 = cur[0]._def;
4385
- const kidLists = cur.map((c) => childrenOf(c._def));
4619
+ const kidLists = cur.map((c) => childrenOf2(c._def));
4386
4620
  const arity = kidLists[0].length;
4387
4621
  if (kidLists.some((k) => k.length !== arity) || arity === 0) break;
4388
4622
  const diff = [];
@@ -4395,7 +4629,7 @@ function findDivergentSlot(members, shapeOf) {
4395
4629
  path.push(i);
4396
4630
  defs.push(d0);
4397
4631
  cur = kidLists.map((k) => k[i]);
4398
- const nextArity = childrenOf(cur[0]._def).length;
4632
+ const nextArity = childrenOf2(cur[0]._def).length;
4399
4633
  const samePayload = cur.every((c) => payloadKey(c, c._def) === payloadKey(cur[0], cur[0]._def));
4400
4634
  if (!samePayload || nextArity === 0) break;
4401
4635
  }
@@ -4403,7 +4637,7 @@ function findDivergentSlot(members, shapeOf) {
4403
4637
  let slotSize = 0;
4404
4638
  const sizeOf = (p) => {
4405
4639
  let n = 1;
4406
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4640
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4407
4641
  return n;
4408
4642
  };
4409
4643
  for (const c of cur) slotSize = Math.max(slotSize, sizeOf(c));
@@ -4412,7 +4646,7 @@ function findDivergentSlot(members, shapeOf) {
4412
4646
  function renderWithHole(p, path, depth = 0) {
4413
4647
  if (depth === path.length) return "\u2039slot\u203A";
4414
4648
  const d = p._def;
4415
- const kids = childrenOf(d);
4649
+ const kids = childrenOf2(d);
4416
4650
  const idx = path[depth];
4417
4651
  const parts = kids.map((k, i) => i === idx ? renderWithHole(k, path, depth + 1) : render(k, 2));
4418
4652
  switch (d.tag) {
@@ -4870,7 +5104,7 @@ function capturesPositionally(p, seen = /* @__PURE__ */ new Set()) {
4870
5104
  seen.add(p);
4871
5105
  const d = p._def;
4872
5106
  if (d.tag === "field" || d.tag === "label") return true;
4873
- for (const k of childrenOf(d)) if (capturesPositionally(k, seen)) return true;
5107
+ for (const k of childrenOf2(d)) if (capturesPositionally(k, seen)) return true;
4874
5108
  return false;
4875
5109
  }
4876
5110
  function detectHandRolledSepBy(terms, shapeOf) {
@@ -4976,7 +5210,7 @@ function nestedNodeTypes(root, out, seen, isRoot) {
4976
5210
  }
4977
5211
  return;
4978
5212
  }
4979
- for (const k of childrenOf(d)) nestedNodeTypes(k, out, seen, false);
5213
+ for (const k of childrenOf2(d)) nestedNodeTypes(k, out, seen, false);
4980
5214
  }
4981
5215
  var MAX_LOST_TYPES = 8;
4982
5216
  function findStructureLoss(visited, resolve) {
@@ -5403,9 +5637,196 @@ function buildGrammarPlan(entry, winners) {
5403
5637
  return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, dispatches, attempts, labels, rules: rules2 };
5404
5638
  }
5405
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
+
5406
5823
  // src/compiler/scannable-run.ts
5407
5824
  var BACKSLASH = 92;
5408
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(", ");
5409
5830
  function classToRanges(cls) {
5410
5831
  if (cls === "\\d") return shorthandRanges("d");
5411
5832
  if (cls === "\\w") return shorthandRanges("w");
@@ -6444,13 +6865,15 @@ function scanBranch(shape, mint) {
6444
6865
  const m = emitShapeMatch(shape, "_e", mint, " ", "c");
6445
6866
  return [...m.setup, ` if (${m.end} > _e) { _e = ${m.end}; continue }`].join("\n");
6446
6867
  }
6447
- function scanBranchLabeled(shape, kindIndex, mint) {
6868
+ function scanBranchLabeled(shape, kindIndex, label2, mint) {
6448
6869
  const m = emitShapeMatch(shape, "_e", mint, " ", "c");
6449
6870
  return [
6450
6871
  ...m.setup,
6451
6872
  ` if (${m.end} > _e) {`,
6452
6873
  ` if (_cap) {`,
6453
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)`,
6454
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})`,
6455
6878
  ` }`,
6456
6879
  ` _e = ${m.end}`,
@@ -6510,6 +6933,11 @@ var CAP_RECORD = [
6510
6933
  ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
6511
6934
  ` }`
6512
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");
6513
6941
  function composeFastLoop(shapes) {
6514
6942
  const mint = makeMint();
6515
6943
  return [
@@ -6538,7 +6966,7 @@ function analyzeLabeledScannableRun(trivia2) {
6538
6966
  const d = getCoreRegexDef(arm.parser);
6539
6967
  const shape = d ? scanShapeFromRegex(d.source, d.flags) : null;
6540
6968
  if (!shape) return null;
6541
- out.push({ shape, kindIndex: arm.kindIndex });
6969
+ out.push({ shape, kindIndex: arm.kindIndex, label: arm.label });
6542
6970
  }
6543
6971
  return out;
6544
6972
  }
@@ -6547,11 +6975,13 @@ function buildLabeledScannableTriviaFnDecl(fnName, arms) {
6547
6975
  return [
6548
6976
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
6549
6977
  ` let _e = _pos`,
6978
+ ` const _rootMark = _cap && _ctx._rootTriviaLog !== undefined ? _ctx._rootTriviaLog.length : 0`,
6550
6979
  ` while (_e < input.length) {`,
6551
6980
  ` const c = input.charCodeAt(_e)`,
6552
- ...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, mint)),
6981
+ ...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, a.label, mint)),
6553
6982
  ` break`,
6554
6983
  ` }`,
6984
+ SELECTED_ROOT_FINISH,
6555
6985
  ` return _e`,
6556
6986
  `}`
6557
6987
  ].join("\n");
@@ -6567,6 +6997,8 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
6567
6997
  ` const _ce = _e + _m${i}[0].length`,
6568
6998
  ` if (_cap) {`,
6569
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})`,
6570
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})`,
6571
7003
  ` }`,
6572
7004
  ` _e = _ce`,
@@ -6578,11 +7010,13 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
6578
7010
  return [
6579
7011
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
6580
7012
  ` let _e = _pos`,
7013
+ ` const _rootMark = _cap && _ctx._rootTriviaLog !== undefined ? _ctx._rootTriviaLog.length : 0`,
6581
7014
  ` while (_e < input.length) {`,
6582
7015
  ` let _matched = false`,
6583
7016
  tryArms,
6584
7017
  ` if (!_matched) break`,
6585
7018
  ` }`,
7019
+ SELECTED_ROOT_FINISH,
6586
7020
  ` return _e`,
6587
7021
  `}`
6588
7022
  ].join("\n");
@@ -6605,6 +7039,8 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
6605
7039
  ` const _ce = _r${i}.span.end`,
6606
7040
  ` if (_cap) {`,
6607
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})`,
6608
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})`,
6609
7045
  ` }`,
6610
7046
  ` _e = _ce`,
@@ -6616,11 +7052,13 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
6616
7052
  return [
6617
7053
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
6618
7054
  ` let _e = _pos`,
7055
+ ` const _rootMark = _cap && _ctx._rootTriviaLog !== undefined ? _ctx._rootTriviaLog.length : 0`,
6619
7056
  ` while (_e < input.length) {`,
6620
7057
  ` let _matched = false`,
6621
7058
  tryArms,
6622
7059
  ` if (!_matched) break`,
6623
7060
  ` }`,
7061
+ SELECTED_ROOT_FINISH,
6624
7062
  ` return _e`,
6625
7063
  `}`
6626
7064
  ].join("\n");
@@ -6843,6 +7281,9 @@ var HOST_READS_DECL = "const _hostReads = (b, n) => { if (b === undefined) retur
6843
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 }";
6844
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] } }";
6845
7283
  var _loweringSink = null;
7284
+ function hasSelectedRootTrivia(ctx) {
7285
+ return ctx.rootTriviaClassified === true;
7286
+ }
6846
7287
  function v(ctx, prefix = "_v") {
6847
7288
  return `${prefix}${ctx.vars++}`;
6848
7289
  }
@@ -7152,10 +7593,12 @@ function planDisjointDispatch(parsers) {
7152
7593
  }
7153
7594
  return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
7154
7595
  }
7155
- function captureRestoreBody(mL, mR, mTl, mLg, mF = null) {
7596
+ function captureRestoreBody(ctx, mL, mR, mTl, mLg, mF = null, mRootLg = null) {
7156
7597
  const fieldRestore = mF ? `; if (_ctx._fields && _ctx._fields.length !== ${mF}) _ctx._fields.length = ${mF}` : "";
7157
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}`;
7158
- 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}`;
7159
7602
  }
7160
7603
  function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
7161
7604
  if (seen.has(p)) return false;
@@ -7365,7 +7808,7 @@ function emitFallible(inner, ctx, pos, swallow = false) {
7365
7808
  `${ind0} ${valV} = ${r.valueVar}; ${endV} = ${r.endVar}; ${okV} = true`,
7366
7809
  `${ind0}}`,
7367
7810
  ...mL ? [
7368
- `${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null, mF)} }`
7811
+ `${ind0}if (!${okV}) { ${captureRestoreBody(ctx, mL, mR, mTl, null, mF)} }`
7369
7812
  ] : []
7370
7813
  ];
7371
7814
  return { stmts, okVar: okV, valVar: valV, endVar: endV, mayCommit };
@@ -7562,11 +8005,13 @@ function emitSeqValues(def, ctx, pos) {
7562
8005
  const markV = v(ctx, "_mk");
7563
8006
  const markTl = v(ctx, "_mktl");
7564
8007
  const markLog = v(ctx, "_mklg");
8008
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
7565
8009
  const scanEndV = v(ctx, "_sne");
7566
8010
  stmts.push(
7567
8011
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
7568
8012
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
7569
8013
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8014
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
7570
8015
  `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`,
7571
8016
  ...emitLineTrack(ctx, curV, scanEndV)
7572
8017
  );
@@ -7575,17 +8020,19 @@ function emitSeqValues(def, ctx, pos) {
7575
8020
  const endAfterV = v(ctx, "_sea");
7576
8021
  stmts.push(
7577
8022
  `${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
7578
- `${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};` : ""} }`
7579
8024
  );
7580
8025
  valueVars.push(r2.valueVar);
7581
8026
  continue;
7582
8027
  } else {
7583
8028
  const trivFn = ensureTriviaFn(ctx);
7584
8029
  const markLog = v(ctx, "_mklg");
8030
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
7585
8031
  const scanEndV = v(ctx, "_sne");
7586
- 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";
7587
8033
  stmts.push(
7588
8034
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8035
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
7589
8036
  `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`,
7590
8037
  ...emitLineTrack(ctx, curV, scanEndV)
7591
8038
  );
@@ -7594,7 +8041,7 @@ function emitSeqValues(def, ctx, pos) {
7594
8041
  const endAfterV = v(ctx, "_sea");
7595
8042
  stmts.push(
7596
8043
  `${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
7597
- `${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};` : ""} }`
7598
8045
  );
7599
8046
  valueVars.push(r2.valueVar);
7600
8047
  continue;
@@ -7836,6 +8283,7 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7836
8283
  const selectorRawMark = selectorNeedsRollback ? v(ctx, "_dsr") : null;
7837
8284
  const selectorTriviaMark = selectorNeedsRollback ? v(ctx, "_dst") : null;
7838
8285
  const selectorLogMark = selectorNeedsRollback ? v(ctx, "_dsg") : null;
8286
+ const selectorRootLogMark = selectorNeedsRollback && hasSelectedRootTrivia(ctx) ? v(ctx, "_dsrg") : null;
7839
8287
  const selectorFieldMark = selectorNeedsRollback ? v(ctx, "_dsf") : null;
7840
8288
  const selectorErrorMark = selectorNeedsRollback ? v(ctx, "_dse") : null;
7841
8289
  const selector = emit(def.selector, ctx, pos);
@@ -7848,6 +8296,7 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7848
8296
  `${ind(ctx)}const ${selectorRawMark} = _ctx._cstRawChildren?.length ?? 0`,
7849
8297
  `${ind(ctx)}const ${selectorTriviaMark} = _ctx._cstTriviaLog?.length ?? 0`,
7850
8298
  `${ind(ctx)}const ${selectorLogMark} = _ctx._triviaLog?.length ?? 0`,
8299
+ ...selectorRootLogMark ? [`${ind(ctx)}const ${selectorRootLogMark} = _ctx._rootTriviaLog?.length ?? 0`] : [],
7851
8300
  `${ind(ctx)}const ${selectorFieldMark} = _ctx._fields?.length ?? 0`,
7852
8301
  `${ind(ctx)}const ${selectorErrorMark} = _ctx._errors?.length ?? 0`
7853
8302
  ] : [],
@@ -7867,16 +8316,18 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7867
8316
  const mayError = mayRecordRecoveryError(parser3, !!ctx.recovery);
7868
8317
  const errMark = mayError ? v(ctx, "_derr") : null;
7869
8318
  const logMark = ctx.activeTrivia ? v(ctx, "_dlog") : null;
8319
+ const rootLogMark = ctx.activeTrivia && hasSelectedRootTrivia(ctx) ? v(ctx, "_drlog") : null;
7870
8320
  if (usesRouted) {
7871
8321
  stmts.push(
7872
8322
  ...routedV ? [`${ind(ctx)}const ${routedV} = _ctx._routed; _ctx._routed = { value: ${selector.valueVar}, span: { start: ${pos}, end: ${selector.endVar} } }`] : [],
7873
8323
  ...selectorLeafMark ? [
7874
- `${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}`
7875
8325
  ] : []
7876
8326
  );
7877
8327
  }
7878
8328
  if (errMark) stmts.push(`${ind(ctx)}const ${errMark} = _ctx._errors?.length ?? 0`);
7879
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`);
7880
8331
  const savedRoutedLocal = ctx.routedLocal;
7881
8332
  const savedNoHoist = ctx.noHoist;
7882
8333
  if (usesRouted && !needsRoutedContext) {
@@ -7891,11 +8342,12 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7891
8342
  stmts.push(...coverageAttempt(ctx, coverageId, pos));
7892
8343
  const errorRollback = errMark ? `if (_ctx._errors && _ctx._errors.length !== ${errMark}) _ctx._errors.length = ${errMark}; ` : "";
7893
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}; ` : "";
7894
8346
  const routedRollback = routedV ? `_ctx._routed = ${routedV}; ` : "";
7895
8347
  const coverageFailure = coverageId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageId)}, phase: 'failure', offset: _ctx._fe }); `;
7896
8348
  stmts.push(
7897
8349
  ...tail.stmts,
7898
- `${ind(ctx)}if (!${tail.okVar}) { ${routedRollback}${errorRollback}${logRollback}${coverageFailure}${committedFailBody(ctx)} }`,
8350
+ `${ind(ctx)}if (!${tail.okVar}) { ${routedRollback}${errorRollback}${logRollback}${rootLogRollback}${coverageFailure}${committedFailBody(ctx)} }`,
7899
8351
  ...routedV ? [`${ind(ctx)}_ctx._routed = ${routedV}`] : [],
7900
8352
  `${ind(ctx)}${outV} = ${valueMode === "tail" ? tail.valVar : `[${selector.valueVar}, ${tail.valVar}]`}`,
7901
8353
  `${ind(ctx)}${outE} = ${tail.endVar}`,
@@ -8076,11 +8528,13 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
8076
8528
  const markRaw = armNeedsRollback ? v(ctx, "_cmr") : null;
8077
8529
  const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
8078
8530
  const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
8531
+ const markRootLog = ctx.activeTrivia && hasSelectedRootTrivia(ctx) ? v(ctx, "_cmlrg") : null;
8079
8532
  const markFields = armNeedsFieldRollback ? v(ctx, "_cmf") : null;
8080
8533
  const markErrors = armMayRecordError ? v(ctx, "_cme") : null;
8081
- 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}` : "";
8082
8536
  const errorRollback = markErrors ? `if (_ctx._errors && _ctx._errors.length !== ${markErrors}) _ctx._errors.length = ${markErrors}` : "";
8083
- const rollback = [captureRollback, errorRollback].filter(Boolean).join("; ");
8537
+ const rollback = [captureRollback, rootRollback, errorRollback].filter(Boolean).join("; ");
8084
8538
  const failSlot = atStart ? staticFx : "_ctx._fx";
8085
8539
  stmts.push(`${ind0}if (${skipCond}) {`);
8086
8540
  if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
@@ -8095,6 +8549,7 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
8095
8549
  ...markFields ? [`${ind(ctx)}const ${markFields} = _ctx._fields?.length ?? 0`] : []
8096
8550
  );
8097
8551
  }
8552
+ if (markRootLog) stmts.push(`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog?.length ?? 0`);
8098
8553
  if (markErrors) stmts.push(`${ind(ctx)}const ${markErrors} = _ctx._errors?.length ?? 0`);
8099
8554
  const arm = emitFallible(p, ctx, pos, atStart);
8100
8555
  const { stmts: armStmts, okVar, valVar, endVar } = arm;
@@ -8163,7 +8618,7 @@ function routedNeedsContextBridge(p, ctx, seen = /* @__PURE__ */ new Set()) {
8163
8618
  const d = p._def;
8164
8619
  if (d.tag === "dispatch") return false;
8165
8620
  if (d.tag === "withCtx") return parserUsesRouted2(d.parser);
8166
- return childrenOf2(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8621
+ return childrenOf3(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8167
8622
  }
8168
8623
  function armReplayInScope(arm, ctx) {
8169
8624
  let p = arm;
@@ -8282,21 +8737,29 @@ function emitMany(def, ctx, pos) {
8282
8737
  const markV = v(ctx, "_mk");
8283
8738
  const markTl = v(ctx, "_mktl");
8284
8739
  const markLog = v(ctx, "_mklg");
8740
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8285
8741
  const npV = v(ctx, "_np");
8286
8742
  stmts.push(
8287
8743
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8288
8744
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8289
8745
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8746
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8290
8747
  `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8291
8748
  ...emitLineTrack(ctx, curV, npV)
8292
8749
  );
8293
8750
  itemPos = npV;
8294
- 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};` : ""} `;
8295
8752
  } else {
8296
8753
  const trivFn = ensureTriviaFn(ctx);
8754
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8297
8755
  const npV = v(ctx, "_np");
8298
- 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
+ );
8299
8761
  itemPos = npV;
8762
+ rollback = markRootLog ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${markRootLog}) _ctx._rootTriviaLog.length = ${markRootLog}; ` : "";
8300
8763
  }
8301
8764
  }
8302
8765
  if (!ctx.recovery && !ctx.coverage && !failsAtStart(def.parser) && needsFirstSetGuard(def.parser)) {
@@ -8356,9 +8819,10 @@ function emitAttempt(p, def, ctx, pos) {
8356
8819
  const raw = v(ctx, "_atr");
8357
8820
  const trivia2 = v(ctx, "_att");
8358
8821
  const log = v(ctx, "_atg");
8822
+ const rootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_atrg") : null;
8359
8823
  const fields = v(ctx, "_atf");
8360
8824
  const errors = v(ctx, "_ate");
8361
- 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}`;
8362
8826
  const traceId = ctx.coverage?.plan.attempts.get(p);
8363
8827
  const traceRollback = traceId === void 0 ? "" : ` _ctx._grammarTrace?.write({ id: ${JSON.stringify(traceId)}, phase: 'rollback', offset: ${pos} });`;
8364
8828
  const preGuard = [];
@@ -8374,7 +8838,7 @@ function emitAttempt(p, def, ctx, pos) {
8374
8838
  return {
8375
8839
  stmts: [
8376
8840
  ...preGuard,
8377
- `${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`,
8378
8842
  ...inner.stmts,
8379
8843
  ...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback}${inner.mayCommit ? ` if (_ctx._fc) ${committedFailBody(ctx)};` : ""} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
8380
8844
  ],
@@ -8416,16 +8880,17 @@ function emitSepBy(_p, def, ctx, pos) {
8416
8880
  const itemFailBreak = (sepEndVar, sepRb, postSepRb) => def.trailing === void 0 ? `${sepRb}break` : `${postSepRb}${curV} = ${sepEndVar}; break`;
8417
8881
  const postSepMarks = () => {
8418
8882
  if (def.trailing === void 0 || !ctx.capturing) return { decl: [], rb: "" };
8419
- 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");
8420
8884
  return {
8421
8885
  decl: [
8422
8886
  `${ind(ctx)}const ${lv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8423
8887
  `${ind(ctx)}const ${rw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8424
8888
  `${ind(ctx)}const ${tl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8425
8889
  `${ind(ctx)}const ${lg} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8890
+ ...rlg ? [`${ind(ctx)}const ${rlg} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8426
8891
  `${ind(ctx)}const ${fl} = _ctx._fields ? _ctx._fields.length : 0`
8427
8892
  ],
8428
- 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}; `
8429
8894
  };
8430
8895
  };
8431
8896
  if (rec) {
@@ -8463,6 +8928,7 @@ function emitSepBy(_p, def, ctx, pos) {
8463
8928
  const markV = v(ctx, "_mk");
8464
8929
  const markTl = v(ctx, "_mktl");
8465
8930
  const markLog = v(ctx, "_mklg");
8931
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8466
8932
  const markLv = v(ctx, "_mklv");
8467
8933
  const markFld = v(ctx, "_mkf");
8468
8934
  const spV = v(ctx, "_sp");
@@ -8470,13 +8936,14 @@ function emitSepBy(_p, def, ctx, pos) {
8470
8936
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8471
8937
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8472
8938
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8939
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8473
8940
  `${ind(ctx)}const ${markLv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8474
8941
  `${ind(ctx)}const ${markFld} = _ctx._fields ? _ctx._fields.length : 0`,
8475
8942
  `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8476
8943
  ...emitLineTrack(ctx, curV, spV)
8477
8944
  );
8478
8945
  sepAtPos = spV;
8479
- 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}; `;
8480
8947
  const sep = emitFallible(def.separator, ctx, sepAtPos, true);
8481
8948
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep;
8482
8949
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}${sep.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
@@ -8494,19 +8961,30 @@ function emitSepBy(_p, def, ctx, pos) {
8494
8961
  );
8495
8962
  } else {
8496
8963
  const trivFn = ensureTriviaFn(ctx);
8964
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8497
8965
  const spV = v(ctx, "_sp");
8498
- 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
+ );
8499
8971
  sepAtPos = spV;
8500
8972
  const sep = emitFallible(def.separator, ctx, sepAtPos, true);
8501
8973
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep;
8502
- 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;
8503
8977
  const npV = v(ctx, "_np");
8504
- 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
+ );
8505
8983
  const next = emitFallible(def.parser, ctx, npV, true);
8506
8984
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8507
8985
  stmts.push(
8508
8986
  ...nextStmts,
8509
- ...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}; ` : "")),
8510
8988
  `${ind(ctx)}${arrV}.push(${nextVal})`,
8511
8989
  `${ind(ctx)}${curV} = ${nextEnd}`
8512
8990
  );
@@ -8601,6 +9079,7 @@ function emitNot(def, ctx, pos) {
8601
9079
  const raw = sinksLive ? v(ctx, "_ntr") : null;
8602
9080
  const tl = sinksLive ? v(ctx, "_ntt") : null;
8603
9081
  const log = sinksLive ? v(ctx, "_ntg") : null;
9082
+ const rootLog = sinksLive && hasSelectedRootTrivia(ctx) ? v(ctx, "_ntrg") : null;
8604
9083
  const fields = sinksLive ? v(ctx, "_ntf") : null;
8605
9084
  const errors = sinksLive ? v(ctx, "_nte") : null;
8606
9085
  const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
@@ -8608,11 +9087,11 @@ function emitNot(def, ctx, pos) {
8608
9087
  return {
8609
9088
  stmts: [
8610
9089
  ...sinksLive ? [
8611
- `${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`
8612
9091
  ] : [],
8613
9092
  ...stmts,
8614
9093
  ...sinksLive ? [
8615
- `${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}`
8616
9095
  ] : [],
8617
9096
  ...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
8618
9097
  ],
@@ -8748,16 +9227,17 @@ function emitToken(def, ctx, pos) {
8748
9227
  const sk = v(ctx, "_tokKinds");
8749
9228
  const stl = v(ctx, "_tokTl");
8750
9229
  const sol = v(ctx, "_tokLog");
9230
+ const srl = hasSelectedRootTrivia(ctx) ? v(ctx, "_tokRootLog") : null;
8751
9231
  const sb = v(ctx, "_tokBuf");
8752
9232
  const valV = v(ctx, "_tok");
8753
9233
  return {
8754
9234
  stmts: [
8755
- `${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`,
8756
- `${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`,
8757
9237
  `${i}try {`,
8758
9238
  ...reindentStmts(inner.stmts, ctx.indent + 1).map((stmt) => stmt.replace(/^(\s*)(?:const|let)\s+/, "$1var ")),
8759
9239
  `${i}} finally {`,
8760
- `${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}`,
8761
9241
  `${i}}`,
8762
9242
  ...emitIfFail(ctx, `!${inner.okVar}`, propagateFailBody(ctx)),
8763
9243
  `${i}const ${valV} = input.slice(${pos}, ${inner.endVar})`,
@@ -8926,7 +9406,8 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8926
9406
  const nodeSpanV = ctx.lineTracking ? v(ctx, "_nspan") : nodeSpanExpr;
8927
9407
  if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
8928
9408
  const buildExpr = structural ? `{ _tag: 'node', type: ${JSON.stringify(def.type)}, span: ${nodeSpanV}, state: ${stV} ?? null, children: ${chV} }` : mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, nodeSpanV, tlV) : def.project !== void 0 ? emitNodeProjectExpr(def.type, def.project, chV) : `${buildRef(ctx)}[${buildIdx}](${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
8929
- const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
9409
+ const hostBuildArgs = `${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV}${def.tags !== void 0 && def.tags.length > 0 ? `, ${JSON.stringify(def.tags)}` : ""}`;
9410
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${hostBuildArgs})`;
8930
9411
  const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
8931
9412
  const hostCollapseExpr = structural ? `_ctx.build !== undefined && _ctx.build._parsemanCstCollapse !== undefined && ${chV}.length === 1 && ${rawV}.length === 1 && _ctx.build._parsemanCstCollapse(${JSON.stringify(def.type)}, ${chV}[0], ${chV}, ${rawV}) ? ${chV}[0] : (${ndExpr})` : ndExpr;
8932
9413
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
@@ -9235,6 +9716,8 @@ function emitDispatch(p, ctx, pos) {
9235
9716
  case "grammar": {
9236
9717
  const savedTrivia = ctx.activeTrivia;
9237
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;
9238
9721
  if (def.clearTrivia) {
9239
9722
  ctx.activeTrivia = void 0;
9240
9723
  ctx.triviaKindLabels = void 0;
@@ -9245,13 +9728,38 @@ function emitDispatch(p, ctx, pos) {
9245
9728
  }
9246
9729
  }
9247
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++;
9248
9735
  const r = emit(def.parser, ctx, pos);
9736
+ if (opaqueRootCapture) ctx.indent--;
9249
9737
  ctx.activeTrivia = savedTrivia;
9250
9738
  ctx.triviaKindLabels = savedKindLabels;
9251
- 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
+ }
9252
9759
  return {
9253
9760
  ...r,
9254
9761
  stmts: [
9762
+ ...strictScopeCheck === void 0 ? [] : [strictScopeCheck],
9255
9763
  `${ind(ctx)}const ${savedCapture} = _ctx.captureTrivia; _ctx.captureTrivia = true`,
9256
9764
  ...r.stmts,
9257
9765
  `${ind(ctx)}_ctx.captureTrivia = ${savedCapture}`
@@ -9389,7 +9897,7 @@ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9389
9897
  return false;
9390
9898
  }
9391
9899
  }
9392
- return childrenOf2(d).some((child) => hasLineTrackingDef(child, seen));
9900
+ return childrenOf3(d).some((child) => hasLineTrackingDef(child, seen));
9393
9901
  }
9394
9902
  function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9395
9903
  if (seen.has(p)) return false;
@@ -9408,7 +9916,7 @@ function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9408
9916
  case "dispatch":
9409
9917
  return false;
9410
9918
  default:
9411
- return childrenOf2(d).some((child) => parserUsesRouted2(child, seen));
9919
+ return childrenOf3(d).some((child) => parserUsesRouted2(child, seen));
9412
9920
  }
9413
9921
  }
9414
9922
  function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
@@ -9423,9 +9931,9 @@ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
9423
9931
  return false;
9424
9932
  }
9425
9933
  }
9426
- return childrenOf2(d).some((child) => hasDirectBuildDef(child, seen));
9934
+ return childrenOf3(d).some((child) => hasDirectBuildDef(child, seen));
9427
9935
  }
9428
- function childrenOf2(def) {
9936
+ function childrenOf3(def) {
9429
9937
  switch (def.tag) {
9430
9938
  case "sequence":
9431
9939
  case "choice":
@@ -9500,7 +10008,7 @@ function subtreeSizes(roots) {
9500
10008
  const def = p._def;
9501
10009
  let s = 1;
9502
10010
  if (def.tag !== "lazy") {
9503
- for (const child of childrenOf2(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
10011
+ for (const child of childrenOf3(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
9504
10012
  }
9505
10013
  sizes.set(p, s);
9506
10014
  return s;
@@ -9521,7 +10029,7 @@ function subtreeSizes(roots) {
9521
10029
  return;
9522
10030
  }
9523
10031
  sz(p);
9524
- for (const child of childrenOf2(def)) visit(child);
10032
+ for (const child of childrenOf3(def)) visit(child);
9525
10033
  };
9526
10034
  for (const root of roots) visit(root);
9527
10035
  return sizes;
@@ -9531,7 +10039,7 @@ function analyzeLazyUsageMulti(roots) {
9531
10039
  const recursive = /* @__PURE__ */ new Set();
9532
10040
  const descended = /* @__PURE__ */ new Set();
9533
10041
  const active = /* @__PURE__ */ new Set();
9534
- function walk2(p) {
10042
+ function walk(p) {
9535
10043
  const def = p._def;
9536
10044
  if (def.tag === "lazy") {
9537
10045
  counts.set(p, (counts.get(p) ?? 0) + 1);
@@ -9548,17 +10056,17 @@ function analyzeLazyUsageMulti(roots) {
9548
10056
  return;
9549
10057
  }
9550
10058
  active.add(p);
9551
- walk2(resolved);
10059
+ walk(resolved);
9552
10060
  active.delete(p);
9553
10061
  return;
9554
10062
  }
9555
10063
  if (isHoistableTag(def.tag)) {
9556
10064
  counts.set(p, (counts.get(p) ?? 0) + 1);
9557
10065
  }
9558
- for (const child of childrenOf2(def)) walk2(child);
10066
+ for (const child of childrenOf3(def)) walk(child);
9559
10067
  }
9560
10068
  const rootsArr = [...roots];
9561
- for (const root of rootsArr) walk2(root);
10069
+ for (const root of rootsArr) walk(root);
9562
10070
  return { counts, recursive, sizes: subtreeSizes(rootsArr) };
9563
10071
  }
9564
10072
  function ruleDependencies(ruleMap) {
@@ -9568,7 +10076,7 @@ function ruleDependencies(ruleMap) {
9568
10076
  for (const [name, comb] of ruleMap) {
9569
10077
  const found = /* @__PURE__ */ new Set();
9570
10078
  const seen = /* @__PURE__ */ new Set();
9571
- const walk2 = (p, isRoot) => {
10079
+ const walk = (p, isRoot) => {
9572
10080
  const def = p._def;
9573
10081
  const boundary = def.tag === "lazy" ? nameOf.get(p) ?? p._ruleName : nameOf.get(p);
9574
10082
  if (!isRoot && boundary !== void 0) {
@@ -9584,12 +10092,12 @@ function ruleDependencies(ruleMap) {
9584
10092
  } catch {
9585
10093
  return;
9586
10094
  }
9587
- walk2(resolved, false);
10095
+ walk(resolved, false);
9588
10096
  return;
9589
10097
  }
9590
- for (const child of childrenOf2(def)) walk2(child, false);
10098
+ for (const child of childrenOf3(def)) walk(child, false);
9591
10099
  };
9592
- walk2(comb, true);
10100
+ walk(comb, true);
9593
10101
  deps.set(name, [...found]);
9594
10102
  }
9595
10103
  return deps;
@@ -9736,6 +10244,7 @@ function compile(combinator, mapFnSources, opts) {
9736
10244
  runDuplicationDiagnostic(combinator, opts?.duplication);
9737
10245
  markUnusedValues(combinator);
9738
10246
  const grammarTrivia = combinator._meta.grammarTrivia;
10247
+ const rootTriviaClassified = grammarTrivia?._meta.rootTriviaClassified === true || combinator._meta.rootTriviaClassified === true;
9739
10248
  const grammarScanSkip = combinator._meta.grammarScanSkip;
9740
10249
  const grammarHostMode = combinator._meta.grammarHostMode;
9741
10250
  const grammarTrackLines = combinator._meta.grammarTrackLines;
@@ -9762,7 +10271,11 @@ function compile(combinator, mapFnSources, opts) {
9762
10271
  lineTracking: opts?.trackLines === true || grammarTrackLines === true || hasLineTrackingDef(combinator),
9763
10272
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
9764
10273
  lazyUsage: analyzeLazyUsage(combinator),
9765
- ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
10274
+ ...rootTriviaClassified ? { rootTriviaClassified: true } : {},
10275
+ ...grammarTrivia ? {
10276
+ activeTrivia: grammarTrivia,
10277
+ triviaKindLabels: grammarTrivia._meta.triviaKindLabels
10278
+ } : {},
9766
10279
  ...grammarScanSkip ? { activeScanSkip: grammarScanSkip } : {}
9767
10280
  };
9768
10281
  const r = emit(combinator, ctx, "_pos");
@@ -9910,10 +10423,11 @@ function assertRuleName(name) {
9910
10423
  );
9911
10424
  }
9912
10425
  }
9913
- function publicRuleWrapperSource(rule, fnSource, ambientTriviaKindLabels) {
9914
- const labels = rule._meta.triviaKindLabels ?? ambientTriviaKindLabels;
9915
- if (labels === void 0) return fnSource;
9916
- 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,"} } })`;
9917
10431
  }
9918
10432
  function compileLinkable(ruleMapArg, ns, opts) {
9919
10433
  if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
@@ -9921,6 +10435,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
9921
10435
  if (opts?.duplication !== void 0) runDuplicationDiagnosticRules(ruleMapArg, opts.duplication);
9922
10436
  for (const [, rule] of ruleMapArg) markUnusedValues(rule);
9923
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);
9924
10439
  const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
9925
10440
  const grammarHostMode = opts?.hostMode ?? ruleMapArg.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
9926
10441
  const grammarTrackLines = opts?.trackLines === true || ruleMapArg.some(([, r]) => r._meta.grammarTrackLines === true);
@@ -9934,6 +10449,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
9934
10449
  return false;
9935
10450
  }
9936
10451
  });
10452
+ const nodeMeta = new Map(ruleMap.map(([name, rule]) => [name, collectGrammarReflection([[name, rule]], { followLazy: false })]));
9937
10453
  const ctx = {
9938
10454
  vars: 0,
9939
10455
  indent: 1,
@@ -9963,7 +10479,11 @@ function compileLinkable(ruleMapArg, ns, opts) {
9963
10479
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
9964
10480
  ns,
9965
10481
  deferFirstSetRefs: true,
9966
- ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
10482
+ ...rootTriviaClassified ? { rootTriviaClassified: true } : {},
10483
+ ...grammarTrivia ? {
10484
+ activeTrivia: grammarTrivia,
10485
+ triviaKindLabels: grammarTrivia._meta.triviaKindLabels
10486
+ } : {},
9967
10487
  ...grammarScanSkip ? { activeScanSkip: grammarScanSkip } : {}
9968
10488
  };
9969
10489
  const seen = /* @__PURE__ */ new Map();
@@ -10009,7 +10529,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10009
10529
  scanExternal(resolved);
10010
10530
  return;
10011
10531
  }
10012
- for (const child of childrenOf2(def)) scanExternal(child);
10532
+ for (const child of childrenOf3(def)) scanExternal(child);
10013
10533
  };
10014
10534
  for (const [, rule] of ruleMap) scanExternal(rule);
10015
10535
  }
@@ -10095,7 +10615,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10095
10615
  ...r.stmts,
10096
10616
  ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`,
10097
10617
  `}`
10098
- ].join("\n"), ctx.triviaKindLabels));
10618
+ ].join("\n"), ctx.triviaKindLabels, ctx.rootTriviaClassified));
10099
10619
  }
10100
10620
  const firstSets = /* @__PURE__ */ new Map();
10101
10621
  const firstSetRecipes = /* @__PURE__ */ new Map();
@@ -10131,6 +10651,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10131
10651
  hostBranchElided: !!ctx.hostBranchElided,
10132
10652
  hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
10133
10653
  isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs),
10654
+ nodeMeta,
10134
10655
  mfFns: mfSrcs ? [] : ctx.mapFns,
10135
10656
  buildFns: buildSrcs ? [] : ctx.buildFns
10136
10657
  };
@@ -10151,7 +10672,7 @@ function hasExternalRuleRef(ruleMap) {
10151
10672
  if (resolved === void 0) return p._ruleName !== void 0;
10152
10673
  return visit(resolved);
10153
10674
  }
10154
- return childrenOf2(def).some(visit);
10675
+ return childrenOf3(def).some(visit);
10155
10676
  };
10156
10677
  return ruleMap.some(([, rule]) => visit(rule));
10157
10678
  }
@@ -10173,7 +10694,7 @@ function hasSemanticReduction(roots, externalRefs) {
10173
10694
  return true;
10174
10695
  }
10175
10696
  }
10176
- return childrenOf2(def).some(visit);
10697
+ return childrenOf3(def).some(visit);
10177
10698
  };
10178
10699
  return roots.some(visit);
10179
10700
  }
@@ -10246,7 +10767,7 @@ function withCtx(extra, combinator) {
10246
10767
  }
10247
10768
 
10248
10769
  // src/compiler/ir-serialize.ts
10249
- function childrenOf3(def) {
10770
+ function childrenOf4(def) {
10250
10771
  switch (def.tag) {
10251
10772
  case "sequence":
10252
10773
  case "choice":
@@ -10310,6 +10831,10 @@ function projectOpt(project) {
10310
10831
  if (project === void 0) return void 0;
10311
10832
  return `project: ${project}`;
10312
10833
  }
10834
+ function tagsOpt(tags) {
10835
+ if (tags === void 0 || tags.length === 0) return void 0;
10836
+ return `tags: ${JSON.stringify(tags)}`;
10837
+ }
10313
10838
  function matcherExpr(entry) {
10314
10839
  switch (entry.kind) {
10315
10840
  case "startsWith":
@@ -10439,6 +10964,7 @@ function evalRuleMapIR(ir) {
10439
10964
  "transform",
10440
10965
  "skip",
10441
10966
  "trivia",
10967
+ "classifiedTrivia",
10442
10968
  "label",
10443
10969
  "field",
10444
10970
  "expect",
@@ -10479,6 +11005,7 @@ function evalRuleMapIR(ir) {
10479
11005
  transform,
10480
11006
  skip,
10481
11007
  trivia,
11008
+ classifiedTrivia,
10482
11009
  label,
10483
11010
  field,
10484
11011
  expect,
@@ -10523,7 +11050,7 @@ var Serializer = class {
10523
11050
  if (target) this.assertNoRuleRef(target, seen);
10524
11051
  return;
10525
11052
  }
10526
- for (const child of childrenOf3(c._def)) this.assertNoRuleRef(child, seen);
11053
+ for (const child of childrenOf4(c._def)) this.assertNoRuleRef(child, seen);
10527
11054
  }
10528
11055
  /** A `rules()` map wraps each rule value in a named `lazy` proxy; the real body is
10529
11056
  * its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
@@ -10587,7 +11114,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10587
11114
  this.counts.set(c, (this.counts.get(c) ?? 0) + 1);
10588
11115
  if ((this.counts.get(c) ?? 0) > 1) return;
10589
11116
  active.add(c);
10590
- for (const child of childrenOf3(def)) this.analyze(child, active);
11117
+ for (const child of childrenOf4(def)) this.analyze(child, active);
10591
11118
  active.delete(c);
10592
11119
  }
10593
11120
  /** Reference `c`: a const var if it has one (emitting its decl first), else inline.
@@ -10704,12 +11231,14 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10704
11231
  case "node": {
10705
11232
  if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
10706
11233
  const projectEntry = projectOpt(def.project);
11234
+ const tagEntry = tagsOpt(def.tags);
10707
11235
  const optEntries = [
10708
11236
  ...def.unwrap ? ["unwrap: true"] : [],
10709
11237
  ...def.collapse ? ["collapse: true"] : [],
10710
11238
  ...projectEntry === void 0 ? [] : [projectEntry],
10711
11239
  ...def.captureTrivia ? ["captureTrivia: true"] : [],
10712
- ...def.trailingTrivia ? ["trailingTrivia: true"] : []
11240
+ ...def.trailingTrivia ? ["trailingTrivia: true"] : [],
11241
+ ...tagEntry === void 0 ? [] : [tagEntry]
10713
11242
  ];
10714
11243
  const opts = optEntries.length > 0 ? `, { ${optEntries.join(", ")} }` : "";
10715
11244
  if (def.type === void 0) {
@@ -10723,8 +11252,11 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10723
11252
  return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
10724
11253
  }
10725
11254
  case "grammar": {
10726
- const trivia2 = def.clearTrivia ? "null" : def.triviaParser ? kid(def.triviaParser) : "undefined";
10727
- 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)})`;
10728
11260
  }
10729
11261
  case "withCtx": {
10730
11262
  if (def.extraSrc === void 0) throw new Unserializable("withCtx without extraSrc");
@@ -10757,15 +11289,16 @@ function normalizeCstCollapse(collapse) {
10757
11289
  }
10758
11290
  return typeof collapse === "function" ? collapse : void 0;
10759
11291
  }
10760
- function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
10761
- return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children2] };
11292
+ function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state, tags) {
11293
+ return tags !== void 0 && tags.length > 0 ? { _tag: "node", type, tags, span: { ...span }, state: state ?? null, children: [...children2] } : { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children2] };
10762
11294
  }
10763
- function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
11295
+ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state, _tags) {
10764
11296
  if (typeof typeOrOptions === "string") {
10765
11297
  return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
10766
11298
  }
10767
11299
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
10768
- const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2);
11300
+ const materializeTags = typeOrOptions?.tags === true;
11301
+ const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2, tags) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2, materializeTags ? tags : void 0);
10769
11302
  host._parsemanCstOutput = true;
10770
11303
  if (collapse) host._parsemanCstCollapse = collapse;
10771
11304
  return host;
@@ -10807,6 +11340,7 @@ function fusedBody(pieces) {
10807
11340
  ...[...winner].map(([k, p]) => p.ruleFns.get(k))
10808
11341
  ];
10809
11342
  const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
11343
+ const reflection = mergeGrammarReflections([...winner].map(([k, p]) => p.nodeMeta.get(k) ?? { nodes: [] }));
10810
11344
  const { mode, elided } = hostModeOfPieces(pieces);
10811
11345
  const rawBody = [
10812
11346
  ...lines,
@@ -10815,6 +11349,7 @@ function fusedBody(pieces) {
10815
11349
  "}",
10816
11350
  `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_MODE.description)}), { value: ${JSON.stringify(mode)}, enumerable: false })`,
10817
11351
  `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_ELIDED.description)}), { value: ${JSON.stringify(elided)}, enumerable: false })`,
11352
+ `Object.defineProperty(_map, Symbol.for('parseman.grammarReflection'), { value: ${grammarReflectionSource(reflection)}, enumerable: false })`,
10818
11353
  // …and on each rule FUNCTION, because `run(map.Rule, …)` is handed the rule, not the
10819
11354
  // map, and would otherwise have nothing to check against. Done once at fuse time,
10820
11355
  // before any rule has been called, so it costs nothing per parse.
@@ -11618,157 +12153,26 @@ function parseDoc(registry, rootRule, input, opts = {}) {
11618
12153
  return new ParseDocImpl(registry, rootRule, opts, { abs: null }, [fail], null, input);
11619
12154
  }
11620
12155
 
11621
- // src/cst/trivia-entries.ts
11622
- var EMPTY_INDICES = Object.freeze([]);
11623
- var EMPTY_GAPS = Object.freeze([]);
11624
- function entryOffset(i, stride) {
11625
- return i * stride;
11626
- }
11627
- function triviaEntries(log, labels, opts) {
11628
- const baseStride = opts?.nodeLog ? 3 : 2;
11629
- const stride = labels ? baseStride + 1 : baseStride;
11630
- const length = Math.floor(log.length / stride);
11631
- return {
11632
- length,
11633
- labels,
11634
- stride,
11635
- start(i) {
11636
- return log[entryOffset(i, stride)];
11637
- },
11638
- end(i) {
11639
- return log[entryOffset(i, stride) + 1];
11640
- },
11641
- insertIndex(i) {
11642
- return opts?.nodeLog ? log[entryOffset(i, stride) + 2] : void 0;
11643
- },
11644
- kindIndex(i) {
11645
- if (!labels) return void 0;
11646
- return log[entryOffset(i, stride) + baseStride];
11647
- },
11648
- kind(i) {
11649
- const ki = labels ? log[entryOffset(i, stride) + baseStride] : void 0;
11650
- return ki !== void 0 ? labels[ki] : void 0;
11651
- },
11652
- text(i, input) {
11653
- const s = log[entryOffset(i, stride)];
11654
- const e = log[entryOffset(i, stride) + 1];
11655
- return input.slice(s, e);
11656
- }
11657
- };
11658
- }
11659
- function appendEntryRange(map, key, first, last) {
11660
- let indices = map.get(key);
11661
- if (!indices) {
11662
- indices = [];
11663
- map.set(key, indices);
11664
- }
11665
- for (let i = first; i <= last; i++) indices.push(i);
11666
- }
11667
- function buildGap(entries, start, end, first, last) {
11668
- const entryIndices = [];
11669
- for (let i = first; i <= last; i++) entryIndices.push(i);
11670
- return {
11671
- start,
11672
- end,
11673
- entryIndices,
11674
- hasKind(kind) {
11675
- for (const i of entryIndices) {
11676
- if (entries.kind(i) === kind) return true;
11677
- }
11678
- return false;
11679
- },
11680
- text(input) {
11681
- return input.slice(start, end);
11682
- }
11683
- };
11684
- }
11685
- function buildRootMaps(entries) {
11686
- const before = /* @__PURE__ */ new Map();
11687
- const after = /* @__PURE__ */ new Map();
11688
- const beforeGaps = /* @__PURE__ */ new Map();
11689
- const afterGaps = /* @__PURE__ */ new Map();
11690
- const gaps = [];
11691
- if (entries.length === 0) return { before, after, beforeGaps, afterGaps, gaps };
11692
- const appendGap = (first2, last, start2, end2) => {
11693
- const gap = buildGap(entries, start2, end2, first2, last);
11694
- gaps.push(gap);
11695
- afterGaps.set(start2, gap);
11696
- beforeGaps.set(end2, gap);
11697
- appendEntryRange(after, start2, first2, last);
11698
- appendEntryRange(before, end2, first2, last);
11699
- };
11700
- let first = 0;
11701
- let start = entries.start(0);
11702
- let end = entries.end(0);
11703
- for (let i = 1; i < entries.length; i++) {
11704
- const nextStart = entries.start(i);
11705
- const nextEnd = entries.end(i);
11706
- if (nextStart === end) {
11707
- end = nextEnd;
11708
- continue;
11709
- }
11710
- appendGap(first, i - 1, start, end);
11711
- first = i;
11712
- start = nextStart;
11713
- end = nextEnd;
11714
- }
11715
- appendGap(first, entries.length - 1, start, end);
11716
- return { before, after, beforeGaps, afterGaps, gaps };
11717
- }
11718
- function buildRootTriviaIndex(log, labels) {
11719
- const entries = triviaEntries(log, labels);
11720
- let maps;
11721
- const getMaps = () => {
11722
- maps ??= buildRootMaps(entries);
11723
- return maps;
11724
- };
11725
- return {
11726
- entries,
11727
- labels,
11728
- get before() {
11729
- return getMaps().before;
11730
- },
11731
- get after() {
11732
- return getMaps().after;
11733
- },
11734
- entryIndicesBefore(offset) {
11735
- return getMaps().before.get(offset) ?? EMPTY_INDICES;
11736
- },
11737
- entryIndicesAfter(offset) {
11738
- return getMaps().after.get(offset) ?? EMPTY_INDICES;
11739
- },
11740
- gapBefore(offset) {
11741
- return getMaps().beforeGaps.get(offset);
11742
- },
11743
- gapAfter(offset) {
11744
- return getMaps().afterGaps.get(offset);
11745
- },
11746
- gaps() {
11747
- return getMaps().gaps;
11748
- },
11749
- gapsWithKind(kind) {
11750
- const kinds = typeof kind === "string" ? [kind] : kind;
11751
- const matches2 = [];
11752
- for (const gap of getMaps().gaps) {
11753
- for (const k of kinds) {
11754
- if (gap.hasKind(k)) {
11755
- matches2.push(gap);
11756
- break;
11757
- }
11758
- }
11759
- }
11760
- return matches2.length === 0 ? EMPTY_GAPS : matches2;
11761
- }
11762
- };
11763
- }
11764
-
11765
12156
  // src/functional/run.ts
11766
12157
  var invoke = (r, input, pos, ctx) => typeof r === "function" ? r(input, pos, ctx) : r.parse(input, pos, ctx);
11767
12158
  function triviaKindLabelsFromRunnable(r) {
11768
12159
  if (!r) return void 0;
11769
12160
  if (typeof r === "function") return r._meta?.triviaKindLabels;
11770
12161
  const grammarTrivia = r._meta.grammarTrivia;
11771
- 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;
11772
12176
  }
11773
12177
  function runOnce(entry, input, options, phase, profileState) {
11774
12178
  if (typeof entry !== "function" && typeof entry?.parse !== "function") {
@@ -11776,13 +12180,31 @@ function runOnce(entry, input, options, phase, profileState) {
11776
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).`
11777
12181
  );
11778
12182
  }
11779
- 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;
11780
12187
  const errors = [];
11781
12188
  const profile = profileState ?? (phase === void 0 ? void 0 : { phase, nodes: 0, childSlots: 0, rawSlots: 0, triviaSlots: 0, fieldSlots: 0, hostCalls: 0 });
11782
12189
  const skipGlobalSinks = phase === "recognizer" || phase === "capture";
11783
12190
  const grammarTrivia = typeof entry !== "function" ? entry._meta.grammarTrivia : void 0;
11784
12191
  const grammarScanSkip = typeof entry !== "function" ? entry._meta.grammarScanSkip : void 0;
11785
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
+ }
11786
12208
  const stamped = entry;
11787
12209
  assertHostModeCompatible(
11788
12210
  typeof entry === "function" ? stamped[FUSED_HOST_MODE] ?? "ast" : entry._meta.grammarHostMode ?? "ast",
@@ -11791,7 +12213,15 @@ function runOnce(entry, input, options, phase, profileState) {
11791
12213
  );
11792
12214
  const ctx = {
11793
12215
  trackLines: false,
11794
- ...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
+ },
11795
12225
  build: options.build,
11796
12226
  state: options.state,
11797
12227
  ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...triviaKindLabels ? { triviaKindLabels } : {} } : triviaKindLabels ? { triviaKindLabels } : {},
@@ -11816,9 +12246,13 @@ function runOnce(entry, input, options, phase, profileState) {
11816
12246
  span: r.span ?? { start: 0, end: 0 },
11817
12247
  expected: r.ok ? [] : r.expected ?? [],
11818
12248
  errors,
11819
- triviaLog,
11820
- ...triviaKindLabels === void 0 ? {} : { triviaKindLabels },
11821
- 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
+ } : {},
11822
12256
  unconsumedFrom
11823
12257
  };
11824
12258
  }
@@ -12243,38 +12677,48 @@ function buildTriviaIndex(root, input = "", opts) {
12243
12677
  }
12244
12678
 
12245
12679
  // src/cst/walk.ts
12246
- function walk(root, visitor, ctx = void 0) {
12247
- const go = (node2, parent) => {
12248
- const descend = visitor.enter ? visitor.enter(node2, parent, ctx) : void 0;
12249
- const children2 = node2.children;
12250
- if (descend !== false && Array.isArray(children2)) {
12251
- for (const child of children2) go(child, node2);
12252
- }
12253
- visitor.leave?.(node2, parent, ctx);
12254
- };
12255
- go(root, null);
12256
- }
12257
- function createVisitor(handlers) {
12258
- const api = {
12259
- visit(node2) {
12260
- const handler = node2.type !== void 0 ? handlers[node2.type] : void 0;
12261
- if (handler) return handler(node2, api);
12262
- api.visitChildren(node2);
12263
- return void 0;
12264
- },
12265
- visitChildren(node2) {
12266
- const out = [];
12267
- const children2 = node2.children;
12268
- if (Array.isArray(children2)) {
12269
- for (const child of children2) {
12270
- const r = api.visit(child);
12271
- if (r !== void 0) out.push(r);
12680
+ function isCombinator(value) {
12681
+ return !!value && typeof value === "object" && "_def" in value && "_meta" in value && typeof value.parse === "function";
12682
+ }
12683
+ function reflectionFor(grammar) {
12684
+ const stamped = grammarReflectionOf(grammar);
12685
+ if (stamped) return stamped;
12686
+ const entries = Object.entries(grammar).filter((entry) => isCombinator(entry[1]));
12687
+ return collectGrammarReflection(entries);
12688
+ }
12689
+ function createVisitor(grammar, spec) {
12690
+ const reflection = reflectionFor(grammar);
12691
+ const typeHandlers = spec.type;
12692
+ const tagSpec = spec.tag;
12693
+ const tagHandlersByType = /* @__PURE__ */ new Map();
12694
+ if (tagSpec !== void 0) {
12695
+ for (const node2 of reflection.nodes) {
12696
+ const handlers = [];
12697
+ for (const tag of node2.tags) {
12698
+ const handler = tagSpec[tag];
12699
+ if (handler) handlers.push(handler);
12700
+ }
12701
+ if (handlers.length > 0) tagHandlersByType.set(node2.type, handlers);
12702
+ }
12703
+ }
12704
+ return (root, ctx = void 0) => {
12705
+ const go = (node2, parent) => {
12706
+ const descend = spec.enter ? spec.enter(node2, parent, ctx) : void 0;
12707
+ if (node2._tag === "node" && node2.type !== void 0) {
12708
+ typeHandlers?.[node2.type]?.(node2, parent, ctx);
12709
+ const tagHandlers = tagHandlersByType.get(node2.type);
12710
+ if (tagHandlers !== void 0) {
12711
+ for (const handler of tagHandlers) handler(node2, parent, ctx);
12272
12712
  }
12273
12713
  }
12274
- return out;
12275
- }
12714
+ const children2 = node2.children;
12715
+ if (descend !== false && Array.isArray(children2)) {
12716
+ for (const child of children2) go(child, node2);
12717
+ }
12718
+ spec.leave?.(node2, parent, ctx);
12719
+ };
12720
+ go(root, null);
12276
12721
  };
12277
- return (node2) => api.visit(node2);
12278
12722
  }
12279
12723
 
12280
12724
  // src/cst/offset-model.ts
@@ -12494,6 +12938,7 @@ export {
12494
12938
  buildTriviaIndex,
12495
12939
  charClassMembers,
12496
12940
  choice,
12941
+ classifiedTrivia,
12497
12942
  collectLeafSlots,
12498
12943
  commentsIn,
12499
12944
  compile,
@@ -12571,7 +13016,6 @@ export {
12571
13016
  trivia,
12572
13017
  triviaEntries,
12573
13018
  triviaKindMask,
12574
- walk,
12575
13019
  when,
12576
13020
  withCtx,
12577
13021
  word