parseman 0.26.3 → 0.27.1

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
@@ -1525,6 +1525,12 @@ function scanTrivia(input, cur, ctx) {
1525
1525
  const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1526
1526
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1527
1527
  }
1528
+ function consumeTrivia(input, cur, ctx) {
1529
+ if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1530
+ const scan = scanTrivia(input, cur, ctx);
1531
+ scan.commit();
1532
+ return scan.end;
1533
+ }
1528
1534
  function fastTriviaScanner(trivia2) {
1529
1535
  const cached = fastTriviaCache.get(trivia2);
1530
1536
  if (cached !== void 0) return cached;
@@ -2305,6 +2311,98 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
2305
2311
  return false;
2306
2312
  }
2307
2313
  }
2314
+ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
2315
+ if (seen.has(p)) return true;
2316
+ seen.add(p);
2317
+ const d = p._def;
2318
+ switch (d.tag) {
2319
+ // Trivia is skipped between/around elements or iterations → a site.
2320
+ case "sequence":
2321
+ case "many":
2322
+ case "oneOrMore":
2323
+ case "sepBy":
2324
+ case "scanTo":
2325
+ case "recover":
2326
+ case "skip":
2327
+ return true;
2328
+ // Nested node manages its own trivia frame; none logs here.
2329
+ case "node":
2330
+ return false;
2331
+ // Pure terminals + trivia-suppressing token: no site.
2332
+ case "regex":
2333
+ case "literal":
2334
+ case "keywords":
2335
+ case "guard":
2336
+ case "token":
2337
+ return false;
2338
+ // Transparent single-child wrappers: recurse.
2339
+ case "optional":
2340
+ case "transform":
2341
+ case "trivia":
2342
+ case "label":
2343
+ case "expect":
2344
+ case "withCtx":
2345
+ case "not":
2346
+ case "field":
2347
+ return parserHasTriviaSite(d.parser, seen);
2348
+ case "choice":
2349
+ return d.parsers.some((x) => parserHasTriviaSite(x, seen));
2350
+ case "grammar":
2351
+ return parserHasTriviaSite(d.parser, seen);
2352
+ case "lazy": {
2353
+ try {
2354
+ return parserHasTriviaSite(d.thunk(), seen);
2355
+ } catch {
2356
+ return true;
2357
+ }
2358
+ }
2359
+ default:
2360
+ return true;
2361
+ }
2362
+ }
2363
+ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
2364
+ if (seen.has(p)) return false;
2365
+ seen.add(p);
2366
+ const d = p._def;
2367
+ switch (d.tag) {
2368
+ case "grammar":
2369
+ return d.captureTrivia === true || parserEnablesTriviaCapture(d.parser, seen);
2370
+ case "node":
2371
+ return false;
2372
+ case "sequence":
2373
+ case "choice":
2374
+ return d.parsers.some((x) => parserEnablesTriviaCapture(x, seen));
2375
+ case "sepBy":
2376
+ return parserEnablesTriviaCapture(d.parser, seen) || parserEnablesTriviaCapture(d.separator, seen);
2377
+ case "skip":
2378
+ return parserEnablesTriviaCapture(d.main, seen) || parserEnablesTriviaCapture(d.skipped, seen);
2379
+ case "scanTo":
2380
+ return parserEnablesTriviaCapture(d.sentinel, seen) || d.skip.some((x) => parserEnablesTriviaCapture(x, seen));
2381
+ case "recover":
2382
+ return parserEnablesTriviaCapture(d.parser, seen) || parserEnablesTriviaCapture(d.sentinel, seen);
2383
+ case "many":
2384
+ case "oneOrMore":
2385
+ case "optional":
2386
+ case "transform":
2387
+ case "trivia":
2388
+ case "token":
2389
+ case "label":
2390
+ case "field":
2391
+ case "expect":
2392
+ case "withCtx":
2393
+ case "not":
2394
+ return parserEnablesTriviaCapture(d.parser, seen);
2395
+ case "lazy": {
2396
+ try {
2397
+ return parserEnablesTriviaCapture(d.thunk(), seen);
2398
+ } catch {
2399
+ return false;
2400
+ }
2401
+ }
2402
+ default:
2403
+ return false;
2404
+ }
2405
+ }
2308
2406
  function buildFieldMap(captures) {
2309
2407
  if (!captures?.length) return void 0;
2310
2408
  const out = {};
@@ -2342,11 +2440,13 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2342
2440
  };
