parseman 0.61.4 → 0.61.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/analysis/diagnostics.js +844 -522
  2. package/dist/analysis/diagnostics.js.map +3 -3
  3. package/dist/cli/index.js +846 -522
  4. package/dist/cli/index.js.map +3 -3
  5. package/dist/cst/capture-buffer.d.ts +3 -2
  6. package/dist/cst/capture-buffer.d.ts.map +1 -1
  7. package/dist/index.js +890 -523
  8. package/dist/index.js.map +3 -3
  9. package/dist/plugin/entry-supercompiler.d.ts +15 -0
  10. package/dist/plugin/entry-supercompiler.d.ts.map +1 -1
  11. package/dist/plugin/index.js +959 -503
  12. package/dist/plugin/index.js.map +3 -3
  13. package/dist/table/assemble.d.ts.map +1 -1
  14. package/dist/table/child-slots.d.ts.map +1 -1
  15. package/dist/table/emit-assembly.d.ts +1 -1
  16. package/dist/table/emit-assembly.d.ts.map +1 -1
  17. package/dist/table/encode.d.ts.map +1 -1
  18. package/dist/table/exec.d.ts.map +1 -1
  19. package/dist/table/fail-clean.d.ts.map +1 -1
  20. package/dist/table/hold-gap.d.ts.map +1 -1
  21. package/dist/table/index.js +421 -54
  22. package/dist/table/index.js.map +3 -3
  23. package/dist/table/late-frame.d.ts +18 -0
  24. package/dist/table/late-frame.d.ts.map +1 -0
  25. package/dist/table/ops.d.ts +17 -0
  26. package/dist/table/ops.d.ts.map +1 -1
  27. package/dist/table/select-hot.d.ts +9 -0
  28. package/dist/table/select-hot.d.ts.map +1 -1
  29. package/dist/table/site-labels.d.ts +5 -1
  30. package/dist/table/site-labels.d.ts.map +1 -1
  31. package/dist/version.d.ts +1 -1
  32. package/package.json +1 -1
  33. package/src/cst/capture-buffer.ts +20 -11
  34. package/src/plugin/entry-supercompiler.ts +110 -1
  35. package/src/table/assemble.ts +124 -4
  36. package/src/table/child-slots.ts +9 -1
  37. package/src/table/emit-assembly.ts +101 -14
  38. package/src/table/encode.ts +69 -6
  39. package/src/table/exec.ts +7 -1
  40. package/src/table/fail-clean.ts +8 -1
  41. package/src/table/hold-gap.ts +5 -1
  42. package/src/table/late-frame.ts +54 -0
  43. package/src/table/ops.ts +19 -0
  44. package/src/table/select-hot.ts +96 -18
  45. package/src/table/site-labels.ts +38 -14
  46. package/src/version.ts +1 -1
@@ -0,0 +1,18 @@
1
+ export type ContinuationGate = {
2
+ /** Class of the first character, past any trivia. */
3
+ readonly cls: number;
4
+ /** The regex row of a leading gap, or −1. */
5
+ readonly gapRow: number;
6
+ /** Class of the character after the gap. */
7
+ readonly follow: number;
8
+ /** Class of an arm that opens without the gap, or −1. */
9
+ readonly other: number;
10
+ };
11
+ export declare function continuationGate(code: ArrayLike<number>, ip: number): ContinuationGate | undefined;
12
+ /**
13
+ * Can this assembly decide at this `OP_CONT`? It needs the tail's test, and the
14
+ * trivia the label names: a scope it cannot name, or one with no scanner, would cost a
15
+ * hit more calls than the frame it saves on a miss.
16
+ */
17
+ export declare function canDecide(code: ArrayLike<number>, ip: number, tri: number, triviaScan: readonly unknown[]): boolean;
18
+ //# sourceMappingURL=late-frame.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"late-frame.d.ts","sourceRoot":"","sources":["../../src/table/late-frame.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,qDAAqD;IACrD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,6CAA6C;IAC7C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,yDAAyD;IACzD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAoBlG;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAGnH"}
@@ -508,6 +508,23 @@ export declare const OP_RGATE2 = 47;
508
508
  * assembly resolves it to one child; it has no body of its own.
509
509
  */
510
510
  export declare const OP_STRICT = 48;
