parseman 0.9.0 → 0.10.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.
@@ -19,5 +19,19 @@ export type BuildNode<N> = (children: ReadonlyArray<unknown>, rawChildren: Reado
19
19
  start: number;
20
20
  end: number;
21
21
  }, triviaLog: readonly number[], state: unknown) => N;
22
- export declare function node<N>(type: string, combinator: Combinator<unknown>, build: BuildNode<N>): Combinator<N>;
22
+ /**
23
+ * Options for `node()`.
24
+ * - `collapse` — a structural wrapper rule (a selector list, an
25
+ * expression-precedence level, …) that IS its single child when it captured
26
+ * exactly one. With `collapse: true`, a one-child match returns that child
27
+ * directly (a leaf unwrapped to its string value, a sub-node as-is) and
28
+ * `build` is NOT called; zero or two-plus children go through `build`
29
+ * normally. Lets a grammar keep readable layered rules without paying a build
30
+ * call per collapsing layer — and without hand-writing
31
+ * `if (children.length === 1) return children[0]` in every wrapper builder.
32
+ */
33
+ export type NodeOptions = {
34
+ collapse?: boolean;
35
+ };
36
+ export declare function node<N>(type: string, combinator: Combinator<unknown>, build: BuildNode<N>, opts?: NodeOptions): Combinator<N>;
23
37
  //# sourceMappingURL=node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/combinators/node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAyC,MAAM,aAAa,CAAA;AAGpF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CACzB,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,EAChC,WAAW,EAAE,aAAa,CAAC,OAAO,CAAC,EACnC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EACpC,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,KAAK,EAAE,OAAO,KACX,CAAC,CAAA;AAEN,wBAAgB,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CA+BzG"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/combinators/node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAoD,MAAM,aAAa,CAAA;AAI/F;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CACzB,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,EAChC,WAAW,EAAE,aAAa,CAAC,OAAO,CAAC,EACnC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EACpC,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,KAAK,EAAE,OAAO,KACX,CAAC,CAAA;AAEN;;;;;;;;;;GAUG;AACH,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAShD,wBAAgB,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC,CA8C7H"}
