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.
@@ -1269,14 +1269,140 @@ function noTrivia(root) {
1269
1269
  return parser({ trivia: null }, root);
1270
1270
  }
1271
1271
 
1272
+ // src/compiler/scannable-run.ts
1273
+ var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1274
+ var META = new Set("()[]{}*+?|^$.".split(""));
1275
+ function parseClassRanges(body) {
1276
+ const ranges = [];
1277
+ let i = 0;
1278
+ const readCp = () => {
1279
+ const ch = body[i];
1280
+ if (ch === void 0) return null;
1281
+ if (ch === "\\") {
1282
+ const e = body[i + 1];
1283
+ if (e === void 0) return null;
1284
+ i += 2;
1285
+ return e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0);
1286
+ }
1287
+ i += ch.length;
1288
+ return ch.codePointAt(0);
1289
+ };
1290
+ while (i < body.length) {
1291
+ const lo = readCp();
1292
+ if (lo === null) return null;
1293
+ if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
1294
+ i += 1;
1295
+ const hi = readCp();
1296
+ if (hi === null) return null;
1297
+ ranges.push([lo, hi]);
1298
+ } else {
1299
+ ranges.push([lo, lo]);
1300
+ }
1301
+ }
1302
+ return ranges.length ? ranges : null;
1303
+ }
1304
+ function literalCodePoints(frag) {
1305
+ const out = [];
1306
+ let i = 0;
1307
+ while (i < frag.length) {
1308
+ const ch = frag[i];
1309
+ if (ch === "\\") {
1310
+ const e = frag[i + 1];
1311
+ if (e === void 0) return null;
1312
+ out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
1313
+ i += 2;
1314
+ continue;
1315
+ }
1316
+ if (META.has(ch)) return null;
1317
+ out.push(ch.codePointAt(0));
1318
+ i += 1;
1319
+ }
1320
+ return out.length ? out : null;
1321
+ }
1322
+ function parseScanShape(source) {
1323
+ let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
1324
+ if (m) {
1325
+ if (m[1].startsWith("^")) return null;
1326
+ const ranges = parseClassRanges(m[1]);
1327
+ return ranges ? { kind: "chars", ranges } : null;
1328
+ }
1329
+ m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
1330
+ if (m) {
1331
+ const open = literalCodePoints(m[1]);
1332
+ const stop = parseClassRanges(m[2]);
1333
+ if (open && stop) return { kind: "until", open, stop };
1334
+ return null;
1335
+ }
1336
+ if (!source.includes("\\\\")) {
1337
+ m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
1338
+ if (m && m[1] && m[2]) {
1339
+ const open = literalCodePoints(m[1]);
1340
+ const close = literalCodePoints(m[2]);
1341
+ if (open && close) return { kind: "delimited", open, close };
1342
+ }
1343
+ }
1344
+ return null;
1345
+ }
1346
+ var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
1347
+ var litCond = (base, cps, firstVar) => cps.map(
1348
+ (cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
1349
+ ).join(" && ");
1350
+ function advanceExpr(shape) {
1351
+ if (shape.kind === "chars") {
1352
+ return {
1353
+ setup: [
1354
+ ` _e++`,
1355
+ ` while (_e < input.length) { const c2 = input.charCodeAt(_e); if (${classCond("c2", shape.ranges)}) _e++; else break }`
1356
+ ],
1357
+ endVar: "_e"
1358
+ };
1359
+ }
1360
+ if (shape.kind === "until") {
1361
+ return {
1362
+ setup: [
1363
+ ` let j = _e + ${shape.open.length}`,
1364
+ ` while (j < input.length && !(${classCond("input.charCodeAt(j)", shape.stop)})) j++`
1365
+ ],
1366
+ endVar: "j"
1367
+ };
1368
+ }
1369
+ const n = shape.close.length;
1370
+ return {
1371
+ setup: [
1372
+ ` let j = _e + ${shape.open.length}`,
1373
+ ` while (j + ${n - 1} < input.length && !(${litCond("j", shape.close)})) j++`
1374
+ ],
1375
+ endVar: n === 1 ? "(j + 1 <= input.length ? j + 1 : input.length)" : `(j + ${n} <= input.length ? j + ${n} : input.length)`
1376
+ };
1377
+ }
1378
+ function guardLine(shape) {
1379
+ const cond = shape.kind === "chars" ? classCond("c", shape.ranges) : litCond("_e", shape.open, "c");
1380
+ return ` if (${cond}) {`;
1381
+ }
1382
+ function scanBranch(shape) {
1383
+ if (shape.kind === "chars") {
1384
+ return ` if (${classCond("c", shape.ranges)}) { _e++; continue }`;
1385
+ }
1386
+ const { setup, endVar } = advanceExpr(shape);
1387
+ return [guardLine(shape), ...setup, ` _e = ${endVar}`, ` continue`, ` }`].join("\n");
1388
+ }
1389
+ function captureChunk(startVar, kindIndex) {
1390
+ return [
1391
+ ` if (_cap) {`,
1392
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(${startVar}, _e, ${kindIndex})`,
1393
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(${startVar}, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
1394
+ ` }`
1395
+ ];
1396
+ }
1397
+ function scanBranchLabeled(shape, kindIndex) {
1398
+ const { setup, endVar } = advanceExpr(shape);
1399
+ const lines = [guardLine(shape), ` const _cs = _e`, ...setup];
1400
+ if (endVar !== "_e") lines.push(` _e = ${endVar}`);
1401
+ lines.push(...captureChunk("_cs", kindIndex), ` continue`, ` }`);
1402
+ return lines.join("\n");
1403
+ }
1404
+
1272
1405
  // src/compiler/trivia-fast-path.ts
1273
- var BLOCK_COMMENT = String.raw`\/\*(?:[^*]|\*(?!\/))*\*\/`;
1274
- var WS_CLASS_SOURCES = /* @__PURE__ */ new Set([
1275
- String.raw`[ \t\n\r\f]+`,
1276
- String.raw`[ \t\n]+`,
1277
- String.raw`[ \t]+`,
1278
- String.raw`[\t\n\f\r ]+`
1279
- ]);
1280
1406
  function unwrapTrivia2(p) {
1281
1407
  let cur = p;
1282
1408
  while (cur._def.tag === "trivia") cur = cur._def.parser;
@@ -1292,36 +1418,30 @@ function choiceArms(p) {
1292
1418
  if (p._def.tag === "choice") return p._def.parsers;
1293
1419
  return null;
1294
1420
  }
1295
- function isWsClassSource(source) {
1296
- return WS_CLASS_SOURCES.has(source);
1297
- }
1298
- function isBlockCommentSource(source) {
1299
- return source === BLOCK_COMMENT;
1421
+ function armShape(arm) {
1422
+ const src = getCoreRegexDef(arm)?.source;
1423
+ return src ? parseScanShape(src) : null;
1300
1424
  }
1301
1425
  function analyzeTriviaFastPath(trivia2) {
1302
1426
  const core = unwrapTrivia2(trivia2);
1303
- const direct = getCoreRegexDef(core)?.source;
1304
- if (direct) {
1305
- if (isWsClassSource(direct)) return "wsOnly";
1306
- return null;
1427
+ const directSrc = getCoreRegexDef(core)?.source;
1428
+ if (directSrc) {
1429
+ const shape = parseScanShape(directSrc);
1430
+ return shape && shape.kind === "chars" ? [shape] : null;
1307
1431
  }
1308
1432
  const inner = oneOrMoreInner(core);
1309
1433
  if (!inner) return null;
1310
- const innerSrc = getCoreRegexDef(inner)?.source;
1311
- if (innerSrc && isWsClassSource(innerSrc)) return "wsOnly";
1434
+ const innerShape = armShape(inner);
1435
+ if (innerShape) return innerShape.kind === "chars" ? [innerShape] : null;
1312
1436
  const arms = choiceArms(inner);
1313
- if (!arms || arms.length !== 2) return null;
1314
- let hasWs = false;
1315
- let hasComment = false;
1437
+ if (!arms || arms.length < 2) return null;
1438
+ const shapes = [];
1316
1439
  for (const arm of arms) {
1317
- const src = getCoreRegexDef(arm)?.source;
1318
- if (!src) return null;
1319
- if (isWsClassSource(src)) hasWs = true;
1320
- else if (isBlockCommentSource(src)) hasComment = true;
1321
- else return null;
1440
+ const shape = armShape(arm);
1441
+ if (!shape) return null;
1442
+ shapes.push(shape);
1322
1443
  }
1323
- if (hasWs && hasComment) return "wsComments";
1324
- return null;
1444
+ return shapes;
1325
1445
  }
1326
1446
  var CAP_RECORD = [
1327
1447
  ` if (_cap && _e > _pos) {`,
@@ -1329,80 +1449,49 @@ var CAP_RECORD = [
1329
1449
  ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
1330
1450
  ` }`
1331
1451
  ].join("\n");
1332
- var WS_ONLY_LOOP = [
1333
- ` while (_e < input.length) {`,
1334
- ` const c = input.charCodeAt(_e)`,
1335
- ` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) { _e++; continue }`,
1336
- ` break`,
1337
- ` }`
1338
- ].join("\n");
1339
- var WS_COMMENTS_LOOP = [
1340
- ` while (_e < input.length) {`,
1341
- ` const c = input.charCodeAt(_e)`,
1342
- ` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) { _e++; continue }`,
1343
- ` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
1344
- ` let j = _e + 2`,
1345
- ` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
1346
- ` _e = j + 2 <= input.length ? j + 2 : input.length`,
1347
- ` continue`,
1348
- ` }`,
1349
- ` break`,
1350
- ` }`
1351
- ].join("\n");
1352
- var CAP_CHUNK = (wsKind, commentKind) => [
1353
- ` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {`,
1354
- ` const _cs = _e`,
1355
- ` _e++`,
1356
- ` while (_e < input.length) {`,
1357
- ` const c2 = input.charCodeAt(_e)`,
1358
- ` if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) _e++`,
1359
- ` else break`,
1360
- ` }`,
1361
- ` if (_cap) {`,
1362
- ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${wsKind})`,
1363
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${wsKind})`,
1364
- ` }`,
1365
- ` continue`,
1366
- ` }`,
1367
- ` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
1368
- ` const _cs = _e`,
1369
- ` let j = _e + 2`,
1370
- ` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
1371
- ` _e = j + 2 <= input.length ? j + 2 : input.length`,
1372
- ` if (_cap) {`,
1373
- ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${commentKind})`,
1374
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${commentKind})`,
1375
- ` }`,
1376
- ` continue`,
1377
- ` }`
1378
- ].join("\n");
1379
- var WS_COMMENTS_LOOP_LABELED = (wsKind, commentKind) => [
1380
- ` while (_e < input.length) {`,
1381
- ` const c = input.charCodeAt(_e)`,
1382
- CAP_CHUNK(wsKind, commentKind),
1383
- ` break`,
1384
- ` }`
1385
- ].join("\n");
1386
- function buildFastTriviaFnDecl(fnName, kind, kindIndices) {
1387
- const loop = kind === "wsComments" && kindIndices ? WS_COMMENTS_LOOP_LABELED(kindIndices.ws, kindIndices.comment) : kind === "wsComments" ? WS_COMMENTS_LOOP : WS_ONLY_LOOP;
1388
- const cap = kindIndices ? "" : CAP_RECORD;
1389
- const lines = [
1452
+ function composeFastLoop(shapes) {
1453
+ return [
1454
+ ` while (_e < input.length) {`,
1455
+ ` const c = input.charCodeAt(_e)`,
1456
+ ...shapes.map(scanBranch),
1457
+ ` break`,
1458
+ ` }`
1459
+ ].join("\n");
1460
+ }
1461
+ function buildFastTriviaFnDecl(fnName, shapes) {
1462
+ return [
1390
1463
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
1391
1464
  ` let _e = _pos`,
1392
- loop
1393
- ];
1394
- if (cap) lines.push(cap);
1395
- lines.push(` return _e`, `}`);
1396
- return lines.join("\n");
1465
+ composeFastLoop(shapes),
1466
+ CAP_RECORD,
1467
+ ` return _e`,
1468
+ `}`
1469
+ ].join("\n");
1397
1470
  }
1398
- function regexArmIndex(spec, isComment) {
1471
+ function analyzeLabeledScannableRun(trivia2) {
1472
+ const spec = analyzeLabeledTrivia(trivia2);
1473
+ if (!spec) return null;
1474
+ const out = [];
1399
1475
  for (const arm of spec.arms) {
1400
1476
  const src = getCoreRegexDef(arm.parser)?.source;
1401
- if (!src) return null;
1402
- const comment = src.includes("\\*") || src.startsWith("\\/\\/");
1403
- if (comment === isComment) return arm.kindIndex;
1477
+ const shape = src ? parseScanShape(src) : null;
1478
+ if (!shape) return null;
1479
+ out.push({ shape, kindIndex: arm.kindIndex });
1404
1480
  }
1405
- return null;
1481
+ return out;
1482
+ }
1483
+ function buildLabeledScannableTriviaFnDecl(fnName, arms) {
1484
+ return [
1485
+ `function ${fnName}(input, _pos, _ctx, _cap) {`,
1486
+ ` let _e = _pos`,
1487
+ ` while (_e < input.length) {`,
1488
+ ` const c = input.charCodeAt(_e)`,
1489
+ ...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
1490
+ ` break`,
1491
+ ` }`,
1492
+ ` return _e`,
1493
+ `}`
1494
+ ].join("\n");
1406
1495
  }
1407
1496
  function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
1408
1497
  const tryArms = spec.arms.map((arm, i) => {
@@ -1435,14 +1524,6 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
1435
1524
  `}`
1436
1525
  ].join("\n");
1437
1526
  }
1438
- function labeledTriviaKindIndices(trivia2) {
1439
- const spec = analyzeLabeledTrivia(trivia2);
1440
- if (!spec || spec.arms.length !== 2) return null;
1441
- const ws = regexArmIndex(spec, false);
1442
- const comment = regexArmIndex(spec, true);
1443
- if (ws === null || comment === null) return null;
1444
- return { ws, comment };
1445
- }
1446
1527
  function labeledTriviaRegexArms(trivia2) {
1447
1528
  const spec = analyzeLabeledTrivia(trivia2);
1448
1529
  if (!spec) return null;
@@ -1690,14 +1771,19 @@ function ensureTriviaFn(ctx) {
1690
1771
  const fnName = `_tf${ctx.triviaFnNames.size}`;
1691
1772
  ctx.triviaFnNames.set(trivia2, fnName);
1692
1773
  ctx.triviaCaptureNames.set(trivia2, fnName);
1693
- const fastKind = analyzeTriviaFastPath(trivia2);
1694
- const kindIndices = labeledTriviaKindIndices(trivia2);
1695
- if (fastKind) {
1696
- ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastKind, kindIndices ?? void 0));
1697
- return fnName;
1698
- }
1699
1774
  const labeledSpec = analyzeLabeledTrivia(trivia2);
1700
- if (labeledSpec) {
1775
+ if (!labeledSpec) {
1776
+ const fastShapes = analyzeTriviaFastPath(trivia2);
1777
+ if (fastShapes) {
1778
+ ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastShapes));
1779
+ return fnName;
1780
+ }
1781
+ } else {
1782
+ const labeledShapes = analyzeLabeledScannableRun(trivia2);
1783
+ if (labeledShapes) {
1784
+ ctx.namedFnDecls.push(buildLabeledScannableTriviaFnDecl(fnName, labeledShapes));
1785
+ return fnName;
1786
+ }
1701
1787
  const regexSpec = labeledTriviaRegexArms(trivia2);
1702
1788
  if (regexSpec) {
1703
1789
  const reNames = [];
@@ -2135,11 +2221,11 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
2135
2221
  const p = def.parsers[idx];
2136
2222
  const litVal = getCoreLiteralValue(p);
2137
2223
  const litLen = litVal.length;
2138
- const litCond = emitLiteralCondition(litVal, pos);
2224
+ const litCond2 = emitLiteralCondition(litVal, pos);
2139
2225
  const kw = first ? "if" : "else if";
2140
2226
  first = false;
2141
2227
  const litEnd = `${pos} + ${litLen}`;
2142
- stmts.push(`${ind(ctx)}${kw} (${litCond}) {`);
2228
+ stmts.push(`${ind(ctx)}${kw} (${litCond2}) {`);
2143
2229
  ctx.indent++;
2144
2230
  const tR = emitTransformChain(p, JSON.stringify(litVal), litEnd, pos, ctx);
2145
2231
  stmts.push(
@@ -2507,6 +2593,16 @@ function emitRuntimeFallback(parser2, ctx, pos) {
2507
2593
  return { stmts, valueVar: vv, endVar: ev };
2508
2594
  }
2509
2595
  function emitLazy(p, def, ctx, pos) {
2596
+ const usage = ctx.lazyUsage;
2597
+ if (usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
2598
+ let resolved;
2599
+ try {
2600
+ resolved = def.thunk();
2601
+ } catch {
2602
+ return emitRuntimeFallback(p, ctx, pos);
2603
+ }
2604
+ return emit(resolved, ctx, pos);
2605
+ }
2510
2606
  if (!ctx.namedParsers.has(p)) {
2511
2607
  const fnName2 = `_pf${ctx.namedParsers.size}`;
2512
2608
  ctx.namedParsers.set(p, fnName2);
@@ -2771,6 +2867,80 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
2771
2867
  return false;
2772
2868
  }
2773
2869
  }
2870
+ function childrenOf(def) {
2871
+ switch (def.tag) {
2872
+ case "sequence":
2873
+ case "choice":
2874
+ return def.parsers;
2875
+ case "many":
2876
+ case "optional":
2877
+ case "transform":
2878
+ case "trivia":
2879
+ case "label":
2880
+ case "grammar":
2881
+ case "not":
2882
+ case "node":
2883
+ case "withCtx":
2884
+ case "expect":
2885
+ return [def.parser];
2886
+ // emitMany's oneOrMore branch and emitSepBy both codegen `def.parser` TWICE
2887
+ // (a mandatory first match, then again inside the repeat loop) — two real
2888
+ // emit() call sites, so the usage analysis must see two edges here or it
2889
+ // undercounts a single-use `parser` ref as inline-safe when it's actually
2890
+ // referenced from two positions within this one compiled function.
2891
+ case "oneOrMore":
2892
+ return [def.parser, def.parser];
2893
+ case "sepBy":
2894
+ return [def.parser, def.parser, def.separator];
2895
+ case "skip":
2896
+ return [def.main, def.skipped];
2897
+ case "recover":
2898
+ return [def.parser, def.sentinel];
2899
+ case "scanTo":
2900
+ return [def.sentinel, ...def.skip];
2901
+ case "lazy":
2902
+ case "literal":
2903
+ case "regex":
2904
+ case "keywords":
2905
+ case "guard":
2906
+ case "unknown":
2907
+ return [];
2908
+ }
2909
+ }
2910
+ function analyzeLazyUsage(root) {
2911
+ return analyzeLazyUsageMulti([root]);
2912
+ }
2913
+ function analyzeLazyUsageMulti(roots) {
2914
+ const counts = /* @__PURE__ */ new Map();
2915
+ const recursive = /* @__PURE__ */ new Set();
2916
+ const descended = /* @__PURE__ */ new Set();
2917
+ const active = /* @__PURE__ */ new Set();
2918
+ function walk(p) {
2919
+ const def = p._def;
2920
+ if (def.tag === "lazy") {
2921
+ counts.set(p, (counts.get(p) ?? 0) + 1);
2922
+ if (active.has(p)) {
2923
+ recursive.add(p);
2924
+ return;
2925
+ }
2926
+ if (descended.has(p)) return;
2927
+ descended.add(p);
2928
+ let resolved;
2929
+ try {
2930
+ resolved = def.thunk();
2931
+ } catch {
2932
+ return;
2933
+ }
2934
+ active.add(p);
2935
+ walk(resolved);
2936
+ active.delete(p);
2937
+ return;
2938
+ }
2939
+ for (const child of childrenOf(def)) walk(child);
2940
+ }
2941
+ for (const root of roots) walk(root);
2942
+ return { counts, recursive };
2943
+ }
2774
2944
  function compile(combinator, mapFnSources) {
2775
2945
  const ctx = {
2776
2946
  vars: 0,
@@ -2787,7 +2957,8 @@ function compile(combinator, mapFnSources) {
2787
2957
  triviaCaptureNames: /* @__PURE__ */ new Map(),
2788
2958
  triviaFnNames: /* @__PURE__ */ new Map(),
2789
2959
  namedFnDecls: [],
2790
- capturing: hasNodeDef(combinator)
2960
+ capturing: hasNodeDef(combinator),
2961
+ lazyUsage: analyzeLazyUsage(combinator)
2791
2962
  };
2792
2963
  const r = emit(combinator, ctx, "_pos");
2793
2964
  const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
@@ -2845,6 +3016,66 @@ function compile(combinator, mapFnSources) {
2845
3016
  }
2846
3017
  };
2847
3018
  }
3019
+ function compileRuleMap(ruleMap) {
3020
+ const ctx = {
3021
+ vars: 0,
3022
+ indent: 1,
3023
+ regexDecls: [],
3024
+ regexMap: /* @__PURE__ */ new Map(),
3025
+ mapFns: [],
3026
+ mapFnSrcs: [],
3027
+ buildFns: [],
3028
+ buildSrcs: [],
3029
+ runtimeParsers: [],
3030
+ needsCollator: false,
3031
+ namedParsers: /* @__PURE__ */ new Map(),
3032
+ triviaCaptureNames: /* @__PURE__ */ new Map(),
3033
+ triviaFnNames: /* @__PURE__ */ new Map(),
3034
+ namedFnDecls: [],
3035
+ capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
3036
+ lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule))
3037
+ };
3038
+ const perEntry = ruleMap.map(([key, rule]) => ({ key, r: emit(rule, ctx, "_pos") }));
3039
+ const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
3040
+ ` : "";
3041
+ const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
3042
+ const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
3043
+ const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
3044
+ const mfCovered = ctx.mapFns.length === 0 || derivedSrcs !== void 0;
3045
+ const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
3046
+ if (!canInline) return null;
3047
+ const mfDecl = derivedSrcs?.length ? ` const _mf = [${derivedSrcs.join(", ")}]` : "";
3048
+ const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
3049
+ const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
3050
+ const hoistedDecls = [
3051
+ ...ctx.regexDecls.map((d) => ` ${d}`),
3052
+ collatorDecl ? ` ${collatorDecl.trim()}` : "",
3053
+ mfDecl,
3054
+ buildDecl,
3055
+ ...namedPrelude.map((l) => ` ${l}`),
3056
+ ...ctx.namedFnDecls.flatMap((decl, i) => {
3057
+ const lines = decl.split("\n").map((l) => ` ${l}`);
3058
+ return i > 0 ? ["", ...lines] : lines;
3059
+ })
3060
+ ].filter(Boolean);
3061
+ const entryFnText = (r) => [
3062
+ `function(input, _pos, _ctx) {`,
3063
+ ` let pos = _pos`,
3064
+ ...r.stmts,
3065
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
3066
+ `}`
3067
+ ].join("\n");
3068
+ const objBody = perEntry.map(({ key, r }) => ` ${JSON.stringify(key)}: ${entryFnText(r).split("\n").join("\n ")}`).join(",\n");
3069
+ const replacement = [
3070
+ `/* @__PURE__ */ (() => {`,
3071
+ ...hoistedDecls,
3072
+ ` return {`,
3073
+ objBody,
3074
+ ` }`,
3075
+ `})()`
3076
+ ].join("\n");
3077
+ return { keys: perEntry.map((e) => e.key), replacement };
3078
+ }
2848
3079
  function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources) {
2849
3080
  const bodyLines = [
2850
3081
  ` let pos = _pos`,
@@ -3126,6 +3357,10 @@ function exprToCombi(node2, scope, code, mfs) {
3126
3357
  }
3127
3358
  }
3128
3359
  if (callee.name === "rules") return null;
3360
+ if (callee.name === "ref") {
3361
+ if (node2.arguments.length !== 0) return null;
3362
+ return ref();
3363
+ }
3129
3364
  if (callee.name === "parser") {
3130
3365
  const [optsArg, rootArg] = node2.arguments;
3131
3366
  if (!optsArg || !rootArg || optsArg.type === "SpreadElement" || rootArg.type === "SpreadElement") return null;
@@ -3365,6 +3600,40 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
3365
3600
  }
3366
3601
  return ruleRefs;
3367
3602
  }
