parseman 0.8.0 → 0.10.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.
@@ -1099,24 +1099,70 @@ function not(combinator) {
1099
1099
  };
1100
1100
  }
1101
1101
 
1102
+ // src/compiler/build-arity.ts
1103
+ var PARAM_LIST_RE = (
1104
+ // (params) => ... | function name?(params) ... | single-ident arrow `x => ...`
1105
+ /^(?:function\b[^(]*\(([^)]*)\)|\(([^)]*)\)\s*=>|([A-Za-z_$][\w$]*)\s*=>)/
1106
+ );
1107
+ var CONFIRMABLE_PARAM_RE = /^[A-Za-z_$][\w$]*\s*\??\s*(?::[^,=]+)?$/;
1108
+ function confirmedBuildArity(src) {
1109
+ const s = src.trim();
1110
+ const m = PARAM_LIST_RE.exec(s);
1111
+ if (!m) return null;
1112
+ if (m[3] !== void 0) return 1;
1113
+ const inner = (m[1] ?? m[2] ?? "").trim();
1114
+ if (inner === "") return 0;
1115
+ const parts = inner.split(",");
1116
+ for (const part of parts) {
1117
+ if (!CONFIRMABLE_PARAM_RE.test(part.trim())) return null;
1118
+ }
1119
+ if (/\barguments\b/.test(s)) return null;
1120
+ return parts.length;
1121
+ }
1122
+ function buildReadsTrivia(def) {
1123
+ const src = def.buildSrc ?? def.build.toString();
1124
+ const arity = confirmedBuildArity(src);
1125
+ if (arity === null) return true;
1126
+ return arity >= 4;
1127
+ }
1128
+ function buildReadsState(def) {
1129
+ const src = def.buildSrc ?? def.build.toString();
1130
+ const arity = confirmedBuildArity(src);
1131
+ if (arity === null) return true;
1132
+ return arity >= 5;
1133
+ }
1134
+
1102
1135
  // src/combinators/node.ts
