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.cjs CHANGED
@@ -49,6 +49,7 @@ __export(index_exports, {
49
49
  createVisitor: () => createVisitor,
50
50
  cstBuildHost: () => cstBuildHost,
51
51
  expect: () => expect,
52
+ field: () => field,
52
53
  gapIsSignificant: () => gapIsSignificant,
53
54
  gapText: () => gapText,
54
55
  guard: () => guard,
@@ -349,7 +350,8 @@ function saveCstMark(ctx) {
349
350
  return {
350
351
  raw: cstRawLen(ctx),
351
352
  tlog: cstTlLen(ctx),
352
- leaves: cstLeavesLen(ctx)
353
+ leaves: cstLeavesLen(ctx),
354
+ fields: ctx._fields?.length ?? 0
353
355
  };
354
356
  }
355
357
  function rollbackBufList(b, keyMulti, keySingle, len) {
@@ -373,11 +375,13 @@ function rollbackCstCapture(ctx, mark) {
373
375
  if (mark.tlog === 0) b.tl = void 0;
374
376
  else b.tl.length = mark.tlog;
375
377
  }
378
+ if (ctx._fields) ctx._fields.length = mark.fields;
376
379
  return;
377
380
  }
378
381
  if (ctx._cstRawChildren) ctx._cstRawChildren.length = mark.raw;
379
382
  if (ctx._cstTriviaLog) ctx._cstTriviaLog.length = mark.tlog;
380
383
  if (ctx._cstLeaves) ctx._cstLeaves.length = mark.leaves;
384
+ if (ctx._fields) ctx._fields.length = mark.fields;
381
385
  }
382
386
  function pushCstTriviaEntry(ctx, start, end, kindIndex) {
383
387
  const insertIdx = cstRawLen(ctx);
@@ -1117,10 +1121,10 @@ function needsDeferredTriviaCommit(ctx) {
1117
1121
  }
1118
1122
  function saveTriviaMark(ctx) {
1119
1123
  const m = saveCstMark(ctx);
1120
- return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1124
+ return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1121
1125
  }
1122
1126
  function rollbackTrivia(ctx, mark) {
1123
- rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves });
1127
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields });
1124
1128
  if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
1125
1129
  }
