parseman 0.18.1 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +7 -3
  2. package/dist/combinators/choice.d.ts.map +1 -1
  3. package/dist/combinators/literal.d.ts.map +1 -1
  4. package/dist/combinators/map.d.ts +5 -0
  5. package/dist/combinators/map.d.ts.map +1 -1
  6. package/dist/combinators/node.d.ts +14 -13
  7. package/dist/combinators/node.d.ts.map +1 -1
  8. package/dist/combinators/parser.d.ts.map +1 -1
  9. package/dist/combinators/ref.d.ts.map +1 -1
  10. package/dist/combinators/regex.d.ts.map +1 -1
  11. package/dist/combinators/repeat.d.ts.map +1 -1
  12. package/dist/combinators/trivia-skip.d.ts +1 -0
  13. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  14. package/dist/compiler/build-arity.d.ts +3 -3
  15. package/dist/compiler/codegen.d.ts.map +1 -1
  16. package/dist/compiler/fields.d.ts +16 -0
  17. package/dist/compiler/fields.d.ts.map +1 -0
  18. package/dist/compiler/inline-build.d.ts.map +1 -1
  19. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  20. package/dist/compiler/linker.d.ts +17 -3
  21. package/dist/compiler/linker.d.ts.map +1 -1
  22. package/dist/compiler/value-usage.d.ts.map +1 -1
  23. package/dist/cst/capture-buffer.d.ts +1 -0
  24. package/dist/cst/capture-buffer.d.ts.map +1 -1
  25. package/dist/index.cjs +498 -78
  26. package/dist/index.cjs.map +4 -4
  27. package/dist/index.d.ts +3 -3
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +498 -78
  30. package/dist/index.js.map +4 -4
  31. package/dist/plugin/evaluator.d.ts.map +1 -1
  32. package/dist/plugin/index.cjs +498 -86
  33. package/dist/plugin/index.cjs.map +4 -4
  34. package/dist/plugin/index.js +498 -86
  35. package/dist/plugin/index.js.map +4 -4
  36. package/dist/types.d.ts +31 -8
  37. package/dist/types.d.ts.map +1 -1
  38. package/package.json +4 -1
@@ -217,6 +217,13 @@ function ref() {
217
217
  define(p) {
218
218
  if (resolved) throw new Error("ref<T>() already defined");
219
219
  resolved = p;
220
+ meta.firstSet = p._meta.firstSet;
221
+ meta.canMatchNewline = p._meta.canMatchNewline;
222
+ meta.isTrivia = p._meta.isTrivia;
223
+ if (p._meta.triviaKindLabels !== void 0) meta.triviaKindLabels = p._meta.triviaKindLabels;
224
+ else delete meta.triviaKindLabels;
225
+ if (p._meta.disjoint !== void 0) meta.disjoint = p._meta.disjoint;
226
+ else delete meta.disjoint;
220
227
  }
221
228
  };
222
229
  return slot;
@@ -324,7 +331,8 @@ function saveCstMark(ctx) {
324
331
  return {
325
332
  raw: cstRawLen(ctx),
326
333
  tlog: cstTlLen(ctx),
327
- leaves: cstLeavesLen(ctx)
334
+ leaves: cstLeavesLen(ctx),
335
+ fields: ctx._fields?.length ?? 0
328
336
  };
329
337
  }
330
338
  function rollbackBufList(b, keyMulti, keySingle, len) {
@@ -348,11 +356,13 @@ function rollbackCstCapture(ctx, mark) {
348
356
  if (mark.tlog === 0) b.tl = void 0;
349
357
  else b.tl.length = mark.tlog;
350
358
  }
359
+ if (ctx._fields) ctx._fields.length = mark.fields;
351
360
  return;
352
361
  }
353
362
  if (ctx._cstRawChildren) ctx._cstRawChildren.length = mark.raw;
354
363
  if (ctx._cstTriviaLog) ctx._cstTriviaLog.length = mark.tlog;
355
364
  if (ctx._cstLeaves) ctx._cstLeaves.length = mark.leaves;
365
+ if (ctx._fields) ctx._fields.length = mark.fields;
356
366
  }