2343
2441
  const unwrap = opts?.unwrap === true;
2344
2442
  const collapse = opts?.collapse === true;
2443
+ const captureTrivia = opts?.captureTrivia === true;
2444
+ const trailingTrivia = opts?.trailingTrivia === true;
2345
2445
  if (unwrap && collapse) {
2346
2446
  throw new Error("node() options cannot set both unwrap and collapse");
2347
2447
  }
2348
- const def = unwrap || collapse ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {} } : baseDef;
2349
- const capturesTrivia = build ? buildReadsTrivia(def) : true;
2448
+ const def = unwrap || collapse || captureTrivia || trailingTrivia ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {} } : baseDef;
2449
+ const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : true);
2350
2450
  const clonesState = build ? buildReadsState(def) : true;
2351
2451
  const capturesFields = parserHasOwnFields(combinator) && (build ? buildReadsFields(def) : true);
2352
2452
  return {
@@ -2362,7 +2462,11 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2362
2462
  ctx._triviaCaptureMask = ctx.build._parsemanTriviaKinds(def.type);
2363
2463
  }
2364
2464
  if (!capturesTrivia) ctx.captureTrivia = false;
2365
- const r = combinator.parse(input, pos, ctx);
2465
+ let r = combinator.parse(input, pos, ctx);
2466
+ if (r.ok && trailingTrivia && ctx.trivia) {
2467
+ const end = consumeTrivia(input, r.span.end, ctx);
2468
+ r = { ...r, span: { start: r.span.start, end } };
2469
+ }
2366
2470
  const fields = capturesFields ? buildFieldMap(ctx._fields) : void 0;
2367
2471
  ctx._fields = savedFields;
2368
2472
  ctx._triviaCaptureMask = savedMask;
@@ -2370,8 +2474,9 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2370
2474
  if (!r.ok) return r;
2371
2475
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
2372
2476
  const nodeType = def.type ?? missingInferredType();
2373
- 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 };
2374
- const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
2477
+ const cstOutput = ctx.build?._parsemanCstOutput === true;
2478
+ 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 ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, r.span, rawChildren, triviaLog, st) : 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 };
2479
+ const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span: r.span };
2375
2480
  if (saved.buf !== void 0 || saved.ch !== void 0) {
2376
2481
  pushCstChild(ctx, built, rawEntry);
2377
2482
  }
@@ -2488,6 +2593,7 @@ function parser(opts, root) {
2488
2593
  parser: root,
2489
2594
  triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
2490
2595
  clearTrivia,
2596
+ ...opts.captureTrivia ? { captureTrivia: true } : {},
2491
2597
  trackLines: opts.trackLines ?? false
2492
2598
  },
2493
2599
  parse(input, pos, _ctx) {
@@ -3649,7 +3755,7 @@ function scanBranchLabeled(shape, kindIndex, mint) {
3649
3755
  ` if (${m.end} > _e) {`,
3650
3756
  ` if (_cap) {`,
3651
3757
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
3652
- ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << kindIndex}))) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
3758
+ ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << kindIndex}))) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
3653
3759
  ` }`,
3654
3760
  ` _e = ${m.end}`,
3655
3761
  ` continue`,
