/** * U4 — THE ASSEMBLY, AS EMITTED SOURCE. * * `assemble.ts` lowers each reachable site to a closure. This file lowers the * same sites to TEXT, compiled once per (grammar × option set) at run start. * Semantics are `assemble.ts`'s, site for site; the identity sweep * (`bench/table-lowering-identity.ts`) gates the two against each other and * against the interpreter, and `expected` is inside the digest it compares. * * ## Why text, and not a rearrangement of `assemble.ts` * * Emission makes each fixed grammar edge a direct generated identifier and is * also the form a build can serialise as an ordinary function literal for CSP. * The closure assembler can bind the same topology directly through scalar * captures; emission's distinct job is to print that binding, not to assert a * V8 property that the semantic design depends on. * * ## What may be shared, and what must be emitted * * A body containing a call to another PIECE must be emitted per site, or the * megamorphism relocates from `assemble.ts` into a shared helper and nothing * changes. That is why `nextTerm` is INLINED at every sequence term here rather * than called. * * The rule this file applies is narrower than "no helpers", and it is the whole * criterion: **a shared emitted-scope helper is sound exactly when it takes no * piece as an argument**, because then it has no call site whose feedback a * second caller could pollute. `_skipTrivia` qualifies — it calls the installed * trivia scanner, which is runtime state in either engine. `nextTerm` does not. * * ## Cycles * * `assemble.ts` needs one forwarding stub per recursive site, * `const fwd = (input, pos, ctx) => target!(input, pos, ctx)` — a SINGLE * function literal that every back-edge in the process funnels through. * Emitted `function` declarations hoist, so a back-edge here is a direct name * reference and the stub does not exist. * * ## Refusal * * Any construct not lowered raises `Unemittable`, naming it. `assemble.ts` * catches that, RECORDS it on the assembly, and falls back to the closure path. * The fallback is observable (`Assembly.emitRefusal`): a silent one would make * a permanently slow path indistinguishable from a fast one, which is exactly * what `encode.ts:1208-1213` refuses to allow for `OP_LIVE`. */ import type { ParseContext } from '../types.ts'; import { type ResolvedClass, type ResolvedTable, type TableProgram } from './program.ts'; /** What the compiled factory hands back — the emitted twin of `Assembly`. */ export type EmittedPiece = (input: string, pos: number, ctx: ParseContext) => unknown; export type EmittedAssembly = { readonly pieces: Record; /** Sites named for `subtreeComb` — the scan pool and the scan-skip sets. */ readonly byIp: Record; readonly end: () => number; readonly begin: (ctx: ParseContext) => void; readonly finish: () => void; }; /** The `new Function` result. Its parameters are `EMITTED_PARAMS`, in order. */ export type EmittedFactory = (...args: readonly unknown[]) => EmittedAssembly; /** A construct the emitter does not lower. Names the CONSTRUCT, never a type. */ export declare class Unemittable extends Error { readonly construct: string; constructor(construct: string); } /** * The names the emitted scope is closed over, in the order the factory takes * them. ONE list, so the parameter list and the argument list cannot drift — a * mismatch binds the wrong function to the wrong name and yields a parser that * runs and is wrong, which no type in this file would catch. */ export declare const EMITTED_PARAMS: readonly ['EC', 'FAIL', 'K', 'FX', 'FNS', 'MASK', 'CLS', 'AFX', 'TRIVIA', 'TRIVIALABELS', 'TRIVIASCAN', 'SCANS', 'DISP', 'DSP', 'EMPTY_FX', 'EMPTY_CH', 'EMPTY_TLOG', 'EMPTY_TL', 'cstCaptureActive', 'pushCstLeaf', 'pushCstChild', 'rollbackTriviaAt', 'rollbackScannedTriviaAt', 'failAt', 'classHas', 'consumeTrivia', 'buildFieldMap', 'projectChild', 'unwrapChild', 'demoteCapturedToRaw', 'cstLeavesLen', 'skipTriviaScanned', 'needsDeferredTriviaCommit', 'scanTrivia', 'advanceTrivia', 'refuseUnclassifiedRootScope', 'spanLines', 'rawEntry', 'lead', 'asciiFoldKey', 'ROUTED_FX', 'SENTS', 'matchesAt', 'recoverScan', 'orSentinel', 'captureError', 'RECOG', 'commitTriviaScan', 'scanTriviaCompact', 'LEX', 'adjacencyHolds', 'LEXPROG']; /** * THE THREE POOLS, SAID AS INDICES INTO THE PROGRAM. * * The pools themselves are `Uint32Array`s, `{ascii, hi}` class objects and * string arrays — printable, but at 129 words per mask and 128 bytes per class * they would dwarf the table they belong to. Every entry is already IN the * program: a class is `cc[i]`, an arm's expected set is `fx[i]`, and a mask is a * pure function of its class row. So the plan is three arrays of small integers, * and `rebuildPools` turns it back into the pools with allocation only — no * string building, and in particular no `Function` constructor. * * This is what lets the macro pre-compile an assembly: the FACTORY is printed as * a real function literal, and its data arguments are rebuilt from this. */ export type PoolPlan = { /** Per `CLS` row: the `cc` index of each entry, `-1` for a null (ungated) arm. */ readonly classes: readonly (readonly number[])[]; /** Per `AFX` row: the `fx` index of each arm's expected set. */ readonly armExpected: readonly (readonly number[])[]; /** * Per `MASK` row: a non-negative legacy `CLS` row index, or `~dispIndex` for * a directly bound choice. The negative form names the EXISTING resolved * dispatch row rather than serialising a second copy of its arm classes. */ readonly masks: readonly number[]; }; /** Everything the compiled factory needs bound, beside the emitted text. */ export type EmitResult = { readonly source: string; /** Site offsets the emitter reached — the emitted twin of `Assembly.reached`. */ readonly reached: ReadonlySet; /** Hoisted per-choice candidate masks, in `MASK` order. */ readonly masks: readonly Uint32Array[]; /** Hoisted per-arm class gates, in `CLS` order. */ readonly classes: readonly (ResolvedClass | null)[][]; /** Hoisted per-arm expected sets, in `AFX` order. */ readonly armExpected: readonly (readonly string[])[][]; /** The same three pools as indices, for a build-time emitter. */ readonly plan: PoolPlan; }; /** * Rebuild the three pools a pre-compiled factory takes, from the resolved table * and the plan the emitter printed. Allocation only. */ export declare function rebuildPools(cc: readonly ResolvedClass[], fx: readonly (readonly string[])[], disp: ResolvedTable['disp'], plan: PoolPlan): { masks: Uint32Array[]; classes: (ResolvedClass | null)[][]; armExpected: (readonly string[])[][]; }; /** * Emit the whole assembly for one resolved table and one option set. * * Throws `Unemittable` for any construct not lowered. It does NOT compile the * text — `assemble.ts` does — so a refusal and a compile failure stay two * distinguishable outcomes at the call site. * * `staticBuild` is true only when `emit.ts` embeds the returned source as an * ordinary factory literal in a macro artifact. It permits macro-only code * shaping without perturbing the runtime `compile()` emitter, whose generated * parser must remain byte-identical when the optimization cannot affect it. */ export declare function emitAssemblySource(t: ResolvedTable, prog: TableProgram, cfg: { hostCst: boolean; hostReadsChildren?: boolean; hostCaptureTrivia?: ((type: string) => boolean) | undefined; trackLines: boolean; tolerant: boolean; coverage: boolean; probe: boolean; }, extraIps?: readonly number[], staticBuild?: boolean): EmitResult; //# sourceMappingURL=emit-assembly.d.ts.map