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
@@ -1071,1031 +1071,1095 @@ function keywords(words, opts = {}) {
1071
1071
  };
1072
1072
  }
1073
1073
 
1074
- // src/recovery/scan.ts
1075
- function probeAt(sentinel, input, pos, ctx) {
1076
- const s = {
1077
- _probe: ctx._probe,
1078
- _errors: ctx._errors,
1079
- _cstLeaves: ctx._cstLeaves,
1080
- _cstChildren: ctx._cstChildren,
1081
- _cstRawChildren: ctx._cstRawChildren,
1082
- _cstTriviaLog: ctx._cstTriviaLog,
1083
- _triviaLog: ctx._triviaLog,
1084
- _fields: ctx._fields,
1085
- _cstBuf: ctx._cstBuf,
1086
- _tolerant: ctx._tolerant,
1087
- _sync: ctx._sync
1088
- };
1089
- ctx._probe = void 0;
1090
- ctx._errors = void 0;
1091
- ctx._cstLeaves = void 0;
1092
- ctx._cstChildren = void 0;
1093
- ctx._cstRawChildren = void 0;
1094
- ctx._cstTriviaLog = void 0;
1095
- ctx._triviaLog = void 0;
1096
- ctx._fields = void 0;
1097
- ctx._cstBuf = void 0;
1098
- ctx._tolerant = void 0;
1099
- ctx._sync = void 0;
1100
- try {
1101
- return sentinel.parse(input, pos, ctx).ok;
1102
- } finally {
1103
- ctx._probe = s._probe;
1104
- ctx._errors = s._errors;
1105
- ctx._cstLeaves = s._cstLeaves;
1106
- ctx._cstChildren = s._cstChildren;
1107
- ctx._cstRawChildren = s._cstRawChildren;
1108
- ctx._cstTriviaLog = s._cstTriviaLog;
1109
- ctx._triviaLog = s._triviaLog;
1110
- ctx._fields = s._fields;
1111
- ctx._cstBuf = s._cstBuf;
1112
- ctx._tolerant = s._tolerant;
1113
- ctx._sync = s._sync;
1114
- }
1074
+ // src/cst/trivia-kinds.ts
1075
+ var labeledTriviaSpecs = /* @__PURE__ */ new WeakMap();
1076
+ function unwrapTrivia(p) {
1077
+ let cur = p;
1078
+ while (cur._def.tag === "trivia") cur = cur._def.parser;
1079
+ return cur;
1115
1080
  }
1116
- function orSentinel(a, b) {
1117
- if (b === void 0) return a;
1118
- return {
1119
- _tag: "orSentinel",
1120
- _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
1121
- _def: { tag: "unknown" },
1122
- parse(input, pos, ctx) {
1123
- const ra = a.parse(input, pos, ctx);
1124
- if (ra.ok) return ra;
1125
- return b.parse(input, pos, ctx);
1126
- }
1127
- };
1081
+ function peelLabel(p) {
1082
+ if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
1083
+ return null;
1128
1084
  }
1129
- function firstSetSentinel(fs2) {
1130
- if (fs2.kind !== "ranges" || fs2.ranges.length === 0) return null;
1131
- const ranges = fs2.ranges;
1132
- return {
1133
- _tag: "firstSetSentinel",
1134
- _meta: { firstSet: fs2, canMatchNewline: false, isTrivia: false },
1135
- _def: { tag: "unknown" },
1136
- parse(input, pos) {
1137
- const code = pos < input.length ? input.codePointAt(pos) : void 0;
1138
- if (code !== void 0) {
1139
- for (const r of ranges) {
1140
- if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
1141
- }
1085
+ function analyzeLabeledTrivia(trivia2) {
1086
+ const cached = labeledTriviaSpecs.get(trivia2);
1087
+ if (cached !== void 0) return cached;
1088
+ let core = unwrapTrivia(trivia2);
1089
+ let minRepeats = 1;
1090
+ if (core._def.tag === "oneOrMore") {
1091
+ core = core._def.parser;
1092
+ minRepeats = 1;
1093
+ } else if (core._def.tag === "many") {
1094
+ core = core._def.parser;
1095
+ minRepeats = 0;
1096
+ }
1097
+ const arms = [];
1098
+ if (core._def.tag === "choice") {
1099
+ for (let i = 0; i < core._def.parsers.length; i++) {
1100
+ const peeled = peelLabel(core._def.parsers[i]);
1101
+ if (!peeled) {
1102
+ labeledTriviaSpecs.set(trivia2, null);
1103
+ return null;
1142
1104
  }
1143
- return { ok: false, expected: [], span: { start: pos, end: pos } };
1105
+ arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
1106
+ }
1107
+ } else {
1108
+ const peeled = peelLabel(core);
1109
+ if (!peeled) {
1110
+ labeledTriviaSpecs.set(trivia2, null);
1111
+ return null;
1144
1112
  }
1113
+ arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
1114
+ }
1115
+ const spec = {
1116
+ labels: arms.map((a) => a.label),
1117
+ arms,
1118
+ minRepeats
1145
1119
  };
1120
+ labeledTriviaSpecs.set(trivia2, spec);
1121
+ return spec;
1146
1122
  }
1147
- function recoverScan(input, from, ctx, sync, expected) {
1148
- let scanPos = from;
1149
- while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1150
- const span = { start: from, end: scanPos };
1151
- if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
1152
- const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1153
- ctx._errors?.push(error);
1154
- return { error, end: scanPos };
1155
- }
1156
- function matchesAt(sentinel, input, pos, ctx) {
1157
- return probeAt(sentinel, input, pos, ctx);
1123
+ function matchArmAt(input, pos, arm, state) {
1124
+ if (!startsFirstSet(arm, input, pos)) return null;
1125
+ const r = arm.parse(input, pos, { trackLines: false, state });
1126
+ if (!r.ok || r.span.end <= pos) return null;
1127
+ return { end: r.span.end };
1158
1128
  }
1159
- function captureError(ctx, error) {
1160
- if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
1129
+ function scanLabeledTriviaChunks(input, cur, spec, state) {
1130
+ const chunks = [];
1131
+ let pos = cur;
1132
+ while (pos < input.length) {
1133
+ let matched = null;
1134
+ for (const arm of spec.arms) {
1135
+ const m = matchArmAt(input, pos, arm.parser, state);
1136
+ if (m) {
1137
+ matched = { end: m.end, kindIndex: arm.kindIndex };
1138
+ break;
1139
+ }
1140
+ }
1141
+ if (!matched) break;
1142
+ chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
1143
+ pos = matched.end;
1144
+ }
1145
+ if (chunks.length < spec.minRepeats) {
1146
+ return { end: cur, chunks: [] };
1147
+ }
1148
+ return { end: pos, chunks };
1161
1149
  }
1162
-
1163
- // src/combinators/expect.ts
1164
- function deriveExpected(c) {
1165
- return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
1150
+ function scanLabeledTriviaEnd(input, cur, spec, state) {
1151
+ let pos = cur;
1152
+ let count = 0;
1153
+ while (pos < input.length) {
1154
+ let matched = null;
1155
+ for (const arm of spec.arms) {
1156
+ matched = matchArmAt(input, pos, arm.parser, state);
1157
+ if (matched) break;
1158
+ }
1159
+ if (!matched) break;
1160
+ pos = matched.end;
1161
+ count++;
1162
+ }
1163
+ return count < spec.minRepeats ? cur : pos;
1166
1164
  }
1167
- function derive(c, seen) {
1168
- const deriveExpected2 = (p) => derive(p, seen);
1169
- const def = c._def;
1170
- switch (def.tag) {
1171
- case "literal":
1172
- return [JSON.stringify(def.value)];
1173
- case "regex":
1174
- return [`/${def.source}/`];
1175
- case "keywords":
1176
- return def.words.map((w) => JSON.stringify(w));
1177
- case "label":
1178
- return [def.label];
1179
- case "choice":
1180
- return def.parsers.flatMap(deriveExpected2);
1181
- case "dispatch":
1182
- return deriveExpected2(def.selector);
1183
- // Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
1184
- // `optional(…)`/`many(…)` can match nothing, so the term after it is equally
1185
- // able to fail first — deriving from term 0 alone named a token the parse
1186
- // never actually required. A leading `not(…)` is zero-width and contributes
1187
- // nothing (its own expectation is about what must NOT be here), but it is
1188
- // nullable, so keep scanning past it. Stop at the first term that must match.
1189
- case "sequence": {
1190
- const out = [];
1191
- for (const term of def.parsers) {
1192
- if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
1193
- if (!matchesEmpty(term)) break;
1165
+ function visitLabeledTrivia(input, cur, spec, state, visit) {
1166
+ if (spec.minRepeats > 1) return void 0;
1167
+ let pos = cur;
1168
+ let count = 0;
1169
+ while (pos < input.length) {
1170
+ let matched = null;
1171
+ for (const arm of spec.arms) {
1172
+ const match = matchArmAt(input, pos, arm.parser, state);
1173
+ if (match) {
1174
+ matched = { end: match.end, kindIndex: arm.kindIndex };
1175
+ break;
1194
1176
  }
1195
- return out;
1196
1177
  }
1197
- case "attempt":
1198
- return deriveExpected2(def.parser);
1199
- // Delegating wrappers whose first-token failure is their inner parser's — must
1200
- // pass through so a start-failure (and the first-set-miss fast-path in attempt/
1201
- // node, which reads this) names the real expected token, not the wrapper tag.
1202
- case "field":
1203
- case "withCtx":
1204
- case "recover":
1205
- return deriveExpected2(def.parser);
1206
- case "skip":
1207
- return deriveExpected2(def.main);
1208
- case "expect":
1209
- return def.expected;
1210
- case "node":
1211
- case "grammar":
1212
- case "trivia":
1213
- case "token":
1214
- case "leaf":
1215
- case "optional":
1216
- case "many":
1217
- case "oneOrMore":
1218
- case "transform":
1219
- case "not":
1220
- case "peek":
1221
- return deriveExpected2(def.parser);
1222
- case "routed":
1223
- return ["routed()"];
1224
- case "lazy": {
1225
- const name = c._ruleName;
1226
- if (seen.has(c)) return name ? [name] : [];
1227
- seen.add(c);
1228
- try {
1229
- return deriveExpected2(def.thunk());
1230
- } catch {
1231
- return name ? [name] : [];
1232
- } finally {
1233
- seen.delete(c);
1178
+ if (!matched) break;
1179
+ visit(pos, matched.end, matched.kindIndex);
1180
+ pos = matched.end;
1181
+ count++;
1182
+ }
1183
+ return count < spec.minRepeats ? cur : pos;
1184
+ }
1185
+ function recordTriviaChunks(ctx, chunks) {
1186
+ const kinds = ctx.triviaKindLabels;
1187
+ const mask = ctx._triviaCaptureMask;
1188
+ const rootLog = ctx._rootTriviaLog;
1189
+ const rootKinds = ctx._rootTriviaKindIndex;
1190
+ const rootMark = rootLog?.length ?? 0;
1191
+ for (const ch2 of chunks) {
1192
+ pushTriviaLogEntry(ctx, ch2.start, ch2.end, kinds ? ch2.kindIndex : void 0);
1193
+ const rootKindIndex = ctx._rootTriviaCapture === false || rootLog === void 0 || rootKinds === void 0 || kinds === void 0 ? -1 : rootKinds[kinds[ch2.kindIndex] ?? ""] ?? -1;
1194
+ if (rootKindIndex >= 0) {
1195
+ rootLog.push(0, 0, ch2.start, ch2.end, rootKindIndex);
1196
+ }
1197
+ if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
1198
+ if (mask === void 0 || kinds === void 0 || (mask & 1 << ch2.kindIndex) !== 0) {
1199
+ pushCstTriviaEntry(ctx, ch2.start, ch2.end, kinds ? ch2.kindIndex : void 0);
1234
1200
  }
1235
1201
  }
1236
- default:
1237
- return [];
1238
1202
  }
1239
- }
1240
- function expect(combinator, label2) {
1241
- const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
1242
- const meta = {
1243
- firstSet: combinator._meta.firstSet,
1244
- canMatchNewline: combinator._meta.canMatchNewline,
1245
- isTrivia: false
1246
- };
1247
- return {
1248
- _tag: "expect",
1249
- _meta: meta,
1250
- _def: { tag: "expect", parser: combinator, label: label2, expected },
1251
- parse(input, pos, ctx) {
1252
- const result = combinator.parse(input, pos, ctx);
1253
- if (result.ok) return result;
1254
- const span = { start: pos, end: pos };
1255
- const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1256
- ctx._errors?.push(error);
1257
- if (ctx._tolerant) captureError(ctx, error);
1258
- return { ok: true, value: error, span: { start: pos, end: pos } };
1203
+ if (rootLog !== void 0 && rootLog.length !== rootMark && chunks.length > 0) {
1204
+ const start = chunks[0].start;
1205
+ const end = chunks[chunks.length - 1].end;
1206
+ for (let offset = rootMark; offset < rootLog.length; offset += 5) {
1207
+ rootLog[offset] = start;
1208
+ rootLog[offset + 1] = end;
1259
1209
  }
1260
- };
1210
+ }
1261
1211
  }
1262
-
1263
- // src/combinators/choice.ts
1264
- function choice(...args) {
1265
- const parsers = args.map((a) => "gate" in a ? a.combinator : a);
1266
- const gates = args.map((a) => "gate" in a ? a.gate : null);
1267
- const hasGates = gates.some((g) => g !== null);
1268
- const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
1269
- let combined = { kind: "empty" };
1270
- for (const p of parsers) combined = union(combined, p._meta.firstSet);
1271
- const meta = {
1272
- firstSet: combined,
1273
- canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
1274
- isTrivia: false,
1275
- disjoint
1276
- };
1277
- const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
1278
- const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
1279
- let greedyLitMap = null;
1280
- let sortedParsers = null;
1281
- const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
1282
- if (strategy?.tag === "greedyClassify") {
1283
- greedyLitMap = /* @__PURE__ */ new Map();
1284
- for (let i = 0; i < parsers.length; i++) {
1285
- if (i === strategy.superIndex) continue;
1286
- const litVal = getCoreLiteralValue(parsers[i]);
1287
- if (litVal !== null) greedyLitMap.set(litVal, i);
1288
- }
1289
- } else if (strategy?.tag === "literalsLongestFirst") {
1290
- sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
1212
+ function triviaKindMask(labels, keep) {
1213
+ if (!labels) return void 0;
1214
+ let mask = 0;
1215
+ for (const name of keep) {
1216
+ const idx = labels.indexOf(name);
1217
+ if (idx >= 0) mask |= 1 << idx;
1291
1218
  }
1219
+ return mask;
1220
+ }
1221
+
1222
+ // src/combinators/trivia-skip.ts
1223
+ var NOOP_COMMIT = () => {
1224
+ };
1225
+ var fastTriviaCache = /* @__PURE__ */ new WeakMap();
1226
+ function needsDeferredTriviaCommit(ctx) {
1227
+ return ctx._triviaLog !== void 0 || ctx._rootTriviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
1228
+ }
1229
+ function saveTriviaMark(ctx) {
1230
+ const m = saveCstMark(ctx);
1292
1231
  return {
1293
- _tag: "choice",
1294
- _meta: meta,
1295
- _def: {
1296
- tag: "choice",
1297
- parsers,
1298
- gates,
1299
- disjoint,
1300
- strategy: strategy ?? { tag: "firstMatch" },
1301
- autoNot
1302
- },
1303
- parse(input, pos, ctx) {
1304
- const expected = [];
1305
- if (disjoint && pos < input.length) {
1306
- const code = input.codePointAt(pos);
1307
- let idx = code < 128 ? asciiDispatch[code] : -1;
1308
- if (idx < 0) {
1309
- for (let i = 0; i < parsers.length; i++) {
1310
- if (inFirstSet(code, parsers[i]._meta.firstSet)) {
1311
- idx = i;
1312
- break;
1313
- }
1232
+ raw: m.raw,
1233
+ tlog: m.tlog,
1234
+ leaves: m.leaves,
1235
+ fields: m.fields,
1236
+ errors: m.errors,
1237
+ log: ctx._triviaLog ? ctx._triviaLog.length : 0,
1238
+ rootLog: ctx._rootTriviaLog ? ctx._rootTriviaLog.length : 0
1239
+ };
1240
+ }
1241
+ function rollbackTrivia(ctx, mark) {
1242
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1243
+ if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1244
+ if (ctx._rootTriviaLog && ctx._rootTriviaLog.length !== mark.rootLog) ctx._rootTriviaLog.length = mark.rootLog;
1245
+ }
1246
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1247
+ const probeCtx = {
1248
+ trackLines: ctx.trackLines,
1249
+ state: ctx.state,
1250
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1251
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1252
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1253
+ };
1254
+ const result = triviaP.parse(input, cur, probeCtx);
1255
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1256
+ return result;
1257
+ }
1258
+ function saveLookaheadMark(ctx) {
1259
+ return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1260
+ }
1261
+ function rollbackLookahead(ctx, mark) {
1262
+ rollbackTrivia(ctx, mark);
1263
+ if (ctx._probe) ctx._probe.best = mark.probeBest;
1264
+ }
1265
+ function scanWithLabels(input, cur, ctx) {
1266
+ const triviaP = ctx.trivia;
1267
+ const spec = analyzeLabeledTrivia(triviaP);
1268
+ if (!spec) return { end: cur, commit: NOOP_COMMIT };
1269
+ const log = ctx._triviaLog;
1270
+ const rootLog = ctx._rootTriviaLog;
1271
+ const rootKinds = ctx._rootTriviaKindIndex;
1272
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1273
+ const mask = ctx._triviaCaptureMask;
1274
+ let fullRows;
1275
+ let rootRows;
1276
+ let cstRows;
1277
+ const visitedEnd = visitLabeledTrivia(input, cur, spec, ctx.state, (start, matchEnd, kindIndex) => {
1278
+ if (log !== void 0) (fullRows ??= []).push(start, matchEnd, kindIndex);
1279
+ const selectedKind = rootLog === void 0 || rootKinds === void 0 || ctx._rootTriviaCapture === false ? void 0 : rootKinds[spec.labels[kindIndex] ?? ""];
1280
+ if (selectedKind !== void 0) (rootRows ??= []).push(start, matchEnd, selectedKind);
1281
+ if (captureTl && (mask === void 0 || (mask & 1 << kindIndex) !== 0)) {
1282
+ (cstRows ??= []).push(start, matchEnd, kindIndex);
1283
+ }
1284
+ });
1285
+ if (visitedEnd !== void 0) {
1286
+ if (visitedEnd === cur) return { end: cur, commit: NOOP_COMMIT };
1287
+ return {
1288
+ end: visitedEnd,
1289
+ commit: () => {
1290
+ if (fullRows !== void 0 && log !== void 0) log.push(...fullRows);
1291
+ if (rootRows !== void 0 && rootLog !== void 0) {
1292
+ for (let i = 0; i < rootRows.length; i += 3) {
1293
+ rootLog.push(cur, visitedEnd, rootRows[i], rootRows[i + 1], rootRows[i + 2]);
1314
1294
  }
1315
1295
  }
1316
- if (idx >= 0) {
1317
- const gate2 = gates[idx];
1318
- if (gate2 && !gate2(ctx.state)) {
1319
- return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
1296
+ if (cstRows !== void 0) {
1297
+ for (let i = 0; i < cstRows.length; i += 3) {
1298
+ pushCstTriviaEntry(ctx, cstRows[i], cstRows[i + 1], cstRows[i + 2]);
1320
1299
  }
1321
- const result = parsers[idx].parse(input, pos, ctx);
1322
- if (result.ok) return result;
1323
- expected.push(...result.expected);
1324
- if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
1325
- return { ok: false, expected, span: { start: pos, end: pos } };
1326
- }
1327
- return {
1328
- ok: false,
1329
- expected: parsers.flatMap((p) => {
1330
- const r = p.parse(input, pos, ctx);
1331
- return r.ok ? [] : r.expected;
1332
- }),
1333
- span: { start: pos, end: pos }
1334
- };
1335
- }
1336
- if (strategy?.tag === "greedyClassify") {
1337
- const superResult = parsers[strategy.superIndex].parse(input, pos, ctx);
1338
- if (!superResult.ok) return superResult;
1339
- const end = superResult.span.end;
1340
- const litIdx = greedyLitMap.get(input.slice(pos, end));
1341
- if (litIdx !== void 0) {
1342
- const litVal = getCoreLiteralValue(parsers[litIdx]);
1343
- const value = applyTransforms(parsers[litIdx], litVal, { start: pos, end });
1344
- return { ok: true, value, span: { start: pos, end } };
1345
- }
1346
- return superResult;
1347
- }
1348
- if (strategy?.tag === "literalsLongestFirst") {
1349
- for (const p of sortedParsers) {
1350
- const r = p.parse(input, pos, ctx);
1351
- if (r.ok) return r;
1352
- expected.push(...r.expected);
1353
- }
1354
- return { ok: false, expected, span: { start: pos, end: pos } };
1355
- }
1356
- for (let i = 0; i < parsers.length; i++) {
1357
- if (gates[i] && !gates[i](ctx.state)) continue;
1358
- const mark = saveCstMark(ctx);
1359
- const logLen = ctx._triviaLog?.length;
1360
- const result = parsers[i].parse(input, pos, ctx);
1361
- if (!result.ok) {
1362
- rollbackCstCapture(ctx, mark);
1363
- if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
1364
- expected.push(...result.expected);
1365
- if (result.committed) return { ok: false, expected, span: result.span, committed: true };
1366
- continue;
1367
1300
  }
1368
- const checks = autoNot[i];
1369
- if (checks && autoNotFires(input, result.span.end, checks)) {
1370
- rollbackCstCapture(ctx, mark);
1371
- if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
1372
- continue;
1373
- }
1374
- return result;
1375
1301
  }
1376
- return { ok: false, expected, span: { start: pos, end: pos } };
1377
- }
1302
+ };
1303
+ }
1304
+ const { end, chunks } = scanLabeledTriviaChunks(input, cur, spec, ctx.state);
1305
+ if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1306
+ return {
1307
+ end,
1308
+ commit: () => recordTriviaChunks(ctx, chunks)
1378
1309
  };
1379
1310
  }
1380
- function detectStrategy(parsers) {
1381
- const regexIndices = [];
1382
- const literalIndices = [];
1383
- for (let i = 0; i < parsers.length; i++) {
1384
- if (getCoreRegexDef(parsers[i]) !== null) regexIndices.push(i);
1385
- else if (getCoreLiteralValue(parsers[i]) !== null) literalIndices.push(i);
1386
- }
1387
- if (regexIndices.length === 1 && literalIndices.length === parsers.length - 1 && literalIndices.length > 0) {
1388
- const superIndex = regexIndices[0];
1389
- const regexDef = getCoreRegexDef(parsers[superIndex]);
1390
- const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
1391
- const re = new RegExp(regexDef.source, flags);
1392
- const allSubsumed = literalIndices.every((i) => {
1393
- const litVal = getCoreLiteralValue(parsers[i]);
1394
- re.lastIndex = 0;
1395
- const m = re.exec(litVal);
1396
- return m !== null && m[0] === litVal;
1397
- });
1398
- if (allSubsumed) return { tag: "greedyClassify", superIndex };
1311
+ function skipWithLabels(input, cur, ctx) {
1312
+ const spec = analyzeLabeledTrivia(ctx.trivia);
1313
+ if (spec) return scanLabeledTriviaEnd(input, cur, spec, ctx.state);
1314
+ const tr = parseTriviaNoCapture(ctx.trivia, input, cur, ctx);
1315
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1316
+ }
1317
+ function advanceTrivia(input, cur, ctx) {
1318
+ const triviaP = ctx.trivia;
1319
+ if (!triviaP) return cur;
1320
+ if (!ctx.trackLines) {
1321
+ const fast2 = fastTriviaScanner(triviaP);
1322
+ if (fast2) return fast2(input, cur);
1323
+ if (ctx.triviaKindLabels) return skipWithLabels(input, cur, ctx);
1324
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1325
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1399
1326
  }
1400
- if (parsers.length === literalIndices.length) {
1401
- const sortedIndices = [...literalIndices].sort(
1402
- (a, b) => getCoreLiteralValue(parsers[b]).length - getCoreLiteralValue(parsers[a]).length
1403
- );
1404
- return { tag: "literalsLongestFirst", sortedIndices };
1327
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1328
+ const fast = fastTriviaScanner(triviaP);
1329
+ if (fast) {
1330
+ const end = fast(input, cur);
1331
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1332
+ return end;
1405
1333
  }
1406
- const shared = detectSharedPrefix(parsers);
1407
- if (shared !== null) return shared;
1408
- return { tag: "firstMatch" };
1409
- }
1410
- function bareLeadingTermKey(term) {
1411
- const d = term._def;
1412
- if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
1413
- if (d.tag === "regex") return `R:${d.source}\0${d.flags}`;
1414
- return null;
1415
- }
1416
- function detectSharedPrefix(parsers) {
1417
- if (parsers.length < 2) return null;
1418
- let key = null;
1419
- let prefix = null;
1420
- const members = [];
1421
- for (let i = 0; i < parsers.length; i++) {
1422
- const term0 = leadingTermOfArm(parsers[i]);
1423
- if (term0 === null) return null;
1424
- const k = bareLeadingTermKey(term0);
1425
- if (k === null) return null;
1426
- if (key === null) {
1427
- key = k;
1428
- prefix = term0;
1429
- } else if (k !== key) return null;
1430
- members.push(i);
1334
+ if (ctx.triviaKindLabels) {
1335
+ const end = skipWithLabels(input, cur, ctx);
1336
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1337
+ return end;
1431
1338
  }
1432
- if (prefix === null || members.length < 2) return null;
1433
- return { tag: "sharedPrefix", prefix, members };
1339
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1340
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1434
1341
  }
1435
- function leadingTermOfArm(arm) {
1436
- let d = arm._def;
1437
- for (; ; ) {
1438
- if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
1439
- d = d.parser._def;
1440
- continue;
1342
+ function scanTrivia(input, cur, ctx) {
1343
+ const triviaP = ctx.trivia;
1344
+ if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1345
+ const log = ctx._triviaLog;
1346
+ const rootLog = ctx._rootTriviaLog;
1347
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1348
+ if (!ctx.trackLines) {
1349
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1350
+ if (fast2 && log === void 0 && !captureTl) {
1351
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1441
1352
  }
1442
- break;
1443
- }
1444
- if (d.tag !== "sequence" || d.parsers.length < 2) return null;
1445
- return d.parsers[0];
1446
- }
1447
- function computeAutoNot(parsers) {
1448
- return parsers.map((p, i) => {
1449
- const litVal = getCoreLiteralValue(p);
1450
- if (litVal === null) return null;
1451
- const checks = [];
1452
- for (let j = i + 1; j < parsers.length; j++) {
1453
- const other = parsers[j];
1454
- const otherLit = getCoreLiteralValue(other);
1455
- if (otherLit !== null && otherLit.startsWith(litVal) && otherLit.length > litVal.length) {
1456
- checks.push({ kind: "startsWith", value: otherLit.slice(litVal.length) });
1457
- continue;
1458
- }
1459
- const regexDef = getCoreRegexDef(other);
1460
- if (regexDef !== null) {
1461
- const contSet = continuationFirstSet(litVal, regexDef.source, regexDef.flags);
1462
- if (contSet !== null) checks.push({ kind: "firstSet", set: contSet });
1463
- }
1353
+ if (ctx.triviaKindLabels && (log !== void 0 || rootLog !== void 0 || captureTl)) {
1354
+ return scanWithLabels(input, cur, ctx);
1464
1355
  }
1465
- return checks.length > 0 ? checks : null;
1466
- });
1467
- }
1468
- function autoNotFires(input, end, checks) {
1469
- for (const check of checks) {
1470
- if (check.kind === "firstSet") {
1471
- const code = end < input.length ? input.codePointAt(end) ?? -1 : -1;
1472
- if (inFirstSet(code, check.set)) return true;
1473
- } else {
1474
- if (input.startsWith(check.value, end)) return true;
1356
+ if (ctx.triviaKindLabels) return { end: skipWithLabels(input, cur, ctx), commit: NOOP_COMMIT };
1357
+ if (log !== void 0 || rootLog !== void 0 || captureTl) {
1358
+ const tr3 = triviaP.parse(input, cur, {
1359
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1360
+ state: ctx.state
1361
+ });
1362
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1363
+ const end = tr3.span.end;
1364
+ return {
1365
+ end,
1366
+ commit: () => {
1367
+ pushTriviaLogEntry(ctx, cur, end);
1368
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1369
+ }
1370
+ };
1475
1371
  }
1372
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1373
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1476
1374
  }
1477
- return false;
1478
- }
1479
- function getCoreLiteralValue(p) {
1480
- const def = p._def;
1481
- if (def.tag === "literal" && !def.caseInsensitive) return def.value;
1482
- if (def.tag === "transform") return getCoreLiteralValue(def.parser);
1483
- return null;
1484
- }
1485
- function getCoreRegexDef(p) {
1486
- const def = p._def;
1487
- if (def.tag === "regex") return { source: def.source, flags: def.flags };
1488
- if (def.tag === "transform") return getCoreRegexDef(def.parser);
1489
- if (def.tag === "label") return getCoreRegexDef(def.parser);
1490
- return null;
1491
- }
1492
- function applyTransforms(p, value, span) {
1493
- const def = p._def;
1494
- if (def.tag === "transform") {
1495
- const inner = applyTransforms(def.parser, value, span);
1496
- return def.fn(inner, span);
1375
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1376
+ const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1377
+ if (fast && log === void 0 && !captureTl) {
1378
+ const end = fast(input, cur);
1379
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1380
+ return { end, commit: NOOP_COMMIT };
1497
1381
  }
1498
- return value;
1499
- }
1500
- function continuationFirstSet(lit, source, flags) {
1501
- const re = new RegExp(source, "y" + flags.replace(/[gy]/g, ""));
1502
- re.lastIndex = 0;
1503
- const base = re.exec(lit);
1504
- if (!base || base[0] !== lit) return null;
1505
- const contCodes = [];
1506
- for (let code = 1; code < 128; code++) {
1507
- re.lastIndex = 0;
1508
- const m = re.exec(lit + String.fromCharCode(code));
1509
- if (m && m[0].length > lit.length) contCodes.push(code);
1382
+ if (ctx.triviaKindLabels && (log !== void 0 || rootLog !== void 0 || captureTl)) {
1383
+ const scan = scanWithLabels(input, cur, ctx);
1384
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1385
+ return scan;
1510
1386
  }
1511
- if (contCodes.length === 0) return null;
1512
- return codesToFirstSet(contCodes);
1513
- }
1514
- function codesToFirstSet(codes) {
1515
- codes.sort((a, b) => a - b);
1516
- const ranges = [];
1517
- let lo = codes[0], hi = codes[0];
1518
- for (let i = 1; i < codes.length; i++) {
1519
- if (codes[i] === hi + 1) {
1520
- hi = codes[i];
1521
- } else {
1522
- ranges.push({ lo, hi });
1523
- lo = hi = codes[i];
1524
- }
1387
+ if (ctx.triviaKindLabels) {
1388
+ const end = skipWithLabels(input, cur, ctx);
1389
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1390
+ return { end, commit: NOOP_COMMIT };
1525
1391
  }
1526
- ranges.push({ lo, hi });
1527
- return { kind: "ranges", ranges };
1392
+ if (log !== void 0 || rootLog !== void 0 || captureTl) {
1393
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1394
+ if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1395
+ const end = tr2.span.end;
1396
+ return {
1397
+ end,
1398
+ commit: () => {
1399
+ pushTriviaLogEntry(ctx, cur, end);
1400
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1401
+ }
1402
+ };
1403
+ }
1404
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1405
+ return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1528
1406
  }
1529
- function inFirstSet(code, fs2) {
1530
- if (fs2.kind === "any") return true;
1531
- if (fs2.kind === "empty") return false;
1532
- for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
1533
- return false;
1407
+ function consumeTrivia(input, cur, ctx) {
1408
+ if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1409
+ const scan = scanTrivia(input, cur, ctx);
1410
+ scan.commit();
1411
+ return scan.end;
1534
1412
  }
1535
- function areDisjoint(sets) {
1536
- if (sets.some((s) => s.kind === "any")) return false;
1537
- for (let i = 0; i < sets.length; i++)
1538
- for (let j = i + 1; j < sets.length; j++)
1539
- if (intersects(sets[i], sets[j])) return false;
1540
- return true;
1413
+ function fastTriviaScanner(trivia2) {
1414
+ const cached = fastTriviaCache.get(trivia2);
1415
+ if (cached !== void 0) return cached;
1416
+ const scanner = buildFastTriviaScanner(trivia2);
1417
+ fastTriviaCache.set(trivia2, scanner);
1418
+ return scanner;
1541
1419
  }
1542
- function buildAsciiDispatch(parsers) {
1543
- const table = Array(128).fill(-1);
1544
- for (let i = 0; i < parsers.length; i++) {
1545
- const fs2 = parsers[i]._meta.firstSet;
1546
- if (fs2.kind !== "ranges") continue;
1547
- for (const { lo, hi } of fs2.ranges) {
1548
- for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
1549
- table[code] = i;
1420
+ function buildFastTriviaScanner(trivia2) {
1421
+ const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1422
+ const direct = regexTriviaScanner(core);
1423
+ if (direct) return direct;
1424
+ const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1425
+ if (!repeat) return null;
1426
+ const one = regexTriviaScanner(repeat);
1427
+ if (one) return loopScanner([one]);
1428
+ if (repeat._def.tag !== "choice") return null;
1429
+ const arms = repeat._def.parsers.map(regexTriviaScanner);
1430
+ if (arms.some((s) => s === null)) return null;
1431
+ return loopScanner(arms);
1432
+ }
1433
+ function loopScanner(arms) {
1434
+ return (input, cur) => {
1435
+ let pos = cur;
1436
+ scan: while (pos < input.length) {
1437
+ for (const arm of arms) {
1438
+ const end = arm(input, pos);
1439
+ if (end > pos) {
1440
+ pos = end;
1441
+ continue scan;
1442
+ }
1550
1443
  }
1444
+ break;
1551
1445
  }
1552
- }
1553
- return table;
1554
- }
1555
-
1556
- // src/cst/trivia-kinds.ts
1557
- function unwrapTrivia(p) {
1558
- let cur = p;
1559
- while (cur._def.tag === "trivia") cur = cur._def.parser;
1560
- return cur;
1446
+ return pos;
1447
+ };
1561
1448
  }
1562
- function peelLabel(p) {
1563
- if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
1564
- return null;
1449
+ function regexTriviaScanner(parser2) {
1450
+ if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1451
+ const source = parser2._def.source;
1452
+ return classRunSource(source) ?? altStarSource(source);
1565
1453
  }
1566
- function analyzeLabeledTrivia(trivia2) {
1567
- let core = unwrapTrivia(trivia2);
1568
- let minRepeats = 1;
1569
- if (core._def.tag === "oneOrMore") {
1570
- core = core._def.parser;
1571
- minRepeats = 1;
1572
- } else if (core._def.tag === "many") {
1573
- core = core._def.parser;
1574
- minRepeats = 0;
1575
- }
1576
- const arms = [];
1577
- if (core._def.tag === "choice") {
1578
- for (let i = 0; i < core._def.parsers.length; i++) {
1579
- const peeled = peelLabel(core._def.parsers[i]);
1580
- if (!peeled) return null;
1581
- arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
1582
- }
1583
- } else {
1584
- const peeled = peelLabel(core);
1585
- if (!peeled) return null;
1586
- arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
1454
+ function inRanges2(cp, ranges) {
1455
+ for (let i = 0; i < ranges.length; i++) {
1456
+ const r = ranges[i];
1457
+ if (cp >= r[0] && cp <= r[1]) return true;
1587
1458
  }
1588
- return { labels: arms.map((a) => a.label), arms, minRepeats };
1459
+ return false;
1589
1460
  }
1590
- function matchArmAt(input, pos, arm) {
1591
- const r = arm.parse(input, pos, { trackLines: false });
1592
- if (!r.ok || r.span.end <= pos) return null;
1593
- return { end: r.span.end };
1461
+ function classScanner(classBody) {
1462
+ const ranges = parseClassRanges(classBody);
1463
+ if (!ranges) return null;
1464
+ return (input, cur) => {
1465
+ let pos = cur;
1466
+ while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1467
+ return pos;
1468
+ };
1594
1469
  }
1595
- function scanLabeledTriviaChunks(input, cur, spec) {
1596
- const chunks = [];
1597
- let pos = cur;
1598
- while (pos < input.length) {
1599
- let matched = null;
1600
- for (const arm of spec.arms) {
1601
- const m = matchArmAt(input, pos, arm.parser);
1602
- if (m) {
1603
- matched = { end: m.end, kindIndex: arm.kindIndex };
1604
- break;
1605
- }
1470
+ function untilLineBreakScanner(leaderCode) {
1471
+ return (input, cur) => {
1472
+ if (input.charCodeAt(cur) !== leaderCode) return cur;
1473
+ let pos = cur + 1;
1474
+ while (pos < input.length) {
1475
+ const cc = input.charCodeAt(pos);
1476
+ if (cc === 10 || cc === 13) break;
1477
+ pos++;
1606
1478
  }
1607
- if (!matched) break;
1608
- chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
1609
- pos = matched.end;
1479
+ return pos;
1480
+ };
1481
+ }
1482
+ function classRunSource(source) {
1483
+ const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1484
+ return m ? classScanner(m[1]) : null;
1485
+ }
1486
+ function classifyTriviaArm(arm) {
1487
+ const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1488
+ if (cls) {
1489
+ const ranges = parseClassRanges(cls[1]);
1490
+ return ranges ? { kind: "class", ranges } : null;
1610
1491
  }
1611
- if (chunks.length < spec.minRepeats) {
1612
- return { end: cur, chunks: [] };
1492
+ const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1493
+ if (lc) {
1494
+ const marker = lc[1];
1495
+ return { kind: "untilLineBreak", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
1613
1496
  }
1614
- return { end: pos, chunks };
1497
+ return null;
1615
1498
  }
1616
- function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
1617
- const chunks = [];
1618
- let pos = cur;
1619
- while (pos < input.length) {
1620
- const c = input.charCodeAt(pos);
1621
- if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {
1622
- const start = pos;
1623
- pos++;
1624
- while (pos < input.length) {
1625
- const c2 = input.charCodeAt(pos);
1626
- if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) pos++;
1627
- else break;
1628
- }
1629
- chunks.push({ start, end: pos, kindIndex: wsKind });
1630
- continue;
1631
- }
1632
- if (c === 47 && input.charCodeAt(pos + 1) === 42) {
1633
- let j = pos + 2;
1634
- while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++;
1635
- if (j + 1 < input.length && input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47) {
1636
- const start = pos;
1637
- pos = j + 2;
1638
- chunks.push({ start, end: pos, kindIndex: commentKind });
1499
+ function armScanner(arm) {
1500
+ if (arm.kind === "untilLineBreak") return untilLineBreakScanner(arm.code);
1501
+ const ranges = arm.ranges;
1502
+ return (input, cur) => {
1503
+ let pos = cur;
1504
+ while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1505
+ return pos;
1506
+ };
1507
+ }
1508
+ function fusedTriviaScanner(ranges, leaders) {
1509
+ const c0 = leaders[0];
1510
+ const single = leaders.length === 1;
1511
+ return (input, cur) => {
1512
+ let pos = cur;
1513
+ const len = input.length;
1514
+ for (; ; ) {
1515
+ const c = input.charCodeAt(pos);
1516
+ if (pos < len && inRanges2(c, ranges)) {
1517
+ pos++;
1518
+ continue;
1519
+ }
1520
+ if (single ? c === c0 : leaders.includes(c)) {
1521
+ pos++;
1522
+ while (pos < len) {
1523
+ const cc = input.charCodeAt(pos);
1524
+ if (cc === 10 || cc === 13) break;
1525
+ pos++;
1526
+ }
1639
1527
  continue;
1640
1528
  }
1641
1529
  break;
1642
1530
  }
1643
- break;
1644
- }
1645
- return { end: pos, chunks };
1646
- }
1647
- function tryFastLabeledScan(input, cur, trivia2) {
1648
- const spec = analyzeLabeledTrivia(trivia2);
1649
- if (!spec || spec.arms.length !== 2) return null;
1650
- const wsArm = spec.arms.find((a) => {
1651
- const src = getCoreRegexDef(a.parser)?.source;
1652
- return src != null && !src.includes("\\*");
1653
- });
1654
- const commentArm = spec.arms.find((a) => {
1655
- const src = getCoreRegexDef(a.parser)?.source;
1656
- return src != null && src.includes("\\*");
1657
- });
1658
- if (!wsArm || !commentArm) return null;
1659
- const { chunks } = scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1660
- if (chunks.length < spec.minRepeats) return { end: cur, chunks: [] };
1661
- return scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1531
+ return pos;
1532
+ };
1662
1533
  }
1663
- function recordTriviaChunks(ctx, chunks) {
1664
- const kinds = ctx.triviaKindLabels;
1665
- const mask = ctx._triviaCaptureMask;
1666
- for (const ch2 of chunks) {
1667
- pushTriviaLogEntry(ctx, ch2.start, ch2.end, kinds ? ch2.kindIndex : void 0);
1668
- if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
1669
- if (mask === void 0 || kinds === void 0 || (mask & 1 << ch2.kindIndex) !== 0) {
1670
- pushCstTriviaEntry(ctx, ch2.start, ch2.end, kinds ? ch2.kindIndex : void 0);
1671
- }
1534
+ function splitTopLevelAlts(body) {
1535
+ const arms = [];
1536
+ let inClass = false;
1537
+ let start = 0;
1538
+ for (let i = 0; i < body.length; i++) {
1539
+ const c = body[i];
1540
+ if (c === "\\") {
1541
+ i++;
1542
+ continue;
1543
+ }
1544
+ if (inClass) {
1545
+ if (c === "]") inClass = false;
1546
+ continue;
1547
+ }
1548
+ if (c === "[") inClass = true;
1549
+ else if (c === "(") return null;
1550
+ else if (c === "|") {
1551
+ arms.push(body.slice(start, i));
1552
+ start = i + 1;
1672
1553
  }
1673
1554
  }
1555
+ arms.push(body.slice(start));
1556
+ return arms;
1674
1557
  }
1675
- function triviaKindMask(labels, keep) {
1676
- if (!labels) return void 0;
1677
- let mask = 0;
1678
- for (const name of keep) {
1679
- const idx = labels.indexOf(name);
1680
- if (idx >= 0) mask |= 1 << idx;
1558
+ function altStarSource(source) {
1559
+ const m = /^\(\?:(.*)\)[*+]$/.exec(source);
1560
+ if (!m) return null;
1561
+ const armSrcs = splitTopLevelAlts(m[1]);
1562
+ if (!armSrcs || armSrcs.length < 2) return null;
1563
+ const arms = [];
1564
+ for (const src of armSrcs) {
1565
+ const arm = classifyTriviaArm(src);
1566
+ if (!arm) return null;
1567
+ arms.push(arm);
1681
1568
  }
1682
- return mask;
1569
+ const ranges = [];
1570
+ const leaders = [];
1571
+ for (const arm of arms) {
1572
+ if (arm.kind === "class") ranges.push(...arm.ranges);
1573
+ else leaders.push(arm.code);
1574
+ }
1575
+ if (leaders.some((code) => inRanges2(code, ranges))) {
1576
+ return loopScanner(arms.map(armScanner));
1577
+ }
1578
+ if (leaders.length === 0) return armScanner({ kind: "class", ranges });
1579
+ return fusedTriviaScanner(ranges, leaders);
1683
1580
  }
1684
1581
 
1685
- // src/combinators/trivia-skip.ts
1686
- var NOOP_COMMIT = () => {
1687
- };
1688
- var fastTriviaCache = /* @__PURE__ */ new WeakMap();
1689
- function needsDeferredTriviaCommit(ctx) {
1690
- return ctx._triviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
1691
- }
1692
- function saveTriviaMark(ctx) {
1693
- const m = saveCstMark(ctx);
1694
- return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, errors: m.errors, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1695
- }
1696
- function rollbackTrivia(ctx, mark) {
1697
- rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1698
- if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1699
- }
1700
- function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1701
- const probeCtx = {
1702
- trackLines: ctx.trackLines,
1703
- state: ctx.state,
1704
- ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1705
- ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1706
- ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1582
+ // src/recovery/scan.ts
1583
+ function probeAt(sentinel, input, pos, ctx) {
1584
+ const s = {
1585
+ _probe: ctx._probe,
1586
+ _errors: ctx._errors,
1587
+ _cstLeaves: ctx._cstLeaves,
1588
+ _cstChildren: ctx._cstChildren,
1589
+ _cstRawChildren: ctx._cstRawChildren,
1590
+ _cstTriviaLog: ctx._cstTriviaLog,
1591
+ _triviaLog: ctx._triviaLog,
1592
+ _rootTriviaLog: ctx._rootTriviaLog,
1593
+ _fields: ctx._fields,
1594
+ _cstBuf: ctx._cstBuf,
1595
+ _tolerant: ctx._tolerant,
1596
+ _sync: ctx._sync
1707
1597
  };
1708
- const result = triviaP.parse(input, cur, probeCtx);
1709
- ctx._lineScannedTo = probeCtx._lineScannedTo;
1710
- return result;
1711
- }
1712
- function saveLookaheadMark(ctx) {
1713
- return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1714
- }
1715
- function rollbackLookahead(ctx, mark) {
1716
- rollbackTrivia(ctx, mark);
1717
- if (ctx._probe) ctx._probe.best = mark.probeBest;
1598
+ ctx._probe = void 0;
1599
+ ctx._errors = void 0;
1600
+ ctx._cstLeaves = void 0;
1601
+ ctx._cstChildren = void 0;
1602
+ ctx._cstRawChildren = void 0;
1603
+ ctx._cstTriviaLog = void 0;
1604
+ ctx._triviaLog = void 0;
1605
+ ctx._rootTriviaLog = void 0;
1606
+ ctx._fields = void 0;
1607
+ ctx._cstBuf = void 0;
1608
+ ctx._tolerant = void 0;
1609
+ ctx._sync = void 0;
1610
+ try {
1611
+ return sentinel.parse(input, pos, ctx).ok;
1612
+ } finally {
1613
+ ctx._probe = s._probe;
1614
+ ctx._errors = s._errors;
1615
+ ctx._cstLeaves = s._cstLeaves;
1616
+ ctx._cstChildren = s._cstChildren;
1617
+ ctx._cstRawChildren = s._cstRawChildren;
1618
+ ctx._cstTriviaLog = s._cstTriviaLog;
1619
+ ctx._triviaLog = s._triviaLog;
1620
+ ctx._rootTriviaLog = s._rootTriviaLog;
1621
+ ctx._fields = s._fields;
1622
+ ctx._cstBuf = s._cstBuf;
1623
+ ctx._tolerant = s._tolerant;
1624
+ ctx._sync = s._sync;
1625
+ }
1718
1626
  }
1719
- function scanWithLabels(input, cur, ctx) {
1720
- const triviaP = ctx.trivia;
1721
- const spec = analyzeLabeledTrivia(triviaP);
1722
- if (!spec) return { end: cur, commit: NOOP_COMMIT };
1723
- const fast = tryFastLabeledScan(input, cur, triviaP);
1724
- const { end, chunks } = fast ?? scanLabeledTriviaChunks(input, cur, spec);
1725
- if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1627
+ function orSentinel(a, b) {
1628
+ if (b === void 0) return a;
1726
1629
  return {
1727
- end,
1728
- commit: () => recordTriviaChunks(ctx, chunks)
1729
- };
1730
- }
1731
- function advanceTrivia(input, cur, ctx) {
1732
- const triviaP = ctx.trivia;
1733
- if (!triviaP) return cur;
1734
- if (!ctx.trackLines) {
1735
- const fast2 = fastTriviaScanner(triviaP);
1736
- if (fast2) return fast2(input, cur);
1737
- if (ctx.triviaKindLabels) {
1738
- const scan = scanWithLabels(input, cur, ctx);
1739
- return scan.end;
1630
+ _tag: "orSentinel",
1631
+ _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
1632
+ _def: { tag: "unknown" },
1633
+ parse(input, pos, ctx) {
1634
+ const ra = a.parse(input, pos, ctx);
1635
+ if (ra.ok) return ra;
1636
+ return b.parse(input, pos, ctx);
1740
1637
  }
1741
- const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1742
- return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1743
- }
1744
- const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1745
- const fast = fastTriviaScanner(triviaP);
1746
- if (fast) {
1747
- const end = fast(input, cur);
1748
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1749
- return end;
1750
- }
1751
- if (ctx.triviaKindLabels) {
1752
- const scan = scanWithLabels(input, cur, ctx);
1753
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1754
- return scan.end;
1755
- }
1756
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1757
- return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1638
+ };
1758
1639
  }
1759
- function scanTrivia(input, cur, ctx) {
1760
- const triviaP = ctx.trivia;
1761
- if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1762
- const log = ctx._triviaLog;
1763
- const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1764
- if (!ctx.trackLines) {
1765
- const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1766
- if (fast2 && log === void 0 && !captureTl) {
1767
- return { end: fast2(input, cur), commit: NOOP_COMMIT };
1768
- }
1769
- if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1770
- return scanWithLabels(input, cur, ctx);
1771
- }
1772
- if (log !== void 0 || captureTl) {
1773
- const tr3 = triviaP.parse(input, cur, {
1774
- trackLines: log !== void 0 ? false : ctx.trackLines,
1775
- state: ctx.state
1776
- });
1777
- if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1778
- const end = tr3.span.end;
1779
- return {
1780
- end,
1781
- commit: () => {
1782
- pushTriviaLogEntry(ctx, cur, end);
1783
- if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1640
+ function firstSetSentinel(fs2) {
1641
+ if (fs2.kind !== "ranges" || fs2.ranges.length === 0) return null;
1642
+ const ranges = fs2.ranges;
1643
+ return {
1644
+ _tag: "firstSetSentinel",
1645
+ _meta: { firstSet: fs2, canMatchNewline: false, isTrivia: false },
1646
+ _def: { tag: "unknown" },
1647
+ parse(input, pos) {
1648
+ const code = pos < input.length ? input.codePointAt(pos) : void 0;
1649
+ if (code !== void 0) {
1650
+ for (const r of ranges) {
1651
+ if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
1784
1652
  }
1785
- };
1653
+ }
1654
+ return { ok: false, expected: [], span: { start: pos, end: pos } };
1786
1655
  }
1787
- const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1788
- return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1789
- }
1790
- const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1791
- const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1792
- if (fast && log === void 0 && !captureTl) {
1793
- const end = fast(input, cur);
1794
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1795
- return { end, commit: NOOP_COMMIT };
1796
- }
1797
- if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1798
- const scan = scanWithLabels(input, cur, ctx);
1799
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1800
- return scan;
1801
- }
1802
- if (log !== void 0 || captureTl) {
1803
- const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1804
- if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1805
- const end = tr2.span.end;
1806
- return {
1807
- end,
1808
- commit: () => {
1809
- pushTriviaLogEntry(ctx, cur, end);
1810
- if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1656
+ };
1657
+ }
1658
+ function recoverScan(input, from, ctx, sync, expected) {
1659
+ let scanPos = from;
1660
+ while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1661
+ const span = { start: from, end: scanPos };
1662
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
1663
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1664
+ ctx._errors?.push(error);
1665
+ return { error, end: scanPos };
1666
+ }
1667
+ function matchesAt(sentinel, input, pos, ctx) {
1668
+ return probeAt(sentinel, input, pos, ctx);
1669
+ }
1670
+ function captureError(ctx, error) {
1671
+ if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
1672
+ }
1673
+
1674
+ // src/combinators/sequence.ts
1675
+ function sequence(...parsers) {
1676
+ const meta = {
1677
+ // Union through the nullable prefix — a leading optional()/many() lets a later
1678
+ // term's first char start the sequence. Just `parsers[0]` under-approximates.
1679
+ firstSet: sequenceFirstSet(parsers),
1680
+ canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
1681
+ isTrivia: false
1682
+ };
1683
+ const def = { tag: "sequence", parsers };
1684
+ let followSentinels;
1685
+ function parseTolerant(input, pos, ctx) {
1686
+ followSentinels ??= parsers.map((_, i) => {
1687
+ const fs2 = parsers.slice(i + 1).reduce(
1688
+ (acc, p) => union(acc, firstSetOf(p)),
1689
+ { kind: "empty" }
1690
+ );
1691
+ return firstSetSentinel(fs2);
1692
+ });
1693
+ const values = def.valueUnused ? void 0 : [];
1694
+ let cur = pos;
1695
+ const inheritedSync = ctx._sync;
1696
+ try {
1697
+ for (let i = 0; i < parsers.length; i++) {
1698
+ ctx._sync = followSentinels[i] ?? inheritedSync;
1699
+ if (ctx.trivia && i > 0) {
1700
+ const mark = saveTriviaMark(ctx);
1701
+ let scanEnd;
1702
+ if (needsDeferredTriviaCommit(ctx)) {
1703
+ const scan = scanTrivia(input, cur, ctx);
1704
+ scan.commit();
1705
+ scanEnd = scan.end;
1706
+ } else {
1707
+ scanEnd = advanceTrivia(input, cur, ctx);
1708
+ }
1709
+ const result2 = parsers[i].parse(input, scanEnd, ctx);
1710
+ if (!result2.ok) return result2;
1711
+ if (result2.span.end > scanEnd) cur = result2.span.end;
1712
+ else rollbackTrivia(ctx, mark);
1713
+ if (values !== void 0) values.push(result2.value);
1714
+ continue;
1715
+ }
1716
+ const result = parsers[i].parse(input, cur, ctx);
1717
+ if (!result.ok) return result;
1718
+ if (values !== void 0) values.push(result.value);
1719
+ cur = result.span.end;
1811
1720
  }
1721
+ } finally {
1722
+ ctx._sync = inheritedSync;
1723
+ }
1724
+ return {
1725
+ ok: true,
1726
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1727
+ value: values ?? void 0,
1728
+ span: { start: pos, end: cur }
1812
1729
  };
1813
1730
  }
1814
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1815
- return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1731
+ return {
1732
+ _tag: "sequence",
1733
+ _meta: meta,
1734
+ _def: def,
1735
+ parse(input, pos, ctx) {
1736
+ if (ctx._tolerant) return parseTolerant(input, pos, ctx);
1737
+ const values = def.valueUnused ? void 0 : [];
1738
+ let cur = pos;
1739
+ for (let i = 0; i < parsers.length; i++) {
1740
+ if (ctx.trivia && i > 0) {
1741
+ let scanEnd;
1742
+ let mark = saveTriviaMark(ctx);
1743
+ if (needsDeferredTriviaCommit(ctx)) {
1744
+ const scan = scanTrivia(input, cur, ctx);
1745
+ scan.commit();
1746
+ scanEnd = scan.end;
1747
+ } else {
1748
+ scanEnd = advanceTrivia(input, cur, ctx);
1749
+ }
1750
+ const result2 = parsers[i].parse(input, scanEnd, ctx);
1751
+ if (!result2.ok) return result2;
1752
+ if (result2.span.end > scanEnd) {
1753
+ cur = result2.span.end;
1754
+ } else {
1755
+ rollbackTrivia(ctx, mark);
1756
+ }
1757
+ if (values !== void 0) values.push(result2.value);
1758
+ continue;
1759
+ }
1760
+ const result = parsers[i].parse(input, cur, ctx);
1761
+ if (!result.ok) return result;
1762
+ if (values !== void 0) values.push(result.value);
1763
+ cur = result.span.end;
1764
+ }
1765
+ return {
1766
+ ok: true,
1767
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1768
+ value: values ?? void 0,
1769
+ span: { start: pos, end: cur }
1770
+ };
1771
+ }
1772
+ };
1816
1773
  }
1817
- function consumeTrivia(input, cur, ctx) {
1818
- if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1819
- const scan = scanTrivia(input, cur, ctx);
1820
- scan.commit();
1821
- return scan.end;
1774
+
1775
+ // src/combinators/expect.ts
1776
+ function deriveExpected(c) {
1777
+ return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
1822
1778
  }
1823
- function fastTriviaScanner(trivia2) {
1824
- const cached = fastTriviaCache.get(trivia2);
1825
- if (cached !== void 0) return cached;
1826
- const scanner = buildFastTriviaScanner(trivia2);
1827
- fastTriviaCache.set(trivia2, scanner);
1828
- return scanner;
1779
+ function derive(c, seen) {
1780
+ const deriveExpected2 = (p) => derive(p, seen);
1781
+ const def = c._def;
1782
+ switch (def.tag) {
1783
+ case "literal":
1784
+ return [JSON.stringify(def.value)];
1785
+ case "regex":
1786
+ return [`/${def.source}/`];
1787
+ case "keywords":
1788
+ return def.words.map((w) => JSON.stringify(w));
1789
+ case "label":
1790
+ return [def.label];
1791
+ case "choice":
1792
+ return def.parsers.flatMap(deriveExpected2);
1793
+ case "dispatch":
1794
+ return deriveExpected2(def.selector);
1795
+ // Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
1796
+ // `optional(…)`/`many(…)` can match nothing, so the term after it is equally
1797
+ // able to fail first — deriving from term 0 alone named a token the parse
1798
+ // never actually required. A leading `not(…)` is zero-width and contributes
1799
+ // nothing (its own expectation is about what must NOT be here), but it is
1800
+ // nullable, so keep scanning past it. Stop at the first term that must match.
1801
+ case "sequence": {
1802
+ const out = [];
1803
+ for (const term of def.parsers) {
1804
+ if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
1805
+ if (!matchesEmpty(term)) break;
1806
+ }
1807
+ return out;
1808
+ }
1809
+ case "attempt":
1810
+ return deriveExpected2(def.parser);
1811
+ // Delegating wrappers whose first-token failure is their inner parser's — must
1812
+ // pass through so a start-failure (and the first-set-miss fast-path in attempt/
1813
+ // node, which reads this) names the real expected token, not the wrapper tag.
1814
+ case "field":
1815
+ case "withCtx":
1816
+ case "recover":
1817
+ return deriveExpected2(def.parser);
1818
+ case "skip":
1819
+ return deriveExpected2(def.main);
1820
+ case "expect":
1821
+ return def.expected;
1822
+ case "node":
1823
+ case "grammar":
1824
+ case "trivia":
1825
+ case "token":
1826
+ case "leaf":
1827
+ case "optional":
1828
+ case "many":
1829
+ case "oneOrMore":
1830
+ case "transform":
1831
+ case "not":
1832
+ case "peek":
1833
+ return deriveExpected2(def.parser);
1834
+ case "routed":
1835
+ return ["routed()"];
1836
+ case "lazy": {
1837
+ const name = c._ruleName;
1838
+ if (seen.has(c)) return name ? [name] : [];
1839
+ seen.add(c);
1840
+ try {
1841
+ return deriveExpected2(def.thunk());
1842
+ } catch {
1843
+ return name ? [name] : [];
1844
+ } finally {
1845
+ seen.delete(c);
1846
+ }
1847
+ }
1848
+ default:
1849
+ return [];
1850
+ }
1829
1851
  }
1830
- function buildFastTriviaScanner(trivia2) {
1831
- const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1832
- const direct = regexTriviaScanner(core);
1833
- if (direct) return direct;
1834
- const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1835
- if (!repeat) return null;
1836
- const one = regexTriviaScanner(repeat);
1837
- if (one) return loopScanner([one]);
1838
- if (repeat._def.tag !== "choice") return null;
1839
- const arms = repeat._def.parsers.map(regexTriviaScanner);
1840
- if (arms.some((s) => s === null)) return null;
1841
- return loopScanner(arms);
1852
+ function expect(combinator, label2) {
1853
+ const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
1854
+ const meta = {
1855
+ firstSet: combinator._meta.firstSet,
1856
+ canMatchNewline: combinator._meta.canMatchNewline,
1857
+ isTrivia: false
1858
+ };
1859
+ return {
1860
+ _tag: "expect",
1861
+ _meta: meta,
1862
+ _def: { tag: "expect", parser: combinator, label: label2, expected },
1863
+ parse(input, pos, ctx) {
1864
+ const result = combinator.parse(input, pos, ctx);
1865
+ if (result.ok) return result;
1866
+ const span = { start: pos, end: pos };
1867
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1868
+ ctx._errors?.push(error);
1869
+ if (ctx._tolerant) captureError(ctx, error);
1870
+ return { ok: true, value: error, span: { start: pos, end: pos } };
1871
+ }
1872
+ };
1842
1873
  }
1843
- function loopScanner(arms) {
1844
- return (input, cur) => {
1845
- let pos = cur;
1846
- scan: while (pos < input.length) {
1847
- for (const arm of arms) {
1848
- const end = arm(input, pos);
1849
- if (end > pos) {
1850
- pos = end;
1851
- continue scan;
1874
+
1875
+ // src/combinators/choice.ts
1876
+ function choice(...args) {
1877
+ const parsers = args.map((a) => "gate" in a ? a.combinator : a);
1878
+ const gates = args.map((a) => "gate" in a ? a.gate : null);
1879
+ const hasGates = gates.some((g) => g !== null);
1880
+ const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
1881
+ let combined = { kind: "empty" };
1882
+ for (const p of parsers) combined = union(combined, p._meta.firstSet);
1883
+ const meta = {
1884
+ firstSet: combined,
1885
+ canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
1886
+ isTrivia: false,
1887
+ disjoint
1888
+ };
1889
+ const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
1890
+ const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
1891
+ let greedyLitMap = null;
1892
+ let sortedParsers = null;
1893
+ const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
1894
+ if (strategy?.tag === "greedyClassify") {
1895
+ greedyLitMap = /* @__PURE__ */ new Map();
1896
+ for (let i = 0; i < parsers.length; i++) {
1897
+ if (i === strategy.superIndex) continue;
1898
+ const litVal = getCoreLiteralValue(parsers[i]);
1899
+ if (litVal !== null) greedyLitMap.set(litVal, i);
1900
+ }
1901
+ } else if (strategy?.tag === "literalsLongestFirst") {
1902
+ sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
1903
+ }
1904
+ return {
1905
+ _tag: "choice",
1906
+ _meta: meta,
1907
+ _def: {
1908
+ tag: "choice",
1909
+ parsers,
1910
+ gates,
1911
+ disjoint,
1912
+ strategy: strategy ?? { tag: "firstMatch" },
1913
+ autoNot
1914
+ },
1915
+ parse(input, pos, ctx) {
1916
+ const expected = [];
1917
+ if (disjoint && pos < input.length) {
1918
+ const code = input.codePointAt(pos);
1919
+ let idx = code < 128 ? asciiDispatch[code] : -1;
1920
+ if (idx < 0) {
1921
+ for (let i = 0; i < parsers.length; i++) {
1922
+ if (inFirstSet(code, parsers[i]._meta.firstSet)) {
1923
+ idx = i;
1924
+ break;
1925
+ }
1926
+ }
1927
+ }
1928
+ if (idx >= 0) {
1929
+ const gate2 = gates[idx];
1930
+ if (gate2 && !gate2(ctx.state)) {
1931
+ return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
1932
+ }
1933
+ const result = parsers[idx].parse(input, pos, ctx);
1934
+ if (result.ok) return result;
1935
+ expected.push(...result.expected);
1936
+ if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
1937
+ return { ok: false, expected, span: { start: pos, end: pos } };
1938
+ }
1939
+ return {
1940
+ ok: false,
1941
+ expected: parsers.flatMap((p) => {
1942
+ const r = p.parse(input, pos, ctx);
1943
+ return r.ok ? [] : r.expected;
1944
+ }),
1945
+ span: { start: pos, end: pos }
1946
+ };
1947
+ }
1948
+ if (strategy?.tag === "greedyClassify") {
1949
+ const superResult = parsers[strategy.superIndex].parse(input, pos, ctx);
1950
+ if (!superResult.ok) return superResult;
1951
+ const end = superResult.span.end;
1952
+ const litIdx = greedyLitMap.get(input.slice(pos, end));
1953
+ if (litIdx !== void 0) {
1954
+ const litVal = getCoreLiteralValue(parsers[litIdx]);
1955
+ const value = applyTransforms(parsers[litIdx], litVal, { start: pos, end });
1956
+ return { ok: true, value, span: { start: pos, end } };
1957
+ }
1958
+ return superResult;
1959
+ }
1960
+ if (strategy?.tag === "literalsLongestFirst") {
1961
+ for (const p of sortedParsers) {
1962
+ const r = p.parse(input, pos, ctx);
1963
+ if (r.ok) return r;
1964
+ expected.push(...r.expected);
1852
1965
  }
1966
+ return { ok: false, expected, span: { start: pos, end: pos } };
1853
1967
  }
1854
- break;
1968
+ for (let i = 0; i < parsers.length; i++) {
1969
+ if (gates[i] && !gates[i](ctx.state)) continue;
1970
+ const mark = saveTriviaMark(ctx);
1971
+ const result = parsers[i].parse(input, pos, ctx);
1972
+ if (!result.ok) {
1973
+ rollbackTrivia(ctx, mark);
1974
+ expected.push(...result.expected);
1975
+ if (result.committed) return { ok: false, expected, span: result.span, committed: true };
1976
+ continue;
1977
+ }
1978
+ const checks = autoNot[i];
1979
+ if (checks && autoNotFires(input, result.span.end, checks)) {
1980
+ rollbackTrivia(ctx, mark);
1981
+ continue;
1982
+ }
1983
+ return result;
1984
+ }
1985
+ return { ok: false, expected, span: { start: pos, end: pos } };
1855
1986
  }
1856
- return pos;
1857
1987
  };
1858
1988
  }
1859
- function regexTriviaScanner(parser2) {
1860
- if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1861
- const source = parser2._def.source;
1862
- return classRunSource(source) ?? altStarSource(source) ?? (blockCommentSource(source) ? scanBlockComment : null);
1863
- }
1864
- function inRanges2(cp, ranges) {
1865
- for (let i = 0; i < ranges.length; i++) {
1866
- const r = ranges[i];
1867
- if (cp >= r[0] && cp <= r[1]) return true;
1989
+ function detectStrategy(parsers) {
1990
+ const regexIndices = [];
1991
+ const literalIndices = [];
1992
+ for (let i = 0; i < parsers.length; i++) {
1993
+ if (getCoreRegexDef(parsers[i]) !== null) regexIndices.push(i);
1994
+ else if (getCoreLiteralValue(parsers[i]) !== null) literalIndices.push(i);
1868
1995
  }
1869
- return false;
1870
- }
1871
- function classScanner(classBody) {
1872
- const ranges = parseClassRanges(classBody);
1873
- if (!ranges) return null;
1874
- return (input, cur) => {
1875
- let pos = cur;
1876
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1877
- return pos;
1878
- };
1879
- }
1880
- function lineCommentScanner(commentCode) {
1881
- return (input, cur) => {
1882
- if (input.charCodeAt(cur) !== commentCode) return cur;
1883
- let pos = cur + 1;
1884
- while (pos < input.length) {
1885
- const cc = input.charCodeAt(pos);
1886
- if (cc === 10 || cc === 13) break;
1887
- pos++;
1888
- }
1889
- return pos;
1890
- };
1891
- }
1892
- function classRunSource(source) {
1893
- const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1894
- return m ? classScanner(m[1]) : null;
1895
- }
1896
- function classifyTriviaArm(arm) {
1897
- const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1898
- if (cls) {
1899
- const ranges = parseClassRanges(cls[1]);
1900
- return ranges ? { kind: "class", ranges } : null;
1996
+ if (regexIndices.length === 1 && literalIndices.length === parsers.length - 1 && literalIndices.length > 0) {
1997
+ const superIndex = regexIndices[0];
1998
+ const regexDef = getCoreRegexDef(parsers[superIndex]);
1999
+ const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
2000
+ const re = new RegExp(regexDef.source, flags);
2001
+ const allSubsumed = literalIndices.every((i) => {
2002
+ const litVal = getCoreLiteralValue(parsers[i]);
2003
+ re.lastIndex = 0;
2004
+ const m = re.exec(litVal);
2005
+ return m !== null && m[0] === litVal;
2006
+ });
2007
+ if (allSubsumed) return { tag: "greedyClassify", superIndex };
1901
2008
  }
1902
- const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1903
- if (lc) {
1904
- const marker = lc[1];
1905
- return { kind: "comment", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
2009
+ if (parsers.length === literalIndices.length) {
2010
+ const sortedIndices = [...literalIndices].sort(
2011
+ (a, b) => getCoreLiteralValue(parsers[b]).length - getCoreLiteralValue(parsers[a]).length
2012
+ );
2013
+ return { tag: "literalsLongestFirst", sortedIndices };
1906
2014
  }
2015
+ const shared = detectSharedPrefix(parsers);
2016
+ if (shared !== null) return shared;
2017
+ return { tag: "firstMatch" };
2018
+ }
2019
+ function bareLeadingTermKey(term) {
2020
+ const d = term._def;
2021
+ if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
2022
+ if (d.tag === "regex") return `R:${d.source}\0${d.flags}`;
1907
2023
  return null;
1908
2024
  }
1909
- function armScanner(arm) {
1910
- if (arm.kind === "comment") return lineCommentScanner(arm.code);
1911
- const ranges = arm.ranges;
1912
- return (input, cur) => {
1913
- let pos = cur;
1914
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1915
- return pos;
1916
- };
2025
+ function detectSharedPrefix(parsers) {
2026
+ if (parsers.length < 2) return null;
2027
+ let key = null;
2028
+ let prefix = null;
2029
+ const members = [];
2030
+ for (let i = 0; i < parsers.length; i++) {
2031
+ const term0 = leadingTermOfArm(parsers[i]);
2032
+ if (term0 === null) return null;
2033
+ const k = bareLeadingTermKey(term0);
2034
+ if (k === null) return null;
2035
+ if (key === null) {
2036
+ key = k;
2037
+ prefix = term0;
2038
+ } else if (k !== key) return null;
2039
+ members.push(i);
2040
+ }
2041
+ if (prefix === null || members.length < 2) return null;
2042
+ return { tag: "sharedPrefix", prefix, members };
1917
2043
  }
1918
- function fusedTriviaScanner(ranges, commentCodes) {
1919
- const c0 = commentCodes[0];
1920
- const single = commentCodes.length === 1;
1921
- return (input, cur) => {
1922
- let pos = cur;
1923
- const len = input.length;
1924
- for (; ; ) {
1925
- const c = input.charCodeAt(pos);
1926
- if (pos < len && inRanges2(c, ranges)) {
1927
- pos++;
2044
+ function leadingTermOfArm(arm) {
2045
+ let d = arm._def;
2046
+ for (; ; ) {
2047
+ if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
2048
+ d = d.parser._def;
2049
+ continue;
2050
+ }
2051
+ break;
2052
+ }
2053
+ if (d.tag !== "sequence" || d.parsers.length < 2) return null;
2054
+ return d.parsers[0];
2055
+ }
2056
+ function computeAutoNot(parsers) {
2057
+ return parsers.map((p, i) => {
2058
+ const litVal = getCoreLiteralValue(p);
2059
+ if (litVal === null) return null;
2060
+ const checks = [];
2061
+ for (let j = i + 1; j < parsers.length; j++) {
2062
+ const other = parsers[j];
2063
+ const otherLit = getCoreLiteralValue(other);
2064
+ if (otherLit !== null && otherLit.startsWith(litVal) && otherLit.length > litVal.length) {
2065
+ checks.push({ kind: "startsWith", value: otherLit.slice(litVal.length) });
1928
2066
  continue;
1929
2067
  }
1930
- if (single ? c === c0 : commentCodes.includes(c)) {
1931
- pos++;
1932
- while (pos < len) {
1933
- const cc = input.charCodeAt(pos);
1934
- if (cc === 10 || cc === 13) break;
1935
- pos++;
1936
- }
1937
- continue;
2068
+ const regexDef = getCoreRegexDef(other);
2069
+ if (regexDef !== null) {
2070
+ const contSet = continuationFirstSet(litVal, regexDef.source, regexDef.flags);
2071
+ if (contSet !== null) checks.push({ kind: "firstSet", set: contSet });
1938
2072
  }
1939
- break;
1940
2073
  }
1941
- return pos;
1942
- };
2074
+ return checks.length > 0 ? checks : null;
2075
+ });
1943
2076
  }
1944
- function splitTopLevelAlts(body) {
1945
- const arms = [];
1946
- let inClass = false;
1947
- let start = 0;
1948
- for (let i = 0; i < body.length; i++) {
1949
- const c = body[i];
1950
- if (c === "\\") {
1951
- i++;
1952
- continue;
1953
- }
1954
- if (inClass) {
1955
- if (c === "]") inClass = false;
1956
- continue;
1957
- }
1958
- if (c === "[") inClass = true;
1959
- else if (c === "(") return null;
1960
- else if (c === "|") {
1961
- arms.push(body.slice(start, i));
1962
- start = i + 1;
2077
+ function autoNotFires(input, end, checks) {
2078
+ for (const check of checks) {
2079
+ if (check.kind === "firstSet") {
2080
+ const code = end < input.length ? input.codePointAt(end) ?? -1 : -1;
2081
+ if (inFirstSet(code, check.set)) return true;
2082
+ } else {
2083
+ if (input.startsWith(check.value, end)) return true;
1963
2084
  }
1964
2085
  }
1965
- arms.push(body.slice(start));
1966
- return arms;
2086
+ return false;
1967
2087
  }
1968
- function altStarSource(source) {
1969
- const m = /^\(\?:(.*)\)[*+]$/.exec(source);
1970
- if (!m) return null;
1971
- const armSrcs = splitTopLevelAlts(m[1]);
1972
- if (!armSrcs || armSrcs.length < 2) return null;
1973
- const arms = [];
1974
- for (const src of armSrcs) {
1975
- const arm = classifyTriviaArm(src);
1976
- if (!arm) return null;
1977
- arms.push(arm);
2088
+ function getCoreLiteralValue(p) {
2089
+ const def = p._def;
2090
+ if (def.tag === "literal" && !def.caseInsensitive) return def.value;
2091
+ if (def.tag === "transform") return getCoreLiteralValue(def.parser);
2092
+ return null;
2093
+ }
2094
+ function getCoreRegexDef(p) {
2095
+ const def = p._def;
2096
+ if (def.tag === "regex") return { source: def.source, flags: def.flags };
2097
+ if (def.tag === "transform") return getCoreRegexDef(def.parser);
2098
+ if (def.tag === "label") return getCoreRegexDef(def.parser);
2099
+ return null;
2100
+ }
2101
+ function applyTransforms(p, value, span) {
2102
+ const def = p._def;
2103
+ if (def.tag === "transform") {
2104
+ const inner = applyTransforms(def.parser, value, span);
2105
+ return def.fn(inner, span);
1978
2106
  }
1979
- const ranges = [];
1980
- const commentCodes = [];
1981
- for (const arm of arms) {
1982
- if (arm.kind === "class") ranges.push(...arm.ranges);
1983
- else commentCodes.push(arm.code);
2107
+ return value;
2108
+ }
2109
+ function continuationFirstSet(lit, source, flags) {
2110
+ const re = new RegExp(source, "y" + flags.replace(/[gy]/g, ""));
2111
+ re.lastIndex = 0;
2112
+ const base = re.exec(lit);
2113
+ if (!base || base[0] !== lit) return null;
2114
+ const contCodes = [];
2115
+ for (let code = 1; code < 128; code++) {
2116
+ re.lastIndex = 0;
2117
+ const m = re.exec(lit + String.fromCharCode(code));
2118
+ if (m && m[0].length > lit.length) contCodes.push(code);
1984
2119
  }
1985
- if (commentCodes.some((code) => inRanges2(code, ranges))) {
1986
- return loopScanner(arms.map(armScanner));
2120
+ if (contCodes.length === 0) return null;
2121
+ return codesToFirstSet(contCodes);
2122
+ }
2123
+ function codesToFirstSet(codes) {
2124
+ codes.sort((a, b) => a - b);
2125
+ const ranges = [];
2126
+ let lo = codes[0], hi = codes[0];
2127
+ for (let i = 1; i < codes.length; i++) {
2128
+ if (codes[i] === hi + 1) {
2129
+ hi = codes[i];
2130
+ } else {
2131
+ ranges.push({ lo, hi });
2132
+ lo = hi = codes[i];
2133
+ }
1987
2134
  }
1988
- if (commentCodes.length === 0) return armScanner({ kind: "class", ranges });
1989
- return fusedTriviaScanner(ranges, commentCodes);
2135
+ ranges.push({ lo, hi });
2136
+ return { kind: "ranges", ranges };
1990
2137
  }
1991
- function blockCommentSource(source) {
1992
- return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
2138
+ function inFirstSet(code, fs2) {
2139
+ if (fs2.kind === "any") return true;
2140
+ if (fs2.kind === "empty") return false;
2141
+ for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
2142
+ return false;
1993
2143
  }
1994
- function scanBlockComment(input, cur) {
1995
- if (input.charCodeAt(cur) !== 47 || input.charCodeAt(cur + 1) !== 42) return cur;
1996
- const close = input.indexOf("*/", cur + 2);
1997
- return close === -1 ? cur : close + 2;
2144
+ function areDisjoint(sets) {
2145
+ if (sets.some((s) => s.kind === "any")) return false;
2146
+ for (let i = 0; i < sets.length; i++)
2147
+ for (let j = i + 1; j < sets.length; j++)
2148
+ if (intersects(sets[i], sets[j])) return false;
2149
+ return true;
1998
2150
  }
1999
-
2000
- // src/combinators/sequence.ts
2001
- function sequence(...parsers) {
2002
- const meta = {
2003
- // Union through the nullable prefix — a leading optional()/many() lets a later
2004
- // term's first char start the sequence. Just `parsers[0]` under-approximates.
2005
- firstSet: sequenceFirstSet(parsers),
2006
- canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
2007
- isTrivia: false
2008
- };
2009
- const def = { tag: "sequence", parsers };
2010
- let followSentinels;
2011
- function parseTolerant(input, pos, ctx) {
2012
- followSentinels ??= parsers.map((_, i) => {
2013
- const fs2 = parsers.slice(i + 1).reduce(
2014
- (acc, p) => union(acc, firstSetOf(p)),
2015
- { kind: "empty" }
2016
- );
2017
- return firstSetSentinel(fs2);
2018
- });
2019
- const values = def.valueUnused ? void 0 : [];
2020
- let cur = pos;
2021
- const inheritedSync = ctx._sync;
2022
- try {
2023
- for (let i = 0; i < parsers.length; i++) {
2024
- ctx._sync = followSentinels[i] ?? inheritedSync;
2025
- if (ctx.trivia && i > 0) {
2026
- const mark = saveTriviaMark(ctx);
2027
- let scanEnd;
2028
- if (needsDeferredTriviaCommit(ctx)) {
2029
- const scan = scanTrivia(input, cur, ctx);
2030
- scan.commit();
2031
- scanEnd = scan.end;
2032
- } else {
2033
- scanEnd = advanceTrivia(input, cur, ctx);
2034
- }
2035
- const result2 = parsers[i].parse(input, scanEnd, ctx);
2036
- if (!result2.ok) return result2;
2037
- if (result2.span.end > scanEnd) cur = result2.span.end;
2038
- else rollbackTrivia(ctx, mark);
2039
- if (values !== void 0) values.push(result2.value);
2040
- continue;
2041
- }
2042
- const result = parsers[i].parse(input, cur, ctx);
2043
- if (!result.ok) return result;
2044
- if (values !== void 0) values.push(result.value);
2045
- cur = result.span.end;
2151
+ function buildAsciiDispatch(parsers) {
2152
+ const table = Array(128).fill(-1);
2153
+ for (let i = 0; i < parsers.length; i++) {
2154
+ const fs2 = parsers[i]._meta.firstSet;
2155
+ if (fs2.kind !== "ranges") continue;
2156
+ for (const { lo, hi } of fs2.ranges) {
2157
+ for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
2158
+ table[code] = i;
2046
2159
  }
2047
- } finally {
2048
- ctx._sync = inheritedSync;
2049
2160
  }
2050
- return {
2051
- ok: true,
2052
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2053
- value: values ?? void 0,
2054
- span: { start: pos, end: cur }
2055
- };
2056
2161
  }
2057
- return {
2058
- _tag: "sequence",
2059
- _meta: meta,
2060
- _def: def,
2061
- parse(input, pos, ctx) {
2062
- if (ctx._tolerant) return parseTolerant(input, pos, ctx);
2063
- const values = def.valueUnused ? void 0 : [];
2064
- let cur = pos;
2065
- for (let i = 0; i < parsers.length; i++) {
2066
- if (ctx.trivia && i > 0) {
2067
- let scanEnd;
2068
- let mark = saveTriviaMark(ctx);
2069
- if (needsDeferredTriviaCommit(ctx)) {
2070
- const scan = scanTrivia(input, cur, ctx);
2071
- scan.commit();
2072
- scanEnd = scan.end;
2073
- } else {
2074
- scanEnd = advanceTrivia(input, cur, ctx);
2075
- }
2076
- const result2 = parsers[i].parse(input, scanEnd, ctx);
2077
- if (!result2.ok) return result2;
2078
- if (result2.span.end > scanEnd) {
2079
- cur = result2.span.end;
2080
- } else {
2081
- rollbackTrivia(ctx, mark);
2082
- }
2083
- if (values !== void 0) values.push(result2.value);
2084
- continue;
2085
- }
2086
- const result = parsers[i].parse(input, cur, ctx);
2087
- if (!result.ok) return result;
2088
- if (values !== void 0) values.push(result.value);
2089
- cur = result.span.end;
2090
- }
2091
- return {
2092
- ok: true,
2093
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2094
- value: values ?? void 0,
2095
- span: { start: pos, end: cur }
2096
- };
2097
- }
2098
- };
2162
+ return table;
2099
2163
  }
2100
2164
 
2101
2165
  // src/combinators/dispatch.ts
@@ -2355,10 +2419,12 @@ function attempt(parser2) {
2355
2419
  }
2356
2420
  const mark = saveCstMark(ctx);
2357
2421
  const log = ctx._triviaLog?.length;
2422
+ const rootLog = ctx._rootTriviaLog?.length;
2358
2423
  const result = parser2.parse(input, pos, ctx);
2359
2424
  if (result.ok) return result;
2360
2425
  rollbackCstCapture(ctx, mark);
2361
2426
  if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
2427
+ if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
2362
2428
  if (result.committed) return result;
2363
2429
  return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
2364
2430
  }
@@ -2762,18 +2828,37 @@ function createParseLineContext(input, pos) {
2762
2828
  }
2763
2829
  function parser(opts, root) {
2764
2830
  const clearTrivia = opts.trivia === null;
2831
+ const opaqueRootCapture = opts.rootCapture === "opaque";
2832
+ if (opaqueRootCapture && opts.trivia === void 0) {
2833
+ throw new TypeError("parser({ rootCapture: 'opaque' }) requires an explicit trivia scope.");
2834
+ }
2765
2835
  return {
2766
2836
  _tag: "grammar",
2767
- _meta: root._meta,
2837
+ _meta: {
2838
+ ...root._meta,
2839
+ // Trivia classification describes this grammar's own active trivia
2840
+ // scope. A directly nested grammar may have classified trivia of its
2841
+ // own, but that must not advertise classification for this wrapper.
2842
+ triviaKindLabels: void 0,
2843
+ rootTriviaClassified: void 0,
2844
+ ...opts.trivia?._meta?.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {},
2845
+ ...opts.trivia?._meta?.rootTriviaClassified ? { rootTriviaClassified: true } : {}
2846
+ },
2768
2847
  _def: {
2769
2848
  tag: "grammar",
2770
2849
  parser: root,
2771
2850
  triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
2772
2851
  clearTrivia,
2852
+ ...opaqueRootCapture ? { rootCapture: "opaque" } : {},
2773
2853
  ...opts.captureTrivia ? { captureTrivia: true } : {},
2774
2854
  trackLines: opts.trackLines ?? false
2775
2855
  },
2776
2856
  parse(input, pos, _ctx) {
2857
+ if (_ctx?._rootTriviaStrictScopes && opts.trivia !== void 0 && opts.trivia !== null && !opts.trivia._meta.rootTriviaClassified && !opaqueRootCapture) {
2858
+ throw new TypeError(
2859
+ "parser(): selected root trivia requires classifiedTrivia() for every local trivia scope, or rootCapture: 'opaque'."
2860
+ );
2861
+ }
2777
2862
  const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
2778
2863
  const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
2779
2864
  const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
@@ -2796,7 +2881,11 @@ function parser(opts, root) {
2796
2881
  // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
2797
2882
  // trivia is inherited rather than re-declared here. No labels → undefined
2798
2883
  // (capture all).
2799
- ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
2884
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {},
2885
+ // This is a property of the local trivia scope, not a post-parse filter:
2886
+ // nested recognition can still skip its trivia, but selected root rows
2887
+ // must never be written for an explicitly opaque region.
2888
+ ...opaqueRootCapture ? { _rootTriviaCapture: false } : {}
2800
2889
  };
2801
2890
  const result = root.parse(input, pos ?? 0, ctx);
2802
2891
  if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
@@ -2810,6 +2899,123 @@ function noTrivia(root) {
2810
2899
  return parser({ trivia: null }, root);
2811
2900
  }
2812
2901
 
2902
+ // src/cst/reflection.ts
2903
+ var GRAMMAR_REFLECTION = /* @__PURE__ */ Symbol.for("parseman.grammarReflection");
2904
+ function childrenOf(def) {
2905
+ switch (def.tag) {
2906
+ case "sequence":
2907
+ case "choice":
2908
+ return def.parsers;
2909
+ case "dispatch":
2910
+ return [
2911
+ def.selector,
2912
+ ...def.cases.map((c) => c.parser),
2913
+ ...def.matchers ? def.matchers.map((c) => c.parser) : [],
2914
+ ...def.otherwise ? [def.otherwise] : []
2915
+ ];
2916
+ case "many":
2917
+ case "oneOrMore":
2918
+ case "optional":
2919
+ case "attempt":
2920
+ case "transform":
2921
+ case "trivia":
2922
+ case "token":
2923
+ case "leaf":
2924
+ case "label":
2925
+ case "field":
2926
+ case "not":
2927
+ case "peek":
2928
+ case "node":
2929
+ case "withCtx":
2930
+ case "recover":
2931
+ case "expect":
2932
+ return [def.parser];
2933
+ case "grammar":
2934
+ return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
2935
+ case "sepBy":
2936
+ return [def.parser, def.separator];
2937
+ case "skip":
2938
+ return [def.main, def.skipped];
2939
+ case "scanTo":
2940
+ return [def.sentinel, ...def.skip];
2941
+ case "literal":
2942
+ case "regex":
2943
+ case "keywords":
2944
+ case "routed":
2945
+ case "guard":
2946
+ case "lazy":
2947
+ case "unknown":
2948
+ return [];
2949
+ }
2950
+ }
2951
+ function addNode(out, def) {
2952
+ if (def.type === void 0) return;
2953
+ const existing = out.get(def.type);
2954
+ if (existing === void 0) {
2955
+ out.set(def.type, def.tags === void 0 ? [] : [...def.tags]);
2956
+ return;
2957
+ }
2958
+ if (def.tags === void 0) return;
2959
+ for (const tag of def.tags) {
2960
+ if (!existing.includes(tag)) existing.push(tag);
2961
+ }
2962
+ }
2963
+ function collectFromCombinator(parser2, out, seen, followLazy = true) {
2964
+ if (seen.has(parser2)) return;
2965
+ seen.add(parser2);
2966
+ const def = parser2._def;
2967
+ if (def.tag === "lazy") {
2968
+ if (followLazy) {
2969
+ try {
2970
+ collectFromCombinator(def.thunk(), out, seen, followLazy);
2971
+ } catch {
2972
+ }
2973
+ }
2974
+ return;
2975
+ }
2976
+ if (def.tag === "node") addNode(out, def);
2977
+ for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy);
2978
+ }
2979
+ function collectGrammarReflection(ruleMap, opts) {
2980
+ const byType = /* @__PURE__ */ new Map();
2981
+ const followLazy = opts?.followLazy !== false;
2982
+ for (const [, rule] of ruleMap) {
2983
+ if (!followLazy && rule._def.tag === "lazy") {
2984
+ try {
2985
+ const resolved = rule._def.thunk();
2986
+ if (resolved._def.tag !== "lazy") collectFromCombinator(resolved, byType, /* @__PURE__ */ new Set(), false);
2987
+ } catch {
2988
+ }
2989
+ continue;
2990
+ }
2991
+ collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
2992
+ }
2993
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
2994
+ }
2995
+ function mergeGrammarReflections(reflections) {
2996
+ const byType = /* @__PURE__ */ new Map();
2997
+ for (const reflection of reflections) {
2998
+ for (const node2 of reflection.nodes) {
2999
+ const tags = byType.get(node2.type);
3000
+ if (tags === void 0) {
3001
+ byType.set(node2.type, [...node2.tags]);
3002
+ continue;
3003
+ }
3004
+ for (const tag of node2.tags) {
3005
+ if (!tags.includes(tag)) tags.push(tag);
3006
+ }
3007
+ }
3008
+ }
3009
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
3010
+ }
3011
+ function attachGrammarReflection(grammar, reflection) {
3012
+ Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true });
3013
+ return grammar;
3014
+ }
3015
+ function grammarReflectionSource(reflection) {
3016
+ return `{ nodes: ${JSON.stringify(reflection.nodes)} }`;
3017
+ }
3018
+
2813
3019
  // src/combinators/parser.ts
2814
3020
  var RULE_ORDER = "__parsemanRuleOrder";
2815
3021
  function tagRule(r, key) {
@@ -2908,6 +3114,7 @@ function rules(a, b) {
2908
3114
  enumerable: false,
2909
3115
  configurable: true
2910
3116
  });
3117
+ attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
2911
3118
  return cache;
2912
3119
  }
2913
3120
 
@@ -3228,13 +3435,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3228
3435
  const collapse = opts?.collapse === true;
3229
3436
  const captureTrivia = opts?.captureTrivia === true;
3230
3437
  const trailingTrivia = opts?.trailingTrivia === true;
3438
+ const tags = opts?.tags;
3231
3439
  if (unwrap && collapse) {
3232
3440
  throw new Error("node() options cannot set both unwrap and collapse");
3233
3441
  }
3234
3442
  if (project !== void 0 && (unwrap || collapse)) {
3235
3443
  throw new Error("node() options cannot combine project with unwrap or collapse");
3236
3444
  }
3237
- 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;
3445
+ 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;
3238
3446
  const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
3239
3447
  const clonesState = build ? buildReadsState(def) : project === void 0;
3240
3448
  const hasOwnFields = parserHasOwnFields(combinator);
@@ -3280,7 +3488,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3280
3488
  const cstOutput = cstOutputHost(ctx.build);
3281
3489
  const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3282
3490
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3283
- 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 };
3491
+ 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 };
3284
3492
  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 };
3285
3493
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3286
3494
  pushCstChild(ctx, built, rawEntry);
@@ -3330,6 +3538,25 @@ function trivia(combinator) {
3330
3538
  parse: combinator.parse.bind(combinator)
3331
3539
  };
3332
3540
  }
3541
+ function classifiedTrivia(arms) {
3542
+ const entries = Object.entries(arms);
3543
+ if (entries.length === 0) {
3544
+ throw new TypeError("classifiedTrivia() requires at least one named trivia arm.");
3545
+ }
3546
+ for (let i = 0; i < entries.length; i++) {
3547
+ const [name, arm] = entries[i];
3548
+ const first = arm._meta.firstSet;
3549
+ if (matchesEmpty(arm) || first.kind !== "ranges" || first.ranges.length === 0) {
3550
+ throw new TypeError(
3551
+ `classifiedTrivia(): ${JSON.stringify(name)} must be non-nullable with a concrete finite first set.`
3552
+ );
3553
+ }
3554
+ }
3555
+ const labeledArms = entries.map(([name, arm]) => label(name, arm));
3556
+ const result = trivia(oneOrMore(choice(...labeledArms)));
3557
+ result._meta.rootTriviaClassified = true;
3558
+ return result;
3559
+ }
3333
3560
  function label(name, combinator) {
3334
3561
  return {
3335
3562
  _tag: combinator._tag,
@@ -3376,6 +3603,7 @@ function token(root) {
3376
3603
  const savedRaw = ctx._cstRawChildren;
3377
3604
  const savedTriviaLog = ctx._cstTriviaLog;
3378
3605
  const savedOuterTriviaLog = ctx._triviaLog;
3606
+ const savedRootTriviaLog = ctx._rootTriviaLog;
3379
3607
  const wasCapturing = cstCaptureActive(ctx);
3380
3608
  ctx.trivia = void 0;
3381
3609
  ctx.triviaKindLabels = void 0;
@@ -3385,6 +3613,7 @@ function token(root) {
3385
3613
  ctx._cstRawChildren = void 0;
3386
3614
  ctx._cstTriviaLog = void 0;
3387
3615
  delete ctx._triviaLog;
3616
+ delete ctx._rootTriviaLog;
3388
3617
  let result;
3389
3618
  try {
3390
3619
  result = root.parse(input, pos, ctx);
@@ -3398,6 +3627,8 @@ function token(root) {
3398
3627
  ctx._cstTriviaLog = savedTriviaLog;
3399
3628
  if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
3400
3629
  else ctx._triviaLog = savedOuterTriviaLog;
3630
+ if (savedRootTriviaLog === void 0) delete ctx._rootTriviaLog;
3631
+ else ctx._rootTriviaLog = savedRootTriviaLog;
3401
3632
  }
3402
3633
  if (!result.ok) return result;
3403
3634
  const span = result.span;
@@ -3452,7 +3683,7 @@ function leaf(root, fn) {
3452
3683
  }
3453
3684
 
3454
3685
  // src/version.ts
3455
- var PARSEMAN_VERSION = "0.42.1";
3686
+ var PARSEMAN_VERSION = "0.44.0";
3456
3687
 
3457
3688
  // src/cst/host-mode.ts
3458
3689
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -3518,7 +3749,7 @@ var SUGGESTIONS = {
3518
3749
  function classifyBroadArm(arm, resolve2) {
3519
3750
  const seen = /* @__PURE__ */ new Set();
3520
3751
  const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
3521
- const walk2 = (p) => {
3752
+ const walk = (p) => {
3522
3753
  if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
3523
3754
  seen.add(p);
3524
3755
  const d = p._def;
@@ -3532,7 +3763,7 @@ function classifyBroadArm(arm, resolve2) {
3532
3763
  // peek() CARRIES its body's first-set, so it is only broad when the body is
3533
3764
  // (or the body is nullable) — walk in for the real cause.
3534
3765
  case "peek":
3535
- return walk2(d.parser);
3766
+ return walk(d.parser);
3536
3767
  case "scanTo":
3537
3768
  return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
3538
3769
  // `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
@@ -3546,7 +3777,7 @@ function classifyBroadArm(arm, resolve2) {
3546
3777
  case "attempt":
3547
3778
  case "withCtx":
3548
3779
  case "recover":
3549
- return walk2(d.parser);
3780
+ return walk(d.parser);
3550
3781
  case "lazy": {
3551
3782
  const name = p._ruleName;
3552
3783
  let target;
@@ -3558,13 +3789,13 @@ function classifyBroadArm(arm, resolve2) {
3558
3789
  return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
3559
3790
  target = bound;
3560
3791
  }
3561
- const inner = walk2(target);
3792
+ const inner = walk(target);
3562
3793
  if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
3563
3794
  return inner;
3564
3795
  }
3565
3796
  case "optional":
3566
3797
  case "many": {
3567
- const inner = walk2(d.parser);
3798
+ const inner = walk(d.parser);
3568
3799
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3569
3800
  }
3570
3801
  case "oneOrMore":
@@ -3577,9 +3808,9 @@ function classifyBroadArm(arm, resolve2) {
3577
3808
  case "node":
3578
3809
  case "grammar":
3579
3810
  case "expect":
3580
- return walk2(d.parser);
3811
+ return walk(d.parser);
3581
3812
  case "skip":
3582
- return walk2(d.main);
3813
+ return walk(d.main);
3583
3814
  case "sequence": {
3584
3815
  let sawNullablePrefix = false;
3585
3816
  for (const t of d.parsers) {
@@ -3588,7 +3819,7 @@ function classifyBroadArm(arm, resolve2) {
3588
3819
  continue;
3589
3820
  }
3590
3821
  if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve2))) {
3591
- const inner = walk2(t);
3822
+ const inner = walk(t);
3592
3823
  if (sawNullablePrefix)
3593
3824
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3594
3825
  return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
@@ -3603,18 +3834,18 @@ function classifyBroadArm(arm, resolve2) {
3603
3834
  }
3604
3835
  case "choice": {
3605
3836
  for (const a of d.parsers) {
3606
- const r = walk2(a);
3837
+ const r = walk(a);
3607
3838
  if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
3608
3839
  }
3609
3840
  return null;
3610
3841
  }
3611
3842
  case "dispatch":
3612
- return walk2(d.selector);
3843
+ return walk(d.selector);
3613
3844
  default:
3614
3845
  return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
3615
3846
  }
3616
3847
  };
3617
- return walk2(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3848
+ return walk(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3618
3849
  }
3619
3850
  function peelToLeading(arm) {
3620
3851
  let d = arm._def;
@@ -3816,7 +4047,7 @@ function formatGatingWarnings(report) {
3816
4047
 
3817
4048
  // src/analysis/duplication.ts
3818
4049
  var siteToString = (s) => s.path === "" ? s.rule : `${s.rule} \u203A ${s.path}`;
3819
- function childrenOf(d) {
4050
+ function childrenOf2(d) {
3820
4051
  switch (d.tag) {
3821
4052
  case "sequence":
3822
4053
  case "choice":
@@ -3950,7 +4181,7 @@ function render(p, depth = 3) {
3950
4181
  break;
3951
4182
  }
3952
4183
  if (depth <= 0) return `${d.tag}(\u2026)`;
3953
- const kids = childrenOf(d).map((k) => render(k, depth - 1));
4184
+ const kids = childrenOf2(d).map((k) => render(k, depth - 1));
3954
4185
  switch (d.tag) {
3955
4186
  case "sequence":
3956
4187
  return `sequence(${kids.join(", ")})`;
@@ -4145,7 +4376,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4145
4376
  if (hit !== void 0) return hit;
4146
4377
  const d = p._def;
4147
4378
  shapeCache.set(p, interner.hole);
4148
- const kids = childrenOf(d);
4379
+ const kids = childrenOf2(d);
4149
4380
  const id = interner.of(`${payloadKey(p, d)}|${kids.map(shapeOf).join(",")}`);
4150
4381
  shapeCache.set(p, id);
4151
4382
  return id;
@@ -4155,7 +4386,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4155
4386
  if (hit !== void 0) return hit;
4156
4387
  sizeCache.set(p, 1);
4157
4388
  let n = 1;
4158
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4389
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4159
4390
  sizeCache.set(p, n);
4160
4391
  return n;
4161
4392
  };
@@ -4168,7 +4399,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4168
4399
  visited.set(p, { p, d, site: here, shape: shapeOf(p), size: sizeOf(p), enclosingNode, enclosingReducer });
4169
4400
  const inner = d.tag === "node" ? d.type ?? "(anonymous)" : enclosingNode;
4170
4401
  const innerReducer = enclosingReducer || d.tag === "node" && (d.build !== void 0 || d.buildSrc !== void 0) || d.tag === "transform" || d.tag === "leaf";
4171
- const kids = childrenOf(d);
4402
+ const kids = childrenOf2(d);
4172
4403
  kids.forEach((k, i) => {
4173
4404
  const seg = segment(d, i);
4174
4405
  visit(k, here.rule, here.path === "" ? seg : `${here.path} \u203A ${seg}`, inner, innerReducer);
@@ -4228,7 +4459,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4228
4459
  function markSubtree(p, into) {
4229
4460
  if (into.has(p)) return;
4230
4461
  into.add(p);
4231
- for (const k of childrenOf(p._def)) markSubtree(k, into);
4462
+ for (const k of childrenOf2(p._def)) markSubtree(k, into);
4232
4463
  }
4233
4464
  var stripMembers = (f) => {
4234
4465
  const { _members, ...rest } = f;
@@ -4268,7 +4499,7 @@ function findNearDuplicates(visited, interner, shapeOf, minSize) {
4268
4499
  if (hit !== void 0) return hit;
4269
4500
  holeCache.set(p, []);
4270
4501
  const d = p._def;
4271
- const kids = childrenOf(d);
4502
+ const kids = childrenOf2(d);
4272
4503
  const kidShapes = kids.map(shapeOf);
4273
4504
  const payload = payloadKey(p, d);
4274
4505
  const out2 = [];
@@ -4329,7 +4560,7 @@ function findDivergentSlot(members, shapeOf) {
4329
4560
  let cur = [...members];
4330
4561
  for (let guard2 = 0; guard2 < 64; guard2++) {
4331
4562
  const d0 = cur[0]._def;
4332
- const kidLists = cur.map((c) => childrenOf(c._def));
4563
+ const kidLists = cur.map((c) => childrenOf2(c._def));
4333
4564
  const arity = kidLists[0].length;
4334
4565
  if (kidLists.some((k) => k.length !== arity) || arity === 0) break;
4335
4566
  const diff = [];
@@ -4342,7 +4573,7 @@ function findDivergentSlot(members, shapeOf) {
4342
4573
  path2.push(i);
4343
4574
  defs.push(d0);
4344
4575
  cur = kidLists.map((k) => k[i]);
4345
- const nextArity = childrenOf(cur[0]._def).length;
4576
+ const nextArity = childrenOf2(cur[0]._def).length;
4346
4577
  const samePayload = cur.every((c) => payloadKey(c, c._def) === payloadKey(cur[0], cur[0]._def));
4347
4578
  if (!samePayload || nextArity === 0) break;
4348
4579
  }
@@ -4350,7 +4581,7 @@ function findDivergentSlot(members, shapeOf) {
4350
4581
  let slotSize = 0;
4351
4582
  const sizeOf = (p) => {
4352
4583
  let n = 1;
4353
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4584
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4354
4585
  return n;
4355
4586
  };
4356
4587
  for (const c of cur) slotSize = Math.max(slotSize, sizeOf(c));
@@ -4359,7 +4590,7 @@ function findDivergentSlot(members, shapeOf) {
4359
4590
  function renderWithHole(p, path2, depth = 0) {
4360
4591
  if (depth === path2.length) return "\u2039slot\u203A";
4361
4592
  const d = p._def;
4362
- const kids = childrenOf(d);
4593
+ const kids = childrenOf2(d);
4363
4594
  const idx = path2[depth];
4364
4595
  const parts = kids.map((k, i) => i === idx ? renderWithHole(k, path2, depth + 1) : render(k, 2));
4365
4596
  switch (d.tag) {
@@ -4817,7 +5048,7 @@ function capturesPositionally(p, seen = /* @__PURE__ */ new Set()) {
4817
5048
  seen.add(p);
4818
5049
  const d = p._def;
4819
5050
  if (d.tag === "field" || d.tag === "label") return true;
4820
- for (const k of childrenOf(d)) if (capturesPositionally(k, seen)) return true;
5051
+ for (const k of childrenOf2(d)) if (capturesPositionally(k, seen)) return true;
4821
5052
  return false;
4822
5053
  }
4823
5054
  function detectHandRolledSepBy(terms, shapeOf) {
@@ -4923,7 +5154,7 @@ function nestedNodeTypes(root, out, seen, isRoot) {
4923
5154
  }
4924
5155
  return;
4925
5156
  }
4926
- for (const k of childrenOf(d)) nestedNodeTypes(k, out, seen, false);
5157
+ for (const k of childrenOf2(d)) nestedNodeTypes(k, out, seen, false);
4927
5158
  }
4928
5159
  var MAX_LOST_TYPES = 8;
4929
5160
  function findStructureLoss(visited, resolve2) {
@@ -5350,9 +5581,18 @@ function buildGrammarPlan(entry, winners) {
5350
5581
  return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, dispatches, attempts, labels, rules: rules2 };
5351
5582
  }
5352
5583
 
5584
+ // src/cst/trivia-entries.ts
5585
+ var EMPTY_INDICES = Object.freeze([]);
5586
+ var EMPTY_GAPS = Object.freeze([]);
5587
+ var SELECTED_ROOT_STRIDE = 5;
5588
+
5353
5589
  // src/compiler/scannable-run.ts
5354
5590
  var BACKSLASH = 92;
5355
5591
  var META = new Set("()[]{}*+?|^$.".split(""));
5592
+ var SELECTED_ROOT_OWNED_RANGE_PLACEHOLDER = Array.from(
5593
+ { length: SELECTED_ROOT_STRIDE - 3 },
5594
+ () => "0"
5595
+ ).join(", ");
5356
5596
  function classToRanges(cls) {
5357
5597
  if (cls === "\\d") return shorthandRanges("d");
5358
5598
  if (cls === "\\w") return shorthandRanges("w");
@@ -6391,13 +6631,15 @@ function scanBranch(shape, mint) {
6391
6631
  const m = emitShapeMatch(shape, "_e", mint, " ", "c");
6392
6632
  return [...m.setup, ` if (${m.end} > _e) { _e = ${m.end}; continue }`].join("\n");
6393
6633
  }
6394
- function scanBranchLabeled(shape, kindIndex, mint) {
6634
+ function scanBranchLabeled(shape, kindIndex, label2, mint) {
6395
6635
  const m = emitShapeMatch(shape, "_e", mint, " ", "c");
6396
6636
  return [
6397
6637
  ...m.setup,
6398
6638
  ` if (${m.end} > _e) {`,
6399
6639
  ` if (_cap) {`,
6400
6640
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
6641
+ ` const _rk = _ctx._rootTriviaCapture === false || _ctx._rootTriviaLog === undefined ? -1 : (_ctx._rootTriviaKindIndex?.[${JSON.stringify(label2)}] ?? -1)`,
6642
+ ` if (_rk >= 0) _ctx._rootTriviaLog.push(${SELECTED_ROOT_OWNED_RANGE_PLACEHOLDER}, _e, ${m.end}, _rk)`,
6401
6643
  ` 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})`,
6402
6644
  ` }`,
6403
6645
  ` _e = ${m.end}`,
@@ -6457,6 +6699,11 @@ var CAP_RECORD = [
6457
6699
  ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
6458
6700
  ` }`
6459
6701
  ].join("\n");
6702
+ var SELECTED_ROOT_FINISH = [
6703
+ ` if (_cap && _ctx._rootTriviaCapture !== false && _ctx._rootTriviaLog !== undefined && _ctx._rootTriviaLog.length !== _rootMark) {`,
6704
+ ` for (let _ri = _rootMark; _ri < _ctx._rootTriviaLog.length; _ri += ${SELECTED_ROOT_STRIDE}) { _ctx._rootTriviaLog[_ri] = _pos; _ctx._rootTriviaLog[_ri + 1] = _e }`,
6705
+ ` }`
6706
+ ].join("\n");
6460
6707
  function composeFastLoop(shapes) {
6461
6708
  const mint = makeMint();
6462
6709
  return [
@@ -6485,7 +6732,7 @@ function analyzeLabeledScannableRun(trivia2) {
6485
6732
  const d = getCoreRegexDef(arm.parser);
6486
6733
  const shape = d ? scanShapeFromRegex(d.source, d.flags) : null;
6487
6734
  if (!shape) return null;
6488
- out.push({ shape, kindIndex: arm.kindIndex });
6735
+ out.push({ shape, kindIndex: arm.kindIndex, label: arm.label });
6489
6736
  }
6490
6737
  return out;
6491
6738
  }
@@ -6494,11 +6741,13 @@ function buildLabeledScannableTriviaFnDecl(fnName, arms) {
6494
6741
  return [
6495
6742
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
6496
6743
  ` let _e = _pos`,
6744
+ ` const _rootMark = _cap && _ctx._rootTriviaLog !== undefined ? _ctx._rootTriviaLog.length : 0`,
6497
6745
  ` while (_e < input.length) {`,
6498
6746
  ` const c = input.charCodeAt(_e)`,
6499
- ...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, mint)),
6747
+ ...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, a.label, mint)),
6500
6748
  ` break`,
6501
6749
  ` }`,
6750
+ SELECTED_ROOT_FINISH,
6502
6751
  ` return _e`,
6503
6752
  `}`
6504
6753
  ].join("\n");
@@ -6514,6 +6763,8 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
6514
6763
  ` const _ce = _e + _m${i}[0].length`,
6515
6764
  ` if (_cap) {`,
6516
6765
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
6766
+ ` const _rk${i} = _ctx._rootTriviaCapture === false || _ctx._rootTriviaLog === undefined ? -1 : (_ctx._rootTriviaKindIndex?.[${JSON.stringify(arm.label)}] ?? -1)`,
6767
+ ` if (_rk${i} >= 0) _ctx._rootTriviaLog.push(0, 0, _e, _ce, _rk${i})`,
6517
6768
  ` 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})`,
6518
6769
  ` }`,
6519
6770
  ` _e = _ce`,
@@ -6525,11 +6776,13 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
6525
6776
  return [
6526
6777
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
6527
6778
  ` let _e = _pos`,
6779
+ ` const _rootMark = _cap && _ctx._rootTriviaLog !== undefined ? _ctx._rootTriviaLog.length : 0`,
6528
6780
  ` while (_e < input.length) {`,
6529
6781
  ` let _matched = false`,
6530
6782
  tryArms,
6531
6783
  ` if (!_matched) break`,
6532
6784
  ` }`,
6785
+ SELECTED_ROOT_FINISH,
6533
6786
  ` return _e`,
6534
6787
  `}`
6535
6788
  ].join("\n");
@@ -6552,6 +6805,8 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
6552
6805
  ` const _ce = _r${i}.span.end`,
6553
6806
  ` if (_cap) {`,
6554
6807
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
6808
+ ` const _rk${i} = _ctx._rootTriviaCapture === false || _ctx._rootTriviaLog === undefined ? -1 : (_ctx._rootTriviaKindIndex?.[${JSON.stringify(arm.label)}] ?? -1)`,
6809
+ ` if (_rk${i} >= 0) _ctx._rootTriviaLog.push(0, 0, _e, _ce, _rk${i})`,
6555
6810
  ` 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})`,
6556
6811
  ` }`,
6557
6812
  ` _e = _ce`,
@@ -6563,11 +6818,13 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
6563
6818
  return [
6564
6819
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
6565
6820
  ` let _e = _pos`,
6821
+ ` const _rootMark = _cap && _ctx._rootTriviaLog !== undefined ? _ctx._rootTriviaLog.length : 0`,
6566
6822
  ` while (_e < input.length) {`,
6567
6823
  ` let _matched = false`,
6568
6824
  tryArms,
6569
6825
  ` if (!_matched) break`,
6570
6826
  ` }`,
6827
+ SELECTED_ROOT_FINISH,
6571
6828
  ` return _e`,
6572
6829
  `}`
6573
6830
  ].join("\n");
@@ -6798,6 +7055,9 @@ function endLoweringCapture() {
6798
7055
  _loweringSink = null;
6799
7056
  return misses;
6800
7057
  }
7058
+ function hasSelectedRootTrivia(ctx) {
7059
+ return ctx.rootTriviaClassified === true;
7060
+ }
6801
7061
  function v(ctx, prefix = "_v") {
6802
7062
  return `${prefix}${ctx.vars++}`;
6803
7063
  }
@@ -7107,10 +7367,12 @@ function planDisjointDispatch(parsers) {
7107
7367
  }
7108
7368
  return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
7109
7369
  }
7110
- function captureRestoreBody(mL, mR, mTl, mLg, mF = null) {
7370
+ function captureRestoreBody(ctx, mL, mR, mTl, mLg, mF = null, mRootLg = null) {
7111
7371
  const fieldRestore = mF ? `; if (_ctx._fields && _ctx._fields.length !== ${mF}) _ctx._fields.length = ${mF}` : "";
7112
7372
  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}`;
7113
- return mLg ? `${base}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${mLg}) _ctx._triviaLog.length = ${mLg}` : base;
7373
+ const logRestore = mLg ? `; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${mLg}) _ctx._triviaLog.length = ${mLg}` : "";
7374
+ const rootLogRestore = hasSelectedRootTrivia(ctx) && mRootLg ? `; if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${mRootLg}) _ctx._rootTriviaLog.length = ${mRootLg}` : "";
7375
+ return `${base}${logRestore}${rootLogRestore}`;
7114
7376
  }
7115
7377
  function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
7116
7378
  if (seen.has(p)) return false;
@@ -7320,7 +7582,7 @@ function emitFallible(inner, ctx, pos, swallow = false) {
7320
7582
  `${ind0} ${valV} = ${r.valueVar}; ${endV} = ${r.endVar}; ${okV} = true`,
7321
7583
  `${ind0}}`,
7322
7584
  ...mL ? [
7323
- `${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null, mF)} }`
7585
+ `${ind0}if (!${okV}) { ${captureRestoreBody(ctx, mL, mR, mTl, null, mF)} }`
7324
7586
  ] : []
7325
7587
  ];
7326
7588
  return { stmts, okVar: okV, valVar: valV, endVar: endV, mayCommit };
@@ -7517,11 +7779,13 @@ function emitSeqValues(def, ctx, pos) {
7517
7779
  const markV = v(ctx, "_mk");
7518
7780
  const markTl = v(ctx, "_mktl");
7519
7781
  const markLog = v(ctx, "_mklg");
7782
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
7520
7783
  const scanEndV = v(ctx, "_sne");
7521
7784
  stmts.push(
7522
7785
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
7523
7786
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
7524
7787
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7788
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
7525
7789
  `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`,
7526
7790
  ...emitLineTrack(ctx, curV, scanEndV)
7527
7791
  );
@@ -7530,17 +7794,19 @@ function emitSeqValues(def, ctx, pos) {
7530
7794
  const endAfterV = v(ctx, "_sea");
7531
7795
  stmts.push(
7532
7796
  `${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
7533
- `${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}; }`
7797
+ `${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};` : ""} }`
7534
7798
  );
7535
7799
  valueVars.push(r2.valueVar);
7536
7800
  continue;
7537
7801
  } else {
7538
7802
  const trivFn = ensureTriviaFn(ctx);
7539
7803
  const markLog = v(ctx, "_mklg");
7804
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
7540
7805
  const scanEndV = v(ctx, "_sne");
7541
- const capArg = ctx.noHoist ? "0" : "_ctx._triviaLog !== undefined ? 2 : 0";
7806
+ const capArg = ctx.noHoist ? "0" : hasSelectedRootTrivia(ctx) ? "(_ctx._triviaLog !== undefined || _ctx._rootTriviaLog !== undefined) ? 2 : 0" : "_ctx._triviaLog !== undefined ? 2 : 0";
7542
7807
  stmts.push(
7543
7808
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7809
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
7544
7810
  `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`,
7545
7811
  ...emitLineTrack(ctx, curV, scanEndV)
7546
7812
  );
@@ -7549,7 +7815,7 @@ function emitSeqValues(def, ctx, pos) {
7549
7815
  const endAfterV = v(ctx, "_sea");
7550
7816
  stmts.push(
7551
7817
  `${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
7552
- `${ind(ctx)}if (${endAfterV} > ${scanEndV}) ${curV} = ${endAfterV}; else if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}`
7818
+ `${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};` : ""} }`
7553
7819
  );
7554
7820
  valueVars.push(r2.valueVar);
7555
7821
  continue;
@@ -7791,6 +8057,7 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7791
8057
  const selectorRawMark = selectorNeedsRollback ? v(ctx, "_dsr") : null;
7792
8058
  const selectorTriviaMark = selectorNeedsRollback ? v(ctx, "_dst") : null;
7793
8059
  const selectorLogMark = selectorNeedsRollback ? v(ctx, "_dsg") : null;
8060
+ const selectorRootLogMark = selectorNeedsRollback && hasSelectedRootTrivia(ctx) ? v(ctx, "_dsrg") : null;
7794
8061
  const selectorFieldMark = selectorNeedsRollback ? v(ctx, "_dsf") : null;
7795
8062
  const selectorErrorMark = selectorNeedsRollback ? v(ctx, "_dse") : null;
7796
8063
  const selector = emit(def.selector, ctx, pos);
@@ -7803,6 +8070,7 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7803
8070
  `${ind(ctx)}const ${selectorRawMark} = _ctx._cstRawChildren?.length ?? 0`,
7804
8071
  `${ind(ctx)}const ${selectorTriviaMark} = _ctx._cstTriviaLog?.length ?? 0`,
7805
8072
  `${ind(ctx)}const ${selectorLogMark} = _ctx._triviaLog?.length ?? 0`,
8073
+ ...selectorRootLogMark ? [`${ind(ctx)}const ${selectorRootLogMark} = _ctx._rootTriviaLog?.length ?? 0`] : [],
7806
8074
  `${ind(ctx)}const ${selectorFieldMark} = _ctx._fields?.length ?? 0`,
7807
8075
  `${ind(ctx)}const ${selectorErrorMark} = _ctx._errors?.length ?? 0`
7808
8076
  ] : [],
@@ -7822,16 +8090,18 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7822
8090
  const mayError = mayRecordRecoveryError(parser3, !!ctx.recovery);
7823
8091
  const errMark = mayError ? v(ctx, "_derr") : null;
7824
8092
  const logMark = ctx.activeTrivia ? v(ctx, "_dlog") : null;
8093
+ const rootLogMark = ctx.activeTrivia && hasSelectedRootTrivia(ctx) ? v(ctx, "_drlog") : null;
7825
8094
  if (usesRouted) {
7826
8095
  stmts.push(
7827
8096
  ...routedV ? [`${ind(ctx)}const ${routedV} = _ctx._routed; _ctx._routed = { value: ${selector.valueVar}, span: { start: ${pos}, end: ${selector.endVar} } }`] : [],
7828
8097
  ...selectorLeafMark ? [
7829
- `${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}`
8098
+ `${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}`
7830
8099
  ] : []
7831
8100
  );
7832
8101
  }
7833
8102
  if (errMark) stmts.push(`${ind(ctx)}const ${errMark} = _ctx._errors?.length ?? 0`);
7834
8103
  if (logMark) stmts.push(`${ind(ctx)}const ${logMark} = _ctx._triviaLog?.length ?? 0`);
8104
+ if (rootLogMark) stmts.push(`${ind(ctx)}const ${rootLogMark} = _ctx._rootTriviaLog?.length ?? 0`);
7835
8105
  const savedRoutedLocal = ctx.routedLocal;
7836
8106
  const savedNoHoist = ctx.noHoist;
7837
8107
  if (usesRouted && !needsRoutedContext) {
@@ -7846,11 +8116,12 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7846
8116
  stmts.push(...coverageAttempt(ctx, coverageId, pos));
7847
8117
  const errorRollback = errMark ? `if (_ctx._errors && _ctx._errors.length !== ${errMark}) _ctx._errors.length = ${errMark}; ` : "";
7848
8118
  const logRollback = logMark ? `if (_ctx._triviaLog && _ctx._triviaLog.length !== ${logMark}) _ctx._triviaLog.length = ${logMark}; ` : "";
8119
+ const rootLogRollback = rootLogMark ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${rootLogMark}) _ctx._rootTriviaLog.length = ${rootLogMark}; ` : "";
7849
8120
  const routedRollback = routedV ? `_ctx._routed = ${routedV}; ` : "";
7850
8121
  const coverageFailure = coverageId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageId)}, phase: 'failure', offset: _ctx._fe }); `;
7851
8122
  stmts.push(
7852
8123
  ...tail.stmts,
7853
- `${ind(ctx)}if (!${tail.okVar}) { ${routedRollback}${errorRollback}${logRollback}${coverageFailure}${committedFailBody(ctx)} }`,
8124
+ `${ind(ctx)}if (!${tail.okVar}) { ${routedRollback}${errorRollback}${logRollback}${rootLogRollback}${coverageFailure}${committedFailBody(ctx)} }`,
7854
8125
  ...routedV ? [`${ind(ctx)}_ctx._routed = ${routedV}`] : [],
7855
8126
  `${ind(ctx)}${outV} = ${valueMode === "tail" ? tail.valVar : `[${selector.valueVar}, ${tail.valVar}]`}`,
7856
8127
  `${ind(ctx)}${outE} = ${tail.endVar}`,
@@ -8031,11 +8302,13 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
8031
8302
  const markRaw = armNeedsRollback ? v(ctx, "_cmr") : null;
8032
8303
  const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
8033
8304
  const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
8305
+ const markRootLog = ctx.activeTrivia && hasSelectedRootTrivia(ctx) ? v(ctx, "_cmlrg") : null;
8034
8306
  const markFields = armNeedsFieldRollback ? v(ctx, "_cmf") : null;
8035
8307
  const markErrors = armMayRecordError ? v(ctx, "_cme") : null;
8036
- const captureRollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog, markFields) : "";
8308
+ const captureRollback = markLeaves ? captureRestoreBody(ctx, markLeaves, markRaw, markTl, markLog, markFields, markRootLog) : "";
8309
+ const rootRollback = markRootLog && !markLeaves ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${markRootLog}) _ctx._rootTriviaLog.length = ${markRootLog}` : "";
8037
8310
  const errorRollback = markErrors ? `if (_ctx._errors && _ctx._errors.length !== ${markErrors}) _ctx._errors.length = ${markErrors}` : "";
8038
- const rollback = [captureRollback, errorRollback].filter(Boolean).join("; ");
8311
+ const rollback = [captureRollback, rootRollback, errorRollback].filter(Boolean).join("; ");
8039
8312
  const failSlot = atStart ? staticFx : "_ctx._fx";
8040
8313
  stmts.push(`${ind0}if (${skipCond}) {`);
8041
8314
  if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
@@ -8050,6 +8323,7 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
8050
8323
  ...markFields ? [`${ind(ctx)}const ${markFields} = _ctx._fields?.length ?? 0`] : []
8051
8324
  );
8052
8325
  }
8326
+ if (markRootLog) stmts.push(`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog?.length ?? 0`);
8053
8327
  if (markErrors) stmts.push(`${ind(ctx)}const ${markErrors} = _ctx._errors?.length ?? 0`);
8054
8328
  const arm = emitFallible(p, ctx, pos, atStart);
8055
8329
  const { stmts: armStmts, okVar, valVar, endVar } = arm;
@@ -8118,7 +8392,7 @@ function routedNeedsContextBridge(p, ctx, seen = /* @__PURE__ */ new Set()) {
8118
8392
  const d = p._def;
8119
8393
  if (d.tag === "dispatch") return false;
8120
8394
  if (d.tag === "withCtx") return parserUsesRouted2(d.parser);
8121
- return childrenOf2(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8395
+ return childrenOf3(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8122
8396
  }
8123
8397
  function armReplayInScope(arm, ctx) {
8124
8398
  let p = arm;
@@ -8237,21 +8511,29 @@ function emitMany(def, ctx, pos) {
8237
8511
  const markV = v(ctx, "_mk");
8238
8512
  const markTl = v(ctx, "_mktl");
8239
8513
  const markLog = v(ctx, "_mklg");
8514
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8240
8515
  const npV = v(ctx, "_np");
8241
8516
  stmts.push(
8242
8517
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8243
8518
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8244
8519
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8520
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8245
8521
  `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8246
8522
  ...emitLineTrack(ctx, curV, npV)
8247
8523
  );
8248
8524
  itemPos = npV;
8249
- 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}; `;
8525
+ 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};` : ""} `;
8250
8526
  } else {
8251
8527
  const trivFn = ensureTriviaFn(ctx);
8528
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8252
8529
  const npV = v(ctx, "_np");
8253
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, npV));
8530
+ stmts.push(
8531
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8532
+ `${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx, ${hasSelectedRootTrivia(ctx) ? "_ctx._rootTriviaLog !== undefined ? 2 : 0" : "0"})`,
8533
+ ...emitLineTrack(ctx, curV, npV)
8534
+ );
8254
8535
  itemPos = npV;
8536
+ rollback = markRootLog ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${markRootLog}) _ctx._rootTriviaLog.length = ${markRootLog}; ` : "";
8255
8537
  }
8256
8538
  }
8257
8539
  if (!ctx.recovery && !ctx.coverage && !failsAtStart(def.parser) && needsFirstSetGuard(def.parser)) {
@@ -8311,9 +8593,10 @@ function emitAttempt(p, def, ctx, pos) {
8311
8593
  const raw = v(ctx, "_atr");
8312
8594
  const trivia2 = v(ctx, "_att");
8313
8595
  const log = v(ctx, "_atg");
8596
+ const rootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_atrg") : null;
8314
8597
  const fields = v(ctx, "_atf");
8315
8598
  const errors = v(ctx, "_ate");
8316
- 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}`;
8599
+ 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}`;
8317
8600
  const traceId = ctx.coverage?.plan.attempts.get(p);
8318
8601
  const traceRollback = traceId === void 0 ? "" : ` _ctx._grammarTrace?.write({ id: ${JSON.stringify(traceId)}, phase: 'rollback', offset: ${pos} });`;
8319
8602
  const preGuard = [];
@@ -8329,7 +8612,7 @@ function emitAttempt(p, def, ctx, pos) {
8329
8612
  return {
8330
8613
  stmts: [
8331
8614
  ...preGuard,
8332
- `${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`,
8615
+ `${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`,
8333
8616
  ...inner.stmts,
8334
8617
  ...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback}${inner.mayCommit ? ` if (_ctx._fc) ${committedFailBody(ctx)};` : ""} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
8335
8618
  ],
@@ -8371,16 +8654,17 @@ function emitSepBy(_p, def, ctx, pos) {
8371
8654
  const itemFailBreak = (sepEndVar, sepRb, postSepRb) => def.trailing === void 0 ? `${sepRb}break` : `${postSepRb}${curV} = ${sepEndVar}; break`;
8372
8655
  const postSepMarks = () => {
8373
8656
  if (def.trailing === void 0 || !ctx.capturing) return { decl: [], rb: "" };
8374
- const lv = v(ctx, "_tlv"), rw = v(ctx, "_trw"), tl = v(ctx, "_ttl"), lg = v(ctx, "_tlg"), fl = v(ctx, "_tlf");
8657
+ 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");
8375
8658
  return {
8376
8659
  decl: [
8377
8660
  `${ind(ctx)}const ${lv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8378
8661
  `${ind(ctx)}const ${rw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8379
8662
  `${ind(ctx)}const ${tl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8380
8663
  `${ind(ctx)}const ${lg} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8664
+ ...rlg ? [`${ind(ctx)}const ${rlg} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8381
8665
  `${ind(ctx)}const ${fl} = _ctx._fields ? _ctx._fields.length : 0`
8382
8666
  ],
8383
- 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}; `
8667
+ 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}; `
8384
8668
  };
8385
8669
  };
8386
8670
  if (rec) {
@@ -8418,6 +8702,7 @@ function emitSepBy(_p, def, ctx, pos) {
8418
8702
  const markV = v(ctx, "_mk");
8419
8703
  const markTl = v(ctx, "_mktl");
8420
8704
  const markLog = v(ctx, "_mklg");
8705
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8421
8706
  const markLv = v(ctx, "_mklv");
8422
8707
  const markFld = v(ctx, "_mkf");
8423
8708
  const spV = v(ctx, "_sp");
@@ -8425,13 +8710,14 @@ function emitSepBy(_p, def, ctx, pos) {
8425
8710
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8426
8711
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8427
8712
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8713
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8428
8714
  `${ind(ctx)}const ${markLv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8429
8715
  `${ind(ctx)}const ${markFld} = _ctx._fields ? _ctx._fields.length : 0`,
8430
8716
  `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8431
8717
  ...emitLineTrack(ctx, curV, spV)
8432
8718
  );
8433
8719
  sepAtPos = spV;
8434
- 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}; `;
8720
+ 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}; `;
8435
8721
  const sep2 = emitFallible(def.separator, ctx, sepAtPos, true);
8436
8722
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep2;
8437
8723
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
@@ -8449,19 +8735,30 @@ function emitSepBy(_p, def, ctx, pos) {
8449
8735
  );
8450
8736
  } else {
8451
8737
  const trivFn = ensureTriviaFn(ctx);
8738
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8452
8739
  const spV = v(ctx, "_sp");
8453
- stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, spV));
8740
+ stmts.push(
8741
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8742
+ `${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx, ${hasSelectedRootTrivia(ctx) ? "_ctx._rootTriviaLog !== undefined ? 2 : 0" : "0"})`,
8743
+ ...emitLineTrack(ctx, curV, spV)
8744
+ );
8454
8745
  sepAtPos = spV;
8455
8746
  const sep2 = emitFallible(def.separator, ctx, sepAtPos, true);
8456
8747
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep2;
8457
- stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
8748
+ const rollbackToSep = markRootLog ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${markRootLog}) _ctx._rootTriviaLog.length = ${markRootLog}; ` : "";
8749
+ stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
8750
+ const postRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_psrlg") : null;
8458
8751
  const npV = v(ctx, "_np");
8459
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`, ...emitLineTrack(ctx, sepEnd, npV));
8752
+ stmts.push(
8753
+ ...postRootLog ? [`${ind(ctx)}const ${postRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8754
+ `${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx, ${hasSelectedRootTrivia(ctx) ? "_ctx._rootTriviaLog !== undefined ? 2 : 0" : "0"})`,
8755
+ ...emitLineTrack(ctx, sepEnd, npV)
8756
+ );
8460
8757
  const next = emitFallible(def.parser, ctx, npV, true);
8461
8758
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8462
8759
  stmts.push(
8463
8760
  ...nextStmts,
8464
- ...failItem(nextOk, next.mayCommit, npV, itemFailBreak(sepEnd, "", "")),
8761
+ ...failItem(nextOk, next.mayCommit, npV, itemFailBreak(sepEnd, rollbackToSep, postRootLog ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${postRootLog}) _ctx._rootTriviaLog.length = ${postRootLog}; ` : "")),
8465
8762
  `${ind(ctx)}${arrV}.push(${nextVal})`,
8466
8763
  `${ind(ctx)}${curV} = ${nextEnd}`
8467
8764
  );
@@ -8556,6 +8853,7 @@ function emitNot(def, ctx, pos) {
8556
8853
  const raw = sinksLive ? v(ctx, "_ntr") : null;
8557
8854
  const tl = sinksLive ? v(ctx, "_ntt") : null;
8558
8855
  const log = sinksLive ? v(ctx, "_ntg") : null;
8856
+ const rootLog = sinksLive && hasSelectedRootTrivia(ctx) ? v(ctx, "_ntrg") : null;
8559
8857
  const fields = sinksLive ? v(ctx, "_ntf") : null;
8560
8858
  const errors = sinksLive ? v(ctx, "_nte") : null;
8561
8859
  const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
@@ -8563,11 +8861,11 @@ function emitNot(def, ctx, pos) {
8563
8861
  return {
8564
8862
  stmts: [
8565
8863
  ...sinksLive ? [
8566
- `${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`
8864
+ `${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`
8567
8865
  ] : [],
8568
8866
  ...stmts,
8569
8867
  ...sinksLive ? [
8570
- `${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}`
8868
+ `${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}`
8571
8869
  ] : [],
8572
8870
  ...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
8573
8871
  ],
@@ -8703,16 +9001,17 @@ function emitToken(def, ctx, pos) {
8703
9001
  const sk = v(ctx, "_tokKinds");
8704
9002
  const stl = v(ctx, "_tokTl");
8705
9003
  const sol = v(ctx, "_tokLog");
9004
+ const srl = hasSelectedRootTrivia(ctx) ? v(ctx, "_tokRootLog") : null;
8706
9005
  const sb = v(ctx, "_tokBuf");
8707
9006
  const valV = v(ctx, "_tok");
8708
9007
  return {
8709
9008
  stmts: [
8710
- `${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`,
8711
- `${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`,
9009
+ `${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`,
9010
+ `${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`,
8712
9011
  `${i}try {`,
8713
9012
  ...reindentStmts(inner.stmts, ctx.indent + 1).map((stmt) => stmt.replace(/^(\s*)(?:const|let)\s+/, "$1var ")),
8714
9013
  `${i}} finally {`,
8715
- `${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}`,
9014
+ `${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}`,
8716
9015
  `${i}}`,
8717
9016
  ...emitIfFail(ctx, `!${inner.okVar}`, propagateFailBody(ctx)),
8718
9017
  `${i}const ${valV} = input.slice(${pos}, ${inner.endVar})`,
@@ -8881,7 +9180,8 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8881
9180
  const nodeSpanV = ctx.lineTracking ? v(ctx, "_nspan") : nodeSpanExpr;
8882
9181
  if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
8883
9182
  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})`;
8884
- const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
9183
+ const hostBuildArgs = `${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV}${def.tags !== void 0 && def.tags.length > 0 ? `, ${JSON.stringify(def.tags)}` : ""}`;
9184
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${hostBuildArgs})`;
8885
9185
  const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
8886
9186
  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;
8887
9187
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
@@ -9190,6 +9490,8 @@ function emitDispatch(p, ctx, pos) {
9190
9490
  case "grammar": {
9191
9491
  const savedTrivia = ctx.activeTrivia;
9192
9492
  const savedKindLabels = ctx.triviaKindLabels;
9493
+ const opaqueRootCapture = hasSelectedRootTrivia(ctx) && def.rootCapture === "opaque";
9494
+ 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;
9193
9495
  if (def.clearTrivia) {
9194
9496
  ctx.activeTrivia = void 0;
9195
9497
  ctx.triviaKindLabels = void 0;
@@ -9200,13 +9502,38 @@ function emitDispatch(p, ctx, pos) {
9200
9502
  }
9201
9503
  }
9202
9504
  const savedCapture = def.captureTrivia ? v(ctx, "_gcap") : null;
9505
+ const savedRootCapture = opaqueRootCapture ? v(ctx, "_grtc") : null;
9506
+ const opaqueValue = opaqueRootCapture ? v(ctx, "_grtv") : null;
9507
+ const opaqueEnd = opaqueRootCapture ? v(ctx, "_grte") : null;
9508
+ if (opaqueRootCapture) ctx.indent++;
9203
9509
  const r = emit(def.parser, ctx, pos);
9510
+ if (opaqueRootCapture) ctx.indent--;
9204
9511
  ctx.activeTrivia = savedTrivia;
9205
9512
  ctx.triviaKindLabels = savedKindLabels;
9206
- if (!savedCapture) return r;
9513
+ if (!savedCapture && !opaqueRootCapture) return strictScopeCheck === void 0 ? r : {
9514
+ ...r,
9515
+ stmts: [strictScopeCheck, ...r.stmts]
9516
+ };
9517
+ if (opaqueRootCapture) {
9518
+ return {
9519
+ stmts: [
9520
+ ...strictScopeCheck === void 0 ? [] : [strictScopeCheck],
9521
+ `${ind(ctx)}let ${opaqueValue}, ${opaqueEnd}`,
9522
+ `${ind(ctx)}${savedCapture ? `const ${savedCapture} = _ctx.captureTrivia; ` : ""}const ${savedRootCapture} = _ctx._rootTriviaCapture; _ctx._rootTriviaCapture = false`,
9523
+ `${ind(ctx)}try {`,
9524
+ ...savedCapture ? [`${ind(ctx)} _ctx.captureTrivia = true`] : [],
9525
+ ...r.stmts,
9526
+ `${ind(ctx)} ${opaqueValue} = ${r.valueVar}; ${opaqueEnd} = ${r.endVar}`,
9527
+ `${ind(ctx)}} finally { ${savedCapture ? `_ctx.captureTrivia = ${savedCapture}; ` : ""}_ctx._rootTriviaCapture = ${savedRootCapture} }`
9528
+ ],
9529
+ valueVar: opaqueValue,
9530
+ endVar: opaqueEnd
9531
+ };
9532
+ }
9207
9533
  return {
9208
9534
  ...r,
9209
9535
  stmts: [
9536
+ ...strictScopeCheck === void 0 ? [] : [strictScopeCheck],
9210
9537
  `${ind(ctx)}const ${savedCapture} = _ctx.captureTrivia; _ctx.captureTrivia = true`,
9211
9538
  ...r.stmts,
9212
9539
  `${ind(ctx)}_ctx.captureTrivia = ${savedCapture}`
@@ -9344,7 +9671,7 @@ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9344
9671
  return false;
9345
9672
  }
9346
9673
  }
9347
- return childrenOf2(d).some((child) => hasLineTrackingDef(child, seen));
9674
+ return childrenOf3(d).some((child) => hasLineTrackingDef(child, seen));
9348
9675
  }
9349
9676
  function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9350
9677
  if (seen.has(p)) return false;
@@ -9363,7 +9690,7 @@ function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9363
9690
  case "dispatch":
9364
9691
  return false;
9365
9692
  default:
9366
- return childrenOf2(d).some((child) => parserUsesRouted2(child, seen));
9693
+ return childrenOf3(d).some((child) => parserUsesRouted2(child, seen));
9367
9694
  }
9368
9695
  }
9369
9696
  function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
@@ -9378,9 +9705,9 @@ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
9378
9705
  return false;
9379
9706
  }
9380
9707
  }
9381
- return childrenOf2(d).some((child) => hasDirectBuildDef(child, seen));
9708
+ return childrenOf3(d).some((child) => hasDirectBuildDef(child, seen));
9382
9709
  }
9383
- function childrenOf2(def) {
9710
+ function childrenOf3(def) {
9384
9711
  switch (def.tag) {
9385
9712
  case "sequence":
9386
9713
  case "choice":
@@ -9455,7 +9782,7 @@ function subtreeSizes(roots) {
9455
9782
  const def = p._def;
9456
9783
  let s = 1;
9457
9784
  if (def.tag !== "lazy") {
9458
- for (const child of childrenOf2(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
9785
+ for (const child of childrenOf3(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
9459
9786
  }
9460
9787
  sizes.set(p, s);
9461
9788
  return s;
@@ -9476,7 +9803,7 @@ function subtreeSizes(roots) {
9476
9803
  return;
9477
9804
  }
9478
9805
  sz(p);
9479
- for (const child of childrenOf2(def)) visit(child);
9806
+ for (const child of childrenOf3(def)) visit(child);
9480
9807
  };
9481
9808
  for (const root of roots) visit(root);
9482
9809
  return sizes;
@@ -9486,7 +9813,7 @@ function analyzeLazyUsageMulti(roots) {
9486
9813
  const recursive = /* @__PURE__ */ new Set();
9487
9814
  const descended = /* @__PURE__ */ new Set();
9488
9815
  const active = /* @__PURE__ */ new Set();
9489
- function walk2(p) {
9816
+ function walk(p) {
9490
9817
  const def = p._def;
9491
9818
  if (def.tag === "lazy") {
9492
9819
  counts.set(p, (counts.get(p) ?? 0) + 1);
@@ -9503,17 +9830,17 @@ function analyzeLazyUsageMulti(roots) {
9503
9830
  return;
9504
9831
  }
9505
9832
  active.add(p);
9506
- walk2(resolved);
9833
+ walk(resolved);
9507
9834
  active.delete(p);
9508
9835
  return;
9509
9836
  }
9510
9837
  if (isHoistableTag(def.tag)) {
9511
9838
  counts.set(p, (counts.get(p) ?? 0) + 1);
9512
9839
  }
9513
- for (const child of childrenOf2(def)) walk2(child);
9840
+ for (const child of childrenOf3(def)) walk(child);
9514
9841
  }
9515
9842
  const rootsArr = [...roots];
9516
- for (const root of rootsArr) walk2(root);
9843
+ for (const root of rootsArr) walk(root);
9517
9844
  return { counts, recursive, sizes: subtreeSizes(rootsArr) };
9518
9845
  }
9519
9846
  function ruleDependencies(ruleMap) {
@@ -9523,7 +9850,7 @@ function ruleDependencies(ruleMap) {
9523
9850
  for (const [name, comb] of ruleMap) {
9524
9851
  const found = /* @__PURE__ */ new Set();
9525
9852
  const seen = /* @__PURE__ */ new Set();
9526
- const walk2 = (p, isRoot) => {
9853
+ const walk = (p, isRoot) => {
9527
9854
  const def = p._def;
9528
9855
  const boundary = def.tag === "lazy" ? nameOf.get(p) ?? p._ruleName : nameOf.get(p);
9529
9856
  if (!isRoot && boundary !== void 0) {
@@ -9539,12 +9866,12 @@ function ruleDependencies(ruleMap) {
9539
9866
  } catch {
9540
9867
  return;
9541
9868
  }
9542
- walk2(resolved, false);
9869
+ walk(resolved, false);
9543
9870
  return;
9544
9871
  }
9545
- for (const child of childrenOf2(def)) walk2(child, false);
9872
+ for (const child of childrenOf3(def)) walk(child, false);
9546
9873
  };
9547
- walk2(comb, true);
9874
+ walk(comb, true);
9548
9875
  deps.set(name, [...found]);
9549
9876
  }
9550
9877
  return deps;
@@ -9691,6 +10018,7 @@ function compile(combinator, mapFnSources, opts) {
9691
10018
  runDuplicationDiagnostic(combinator, opts?.duplication);
9692
10019
  markUnusedValues(combinator);
9693
10020
  const grammarTrivia = combinator._meta.grammarTrivia;
10021
+ const rootTriviaClassified = grammarTrivia?._meta.rootTriviaClassified === true || combinator._meta.rootTriviaClassified === true;
9694
10022
  const grammarScanSkip = combinator._meta.grammarScanSkip;
9695
10023
  const grammarHostMode = combinator._meta.grammarHostMode;
9696
10024
  const grammarTrackLines = combinator._meta.grammarTrackLines;
@@ -9717,7 +10045,11 @@ function compile(combinator, mapFnSources, opts) {
9717
10045
  lineTracking: opts?.trackLines === true || grammarTrackLines === true || hasLineTrackingDef(combinator),
9718
10046
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
9719
10047
  lazyUsage: analyzeLazyUsage(combinator),
9720
- ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
10048
+ ...rootTriviaClassified ? { rootTriviaClassified: true } : {},
10049
+ ...grammarTrivia ? {
10050
+ activeTrivia: grammarTrivia,
10051
+ triviaKindLabels: grammarTrivia._meta.triviaKindLabels
10052
+ } : {},
9721
10053
  ...grammarScanSkip ? { activeScanSkip: grammarScanSkip } : {}
9722
10054
  };
9723
10055
  const r = emit(combinator, ctx, "_pos");
@@ -9865,10 +10197,11 @@ function assertRuleName(name) {
9865
10197
  );
9866
10198
  }
9867
10199
  }
9868
- function publicRuleWrapperSource(rule, fnSource, ambientTriviaKindLabels) {
9869
- const labels = rule._meta.triviaKindLabels ?? ambientTriviaKindLabels;
9870
- if (labels === void 0) return fnSource;
9871
- return `Object.assign(${fnSource}, { _meta: { triviaKindLabels: ${JSON.stringify(labels)} } })`;
10200
+ function publicRuleWrapperSource(rule, fnSource, ambientTriviaKindLabels, ambientRootTriviaClassified) {
10201
+ const labels = ambientTriviaKindLabels ?? rule._meta.triviaKindLabels;
10202
+ const classified = ambientRootTriviaClassified ?? rule._meta.rootTriviaClassified;
10203
+ if (labels === void 0 && classified === void 0) return fnSource;
10204
+ return `Object.assign(${fnSource}, { _meta: {${labels === void 0 ? "" : ` triviaKindLabels: ${JSON.stringify(labels)},`}${classified === void 0 ? "" : " rootTriviaClassified: true,"} } })`;
9872
10205
  }
9873
10206
  function compileRuleMap(ruleMap, opts) {
9874
10207
  runGatingDiagnosticRules(ruleMap, opts?.gating);
@@ -9876,6 +10209,7 @@ function compileRuleMap(ruleMap, opts) {
9876
10209
  for (const [, rule] of ruleMap) markUnusedValues(rule);
9877
10210
  const coverageWinners = opts?.coverage ? Object.fromEntries(ruleMap.filter(([, rule]) => rule._def.tag !== "lazy")) : void 0;
9878
10211
  const grammarTrivia = opts?.trivia;
10212
+ const rootTriviaClassified = grammarTrivia?._meta.rootTriviaClassified === true || ruleMap.some(([, rule]) => rule._meta.rootTriviaClassified === true);
9879
10213
  const grammarScanSkip = opts?.scanSkip ?? ruleMap.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
9880
10214
  const grammarHostMode = opts?.hostMode ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
9881
10215
  const grammarTrackLines = opts?.trackLines === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
@@ -9902,7 +10236,11 @@ function compileRuleMap(ruleMap, opts) {
9902
10236
  lineTracking: grammarTrackLines,
9903
10237
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(ruleMap.map(([, rule]) => rule), coverageWinners) } } : {},
9904
10238
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
9905
- ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
10239
+ ...rootTriviaClassified ? { rootTriviaClassified: true } : {},
10240
+ ...grammarTrivia ? {
10241
+ activeTrivia: grammarTrivia,
10242
+ triviaKindLabels: grammarTrivia._meta.triviaKindLabels
10243
+ } : {},
9906
10244
  ...grammarScanSkip ? { activeScanSkip: grammarScanSkip } : {}
9907
10245
  };
9908
10246
  const seenNames = /* @__PURE__ */ new Map();
@@ -9990,7 +10328,7 @@ function compileRuleMap(ruleMap, opts) {
9990
10328
  return [`function(input, _pos, _ctx) {`, ...body, `}`].join("\n");
9991
10329
  };
9992
10330
  const objBody = perEntry.map(({ key, rule, r }) => {
9993
- const src = publicRuleWrapperSource(rule, entryFnText(r, rule), ctx.triviaKindLabels);
10331
+ const src = publicRuleWrapperSource(rule, entryFnText(r, rule), ctx.triviaKindLabels, ctx.rootTriviaClassified);
9994
10332
  return ` ${JSON.stringify(key)}: ${src.split("\n").join("\n ")}`;
9995
10333
  }).join(",\n");
9996
10334
  const replacement = [
@@ -10009,6 +10347,7 @@ function compileRuleMap(ruleMap, opts) {
10009
10347
  // host check passes vacuously — see `withHostMode` in the plugin.
10010
10348
  hostMode: ctx.hostMode ?? "ast",
10011
10349
  hostBranchElided: !!ctx.hostBranchElided,
10350
+ reflection: collectGrammarReflection(ruleMap),
10012
10351
  ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions }
10013
10352
  };
10014
10353
  }
@@ -10018,6 +10357,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10018
10357
  if (opts?.duplication !== void 0) runDuplicationDiagnosticRules(ruleMapArg, opts.duplication);
10019
10358
  for (const [, rule] of ruleMapArg) markUnusedValues(rule);
10020
10359
  const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
10360
+ const rootTriviaClassified = grammarTrivia?._meta.rootTriviaClassified === true || ruleMapArg.some(([, rule]) => rule._meta.rootTriviaClassified === true);
10021
10361
  const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
10022
10362
  const grammarHostMode = opts?.hostMode ?? ruleMapArg.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
10023
10363
  const grammarTrackLines = opts?.trackLines === true || ruleMapArg.some(([, r]) => r._meta.grammarTrackLines === true);
@@ -10031,6 +10371,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10031
10371
  return false;
10032
10372
  }
10033
10373
  });
10374
+ const nodeMeta = new Map(ruleMap.map(([name, rule]) => [name, collectGrammarReflection([[name, rule]], { followLazy: false })]));
10034
10375
  const ctx = {
10035
10376
  vars: 0,
10036
10377
  indent: 1,
@@ -10060,7 +10401,11 @@ function compileLinkable(ruleMapArg, ns, opts) {
10060
10401
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
10061
10402
  ns,
10062
10403
  deferFirstSetRefs: true,
10063
- ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
10404
+ ...rootTriviaClassified ? { rootTriviaClassified: true } : {},
10405
+ ...grammarTrivia ? {
10406
+ activeTrivia: grammarTrivia,
10407
+ triviaKindLabels: grammarTrivia._meta.triviaKindLabels
10408
+ } : {},
10064
10409
  ...grammarScanSkip ? { activeScanSkip: grammarScanSkip } : {}
10065
10410
  };
10066
10411
  const seen = /* @__PURE__ */ new Map();
@@ -10106,7 +10451,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10106
10451
  scanExternal(resolved);
10107
10452
  return;
10108
10453
  }
10109
- for (const child of childrenOf2(def)) scanExternal(child);
10454
+ for (const child of childrenOf3(def)) scanExternal(child);
10110
10455
  };
10111
10456
  for (const [, rule] of ruleMap) scanExternal(rule);
10112
10457
  }
@@ -10192,7 +10537,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10192
10537
  ...r.stmts,
10193
10538
  ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`,
10194
10539
  `}`
10195
- ].join("\n"), ctx.triviaKindLabels));
10540
+ ].join("\n"), ctx.triviaKindLabels, ctx.rootTriviaClassified));
10196
10541
  }
10197
10542
  const firstSets = /* @__PURE__ */ new Map();
10198
10543
  const firstSetRecipes = /* @__PURE__ */ new Map();
@@ -10228,6 +10573,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10228
10573
  hostBranchElided: !!ctx.hostBranchElided,
10229
10574
  hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
10230
10575
  isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs),
10576
+ nodeMeta,
10231
10577
  mfFns: mfSrcs ? [] : ctx.mapFns,
10232
10578
  buildFns: buildSrcs ? [] : ctx.buildFns
10233
10579
  };
@@ -10248,7 +10594,7 @@ function hasExternalRuleRef(ruleMap) {
10248
10594
  if (resolved === void 0) return p._ruleName !== void 0;
10249
10595
  return visit(resolved);
10250
10596
  }
10251
- return childrenOf2(def).some(visit);
10597
+ return childrenOf3(def).some(visit);
10252
10598
  };
10253
10599
  return ruleMap.some(([, rule]) => visit(rule));
10254
10600
  }
@@ -10270,7 +10616,7 @@ function hasSemanticReduction(roots, externalRefs) {
10270
10616
  return true;
10271
10617
  }
10272
10618
  }
10273
- return childrenOf2(def).some(visit);
10619
+ return childrenOf3(def).some(visit);
10274
10620
  };
10275
10621
  return roots.some(visit);
10276
10622
  }
@@ -10343,7 +10689,7 @@ function withCtx(extra, combinator) {
10343
10689
  }
10344
10690
 
10345
10691
  // src/compiler/ir-serialize.ts
10346
- function childrenOf3(def) {
10692
+ function childrenOf4(def) {
10347
10693
  switch (def.tag) {
10348
10694
  case "sequence":
10349
10695
  case "choice":
@@ -10407,6 +10753,10 @@ function projectOpt(project) {
10407
10753
  if (project === void 0) return void 0;
10408
10754
  return `project: ${project}`;
10409
10755
  }
10756
+ function tagsOpt(tags) {
10757
+ if (tags === void 0 || tags.length === 0) return void 0;
10758
+ return `tags: ${JSON.stringify(tags)}`;
10759
+ }
10410
10760
  function matcherExpr(entry) {
10411
10761
  switch (entry.kind) {
10412
10762
  case "startsWith":
@@ -10536,6 +10886,7 @@ function evalRuleMapIR(ir) {
10536
10886
  "transform",
10537
10887
  "skip",
10538
10888
  "trivia",
10889
+ "classifiedTrivia",
10539
10890
  "label",
10540
10891
  "field",
10541
10892
  "expect",
@@ -10576,6 +10927,7 @@ function evalRuleMapIR(ir) {
10576
10927
  transform,
10577
10928
  skip,
10578
10929
  trivia,
10930
+ classifiedTrivia,
10579
10931
  label,
10580
10932
  field,
10581
10933
  expect,
@@ -10620,7 +10972,7 @@ var Serializer = class {
10620
10972
  if (target) this.assertNoRuleRef(target, seen);
10621
10973
  return;
10622
10974
  }
10623
- for (const child of childrenOf3(c._def)) this.assertNoRuleRef(child, seen);
10975
+ for (const child of childrenOf4(c._def)) this.assertNoRuleRef(child, seen);
10624
10976
  }
10625
10977
  /** A `rules()` map wraps each rule value in a named `lazy` proxy; the real body is
10626
10978
  * its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
@@ -10684,7 +11036,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10684
11036
  this.counts.set(c, (this.counts.get(c) ?? 0) + 1);
10685
11037
  if ((this.counts.get(c) ?? 0) > 1) return;
10686
11038
  active.add(c);
10687
- for (const child of childrenOf3(def)) this.analyze(child, active);
11039
+ for (const child of childrenOf4(def)) this.analyze(child, active);
10688
11040
  active.delete(c);
10689
11041
  }
10690
11042
  /** Reference `c`: a const var if it has one (emitting its decl first), else inline.
@@ -10801,12 +11153,14 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10801
11153
  case "node": {
10802
11154
  if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
10803
11155
  const projectEntry = projectOpt(def.project);
11156
+ const tagEntry = tagsOpt(def.tags);
10804
11157
  const optEntries = [
10805
11158
  ...def.unwrap ? ["unwrap: true"] : [],
10806
11159
  ...def.collapse ? ["collapse: true"] : [],
10807
11160
  ...projectEntry === void 0 ? [] : [projectEntry],
10808
11161
  ...def.captureTrivia ? ["captureTrivia: true"] : [],
10809
- ...def.trailingTrivia ? ["trailingTrivia: true"] : []
11162
+ ...def.trailingTrivia ? ["trailingTrivia: true"] : [],
11163
+ ...tagEntry === void 0 ? [] : [tagEntry]
10810
11164
  ];
10811
11165
  const opts = optEntries.length > 0 ? `, { ${optEntries.join(", ")} }` : "";
10812
11166
  if (def.type === void 0) {
@@ -10820,8 +11174,11 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10820
11174
  return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
10821
11175
  }
10822
11176
  case "grammar": {
10823
- const trivia2 = def.clearTrivia ? "null" : def.triviaParser ? kid(def.triviaParser) : "undefined";
10824
- return `parser({ trivia: ${trivia2}${def.captureTrivia ? ", captureTrivia: true" : ""}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
11177
+ const classified = def.triviaParser?._meta.rootTriviaClassified === true;
11178
+ const spec = classified && def.triviaParser ? analyzeLabeledTrivia(def.triviaParser) : null;
11179
+ if (classified && !spec) throw new Unserializable("classified trivia without labeled arms");
11180
+ 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";
11181
+ return `parser({ trivia: ${trivia2}${def.captureTrivia ? ", captureTrivia: true" : ""}${def.rootCapture ? `, rootCapture: ${JSON.stringify(def.rootCapture)}` : ""}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
10825
11182
  }
10826
11183
  case "withCtx": {
10827
11184
  if (def.extraSrc === void 0) throw new Unserializable("withCtx without extraSrc");
@@ -10844,15 +11201,16 @@ function normalizeCstCollapse(collapse) {
10844
11201
  }
10845
11202
  return typeof collapse === "function" ? collapse : void 0;
10846
11203
  }
10847
- function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
10848
- return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children2] };
11204
+ function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state, tags) {
11205
+ 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] };
10849
11206
  }
10850
- function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
11207
+ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state, _tags) {
10851
11208
  if (typeof typeOrOptions === "string") {
10852
11209
  return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
10853
11210
  }
10854
11211
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
10855
- const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2);
11212
+ const materializeTags = typeOrOptions?.tags === true;
11213
+ const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2, tags) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2, materializeTags ? tags : void 0);
10856
11214
  host._parsemanCstOutput = true;
10857
11215
  if (collapse) host._parsemanCstCollapse = collapse;
10858
11216
  return host;
@@ -10873,7 +11231,7 @@ function pickPieces(pieces, names) {
10873
11231
  };
10874
11232
  for (const n of names) visit(n);
10875
11233
  const filt = (m, pc) => new Map([...m].filter(([k]) => keep.has(k) && winner.get(k) === pc));
10876
- return pieces.map((pc) => ({ ...pc, keys: pc.keys.filter((k) => keep.has(k) && winner.get(k) === pc), ruleFns: filt(pc.ruleFns, pc), wrappers: filt(pc.wrappers, pc), deps: filt(pc.deps, pc) })).filter((pc) => pc.keys.length > 0);
11234
+ return pieces.map((pc) => ({ ...pc, keys: pc.keys.filter((k) => keep.has(k) && winner.get(k) === pc), ruleFns: filt(pc.ruleFns, pc), wrappers: filt(pc.wrappers, pc), deps: filt(pc.deps, pc), nodeMeta: filt(pc.nodeMeta, pc) })).filter((pc) => pc.keys.length > 0);
10877
11235
  }
10878
11236
  function fusedBody(pieces) {
10879
11237
  for (const p of pieces) {
@@ -10911,6 +11269,7 @@ function fusedBody(pieces) {
10911
11269
  ...[...winner].map(([k, p]) => p.ruleFns.get(k))
10912
11270
  ];
10913
11271
  const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
11272
+ const reflection = mergeGrammarReflections([...winner].map(([k, p]) => p.nodeMeta.get(k) ?? { nodes: [] }));
10914
11273
  const { mode, elided } = hostModeOfPieces(pieces);
10915
11274
  const rawBody = [
10916
11275
  ...lines,
@@ -10919,6 +11278,7 @@ function fusedBody(pieces) {
10919
11278
  "}",
10920
11279
  `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_MODE.description)}), { value: ${JSON.stringify(mode)}, enumerable: false })`,
10921
11280
  `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_ELIDED.description)}), { value: ${JSON.stringify(elided)}, enumerable: false })`,
11281
+ `Object.defineProperty(_map, Symbol.for('parseman.grammarReflection'), { value: ${grammarReflectionSource(reflection)}, enumerable: false })`,
10922
11282
  // …and on each rule FUNCTION, because `run(map.Rule, …)` is handed the rule, not the
10923
11283
  // map, and would otherwise have nothing to check against. Done once at fuse time,
10924
11284
  // before any rule has been called, so it costs nothing per parse.
@@ -11050,10 +11410,6 @@ function gate(predicate) {
11050
11410
  };
11051
11411
  }
11052
11412
 
11053
- // src/cst/trivia-entries.ts
11054
- var EMPTY_INDICES = Object.freeze([]);
11055
- var EMPTY_GAPS = Object.freeze([]);
11056
-
11057
11413
  // src/cst/offset-model.ts
11058
11414
  var { isFinite } = Number;
11059
11415
 
@@ -11209,19 +11565,19 @@ function directBuilderUnsupportedBindings(src) {
11209
11565
  // src/plugin/evaluator.ts
11210
11566
  function stripTsFromSource(node2, code) {
11211
11567
  const cuts = [];
11212
- const walk2 = (n) => {
11568
+ const walk = (n) => {
11213
11569
  if (!n || typeof n !== "object") return;
11214
11570
  const rec = n;
11215
11571
  if (typeof rec.type === "string" && rec.type.startsWith("TS") && typeof rec.start === "number" && typeof rec.end === "number") {
11216
11572
  const ex = rec.expression;
11217
11573
  if ((rec.type === "TSAsExpression" || rec.type === "TSSatisfiesExpression" || rec.type === "TSNonNullExpression" || rec.type === "TSInstantiationExpression") && ex && typeof ex.end === "number") {
11218
11574
  cuts.push([ex.end, rec.end]);
11219
- walk2(rec.expression);
11575
+ walk(rec.expression);
11220
11576
  return;
11221
11577
  }
11222
11578
  if (rec.type === "TSTypeAssertion" && ex && typeof ex.start === "number") {
11223
11579
  cuts.push([rec.start, ex.start]);
11224
- walk2(rec.expression);
11580
+ walk(rec.expression);
11225
11581
  return;
11226
11582
  }
11227
11583
  cuts.push([rec.start, rec.end]);
@@ -11231,11 +11587,11 @@ function stripTsFromSource(node2, code) {
11231
11587
  if (key === "type" || key === "start" || key === "end") continue;
11232
11588
  const v2 = rec[key];
11233
11589
  if (Array.isArray(v2)) {
11234
- for (const item of v2) walk2(item);
11235
- } else if (v2 && typeof v2 === "object") walk2(v2);
11590
+ for (const item of v2) walk(item);
11591
+ } else if (v2 && typeof v2 === "object") walk(v2);
11236
11592
  }
11237
11593
  };
11238
- walk2(node2);
11594
+ walk(node2);
11239
11595
  const start = node2.start;
11240
11596
  const end = node2.end;
11241
11597
  if (cuts.length === 0) return code.slice(start, end);
@@ -11305,6 +11661,9 @@ var SUPPORTED = {
11305
11661
  optional: (...a) => optional(a[0]),
11306
11662
  skip: (...a) => skip(a[0], a[1]),
11307
11663
  trivia: (...a) => trivia(a[0]),
11664
+ classifiedTrivia: (...a) => classifiedTrivia(
11665
+ a[0]
11666
+ ),
11308
11667
  label: (...a) => label(a[0], a[1]),
11309
11668
  field: (...a) => field(a[0], a[1]),
11310
11669
  noTrivia: (...a) => noTrivia(a[0]),
@@ -11414,10 +11773,38 @@ function scopeGet(scope, name, mfs) {
11414
11773
  if (isCombinator(entry)) return entry;
11415
11774
  return null;
11416
11775
  }
11776
+ function unwrapStaticExpr(expr) {
11777
+ let cur = expr;
11778
+ while (cur.type === "TSAsExpression" || cur.type === "TSSatisfiesExpression" || cur.type === "TSNonNullExpression" || cur.type === "TSTypeAssertion" || cur.type === "TSInstantiationExpression" || cur.type === "ParenthesizedExpression") {
11779
+ if (!cur.expression) break;
11780
+ cur = cur.expression;
11781
+ }
11782
+ return cur;
11783
+ }
11417
11784
  function staticLiteralValue(expr) {
11418
- const val = expr;
11785
+ const val = unwrapStaticExpr(expr);
11419
11786
  return val.type === "Literal" || val.type === "BooleanLiteral" || val.type === "NumericLiteral" ? val.value : void 0;
11420
11787
  }
11788
+ function staticStringArray(expr, scope) {
11789
+ const id = unwrapStaticExpr(expr);
11790
+ if (id.type === "Identifier" && scope !== void 0 && id.name !== void 0) {
11791
+ const scoped = scope.get(id.name);
11792
+ const value = isStaticValueEntry(scoped) ? scoped.value : scoped;
11793
+ return Array.isArray(value) && value.every((v2) => typeof v2 === "string") ? value : void 0;
11794
+ }
11795
+ const arr = id;
11796
+ if (arr.type !== "ArrayExpression" || !Array.isArray(arr.elements)) return void 0;
11797
+ const out = [];
11798
+ for (const el of arr.elements) {
11799
+ if (!el || el.type === "SpreadElement") return void 0;
11800
+ const value = staticLiteralValue(el);
11801
+ if (typeof value !== "string") return void 0;
11802
+ out.push(value);
11803
+ }
11804
+ return out;
11805
+ }
11806
+ var STATIC_NODE_OPTIONS_FAILED = /* @__PURE__ */ Symbol("parseman.staticNodeOptions.failed");
11807
+ var STATIC_NODE_OPTIONS_NOT_OPTIONS = /* @__PURE__ */ Symbol("parseman.staticNodeOptions.notOptions");
11421
11808
  function staticNodeProject(expr) {
11422
11809
  const literalValue = staticLiteralValue(expr);
11423
11810
  if (typeof literalValue === "number" && Number.isInteger(literalValue) && literalValue >= 0) {
@@ -11426,23 +11813,60 @@ function staticNodeProject(expr) {
11426
11813
  if (typeof literalValue === "number" || literalValue !== void 0) return { ok: false };
11427
11814
  return { ok: false };
11428
11815
  }
11429
- function staticNodeOptions(expr) {
11430
- if (expr.type !== "ObjectExpression") return void 0;
11816
+ function scopedStaticValue(expr, scope) {
11817
+ const unwrapped = unwrapStaticExpr(expr);
11818
+ if (unwrapped.type !== "Identifier" || unwrapped.name === "undefined") return { found: false };
11819
+ if (!scope.has(unwrapped.name)) return { found: false };
11820
+ const scoped = scope.get(unwrapped.name);
11821
+ const value = isStaticValueEntry(scoped) ? scoped.value : scoped;
11822
+ return { found: true, value };
11823
+ }
11824
+ function staticNodeOptionsFromValue(value) {
11825
+ if (value === null || typeof value !== "object" || Array.isArray(value) || isCombinator(value)) return STATIC_NODE_OPTIONS_NOT_OPTIONS;
11826
+ const opts = {};
11827
+ const rec = value;
11828
+ for (const name of Object.keys(rec)) {
11829
+ const v2 = rec[name];
11830
+ if (name === "unwrap" || name === "collapse" || name === "captureTrivia" || name === "trailingTrivia") {
11831
+ if (v2 === true) opts[name] = true;
11832
+ else if (v2 !== false && v2 !== void 0) return STATIC_NODE_OPTIONS_FAILED;
11833
+ } else if (name === "project") {
11834
+ if (typeof v2 !== "number" || !Number.isInteger(v2) || v2 < 0) return STATIC_NODE_OPTIONS_FAILED;
11835
+ opts.project = v2;
11836
+ } else if (name === "tags") {
11837
+ if (!Array.isArray(v2) || !v2.every((item) => typeof item === "string")) return STATIC_NODE_OPTIONS_FAILED;
11838
+ opts.tags = v2;
11839
+ }
11840
+ }
11841
+ return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 ? opts : void 0;
11842
+ }
11843
+ function staticNodeOptions(expr, scope) {
11844
+ const unwrapped = unwrapStaticExpr(expr);
11845
+ const scoped = scopedStaticValue(unwrapped, scope);
11846
+ if (scoped.found) return staticNodeOptionsFromValue(scoped.value);
11847
+ if (unwrapped.type !== "ObjectExpression") return STATIC_NODE_OPTIONS_NOT_OPTIONS;
11431
11848
  const opts = {};
11432
- for (const prop of expr.properties) {
11849
+ for (const prop of unwrapped.properties) {
11850
+ if (prop.type !== "Property") return STATIC_NODE_OPTIONS_FAILED;
11433
11851
  const p = prop;
11434
- if (p.computed) continue;
11852
+ if (p.computed) return STATIC_NODE_OPTIONS_FAILED;
11435
11853
  const key = p.key;
11436
11854
  const name = key.type === "Identifier" ? key.name : key.type === "Literal" ? String(key.value) : void 0;
11437
11855
  if (name === "unwrap" || name === "collapse" || name === "captureTrivia" || name === "trailingTrivia") {
11438
- if (staticLiteralValue(p.value) === true) opts[name] = true;
11856
+ const value = staticLiteralValue(p.value);
11857
+ if (value === true) opts[name] = true;
11858
+ else if (value !== false && value !== void 0) return STATIC_NODE_OPTIONS_FAILED;
11439
11859
  } else if (name === "project") {
11440
11860
  const project = staticNodeProject(p.value);
11441
- if (project?.ok === false) return { project: -1 };
11861
+ if (project?.ok === false) return STATIC_NODE_OPTIONS_FAILED;
11442
11862
  if (project !== void 0) opts.project = project.value;
11863
+ } else if (name === "tags") {
11864
+ const tags = staticStringArray(p.value, scope);
11865
+ if (tags === void 0) return STATIC_NODE_OPTIONS_FAILED;
11866
+ opts.tags = tags;
11443
11867
  }
11444
11868
  }
11445
- return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia ? opts : void 0;
11869
+ return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 ? opts : void 0;
11446
11870
  }
11447
11871
  function exprToCombi(node2, scope, code, mfs) {
11448
11872
  if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
@@ -11517,10 +11941,23 @@ function exprToCombi(node2, scope, code, mfs) {
11517
11941
  const inner = anyValue(parserArg, scope, code, mfs);
11518
11942
  if (!isCombinator(inner)) return null;
11519
11943
  const be = buildArg;
11520
- const hasBuild = be !== void 0 && be.type !== "SpreadElement" && be.type !== "ObjectExpression" && !(be.type === "Identifier" && be.name === "undefined");
11944
+ const buildExpr = be === void 0 || be.type === "SpreadElement" ? void 0 : unwrapStaticExpr(be);
11945
+ const buildArgOptions = be !== void 0 && be.type !== "SpreadElement" ? staticNodeOptions(buildArg, scope) : STATIC_NODE_OPTIONS_NOT_OPTIONS;
11946
+ if (buildArgOptions === STATIC_NODE_OPTIONS_FAILED) return null;
11947
+ const buildArgIsOptions = buildArgOptions !== STATIC_NODE_OPTIONS_NOT_OPTIONS;
11948
+ const scopedBuild = buildExpr === void 0 ? { found: false } : scopedStaticValue(buildExpr, scope);
11949
+ const absentBuild = buildExpr?.type === "Identifier" && buildExpr.name === "undefined" || buildExpr !== void 0 && staticLiteralValue(buildExpr) === null || scopedBuild.found && (scopedBuild.value === void 0 || scopedBuild.value === null);
11950
+ const hasBuild = be !== void 0 && be.type !== "SpreadElement" && !buildArgIsOptions && !absentBuild;
11521
11951
  const buildSrc = hasBuild ? stripTsFromSource(be, code) : void 0;
11522
- const optionsArg = hasBuild ? optsArg : be?.type === "ObjectExpression" ? buildArg : optsArg;
11523
- const opts = optionsArg !== void 0 && optionsArg.type !== "SpreadElement" ? staticNodeOptions(optionsArg) : void 0;
11952
+ let opts;
11953
+ if (buildArgIsOptions) {
11954
+ opts = buildArgOptions;
11955
+ } else if (optsArg !== void 0) {
11956
+ if (optsArg.type === "SpreadElement") return null;
11957
+ const optsResult = staticNodeOptions(optsArg, scope);
11958
+ if (optsResult === STATIC_NODE_OPTIONS_FAILED || optsResult === STATIC_NODE_OPTIONS_NOT_OPTIONS) return null;
11959
+ opts = optsResult;
11960
+ }
11524
11961
  try {
11525
11962
  const combi = explicitType !== void 0 ? node(explicitType, inner, hasBuild ? () => null : void 0, opts) : node(inner, hasBuild ? () => null : void 0, opts);
11526
11963
  if (combi._def.tag === "node" && buildSrc !== void 0) {
@@ -11743,6 +12180,10 @@ function exprToCombi(node2, scope, code, mfs) {
11743
12180
  }
11744
12181
  }
11745
12182
  function anyValue(node2, scope, code, mfs) {
12183
+ if (node2.type === "TSAsExpression" || node2.type === "TSSatisfiesExpression" || node2.type === "TSNonNullExpression" || node2.type === "TSTypeAssertion" || node2.type === "TSInstantiationExpression" || node2.type === "ParenthesizedExpression") {
12184
+ const inner = node2.expression;
12185
+ return inner ? anyValue(inner, scope, code, mfs) : null;
12186
+ }
11746
12187
  if (node2.type === "Literal") {
11747
12188
  if ("regex" in node2 && node2.regex !== null && node2.regex !== void 0) {
11748
12189
  return new RegExp(node2.regex.pattern, node2.regex.flags);
@@ -11866,6 +12307,9 @@ function collectRuleKeys(retObj) {
11866
12307
  }
11867
12308
  return out;
11868
12309
  }
12310
+ function evaluateStaticValue(node2, scope, code) {
12311
+ return anyValue(node2, scope, code, []);
12312
+ }
11869
12313
  function flattenRuleEntries(retObj, scope, code) {
11870
12314
  const out = [];
11871
12315
  for (const prop of retObj.properties) {
@@ -12031,6 +12475,18 @@ function referencesAny(node2, names, scope) {
12031
12475
  // src/plugin/index.ts
12032
12476
  import { createHash } from "node:crypto";
12033
12477
  var PARSEMAN_MODULE = "parseman";
12478
+ function unwrapStaticExpression(expr) {
12479
+ let cur = expr;
12480
+ while (cur.type === "TSAsExpression" || cur.type === "TSSatisfiesExpression" || cur.type === "TSNonNullExpression" || cur.type === "TSTypeAssertion" || cur.type === "TSInstantiationExpression" || cur.type === "ParenthesizedExpression") {
12481
+ if (!cur.expression) break;
12482
+ cur = cur.expression;
12483
+ }
12484
+ return cur;
12485
+ }
12486
+ function isStaticNullishExpression(expr) {
12487
+ const unwrapped = unwrapStaticExpression(expr);
12488
+ return unwrapped.type === "Identifier" && unwrapped.name === "undefined" || unwrapped.type === "Literal" && unwrapped.value === null;
12489
+ }
12034
12490
  var _compiledResolver = null;
12035
12491
  function getCompiledResolver() {
12036
12492
  return _compiledResolver ??= new ResolverFactory({
@@ -12513,6 +12969,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12513
12969
  ruleMap: evaluated.ruleMap,
12514
12970
  hostMode: compiled?.hostMode ?? "ast",
12515
12971
  hostBranchElided: compiled?.hostBranchElided ?? false,
12972
+ reflection: compiled?.reflection ?? { nodes: [] },
12516
12973
  ...compiled?.coverageDefinitions ? { coverageDefinitions: compiled.coverageDefinitions } : {},
12517
12974
  ...evaluated.trivia ? { trivia: evaluated.trivia } : {},
12518
12975
  ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {},
@@ -12533,7 +12990,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12533
12990
  const mapLit = (m, stripVals = false) => `new Map([${[...m].map(
12534
12991
  ([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(stripVals && typeof v2 === "string" ? stripIndent(v2) : v2)}]`
12535
12992
  ).join(", ")}])`;
12536
- return `{ v: ${JSON.stringify(p.v)}, ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude.map(stripIndent))}, ruleFns: ${mapLit(p.ruleFns, true)}, wrappers: ${mapLit(p.wrappers, true)}, firstSets: ${mapLit(p.firstSets)}, nullable: ${p.nullable ? mapLit(p.nullable) : "new Map()"}, firstSetRecipes: ${p.firstSetRecipes ? mapLit(p.firstSetRecipes) : "new Map()"}, deps: ${mapLit(p.deps)}, hostMode: ${JSON.stringify(p.hostMode ?? "ast")}, hostBranchElided: ${p.hostBranchElided === true}, needsEmptyTl: ${p.needsEmptyTl}, needsHostReads: ${p.needsHostReads}, hasDirectBuilders: ${p.hasDirectBuilders === true}, isRecognitionOnly: ${p.isRecognitionOnly === true}, mfFns: [], buildFns: [] }`;
12993
+ return `{ v: ${JSON.stringify(p.v)}, ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude.map(stripIndent))}, ruleFns: ${mapLit(p.ruleFns, true)}, wrappers: ${mapLit(p.wrappers, true)}, firstSets: ${mapLit(p.firstSets)}, nullable: ${p.nullable ? mapLit(p.nullable) : "new Map()"}, firstSetRecipes: ${p.firstSetRecipes ? mapLit(p.firstSetRecipes) : "new Map()"}, deps: ${mapLit(p.deps)}, nodeMeta: ${mapLit(p.nodeMeta)}, hostMode: ${JSON.stringify(p.hostMode ?? "ast")}, hostBranchElided: ${p.hostBranchElided === true}, needsEmptyTl: ${p.needsEmptyTl}, needsHostReads: ${p.needsHostReads}, hasDirectBuilders: ${p.hasDirectBuilders === true}, isRecognitionOnly: ${p.isRecognitionOnly === true}, mfFns: [], buildFns: [] }`;
12537
12994
  };
12538
12995
  const isSpread = (it) => "__spreadLocal" in it;
12539
12996
  const isIR = (it) => "ir" in it && !("ruleFns" in it);
@@ -12542,6 +12999,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12542
12999
  const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
12543
13000
  const withLeafMarker = (grammarExpr) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.leafComposed'), { value: true, enumerable: false })`;
12544
13001
  const withHostMode = (grammarExpr, mode, elided) => `/* @__PURE__ */ (m => { for (const k of Object.keys(m)) { Object.defineProperty(m[k], Symbol.for('parseman.fusedHostMode'), { value: ${JSON.stringify(mode)}, enumerable: false }); Object.defineProperty(m[k], Symbol.for('parseman.fusedHostElided'), { value: ${JSON.stringify(elided)}, enumerable: false }) } Object.defineProperty(m, Symbol.for('parseman.fusedHostMode'), { value: ${JSON.stringify(mode)}, enumerable: false }); Object.defineProperty(m, Symbol.for('parseman.fusedHostElided'), { value: ${JSON.stringify(elided)}, enumerable: false }); return m })(${grammarExpr})`;
13002
+ const withGrammarReflection = (grammarExpr, reflection) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.grammarReflection'), { value: ${grammarReflectionSource(reflection)}, enumerable: false })`;
12545
13003
  const withCoverageDefinitions = (grammarExpr, definitions) => !grammarCoverage ? grammarExpr : `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.grammarCoverageDefinitions'), { value: Object.freeze(${JSON.stringify(definitions)}.map(Object.freeze)), enumerable: false })`;
12546
13004
  const emittedCoverageDefinitions = (source) => {
12547
13005
  const ids = /* @__PURE__ */ new Set();
@@ -12937,7 +13395,14 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12937
13395
  const kind = vd.kind ?? "const";
12938
13396
  if (d.id.type === "Identifier") {
12939
13397
  const varName = d.id.name;
12940
- if (!referencesAny(init, allNames, scope)) continue;
13398
+ if (!referencesAny(init, allNames, scope)) {
13399
+ const staticValue = evaluateStaticValue(init, scope, code);
13400
+ if (staticValue !== null && staticValue !== void 0 || isStaticNullishExpression(init)) {
13401
+ ;
13402
+ scope.set(varName, staticValue);
13403
+ }
13404
+ continue;
13405
+ }
12941
13406
  if (refNames.has(varName)) {
12942
13407
  const refEntry = scope.get(varName);
12943
13408
  const refCombi = refEntry?.combi ?? null;
@@ -12977,6 +13442,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12977
13442
  );
12978
13443
  if (compiledRules.replacement !== null) {
12979
13444
  replacement = withHostMode(replacement, compiledRules.hostMode, compiledRules.hostBranchElided);
13445
+ replacement = withGrammarReflection(replacement, compiledRules.reflection);
12980
13446
  }
12981
13447
  if (exportPrefix && pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
12982
13448
  const ir = serializeRuleMap([...compiledRules.ruleMap], compiledRules.scanSkip);
@@ -13004,7 +13470,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
13004
13470
  if (isComposeLeafCall(init)) {
13005
13471
  const fused = compileComposeLeafCall(init);
13006
13472
  if (!fused) {
13007
- throw new Error(`${id}:${lineOf(init.start)} \u2014 composeLeaf() must macro-fuse; runtime composition is forbidden`);
13473
+ const reasons = warnings.slice(-4);
13474
+ throw new Error(
13475
+ `${id}:${lineOf(init.start)} \u2014 composeLeaf() must macro-fuse; runtime composition is forbidden` + (reasons.length ? `
13476
+ causes:
13477
+ - ${reasons.join("\n - ")}` : "")
13478
+ );
13008
13479
  }
13009
13480
  replacements.push({ start: init.start, end: init.end, replacement: fused.replacement });
13010
13481
  markUsedImportedFactories(fused.importedFactories);
@@ -13033,6 +13504,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
13033
13504
  scope.set(varName, combiArray);
13034
13505
  continue;
13035
13506
  }
13507
+ const staticValue = evaluateStaticValue(init, scope, code);
13508
+ if (staticValue !== null && staticValue !== void 0 || isStaticNullishExpression(init)) {
13509
+ ;
13510
+ scope.set(varName, staticValue);
13511
+ continue;
13512
+ }
13036
13513
  if (factoryDecls.has(varName)) continue;
13037
13514
  warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
13038
13515
  continue;
@@ -13108,7 +13585,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
13108
13585
  const replacementRanges = runtimeComposeFallback ? [] : replacements.map(({ start, end }) => ({ start, end }));
13109
13586
  const isInsideReplacement = (start, end) => replacementRanges.some((r) => start >= r.start && end <= r.end);
13110
13587
  const importedBindingStillReferenced = (local) => {
13111
- const walk2 = (node2, parent, parentKey) => {
13588
+ const walk = (node2, parent, parentKey) => {
13112
13589
  if (!node2 || typeof node2 !== "object") return false;
13113
13590
  const rec = node2;
13114
13591
  if (rec.type === "ImportDeclaration") return false;
@@ -13123,14 +13600,14 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
13123
13600
  if (key === "type" || key === "start" || key === "end") continue;
13124
13601
  const value = rec[key];
13125
13602
  if (Array.isArray(value)) {
13126
- if (value.some((child) => walk2(child, rec, key))) return true;
13127
- } else if (walk2(value, rec, key)) {
13603
+ if (value.some((child) => walk(child, rec, key))) return true;
13604
+ } else if (walk(value, rec, key)) {
13128
13605
  return true;
13129
13606
  }
13130
13607
  }
13131
13608
  return false;
13132
13609
  };
13133
- return body.some((stmt) => walk2(stmt));
13610
+ return body.some((stmt) => walk(stmt));
13134
13611
  };
13135
13612
  for (const imp of ordinaryImports) {
13136
13613
  if (!runtimeComposeFallback && imp.specifiers.length > 0 && imp.specifiers.every(