import { AgencyComment, AgencyMultiLineComment, AgencyNode, AgencyProgram, Assignment, Comprehension, DebuggerStatement, Hole, InterruptStatement, Literal, NewLine, ParallelBlock, Scope, SeqBlock, TypeAlias, VariableType } from "../types.js"; import { LineComment } from "../types/base.js"; import { ListTrivia, TriviaNode } from "../types/dataStructures.js"; /** One rendered list item. `leadingLines` are whole lines that print above * it (an object-type property's `@validate` tags); the shared renderer * indents them, so callers hand back plain text. */ type RenderedListItem = { leadingLines?: string[]; code: string; }; type RenderListWithTriviaOptions = { items: T[]; trivia: ListTrivia[] | undefined; open: string; close: string; renderItem: (item: T, index: number) => RenderedListItem; separator: (index: number, itemCount: number) => string; }; import { AccessChainElement, ValueAccess } from "../types/access.js"; import { CodeLiteral } from "../types/codeLiteral.js"; import { BlockArgument } from "../types/blockArgument.js"; import { AgencyArray, AgencyObject } from "../types/dataStructures.js"; import { FunctionCall, FunctionDefinition, FunctionParameter } from "../types/function.js"; import { GraphNodeDefinition } from "../types/graphNode.js"; import { IfElse } from "../types/ifElse.js"; import { ImportNameType, ImportNodeStatement, ImportStatement } from "../types/importStatement.js"; import { ExportFromStatement } from "../types/exportFromStatement.js"; import { EffectDeclaration } from "../types/effectDeclaration.js"; import { MatchBlock } from "../types/matchBlock.js"; import { ReturnStatement } from "../types/returnStatement.js"; import { GotoStatement } from "../types/gotoStatement.js"; import { ForLoop } from "../types/forLoop.js"; import { WhileLoop } from "../types/whileLoop.js"; import { AgencyConfig } from "../config/config.js"; import { MessageThread } from "../types/messageThread.js"; import { Skill } from "../types/skill.js"; import { BinOpArgument, BinOpExpression, Operator } from "../types/binop.js"; import { Keyword } from "../types/keyword.js"; import { HandleBlock } from "../types/handleBlock.js"; import { FinalizeBlock } from "../types/finalizeBlock.js"; import { GuardBlock } from "../types/guardBlock.js"; import { Tag } from "../types/tag.js"; import { NewExpression } from "../types/newExpression.js"; import { BindingPattern, MatchPattern } from "../types/pattern.js"; type TypeRenderPolicy = "source" | "display"; export declare function escapeStringText(s: string, delim: '"' | "'" | "`"): string; export declare class AgencyGenerator { protected graphNodes: GraphNodeDefinition[]; protected generatedStatements: string[]; protected generatedTypeAliases: string[]; protected typeAliases: Record; protected functionsUsed: Set; protected importNodes: ImportStatement[]; protected importedNodes: ImportNodeStatement[]; /** Comments / blank lines at the very top of the file that must stay * above the (sorted) imports block — e.g. file-level `// @tc-nocheck`, * shebangs, module docstrings. Populated by `partitionImports()`. */ protected importHeaderNodes: AgencyNode[]; /** Comments attached to each import statement (no blank line between * them and the import). Travel with the import when imports get * sorted, so `// @tc-ignore` above an import keeps suppressing it. */ protected importAttachedComments: Map; protected functionDefinitions: Record; protected currentScope: Scope[]; protected agencyConfig: AgencyConfig; private indentLevel; private indentSize; private debug; /** * When true, imports render in-place (in their original source position) * rather than being hoisted to the top of the file and sorted. Used by * `agency literate weave`, which needs to render the file in source * order so the prose / code alternation matches what the author wrote. */ protected preserveOrder: boolean; constructor(args?: { config?: AgencyConfig; preserveOrder?: boolean; debug?: boolean; }); protected trace(methodName: string, result: string): string; configDefaults(): Partial; generate(program: AgencyProgram): { output: string; }; /** * Split the node stream into three pieces: * * 1. `importHeaderNodes` — contiguous comments/blank lines at the * very top of the file (before any code or any import-attached * comment). These render unchanged above the sorted imports block. * 2. Imports + comments attached to each one — tracked in * `importAttachedComments`. A comment is "attached" iff it sits * directly above its import with no blank line between them. This * keeps `// @tc-ignore` above an import even when the import is * moved by sorting. * 3. Everything else — returned as the new node stream for the * remaining passes to process normally. * * Imports themselves are kept in `program.nodes` (returned here) so the * existing collection pass for `importNodes` / `importedNodes` still * sees them. The render path skips them because `processNode` for an * import returns "". */ private partitionImports; /** * Render the top-of-file header (comments + blank lines that sit above * the imports block). Returns an empty string when there are none. */ private renderImportHeader; addIfNonEmpty(str: string, lines: string[]): void; protected preprocessAST(): void; protected generateBuiltins(): string; protected generateImports(): string; protected preprocess(): string; protected postprocess(): string; protected collectFunctionSignature(node: FunctionDefinition): void; protected processGraphNodeName(node: GraphNodeDefinition): void; processNode(node: AgencyNode): string; private processNodeInner; protected formatHole(node: Hole): string; protected processInterruptStatement(node: InterruptStatement): string; protected needsParensLeft(child: BinOpArgument, parentOp: Operator): boolean; protected needsParensRight(child: BinOpArgument, parentOp: Operator): boolean; protected startScope(scope: Scope): void; protected endScope(): void; protected getCurrentScope(): Scope; protected isAgencyFunction(functionName: string, context: "valueAccess" | "functionArg" | "topLevelStatement"): boolean; private indent; private increaseIndent; private decreaseIndent; private indentStr; /** `renderItems` is a callback, not an array, because a multi-line item has * to be built at the depth it will actually print at. Rewriting an * already-rendered item's lines instead would reach inside triple-quoted * strings, whose newlines are data rather than layout, and silently change * the program. So the items are rendered once to measure, and rendered * again after the indent increases if the list turns out to wrap. */ private wrapList; private renderParams; private renderTypeSource; private renderTypeDisplay; private objectTypeHasPropertyMetadata; private renderType; private stringifyProp; /** One trivia node as its own source line. */ protected renderTriviaNode(node: TriviaNode): string; /** Comments that print on their own line ABOVE item `anchorIndex`. */ protected beforeTriviaAt(trivia: ListTrivia[] | undefined, anchorIndex: number): TriviaNode[]; /** Comments that print at the END of item `anchorIndex`'s line. */ protected trailingTriviaAt(trivia: ListTrivia[] | undefined, anchorIndex: number): LineComment[]; /** Render one multiline list, owning indentation and both comment * placements. Callers supply how to render an item and where separators * go; they must not place comments themselves. */ protected renderListWithTrivia(args: RenderListWithTriviaOptions): string; protected aliasedTypeToString(aliasedType: VariableType, policy: TypeRenderPolicy): string; private renderObjectTypeSource; protected processEffectDeclaration(node: EffectDeclaration): string; protected processTypeAlias(node: TypeAlias): string; private composeTypeAliasAssignment; /** * Format the `` chunk of a generic alias declaration. * Returns an empty string when there are no type params. */ private formatTypeParams; /** * Format the `(low: number, high: number = 10)` chunk of a * value-parameterized alias declaration. Returns an empty string when * there are no value params. */ private formatValueParams; protected processAssignment(node: Assignment): string; /** * Format a pattern AST node back into Agency pattern syntax. Used by the * formatter (it sees the un-lowered AST). Handles binding patterns and * match patterns; for match patterns, literal sub-nodes are formatted via * the existing literal/expression code paths. */ protected formatPattern(pattern: BindingPattern | MatchPattern): string; /** * The right side of the `is` operator. A test-only type pattern prints as * the bare type here, because the operator is already on the page — * `x is string`, not `x is _: string`. */ private formatIsRhs; /** * An arm's left side. Type patterns print differently here than after the * `is` operator: the test-only form needs the `is` keyword written out * (`is string =>`), and since `is Type` and `_: Type` parse to the same * node, `_: Type` intentionally normalizes to `is Type`. */ private formatArmCaseValue; private formatObjectPattern; private formatArrayPattern; protected generateLiteral(literal: Literal): string; private generateStringLiteral; /** * Render a docstring body for re-emission under the formatter's * current indent level. Strips the common leading indentation * ("dedent") rather than every line's leading whitespace, so inner * structure — ```code fences```, indented bullet sub-items, sample * snippets — survives a round-trip. The caller wraps the returned * lines in `"""` ... `"""` and re-indents. */ private formatDocStringLines; private generateMultiLineStringLiteral; protected effectSetTypeToString(type: VariableType): string; protected formatRaisesClause(raises: VariableType | undefined): string; /** Build the signature line(s) for a `def`/`node` — the given `prefix`, * wrapped params, return type, and `raises` clause — with `opener` * appended (`" {"` for a full definition, `""` for a signature-only * render). The params wrap onto their own lines via `wrapList` when they * don't fit. */ private buildSignature; /** The signature of a `def`/`node`/`type` with no export keyword, no * markers, no docs, and no body — used by `agency doc` and by * `std::agency`'s `describe`. For defs/nodes this is * `name(params): ReturnType raises <...>` (params wrap the way the * formatter wraps source); for type aliases it is the declaration line * `type Name<...> = ...`, produced here rather than by printing the * node and stripping keywords, so all three kinds share one canonical * signature path. */ signatureOf(node: FunctionDefinition | GraphNodeDefinition | TypeAlias): string; /** The declared return type of a `def`/`node`, printed the way * `signatureOf` prints it, or null when none is declared. */ returnTypeOf(node: FunctionDefinition | GraphNodeDefinition): string | null; /** A parameter's declared type, printed the way `signatureOf` prints * it, or null when none is declared. */ paramTypeOf(param: FunctionParameter): string | null; /** A parameter's default as Agency source, such as `0` or `null`, or * null when it has none. */ paramDefaultOf(param: FunctionParameter): string | null; /** Render the annotations that precede a type alias declaration. */ declarationAnnotationPrefixOf(node: TypeAlias): string; protected processFunctionDefinition(node: FunctionDefinition): string; protected processFunctionCall(node: FunctionCall): string; protected renderArgs(args: FunctionCall["arguments"], block?: BlockArgument): string[]; /** A list of already-rendered items laid out across lines with its * comments, or `undefined` when there are none. Callers keep their own * inline or wrapping behavior for the no-comment case, so trivia-free * output is untouched; a comment cannot share a line with what follows * it, so its presence forces the multiline form. */ protected renderListIfTrivia(rendered: string[], trivia: ListTrivia[] | undefined, open: string, close: string): string | undefined; /** The one way a parenthesized `( ... )` list is printed. Without trivia * it keeps `wrapList`'s compact/wrapping behavior exactly; with trivia it * goes multiline. `rendered` must already be in canonical print order — * for a call that means `renderArgs` has appended any inline block last, * matching how `extractInlineBlock` remapped the anchors. */ protected renderParenList(renderItems: () => string[], trivia: ListTrivia[] | undefined, prefix: string, suffix: string): string; /** A parenthesized argument list that normally stays on one line * (access chains, `interrupt`, `guard`). Trivia forces the multiline * form, since a `//` comment cannot share a line with what follows it. */ protected renderArgList(args: FunctionCall["arguments"], block?: BlockArgument, trivia?: ListTrivia[]): string; protected generateFunctionCallExpression(node: FunctionCall, context: "valueAccess" | "functionArg" | "topLevelStatement"): string; protected processAgencyArray(node: AgencyArray): string; private renderArrayItem; protected processAgencyObject(node: AgencyObject): string; private renderObjectEntry; private addQuotesToKey; protected processValueAccess(node: ValueAccess): string; /** Print `[| ... |]`. An expr-kind body that fits one line prints * inline; everything else prints multi-line with the body one indent * level deeper and `|]` aligned with the statement. The body text is * the SHARED canonical print (printCodeLiteralBody), so what fmt * shows and what codegen embeds are the same text — and yes, fmt * REFORMATS bodies deliberately: a literal body is held to the same * standard a template file is. */ protected processCodeLiteral(node: CodeLiteral): string; protected asyncAwaitPrefix(code: string, async?: boolean): string; protected processMatchBlock(node: MatchBlock): string; /** One arm, without its final newline — a complete construct an * attached comment can follow. */ private renderInlineMatchArm; private renderBlockMatchArm; /** Whether a one-statement arm body may print inline (`=> stmt`) * rather than as a `{ ... }` block. * * The author's form wins: an arm written as a block prints as a block * (`blockBody`, set by the parser), an arm written inline prints * inline. The shape tests below only decide for cases with NO * recorded form — ASTs built programmatically — where inline is only * safe for the shapes the single-statement arm grammar accepts; * anything else must take block form or the printed arm will not * re-parse (#708). */ protected armPrintsInline(caseNode: { body: AgencyNode[]; blockBody?: true; }): boolean; protected processForLoop(node: ForLoop): string; protected processWhileLoop(node: WhileLoop): string; protected processIfElse(node: IfElse): string; protected processReturnStatement(node: ReturnStatement): string; /** Print an `if ... then ... else` expression (an `ifElse` in value position) * as its single-line surface form so it round-trips through the parser. */ protected formatIfExpression(node: IfElse): string; protected processGotoStatement(node: GotoStatement): string; protected processDebuggerStatement(node: DebuggerStatement): string; /** The sole owner of line-comment source text. Do not rebuild * `//${content}` anywhere else. */ protected commentText(comment: LineComment): string; protected processComment(node: AgencyComment): string; /** Place an attached comment after already-rendered code. Empty code has * no construct to attach to, so the comment is left to its owner. */ protected appendTrailingComment(code: string, comment: LineComment | undefined): string; protected processMultiLineComment(node: AgencyMultiLineComment): string; protected formatDocComment(node: { docComment?: AgencyMultiLineComment; }): string; protected processImportStatement(node: ImportStatement): string; /** The names inside an import's `{ ... }`, with their aliases and * retry-safety markers. Shared by the sole-named and mixed paths. */ private renderImportNames; protected processImportNameType(node: ImportNameType): string; /** Render an imported/re-exported name with its `destructive` or * `idempotent` prefix. A name carries at most one marker. */ private prefixMarkedName; protected processImportNodeStatement(node: ImportNodeStatement): string; protected processExportFromStatement(node: ExportFromStatement): string; private sortAndRenderImports; protected processGraphNode(node: GraphNodeDefinition): string; protected processNewExpression(node: NewExpression): string; protected processTool(node: FunctionDefinition): string; protected processNewLine(_node: NewLine): string; protected renderBody(body: AgencyNode[]): string; protected processMessageThread(node: MessageThread): string; protected processParallelBlock(node: ParallelBlock): string; protected processSeqBlock(node: SeqBlock): string; protected processHandleBlock(node: HandleBlock): string; /** Canonical form: no parens, and the binder prints through the same * param renderer blocks use. A stray `as` with no binder (the shared * grammar's no-param form) canonicalizes away — printing IS the * migration, like guard's legacy `as`. */ protected processFinalizeBlock(node: FinalizeBlock): string; /** Canonical form: the head prints through the same argument * renderer function calls use (source order for free), parens * always present, never a legacy `as` — printing old syntax through * the generator IS the `as`-removal migration. */ protected processGuardBlock(node: GuardBlock): string; protected processComprehension(node: Comprehension): string; protected processSkill(node: Skill): string; protected processBinOpExpression(node: BinOpExpression, assigned?: boolean): string; private flattenPipeChain; protected processAccessChainElement(node: AccessChainElement): string; /** A tag prints its arguments the way a call prints its own: a decorator * tag's arguments are call arguments (named ones included), and the * reserved tags' static arguments are a subset of that grammar. */ protected formatTag(tag: Tag): string; /** A def's decorator tags, one line each, as the author wrote them. Reads * `decorators` (where the lowering moves them) and any decorator tags * still on `tags`, so an unlowered tree prints the same lines. `agency * doc` puts these above the signature. */ decoratorLines(fn: FunctionDefinition): string[]; protected formatAttachedTags(node: { tags?: Tag[]; }): string; protected processKeyword(node: Keyword): string; } /** The canonical printed body of a code literal, WITHOUT the brackets. * Shared by the formatter (which wraps it in `[| |]`) and codegen * (which embeds it for runtime reconstruction), so what fmt shows and * what the generated program reconstructs are the same text. */ export declare function printCodeLiteralBody(node: CodeLiteral): string; export declare function generateAgency(program: AgencyProgram, opts?: { preserveOrder?: boolean; debug?: boolean; }): string; /** * Formatter-exact source rendering of a single expression — the same * renderer interpolations use, so string quotes and escapes are preserved. * NOT `expressionToString` (lib/utils/node.ts), which drops string quotes. */ export declare function generateExpression(expr: AgencyNode): string; export {};