1126
1130
  function scanWithLabels(input, cur, ctx) {
@@ -1494,6 +1498,13 @@ function ref() {
1494
1498
  define(p) {
1495
1499
  if (resolved) throw new Error("ref<T>() already defined");
1496
1500
  resolved = p;
1501
+ meta.firstSet = p._meta.firstSet;
1502
+ meta.canMatchNewline = p._meta.canMatchNewline;
1503
+ meta.isTrivia = p._meta.isTrivia;
1504
+ if (p._meta.triviaKindLabels !== void 0) meta.triviaKindLabels = p._meta.triviaKindLabels;
1505
+ else delete meta.triviaKindLabels;
1506
+ if (p._meta.disjoint !== void 0) meta.disjoint = p._meta.disjoint;
1507
+ else delete meta.disjoint;
1497
1508
  }
1498
1509
  };
1499
1510
  return slot;
@@ -1534,6 +1545,9 @@ function markUnusedValues(root) {
1534
1545
  case "transform":
1535
1546
  visit(def.parser, true);
1536
1547
  return;
1548
+ case "field":
1549
+ visit(def.parser, true);
1550
+ return;
1537
1551
  case "skip":
1538
1552
  visit(def.main, consumed);
1539
1553
  visit(def.skipped, false);
@@ -1585,6 +1599,11 @@ function markUnusedValues(root) {
1585
1599
  }
1586
1600
 
1587
1601
  // src/combinators/parser.ts
1602
+ function tagRule(r, key) {
1603
+ ;
1604
+ r._ruleName = key;
1605
+ if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
1606
+ }
1588
1607
  function rules(factory) {
1589
1608
  const cache = {};
1590
1609
  const proxy = new Proxy(cache, {
@@ -1593,7 +1612,7 @@ function rules(factory) {
1593
1612
  const record = target;
1594
1613
  if (!(key in record)) {
1595
1614
  const r = ref();
1596
- r._ruleName = key;
1615
+ tagRule(r, key);
1597
1616
  record[key] = r;
1598
1617
  }
1599
1618
  return record[key];
@@ -1604,12 +1623,12 @@ function rules(factory) {
1604
1623
  const placeholder = cache[key];
1605
1624
  const parser2 = definitions[key];
1606
1625
  if (placeholder !== void 0 && typeof placeholder.define === "function") {
1607
- ;
1626
+ tagRule(parser2, key);
1608
1627
  placeholder.define(parser2);
1609
1628
  placeholder._meta.firstSet = parser2._meta.firstSet;
1610
1629
  placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
1611
1630
  } else {
1612
- ;
1631
+ tagRule(parser2, key);
1613
1632
  cache[key] = parser2;
1614
1633
  }
1615
1634
  }
@@ -1666,44 +1685,130 @@ function buildReadsTrivia(def) {
1666
1685
  const src = def.buildSrc ?? def.build.toString();
1667
1686
  const arity = confirmedBuildArity(src);
1668
1687
  if (arity === null) return true;
1669
- return arity >= 4;
1688
+ return arity >= 5;
1670
1689
  }
1671
1690
  function buildReadsState(def) {
1672
1691
  if (!def.build) return true;
1673
1692
  const src = def.buildSrc ?? def.build.toString();
1674
1693
  const arity = confirmedBuildArity(src);
1675
1694
  if (arity === null) return true;
1676
- return arity >= 5;
1695
+ return arity >= 6;
1696
+ }
1697
+
1698
+ // src/compiler/fields.ts
1699
+ function buildReadsFields(def) {
1700
+ if (!def.build) return true;
1701
+ const src = def.buildSrc ?? def.build.toString();
1702
+ const arity = confirmedBuildArity(src);
1703
+ if (arity === null) return true;
1704
+ return arity >= 2;
1705
+ }
1706
+ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
1707
+ if (seen.has(p)) return false;
1708
+ seen.add(p);
1709
+ const d = p._def;
1710
+ switch (d.tag) {
1711
+ case "field":
1712
+ return true;
1713
+ case "node":
1714
+ return false;
1715
+ case "lazy": {
1716
+ try {
1717
+ return parserHasOwnFields(d.thunk(), seen);
1718
+ } catch {
1719
+ return false;
1720
+ }
1721
+ }
1722
+ case "sequence":
1723
+ case "choice":
1724
+ return d.parsers.some((x) => parserHasOwnFields(x, seen));
1725
+ case "sepBy":
1726
+ return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
1727
+ case "skip":
1728
+ return parserHasOwnFields(d.main, seen) || parserHasOwnFields(d.skipped, seen);
1729
+ case "grammar":
1730
+ return parserHasOwnFields(d.parser, seen) || (d.triviaParser ? parserHasOwnFields(d.triviaParser, seen) : false);
1731
+ case "scanTo":
1732
+ return parserHasOwnFields(d.sentinel, seen) || d.skip.some((x) => parserHasOwnFields(x, seen));
1733
+ case "recover":
1734
+ return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.sentinel, seen);
1735
+ case "many":
1736
+ case "oneOrMore":
1737
+ case "optional":
1738
+ case "transform":
1739
+ case "trivia":
1740
+ case "token":
1741
+ case "label":
1742
+ case "expect":
1743
+ case "withCtx":
1744
+ case "not":
1745
+ return parserHasOwnFields(d.parser, seen);
1746
+ default:
1747
+ return false;
1748
+ }
1749
+ }
1750
+ function buildFieldMap(captures) {
1751
+ if (!captures?.length) return void 0;
1752
+ const out = {};
1753
+ for (const cap of captures) {
1754
+ const entry = { value: cap.value, span: cap.span };
1755
+ const current = out[cap.name];
1756
+ if (current === void 0) out[cap.name] = entry;
1757
+ else if (Array.isArray(current)) current.push(entry);
1758
+ else out[cap.name] = [current, entry];
1759
+ }
1760
+ return out;
1677
1761
  }
1678
1762
 
1679
1763
  // src/combinators/node.ts
1680
- function collapseChild(child) {
1764
+ function unwrapChild(child) {
1681
1765
  return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
1682
1766
  }
1683
- function node(type, combinator, build, opts) {
1767
+ function isCstChild(value) {
1768
+ return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "error");
1769
+ }
1770
+ function missingInferredType() {
1771
+ throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
1772
+ }
1773
+ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
1774
+ const hasExplicitType = typeof typeOrCombinator === "string";
1775
+ const type = hasExplicitType ? typeOrCombinator : void 0;
1776
+ const combinator = hasExplicitType ? combinatorOrBuild : typeOrCombinator;
1777
+ const build = hasExplicitType ? buildOrOpts : combinatorOrBuild;
1778
+ const opts = hasExplicitType ? maybeOpts : buildOrOpts;
1779
+ const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build } };
1684
1780
  const meta = {
1685
1781
  firstSet: combinator._meta.firstSet,
1686
1782
  canMatchNewline: combinator._meta.canMatchNewline,
1687
1783
  isTrivia: false
1688
1784
  };
1785
+ const unwrap = opts?.unwrap === true;
1689
1786
  const collapse = opts?.collapse === true;
1690
- const def = collapse ? { tag: "node", type, parser: combinator, build, collapse: true } : { tag: "node", type, parser: combinator, build };
1787
+ if (unwrap && collapse) {
1788
+ throw new Error("node() options cannot set both unwrap and collapse");
1789
+ }
1790
+ const def = unwrap || collapse ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {} } : baseDef;
1691
1791
  const capturesTrivia = build ? buildReadsTrivia(def) : true;
1692
1792
  const clonesState = build ? buildReadsState(def) : true;
1793
+ const capturesFields = parserHasOwnFields(combinator) && (build ? buildReadsFields(def) : true);
1693
1794
  return {
1694
1795
  _tag: "node",
1695
1796
  _meta: meta,
1696
1797
  _def: def,
1697
1798
  parse(input, pos, ctx) {
1698
1799
  const saved = beginCstNodeCapture(ctx);
1800
+ const savedFields = ctx._fields;
1801
+ ctx._fields = capturesFields ? [] : void 0;
1699
1802
  if (!capturesTrivia) ctx.captureTrivia = false;
1700
1803
  const r = combinator.parse(input, pos, ctx);
1804
+ const fields = capturesFields ? buildFieldMap(ctx._fields) : void 0;
1805
+ ctx._fields = savedFields;
1701
1806
  const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
1702
1807
  if (!r.ok) return r;
1703
1808
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
1704
- 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 };
1705
- const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
1706
- const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
1809
+ const nodeType = def.type ?? missingInferredType();
1810
+ 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 };
1811
+ const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
1707
1812
  if (saved.buf !== void 0 || saved.ch !== void 0) {
1708
1813
  pushCstChild(ctx, built, rawEntry);
1709
1814
  }
@@ -1764,6 +1869,19 @@ function label(name, combinator) {
1764
1869
  }
1765
1870
  };
1766
1871
  }