@@ -0,0 +1,26 @@
1
+ import type { ParserDef } from '../types.ts';
2
+ /**
3
+ * Per-node CST-trivia (4th build arg) and state-clone (5th build arg) capture is
4
+ * dead work when the build never declares those formal params. This module derives
5
+ * a build's *confirmed* formal-parameter arity so all three eval paths (interpreter,
6
+ * compiler, macro) can elide that capture identically.
7
+ *
8
+ * Conservative by construction: any source we can't confidently parse — rest params,
9
+ * destructuring, `arguments`, an unrecognized shape — yields `null` (arity unknown),
10
+ * and callers KEEP full capture. We only ever return a number when the parameter list
11
+ * is a plain comma-separated list of simple identifiers.
12
+ */
13
+ type NodeDef = Extract<ParserDef, {
14
+ tag: 'node';
15
+ }>;
16
+ /**
17
+ * Confirmed count of simple formal parameters the build declares, or `null` when the
18
+ * source can't be parsed into a plain identifier list (→ caller keeps full capture).
19
+ */
20
+ export declare function confirmedBuildArity(src: string): number | null;
21
+ /** Build reads the 4th (triviaLog) arg? Unknown/unparseable → true (keep capture). */
22
+ export declare function buildReadsTrivia(def: NodeDef): boolean;
23
+ /** Build reads the 5th (state) arg? Unknown/unparseable → true (keep state clone). */
24
+ export declare function buildReadsState(def: NodeDef): boolean;
25
+ export {};
26
+ //# sourceMappingURL=build-arity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-arity.d.ts","sourceRoot":"","sources":["../../src/compiler/build-arity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C;;;;;;;;;;GAUG;AAEH,KAAK,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAkBlD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuB9D;AAED,sFAAsF;AACtF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAKtD;AAED,sFAAsF;AACtF,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAKrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../../src/compiler/codegen.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,UAAU,EAAuB,WAAW,EAAE,YAAY,EAAE,UAAU,EAAkB,MAAM,aAAa,CAAA;AAoiDzH,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAClD,kGAAkG;IAClG,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAChF;;;;OAIG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG;QAAE,MAAM,EAAE,UAAU,EAAE,CAAA;KAAE,CAAA;IACvF,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC,CAAA;AAoJD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,CAsGhG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,GAC7D;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA4EhD"}
1
+ {"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../../src/compiler/codegen.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,UAAU,EAAuB,WAAW,EAAE,YAAY,EAAE,UAAU,EAAkB,MAAM,aAAa,CAAA;AA4jDzH,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAClD,kGAAkG;IAClG,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAChF;;;;OAIG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG;QAAE,MAAM,EAAE,UAAU,EAAE,CAAA;KAAE,CAAA;IACvF,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC,CAAA;AAoJD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,CAyGhG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,GAC7D;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA6EhD"}
@@ -1 +1 @@
1
- {"version":3,"file":"doc.d.ts","sourceRoot":"","sources":["../../src/functional/doc.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAElE,qFAAqF;AACrF,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,KAAK,WAAW,CAAC,CAAC,CAAC,CAAA;AAEzF,gFAAgF;AAChF,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;AAEnD,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,QAAQ,IAAI;IACrD,wEAAwE;IACxE,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,GAAG,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;CAC1E,CAAA;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,QAAQ;IAC/C,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,CAAA;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;CACtE;AAuHD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,QAAQ,EAClD,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EACrB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,oBAAoB,CAAC,CAAC,CAAM,GACjC,aAAa,CAAC,CAAC,CAAC,CASlB"}
1
+ {"version":3,"file":"doc.d.ts","sourceRoot":"","sources":["../../src/functional/doc.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAElE,qFAAqF;AACrF,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,KAAK,WAAW,CAAC,CAAC,CAAC,CAAA;AAEzF,gFAAgF;AAChF,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;AAEnD,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,QAAQ,IAAI;IACrD,wEAAwE;IACxE,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,GAAG,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;CAC1E,CAAA;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,QAAQ;IAC/C,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,CAAA;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;CACtE;AA0KD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,QAAQ,EAClD,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EACrB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,oBAAoB,CAAC,CAAC,CAAM,GACjC,aAAa,CAAC,CAAC,CAAC,CASlB"}
package/dist/index.cjs CHANGED
@@ -1200,24 +1200,70 @@ function not(combinator) {
1200
1200
  };
1201
1201
  }
1202
1202
 
1203
+ // src/compiler/build-arity.ts
1204
+ var PARAM_LIST_RE = (
1205
+ // (params) => ... | function name?(params) ... | single-ident arrow `x => ...`
1206
+ /^(?:function\b[^(]*\(([^)]*)\)|\(([^)]*)\)\s*=>|([A-Za-z_$][\w$]*)\s*=>)/
1207
+ );
1208
+ var CONFIRMABLE_PARAM_RE = /^[A-Za-z_$][\w$]*\s*\??\s*(?::[^,=]+)?$/;
1209
+ function confirmedBuildArity(src) {
1210
+ const s = src.trim();
1211
+ const m = PARAM_LIST_RE.exec(s);
1212
+ if (!m) return null;
1213
+ if (m[3] !== void 0) return 1;
1214
+ const inner = (m[1] ?? m[2] ?? "").trim();
1215
+ if (inner === "") return 0;
1216
+ const parts = inner.split(",");
1217
+ for (const part of parts) {
1218
+ if (!CONFIRMABLE_PARAM_RE.test(part.trim())) return null;
1219
+ }
1220
+ if (/\barguments\b/.test(s)) return null;
1221
+ return parts.length;
1222
+ }
1223
+ function buildReadsTrivia(def) {
1224
+ const src = def.buildSrc ?? def.build.toString();
1225
+ const arity = confirmedBuildArity(src);
1226
+ if (arity === null) return true;
1227
+ return arity >= 4;
1228
+ }
1229
+ function buildReadsState(def) {
1230
+ const src = def.buildSrc ?? def.build.toString();
1231
+ const arity = confirmedBuildArity(src);
1232
+ if (arity === null) return true;
1233
+ return arity >= 5;
1234
+ }
1235
+
1203
1236
  // src/combinators/node.ts
