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
@@ -182,6 +182,13 @@ function ref() {
182
182
  define(p) {
183
183
  if (resolved) throw new Error("ref<T>() already defined");
184
184
  resolved = p;
185
+ meta.firstSet = p._meta.firstSet;
186
+ meta.canMatchNewline = p._meta.canMatchNewline;
187
+ meta.isTrivia = p._meta.isTrivia;
188
+ if (p._meta.triviaKindLabels !== void 0) meta.triviaKindLabels = p._meta.triviaKindLabels;
189
+ else delete meta.triviaKindLabels;
190
+ if (p._meta.disjoint !== void 0) meta.disjoint = p._meta.disjoint;
191
+ else delete meta.disjoint;
185
192
  }
186
193
  };
187
194
  return slot;
@@ -289,7 +296,8 @@ function saveCstMark(ctx) {
289
296
  return {
290
297
  raw: cstRawLen(ctx),
291
298
  tlog: cstTlLen(ctx),
292
- leaves: cstLeavesLen(ctx)
299
+ leaves: cstLeavesLen(ctx),
300
+ fields: ctx._fields?.length ?? 0
293
301
  };
294
302
  }
295
303
  function rollbackBufList(b, keyMulti, keySingle, len) {
@@ -313,11 +321,13 @@ function rollbackCstCapture(ctx, mark) {
313
321
  if (mark.tlog === 0) b.tl = void 0;
314
322
  else b.tl.length = mark.tlog;
315
323
  }
324
+ if (ctx._fields) ctx._fields.length = mark.fields;
316
325
  return;
317
326
  }
318
327
  if (ctx._cstRawChildren) ctx._cstRawChildren.length = mark.raw;
319
328
  if (ctx._cstTriviaLog) ctx._cstTriviaLog.length = mark.tlog;
320
329
  if (ctx._cstLeaves) ctx._cstLeaves.length = mark.leaves;
330
+ if (ctx._fields) ctx._fields.length = mark.fields;
321
331
  }
