parseman 0.61.3 → 0.61.5

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 (45) 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 +846 -520
  8. package/dist/index.js.map +3 -3
  9. package/dist/plugin/index.d.ts +22 -3
  10. package/dist/plugin/index.d.ts.map +1 -1
  11. package/dist/plugin/index.js +872 -520
  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 +368 -42
  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.map +1 -1
  28. package/dist/table/site-labels.d.ts +5 -1
  29. package/dist/table/site-labels.d.ts.map +1 -1
  30. package/dist/version.d.ts +1 -1
  31. package/package.json +1 -1
  32. package/src/cst/capture-buffer.ts +20 -11
  33. package/src/plugin/index.ts +76 -23
  34. package/src/table/assemble.ts +124 -4
  35. package/src/table/child-slots.ts +9 -1
  36. package/src/table/emit-assembly.ts +101 -14
  37. package/src/table/encode.ts +69 -6
  38. package/src/table/exec.ts +7 -1
  39. package/src/table/fail-clean.ts +8 -1
  40. package/src/table/hold-gap.ts +5 -1
  41. package/src/table/late-frame.ts +54 -0
  42. package/src/table/ops.ts +19 -0
  43. package/src/table/select-hot.ts +7 -1
  44. package/src/table/site-labels.ts +38 -14
  45. 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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"}