1872
+ function field(name, combinator) {
1873
+ return {
1874
+ _tag: combinator._tag,
1875
+ _meta: combinator._meta,
1876
+ _def: { tag: "field", name, parser: combinator },
1877
+ parse(input, pos, ctx) {
1878
+ const result = combinator.parse(input, pos, ctx);
1879
+ if (!result.ok) return result;
1880
+ ctx._fields?.push({ name, value: result.value, span: result.span });
1881
+ return result;
1882
+ }
1883
+ };
1884
+ }
1767
1885
 
1768
1886
  // src/compiler/line-index.ts
1769
1887
  function buildLineIndex(input) {
@@ -3246,21 +3364,22 @@ function emitScannableTerminal(shape, c) {
3246
3364
  }
3247
3365
 
3248
3366
  // src/compiler/inline-build.ts
3249
- 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*$/;
3367
+ 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*$/;
3250
3368
  function escapeRegExp(s) {
3251
3369
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3252
3370
  }
3253
3371
  function looseMkBuildRe(type) {
3254
3372
  return new RegExp(
3255
- 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*$`
3373
+ 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*$`
3256
3374
  );
3257
3375
  }
3258
3376
  function analyzeMkInlineBuild(def) {
3259
3377
  if (!def.build) return null;
3378
+ if (def.type === void 0) return null;
3260
3379
  const src = (def.buildSrc ?? def.build.toString()).trim();
3261
3380
  const strict = src.match(MK_BUILD_RE);
3262
3381
  if (strict) {
3263
- const mkType = strict[2];
3382
+ const mkType = strict[6];
3264
3383
  return mkType === def.type ? def.type : null;
3265
3384
  }
3266
3385
  if (looseMkBuildRe(def.type).test(src)) return def.type;
@@ -3587,8 +3706,9 @@ function planDisjointDispatch(parsers) {
3587
3706
  }
3588
3707
  return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
3589
3708
  }
3590
- function captureRestoreBody(mL, mR, mTl, mLg) {
3591
- const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
3709
+ function captureRestoreBody(mL, mR, mTl, mLg, mF = null) {
3710
+ const fieldRestore = mF ? `; if (_ctx._fields) _ctx._fields.length = ${mF}` : "";
3711
+ const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}${fieldRestore}`;
3592
3712
  return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
3593
3713
  }
3594
3714
  function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
@@ -3636,6 +3756,7 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
3636
3756
  // Delegating wrappers: defer to the wrapped parser.
3637
3757
  case "transform":
3638
3758
  case "label":
3759
+ case "field":
3639
3760
  case "expect":
3640
3761
  case "withCtx":
3641
3762
  case "grammar":
@@ -3682,6 +3803,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
3682
3803
  case "optional":
3683
3804
  case "transform":
3684
3805
  case "label":
3806
+ case "field":
3685
3807
  case "expect":
3686
3808
  case "withCtx":
3687
3809
  case "grammar":
@@ -3719,22 +3841,25 @@ function emitFallible(inner, ctx, pos, swallow = false) {
3719
3841
  ctx.recordFail = savedRecord;
3720
3842
  const ind0 = ind(ctx);
3721
3843
  const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
3844
+ const needsFieldRollback = needsRollback && parserHasOwnFields(inner);
3722
3845
  const mL = needsRollback ? v(ctx, "_fcl") : null;
3723
3846
  const mR = needsRollback ? v(ctx, "_fcr") : null;
3724
3847
  const mTl = needsRollback ? v(ctx, "_fctl") : null;
3848
+ const mF = needsFieldRollback ? v(ctx, "_fcf") : null;
3725
3849
  const stmts = [
3726
3850
  `${ind0}let ${okV} = false, ${valV}, ${endV} = ${pos}`,
3727
3851
  ...mL ? [
3728
3852
  `${ind0}const ${mL} = _ctx._cstLeaves?.length ?? 0`,
3729
3853
  `${ind0}const ${mR} = _ctx._cstRawChildren?.length ?? 0`,
3730
- `${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0`
3854
+ `${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0`,
3855
+ ...mF ? [`${ind0}const ${mF} = _ctx._fields?.length ?? 0`] : []
3731
3856
  ] : [],
3732
3857
  `${ind0}${lbl}: {`,
3733
3858
  ...r.stmts,
3734
3859
  `${ind0} ${valV} = ${r.valueVar}; ${endV} = ${r.endVar}; ${okV} = true`,
3735
3860
  `${ind0}}`,
3736
3861
  ...mL ? [
3737
- `${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null)} }`
3862
+ `${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null, mF)} }`
3738
3863
  ] : []
3739
3864
  ];
3740
3865
  return { stmts, okVar: okV, valVar: valV, endVar: endV };
@@ -3961,6 +4086,7 @@ function failsAtStart(p) {
3961
4086
  return true;
3962
4087
  case "transform":
3963
4088
  case "label":
4089
+ case "field":
3964
4090
  return failsAtStart(d.parser);
3965
4091
  default:
3966
4092
  return false;
@@ -3980,6 +4106,7 @@ function canMatchEmptyAtStart(p) {
3980
4106
  return true;
3981
4107
  case "transform":
3982
4108
  case "label":
4109
+ case "field":
3983
4110
  return canMatchEmptyAtStart(d.parser);
3984
4111
  case "literal":
3985
4112
  return d.value.length === 0;
@@ -4162,11 +4289,13 @@ function emitFirstMatch(def, ctx, pos) {
4162
4289
  const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
4163
4290
  const armHasAutoNot = !!(autoNot && autoNot.length > 0);
4164
4291
  const armNeedsRollback = ctx.capturing && (mayLeavePartialCapture(p) || armHasAutoNot && capturesLeaf(p));
4292
+ const armNeedsFieldRollback = armNeedsRollback && parserHasOwnFields(p);
4165
4293
  const markLeaves = armNeedsRollback ? v(ctx, "_cml") : null;
4166
4294
  const markRaw = armNeedsRollback ? v(ctx, "_cmr") : null;
4167
4295
  const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
4168
4296
  const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
4169
- const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
4297
+ const markFields = armNeedsFieldRollback ? v(ctx, "_cmf") : null;
4298
+ const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog, markFields) : "";
4170
4299
  const failSlot = atStart ? staticFx : "_ctx._fx";
4171
4300
  stmts.push(`${ind0}if (${skipCond}) {`);
4172
4301
  if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
@@ -4176,7 +4305,8 @@ function emitFirstMatch(def, ctx, pos) {
4176
4305
  `${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
4177
4306
  `${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
4178
4307
  `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
4179
- `${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
4308
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`,
4309
+ ...markFields ? [`${ind(ctx)}const ${markFields} = _ctx._fields?.length ?? 0`] : []
4180
4310
  );
4181
4311
  }
