parseman 0.18.2 → 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.
package/dist/index.js CHANGED
@@ -252,7 +252,8 @@ function saveCstMark(ctx) {
252
252
  return {
253
253
  raw: cstRawLen(ctx),
254
254
  tlog: cstTlLen(ctx),
255
- leaves: cstLeavesLen(ctx)
255
+ leaves: cstLeavesLen(ctx),
256
+ fields: ctx._fields?.length ?? 0
256
257
  };
257
258
  }
258
259
  function rollbackBufList(b, keyMulti, keySingle, len) {
@@ -276,11 +277,13 @@ function rollbackCstCapture(ctx, mark) {
276
277
  if (mark.tlog === 0) b.tl = void 0;
277
278
  else b.tl.length = mark.tlog;
278
279
  }
280
+ if (ctx._fields) ctx._fields.length = mark.fields;
279
281
  return;
280
282
  }
281
283
  if (ctx._cstRawChildren) ctx._cstRawChildren.length = mark.raw;
282
284
  if (ctx._cstTriviaLog) ctx._cstTriviaLog.length = mark.tlog;
283
285
  if (ctx._cstLeaves) ctx._cstLeaves.length = mark.leaves;
286
+ if (ctx._fields) ctx._fields.length = mark.fields;
284
287
  }
285
288
  function pushCstTriviaEntry(ctx, start, end, kindIndex) {
286
289
  const insertIdx = cstRawLen(ctx);
@@ -1020,10 +1023,10 @@ function needsDeferredTriviaCommit(ctx) {
1020
1023
  }
1021
1024
  function saveTriviaMark(ctx) {
1022
1025
  const m = saveCstMark(ctx);
1023
- return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1026
+ return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1024
1027
  }
1025
1028
  function rollbackTrivia(ctx, mark) {
1026
- rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves });
1029
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields });
1027
1030
  if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
1028
1031
  }
