parseman 0.8.0 → 0.9.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.
@@ -1234,14 +1234,140 @@ function noTrivia(root) {
1234
1234
  return parser({ trivia: null }, root);
1235
1235
  }
1236
1236
 
1237
+ // src/compiler/scannable-run.ts
1238
+ var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1239
+ var META = new Set("()[]{}*+?|^$.".split(""));
1240
+ function parseClassRanges(body) {
1241
+ const ranges = [];
1242
+ let i = 0;
1243
+ const readCp = () => {
1244
+ const ch = body[i];
1245
+ if (ch === void 0) return null;
1246
+ if (ch === "\\") {
1247
+ const e = body[i + 1];
1248
+ if (e === void 0) return null;
1249
+ i += 2;
1250
+ return e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0);
1251
+ }
1252
+ i += ch.length;
1253
+ return ch.codePointAt(0);
1254
+ };
1255
+ while (i < body.length) {
1256
+ const lo = readCp();
1257
+ if (lo === null) return null;
1258
+ if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
1259
+ i += 1;
1260
+ const hi = readCp();
1261
+ if (hi === null) return null;
1262
+ ranges.push([lo, hi]);
1263
+ } else {
1264
+ ranges.push([lo, lo]);
1265
+ }
1266
+ }
1267
+ return ranges.length ? ranges : null;
1268
+ }
1269
+ function literalCodePoints(frag) {
1270
+ const out = [];
1271
+ let i = 0;
1272
+ while (i < frag.length) {
1273
+ const ch = frag[i];
1274
+ if (ch === "\\") {
1275
+ const e = frag[i + 1];
1276
+ if (e === void 0) return null;
1277
+ out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
1278
+ i += 2;
1279
+ continue;
1280
+ }
1281
+ if (META.has(ch)) return null;
1282
+ out.push(ch.codePointAt(0));
1283
+ i += 1;
1284
+ }
1285
+ return out.length ? out : null;
1286
+ }
1287
+ function parseScanShape(source) {
1288
+ let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
1289
+ if (m) {
1290
+ if (m[1].startsWith("^")) return null;
1291
+ const ranges = parseClassRanges(m[1]);
1292
+ return ranges ? { kind: "chars", ranges } : null;
1293
+ }
1294
+ m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
1295
+ if (m) {
1296
+ const open = literalCodePoints(m[1]);
1297
+ const stop = parseClassRanges(m[2]);
1298
+ if (open && stop) return { kind: "until", open, stop };
1299
+ return null;
1300
+ }
1301
+ if (!source.includes("\\\\")) {
1302
+ m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
1303
+ if (m && m[1] && m[2]) {
1304
+ const open = literalCodePoints(m[1]);
1305
+ const close = literalCodePoints(m[2]);
1306
+ if (open && close) return { kind: "delimited", open, close };
1307
+ }
1308
+ }
1309
+ return null;
1310
+ }
1311
+ var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
1312
+ var litCond = (base, cps, firstVar) => cps.map(
1313
+ (cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
1314
+ ).join(" && ");
1315
+ function advanceExpr(shape) {
1316
+ if (shape.kind === "chars") {
1317
+ return {
1318
+ setup: [
1319
+ ` _e++`,
1320
+ ` while (_e < input.length) { const c2 = input.charCodeAt(_e); if (${classCond("c2", shape.ranges)}) _e++; else break }`
1321
+ ],
1322
+ endVar: "_e"
1323
+ };
1324
+ }
1325
+ if (shape.kind === "until") {
1326
+ return {
1327
+ setup: [
1328
+ ` let j = _e + ${shape.open.length}`,
1329
+ ` while (j < input.length && !(${classCond("input.charCodeAt(j)", shape.stop)})) j++`
1330
+ ],
1331
+ endVar: "j"
1332
+ };
1333
+ }
1334
+ const n = shape.close.length;
1335
+ return {
1336
+ setup: [
1337
+ ` let j = _e + ${shape.open.length}`,
1338
+ ` while (j + ${n - 1} < input.length && !(${litCond("j", shape.close)})) j++`
1339
+ ],
1340
+ endVar: n === 1 ? "(j + 1 <= input.length ? j + 1 : input.length)" : `(j + ${n} <= input.length ? j + ${n} : input.length)`
1341
+ };
1342
+ }
1343
+ function guardLine(shape) {
1344
+ const cond = shape.kind === "chars" ? classCond("c", shape.ranges) : litCond("_e", shape.open, "c");
1345
+ return ` if (${cond}) {`;
1346
+ }
1347
+ function scanBranch(shape) {
1348
+ if (shape.kind === "chars") {
1349
+ return ` if (${classCond("c", shape.ranges)}) { _e++; continue }`;
1350
+ }
1351
+ const { setup, endVar } = advanceExpr(shape);
1352
+ return [guardLine(shape), ...setup, ` _e = ${endVar}`, ` continue`, ` }`].join("\n");
1353
+ }
1354
+ function captureChunk(startVar, kindIndex) {
1355
+ return [
1356
+ ` if (_cap) {`,
1357
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(${startVar}, _e, ${kindIndex})`,
1358
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(${startVar}, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
1359
+ ` }`
1360
+ ];
1361
+ }
1362
+ function scanBranchLabeled(shape, kindIndex) {
1363
+ const { setup, endVar } = advanceExpr(shape);
1364
+ const lines = [guardLine(shape), ` const _cs = _e`, ...setup];
1365
+ if (endVar !== "_e") lines.push(` _e = ${endVar}`);
1366
+ lines.push(...captureChunk("_cs", kindIndex), ` continue`, ` }`);
1367
+ return lines.join("\n");
1368
+ }
1369
+
1237
1370
  // 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
1371
  function unwrapTrivia2(p) {
1246
1372
  let cur = p;
1247
1373
  while (cur._def.tag === "trivia") cur = cur._def.parser;
@@ -1257,36 +1383,30 @@ function choiceArms(p) {
1257
1383
  if (p._def.tag === "choice") return p._def.parsers;
1258
1384
  return null;
1259
1385
  }
1260
- function isWsClassSource(source) {
1261
- return WS_CLASS_SOURCES.has(source);
1262
- }
1263
- function isBlockCommentSource(source) {
1264
- return source === BLOCK_COMMENT;
1386
+ function armShape(arm) {
1387
+ const src = getCoreRegexDef(arm)?.source;
1388
+ return src ? parseScanShape(src) : null;
1265
1389
  }
1266
1390
  function analyzeTriviaFastPath(trivia2) {
1267
1391
  const core = unwrapTrivia2(trivia2);
1268
- const direct = getCoreRegexDef(core)?.source;
1269
- if (direct) {
1270
- if (isWsClassSource(direct)) return "wsOnly";
1271
- return null;
1392
+ const directSrc = getCoreRegexDef(core)?.source;
1393
+ if (directSrc) {
1394
+ const shape = parseScanShape(directSrc);
1395
+ return shape && shape.kind === "chars" ? [shape] : null;
1272
1396
  }
1273
1397
  const inner = oneOrMoreInner(core);
1274
1398
  if (!inner) return null;
1275
- const innerSrc = getCoreRegexDef(inner)?.source;
1276
- if (innerSrc && isWsClassSource(innerSrc)) return "wsOnly";
1399
+ const innerShape = armShape(inner);
1400
+ if (innerShape) return innerShape.kind === "chars" ? [innerShape] : null;
1277
1401
  const arms = choiceArms(inner);
1278
- if (!arms || arms.length !== 2) return null;
1279
- let hasWs = false;
1280
- let hasComment = false;
1402
+ if (!arms || arms.length < 2) return null;
1403
+ const shapes = [];
1281
1404
  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;
1405
+ const shape = armShape(arm);
1406
+ if (!shape) return null;
1407
+ shapes.push(shape);
1287
1408
  }
1288
- if (hasWs && hasComment) return "wsComments";
1289
- return null;
1409
+ return shapes;
1290
1410
  }
1291
1411
  var CAP_RECORD = [
1292
1412
  ` if (_cap && _e > _pos) {`,
@@ -1294,80 +1414,49 @@ var CAP_RECORD = [
1294
1414
  ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
1295
1415
  ` }`
1296
1416
  ].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 = [
1417
+ function composeFastLoop(shapes) {
1418
+ return [
1419
+ ` while (_e < input.length) {`,
1420
+ ` const c = input.charCodeAt(_e)`,
1421
+ ...shapes.map(scanBranch),
1422
+ ` break`,
1423
+ ` }`
1424
+ ].join("\n");
1425
+ }
1426
+ function buildFastTriviaFnDecl(fnName, shapes) {
1427
+ return [
1355
1428
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
1356
1429
  ` let _e = _pos`,
1357
- loop
1358
- ];
1359
- if (cap) lines.push(cap);
1360
- lines.push(` return _e`, `}`);
1361
- return lines.join("\n");
1430
+ composeFastLoop(shapes),
1431
+ CAP_RECORD,
1432
+ ` return _e`,
1433
+ `}`
1434
+ ].join("\n");
1362
1435
  }
1363
- function regexArmIndex(spec, isComment) {
1436
+ function analyzeLabeledScannableRun(trivia2) {
1437
+ const spec = analyzeLabeledTrivia(trivia2);
1438
+ if (!spec) return null;
1439
+ const out = [];
1364
1440
  for (const arm of spec.arms) {
1365
1441
  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;
1442
+ const shape = src ? parseScanShape(src) : null;
1443
+ if (!shape) return null;
1444
+ out.push({ shape, kindIndex: arm.kindIndex });
1369
1445
  }
1370
- return null;
1446
+ return out;
1447
+ }
1448
+ function buildLabeledScannableTriviaFnDecl(fnName, arms) {
1449
+ return [
1450
+ `function ${fnName}(input, _pos, _ctx, _cap) {`,
1451
+ ` let _e = _pos`,
1452
+ ` while (_e < input.length) {`,
1453
+ ` const c = input.charCodeAt(_e)`,
1454
+ ...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
1455
+ ` break`,
1456
+ ` }`,
1457
+ ` return _e`,
1458
+ `}`
1459
+ ].join("\n");
1371
1460
  }
1372
1461
  function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
1373
1462
  const tryArms = spec.arms.map((arm, i) => {
@@ -1400,14 +1489,6 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
1400
1489
  `}`
1401
1490
  ].join("\n");
1402
1491
  }
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
1492
  function labeledTriviaRegexArms(trivia2) {
1412
1493
  const spec = analyzeLabeledTrivia(trivia2);
1413
1494
  if (!spec) return null;
@@ -1655,14 +1736,19 @@ function ensureTriviaFn(ctx) {
1655
1736
  const fnName = `_tf${ctx.triviaFnNames.size}`;
1656
1737
  ctx.triviaFnNames.set(trivia2, fnName);
1657
1738
  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
1739
  const labeledSpec = analyzeLabeledTrivia(trivia2);
1665
- if (labeledSpec) {
1740
+ if (!labeledSpec) {
1741
+ const fastShapes = analyzeTriviaFastPath(trivia2);
1742
+ if (fastShapes) {
1743
+ ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastShapes));
1744
+ return fnName;
1745
+ }
1746
+ } else {
1747
+ const labeledShapes = analyzeLabeledScannableRun(trivia2);
1748
+ if (labeledShapes) {
1749
+ ctx.namedFnDecls.push(buildLabeledScannableTriviaFnDecl(fnName, labeledShapes));
1750
+ return fnName;
1751
+ }
1666
1752
  const regexSpec = labeledTriviaRegexArms(trivia2);
1667
1753
  if (regexSpec) {
1668
1754
  const reNames = [];
@@ -2100,11 +2186,11 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
2100
2186
  const p = def.parsers[idx];
2101
2187
  const litVal = getCoreLiteralValue(p);
2102
2188
  const litLen = litVal.length;
2103
- const litCond = emitLiteralCondition(litVal, pos);
2189
+ const litCond2 = emitLiteralCondition(litVal, pos);
2104
2190
  const kw = first ? "if" : "else if";
2105
2191
  first = false;
2106
2192
  const litEnd = `${pos} + ${litLen}`;
2107
- stmts.push(`${ind(ctx)}${kw} (${litCond}) {`);
2193
+ stmts.push(`${ind(ctx)}${kw} (${litCond2}) {`);
2108
2194
  ctx.indent++;
2109
2195
  const tR = emitTransformChain(p, JSON.stringify(litVal), litEnd, pos, ctx);
2110
2196
  stmts.push(
@@ -2472,6 +2558,16 @@ function emitRuntimeFallback(parser2, ctx, pos) {
2472
2558
  return { stmts, valueVar: vv, endVar: ev };
2473
2559
  }
2474
2560
  function emitLazy(p, def, ctx, pos) {
2561
+ const usage = ctx.lazyUsage;
2562
+ if (usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
2563
+ let resolved;
2564
+ try {
2565
+ resolved = def.thunk();
2566
+ } catch {
2567
+ return emitRuntimeFallback(p, ctx, pos);
2568
+ }
2569
+ return emit(resolved, ctx, pos);
2570
+ }
2475
2571
  if (!ctx.namedParsers.has(p)) {
2476
2572
  const fnName2 = `_pf${ctx.namedParsers.size}`;
2477
2573
  ctx.namedParsers.set(p, fnName2);
@@ -2736,6 +2832,80 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
2736
2832
  return false;
2737
2833
  }
2738
2834
  }
2835
+ function childrenOf(def) {
2836
+ switch (def.tag) {
2837
+ case "sequence":
2838
+ case "choice":
2839
+ return def.parsers;
2840
+ case "many":
2841
+ case "optional":
2842
+ case "transform":
2843
+ case "trivia":
2844
+ case "label":
2845
+ case "grammar":
2846
+ case "not":
2847
+ case "node":
2848
+ case "withCtx":
2849
+ case "expect":
2850
+ return [def.parser];
2851
+ // emitMany's oneOrMore branch and emitSepBy both codegen `def.parser` TWICE
2852
+ // (a mandatory first match, then again inside the repeat loop) — two real
2853
+ // emit() call sites, so the usage analysis must see two edges here or it
2854
+ // undercounts a single-use `parser` ref as inline-safe when it's actually
2855
+ // referenced from two positions within this one compiled function.
2856
+ case "oneOrMore":
2857
+ return [def.parser, def.parser];
2858
+ case "sepBy":
2859
+ return [def.parser, def.parser, def.separator];
2860
+ case "skip":
2861
+ return [def.main, def.skipped];
2862
+ case "recover":
2863
+ return [def.parser, def.sentinel];
2864
+ case "scanTo":
2865
+ return [def.sentinel, ...def.skip];
2866
+ case "lazy":
2867
+ case "literal":
2868
+ case "regex":
2869
+ case "keywords":
2870
+ case "guard":
2871
+ case "unknown":
2872
+ return [];
2873
+ }
2874
+ }
2875
+ function analyzeLazyUsage(root) {
2876
+ return analyzeLazyUsageMulti([root]);
2877
+ }
2878
+ function analyzeLazyUsageMulti(roots) {
2879
+ const counts = /* @__PURE__ */ new Map();
2880
+ const recursive = /* @__PURE__ */ new Set();
2881
+ const descended = /* @__PURE__ */ new Set();
2882
+ const active = /* @__PURE__ */ new Set();
2883
+ function walk(p) {
2884
+ const def = p._def;
2885
+ if (def.tag === "lazy") {
2886
+ counts.set(p, (counts.get(p) ?? 0) + 1);
2887
+ if (active.has(p)) {
2888
+ recursive.add(p);
2889
+ return;
2890
+ }
2891
+ if (descended.has(p)) return;
2892
+ descended.add(p);
2893
+ let resolved;
2894
+ try {
2895
+ resolved = def.thunk();
2896
+ } catch {
2897
+ return;
2898
+ }
2899
+ active.add(p);
2900
+ walk(resolved);
2901
+ active.delete(p);
2902
+ return;
2903
+ }
2904
+ for (const child of childrenOf(def)) walk(child);
2905
+ }
2906
+ for (const root of roots) walk(root);
2907
+ return { counts, recursive };
2908
+ }
2739
2909
  function compile(combinator, mapFnSources) {
2740
2910
  const ctx = {
2741
2911
  vars: 0,
@@ -2752,7 +2922,8 @@ function compile(combinator, mapFnSources) {
2752
2922
  triviaCaptureNames: /* @__PURE__ */ new Map(),
2753
2923
  triviaFnNames: /* @__PURE__ */ new Map(),
2754
2924
  namedFnDecls: [],
2755
- capturing: hasNodeDef(combinator)
2925
+ capturing: hasNodeDef(combinator),
2926
+ lazyUsage: analyzeLazyUsage(combinator)
2756
2927
  };
2757
2928
  const r = emit(combinator, ctx, "_pos");
2758
2929
  const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
@@ -2810,6 +2981,66 @@ function compile(combinator, mapFnSources) {
2810
2981
  }
2811
2982
  };
2812
2983
  }
2984
+ function compileRuleMap(ruleMap) {
2985
+ const ctx = {
2986
+ vars: 0,
2987
+ indent: 1,
2988
+ regexDecls: [],
2989
+ regexMap: /* @__PURE__ */ new Map(),
2990
+ mapFns: [],
2991
+ mapFnSrcs: [],
2992
+ buildFns: [],
2993
+ buildSrcs: [],
2994
+ runtimeParsers: [],
2995
+ needsCollator: false,
2996
+ namedParsers: /* @__PURE__ */ new Map(),
2997
+ triviaCaptureNames: /* @__PURE__ */ new Map(),
2998
+ triviaFnNames: /* @__PURE__ */ new Map(),
2999
+ namedFnDecls: [],
3000
+ capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
3001
+ lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule))
3002
+ };
3003
+ const perEntry = ruleMap.map(([key, rule]) => ({ key, r: emit(rule, ctx, "_pos") }));
3004
+ const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
3005
+ ` : "";
3006
+ const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
3007
+ const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
3008
+ const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
3009
+ const mfCovered = ctx.mapFns.length === 0 || derivedSrcs !== void 0;
3010
+ const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
3011
+ if (!canInline) return null;
3012
+ const mfDecl = derivedSrcs?.length ? ` const _mf = [${derivedSrcs.join(", ")}]` : "";
3013
+ const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
3014
+ const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
3015
+ const hoistedDecls = [
3016
+ ...ctx.regexDecls.map((d) => ` ${d}`),
3017
+ collatorDecl ? ` ${collatorDecl.trim()}` : "",
3018
+ mfDecl,
3019
+ buildDecl,
3020
+ ...namedPrelude.map((l) => ` ${l}`),
3021
+ ...ctx.namedFnDecls.flatMap((decl, i) => {
3022
+ const lines = decl.split("\n").map((l) => ` ${l}`);
3023
+ return i > 0 ? ["", ...lines] : lines;
3024
+ })
3025
+ ].filter(Boolean);
3026
+ const entryFnText = (r) => [
3027
+ `function(input, _pos, _ctx) {`,
3028
+ ` let pos = _pos`,
3029
+ ...r.stmts,
3030
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
3031
+ `}`
3032
+ ].join("\n");
3033
+ const objBody = perEntry.map(({ key, r }) => ` ${JSON.stringify(key)}: ${entryFnText(r).split("\n").join("\n ")}`).join(",\n");
3034
+ const replacement = [
3035
+ `/* @__PURE__ */ (() => {`,
3036
+ ...hoistedDecls,
3037
+ ` return {`,
3038
+ objBody,
3039
+ ` }`,
3040
+ `})()`
3041
+ ].join("\n");
3042
+ return { keys: perEntry.map((e) => e.key), replacement };
3043
+ }
2813
3044
  function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources) {
2814
3045
  const bodyLines = [
2815
3046
  ` let pos = _pos`,
@@ -3091,6 +3322,10 @@ function exprToCombi(node2, scope, code, mfs) {
3091
3322
  }
3092
3323
  }
3093
3324
  if (callee.name === "rules") return null;
3325
+ if (callee.name === "ref") {
3326
+ if (node2.arguments.length !== 0) return null;
3327
+ return ref();
3328
+ }
3094
3329
  if (callee.name === "parser") {
3095
3330
  const [optsArg, rootArg] = node2.arguments;
3096
3331
  if (!optsArg || !rootArg || optsArg.type === "SpreadElement" || rootArg.type === "SpreadElement") return null;
@@ -3330,6 +3565,40 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
3330
3565
  }
3331
3566
  return ruleRefs;
3332
3567
  }