@@ -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":"AAwBA;;;;;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"}
@@ -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.3";
16
+ export declare const PARSEMAN_VERSION = "0.61.5";
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.3",
3
+ "version": "0.61.5",
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 {
@@ -21,6 +21,7 @@
21
21
  import * as fs from 'node:fs'
22
22
  import * as path from 'node:path'
23
23
  import { createUnplugin } from 'unplugin'
24
+ import type { UnpluginBuildContext } from 'unplugin'
24
25
  import { parseSync } from 'oxc-parser'
25
26
  import { ResolverFactory } from 'oxc-resolver'
26
27
  import MagicString from 'magic-string'
@@ -183,13 +184,29 @@ export type ParsecraftPluginOptions = {
183
184
 
184
185
  /** A corpus a build parses to rank selective-assembly sites — see `assemblyProfile`. */
185
186
  export type AssemblyProfile = {
186
- /** The rule each corpus file is parsed from, e.g. `'Stylesheet'`. A grammar
187
- * without this rule keeps the static ranking. */
187
+ /** The rule each corpus file is parsed from, e.g. `'Stylesheet'`. A grammar without
188
+ * this rule keeps the static ranking. A build fails when grammars were profiled and
189
+ * none has the rule. A direct `transformMacro` caller gets `assemblyProfile:
190
+ * 'no-entry'` on the result instead, and has to read it. */
188
191
  readonly entry: string
189
192
  /** Paths of representative inputs. At least one must have content, and each that
190
193
  * does has to parse in full without recovery, or the build fails. A file with only
191
- * whitespace counts as empty. */
194
+ * whitespace counts as empty. A callback the corpus doesn't reach is never run, so
195
+ * nothing checks it. */
192
196
  readonly files: readonly string[]
197
+ /**
198
+ * The values of names a grammar's reducers and gates import, for the corpus run.
199
+ * Their source runs at build time with no module around it, so a grammar whose
200
+ * callbacks read imported helpers passes those helpers here. See
201
+ * `docs/guide/macro-mode.md` for what this does and doesn't cover.
202
+ *
203
+ * It's one flat namespace, so two modules can't bind one name to different helpers.
204
+ * A name that's already a global fails the build. A helper named like a global and
205
+ * left out of `scope` silently uses the global. A direct `node()` builder doesn't
206
+ * run in the corpus run: a plain node stands in for it, so a helper only a builder
207
+ * reads is neither needed here nor checked.
208
+ */
209
+ readonly scope?: Readonly<Record<string, unknown>>
193
210
  }
194
211
 
195
212
  /** Corpus file contents, re-read when a file's mtime moves, so a watch build that
@@ -558,6 +575,9 @@ export default createUnplugin((opts: ParsecraftPluginOptions = {}, meta) => {
558
575
  // Module id → its carried lists, under a bundler that writes them beside the chunk.
559
576
  // Any other bundler gets them inline, as before.
560
577
  const sinks = SIDECAR_FRAMEWORKS.has(meta.framework) ? new Map<string, CarriedSink>() : undefined
578
+ // Module id → whether its grammars ran the corpus. A module without the entry rule is
579
+ // fine on its own, because one plugin sees many modules, so only the build can tell.
580
+ const profiled = new Map<string, NonNullable<TransformMacroResult['assemblyProfile']>>()
561
581
  // `order: 'post'`: the sidecar records a digest of each chunk's code, so it is
562
582
  // taken after every other plugin's `generateBundle` has rewritten that code (Vite
563
583
  // rewrites a dynamic `import()` through its preload helper there).
@@ -579,10 +599,19 @@ export default createUnplugin((opts: ParsecraftPluginOptions = {}, meta) => {
579
599
  return /\.[jt]sx?$/.test(id) && !id.includes('node_modules')
580
600
  },
581
601
 
602
+ buildEnd(this: UnpluginBuildContext & { getModuleIds?: () => Iterable<string> }, error?: unknown) {
603
+ if (error) return
604
+ // esbuild, webpack and rspack can't list a build's modules, so there every module seen counts.
605
+ const ids = [...this.getModuleIds?.() ?? profiled.keys()].filter(id => profiled.has(id))
606
+ if (ids.length === 0 || ids.some(id => profiled.get(id) === 'ran')) return
607
+ throw new Error(`assemblyProfile: no grammar has rule "${opts.assemblyProfile?.entry}", so the corpus never ran (${ids.join(', ')})`)
608
+ },
609
+
582
610
  transform(this: { warn?: (msg: string) => void; addWatchFile?: (file: string) => void }, code: string, id: string) {
583
611
  // What this module recorded on an earlier build of a watch session is stale
584
612
  // from here: it records again below, or it no longer lowers anything.
585
613
  sinks?.delete(id)
614
+ profiled.delete(id)
586
615
  if (!code.includes('parseman')) return null
587
616
  if (!code.includes('macro')) return null
588
617
  // A corpus edit changes which sites a profiled build generates, so a watch
@@ -592,6 +621,7 @@ export default createUnplugin((opts: ParsecraftPluginOptions = {}, meta) => {
592
621
  const sink: CarriedSink | undefined = sinks && new Map()
593
622
  const result = transformMacro(code, id, moduleAliases, opts.warnUnloweredRegex === true, opts.recovery === true, opts.grammarCoverage === true, opts.assemblyProfile, opts.assemblyBudget, sink)
594
623
  if (sinks && sink && sink.size > 0) sinks.set(id, sink)
624
+ if (result?.assemblyProfile !== undefined) profiled.set(id, result.assemblyProfile)
595
625
  if (result?.warnings.length) {
596
626
  for (const w of result.warnings) {
597
627
  if (typeof this?.warn === 'function') this.warn(`[parseman] ${w}`)
@@ -786,6 +816,9 @@ export type TransformMacroResult = {
786
816
  map: ReturnType<MagicString['generateMap']>
787
817
  /** Diagnostics for macro-referencing shapes that fell back to the interpreter. */
788
818
  warnings: string[]
819
+ /** With an `assemblyProfile`: `'ran'` when a grammar here parsed the corpus,
820
+ * `'no-entry'` when the grammars that could have don't have its `entry` rule. */
821
+ assemblyProfile?: 'ran' | 'no-entry'
789
822
  /** The sites the module's `assemblyBudget` bought, by program offset, in rank order. */
790
823
  assemblySites: number[]
791
824
  }
@@ -2403,10 +2436,15 @@ function transformMacroImpl(
2403
2436
  /** Measured site calls over the optional corpus, or `undefined` for the static
2404
2437
  * ranking. A corpus that cannot run fails the build: a static ranking standing in
2405
2438
  * for the measured one configured is a slower parser that nothing reports. */
2439
+ let profileOutcome: TransformMacroResult['assemblyProfile']
2406
2440
  const profileOf = (compiled: CompiledRuleMapTable): Int32Array | undefined => {
2407
2441
  // Only the default AST/no-lines table carries an assembly, so only it is profiled.
2408
- if (assemblyProfile === undefined || compiled.hostMode !== 'ast' || compiled.prog.lines === 1
2409
- || !(assemblyProfile.entry in compiled.prog.rules)) return undefined
2442
+ if (assemblyProfile === undefined || compiled.hostMode !== 'ast' || compiled.prog.lines === 1) return undefined
2443
+ if (!(assemblyProfile.entry in compiled.prog.rules)) {
2444
+ profileOutcome ??= 'no-entry'
2445
+ return undefined
2446
+ }
2447
+ profileOutcome = 'ran'
2410
2448
  const refuse = (why: string): never => { throw new Error(`${id} — assemblyProfile: ${why}`) }
2411
2449
  if (corpusTexts(assemblyProfile).every(text => text.trim() === '')) refuse('`files` has no non-empty file')
2412
2450
  let thrower: unknown
@@ -2421,24 +2459,38 @@ function transformMacroImpl(
2421
2459
  },
2422
2460
  })
2423
2461
  })