@@ -3705,7 +3811,7 @@ function analyzeTriviaFastPath(trivia2) {
3705
3811
  var CAP_RECORD = [
3706
3812
  ` if (_cap && _e > _pos) {`,
3707
3813
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
3708
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
3814
+ ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
3709
3815
  ` }`
3710
3816
  ].join("\n");
3711
3817
  function composeFastLoop(shapes) {
@@ -3765,7 +3871,7 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
3765
3871
  ` const _ce = _e + _m${i}[0].length`,
3766
3872
  ` if (_cap) {`,
3767
3873
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
3768
- ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3874
+ ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3769
3875
  ` }`,
3770
3876
  ` _e = _ce`,
3771
3877
  ` _matched = true`,
@@ -3803,7 +3909,7 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
3803
3909
  ` const _ce = _r${i}.span.end`,
3804
3910
  ` if (_cap) {`,
3805
3911
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
3806
- ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3912
+ ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3807
3913
  ` }`,
3808
3914
  ` _e = _ce`,
3809
3915
  ` _matched = true`,
@@ -4146,7 +4252,7 @@ function ensureTriviaFn(ctx) {
4146
4252
  ` }`,
4147
4253
  ` if (_cap && _e > _pos) {`,
4148
4254
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
4149
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
4255
+ ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
4150
4256
  ` }`,
4151
4257
  ` return _e`,
4152
4258
  `}`
@@ -5281,47 +5387,71 @@ function emitNode(def, ctx, pos) {
5281
5387
  ctx.buildSrcs.push(def.buildSrc ?? null);
5282
5388
  }
5283
5389
  const i = ind(ctx);
5284
- const capturesTrivia = mkType !== null || !structural && buildReadsTrivia(def);
5390
+ const capturesTrivia = mkType !== null || def.captureTrivia === true || def.trailingTrivia === true || !structural && buildReadsTrivia(def);
5285
5391
  const clonesState = !structural && buildReadsState(def);
5286
5392
  const hasFields = parserHasOwnFields(def.parser);
5287
5393
  const capturesFields = hasFields && !structural && buildReadsFields(def);
5394
+ const innerEnablesTriviaCapture = parserEnablesTriviaCapture(def.parser);
5288
5395
  const chV = v(ctx, "_ch");
5289
5396
  const rawV = v(ctx, "_raw");
5290
- const capTLv = structural ? v(ctx, "_ctl") : null;
5397
+ const capTLv = structural || capturesTrivia ? v(ctx, "_ctl") : null;
5291
5398
  const capSTv = structural ? v(ctx, "_cst") : null;
5292
5399
  const capFv = structural && hasFields ? v(ctx, "_cf") : null;
5293
5400
  const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
5294
- if (!capturesTrivia) ctx.needsEmptyTl = true;
5401
+ if (!capturesTrivia || structural) ctx.needsEmptyTl = true;
5295
5402
  const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
5296
5403
  const smk = structural ? v(ctx, "_smk") : null;
5297
5404
  if (structural) ctx.needsHostReads = true;
5298
5405
  const hostTriviaGate = `_ctx.build !== undefined && (_ctx.build._parsemanCaptureTrivia !== undefined ? _ctx.build._parsemanCaptureTrivia(${JSON.stringify(def.type)}) : (_ctx._pmCapTL ??= _hostReads(_ctx.build, 5)))`;
5299
- 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)` : ""}
5300
- ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
5301
- const innerTl = structural ? `${capTLv} ? ${tlV} : undefined` : capturesTrivia ? tlV : "undefined";
5302
- const fieldsOn = structural ? capFv ?? "false" : capturesFields ? "true" : "false";
5406
+ const pmV = v(ctx, "_pm"), recV = v(ctx, "_rec"), capV = v(ctx, "_cap");
5407
+ const profHoist = `${i}const ${pmV} = _ctx._pmProfile, ${recV} = ${pmV}?.phase === 'recognizer', ${capV} = ${pmV}?.phase === 'capture'`;
5408
+ const profileRecognizer = recV;
5409
+ const profileCapture = capV;
5410
+ const structuralCapturesTrivia = structural && (def.captureTrivia === true || def.trailingTrivia === true);
5411
+ const allocStmt = structural ? `${i}const ${capTLv} = !(${profileRecognizer}) && (${profileCapture} || ${structuralCapturesTrivia ? "true" : hostTriviaGate}), ${capSTv} = !(${profileRecognizer} || ${profileCapture}) && (_ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 6)))${capFv ? `, ${capFv} = !(${profileRecognizer}) && (${profileCapture} || (_ctx.build !== undefined && _hostReads(_ctx.build, 2)))` : ""}
5412
+ ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : ${innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`}` : capturesTrivia ? `${i}const ${capTLv} = !(${profileRecognizer}), ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : []` : `${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : []`;
5413
+ const innerTl = structural || capturesTrivia ? structural && !innerEnablesTriviaCapture ? `${capTLv} ? ${tlV} : undefined` : tlV : "undefined";
5414
+ const fieldsOn = structural ? capFv ?? "false" : `${profileRecognizer} ? false : ${capturesFields ? "true" : "false"}`;
5303
5415
  const sf = hasFields ? v(ctx, "_sf") : null;
5304
5416
  const fArr = hasFields ? v(ctx, "_fa") : null;
5305
5417
  const fObj = hasFields ? v(ctx, "_fields") : "undefined";
5418
+ const needsTriviaFrame = def.trailingTrivia === true || parserHasTriviaSite(def.parser);
5419
+ const saveTrivia = needsTriviaFrame ? `, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog${smk ? `, ${smk} = _ctx._triviaCaptureMask` : ""}` : "";
5420
+ const installTrivia = needsTriviaFrame ? `; _ctx.captureTrivia = ${capTLv ?? "false"}; _ctx._cstTriviaLog = ${innerTl}${smk ? `; _ctx._triviaCaptureMask = ${capTLv} && _ctx.build !== undefined && _ctx.build._parsemanTriviaKinds !== undefined ? _ctx.build._parsemanTriviaKinds(${JSON.stringify(def.type)}) : ${smk}` : ""}` : "";
5421
+ const restoreTrivia = needsTriviaFrame ? `; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${smk ? `; _ctx._triviaCaptureMask = ${smk}` : ""}` : "";
5306
5422
  const stmts = [
5423
+ profHoist,
5307
5424
  allocStmt,
5308
- `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog${smk ? `, ${smk} = _ctx._triviaCaptureMask` : ""}${sf ? `, ${sf} = _ctx._fields` : ""}`,
5309
- `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}${smk ? `; _ctx._triviaCaptureMask = ${capTLv} && _ctx.build._parsemanTriviaKinds !== undefined ? _ctx.build._parsemanTriviaKinds(${JSON.stringify(def.type)}) : ${smk}` : ""}${sf ? `; _ctx._fields = ${fieldsOn} ? [] : undefined` : ""}`
5425
+ `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren${saveTrivia}${sf ? `, ${sf} = _ctx._fields` : ""}`,
5426
+ `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}${installTrivia}${sf ? `; _ctx._fields = ${fieldsOn} ? [] : undefined` : ""}`
5310
5427
  ];
5311
- const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
5428
+ const { stmts: innerStmts, okVar, endVar: innerEndVar } = emitFallible(def.parser, ctx, pos);
5429
+ const endVar = def.trailingTrivia === true && ctx.activeTrivia ? v(ctx, "_trailend") : innerEndVar;
5312
5430
  stmts.push(...innerStmts);
5431
+ if (def.trailingTrivia === true && ctx.activeTrivia) {
5432
+ const triviaFn = ensureTriviaFn(ctx);
5433
+ stmts.push(`${i}const ${endVar} = ${okVar} ? ${triviaFn}(input, ${innerEndVar}, _ctx, 1) : ${innerEndVar}`);
5434
+ }
5313
5435
  if (sf && fArr) {
5314
5436
  stmts.push(`${i}const ${fArr} = _ctx._fields`);
5315
5437
  }
5316
- stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${smk ? `; _ctx._triviaCaptureMask = ${smk}` : ""}${sf ? `; _ctx._fields = ${sf}` : ""}`);
5438
+ stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}${restoreTrivia}${sf ? `; _ctx._fields = ${sf}` : ""}`);
5317
5439
  stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