3603
+ function isDefinableRef(v2) {
3604
+ return isCombinator(v2) && typeof v2.define === "function" && v2._def.tag === "lazy";
3605
+ }
3606
+ function evaluateRefDeclaration(init, name, scope) {
3607
+ if (init.type !== "CallExpression") return null;
3608
+ const callee = init.callee;
3609
+ if (callee.type !== "Identifier" || callee.name !== "ref") return null;
3610
+ if (init.arguments.length !== 0) return null;
3611
+ const slot = ref();
3612
+ scope.set(name, { combi: slot, mfSrcs: [] });
3613
+ return slot;
3614
+ }
3615
+ function applyDefineStatement(callExpr, scope, code) {
3616
+ if (callExpr.type !== "CallExpression") return false;
3617
+ const callee = callExpr.callee;
3618
+ if (callee.type !== "MemberExpression") return false;
3619
+ const mem = callee;
3620
+ if (mem.computed) return false;
3621
+ if (mem.property.type !== "Identifier" || mem.property.name !== "define") return false;
3622
+ if (mem.object.type !== "Identifier" || !mem.object.name) return false;
3623
+ const target = scope.get(mem.object.name);
3624
+ const refCombi = isScopeEntry(target) ? target.combi : isCombinator(target) ? target : null;
3625
+ if (!refCombi || !isDefinableRef(refCombi)) return false;
3626
+ const args = callExpr.arguments;
3627
+ if (args.length !== 1 || args[0].type === "SpreadElement") return false;
3628
+ const inner = anyValue(args[0], scope, code, []);
3629
+ if (!isCombinator(inner)) return false;
3630
+ try {
3631
+ refCombi.define(inner);
3632
+ } catch {
3633
+ return false;
3634
+ }
3635
+ return true;
3636
+ }
3368
3637
  function referencesAny(node2, names, scope) {
3369
3638
  if (node2.type === "Identifier") {
3370
3639
  return names.has(node2.name) || scope.has(node2.name);
@@ -3439,6 +3708,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
3439
3708
  const replacements = [];
3440
3709
  const warnings = [];
3441
3710
  let anyUnresolved = false;
3711
+ let ruleMapHolderCounter = 0;
3442
3712
  const lineOf = (pos) => {
3443
3713
  let line = 1;
3444
3714
  for (let i = 0; i < pos && i < code.length; i++) if (code.charCodeAt(i) === 10) line++;
@@ -3460,18 +3730,53 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
3460
3730
  warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
3461
3731
  return null;
3462
3732
  }
3463
- const entries = [];
3464
- for (const [ruleKey, rule] of ruleMap) {
3465
- const compiled = compile(rule);
3466
- if (compiled.inlineExpression === null) {
3467
- warn(init.start, `${label2}: rule "${ruleKey}" couldn't be inlined`);
3468
- return null;
3469
- }
3470
- entries.push([ruleKey, compiled.inlineExpression]);
3733
+ const compiled = compileRuleMap([...ruleMap]);
3734
+ if (!compiled) {
3735
+ warn(init.start, `${label2}: rule map couldn't be inlined`);
3736
+ return null;
3471
3737
  }
3472
- return { entries, ruleMap };
3738
+ return { replacement: compiled.replacement, ruleMap };
3473
3739
  };
3474
3740
  const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
3741
+ const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
3742
+ const refNames = /* @__PURE__ */ new Set();
3743
+ for (const stmt of body) {
3744
+ const innerVd = unwrapVd(stmt);
3745
+ if (!innerVd) continue;
3746
+ for (const decl of innerVd.declarations) {
3747
+ const d = decl;
3748
+ if (!d.init || d.id.type !== "Identifier") continue;
3749
+ const init = d.init;
3750
+ if (init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "ref") {
3751
+ refNames.add(d.id.name);
3752
+ }
3753
+ }
3754
+ }
3755
+ const defineRemovals = [];
3756
+ if (refNames.size > 0) {
3757
+ for (const stmt of body) {
3758
+ if (stmt.type === "ExpressionStatement") {
3759
+ const expr = stmt.expression;
3760
+ if (applyDefineStatement(expr, scope, code)) {
3761
+ defineRemovals.push({ start: stmt.start, end: stmt.end });
3762
+ }
3763
+ continue;
3764
+ }
3765
+ const innerVd = unwrapVd(stmt);
3766
+ if (!innerVd) continue;
3767
+ for (const decl of innerVd.declarations) {
3768
+ const d = decl;
3769
+ if (!d.init || d.id.type !== "Identifier") continue;
3770
+ const name = d.id.name;
3771
+ const init = d.init;
3772
+ if (evaluateRefDeclaration(init, name, scope)) continue;
3773
+ if (scope.has(name)) continue;
3774
+ if (!referencesAny(init, allNames, scope)) continue;
3775
+ const combi = evaluateExpr(init, scope, code, []);
3776
+ if (combi) scope.set(name, { combi, mfSrcs: [] });
3777
+ }
3778
+ }
3779
+ }
3475
3780
  for (const stmt of body) {
3476
3781
  let vd = null;
3477
3782
  let stmtStart = stmt.start;
@@ -3495,15 +3800,26 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
3495
3800
  if (d.id.type === "Identifier") {
3496
3801
  const varName = d.id.name;
3497
3802
  if (!referencesAny(init, allNames, scope)) continue;
3803
+ if (refNames.has(varName)) {
3804
+ const refEntry = scope.get(varName);
3805
+ const refCombi = refEntry?.combi ?? null;
3806
+ if (refCombi) {
3807
+ const compiled2 = compile(refCombi);
3808
+ if (compiled2.inlineExpression === null) {
3809
+ warn(init.start, `"${varName}" is a ref() that couldn't be inlined (was .define() called with a static combinator?)`);
3810
+ continue;
3811
+ }
3812
+ replacements.push({ start: init.start, end: init.end, replacement: compiled2.inlineExpression });
3813
+ continue;
3814
+ }
3815
+ }
3498
3816
  if (isRulesCall(init)) {
3499
3817
  const compiledRules = compileRulesFactory(init, varName);
3500
3818
  if (!compiledRules) continue;
3501
3819
  replacements.push({
3502
3820
  start: init.start,
3503
3821
  end: init.end,
3504
- replacement: `{
3505
- ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
3506
- }`
3822
+ replacement: compiledRules.replacement
3507
3823
  });
3508
3824
  continue;
3509
3825
  }
@@ -3538,9 +3854,8 @@ ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
3538
3854
  }
3539
3855
  const compiledRules = compileRulesFactory(init, "{ \u2026 }");
3540
3856
  if (!compiledRules) continue;
3541
- const byKey = new Map(compiledRules.entries);
3542
3857
  const pattern = d.id;
3543
- const lines = [];
3858
+ const bindings = [];
3544
3859
  let allOk = true;
3545
3860
  for (const prop of pattern.properties) {
3546
3861
  const p = prop;
@@ -3555,17 +3870,21 @@ ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
3555
3870
  allOk = false;
3556
3871
  break;
3557
3872
  }
3558
- const inlineExpr = byKey.get(ruleKey);
3559
- if (inlineExpr === void 0) {
3873
+ if (!compiledRules.ruleMap.has(ruleKey)) {
3560
3874
  warn(init.start, `destructured rule "${ruleKey}" isn't returned by the rules() factory`);
3561
3875
  allOk = false;
3562
3876
  break;
3563
3877
  }
3564
- lines.push(`${exportPrefix}${kind} ${localName} = ${inlineExpr}`);
3878
+ bindings.push({ ruleKey, localName });
3565
3879
  const rule = compiledRules.ruleMap.get(ruleKey);
3566
3880
  if (rule) scope.set(localName, { combi: rule, mfSrcs: [] });
3567
3881
  }
3568
3882
  if (!allOk) continue;
3883
+ const holderVar = `__rules${ruleMapHolderCounter++}`;
3884
+ const lines = [
3885
+ `const ${holderVar} = ${compiledRules.replacement}`,
3886
+ ...bindings.map(({ ruleKey, localName }) => `${exportPrefix}${kind} ${localName} = ${holderVar}[${JSON.stringify(ruleKey)}]`)
3887
+ ];
3569
3888
  replacements.push({
3570
3889
  start: stmtStart,
3571
3890
  end: stmtEnd,
@@ -3579,6 +3898,9 @@ ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
3579
3898
  imp.fullyResolved = !anyUnresolved;
3580
3899
  }
3581
3900
  const ms = new import_magic_string.default(code);
3901
+ if (!anyUnresolved) {
3902
+ for (const { start, end } of defineRemovals) ms.remove(start, end);
3903
+ }
3582
3904
  for (const imp of macroImports) {
3583
3905
  if (imp.fullyResolved) {
3584
3906
  ms.remove(imp.start, imp.end);