4182
4312
  const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
@@ -4569,6 +4699,9 @@ function emitToken(def, ctx, pos) {
4569
4699
  };
4570
4700
  }
4571
4701
  function emitNode(def, ctx, pos) {
4702
+ if (def.type === void 0) {
4703
+ throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
4704
+ }
4572
4705
  const structural = def.build === void 0;
4573
4706
  const mkType = structural ? null : analyzeMkInlineBuild(def);
4574
4707
  let buildIdx = null;
@@ -4580,25 +4713,36 @@ function emitNode(def, ctx, pos) {
4580
4713
  const i = ind(ctx);
4581
4714
  const capturesTrivia = mkType !== null || !structural && buildReadsTrivia(def);
4582
4715
  const clonesState = !structural && buildReadsState(def);
4716
+ const hasFields = parserHasOwnFields(def.parser);
4717
+ const capturesFields = hasFields && !structural && buildReadsFields(def);
4583
4718
  const chV = v(ctx, "_ch");
4584
4719
  const rawV = v(ctx, "_raw");
4585
4720
  const capTLv = structural ? v(ctx, "_ctl") : null;
4586
4721
  const capSTv = structural ? v(ctx, "_cst") : null;
4722
+ const capFv = structural && hasFields ? v(ctx, "_cf") : null;
4587
4723
  const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
4588
4724
  if (!capturesTrivia) ctx.needsEmptyTl = true;
4589
4725
  const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
4590
4726
  if (structural) ctx.needsHostReads = true;
4591
- const allocStmt = structural ? `${i}const ${capTLv} = _ctx._pmCapTL ??= _hostReads(_ctx.build, 4), ${capSTv} = _ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 5))
4727
+ const hostTriviaGate = `_ctx.build !== undefined && (_ctx.build._parsemanCaptureTrivia !== undefined ? _ctx.build._parsemanCaptureTrivia(${JSON.stringify(def.type)}) : (_ctx._pmCapTL ??= _hostReads(_ctx.build, 5)))`;
4728
+ 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)` : ""}
4592
4729
  ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
4593
4730
  const innerTl = structural ? `${capTLv} ? ${tlV} : undefined` : capturesTrivia ? tlV : "undefined";
4731
+ const fieldsOn = structural ? capFv ?? "false" : capturesFields ? "true" : "false";
4732
+ const sf = hasFields ? v(ctx, "_sf") : null;
4733
+ const fArr = hasFields ? v(ctx, "_fa") : null;
4734
+ const fObj = hasFields ? v(ctx, "_fields") : "undefined";
4594
4735
  const stmts = [
4595
4736
  allocStmt,
4596
- `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
4597
- `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}`
4737
+ `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog${sf ? `, ${sf} = _ctx._fields` : ""}`,
4738
+ `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}${sf ? `; _ctx._fields = ${fieldsOn} ? [] : undefined` : ""}`
4598
4739
  ];
4599
4740
  const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
4600
4741
  stmts.push(...innerStmts);
4601
- stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
4742
+ if (sf && fArr) {
4743
+ stmts.push(`${i}const ${fArr} = _ctx._fields`);
4744
+ }
4745
+ stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${sf ? `; _ctx._fields = ${sf}` : ""}`);
4602
4746
  stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