5440
+ stmts.push(
5441
+ `${i}if (_ctx._pmProfile) {`,
5442
+ `${i} _ctx._pmProfile.nodes++`,
5443
+ `${i} if (${profileCapture}) {`,
5444
+ `${i} _ctx._pmProfile.childSlots += ${chV}.length; _ctx._pmProfile.rawSlots += ${rawV}.length; _ctx._pmProfile.triviaSlots += ${tlV}.length${fArr ? `; _ctx._pmProfile.fieldSlots += ${fArr} ? ${fArr}.length : 0` : ""}`,
5445
+ `${i} }`,
5446
+ `${i}}`
5447
+ );
5318
5448
  let stV = "undefined";
5319
5449
  if (structural) {
5320
5450
  stV = v(ctx, "_nst");
5321
5451
  stmts.push(`${i}const ${stV} = ${capSTv} && _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
5322
5452
  } else if (clonesState) {
5323
5453
  stV = v(ctx, "_nst");
5324
- stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
5454
+ stmts.push(`${i}const ${stV} = !(${profileRecognizer} || ${profileCapture}) && _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
5325
5455
  }
5326
5456
  if (sf && fArr) {
5327
5457
  const fe = v(ctx, "_fe");
@@ -5341,15 +5471,18 @@ ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capt
5341
5471
  }
5342
5472
  const ndV = v(ctx, "_nd");
5343
5473
  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})`;
5344
- 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;
5345
- 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;
5474
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, { start: ${pos}, end: ${endVar} }, ${rawV}, ${tlV}, ${stV})`;
5475
+ const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : `_ctx.build?._parsemanCstOutput === true ? (${hostBuildExpr}) : (${buildExpr})`;
5476
+ const hostCollapseExpr = 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;
5346
5477
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
5347
5478
  const collapseExpr = `${chV}.length === 1 ? ${chV}[0] : (${ndExpr})`;