1204
- function node(type, combinator, build) {
1237
+ function collapseChild(child) {
1238
+ return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
1239
+ }
1240
+ function node(type, combinator, build, opts) {
1205
1241
  const meta = {
1206
1242
  firstSet: combinator._meta.firstSet,
1207
1243
  canMatchNewline: combinator._meta.canMatchNewline,
1208
1244
  isTrivia: false
1209
1245
  };
1246
+ const collapse = opts?.collapse === true;
1247
+ const def = collapse ? { tag: "node", type, parser: combinator, build, collapse: true } : { tag: "node", type, parser: combinator, build };
1248
+ const capturesTrivia = buildReadsTrivia(def);
1249
+ const clonesState = buildReadsState(def);
1210
1250
  return {
1211
1251
  _tag: "node",
1212
1252
  _meta: meta,
1213
- _def: { tag: "node", type, parser: combinator, build },
1253
+ _def: def,
1214
1254
  parse(input, pos, ctx) {
1215
1255
  const saved = beginCstNodeCapture(ctx);
1256
+ if (!capturesTrivia) ctx.captureTrivia = false;
1216
1257
  const r = combinator.parse(input, pos, ctx);
1217
1258
  const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
1218
1259
  if (!r.ok) return r;
1219
- const state = ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
1220
- const built = build(children, rawChildren, r.span, triviaLog, state);
1260
+ const built = collapse && children.length === 1 ? collapseChild(children[0]) : build(
1261
+ children,
1262
+ rawChildren,
1263
+ r.span,
1264
+ triviaLog,
1265
+ clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0
1266
+ );
1221
1267
  const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
1222
1268
  const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
1223
1269
  if (saved.buf !== void 0 || saved.ch !== void 0) {
@@ -2652,25 +2698,34 @@ function emitNode(def, ctx, pos) {
2652
2698
  ctx.buildSrcs.push(def.buildSrc ?? null);
2653
2699
  }
2654
2700
  const i = ind(ctx);
2701
+ const capturesTrivia = mkType !== null || buildReadsTrivia(def);
2702
+ const clonesState = buildReadsState(def);
2655
2703
  const chV = v(ctx, "_ch");
2656
2704
  const rawV = v(ctx, "_raw");
2657
- const tlV = v(ctx, "_tl");
2705
+ const tlV = capturesTrivia ? v(ctx, "_tl") : "_EMPTY_TL";
2706
+ if (!capturesTrivia) ctx.needsEmptyTl = true;
2658
2707
  const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
2708
+ const allocStmt = capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
2709
+ const innerTl = capturesTrivia ? tlV : "undefined";
2659
2710
  const stmts = [
2660
- `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []`,
2711
+ allocStmt,
2661
2712
  `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
2662
- `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${tlV}`
2713
+ `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}`
2663
2714
  ];
2664
2715
  const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
2665
2716
  stmts.push(...innerStmts);
2666
2717
  stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
2667
2718
  stmts.push(...emitIfFail(ctx, `!${okVar}`, failBody(ctx, '"node"', pos)));
2668
- const stV = v(ctx, "_nst");
2669
- stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
2719
+ let stV = "undefined";
2720
+ if (clonesState) {
2721
+ stV = v(ctx, "_nst");
2722
+ stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
2723
+ }
2670
2724
  const ndV = v(ctx, "_nd");
2671
2725
  const ndExpr = mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : `_build[${buildIdx}](${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV})`;
2726
+ 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;
2672
2727
  stmts.push(
2673
- `${i}const ${ndV} = ${ndExpr}`,
2728
+ `${i}const ${ndV} = ${finalExpr}`,
2674
2729
  `${i}if (${sc}) ${sc}.push(${ndV})`,
2675
2730
  `${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} } })`
2676
2731
  );
@@ -3062,7 +3117,9 @@ function compile(combinator, mapFnSources) {
3062
3117
  const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
3063
3118
  ` : "";
3064
3119
  const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
3120
+ const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
3065
3121
  const source = [
3122
+ ...emptyTlDecls,
3066
3123
  ...ctx.regexDecls,
3067
3124
  "",
3068
3125
  ...namedPrelude,
@@ -3074,6 +3131,7 @@ function compile(combinator, mapFnSources) {
3074
3131
  `}`
3075
3132
  ].join("\n");
3076
3133
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
3134
+ ...emptyTlDecls,
3077
3135
  ...ctx.regexDecls,
3078
3136
  collatorDecl,
3079
3137
  ...namedPrelude,
@@ -3127,10 +3185,12 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
3127
3185
  ].join("\n");
3128
3186
  const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
3129
3187
  const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
3130
- const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl;
3188
+ const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
3189
+ const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
3131
3190
  if (!needsWrapper) return innerFn;
3132
3191
  const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
3133
3192
  const hoistedDecls = [
3193
+ emptyTlDecl,
3134
3194
  ...ctx.regexDecls.map((d) => ` ${d}`),
3135
3195
  collatorDecl ? ` ${collatorDecl.trim()}` : "",
3136
3196
  mfDecl,
@@ -3415,6 +3475,31 @@ function replaceAtPath(rebuild, root, path, newNode) {
3415
3475
  function defaultRebuild(node2, children) {
3416
3476
  return { ...node2, children };
3417
3477
  }
3478
+ function shiftSpans(child, delta) {
3479
+ const c = child;
3480
+ const span = { start: c.span.start + delta, end: c.span.end + delta };
3481
+ if (Array.isArray(c.children)) {
3482
+ return { ...c, span, children: c.children.map((g) => shiftSpans(g, delta)) };
3483
+ }
3484
+ return { ...c, span };
3485
+ }
3486
+ function graftAndShift(root, path, newNode, delta) {
3487
+ if (path.length === 0) return newNode;
3488
+ const [idx, ...rest] = path;
3489
+ const oldChildren = root.children;
3490
+ const newChildren = oldChildren.map((child, i) => {
3491
+ if (i < idx) return child;
3492
+ if (i === idx) {
3493
+ return rest.length === 0 ? newNode : graftAndShift(child, rest, newNode, delta);
3494
+ }
3495
+ return shiftSpans(child, delta);
3496
+ });
3497
+ return {
3498
+ ...root,
3499
+ span: { start: root.span.start, end: root.span.end + delta },
3500
+ children: newChildren
3501
+ };
3502
+ }
3418
3503
  var FunctionalDocImpl = class _FunctionalDocImpl {
3419
3504
  _registry;
3420
3505
  _rootRule;
@@ -3452,7 +3537,12 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
3452
3537
  const r = ruleFn(newInput, node2.span.start, ctx);
3453
3538
  if (!r.ok) continue;
3454
3539
  if (r.span.end === node2.span.end + delta) {
3455
- const newTree = replaceAtPath(rebuild, this.tree, path, r.value);
3540
+ if (delta === 0) {
3541
+ const newTree2 = replaceAtPath(rebuild, this.tree, path, r.value);
3542
+ return new _FunctionalDocImpl(this._registry, this._rootRule, this._opts, newTree2, [], newInput);
3543
+ }
3544
+ if (this._opts.rebuild) return reparse();
3545
+ const newTree = graftAndShift(this.tree, path, r.value, delta);
3456
3546
  return new _FunctionalDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
3457
3547
  }
3458
3548
  }