511
+ /**
512
+ * `CONT corner wrapper tail` — a `collapse` node that is `sequence(corner, tail)`, with
513
+ * a tail that can take nothing and a corner that is one node. A strict assembly runs
514
+ * `corner` in the PARENT's collector and looks at the next token. Where `tail` cannot
515
+ * start there, the corner is the result: no frame, no children, no builder. Where it
516
+ * can, the corner's publication is taken back and `wrapper`'s frame opens around it and
517
+ * `tail`, in this one function. `tail` is read for the decision (`late-frame.ts`). Where
518
+ * no decision can be made, and under recovery, a probe or the reference driver, this
519
+ * is `wrapper`, the node as written.
520
+ */
521
+ export declare const OP_CONT = 49;
522
+ /**
523
+ * `SEED corner` — marks the first term of `wrapper`'s body. Run, it is `corner`. It
524
+ * exists so the labels can leave out the edge from a late frame to its corner, which
525
+ * never runs there.
526
+ */
527
+ export declare const OP_SEED = 50;
511
528
  /**
512
529
  * `DISPATCH sel d other otherRouted n a1 … an` — `dispatch()`.
513
530
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../../src/table/ops.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../../src/table/ops.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,15 @@ export declare function selectHotSites(code: readonly number[], entries: readonl
8
8
  /** Measured calls per site (`countSiteVisits` over a corpus). Replaces the
9
9
  * static estimate when given. */
10
10
  profile?: ArrayLike<number>): Set<number>;
11
+ /**
12
+ * The sites the function for `ip` calls, as `emit-assembly.ts` lowers it. An `OP_CONT`
13
+ * with a body is a late frame, lowered with its wrapper in one function, calling its
14
+ * corner and its tail. With no body, the emitter can't decide there and it's an alias
15
+ * to its wrapper, as `OP_SEED` is to its corner. A gap gate has no body either, so its
16
+ * item counts as the repeat's own child, and adding the row moves no other site's
17
+ * estimate (`gap-gate.ts`).
18
+ */
19
+ export declare function edgesAsLowered(code: readonly number[], siteBytes: ReadonlyMap<number, number>, ip: number, out: number[]): void;
11
20
  /**
12
21
  * The largest prefix of the ranking whose real source fits `ceiling`. `siteBytes`
13
22
  * leaves out what sites add around their bodies, so the estimate can overshoot:
@@ -1 +1 @@
1
- {"version":3,"file":"select-hot.d.ts","sourceRoot":"","sources":["../../src/table/select-hot.ts"],"names":[],"mappings":"AAuBA;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACtC,MAAM,EAAE,MAAM;AACd;iCACiC;AACjC,OAAO,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,GAC1B,GAAG,CAAC,MAAM,CAAC,CAgBb;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC;AACtC,2DAA2D;AAC3D,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,KAAK,MAAM,EACnD,OAAO,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,GAC1B;IAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAQ7E"}
1
+ {"version":3,"file":"select-hot.d.ts","sourceRoot":"","sources":["../../src/table/select-hot.ts"],"names":[],"mappings":"AAqCA;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACtC,MAAM,EAAE,MAAM;AACd;iCACiC;AACjC,OAAO,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,GAC1B,GAAG,CAAC,MAAM,CAAC,CAUb;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CAe/H;AAqDD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC;AACtC,2DAA2D;AAC3D,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,KAAK,MAAM,EACnD,OAAO,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,GAC1B;IAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAQ7E"}
@@ -69,6 +69,8 @@ export declare function gatedPassCollapse(code: Int32Array, ip: number, hostCst:
69
69
  /** A site's label, or `TOP` for anything the walk did not reach. */
70
70
  export type SiteLabels = {
71
71
  at: (ip: number) => SiteLabel;
72
+ /** The `OP_CONT` sites whose corner runs in the parent's collector in every engine. */
73
+ late: ReadonlySet<number>;
72
74
  };
73
75
  /**
74
76
  * Compute a label for every site reachable from `roots`.
@@ -82,7 +84,9 @@ export type SiteLabels = {
82
84
  * The meet only ever moves a label toward `TOP`, and `TOP` is absorbing, so the
83
85
  * lattice has height three and the walk terminates.
84
86
  */
