/** * Compiled Formula — The output of the full compilation pipeline. * * A `CompiledFormula` packages together: * - The original `FormulaInstance` metadata * - The bound expression tree (from the binder) * - Static dependency information * - Metadata flags (volatile, dynamic refs, etc.) * * ## Static Dependency Extraction * * Dependencies are extracted from the `BoundExpr` tree. Since names and * structured refs are already resolved by the binder, the dependency * extraction is a simple tree walk that collects `BoundCellRef` and * `BoundAreaRef` nodes. * * Runtime-dependent references (INDIRECT, OFFSET) cannot be captured * statically — the `hasDynamicRefs` flag marks formulas that may have * additional runtime dependencies. Those functions re-parse their dynamic * arguments at evaluation time using their own parser invocation (the raw * AST is not retained on the CompiledFormula). */ import type { FormulaInstance } from "../integration/formula-instance.js"; import type { WorkbookSnapshot } from "../integration/workbook-snapshot.js"; import type { AstNode } from "../syntax/ast.js"; import type { BoundExpr } from "./bound-ast.js"; /** * The complete compiled representation of a formula. */ export interface CompiledFormula { /** The original formula instance metadata. */ readonly instance: FormulaInstance; /** The bound expression tree (the evaluator executes this). */ readonly bound: BoundExpr; /** Statically extractable dependencies. */ readonly staticDeps: StaticDependencySet; /** Whether this formula uses volatile functions (RAND, NOW, etc.). */ readonly isVolatile: boolean; /** Whether this formula contains INDIRECT/OFFSET (runtime-dependent refs). */ readonly hasDynamicRefs: boolean; /** Whether this formula contains any lambda expressions. */ readonly containsLambda: boolean; /** * Whether the top-level function is a known dynamic array function * (FILTER, SORT, UNIQUE, SEQUENCE, etc.). Determined once at compile * time — consumers should use this flag instead of re-checking the AST * or bound expression. */ readonly isDynamicArrayFunction: boolean; /** * Whether the top-level function is SUBTOTAL or AGGREGATE. When an * outer SUBTOTAL/AGGREGATE range aggregates a cell whose formula is * itself a SUBTOTAL/AGGREGATE call, that cell must be skipped so its * result is not double-counted. This flag lets `buildRangeArray` * mark those cells with the array's `subtotalMask`. */ readonly isSubtotalOutput: boolean; } /** * A single cell dependency. */ export interface CellDep { readonly sheet: string; readonly row: number; readonly col: number; } /** * A rectangular area dependency. */ export interface AreaDep { readonly sheet: string; readonly top: number; readonly left: number; readonly bottom: number; readonly right: number; } /** * All statically extractable dependencies of a formula. */ export interface StaticDependencySet { /** Individual cell references. */ readonly cells: readonly CellDep[]; /** Range references. */ readonly areas: readonly AreaDep[]; } /** * Callback to resolve a defined name into its parsed/bound expression * for dependency extraction. Returns the bound expression if the name * resolves to a formula, or undefined if it can't be resolved. */ export type NameDepResolver = (name: string) => { deps: StaticDependencySet; hasDynamicRefs: boolean; /** * Whether the defined-name's formula (transitively) uses a volatile * function like NOW/RAND/OFFSET/INDIRECT. The resolver must propagate * this so the OUTER formula inherits volatility — otherwise the * session result cache would hold stale values across calc runs. */ isVolatile: boolean; } | undefined; /** * Extract static dependencies from a bound expression tree. * * @param expr - The bound expression tree to analyze * @param snapshot - Optional snapshot for resolving structured references * @param nameResolver - Optional resolver for formula-based defined name dependencies */ export declare function extractStaticDeps(expr: BoundExpr, snapshot?: WorkbookSnapshot, nameResolver?: NameDepResolver): StaticDependencySet; /** * Check if the formula's top-level expression is a known dynamic array * function. Checks both the raw AST (for prefixed names like `_XLFN.FILTER`) * and the bound expression (for the canonical uppercase name). */ export declare function detectDynamicArrayFunction(ast: AstNode, bound: BoundExpr): boolean; /** * Check if the formula's top-level expression is SUBTOTAL or AGGREGATE. * * Excel's SUBTOTAL/AGGREGATE functions deliberately skip any cell whose * own source formula is itself a SUBTOTAL or AGGREGATE call — this is * what makes the classic "totals row inside a filtered range" case not * double-count. `buildRangeArray` reads this flag off the compiled * formula to decide whether to set `subtotalMask[r][c]`. */ export declare function detectSubtotalOutput(ast: AstNode, bound: BoundExpr): boolean; /** * Analyze a bound expression for volatile functions and dynamic references. * * @param nameResolver - Optional; if a NameExpr resolves to a formula-based * defined name containing INDIRECT/OFFSET, the outer formula inherits * `hasDynamicRefs = true`. */ export declare function analyzeExpr(expr: BoundExpr, nameResolver?: NameDepResolver): { isVolatile: boolean; hasDynamicRefs: boolean; containsLambda: boolean; };