2424
- return countSiteVisits(compiled.prog, rules => {
2425
- const entry = rules[assemblyProfile.entry]!
2426
- corpusTexts(assemblyProfile).forEach((text, i) => {
2427
- const file = assemblyProfile.files[i]!
2428
- if (text.trim() === '') return
2429
- let result
2430
- try { result = run(entry, text) } catch (e) {
2431
- const source = String(thrower).replace(/\s+/g, ' ').slice(0, 200)
2432
- throw new Error(`${id} — assemblyProfile: ${thrower === undefined ? 'the parse' : `gate or reducer \`${source}\``} threw on ${file}: ${String(e)}`, { cause: e })
2433
- }
2434
- if (!result.ok || result.unconsumedFrom !== null) {
2435
- refuse(`${file} does not parse from rule "${assemblyProfile.entry}": it stops at offset ${result.ok ? result.unconsumedFrom : result.span.end}`)
2436
- }
2437
- if (result.errors.length > 0) {
2438
- refuse(`${file} parses only by recovering from ${result.errors.length} error(s), first at offset ${result.errors[0]!.span.start}`)
2439
- }
2440
- })
2441
- }, fns)
2462
+ // ponytail: callback source is evaluated in global scope, so the corpus run sees
2463
+ // `scope` as globals for this one synchronous call, and nothing can interleave with
2464
+ // it. Async work a callback starts that outlives the call no longer sees them;
2465
+ // rebind each callback instead if a helper ever needs that.
2466
+ const scope = assemblyProfile.scope ?? {}
2467
+ const names = Object.keys(scope)
2468
+ for (const name of names) {
2469
+ if (name in globalThis) refuse(`\`scope\` name "${name}" is already a global; a callback couldn't tell which one it reads`)
2470
+ }
2471
+ try {
2472
+ for (const name of names) Object.defineProperty(globalThis, name, { value: scope[name], configurable: true })
2473
+ return countSiteVisits(compiled.prog, rules => {
2474
+ const entry = rules[assemblyProfile.entry]!
2475
+ corpusTexts(assemblyProfile).forEach((text, i) => {
2476
+ const file = assemblyProfile.files[i]!
2477
+ if (text.trim() === '') return
2478
+ let result
2479
+ try { result = run(entry, text) } catch (e) {
2480
+ const source = String(thrower).replace(/\s+/g, ' ').slice(0, 200)
2481
+ throw new Error(`${id} — assemblyProfile: ${thrower === undefined ? 'the parse' : `gate or reducer \`${source}\``} threw on ${file}: ${String(e)}`, { cause: e })
2482
+ }
2483
+ if (!result.ok || result.unconsumedFrom !== null) {
2484
+ refuse(`${file} does not parse from rule "${assemblyProfile.entry}": it stops at offset ${result.ok ? result.unconsumedFrom : result.span.end}`)
2485
+ }
2486
+ if (result.errors.length > 0) {
2487
+ refuse(`${file} parses only by recovering from ${result.errors.length} error(s), first at offset ${result.errors[0]!.span.start}`)
2488
+ }
2489
+ })
2490
+ }, fns)
2491
+ } finally {
2492
+ for (const name of names) Reflect.deleteProperty(globalThis, name)
2493
+ }
2442
2494
  }
2443
2495
  /** The fused table's replacement, with its selective default assembly if any. */
2444
2496
  const assembledReplacement = (compiled: CompiledRuleMapTable, metadata: string, select: ReadonlySet<number>): string =>
@@ -3321,5 +3373,6 @@ function transformMacroImpl(
3321
3373
  code: ms.toString(),
3322
3374
  map: ms.generateMap({ hires: true }),
3323
3375
  warnings,
3376
+ ...(profileOutcome === undefined ? {} : { assemblyProfile: profileOutcome }),
3324
3377
  }
3325
3378
  }
@@ -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)