85
- export declare function computeSiteLabels(code: Int32Array, roots: Iterable<number>, hostCst: boolean): SiteLabels;
87
+ export declare function computeSiteLabels(code: Int32Array, roots: Iterable<number>, hostCst: boolean,
88
+ /** Does this assembly run this `OP_CONT` late, in EVERY engine it holds? Absent: none. */
89
+ late?: (ip: number, tri: number) => boolean): SiteLabels;
86
90
  /**
87
91
  * Does a piece entered at `ip` need the caller's INSTALLED trivia scanner handed
88
92
  * across an engine boundary?
@@ -1 +1 @@
1
- {"version":3,"file":"site-labels.d.ts","sourceRoot":"","sources":["../../src/table/site-labels.ts"],"names":[],"mappings":"AAqEA,iFAAiF;AACjF,eAAO,MAAM,WAAW,KAAK,CAAA;AAC7B,qDAAqD;AACrD,eAAO,MAAM,QAAQ,KAAK,CAAA;AAC1B,0EAA0E;AAE1E,qDAAqD;AACrD,eAAO,MAAM,WAAW,IAAI,CAAA;AAC5B,yDAAyD;AACzD,eAAO,MAAM,OAAO,IAAI,CAAA;AACxB,uDAAuD;AACvD,eAAO,MAAM,MAAM,IAAI,CAAA;AAIvB,uCAAuC;AACvC,eAAO,MAAM,WAAW,IAAI,CAAA;AAC5B,oDAAoD;AACpD,eAAO,MAAM,QAAQ,IAAI,CAAA;AAEzB,MAAM,MAAM,SAAS,GAAG;IACtB,mFAAmF;IACnF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAA;IACrB,kDAAkD;IAClD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,4CAA4C;IAC5C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,wDAAwD;AACxD,eAAO,MAAM,GAAG,EAAE,SAAgF,CAAA;AAWlG;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAarF;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAYpF;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAuB/I;AAqED,oEAAoE;AACpE,MAAM,MAAM,UAAU,GAAG;IAAE,EAAE,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,SAAS,CAAA;CAAE,CAAA;AAE1D;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,EACvB,OAAO,EAAE,OAAO,GACf,UAAU,CA6BZ;AAED;;;;;;;;;;;;;GAaG;AACH;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,GAAG,OAAO,CAKpF;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAqB3F;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC;IAAE,KAAK,EAAE;QAAE,QAAQ,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,SAAS;gBAAE,EAAE,EAAE,MAAM,CAAC;gBAAC,EAAE,EAAE,MAAM,CAAA;aAAE,EAAE,CAAA;SAAE,CAAA;KAAE,CAAA;CAAE,GAAG,SAAS,CAAC,EAAE,GACtI,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,SAAS,CAOnD"}
1
+ {"version":3,"file":"site-labels.d.ts","sourceRoot":"","sources":["../../src/table/site-labels.ts"],"names":[],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -13,5 +13,5 @@
13
13
  * Kept in sync with `package.json` by `test/unit/version-sync.test.ts`, which fails
14
14
  * the build if this constant drifts from the published version.
15
15
  */