1029
1032
  function scanWithLabels(input, cur, ctx) {
@@ -1397,6 +1400,13 @@ function ref() {
1397
1400
  define(p) {
1398
1401
  if (resolved) throw new Error("ref<T>() already defined");
1399
1402
  resolved = p;
1403
+ meta.firstSet = p._meta.firstSet;
1404
+ meta.canMatchNewline = p._meta.canMatchNewline;
1405
+ meta.isTrivia = p._meta.isTrivia;
1406
+ if (p._meta.triviaKindLabels !== void 0) meta.triviaKindLabels = p._meta.triviaKindLabels;
1407
+ else delete meta.triviaKindLabels;
1408
+ if (p._meta.disjoint !== void 0) meta.disjoint = p._meta.disjoint;
1409
+ else delete meta.disjoint;
1400
1410
  }
1401
1411
  };
1402
1412
  return slot;
@@ -1437,6 +1447,9 @@ function markUnusedValues(root) {
1437
1447
  case "transform":
1438
1448
  visit(def.parser, true);
1439
1449
  return;
1450
+ case "field":
1451
+ visit(def.parser, true);
1452
+ return;
1440
1453
  case "skip":
1441
1454
  visit(def.main, consumed);
1442
1455
  visit(def.skipped, false);
@@ -1488,6 +1501,11 @@ function markUnusedValues(root) {
1488
1501
  }
1489
1502
 
1490
1503
  // src/combinators/parser.ts
1504
+ function tagRule(r, key) {
1505
+ ;
1506
+ r._ruleName = key;
1507
+ if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
1508
+ }
1491
1509
  function rules(factory) {
1492
1510
  const cache = {};
1493
1511
  const proxy = new Proxy(cache, {
@@ -1496,7 +1514,7 @@ function rules(factory) {
1496
1514
  const record = target;
1497
1515
  if (!(key in record)) {
1498
1516
  const r = ref();
1499
- r._ruleName = key;
1517
+ tagRule(r, key);
1500
1518
  record[key] = r;
1501
1519
  }
1502
1520
  return record[key];
@@ -1507,12 +1525,12 @@ function rules(factory) {
1507
1525
  const placeholder = cache[key];
1508
1526
  const parser2 = definitions[key];
1509
1527
  if (placeholder !== void 0 && typeof placeholder.define === "function") {
1510
- ;
1528
+ tagRule(parser2, key);
1511
1529
  placeholder.define(parser2);
1512
1530
  placeholder._meta.firstSet = parser2._meta.firstSet;
1513
1531
  placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
1514
1532
  } else {
1515
- ;
1533
+ tagRule(parser2, key);
1516
1534
  cache[key] = parser2;
1517
1535
  }
1518
1536
  }
@@ -1569,44 +1587,130 @@ function buildReadsTrivia(def) {
1569
1587
  const src = def.buildSrc ?? def.build.toString();
1570
1588
  const arity = confirmedBuildArity(src);
1571
1589
  if (arity === null) return true;
1572
- return arity >= 4;
1590
+ return arity >= 5;
1573
1591
  }
1574
1592
  function buildReadsState(def) {
1575
1593
  if (!def.build) return true;
1576
1594
  const src = def.buildSrc ?? def.build.toString();
1577
1595
  const arity = confirmedBuildArity(src);
1578
1596
  if (arity === null) return true;
1579
- return arity >= 5;
1597
+ return arity >= 6;
1598
+ }
1599
+
1600
+ // src/compiler/fields.ts
1601
+ function buildReadsFields(def) {
1602
+ if (!def.build) return true;
1603
+ const src = def.buildSrc ?? def.build.toString();
1604
+ const arity = confirmedBuildArity(src);
1605
+ if (arity === null) return true;
1606
+ return arity >= 2;
1607
+ }
1608
+ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
1609
+ if (seen.has(p)) return false;
1610
+ seen.add(p);
1611
+ const d = p._def;
1612
+ switch (d.tag) {
1613
+ case "field":
1614
+ return true;
1615
+ case "node":
1616
+ return false;
1617
+ case "lazy": {
1618
+ try {
1619
+ return parserHasOwnFields(d.thunk(), seen);
1620
+ } catch {
1621
+ return false;
1622
+ }
1623
+ }
1624
+ case "sequence":
1625
+ case "choice":
1626
+ return d.parsers.some((x) => parserHasOwnFields(x, seen));
1627
+ case "sepBy":
1628
+ return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
1629
+ case "skip":
1630
+ return parserHasOwnFields(d.main, seen) || parserHasOwnFields(d.skipped, seen);
1631
+ case "grammar":
1632
+ return parserHasOwnFields(d.parser, seen) || (d.triviaParser ? parserHasOwnFields(d.triviaParser, seen) : false);
1633
+ case "scanTo":
1634
+ return parserHasOwnFields(d.sentinel, seen) || d.skip.some((x) => parserHasOwnFields(x, seen));
1635
+ case "recover":
1636
+ return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.sentinel, seen);
1637
+ case "many":
1638
+ case "oneOrMore":
1639
+ case "optional":
1640
+ case "transform":
1641
+ case "trivia":
1642
+ case "token":
1643
+ case "label":
1644
+ case "expect":
1645
+ case "withCtx":
1646
+ case "not":
1647
+ return parserHasOwnFields(d.parser, seen);
1648
+ default:
1649
+ return false;
1650
+ }
1651
+ }
1652
+ function buildFieldMap(captures) {
1653
+ if (!captures?.length) return void 0;
1654
+ const out = {};
1655
+ for (const cap of captures) {
1656
+ const entry = { value: cap.value, span: cap.span };
1657
+ const current = out[cap.name];
1658
+ if (current === void 0) out[cap.name] = entry;
1659
+ else if (Array.isArray(current)) current.push(entry);
1660
+ else out[cap.name] = [current, entry];
1661
+ }
1662
+ return out;
1580
1663
  }
1581
1664
 
1582
1665
  // src/combinators/node.ts
1583
- function collapseChild(child) {
1666
+ function unwrapChild(child) {
1584
1667
  return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
1585
1668
  }
1586
- function node(type, combinator, build, opts) {
1669
+ function isCstChild(value) {
1670
+ return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "error");
1671
+ }
1672
+ function missingInferredType() {
1673
+ throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
1674
+ }
1675
+ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
1676
+ const hasExplicitType = typeof typeOrCombinator === "string";
1677
+ const type = hasExplicitType ? typeOrCombinator : void 0;
1678
+ const combinator = hasExplicitType ? combinatorOrBuild : typeOrCombinator;
1679
+ const build = hasExplicitType ? buildOrOpts : combinatorOrBuild;
1680
+ const opts = hasExplicitType ? maybeOpts : buildOrOpts;
1681
+ const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build } };
1587
1682
  const meta = {
1588
1683
  firstSet: combinator._meta.firstSet,
1589
1684
  canMatchNewline: combinator._meta.canMatchNewline,
1590
1685
  isTrivia: false
1591
1686
  };
1687
+ const unwrap = opts?.unwrap === true;
1592
1688
  const collapse = opts?.collapse === true;
1593
- const def = collapse ? { tag: "node", type, parser: combinator, build, collapse: true } : { tag: "node", type, parser: combinator, build };
1689
+ if (unwrap && collapse) {
1690
+ throw new Error("node() options cannot set both unwrap and collapse");
1691
+ }
1692
+ const def = unwrap || collapse ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {} } : baseDef;
1594
1693
  const capturesTrivia = build ? buildReadsTrivia(def) : true;
1595
1694
  const clonesState = build ? buildReadsState(def) : true;
1695
+ const capturesFields = parserHasOwnFields(combinator) && (build ? buildReadsFields(def) : true);
1596
1696
  return {
1597
1697
  _tag: "node",
1598
1698
  _meta: meta,
1599
1699
  _def: def,
1600
1700
  parse(input, pos, ctx) {
1601
1701
  const saved = beginCstNodeCapture(ctx);
1702
+ const savedFields = ctx._fields;
1703
+ ctx._fields = capturesFields ? [] : void 0;
1602
1704
  if (!capturesTrivia) ctx.captureTrivia = false;
1603
1705
  const r = combinator.parse(input, pos, ctx);
1706
+ const fields = capturesFields ? buildFieldMap(ctx._fields) : void 0;
1707
+ ctx._fields = savedFields;
1604
1708
  const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
1605
1709
  if (!r.ok) return r;
1606
1710
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
1607
- 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 };
1608
- const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
1609
- const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
1711
+ const nodeType = def.type ?? missingInferredType();
1712
+ 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 };
1713
+ const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
1610
1714
  if (saved.buf !== void 0 || saved.ch !== void 0) {
1611
1715
  pushCstChild(ctx, built, rawEntry);
1612
1716
  }
@@ -1667,6 +1771,19 @@ function label(name, combinator) {
1667
1771
  }
1668
1772
  };
1669
1773
  }