4603
4747
  let stV = "undefined";
4604
4748
  if (structural) {
@@ -4608,14 +4752,33 @@ ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capt
4608
4752
  stV = v(ctx, "_nst");
4609
4753
  stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
4610
4754
  }
4755
+ if (sf && fArr) {
4756
+ const fe = v(ctx, "_fe");
4757
+ const cur = v(ctx, "_fc");
4758
+ stmts.push(
4759
+ `${i}let ${fObj} = undefined`,
4760
+ `${i}if (${fArr} && ${fArr}.length) {`,
4761
+ `${i} ${fObj} = {}`,
4762
+ `${i} for (const ${fe} of ${fArr}) {`,
4763
+ `${i} const ${cur} = ${fObj}[${fe}.name], _entry = { value: ${fe}.value, span: ${fe}.span }`,
4764
+ `${i} if (${cur} === undefined) ${fObj}[${fe}.name] = _entry`,
4765
+ `${i} else if (Array.isArray(${cur})) ${cur}.push(_entry)`,
4766
+ `${i} else ${fObj}[${fe}.name] = [${cur}, _entry]`,
4767
+ `${i} }`,
4768
+ `${i}}`
4769
+ );
4770
+ }
4611
4771
  const ndV = v(ctx, "_nd");
4612
- 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})`;
4613
- const ndExpr = ctx.ns || structural ? `_ctx.build !== undefined ? _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV}) : (${buildExpr})` : buildExpr;
4614
- 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;
4772
+ 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})`;
4773
+ 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;
4774
+ 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;
4775
+ const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
4776
+ const collapseExpr = `${chV}.length === 1 ? ${chV}[0] : (${ndExpr})`;
4777
+ const finalExpr = def.unwrap ? unwrapExpr : def.collapse ? collapseExpr : hostCollapseExpr;
4615
4778
  stmts.push(
4616
4779
  `${i}const ${ndV} = ${finalExpr}`,
4617
4780
  `${i}if (${sc}) ${sc}.push(${ndV})`,
4618
- `${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} } })`
4781
+ `${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} } })`
4619
4782
  );