5348
5479
  const finalExpr = def.unwrap ? unwrapExpr : def.collapse ? collapseExpr : hostCollapseExpr;
5349
5480
  stmts.push(
5350
- `${i}const ${ndV} = ${finalExpr}`,
5351
- `${i}if (${sc}) ${sc}.push(${ndV})`,
5352
- `${i}if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && (${ndV}._tag === 'node' || ${ndV}._tag === 'leaf' || ${ndV}._tag === 'parseError')) ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : '', span: { start: ${pos}, end: ${endVar} } })`
5481
+ `${i}const ${ndV} = (${profileRecognizer} || ${profileCapture}) ? undefined : (${finalExpr})`,
5482
+ `${i}if (!(${profileRecognizer})) {`,
5483
+ `${i} if (${sc}) ${sc}.push(${ndV})`,
5484
+ `${i} if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && (${ndV}._tag === 'node' || ${ndV}._tag === 'leaf' || ${ndV}._tag === 'parseError')) ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : (typeof ${ndV} === 'object' && ${ndV} !== null ? input.slice(${pos}, ${endVar}) : ''), span: { start: ${pos}, end: ${endVar} } })`,
5485
+ `${i}}`
5353
5486
  );
5354
5487
  return { stmts, valueVar: ndV, endVar };
5355
5488
  }
@@ -5591,10 +5724,19 @@ function emitDispatch(p, ctx, pos) {
5591
5724
  ctx.triviaKindLabels = def.triviaParser._meta.triviaKindLabels;
5592
5725
  }
5593
5726
  }
5727
+ const savedCapture = def.captureTrivia ? v(ctx, "_gcap") : null;
5594
5728
  const r = emit(def.parser, ctx, pos);
5595
5729
  ctx.activeTrivia = savedTrivia;
5596
5730
  ctx.triviaKindLabels = savedKindLabels;
5597
- return r;
5731
+ if (!savedCapture) return r;
5732
+ return {
5733
+ ...r,
5734
+ stmts: [
5735
+ `${ind(ctx)}const ${savedCapture} = _ctx.captureTrivia; _ctx.captureTrivia = true`,
5736
+ ...r.stmts,
5737
+ `${ind(ctx)}_ctx.captureTrivia = ${savedCapture}`
5738
+ ]
5739
+ };
5598
5740
  }
5599
5741
  case "not":