3568
+ function isDefinableRef(v2) {
3569
+ return isCombinator(v2) && typeof v2.define === "function" && v2._def.tag === "lazy";
3570
+ }
3571
+ function evaluateRefDeclaration(init, name, scope) {
3572
+ if (init.type !== "CallExpression") return null;
3573
+ const callee = init.callee;
3574
+ if (callee.type !== "Identifier" || callee.name !== "ref") return null;
3575
+ if (init.arguments.length !== 0) return null;
3576
+ const slot = ref();
3577
+ scope.set(name, { combi: slot, mfSrcs: [] });
3578
+ return slot;
3579
+ }
3580
+ function applyDefineStatement(callExpr, scope, code) {
3581
+ if (callExpr.type !== "CallExpression") return false;
3582
+ const callee = callExpr.callee;
3583
+ if (callee.type !== "MemberExpression") return false;
3584
+ const mem = callee;
3585
+ if (mem.computed) return false;
3586
+ if (mem.property.type !== "Identifier" || mem.property.name !== "define") return false;
3587
+ if (mem.object.type !== "Identifier" || !mem.object.name) return false;
3588
+ const target = scope.get(mem.object.name);
3589
+ const refCombi = isScopeEntry(target) ? target.combi : isCombinator(target) ? target : null;
3590
+ if (!refCombi || !isDefinableRef(refCombi)) return false;
3591
+ const args = callExpr.arguments;
3592
+ if (args.length !== 1 || args[0].type === "SpreadElement") return false;
3593
+ const inner = anyValue(args[0], scope, code, []);
3594
+ if (!isCombinator(inner)) return false;
3595
+ try {
3596
+ refCombi.define(inner);
3597
+ } catch {
3598
+ return false;
3599
+ }
3600
+ return true;
3601
+ }
3333
3602
  function referencesAny(node2, names, scope) {
3334
3603
  if (node2.type === "Identifier") {
3335
3604
  return names.has(node2.name) || scope.has(node2.name);
@@ -3404,6 +3673,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
3404
3673
  const replacements = [];
3405
3674
  const warnings = [];
3406
3675
  let anyUnresolved = false;
3676
+ let ruleMapHolderCounter = 0;
3407
3677
  const lineOf = (pos) => {
3408
3678
  let line = 1;
3409
3679
  for (let i = 0; i < pos && i < code.length; i++) if (code.charCodeAt(i) === 10) line++;
@@ -3425,18 +3695,53 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
3425
3695
  warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
3426
3696
  return null;
3427
3697
  }
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]);
3698
+ const compiled = compileRuleMap([...ruleMap]);
3699
+ if (!compiled) {
3700
+ warn(init.start, `${label2}: rule map couldn't be inlined`);
3701
+ return null;
3436
3702
  }