4620
4783
  return { stmts, valueVar: ndV, endVar };
4621
4784
  }
@@ -4830,6 +4993,17 @@ function emitDispatch(p, ctx, pos) {
4830
4993
  endVar: inner.endVar
4831
4994
  };
4832
4995
  }
4996
+ case "field": {
4997
+ const inner = emit(def.parser, ctx, pos);
4998
+ return {
4999
+ stmts: [
5000
+ ...inner.stmts,
5001
+ `${ind(ctx)}if (_ctx._fields) _ctx._fields.push({ name: ${JSON.stringify(def.name)}, value: ${inner.valueVar}, span: { start: ${pos}, end: ${inner.endVar} } })`
5002
+ ],
5003
+ valueVar: inner.valueVar,
5004
+ endVar: inner.endVar
5005
+ };
5006
+ }
4833
5007
  case "grammar": {
4834
5008
  const savedTrivia = ctx.activeTrivia;
4835
5009
  const savedKindLabels = ctx.triviaKindLabels;
@@ -4931,6 +5105,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
4931
5105
  case "trivia":
4932
5106
  case "token":
4933
5107
  case "label":
5108
+ case "field":
4934
5109
  case "optional":
4935
5110
  case "many":
4936
5111
  case "oneOrMore":
@@ -4967,6 +5142,7 @@ function childrenOf(def) {
4967
5142
  case "trivia":
4968
5143
  case "token":
4969
5144
  case "label":
5145
+ case "field":
4970
5146
  case "grammar":
4971
5147
  case "not":
4972
5148
  case "node":
@@ -5208,6 +5384,11 @@ function assertRuleName(name) {
5208
5384
  );
5209
5385
  }
5210
5386
  }