357
367
  function pushCstTriviaEntry(ctx, start, end, kindIndex) {
358
368
  const insertIdx = cstRawLen(ctx);
@@ -421,13 +431,12 @@ function literal(value, opts = {}) {
421
431
  if (end > input.length) {
422
432
  return failAt(ctx, [JSON.stringify(value)], pos);
423
433
  }
424
- const slice = input.slice(pos, end);
425
- const matched = caseInsensitive ? asciiFoldEq(slice, value) : slice === value;
426
- if (matched) {
434
+ const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
435
+ if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
427
436
  const span = { start: pos, end };
428
- const leaf = { _tag: "leaf", value: slice, span };
437
+ const leaf = { _tag: "leaf", value: matchedValue, span };
429
438
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
430
- return { ok: true, value: slice, span };
439
+ return { ok: true, value: matchedValue, span };
431
440
  }
432
441
  return failAt(ctx, [JSON.stringify(value)], pos);
433
442
  }
@@ -440,10 +449,102 @@ function registerRegexAnalyzer(analyzer) {
440
449
  firstSetAnalyzer = analyzer;
441
450
  }
442
451
  var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
452
+ var SCAN_BAIL_AT = 64;
453
+ var SPACE_RANGES = [
454
+ [9, 13],
455
+ [32, 32],
456
+ [160, 160],
457
+ [5760, 5760],
458
+ [8192, 8202],
459
+ [8232, 8233],
460
+ [8239, 8239],
461
+ [8287, 8287],
462
+ [12288, 12288],
463
+ [65279, 65279]
464
+ ];
465
+ var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
466
+ function shorthandRanges(ch) {
467
+ if (ch === "d") return [[48, 57]];
468
+ if (ch === "s") return SPACE_RANGES;
469
+ return [[48, 57], [65, 90], [97, 122], [95, 95]];
470
+ }
471
+ function inRanges(cp, ranges) {
472
+ for (let i = 0; i < ranges.length; i++) {
473
+ const [lo, hi] = ranges[i];
474
+ if (cp >= lo && cp <= hi) return true;
475
+ }
476
+ return false;
477
+ }
478
+ function readClassRanges(body) {
479
+ if (body.startsWith("^")) return null;
480
+ const ranges = [];
481
+ let i = 0;
482
+ const readAtom = () => {
483
+ const ch = body[i];
484
+ if (ch === void 0) return null;
485
+ if (ch === "\\") {
486
+ const esc = body[i + 1];
487
+ if (esc === void 0) return null;
488
+ i += 2;
489
+ if (esc === "d" || esc === "w" || esc === "s") return { ranges: shorthandRanges(esc) };
490
+ if (esc in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[esc] };
491
+ if (esc >= "a" && esc <= "z" || esc >= "A" && esc <= "Z") return null;
492
+ return { cp: esc.charCodeAt(0) };
493
+ }
494
+ i++;
495
+ return { cp: ch.charCodeAt(0) };
496
+ };
497
+ while (i < body.length) {
498
+ const lo = readAtom();
499
+ if (!lo) return null;
500
+ if ("ranges" in lo) {
501
+ ranges.push(...lo.ranges);
502
+ continue;
503
+ }
504
+ if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
505
+ i++;
506
+ const hi = readAtom();
507
+ if (!hi || "ranges" in hi) return null;
508
+ ranges.push([lo.cp, hi.cp]);
509
+ } else {
510
+ ranges.push([lo.cp, lo.cp]);
511
+ }
512
+ }
513
+ return ranges.length ? ranges : null;
514
+ }
515
+ function shortScanner(source, flags) {
516
+ if (/[imsuvy]/.test(flags)) return null;
517
+ let ranges = null;
518
+ let quant = "";
519
+ if (source[0] === "[") {
520
+ let end = 1;
521
+ while (end < source.length && source[end] !== "]") {
522
+ if (source[end] === "\\") end += 2;
523
+ else end++;
524
+ }
525
+ if (source[end] !== "]") return null;
526
+ ranges = readClassRanges(source.slice(1, end));
527
+ quant = source.slice(end + 1);
528
+ } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
529
+ ranges = shorthandRanges(source[1]);
530
+ quant = source.slice(2);
531
+ }
532
+ if (!ranges || quant !== "+" && quant !== "*") return null;
533
+ const minOne = quant === "+";
534
+ return (input, pos) => {
535
+ let end = pos;
536
+ while (end < input.length && inRanges(input.charCodeAt(end), ranges)) {
537
+ end++;
538
+ if (end - pos >= SCAN_BAIL_AT) return void 0;
539
+ }
540
+ return minOne && end === pos ? null : end;
541
+ };
542
+ }
443
543
  function regex(pattern, flags = "") {
444
544
  const source = typeof pattern === "string" ? pattern : pattern.source;
445
545
  const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
446
546
  const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
547
+ const scan = shortScanner(source, resolvedFlags);
447
548
  const { firstSet, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
448
549
  const meta = { firstSet, canMatchNewline, isTrivia: false };
449
550
  return {
@@ -451,6 +552,15 @@ function regex(pattern, flags = "") {
451
552
  _meta: meta,
452
553
  _def: { tag: "regex", source, flags: resolvedFlags },
453
554
  parse(input, pos, ctx) {
555
+ const scanEnd = scan?.(input, pos);
556
+ if (scanEnd !== void 0) {
557
+ if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
558
+ const value = input.slice(pos, scanEnd);
559
+ const span2 = { start: pos, end: scanEnd };
560
+ const leaf2 = { _tag: "leaf", value, span: span2 };
561
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf2);
562
+ return { ok: true, value, span: span2 };
563
+ }
454
564
  anchored.lastIndex = pos;
455
565
  const m = anchored.exec(input);
456
566
  if (m === null) {
@@ -631,13 +741,10 @@ function choice(...args) {
631
741
  };
632
742
  const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
633
743
  const autoNot = !disjoint && !hasGates && strategy?.tag === "firstMatch" ? computeAutoNot(parsers) : parsers.map(() => null);
634
- let greedyRe = null;
635
744
  let greedyLitMap = null;
636
745
  let sortedParsers = null;
746
+ const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
637
747
  if (strategy?.tag === "greedyClassify") {
638
- const regexDef = getCoreRegexDef(parsers[strategy.superIndex]);
639
- const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
640
- greedyRe = new RegExp(regexDef.source, flags);
641
748
  greedyLitMap = /* @__PURE__ */ new Map();
642
749
  for (let i = 0; i < parsers.length; i++) {
643
750
  if (i === strategy.superIndex) continue;
@@ -662,14 +769,21 @@ function choice(...args) {
662
769
  const expected = [];
663
770
  if (disjoint && pos < input.length) {
664
771
  const code = input.codePointAt(pos);
665
- for (const parser2 of parsers) {
666
- if (inFirstSet(code, parser2._meta.firstSet)) {
667
- const result = parser2.parse(input, pos, ctx);
668
- if (result.ok) return result;
669
- expected.push(...result.expected);
670
- return { ok: false, expected, span: { start: pos, end: pos } };
772
+ let parser2 = code < 128 ? asciiDispatch?.[code] ?? null : null;
773
+ if (!parser2) {
774
+ for (const p of parsers) {
775
+ if (inFirstSet(code, p._meta.firstSet)) {
776
+ parser2 = p;
777
+ break;
778
+ }
671
779
  }
672
780
  }
781
+ if (parser2) {
782
+ const result = parser2.parse(input, pos, ctx);
783
+ if (result.ok) return result;
784
+ expected.push(...result.expected);
785
+ return { ok: false, expected, span: { start: pos, end: pos } };
786
+ }
673
787
  return {
674
788
  ok: false,
675
789
  expected: parsers.flatMap((p) => {
@@ -845,6 +959,19 @@ function areDisjoint(sets) {
845
959
  if (intersects(sets[i], sets[j])) return false;
846
960
  return true;
847
961
  }
962
+ function buildAsciiDispatch(parsers) {
963
+ const table = Array(128).fill(null);
964
+ for (const parser2 of parsers) {
965
+ const fs2 = parser2._meta.firstSet;
966
+ if (fs2.kind !== "ranges") continue;
967
+ for (const { lo, hi } of fs2.ranges) {
968
+ for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
969
+ table[code] = parser2;
970
+ }
971
+ }
972
+ }
973
+ return table;
974
+ }
848
975
 
849
976
  // src/cst/trivia-kinds.ts
850
977
  function unwrapTrivia(p) {
@@ -966,15 +1093,16 @@ function recordTriviaChunks(ctx, chunks) {
966
1093
  // src/combinators/trivia-skip.ts
967
1094
  var NOOP_COMMIT = () => {
968
1095
  };
1096
+ var fastTriviaCache = /* @__PURE__ */ new WeakMap();
969
1097
  function needsDeferredTriviaCommit(ctx) {
970
1098
  return ctx._triviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
971
1099
  }
972
1100
  function saveTriviaMark(ctx) {
973
1101
  const m = saveCstMark(ctx);
974
- return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1102
+ return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
975
1103
  }
976
1104
  function rollbackTrivia(ctx, mark) {
977
- rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves });
1105
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields });
978
1106
  if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
979
1107
  }
980
1108
  function scanWithLabels(input, cur, ctx) {
@@ -992,6 +1120,8 @@ function scanWithLabels(input, cur, ctx) {
992
1120
  function advanceTrivia(input, cur, ctx) {
993
1121
  const triviaP = ctx.trivia;
994
1122
  if (!triviaP) return cur;
1123
+ const fast = fastTriviaScanner(triviaP);
1124
+ if (fast) return fast(input, cur);
995
1125
  if (ctx.triviaKindLabels) {
996
1126
  const scan = scanWithLabels(input, cur, ctx);
997
1127
  return scan.end;
@@ -1004,6 +1134,10 @@ function scanTrivia(input, cur, ctx) {
1004
1134
  if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
1005
1135
  const log = ctx._triviaLog;
1006
1136
  const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1137
+ const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1138
+ if (fast && log === void 0 && !captureTl) {
1139
+ return { end: fast(input, cur), commit: NOOP_COMMIT };
1140
+ }
1007
1141
  if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
1008
1142
  return scanWithLabels(input, cur, ctx);
1009
1143
  }
@@ -1025,6 +1159,79 @@ function scanTrivia(input, cur, ctx) {
1025
1159
  const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1026
1160
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1027
1161
  }
1162
+ function fastTriviaScanner(trivia2) {
1163
+ const cached = fastTriviaCache.get(trivia2);
1164
+ if (cached !== void 0) return cached;
1165
+ const scanner = buildFastTriviaScanner(trivia2);
1166
+ fastTriviaCache.set(trivia2, scanner);
1167
+ return scanner;
1168
+ }
1169
+ function buildFastTriviaScanner(trivia2) {
1170
+ const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1171
+ const direct = regexTriviaScanner(core);
1172
+ if (direct) return direct;
1173
+ const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1174
+ if (!repeat) return null;
1175
+ const one = regexTriviaScanner(repeat);
1176
+ if (one) return loopScanner([one]);
1177
+ if (repeat._def.tag !== "choice") return null;
1178
+ const arms = repeat._def.parsers.map(regexTriviaScanner);
1179
+ if (arms.some((s) => s === null)) return null;
1180
+ return loopScanner(arms);
1181
+ }
1182
+ function loopScanner(arms) {
1183
+ return (input, cur) => {
1184
+ let pos = cur;
1185
+ scan: while (pos < input.length) {
1186
+ for (const arm of arms) {
1187
+ const end = arm(input, pos);
1188
+ if (end > pos) {
1189
+ pos = end;
1190
+ continue scan;
1191
+ }
1192
+ }
1193
+ break;
1194
+ }
1195
+ return pos;
1196
+ };
1197
+ }
1198
+ function regexTriviaScanner(parser2) {
1199
+ if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1200
+ const ws = whitespaceSource(parser2._def.source);
1201
+ if (ws) return ws;
1202
+ return blockCommentSource(parser2._def.source) ? scanBlockComment : null;
1203
+ }
1204
+ function whitespaceSource(source) {
1205
+ const m = /^\[([ \\tnrf]+)\][*+]$/.exec(source);
1206
+ if (!m) return null;
1207
+ const chars = /* @__PURE__ */ new Set();
1208
+ for (let i = 0; i < m[1].length; i++) {
1209
+ const ch = m[1][i];
1210
+ if (ch !== "\\") {
1211
+ chars.add(ch.charCodeAt(0));
1212
+ continue;
1213
+ }
1214
+ const esc = m[1][++i];
1215
+ if (esc === "t") chars.add(9);
1216
+ else if (esc === "n") chars.add(10);
1217
+ else if (esc === "r") chars.add(13);
1218
+ else if (esc === "f") chars.add(12);
1219
+ else return null;
1220
+ }
1221
+ return (input, cur) => {
1222
+ let pos = cur;
1223
+ while (pos < input.length && chars.has(input.charCodeAt(pos))) pos++;
1224
+ return pos;
1225
+ };
1226
+ }
1227
+ function blockCommentSource(source) {
1228
+ return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
1229
+ }
1230
+ function scanBlockComment(input, cur) {
1231
+ if (input.charCodeAt(cur) !== 47 || input.charCodeAt(cur + 1) !== 42) return cur;
1232
+ const close = input.indexOf("*/", cur + 2);
1233
+ return close === -1 ? cur : close + 2;
1234
+ }
1028
1235
 
1029
1236
  // src/combinators/sequence.ts
1030
1237
  function sequence(...parsers) {
@@ -1163,11 +1370,15 @@ function optional(combinator) {
1163
1370
  canMatchNewline: combinator._meta.canMatchNewline,
1164
1371
  isTrivia: false
1165
1372
  };
1373
+ const firstSetSkippable = !matchesEmpty(combinator);
1166
1374
  return {
1167
1375
  _tag: "optional",
1168
1376
  _meta: meta,
1169
1377
  _def: { tag: "optional", parser: combinator },
1170
1378
  parse(input, pos, ctx) {
1379
+ if (firstSetSkippable && ctx._probe === void 0 && !startsFirstSet(combinator, input, pos)) {
1380
+ return { ok: true, value: null, span: { start: pos, end: pos } };
1381
+ }
1171
1382
  const mark = saveTriviaMark(ctx);
1172
1383
  const result = combinator.parse(input, pos, ctx);
1173
1384
  if (result.ok) return result;
@@ -1176,6 +1387,15 @@ function optional(combinator) {
1176
1387
  }
1177
1388
  };
1178
1389
  }
1390
+ function startsFirstSet(combinator, input, pos) {
1391
+ const fs2 = combinator._meta.firstSet;
1392
+ if (fs2.kind === "any") return true;
1393
+ if (fs2.kind === "empty") return false;
1394
+ const code = input.codePointAt(pos);
1395
+ if (code === void 0) return false;
1396
+ for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
1397
+ return false;
1398
+ }
1179
1399
  function sepBy(combinator, separator) {
1180
1400
  const meta = {
1181
1401
  firstSet: combinator._meta.firstSet,
@@ -1266,6 +1486,9 @@ function markUnusedValues(root) {
1266
1486
  case "transform":
1267
1487
  visit(def.parser, true);
1268
1488
  return;
1489
+ case "field":
1490
+ visit(def.parser, true);
1491
+ return;
1269
1492
  case "skip":
1270
1493
  visit(def.main, consumed);
1271
1494
  visit(def.skipped, false);
@@ -1317,6 +1540,11 @@ function markUnusedValues(root) {
1317
1540
  }
1318
1541
 
1319
1542
  // src/combinators/parser.ts
1543
+ function tagRule(r, key) {
1544
+ ;
1545
+ r._ruleName = key;
1546
+ if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
1547
+ }
1320
1548
  function rules(factory) {
1321
1549
  const cache = {};
1322
1550
  const proxy = new Proxy(cache, {
@@ -1325,7 +1553,7 @@ function rules(factory) {
1325
1553
  const record = target;
1326
1554
  if (!(key in record)) {
1327
1555
  const r = ref();
1328
- r._ruleName = key;
1556
+ tagRule(r, key);
1329
1557
  record[key] = r;
1330
1558
  }
1331
1559
  return record[key];
@@ -1336,12 +1564,12 @@ function rules(factory) {
1336
1564
  const placeholder = cache[key];
1337
1565
  const parser2 = definitions[key];
1338
1566
  if (placeholder !== void 0 && typeof placeholder.define === "function") {
1339
- ;
1567
+ tagRule(parser2, key);
1340
1568
  placeholder.define(parser2);
1341
1569
  placeholder._meta.firstSet = parser2._meta.firstSet;
1342
1570
  placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
1343
1571
  } else {
1344
- ;
1572
+ tagRule(parser2, key);
1345
1573
  cache[key] = parser2;
1346
1574
  }
1347
1575
  }
@@ -1398,44 +1626,130 @@ function buildReadsTrivia(def) {
1398
1626
  const src = def.buildSrc ?? def.build.toString();
1399
1627
  const arity = confirmedBuildArity(src);
1400
1628
  if (arity === null) return true;
1401
- return arity >= 4;
1629
+ return arity >= 5;
1402
1630
  }
1403
1631
  function buildReadsState(def) {
1404
1632
  if (!def.build) return true;
1405
1633
  const src = def.buildSrc ?? def.build.toString();
1406
1634
  const arity = confirmedBuildArity(src);
1407
1635
  if (arity === null) return true;
1408
- return arity >= 5;
1636
+ return arity >= 6;
1637
+ }
1638
+
1639
+ // src/compiler/fields.ts
1640
+ function buildReadsFields(def) {
1641
+ if (!def.build) return true;
1642
+ const src = def.buildSrc ?? def.build.toString();
1643
+ const arity = confirmedBuildArity(src);
1644
+ if (arity === null) return true;
1645
+ return arity >= 2;
1646
+ }
1647
+ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
1648
+ if (seen.has(p)) return false;
1649
+ seen.add(p);
1650
+ const d = p._def;
1651
+ switch (d.tag) {
1652
+ case "field":
1653
+ return true;
1654
+ case "node":
1655
+ return false;
1656
+ case "lazy": {
1657
+ try {
1658
+ return parserHasOwnFields(d.thunk(), seen);
1659
+ } catch {
1660
+ return false;
1661
+ }
1662
+ }
1663
+ case "sequence":
1664
+ case "choice":
1665
+ return d.parsers.some((x) => parserHasOwnFields(x, seen));
1666
+ case "sepBy":
1667
+ return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
1668
+ case "skip":
1669
+ return parserHasOwnFields(d.main, seen) || parserHasOwnFields(d.skipped, seen);
1670
+ case "grammar":
1671
+ return parserHasOwnFields(d.parser, seen) || (d.triviaParser ? parserHasOwnFields(d.triviaParser, seen) : false);
1672
+ case "scanTo":
1673
+ return parserHasOwnFields(d.sentinel, seen) || d.skip.some((x) => parserHasOwnFields(x, seen));
1674
+ case "recover":
1675
+ return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.sentinel, seen);
1676
+ case "many":
1677
+ case "oneOrMore":
1678
+ case "optional":
1679
+ case "transform":
1680
+ case "trivia":
1681
+ case "token":
1682
+ case "label":
1683
+ case "expect":
1684
+ case "withCtx":
1685
+ case "not":
1686
+ return parserHasOwnFields(d.parser, seen);
1687
+ default:
1688
+ return false;
1689
+ }
1690
+ }
1691
+ function buildFieldMap(captures) {
1692
+ if (!captures?.length) return void 0;
1693
+ const out = {};
1694
+ for (const cap of captures) {
1695
+ const entry = { value: cap.value, span: cap.span };
1696
+ const current = out[cap.name];
1697
+ if (current === void 0) out[cap.name] = entry;
1698
+ else if (Array.isArray(current)) current.push(entry);
1699
+ else out[cap.name] = [current, entry];
1700
+ }
1701
+ return out;
1409
1702
  }
1410
1703
 
1411
1704
  // src/combinators/node.ts
1412
- function collapseChild(child) {
1705
+ function unwrapChild(child) {
1413
1706
  return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
1414
1707
  }
1415
- function node(type, combinator, build, opts) {
1708
+ function isCstChild(value) {
1709
+ return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "error");
1710
+ }
1711
+ function missingInferredType() {
1712
+ throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
1713
+ }
1714
+ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
1715
+ const hasExplicitType = typeof typeOrCombinator === "string";
1716
+ const type = hasExplicitType ? typeOrCombinator : void 0;
1717
+ const combinator = hasExplicitType ? combinatorOrBuild : typeOrCombinator;
1718
+ const build = hasExplicitType ? buildOrOpts : combinatorOrBuild;
1719
+ const opts = hasExplicitType ? maybeOpts : buildOrOpts;
1720
+ const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build } };
1416
1721
  const meta = {
1417
1722
  firstSet: combinator._meta.firstSet,
1418
1723
  canMatchNewline: combinator._meta.canMatchNewline,
1419
1724
  isTrivia: false
1420
1725
  };
1726
+ const unwrap = opts?.unwrap === true;
1421
1727
  const collapse = opts?.collapse === true;
1422
- const def = collapse ? { tag: "node", type, parser: combinator, build, collapse: true } : { tag: "node", type, parser: combinator, build };
1728
+ if (unwrap && collapse) {
1729
+ throw new Error("node() options cannot set both unwrap and collapse");
1730
+ }
1731
+ const def = unwrap || collapse ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {} } : baseDef;
1423
1732
  const capturesTrivia = build ? buildReadsTrivia(def) : true;
1424
1733
  const clonesState = build ? buildReadsState(def) : true;
1734
+ const capturesFields = parserHasOwnFields(combinator) && (build ? buildReadsFields(def) : true);
1425
1735
  return {
1426
1736
  _tag: "node",
1427
1737
  _meta: meta,
1428
1738
  _def: def,
1429
1739
  parse(input, pos, ctx) {
1430
1740
  const saved = beginCstNodeCapture(ctx);
1741
+ const savedFields = ctx._fields;
1742
+ ctx._fields = capturesFields ? [] : void 0;
1431
1743
  if (!capturesTrivia) ctx.captureTrivia = false;
1432
1744
  const r = combinator.parse(input, pos, ctx);
1745
+ const fields = capturesFields ? buildFieldMap(ctx._fields) : void 0;
1746
+ ctx._fields = savedFields;
1433
1747
  const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
1434
1748
  if (!r.ok) return r;
1435
1749
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
1436
- const built = collapse && children.length === 1 ? collapseChild(children[0]) : build ? build(children, rawChildren, r.span, triviaLog, st) : ctx.build ? ctx.build(type, children, rawChildren, r.span, triviaLog, st) : { _tag: "node", type, span: { start: r.span.start, end: r.span.end }, state: st ?? null, children };
1437
- const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
1438
- const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
1750
+ const nodeType = def.type ?? missingInferredType();
1751
+ const built = unwrap && children.length === 1 ? unwrapChild(children[0]) : collapse && children.length === 1 ? children[0] : !build && ctx.build?._parsemanCstCollapse && children.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children[0], children, rawChildren) ? children[0] : build ? build(children, fields, r.span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children, fields, r.span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: { start: r.span.start, end: r.span.end }, state: st ?? null, children };
1752
+ const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
1439
1753
  if (saved.buf !== void 0 || saved.ch !== void 0) {
1440
1754
  pushCstChild(ctx, built, rawEntry);
1441
1755
  }
@@ -1453,7 +1767,7 @@ function transform(combinator, fn) {
1453
1767
  parse(input, pos, ctx) {
1454
1768
  const result = combinator.parse(input, pos, ctx);
1455
1769
  if (!result.ok) return result;
1456
- return { ...result, value: fn(result.value, result.span) };
1770
+ return { ok: true, value: fn(result.value, result.span), span: result.span };
1457
1771
  }
1458
1772
  };
1459
1773
  }
@@ -1467,7 +1781,7 @@ function skip(main, skipped) {
1467
1781
  if (!result.ok) return result;
1468
1782
  const s = skipped.parse(input, result.span.end, ctx);
1469
1783
  if (!s.ok) return result;
1470
- return { ...result, span: { start: result.span.start, end: s.span.end } };
1784
+ return { ok: true, value: result.value, span: { start: result.span.start, end: s.span.end } };
1471
1785
  }
1472
1786
  };
1473
1787
  }
@@ -1491,7 +1805,20 @@ function label(name, combinator) {
1491
1805
  _def: { tag: "label", label: name, parser: combinator },
1492
1806
  parse(input, pos, ctx) {
1493
1807
  const result = combinator.parse(input, pos, ctx);
1494
- if (!result.ok) return { ...result, expected: [name] };
1808
+ if (!result.ok) return { ok: false, expected: [name], span: result.span };
1809
+ return result;
1810
+ }
1811
+ };
1812
+ }
1813
+ function field(name, combinator) {
1814
+ return {
1815
+ _tag: combinator._tag,
1816
+ _meta: combinator._meta,
1817
+ _def: { tag: "field", name, parser: combinator },
1818
+ parse(input, pos, ctx) {
1819
+ const result = combinator.parse(input, pos, ctx);
1820
+ if (!result.ok) return result;
1821
+ ctx._fields?.push({ name, value: result.value, span: result.span });
1495
1822
  return result;
1496
1823
  }
1497
1824
  };
@@ -1682,9 +2009,9 @@ function expect(combinator, label2) {
1682
2009
 
1683
2010
  // src/compiler/scannable-run.ts
1684
2011
  var BACKSLASH = 92;
1685
- var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
2012
+ var CLASS_ESCAPES2 = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1686
2013
  var META = new Set("()[]{}*+?|^$.".split(""));
1687
- var SPACE_RANGES = [
2014
+ var SPACE_RANGES2 = [
1688
2015
  [9, 13],
1689
2016
  [32, 32],
1690
2017
  [160, 160],
@@ -1697,9 +2024,9 @@ var SPACE_RANGES = [
1697
2024
  [12288, 12288],
1698
2025
  [65279, 65279]
1699
2026
  ];
1700
- function shorthandRanges(ch) {
2027
+ function shorthandRanges2(ch) {
1701
2028
  if (ch === "d") return [[48, 57]];
1702
- if (ch === "s") return SPACE_RANGES;
2029
+ if (ch === "s") return SPACE_RANGES2;
1703
2030
  return [[48, 57], [65, 90], [97, 122], [95, 95]];
1704
2031
  }
1705
2032
  function readUnicodeEscape(body, i) {
@@ -1723,8 +2050,8 @@ function parseClassRanges(body) {
1723
2050
  const e = body[i + 1];
1724
2051
  if (e === void 0) return null;
1725
2052
  i += 2;
1726
- if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1727
- if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
2053
+ if (e in CLASS_ESCAPES2) return { cp: CLASS_ESCAPES2[e] };
2054
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges2(e) };
1728
2055
  if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1729
2056
  return { cp: e.codePointAt(0) };
1730
2057
  }
@@ -1750,9 +2077,9 @@ function parseClassRanges(body) {
1750
2077
  return ranges.length ? ranges : null;
1751
2078
  }
1752
2079
  function classToRanges(cls) {
1753
- if (cls === "\\d") return shorthandRanges("d");
1754
- if (cls === "\\w") return shorthandRanges("w");
1755
- if (cls === "\\s") return shorthandRanges("s");
2080
+ if (cls === "\\d") return shorthandRanges2("d");
2081
+ if (cls === "\\w") return shorthandRanges2("w");
2082
+ if (cls === "\\s") return shorthandRanges2("s");
1756
2083
  const body = cls.slice(1, -1);
1757
2084
  if (body.startsWith("^")) return null;
1758
2085
  return parseClassRanges(body);
@@ -1770,7 +2097,7 @@ function literalCodePoints(frag) {
1770
2097
  if (ch === "\\") {
1771
2098
  const e = frag[i + 1];
1772
2099
  if (e === void 0) return null;
1773
- out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
2100
+ out.push(e in CLASS_ESCAPES2 ? CLASS_ESCAPES2[e] : e.codePointAt(0));
1774
2101
  i += 2;
1775
2102
  continue;
1776
2103
  }
@@ -1863,7 +2190,7 @@ function parseSeqParts(source) {
1863
2190
  if (e === "d" || e === "w" || e === "s") {
1864
2191
  flush();
1865
2192
  i += 2;
1866
- i += runFrom(shorthandRanges(e), false, source[i]);
2193
+ i += runFrom(shorthandRanges2(e), false, source[i]);
1867
2194
  continue;
1868
2195
  }
1869
2196
  const uni = readUnicodeEscape(source, i);
@@ -1872,8 +2199,8 @@ function parseSeqParts(source) {
1872
2199
  cp2 = uni.cp;
1873
2200
  i = uni.next;
1874
2201
  } else if (e === void 0) return null;
1875
- else if (e in CLASS_ESCAPES) {
1876
- cp2 = CLASS_ESCAPES[e];
2202
+ else if (e in CLASS_ESCAPES2) {
2203
+ cp2 = CLASS_ESCAPES2[e];
1877
2204
  i += 2;
1878
2205
  } else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1879
2206
  else {
@@ -1980,7 +2307,7 @@ function rangesSubset(sub, sup) {
1980
2307
  }
1981
2308
  function parseClassOperand(body) {
1982
2309
  if (body === "\\d" || body === "\\w" || body === "\\s") {
1983
- return { ranges: shorthandRanges(body[1]), negated: false };
2310
+ return { ranges: shorthandRanges2(body[1]), negated: false };
1984
2311
  }
1985
2312
  if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
1986
2313
  let inner = body.slice(1, -1);
@@ -2413,7 +2740,7 @@ function parseScanShapeCore(source) {
2413
2740
  }
2414
2741
  m = /^\\([dws])([+*])$/.exec(source);
2415
2742
  if (m) {
2416
- return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
2743
+ return { kind: "chars", ranges: shorthandRanges2(m[1]), minOne: m[2] === "+" };
2417
2744
  }
2418
2745
  m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
2419
2746
  if (m) {
@@ -2955,21 +3282,22 @@ function emitScannableTerminal(shape, c) {
2955
3282
  }
2956
3283
 
2957
3284
  // src/compiler/inline-build.ts
2958
- var MK_BUILD_RE = /^\(\s*(?:\w+\s*,\s*){3}\w+\s*\)\s*=>\s*mk\s*\(\s*(['"])([^'"]+)\1\s*,\s*\w+\s*,\s*\w+\s*,\s*\w+\s*,\s*\w+\s*\)\s*$/;
3285
+ var MK_BUILD_RE = /^\(\s*(\w+)\s*,\s*\w+\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\)\s*=>\s*mk\s*\(\s*(['"])([^'"]+)\5\s*,\s*\1\s*,\s*\3\s*,\s*\2\s*,\s*\4\s*\)\s*$/;
2959
3286
  function escapeRegExp(s) {
2960
3287
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2961
3288
  }
2962
3289
  function looseMkBuildRe(type) {
2963
3290
  return new RegExp(
2964
- String.raw`^\(\s*(?:\w+\s*,\s*){3}\w+\s*\)\s*=>\s*.+\(\s*(['"])${escapeRegExp(type)}\1\s*,\s*\w+\s*,\s*\w+\s*,\s*\w+\s*,\s*\w+\s*\)\s*$`
3291
+ String.raw`^\(\s*(\w+)\s*,\s*\w+\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\)\s*=>\s*.+\(\s*(['"])${escapeRegExp(type)}\5\s*,\s*\1\s*,\s*\3\s*,\s*\2\s*,\s*\4\s*\)\s*$`
2965
3292
  );
2966
3293
  }
2967
3294
  function analyzeMkInlineBuild(def) {
2968
3295
  if (!def.build) return null;
3296
+ if (def.type === void 0) return null;
2969
3297
  const src = (def.buildSrc ?? def.build.toString()).trim();
2970
3298
  const strict = src.match(MK_BUILD_RE);
2971
3299
  if (strict) {
2972
- const mkType = strict[2];
3300
+ const mkType = strict[6];
2973
3301
  return mkType === def.type ? def.type : null;
2974
3302
  }
2975
3303
  if (looseMkBuildRe(def.type).test(src)) return def.type;
@@ -3304,8 +3632,9 @@ function planDisjointDispatch(parsers) {
3304
3632
  }
3305
3633
  return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
3306
3634
  }
3307
- function captureRestoreBody(mL, mR, mTl, mLg) {
3308
- const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
3635
+ function captureRestoreBody(mL, mR, mTl, mLg, mF = null) {
3636
+ const fieldRestore = mF ? `; if (_ctx._fields) _ctx._fields.length = ${mF}` : "";
3637
+ const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}${fieldRestore}`;
3309
3638
  return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
3310
3639
  }
3311
3640
  function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
@@ -3353,6 +3682,7 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
3353
3682
  // Delegating wrappers: defer to the wrapped parser.
3354
3683
  case "transform":
3355
3684
  case "label":
3685
+ case "field":
3356
3686
  case "expect":
3357
3687
  case "withCtx":
3358
3688
  case "grammar":
@@ -3399,6 +3729,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
3399
3729
  case "optional":
3400
3730
  case "transform":
3401
3731
  case "label":
3732
+ case "field":
3402
3733
  case "expect":
3403
3734
  case "withCtx":
3404
3735
  case "grammar":
@@ -3436,22 +3767,25 @@ function emitFallible(inner, ctx, pos, swallow = false) {
3436
3767
  ctx.recordFail = savedRecord;
3437
3768
  const ind0 = ind(ctx);
3438
3769
  const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
3770
+ const needsFieldRollback = needsRollback && parserHasOwnFields(inner);
3439
3771
  const mL = needsRollback ? v(ctx, "_fcl") : null;
3440
3772
  const mR = needsRollback ? v(ctx, "_fcr") : null;
3441
3773
  const mTl = needsRollback ? v(ctx, "_fctl") : null;
3774
+ const mF = needsFieldRollback ? v(ctx, "_fcf") : null;
3442
3775
  const stmts = [
3443
3776
  `${ind0}let ${okV} = false, ${valV}, ${endV} = ${pos}`,
3444
3777
  ...mL ? [
3445
3778
  `${ind0}const ${mL} = _ctx._cstLeaves?.length ?? 0`,
3446
3779
  `${ind0}const ${mR} = _ctx._cstRawChildren?.length ?? 0`,
3447
- `${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0`
3780
+ `${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0`,
3781
+ ...mF ? [`${ind0}const ${mF} = _ctx._fields?.length ?? 0`] : []
3448
3782
  ] : [],
3449
3783
  `${ind0}${lbl}: {`,
3450
3784
  ...r.stmts,
3451
3785
  `${ind0} ${valV} = ${r.valueVar}; ${endV} = ${r.endVar}; ${okV} = true`,
3452
3786
  `${ind0}}`,
3453
3787
  ...mL ? [
3454
- `${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null)} }`
3788
+ `${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null, mF)} }`
3455
3789
  ] : []
3456
3790
  ];
3457
3791
  return { stmts, okVar: okV, valVar: valV, endVar: endV };
@@ -3678,6 +4012,7 @@ function failsAtStart(p) {
3678
4012
  return true;
3679
4013
  case "transform":
3680
4014
  case "label":
4015
+ case "field":
3681
4016
  return failsAtStart(d.parser);
3682
4017
  default:
3683
4018
  return false;
@@ -3697,6 +4032,7 @@ function canMatchEmptyAtStart(p) {
3697
4032
  return true;
3698
4033
  case "transform":
3699
4034
  case "label":
4035
+ case "field":
3700
4036
  return canMatchEmptyAtStart(d.parser);
3701
4037
  case "literal":
3702
4038
  return d.value.length === 0;
@@ -3879,11 +4215,13 @@ function emitFirstMatch(def, ctx, pos) {
3879
4215
  const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
3880
4216
  const armHasAutoNot = !!(autoNot && autoNot.length > 0);
3881
4217
  const armNeedsRollback = ctx.capturing && (mayLeavePartialCapture(p) || armHasAutoNot && capturesLeaf(p));
4218
+ const armNeedsFieldRollback = armNeedsRollback && parserHasOwnFields(p);
3882
4219
  const markLeaves = armNeedsRollback ? v(ctx, "_cml") : null;
3883
4220
  const markRaw = armNeedsRollback ? v(ctx, "_cmr") : null;
3884
4221
  const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
3885
4222
  const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
3886
- const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
4223
+ const markFields = armNeedsFieldRollback ? v(ctx, "_cmf") : null;
4224
+ const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog, markFields) : "";
3887
4225
  const failSlot = atStart ? staticFx : "_ctx._fx";
3888
4226
  stmts.push(`${ind0}if (${skipCond}) {`);
3889
4227
  if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
@@ -3893,7 +4231,8 @@ function emitFirstMatch(def, ctx, pos) {
3893
4231
  `${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
3894
4232
  `${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
3895
4233
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
3896
- `${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
4234
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`,
4235
+ ...markFields ? [`${ind(ctx)}const ${markFields} = _ctx._fields?.length ?? 0`] : []
3897
4236
  );
3898
4237
  }
3899
4238
  const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
@@ -4286,6 +4625,9 @@ function emitToken(def, ctx, pos) {
4286
4625
  };
4287
4626
  }
4288
4627
  function emitNode(def, ctx, pos) {
4628
+ if (def.type === void 0) {
4629
+ throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
4630
+ }
4289
4631
  const structural = def.build === void 0;
4290
4632
  const mkType = structural ? null : analyzeMkInlineBuild(def);
4291
4633
  let buildIdx = null;
@@ -4297,25 +4639,36 @@ function emitNode(def, ctx, pos) {
4297
4639
  const i = ind(ctx);
4298
4640
  const capturesTrivia = mkType !== null || !structural && buildReadsTrivia(def);
4299
4641
  const clonesState = !structural && buildReadsState(def);
4642
+ const hasFields = parserHasOwnFields(def.parser);
4643
+ const capturesFields = hasFields && !structural && buildReadsFields(def);
4300
4644
  const chV = v(ctx, "_ch");
4301
4645
  const rawV = v(ctx, "_raw");
4302
4646
  const capTLv = structural ? v(ctx, "_ctl") : null;
4303
4647
  const capSTv = structural ? v(ctx, "_cst") : null;
4648
+ const capFv = structural && hasFields ? v(ctx, "_cf") : null;
4304
4649
  const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
4305
4650
  if (!capturesTrivia) ctx.needsEmptyTl = true;
4306
4651
  const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
4307
4652
  if (structural) ctx.needsHostReads = true;
4308
- const allocStmt = structural ? `${i}const ${capTLv} = _ctx._pmCapTL ??= _hostReads(_ctx.build, 4), ${capSTv} = _ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 5))
4653
+ const hostTriviaGate = `_ctx.build !== undefined && (_ctx.build._parsemanCaptureTrivia !== undefined ? _ctx.build._parsemanCaptureTrivia(${JSON.stringify(def.type)}) : (_ctx._pmCapTL ??= _hostReads(_ctx.build, 5)))`;
4654
+ const allocStmt = structural ? `${i}const ${capTLv} = ${hostTriviaGate}, ${capSTv} = _ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 6))${capFv ? `, ${capFv} = _ctx.build !== undefined && _hostReads(_ctx.build, 2)` : ""}
4309
4655
  ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
4310
4656
  const innerTl = structural ? `${capTLv} ? ${tlV} : undefined` : capturesTrivia ? tlV : "undefined";
4657
+ const fieldsOn = structural ? capFv ?? "false" : capturesFields ? "true" : "false";
4658
+ const sf = hasFields ? v(ctx, "_sf") : null;
4659
+ const fArr = hasFields ? v(ctx, "_fa") : null;
4660
+ const fObj = hasFields ? v(ctx, "_fields") : "undefined";
4311
4661
  const stmts = [
4312
4662
  allocStmt,
4313
- `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
4314
- `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}`
4663
+ `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog${sf ? `, ${sf} = _ctx._fields` : ""}`,
4664
+ `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}${sf ? `; _ctx._fields = ${fieldsOn} ? [] : undefined` : ""}`
4315
4665
  ];
4316
4666
  const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
4317
4667
  stmts.push(...innerStmts);
4318
- stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
4668
+ if (sf && fArr) {
4669
+ stmts.push(`${i}const ${fArr} = _ctx._fields`);
4670
+ }
4671
+ stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${sf ? `; _ctx._fields = ${sf}` : ""}`);
4319
4672
  stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
4320
4673
  let stV = "undefined";
4321
4674
  if (structural) {
@@ -4325,14 +4678,33 @@ ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capt
4325
4678
  stV = v(ctx, "_nst");
4326
4679
  stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
4327
4680
  }
4681
+ if (sf && fArr) {
4682
+ const fe = v(ctx, "_fe");
4683
+ const cur = v(ctx, "_fc");
4684
+ stmts.push(
4685
+ `${i}let ${fObj} = undefined`,
4686
+ `${i}if (${fArr} && ${fArr}.length) {`,
4687
+ `${i} ${fObj} = {}`,
4688
+ `${i} for (const ${fe} of ${fArr}) {`,
4689
+ `${i} const ${cur} = ${fObj}[${fe}.name], _entry = { value: ${fe}.value, span: ${fe}.span }`,
4690
+ `${i} if (${cur} === undefined) ${fObj}[${fe}.name] = _entry`,
4691
+ `${i} else if (Array.isArray(${cur})) ${cur}.push(_entry)`,
4692
+ `${i} else ${fObj}[${fe}.name] = [${cur}, _entry]`,
4693
+ `${i} }`,
4694
+ `${i}}`
4695
+ );
4696
+ }
4328
4697
  const ndV = v(ctx, "_nd");
4329
- const buildExpr = structural ? `{ _tag: 'node', type: ${JSON.stringify(def.type)}, span: { start: ${pos}, end: ${endVar} }, state: ${stV} ?? null, children: ${chV} }` : mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : `${buildRef(ctx)}[${buildIdx}](${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV})`;
4330
- const ndExpr = ctx.ns || structural ? `_ctx.build !== undefined ? _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV}) : (${buildExpr})` : buildExpr;
4331
- 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;
4698
+ const buildExpr = structural ? `{ _tag: 'node', type: ${JSON.stringify(def.type)}, span: { start: ${pos}, end: ${endVar} }, state: ${stV} ?? null, children: ${chV} }` : mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : `${buildRef(ctx)}[${buildIdx}](${chV}, ${fObj}, { start: ${pos}, end: ${endVar} }, ${rawV}, ${tlV}, ${stV})`;
4699
+ const ndExpr = ctx.ns || structural ? `_ctx.build !== undefined ? _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, { start: ${pos}, end: ${endVar} }, ${rawV}, ${tlV}, ${stV}) : (${buildExpr})` : buildExpr;
4700
+ const hostCollapseExpr = ctx.ns || structural ? `_ctx.build !== undefined && _ctx.build._parsemanCstCollapse !== undefined && ${chV}.length === 1 && ${rawV}.length === 1 && _ctx.build._parsemanCstCollapse(${JSON.stringify(def.type)}, ${chV}[0], ${chV}, ${rawV}) ? ${chV}[0] : (${ndExpr})` : ndExpr;
4701
+ const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
4702
+ const collapseExpr = `${chV}.length === 1 ? ${chV}[0] : (${ndExpr})`;
4703
+ const finalExpr = def.unwrap ? unwrapExpr : def.collapse ? collapseExpr : hostCollapseExpr;
4332
4704
  stmts.push(
4333
4705
  `${i}const ${ndV} = ${finalExpr}`,
4334
4706
  `${i}if (${sc}) ${sc}.push(${ndV})`,
4335
- `${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} } })`
4707
+ `${i}if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && (${ndV}._tag === 'node' || ${ndV}._tag === 'leaf' || ${ndV}._tag === 'error')) ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : '', span: { start: ${pos}, end: ${endVar} } })`
4336
4708
  );
4337
4709
  return { stmts, valueVar: ndV, endVar };
4338
4710
  }
@@ -4547,6 +4919,17 @@ function emitDispatch(p, ctx, pos) {
4547
4919
  endVar: inner.endVar
4548
4920
  };
4549
4921
  }
4922
+ case "field": {
4923
+ const inner = emit(def.parser, ctx, pos);
4924
+ return {
4925
+ stmts: [
4926
+ ...inner.stmts,
4927
+ `${ind(ctx)}if (_ctx._fields) _ctx._fields.push({ name: ${JSON.stringify(def.name)}, value: ${inner.valueVar}, span: { start: ${pos}, end: ${inner.endVar} } })`
4928
+ ],
4929
+ valueVar: inner.valueVar,
4930
+ endVar: inner.endVar
4931
+ };
4932
+ }
4550
4933
  case "grammar": {
4551
4934
  const savedTrivia = ctx.activeTrivia;
4552
4935
  const savedKindLabels = ctx.triviaKindLabels;
@@ -4648,6 +5031,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
4648
5031
  case "trivia":
4649
5032
  case "token":
4650
5033
  case "label":
5034
+ case "field":
4651
5035
  case "optional":
4652
5036
  case "many":
4653
5037
  case "oneOrMore":
@@ -4684,6 +5068,7 @@ function childrenOf(def) {
4684
5068
  case "trivia":
4685
5069
  case "token":
4686
5070
  case "label":
5071
+ case "field":
4687
5072
  case "grammar":
4688
5073
  case "not":
4689
5074
  case "node":
@@ -4925,6 +5310,11 @@ function assertRuleName(name) {
4925
5310
  );
4926
5311
  }
4927
5312
  }
5313
+ function publicRuleWrapperSource(rule, fnSource) {
5314
+ const labels = rule._meta.triviaKindLabels;
5315
+ if (labels === void 0) return fnSource;
5316
+ return `Object.assign(${fnSource}, { _meta: { triviaKindLabels: ${JSON.stringify(labels)} } })`;
5317
+ }
4928
5318
  function compileRuleMap(ruleMap) {
4929
5319
  for (const [, rule] of ruleMap) markUnusedValues(rule);
4930
5320
  const ctx = {
@@ -4957,7 +5347,7 @@ function compileRuleMap(ruleMap) {
4957
5347
  ruleNames.set(rule, n === 0 ? base : `${base}$${n}`);
4958
5348
  }
4959
5349
  ctx.ruleNames = ruleNames;
4960
- const perEntry = ruleMap.map(([key, rule]) => ({ key, r: emit(rule, ctx, "_pos") }));
5350
+ const perEntry = ruleMap.map(([key, rule]) => ({ key, rule, r: emit(rule, ctx, "_pos") }));
4961
5351
  const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
4962
5352
  const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
4963
5353
  const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
@@ -4987,7 +5377,10 @@ function compileRuleMap(ruleMap) {
4987
5377
  ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
4988
5378
  `}`
4989
5379
  ].join("\n");
4990
- const objBody = perEntry.map(({ key, r }) => ` ${JSON.stringify(key)}: ${entryFnText(r).split("\n").join("\n ")}`).join(",\n");
5380
+ const objBody = perEntry.map(({ key, rule, r }) => {
5381
+ const src = publicRuleWrapperSource(rule, entryFnText(r));
5382
+ return ` ${JSON.stringify(key)}: ${src.split("\n").join("\n ")}`;
5383
+ }).join(",\n");
4991
5384
  const replacement = [
4992
5385
  `/* @__PURE__ */ (() => {`,
4993
5386
  ...hoistedDecls,
@@ -5100,7 +5493,7 @@ function compileLinkable(ruleMapArg, ns) {
5100
5493
  ctx.recordFail = savedRec;
5101
5494
  pushNamedFnDecl(ctx, fn, body.stmts, body.valueVar, body.endVar);
5102
5495
  }
5103
- perEntry.push({ key, r: emitNamedFnCall(ctx, fn, "_pos") });
5496
+ perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
5104
5497
  }
5105
5498
  if (ctx.runtimeParsers.length !== 0) return null;
5106
5499
  const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
@@ -5123,14 +5516,14 @@ function compileLinkable(ruleMapArg, ns) {
5123
5516
  ...privateFns
5124
5517
  ].filter(Boolean);
5125
5518
  const wrappers = /* @__PURE__ */ new Map();
5126
- for (const { key, r } of perEntry) {
5127
- wrappers.set(key, [
5519
+ for (const { key, rule, r } of perEntry) {
5520
+ wrappers.set(key, publicRuleWrapperSource(rule, [
5128
5521
  `function(input, _pos, _ctx) {`,
5129
5522
  ` let pos = _pos`,
5130
5523
  ...r.stmts,
5131
5524
  ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
5132
5525
  `}`
5133
- ].join("\n"));
5526
+ ].join("\n")));
5134
5527
  }
5135
5528
  const firstSets = /* @__PURE__ */ new Map();
5136
5529
  for (const [key, rule] of ruleMap) {
@@ -5305,6 +5698,7 @@ function childrenOf2(def) {
5305
5698
  case "trivia":
5306
5699
  case "token":
5307
5700
  case "label":
5701
+ case "field":
5308
5702
  case "not":
5309
5703
  case "node":
5310
5704
  case "expect":
@@ -5390,6 +5784,7 @@ function evalRuleMapIR(ir) {
5390
5784
  "skip",
5391
5785
  "trivia",
5392
5786
  "label",
5787
+ "field",
5393
5788
  "expect",
5394
5789
  "_tf",
5395
5790
  "_nd",
@@ -5416,6 +5811,7 @@ function evalRuleMapIR(ir) {
5416
5811
  skip,
5417
5812
  trivia,
5418
5813
  label,
5814
+ field,
5419
5815
  expect,
5420
5816
  _tf,
5421
5817
  _nd
@@ -5549,6 +5945,8 @@ ${body}
5549
5945
  return `token(${kid(def.parser)})`;
5550
5946
  case "label":
5551
5947
  return `label(${JSON.stringify(def.label)}, ${kid(def.parser)})`;
5948
+ case "field":
5949
+ return `field(${JSON.stringify(def.name)}, ${kid(def.parser)})`;
5552
5950
  case "expect":
5553
5951
  return `expect(${kid(def.parser)}${def.label !== void 0 ? `, ${JSON.stringify(def.label)}` : ""})`;
5554
5952
  case "skip":
@@ -5561,7 +5959,11 @@ ${body}
5561
5959
  }
5562
5960
  case "node": {
5563
5961
  if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
5564
- const opts = def.collapse ? `, { collapse: true }` : "";
5962
+ const opts = def.unwrap || def.collapse ? `, { ${def.unwrap ? "unwrap: true" : "collapse: true"} }` : "";
5963
+ if (def.type === void 0) {
5964
+ if (def.buildSrc !== void 0) throw new Unserializable("inferred node build without inferred type");
5965
+ return opts ? `node(${kid(def.parser)}, undefined${opts})` : `node(${kid(def.parser)})`;
5966
+ }
5565
5967
  if (def.buildSrc !== void 0) return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts})`;
5566
5968
  return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
5567
5969
  }
@@ -5588,8 +5990,9 @@ function fusedBody(pieces) {
5588
5990
  }
5589
5991
  }
5590
5992
  const contributing = new Set(winner.values());
5591
- const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
5592
- const needsHostReads = [...contributing].some((p) => p.needsHostReads);
5993
+ const contributingPieces = [...contributing];
5994
+ const needsEmptyTl = contributingPieces.some((p) => p.needsEmptyTl);
5995
+ const needsHostReads = contributingPieces.some((p) => p.needsHostReads);
5593
5996
  const lines = [
5594
5997
  // Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
5595
5998
  "const _pfFail = {}",
@@ -5597,7 +6000,7 @@ function fusedBody(pieces) {
5597
6000
  ...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
5598
6001
  ...needsHostReads ? [HOST_READS_DECL] : [],
5599
6002
  // Each contributing artifact's namespaced private prelude (regexes, _pf, …).
5600
- ...[...contributing].flatMap((p) => p.prelude),
6003
+ ...[...new Set(contributingPieces.flatMap((p) => p.prelude))],
5601
6004
  // The winning `_r_<Name>` function for each rule (one per name → no redeclare).
5602
6005
  ...[...winner].map(([k, p]) => p.ruleFns.get(k))
5603
6006
  ];
@@ -5620,7 +6023,7 @@ function fusedBody(pieces) {
5620
6023
  }
5621
6024
  );
5622
6025
  const env = {};
5623
- for (const p of contributing) {
6026
+ for (const p of contributingPieces) {
5624
6027
  if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
5625
6028
  if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
5626
6029
  }
@@ -5668,6 +6071,7 @@ var SUPPORTED = {
5668
6071
  sepBy: (...a) => sepBy(a[0], a[1]),
5669
6072
  trivia: (...a) => trivia(a[0]),
5670
6073
  label: (...a) => label(a[0], a[1]),
6074
+ field: (...a) => field(a[0], a[1]),
5671
6075
  noTrivia: (...a) => noTrivia(a[0]),
5672
6076
  token: (...a) => token(a[0]),
5673
6077
  expect: (...a) => expect(a[0], a[1])
@@ -5688,19 +6092,20 @@ function scopeGet(scope, name, mfs) {
5688
6092
  if (isCombinator(entry)) return entry;
5689
6093
  return null;
5690
6094
  }
5691
- function staticCollapseOption(expr) {
5692
- if (expr.type !== "ObjectExpression") return false;
6095
+ function staticNodeOptions(expr) {
6096
+ if (expr.type !== "ObjectExpression") return void 0;
6097
+ const opts = {};
5693
6098
  for (const prop of expr.properties) {
5694
6099
  const p = prop;
5695
6100
  if (p.computed) continue;
5696
6101
  const key = p.key;
5697
6102
  const name = key.type === "Identifier" ? key.name : key.type === "Literal" ? String(key.value) : void 0;
5698
- if (name === "collapse") {
6103
+ if (name === "unwrap" || name === "collapse") {
5699
6104
  const val = p.value;
5700
- return (val.type === "Literal" || val.type === "BooleanLiteral") && val.value === true;
6105
+ if ((val.type === "Literal" || val.type === "BooleanLiteral") && val.value === true) opts[name] = true;
5701
6106
  }
5702
6107
  }
5703
- return false;
6108
+ return opts.unwrap || opts.collapse ? opts : void 0;
5704
6109
  }
5705
6110
  function exprToCombi(node2, scope, code, mfs) {
5706
6111
  if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
@@ -5750,18 +6155,22 @@ function exprToCombi(node2, scope, code, mfs) {
5750
6155
  }
5751
6156
  }
5752
6157
  if (callee.name === "node" && code !== void 0) {
5753
- const [typeArg, parserArg, buildArg, optsArg] = node2.arguments;
5754
- if (!typeArg || !parserArg || typeArg.type === "SpreadElement" || parserArg.type === "SpreadElement") return null;
5755
- const typeVal = anyValue(typeArg, scope, code);
5756
- if (typeof typeVal !== "string") return null;
6158
+ const [firstArg, secondArg, thirdArg, fourthArg] = node2.arguments;
6159
+ if (!firstArg || firstArg.type === "SpreadElement") return null;
6160
+ const firstVal = anyValue(firstArg, scope, code, mfs);
6161
+ const explicitType = typeof firstVal === "string" ? firstVal : void 0;
6162
+ const parserArg = explicitType !== void 0 ? secondArg : firstArg;
6163
+ const buildArg = explicitType !== void 0 ? thirdArg : secondArg;
6164
+ const optsArg = explicitType !== void 0 ? fourthArg : thirdArg;
6165
+ if (!parserArg || parserArg.type === "SpreadElement") return null;
5757
6166
  const inner = anyValue(parserArg, scope, code, mfs);
5758
6167
  if (!isCombinator(inner)) return null;
5759
6168
  const be = buildArg;
5760
6169
  const hasBuild = be !== void 0 && be.type !== "SpreadElement" && !(be.type === "Identifier" && be.name === "undefined");
5761
6170
  const buildSrc = hasBuild ? code.slice(be.start, be.end) : void 0;
5762
- const collapse = optsArg !== void 0 && optsArg.type !== "SpreadElement" ? staticCollapseOption(optsArg) : false;
6171
+ const opts = optsArg !== void 0 && optsArg.type !== "SpreadElement" ? staticNodeOptions(optsArg) : void 0;
5763
6172
  try {
5764
- const combi = node(typeVal, inner, hasBuild ? () => null : void 0, collapse ? { collapse: true } : void 0);
6173
+ const combi = explicitType !== void 0 ? node(explicitType, inner, hasBuild ? () => null : void 0, opts) : node(inner, hasBuild ? () => null : void 0, opts);
5765
6174
  if (combi._def.tag === "node" && buildSrc !== void 0) combi._def.buildSrc = buildSrc;
5766
6175
  return combi;
5767
6176
  } catch {
@@ -6027,7 +6436,9 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
6027
6436
  if (!keys) return null;
6028
6437
  const ruleRefs = /* @__PURE__ */ new Map();
6029
6438
  const tagRef = (r, name) => {
6439
+ ;
6030
6440
  r._ruleName = name;
6441
+ if (r._def.tag === "node" && r._def.type === void 0) r._def.type = name;
6031
6442
  };
6032
6443
  for (const key of keys) {
6033
6444
  if (!ruleRefs.has(key)) {
@@ -6059,6 +6470,7 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
6059
6470
  for (const [key, e] of finalByKey) {
6060
6471
  const val = anyValue(e.value, e.scope, e.code, mapFnSources);
6061
6472
  if (!isCombinator(val)) return null;
6473
+ tagRef(val, key);
6062
6474
  ruleRefs.get(key).define(val);
6063
6475
  }
6064
6476
  return ruleRefs;