1103
- function node(type, combinator, build) {
1136
+ function collapseChild(child) {
1137
+ return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
1138
+ }
1139
+ function node(type, combinator, build, opts) {
1104
1140
  const meta = {
1105
1141
  firstSet: combinator._meta.firstSet,
1106
1142
  canMatchNewline: combinator._meta.canMatchNewline,
1107
1143
  isTrivia: false
1108
1144
  };
1145
+ const collapse = opts?.collapse === true;
1146
+ const def = collapse ? { tag: "node", type, parser: combinator, build, collapse: true } : { tag: "node", type, parser: combinator, build };
1147
+ const capturesTrivia = buildReadsTrivia(def);
1148
+ const clonesState = buildReadsState(def);
1109
1149
  return {
1110
1150
  _tag: "node",
1111
1151
  _meta: meta,
1112
- _def: { tag: "node", type, parser: combinator, build },
1152
+ _def: def,
1113
1153
  parse(input, pos, ctx) {
1114
1154
  const saved = beginCstNodeCapture(ctx);
1155
+ if (!capturesTrivia) ctx.captureTrivia = false;
1115
1156
  const r = combinator.parse(input, pos, ctx);
1116
1157
  const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
1117
1158
  if (!r.ok) return r;
1118
- const state = ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
1119
- const built = build(children, rawChildren, r.span, triviaLog, state);
1159
+ const built = collapse && children.length === 1 ? collapseChild(children[0]) : build(
1160
+ children,
1161
+ rawChildren,
1162
+ r.span,
1163
+ triviaLog,
1164
+ clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0
1165
+ );
1120
1166
  const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
1121
1167
  const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
1122
1168
  if (saved.buf !== void 0 || saved.ch !== void 0) {
@@ -1234,14 +1280,140 @@ function noTrivia(root) {
1234
1280
  return parser({ trivia: null }, root);
1235
1281
  }
1236
1282
 
1283
+ // src/compiler/scannable-run.ts
1284
+ var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1285
+ var META = new Set("()[]{}*+?|^$.".split(""));
1286
+ function parseClassRanges(body) {
1287
+ const ranges = [];
1288
+ let i = 0;
1289
+ const readCp = () => {
1290
+ const ch = body[i];
1291
+ if (ch === void 0) return null;
1292
+ if (ch === "\\") {
1293
+ const e = body[i + 1];
1294
+ if (e === void 0) return null;
1295
+ i += 2;
1296
+ return e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0);
1297
+ }
1298
+ i += ch.length;
1299
+ return ch.codePointAt(0);
1300
+ };
1301
+ while (i < body.length) {
1302
+ const lo = readCp();
1303
+ if (lo === null) return null;
1304
+ if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
1305
+ i += 1;
1306
+ const hi = readCp();
1307
+ if (hi === null) return null;
1308
+ ranges.push([lo, hi]);
1309
+ } else {
1310
+ ranges.push([lo, lo]);
1311
+ }
1312
+ }
1313
+ return ranges.length ? ranges : null;
1314
+ }
1315
+ function literalCodePoints(frag) {
1316
+ const out = [];
1317
+ let i = 0;
1318
+ while (i < frag.length) {
1319
+ const ch = frag[i];
1320
+ if (ch === "\\") {
1321
+ const e = frag[i + 1];
1322
+ if (e === void 0) return null;
1323
+ out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
1324
+ i += 2;
1325
+ continue;
1326
+ }
1327
+ if (META.has(ch)) return null;
1328
+ out.push(ch.codePointAt(0));
1329
+ i += 1;
1330
+ }
1331
+ return out.length ? out : null;
1332
+ }
1333
+ function parseScanShape(source) {
1334
+ let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
1335
+ if (m) {
1336
+ if (m[1].startsWith("^")) return null;
1337
+ const ranges = parseClassRanges(m[1]);
1338
+ return ranges ? { kind: "chars", ranges } : null;
1339
+ }
1340
+ m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
1341
+ if (m) {
1342
+ const open = literalCodePoints(m[1]);
1343
+ const stop = parseClassRanges(m[2]);
1344
+ if (open && stop) return { kind: "until", open, stop };
1345
+ return null;
1346
+ }
1347
+ if (!source.includes("\\\\")) {
1348
+ m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
1349
+ if (m && m[1] && m[2]) {
1350
+ const open = literalCodePoints(m[1]);
1351
+ const close = literalCodePoints(m[2]);
1352
+ if (open && close) return { kind: "delimited", open, close };
1353
+ }
1354
+ }
1355
+ return null;
1356
+ }
1357
+ var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
1358
+ var litCond = (base, cps, firstVar) => cps.map(
1359
+ (cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
1360
+ ).join(" && ");
1361
+ function advanceExpr(shape) {
1362
+ if (shape.kind === "chars") {
1363
+ return {
1364
+ setup: [
1365
+ ` _e++`,
1366
+ ` while (_e < input.length) { const c2 = input.charCodeAt(_e); if (${classCond("c2", shape.ranges)}) _e++; else break }`
1367
+ ],
1368
+ endVar: "_e"
1369
+ };
1370
+ }
1371
+ if (shape.kind === "until") {
1372
+ return {
1373
+ setup: [
1374
+ ` let j = _e + ${shape.open.length}`,
1375
+ ` while (j < input.length && !(${classCond("input.charCodeAt(j)", shape.stop)})) j++`
1376
+ ],
1377
+ endVar: "j"
1378
+ };
1379
+ }
1380
+ const n = shape.close.length;
1381
+ return {
1382
+ setup: [
1383
+ ` let j = _e + ${shape.open.length}`,
1384
+ ` while (j + ${n - 1} < input.length && !(${litCond("j", shape.close)})) j++`
1385
+ ],
1386
+ endVar: n === 1 ? "(j + 1 <= input.length ? j + 1 : input.length)" : `(j + ${n} <= input.length ? j + ${n} : input.length)`
1387
+ };
1388
+ }
1389
+ function guardLine(shape) {
1390
+ const cond = shape.kind === "chars" ? classCond("c", shape.ranges) : litCond("_e", shape.open, "c");
1391
+ return ` if (${cond}) {`;
1392
+ }
1393
+ function scanBranch(shape) {
1394
+ if (shape.kind === "chars") {
1395
+ return ` if (${classCond("c", shape.ranges)}) { _e++; continue }`;
1396
+ }
1397
+ const { setup, endVar } = advanceExpr(shape);
1398
+ return [guardLine(shape), ...setup, ` _e = ${endVar}`, ` continue`, ` }`].join("\n");
1399
+ }
1400
+ function captureChunk(startVar, kindIndex) {
1401
+ return [
1402
+ ` if (_cap) {`,
1403
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(${startVar}, _e, ${kindIndex})`,
1404
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(${startVar}, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
1405
+ ` }`
1406
+ ];
1407
+ }
1408
+ function scanBranchLabeled(shape, kindIndex) {
1409
+ const { setup, endVar } = advanceExpr(shape);
1410
+ const lines = [guardLine(shape), ` const _cs = _e`, ...setup];
1411
+ if (endVar !== "_e") lines.push(` _e = ${endVar}`);
1412
+ lines.push(...captureChunk("_cs", kindIndex), ` continue`, ` }`);
1413
+ return lines.join("\n");
1414
+ }
1415
+
1237
1416
  // src/compiler/trivia-fast-path.ts
1238
- var BLOCK_COMMENT = String.raw`\/\*(?:[^*]|\*(?!\/))*\*\/`;
1239
- var WS_CLASS_SOURCES = /* @__PURE__ */ new Set([
1240
- String.raw`[ \t\n\r\f]+`,
1241
- String.raw`[ \t\n]+`,
1242
- String.raw`[ \t]+`,
1243
- String.raw`[\t\n\f\r ]+`
1244
- ]);
1245
1417
  function unwrapTrivia2(p) {
1246
1418
  let cur = p;
1247
1419
  while (cur._def.tag === "trivia") cur = cur._def.parser;
@@ -1257,36 +1429,30 @@ function choiceArms(p) {
1257
1429
  if (p._def.tag === "choice") return p._def.parsers;
1258
1430
  return null;
1259
1431
  }
1260
- function isWsClassSource(source) {
1261
- return WS_CLASS_SOURCES.has(source);
1262
- }
1263
- function isBlockCommentSource(source) {
1264
- return source === BLOCK_COMMENT;
1432
+ function armShape(arm) {
1433
+ const src = getCoreRegexDef(arm)?.source;
1434
+ return src ? parseScanShape(src) : null;
1265
1435
  }
1266
1436
  function analyzeTriviaFastPath(trivia2) {
1267
1437
  const core = unwrapTrivia2(trivia2);
1268
- const direct = getCoreRegexDef(core)?.source;
1269
- if (direct) {
1270
- if (isWsClassSource(direct)) return "wsOnly";
1271
- return null;
1438
+ const directSrc = getCoreRegexDef(core)?.source;
1439
+ if (directSrc) {
1440
+ const shape = parseScanShape(directSrc);
1441
+ return shape && shape.kind === "chars" ? [shape] : null;
1272
1442
  }
1273
1443
  const inner = oneOrMoreInner(core);
1274
1444
  if (!inner) return null;
1275
- const innerSrc = getCoreRegexDef(inner)?.source;
1276
- if (innerSrc && isWsClassSource(innerSrc)) return "wsOnly";
1445
+ const innerShape = armShape(inner);
1446
+ if (innerShape) return innerShape.kind === "chars" ? [innerShape] : null;
1277
1447
  const arms = choiceArms(inner);
1278
- if (!arms || arms.length !== 2) return null;
1279
- let hasWs = false;
1280
- let hasComment = false;
1448
+ if (!arms || arms.length < 2) return null;
1449
+ const shapes = [];
1281
1450
  for (const arm of arms) {
1282
- const src = getCoreRegexDef(arm)?.source;
1283
- if (!src) return null;
1284
- if (isWsClassSource(src)) hasWs = true;
1285
- else if (isBlockCommentSource(src)) hasComment = true;
1286
- else return null;
1451
+ const shape = armShape(arm);
1452
+ if (!shape) return null;
1453
+ shapes.push(shape);
1287
1454
  }
1288
- if (hasWs && hasComment) return "wsComments";
1289
- return null;
1455
+ return shapes;
1290
1456
  }
1291
1457
  var CAP_RECORD = [
1292
1458
  ` if (_cap && _e > _pos) {`,
@@ -1294,80 +1460,49 @@ var CAP_RECORD = [
1294
1460
  ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
1295
1461
  ` }`
1296
1462
  ].join("\n");
1297
- var WS_ONLY_LOOP = [
1298
- ` while (_e < input.length) {`,
1299
- ` const c = input.charCodeAt(_e)`,
1300
- ` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) { _e++; continue }`,
1301
- ` break`,
1302
- ` }`
1303
- ].join("\n");
1304
- var WS_COMMENTS_LOOP = [
1305
- ` while (_e < input.length) {`,
1306
- ` const c = input.charCodeAt(_e)`,
1307
- ` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) { _e++; continue }`,
1308
- ` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
1309
- ` let j = _e + 2`,
1310
- ` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
1311
- ` _e = j + 2 <= input.length ? j + 2 : input.length`,
1312
- ` continue`,
1313
- ` }`,
1314
- ` break`,
1315
- ` }`
1316
- ].join("\n");
1317
- var CAP_CHUNK = (wsKind, commentKind) => [
1318
- ` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {`,
1319
- ` const _cs = _e`,
1320
- ` _e++`,
1321
- ` while (_e < input.length) {`,
1322
- ` const c2 = input.charCodeAt(_e)`,
1323
- ` if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) _e++`,
1324
- ` else break`,
1325
- ` }`,
1326
- ` if (_cap) {`,
1327
- ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${wsKind})`,
1328
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${wsKind})`,
1329
- ` }`,
1330
- ` continue`,
1331
- ` }`,
1332
- ` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
1333
- ` const _cs = _e`,
1334
- ` let j = _e + 2`,
1335
- ` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
1336
- ` _e = j + 2 <= input.length ? j + 2 : input.length`,
1337
- ` if (_cap) {`,
1338
- ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${commentKind})`,
1339
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${commentKind})`,
1340
- ` }`,
1341
- ` continue`,
1342
- ` }`
1343
- ].join("\n");
1344
- var WS_COMMENTS_LOOP_LABELED = (wsKind, commentKind) => [
1345
- ` while (_e < input.length) {`,
1346
- ` const c = input.charCodeAt(_e)`,
1347
- CAP_CHUNK(wsKind, commentKind),
1348
- ` break`,
1349
- ` }`
1350
- ].join("\n");
1351
- function buildFastTriviaFnDecl(fnName, kind, kindIndices) {
1352
- const loop = kind === "wsComments" && kindIndices ? WS_COMMENTS_LOOP_LABELED(kindIndices.ws, kindIndices.comment) : kind === "wsComments" ? WS_COMMENTS_LOOP : WS_ONLY_LOOP;
1353
- const cap = kindIndices ? "" : CAP_RECORD;
1354
- const lines = [
1463
+ function composeFastLoop(shapes) {
1464
+ return [
1465
+ ` while (_e < input.length) {`,
1466
+ ` const c = input.charCodeAt(_e)`,
1467
+ ...shapes.map(scanBranch),
1468
+ ` break`,
1469
+ ` }`
1470
+ ].join("\n");
1471
+ }
1472
+ function buildFastTriviaFnDecl(fnName, shapes) {
1473
+ return [
1355
1474
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
1356
1475
  ` let _e = _pos`,
1357
- loop
1358
- ];
1359
- if (cap) lines.push(cap);
1360
- lines.push(` return _e`, `}`);
1361
- return lines.join("\n");
1476
+ composeFastLoop(shapes),
1477
+ CAP_RECORD,
1478
+ ` return _e`,
1479
+ `}`
1480
+ ].join("\n");
1362
1481
  }
1363
- function regexArmIndex(spec, isComment) {
1482
+ function analyzeLabeledScannableRun(trivia2) {
1483
+ const spec = analyzeLabeledTrivia(trivia2);
1484
+ if (!spec) return null;
1485
+ const out = [];
1364
1486
  for (const arm of spec.arms) {
1365
1487
  const src = getCoreRegexDef(arm.parser)?.source;
1366
- if (!src) return null;
1367
- const comment = src.includes("\\*") || src.startsWith("\\/\\/");
1368
- if (comment === isComment) return arm.kindIndex;
1488
+ const shape = src ? parseScanShape(src) : null;
1489
+ if (!shape) return null;
1490
+ out.push({ shape, kindIndex: arm.kindIndex });
1369
1491
  }
1370
- return null;
1492
+ return out;
1493
+ }
1494
+ function buildLabeledScannableTriviaFnDecl(fnName, arms) {
1495
+ return [
1496
+ `function ${fnName}(input, _pos, _ctx, _cap) {`,
1497
+ ` let _e = _pos`,
1498
+ ` while (_e < input.length) {`,
1499
+ ` const c = input.charCodeAt(_e)`,
1500
+ ...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
1501
+ ` break`,
1502
+ ` }`,
1503
+ ` return _e`,
1504
+ `}`
1505
+ ].join("\n");
1371
1506
  }
1372
1507
  function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
1373
1508
  const tryArms = spec.arms.map((arm, i) => {
@@ -1400,14 +1535,6 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
1400
1535
  `}`
1401
1536
  ].join("\n");
1402
1537
  }
1403
- function labeledTriviaKindIndices(trivia2) {
1404
- const spec = analyzeLabeledTrivia(trivia2);
1405
- if (!spec || spec.arms.length !== 2) return null;
1406
- const ws = regexArmIndex(spec, false);
1407
- const comment = regexArmIndex(spec, true);
1408
- if (ws === null || comment === null) return null;
1409
- return { ws, comment };
1410
- }
1411
1538
  function labeledTriviaRegexArms(trivia2) {
1412
1539
  const spec = analyzeLabeledTrivia(trivia2);
1413
1540
  if (!spec) return null;
@@ -1655,14 +1782,19 @@ function ensureTriviaFn(ctx) {
1655
1782
  const fnName = `_tf${ctx.triviaFnNames.size}`;
1656
1783
  ctx.triviaFnNames.set(trivia2, fnName);
1657
1784
  ctx.triviaCaptureNames.set(trivia2, fnName);
1658
- const fastKind = analyzeTriviaFastPath(trivia2);
1659
- const kindIndices = labeledTriviaKindIndices(trivia2);
1660
- if (fastKind) {
1661
- ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastKind, kindIndices ?? void 0));
1662
- return fnName;
1663
- }
1664
1785
  const labeledSpec = analyzeLabeledTrivia(trivia2);
1665
- if (labeledSpec) {
1786
+ if (!labeledSpec) {
1787
+ const fastShapes = analyzeTriviaFastPath(trivia2);
1788
+ if (fastShapes) {
1789
+ ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastShapes));
1790
+ return fnName;
1791
+ }
1792
+ } else {
1793
+ const labeledShapes = analyzeLabeledScannableRun(trivia2);
1794
+ if (labeledShapes) {
1795
+ ctx.namedFnDecls.push(buildLabeledScannableTriviaFnDecl(fnName, labeledShapes));
1796
+ return fnName;
1797
+ }
1666
1798
  const regexSpec = labeledTriviaRegexArms(trivia2);
1667
1799
  if (regexSpec) {
1668
1800
  const reNames = [];
@@ -2100,11 +2232,11 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
2100
2232
  const p = def.parsers[idx];
2101
2233
  const litVal = getCoreLiteralValue(p);
2102
2234
  const litLen = litVal.length;
2103
- const litCond = emitLiteralCondition(litVal, pos);
2235
+ const litCond2 = emitLiteralCondition(litVal, pos);
2104
2236
  const kw = first ? "if" : "else if";
2105
2237
  first = false;
2106
2238
  const litEnd = `${pos} + ${litLen}`;
2107
- stmts.push(`${ind(ctx)}${kw} (${litCond}) {`);
2239
+ stmts.push(`${ind(ctx)}${kw} (${litCond2}) {`);
2108
2240
  ctx.indent++;
2109
2241
  const tR = emitTransformChain(p, JSON.stringify(litVal), litEnd, pos, ctx);
2110
2242
  stmts.push(
@@ -2433,25 +2565,34 @@ function emitNode(def, ctx, pos) {
2433
2565
  ctx.buildSrcs.push(def.buildSrc ?? null);
2434
2566
  }
2435
2567
  const i = ind(ctx);
2568
+ const capturesTrivia = mkType !== null || buildReadsTrivia(def);
2569
+ const clonesState = buildReadsState(def);
2436
2570
  const chV = v(ctx, "_ch");
2437
2571
  const rawV = v(ctx, "_raw");
2438
- const tlV = v(ctx, "_tl");
2572
+ const tlV = capturesTrivia ? v(ctx, "_tl") : "_EMPTY_TL";
2573
+ if (!capturesTrivia) ctx.needsEmptyTl = true;
2439
2574
  const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
2575
+ const allocStmt = capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
2576
+ const innerTl = capturesTrivia ? tlV : "undefined";
2440
2577
  const stmts = [
2441
- `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []`,
2578
+ allocStmt,
2442
2579
  `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
2443
- `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${tlV}`
2580
+ `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}`
2444
2581
  ];
2445
2582
  const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
2446
2583
  stmts.push(...innerStmts);
2447
2584
  stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
2448
2585
  stmts.push(...emitIfFail(ctx, `!${okVar}`, failBody(ctx, '"node"', pos)));
2449
- const stV = v(ctx, "_nst");
2450
- stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
2586
+ let stV = "undefined";
2587
+ if (clonesState) {
2588
+ stV = v(ctx, "_nst");
2589
+ stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
2590
+ }
2451
2591
  const ndV = v(ctx, "_nd");
2452
2592
  const ndExpr = mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : `_build[${buildIdx}](${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV})`;
2593
+ const finalExpr = def.collapse ? `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})` : ndExpr;
2453
2594
  stmts.push(
2454
- `${i}const ${ndV} = ${ndExpr}`,
2595
+ `${i}const ${ndV} = ${finalExpr}`,
2455
2596
  `${i}if (${sc}) ${sc}.push(${ndV})`,
2456
2597
  `${i}if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && ${ndV}._tag === 'node') ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : '', span: { start: ${pos}, end: ${endVar} } })`
2457
2598
  );
@@ -2472,6 +2613,16 @@ function emitRuntimeFallback(parser2, ctx, pos) {
2472
2613
  return { stmts, valueVar: vv, endVar: ev };
2473
2614
  }
2474
2615
  function emitLazy(p, def, ctx, pos) {
2616
+ const usage = ctx.lazyUsage;
2617
+ if (usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
2618
+ let resolved;
2619
+ try {
2620
+ resolved = def.thunk();
2621
+ } catch {
2622
+ return emitRuntimeFallback(p, ctx, pos);
2623
+ }
2624
+ return emit(resolved, ctx, pos);
2625
+ }
2475
2626
  if (!ctx.namedParsers.has(p)) {
2476
2627
  const fnName2 = `_pf${ctx.namedParsers.size}`;
2477
2628
  ctx.namedParsers.set(p, fnName2);
@@ -2736,6 +2887,80 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
2736
2887
  return false;
2737
2888
  }
2738
2889
  }
2890
+ function childrenOf(def) {
2891
+ switch (def.tag) {
2892
+ case "sequence":
2893
+ case "choice":
2894
+ return def.parsers;
2895
+ case "many":
2896
+ case "optional":
2897
+ case "transform":
2898
+ case "trivia":
2899
+ case "label":
2900
+ case "grammar":
2901
+ case "not":
2902
+ case "node":
2903
+ case "withCtx":
2904
+ case "expect":
2905
+ return [def.parser];
2906
+ // emitMany's oneOrMore branch and emitSepBy both codegen `def.parser` TWICE
2907
+ // (a mandatory first match, then again inside the repeat loop) — two real
2908
+ // emit() call sites, so the usage analysis must see two edges here or it
2909
+ // undercounts a single-use `parser` ref as inline-safe when it's actually
2910
+ // referenced from two positions within this one compiled function.
2911
+ case "oneOrMore":
2912
+ return [def.parser, def.parser];
2913
+ case "sepBy":
2914
+ return [def.parser, def.parser, def.separator];
2915
+ case "skip":
2916
+ return [def.main, def.skipped];
2917
+ case "recover":
2918
+ return [def.parser, def.sentinel];
2919
+ case "scanTo":
2920
+ return [def.sentinel, ...def.skip];
2921
+ case "lazy":
2922
+ case "literal":
2923
+ case "regex":
2924
+ case "keywords":
2925
+ case "guard":
2926
+ case "unknown":
2927
+ return [];
2928
+ }
2929
+ }
2930
+ function analyzeLazyUsage(root) {
2931
+ return analyzeLazyUsageMulti([root]);
2932
+ }
2933
+ function analyzeLazyUsageMulti(roots) {
2934
+ const counts = /* @__PURE__ */ new Map();
2935
+ const recursive = /* @__PURE__ */ new Set();
2936
+ const descended = /* @__PURE__ */ new Set();
2937
+ const active = /* @__PURE__ */ new Set();
2938
+ function walk(p) {
2939
+ const def = p._def;
2940
+ if (def.tag === "lazy") {
2941
+ counts.set(p, (counts.get(p) ?? 0) + 1);
2942
+ if (active.has(p)) {
2943
+ recursive.add(p);
2944
+ return;
2945
+ }
2946
+ if (descended.has(p)) return;
2947
+ descended.add(p);
2948
+ let resolved;
2949
+ try {
2950
+ resolved = def.thunk();
2951
+ } catch {
2952
+ return;
2953
+ }
2954
+ active.add(p);
2955
+ walk(resolved);
2956
+ active.delete(p);
2957
+ return;
2958
+ }
2959
+ for (const child of childrenOf(def)) walk(child);
2960
+ }
2961
+ for (const root of roots) walk(root);
2962
+ return { counts, recursive };
2963
+ }
2739
2964
  function compile(combinator, mapFnSources) {
2740
2965
  const ctx = {
2741
2966
  vars: 0,
@@ -2752,13 +2977,16 @@ function compile(combinator, mapFnSources) {
2752
2977
  triviaCaptureNames: /* @__PURE__ */ new Map(),
2753
2978
  triviaFnNames: /* @__PURE__ */ new Map(),
2754
2979
  namedFnDecls: [],
2755
- capturing: hasNodeDef(combinator)
2980
+ capturing: hasNodeDef(combinator),
2981
+ lazyUsage: analyzeLazyUsage(combinator)
2756
2982
  };
2757
2983
  const r = emit(combinator, ctx, "_pos");
2758
2984
  const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
2759
2985
  ` : "";
2760
2986
  const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
2987
+ const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
2761
2988
  const source = [
2989
+ ...emptyTlDecls,
2762
2990
  ...ctx.regexDecls,
2763
2991
  "",
2764
2992
  ...namedPrelude,
@@ -2770,6 +2998,7 @@ function compile(combinator, mapFnSources) {
2770
2998
  `}`
2771
2999
  ].join("\n");
2772
3000
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
3001
+ ...emptyTlDecls,
2773
3002
  ...ctx.regexDecls,
2774
3003
  collatorDecl,
2775
3004
  ...namedPrelude,
@@ -2810,6 +3039,67 @@ function compile(combinator, mapFnSources) {
2810
3039
  }
2811
3040
  };
2812
3041
  }
3042
+ function compileRuleMap(ruleMap) {
3043
+ const ctx = {
3044
+ vars: 0,
3045
+ indent: 1,
3046
+ regexDecls: [],
3047
+ regexMap: /* @__PURE__ */ new Map(),
3048
+ mapFns: [],
3049
+ mapFnSrcs: [],
3050
+ buildFns: [],
3051
+ buildSrcs: [],
3052
+ runtimeParsers: [],
3053
+ needsCollator: false,
3054
+ namedParsers: /* @__PURE__ */ new Map(),
3055
+ triviaCaptureNames: /* @__PURE__ */ new Map(),
3056
+ triviaFnNames: /* @__PURE__ */ new Map(),
3057
+ namedFnDecls: [],
3058
+ capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
3059
+ lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule))
3060
+ };
3061
+ const perEntry = ruleMap.map(([key, rule]) => ({ key, r: emit(rule, ctx, "_pos") }));
3062
+ const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
3063
+ ` : "";
3064
+ const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
3065
+ const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
3066
+ const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
3067
+ const mfCovered = ctx.mapFns.length === 0 || derivedSrcs !== void 0;
3068
+ const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
3069
+ if (!canInline) return null;
3070
+ const mfDecl = derivedSrcs?.length ? ` const _mf = [${derivedSrcs.join(", ")}]` : "";
3071
+ const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
3072
+ const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
3073
+ const hoistedDecls = [
3074
+ ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
3075
+ ...ctx.regexDecls.map((d) => ` ${d}`),
3076
+ collatorDecl ? ` ${collatorDecl.trim()}` : "",
3077
+ mfDecl,
3078
+ buildDecl,
3079
+ ...namedPrelude.map((l) => ` ${l}`),
3080
+ ...ctx.namedFnDecls.flatMap((decl, i) => {
3081
+ const lines = decl.split("\n").map((l) => ` ${l}`);
3082
+ return i > 0 ? ["", ...lines] : lines;
3083
+ })
3084
+ ].filter(Boolean);
3085
+ const entryFnText = (r) => [
3086
+ `function(input, _pos, _ctx) {`,
3087
+ ` let pos = _pos`,
3088
+ ...r.stmts,
3089
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
3090
+ `}`
3091
+ ].join("\n");
3092
+ const objBody = perEntry.map(({ key, r }) => ` ${JSON.stringify(key)}: ${entryFnText(r).split("\n").join("\n ")}`).join(",\n");
3093
+ const replacement = [
3094
+ `/* @__PURE__ */ (() => {`,
3095
+ ...hoistedDecls,
3096
+ ` return {`,
3097
+ objBody,
3098
+ ` }`,
3099
+ `})()`
3100
+ ].join("\n");
3101
+ return { keys: perEntry.map((e) => e.key), replacement };
3102
+ }
2813
3103
  function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources) {
2814
3104
  const bodyLines = [
2815
3105
  ` let pos = _pos`,
@@ -2823,10 +3113,12 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
2823
3113
  ].join("\n");
2824
3114
  const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
2825
3115
  const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
2826
- const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl;
3116
+ const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
3117
+ const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
2827
3118
  if (!needsWrapper) return innerFn;
2828
3119
  const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
2829
3120
  const hoistedDecls = [
3121
+ emptyTlDecl,
2830
3122
  ...ctx.regexDecls.map((d) => ` ${d}`),
2831
3123
  collatorDecl ? ` ${collatorDecl.trim()}` : "",
2832
3124
  mfDecl,
@@ -3027,6 +3319,20 @@ function scopeGet(scope, name, mfs) {
3027
3319
  if (isCombinator(entry)) return entry;
3028
3320
  return null;
3029
3321
  }
3322
+ function staticCollapseOption(expr) {
3323
+ if (expr.type !== "ObjectExpression") return false;
3324
+ for (const prop of expr.properties) {
3325
+ const p = prop;
3326
+ if (p.computed) continue;
3327
+ const key = p.key;
3328
+ const name = key.type === "Identifier" ? key.name : key.type === "Literal" ? String(key.value) : void 0;
3329
+ if (name === "collapse") {
3330
+ const val = p.value;
3331
+ return (val.type === "Literal" || val.type === "BooleanLiteral") && val.value === true;
3332
+ }
3333
+ }
3334
+ return false;
3335
+ }
3030
3336
  function exprToCombi(node2, scope, code, mfs) {
3031
3337
  if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
3032
3338
  if (node2.type !== "CallExpression") return null;
@@ -3075,15 +3381,16 @@ function exprToCombi(node2, scope, code, mfs) {
3075
3381
  }
3076
3382
  }
3077
3383
  if (callee.name === "node" && code !== void 0) {
3078
- const [typeArg, parserArg, buildArg] = node2.arguments;
3384
+ const [typeArg, parserArg, buildArg, optsArg] = node2.arguments;
3079
3385
  if (!typeArg || !parserArg || !buildArg || typeArg.type === "SpreadElement" || parserArg.type === "SpreadElement" || buildArg.type === "SpreadElement") return null;
3080
3386
  const typeVal = anyValue(typeArg, scope, code);
3081
3387
  if (typeof typeVal !== "string") return null;
3082
3388
  const inner = anyValue(parserArg, scope, code, mfs);
3083
3389
  if (!isCombinator(inner)) return null;
3084
3390
  const buildSrc = code.slice(buildArg.start, buildArg.end);
3391
+ const collapse = optsArg !== void 0 && optsArg.type !== "SpreadElement" ? staticCollapseOption(optsArg) : false;
3085
3392
  try {
3086
- const combi = node(typeVal, inner, () => null);
3393
+ const combi = node(typeVal, inner, () => null, collapse ? { collapse: true } : void 0);
3087
3394
  if (combi._def.tag === "node") combi._def.buildSrc = buildSrc;
3088
3395
  return combi;
3089
3396
  } catch {
@@ -3091,6 +3398,10 @@ function exprToCombi(node2, scope, code, mfs) {
3091
3398
  }
3092
3399
  }
3093
3400
  if (callee.name === "rules") return null;
3401
+ if (callee.name === "ref") {
3402
+ if (node2.arguments.length !== 0) return null;
3403
+ return ref();
3404
+ }
3094
3405
  if (callee.name === "parser") {
3095
3406
  const [optsArg, rootArg] = node2.arguments;
3096
3407
  if (!optsArg || !rootArg || optsArg.type === "SpreadElement" || rootArg.type === "SpreadElement") return null;
@@ -3330,6 +3641,40 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
3330
3641
  }
3331
3642
  return ruleRefs;
3332
3643
  }
3644
+ function isDefinableRef(v2) {
3645
+ return isCombinator(v2) && typeof v2.define === "function" && v2._def.tag === "lazy";
3646
+ }
3647
+ function evaluateRefDeclaration(init, name, scope) {
3648
+ if (init.type !== "CallExpression") return null;
3649
+ const callee = init.callee;
3650
+ if (callee.type !== "Identifier" || callee.name !== "ref") return null;
3651
+ if (init.arguments.length !== 0) return null;
3652
+ const slot = ref();
3653
+ scope.set(name, { combi: slot, mfSrcs: [] });
3654
+ return slot;
3655
+ }
3656
+ function applyDefineStatement(callExpr, scope, code) {
3657
+ if (callExpr.type !== "CallExpression") return false;
3658
+ const callee = callExpr.callee;
3659
+ if (callee.type !== "MemberExpression") return false;
3660
+ const mem = callee;
3661
+ if (mem.computed) return false;
3662
+ if (mem.property.type !== "Identifier" || mem.property.name !== "define") return false;
3663
+ if (mem.object.type !== "Identifier" || !mem.object.name) return false;
3664
+ const target = scope.get(mem.object.name);
3665
+ const refCombi = isScopeEntry(target) ? target.combi : isCombinator(target) ? target : null;
3666
+ if (!refCombi || !isDefinableRef(refCombi)) return false;
3667
+ const args = callExpr.arguments;
3668
+ if (args.length !== 1 || args[0].type === "SpreadElement") return false;
3669
+ const inner = anyValue(args[0], scope, code, []);
3670
+ if (!isCombinator(inner)) return false;
3671
+ try {
3672
+ refCombi.define(inner);
3673
+ } catch {
3674
+ return false;
3675
+ }
3676
+ return true;
3677
+ }
3333
3678
  function referencesAny(node2, names, scope) {
3334
3679
  if (node2.type === "Identifier") {
3335
3680
  return names.has(node2.name) || scope.has(node2.name);
@@ -3404,6 +3749,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
3404
3749
  const replacements = [];
3405
3750
  const warnings = [];
3406
3751
  let anyUnresolved = false;
3752
+ let ruleMapHolderCounter = 0;
3407
3753
  const lineOf = (pos) => {
3408
3754
  let line = 1;
3409
3755
  for (let i = 0; i < pos && i < code.length; i++) if (code.charCodeAt(i) === 10) line++;
@@ -3425,18 +3771,53 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
3425
3771
  warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
3426
3772
  return null;
3427
3773
  }
3428
- const entries = [];
3429
- for (const [ruleKey, rule] of ruleMap) {
3430
- const compiled = compile(rule);
3431
- if (compiled.inlineExpression === null) {
3432
- warn(init.start, `${label2}: rule "${ruleKey}" couldn't be inlined`);
3433
- return null;
3434
- }
3435
- entries.push([ruleKey, compiled.inlineExpression]);
3774
+ const compiled = compileRuleMap([...ruleMap]);
3775
+ if (!compiled) {
3776
+ warn(init.start, `${label2}: rule map couldn't be inlined`);
3777
+ return null;
3436
3778
  }
3437
- return { entries, ruleMap };
3779
+ return { replacement: compiled.replacement, ruleMap };
3438
3780
  };
3439
3781
  const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
3782
+ const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
3783
+ const refNames = /* @__PURE__ */ new Set();
3784
+ for (const stmt of body) {
3785
+ const innerVd = unwrapVd(stmt);
3786
+ if (!innerVd) continue;
3787
+ for (const decl of innerVd.declarations) {
3788
+ const d = decl;
3789
+ if (!d.init || d.id.type !== "Identifier") continue;
3790
+ const init = d.init;
3791
+ if (init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "ref") {
3792
+ refNames.add(d.id.name);
3793
+ }
3794
+ }
3795
+ }
3796
+ const defineRemovals = [];
3797
+ if (refNames.size > 0) {
3798
+ for (const stmt of body) {
3799
+ if (stmt.type === "ExpressionStatement") {
3800
+ const expr = stmt.expression;
3801
+ if (applyDefineStatement(expr, scope, code)) {
3802
+ defineRemovals.push({ start: stmt.start, end: stmt.end });
3803
+ }
3804
+ continue;
3805
+ }
3806
+ const innerVd = unwrapVd(stmt);
3807
+ if (!innerVd) continue;
3808
+ for (const decl of innerVd.declarations) {
3809
+ const d = decl;
3810
+ if (!d.init || d.id.type !== "Identifier") continue;
3811
+ const name = d.id.name;
3812
+ const init = d.init;
3813
+ if (evaluateRefDeclaration(init, name, scope)) continue;
3814
+ if (scope.has(name)) continue;
3815
+ if (!referencesAny(init, allNames, scope)) continue;
3816
+ const combi = evaluateExpr(init, scope, code, []);
3817
+ if (combi) scope.set(name, { combi, mfSrcs: [] });
3818
+ }
3819
+ }
3820
+ }
3440
3821
  for (const stmt of body) {
3441
3822
  let vd = null;
3442
3823
  let stmtStart = stmt.start;
@@ -3460,15 +3841,26 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
3460
3841
  if (d.id.type === "Identifier") {
3461
3842
  const varName = d.id.name;
3462
3843
  if (!referencesAny(init, allNames, scope)) continue;
3844
+ if (refNames.has(varName)) {
3845
+ const refEntry = scope.get(varName);
3846
+ const refCombi = refEntry?.combi ?? null;
3847
+ if (refCombi) {
3848
+ const compiled2 = compile(refCombi);
3849
+ if (compiled2.inlineExpression === null) {
3850
+ warn(init.start, `"${varName}" is a ref() that couldn't be inlined (was .define() called with a static combinator?)`);
3851
+ continue;
3852
+ }
3853
+ replacements.push({ start: init.start, end: init.end, replacement: compiled2.inlineExpression });
3854
+ continue;
3855
+ }
3856
+ }
3463
3857
  if (isRulesCall(init)) {
3464
3858
  const compiledRules = compileRulesFactory(init, varName);
3465
3859
  if (!compiledRules) continue;
3466
3860
  replacements.push({
3467
3861
  start: init.start,
3468
3862
  end: init.end,
3469
- replacement: `{
3470
- ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
3471
- }`
3863
+ replacement: compiledRules.replacement
3472
3864
  });
3473
3865
  continue;
3474
3866
  }
@@ -3503,9 +3895,8 @@ ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
3503
3895
  }
3504
3896
  const compiledRules = compileRulesFactory(init, "{ \u2026 }");
3505
3897
  if (!compiledRules) continue;
3506
- const byKey = new Map(compiledRules.entries);
3507
3898
  const pattern = d.id;
3508
- const lines = [];
3899
+ const bindings = [];
3509
3900
  let allOk = true;
3510
3901
  for (const prop of pattern.properties) {
3511
3902
  const p = prop;
@@ -3520,17 +3911,21 @@ ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
3520
3911
  allOk = false;
3521
3912
  break;
3522
3913
  }
3523
- const inlineExpr = byKey.get(ruleKey);
3524
- if (inlineExpr === void 0) {
3914
+ if (!compiledRules.ruleMap.has(ruleKey)) {
3525
3915
  warn(init.start, `destructured rule "${ruleKey}" isn't returned by the rules() factory`);
3526
3916
  allOk = false;
3527
3917
  break;
3528
3918
  }
3529
- lines.push(`${exportPrefix}${kind} ${localName} = ${inlineExpr}`);
3919
+ bindings.push({ ruleKey, localName });
3530
3920
  const rule = compiledRules.ruleMap.get(ruleKey);
3531
3921
  if (rule) scope.set(localName, { combi: rule, mfSrcs: [] });
3532
3922
  }
3533
3923
  if (!allOk) continue;
3924
+ const holderVar = `__rules${ruleMapHolderCounter++}`;
3925
+ const lines = [
3926
+ `const ${holderVar} = ${compiledRules.replacement}`,
3927
+ ...bindings.map(({ ruleKey, localName }) => `${exportPrefix}${kind} ${localName} = ${holderVar}[${JSON.stringify(ruleKey)}]`)
3928
+ ];
3534
3929
  replacements.push({
3535
3930
  start: stmtStart,
3536
3931
  end: stmtEnd,
@@ -3544,6 +3939,9 @@ ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
3544
3939
  imp.fullyResolved = !anyUnresolved;
3545
3940
  }
3546
3941
  const ms = new MagicString(code);
3942
+ if (!anyUnresolved) {
3943
+ for (const { start, end } of defineRemovals) ms.remove(start, end);
3944
+ }
3547
3945
  for (const imp of macroImports) {
3548
3946
  if (imp.fullyResolved) {
3549
3947
  ms.remove(imp.start, imp.end);