5387
+ function publicRuleWrapperSource(rule, fnSource) {
5388
+ const labels = rule._meta.triviaKindLabels;
5389
+ if (labels === void 0) return fnSource;
5390
+ return `Object.assign(${fnSource}, { _meta: { triviaKindLabels: ${JSON.stringify(labels)} } })`;
5391
+ }
5211
5392
  function compileLinkable(ruleMapArg, ns) {
5212
5393
  if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
5213
5394
  for (const [, rule] of ruleMapArg) markUnusedValues(rule);
@@ -5310,7 +5491,7 @@ function compileLinkable(ruleMapArg, ns) {
5310
5491
  ctx.recordFail = savedRec;
5311
5492
  pushNamedFnDecl(ctx, fn, body.stmts, body.valueVar, body.endVar);
5312
5493
  }
5313
- perEntry.push({ key, r: emitNamedFnCall(ctx, fn, "_pos") });
5494
+ perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
5314
5495
  }
5315
5496
  if (ctx.runtimeParsers.length !== 0) return null;
5316
5497
  const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
@@ -5333,14 +5514,14 @@ function compileLinkable(ruleMapArg, ns) {
5333
5514
  ...privateFns
5334
5515
  ].filter(Boolean);
5335
5516
  const wrappers = /* @__PURE__ */ new Map();