1774
+ function field(name, combinator) {
1775
+ return {
1776
+ _tag: combinator._tag,
1777
+ _meta: combinator._meta,
1778
+ _def: { tag: "field", name, parser: combinator },
1779
+ parse(input, pos, ctx) {
1780
+ const result = combinator.parse(input, pos, ctx);
1781
+ if (!result.ok) return result;
1782
+ ctx._fields?.push({ name, value: result.value, span: result.span });
1783
+ return result;
1784
+ }
1785
+ };
1786
+ }
1670
1787
 
1671
1788
  // src/compiler/line-index.ts
1672
1789
  function buildLineIndex(input) {
@@ -3149,21 +3266,22 @@ function emitScannableTerminal(shape, c) {
3149
3266
  }
3150
3267
 
3151
3268
  // src/compiler/inline-build.ts
3152
- 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*$/;
3269
+ 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*$/;
3153
3270
  function escapeRegExp(s) {
3154
3271
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3155
3272
  }
3156
3273
  function looseMkBuildRe(type) {
3157
3274
  return new RegExp(
3158
- 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*$`
3275
+ 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*$`
3159
3276
  );
3160
3277
  }
3161
3278
  function analyzeMkInlineBuild(def) {
3162
3279
  if (!def.build) return null;
3280
+ if (def.type === void 0) return null;
3163
3281
  const src = (def.buildSrc ?? def.build.toString()).trim();
3164
3282
  const strict = src.match(MK_BUILD_RE);
3165
3283
  if (strict) {
3166
- const mkType = strict[2];
3284
+ const mkType = strict[6];
3167
3285
  return mkType === def.type ? def.type : null;
3168
3286
  }
3169
3287
  if (looseMkBuildRe(def.type).test(src)) return def.type;
@@ -3490,8 +3608,9 @@ function planDisjointDispatch(parsers) {
3490
3608
  }
3491
3609
  return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
3492
3610
  }
3493
- function captureRestoreBody(mL, mR, mTl, mLg) {
3494
- const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
3611
+ function captureRestoreBody(mL, mR, mTl, mLg, mF = null) {
3612
+ const fieldRestore = mF ? `; if (_ctx._fields) _ctx._fields.length = ${mF}` : "";
3613
+ const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}${fieldRestore}`;
3495
3614
  return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
3496
3615
  }
3497
3616
  function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
@@ -3539,6 +3658,7 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
3539
3658
  // Delegating wrappers: defer to the wrapped parser.
3540
3659
  case "transform":
3541
3660
  case "label":
3661
+ case "field":
3542
3662
  case "expect":
3543
3663
  case "withCtx":
3544
3664
  case "grammar":
@@ -3585,6 +3705,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
3585
3705
  case "optional":
3586
3706
  case "transform":
3587
3707
  case "label":
3708
+ case "field":
3588
3709
  case "expect":
3589
3710
  case "withCtx":
3590
3711
  case "grammar":
@@ -3622,22 +3743,25 @@ function emitFallible(inner, ctx, pos, swallow = false) {
3622
3743
  ctx.recordFail = savedRecord;
3623
3744
  const ind0 = ind(ctx);
3624
3745
  const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
3746
+ const needsFieldRollback = needsRollback && parserHasOwnFields(inner);
3625
3747
  const mL = needsRollback ? v(ctx, "_fcl") : null;
3626
3748
  const mR = needsRollback ? v(ctx, "_fcr") : null;
3627
3749
  const mTl = needsRollback ? v(ctx, "_fctl") : null;
3750
+ const mF = needsFieldRollback ? v(ctx, "_fcf") : null;
3628
3751
  const stmts = [
3629
3752
  `${ind0}let ${okV} = false, ${valV}, ${endV} = ${pos}`,
3630
3753
  ...mL ? [
3631
3754
  `${ind0}const ${mL} = _ctx._cstLeaves?.length ?? 0`,
3632
3755
  `${ind0}const ${mR} = _ctx._cstRawChildren?.length ?? 0`,
3633
- `${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0`
3756
+ `${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0`,
3757
+ ...mF ? [`${ind0}const ${mF} = _ctx._fields?.length ?? 0`] : []
3634
3758
  ] : [],
3635
3759
  `${ind0}${lbl}: {`,
3636
3760
  ...r.stmts,
3637
3761
  `${ind0} ${valV} = ${r.valueVar}; ${endV} = ${r.endVar}; ${okV} = true`,
3638
3762
  `${ind0}}`,
3639
3763
  ...mL ? [
3640
- `${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null)} }`
3764
+ `${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null, mF)} }`
3641
3765
  ] : []
3642
3766
  ];
3643
3767
  return { stmts, okVar: okV, valVar: valV, endVar: endV };
@@ -3864,6 +3988,7 @@ function failsAtStart(p) {
3864
3988
  return true;
3865
3989
  case "transform":
3866
3990
  case "label":
3991
+ case "field":
3867
3992
  return failsAtStart(d.parser);
3868
3993
  default:
3869
3994
  return false;
@@ -3883,6 +4008,7 @@ function canMatchEmptyAtStart(p) {
3883
4008
  return true;
3884
4009
  case "transform":
3885
4010
  case "label":
4011
+ case "field":
3886
4012
  return canMatchEmptyAtStart(d.parser);
3887
4013
  case "literal":
3888
4014
  return d.value.length === 0;
@@ -4065,11 +4191,13 @@ function emitFirstMatch(def, ctx, pos) {
4065
4191
  const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
4066
4192
  const armHasAutoNot = !!(autoNot && autoNot.length > 0);
4067
4193
  const armNeedsRollback = ctx.capturing && (mayLeavePartialCapture(p) || armHasAutoNot && capturesLeaf(p));
4194
+ const armNeedsFieldRollback = armNeedsRollback && parserHasOwnFields(p);
4068
4195
  const markLeaves = armNeedsRollback ? v(ctx, "_cml") : null;
4069
4196
  const markRaw = armNeedsRollback ? v(ctx, "_cmr") : null;
4070
4197
  const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
4071
4198
  const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
4072
- const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
4199
+ const markFields = armNeedsFieldRollback ? v(ctx, "_cmf") : null;
4200
+ const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog, markFields) : "";
4073
4201
  const failSlot = atStart ? staticFx : "_ctx._fx";
4074
4202
  stmts.push(`${ind0}if (${skipCond}) {`);
4075
4203
  if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
@@ -4079,7 +4207,8 @@ function emitFirstMatch(def, ctx, pos) {
4079
4207
  `${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
4080
4208
  `${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
4081
4209
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
4082
- `${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
4210
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`,
4211
+ ...markFields ? [`${ind(ctx)}const ${markFields} = _ctx._fields?.length ?? 0`] : []
4083
4212
  );
4084
4213
  }
4085
4214
  const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
@@ -4472,6 +4601,9 @@ function emitToken(def, ctx, pos) {
4472
4601
  };
4473
4602
  }
4474
4603
  function emitNode(def, ctx, pos) {
4604
+ if (def.type === void 0) {
4605
+ throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
4606
+ }
4475
4607
  const structural = def.build === void 0;
4476
4608
  const mkType = structural ? null : analyzeMkInlineBuild(def);
4477
4609
  let buildIdx = null;
@@ -4483,25 +4615,36 @@ function emitNode(def, ctx, pos) {
4483
4615
  const i = ind(ctx);
4484
4616
  const capturesTrivia = mkType !== null || !structural && buildReadsTrivia(def);
4485
4617
  const clonesState = !structural && buildReadsState(def);
4618
+ const hasFields = parserHasOwnFields(def.parser);
4619
+ const capturesFields = hasFields && !structural && buildReadsFields(def);
4486
4620
  const chV = v(ctx, "_ch");
4487
4621
  const rawV = v(ctx, "_raw");
4488
4622
  const capTLv = structural ? v(ctx, "_ctl") : null;
4489
4623
  const capSTv = structural ? v(ctx, "_cst") : null;
4624
+ const capFv = structural && hasFields ? v(ctx, "_cf") : null;
4490
4625
  const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
4491
4626
  if (!capturesTrivia) ctx.needsEmptyTl = true;
4492
4627
  const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
4493
4628
  if (structural) ctx.needsHostReads = true;
4494
- const allocStmt = structural ? `${i}const ${capTLv} = _ctx._pmCapTL ??= _hostReads(_ctx.build, 4), ${capSTv} = _ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 5))
4629
+ const hostTriviaGate = `_ctx.build !== undefined && (_ctx.build._parsemanCaptureTrivia !== undefined ? _ctx.build._parsemanCaptureTrivia(${JSON.stringify(def.type)}) : (_ctx._pmCapTL ??= _hostReads(_ctx.build, 5)))`;
4630
+ 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)` : ""}
4495
4631
  ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
4496
4632
  const innerTl = structural ? `${capTLv} ? ${tlV} : undefined` : capturesTrivia ? tlV : "undefined";
4633
+ const fieldsOn = structural ? capFv ?? "false" : capturesFields ? "true" : "false";
4634
+ const sf = hasFields ? v(ctx, "_sf") : null;
4635
+ const fArr = hasFields ? v(ctx, "_fa") : null;
4636
+ const fObj = hasFields ? v(ctx, "_fields") : "undefined";
4497
4637
  const stmts = [
4498
4638
  allocStmt,
4499
- `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
4500
- `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}`
4639
+ `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog${sf ? `, ${sf} = _ctx._fields` : ""}`,
4640
+ `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}${sf ? `; _ctx._fields = ${fieldsOn} ? [] : undefined` : ""}`
4501
4641
  ];