3437
- return { entries, ruleMap };
3703
+ return { replacement: compiled.replacement, ruleMap };
3438
3704
  };
3439
3705
  const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
3706
+ const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
3707
+ const refNames = /* @__PURE__ */ new Set();
3708
+ for (const stmt of body) {
3709
+ const innerVd = unwrapVd(stmt);
3710
+ if (!innerVd) continue;
3711
+ for (const decl of innerVd.declarations) {
3712
+ const d = decl;
3713
+ if (!d.init || d.id.type !== "Identifier") continue;
3714
+ const init = d.init;
3715
+ if (init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "ref") {
3716
+ refNames.add(d.id.name);
3717
+ }
3718
+ }
3719
+ }
3720
+ const defineRemovals = [];
3721
+ if (refNames.size > 0) {
3722
+ for (const stmt of body) {
3723
+ if (stmt.type === "ExpressionStatement") {
3724
+ const expr = stmt.expression;
3725
+ if (applyDefineStatement(expr, scope, code)) {
3726
+ defineRemovals.push({ start: stmt.start, end: stmt.end });
3727
+ }
3728
+ continue;
3729
+ }
3730
+ const innerVd = unwrapVd(stmt);
3731
+ if (!innerVd) continue;
3732
+ for (const decl of innerVd.declarations) {
3733
+ const d = decl;
3734
+ if (!d.init || d.id.type !== "Identifier") continue;
3735
+ const name = d.id.name;
3736
+ const init = d.init;
3737
+ if (evaluateRefDeclaration(init, name, scope)) continue;
3738
+ if (scope.has(name)) continue;
3739
+ if (!referencesAny(init, allNames, scope)) continue;
3740
+ const combi = evaluateExpr(init, scope, code, []);
3741
+ if (combi) scope.set(name, { combi, mfSrcs: [] });
3742
+ }
3743
+ }
3744
+ }
3440
3745
  for (const stmt of body) {
3441
3746
  let vd = null;
3442
3747
  let stmtStart = stmt.start;
@@ -3460,15 +3765,26 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
3460
3765
  if (d.id.type === "Identifier") {
3461
3766
  const varName = d.id.name;
3462
3767
  if (!referencesAny(init, allNames, scope)) continue;
3768
+ if (refNames.has(varName)) {
3769
+ const refEntry = scope.get(varName);
3770
+ const refCombi = refEntry?.combi ?? null;
3771
+ if (refCombi) {
3772
+ const compiled2 = compile(refCombi);
3773
+ if (compiled2.inlineExpression === null) {
3774
+ warn(init.start, `"${varName}" is a ref() that couldn't be inlined (was .define() called with a static combinator?)`);
3775
+ continue;
3776
+ }
3777
+ replacements.push({ start: init.start, end: init.end, replacement: compiled2.inlineExpression });
3778
+ continue;
3779
+ }
3780
+ }
3463
3781
  if (isRulesCall(init)) {
3464
3782
  const compiledRules = compileRulesFactory(init, varName);
3465
3783
  if (!compiledRules) continue;
3466
3784
  replacements.push({
3467
3785
  start: init.start,
3468
3786
  end: init.end,
3469
- replacement: `{
3470
- ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
3471
- }`
3787
+ replacement: compiledRules.replacement
3472
3788
  });
3473
3789
  continue;
3474
3790
  }
@@ -3503,9 +3819,8 @@ ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
3503
3819
  }
3504
3820
  const compiledRules = compileRulesFactory(init, "{ \u2026 }");
3505
3821
  if (!compiledRules) continue;
3506
- const byKey = new Map(compiledRules.entries);
3507
3822
  const pattern = d.id;
3508
- const lines = [];
3823
+ const bindings = [];
3509
3824
  let allOk = true;
3510
3825
  for (const prop of pattern.properties) {
3511
3826
  const p = prop;
@@ -3520,17 +3835,21 @@ ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
3520
3835
  allOk = false;
3521
3836
  break;
3522
3837
  }
3523
- const inlineExpr = byKey.get(ruleKey);
3524
- if (inlineExpr === void 0) {
3838
+ if (!compiledRules.ruleMap.has(ruleKey)) {
3525
3839
  warn(init.start, `destructured rule "${ruleKey}" isn't returned by the rules() factory`);
3526
3840
  allOk = false;
3527
3841
  break;
3528
3842
  }
3529
- lines.push(`${exportPrefix}${kind} ${localName} = ${inlineExpr}`);
3843
+ bindings.push({ ruleKey, localName });
3530
3844
  const rule = compiledRules.ruleMap.get(ruleKey);
3531
3845
  if (rule) scope.set(localName, { combi: rule, mfSrcs: [] });
3532
3846
  }
3533
3847
  if (!allOk) continue;
3848
+ const holderVar = `__rules${ruleMapHolderCounter++}`;
3849
+ const lines = [
3850
+ `const ${holderVar} = ${compiledRules.replacement}`,
3851
+ ...bindings.map(({ ruleKey, localName }) => `${exportPrefix}${kind} ${localName} = ${holderVar}[${JSON.stringify(ruleKey)}]`)
3852
+ ];
3534
3853
  replacements.push({
3535
3854
  start: stmtStart,
3536
3855
  end: stmtEnd,
@@ -3544,6 +3863,9 @@ ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
3544
3863
  imp.fullyResolved = !anyUnresolved;
3545
3864
  }
3546
3865
  const ms = new MagicString(code);
3866
+ if (!anyUnresolved) {
3867
+ for (const { start, end } of defineRemovals) ms.remove(start, end);
3868
+ }
3547
3869
  for (const imp of macroImports) {
3548
3870
  if (imp.fullyResolved) {
3549
3871
  ms.remove(imp.start, imp.end);