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
@@ -1106,1031 +1106,1095 @@ function keywords(words, opts = {}) {
1106
1106
  };
1107
1107
  }
1108
1108
 
1109
- // src/recovery/scan.ts
1110
- function probeAt(sentinel, input, pos, ctx) {
1111
- const s = {
1112
- _probe: ctx._probe,
1113
- _errors: ctx._errors,
1114
- _cstLeaves: ctx._cstLeaves,
1115
- _cstChildren: ctx._cstChildren,
1116
- _cstRawChildren: ctx._cstRawChildren,
1117
- _cstTriviaLog: ctx._cstTriviaLog,
1118
- _triviaLog: ctx._triviaLog,
1119
- _fields: ctx._fields,
1120
- _cstBuf: ctx._cstBuf,
1121
- _tolerant: ctx._tolerant,
1122
- _sync: ctx._sync
1123
- };
1124
- ctx._probe = void 0;
1125
- ctx._errors = void 0;
1126
- ctx._cstLeaves = void 0;
1127
- ctx._cstChildren = void 0;
1128
- ctx._cstRawChildren = void 0;
1129
- ctx._cstTriviaLog = void 0;
1130
- ctx._triviaLog = void 0;
1131
- ctx._fields = void 0;
1132
- ctx._cstBuf = void 0;
1133
- ctx._tolerant = void 0;
1134
- ctx._sync = void 0;
1135
- try {
1136
- return sentinel.parse(input, pos, ctx).ok;
1137
- } finally {
1138
- ctx._probe = s._probe;
1139
- ctx._errors = s._errors;
1140
- ctx._cstLeaves = s._cstLeaves;
1141
- ctx._cstChildren = s._cstChildren;
1142
- ctx._cstRawChildren = s._cstRawChildren;
1143
- ctx._cstTriviaLog = s._cstTriviaLog;
1144
- ctx._triviaLog = s._triviaLog;
1145
- ctx._fields = s._fields;
1146
- ctx._cstBuf = s._cstBuf;
1147
- ctx._tolerant = s._tolerant;
1148
- ctx._sync = s._sync;
1149
- }
1109
+ // src/cst/trivia-kinds.ts
1110
+ var labeledTriviaSpecs = /* @__PURE__ */ new WeakMap();
1111
+ function unwrapTrivia(p) {
1112
+ let cur = p;
1113
+ while (cur._def.tag === "trivia") cur = cur._def.parser;
1114
+ return cur;
1150
1115
  }
1151
- function orSentinel(a, b) {
1152
- if (b === void 0) return a;
1153
- return {
1154
- _tag: "orSentinel",
1155
- _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
1156
- _def: { tag: "unknown" },
1157
- parse(input, pos, ctx) {
1158
- const ra = a.parse(input, pos, ctx);
1159
- if (ra.ok) return ra;
1160
- return b.parse(input, pos, ctx);
1161
- }
1162
- };
1116
+ function peelLabel(p) {
1117
+ if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
1118
+ return null;
1163
1119
  }
1164
- function firstSetSentinel(fs2) {
1165
- if (fs2.kind !== "ranges" || fs2.ranges.length === 0) return null;
1166
- const ranges = fs2.ranges;
1167
- return {
1168
- _tag: "firstSetSentinel",
1169
- _meta: { firstSet: fs2, canMatchNewline: false, isTrivia: false },
1170
- _def: { tag: "unknown" },
1171
- parse(input, pos) {
1172
- const code = pos < input.length ? input.codePointAt(pos) : void 0;
1173
- if (code !== void 0) {
1174
- for (const r of ranges) {
1175
- if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
1176
- }
1120
+ function analyzeLabeledTrivia(trivia2) {
1121
+ const cached = labeledTriviaSpecs.get(trivia2);
1122
+ if (cached !== void 0) return cached;
1123
+ let core = unwrapTrivia(trivia2);
1124
+ let minRepeats = 1;
1125
+ if (core._def.tag === "oneOrMore") {
1126
+ core = core._def.parser;
1127
+ minRepeats = 1;
1128
+ } else if (core._def.tag === "many") {
1129
+ core = core._def.parser;
1130
+ minRepeats = 0;
1131
+ }
1132
+ const arms = [];
1133
+ if (core._def.tag === "choice") {
1134
+ for (let i = 0; i < core._def.parsers.length; i++) {
1135
+ const peeled = peelLabel(core._def.parsers[i]);
1136
+ if (!peeled) {
1137
+ labeledTriviaSpecs.set(trivia2, null);
1138
+ return null;
1177
1139
  }
1178
- return { ok: false, expected: [], span: { start: pos, end: pos } };
1140
+ arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
1141
+ }
1142
+ } else {
1143
+ const peeled = peelLabel(core);
1144
+ if (!peeled) {
1145
+ labeledTriviaSpecs.set(trivia2, null);
1146
+ return null;
1179
1147
  }
1148
+ arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
1149
+ }
1150
+ const spec = {
1151
+ labels: arms.map((a) => a.label),
1152
+ arms,
1153
+ minRepeats
1180
1154
  };
1155
+ labeledTriviaSpecs.set(trivia2, spec);
1156
+ return spec;
1181
1157
  }
1182
- function recoverScan(input, from, ctx, sync, expected) {
1183
- let scanPos = from;
1184
- while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1185
- const span = { start: from, end: scanPos };
1186
- if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
1187
- const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1188
- ctx._errors?.push(error);
1189
- return { error, end: scanPos };
1190
- }
1191
- function matchesAt(sentinel, input, pos, ctx) {
1192
- return probeAt(sentinel, input, pos, ctx);
1158
+ function matchArmAt(input, pos, arm, state) {
1159
+ if (!startsFirstSet(arm, input, pos)) return null;
1160
+ const r = arm.parse(input, pos, { trackLines: false, state });
1161
+ if (!r.ok || r.span.end <= pos) return null;
1162
+ return { end: r.span.end };
1193
1163
  }
1194
- function captureError(ctx, error) {
1195
- if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
1164
+ function scanLabeledTriviaChunks(input, cur, spec, state) {
1165
+ const chunks = [];
1166
+ let pos = cur;
1167
+ while (pos < input.length) {
1168
+ let matched = null;
1169
+ for (const arm of spec.arms) {
1170
+ const m = matchArmAt(input, pos, arm.parser, state);
1171
+ if (m) {
1172
+ matched = { end: m.end, kindIndex: arm.kindIndex };
1173
+ break;
1174
+ }
1175
+ }
1176
+ if (!matched) break;
1177
+ chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
1178
+ pos = matched.end;
1179
+ }
1180
+ if (chunks.length < spec.minRepeats) {
1181
+ return { end: cur, chunks: [] };
1182
+ }
1183
+ return { end: pos, chunks };
1196
1184
  }
1197
-
1198
- // src/combinators/expect.ts
1199
- function deriveExpected(c) {
1200
- return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
1185
+ function scanLabeledTriviaEnd(input, cur, spec, state) {
1186
+ let pos = cur;
1187
+ let count = 0;
1188
+ while (pos < input.length) {
1189
+ let matched = null;
1190
+ for (const arm of spec.arms) {
1191
+ matched = matchArmAt(input, pos, arm.parser, state);
1192
+ if (matched) break;
1193
+ }
1194
+ if (!matched) break;
1195
+ pos = matched.end;
1196
+ count++;
1197
+ }
1198
+ return count < spec.minRepeats ? cur : pos;
1201
1199
  }
1202
- function derive(c, seen) {
1203
- const deriveExpected2 = (p) => derive(p, seen);
1204
- const def = c._def;
1205
- switch (def.tag) {
1206
- case "literal":
1207
- return [JSON.stringify(def.value)];
1208
- case "regex":
1209
- return [`/${def.source}/`];
1210
- case "keywords":
1211
- return def.words.map((w) => JSON.stringify(w));
1212
- case "label":
1213
- return [def.label];
1214
- case "choice":
1215
- return def.parsers.flatMap(deriveExpected2);
1216
- case "dispatch":
1217
- return deriveExpected2(def.selector);
1218
- // Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
1219
- // `optional(…)`/`many(…)` can match nothing, so the term after it is equally
1220
- // able to fail first — deriving from term 0 alone named a token the parse
1221
- // never actually required. A leading `not(…)` is zero-width and contributes
1222
- // nothing (its own expectation is about what must NOT be here), but it is
1223
- // nullable, so keep scanning past it. Stop at the first term that must match.
1224
- case "sequence": {
1225
- const out = [];
1226
- for (const term of def.parsers) {
1227
- if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
1228
- if (!matchesEmpty(term)) break;
1200
+ function visitLabeledTrivia(input, cur, spec, state, visit) {
1201
+ if (spec.minRepeats > 1) return void 0;
1202
+ let pos = cur;
1203
+ let count = 0;
1204
+ while (pos < input.length) {
1205
+ let matched = null;
1206
+ for (const arm of spec.arms) {
1207
+ const match = matchArmAt(input, pos, arm.parser, state);
1208
+ if (match) {
1209
+ matched = { end: match.end, kindIndex: arm.kindIndex };
1210
+ break;
1229
1211
  }
1230
- return out;
1231
1212
  }
1232
- case "attempt":
1233
- return deriveExpected2(def.parser);
1234
- // Delegating wrappers whose first-token failure is their inner parser's — must
1235
- // pass through so a start-failure (and the first-set-miss fast-path in attempt/
1236
- // node, which reads this) names the real expected token, not the wrapper tag.
1237
- case "field":
1238
- case "withCtx":
1239
- case "recover":
1240
- return deriveExpected2(def.parser);
1241
- case "skip":
1242
- return deriveExpected2(def.main);
1243
- case "expect":
1244
- return def.expected;
1245
- case "node":
1246
- case "grammar":
1247
- case "trivia":
1248
- case "token":
1249
- case "leaf":
1250
- case "optional":
1251
- case "many":
1252
- case "oneOrMore":
1253
- case "transform":
1254
- case "not":
1255
- case "peek":
1256
- return deriveExpected2(def.parser);
1257
- case "routed":
1258
- return ["routed()"];
1259
- case "lazy": {
1260
- const name = c._ruleName;
1261
- if (seen.has(c)) return name ? [name] : [];
1262
- seen.add(c);
1263
- try {
1264
- return deriveExpected2(def.thunk());
1265
- } catch {
1266
- return name ? [name] : [];
1267
- } finally {
1268
- seen.delete(c);
1213
+ if (!matched) break;
1214
+ visit(pos, matched.end, matched.kindIndex);
1215
+ pos = matched.end;
1216
+ count++;
1217
+ }
1218
+ return count < spec.minRepeats ? cur : pos;
1219
+ }
1220
+ function recordTriviaChunks(ctx, chunks) {
1221
+ const kinds = ctx.triviaKindLabels;
1222
+ const mask = ctx._triviaCaptureMask;
1223
+ const rootLog = ctx._rootTriviaLog;
1224
+ const rootKinds = ctx._rootTriviaKindIndex;
1225
+ const rootMark = rootLog?.length ?? 0;
1226
+ for (const ch2 of chunks) {
1227
+ pushTriviaLogEntry(ctx, ch2.start, ch2.end, kinds ? ch2.kindIndex : void 0);
1228
+ const rootKindIndex = ctx._rootTriviaCapture === false || rootLog === void 0 || rootKinds === void 0 || kinds === void 0 ? -1 : rootKinds[kinds[ch2.kindIndex] ?? ""] ?? -1;
1229
+ if (rootKindIndex >= 0) {
1230
+ rootLog.push(0, 0, ch2.start, ch2.end, rootKindIndex);
1231
+ }
1232
+ if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
1233
+ if (mask === void 0 || kinds === void 0 || (mask & 1 << ch2.kindIndex) !== 0) {
1234
+ pushCstTriviaEntry(ctx, ch2.start, ch2.end, kinds ? ch2.kindIndex : void 0);
1269
1235
  }
1270
1236
  }
1271
- default:
1272
- return [];
1273
1237
  }
1274
- }
1275
- function expect(combinator, label2) {
1276
- const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
1277
- const meta = {
1278
- firstSet: combinator._meta.firstSet,
1279
- canMatchNewline: combinator._meta.canMatchNewline,
1280
- isTrivia: false
1281
- };
1282
- return {
1283
- _tag: "expect",
1284
- _meta: meta,
1285
- _def: { tag: "expect", parser: combinator, label: label2, expected },
1286
- parse(input, pos, ctx) {
1287
- const result = combinator.parse(input, pos, ctx);
1288
- if (result.ok) return result;
1289
- const span = { start: pos, end: pos };
1290
- const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1291
- ctx._errors?.push(error);
1292
- if (ctx._tolerant) captureError(ctx, error);
1293
- return { ok: true, value: error, span: { start: pos, end: pos } };
1238
+ if (rootLog !== void 0 && rootLog.length !== rootMark && chunks.length > 0) {
1239
+ const start = chunks[0].start;
1240
+ const end = chunks[chunks.length - 1].end;
1241
+ for (let offset = rootMark; offset < rootLog.length; offset += 5) {
1242
+ rootLog[offset] = start;
1243
+ rootLog[offset + 1] = end;
1294
1244
  }
1295
- };
1245
+ }
1296
1246
  }
1297
-
1298
- // src/combinators/choice.ts
1299
- function choice(...args) {
1300
- const parsers = args.map((a) => "gate" in a ? a.combinator : a);
1301
- const gates = args.map((a) => "gate" in a ? a.gate : null);
1302
- const hasGates = gates.some((g) => g !== null);
1303
- const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
1304
- let combined = { kind: "empty" };
1305
- for (const p of parsers) combined = union(combined, p._meta.firstSet);
1306
- const meta = {
1307
- firstSet: combined,
1308
- canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
1309
- isTrivia: false,
1310
- disjoint
1311
- };
1312
- const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
1313
- const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
1314
- let greedyLitMap = null;
1315
- let sortedParsers = null;
1316
- const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
1317
- if (strategy?.tag === "greedyClassify") {
1318
- greedyLitMap = /* @__PURE__ */ new Map();
1319
- for (let i = 0; i < parsers.length; i++) {
1320
- if (i === strategy.superIndex) continue;
1321
- const litVal = getCoreLiteralValue(parsers[i]);
1322
- if (litVal !== null) greedyLitMap.set(litVal, i);
1323
- }
1324
- } else if (strategy?.tag === "literalsLongestFirst") {
1325
- sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
1247
+ function triviaKindMask(labels, keep) {
1248
+ if (!labels) return void 0;
1249
+ let mask = 0;
1250
+ for (const name of keep) {
1251
+ const idx = labels.indexOf(name);
1252
+ if (idx >= 0) mask |= 1 << idx;
1326
1253
  }
1254
+ return mask;
1255
+ }
1256
+
1257
+ // src/combinators/trivia-skip.ts
1258
+ var NOOP_COMMIT = () => {
1259
+ };
1260
+ var fastTriviaCache = /* @__PURE__ */ new WeakMap();
1261
+ function needsDeferredTriviaCommit(ctx) {
1262
+ return ctx._triviaLog !== void 0 || ctx._rootTriviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
1263
+ }
1264
+ function saveTriviaMark(ctx) {
1265
+ const m = saveCstMark(ctx);
1327
1266
  return {
1328
- _tag: "choice",
1329
- _meta: meta,
1330
- _def: {
1331
- tag: "choice",
1332
- parsers,
1333
- gates,
1334
- disjoint,
1335
- strategy: strategy ?? { tag: "firstMatch" },
1336
- autoNot
1337
- },
1338
- parse(input, pos, ctx) {
1339
- const expected = [];
1340
- if (disjoint && pos < input.length) {
1341
- const code = input.codePointAt(pos);
1342
- let idx = code < 128 ? asciiDispatch[code] : -1;
1343
- if (idx < 0) {
1344
- for (let i = 0; i < parsers.length; i++) {
1345
- if (inFirstSet(code, parsers[i]._meta.firstSet)) {
1346
- idx = i;
1347
- break;
1348
- }
1267
+ raw: m.raw,
1268
+ tlog: m.tlog,
1269
+ leaves: m.leaves,
1270
+ fields: m.fields,
1271
+ errors: m.errors,
1272
+ log: ctx._triviaLog ? ctx._triviaLog.length : 0,
1273
+ rootLog: ctx._rootTriviaLog ? ctx._rootTriviaLog.length : 0
1274
+ };
1275
+ }
1276
+ function rollbackTrivia(ctx, mark) {
1277
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1278
+ if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1279
+ if (ctx._rootTriviaLog && ctx._rootTriviaLog.length !== mark.rootLog) ctx._rootTriviaLog.length = mark.rootLog;
1280
+ }
1281
+ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1282
+ const probeCtx = {
1283
+ trackLines: ctx.trackLines,
1284
+ state: ctx.state,
1285
+ ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1286
+ ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1287
+ ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1288
+ };
1289
+ const result = triviaP.parse(input, cur, probeCtx);
1290
+ ctx._lineScannedTo = probeCtx._lineScannedTo;
1291
+ return result;
1292
+ }
1293
+ function saveLookaheadMark(ctx) {
1294
+ return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1295
+ }
1296
+ function rollbackLookahead(ctx, mark) {
1297
+ rollbackTrivia(ctx, mark);
1298
+ if (ctx._probe) ctx._probe.best = mark.probeBest;
1299
+ }
1300
+ function scanWithLabels(input, cur, ctx) {
1301
+ const triviaP = ctx.trivia;
1302
+ const spec = analyzeLabeledTrivia(triviaP);
1303
+ if (!spec) return { end: cur, commit: NOOP_COMMIT };
1304
+ const log = ctx._triviaLog;
1305
+ const rootLog = ctx._rootTriviaLog;
1306
+ const rootKinds = ctx._rootTriviaKindIndex;
1307
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1308
+ const mask = ctx._triviaCaptureMask;
1309
+ let fullRows;
1310
+ let rootRows;
1311
+ let cstRows;
1312
+ const visitedEnd = visitLabeledTrivia(input, cur, spec, ctx.state, (start, matchEnd, kindIndex) => {
1313
+ if (log !== void 0) (fullRows ??= []).push(start, matchEnd, kindIndex);
1314
+ const selectedKind = rootLog === void 0 || rootKinds === void 0 || ctx._rootTriviaCapture === false ? void 0 : rootKinds[spec.labels[kindIndex] ?? ""];
1315
+ if (selectedKind !== void 0) (rootRows ??= []).push(start, matchEnd, selectedKind);
1316
+ if (captureTl && (mask === void 0 || (mask & 1 << kindIndex) !== 0)) {
1317
+ (cstRows ??= []).push(start, matchEnd, kindIndex);
1318
+ }
1319
+ });
1320
+ if (visitedEnd !== void 0) {
1321
+ if (visitedEnd === cur) return { end: cur, commit: NOOP_COMMIT };
1322
+ return {
1323
+ end: visitedEnd,
1324
+ commit: () => {
1325
+ if (fullRows !== void 0 && log !== void 0) log.push(...fullRows);
1326
+ if (rootRows !== void 0 && rootLog !== void 0) {
1327
+ for (let i = 0; i < rootRows.length; i += 3) {
1328
+ rootLog.push(cur, visitedEnd, rootRows[i], rootRows[i + 1], rootRows[i + 2]);
1349
1329
  }
1350
1330
  }
1351
- if (idx >= 0) {
1352
- const gate2 = gates[idx];
1353
- if (gate2 && !gate2(ctx.state)) {
1354
- return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
1331
+ if (cstRows !== void 0) {
1332
+ for (let i = 0; i < cstRows.length; i += 3) {
1333
+ pushCstTriviaEntry(ctx, cstRows[i], cstRows[i + 1], cstRows[i + 2]);
1355
1334
  }
1356
- const result = parsers[idx].parse(input, pos, ctx);
1357
- if (result.ok) return result;
1358
- expected.push(...result.expected);
1359
- if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
1360
- return { ok: false, expected, span: { start: pos, end: pos } };
1361
- }
1362
- return {
1363
- ok: false,
1364
- expected: parsers.flatMap((p) => {
1365
- const r = p.parse(input, pos, ctx);
1366
- return r.ok ? [] : r.expected;
1367
- }),
1368
- span: { start: pos, end: pos }
1369
- };
1370
- }
1371
- if (strategy?.tag === "greedyClassify") {
1372
- const superResult = parsers[strategy.superIndex].parse(input, pos, ctx);
1373
- if (!superResult.ok) return superResult;
1374
- const end = superResult.span.end;
1375
- const litIdx = greedyLitMap.get(input.slice(pos, end));
1376
- if (litIdx !== void 0) {
1377
- const litVal = getCoreLiteralValue(parsers[litIdx]);
1378
- const value = applyTransforms(parsers[litIdx], litVal, { start: pos, end });
1379
- return { ok: true, value, span: { start: pos, end } };
1380
- }
1381
- return superResult;
1382
- }
1383
- if (strategy?.tag === "literalsLongestFirst") {
1384
- for (const p of sortedParsers) {
1385
- const r = p.parse(input, pos, ctx);
1386
- if (r.ok) return r;
1387
- expected.push(...r.expected);
1388
- }
1389
- return { ok: false, expected, span: { start: pos, end: pos } };
1390
- }
1391
- for (let i = 0; i < parsers.length; i++) {
1392
- if (gates[i] && !gates[i](ctx.state)) continue;
1393
- const mark = saveCstMark(ctx);
1394
- const logLen = ctx._triviaLog?.length;
1395
- const result = parsers[i].parse(input, pos, ctx);
1396
- if (!result.ok) {
1397
- rollbackCstCapture(ctx, mark);
1398
- if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
1399
- expected.push(...result.expected);
1400
- if (result.committed) return { ok: false, expected, span: result.span, committed: true };
1401
- continue;
1402
1335
  }
1403
- const checks = autoNot[i];
1404
- if (checks && autoNotFires(input, result.span.end, checks)) {
1405
- rollbackCstCapture(ctx, mark);
1406
- if (logLen !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== logLen) ctx._triviaLog.length = logLen;
1407
- continue;
1408
- }
1409
- return result;
1410
1336
  }
1411
- return { ok: false, expected, span: { start: pos, end: pos } };
1412
- }
1337
+ };
1338
+ }
1339
+ const { end, chunks } = scanLabeledTriviaChunks(input, cur, spec, ctx.state);
1340
+ if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1341
+ return {
1342
+ end,
1343
+ commit: () => recordTriviaChunks(ctx, chunks)
1413
1344
  };
1414
1345
  }
1415
- function detectStrategy(parsers) {
1416
- const regexIndices = [];
1417
- const literalIndices = [];
1418
- for (let i = 0; i < parsers.length; i++) {
1419
- if (getCoreRegexDef(parsers[i]) !== null) regexIndices.push(i);
1420
- else if (getCoreLiteralValue(parsers[i]) !== null) literalIndices.push(i);
1421
- }
1422
- if (regexIndices.length === 1 && literalIndices.length === parsers.length - 1 && literalIndices.length > 0) {
1423
- const superIndex = regexIndices[0];
1424
- const regexDef = getCoreRegexDef(parsers[superIndex]);
1425
- const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
1426
- const re = new RegExp(regexDef.source, flags);
1427
- const allSubsumed = literalIndices.every((i) => {
1428
- const litVal = getCoreLiteralValue(parsers[i]);
1429
- re.lastIndex = 0;
1430
- const m = re.exec(litVal);
1431
- return m !== null && m[0] === litVal;
1432
- });
1433
- if (allSubsumed) return { tag: "greedyClassify", superIndex };
1346
+ function skipWithLabels(input, cur, ctx) {
1347
+ const spec = analyzeLabeledTrivia(ctx.trivia);
1348
+ if (spec) return scanLabeledTriviaEnd(input, cur, spec, ctx.state);
1349
+ const tr = parseTriviaNoCapture(ctx.trivia, input, cur, ctx);
1350
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1351
+ }
1352
+ function advanceTrivia(input, cur, ctx) {
1353
+ const triviaP = ctx.trivia;
1354
+ if (!triviaP) return cur;
1355
+ if (!ctx.trackLines) {
1356
+ const fast2 = fastTriviaScanner(triviaP);
1357
+ if (fast2) return fast2(input, cur);
1358
+ if (ctx.triviaKindLabels) return skipWithLabels(input, cur, ctx);
1359
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1360
+ return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1434
1361
  }
1435
- if (parsers.length === literalIndices.length) {
1436
- const sortedIndices = [...literalIndices].sort(
1437
- (a, b) => getCoreLiteralValue(parsers[b]).length - getCoreLiteralValue(parsers[a]).length
1438
- );
1439
- return { tag: "literalsLongestFirst", sortedIndices };
1362
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1363
+ const fast = fastTriviaScanner(triviaP);
1364
+ if (fast) {
1365
+ const end = fast(input, cur);
1366
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1367
+ return end;
1440
1368
  }
1441
- const shared = detectSharedPrefix(parsers);
1442
- if (shared !== null) return shared;
1443
- return { tag: "firstMatch" };
1444
- }
1445
- function bareLeadingTermKey(term) {
1446
- const d = term._def;
1447
- if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
1448
- if (d.tag === "regex") return `R:${d.source}\0${d.flags}`;
1449
- return null;
1450
- }
1451
- function detectSharedPrefix(parsers) {
1452
- if (parsers.length < 2) return null;
1453
- let key = null;
1454
- let prefix = null;
1455
- const members = [];
1456
- for (let i = 0; i < parsers.length; i++) {
1457
- const term0 = leadingTermOfArm(parsers[i]);
1458
- if (term0 === null) return null;
1459
- const k = bareLeadingTermKey(term0);
1460
- if (k === null) return null;
1461
- if (key === null) {
1462
- key = k;
1463
- prefix = term0;
1464
- } else if (k !== key) return null;
1465
- members.push(i);
1369
+ if (ctx.triviaKindLabels) {
1370
+ const end = skipWithLabels(input, cur, ctx);
1371
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1372
+ return end;
1466
1373
  }
1467
- if (prefix === null || members.length < 2) return null;
1468
- return { tag: "sharedPrefix", prefix, members };
1374
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1375
+ return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1469
1376
  }
1470
- function leadingTermOfArm(arm) {
1471
- let d = arm._def;
1472
- for (; ; ) {
1473
- if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
1474
- d = d.parser._def;
1475
- continue;
1377
+ function scanTrivia(input, cur, ctx) {
1378
+ const triviaP = ctx.trivia;
1379
+ if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1380
+ const log = ctx._triviaLog;
1381
+ const rootLog = ctx._rootTriviaLog;
1382
+ const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1383
+ if (!ctx.trackLines) {
1384
+ const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1385
+ if (fast2 && log === void 0 && !captureTl) {
1386
+ return { end: fast2(input, cur), commit: NOOP_COMMIT };
1476
1387
  }
1477
- break;
1478
- }
1479
- if (d.tag !== "sequence" || d.parsers.length < 2) return null;
1480
- return d.parsers[0];
1481
- }
1482
- function computeAutoNot(parsers) {
1483
- return parsers.map((p, i) => {
1484
- const litVal = getCoreLiteralValue(p);
1485
- if (litVal === null) return null;
1486
- const checks = [];
1487
- for (let j = i + 1; j < parsers.length; j++) {
1488
- const other = parsers[j];
1489
- const otherLit = getCoreLiteralValue(other);
1490
- if (otherLit !== null && otherLit.startsWith(litVal) && otherLit.length > litVal.length) {
1491
- checks.push({ kind: "startsWith", value: otherLit.slice(litVal.length) });
1492
- continue;
1493
- }
1494
- const regexDef = getCoreRegexDef(other);
1495
- if (regexDef !== null) {
1496
- const contSet = continuationFirstSet(litVal, regexDef.source, regexDef.flags);
1497
- if (contSet !== null) checks.push({ kind: "firstSet", set: contSet });
1498
- }
1388
+ if (ctx.triviaKindLabels && (log !== void 0 || rootLog !== void 0 || captureTl)) {
1389
+ return scanWithLabels(input, cur, ctx);
1499
1390
  }
1500
- return checks.length > 0 ? checks : null;
1501
- });
1502
- }
1503
- function autoNotFires(input, end, checks) {
1504
- for (const check of checks) {
1505
- if (check.kind === "firstSet") {
1506
- const code = end < input.length ? input.codePointAt(end) ?? -1 : -1;
1507
- if (inFirstSet(code, check.set)) return true;
1508
- } else {
1509
- if (input.startsWith(check.value, end)) return true;
1391
+ if (ctx.triviaKindLabels) return { end: skipWithLabels(input, cur, ctx), commit: NOOP_COMMIT };
1392
+ if (log !== void 0 || rootLog !== void 0 || captureTl) {
1393
+ const tr3 = triviaP.parse(input, cur, {
1394
+ trackLines: log !== void 0 ? false : ctx.trackLines,
1395
+ state: ctx.state
1396
+ });
1397
+ if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1398
+ const end = tr3.span.end;
1399
+ return {
1400
+ end,
1401
+ commit: () => {
1402
+ pushTriviaLogEntry(ctx, cur, end);
1403
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1404
+ }
1405
+ };
1510
1406
  }
1407
+ const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1408
+ return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1511
1409
  }
1512
- return false;
1513
- }
1514
- function getCoreLiteralValue(p) {
1515
- const def = p._def;
1516
- if (def.tag === "literal" && !def.caseInsensitive) return def.value;
1517
- if (def.tag === "transform") return getCoreLiteralValue(def.parser);
1518
- return null;
1519
- }
1520
- function getCoreRegexDef(p) {
1521
- const def = p._def;
1522
- if (def.tag === "regex") return { source: def.source, flags: def.flags };
1523
- if (def.tag === "transform") return getCoreRegexDef(def.parser);
1524
- if (def.tag === "label") return getCoreRegexDef(def.parser);
1525
- return null;
1526
- }
1527
- function applyTransforms(p, value, span) {
1528
- const def = p._def;
1529
- if (def.tag === "transform") {
1530
- const inner = applyTransforms(def.parser, value, span);
1531
- return def.fn(inner, span);
1410
+ const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1411
+ const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1412
+ if (fast && log === void 0 && !captureTl) {
1413
+ const end = fast(input, cur);
1414
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1415
+ return { end, commit: NOOP_COMMIT };
1532
1416
  }
1533
- return value;
1534
- }
1535
- function continuationFirstSet(lit, source, flags) {
1536
- const re = new RegExp(source, "y" + flags.replace(/[gy]/g, ""));
1537
- re.lastIndex = 0;
1538
- const base = re.exec(lit);
1539
- if (!base || base[0] !== lit) return null;
1540
- const contCodes = [];
1541
- for (let code = 1; code < 128; code++) {
1542
- re.lastIndex = 0;
1543
- const m = re.exec(lit + String.fromCharCode(code));
1544
- if (m && m[0].length > lit.length) contCodes.push(code);
1417
+ if (ctx.triviaKindLabels && (log !== void 0 || rootLog !== void 0 || captureTl)) {
1418
+ const scan = scanWithLabels(input, cur, ctx);
1419
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1420
+ return scan;
1545
1421
  }
1546
- if (contCodes.length === 0) return null;
1547
- return codesToFirstSet(contCodes);
1548
- }
1549
- function codesToFirstSet(codes) {
1550
- codes.sort((a, b) => a - b);
1551
- const ranges = [];
1552
- let lo = codes[0], hi = codes[0];
1553
- for (let i = 1; i < codes.length; i++) {
1554
- if (codes[i] === hi + 1) {
1555
- hi = codes[i];
1556
- } else {
1557
- ranges.push({ lo, hi });
1558
- lo = hi = codes[i];
1559
- }
1422
+ if (ctx.triviaKindLabels) {
1423
+ const end = skipWithLabels(input, cur, ctx);
1424
+ if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1425
+ return { end, commit: NOOP_COMMIT };
1560
1426
  }
1561
- ranges.push({ lo, hi });
1562
- return { kind: "ranges", ranges };
1427
+ if (log !== void 0 || rootLog !== void 0 || captureTl) {
1428
+ const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1429
+ if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1430
+ const end = tr2.span.end;
1431
+ return {
1432
+ end,
1433
+ commit: () => {
1434
+ pushTriviaLogEntry(ctx, cur, end);
1435
+ if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1436
+ }
1437
+ };
1438
+ }
1439
+ const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1440
+ return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1563
1441
  }
1564
- function inFirstSet(code, fs2) {
1565
- if (fs2.kind === "any") return true;
1566
- if (fs2.kind === "empty") return false;
1567
- for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
1568
- return false;
1442
+ function consumeTrivia(input, cur, ctx) {
1443
+ if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1444
+ const scan = scanTrivia(input, cur, ctx);
1445
+ scan.commit();
1446
+ return scan.end;
1569
1447
  }
1570
- function areDisjoint(sets) {
1571
- if (sets.some((s) => s.kind === "any")) return false;
1572
- for (let i = 0; i < sets.length; i++)
1573
- for (let j = i + 1; j < sets.length; j++)
1574
- if (intersects(sets[i], sets[j])) return false;
1575
- return true;
1448
+ function fastTriviaScanner(trivia2) {
1449
+ const cached = fastTriviaCache.get(trivia2);
1450
+ if (cached !== void 0) return cached;
1451
+ const scanner = buildFastTriviaScanner(trivia2);
1452
+ fastTriviaCache.set(trivia2, scanner);
1453
+ return scanner;
1576
1454
  }
1577
- function buildAsciiDispatch(parsers) {
1578
- const table = Array(128).fill(-1);
1579
- for (let i = 0; i < parsers.length; i++) {
1580
- const fs2 = parsers[i]._meta.firstSet;
1581
- if (fs2.kind !== "ranges") continue;
1582
- for (const { lo, hi } of fs2.ranges) {
1583
- for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
1584
- table[code] = i;
1455
+ function buildFastTriviaScanner(trivia2) {
1456
+ const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1457
+ const direct = regexTriviaScanner(core);
1458
+ if (direct) return direct;
1459
+ const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1460
+ if (!repeat) return null;
1461
+ const one = regexTriviaScanner(repeat);
1462
+ if (one) return loopScanner([one]);
1463
+ if (repeat._def.tag !== "choice") return null;
1464
+ const arms = repeat._def.parsers.map(regexTriviaScanner);
1465
+ if (arms.some((s) => s === null)) return null;
1466
+ return loopScanner(arms);
1467
+ }
1468
+ function loopScanner(arms) {
1469
+ return (input, cur) => {
1470
+ let pos = cur;
1471
+ scan: while (pos < input.length) {
1472
+ for (const arm of arms) {
1473
+ const end = arm(input, pos);
1474
+ if (end > pos) {
1475
+ pos = end;
1476
+ continue scan;
1477
+ }
1585
1478
  }
1479
+ break;
1586
1480
  }
1587
- }
1588
- return table;
1589
- }
1590
-
1591
- // src/cst/trivia-kinds.ts
1592
- function unwrapTrivia(p) {
1593
- let cur = p;
1594
- while (cur._def.tag === "trivia") cur = cur._def.parser;
1595
- return cur;
1481
+ return pos;
1482
+ };
1596
1483
  }
1597
- function peelLabel(p) {
1598
- if (p._def.tag === "label") return { label: p._def.label, parser: p._def.parser };
1599
- return null;
1484
+ function regexTriviaScanner(parser2) {
1485
+ if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1486
+ const source = parser2._def.source;
1487
+ return classRunSource(source) ?? altStarSource(source);
1600
1488
  }
1601
- function analyzeLabeledTrivia(trivia2) {
1602
- let core = unwrapTrivia(trivia2);
1603
- let minRepeats = 1;
1604
- if (core._def.tag === "oneOrMore") {
1605
- core = core._def.parser;
1606
- minRepeats = 1;
1607
- } else if (core._def.tag === "many") {
1608
- core = core._def.parser;
1609
- minRepeats = 0;
1610
- }
1611
- const arms = [];
1612
- if (core._def.tag === "choice") {
1613
- for (let i = 0; i < core._def.parsers.length; i++) {
1614
- const peeled = peelLabel(core._def.parsers[i]);
1615
- if (!peeled) return null;
1616
- arms.push({ label: peeled.label, kindIndex: i, parser: peeled.parser });
1617
- }
1618
- } else {
1619
- const peeled = peelLabel(core);
1620
- if (!peeled) return null;
1621
- arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
1489
+ function inRanges2(cp, ranges) {
1490
+ for (let i = 0; i < ranges.length; i++) {
1491
+ const r = ranges[i];
1492
+ if (cp >= r[0] && cp <= r[1]) return true;
1622
1493
  }
1623
- return { labels: arms.map((a) => a.label), arms, minRepeats };
1494
+ return false;
1624
1495
  }
1625
- function matchArmAt(input, pos, arm) {
1626
- const r = arm.parse(input, pos, { trackLines: false });
1627
- if (!r.ok || r.span.end <= pos) return null;
1628
- return { end: r.span.end };
1496
+ function classScanner(classBody) {
1497
+ const ranges = parseClassRanges(classBody);
1498
+ if (!ranges) return null;
1499
+ return (input, cur) => {
1500
+ let pos = cur;
1501
+ while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1502
+ return pos;
1503
+ };
1629
1504
  }
1630
- function scanLabeledTriviaChunks(input, cur, spec) {
1631
- const chunks = [];
1632
- let pos = cur;
1633
- while (pos < input.length) {
1634
- let matched = null;
1635
- for (const arm of spec.arms) {
1636
- const m = matchArmAt(input, pos, arm.parser);
1637
- if (m) {
1638
- matched = { end: m.end, kindIndex: arm.kindIndex };
1639
- break;
1640
- }
1505
+ function untilLineBreakScanner(leaderCode) {
1506
+ return (input, cur) => {
1507
+ if (input.charCodeAt(cur) !== leaderCode) return cur;
1508
+ let pos = cur + 1;
1509
+ while (pos < input.length) {
1510
+ const cc = input.charCodeAt(pos);
1511
+ if (cc === 10 || cc === 13) break;
1512
+ pos++;
1641
1513
  }
1642
- if (!matched) break;
1643
- chunks.push({ start: pos, end: matched.end, kindIndex: matched.kindIndex });
1644
- pos = matched.end;
1514
+ return pos;
1515
+ };
1516
+ }
1517
+ function classRunSource(source) {
1518
+ const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1519
+ return m ? classScanner(m[1]) : null;
1520
+ }
1521
+ function classifyTriviaArm(arm) {
1522
+ const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1523
+ if (cls) {
1524
+ const ranges = parseClassRanges(cls[1]);
1525
+ return ranges ? { kind: "class", ranges } : null;
1645
1526
  }
1646
- if (chunks.length < spec.minRepeats) {
1647
- return { end: cur, chunks: [] };
1527
+ const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1528
+ if (lc) {
1529
+ const marker = lc[1];
1530
+ return { kind: "untilLineBreak", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
1648
1531
  }
1649
- return { end: pos, chunks };
1532
+ return null;
1650
1533
  }
1651
- function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
1652
- const chunks = [];
1653
- let pos = cur;
1654
- while (pos < input.length) {
1655
- const c = input.charCodeAt(pos);
1656
- if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {
1657
- const start = pos;
1658
- pos++;
1659
- while (pos < input.length) {
1660
- const c2 = input.charCodeAt(pos);
1661
- if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) pos++;
1662
- else break;
1663
- }
1664
- chunks.push({ start, end: pos, kindIndex: wsKind });
1665
- continue;
1666
- }
1667
- if (c === 47 && input.charCodeAt(pos + 1) === 42) {
1668
- let j = pos + 2;
1669
- while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++;
1670
- if (j + 1 < input.length && input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47) {
1671
- const start = pos;
1672
- pos = j + 2;
1673
- chunks.push({ start, end: pos, kindIndex: commentKind });
1534
+ function armScanner(arm) {
1535
+ if (arm.kind === "untilLineBreak") return untilLineBreakScanner(arm.code);
1536
+ const ranges = arm.ranges;
1537
+ return (input, cur) => {
1538
+ let pos = cur;
1539
+ while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1540
+ return pos;
1541
+ };
1542
+ }
1543
+ function fusedTriviaScanner(ranges, leaders) {
1544
+ const c0 = leaders[0];
1545
+ const single = leaders.length === 1;
1546
+ return (input, cur) => {
1547
+ let pos = cur;
1548
+ const len = input.length;
1549
+ for (; ; ) {
1550
+ const c = input.charCodeAt(pos);
1551
+ if (pos < len && inRanges2(c, ranges)) {
1552
+ pos++;
1553
+ continue;
1554
+ }
1555
+ if (single ? c === c0 : leaders.includes(c)) {
1556
+ pos++;
1557
+ while (pos < len) {
1558
+ const cc = input.charCodeAt(pos);
1559
+ if (cc === 10 || cc === 13) break;
1560
+ pos++;
1561
+ }
1674
1562
  continue;
1675
1563
  }
1676
1564
  break;
1677
1565
  }
1678
- break;
1679
- }
1680
- return { end: pos, chunks };
1681
- }
1682
- function tryFastLabeledScan(input, cur, trivia2) {
1683
- const spec = analyzeLabeledTrivia(trivia2);
1684
- if (!spec || spec.arms.length !== 2) return null;
1685
- const wsArm = spec.arms.find((a) => {
1686
- const src = getCoreRegexDef(a.parser)?.source;
1687
- return src != null && !src.includes("\\*");
1688
- });
1689
- const commentArm = spec.arms.find((a) => {
1690
- const src = getCoreRegexDef(a.parser)?.source;
1691
- return src != null && src.includes("\\*");
1692
- });
1693
- if (!wsArm || !commentArm) return null;
1694
- const { chunks } = scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1695
- if (chunks.length < spec.minRepeats) return { end: cur, chunks: [] };
1696
- return scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
1566
+ return pos;
1567
+ };
1697
1568
  }
1698
- function recordTriviaChunks(ctx, chunks) {
1699
- const kinds = ctx.triviaKindLabels;
1700
- const mask = ctx._triviaCaptureMask;
1701
- for (const ch2 of chunks) {
1702
- pushTriviaLogEntry(ctx, ch2.start, ch2.end, kinds ? ch2.kindIndex : void 0);
1703
- if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
1704
- if (mask === void 0 || kinds === void 0 || (mask & 1 << ch2.kindIndex) !== 0) {
1705
- pushCstTriviaEntry(ctx, ch2.start, ch2.end, kinds ? ch2.kindIndex : void 0);
1706
- }
1569
+ function splitTopLevelAlts(body) {
1570
+ const arms = [];
1571
+ let inClass = false;
1572
+ let start = 0;
1573
+ for (let i = 0; i < body.length; i++) {
1574
+ const c = body[i];
1575
+ if (c === "\\") {
1576
+ i++;
1577
+ continue;
1578
+ }
1579
+ if (inClass) {
1580
+ if (c === "]") inClass = false;
1581
+ continue;
1582
+ }
1583
+ if (c === "[") inClass = true;
1584
+ else if (c === "(") return null;
1585
+ else if (c === "|") {
1586
+ arms.push(body.slice(start, i));
1587
+ start = i + 1;
1707
1588
  }
1708
1589
  }
1590
+ arms.push(body.slice(start));
1591
+ return arms;
1709
1592
  }
1710
- function triviaKindMask(labels, keep) {
1711
- if (!labels) return void 0;
1712
- let mask = 0;
1713
- for (const name of keep) {
1714
- const idx = labels.indexOf(name);
1715
- if (idx >= 0) mask |= 1 << idx;
1593
+ function altStarSource(source) {
1594
+ const m = /^\(\?:(.*)\)[*+]$/.exec(source);
1595
+ if (!m) return null;
1596
+ const armSrcs = splitTopLevelAlts(m[1]);
1597
+ if (!armSrcs || armSrcs.length < 2) return null;
1598
+ const arms = [];
1599
+ for (const src of armSrcs) {
1600
+ const arm = classifyTriviaArm(src);
1601
+ if (!arm) return null;
1602
+ arms.push(arm);
1716
1603
  }
1717
- return mask;
1604
+ const ranges = [];
1605
+ const leaders = [];
1606
+ for (const arm of arms) {
1607
+ if (arm.kind === "class") ranges.push(...arm.ranges);
1608
+ else leaders.push(arm.code);
1609
+ }
1610
+ if (leaders.some((code) => inRanges2(code, ranges))) {
1611
+ return loopScanner(arms.map(armScanner));
1612
+ }
1613
+ if (leaders.length === 0) return armScanner({ kind: "class", ranges });
1614
+ return fusedTriviaScanner(ranges, leaders);
1718
1615
  }
1719
1616
 
1720
- // src/combinators/trivia-skip.ts
1721
- var NOOP_COMMIT = () => {
1722
- };
1723
- var fastTriviaCache = /* @__PURE__ */ new WeakMap();
1724
- function needsDeferredTriviaCommit(ctx) {
1725
- return ctx._triviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
1726
- }
1727
- function saveTriviaMark(ctx) {
1728
- const m = saveCstMark(ctx);
1729
- return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, errors: m.errors, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1730
- }
1731
- function rollbackTrivia(ctx, mark) {
1732
- rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1733
- if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1734
- }
1735
- function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1736
- const probeCtx = {
1737
- trackLines: ctx.trackLines,
1738
- state: ctx.state,
1739
- ...ctx._lineIndex ? { _lineIndex: ctx._lineIndex } : {},
1740
- ...ctx._lineStarts ? { _lineStarts: ctx._lineStarts } : {},
1741
- ...ctx._lineScannedTo !== void 0 ? { _lineScannedTo: ctx._lineScannedTo } : {}
1617
+ // src/recovery/scan.ts
1618
+ function probeAt(sentinel, input, pos, ctx) {
1619
+ const s = {
1620
+ _probe: ctx._probe,
1621
+ _errors: ctx._errors,
1622
+ _cstLeaves: ctx._cstLeaves,
1623
+ _cstChildren: ctx._cstChildren,
1624
+ _cstRawChildren: ctx._cstRawChildren,
1625
+ _cstTriviaLog: ctx._cstTriviaLog,
1626
+ _triviaLog: ctx._triviaLog,
1627
+ _rootTriviaLog: ctx._rootTriviaLog,
1628
+ _fields: ctx._fields,
1629
+ _cstBuf: ctx._cstBuf,
1630
+ _tolerant: ctx._tolerant,
1631
+ _sync: ctx._sync
1742
1632
  };
1743
- const result = triviaP.parse(input, cur, probeCtx);
1744
- ctx._lineScannedTo = probeCtx._lineScannedTo;
1745
- return result;
1746
- }
1747
- function saveLookaheadMark(ctx) {
1748
- return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1749
- }
1750
- function rollbackLookahead(ctx, mark) {
1751
- rollbackTrivia(ctx, mark);
1752
- if (ctx._probe) ctx._probe.best = mark.probeBest;
1633
+ ctx._probe = void 0;
1634
+ ctx._errors = void 0;
1635
+ ctx._cstLeaves = void 0;
1636
+ ctx._cstChildren = void 0;
1637
+ ctx._cstRawChildren = void 0;
1638
+ ctx._cstTriviaLog = void 0;
1639
+ ctx._triviaLog = void 0;
1640
+ ctx._rootTriviaLog = void 0;
1641
+ ctx._fields = void 0;
1642
+ ctx._cstBuf = void 0;
1643
+ ctx._tolerant = void 0;
1644
+ ctx._sync = void 0;
1645
+ try {
1646
+ return sentinel.parse(input, pos, ctx).ok;
1647
+ } finally {
1648
+ ctx._probe = s._probe;
1649
+ ctx._errors = s._errors;
1650
+ ctx._cstLeaves = s._cstLeaves;
1651
+ ctx._cstChildren = s._cstChildren;
1652
+ ctx._cstRawChildren = s._cstRawChildren;
1653
+ ctx._cstTriviaLog = s._cstTriviaLog;
1654
+ ctx._triviaLog = s._triviaLog;
1655
+ ctx._rootTriviaLog = s._rootTriviaLog;
1656
+ ctx._fields = s._fields;
1657
+ ctx._cstBuf = s._cstBuf;
1658
+ ctx._tolerant = s._tolerant;
1659
+ ctx._sync = s._sync;
1660
+ }
1753
1661
  }
1754
- function scanWithLabels(input, cur, ctx) {
1755
- const triviaP = ctx.trivia;
1756
- const spec = analyzeLabeledTrivia(triviaP);
1757
- if (!spec) return { end: cur, commit: NOOP_COMMIT };
1758
- const fast = tryFastLabeledScan(input, cur, triviaP);
1759
- const { end, chunks } = fast ?? scanLabeledTriviaChunks(input, cur, spec);
1760
- if (end === cur) return { end: cur, commit: NOOP_COMMIT };
1662
+ function orSentinel(a, b) {
1663
+ if (b === void 0) return a;
1761
1664
  return {
1762
- end,
1763
- commit: () => recordTriviaChunks(ctx, chunks)
1764
- };
1765
- }
1766
- function advanceTrivia(input, cur, ctx) {
1767
- const triviaP = ctx.trivia;
1768
- if (!triviaP) return cur;
1769
- if (!ctx.trackLines) {
1770
- const fast2 = fastTriviaScanner(triviaP);
1771
- if (fast2) return fast2(input, cur);
1772
- if (ctx.triviaKindLabels) {
1773
- const scan = scanWithLabels(input, cur, ctx);
1774
- return scan.end;
1665
+ _tag: "orSentinel",
1666
+ _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
1667
+ _def: { tag: "unknown" },
1668
+ parse(input, pos, ctx) {
1669
+ const ra = a.parse(input, pos, ctx);
1670
+ if (ra.ok) return ra;
1671
+ return b.parse(input, pos, ctx);
1775
1672
  }
1776
- const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1777
- return tr2.ok && tr2.span.end > cur ? tr2.span.end : cur;
1778
- }
1779
- const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1780
- const fast = fastTriviaScanner(triviaP);
1781
- if (fast) {
1782
- const end = fast(input, cur);
1783
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1784
- return end;
1785
- }
1786
- if (ctx.triviaKindLabels) {
1787
- const scan = scanWithLabels(input, cur, ctx);
1788
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1789
- return scan.end;
1790
- }
1791
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1792
- return tr.ok && tr.span.end > cur ? tr.span.end : cur;
1673
+ };
1793
1674
  }
1794
- function scanTrivia(input, cur, ctx) {
1795
- const triviaP = ctx.trivia;
1796
- if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1797
- const log = ctx._triviaLog;
1798
- const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1799
- if (!ctx.trackLines) {
1800
- const fast2 = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1801
- if (fast2 && log === void 0 && !captureTl) {
1802
- return { end: fast2(input, cur), commit: NOOP_COMMIT };
1803
- }
1804
- if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1805
- return scanWithLabels(input, cur, ctx);
1806
- }
1807
- if (log !== void 0 || captureTl) {
1808
- const tr3 = triviaP.parse(input, cur, {
1809
- trackLines: log !== void 0 ? false : ctx.trackLines,
1810
- state: ctx.state
1811
- });
1812
- if (!tr3.ok || tr3.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1813
- const end = tr3.span.end;
1814
- return {
1815
- end,
1816
- commit: () => {
1817
- pushTriviaLogEntry(ctx, cur, end);
1818
- if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1675
+ function firstSetSentinel(fs2) {
1676
+ if (fs2.kind !== "ranges" || fs2.ranges.length === 0) return null;
1677
+ const ranges = fs2.ranges;
1678
+ return {
1679
+ _tag: "firstSetSentinel",
1680
+ _meta: { firstSet: fs2, canMatchNewline: false, isTrivia: false },
1681
+ _def: { tag: "unknown" },
1682
+ parse(input, pos) {
1683
+ const code = pos < input.length ? input.codePointAt(pos) : void 0;
1684
+ if (code !== void 0) {
1685
+ for (const r of ranges) {
1686
+ if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
1819
1687
  }
1820
- };
1688
+ }
1689
+ return { ok: false, expected: [], span: { start: pos, end: pos } };
1821
1690
  }
1822
- const tr2 = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1823
- return { end: tr2.ok ? tr2.span.end : cur, commit: NOOP_COMMIT };
1824
- }
1825
- const trackTriviaLines = ctx.trackLines && triviaP._meta.canMatchNewline;
1826
- const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1827
- if (fast && log === void 0 && !captureTl) {
1828
- const end = fast(input, cur);
1829
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, end);
1830
- return { end, commit: NOOP_COMMIT };
1831
- }
1832
- if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1833
- const scan = scanWithLabels(input, cur, ctx);
1834
- if (trackTriviaLines) recordLineRangeFromContext(ctx, input, cur, scan.end);
1835
- return scan;
1836
- }
1837
- if (log !== void 0 || captureTl) {
1838
- const tr2 = parseTriviaNoCapture(triviaP, input, cur, ctx);
1839
- if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
1840
- const end = tr2.span.end;
1841
- return {
1842
- end,
1843
- commit: () => {
1844
- pushTriviaLogEntry(ctx, cur, end);
1845
- if (captureTl) pushCstTriviaEntry(ctx, cur, end);
1691
+ };
1692
+ }
1693
+ function recoverScan(input, from, ctx, sync, expected) {
1694
+ let scanPos = from;
1695
+ while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1696
+ const span = { start: from, end: scanPos };
1697
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos);
1698
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1699
+ ctx._errors?.push(error);
1700
+ return { error, end: scanPos };
1701
+ }
1702
+ function matchesAt(sentinel, input, pos, ctx) {
1703
+ return probeAt(sentinel, input, pos, ctx);
1704
+ }
1705
+ function captureError(ctx, error) {
1706
+ if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
1707
+ }
1708
+
1709
+ // src/combinators/sequence.ts
1710
+ function sequence(...parsers) {
1711
+ const meta = {
1712
+ // Union through the nullable prefix — a leading optional()/many() lets a later
1713
+ // term's first char start the sequence. Just `parsers[0]` under-approximates.
1714
+ firstSet: sequenceFirstSet(parsers),
1715
+ canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
1716
+ isTrivia: false
1717
+ };
1718
+ const def = { tag: "sequence", parsers };
1719
+ let followSentinels;
1720
+ function parseTolerant(input, pos, ctx) {
1721
+ followSentinels ??= parsers.map((_, i) => {
1722
+ const fs2 = parsers.slice(i + 1).reduce(
1723
+ (acc, p) => union(acc, firstSetOf(p)),
1724
+ { kind: "empty" }
1725
+ );
1726
+ return firstSetSentinel(fs2);
1727
+ });
1728
+ const values = def.valueUnused ? void 0 : [];
1729
+ let cur = pos;
1730
+ const inheritedSync = ctx._sync;
1731
+ try {
1732
+ for (let i = 0; i < parsers.length; i++) {
1733
+ ctx._sync = followSentinels[i] ?? inheritedSync;
1734
+ if (ctx.trivia && i > 0) {
1735
+ const mark = saveTriviaMark(ctx);
1736
+ let scanEnd;
1737
+ if (needsDeferredTriviaCommit(ctx)) {
1738
+ const scan = scanTrivia(input, cur, ctx);
1739
+ scan.commit();
1740
+ scanEnd = scan.end;
1741
+ } else {
1742
+ scanEnd = advanceTrivia(input, cur, ctx);
1743
+ }
1744
+ const result2 = parsers[i].parse(input, scanEnd, ctx);
1745
+ if (!result2.ok) return result2;
1746
+ if (result2.span.end > scanEnd) cur = result2.span.end;
1747
+ else rollbackTrivia(ctx, mark);
1748
+ if (values !== void 0) values.push(result2.value);
1749
+ continue;
1750
+ }
1751
+ const result = parsers[i].parse(input, cur, ctx);
1752
+ if (!result.ok) return result;
1753
+ if (values !== void 0) values.push(result.value);
1754
+ cur = result.span.end;
1846
1755
  }
1756
+ } finally {
1757
+ ctx._sync = inheritedSync;
1758
+ }
1759
+ return {
1760
+ ok: true,
1761
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1762
+ value: values ?? void 0,
1763
+ span: { start: pos, end: cur }
1847
1764
  };
1848
1765
  }
1849
- const tr = parseTriviaNoCapture(triviaP, input, cur, ctx);
1850
- return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1766
+ return {
1767
+ _tag: "sequence",
1768
+ _meta: meta,
1769
+ _def: def,
1770
+ parse(input, pos, ctx) {
1771
+ if (ctx._tolerant) return parseTolerant(input, pos, ctx);
1772
+ const values = def.valueUnused ? void 0 : [];
1773
+ let cur = pos;
1774
+ for (let i = 0; i < parsers.length; i++) {
1775
+ if (ctx.trivia && i > 0) {
1776
+ let scanEnd;
1777
+ let mark = saveTriviaMark(ctx);
1778
+ if (needsDeferredTriviaCommit(ctx)) {
1779
+ const scan = scanTrivia(input, cur, ctx);
1780
+ scan.commit();
1781
+ scanEnd = scan.end;
1782
+ } else {
1783
+ scanEnd = advanceTrivia(input, cur, ctx);
1784
+ }
1785
+ const result2 = parsers[i].parse(input, scanEnd, ctx);
1786
+ if (!result2.ok) return result2;
1787
+ if (result2.span.end > scanEnd) {
1788
+ cur = result2.span.end;
1789
+ } else {
1790
+ rollbackTrivia(ctx, mark);
1791
+ }
1792
+ if (values !== void 0) values.push(result2.value);
1793
+ continue;
1794
+ }
1795
+ const result = parsers[i].parse(input, cur, ctx);
1796
+ if (!result.ok) return result;
1797
+ if (values !== void 0) values.push(result.value);
1798
+ cur = result.span.end;
1799
+ }
1800
+ return {
1801
+ ok: true,
1802
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1803
+ value: values ?? void 0,
1804
+ span: { start: pos, end: cur }
1805
+ };
1806
+ }
1807
+ };
1851
1808
  }
1852
- function consumeTrivia(input, cur, ctx) {
1853
- if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1854
- const scan = scanTrivia(input, cur, ctx);
1855
- scan.commit();
1856
- return scan.end;
1809
+
1810
+ // src/combinators/expect.ts
1811
+ function deriveExpected(c) {
1812
+ return [...new Set(derive(c, /* @__PURE__ */ new Set()))];
1857
1813
  }
1858
- function fastTriviaScanner(trivia2) {
1859
- const cached = fastTriviaCache.get(trivia2);
1860
- if (cached !== void 0) return cached;
1861
- const scanner = buildFastTriviaScanner(trivia2);
1862
- fastTriviaCache.set(trivia2, scanner);
1863
- return scanner;
1814
+ function derive(c, seen) {
1815
+ const deriveExpected2 = (p) => derive(p, seen);
1816
+ const def = c._def;
1817
+ switch (def.tag) {
1818
+ case "literal":
1819
+ return [JSON.stringify(def.value)];
1820
+ case "regex":
1821
+ return [`/${def.source}/`];
1822
+ case "keywords":
1823
+ return def.words.map((w) => JSON.stringify(w));
1824
+ case "label":
1825
+ return [def.label];
1826
+ case "choice":
1827
+ return def.parsers.flatMap(deriveExpected2);
1828
+ case "dispatch":
1829
+ return deriveExpected2(def.selector);
1830
+ // Union through the NULLABLE prefix, mirroring `sequenceFirstSet`. A leading
1831
+ // `optional(…)`/`many(…)` can match nothing, so the term after it is equally
1832
+ // able to fail first — deriving from term 0 alone named a token the parse
1833
+ // never actually required. A leading `not(…)` is zero-width and contributes
1834
+ // nothing (its own expectation is about what must NOT be here), but it is
1835
+ // nullable, so keep scanning past it. Stop at the first term that must match.
1836
+ case "sequence": {
1837
+ const out = [];
1838
+ for (const term of def.parsers) {
1839
+ if (!isZeroWidthAssertion(term)) out.push(...deriveExpected2(term));
1840
+ if (!matchesEmpty(term)) break;
1841
+ }
1842
+ return out;
1843
+ }
1844
+ case "attempt":
1845
+ return deriveExpected2(def.parser);
1846
+ // Delegating wrappers whose first-token failure is their inner parser's — must
1847
+ // pass through so a start-failure (and the first-set-miss fast-path in attempt/
1848
+ // node, which reads this) names the real expected token, not the wrapper tag.
1849
+ case "field":
1850
+ case "withCtx":
1851
+ case "recover":
1852
+ return deriveExpected2(def.parser);
1853
+ case "skip":
1854
+ return deriveExpected2(def.main);
1855
+ case "expect":
1856
+ return def.expected;
1857
+ case "node":
1858
+ case "grammar":
1859
+ case "trivia":
1860
+ case "token":
1861
+ case "leaf":
1862
+ case "optional":
1863
+ case "many":
1864
+ case "oneOrMore":
1865
+ case "transform":
1866
+ case "not":
1867
+ case "peek":
1868
+ return deriveExpected2(def.parser);
1869
+ case "routed":
1870
+ return ["routed()"];
1871
+ case "lazy": {
1872
+ const name = c._ruleName;
1873
+ if (seen.has(c)) return name ? [name] : [];
1874
+ seen.add(c);
1875
+ try {
1876
+ return deriveExpected2(def.thunk());
1877
+ } catch {
1878
+ return name ? [name] : [];
1879
+ } finally {
1880
+ seen.delete(c);
1881
+ }
1882
+ }
1883
+ default:
1884
+ return [];
1885
+ }
1864
1886
  }
1865
- function buildFastTriviaScanner(trivia2) {
1866
- const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1867
- const direct = regexTriviaScanner(core);
1868
- if (direct) return direct;
1869
- const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1870
- if (!repeat) return null;
1871
- const one = regexTriviaScanner(repeat);
1872
- if (one) return loopScanner([one]);
1873
- if (repeat._def.tag !== "choice") return null;
1874
- const arms = repeat._def.parsers.map(regexTriviaScanner);
1875
- if (arms.some((s) => s === null)) return null;
1876
- return loopScanner(arms);
1887
+ function expect(combinator, label2) {
1888
+ const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
1889
+ const meta = {
1890
+ firstSet: combinator._meta.firstSet,
1891
+ canMatchNewline: combinator._meta.canMatchNewline,
1892
+ isTrivia: false
1893
+ };
1894
+ return {
1895
+ _tag: "expect",
1896
+ _meta: meta,
1897
+ _def: { tag: "expect", parser: combinator, label: label2, expected },
1898
+ parse(input, pos, ctx) {
1899
+ const result = combinator.parse(input, pos, ctx);
1900
+ if (result.ok) return result;
1901
+ const span = { start: pos, end: pos };
1902
+ const error = { _tag: "parseError", span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected };
1903
+ ctx._errors?.push(error);
1904
+ if (ctx._tolerant) captureError(ctx, error);
1905
+ return { ok: true, value: error, span: { start: pos, end: pos } };
1906
+ }
1907
+ };
1877
1908
  }
1878
- function loopScanner(arms) {
1879
- return (input, cur) => {
1880
- let pos = cur;
1881
- scan: while (pos < input.length) {
1882
- for (const arm of arms) {
1883
- const end = arm(input, pos);
1884
- if (end > pos) {
1885
- pos = end;
1886
- continue scan;
1909
+
1910
+ // src/combinators/choice.ts
1911
+ function choice(...args) {
1912
+ const parsers = args.map((a) => "gate" in a ? a.combinator : a);
1913
+ const gates = args.map((a) => "gate" in a ? a.gate : null);
1914
+ const hasGates = gates.some((g) => g !== null);
1915
+ const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
1916
+ let combined = { kind: "empty" };
1917
+ for (const p of parsers) combined = union(combined, p._meta.firstSet);
1918
+ const meta = {
1919
+ firstSet: combined,
1920
+ canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
1921
+ isTrivia: false,
1922
+ disjoint
1923
+ };
1924
+ const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
1925
+ const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
1926
+ let greedyLitMap = null;
1927
+ let sortedParsers = null;
1928
+ const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
1929
+ if (strategy?.tag === "greedyClassify") {
1930
+ greedyLitMap = /* @__PURE__ */ new Map();
1931
+ for (let i = 0; i < parsers.length; i++) {
1932
+ if (i === strategy.superIndex) continue;
1933
+ const litVal = getCoreLiteralValue(parsers[i]);
1934
+ if (litVal !== null) greedyLitMap.set(litVal, i);
1935
+ }
1936
+ } else if (strategy?.tag === "literalsLongestFirst") {
1937
+ sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
1938
+ }
1939
+ return {
1940
+ _tag: "choice",
1941
+ _meta: meta,
1942
+ _def: {
1943
+ tag: "choice",
1944
+ parsers,
1945
+ gates,
1946
+ disjoint,
1947
+ strategy: strategy ?? { tag: "firstMatch" },
1948
+ autoNot
1949
+ },
1950
+ parse(input, pos, ctx) {
1951
+ const expected = [];
1952
+ if (disjoint && pos < input.length) {
1953
+ const code = input.codePointAt(pos);
1954
+ let idx = code < 128 ? asciiDispatch[code] : -1;
1955
+ if (idx < 0) {
1956
+ for (let i = 0; i < parsers.length; i++) {
1957
+ if (inFirstSet(code, parsers[i]._meta.firstSet)) {
1958
+ idx = i;
1959
+ break;
1960
+ }
1961
+ }
1962
+ }
1963
+ if (idx >= 0) {
1964
+ const gate2 = gates[idx];
1965
+ if (gate2 && !gate2(ctx.state)) {
1966
+ return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
1967
+ }
1968
+ const result = parsers[idx].parse(input, pos, ctx);
1969
+ if (result.ok) return result;
1970
+ expected.push(...result.expected);
1971
+ if (result.committed) return { ok: false, expected, span: { start: pos, end: pos }, committed: true };
1972
+ return { ok: false, expected, span: { start: pos, end: pos } };
1973
+ }
1974
+ return {
1975
+ ok: false,
1976
+ expected: parsers.flatMap((p) => {
1977
+ const r = p.parse(input, pos, ctx);
1978
+ return r.ok ? [] : r.expected;
1979
+ }),
1980
+ span: { start: pos, end: pos }
1981
+ };
1982
+ }
1983
+ if (strategy?.tag === "greedyClassify") {
1984
+ const superResult = parsers[strategy.superIndex].parse(input, pos, ctx);
1985
+ if (!superResult.ok) return superResult;
1986
+ const end = superResult.span.end;
1987
+ const litIdx = greedyLitMap.get(input.slice(pos, end));
1988
+ if (litIdx !== void 0) {
1989
+ const litVal = getCoreLiteralValue(parsers[litIdx]);
1990
+ const value = applyTransforms(parsers[litIdx], litVal, { start: pos, end });
1991
+ return { ok: true, value, span: { start: pos, end } };
1992
+ }
1993
+ return superResult;
1994
+ }
1995
+ if (strategy?.tag === "literalsLongestFirst") {
1996
+ for (const p of sortedParsers) {
1997
+ const r = p.parse(input, pos, ctx);
1998
+ if (r.ok) return r;
1999
+ expected.push(...r.expected);
1887
2000
  }
2001
+ return { ok: false, expected, span: { start: pos, end: pos } };
1888
2002
  }
1889
- break;
2003
+ for (let i = 0; i < parsers.length; i++) {
2004
+ if (gates[i] && !gates[i](ctx.state)) continue;
2005
+ const mark = saveTriviaMark(ctx);
2006
+ const result = parsers[i].parse(input, pos, ctx);
2007
+ if (!result.ok) {
2008
+ rollbackTrivia(ctx, mark);
2009
+ expected.push(...result.expected);
2010
+ if (result.committed) return { ok: false, expected, span: result.span, committed: true };
2011
+ continue;
2012
+ }
2013
+ const checks = autoNot[i];
2014
+ if (checks && autoNotFires(input, result.span.end, checks)) {
2015
+ rollbackTrivia(ctx, mark);
2016
+ continue;
2017
+ }
2018
+ return result;
2019
+ }
2020
+ return { ok: false, expected, span: { start: pos, end: pos } };
1890
2021
  }
1891
- return pos;
1892
2022
  };
1893
2023
  }
1894
- function regexTriviaScanner(parser2) {
1895
- if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1896
- const source = parser2._def.source;
1897
- return classRunSource(source) ?? altStarSource(source) ?? (blockCommentSource(source) ? scanBlockComment : null);
1898
- }
1899
- function inRanges2(cp, ranges) {
1900
- for (let i = 0; i < ranges.length; i++) {
1901
- const r = ranges[i];
1902
- if (cp >= r[0] && cp <= r[1]) return true;
2024
+ function detectStrategy(parsers) {
2025
+ const regexIndices = [];
2026
+ const literalIndices = [];
2027
+ for (let i = 0; i < parsers.length; i++) {
2028
+ if (getCoreRegexDef(parsers[i]) !== null) regexIndices.push(i);
2029
+ else if (getCoreLiteralValue(parsers[i]) !== null) literalIndices.push(i);
1903
2030
  }
1904
- return false;
1905
- }
1906
- function classScanner(classBody) {
1907
- const ranges = parseClassRanges(classBody);
1908
- if (!ranges) return null;
1909
- return (input, cur) => {
1910
- let pos = cur;
1911
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1912
- return pos;
1913
- };
1914
- }
1915
- function lineCommentScanner(commentCode) {
1916
- return (input, cur) => {
1917
- if (input.charCodeAt(cur) !== commentCode) return cur;
1918
- let pos = cur + 1;
1919
- while (pos < input.length) {
1920
- const cc = input.charCodeAt(pos);
1921
- if (cc === 10 || cc === 13) break;
1922
- pos++;
1923
- }
1924
- return pos;
1925
- };
1926
- }
1927
- function classRunSource(source) {
1928
- const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1929
- return m ? classScanner(m[1]) : null;
1930
- }
1931
- function classifyTriviaArm(arm) {
1932
- const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1933
- if (cls) {
1934
- const ranges = parseClassRanges(cls[1]);
1935
- return ranges ? { kind: "class", ranges } : null;
2031
+ if (regexIndices.length === 1 && literalIndices.length === parsers.length - 1 && literalIndices.length > 0) {
2032
+ const superIndex = regexIndices[0];
2033
+ const regexDef = getCoreRegexDef(parsers[superIndex]);
2034
+ const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
2035
+ const re = new RegExp(regexDef.source, flags);
2036
+ const allSubsumed = literalIndices.every((i) => {
2037
+ const litVal = getCoreLiteralValue(parsers[i]);
2038
+ re.lastIndex = 0;
2039
+ const m = re.exec(litVal);
2040
+ return m !== null && m[0] === litVal;
2041
+ });
2042
+ if (allSubsumed) return { tag: "greedyClassify", superIndex };
1936
2043
  }
1937
- const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1938
- if (lc) {
1939
- const marker = lc[1];
1940
- return { kind: "comment", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
2044
+ if (parsers.length === literalIndices.length) {
2045
+ const sortedIndices = [...literalIndices].sort(
2046
+ (a, b) => getCoreLiteralValue(parsers[b]).length - getCoreLiteralValue(parsers[a]).length
2047
+ );
2048
+ return { tag: "literalsLongestFirst", sortedIndices };
1941
2049
  }
2050
+ const shared = detectSharedPrefix(parsers);
2051
+ if (shared !== null) return shared;
2052
+ return { tag: "firstMatch" };
2053
+ }
2054
+ function bareLeadingTermKey(term) {
2055
+ const d = term._def;
2056
+ if (d.tag === "literal" && !d.caseInsensitive) return `L:${d.value}`;
2057
+ if (d.tag === "regex") return `R:${d.source}\0${d.flags}`;
1942
2058
  return null;
1943
2059
  }
1944
- function armScanner(arm) {
1945
- if (arm.kind === "comment") return lineCommentScanner(arm.code);
1946
- const ranges = arm.ranges;
1947
- return (input, cur) => {
1948
- let pos = cur;
1949
- while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1950
- return pos;
1951
- };
2060
+ function detectSharedPrefix(parsers) {
2061
+ if (parsers.length < 2) return null;
2062
+ let key = null;
2063
+ let prefix = null;
2064
+ const members = [];
2065
+ for (let i = 0; i < parsers.length; i++) {
2066
+ const term0 = leadingTermOfArm(parsers[i]);
2067
+ if (term0 === null) return null;
2068
+ const k = bareLeadingTermKey(term0);
2069
+ if (k === null) return null;
2070
+ if (key === null) {
2071
+ key = k;
2072
+ prefix = term0;
2073
+ } else if (k !== key) return null;
2074
+ members.push(i);
2075
+ }
2076
+ if (prefix === null || members.length < 2) return null;
2077
+ return { tag: "sharedPrefix", prefix, members };
1952
2078
  }
1953
- function fusedTriviaScanner(ranges, commentCodes) {
1954
- const c0 = commentCodes[0];
1955
- const single = commentCodes.length === 1;
1956
- return (input, cur) => {
1957
- let pos = cur;
1958
- const len = input.length;
1959
- for (; ; ) {
1960
- const c = input.charCodeAt(pos);
1961
- if (pos < len && inRanges2(c, ranges)) {
1962
- pos++;
2079
+ function leadingTermOfArm(arm) {
2080
+ let d = arm._def;
2081
+ for (; ; ) {
2082
+ if (d.tag === "node" || d.tag === "grammar" || d.tag === "transform" || d.tag === "label") {
2083
+ d = d.parser._def;
2084
+ continue;
2085
+ }
2086
+ break;
2087
+ }
2088
+ if (d.tag !== "sequence" || d.parsers.length < 2) return null;
2089
+ return d.parsers[0];
2090
+ }
2091
+ function computeAutoNot(parsers) {
2092
+ return parsers.map((p, i) => {
2093
+ const litVal = getCoreLiteralValue(p);
2094
+ if (litVal === null) return null;
2095
+ const checks = [];
2096
+ for (let j = i + 1; j < parsers.length; j++) {
2097
+ const other = parsers[j];
2098
+ const otherLit = getCoreLiteralValue(other);
2099
+ if (otherLit !== null && otherLit.startsWith(litVal) && otherLit.length > litVal.length) {
2100
+ checks.push({ kind: "startsWith", value: otherLit.slice(litVal.length) });
1963
2101
  continue;
1964
2102
  }
1965
- if (single ? c === c0 : commentCodes.includes(c)) {
1966
- pos++;
1967
- while (pos < len) {
1968
- const cc = input.charCodeAt(pos);
1969
- if (cc === 10 || cc === 13) break;
1970
- pos++;
1971
- }
1972
- continue;
2103
+ const regexDef = getCoreRegexDef(other);
2104
+ if (regexDef !== null) {
2105
+ const contSet = continuationFirstSet(litVal, regexDef.source, regexDef.flags);
2106
+ if (contSet !== null) checks.push({ kind: "firstSet", set: contSet });
1973
2107
  }
1974
- break;
1975
2108
  }
1976
- return pos;
1977
- };
2109
+ return checks.length > 0 ? checks : null;
2110
+ });
1978
2111
  }
1979
- function splitTopLevelAlts(body) {
1980
- const arms = [];
1981
- let inClass = false;
1982
- let start = 0;
1983
- for (let i = 0; i < body.length; i++) {
1984
- const c = body[i];
1985
- if (c === "\\") {
1986
- i++;
1987
- continue;
1988
- }
1989
- if (inClass) {
1990
- if (c === "]") inClass = false;
1991
- continue;
1992
- }
1993
- if (c === "[") inClass = true;
1994
- else if (c === "(") return null;
1995
- else if (c === "|") {
1996
- arms.push(body.slice(start, i));
1997
- start = i + 1;
2112
+ function autoNotFires(input, end, checks) {
2113
+ for (const check of checks) {
2114
+ if (check.kind === "firstSet") {
2115
+ const code = end < input.length ? input.codePointAt(end) ?? -1 : -1;
2116
+ if (inFirstSet(code, check.set)) return true;
2117
+ } else {
2118
+ if (input.startsWith(check.value, end)) return true;
1998
2119
  }
1999
2120
  }
2000
- arms.push(body.slice(start));
2001
- return arms;
2121
+ return false;
2002
2122
  }
2003
- function altStarSource(source) {
2004
- const m = /^\(\?:(.*)\)[*+]$/.exec(source);
2005
- if (!m) return null;
2006
- const armSrcs = splitTopLevelAlts(m[1]);
2007
- if (!armSrcs || armSrcs.length < 2) return null;
2008
- const arms = [];
2009
- for (const src of armSrcs) {
2010
- const arm = classifyTriviaArm(src);
2011
- if (!arm) return null;
2012
- arms.push(arm);
2123
+ function getCoreLiteralValue(p) {
2124
+ const def = p._def;
2125
+ if (def.tag === "literal" && !def.caseInsensitive) return def.value;
2126
+ if (def.tag === "transform") return getCoreLiteralValue(def.parser);
2127
+ return null;
2128
+ }
2129
+ function getCoreRegexDef(p) {
2130
+ const def = p._def;
2131
+ if (def.tag === "regex") return { source: def.source, flags: def.flags };
2132
+ if (def.tag === "transform") return getCoreRegexDef(def.parser);
2133
+ if (def.tag === "label") return getCoreRegexDef(def.parser);
2134
+ return null;
2135
+ }
2136
+ function applyTransforms(p, value, span) {
2137
+ const def = p._def;
2138
+ if (def.tag === "transform") {
2139
+ const inner = applyTransforms(def.parser, value, span);
2140
+ return def.fn(inner, span);
2013
2141
  }
2014
- const ranges = [];
2015
- const commentCodes = [];
2016
- for (const arm of arms) {
2017
- if (arm.kind === "class") ranges.push(...arm.ranges);
2018
- else commentCodes.push(arm.code);
2142
+ return value;
2143
+ }
2144
+ function continuationFirstSet(lit, source, flags) {
2145
+ const re = new RegExp(source, "y" + flags.replace(/[gy]/g, ""));
2146
+ re.lastIndex = 0;
2147
+ const base = re.exec(lit);
2148
+ if (!base || base[0] !== lit) return null;
2149
+ const contCodes = [];
2150
+ for (let code = 1; code < 128; code++) {
2151
+ re.lastIndex = 0;
2152
+ const m = re.exec(lit + String.fromCharCode(code));
2153
+ if (m && m[0].length > lit.length) contCodes.push(code);
2019
2154
  }
2020
- if (commentCodes.some((code) => inRanges2(code, ranges))) {
2021
- return loopScanner(arms.map(armScanner));
2155
+ if (contCodes.length === 0) return null;
2156
+ return codesToFirstSet(contCodes);
2157
+ }
2158
+ function codesToFirstSet(codes) {
2159
+ codes.sort((a, b) => a - b);
2160
+ const ranges = [];
2161
+ let lo = codes[0], hi = codes[0];
2162
+ for (let i = 1; i < codes.length; i++) {
2163
+ if (codes[i] === hi + 1) {
2164
+ hi = codes[i];
2165
+ } else {
2166
+ ranges.push({ lo, hi });
2167
+ lo = hi = codes[i];
2168
+ }
2022
2169
  }
2023
- if (commentCodes.length === 0) return armScanner({ kind: "class", ranges });
2024
- return fusedTriviaScanner(ranges, commentCodes);
2170
+ ranges.push({ lo, hi });
2171
+ return { kind: "ranges", ranges };
2025
2172
  }
2026
- function blockCommentSource(source) {
2027
- return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
2173
+ function inFirstSet(code, fs2) {
2174
+ if (fs2.kind === "any") return true;
2175
+ if (fs2.kind === "empty") return false;
2176
+ for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
2177
+ return false;
2028
2178
  }
2029
- function scanBlockComment(input, cur) {
2030
- if (input.charCodeAt(cur) !== 47 || input.charCodeAt(cur + 1) !== 42) return cur;
2031
- const close = input.indexOf("*/", cur + 2);
2032
- return close === -1 ? cur : close + 2;
2179
+ function areDisjoint(sets) {
2180
+ if (sets.some((s) => s.kind === "any")) return false;
2181
+ for (let i = 0; i < sets.length; i++)
2182
+ for (let j = i + 1; j < sets.length; j++)
2183
+ if (intersects(sets[i], sets[j])) return false;
2184
+ return true;
2033
2185
  }
2034
-
2035
- // src/combinators/sequence.ts
2036
- function sequence(...parsers) {
2037
- const meta = {
2038
- // Union through the nullable prefix — a leading optional()/many() lets a later
2039
- // term's first char start the sequence. Just `parsers[0]` under-approximates.
2040
- firstSet: sequenceFirstSet(parsers),
2041
- canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
2042
- isTrivia: false
2043
- };
2044
- const def = { tag: "sequence", parsers };
2045
- let followSentinels;
2046
- function parseTolerant(input, pos, ctx) {
2047
- followSentinels ??= parsers.map((_, i) => {
2048
- const fs2 = parsers.slice(i + 1).reduce(
2049
- (acc, p) => union(acc, firstSetOf(p)),
2050
- { kind: "empty" }
2051
- );
2052
- return firstSetSentinel(fs2);
2053
- });
2054
- const values = def.valueUnused ? void 0 : [];
2055
- let cur = pos;
2056
- const inheritedSync = ctx._sync;
2057
- try {
2058
- for (let i = 0; i < parsers.length; i++) {
2059
- ctx._sync = followSentinels[i] ?? inheritedSync;
2060
- if (ctx.trivia && i > 0) {
2061
- const mark = saveTriviaMark(ctx);
2062
- let scanEnd;
2063
- if (needsDeferredTriviaCommit(ctx)) {
2064
- const scan = scanTrivia(input, cur, ctx);
2065
- scan.commit();
2066
- scanEnd = scan.end;
2067
- } else {
2068
- scanEnd = advanceTrivia(input, cur, ctx);
2069
- }
2070
- const result2 = parsers[i].parse(input, scanEnd, ctx);
2071
- if (!result2.ok) return result2;
2072
- if (result2.span.end > scanEnd) cur = result2.span.end;
2073
- else rollbackTrivia(ctx, mark);
2074
- if (values !== void 0) values.push(result2.value);
2075
- continue;
2076
- }
2077
- const result = parsers[i].parse(input, cur, ctx);
2078
- if (!result.ok) return result;
2079
- if (values !== void 0) values.push(result.value);
2080
- cur = result.span.end;
2186
+ function buildAsciiDispatch(parsers) {
2187
+ const table = Array(128).fill(-1);
2188
+ for (let i = 0; i < parsers.length; i++) {
2189
+ const fs2 = parsers[i]._meta.firstSet;
2190
+ if (fs2.kind !== "ranges") continue;
2191
+ for (const { lo, hi } of fs2.ranges) {
2192
+ for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
2193
+ table[code] = i;
2081
2194
  }
2082
- } finally {
2083
- ctx._sync = inheritedSync;
2084
2195
  }
2085
- return {
2086
- ok: true,
2087
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2088
- value: values ?? void 0,
2089
- span: { start: pos, end: cur }
2090
- };
2091
2196
  }
2092
- return {
2093
- _tag: "sequence",
2094
- _meta: meta,
2095
- _def: def,
2096
- parse(input, pos, ctx) {
2097
- if (ctx._tolerant) return parseTolerant(input, pos, ctx);
2098
- const values = def.valueUnused ? void 0 : [];
2099
- let cur = pos;
2100
- for (let i = 0; i < parsers.length; i++) {
2101
- if (ctx.trivia && i > 0) {
2102
- let scanEnd;
2103
- let mark = saveTriviaMark(ctx);
2104
- if (needsDeferredTriviaCommit(ctx)) {
2105
- const scan = scanTrivia(input, cur, ctx);
2106
- scan.commit();
2107
- scanEnd = scan.end;
2108
- } else {
2109
- scanEnd = advanceTrivia(input, cur, ctx);
2110
- }
2111
- const result2 = parsers[i].parse(input, scanEnd, ctx);
2112
- if (!result2.ok) return result2;
2113
- if (result2.span.end > scanEnd) {
2114
- cur = result2.span.end;
2115
- } else {
2116
- rollbackTrivia(ctx, mark);
2117
- }
2118
- if (values !== void 0) values.push(result2.value);
2119
- continue;
2120
- }
2121
- const result = parsers[i].parse(input, cur, ctx);
2122
- if (!result.ok) return result;
2123
- if (values !== void 0) values.push(result.value);
2124
- cur = result.span.end;
2125
- }
2126
- return {
2127
- ok: true,
2128
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2129
- value: values ?? void 0,
2130
- span: { start: pos, end: cur }
2131
- };
2132
- }
2133
- };
2197
+ return table;
2134
2198
  }
2135
2199
 
2136
2200
  // src/combinators/dispatch.ts
@@ -2390,10 +2454,12 @@ function attempt(parser2) {
2390
2454
  }
2391
2455
  const mark = saveCstMark(ctx);
2392
2456
  const log = ctx._triviaLog?.length;
2457
+ const rootLog = ctx._rootTriviaLog?.length;
2393
2458
  const result = parser2.parse(input, pos, ctx);
2394
2459
  if (result.ok) return result;
2395
2460
  rollbackCstCapture(ctx, mark);
2396
2461
  if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
2462
+ if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
2397
2463
  if (result.committed) return result;
2398
2464
  return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
2399
2465
  }
@@ -2797,18 +2863,37 @@ function createParseLineContext(input, pos) {
2797
2863
  }
2798
2864
  function parser(opts, root) {
2799
2865
  const clearTrivia = opts.trivia === null;
2866
+ const opaqueRootCapture = opts.rootCapture === "opaque";
2867
+ if (opaqueRootCapture && opts.trivia === void 0) {
2868
+ throw new TypeError("parser({ rootCapture: 'opaque' }) requires an explicit trivia scope.");
2869
+ }
2800
2870
  return {
2801
2871
  _tag: "grammar",
2802
- _meta: root._meta,
2872
+ _meta: {
2873
+ ...root._meta,
2874
+ // Trivia classification describes this grammar's own active trivia
2875
+ // scope. A directly nested grammar may have classified trivia of its
2876
+ // own, but that must not advertise classification for this wrapper.
2877
+ triviaKindLabels: void 0,
2878
+ rootTriviaClassified: void 0,
2879
+ ...opts.trivia?._meta?.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {},
2880
+ ...opts.trivia?._meta?.rootTriviaClassified ? { rootTriviaClassified: true } : {}
2881
+ },
2803
2882
  _def: {
2804
2883
  tag: "grammar",
2805
2884
  parser: root,
2806
2885
  triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
2807
2886
  clearTrivia,
2887
+ ...opaqueRootCapture ? { rootCapture: "opaque" } : {},
2808
2888
  ...opts.captureTrivia ? { captureTrivia: true } : {},
2809
2889
  trackLines: opts.trackLines ?? false
2810
2890
  },
2811
2891
  parse(input, pos, _ctx) {
2892
+ if (_ctx?._rootTriviaStrictScopes && opts.trivia !== void 0 && opts.trivia !== null && !opts.trivia._meta.rootTriviaClassified && !opaqueRootCapture) {
2893
+ throw new TypeError(
2894
+ "parser(): selected root trivia requires classifiedTrivia() for every local trivia scope, or rootCapture: 'opaque'."
2895
+ );
2896
+ }
2812
2897
  const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
2813
2898
  const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
2814
2899
  const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
@@ -2831,7 +2916,11 @@ function parser(opts, root) {
2831
2916
  // labels (`_ctx.triviaKindLabels`), so captureTriviaKinds still applies when
2832
2917
  // trivia is inherited rather than re-declared here. No labels → undefined
2833
2918
  // (capture all).
2834
- ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {}
2919
+ ...opts.captureTriviaKinds && !clearTrivia ? { _triviaCaptureMask: triviaKindMask(opts.trivia?._meta.triviaKindLabels ?? _ctx?.triviaKindLabels, opts.captureTriviaKinds) } : {},
2920
+ // This is a property of the local trivia scope, not a post-parse filter:
2921
+ // nested recognition can still skip its trivia, but selected root rows
2922
+ // must never be written for an explicitly opaque region.
2923
+ ...opaqueRootCapture ? { _rootTriviaCapture: false } : {}
2835
2924
  };
2836
2925
  const result = root.parse(input, pos ?? 0, ctx);
2837
2926
  if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
@@ -2845,6 +2934,123 @@ function noTrivia(root) {
2845
2934
  return parser({ trivia: null }, root);
2846
2935
  }
2847
2936
 
2937
+ // src/cst/reflection.ts
2938
+ var GRAMMAR_REFLECTION = /* @__PURE__ */ Symbol.for("parseman.grammarReflection");
2939
+ function childrenOf(def) {
2940
+ switch (def.tag) {
2941
+ case "sequence":
2942
+ case "choice":
2943
+ return def.parsers;
2944
+ case "dispatch":
2945
+ return [
2946
+ def.selector,
2947
+ ...def.cases.map((c) => c.parser),
2948
+ ...def.matchers ? def.matchers.map((c) => c.parser) : [],
2949
+ ...def.otherwise ? [def.otherwise] : []
2950
+ ];
2951
+ case "many":
2952
+ case "oneOrMore":
2953
+ case "optional":
2954
+ case "attempt":
2955
+ case "transform":
2956
+ case "trivia":
2957
+ case "token":
2958
+ case "leaf":
2959
+ case "label":
2960
+ case "field":
2961
+ case "not":
2962
+ case "peek":
2963
+ case "node":
2964
+ case "withCtx":
2965
+ case "recover":
2966
+ case "expect":
2967
+ return [def.parser];
2968
+ case "grammar":
2969
+ return def.triviaParser ? [def.parser, def.triviaParser] : [def.parser];
2970
+ case "sepBy":
2971
+ return [def.parser, def.separator];
2972
+ case "skip":
2973
+ return [def.main, def.skipped];
2974
+ case "scanTo":
2975
+ return [def.sentinel, ...def.skip];
2976
+ case "literal":
2977
+ case "regex":
2978
+ case "keywords":
2979
+ case "routed":
2980
+ case "guard":
2981
+ case "lazy":
2982
+ case "unknown":
2983
+ return [];
2984
+ }
2985
+ }
2986
+ function addNode(out, def) {
2987
+ if (def.type === void 0) return;
2988
+ const existing = out.get(def.type);
2989
+ if (existing === void 0) {
2990
+ out.set(def.type, def.tags === void 0 ? [] : [...def.tags]);
2991
+ return;
2992
+ }
2993
+ if (def.tags === void 0) return;
2994
+ for (const tag of def.tags) {
2995
+ if (!existing.includes(tag)) existing.push(tag);
2996
+ }
2997
+ }
2998
+ function collectFromCombinator(parser2, out, seen, followLazy = true) {
2999
+ if (seen.has(parser2)) return;
3000
+ seen.add(parser2);
3001
+ const def = parser2._def;
3002
+ if (def.tag === "lazy") {
3003
+ if (followLazy) {
3004
+ try {
3005
+ collectFromCombinator(def.thunk(), out, seen, followLazy);
3006
+ } catch {
3007
+ }
3008
+ }
3009
+ return;
3010
+ }
3011
+ if (def.tag === "node") addNode(out, def);
3012
+ for (const child of childrenOf(def)) collectFromCombinator(child, out, seen, followLazy);
3013
+ }
3014
+ function collectGrammarReflection(ruleMap, opts) {
3015
+ const byType = /* @__PURE__ */ new Map();
3016
+ const followLazy = opts?.followLazy !== false;
3017
+ for (const [, rule] of ruleMap) {
3018
+ if (!followLazy && rule._def.tag === "lazy") {
3019
+ try {
3020
+ const resolved = rule._def.thunk();
3021
+ if (resolved._def.tag !== "lazy") collectFromCombinator(resolved, byType, /* @__PURE__ */ new Set(), false);
3022
+ } catch {
3023
+ }
3024
+ continue;
3025
+ }
3026
+ collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
3027
+ }
3028
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
3029
+ }
3030
+ function mergeGrammarReflections(reflections) {
3031
+ const byType = /* @__PURE__ */ new Map();
3032
+ for (const reflection of reflections) {
3033
+ for (const node2 of reflection.nodes) {
3034
+ const tags = byType.get(node2.type);
3035
+ if (tags === void 0) {
3036
+ byType.set(node2.type, [...node2.tags]);
3037
+ continue;
3038
+ }
3039
+ for (const tag of node2.tags) {
3040
+ if (!tags.includes(tag)) tags.push(tag);
3041
+ }
3042
+ }
3043
+ }
3044
+ return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
3045
+ }
3046
+ function attachGrammarReflection(grammar, reflection) {
3047
+ Object.defineProperty(grammar, GRAMMAR_REFLECTION, { value: reflection, enumerable: false, configurable: true });
3048
+ return grammar;
3049
+ }
3050
+ function grammarReflectionSource(reflection) {
3051
+ return `{ nodes: ${JSON.stringify(reflection.nodes)} }`;
3052
+ }
3053
+
2848
3054
  // src/combinators/parser.ts
2849
3055
  var RULE_ORDER = "__parsemanRuleOrder";
2850
3056
  function tagRule(r, key) {
@@ -2943,6 +3149,7 @@ function rules(a, b) {
2943
3149
  enumerable: false,
2944
3150
  configurable: true
2945
3151
  });
3152
+ attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
2946
3153
  return cache;
2947
3154
  }
2948
3155
 
@@ -3263,13 +3470,14 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3263
3470
  const collapse = opts?.collapse === true;
3264
3471
  const captureTrivia = opts?.captureTrivia === true;
3265
3472
  const trailingTrivia = opts?.trailingTrivia === true;
3473
+ const tags = opts?.tags;
3266
3474
  if (unwrap && collapse) {
3267
3475
  throw new Error("node() options cannot set both unwrap and collapse");
3268
3476
  }
3269
3477
  if (project !== void 0 && (unwrap || collapse)) {
3270
3478
  throw new Error("node() options cannot combine project with unwrap or collapse");
3271
3479
  }
3272
- 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;
3480
+ 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;
3273
3481
  const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
3274
3482
  const clonesState = build ? buildReadsState(def) : project === void 0;
3275
3483
  const hasOwnFields = parserHasOwnFields(combinator);
@@ -3315,7 +3523,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
3315
3523
  const cstOutput = cstOutputHost(ctx.build);
3316
3524
  const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
3317
3525
  const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
3318
- 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 };
3526
+ 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 };
3319
3527
  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 };
3320
3528
  if (saved.buf !== void 0 || saved.ch !== void 0) {
3321
3529
  pushCstChild(ctx, built, rawEntry);
@@ -3365,6 +3573,25 @@ function trivia(combinator) {
3365
3573
  parse: combinator.parse.bind(combinator)
3366
3574
  };
3367
3575
  }
3576
+ function classifiedTrivia(arms) {
3577
+ const entries = Object.entries(arms);
3578
+ if (entries.length === 0) {
3579
+ throw new TypeError("classifiedTrivia() requires at least one named trivia arm.");
3580
+ }
3581
+ for (let i = 0; i < entries.length; i++) {
3582
+ const [name, arm] = entries[i];
3583
+ const first = arm._meta.firstSet;
3584
+ if (matchesEmpty(arm) || first.kind !== "ranges" || first.ranges.length === 0) {
3585
+ throw new TypeError(
3586
+ `classifiedTrivia(): ${JSON.stringify(name)} must be non-nullable with a concrete finite first set.`
3587
+ );
3588
+ }
3589
+ }
3590
+ const labeledArms = entries.map(([name, arm]) => label(name, arm));
3591
+ const result = trivia(oneOrMore(choice(...labeledArms)));
3592
+ result._meta.rootTriviaClassified = true;
3593
+ return result;
3594
+ }
3368
3595
  function label(name, combinator) {
3369
3596
  return {
3370
3597
  _tag: combinator._tag,
@@ -3411,6 +3638,7 @@ function token(root) {
3411
3638
  const savedRaw = ctx._cstRawChildren;
3412
3639
  const savedTriviaLog = ctx._cstTriviaLog;
3413
3640
  const savedOuterTriviaLog = ctx._triviaLog;
3641
+ const savedRootTriviaLog = ctx._rootTriviaLog;
3414
3642
  const wasCapturing = cstCaptureActive(ctx);
3415
3643
  ctx.trivia = void 0;
3416
3644
  ctx.triviaKindLabels = void 0;
@@ -3420,6 +3648,7 @@ function token(root) {
3420
3648
  ctx._cstRawChildren = void 0;
3421
3649
  ctx._cstTriviaLog = void 0;
3422
3650
  delete ctx._triviaLog;
3651
+ delete ctx._rootTriviaLog;
3423
3652
  let result;
3424
3653
  try {
3425
3654
  result = root.parse(input, pos, ctx);
@@ -3433,6 +3662,8 @@ function token(root) {
3433
3662
  ctx._cstTriviaLog = savedTriviaLog;
3434
3663
  if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
3435
3664
  else ctx._triviaLog = savedOuterTriviaLog;
3665
+ if (savedRootTriviaLog === void 0) delete ctx._rootTriviaLog;
3666
+ else ctx._rootTriviaLog = savedRootTriviaLog;
3436
3667
  }
3437
3668
  if (!result.ok) return result;
3438
3669
  const span = result.span;
@@ -3487,7 +3718,7 @@ function leaf(root, fn) {
3487
3718
  }
3488
3719
 
3489
3720
  // src/version.ts
3490
- var PARSEMAN_VERSION = "0.42.1";
3721
+ var PARSEMAN_VERSION = "0.44.0";
3491
3722
 
3492
3723
  // src/cst/host-mode.ts
3493
3724
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
@@ -3553,7 +3784,7 @@ var SUGGESTIONS = {
3553
3784
  function classifyBroadArm(arm, resolve2) {
3554
3785
  const seen = /* @__PURE__ */ new Set();
3555
3786
  const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
3556
- const walk2 = (p) => {
3787
+ const walk = (p) => {
3557
3788
  if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
3558
3789
  seen.add(p);
3559
3790
  const d = p._def;
@@ -3567,7 +3798,7 @@ function classifyBroadArm(arm, resolve2) {
3567
3798
  // peek() CARRIES its body's first-set, so it is only broad when the body is
3568
3799
  // (or the body is nullable) — walk in for the real cause.
3569
3800
  case "peek":
3570
- return walk2(d.parser);
3801
+ return walk(d.parser);
3571
3802
  case "scanTo":
3572
3803
  return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
3573
3804
  // `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
@@ -3581,7 +3812,7 @@ function classifyBroadArm(arm, resolve2) {
3581
3812
  case "attempt":
3582
3813
  case "withCtx":
3583
3814
  case "recover":
3584
- return walk2(d.parser);
3815
+ return walk(d.parser);
3585
3816
  case "lazy": {
3586
3817
  const name = p._ruleName;
3587
3818
  let target;
@@ -3593,13 +3824,13 @@ function classifyBroadArm(arm, resolve2) {
3593
3824
  return { cause: "cross-artifact-ref", detail: `unresolved ref${name ? ` g.${name}` : ""}`, unresolvedExternal: name !== void 0 };
3594
3825
  target = bound;
3595
3826
  }
3596
- const inner = walk2(target);
3827
+ const inner = walk(target);
3597
3828
  if (name !== void 0) return inner ? relabel(inner, "cross-artifact-ref", `via ref g.${name} \u2192 ${inner.detail}`) : null;
3598
3829
  return inner;
3599
3830
  }
3600
3831
  case "optional":
3601
3832
  case "many": {
3602
- const inner = walk2(d.parser);
3833
+ const inner = walk(d.parser);
3603
3834
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3604
3835
  }
3605
3836
  case "oneOrMore":
@@ -3612,9 +3843,9 @@ function classifyBroadArm(arm, resolve2) {
3612
3843
  case "node":
3613
3844
  case "grammar":
3614
3845
  case "expect":
3615
- return walk2(d.parser);
3846
+ return walk(d.parser);
3616
3847
  case "skip":
3617
- return walk2(d.main);
3848
+ return walk(d.main);
3618
3849
  case "sequence": {
3619
3850
  let sawNullablePrefix = false;
3620
3851
  for (const t of d.parsers) {
@@ -3623,7 +3854,7 @@ function classifyBroadArm(arm, resolve2) {
3623
3854
  continue;
3624
3855
  }
3625
3856
  if (isAny(firstSetOf(t, /* @__PURE__ */ new Set(), resolve2))) {
3626
- const inner = walk2(t);
3857
+ const inner = walk(t);
3627
3858
  if (sawNullablePrefix)
3628
3859
  return relabel(inner, "nullable-prefix", inner ? `nullable prefix \u2192 ${inner.detail}` : "nullable prefix to broad term");
3629
3860
  return inner ?? { cause: "broad-recognizer", detail: "sequence leading term is broad" };
@@ -3638,18 +3869,18 @@ function classifyBroadArm(arm, resolve2) {
3638
3869
  }
3639
3870
  case "choice": {
3640
3871
  for (const a of d.parsers) {
3641
- const r = walk2(a);
3872
+ const r = walk(a);
3642
3873
  if (r) return relabel(r, r.cause, `choice arm \u2192 ${r.detail}`);
3643
3874
  }
3644
3875
  return null;
3645
3876
  }
3646
3877
  case "dispatch":
3647
- return walk2(d.selector);
3878
+ return walk(d.selector);
3648
3879
  default:
3649
3880
  return { cause: "broad-recognizer", detail: `unmodeled construct (${d.tag})` };
3650
3881
  }
3651
3882
  };
3652
- return walk2(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3883
+ return walk(arm) ?? { cause: "broad-recognizer", detail: "broad (cause not localized)" };
3653
3884
  }
3654
3885
  function peelToLeading(arm) {
3655
3886
  let d = arm._def;
@@ -3851,7 +4082,7 @@ function formatGatingWarnings(report) {
3851
4082
 
3852
4083
  // src/analysis/duplication.ts
3853
4084
  var siteToString = (s) => s.path === "" ? s.rule : `${s.rule} \u203A ${s.path}`;
3854
- function childrenOf(d) {
4085
+ function childrenOf2(d) {
3855
4086
  switch (d.tag) {
3856
4087
  case "sequence":
3857
4088
  case "choice":
@@ -3985,7 +4216,7 @@ function render(p, depth = 3) {
3985
4216
  break;
3986
4217
  }
3987
4218
  if (depth <= 0) return `${d.tag}(\u2026)`;
3988
- const kids = childrenOf(d).map((k) => render(k, depth - 1));
4219
+ const kids = childrenOf2(d).map((k) => render(k, depth - 1));
3989
4220
  switch (d.tag) {
3990
4221
  case "sequence":
3991
4222
  return `sequence(${kids.join(", ")})`;
@@ -4180,7 +4411,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4180
4411
  if (hit !== void 0) return hit;
4181
4412
  const d = p._def;
4182
4413
  shapeCache.set(p, interner.hole);
4183
- const kids = childrenOf(d);
4414
+ const kids = childrenOf2(d);
4184
4415
  const id = interner.of(`${payloadKey(p, d)}|${kids.map(shapeOf).join(",")}`);
4185
4416
  shapeCache.set(p, id);
4186
4417
  return id;
@@ -4190,7 +4421,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4190
4421
  if (hit !== void 0) return hit;
4191
4422
  sizeCache.set(p, 1);
4192
4423
  let n = 1;
4193
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4424
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4194
4425
  sizeCache.set(p, n);
4195
4426
  return n;
4196
4427
  };
@@ -4203,7 +4434,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4203
4434
  visited.set(p, { p, d, site: here, shape: shapeOf(p), size: sizeOf(p), enclosingNode, enclosingReducer });
4204
4435
  const inner = d.tag === "node" ? d.type ?? "(anonymous)" : enclosingNode;
4205
4436
  const innerReducer = enclosingReducer || d.tag === "node" && (d.build !== void 0 || d.buildSrc !== void 0) || d.tag === "transform" || d.tag === "leaf";
4206
- const kids = childrenOf(d);
4437
+ const kids = childrenOf2(d);
4207
4438
  kids.forEach((k, i) => {
4208
4439
  const seg = segment(d, i);
4209
4440
  visit(k, here.rule, here.path === "" ? seg : `${here.path} \u203A ${seg}`, inner, innerReducer);
@@ -4263,7 +4494,7 @@ function analyzeDuplicationRules(ruleMap, opts) {
4263
4494
  function markSubtree(p, into) {
4264
4495
  if (into.has(p)) return;
4265
4496
  into.add(p);
4266
- for (const k of childrenOf(p._def)) markSubtree(k, into);
4497
+ for (const k of childrenOf2(p._def)) markSubtree(k, into);
4267
4498
  }
4268
4499
  var stripMembers = (f) => {
4269
4500
  const { _members, ...rest } = f;
@@ -4303,7 +4534,7 @@ function findNearDuplicates(visited, interner, shapeOf, minSize) {
4303
4534
  if (hit !== void 0) return hit;
4304
4535
  holeCache.set(p, []);
4305
4536
  const d = p._def;
4306
- const kids = childrenOf(d);
4537
+ const kids = childrenOf2(d);
4307
4538
  const kidShapes = kids.map(shapeOf);
4308
4539
  const payload = payloadKey(p, d);
4309
4540
  const out2 = [];
@@ -4364,7 +4595,7 @@ function findDivergentSlot(members, shapeOf) {
4364
4595
  let cur = [...members];
4365
4596
  for (let guard2 = 0; guard2 < 64; guard2++) {
4366
4597
  const d0 = cur[0]._def;
4367
- const kidLists = cur.map((c) => childrenOf(c._def));
4598
+ const kidLists = cur.map((c) => childrenOf2(c._def));
4368
4599
  const arity = kidLists[0].length;
4369
4600
  if (kidLists.some((k) => k.length !== arity) || arity === 0) break;
4370
4601
  const diff = [];
@@ -4377,7 +4608,7 @@ function findDivergentSlot(members, shapeOf) {
4377
4608
  path2.push(i);
4378
4609
  defs.push(d0);
4379
4610
  cur = kidLists.map((k) => k[i]);
4380
- const nextArity = childrenOf(cur[0]._def).length;
4611
+ const nextArity = childrenOf2(cur[0]._def).length;
4381
4612
  const samePayload = cur.every((c) => payloadKey(c, c._def) === payloadKey(cur[0], cur[0]._def));
4382
4613
  if (!samePayload || nextArity === 0) break;
4383
4614
  }
@@ -4385,7 +4616,7 @@ function findDivergentSlot(members, shapeOf) {
4385
4616
  let slotSize = 0;
4386
4617
  const sizeOf = (p) => {
4387
4618
  let n = 1;
4388
- for (const k of childrenOf(p._def)) n += sizeOf(k);
4619
+ for (const k of childrenOf2(p._def)) n += sizeOf(k);
4389
4620
  return n;
4390
4621
  };
4391
4622
  for (const c of cur) slotSize = Math.max(slotSize, sizeOf(c));
@@ -4394,7 +4625,7 @@ function findDivergentSlot(members, shapeOf) {
4394
4625
  function renderWithHole(p, path2, depth = 0) {
4395
4626
  if (depth === path2.length) return "\u2039slot\u203A";
4396
4627
  const d = p._def;
4397
- const kids = childrenOf(d);
4628
+ const kids = childrenOf2(d);
4398
4629
  const idx = path2[depth];
4399
4630
  const parts = kids.map((k, i) => i === idx ? renderWithHole(k, path2, depth + 1) : render(k, 2));
4400
4631
  switch (d.tag) {
@@ -4852,7 +5083,7 @@ function capturesPositionally(p, seen = /* @__PURE__ */ new Set()) {
4852
5083
  seen.add(p);
4853
5084
  const d = p._def;
4854
5085
  if (d.tag === "field" || d.tag === "label") return true;
4855
- for (const k of childrenOf(d)) if (capturesPositionally(k, seen)) return true;
5086
+ for (const k of childrenOf2(d)) if (capturesPositionally(k, seen)) return true;
4856
5087
  return false;
4857
5088
  }
4858
5089
  function detectHandRolledSepBy(terms, shapeOf) {
@@ -4958,7 +5189,7 @@ function nestedNodeTypes(root, out, seen, isRoot) {
4958
5189
  }
4959
5190
  return;
4960
5191
  }
4961
- for (const k of childrenOf(d)) nestedNodeTypes(k, out, seen, false);
5192
+ for (const k of childrenOf2(d)) nestedNodeTypes(k, out, seen, false);
4962
5193
  }
4963
5194
  var MAX_LOST_TYPES = 8;
4964
5195
  function findStructureLoss(visited, resolve2) {
@@ -5385,9 +5616,18 @@ function buildGrammarPlan(entry, winners) {
5385
5616
  return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, dispatches, attempts, labels, rules: rules2 };
5386
5617
  }
5387
5618
 
5619
+ // src/cst/trivia-entries.ts
5620
+ var EMPTY_INDICES = Object.freeze([]);
5621
+ var EMPTY_GAPS = Object.freeze([]);
5622
+ var SELECTED_ROOT_STRIDE = 5;
5623
+
5388
5624
  // src/compiler/scannable-run.ts
5389
5625
  var BACKSLASH = 92;
5390
5626
  var META = new Set("()[]{}*+?|^$.".split(""));
5627
+ var SELECTED_ROOT_OWNED_RANGE_PLACEHOLDER = Array.from(
5628
+ { length: SELECTED_ROOT_STRIDE - 3 },
5629
+ () => "0"
5630
+ ).join(", ");
5391
5631
  function classToRanges(cls) {
5392
5632
  if (cls === "\\d") return shorthandRanges("d");
5393
5633
  if (cls === "\\w") return shorthandRanges("w");
@@ -6426,13 +6666,15 @@ function scanBranch(shape, mint) {
6426
6666
  const m = emitShapeMatch(shape, "_e", mint, " ", "c");
6427
6667
  return [...m.setup, ` if (${m.end} > _e) { _e = ${m.end}; continue }`].join("\n");
6428
6668
  }
6429
- function scanBranchLabeled(shape, kindIndex, mint) {
6669
+ function scanBranchLabeled(shape, kindIndex, label2, mint) {
6430
6670
  const m = emitShapeMatch(shape, "_e", mint, " ", "c");
6431
6671
  return [
6432
6672
  ...m.setup,
6433
6673
  ` if (${m.end} > _e) {`,
6434
6674
  ` if (_cap) {`,
6435
6675
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
6676
+ ` const _rk = _ctx._rootTriviaCapture === false || _ctx._rootTriviaLog === undefined ? -1 : (_ctx._rootTriviaKindIndex?.[${JSON.stringify(label2)}] ?? -1)`,
6677
+ ` if (_rk >= 0) _ctx._rootTriviaLog.push(${SELECTED_ROOT_OWNED_RANGE_PLACEHOLDER}, _e, ${m.end}, _rk)`,
6436
6678
  ` 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})`,
6437
6679
  ` }`,
6438
6680
  ` _e = ${m.end}`,
@@ -6492,6 +6734,11 @@ var CAP_RECORD = [
6492
6734
  ` if (_cap === 1 && _ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
6493
6735
  ` }`
6494
6736
  ].join("\n");
6737
+ var SELECTED_ROOT_FINISH = [
6738
+ ` if (_cap && _ctx._rootTriviaCapture !== false && _ctx._rootTriviaLog !== undefined && _ctx._rootTriviaLog.length !== _rootMark) {`,
6739
+ ` for (let _ri = _rootMark; _ri < _ctx._rootTriviaLog.length; _ri += ${SELECTED_ROOT_STRIDE}) { _ctx._rootTriviaLog[_ri] = _pos; _ctx._rootTriviaLog[_ri + 1] = _e }`,
6740
+ ` }`
6741
+ ].join("\n");
6495
6742
  function composeFastLoop(shapes) {
6496
6743
  const mint = makeMint();
6497
6744
  return [
@@ -6520,7 +6767,7 @@ function analyzeLabeledScannableRun(trivia2) {
6520
6767
  const d = getCoreRegexDef(arm.parser);
6521
6768
  const shape = d ? scanShapeFromRegex(d.source, d.flags) : null;
6522
6769
  if (!shape) return null;
6523
- out.push({ shape, kindIndex: arm.kindIndex });
6770
+ out.push({ shape, kindIndex: arm.kindIndex, label: arm.label });
6524
6771
  }
6525
6772
  return out;
6526
6773
  }
@@ -6529,11 +6776,13 @@ function buildLabeledScannableTriviaFnDecl(fnName, arms) {
6529
6776
  return [
6530
6777
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
6531
6778
  ` let _e = _pos`,
6779
+ ` const _rootMark = _cap && _ctx._rootTriviaLog !== undefined ? _ctx._rootTriviaLog.length : 0`,
6532
6780
  ` while (_e < input.length) {`,
6533
6781
  ` const c = input.charCodeAt(_e)`,
6534
- ...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, mint)),
6782
+ ...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, a.label, mint)),
6535
6783
  ` break`,
6536
6784
  ` }`,
6785
+ SELECTED_ROOT_FINISH,
6537
6786
  ` return _e`,
6538
6787
  `}`
6539
6788
  ].join("\n");
@@ -6549,6 +6798,8 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
6549
6798
  ` const _ce = _e + _m${i}[0].length`,
6550
6799
  ` if (_cap) {`,
6551
6800
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
6801
+ ` const _rk${i} = _ctx._rootTriviaCapture === false || _ctx._rootTriviaLog === undefined ? -1 : (_ctx._rootTriviaKindIndex?.[${JSON.stringify(arm.label)}] ?? -1)`,
6802
+ ` if (_rk${i} >= 0) _ctx._rootTriviaLog.push(0, 0, _e, _ce, _rk${i})`,
6552
6803
  ` 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})`,
6553
6804
  ` }`,
6554
6805
  ` _e = _ce`,
@@ -6560,11 +6811,13 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
6560
6811
  return [
6561
6812
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
6562
6813
  ` let _e = _pos`,
6814
+ ` const _rootMark = _cap && _ctx._rootTriviaLog !== undefined ? _ctx._rootTriviaLog.length : 0`,
6563
6815
  ` while (_e < input.length) {`,
6564
6816
  ` let _matched = false`,
6565
6817
  tryArms,
6566
6818
  ` if (!_matched) break`,
6567
6819
  ` }`,
6820
+ SELECTED_ROOT_FINISH,
6568
6821
  ` return _e`,
6569
6822
  `}`
6570
6823
  ].join("\n");
@@ -6587,6 +6840,8 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
6587
6840
  ` const _ce = _r${i}.span.end`,
6588
6841
  ` if (_cap) {`,
6589
6842
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
6843
+ ` const _rk${i} = _ctx._rootTriviaCapture === false || _ctx._rootTriviaLog === undefined ? -1 : (_ctx._rootTriviaKindIndex?.[${JSON.stringify(arm.label)}] ?? -1)`,
6844
+ ` if (_rk${i} >= 0) _ctx._rootTriviaLog.push(0, 0, _e, _ce, _rk${i})`,
6590
6845
  ` 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})`,
6591
6846
  ` }`,
6592
6847
  ` _e = _ce`,
@@ -6598,11 +6853,13 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
6598
6853
  return [
6599
6854
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
6600
6855
  ` let _e = _pos`,
6856
+ ` const _rootMark = _cap && _ctx._rootTriviaLog !== undefined ? _ctx._rootTriviaLog.length : 0`,
6601
6857
  ` while (_e < input.length) {`,
6602
6858
  ` let _matched = false`,
6603
6859
  tryArms,
6604
6860
  ` if (!_matched) break`,
6605
6861
  ` }`,
6862
+ SELECTED_ROOT_FINISH,
6606
6863
  ` return _e`,
6607
6864
  `}`
6608
6865
  ].join("\n");
@@ -6833,6 +7090,9 @@ function endLoweringCapture() {
6833
7090
  _loweringSink = null;
6834
7091
  return misses;
6835
7092
  }
7093
+ function hasSelectedRootTrivia(ctx) {
7094
+ return ctx.rootTriviaClassified === true;
7095
+ }
6836
7096
  function v(ctx, prefix = "_v") {
6837
7097
  return `${prefix}${ctx.vars++}`;
6838
7098
  }
@@ -7142,10 +7402,12 @@ function planDisjointDispatch(parsers) {
7142
7402
  }
7143
7403
  return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
7144
7404
  }
7145
- function captureRestoreBody(mL, mR, mTl, mLg, mF = null) {
7405
+ function captureRestoreBody(ctx, mL, mR, mTl, mLg, mF = null, mRootLg = null) {
7146
7406
  const fieldRestore = mF ? `; if (_ctx._fields && _ctx._fields.length !== ${mF}) _ctx._fields.length = ${mF}` : "";
7147
7407
  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}`;
7148
- return mLg ? `${base}; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${mLg}) _ctx._triviaLog.length = ${mLg}` : base;
7408
+ const logRestore = mLg ? `; if (_ctx._triviaLog && _ctx._triviaLog.length !== ${mLg}) _ctx._triviaLog.length = ${mLg}` : "";
7409
+ const rootLogRestore = hasSelectedRootTrivia(ctx) && mRootLg ? `; if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${mRootLg}) _ctx._rootTriviaLog.length = ${mRootLg}` : "";
7410
+ return `${base}${logRestore}${rootLogRestore}`;
7149
7411
  }
7150
7412
  function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
7151
7413
  if (seen.has(p)) return false;
@@ -7355,7 +7617,7 @@ function emitFallible(inner, ctx, pos, swallow = false) {
7355
7617
  `${ind0} ${valV} = ${r.valueVar}; ${endV} = ${r.endVar}; ${okV} = true`,
7356
7618
  `${ind0}}`,
7357
7619
  ...mL ? [
7358
- `${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null, mF)} }`
7620
+ `${ind0}if (!${okV}) { ${captureRestoreBody(ctx, mL, mR, mTl, null, mF)} }`
7359
7621
  ] : []
7360
7622
  ];
7361
7623
  return { stmts, okVar: okV, valVar: valV, endVar: endV, mayCommit };
@@ -7552,11 +7814,13 @@ function emitSeqValues(def, ctx, pos) {
7552
7814
  const markV = v(ctx, "_mk");
7553
7815
  const markTl = v(ctx, "_mktl");
7554
7816
  const markLog = v(ctx, "_mklg");
7817
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
7555
7818
  const scanEndV = v(ctx, "_sne");
7556
7819
  stmts.push(
7557
7820
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
7558
7821
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
7559
7822
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7823
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
7560
7824
  `${ind(ctx)}const ${scanEndV} = ${capFn}(input, ${curV}, _ctx, 1)`,
7561
7825
  ...emitLineTrack(ctx, curV, scanEndV)
7562
7826
  );
@@ -7565,17 +7829,19 @@ function emitSeqValues(def, ctx, pos) {
7565
7829
  const endAfterV = v(ctx, "_sea");
7566
7830
  stmts.push(
7567
7831
  `${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
7568
- `${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}; }`
7832
+ `${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};` : ""} }`
7569
7833
  );
7570
7834
  valueVars.push(r2.valueVar);
7571
7835
  continue;
7572
7836
  } else {
7573
7837
  const trivFn = ensureTriviaFn(ctx);
7574
7838
  const markLog = v(ctx, "_mklg");
7839
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
7575
7840
  const scanEndV = v(ctx, "_sne");
7576
- const capArg = ctx.noHoist ? "0" : "_ctx._triviaLog !== undefined ? 2 : 0";
7841
+ const capArg = ctx.noHoist ? "0" : hasSelectedRootTrivia(ctx) ? "(_ctx._triviaLog !== undefined || _ctx._rootTriviaLog !== undefined) ? 2 : 0" : "_ctx._triviaLog !== undefined ? 2 : 0";
7577
7842
  stmts.push(
7578
7843
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
7844
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
7579
7845
  `${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx, ${capArg})`,
7580
7846
  ...emitLineTrack(ctx, curV, scanEndV)
7581
7847
  );
@@ -7584,7 +7850,7 @@ function emitSeqValues(def, ctx, pos) {
7584
7850
  const endAfterV = v(ctx, "_sea");
7585
7851
  stmts.push(
7586
7852
  `${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
7587
- `${ind(ctx)}if (${endAfterV} > ${scanEndV}) ${curV} = ${endAfterV}; else if (_ctx._triviaLog && _ctx._triviaLog.length !== ${markLog}) _ctx._triviaLog.length = ${markLog}`
7853
+ `${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};` : ""} }`
7588
7854
  );
7589
7855
  valueVars.push(r2.valueVar);
7590
7856
  continue;
@@ -7826,6 +8092,7 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7826
8092
  const selectorRawMark = selectorNeedsRollback ? v(ctx, "_dsr") : null;
7827
8093
  const selectorTriviaMark = selectorNeedsRollback ? v(ctx, "_dst") : null;
7828
8094
  const selectorLogMark = selectorNeedsRollback ? v(ctx, "_dsg") : null;
8095
+ const selectorRootLogMark = selectorNeedsRollback && hasSelectedRootTrivia(ctx) ? v(ctx, "_dsrg") : null;
7829
8096
  const selectorFieldMark = selectorNeedsRollback ? v(ctx, "_dsf") : null;
7830
8097
  const selectorErrorMark = selectorNeedsRollback ? v(ctx, "_dse") : null;
7831
8098
  const selector = emit(def.selector, ctx, pos);
@@ -7838,6 +8105,7 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7838
8105
  `${ind(ctx)}const ${selectorRawMark} = _ctx._cstRawChildren?.length ?? 0`,
7839
8106
  `${ind(ctx)}const ${selectorTriviaMark} = _ctx._cstTriviaLog?.length ?? 0`,
7840
8107
  `${ind(ctx)}const ${selectorLogMark} = _ctx._triviaLog?.length ?? 0`,
8108
+ ...selectorRootLogMark ? [`${ind(ctx)}const ${selectorRootLogMark} = _ctx._rootTriviaLog?.length ?? 0`] : [],
7841
8109
  `${ind(ctx)}const ${selectorFieldMark} = _ctx._fields?.length ?? 0`,
7842
8110
  `${ind(ctx)}const ${selectorErrorMark} = _ctx._errors?.length ?? 0`
7843
8111
  ] : [],
@@ -7857,16 +8125,18 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7857
8125
  const mayError = mayRecordRecoveryError(parser3, !!ctx.recovery);
7858
8126
  const errMark = mayError ? v(ctx, "_derr") : null;
7859
8127
  const logMark = ctx.activeTrivia ? v(ctx, "_dlog") : null;
8128
+ const rootLogMark = ctx.activeTrivia && hasSelectedRootTrivia(ctx) ? v(ctx, "_drlog") : null;
7860
8129
  if (usesRouted) {
7861
8130
  stmts.push(
7862
8131
  ...routedV ? [`${ind(ctx)}const ${routedV} = _ctx._routed; _ctx._routed = { value: ${selector.valueVar}, span: { start: ${pos}, end: ${selector.endVar} } }`] : [],
7863
8132
  ...selectorLeafMark ? [
7864
- `${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}`
8133
+ `${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}`
7865
8134
  ] : []
7866
8135
  );
7867
8136
  }
7868
8137
  if (errMark) stmts.push(`${ind(ctx)}const ${errMark} = _ctx._errors?.length ?? 0`);
7869
8138
  if (logMark) stmts.push(`${ind(ctx)}const ${logMark} = _ctx._triviaLog?.length ?? 0`);
8139
+ if (rootLogMark) stmts.push(`${ind(ctx)}const ${rootLogMark} = _ctx._rootTriviaLog?.length ?? 0`);
7870
8140
  const savedRoutedLocal = ctx.routedLocal;
7871
8141
  const savedNoHoist = ctx.noHoist;
7872
8142
  if (usesRouted && !needsRoutedContext) {
@@ -7881,11 +8151,12 @@ function emitDispatchCombinator(parser2, def, ctx, pos, valueMode = "pair") {
7881
8151
  stmts.push(...coverageAttempt(ctx, coverageId, pos));
7882
8152
  const errorRollback = errMark ? `if (_ctx._errors && _ctx._errors.length !== ${errMark}) _ctx._errors.length = ${errMark}; ` : "";
7883
8153
  const logRollback = logMark ? `if (_ctx._triviaLog && _ctx._triviaLog.length !== ${logMark}) _ctx._triviaLog.length = ${logMark}; ` : "";
8154
+ const rootLogRollback = rootLogMark ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${rootLogMark}) _ctx._rootTriviaLog.length = ${rootLogMark}; ` : "";
7884
8155
  const routedRollback = routedV ? `_ctx._routed = ${routedV}; ` : "";
7885
8156
  const coverageFailure = coverageId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageId)}, phase: 'failure', offset: _ctx._fe }); `;
7886
8157
  stmts.push(
7887
8158
  ...tail.stmts,
7888
- `${ind(ctx)}if (!${tail.okVar}) { ${routedRollback}${errorRollback}${logRollback}${coverageFailure}${committedFailBody(ctx)} }`,
8159
+ `${ind(ctx)}if (!${tail.okVar}) { ${routedRollback}${errorRollback}${logRollback}${rootLogRollback}${coverageFailure}${committedFailBody(ctx)} }`,
7889
8160
  ...routedV ? [`${ind(ctx)}_ctx._routed = ${routedV}`] : [],
7890
8161
  `${ind(ctx)}${outV} = ${valueMode === "tail" ? tail.valVar : `[${selector.valueVar}, ${tail.valVar}]`}`,
7891
8162
  `${ind(ctx)}${outE} = ${tail.endVar}`,
@@ -8066,11 +8337,13 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
8066
8337
  const markRaw = armNeedsRollback ? v(ctx, "_cmr") : null;
8067
8338
  const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
8068
8339
  const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
8340
+ const markRootLog = ctx.activeTrivia && hasSelectedRootTrivia(ctx) ? v(ctx, "_cmlrg") : null;
8069
8341
  const markFields = armNeedsFieldRollback ? v(ctx, "_cmf") : null;
8070
8342
  const markErrors = armMayRecordError ? v(ctx, "_cme") : null;
8071
- const captureRollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog, markFields) : "";
8343
+ const captureRollback = markLeaves ? captureRestoreBody(ctx, markLeaves, markRaw, markTl, markLog, markFields, markRootLog) : "";
8344
+ const rootRollback = markRootLog && !markLeaves ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${markRootLog}) _ctx._rootTriviaLog.length = ${markRootLog}` : "";
8072
8345
  const errorRollback = markErrors ? `if (_ctx._errors && _ctx._errors.length !== ${markErrors}) _ctx._errors.length = ${markErrors}` : "";
8073
- const rollback = [captureRollback, errorRollback].filter(Boolean).join("; ");
8346
+ const rollback = [captureRollback, rootRollback, errorRollback].filter(Boolean).join("; ");
8074
8347
  const failSlot = atStart ? staticFx : "_ctx._fx";
8075
8348
  stmts.push(`${ind0}if (${skipCond}) {`);
8076
8349
  if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
@@ -8085,6 +8358,7 @@ function emitFirstMatch(def, ctx, pos, coverageBase, preStmts, armGuardPrefix) {
8085
8358
  ...markFields ? [`${ind(ctx)}const ${markFields} = _ctx._fields?.length ?? 0`] : []
8086
8359
  );
8087
8360
  }
8361
+ if (markRootLog) stmts.push(`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog?.length ?? 0`);
8088
8362
  if (markErrors) stmts.push(`${ind(ctx)}const ${markErrors} = _ctx._errors?.length ?? 0`);
8089
8363
  const arm = emitFallible(p, ctx, pos, atStart);
8090
8364
  const { stmts: armStmts, okVar, valVar, endVar } = arm;
@@ -8153,7 +8427,7 @@ function routedNeedsContextBridge(p, ctx, seen = /* @__PURE__ */ new Set()) {
8153
8427
  const d = p._def;
8154
8428
  if (d.tag === "dispatch") return false;
8155
8429
  if (d.tag === "withCtx") return parserUsesRouted2(d.parser);
8156
- return childrenOf2(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8430
+ return childrenOf3(d).some((child) => routedNeedsContextBridge(child, ctx, seen));
8157
8431
  }
8158
8432
  function armReplayInScope(arm, ctx) {
8159
8433
  let p = arm;
@@ -8272,21 +8546,29 @@ function emitMany(def, ctx, pos) {
8272
8546
  const markV = v(ctx, "_mk");
8273
8547
  const markTl = v(ctx, "_mktl");
8274
8548
  const markLog = v(ctx, "_mklg");
8549
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8275
8550
  const npV = v(ctx, "_np");
8276
8551
  stmts.push(
8277
8552
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8278
8553
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8279
8554
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8555
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8280
8556
  `${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8281
8557
  ...emitLineTrack(ctx, curV, npV)
8282
8558
  );
8283
8559
  itemPos = npV;
8284
- 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}; `;
8560
+ 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};` : ""} `;
8285
8561
  } else {
8286
8562
  const trivFn = ensureTriviaFn(ctx);
8563
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8287
8564
  const npV = v(ctx, "_np");
8288
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, npV));
8565
+ stmts.push(
8566
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8567
+ `${ind(ctx)}const ${npV} = ${trivFn}(input, ${curV}, _ctx, ${hasSelectedRootTrivia(ctx) ? "_ctx._rootTriviaLog !== undefined ? 2 : 0" : "0"})`,
8568
+ ...emitLineTrack(ctx, curV, npV)
8569
+ );
8289
8570
  itemPos = npV;
8571
+ rollback = markRootLog ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${markRootLog}) _ctx._rootTriviaLog.length = ${markRootLog}; ` : "";
8290
8572
  }
8291
8573
  }
8292
8574
  if (!ctx.recovery && !ctx.coverage && !failsAtStart(def.parser) && needsFirstSetGuard(def.parser)) {
@@ -8346,9 +8628,10 @@ function emitAttempt(p, def, ctx, pos) {
8346
8628
  const raw = v(ctx, "_atr");
8347
8629
  const trivia2 = v(ctx, "_att");
8348
8630
  const log = v(ctx, "_atg");
8631
+ const rootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_atrg") : null;
8349
8632
  const fields = v(ctx, "_atf");
8350
8633
  const errors = v(ctx, "_ate");
8351
- 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}`;
8634
+ 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}`;
8352
8635
  const traceId = ctx.coverage?.plan.attempts.get(p);
8353
8636
  const traceRollback = traceId === void 0 ? "" : ` _ctx._grammarTrace?.write({ id: ${JSON.stringify(traceId)}, phase: 'rollback', offset: ${pos} });`;
8354
8637
  const preGuard = [];
@@ -8364,7 +8647,7 @@ function emitAttempt(p, def, ctx, pos) {
8364
8647
  return {
8365
8648
  stmts: [
8366
8649
  ...preGuard,
8367
- `${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`,
8650
+ `${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`,
8368
8651
  ...inner.stmts,
8369
8652
  ...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback}${inner.mayCommit ? ` if (_ctx._fc) ${committedFailBody(ctx)};` : ""} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
8370
8653
  ],
@@ -8406,16 +8689,17 @@ function emitSepBy(_p, def, ctx, pos) {
8406
8689
  const itemFailBreak = (sepEndVar, sepRb, postSepRb) => def.trailing === void 0 ? `${sepRb}break` : `${postSepRb}${curV} = ${sepEndVar}; break`;
8407
8690
  const postSepMarks = () => {
8408
8691
  if (def.trailing === void 0 || !ctx.capturing) return { decl: [], rb: "" };
8409
- const lv = v(ctx, "_tlv"), rw = v(ctx, "_trw"), tl = v(ctx, "_ttl"), lg = v(ctx, "_tlg"), fl = v(ctx, "_tlf");
8692
+ 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");
8410
8693
  return {
8411
8694
  decl: [
8412
8695
  `${ind(ctx)}const ${lv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8413
8696
  `${ind(ctx)}const ${rw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8414
8697
  `${ind(ctx)}const ${tl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8415
8698
  `${ind(ctx)}const ${lg} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8699
+ ...rlg ? [`${ind(ctx)}const ${rlg} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8416
8700
  `${ind(ctx)}const ${fl} = _ctx._fields ? _ctx._fields.length : 0`
8417
8701
  ],
8418
- 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}; `
8702
+ 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}; `
8419
8703
  };
8420
8704
  };
8421
8705
  if (rec) {
@@ -8453,6 +8737,7 @@ function emitSepBy(_p, def, ctx, pos) {
8453
8737
  const markV = v(ctx, "_mk");
8454
8738
  const markTl = v(ctx, "_mktl");
8455
8739
  const markLog = v(ctx, "_mklg");
8740
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8456
8741
  const markLv = v(ctx, "_mklv");
8457
8742
  const markFld = v(ctx, "_mkf");
8458
8743
  const spV = v(ctx, "_sp");
@@ -8460,13 +8745,14 @@ function emitSepBy(_p, def, ctx, pos) {
8460
8745
  `${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
8461
8746
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog ? _ctx._cstTriviaLog.length : 0`,
8462
8747
  `${ind(ctx)}const ${markLog} = _ctx._triviaLog ? _ctx._triviaLog.length : 0`,
8748
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8463
8749
  `${ind(ctx)}const ${markLv} = _ctx._cstLeaves ? _ctx._cstLeaves.length : 0`,
8464
8750
  `${ind(ctx)}const ${markFld} = _ctx._fields ? _ctx._fields.length : 0`,
8465
8751
  `${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx, 1)`,
8466
8752
  ...emitLineTrack(ctx, curV, spV)
8467
8753
  );
8468
8754
  sepAtPos = spV;
8469
- 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}; `;
8755
+ 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}; `;
8470
8756
  const sep2 = emitFallible(def.separator, ctx, sepAtPos, true);
8471
8757
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep2;
8472
8758
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
@@ -8484,19 +8770,30 @@ function emitSepBy(_p, def, ctx, pos) {
8484
8770
  );
8485
8771
  } else {
8486
8772
  const trivFn = ensureTriviaFn(ctx);
8773
+ const markRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_mkrlg") : null;
8487
8774
  const spV = v(ctx, "_sp");
8488
- stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`, ...emitLineTrack(ctx, curV, spV));
8775
+ stmts.push(
8776
+ ...markRootLog ? [`${ind(ctx)}const ${markRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8777
+ `${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx, ${hasSelectedRootTrivia(ctx) ? "_ctx._rootTriviaLog !== undefined ? 2 : 0" : "0"})`,
8778
+ ...emitLineTrack(ctx, curV, spV)
8779
+ );
8489
8780
  sepAtPos = spV;
8490
8781
  const sep2 = emitFallible(def.separator, ctx, sepAtPos, true);
8491
8782
  const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = sep2;
8492
- stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
8783
+ const rollbackToSep = markRootLog ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${markRootLog}) _ctx._rootTriviaLog.length = ${markRootLog}; ` : "";
8784
+ stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}${sep2.mayCommit ? `if (_ctx._fc) ${committedFailBody(ctx)}; ` : ""}break }`);
8785
+ const postRootLog = hasSelectedRootTrivia(ctx) ? v(ctx, "_psrlg") : null;
8493
8786
  const npV = v(ctx, "_np");
8494
- stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`, ...emitLineTrack(ctx, sepEnd, npV));
8787
+ stmts.push(
8788
+ ...postRootLog ? [`${ind(ctx)}const ${postRootLog} = _ctx._rootTriviaLog ? _ctx._rootTriviaLog.length : 0`] : [],
8789
+ `${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx, ${hasSelectedRootTrivia(ctx) ? "_ctx._rootTriviaLog !== undefined ? 2 : 0" : "0"})`,
8790
+ ...emitLineTrack(ctx, sepEnd, npV)
8791
+ );
8495
8792
  const next = emitFallible(def.parser, ctx, npV, true);
8496
8793
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = next;
8497
8794
  stmts.push(
8498
8795
  ...nextStmts,
8499
- ...failItem(nextOk, next.mayCommit, npV, itemFailBreak(sepEnd, "", "")),
8796
+ ...failItem(nextOk, next.mayCommit, npV, itemFailBreak(sepEnd, rollbackToSep, postRootLog ? `if (_ctx._rootTriviaLog && _ctx._rootTriviaLog.length !== ${postRootLog}) _ctx._rootTriviaLog.length = ${postRootLog}; ` : "")),
8500
8797
  `${ind(ctx)}${arrV}.push(${nextVal})`,
8501
8798
  `${ind(ctx)}${curV} = ${nextEnd}`
8502
8799
  );
@@ -8591,6 +8888,7 @@ function emitNot(def, ctx, pos) {
8591
8888
  const raw = sinksLive ? v(ctx, "_ntr") : null;
8592
8889
  const tl = sinksLive ? v(ctx, "_ntt") : null;
8593
8890
  const log = sinksLive ? v(ctx, "_ntg") : null;
8891
+ const rootLog = sinksLive && hasSelectedRootTrivia(ctx) ? v(ctx, "_ntrg") : null;
8594
8892
  const fields = sinksLive ? v(ctx, "_ntf") : null;
8595
8893
  const errors = sinksLive ? v(ctx, "_nte") : null;
8596
8894
  const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
@@ -8598,11 +8896,11 @@ function emitNot(def, ctx, pos) {
8598
8896
  return {
8599
8897
  stmts: [
8600
8898
  ...sinksLive ? [
8601
- `${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`
8899
+ `${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`
8602
8900
  ] : [],
8603
8901
  ...stmts,
8604
8902
  ...sinksLive ? [
8605
- `${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}`
8903
+ `${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}`
8606
8904
  ] : [],
8607
8905
  ...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
8608
8906
  ],
@@ -8738,16 +9036,17 @@ function emitToken(def, ctx, pos) {
8738
9036
  const sk = v(ctx, "_tokKinds");
8739
9037
  const stl = v(ctx, "_tokTl");
8740
9038
  const sol = v(ctx, "_tokLog");
9039
+ const srl = hasSelectedRootTrivia(ctx) ? v(ctx, "_tokRootLog") : null;
8741
9040
  const sb = v(ctx, "_tokBuf");
8742
9041
  const valV = v(ctx, "_tok");
8743
9042
  return {
8744
9043
  stmts: [
8745
- `${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`,
8746
- `${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`,
9044
+ `${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`,
9045
+ `${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`,
8747
9046
  `${i}try {`,
8748
9047
  ...reindentStmts(inner.stmts, ctx.indent + 1).map((stmt) => stmt.replace(/^(\s*)(?:const|let)\s+/, "$1var ")),
8749
9048
  `${i}} finally {`,
8750
- `${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}`,
9049
+ `${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}`,
8751
9050
  `${i}}`,
8752
9051
  ...emitIfFail(ctx, `!${inner.okVar}`, propagateFailBody(ctx)),
8753
9052
  `${i}const ${valV} = input.slice(${pos}, ${inner.endVar})`,
@@ -8916,7 +9215,8 @@ ${i}const ${chV} = ${chAlloc}, ${rawV} = ${profileRecognizer} ? undefined : [],
8916
9215
  const nodeSpanV = ctx.lineTracking ? v(ctx, "_nspan") : nodeSpanExpr;
8917
9216
  if (ctx.lineTracking) stmts.push(`${i}const ${nodeSpanV} = ${nodeSpanExpr}`);
8918
9217
  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})`;
8919
- const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV})`;
9218
+ const hostBuildArgs = `${JSON.stringify(def.type)}, ${chV}, ${fObj}, ${nodeSpanV}, ${rawV}, ${tlV}, ${stV}${def.tags !== void 0 && def.tags.length > 0 ? `, ${JSON.stringify(def.tags)}` : ""}`;
9219
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${hostBuildArgs})`;
8920
9220
  const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : cstOut ? hostBuildExpr : buildExpr;
8921
9221
  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;
8922
9222
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
@@ -9225,6 +9525,8 @@ function emitDispatch(p, ctx, pos) {
9225
9525
  case "grammar": {
9226
9526
  const savedTrivia = ctx.activeTrivia;
9227
9527
  const savedKindLabels = ctx.triviaKindLabels;
9528
+ const opaqueRootCapture = hasSelectedRootTrivia(ctx) && def.rootCapture === "opaque";
9529
+ 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;
9228
9530
  if (def.clearTrivia) {
9229
9531
  ctx.activeTrivia = void 0;
9230
9532
  ctx.triviaKindLabels = void 0;
@@ -9235,13 +9537,38 @@ function emitDispatch(p, ctx, pos) {
9235
9537
  }
9236
9538
  }
9237
9539
  const savedCapture = def.captureTrivia ? v(ctx, "_gcap") : null;
9540
+ const savedRootCapture = opaqueRootCapture ? v(ctx, "_grtc") : null;
9541
+ const opaqueValue = opaqueRootCapture ? v(ctx, "_grtv") : null;
9542
+ const opaqueEnd = opaqueRootCapture ? v(ctx, "_grte") : null;
9543
+ if (opaqueRootCapture) ctx.indent++;
9238
9544
  const r = emit(def.parser, ctx, pos);
9545
+ if (opaqueRootCapture) ctx.indent--;
9239
9546
  ctx.activeTrivia = savedTrivia;
9240
9547
  ctx.triviaKindLabels = savedKindLabels;
9241
- if (!savedCapture) return r;
9548
+ if (!savedCapture && !opaqueRootCapture) return strictScopeCheck === void 0 ? r : {
9549
+ ...r,
9550
+ stmts: [strictScopeCheck, ...r.stmts]
9551
+ };
9552
+ if (opaqueRootCapture) {
9553
+ return {
9554
+ stmts: [
9555
+ ...strictScopeCheck === void 0 ? [] : [strictScopeCheck],
9556
+ `${ind(ctx)}let ${opaqueValue}, ${opaqueEnd}`,
9557
+ `${ind(ctx)}${savedCapture ? `const ${savedCapture} = _ctx.captureTrivia; ` : ""}const ${savedRootCapture} = _ctx._rootTriviaCapture; _ctx._rootTriviaCapture = false`,
9558
+ `${ind(ctx)}try {`,
9559
+ ...savedCapture ? [`${ind(ctx)} _ctx.captureTrivia = true`] : [],
9560
+ ...r.stmts,
9561
+ `${ind(ctx)} ${opaqueValue} = ${r.valueVar}; ${opaqueEnd} = ${r.endVar}`,
9562
+ `${ind(ctx)}} finally { ${savedCapture ? `_ctx.captureTrivia = ${savedCapture}; ` : ""}_ctx._rootTriviaCapture = ${savedRootCapture} }`
9563
+ ],
9564
+ valueVar: opaqueValue,
9565
+ endVar: opaqueEnd
9566
+ };
9567
+ }
9242
9568
  return {
9243
9569
  ...r,
9244
9570
  stmts: [
9571
+ ...strictScopeCheck === void 0 ? [] : [strictScopeCheck],
9245
9572
  `${ind(ctx)}const ${savedCapture} = _ctx.captureTrivia; _ctx.captureTrivia = true`,
9246
9573
  ...r.stmts,
9247
9574
  `${ind(ctx)}_ctx.captureTrivia = ${savedCapture}`
@@ -9379,7 +9706,7 @@ function hasLineTrackingDef(p, seen = /* @__PURE__ */ new Set()) {
9379
9706
  return false;
9380
9707
  }
9381
9708
  }
9382
- return childrenOf2(d).some((child) => hasLineTrackingDef(child, seen));
9709
+ return childrenOf3(d).some((child) => hasLineTrackingDef(child, seen));
9383
9710
  }
9384
9711
  function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9385
9712
  if (seen.has(p)) return false;
@@ -9398,7 +9725,7 @@ function parserUsesRouted2(p, seen = /* @__PURE__ */ new Set()) {
9398
9725
  case "dispatch":
9399
9726
  return false;
9400
9727
  default:
9401
- return childrenOf2(d).some((child) => parserUsesRouted2(child, seen));
9728
+ return childrenOf3(d).some((child) => parserUsesRouted2(child, seen));
9402
9729
  }
9403
9730
  }
9404
9731
  function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
@@ -9413,9 +9740,9 @@ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
9413
9740
  return false;
9414
9741
  }
9415
9742
  }
9416
- return childrenOf2(d).some((child) => hasDirectBuildDef(child, seen));
9743
+ return childrenOf3(d).some((child) => hasDirectBuildDef(child, seen));
9417
9744
  }
9418
- function childrenOf2(def) {
9745
+ function childrenOf3(def) {
9419
9746
  switch (def.tag) {
9420
9747
  case "sequence":
9421
9748
  case "choice":
@@ -9490,7 +9817,7 @@ function subtreeSizes(roots) {
9490
9817
  const def = p._def;
9491
9818
  let s = 1;
9492
9819
  if (def.tag !== "lazy") {
9493
- for (const child of childrenOf2(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
9820
+ for (const child of childrenOf3(def)) s += child._def.tag === "lazy" ? 1 : sz(child);
9494
9821
  }
9495
9822
  sizes.set(p, s);
9496
9823
  return s;
@@ -9511,7 +9838,7 @@ function subtreeSizes(roots) {
9511
9838
  return;
9512
9839
  }
9513
9840
  sz(p);
9514
- for (const child of childrenOf2(def)) visit(child);
9841
+ for (const child of childrenOf3(def)) visit(child);
9515
9842
  };
9516
9843
  for (const root of roots) visit(root);
9517
9844
  return sizes;
@@ -9521,7 +9848,7 @@ function analyzeLazyUsageMulti(roots) {
9521
9848
  const recursive = /* @__PURE__ */ new Set();
9522
9849
  const descended = /* @__PURE__ */ new Set();
9523
9850
  const active = /* @__PURE__ */ new Set();
9524
- function walk2(p) {
9851
+ function walk(p) {
9525
9852
  const def = p._def;
9526
9853
  if (def.tag === "lazy") {
9527
9854
  counts.set(p, (counts.get(p) ?? 0) + 1);
@@ -9538,17 +9865,17 @@ function analyzeLazyUsageMulti(roots) {
9538
9865
  return;
9539
9866
  }
9540
9867
  active.add(p);
9541
- walk2(resolved);
9868
+ walk(resolved);
9542
9869
  active.delete(p);
9543
9870
  return;
9544
9871
  }
9545
9872
  if (isHoistableTag(def.tag)) {
9546
9873
  counts.set(p, (counts.get(p) ?? 0) + 1);
9547
9874
  }
9548
- for (const child of childrenOf2(def)) walk2(child);
9875
+ for (const child of childrenOf3(def)) walk(child);
9549
9876
  }
9550
9877
  const rootsArr = [...roots];
9551
- for (const root of rootsArr) walk2(root);
9878
+ for (const root of rootsArr) walk(root);
9552
9879
  return { counts, recursive, sizes: subtreeSizes(rootsArr) };
9553
9880
  }
9554
9881
  function ruleDependencies(ruleMap) {
@@ -9558,7 +9885,7 @@ function ruleDependencies(ruleMap) {
9558
9885
  for (const [name, comb] of ruleMap) {
9559
9886
  const found = /* @__PURE__ */ new Set();
9560
9887
  const seen = /* @__PURE__ */ new Set();
9561
- const walk2 = (p, isRoot) => {
9888
+ const walk = (p, isRoot) => {
9562
9889
  const def = p._def;
9563
9890
  const boundary = def.tag === "lazy" ? nameOf.get(p) ?? p._ruleName : nameOf.get(p);
9564
9891
  if (!isRoot && boundary !== void 0) {
@@ -9574,12 +9901,12 @@ function ruleDependencies(ruleMap) {
9574
9901
  } catch {
9575
9902
  return;
9576
9903
  }
9577
- walk2(resolved, false);
9904
+ walk(resolved, false);
9578
9905
  return;
9579
9906
  }
9580
- for (const child of childrenOf2(def)) walk2(child, false);
9907
+ for (const child of childrenOf3(def)) walk(child, false);
9581
9908
  };
9582
- walk2(comb, true);
9909
+ walk(comb, true);
9583
9910
  deps.set(name, [...found]);
9584
9911
  }
9585
9912
  return deps;
@@ -9726,6 +10053,7 @@ function compile(combinator, mapFnSources, opts) {
9726
10053
  runDuplicationDiagnostic(combinator, opts?.duplication);
9727
10054
  markUnusedValues(combinator);
9728
10055
  const grammarTrivia = combinator._meta.grammarTrivia;
10056
+ const rootTriviaClassified = grammarTrivia?._meta.rootTriviaClassified === true || combinator._meta.rootTriviaClassified === true;
9729
10057
  const grammarScanSkip = combinator._meta.grammarScanSkip;
9730
10058
  const grammarHostMode = combinator._meta.grammarHostMode;
9731
10059
  const grammarTrackLines = combinator._meta.grammarTrackLines;
@@ -9752,7 +10080,11 @@ function compile(combinator, mapFnSources, opts) {
9752
10080
  lineTracking: opts?.trackLines === true || grammarTrackLines === true || hasLineTrackingDef(combinator),
9753
10081
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
9754
10082
  lazyUsage: analyzeLazyUsage(combinator),
9755
- ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
10083
+ ...rootTriviaClassified ? { rootTriviaClassified: true } : {},
10084
+ ...grammarTrivia ? {
10085
+ activeTrivia: grammarTrivia,
10086
+ triviaKindLabels: grammarTrivia._meta.triviaKindLabels
10087
+ } : {},
9756
10088
  ...grammarScanSkip ? { activeScanSkip: grammarScanSkip } : {}
9757
10089
  };
9758
10090
  const r = emit(combinator, ctx, "_pos");
@@ -9900,10 +10232,11 @@ function assertRuleName(name) {
9900
10232
  );
9901
10233
  }
9902
10234
  }
9903
- function publicRuleWrapperSource(rule, fnSource, ambientTriviaKindLabels) {
9904
- const labels = rule._meta.triviaKindLabels ?? ambientTriviaKindLabels;
9905
- if (labels === void 0) return fnSource;
9906
- return `Object.assign(${fnSource}, { _meta: { triviaKindLabels: ${JSON.stringify(labels)} } })`;
10235
+ function publicRuleWrapperSource(rule, fnSource, ambientTriviaKindLabels, ambientRootTriviaClassified) {
10236
+ const labels = ambientTriviaKindLabels ?? rule._meta.triviaKindLabels;
10237
+ const classified = ambientRootTriviaClassified ?? rule._meta.rootTriviaClassified;
10238
+ if (labels === void 0 && classified === void 0) return fnSource;
10239
+ return `Object.assign(${fnSource}, { _meta: {${labels === void 0 ? "" : ` triviaKindLabels: ${JSON.stringify(labels)},`}${classified === void 0 ? "" : " rootTriviaClassified: true,"} } })`;
9907
10240
  }
9908
10241
  function compileRuleMap(ruleMap, opts) {
9909
10242
  runGatingDiagnosticRules(ruleMap, opts?.gating);
@@ -9911,6 +10244,7 @@ function compileRuleMap(ruleMap, opts) {
9911
10244
  for (const [, rule] of ruleMap) markUnusedValues(rule);
9912
10245
  const coverageWinners = opts?.coverage ? Object.fromEntries(ruleMap.filter(([, rule]) => rule._def.tag !== "lazy")) : void 0;
9913
10246
  const grammarTrivia = opts?.trivia;
10247
+ const rootTriviaClassified = grammarTrivia?._meta.rootTriviaClassified === true || ruleMap.some(([, rule]) => rule._meta.rootTriviaClassified === true);
9914
10248
  const grammarScanSkip = opts?.scanSkip ?? ruleMap.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
9915
10249
  const grammarHostMode = opts?.hostMode ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
9916
10250
  const grammarTrackLines = opts?.trackLines === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
@@ -9937,7 +10271,11 @@ function compileRuleMap(ruleMap, opts) {
9937
10271
  lineTracking: grammarTrackLines,
9938
10272
  ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(ruleMap.map(([, rule]) => rule), coverageWinners) } } : {},
9939
10273
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
9940
- ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
10274
+ ...rootTriviaClassified ? { rootTriviaClassified: true } : {},
10275
+ ...grammarTrivia ? {
10276
+ activeTrivia: grammarTrivia,
10277
+ triviaKindLabels: grammarTrivia._meta.triviaKindLabels
10278
+ } : {},
9941
10279
  ...grammarScanSkip ? { activeScanSkip: grammarScanSkip } : {}
9942
10280
  };
9943
10281
  const seenNames = /* @__PURE__ */ new Map();
@@ -10025,7 +10363,7 @@ function compileRuleMap(ruleMap, opts) {
10025
10363
  return [`function(input, _pos, _ctx) {`, ...body, `}`].join("\n");
10026
10364
  };
10027
10365
  const objBody = perEntry.map(({ key, rule, r }) => {
10028
- const src = publicRuleWrapperSource(rule, entryFnText(r, rule), ctx.triviaKindLabels);
10366
+ const src = publicRuleWrapperSource(rule, entryFnText(r, rule), ctx.triviaKindLabels, ctx.rootTriviaClassified);
10029
10367
  return ` ${JSON.stringify(key)}: ${src.split("\n").join("\n ")}`;
10030
10368
  }).join(",\n");
10031
10369
  const replacement = [
@@ -10044,6 +10382,7 @@ function compileRuleMap(ruleMap, opts) {
10044
10382
  // host check passes vacuously — see `withHostMode` in the plugin.
10045
10383
  hostMode: ctx.hostMode ?? "ast",
10046
10384
  hostBranchElided: !!ctx.hostBranchElided,
10385
+ reflection: collectGrammarReflection(ruleMap),
10047
10386
  ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions }
10048
10387
  };
10049
10388
  }
@@ -10053,6 +10392,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10053
10392
  if (opts?.duplication !== void 0) runDuplicationDiagnosticRules(ruleMapArg, opts.duplication);
10054
10393
  for (const [, rule] of ruleMapArg) markUnusedValues(rule);
10055
10394
  const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
10395
+ const rootTriviaClassified = grammarTrivia?._meta.rootTriviaClassified === true || ruleMapArg.some(([, rule]) => rule._meta.rootTriviaClassified === true);
10056
10396
  const grammarScanSkip = opts?.scanSkip ?? ruleMapArg.map(([, r]) => r._meta.grammarScanSkip).find(Boolean);
10057
10397
  const grammarHostMode = opts?.hostMode ?? ruleMapArg.map(([, r]) => r._meta.grammarHostMode).find(Boolean);
10058
10398
  const grammarTrackLines = opts?.trackLines === true || ruleMapArg.some(([, r]) => r._meta.grammarTrackLines === true);
@@ -10066,6 +10406,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10066
10406
  return false;
10067
10407
  }
10068
10408
  });
10409
+ const nodeMeta = new Map(ruleMap.map(([name, rule]) => [name, collectGrammarReflection([[name, rule]], { followLazy: false })]));
10069
10410
  const ctx = {
10070
10411
  vars: 0,
10071
10412
  indent: 1,
@@ -10095,7 +10436,11 @@ function compileLinkable(ruleMapArg, ns, opts) {
10095
10436
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
10096
10437
  ns,
10097
10438
  deferFirstSetRefs: true,
10098
- ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {},
10439
+ ...rootTriviaClassified ? { rootTriviaClassified: true } : {},
10440
+ ...grammarTrivia ? {
10441
+ activeTrivia: grammarTrivia,
10442
+ triviaKindLabels: grammarTrivia._meta.triviaKindLabels
10443
+ } : {},
10099
10444
  ...grammarScanSkip ? { activeScanSkip: grammarScanSkip } : {}
10100
10445
  };
10101
10446
  const seen = /* @__PURE__ */ new Map();
@@ -10141,7 +10486,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10141
10486
  scanExternal(resolved);
10142
10487
  return;
10143
10488
  }
10144
- for (const child of childrenOf2(def)) scanExternal(child);
10489
+ for (const child of childrenOf3(def)) scanExternal(child);
10145
10490
  };
10146
10491
  for (const [, rule] of ruleMap) scanExternal(rule);
10147
10492
  }
@@ -10227,7 +10572,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10227
10572
  ...r.stmts,
10228
10573
  ` return { ok: true, value: ${r.valueVar}, span: ${resultSpan} }`,
10229
10574
  `}`
10230
- ].join("\n"), ctx.triviaKindLabels));
10575
+ ].join("\n"), ctx.triviaKindLabels, ctx.rootTriviaClassified));
10231
10576
  }
10232
10577
  const firstSets = /* @__PURE__ */ new Map();
10233
10578
  const firstSetRecipes = /* @__PURE__ */ new Map();
@@ -10263,6 +10608,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
10263
10608
  hostBranchElided: !!ctx.hostBranchElided,
10264
10609
  hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
10265
10610
  isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs),
10611
+ nodeMeta,
10266
10612
  mfFns: mfSrcs ? [] : ctx.mapFns,
10267
10613
  buildFns: buildSrcs ? [] : ctx.buildFns
10268
10614
  };
@@ -10283,7 +10629,7 @@ function hasExternalRuleRef(ruleMap) {
10283
10629
  if (resolved === void 0) return p._ruleName !== void 0;
10284
10630
  return visit(resolved);
10285
10631
  }
10286
- return childrenOf2(def).some(visit);
10632
+ return childrenOf3(def).some(visit);
10287
10633
  };
10288
10634
  return ruleMap.some(([, rule]) => visit(rule));
10289
10635
  }
@@ -10305,7 +10651,7 @@ function hasSemanticReduction(roots, externalRefs) {
10305
10651
  return true;
10306
10652
  }
10307
10653
  }
10308
- return childrenOf2(def).some(visit);
10654
+ return childrenOf3(def).some(visit);
10309
10655
  };
10310
10656
  return roots.some(visit);
10311
10657
  }
@@ -10378,7 +10724,7 @@ function withCtx(extra, combinator) {
10378
10724
  }
10379
10725
 
10380
10726
  // src/compiler/ir-serialize.ts
10381
- function childrenOf3(def) {
10727
+ function childrenOf4(def) {
10382
10728
  switch (def.tag) {
10383
10729
  case "sequence":
10384
10730
  case "choice":
@@ -10442,6 +10788,10 @@ function projectOpt(project) {
10442
10788
  if (project === void 0) return void 0;
10443
10789
  return `project: ${project}`;
10444
10790
  }
10791
+ function tagsOpt(tags) {
10792
+ if (tags === void 0 || tags.length === 0) return void 0;
10793
+ return `tags: ${JSON.stringify(tags)}`;
10794
+ }
10445
10795
  function matcherExpr(entry) {
10446
10796
  switch (entry.kind) {
10447
10797
  case "startsWith":
@@ -10571,6 +10921,7 @@ function evalRuleMapIR(ir) {
10571
10921
  "transform",
10572
10922
  "skip",
10573
10923
  "trivia",
10924
+ "classifiedTrivia",
10574
10925
  "label",
10575
10926
  "field",
10576
10927
  "expect",
@@ -10611,6 +10962,7 @@ function evalRuleMapIR(ir) {
10611
10962
  transform,
10612
10963
  skip,
10613
10964
  trivia,
10965
+ classifiedTrivia,
10614
10966
  label,
10615
10967
  field,
10616
10968
  expect,
@@ -10655,7 +11007,7 @@ var Serializer = class {
10655
11007
  if (target) this.assertNoRuleRef(target, seen);
10656
11008
  return;
10657
11009
  }
10658
- for (const child of childrenOf3(c._def)) this.assertNoRuleRef(child, seen);
11010
+ for (const child of childrenOf4(c._def)) this.assertNoRuleRef(child, seen);
10659
11011
  }
10660
11012
  /** A `rules()` map wraps each rule value in a named `lazy` proxy; the real body is
10661
11013
  * its target (compileLinkable unwraps the same way). A ref INSIDE a body stays a
@@ -10719,7 +11071,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10719
11071
  this.counts.set(c, (this.counts.get(c) ?? 0) + 1);
10720
11072
  if ((this.counts.get(c) ?? 0) > 1) return;
10721
11073
  active.add(c);
10722
- for (const child of childrenOf3(def)) this.analyze(child, active);
11074
+ for (const child of childrenOf4(def)) this.analyze(child, active);
10723
11075
  active.delete(c);
10724
11076
  }
10725
11077
  /** Reference `c`: a const var if it has one (emitting its decl first), else inline.
@@ -10836,12 +11188,14 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10836
11188
  case "node": {
10837
11189
  if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
10838
11190
  const projectEntry = projectOpt(def.project);
11191
+ const tagEntry = tagsOpt(def.tags);
10839
11192
  const optEntries = [
10840
11193
  ...def.unwrap ? ["unwrap: true"] : [],
10841
11194
  ...def.collapse ? ["collapse: true"] : [],
10842
11195
  ...projectEntry === void 0 ? [] : [projectEntry],
10843
11196
  ...def.captureTrivia ? ["captureTrivia: true"] : [],
10844
- ...def.trailingTrivia ? ["trailingTrivia: true"] : []
11197
+ ...def.trailingTrivia ? ["trailingTrivia: true"] : [],
11198
+ ...tagEntry === void 0 ? [] : [tagEntry]
10845
11199
  ];
10846
11200
  const opts = optEntries.length > 0 ? `, { ${optEntries.join(", ")} }` : "";
10847
11201
  if (def.type === void 0) {
@@ -10855,8 +11209,11 @@ ${outerDecls.map((d) => " " + d).join("\n")}
10855
11209
  return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
10856
11210
  }
10857
11211
  case "grammar": {
10858
- const trivia2 = def.clearTrivia ? "null" : def.triviaParser ? kid(def.triviaParser) : "undefined";
10859
- return `parser({ trivia: ${trivia2}${def.captureTrivia ? ", captureTrivia: true" : ""}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
11212
+ const classified = def.triviaParser?._meta.rootTriviaClassified === true;
11213
+ const spec = classified && def.triviaParser ? analyzeLabeledTrivia(def.triviaParser) : null;
11214
+ if (classified && !spec) throw new Unserializable("classified trivia without labeled arms");
11215
+ 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";
11216
+ return `parser({ trivia: ${trivia2}${def.captureTrivia ? ", captureTrivia: true" : ""}${def.rootCapture ? `, rootCapture: ${JSON.stringify(def.rootCapture)}` : ""}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
10860
11217
  }
10861
11218
  case "withCtx": {
10862
11219
  if (def.extraSrc === void 0) throw new Unserializable("withCtx without extraSrc");
@@ -10879,15 +11236,16 @@ function normalizeCstCollapse(collapse) {
10879
11236
  }
10880
11237
  return typeof collapse === "function" ? collapse : void 0;
10881
11238
  }
10882
- function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
10883
- return { _tag: "node", type, span: { ...span }, state: state ?? null, children: [...children2] };
11239
+ function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state, tags) {
11240
+ 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] };
10884
11241
  }
10885
- function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
11242
+ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state, _tags) {
10886
11243
  if (typeof typeOrOptions === "string") {
10887
11244
  return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
10888
11245
  }
10889
11246
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
10890
- const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2);
11247
+ const materializeTags = typeOrOptions?.tags === true;
11248
+ const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2, tags) => buildCstNode(type, children3 ?? [], fields, span2, rawChildren2, triviaLog2, state2, materializeTags ? tags : void 0);
10891
11249
  host._parsemanCstOutput = true;
10892
11250
  if (collapse) host._parsemanCstCollapse = collapse;
10893
11251
  return host;
@@ -10908,7 +11266,7 @@ function pickPieces(pieces, names) {
10908
11266
  };
10909
11267
  for (const n of names) visit(n);
10910
11268
  const filt = (m, pc) => new Map([...m].filter(([k]) => keep.has(k) && winner.get(k) === pc));
10911
- 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);
11269
+ 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);
10912
11270
  }
10913
11271
  function fusedBody(pieces) {
10914
11272
  for (const p of pieces) {
@@ -10946,6 +11304,7 @@ function fusedBody(pieces) {
10946
11304
  ...[...winner].map(([k, p]) => p.ruleFns.get(k))
10947
11305
  ];
10948
11306
  const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
11307
+ const reflection = mergeGrammarReflections([...winner].map(([k, p]) => p.nodeMeta.get(k) ?? { nodes: [] }));
10949
11308
  const { mode, elided } = hostModeOfPieces(pieces);
10950
11309
  const rawBody = [
10951
11310
  ...lines,
@@ -10954,6 +11313,7 @@ function fusedBody(pieces) {
10954
11313
  "}",
10955
11314
  `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_MODE.description)}), { value: ${JSON.stringify(mode)}, enumerable: false })`,
10956
11315
  `Object.defineProperty(_map, Symbol.for(${JSON.stringify(FUSED_HOST_ELIDED.description)}), { value: ${JSON.stringify(elided)}, enumerable: false })`,
11316
+ `Object.defineProperty(_map, Symbol.for('parseman.grammarReflection'), { value: ${grammarReflectionSource(reflection)}, enumerable: false })`,
10957
11317
  // …and on each rule FUNCTION, because `run(map.Rule, …)` is handed the rule, not the
10958
11318
  // map, and would otherwise have nothing to check against. Done once at fuse time,
10959
11319
  // before any rule has been called, so it costs nothing per parse.
@@ -11085,10 +11445,6 @@ function gate(predicate) {
11085
11445
  };
11086
11446
  }
11087
11447
 
11088
- // src/cst/trivia-entries.ts
11089
- var EMPTY_INDICES = Object.freeze([]);
11090
- var EMPTY_GAPS = Object.freeze([]);
11091
-
11092
11448
  // src/cst/offset-model.ts
11093
11449
  var { isFinite } = Number;
11094
11450
 
@@ -11244,19 +11600,19 @@ function directBuilderUnsupportedBindings(src) {
11244
11600
  // src/plugin/evaluator.ts
11245
11601
  function stripTsFromSource(node2, code) {
11246
11602
  const cuts = [];
11247
- const walk2 = (n) => {
11603
+ const walk = (n) => {
11248
11604
  if (!n || typeof n !== "object") return;
11249
11605
  const rec = n;
11250
11606
  if (typeof rec.type === "string" && rec.type.startsWith("TS") && typeof rec.start === "number" && typeof rec.end === "number") {
11251
11607
  const ex = rec.expression;
11252
11608
  if ((rec.type === "TSAsExpression" || rec.type === "TSSatisfiesExpression" || rec.type === "TSNonNullExpression" || rec.type === "TSInstantiationExpression") && ex && typeof ex.end === "number") {
11253
11609
  cuts.push([ex.end, rec.end]);
11254
- walk2(rec.expression);
11610
+ walk(rec.expression);
11255
11611
  return;
11256
11612
  }
11257
11613
  if (rec.type === "TSTypeAssertion" && ex && typeof ex.start === "number") {
11258
11614
  cuts.push([rec.start, ex.start]);
11259
- walk2(rec.expression);
11615
+ walk(rec.expression);
11260
11616
  return;
11261
11617
  }
11262
11618
  cuts.push([rec.start, rec.end]);
@@ -11266,11 +11622,11 @@ function stripTsFromSource(node2, code) {
11266
11622
  if (key === "type" || key === "start" || key === "end") continue;
11267
11623
  const v2 = rec[key];
11268
11624
  if (Array.isArray(v2)) {
11269
- for (const item of v2) walk2(item);
11270
- } else if (v2 && typeof v2 === "object") walk2(v2);
11625
+ for (const item of v2) walk(item);
11626
+ } else if (v2 && typeof v2 === "object") walk(v2);
11271
11627
  }
11272
11628
  };
11273
- walk2(node2);
11629
+ walk(node2);
11274
11630
  const start = node2.start;
11275
11631
  const end = node2.end;
11276
11632
  if (cuts.length === 0) return code.slice(start, end);
@@ -11340,6 +11696,9 @@ var SUPPORTED = {
11340
11696
  optional: (...a) => optional(a[0]),
11341
11697
  skip: (...a) => skip(a[0], a[1]),
11342
11698
  trivia: (...a) => trivia(a[0]),
11699
+ classifiedTrivia: (...a) => classifiedTrivia(
11700
+ a[0]
11701
+ ),
11343
11702
  label: (...a) => label(a[0], a[1]),
11344
11703
  field: (...a) => field(a[0], a[1]),
11345
11704
  noTrivia: (...a) => noTrivia(a[0]),
@@ -11449,10 +11808,38 @@ function scopeGet(scope, name, mfs) {
11449
11808
  if (isCombinator(entry)) return entry;
11450
11809
  return null;
11451
11810
  }
11811
+ function unwrapStaticExpr(expr) {
11812
+ let cur = expr;
11813
+ while (cur.type === "TSAsExpression" || cur.type === "TSSatisfiesExpression" || cur.type === "TSNonNullExpression" || cur.type === "TSTypeAssertion" || cur.type === "TSInstantiationExpression" || cur.type === "ParenthesizedExpression") {
11814
+ if (!cur.expression) break;
11815
+ cur = cur.expression;
11816
+ }
11817
+ return cur;
11818
+ }
11452
11819
  function staticLiteralValue(expr) {
11453
- const val = expr;
11820
+ const val = unwrapStaticExpr(expr);
11454
11821
  return val.type === "Literal" || val.type === "BooleanLiteral" || val.type === "NumericLiteral" ? val.value : void 0;
11455
11822
  }
11823
+ function staticStringArray(expr, scope) {
11824
+ const id = unwrapStaticExpr(expr);
11825
+ if (id.type === "Identifier" && scope !== void 0 && id.name !== void 0) {
11826
+ const scoped = scope.get(id.name);
11827
+ const value = isStaticValueEntry(scoped) ? scoped.value : scoped;
11828
+ return Array.isArray(value) && value.every((v2) => typeof v2 === "string") ? value : void 0;
11829
+ }
11830
+ const arr = id;
11831
+ if (arr.type !== "ArrayExpression" || !Array.isArray(arr.elements)) return void 0;
11832
+ const out = [];
11833
+ for (const el of arr.elements) {
11834
+ if (!el || el.type === "SpreadElement") return void 0;
11835
+ const value = staticLiteralValue(el);
11836
+ if (typeof value !== "string") return void 0;
11837
+ out.push(value);
11838
+ }
11839
+ return out;
11840
+ }
11841
+ var STATIC_NODE_OPTIONS_FAILED = /* @__PURE__ */ Symbol("parseman.staticNodeOptions.failed");
11842
+ var STATIC_NODE_OPTIONS_NOT_OPTIONS = /* @__PURE__ */ Symbol("parseman.staticNodeOptions.notOptions");
11456
11843
  function staticNodeProject(expr) {
11457
11844
  const literalValue = staticLiteralValue(expr);
11458
11845
  if (typeof literalValue === "number" && Number.isInteger(literalValue) && literalValue >= 0) {
@@ -11461,23 +11848,60 @@ function staticNodeProject(expr) {
11461
11848
  if (typeof literalValue === "number" || literalValue !== void 0) return { ok: false };
11462
11849
  return { ok: false };
11463
11850
  }
11464
- function staticNodeOptions(expr) {
11465
- if (expr.type !== "ObjectExpression") return void 0;
11851
+ function scopedStaticValue(expr, scope) {
11852
+ const unwrapped = unwrapStaticExpr(expr);
11853
+ if (unwrapped.type !== "Identifier" || unwrapped.name === "undefined") return { found: false };
11854
+ if (!scope.has(unwrapped.name)) return { found: false };
11855
+ const scoped = scope.get(unwrapped.name);
11856
+ const value = isStaticValueEntry(scoped) ? scoped.value : scoped;
11857
+ return { found: true, value };
11858
+ }
11859
+ function staticNodeOptionsFromValue(value) {
11860
+ if (value === null || typeof value !== "object" || Array.isArray(value) || isCombinator(value)) return STATIC_NODE_OPTIONS_NOT_OPTIONS;
11861
+ const opts = {};
11862
+ const rec = value;
11863
+ for (const name of Object.keys(rec)) {
11864
+ const v2 = rec[name];
11865
+ if (name === "unwrap" || name === "collapse" || name === "captureTrivia" || name === "trailingTrivia") {
11866
+ if (v2 === true) opts[name] = true;
11867
+ else if (v2 !== false && v2 !== void 0) return STATIC_NODE_OPTIONS_FAILED;
11868
+ } else if (name === "project") {
11869
+ if (typeof v2 !== "number" || !Number.isInteger(v2) || v2 < 0) return STATIC_NODE_OPTIONS_FAILED;
11870
+ opts.project = v2;
11871
+ } else if (name === "tags") {
11872
+ if (!Array.isArray(v2) || !v2.every((item) => typeof item === "string")) return STATIC_NODE_OPTIONS_FAILED;
11873
+ opts.tags = v2;
11874
+ }
11875
+ }
11876
+ return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 ? opts : void 0;
11877
+ }
11878
+ function staticNodeOptions(expr, scope) {
11879
+ const unwrapped = unwrapStaticExpr(expr);
11880
+ const scoped = scopedStaticValue(unwrapped, scope);
11881
+ if (scoped.found) return staticNodeOptionsFromValue(scoped.value);
11882
+ if (unwrapped.type !== "ObjectExpression") return STATIC_NODE_OPTIONS_NOT_OPTIONS;
11466
11883
  const opts = {};
11467
- for (const prop of expr.properties) {
11884
+ for (const prop of unwrapped.properties) {
11885
+ if (prop.type !== "Property") return STATIC_NODE_OPTIONS_FAILED;
11468
11886
  const p = prop;
11469
- if (p.computed) continue;
11887
+ if (p.computed) return STATIC_NODE_OPTIONS_FAILED;
11470
11888
  const key = p.key;
11471
11889
  const name = key.type === "Identifier" ? key.name : key.type === "Literal" ? String(key.value) : void 0;
11472
11890
  if (name === "unwrap" || name === "collapse" || name === "captureTrivia" || name === "trailingTrivia") {
11473
- if (staticLiteralValue(p.value) === true) opts[name] = true;
11891
+ const value = staticLiteralValue(p.value);
11892
+ if (value === true) opts[name] = true;
11893
+ else if (value !== false && value !== void 0) return STATIC_NODE_OPTIONS_FAILED;
11474
11894
  } else if (name === "project") {
11475
11895
  const project = staticNodeProject(p.value);
11476
- if (project?.ok === false) return { project: -1 };
11896
+ if (project?.ok === false) return STATIC_NODE_OPTIONS_FAILED;
11477
11897
  if (project !== void 0) opts.project = project.value;
11898
+ } else if (name === "tags") {
11899
+ const tags = staticStringArray(p.value, scope);
11900
+ if (tags === void 0) return STATIC_NODE_OPTIONS_FAILED;
11901
+ opts.tags = tags;
11478
11902
  }
11479
11903
  }
11480
- return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia ? opts : void 0;
11904
+ return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 ? opts : void 0;
11481
11905
  }
11482
11906
  function exprToCombi(node2, scope, code, mfs) {
11483
11907
  if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
@@ -11552,10 +11976,23 @@ function exprToCombi(node2, scope, code, mfs) {
11552
11976
  const inner = anyValue(parserArg, scope, code, mfs);
11553
11977
  if (!isCombinator(inner)) return null;
11554
11978
  const be = buildArg;
11555
- const hasBuild = be !== void 0 && be.type !== "SpreadElement" && be.type !== "ObjectExpression" && !(be.type === "Identifier" && be.name === "undefined");
11979
+ const buildExpr = be === void 0 || be.type === "SpreadElement" ? void 0 : unwrapStaticExpr(be);
11980
+ const buildArgOptions = be !== void 0 && be.type !== "SpreadElement" ? staticNodeOptions(buildArg, scope) : STATIC_NODE_OPTIONS_NOT_OPTIONS;
11981
+ if (buildArgOptions === STATIC_NODE_OPTIONS_FAILED) return null;
11982
+ const buildArgIsOptions = buildArgOptions !== STATIC_NODE_OPTIONS_NOT_OPTIONS;
11983
+ const scopedBuild = buildExpr === void 0 ? { found: false } : scopedStaticValue(buildExpr, scope);
11984
+ const absentBuild = buildExpr?.type === "Identifier" && buildExpr.name === "undefined" || buildExpr !== void 0 && staticLiteralValue(buildExpr) === null || scopedBuild.found && (scopedBuild.value === void 0 || scopedBuild.value === null);
11985
+ const hasBuild = be !== void 0 && be.type !== "SpreadElement" && !buildArgIsOptions && !absentBuild;
11556
11986
  const buildSrc = hasBuild ? stripTsFromSource(be, code) : void 0;
11557
- const optionsArg = hasBuild ? optsArg : be?.type === "ObjectExpression" ? buildArg : optsArg;
11558
- const opts = optionsArg !== void 0 && optionsArg.type !== "SpreadElement" ? staticNodeOptions(optionsArg) : void 0;
11987
+ let opts;
11988
+ if (buildArgIsOptions) {
11989
+ opts = buildArgOptions;
11990
+ } else if (optsArg !== void 0) {
11991
+ if (optsArg.type === "SpreadElement") return null;
11992
+ const optsResult = staticNodeOptions(optsArg, scope);
11993
+ if (optsResult === STATIC_NODE_OPTIONS_FAILED || optsResult === STATIC_NODE_OPTIONS_NOT_OPTIONS) return null;
11994
+ opts = optsResult;
11995
+ }
11559
11996
  try {
11560
11997
  const combi = explicitType !== void 0 ? node(explicitType, inner, hasBuild ? () => null : void 0, opts) : node(inner, hasBuild ? () => null : void 0, opts);
11561
11998
  if (combi._def.tag === "node" && buildSrc !== void 0) {
@@ -11778,6 +12215,10 @@ function exprToCombi(node2, scope, code, mfs) {
11778
12215
  }
11779
12216
  }
11780
12217
  function anyValue(node2, scope, code, mfs) {
12218
+ if (node2.type === "TSAsExpression" || node2.type === "TSSatisfiesExpression" || node2.type === "TSNonNullExpression" || node2.type === "TSTypeAssertion" || node2.type === "TSInstantiationExpression" || node2.type === "ParenthesizedExpression") {
12219
+ const inner = node2.expression;
12220
+ return inner ? anyValue(inner, scope, code, mfs) : null;
12221
+ }
11781
12222
  if (node2.type === "Literal") {
11782
12223
  if ("regex" in node2 && node2.regex !== null && node2.regex !== void 0) {
11783
12224
  return new RegExp(node2.regex.pattern, node2.regex.flags);
@@ -11901,6 +12342,9 @@ function collectRuleKeys(retObj) {
11901
12342
  }
11902
12343
  return out;
11903
12344
  }
12345
+ function evaluateStaticValue(node2, scope, code) {
12346
+ return anyValue(node2, scope, code, []);
12347
+ }
11904
12348
  function flattenRuleEntries(retObj, scope, code) {
11905
12349
  const out = [];
11906
12350
  for (const prop of retObj.properties) {
@@ -12066,6 +12510,18 @@ function referencesAny(node2, names, scope) {
12066
12510
  // src/plugin/index.ts
12067
12511
  var import_node_crypto = require("node:crypto");
12068
12512
  var PARSEMAN_MODULE = "parseman";
12513
+ function unwrapStaticExpression(expr) {
12514
+ let cur = expr;
12515
+ while (cur.type === "TSAsExpression" || cur.type === "TSSatisfiesExpression" || cur.type === "TSNonNullExpression" || cur.type === "TSTypeAssertion" || cur.type === "TSInstantiationExpression" || cur.type === "ParenthesizedExpression") {
12516
+ if (!cur.expression) break;
12517
+ cur = cur.expression;
12518
+ }
12519
+ return cur;
12520
+ }
12521
+ function isStaticNullishExpression(expr) {
12522
+ const unwrapped = unwrapStaticExpression(expr);
12523
+ return unwrapped.type === "Identifier" && unwrapped.name === "undefined" || unwrapped.type === "Literal" && unwrapped.value === null;
12524
+ }
12069
12525
  var _compiledResolver = null;
12070
12526
  function getCompiledResolver() {
12071
12527
  return _compiledResolver ??= new import_oxc_resolver.ResolverFactory({
@@ -12548,6 +13004,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12548
13004
  ruleMap: evaluated.ruleMap,
12549
13005
  hostMode: compiled?.hostMode ?? "ast",
12550
13006
  hostBranchElided: compiled?.hostBranchElided ?? false,
13007
+ reflection: compiled?.reflection ?? { nodes: [] },
12551
13008
  ...compiled?.coverageDefinitions ? { coverageDefinitions: compiled.coverageDefinitions } : {},
12552
13009
  ...evaluated.trivia ? { trivia: evaluated.trivia } : {},
12553
13010
  ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {},
@@ -12568,7 +13025,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12568
13025
  const mapLit = (m, stripVals = false) => `new Map([${[...m].map(
12569
13026
  ([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(stripVals && typeof v2 === "string" ? stripIndent(v2) : v2)}]`
12570
13027
  ).join(", ")}])`;
12571
- 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: [] }`;
13028
+ 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: [] }`;
12572
13029
  };
12573
13030
  const isSpread = (it) => "__spreadLocal" in it;
12574
13031
  const isIR = (it) => "ir" in it && !("ruleFns" in it);
@@ -12577,6 +13034,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12577
13034
  const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
12578
13035
  const withLeafMarker = (grammarExpr) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.leafComposed'), { value: true, enumerable: false })`;
12579
13036
  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})`;
13037
+ const withGrammarReflection = (grammarExpr, reflection) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.grammarReflection'), { value: ${grammarReflectionSource(reflection)}, enumerable: false })`;
12580
13038
  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 })`;
12581
13039
  const emittedCoverageDefinitions = (source) => {
12582
13040
  const ids = /* @__PURE__ */ new Set();
@@ -12972,7 +13430,14 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
12972
13430
  const kind = vd.kind ?? "const";
12973
13431
  if (d.id.type === "Identifier") {
12974
13432
  const varName = d.id.name;
12975
- if (!referencesAny(init, allNames, scope)) continue;
13433
+ if (!referencesAny(init, allNames, scope)) {
13434
+ const staticValue = evaluateStaticValue(init, scope, code);
13435
+ if (staticValue !== null && staticValue !== void 0 || isStaticNullishExpression(init)) {
13436
+ ;
13437
+ scope.set(varName, staticValue);
13438
+ }
13439
+ continue;
13440
+ }
12976
13441
  if (refNames.has(varName)) {
12977
13442
  const refEntry = scope.get(varName);
12978
13443
  const refCombi = refEntry?.combi ?? null;
@@ -13012,6 +13477,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
13012
13477
  );
13013
13478
  if (compiledRules.replacement !== null) {
13014
13479
  replacement = withHostMode(replacement, compiledRules.hostMode, compiledRules.hostBranchElided);
13480
+ replacement = withGrammarReflection(replacement, compiledRules.reflection);
13015
13481
  }
13016
13482
  if (exportPrefix && pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
13017
13483
  const ir = serializeRuleMap([...compiledRules.ruleMap], compiledRules.scanSkip);
@@ -13039,7 +13505,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
13039
13505
  if (isComposeLeafCall(init)) {
13040
13506
  const fused = compileComposeLeafCall(init);
13041
13507
  if (!fused) {
13042
- throw new Error(`${id}:${lineOf(init.start)} \u2014 composeLeaf() must macro-fuse; runtime composition is forbidden`);
13508
+ const reasons = warnings.slice(-4);
13509
+ throw new Error(
13510
+ `${id}:${lineOf(init.start)} \u2014 composeLeaf() must macro-fuse; runtime composition is forbidden` + (reasons.length ? `
13511
+ causes:
13512
+ - ${reasons.join("\n - ")}` : "")
13513
+ );
13043
13514
  }
13044
13515
  replacements.push({ start: init.start, end: init.end, replacement: fused.replacement });
13045
13516
  markUsedImportedFactories(fused.importedFactories);
@@ -13068,6 +13539,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
13068
13539
  scope.set(varName, combiArray);
13069
13540
  continue;
13070
13541
  }
13542
+ const staticValue = evaluateStaticValue(init, scope, code);
13543
+ if (staticValue !== null && staticValue !== void 0 || isStaticNullishExpression(init)) {
13544
+ ;
13545
+ scope.set(varName, staticValue);
13546
+ continue;
13547
+ }
13071
13548
  if (factoryDecls.has(varName)) continue;
13072
13549
  warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
13073
13550
  continue;
@@ -13143,7 +13620,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
13143
13620
  const replacementRanges = runtimeComposeFallback ? [] : replacements.map(({ start, end }) => ({ start, end }));
13144
13621
  const isInsideReplacement = (start, end) => replacementRanges.some((r) => start >= r.start && end <= r.end);
13145
13622
  const importedBindingStillReferenced = (local) => {
13146
- const walk2 = (node2, parent, parentKey) => {
13623
+ const walk = (node2, parent, parentKey) => {
13147
13624
  if (!node2 || typeof node2 !== "object") return false;
13148
13625
  const rec = node2;
13149
13626
  if (rec.type === "ImportDeclaration") return false;
@@ -13158,14 +13635,14 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
13158
13635
  if (key === "type" || key === "start" || key === "end") continue;
13159
13636
  const value = rec[key];
13160
13637
  if (Array.isArray(value)) {
13161
- if (value.some((child) => walk2(child, rec, key))) return true;
13162
- } else if (walk2(value, rec, key)) {
13638
+ if (value.some((child) => walk(child, rec, key))) return true;
13639
+ } else if (walk(value, rec, key)) {
13163
13640
  return true;
13164
13641
  }
13165
13642
  }
13166
13643
  return false;
13167
13644
  };
13168
- return body.some((stmt) => walk2(stmt));
13645
+ return body.some((stmt) => walk(stmt));
13169
13646
  };
13170
13647
  for (const imp of ordinaryImports) {
13171
13648
  if (!runtimeComposeFallback && imp.specifiers.length > 0 && imp.specifiers.every(