322
332
  function pushCstTriviaEntry(ctx, start, end, kindIndex) {
323
333
  const insertIdx = cstRawLen(ctx);
@@ -386,13 +396,12 @@ function literal(value, opts = {}) {
386
396
  if (end > input.length) {
387
397
  return failAt(ctx, [JSON.stringify(value)], pos);
388
398
  }
389
- const slice = input.slice(pos, end);
390
- const matched = caseInsensitive ? asciiFoldEq(slice, value) : slice === value;
391
- if (matched) {
399
+ const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
400
+ if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
392
401
  const span = { start: pos, end };
393
- const leaf = { _tag: "leaf", value: slice, span };
402
+ const leaf = { _tag: "leaf", value: matchedValue, span };
394
403
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
395
- return { ok: true, value: slice, span };
404
+ return { ok: true, value: matchedValue, span };
396
405
  }
397
406
  return failAt(ctx, [JSON.stringify(value)], pos);
398
407
  }
@@ -405,10 +414,102 @@ function registerRegexAnalyzer(analyzer) {
405
414
  firstSetAnalyzer = analyzer;
406
415
  }
407
416
  var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
417
+ var SCAN_BAIL_AT = 64;
418
+ var SPACE_RANGES = [
419
+ [9, 13],
420
+ [32, 32],
421
+ [160, 160],
422
+ [5760, 5760],
423
+ [8192, 8202],
424
+ [8232, 8233],
425
+ [8239, 8239],
426
+ [8287, 8287],
427
+ [12288, 12288],
428
+ [65279, 65279]
429
+ ];
430
+ var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
431
+ function shorthandRanges(ch) {
432
+ if (ch === "d") return [[48, 57]];
433
+ if (ch === "s") return SPACE_RANGES;
434
+ return [[48, 57], [65, 90], [97, 122], [95, 95]];
435
+ }
436
+ function inRanges(cp, ranges) {
437
+ for (let i = 0; i < ranges.length; i++) {
438
+ const [lo, hi] = ranges[i];
439
+ if (cp >= lo && cp <= hi) return true;
440
+ }
441
+ return false;
442
+ }
443
+ function readClassRanges(body) {
444
+ if (body.startsWith("^")) return null;
445
+ const ranges = [];
446
+ let i = 0;
447
+ const readAtom = () => {
448
+ const ch = body[i];
449
+ if (ch === void 0) return null;
450
+ if (ch === "\\") {
451
+ const esc = body[i + 1];
452
+ if (esc === void 0) return null;
453
+ i += 2;
454
+ if (esc === "d" || esc === "w" || esc === "s") return { ranges: shorthandRanges(esc) };
455
+ if (esc in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[esc] };
456
+ if (esc >= "a" && esc <= "z" || esc >= "A" && esc <= "Z") return null;
457
+ return { cp: esc.charCodeAt(0) };
458
+ }
459
+ i++;
460
+ return { cp: ch.charCodeAt(0) };
461
+ };
462
+ while (i < body.length) {
463
+ const lo = readAtom();
464
+ if (!lo) return null;
465
+ if ("ranges" in lo) {
466
+ ranges.push(...lo.ranges);
467
+ continue;
468
+ }
469
+ if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
470
+ i++;
471
+ const hi = readAtom();
472
+ if (!hi || "ranges" in hi) return null;
473
+ ranges.push([lo.cp, hi.cp]);
474
+ } else {
475
+ ranges.push([lo.cp, lo.cp]);
476
+ }
477
+ }
478
+ return ranges.length ? ranges : null;
479
+ }
480
+ function shortScanner(source, flags) {
481
+ if (/[imsuvy]/.test(flags)) return null;
482
+ let ranges = null;
483
+ let quant = "";
484
+ if (source[0] === "[") {
485
+ let end = 1;
486
+ while (end < source.length && source[end] !== "]") {
487
+ if (source[end] === "\\") end += 2;
488
+ else end++;
489
+ }
490
+ if (source[end] !== "]") return null;
491
+ ranges = readClassRanges(source.slice(1, end));
492
+ quant = source.slice(end + 1);
493
+ } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
494
+ ranges = shorthandRanges(source[1]);
495
+ quant = source.slice(2);
496
+ }
497
+ if (!ranges || quant !== "+" && quant !== "*") return null;
498
+ const minOne = quant === "+";
499
+ return (input, pos) => {
500
+ let end = pos;
501
+ while (end < input.length && inRanges(input.charCodeAt(end), ranges)) {
502
+ end++;
503
+ if (end - pos >= SCAN_BAIL_AT) return void 0;
504
+ }
505
+ return minOne && end === pos ? null : end;
506
+ };
507
+ }
408
508
  function regex(pattern, flags = "") {
409
509
  const source = typeof pattern === "string" ? pattern : pattern.source;
410
510
  const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
411
511
  const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
512
+ const scan = shortScanner(source, resolvedFlags);
412
513
  const { firstSet, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
413
514
  const meta = { firstSet, canMatchNewline, isTrivia: false };
414
515
  return {
@@ -416,6 +517,15 @@ function regex(pattern, flags = "") {
416
517
  _meta: meta,
417
518
  _def: { tag: "regex", source, flags: resolvedFlags },
418
519
  parse(input, pos, ctx) {
520
+ const scanEnd = scan?.(input, pos);
521
+ if (scanEnd !== void 0) {
522
+ if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
523
+ const value = input.slice(pos, scanEnd);
524
+ const span2 = { start: pos, end: scanEnd };
525
+ const leaf2 = { _tag: "leaf", value, span: span2 };
526
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf2);
527
+ return { ok: true, value, span: span2 };
528
+ }
419
529
  anchored.lastIndex = pos;
420
530
  const m = anchored.exec(input);
421
531
  if (m === null) {
@@ -596,13 +706,10 @@ function choice(...args) {
596
706
  };
597
707
  const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
598
708
  const autoNot = !disjoint && !hasGates && strategy?.tag === "firstMatch" ? computeAutoNot(parsers) : parsers.map(() => null);
599
- let greedyRe = null;
600
709
  let greedyLitMap = null;
601
710
  let sortedParsers = null;
711
+ const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
602
712
  if (strategy?.tag === "greedyClassify") {
603
- const regexDef = getCoreRegexDef(parsers[strategy.superIndex]);
604
- const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
605
- greedyRe = new RegExp(regexDef.source, flags);
606
713
  greedyLitMap = /* @__PURE__ */ new Map();
607
714
  for (let i = 0; i < parsers.length; i++) {
608
715
  if (i === strategy.superIndex) continue;
@@ -627,14 +734,21 @@ function choice(...args) {
627
734
  const expected = [];
628
735
  if (disjoint && pos < input.length) {
629
736
  const code = input.codePointAt(pos);
630
- for (const parser2 of parsers) {
631
- if (inFirstSet(code, parser2._meta.firstSet)) {
632
- const result = parser2.parse(input, pos, ctx);
633
- if (result.ok) return result;
634
- expected.push(...result.expected);
635
- return { ok: false, expected, span: { start: pos, end: pos } };
737
+ let parser2 = code < 128 ? asciiDispatch?.[code] ?? null : null;
738
+ if (!parser2) {
739
+ for (const p of parsers) {
740
+ if (inFirstSet(code, p._meta.firstSet)) {
741
+ parser2 = p;
742
+ break;
743
+ }
636
744
  }
637
745
  }
746
+ if (parser2) {
747
+ const result = parser2.parse(input, pos, ctx);
748
+ if (result.ok) return result;
749
+ expected.push(...result.expected);
750
+ return { ok: false, expected, span: { start: pos, end: pos } };
751
+ }
638
752
  return {
639
753
  ok: false,
640
754
  expected: parsers.flatMap((p) => {
@@ -810,6 +924,19 @@ function areDisjoint(sets) {
810
924
  if (intersects(sets[i], sets[j])) return false;
811
925
  return true;
812
926
  }
927
+ function buildAsciiDispatch(parsers) {
928
+ const table = Array(128).fill(null);
929
+ for (const parser2 of parsers) {
930
+ const fs2 = parser2._meta.firstSet;
931
+ if (fs2.kind !== "ranges") continue;
932
+ for (const { lo, hi } of fs2.ranges) {
933
+ for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
934
+ table[code] = parser2;
935
+ }
936
+ }
937
+ }
938
+ return table;
939
+ }
813
940
 
814
941
  // src/cst/trivia-kinds.ts
815
942
  function unwrapTrivia(p) {
@@ -931,15 +1058,16 @@ function recordTriviaChunks(ctx, chunks) {
931
1058
  // src/combinators/trivia-skip.ts
932
1059
  var NOOP_COMMIT = () => {
933
1060
  };
1061
+ var fastTriviaCache = /* @__PURE__ */ new WeakMap();
934
1062
  function needsDeferredTriviaCommit(ctx) {
935
1063
  return ctx._triviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
936
1064
  }
937
1065
  function saveTriviaMark(ctx) {
938
1066
  const m = saveCstMark(ctx);
939
- return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1067
+ return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
940
1068
  }
941
1069
  function rollbackTrivia(ctx, mark) {
942
- rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves });
1070
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields });
943
1071
  if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
944
1072
  }
945
1073
  function scanWithLabels(input, cur, ctx) {
@@ -957,6 +1085,8 @@ function scanWithLabels(input, cur, ctx) {
957
1085
  function advanceTrivia(input, cur, ctx) {
958
1086
  const triviaP = ctx.trivia;
959
1087
  if (!triviaP) return cur;
1088
+ const fast = fastTriviaScanner(triviaP);
1089
+ if (fast) return fast(input, cur);
960
1090
  if (ctx.triviaKindLabels) {
961
1091
  const scan = scanWithLabels(input, cur, ctx);
962
1092
  return scan.end;
@@ -969,6 +1099,10 @@ function scanTrivia(input, cur, ctx) {
969
1099
  if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
970
1100
  const log = ctx._triviaLog;
971
1101
  const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1102
+ const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1103
+ if (fast && log === void 0 && !captureTl) {
1104
+ return { end: fast(input, cur), commit: NOOP_COMMIT };
1105
+ }
972
1106
  if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
973
1107
  return scanWithLabels(input, cur, ctx);
974
1108
  }
@@ -990,6 +1124,79 @@ function scanTrivia(input, cur, ctx) {
990
1124
  const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
991
1125
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
992
1126
  }
1127
+ function fastTriviaScanner(trivia2) {
1128
+ const cached = fastTriviaCache.get(trivia2);
1129
+ if (cached !== void 0) return cached;
1130
+ const scanner = buildFastTriviaScanner(trivia2);
1131
+ fastTriviaCache.set(trivia2, scanner);
1132
+ return scanner;
1133
+ }
1134
+ function buildFastTriviaScanner(trivia2) {
1135
+ const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1136
+ const direct = regexTriviaScanner(core);
1137
+ if (direct) return direct;
1138
+ const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1139
+ if (!repeat) return null;
1140
+ const one = regexTriviaScanner(repeat);
1141
+ if (one) return loopScanner([one]);
1142
+ if (repeat._def.tag !== "choice") return null;
1143
+ const arms = repeat._def.parsers.map(regexTriviaScanner);
1144
+ if (arms.some((s) => s === null)) return null;
1145
+ return loopScanner(arms);
1146
+ }
1147
+ function loopScanner(arms) {
1148
+ return (input, cur) => {
1149
+ let pos = cur;
1150
+ scan: while (pos < input.length) {
1151
+ for (const arm of arms) {
1152
+ const end = arm(input, pos);
1153
+ if (end > pos) {
1154
+ pos = end;
1155
+ continue scan;
1156
+ }
1157
+ }
1158
+ break;
1159
+ }
1160
+ return pos;
1161
+ };
1162
+ }
1163
+ function regexTriviaScanner(parser2) {
1164
+ if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1165
+ const ws = whitespaceSource(parser2._def.source);
1166
+ if (ws) return ws;
1167
+ return blockCommentSource(parser2._def.source) ? scanBlockComment : null;
1168
+ }
1169
+ function whitespaceSource(source) {
1170
+ const m = /^\[([ \\tnrf]+)\][*+]$/.exec(source);
1171
+ if (!m) return null;
1172
+ const chars = /* @__PURE__ */ new Set();
1173
+ for (let i = 0; i < m[1].length; i++) {
1174
+ const ch = m[1][i];
1175
+ if (ch !== "\\") {
1176
+ chars.add(ch.charCodeAt(0));
1177
+ continue;
1178
+ }
1179
+ const esc = m[1][++i];
1180
+ if (esc === "t") chars.add(9);
1181
+ else if (esc === "n") chars.add(10);
1182
+ else if (esc === "r") chars.add(13);
1183
+ else if (esc === "f") chars.add(12);
1184
+ else return null;
1185
+ }
1186
+ return (input, cur) => {
1187
+ let pos = cur;
1188
+ while (pos < input.length && chars.has(input.charCodeAt(pos))) pos++;
1189
+ return pos;
1190
+ };
1191
+ }
1192
+ function blockCommentSource(source) {
1193
+ return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
1194
+ }
1195
+ function scanBlockComment(input, cur) {
1196
+ if (input.charCodeAt(cur) !== 47 || input.charCodeAt(cur + 1) !== 42) return cur;
1197
+ const close = input.indexOf("*/", cur + 2);
1198
+ return close === -1 ? cur : close + 2;
1199
+ }
993
1200
 
994
1201
  // src/combinators/sequence.ts
995
1202
  function sequence(...parsers) {
@@ -1128,11 +1335,15 @@ function optional(combinator) {
1128
1335
  canMatchNewline: combinator._meta.canMatchNewline,
1129
1336
  isTrivia: false
1130
1337
  };
1338
+ const firstSetSkippable = !matchesEmpty(combinator);
1131
1339
  return {
1132
1340
  _tag: "optional",
1133
1341
  _meta: meta,
1134
1342
  _def: { tag: "optional", parser: combinator },
1135
1343
  parse(input, pos, ctx) {
1344
+ if (firstSetSkippable && ctx._probe === void 0 && !startsFirstSet(combinator, input, pos)) {
1345
+ return { ok: true, value: null, span: { start: pos, end: pos } };
1346
+ }
1136
1347
  const mark = saveTriviaMark(ctx);
1137
1348
  const result = combinator.parse(input, pos, ctx);
1138
1349
  if (result.ok) return result;
@@ -1141,6 +1352,15 @@ function optional(combinator) {
1141
1352
  }
1142
1353
  };
1143
1354
  }
1355
+ function startsFirstSet(combinator, input, pos) {
1356
+ const fs2 = combinator._meta.firstSet;
1357
+ if (fs2.kind === "any") return true;
1358
+ if (fs2.kind === "empty") return false;
1359
+ const code = input.codePointAt(pos);
1360
+ if (code === void 0) return false;
1361
+ for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
1362
+ return false;
1363
+ }
1144
1364
  function sepBy(combinator, separator) {
1145
1365
  const meta = {
1146
1366
  firstSet: combinator._meta.firstSet,
@@ -1231,6 +1451,9 @@ function markUnusedValues(root) {
1231
1451
  case "transform":
1232
1452
  visit(def.parser, true);
1233
1453
  return;
1454
+ case "field":
1455
+ visit(def.parser, true);
1456
+ return;
1234
1457
  case "skip":
1235
1458
  visit(def.main, consumed);
1236
1459
  visit(def.skipped, false);
@@ -1282,6 +1505,11 @@ function markUnusedValues(root) {
1282
1505
  }
1283
1506
 
1284
1507
  // src/combinators/parser.ts
1508
+ function tagRule(r, key) {
1509
+ ;
1510
+ r._ruleName = key;
1511
+ if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
1512
+ }
1285
1513
  function rules(factory) {
1286
1514
  const cache = {};
1287
1515
  const proxy = new Proxy(cache, {
@@ -1290,7 +1518,7 @@ function rules(factory) {
1290
1518
  const record = target;
1291
1519
  if (!(key in record)) {
1292
1520
  const r = ref();
1293
- r._ruleName = key;
1521
+ tagRule(r, key);
1294
1522
  record[key] = r;
1295
1523
  }
1296
1524
  return record[key];
@@ -1301,12 +1529,12 @@ function rules(factory) {
1301
1529
  const placeholder = cache[key];
1302
1530
  const parser2 = definitions[key];
1303
1531
  if (placeholder !== void 0 && typeof placeholder.define === "function") {
1304
- ;
1532
+ tagRule(parser2, key);
1305
1533
  placeholder.define(parser2);
1306
1534
  placeholder._meta.firstSet = parser2._meta.firstSet;
1307
1535
  placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
1308
1536
  } else {
1309
- ;
1537
+ tagRule(parser2, key);
1310
1538
  cache[key] = parser2;
1311
1539
  }
1312
1540
  }
@@ -1363,44 +1591,130 @@ function buildReadsTrivia(def) {
1363
1591
  const src = def.buildSrc ?? def.build.toString();
1364
1592
  const arity = confirmedBuildArity(src);
1365
1593
  if (arity === null) return true;
1366
- return arity >= 4;
1594
+ return arity >= 5;
1367
1595
  }
1368
1596
  function buildReadsState(def) {
1369
1597
  if (!def.build) return true;
1370
1598
  const src = def.buildSrc ?? def.build.toString();
1371
1599
  const arity = confirmedBuildArity(src);
1372
1600
  if (arity === null) return true;
1373
- return arity >= 5;
1601
+ return arity >= 6;
1602
+ }
1603
+
1604
+ // src/compiler/fields.ts
1605
+ function buildReadsFields(def) {
1606
+ if (!def.build) return true;
1607
+ const src = def.buildSrc ?? def.build.toString();
1608
+ const arity = confirmedBuildArity(src);
1609
+ if (arity === null) return true;
1610
+ return arity >= 2;
1611
+ }
1612
+ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
1613
+ if (seen.has(p)) return false;
1614
+ seen.add(p);
1615
+ const d = p._def;
1616
+ switch (d.tag) {
1617
+ case "field":
1618
+ return true;
1619
+ case "node":
1620
+ return false;
1621
+ case "lazy": {
1622
+ try {
1623
+ return parserHasOwnFields(d.thunk(), seen);
1624
+ } catch {
1625
+ return false;
1626
+ }
1627
+ }
1628
+ case "sequence":
1629
+ case "choice":
1630
+ return d.parsers.some((x) => parserHasOwnFields(x, seen));
1631
+ case "sepBy":
1632
+ return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
1633
+ case "skip":
1634
+ return parserHasOwnFields(d.main, seen) || parserHasOwnFields(d.skipped, seen);
1635
+ case "grammar":
1636
+ return parserHasOwnFields(d.parser, seen) || (d.triviaParser ? parserHasOwnFields(d.triviaParser, seen) : false);
1637
+ case "scanTo":
1638
+ return parserHasOwnFields(d.sentinel, seen) || d.skip.some((x) => parserHasOwnFields(x, seen));
1639
+ case "recover":
1640
+ return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.sentinel, seen);
1641
+ case "many":
1642
+ case "oneOrMore":
1643
+ case "optional":
1644
+ case "transform":
1645
+ case "trivia":
1646
+ case "token":
1647
+ case "label":
1648
+ case "expect":
1649
+ case "withCtx":
1650
+ case "not":
1651
+ return parserHasOwnFields(d.parser, seen);
1652
+ default:
1653
+ return false;
1654
+ }
1655
+ }
1656
+ function buildFieldMap(captures) {
1657
+ if (!captures?.length) return void 0;
1658
+ const out = {};
1659
+ for (const cap of captures) {
1660
+ const entry = { value: cap.value, span: cap.span };
1661
+ const current = out[cap.name];
1662
+ if (current === void 0) out[cap.name] = entry;
1663
+ else if (Array.isArray(current)) current.push(entry);
1664
+ else out[cap.name] = [current, entry];
1665
+ }
1666
+ return out;
1374
1667
  }
1375
1668
 
1376
1669
  // src/combinators/node.ts
1377
- function collapseChild(child) {
1670
+ function unwrapChild(child) {
1378
1671
  return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
1379
1672
  }
1380
- function node(type, combinator, build, opts) {
1673
+ function isCstChild(value) {
1674
+ return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "error");
1675
+ }
1676
+ function missingInferredType() {
1677
+ throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
1678
+ }
1679
+ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
1680
+ const hasExplicitType = typeof typeOrCombinator === "string";
1681
+ const type = hasExplicitType ? typeOrCombinator : void 0;
1682
+ const combinator = hasExplicitType ? combinatorOrBuild : typeOrCombinator;
1683
+ const build = hasExplicitType ? buildOrOpts : combinatorOrBuild;
1684
+ const opts = hasExplicitType ? maybeOpts : buildOrOpts;
1685
+ const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build } };
1381
1686
  const meta = {
1382
1687
  firstSet: combinator._meta.firstSet,
1383
1688
  canMatchNewline: combinator._meta.canMatchNewline,
1384
1689
  isTrivia: false
1385
1690
  };
1691
+ const unwrap = opts?.unwrap === true;
1386
1692
  const collapse = opts?.collapse === true;
1387
- const def = collapse ? { tag: "node", type, parser: combinator, build, collapse: true } : { tag: "node", type, parser: combinator, build };
1693
+ if (unwrap && collapse) {
1694
+ throw new Error("node() options cannot set both unwrap and collapse");
1695
+ }
1696
+ const def = unwrap || collapse ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {} } : baseDef;
1388
1697
  const capturesTrivia = build ? buildReadsTrivia(def) : true;
1389
1698
  const clonesState = build ? buildReadsState(def) : true;
1699
+ const capturesFields = parserHasOwnFields(combinator) && (build ? buildReadsFields(def) : true);
1390
1700
  return {
1391
1701
  _tag: "node",
1392
1702
  _meta: meta,
1393
1703
  _def: def,
1394
1704
  parse(input, pos, ctx) {
1395
1705
  const saved = beginCstNodeCapture(ctx);
1706
+ const savedFields = ctx._fields;
1707
+ ctx._fields = capturesFields ? [] : void 0;
1396
1708
  if (!capturesTrivia) ctx.captureTrivia = false;
1397
1709
  const r = combinator.parse(input, pos, ctx);
1710
+ const fields = capturesFields ? buildFieldMap(ctx._fields) : void 0;
1711
+ ctx._fields = savedFields;
1398
1712
  const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
1399
1713
  if (!r.ok) return r;
1400
1714
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
1401
- 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 };
1402
- const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
1403
- const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
1715
+ const nodeType = def.type ?? missingInferredType();
1716
+ 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 };
1717
+ const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
1404
1718
  if (saved.buf !== void 0 || saved.ch !== void 0) {
1405
1719
  pushCstChild(ctx, built, rawEntry);
1406
1720
  }
@@ -1418,7 +1732,7 @@ function transform(combinator, fn) {
1418
1732
  parse(input, pos, ctx) {
1419
1733
  const result = combinator.parse(input, pos, ctx);
1420
1734
  if (!result.ok) return result;
1421
- return { ...result, value: fn(result.value, result.span) };
1735
+ return { ok: true, value: fn(result.value, result.span), span: result.span };
1422
1736
  }
1423
1737
  };
1424
1738
  }
@@ -1432,7 +1746,7 @@ function skip(main, skipped) {
1432
1746
  if (!result.ok) return result;
1433
1747
  const s = skipped.parse(input, result.span.end, ctx);
1434
1748
  if (!s.ok) return result;
1435
- return { ...result, span: { start: result.span.start, end: s.span.end } };
1749
+ return { ok: true, value: result.value, span: { start: result.span.start, end: s.span.end } };
1436
1750
  }
1437
1751
  };
1438
1752
  }
@@ -1456,7 +1770,20 @@ function label(name, combinator) {
1456
1770
  _def: { tag: "label", label: name, parser: combinator },
1457
1771
  parse(input, pos, ctx) {
1458
1772
  const result = combinator.parse(input, pos, ctx);
1459
- if (!result.ok) return { ...result, expected: [name] };
1773
+ if (!result.ok) return { ok: false, expected: [name], span: result.span };
1774
+ return result;
1775
+ }
1776
+ };
1777
+ }
1778
+ function field(name, combinator) {
1779
+ return {
1780
+ _tag: combinator._tag,
1781
+ _meta: combinator._meta,
1782
+ _def: { tag: "field", name, parser: combinator },
1783
+ parse(input, pos, ctx) {
1784
+ const result = combinator.parse(input, pos, ctx);
1785
+ if (!result.ok) return result;
1786
+ ctx._fields?.push({ name, value: result.value, span: result.span });
1460
1787
  return result;
1461
1788
  }
1462
1789
  };
@@ -1647,9 +1974,9 @@ function expect(combinator, label2) {
1647
1974
 
1648
1975
  // src/compiler/scannable-run.ts
1649
1976
  var BACKSLASH = 92;
1650
- var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1977
+ var CLASS_ESCAPES2 = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1651
1978
  var META = new Set("()[]{}*+?|^$.".split(""));
1652
- var SPACE_RANGES = [
1979
+ var SPACE_RANGES2 = [
1653
1980
  [9, 13],
1654
1981
  [32, 32],
1655
1982
  [160, 160],
@@ -1662,9 +1989,9 @@ var SPACE_RANGES = [
1662
1989
  [12288, 12288],
1663
1990
  [65279, 65279]
1664
1991
  ];
1665
- function shorthandRanges(ch) {
1992
+ function shorthandRanges2(ch) {
1666
1993
  if (ch === "d") return [[48, 57]];
1667
- if (ch === "s") return SPACE_RANGES;
1994
+ if (ch === "s") return SPACE_RANGES2;
1668
1995
  return [[48, 57], [65, 90], [97, 122], [95, 95]];
1669
1996
  }
1670
1997
  function readUnicodeEscape(body, i) {
@@ -1688,8 +2015,8 @@ function parseClassRanges(body) {
1688
2015
  const e = body[i + 1];
1689
2016
  if (e === void 0) return null;
1690
2017
  i += 2;
1691
- if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1692
- if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
2018
+ if (e in CLASS_ESCAPES2) return { cp: CLASS_ESCAPES2[e] };
2019
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges2(e) };
1693
2020
  if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1694
2021
  return { cp: e.codePointAt(0) };
1695
2022
  }
@@ -1715,9 +2042,9 @@ function parseClassRanges(body) {
1715
2042
  return ranges.length ? ranges : null;
1716
2043
  }
1717
2044
  function classToRanges(cls) {
1718
- if (cls === "\\d") return shorthandRanges("d");
1719
- if (cls === "\\w") return shorthandRanges("w");
1720
- if (cls === "\\s") return shorthandRanges("s");
2045
+ if (cls === "\\d") return shorthandRanges2("d");
2046
+ if (cls === "\\w") return shorthandRanges2("w");
2047
+ if (cls === "\\s") return shorthandRanges2("s");
1721
2048
  const body = cls.slice(1, -1);
1722
2049
  if (body.startsWith("^")) return null;
1723
2050
  return parseClassRanges(body);
@@ -1735,7 +2062,7 @@ function literalCodePoints(frag) {
1735
2062
  if (ch === "\\") {
1736
2063
  const e = frag[i + 1];
1737
2064
  if (e === void 0) return null;
1738
- out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
2065
+ out.push(e in CLASS_ESCAPES2 ? CLASS_ESCAPES2[e] : e.codePointAt(0));
1739
2066
  i += 2;
1740
2067
  continue;
1741
2068
  }
@@ -1828,7 +2155,7 @@ function parseSeqParts(source) {
1828
2155
  if (e === "d" || e === "w" || e === "s") {
1829
2156
  flush();
1830
2157
  i += 2;
1831
- i += runFrom(shorthandRanges(e), false, source[i]);
2158
+ i += runFrom(shorthandRanges2(e), false, source[i]);
1832
2159
  continue;
1833
2160
  }
1834
2161
  const uni = readUnicodeEscape(source, i);
@@ -1837,8 +2164,8 @@ function parseSeqParts(source) {
1837
2164
  cp2 = uni.cp;
1838
2165
  i = uni.next;
1839
2166
  } else if (e === void 0) return null;
1840
- else if (e in CLASS_ESCAPES) {
1841
- cp2 = CLASS_ESCAPES[e];
2167
+ else if (e in CLASS_ESCAPES2) {
2168
+ cp2 = CLASS_ESCAPES2[e];
1842
2169
  i += 2;
1843
2170
  } else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1844
2171
  else {
@@ -1945,7 +2272,7 @@ function rangesSubset(sub, sup) {
1945
2272
  }
1946
2273
  function parseClassOperand(body) {
1947
2274
  if (body === "\\d" || body === "\\w" || body === "\\s") {
1948
- return { ranges: shorthandRanges(body[1]), negated: false };
2275
+ return { ranges: shorthandRanges2(body[1]), negated: false };
1949
2276
  }
1950
2277
  if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
1951
2278
  let inner = body.slice(1, -1);
@@ -2378,7 +2705,7 @@ function parseScanShapeCore(source) {
2378
2705
  }
2379
2706
  m = /^\\([dws])([+*])$/.exec(source);
2380
2707
  if (m) {
2381
- return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
2708
+ return { kind: "chars", ranges: shorthandRanges2(m[1]), minOne: m[2] === "+" };
2382
2709
  }
2383
2710
  m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
2384
2711
  if (m) {
@@ -2920,21 +3247,22 @@ function emitScannableTerminal(shape, c) {
2920
3247
  }
2921
3248
 
2922
3249
  // src/compiler/inline-build.ts
2923
- 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*$/;
3250
+ 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*$/;
2924
3251
  function escapeRegExp(s) {
2925
3252
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2926
3253
  }
2927
3254
  function looseMkBuildRe(type) {
2928
3255
  return new RegExp(
2929
- 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*$`
3256
+ 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*$`
2930
3257
  );
2931
3258
  }
2932
3259
  function analyzeMkInlineBuild(def) {
2933
3260
  if (!def.build) return null;
3261
+ if (def.type === void 0) return null;
2934
3262
  const src = (def.buildSrc ?? def.build.toString()).trim();
2935
3263
  const strict = src.match(MK_BUILD_RE);
2936
3264
  if (strict) {
2937
- const mkType = strict[2];
3265
+ const mkType = strict[6];
2938
3266
  return mkType === def.type ? def.type : null;
2939
3267
  }
2940
3268
  if (looseMkBuildRe(def.type).test(src)) return def.type;
@@ -3269,8 +3597,9 @@ function planDisjointDispatch(parsers) {
3269
3597
  }
3270
3598
  return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
3271
3599
  }
3272
- function captureRestoreBody(mL, mR, mTl, mLg) {
3273
- const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
3600
+ function captureRestoreBody(mL, mR, mTl, mLg, mF = null) {
3601
+ const fieldRestore = mF ? `; if (_ctx._fields) _ctx._fields.length = ${mF}` : "";
3602
+ const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}${fieldRestore}`;
3274
3603
  return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
3275
3604
  }
3276
3605
  function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
@@ -3318,6 +3647,7 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
3318
3647
  // Delegating wrappers: defer to the wrapped parser.
3319
3648
  case "transform":
3320
3649
  case "label":
3650
+ case "field":
3321
3651
  case "expect":
3322
3652
  case "withCtx":
3323
3653
  case "grammar":
@@ -3364,6 +3694,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
3364
3694
  case "optional":
3365
3695
  case "transform":
3366
3696
  case "label":
3697
+ case "field":
3367
3698
  case "expect":
3368
3699
  case "withCtx":
3369
3700
  case "grammar":
@@ -3401,22 +3732,25 @@ function emitFallible(inner, ctx, pos, swallow = false) {
3401
3732
  ctx.recordFail = savedRecord;
3402
3733
  const ind0 = ind(ctx);
3403
3734
  const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
3735
+ const needsFieldRollback = needsRollback && parserHasOwnFields(inner);
3404
3736
  const mL = needsRollback ? v(ctx, "_fcl") : null;
3405
3737
  const mR = needsRollback ? v(ctx, "_fcr") : null;
3406
3738
  const mTl = needsRollback ? v(ctx, "_fctl") : null;
3739
+ const mF = needsFieldRollback ? v(ctx, "_fcf") : null;
3407
3740
  const stmts = [
3408
3741
  `${ind0}let ${okV} = false, ${valV}, ${endV} = ${pos}`,
3409
3742
  ...mL ? [
3410
3743
  `${ind0}const ${mL} = _ctx._cstLeaves?.length ?? 0`,
3411
3744
  `${ind0}const ${mR} = _ctx._cstRawChildren?.length ?? 0`,
3412
- `${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0`
3745
+ `${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0`,
3746
+ ...mF ? [`${ind0}const ${mF} = _ctx._fields?.length ?? 0`] : []
3413
3747
  ] : [],
3414
3748
  `${ind0}${lbl}: {`,
3415
3749
  ...r.stmts,
3416
3750
  `${ind0} ${valV} = ${r.valueVar}; ${endV} = ${r.endVar}; ${okV} = true`,
3417
3751
  `${ind0}}`,
3418
3752
  ...mL ? [
3419
- `${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null)} }`
3753
+ `${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null, mF)} }`
3420
3754
  ] : []
3421
3755
  ];
3422
3756
  return { stmts, okVar: okV, valVar: valV, endVar: endV };
@@ -3643,6 +3977,7 @@ function failsAtStart(p) {
3643
3977
  return true;
3644
3978
  case "transform":
3645
3979
  case "label":
3980
+ case "field":
3646
3981
  return failsAtStart(d.parser);
3647
3982
  default:
3648
3983
  return false;
@@ -3662,6 +3997,7 @@ function canMatchEmptyAtStart(p) {
3662
3997
  return true;
3663
3998
  case "transform":
3664
3999
  case "label":
4000
+ case "field":
3665
4001
  return canMatchEmptyAtStart(d.parser);
3666
4002
  case "literal":
3667
4003
  return d.value.length === 0;
@@ -3844,11 +4180,13 @@ function emitFirstMatch(def, ctx, pos) {
3844
4180
  const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
3845
4181
  const armHasAutoNot = !!(autoNot && autoNot.length > 0);
3846
4182
  const armNeedsRollback = ctx.capturing && (mayLeavePartialCapture(p) || armHasAutoNot && capturesLeaf(p));
4183
+ const armNeedsFieldRollback = armNeedsRollback && parserHasOwnFields(p);
3847
4184
  const markLeaves = armNeedsRollback ? v(ctx, "_cml") : null;
3848
4185
  const markRaw = armNeedsRollback ? v(ctx, "_cmr") : null;
3849
4186
  const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
3850
4187
  const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
3851
- const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
4188
+ const markFields = armNeedsFieldRollback ? v(ctx, "_cmf") : null;
4189
+ const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog, markFields) : "";
3852
4190
  const failSlot = atStart ? staticFx : "_ctx._fx";
3853
4191
  stmts.push(`${ind0}if (${skipCond}) {`);
3854
4192
  if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
@@ -3858,7 +4196,8 @@ function emitFirstMatch(def, ctx, pos) {
3858
4196
  `${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
3859
4197
  `${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
3860
4198
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
3861
- `${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
4199
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`,
4200
+ ...markFields ? [`${ind(ctx)}const ${markFields} = _ctx._fields?.length ?? 0`] : []
3862
4201
  );
3863
4202
  }
3864
4203
  const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
@@ -4251,6 +4590,9 @@ function emitToken(def, ctx, pos) {
4251
4590
  };
4252
4591
  }
4253
4592
  function emitNode(def, ctx, pos) {
4593
+ if (def.type === void 0) {
4594
+ throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
4595
+ }
4254
4596
  const structural = def.build === void 0;
4255
4597
  const mkType = structural ? null : analyzeMkInlineBuild(def);
4256
4598
  let buildIdx = null;
@@ -4262,25 +4604,36 @@ function emitNode(def, ctx, pos) {
4262
4604
  const i = ind(ctx);
4263
4605
  const capturesTrivia = mkType !== null || !structural && buildReadsTrivia(def);
4264
4606
  const clonesState = !structural && buildReadsState(def);
4607
+ const hasFields = parserHasOwnFields(def.parser);
4608
+ const capturesFields = hasFields && !structural && buildReadsFields(def);
4265
4609
  const chV = v(ctx, "_ch");
4266
4610
  const rawV = v(ctx, "_raw");
4267
4611
  const capTLv = structural ? v(ctx, "_ctl") : null;
4268
4612
  const capSTv = structural ? v(ctx, "_cst") : null;
4613
+ const capFv = structural && hasFields ? v(ctx, "_cf") : null;
4269
4614
  const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
4270
4615
  if (!capturesTrivia) ctx.needsEmptyTl = true;
4271
4616
  const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
4272
4617
  if (structural) ctx.needsHostReads = true;
4273
- const allocStmt = structural ? `${i}const ${capTLv} = _ctx._pmCapTL ??= _hostReads(_ctx.build, 4), ${capSTv} = _ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 5))
4618
+ const hostTriviaGate = `_ctx.build !== undefined && (_ctx.build._parsemanCaptureTrivia !== undefined ? _ctx.build._parsemanCaptureTrivia(${JSON.stringify(def.type)}) : (_ctx._pmCapTL ??= _hostReads(_ctx.build, 5)))`;
4619
+ 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)` : ""}
4274
4620
  ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
4275
4621
  const innerTl = structural ? `${capTLv} ? ${tlV} : undefined` : capturesTrivia ? tlV : "undefined";
4622
+ const fieldsOn = structural ? capFv ?? "false" : capturesFields ? "true" : "false";
4623
+ const sf = hasFields ? v(ctx, "_sf") : null;
4624
+ const fArr = hasFields ? v(ctx, "_fa") : null;
4625
+ const fObj = hasFields ? v(ctx, "_fields") : "undefined";
4276
4626
  const stmts = [
4277
4627
  allocStmt,
4278
- `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
4279
- `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}`
4628
+ `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog${sf ? `, ${sf} = _ctx._fields` : ""}`,
4629
+ `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}${sf ? `; _ctx._fields = ${fieldsOn} ? [] : undefined` : ""}`
4280
4630
  ];
4281
4631
  const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
4282
4632
  stmts.push(...innerStmts);
4283
- stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
4633
+ if (sf && fArr) {
4634
+ stmts.push(`${i}const ${fArr} = _ctx._fields`);
4635
+ }
4636
+ stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${sf ? `; _ctx._fields = ${sf}` : ""}`);
4284
4637
  stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
4285
4638
  let stV = "undefined";
4286
4639
  if (structural) {
@@ -4290,14 +4643,33 @@ ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capt
4290
4643
  stV = v(ctx, "_nst");
4291
4644
  stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
4292
4645
  }
4646
+ if (sf && fArr) {
4647
+ const fe = v(ctx, "_fe");
4648
+ const cur = v(ctx, "_fc");
4649
+ stmts.push(
4650
+ `${i}let ${fObj} = undefined`,
4651
+ `${i}if (${fArr} && ${fArr}.length) {`,
4652
+ `${i} ${fObj} = {}`,
4653
+ `${i} for (const ${fe} of ${fArr}) {`,
4654
+ `${i} const ${cur} = ${fObj}[${fe}.name], _entry = { value: ${fe}.value, span: ${fe}.span }`,
4655
+ `${i} if (${cur} === undefined) ${fObj}[${fe}.name] = _entry`,
4656
+ `${i} else if (Array.isArray(${cur})) ${cur}.push(_entry)`,
4657
+ `${i} else ${fObj}[${fe}.name] = [${cur}, _entry]`,
4658
+ `${i} }`,
4659
+ `${i}}`
4660
+ );
4661
+ }
4293
4662
  const ndV = v(ctx, "_nd");
4294
- 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})`;
4295
- const ndExpr = ctx.ns || structural ? `_ctx.build !== undefined ? _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV}) : (${buildExpr})` : buildExpr;
4296
- 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;
4663
+ 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})`;
4664
+ 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;
4665
+ 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;
4666
+ const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
4667
+ const collapseExpr = `${chV}.length === 1 ? ${chV}[0] : (${ndExpr})`;
4668
+ const finalExpr = def.unwrap ? unwrapExpr : def.collapse ? collapseExpr : hostCollapseExpr;
4297
4669
  stmts.push(
4298
4670
  `${i}const ${ndV} = ${finalExpr}`,
4299
4671
  `${i}if (${sc}) ${sc}.push(${ndV})`,
4300
- `${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} } })`
4672
+ `${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} } })`
4301
4673
  );
4302
4674
  return { stmts, valueVar: ndV, endVar };
4303
4675
  }
@@ -4512,6 +4884,17 @@ function emitDispatch(p, ctx, pos) {
4512
4884
  endVar: inner.endVar
4513
4885
  };
4514
4886
  }
4887
+ case "field": {
4888
+ const inner = emit(def.parser, ctx, pos);
4889
+ return {
4890
+ stmts: [
4891
+ ...inner.stmts,
4892
+ `${ind(ctx)}if (_ctx._fields) _ctx._fields.push({ name: ${JSON.stringify(def.name)}, value: ${inner.valueVar}, span: { start: ${pos}, end: ${inner.endVar} } })`
4893
+ ],
4894
+ valueVar: inner.valueVar,
4895
+ endVar: inner.endVar
4896
+ };
4897
+ }
4515
4898
  case "grammar": {
4516
4899
  const savedTrivia = ctx.activeTrivia;
4517
4900
  const savedKindLabels = ctx.triviaKindLabels;
@@ -4613,6 +4996,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
4613
4996
  case "trivia":
4614
4997
  case "token":
4615
4998
  case "label":
4999
+ case "field":
4616
5000
  case "optional":
4617
5001
  case "many":
4618
5002
  case "oneOrMore":
@@ -4649,6 +5033,7 @@ function childrenOf(def) {
4649
5033
  case "trivia":
4650
5034
  case "token":
4651
5035
  case "label":
5036
+ case "field":
4652
5037
  case "grammar":
4653
5038
  case "not":
4654
5039
  case "node":
@@ -4890,6 +5275,11 @@ function assertRuleName(name) {
4890
5275
  );
4891
5276
  }
4892
5277
  }
5278
+ function publicRuleWrapperSource(rule, fnSource) {
5279
+ const labels = rule._meta.triviaKindLabels;
5280
+ if (labels === void 0) return fnSource;
5281
+ return `Object.assign(${fnSource}, { _meta: { triviaKindLabels: ${JSON.stringify(labels)} } })`;
5282
+ }
4893
5283
  function compileRuleMap(ruleMap) {
4894
5284
  for (const [, rule] of ruleMap) markUnusedValues(rule);
4895
5285
  const ctx = {
@@ -4922,7 +5312,7 @@ function compileRuleMap(ruleMap) {
4922
5312
  ruleNames.set(rule, n === 0 ? base : `${base}$${n}`);
4923
5313
  }
4924
5314
  ctx.ruleNames = ruleNames;
4925
- const perEntry = ruleMap.map(([key, rule]) => ({ key, r: emit(rule, ctx, "_pos") }));
5315
+ const perEntry = ruleMap.map(([key, rule]) => ({ key, rule, r: emit(rule, ctx, "_pos") }));
4926
5316
  const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
4927
5317
  const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
4928
5318
  const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
@@ -4952,7 +5342,10 @@ function compileRuleMap(ruleMap) {
4952
5342
  ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
4953
5343
  `}`
4954
5344
  ].join("\n");
4955
- const objBody = perEntry.map(({ key, r }) => ` ${JSON.stringify(key)}: ${entryFnText(r).split("\n").join("\n ")}`).join(",\n");
5345
+ const objBody = perEntry.map(({ key, rule, r }) => {
5346
+ const src = publicRuleWrapperSource(rule, entryFnText(r));
5347
+ return ` ${JSON.stringify(key)}: ${src.split("\n").join("\n ")}`;
5348
+ }).join(",\n");
4956
5349
  const replacement = [
4957
5350
  `/* @__PURE__ */ (() => {`,
4958
5351
  ...hoistedDecls,
@@ -5065,7 +5458,7 @@ function compileLinkable(ruleMapArg, ns) {
5065
5458
  ctx.recordFail = savedRec;
5066
5459
  pushNamedFnDecl(ctx, fn, body.stmts, body.valueVar, body.endVar);
5067
5460
  }
5068
- perEntry.push({ key, r: emitNamedFnCall(ctx, fn, "_pos") });
5461
+ perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
5069
5462
  }
5070
5463
  if (ctx.runtimeParsers.length !== 0) return null;
5071
5464
  const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
@@ -5088,14 +5481,14 @@ function compileLinkable(ruleMapArg, ns) {
5088
5481
  ...privateFns
5089
5482
  ].filter(Boolean);
5090
5483
  const wrappers = /* @__PURE__ */ new Map();
5091
- for (const { key, r } of perEntry) {
5092
- wrappers.set(key, [
5484
+ for (const { key, rule, r } of perEntry) {
5485
+ wrappers.set(key, publicRuleWrapperSource(rule, [
5093
5486
  `function(input, _pos, _ctx) {`,
5094
5487
  ` let pos = _pos`,
5095
5488
  ...r.stmts,
5096
5489
  ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
5097
5490
  `}`
5098
- ].join("\n"));
5491
+ ].join("\n")));
5099
5492
  }
5100
5493
  const firstSets = /* @__PURE__ */ new Map();
5101
5494
  for (const [key, rule] of ruleMap) {
@@ -5270,6 +5663,7 @@ function childrenOf2(def) {
5270
5663
  case "trivia":
5271
5664
  case "token":
5272
5665
  case "label":
5666
+ case "field":
5273
5667
  case "not":
5274
5668
  case "node":
5275
5669
  case "expect":
@@ -5355,6 +5749,7 @@ function evalRuleMapIR(ir) {
5355
5749
  "skip",
5356
5750
  "trivia",
5357
5751
  "label",
5752
+ "field",
5358
5753
  "expect",
5359
5754
  "_tf",
5360
5755
  "_nd",
@@ -5381,6 +5776,7 @@ function evalRuleMapIR(ir) {
5381
5776
  skip,
5382
5777
  trivia,
5383
5778
  label,
5779
+ field,
5384
5780
  expect,
5385
5781
  _tf,
5386
5782
  _nd
@@ -5514,6 +5910,8 @@ ${body}
5514
5910
  return `token(${kid(def.parser)})`;
5515
5911
  case "label":
5516
5912
  return `label(${JSON.stringify(def.label)}, ${kid(def.parser)})`;
5913
+ case "field":
5914
+ return `field(${JSON.stringify(def.name)}, ${kid(def.parser)})`;
5517
5915
  case "expect":
5518
5916
  return `expect(${kid(def.parser)}${def.label !== void 0 ? `, ${JSON.stringify(def.label)}` : ""})`;
5519
5917
  case "skip":
@@ -5526,7 +5924,11 @@ ${body}
5526
5924
  }
5527
5925
  case "node": {
5528
5926
  if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
5529
- const opts = def.collapse ? `, { collapse: true }` : "";
5927
+ const opts = def.unwrap || def.collapse ? `, { ${def.unwrap ? "unwrap: true" : "collapse: true"} }` : "";
5928
+ if (def.type === void 0) {
5929
+ if (def.buildSrc !== void 0) throw new Unserializable("inferred node build without inferred type");
5930
+ return opts ? `node(${kid(def.parser)}, undefined${opts})` : `node(${kid(def.parser)})`;
5931
+ }
5530
5932
  if (def.buildSrc !== void 0) return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts})`;
5531
5933
  return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
5532
5934
  }
@@ -5553,8 +5955,9 @@ function fusedBody(pieces) {
5553
5955
  }
5554
5956
  }
5555
5957
  const contributing = new Set(winner.values());
5556
- const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
5557
- const needsHostReads = [...contributing].some((p) => p.needsHostReads);
5958
+ const contributingPieces = [...contributing];
5959
+ const needsEmptyTl = contributingPieces.some((p) => p.needsEmptyTl);
5960
+ const needsHostReads = contributingPieces.some((p) => p.needsHostReads);
5558
5961
  const lines = [
5559
5962
  // Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
5560
5963
  "const _pfFail = {}",
@@ -5562,7 +5965,7 @@ function fusedBody(pieces) {
5562
5965
  ...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
5563
5966
  ...needsHostReads ? [HOST_READS_DECL] : [],
5564
5967
  // Each contributing artifact's namespaced private prelude (regexes, _pf, …).
5565
- ...[...contributing].flatMap((p) => p.prelude),
5968
+ ...[...new Set(contributingPieces.flatMap((p) => p.prelude))],
5566
5969
  // The winning `_r_<Name>` function for each rule (one per name → no redeclare).
5567
5970
  ...[...winner].map(([k, p]) => p.ruleFns.get(k))
5568
5971
  ];
@@ -5585,7 +5988,7 @@ function fusedBody(pieces) {
5585
5988
  }
5586
5989
  );
5587
5990
  const env = {};
5588
- for (const p of contributing) {
5991
+ for (const p of contributingPieces) {
5589
5992
  if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
5590
5993
  if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
5591
5994
  }
@@ -5633,6 +6036,7 @@ var SUPPORTED = {
5633
6036
  sepBy: (...a) => sepBy(a[0], a[1]),
5634
6037
  trivia: (...a) => trivia(a[0]),
5635
6038
  label: (...a) => label(a[0], a[1]),
6039
+ field: (...a) => field(a[0], a[1]),
5636
6040
  noTrivia: (...a) => noTrivia(a[0]),
5637
6041
  token: (...a) => token(a[0]),
5638
6042
  expect: (...a) => expect(a[0], a[1])
@@ -5653,19 +6057,20 @@ function scopeGet(scope, name, mfs) {
5653
6057
  if (isCombinator(entry)) return entry;
5654
6058
  return null;
5655
6059
  }
5656
- function staticCollapseOption(expr) {
5657
- if (expr.type !== "ObjectExpression") return false;
6060
+ function staticNodeOptions(expr) {
6061
+ if (expr.type !== "ObjectExpression") return void 0;
6062
+ const opts = {};
5658
6063
  for (const prop of expr.properties) {
5659
6064
  const p = prop;
5660
6065
  if (p.computed) continue;
5661
6066
  const key = p.key;
5662
6067
  const name = key.type === "Identifier" ? key.name : key.type === "Literal" ? String(key.value) : void 0;
5663
- if (name === "collapse") {
6068
+ if (name === "unwrap" || name === "collapse") {
5664
6069
  const val = p.value;
5665
- return (val.type === "Literal" || val.type === "BooleanLiteral") && val.value === true;
6070
+ if ((val.type === "Literal" || val.type === "BooleanLiteral") && val.value === true) opts[name] = true;
5666
6071
  }
5667
6072
  }
5668
- return false;
6073
+ return opts.unwrap || opts.collapse ? opts : void 0;
5669
6074
  }
5670
6075
  function exprToCombi(node2, scope, code, mfs) {
5671
6076
  if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
@@ -5715,18 +6120,22 @@ function exprToCombi(node2, scope, code, mfs) {
5715
6120
  }
5716
6121
  }
5717
6122
  if (callee.name === "node" && code !== void 0) {
5718
- const [typeArg, parserArg, buildArg, optsArg] = node2.arguments;
5719
- if (!typeArg || !parserArg || typeArg.type === "SpreadElement" || parserArg.type === "SpreadElement") return null;
5720
- const typeVal = anyValue(typeArg, scope, code);
5721
- if (typeof typeVal !== "string") return null;
6123
+ const [firstArg, secondArg, thirdArg, fourthArg] = node2.arguments;
6124
+ if (!firstArg || firstArg.type === "SpreadElement") return null;
6125
+ const firstVal = anyValue(firstArg, scope, code, mfs);
6126
+ const explicitType = typeof firstVal === "string" ? firstVal : void 0;
6127
+ const parserArg = explicitType !== void 0 ? secondArg : firstArg;
6128
+ const buildArg = explicitType !== void 0 ? thirdArg : secondArg;
6129
+ const optsArg = explicitType !== void 0 ? fourthArg : thirdArg;
6130
+ if (!parserArg || parserArg.type === "SpreadElement") return null;
5722
6131
  const inner = anyValue(parserArg, scope, code, mfs);
5723
6132
  if (!isCombinator(inner)) return null;
5724
6133
  const be = buildArg;
5725
6134
  const hasBuild = be !== void 0 && be.type !== "SpreadElement" && !(be.type === "Identifier" && be.name === "undefined");
5726
6135
  const buildSrc = hasBuild ? code.slice(be.start, be.end) : void 0;
5727
- const collapse = optsArg !== void 0 && optsArg.type !== "SpreadElement" ? staticCollapseOption(optsArg) : false;
6136
+ const opts = optsArg !== void 0 && optsArg.type !== "SpreadElement" ? staticNodeOptions(optsArg) : void 0;
5728
6137
  try {
5729
- const combi = node(typeVal, inner, hasBuild ? () => null : void 0, collapse ? { collapse: true } : void 0);
6138
+ const combi = explicitType !== void 0 ? node(explicitType, inner, hasBuild ? () => null : void 0, opts) : node(inner, hasBuild ? () => null : void 0, opts);
5730
6139
  if (combi._def.tag === "node" && buildSrc !== void 0) combi._def.buildSrc = buildSrc;
5731
6140
  return combi;
5732
6141
  } catch {
@@ -5992,7 +6401,9 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
5992
6401
  if (!keys) return null;
5993
6402
  const ruleRefs = /* @__PURE__ */ new Map();
5994
6403
  const tagRef = (r, name) => {
6404
+ ;
5995
6405
  r._ruleName = name;
6406
+ if (r._def.tag === "node" && r._def.type === void 0) r._def.type = name;
5996
6407
  };
5997
6408
  for (const key of keys) {
5998
6409
  if (!ruleRefs.has(key)) {
@@ -6024,6 +6435,7 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
6024
6435
  for (const [key, e] of finalByKey) {
6025
6436
  const val = anyValue(e.value, e.scope, e.code, mapFnSources);
6026
6437
  if (!isCombinator(val)) return null;
6438
+ tagRef(val, key);
6027
6439
  ruleRefs.get(key).define(val);
6028
6440
  }
6029
6441
  return ruleRefs;