16
- export declare const PARSEMAN_VERSION = "0.61.4";
16
+ export declare const PARSEMAN_VERSION = "0.61.6";
17
17
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "parseman",
3
- "version": "0.61.4",
3
+ "version": "0.61.6",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "engines": {
6
6
  "node": "^20.19.0 || >=22.13.0"
@@ -122,26 +122,35 @@ export function pushCstChild(ctx: ParseContext, built: unknown, rawEntry: unknow
122
122
  /**
123
123
  * Undo the LAST `pushCstChild` into the active collector, whichever form it took:
124
124
  * a child the lazy buffer's empty sentinel dropped (`undefined` first) leaves no
125
- * semantic entry to remove, only its raw one.
125
+ * semantic entry to remove, only its raw one. Returns that raw entry, or `undefined`
126
+ * when this collector keeps none.
126
127
  */
127
- export function popCstChild(ctx: ParseContext): void {
128
+ export function popCstChild(ctx: ParseContext): unknown {
128
129
  const b = ctx._cstBuf
129
130
  if (b) {
130
131
  const ch = b.ch
131
- if (b.rawOnly !== true && ch) {
132
- ch.pop()
133
- if (ch.length === 1) { b.single = ch[0]; b.ch = undefined }
132
+ if (b.rawOnly !== true) {
133
+ if (ch) {
134
+ ch.pop()
135
+ if (ch.length === 1) { b.single = ch[0]; b.ch = undefined }
136
+ } else b.single = undefined
137
+ }
138
+ if (b.noRaw === true) {
139
+ b.rawLen = (b.rawLen ?? 1) - 1
140
+ return undefined
134
141
  }
135
- if (b.noRaw === true) b.rawLen = (b.rawLen ?? 1) - 1
136
- else if (b.raw) {
137
- b.raw.pop()
142
+ if (b.raw) {
143
+ const entry = b.raw.pop()
138
144
  if (b.raw.length === 1) { b.rawSingle = b.raw[0]; b.raw = undefined }
139
- } else b.rawSingle = undefined
140
- return
145
+ return entry
146
+ }
147
+ const entry = b.rawSingle
148
+ b.rawSingle = undefined
149
+ return entry
141
150
  }
142
151
  if (ctx._cstChildren) ctx._cstChildren.pop()
143
152
  else if (ctx._cstLeaves) ctx._cstLeaves.pop()
144
- if (ctx._cstRawChildren) ctx._cstRawChildren.pop()
153
+ return ctx._cstRawChildren?.pop()
145
154
  }
146
155
 
147
156
  export function cstRawLen(ctx: ParseContext): number {
@@ -65,6 +65,8 @@ type Call = {
65
65
  type Edit = { readonly start: number; readonly end: number; readonly text: string }
66
66
 
67
67
  const PIECE = /^_pf(\d+)$/
68
+ const MERGEABLE = /^_(?:pf|sk)\d+$/
69
+ const TEMP = /^_t\d+_/
68
70
  const WRAP = 'function __parseman_supercompile__(){\n'
69
71
  const SUFFIX = '\n}'
70
72
 
@@ -225,6 +227,113 @@ export function supercompileEntryAssembly(
225
227
  return supercompileEmittedAssembly(prog, cfg, extraIps, emitted)
226
228
  }
227
229
 
230
+ /** A name in the source: a site, or a temporary (`tmp` in `emit-assembly.ts`). */
231
+ type NameUse = { readonly start: number; readonly end: number; readonly name: string }
232
+
233
+ /** The uses of site and temporary names under `root`, and the names bound there. */
234
+ function namesUnder(root: Ast, uses: NameUse[], bound: Set<string>): boolean {
235
+ let plain = true
236
+ const bind = (id: Ast | undefined): void => {
237
+ const name = nameOf(id)
238
+ if (name !== undefined) bound.add(name)
239
+ }
240
+ const walk = (node: Ast, parent: Ast | undefined): void => {
241
+ if (node.type === 'VariableDeclarator') bind(ast(node.id))
242
+ else if (node.type === 'LabeledStatement') bind(ast(node.label))
243
+ else if (isFunction(node)) {
244
+ if (node !== root) bind(ast(node.id))
245
+ nodes(node.params).forEach(bind)
246
+ }
247
+ const name = nameOf(node)
248
+ if (name !== undefined && (MERGEABLE.test(name) || TEMP.test(name))) {
249
+ const isKey = parent?.type === 'Property' && parent.key === node && parent.computed !== true
250
+ const isProperty = parent?.type === 'MemberExpression' && parent.property === node && parent.computed !== true
251
+ if (parent?.type === 'Property' && parent.shorthand === true) plain = false
252
+ if (!isKey && !isProperty) uses.push({ ...range(node), name })
253
+ }
254
+ for (const child of children(node)) walk(child, node)
255
+ }
256
+ walk(root, undefined)
257
+ return plain
258
+ }
259
+
260
+ /**
261
+ * Functions that read the same are one function: `optional(x)` written in 25 places
262
+ * is 25 rows and one body, and a trivia skip can be pooled under two labels that
263
+ * lower alike. This runs after the join, so a site with one caller has
264
+ * already gone into it, the selection is untouched and every call a parse makes is
265
+ * still made. Each pass can make the callers of a merged function read alike, so it
266
+ * repeats until nothing merges.
267
+ *
268
+ * Only identifiers are compared or renamed: a grammar's own text reaches this source
269
+ * as strings and regexes, and they count as written. A temporary is local by the
270
+ * emitter's contract and is numbered per assembly, so one this function binds is
271
+ * compared in order of use. A site name that's also bound somewhere, or abbreviated
272
+ * in `{_pf1}`, can't be renamed by name, so that source stays as it is.
273
+ */
274
+ export function mergeSameBodies(source: string): string {
275
+ const parsed = parseSync('parseman-merge-same-bodies.js', WRAP + source + SUFFIX)
276
+ if (parsed.errors.length > 0) return source
277
+ const statements = nodes(ast(nodes((parsed.program as unknown as Ast).body)[0]?.body)?.body)
278
+ type Body = NameUse & { readonly uses: readonly NameUse[]; readonly local: ReadonlySet<string> }
279
+ const bodies: Body[] = []
280
+ const others: NameUse[] = []
281
+ const declared = new Set<string>()
282
+ for (const statement of statements) {
283
+ const name = statement.type === 'FunctionDeclaration' ? nameOf(ast(statement.id)) : undefined
284
+ const uses: NameUse[] = []
285
+ const bound = new Set<string>()
286
+ if (!namesUnder(statement, uses, bound)) return source
287
+ for (const local of bound) declared.add(local)
288
+ if (name === undefined || !MERGEABLE.test(name)) others.push(...uses)
289
+ else bodies.push({ ...range(statement), name, uses: uses.sort((a, b) => a.start - b.start), local: bound })
290
+ }
291
+ for (const local of declared) if (MERGEABLE.test(local)) return source
292
+
293
+ const merged = new Map<string, string>()
294
+ const current = (name: string): string => {
295
+ let at = name
296
+ for (let next = merged.get(at); next !== undefined; next = merged.get(at)) at = next
297
+ return at
298
+ }
299
+ let live = bodies
300
+ for (let more = true; more;) {
301
+ more = false
302
+ const first = new Map<string, string>()
303
+ live = live.filter(body => {
304
+ const temps = new Map<string, number>()
305
+ const edits = body.uses.map((use): Edit => {
306
+ if (MERGEABLE.test(use.name)) {
307
+ const site = current(use.name)
308
+ return { ...use, text: site === body.name ? '#self' : site }
309
+ }
310
+ if (!body.local.has(use.name)) return { ...use, text: use.name }
311
+ const seen = temps.get(use.name) ?? temps.size
312
+ temps.set(use.name, seen)
313
+ return { ...use, text: `#t${seen}` }
314
+ })
315
+ const key = render(source, body.start, body.end, edits)
316
+ const kept = first.get(key)
317
+ if (kept === undefined) { first.set(key, body.name); return true }
318
+ merged.set(body.name, kept)
319
+ more = true
320
+ return false
321
+ })
322
+ }
323
+ if (merged.size === 0) return source
324
+
325
+ const edits: Edit[] = []
326
+ const rename = (use: NameUse): void => {
327
+ if (MERGEABLE.test(use.name) && merged.has(use.name)) edits.push({ ...use, text: current(use.name) })
328
+ }
329
+ others.forEach(rename)
330
+ for (const body of bodies) {
331
+ if (merged.has(body.name)) edits.push({ start: body.start, end: body.end, text: '' })
332
+ else body.uses.forEach(rename)
333
+ }
334
+ return render(source, 0, source.length, edits)
335
+ }
336
+
228
337
  /**
229
338
  * Plugin-only final splice for an ordinary macro replacement. The table
230
339
  * compiler emits its canonical callback-free public artifact first; this pass
@@ -274,7 +383,7 @@ export function supercompileRuleMapReplacement(
274
383
  const start = body.start - prefix.length + 1
275
384
  const end = body.end - prefix.length - 1
276
385
  if (replacement.slice(start, end) !== FACTORY_ARITY_GUARD + canonical.source + '\n') return replacement
277
- const transformed = replacement.slice(0, start) + FACTORY_ARITY_GUARD + candidate.source + '\n' + replacement.slice(end)
386
+ const transformed = replacement.slice(0, start) + FACTORY_ARITY_GUARD + mergeSameBodies(candidate.source) + '\n' + replacement.slice(end)
278
387
  const checked = parseSync('parseman-entry-supercompile-replacement-result.js', prefix + transformed)
279
388
  return checked.errors.length === 0 ? transformed : replacement
280
389
  }
@@ -83,6 +83,7 @@ import {
83
83
  cstCaptureActive, cstLeavesLen, cstTlLen,
84
84
  demoteCapturedToRaw, pushCstChild, pushCstLeaf,
85
85
  type CstCaptureBuf,
86
+ popCstChild,
86
87
  } from '../cst/capture-buffer.ts'
87
88
  import { PlainBuf, PlainField, PlainLeaf, PlainNode, PlainSpan } from '../cst/plain.js'
88
89
  import {
@@ -91,7 +92,7 @@ import {
91
92
  OP_LIT_TRACK, OP_RX_TRACK, OP_NODE_TRACK, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, SCOPE_INHERIT, OP_EXPECT, OP_SEQX, OP_SCAN,
92
93
  OP_LIVE,
93
94
  OP_FIELD, OP_DISPATCH, OP_ROUTED, OP_LIT_CI, OP_LIT_CI_TRACK, OP_TOKEN, OP_WITHCTX, OP_GUARD, OP_ATTEMPT, OP_LABEL,
94
- OP_COV, OP_GAPGATE, OP_STRICT,
95
+ OP_COV, OP_GAPGATE, OP_STRICT, OP_CONT, OP_SEED,
95
96
  OP_ADJ, OP_GREEDY, OP_REJECT, OP_ARMGATE, OP_RGATE, OP_RGATE2,
96
97
  OP_LEX_BODY, OP_LEX_PROGRAM,
97
98
  } from './ops.ts'
@@ -110,6 +111,7 @@ import {
110
111
  type EmittedAssembly, type EmittedFactory,
111
112
  } from './emit-assembly.ts'
112
113
  import { collapseEmpty, lead, rawEntry, spanLines } from './run-support.ts'
114
+ import { canDecide, continuationGate } from './late-frame.ts'
113
115
  import { holdsGap, rowNullable } from './hold-gap.ts'
114
116
  import { gapRows, newGapSlot } from './gap-gate.ts'
115
117
  import { failCleanAnalysis } from './fail-clean.ts'
@@ -133,7 +135,7 @@ import {
133
135
  type SubtreeRef, type TableProgram, type TableRule,
134
136
  } from './program.ts'
135
137
  import { stampRuleMap } from './stamp.ts'
136
- import { computeSiteLabels, gatedPassCollapse, passCollapse, reachableSites, scanHandoffNeeded, scopeIsIdentity, triviaLeadRanges } from './site-labels.ts'
138
+ import { CAP_OFF, CAP_ON, type SiteLabel, computeSiteLabels, gatedPassCollapse, passCollapse, reachableSites, scanHandoffNeeded, scopeIsIdentity, triviaLeadRanges, TRI_NONE } from './site-labels.ts'
137
139
  import { refuseUnclassifiedRootScope } from '../cst/root-trivia-scope.ts'
138
140
  import { captureError, firstSetSentinel, matchesAt, orSentinel, recoverScan } from '../recovery/scan.ts'
139
141
  import {
@@ -544,6 +546,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
544
546
  const GAP = newGapSlot()
545
547
  const closureLabels = computeSiteLabels(
546
548
  code, [...Object.values(prog.rules), ...labelExtraIps], hostCst,
549
+ cfg.tolerant || cfg.probe ? undefined : (ip, tri) => canDecide(code, ip, tri, t.triviaScan),
547
550
  )
548
551
 
549
552
  /**
@@ -1177,6 +1180,41 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
1177
1180
  return cur
1178
1181
  }
1179
1182
 
1183
+ /** Whether `cls` admits the character at `at`, without a call for the ASCII answer. */
1184
+ function startsTail(cls: ResolvedClass, input: string, at: number): boolean {
1185
+ const c = input.charCodeAt(at)
1186
+ return c < 128 ? cls.ascii[c] === 1 : classHas(cls, lead(input, at))
1187
+ }
1188
+
1189
+ /**
1190
+ * The tail of a late frame. The gap was scanned to decide, so with no gap there is
1191
+ * nothing to record or undo, and with nothing recording it is not scanned again.
1192
+ */
1193
+ function lateTail(tail: Piece, input: string, end: number, at: number, ctx: ParseContext, frame: SiteLabel): unknown {
1194
+ if (at === end) return tail(input, end, ctx)
1195
+ if (ctx._triviaLog === undefined && ctx._rootTriviaLog === undefined
1196
+ && (frame.cap === CAP_OFF || !((frame.cap === CAP_ON || ctx.captureTrivia === true)
1197
+ && (frame.buf || ctx._cstBuf !== undefined || ctx._cstTriviaLog !== undefined)))) {
1198
+ const v = tail(input, at, ctx)
1199
+ if (v !== FAIL && EC.e <= at) EC.e = end
1200
+ return v
1201
+ }
1202
+ const need = rollbackNeeded(ctx)
1203
+ const mTl = need ? cstTlLen(ctx) : 0
1204
+ const mLog = need ? ctx._triviaLog?.length ?? 0 : 0
1205
+ const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
1206
+ const scanEnd = skipTrivia(input, end, ctx)
1207
+ const scanTl = need ? cstTlLen(ctx) : 0
1208
+ const scanLog = need ? ctx._triviaLog?.length ?? 0 : 0
1209
+ const scanRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0
1210
+ const v = tail(input, scanEnd, ctx)
1211
+ if (v !== FAIL && EC.e <= scanEnd) {
1212
+ if (need) rollbackScannedTriviaAt(ctx, mTl, scanTl, mLog, scanLog, mRoot, scanRoot)
1213
+ EC.e = end
1214
+ }
1215
+ return v
1216
+ }
1217
+
1180
1218
  /** A non-first term at a site proven to run under an open node capture buffer. */
1181
1219
  function nextTermBuffered(child: Piece, input: string, cur: number, ctx: ParseContext): number {
1182
1220
  const t = ctx.trivia
@@ -1846,6 +1884,88 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
1846
1884
  case OP_EMPTY:
1847
1885
  return (_input, pos) => { EC.e = pos; return '' }
1848
1886
 
1887
+ case OP_SEED:
1888
+ return link(code[ip + 1]!)
1889
+
1890
+ case OP_CONT: {
1891
+ const wrapper = code[ip + 2]!
1892
+ if (!closureLabels.late.has(ip)) return lower(wrapper)
1893
+ const corner = link(code[ip + 1]!)
1894
+ const tail = link(code[ip + 3]!)
1895
+ const g = continuationGate(code, code[ip + 3]!)!
1896
+ const tri = closureLabels.at(ip).tri
1897
+ // The gap the tail would start past, read without recording anything: no scan
1898
+ // at a character no trivia can start.
1899
+ const scan = tri === TRI_NONE ? undefined : t.triviaScan[tri]!
1900
+ const leads = tri === TRI_NONE ? null : LEAD_TABLES[tri]!
1901
+ const cls = cc[g.cls]!
1902
+ const gapRe = g.gapRow < 0 ? undefined : k[code[g.gapRow + 1]!] as RegExp
1903
+ const follow = g.follow < 0 ? undefined : cc[g.follow]!
1904
+ const other = g.other < 0 ? undefined : cc[g.other]!
1905
+ const mayStartPastGap = (input: string, at: number): boolean => {
1906
+ gapRe!.lastIndex = at
1907
+ const q = gapRe!.test(input) ? gapRe!.lastIndex : -1
1908
+ return (q >= 0 && classHas(follow!, lead(input, q))) || (other !== undefined && classHas(other, lead(input, at)))
1909
+ }
1910
+ const frame = closureLabels.at(code[wrapper + 2]!)
1911
+ const flags = code[wrapper + 3]!
1912
+ const buildIdx = code[wrapper + 1]!
1913
+ // The seed for this frame, handed over before anything can parse.
1914
+ let held: unknown
1915
+ let heldRaw: unknown
1916
+ let heldEnd = 0
1917
+ let heldAt = 0
1918
+ const omitsRaw = !hostCst && buildIdx >= 0 && code[wrapper + 4]! < 0 && (flags & 2) !== 0
1919
+ const body: Piece = omitsRaw
1920
+ ? (input, _pos, ctx) => {
1921
+ const b = ctx._cstBuf
1922
+ if (b !== undefined) {
1923
+ b.single = held
1924
+ b.rawLen = 1
1925
+ } else ctx._cstChildren!.push(held)
1926
+ held = undefined
1927
+ heldRaw = undefined
1928
+ return lateTail(tail, input, heldEnd, heldAt, ctx, frame)
1929
+ }
1930
+ : (input, pos, ctx) => {
1931
+ const b = ctx._cstBuf
1932
+ if (b !== undefined) {
1933
+ b.single = held
1934
+ b.rawSingle = heldRaw ?? rawEntry(held, input, pos, heldEnd)
1935
+ } else ctx._cstChildren!.push(held)
1936
+ held = undefined
1937
+ heldRaw = undefined
1938
+ return lateTail(tail, input, heldEnd, heldAt, ctx, frame)
1939
+ }
1940
+ let open: Piece
1941
+ // An AST reducer: the frame is one of the two lean bodies. Any other frame is
1942
+ // the node's own closure, with `body` for its child.
1943
+ if (!hostCst && !cfg.trackLines && code[wrapper] === OP_NODE && code[wrapper + 4]! < 0 && (flags === 32 || flags === 34)) {
1944
+ const build = fns[buildIdx] as NodeBuilder
1945
+ open = flags === 34 ? childrenOnlyCollapseBuildNode(body, build) : collapseBuildNode(body, build)
1946
+ } else {
1947
+ NODE_BODY = body
1948
+ open = lower(wrapper)
1949
+ }
1950
+ return (input, pos, ctx) => {
1951
+ const v = corner(input, pos, ctx)
1952
+ if (v === FAIL) return FAIL
1953
+ const end = EC.e
1954
+ let at = end
1955
+ if (scan !== undefined) {
1956
+ const c = input.charCodeAt(end)
1957
+ if (leads === null || !(c < 128) || leads[c] !== 0) at = scan(input, end)
1958
+ }
1959
+ if (v !== undefined && !(gapRe === undefined ? startsTail(cls, input, at) : mayStartPastGap(input, at))) return v
1960
+ if (ctx._cstBuf === undefined && ctx._cstRawChildren === undefined && ctx._cstChildren !== undefined) ctx._cstChildren.pop()
1961
+ else if (ctx._cstBuf !== undefined || ctx._cstChildren !== undefined) heldRaw = popCstChild(ctx)
1962
+ held = v
1963
+ heldEnd = end
1964
+ heldAt = at
1965
+ return open(input, pos, ctx)
1966
+ }
1967
+ }
1968
+
1849
1969
  /* ── transparent / structural ────────────────────────────────────────── */
1850
1970
 
1851
1971
  case OP_GUARD: {
@@ -4290,7 +4410,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
4290
4410
  matchesAt, recoverScan, orSentinel, captureError,
4291
4411
  scalarRecognizers, t.lex, adjacencyHolds, t.lexPrograms,
4292
4412
  LEAD, triviaFieldCapture, skipDeferredTrivia,
4293
- collapseEmpty,
4413
+ collapseEmpty, popCstChild,
4294
4414
  PlainSpan, PlainLeaf, PlainNode, PlainField, PlainBuf, GAP,
4295
4415
  )
4296
4416
  emitReached = new Set(pre.reached)
@@ -4375,7 +4495,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
4375
4495
  matchesAt, recoverScan, orSentinel, captureError,
4376
4496
  scalarRecognizers, t.lex, adjacencyHolds, t.lexPrograms,
4377
4497
  LEAD, triviaFieldCapture, skipDeferredTrivia,
4378
- collapseEmpty,
4498
+ collapseEmpty, popCstChild,
4379
4499
  PlainSpan, PlainLeaf, PlainNode, PlainField, PlainBuf, GAP,
4380
4500
  )
4381
4501
  emitReached = em.reached
@@ -4,7 +4,7 @@ import {
4
4
  OP_LIT_CI_TRACK, OP_LIT_TRACK, OP_LIVE, OP_NODE, OP_NODE_TRACK, OP_NOT, OP_OPT,
5
5
  OP_PEEK, OP_REJECT, OP_REP, OP_REPV, OP_ROUTED, OP_RULE, OP_RX, OP_RX_TRACK, OP_SCAN,
6
6
  OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, OP_SEQ, OP_SEQV, OP_SEQX, OP_TOKEN, OP_WITHCTX, OP_XFORM,
7
- OP_LEX_BODY, OP_LEX_PROGRAM, OP_GAPGATE, OP_RGATE2, OP_STRICT,
7
+ OP_LEX_BODY, OP_LEX_PROGRAM, OP_GAPGATE, OP_RGATE2, OP_STRICT, OP_CONT, OP_SEED,
8
8
  } from './ops.ts'
9
9
 
10
10
  /**
@@ -73,6 +73,14 @@ export function childSlotOffsets(code: ArrayLike<number>, ip: number, out: numbe
73
73
  out.push(ip + 2)
74
74
  return true
75
75
 
76
+ // `CONT corner wrapper tail`: `tail` is reached through `wrapper`.
77
+ case OP_CONT:
78
+ out.push(ip + 1, ip + 2)
79
+ return true
80
+ case OP_SEED:
81
+ out.push(ip + 1)
82
+ return true
83
+
76
84
  // `STRICT strict tolerant` — both encodings are reachable; an assembly keeps one.
77
85
  case OP_STRICT:
78
86
  out.push(ip + 1, ip + 2)