5600
5742
  return emitNot(def, ctx, pos);
@@ -6357,9 +6499,15 @@ function evalRuleMapIR(ir) {
6357
6499
  t._def.fnSrc = src;
6358
6500
  return t;
6359
6501
  };
6360
- const _nd = (type, child, src, opts) => {
6361
- const n = node(type, child, void 0, opts);
6502
+ const _nd = (type, child, src, opts, staticError) => {
6503
+ if (staticError !== void 0 && staticError.length > 0) {
6504
+ throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
6505
+ }
6506
+ const n = node(type, child, (() => {
6507
+ throw new Error("IR node build requires static re-lowering");
6508
+ }), opts);
6362
6509
  n._def.buildSrc = src;
6510
+ if (staticError !== void 0) n._def.buildStaticError = staticError;
6363
6511
  return n;
6364
6512
  };
6365
6513
  const _gch = (items) => {
@@ -6609,17 +6757,26 @@ ${body}
6609
6757
  }
6610
6758
  case "node": {
6611
6759
  if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
6612
- const opts = def.unwrap || def.collapse ? `, { ${def.unwrap ? "unwrap: true" : "collapse: true"} }` : "";
6760
+ const optEntries = [
6761
+ ...def.unwrap ? ["unwrap: true"] : [],
6762
+ ...def.collapse ? ["collapse: true"] : [],
6763
+ ...def.captureTrivia ? ["captureTrivia: true"] : [],
6764
+ ...def.trailingTrivia ? ["trailingTrivia: true"] : []
6765
+ ];
6766
+ const opts = optEntries.length > 0 ? `, { ${optEntries.join(", ")} }` : "";
6613
6767
  if (def.type === void 0) {
6614
6768
  if (def.buildSrc !== void 0) throw new Unserializable("inferred node build without inferred type");
6615
6769
  return opts ? `node(${kid(def.parser)}, undefined${opts})` : `node(${kid(def.parser)})`;
6616
6770
  }
6617
- if (def.buildSrc !== void 0) return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts})`;
6771
+ if (def.buildSrc !== void 0) {
6772
+ const staticError = def.buildStaticError === void 0 ? "" : `${opts ? "" : ", undefined"}, ${JSON.stringify(def.buildStaticError)}`;
6773
+ return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts}${staticError})`;
6774
+ }
6618
6775
  return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
6619
6776
  }