5336
- for (const { key, r } of perEntry) {
5337
- wrappers.set(key, [
5517
+ for (const { key, rule, r } of perEntry) {
5518
+ wrappers.set(key, publicRuleWrapperSource(rule, [
5338
5519
  `function(input, _pos, _ctx) {`,
5339
5520
  ` let pos = _pos`,
5340
5521
  ...r.stmts,
5341
5522
  ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
5342
5523
  `}`
5343
- ].join("\n"));
5524
+ ].join("\n")));
5344
5525
  }
5345
5526
  const firstSets = /* @__PURE__ */ new Map();
5346
5527
  for (const [key, rule] of ruleMap) {
@@ -5543,6 +5724,7 @@ function evalRuleMapIR(ir) {
5543
5724
  "skip",
5544
5725
  "trivia",
5545
5726
  "label",
5727
+ "field",
5546
5728
  "expect",
5547
5729
  "_tf",
5548
5730
  "_nd",
@@ -5569,6 +5751,7 @@ function evalRuleMapIR(ir) {
5569
5751
  skip,
5570
5752
  trivia,
5571
5753
  label,
5754
+ field,
5572
5755
  expect,
5573
5756
  _tf,
5574
5757
  _nd
@@ -5583,9 +5766,26 @@ function linkable(rulesMap, ns) {
5583
5766
  if (!pieces) throw new Error("linkable(): this grammar cannot be compiled to a linkable artifact (contains a runtime-only parser fallback)");
5584
5767
  return pieces;
5585
5768
  }
5586
- function cstBuildHost(type, children, _rawChildren, span, _triviaLog, state) {
5769
+ function normalizeCstCollapse(collapse) {
5770
+ if (collapse === true) return () => true;
5771
+ if (Array.isArray(collapse)) {
5772
+ const types = new Set(collapse);
5773
+ return (type) => types.has(type);
5774
+ }
5775
+ return typeof collapse === "function" ? collapse : void 0;
5776
+ }
5777
+ function buildCstNode(type, children, _fields, span, _rawChildren, _triviaLog, state) {
5587
5778
  return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children] };
5588
5779
  }
5780
+ function cstBuildHost(typeOrOptions, children, _fields, span, rawChildren, triviaLog, state) {
5781
+ if (typeof typeOrOptions === "string") {
5782
+ return buildCstNode(typeOrOptions, children ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
5783
+ }
5784
+ const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
5785
+ const host = (type, children2, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children2, fields, span2, rawChildren2, triviaLog2, state2);
5786
+ if (collapse) host._parsemanCstCollapse = collapse;
5787
+ return host;
5788
+ }
5589
5789
  function pick(grammar, names) {
5590
5790
  const p = grammar.ruleFns instanceof Map ? grammar : linkable(grammar);
5591
5791
  const keep = /* @__PURE__ */ new Set();
@@ -5617,8 +5817,9 @@ function fusedBody(pieces) {
5617
5817
  }
5618
5818
  }
5619
5819
  const contributing = new Set(winner.values());
5620
- const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
5621
- const needsHostReads = [...contributing].some((p) => p.needsHostReads);
5820
+ const contributingPieces = [...contributing];
5821
+ const needsEmptyTl = contributingPieces.some((p) => p.needsEmptyTl);
5822
+ const needsHostReads = contributingPieces.some((p) => p.needsHostReads);
5622
5823
  const lines = [
5623
5824
  // Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
5624
5825
  "const _pfFail = {}",
@@ -5626,7 +5827,7 @@ function fusedBody(pieces) {
5626
5827
  ...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
5627
5828
  ...needsHostReads ? [HOST_READS_DECL] : [],
5628
5829
  // Each contributing artifact's namespaced private prelude (regexes, _pf, …).
5629
- ...[...contributing].flatMap((p) => p.prelude),
5830
+ ...[...new Set(contributingPieces.flatMap((p) => p.prelude))],
5630
5831
  // The winning `_r_<Name>` function for each rule (one per name → no redeclare).
5631
5832
  ...[...winner].map(([k, p]) => p.ruleFns.get(k))
5632
5833
  ];
@@ -5649,7 +5850,7 @@ function fusedBody(pieces) {
5649
5850
  }
5650
5851
  );
5651
5852
  const env = {};
5652
- for (const p of contributing) {
5853
+ for (const p of contributingPieces) {
5653
5854
  if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
5654
5855
  if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
5655
5856
  }
@@ -5669,14 +5870,23 @@ function materializePiece(p) {
5669
5870
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
5670
5871
  return pieces;
5671
5872
  }
5672
- function itemPieces(item) {
5873
+ function nextComposeNs(used) {
5874
+ let ns;
5875
+ do {
5876
+ ns = `_lk${_nsCounter++}_`;
5877
+ } while (used.has(ns));
5878
+ used.add(ns);
5879
+ return ns;
5880
+ }
5881
+ function itemPieces(item, used) {
5673
5882
  const carried = item[COMPOSED_PIECES];
5674
- if (Array.isArray(carried)) return carried.map(materializePiece);
5675
- if (item.ruleFns instanceof Map) return [item];
5676
- return [linkable(item)];
5883
+ const pieces = Array.isArray(carried) ? carried.map(materializePiece) : item.ruleFns instanceof Map ? [item] : [linkable(item, nextComposeNs(used))];
5884
+ for (const p of pieces) used.add(p.ns);
5885
+ return pieces;
5677
5886
  }
5678
5887
  function compose(items) {
5679
- const pieces = items.flatMap(itemPieces);
5888
+ const used = /* @__PURE__ */ new Set();
5889
+ const pieces = items.flatMap((item) => itemPieces(item, used));
5680
5890
  const map = fuseRules(pieces);
5681
5891
  Object.defineProperty(map, COMPOSED_PIECES, { value: pieces, enumerable: false });
5682
5892
  return map;