import type { AgencyNode, AgencyProgram, SourceLocation, TypeAliasEntry } from "../types.js"; import type { FunctionDefinition, FunctionParameter } from "../types/function.js"; import type { AliasResolver } from "../types/function.js"; import type { TypeCheckError } from "../typeChecker/types.js"; import type { Tag } from "../types/tag.js"; import type { ImportedFunctionSignature } from "../compilationUnit.js"; import { ScopedTypeAliases } from "../compilationUnit.js"; /** * Lower a decorator tag on a def into a call with the def's body as the * block argument (docs/dev/language/decorators.md). * * @budgeted(cost: maxCost) * def research(task: string, maxCost: number): Result { BODY } * * becomes * * def research(task: string, maxCost: number): Result { * const __call_0 = { name: "research", args: { task: task, maxCost: maxCost }, id: , inToolCall: } * return budgeted(cost: maxCost, call: __call_0) { * * BODY * } * } * * The tag's own arguments come first, as written; the `call` record is * passed by name after them, so it never competes with a positional tag * argument for a slot. The record is built in its own statement, so it is * its own step: a resume re-runs the step that calls the decorator, and * rebuilding the record there would read parameters the decorator has * since rebound. The block takes one parameter, `__args`, the record a * decorator may call it with, and starts with a `rebindParameters` * statement that stores the def's parameters from it (lib/runtime/rebind.ts). * Stacked tags nest, outermost first: `@a @b def f` becomes * `const __call_0 = ...; return a(...) { rebind; const __call_1 = ...; return b(...) { rebind; BODY } }`. * * MUTATES IN PLACE, for the reason guardDesugar.ts gives: the compilation * unit built before the TypeChecker constructor runs already holds the def * objects, so a copying rewrite would leave it pointing at undecorated * bodies. The innermost block holds the def's own statement objects, so no * reference into the body goes stale either. Idempotent: the tags move from * `def.tags` and the top-level list onto `def.decorators`, and a second run * finds nothing above or on the def. * * The pass resolves nothing. `decoratorProblems` is the pure check that * says what is wrong with a program's decorators; prepareProgram reports * its answer. The desugar lowers the tag whatever the name resolves to. */ /** What the lowering and the checks need to know about a decorator: its * parameters (for the shape check), whether it is imported (so the call * carries the scope the resolver expects), and whether it is declared * `decorator def` at all. */ export type DecoratorSignature = { parameters: FunctionParameter[]; imported: boolean; decorator: boolean; }; export type DecoratorLookup = (name: string) => DecoratorSignature | undefined; /** The one lookup both callers use, built from the two tables every caller * already has. */ export declare function decoratorLookup(functionDefinitions: Record, importedFunctions: Record): DecoratorLookup; export type DecoratedDef = { def: FunctionDefinition; tags: Tag[]; }; /** Every top-level def with the decorator tags written above it, outermost * first, whether the tags are still standalone nodes or `collectTags` has * already attached them to `def.tags`. A def whose decorators were already * lowered has none above it and is left out. */ export declare function decoratorsAbove(nodes: AgencyNode[]): DecoratedDef[]; /** The body a decorated def gets: the outermost decorator's record and one * `return` of its call; each block starting with the rebind, then the next * decorator's record and call; and the def's original statements after * the rebind in the innermost block. Pure: the def is read, never written. */ export declare function loweredBody(def: FunctionDefinition, tags: Tag[], lookup: DecoratorLookup): AgencyNode[]; /** The one function that writes. Every decorator def gets its slots typed * first (`withDecoratorTypes`), then each decorated def gets its lowered * body. The order matters: a decorated decorator's own call record leaves * block-typed parameters out, so its block slot must be typed before the * record is built. */ export declare function desugarDecorators(program: AgencyProgram, lookup: DecoratorLookup): AgencyProgram; export type DecoratorProblem = { message: string; loc: SourceLocation | undefined; }; /** The tables a program's decorator names resolve against. */ export type DecoratorScope = { functionDefinitions: Record; importedFunctions: Record; /** The program's type aliases, so a record slot typed with an alias of * `Call` (`type Ctx = Call`) is accepted. Left out, only the name * `Call` is. */ typeAliases?: ScopedTypeAliases; }; /** An alias resolver over `aliases`, for the record-slot check. */ export declare function aliasResolver(aliases: Record): AliasResolver; /** The three mistakes the decorator stage and the checker both report, * by the checker's diagnostic name. A caller that ran the stage drops * the checker's copy of each (the editor, `agency typecheck`). */ export declare function repeatsDecoratorStage(name: string): boolean; /** * Everything wrong with a program's decorators, before or after the * lowering: * * - a decorator written on anything but a top-level def, * - a def that decorates itself, * - defs that decorate each other in a loop (`@b def a` and `@a def b`), * - a decorator imported from JavaScript, which cannot take a block, * - a decorator name that is neither declared nor imported, * - a tag naming a def that is not declared `decorator def`, * - a `decorator def` in the wrong shape (`decoratorShapeProblem`): a * local one at the def, an imported one at the tag, * - a def that is a decorator in all but the keyword * (`isDecoratorMissingKeyword`), at the def. * * The last three render through the diagnostic registry (`decoratorShape`, * `notADecorator`, `decoratorMissingKeyword`), so the checker's copies say * the same thing. Pure; prepareProgram and the CLI build turn the list * into errors. */ export declare function decoratorProblems(program: AgencyProgram, scope: DecoratorScope): DecoratorProblem[]; /** A def that is wrong as a decorator: a `decorator def` whose parameters * are not a decorator's, or a plain def that is a decorator in all but * the keyword. */ export type DecoratorDefProblem = { kind: "shape"; name: string; problem: string; loc: SourceLocation | undefined; } | { kind: "missingKeyword"; name: string; loc: SourceLocation | undefined; }; /** Every def among `nodes` that is wrong as a decorator, as data: the * decorator stage and the checker each render it with * `decoratorDefDiagnostic`. */ export declare function decoratorDefProblems(nodes: AgencyNode[], resolve?: AliasResolver): DecoratorDefProblem[]; /** The one rendering of a `DecoratorDefProblem`: AG6043 for the shape, * AG6045 for the missing keyword. */ export declare function decoratorDefDiagnostic(found: DecoratorDefProblem): TypeCheckError; /** What is wrong with a tag that resolved to `signature`, as data: the def * is not a decorator, or it is an imported decorator in the wrong shape. * A local decorator's shape is reported at the def by * `decoratorDefProblems`, so a tag on it is not reported again. */ export type TagProblem = { kind: "notADecorator"; } | { kind: "shape"; problem: string; }; export declare function tagProblem(signature: DecoratorSignature, resolve?: AliasResolver): TagProblem | null;