6620
6777
  case "grammar": {
6621
6778
  const trivia2 = def.clearTrivia ? "null" : def.triviaParser ? kid(def.triviaParser) : "undefined";
6622
- return `parser({ trivia: ${trivia2}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
6779
+ return `parser({ trivia: ${trivia2}${def.captureTrivia ? ", captureTrivia: true" : ""}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
6623
6780
  }
6624
6781
  case "withCtx": {
6625
6782
  if (def.extraSrc === void 0) throw new Unserializable("withCtx without extraSrc");
@@ -6657,9 +6814,11 @@ function cstBuildHost(typeOrOptions, children, _fields, span, rawChildren, trivi
6657
6814
  }
6658
6815
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
6659
6816
  const host = (type, children2, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children2, fields, span2, rawChildren2, triviaLog2, state2);
6817
+ host._parsemanCstOutput = true;
6660
6818
  if (collapse) host._parsemanCstCollapse = collapse;
6661
6819
  return host;
6662
6820
  }
6821
+ cstBuildHost._parsemanCstOutput = true;
6663
6822
  function fusedBody(pieces) {
6664
6823
  const winner = /* @__PURE__ */ new Map();
6665
6824
  for (const p of pieces) for (const k of p.keys) winner.set(k, p);
@@ -7317,7 +7476,7 @@ function parseDoc(registry, rootRule, input, opts = {}) {
7317
7476
 
7318
7477
  // src/functional/run.ts
7319
7478
  var invoke = (r, input, pos, ctx) => typeof r === "function" ? r(input, pos, ctx) : r.parse(input, pos, ctx);
7320
- function run(entry, input, options = {}) {
7479
+ function runOnce(entry, input, options, phase, profileState) {
7321
7480
  if (typeof entry !== "function" && typeof entry?.parse !== "function") {
7322
7481
  throw new TypeError(
7323
7482
  `run(): start production is ${entry === null ? "null" : typeof entry}, not a rule \u2014 the requested grammar rule does not exist (check the rule name).`
@@ -7325,11 +7484,12 @@ function run(entry, input, options = {}) {
7325
7484
  }
7326
7485
  const triviaLog = [];
7327
7486
  const errors = [];
7487
+ const profile = profileState ?? (phase === void 0 ? void 0 : { phase, nodes: 0, childSlots: 0, rawSlots: 0, triviaSlots: 0, fieldSlots: 0, hostCalls: 0 });
7488
+ const skipGlobalSinks = phase === "recognizer" || phase === "capture";
7328
7489
  const grammarTrivia = typeof entry !== "function" ? entry._meta.grammarTrivia : void 0;
7329
7490
  const ctx = {
7330
7491
  trackLines: false,
7331
- _triviaLog: triviaLog,
7332
- _errors: errors,
7492
+ ...skipGlobalSinks ? { _pmProfile: profile } : { _triviaLog: triviaLog, _errors: errors, ...profile === void 0 ? {} : { _pmProfile: profile } },
7333
7493
  build: options.build,
7334
7494
  state: options.state,
7335
7495
  ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
@@ -7356,6 +7516,38 @@ function run(entry, input, options = {}) {
7356
7516
  unconsumedFrom
7357
7517
  };
7358
7518
  }
7519
+ function clonePassState(state) {
7520
+ if (state === null || typeof state !== "object") return state;
7521
+ return Array.isArray(state) ? [...state] : { ...state };
7522
+ }
7523
+ function profilePass(entry, input, options, phase) {
7524
+ const state = { phase, nodes: 0, childSlots: 0, rawSlots: 0, triviaSlots: 0, fieldSlots: 0, hostCalls: 0 };
7525
+ const passOptions = options.state === void 0 ? options : { ...options, state: clonePassState(options.state) };
7526
+ const start = performance.now();
7527
+ const result = runOnce(entry, input, passOptions, phase, state);
7528
+ const { phase: _phase, ...counts } = state;
7529
+ return { result, profile: { ms: performance.now() - start, ...counts } };
7530
+ }
7531
+ function run(entry, input, options = {}) {
7532
+ if (!options.profile) return runOnce(entry, input, options);
7533
+ if (typeof entry !== "function") {
7534
+ throw new TypeError("run({ profile: true }) requires a compiled parser entry");
7535
+ }
7536
+ const recognizer = profilePass(entry, input, options, "recognizer");
7537
+ const capture = profilePass(entry, input, options, "capture");
7538
+ const host = profilePass(entry, input, options, "host");
7539
+ if (recognizer.result.ok !== host.result.ok || capture.result.ok !== host.result.ok || recognizer.result.unconsumedFrom !== host.result.unconsumedFrom || capture.result.unconsumedFrom !== host.result.unconsumedFrom) {
7540
+ throw new Error("run({ profile: true }) changed recognition; the grammar is not profile-safe");
7541
+ }
7542
+ return {
7543
+ ...host.result,
7544
+ profile: {
7545
+ recognizer: recognizer.profile,
7546
+ structuralCapture: capture.profile,
7547
+ hostConstruction: host.profile
7548
+ }
7549
+ };
7550
+ }
7359
7551
 
7360
7552
  // src/cst/trivia-index.ts
7361
7553
  function merge(map, key, run2) {
@@ -7476,6 +7668,9 @@ function triviaEntries(log, labels, opts) {
7476
7668
  end(i) {
7477
7669
  return log[entryOffset(i, stride) + 1];
7478
7670
  },
7671
+ insertIndex(i) {
7672
+ return opts?.nodeLog ? log[entryOffset(i, stride) + 2] : void 0;
7673
+ },
7479
7674
  kindIndex(i) {
7480
7675
  if (!labels) return void 0;
7481
7676
  return log[entryOffset(i, stride) + baseStride];