import { type HostMode } from '../cst/host-mode.ts'; import { type LinkableTable } from './compile-linkable-table.ts'; import type { BuildHost, Combinator, CstCollapsePredicate, ParseContext, ParseResult } from '../types.ts'; import type { Runnable } from '../functional/run.ts'; export declare function linkable(rulesMap: Record>, ns?: string, trivia?: Combinator, hostMode?: HostMode): LinkableTable; export type CstBuildHostOptions = { /** * Collapse transparent one-child CST wrapper nodes at build time. * - `true`: collapse any one-child node whose rawChildren also has exactly one * entry, so trivia/error boundaries are not silently dropped. * - `string[]`: collapse only these grammar node types. * - predicate: final policy hook for language-specific public CSTs. */ collapse?: boolean | readonly string[] | CstCollapsePredicate; /** * Materialize `node(..., { tags })` grammar metadata onto produced CST nodes. * When omitted, tags stay in grammar reflection for zero per-node tree cost. */ tags?: boolean; }; /** * A generic positioned-CST build host (RULE_ABI_PLAN §7). Pass as `ctx.build` * (or `parseDoc(..., { build: cstBuildHost })`) to make ANY linkable/fused * grammar produce a uniform CST — `{ _tag:'node', type, span, state, children }` * — instead of its own eval-AST builders. This is the host the linter and IDE * drivers use; the eval driver leaves `ctx.build` unset (grammar's own builders). * * For public syntax trees, call `cstBuildHost({ collapse })`: Parseman will skip * allocating wrapper CST nodes whose single child should stand in for the rule. */ export declare function cstBuildHost(options?: CstBuildHostOptions): BuildHost; export declare function cstBuildHost(type: string, children: ReadonlyArray, fields: unknown, span: { start: number; end: number; }, rawChildren?: ReadonlyArray, triviaLog?: readonly number[], state?: unknown, tags?: readonly string[] | undefined): unknown; /** * A fused function receives the full ParseContext through `run()`. Direct * callers historically supplied a plain context object, so keep that usage * valid while making the function assignable to the public `Runnable` type. * Generated code treats optional framework fields as absent when they are not * provided, matching the interpreter's normal defaults. */ export type FusedRule = (input: string, pos: number, ctx: ParseContext | Record) => ParseResult & { readonly value?: unknown; }; export { FUSED_HOST_MODE, FUSED_HOST_ELIDED } from '../cst/host-mode.ts'; /** The host mode a fused/composed rule map was built for. Defaults to 'ast'. */ export declare function fusedHostModeOf(registry: object): HostMode; /** Whether a fused/composed rule map dropped any direct builder's CST branch. */ export declare function fusedHostElidedOf(registry: object): boolean; /** * Compose grammars/artifacts into a runnable parser map — the ONLY public * composition entry point. `compose([base, ext, …])`: later entries override * earlier ones by rule name, and because fusion re-binds every reference in one * shared scope, an override reroutes the base's OWN calls too (open recursion). * * Each entry may be a **grammar** (a `rules()` result — a map of combinators, * linkable-ified here) OR an already-compiled **linkable artifact** (what the * macro emits and a package ships). So a package needs no opt-in wrapper to be * composable — `compose([importedGrammar, myRules])` just works. * * The macro compiles `compose([...])` to STATIC fused source (no `new Function`). * Called at runtime (no macro, like `compile()`) it fuses via `new Function`. */ /** A composed parser carries its flattened source pieces (non-enumerable) so it * can be composed AGAIN — `compose([lessGrammar, delta])` where `lessGrammar` is * itself a `compose([...])` result. */ export declare const COMPOSED_PIECES: unique symbol; /** The carried pieces a `compose()`/`composeLeaf()` result holds, or `undefined` when * the value is not a composed grammar. This is what makes a fused grammar analysable: * the pieces are re-lowerable IR even though the fused map itself is only functions. */ export declare function composedPiecesOf(grammar: Record): ReadonlyArray | undefined; /** The compact IR form a grammar carries instead of its lowered rule source: the * combinator-construction expression, re-lowered here at fuse time. */ export type IRPiece = { ns: string; ir: string; trackLines?: true; }; /** Memoize a zero-arg thunk, keeping it LAZY. Used where two diagnostic thunks want * the same carried-IR hydration: the work must not happen when the diagnostic is off, * and must not happen twice when it is on. */ export declare function once(fn: () => T): () => T; /** The re-lowerable carried pieces' rule maps, in compose order — the input to the * gating analysis (`diagnoseGrammar`). An opaque precompiled artifact contributes no * combinator graph, so it is skipped: a hole it would have bound stays unresolved * and its choice stays deferred, never falsely warned. * * Skipping is not the same as having nothing to say. Use `carriedRuleMapsDetailed` * where the skip must be REPORTED — a diagnostic that drops part of the grammar and * then returns a clean result is indistinguishable from one that verified it. */ export declare function carriedRuleMaps(carried: ReadonlyArray): Array]>>; /** `carriedRuleMaps` plus the pieces it could NOT re-lower, named by namespace and * rule count, so a caller can report exactly how much of the grammar went unseen. */ export declare function carriedRuleMapsDetailed(carried: ReadonlyArray): { maps: Array]>>; opaque: Array<{ ns: string; ruleNames: string[]; }>; }; /** * Recover the override-winner COMBINATOR map behind a `compose()` result, plus the * pieces that could not be recovered. * * A fused map holds rule functions, so any consumer that walks a combinator graph * (gating analysis, the spec/EBNF/railroad model) cannot read it directly. The graph * is not lost, though — `compose()` retains re-lowerable IR — so this is the single * shared recovery both consumers use. Sharing it is the point: two copies of this * logic is how one walker gets fixed and the other silently keeps failing. * * Returns `undefined` when `grammar` is not a composed result. */ export declare function recoverComposedRules(grammar: Record): { rules: Map>; opaque: Array<{ ns: string; ruleNames: string[]; }>; } | undefined; /** Return the final override-winner combinator map carried by runtime * `compose()`, or `undefined` when a precompiled opaque artifact participated. * This is intentionally internal: callers must not treat it as a parser API. */ export declare function composedCoverageRules(grammar: Record): Record> | undefined; export declare function compose(items: Array>, /** * Compile-time host mode for the fused artifact, same meaning as * `compile(g, { hostMode })`. Omit (or `'ast'`) for the eval driver — the fused rules * build through the grammar's own `build` callbacks and carry no positioned-CST * branch. Pass `'cst'` to fuse a SECOND artifact from the same pieces for the linter / * IDE / language-service driver. Two compilations of one grammar, decided here, rather * than one artifact deciding per node on every parse. */ opts?: { hostMode?: HostMode; }): Record; /** * Compose a terminal grammar. This is for a leaf parser that overlays local * semantic reductions on reusable recognition rules. * * Under the macro this lowers to STATIC fused source (functions), exactly like * `compose()`. It is still macro-only as a *compiled* artifact: without macro * lowering there is no safe way to keep lexical builders out of carried IR, so it * never falls back to runtime CODEGEN composition. * * Called at runtime (no macro) it returns the INTERPRETED fuse of the same items — * a combinator map, not a map of compiled functions (`fuseInterpreted`), fused lazily * per rule name. * * THE RETURN TYPE IS `Runnable`, NOT `FusedRule`, BECAUSE BOTH PATHS ARE REAL. A macro * build yields fused functions; an un-macro'd call yields combinators. `Runnable` is * already the library's name for "either of those" — it is what `run()` and * `parseDoc()` take — so the declared type is TRUE on both paths and a caller needs no * narrowing to use the result. This used to declare `Record` and * launder the runtime path through an `as unknown as`, which let a caller hold a * combinator map while the type promised compiled functions. * * Do NOT "fix" this by deleting the runtime path. It is load-bearing: the `bench/jess` * harness family and two differential-gate legs (`emit-identity-one`, * `scan-shape-oracle-one`) import un-macro'd grammar modules and depend on this lazy * interpreted fuse, one dialect per process. Whether an un-macro'd `composeLeaf()` * should exist at all is a separate, open owner question — but while the gates depend * on it, it exists, and the type says so. */ export declare function composeLeaf(items: Array>): Record; /** Whether `map` is an interpreted fuse (a combinator map) rather than a compiled * `compose()` result (a map of fused functions). INTERNAL — not re-exported from * `src/index.ts`. A consumer never has to ask this question: what it holds is * whatever the macro built. Diagnostics that fuse interpreted on purpose do. */ export declare function isInterpretedFuse(map: object): boolean; /** * Materialize a composition as a RUNNABLE INTERPRETED rule map — the interpreted * counterpart of `compose()`, with identical fuse semantics (later piece wins, * override reroutes the base's own calls, composing trivia governs every rule). * No codegen, no `new Function`, no macro build step: the result is a plain map of * combinators that `run()` / `parseDoc()` accept exactly like a fused map. * * This is what diagnostics and profiling run against — they must stay in * interpreted mode, and before this they could not see a composed grammar at all. * * Items are the SAME items `compose()`/`composeLeaf()` take: `rules()` maps * (the intended input), a prior `fuseInterpreted()` result, or a runtime * `compose()` result (re-lowered from its carried IR — note that carried IR cannot * materialize direct `node()` builders, so prefer the source maps). A precompiled * `linkable()` artifact is rejected: it has no combinator graph. * * MUTATION: binding a cross-piece hole rewrites the shared placeholder object every * call site already holds — that IS how an override reaches a base piece's own * calls. A second, DIFFERENT fusion over the same piece objects therefore throws * rather than silently rewriting the first one's parser. */ export declare function fuseInterpreted(items: Array>, opts?: { hostMode?: HostMode; }): Record>; //# sourceMappingURL=linker.d.ts.map