import { BindingPattern } from "../../types.js"; import { Code } from "./code.js"; /** Reserved identifier prefix for hygienic renames. ASCII on purpose: * renamed names get printed to source and re-parsed by a subprocess, so * they must stay legal identifiers (`lib/parsers/parsers.ts` varNameChar). * Collisions with renamed names are impossible by construction because * the fresh-name counter seeds ABOVE every `__hyg` already present in * the template and the fillers (see maxHygieneIndex) — which is also what * lets previously renamed output be filled again. */ export declare const RESERVED_PREFIX = "__hyg"; /** A rename that applies only within one scope of the template. The scope * key comes from the walk's scope chain (`fn:name` / `node:name` / * `global`), so renaming `tmp` in `def b` cannot touch `tmp` in `def a` * — a flat name→name map cannot express that distinction. */ export type ScopedRename = { scopeKey: string; from: string; to: string; }; export type RenamePlan = { template: ScopedRename[]; /** Per-graft flat maps, keyed by hole name (and element index for * splices, as `name[i]`). A filler is one fragment grafted into one * place, so its whole tree is the scope. */ fillers: Record>; }; /** Names a binding pattern introduces. Fed only from binding positions * (let/const, for-loop and comprehension binders), where the parser * produces binding patterns only. Match-position kinds that can share * these unions (literals, wildcards, resultPattern) bind nothing here — * note that skipping resultPattern is also why `is success(v)` binders * stay untracked (recorded known limit, see the templates guide). */ export declare function patternBinders(pattern: BindingPattern): string[]; /** All binders anywhere in a fragment, deduplicated. */ export declare function bindersOf(code: Code): string[]; /** Names a fragment USES but does not bind — the side of capture an * earlier plan draft got wrong: comparing binders to binders finds * nothing, because `tmp` used as an expression binds nothing. * * LOAD-BEARING DEPENDENCY: this is exactly as complete as `walkNodes`' * descent (lib/utils/node.ts). A node kind whose expression children the * walker misses under-reports free names here, no test fails, and a * filler silently captures a template binder — the precise bug hygiene * exists to prevent, failing open. (The guardBlock head-argument gap * fixed in this feature's PR is the historical example.) The tripwire * for this path is the walker-coverage corpus invariants in * lib/utils/expressionSlots.test.ts ("walker coverage" describe block): * they check, structurally and in both parse modes, that walkNodes * reaches every expression position — no hand enumeration to forget. * Gaps they have found but not yet fixed are listed there in * KNOWN_WALKER_GAPS; hygiene inherits each one until its walker-fix PR * lands. */ export declare function freeNamesOf(code: Code): string[]; /** * The highest `__hyg` index appearing anywhere in a fragment's names — * binders, uses, and declaration names alike. Fresh renames start ABOVE * the max across the template and every filler, which is what makes a * rename collision impossible by construction. This replaced an earlier * reject-any-`__hyg`-input rule: rejection cannot tell a renamer-produced * name from a caller-supplied one, so it broke the compose workflow — a * second fill rejected the first fill's own output. */ export declare function maxHygieneIndex(code: Code): number; /** * The three collision sets, each computed against the binders VISIBLE AT * THE HOLE — never `bindersOf(template)`, which is the whole file: * * 1. visible template binder ∩ filler free name → rename the TEMPLATE's * binder, within its own scope only (the filler meant something else * by that name). * 2. filler binder ∩ visible template binder → rename the FILLER's binder * (the template keeps its spelling; the filler owns the noise). * 3. the same name bound by more than one filler grafted into the same * scope → each filler gets its own fresh name. * * Renaming is selective — only colliding names change — so generated code * normally reads as written. */ export declare function computeRenames(template: Code, values: Record): RenamePlan; /** Flat rename over a whole fragment — for fillers, where the fragment IS * the scope. Rewrites binders and uses alike, so a filler that both * declares and uses a renamed name stays internally consistent. */ export declare function applyRenames(code: Code, renames: Record): Code; /** Scope-aware rename over the template: each rename applies only inside * its owning scope's subtree, and stops at an inner def/node that rebinds * the same name (the inner binding shadows it). The active-rename list is * threaded through the recursion so a deactivation holds for the whole * inner subtree, not just the node where it was decided. */ export declare function applyScopedRenames(code: Code, renames: ScopedRename[]): Code;