4502
4642
  const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
4503
4643
  stmts.push(...innerStmts);
4504
- stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
4644
+ if (sf && fArr) {
4645
+ stmts.push(`${i}const ${fArr} = _ctx._fields`);
4646
+ }
4647
+ stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${sf ? `; _ctx._fields = ${sf}` : ""}`);
4505
4648
  stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
4506
4649
  let stV = "undefined";
4507
4650
  if (structural) {
@@ -4511,14 +4654,33 @@ ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capt
4511
4654
  stV = v(ctx, "_nst");
4512
4655
  stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
4513
4656
  }
4657
+ if (sf && fArr) {
4658
+ const fe = v(ctx, "_fe");
4659
+ const cur = v(ctx, "_fc");
4660
+ stmts.push(
4661
+ `${i}let ${fObj} = undefined`,
4662
+ `${i}if (${fArr} && ${fArr}.length) {`,
4663
+ `${i} ${fObj} = {}`,
4664
+ `${i} for (const ${fe} of ${fArr}) {`,
4665
+ `${i} const ${cur} = ${fObj}[${fe}.name], _entry = { value: ${fe}.value, span: ${fe}.span }`,
4666
+ `${i} if (${cur} === undefined) ${fObj}[${fe}.name] = _entry`,
4667
+ `${i} else if (Array.isArray(${cur})) ${cur}.push(_entry)`,
4668
+ `${i} else ${fObj}[${fe}.name] = [${cur}, _entry]`,
4669
+ `${i} }`,
4670
+ `${i}}`
4671
+ );
4672
+ }
4514
4673
  const ndV = v(ctx, "_nd");
4515
- 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})`;
4516
- const ndExpr = ctx.ns || structural ? `_ctx.build !== undefined ? _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV}) : (${buildExpr})` : buildExpr;
4517
- 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;
4674
+ 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})`;
4675
+ 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;
4676
+ 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;
4677
+ const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
4678
+ const collapseExpr = `${chV}.length === 1 ? ${chV}[0] : (${ndExpr})`;
4679
+ const finalExpr = def.unwrap ? unwrapExpr : def.collapse ? collapseExpr : hostCollapseExpr;
4518
4680
  stmts.push(
4519
4681
  `${i}const ${ndV} = ${finalExpr}`,
4520
4682
  `${i}if (${sc}) ${sc}.push(${ndV})`,
4521
- `${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} } })`
4683
+ `${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} } })`
4522
4684
  );
4523
4685
  return { stmts, valueVar: ndV, endVar };
4524
4686
  }
@@ -4733,6 +4895,17 @@ function emitDispatch(p, ctx, pos) {
4733
4895
  endVar: inner.endVar
4734
4896
  };
4735
4897
  }
4898
+ case "field": {
4899
+ const inner = emit(def.parser, ctx, pos);
4900
+ return {
4901
+ stmts: [
4902
+ ...inner.stmts,
4903
+ `${ind(ctx)}if (_ctx._fields) _ctx._fields.push({ name: ${JSON.stringify(def.name)}, value: ${inner.valueVar}, span: { start: ${pos}, end: ${inner.endVar} } })`
4904
+ ],
4905
+ valueVar: inner.valueVar,
4906
+ endVar: inner.endVar
4907
+ };
4908
+ }
4736
4909
  case "grammar": {
4737
4910
  const savedTrivia = ctx.activeTrivia;
4738
4911
  const savedKindLabels = ctx.triviaKindLabels;
@@ -4834,6 +5007,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
4834
5007
  case "trivia":
4835
5008
  case "token":
4836
5009
  case "label":
5010
+ case "field":
4837
5011
  case "optional":
4838
5012
  case "many":
4839
5013
  case "oneOrMore":
@@ -4870,6 +5044,7 @@ function childrenOf(def) {
4870
5044
  case "trivia":
4871
5045
  case "token":
4872
5046
  case "label":
5047
+ case "field":
4873
5048
  case "grammar":
4874
5049
  case "not":
4875
5050
  case "node":
@@ -5111,6 +5286,11 @@ function assertRuleName(name) {
5111
5286
  );
5112
5287
  }
5113
5288
  }
5289
+ function publicRuleWrapperSource(rule, fnSource) {
5290
+ const labels = rule._meta.triviaKindLabels;
5291
+ if (labels === void 0) return fnSource;
5292
+ return `Object.assign(${fnSource}, { _meta: { triviaKindLabels: ${JSON.stringify(labels)} } })`;
5293
+ }
5114
5294
  function compileLinkable(ruleMapArg, ns) {
5115
5295
  if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
5116
5296
  for (const [, rule] of ruleMapArg) markUnusedValues(rule);
@@ -5213,7 +5393,7 @@ function compileLinkable(ruleMapArg, ns) {
5213
5393
  ctx.recordFail = savedRec;
5214
5394
  pushNamedFnDecl(ctx, fn, body.stmts, body.valueVar, body.endVar);
5215
5395
  }
5216
- perEntry.push({ key, r: emitNamedFnCall(ctx, fn, "_pos") });
5396
+ perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
5217
5397
  }
5218
5398
  if (ctx.runtimeParsers.length !== 0) return null;
5219
5399
  const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
@@ -5236,14 +5416,14 @@ function compileLinkable(ruleMapArg, ns) {
5236
5416
  ...privateFns
5237
5417
  ].filter(Boolean);
5238
5418
  const wrappers = /* @__PURE__ */ new Map();
5239
- for (const { key, r } of perEntry) {
5240
- wrappers.set(key, [
5419
+ for (const { key, rule, r } of perEntry) {
5420
+ wrappers.set(key, publicRuleWrapperSource(rule, [
5241
5421
  `function(input, _pos, _ctx) {`,
5242
5422
  ` let pos = _pos`,
5243
5423
  ...r.stmts,
5244
5424
  ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
5245
5425
  `}`
5246
- ].join("\n"));
5426
+ ].join("\n")));
5247
5427
  }
5248
5428
  const firstSets = /* @__PURE__ */ new Map();
5249
5429
  for (const [key, rule] of ruleMap) {
@@ -5446,6 +5626,7 @@ function evalRuleMapIR(ir) {
5446
5626
  "skip",
5447
5627
  "trivia",
5448
5628
  "label",
5629
+ "field",
5449
5630
  "expect",
5450
5631
  "_tf",
5451
5632
  "_nd",
@@ -5472,6 +5653,7 @@ function evalRuleMapIR(ir) {
5472
5653
  skip,
5473
5654
  trivia,
5474
5655
  label,
5656
+ field,
5475
5657
  expect,
5476
5658
  _tf,
5477
5659
  _nd
@@ -5486,9 +5668,26 @@ function linkable(rulesMap, ns) {
5486
5668
  if (!pieces) throw new Error("linkable(): this grammar cannot be compiled to a linkable artifact (contains a runtime-only parser fallback)");
5487
5669
  return pieces;
5488
5670
  }
5489
- function cstBuildHost(type, children, _rawChildren, span, _triviaLog, state) {
5671
+ function normalizeCstCollapse(collapse) {
5672
+ if (collapse === true) return () => true;
5673
+ if (Array.isArray(collapse)) {
5674
+ const types = new Set(collapse);
5675
+ return (type) => types.has(type);
5676
+ }
5677
+ return typeof collapse === "function" ? collapse : void 0;
5678
+ }
5679
+ function buildCstNode(type, children, _fields, span, _rawChildren, _triviaLog, state) {
5490
5680
  return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children] };
5491
5681
  }
5682
+ function cstBuildHost(typeOrOptions, children, _fields, span, rawChildren, triviaLog, state) {
5683
+ if (typeof typeOrOptions === "string") {
5684
+ return buildCstNode(typeOrOptions, children ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
5685
+ }
5686
+ const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
5687
+ const host = (type, children2, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children2, fields, span2, rawChildren2, triviaLog2, state2);
5688
+ if (collapse) host._parsemanCstCollapse = collapse;
5689
+ return host;
5690
+ }
5492
5691
  function pick(grammar, names) {
5493
5692
  const p = grammar.ruleFns instanceof Map ? grammar : linkable(grammar);
5494
5693
  const keep = /* @__PURE__ */ new Set();
@@ -5520,8 +5719,9 @@ function fusedBody(pieces) {
5520
5719
  }
5521
5720
  }
5522
5721
  const contributing = new Set(winner.values());
5523
- const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
5524
- const needsHostReads = [...contributing].some((p) => p.needsHostReads);
5722
+ const contributingPieces = [...contributing];
5723
+ const needsEmptyTl = contributingPieces.some((p) => p.needsEmptyTl);
5724
+ const needsHostReads = contributingPieces.some((p) => p.needsHostReads);
5525
5725
  const lines = [
5526
5726
  // Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
5527
5727
  "const _pfFail = {}",
@@ -5529,7 +5729,7 @@ function fusedBody(pieces) {
5529
5729
  ...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
5530
5730
  ...needsHostReads ? [HOST_READS_DECL] : [],
5531
5731
  // Each contributing artifact's namespaced private prelude (regexes, _pf, …).
5532
- ...[...contributing].flatMap((p) => p.prelude),
5732
+ ...[...new Set(contributingPieces.flatMap((p) => p.prelude))],
5533
5733
  // The winning `_r_<Name>` function for each rule (one per name → no redeclare).
5534
5734
  ...[...winner].map(([k, p]) => p.ruleFns.get(k))
5535
5735
  ];
@@ -5552,7 +5752,7 @@ function fusedBody(pieces) {
5552
5752
  }
5553
5753
  );
5554
5754
  const env = {};
5555
- for (const p of contributing) {
5755
+ for (const p of contributingPieces) {
5556
5756
  if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
5557
5757
  if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
5558
5758
  }
@@ -5572,14 +5772,23 @@ function materializePiece(p) {
5572
5772
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
5573
5773
  return pieces;
5574
5774
  }
5575
- function itemPieces(item) {
5775
+ function nextComposeNs(used) {
5776
+ let ns;
5777
+ do {
5778
+ ns = `_lk${_nsCounter++}_`;
5779
+ } while (used.has(ns));
5780
+ used.add(ns);
5781
+ return ns;
5782
+ }
5783
+ function itemPieces(item, used) {
5576
5784
  const carried = item[COMPOSED_PIECES];
5577
- if (Array.isArray(carried)) return carried.map(materializePiece);
5578
- if (item.ruleFns instanceof Map) return [item];
5579
- return [linkable(item)];
5785
+ const pieces = Array.isArray(carried) ? carried.map(materializePiece) : item.ruleFns instanceof Map ? [item] : [linkable(item, nextComposeNs(used))];
5786
+ for (const p of pieces) used.add(p.ns);
5787
+ return pieces;
5580
5788
  }
5581
5789
  function compose(items) {
5582
- const pieces = items.flatMap(itemPieces);
5790
+ const used = /* @__PURE__ */ new Set();
5791
+ const pieces = items.flatMap((item) => itemPieces(item, used));
5583
5792
  const map = fuseRules(pieces);
5584
5793
  Object.defineProperty(map, COMPOSED_PIECES, { value: pieces, enumerable: false });
5585
5794
  return map;
@@ -6305,6 +6514,7 @@ export {
6305
6514
  createVisitor,
6306
6515
  cstBuildHost,
6307
6516
  expect,
6517
+ field,
6308
6518
  gapIsSignificant,
6309
6519
  gapText,
6310
6520
  guard,