/** * @factory/shared — the formula AST discriminated union (TASK-0084 R2). * * The AST produced by `packages/database-engine`'s parser is JSON-serializable * (stored in `formulas.ast`); THIS type is the single source the editor's * autocomplete, the Phase-12 "generate formula" AI tool, and the engine's * evaluator all consume. Keeping it here (not in the engine) is a recorded * decision: the editor and the AI gateway must not depend on the database * engine just for the AST shape. * * Node kinds: * - `Literal` — number / string / date (ISO string) / bool. * - `NullLiteral` — `null` (added to the spec's union; array aggregates need * null elements and the null-matrix goldens — recorded). * - `ArrayLiteral` — `[a, b, c]` (same reason — the function set takes * list arguments; recorded). * - `Prop` — `prop("Name")` (also reachable via bare `prop` calls). * - `Call` — fixed function table, closed set. * - `BinaryOp` — arithmetic / comparison / logical (left-assoc). * - `UnaryOp` — `-x`, `not x`. * - `If` — `if(c, a, b)`; the ternary `c ? a : b` parses to the same node. */ export type FormulaLiteralType = "number" | "string" | "date" | "bool"; export type FormulaAstNode = | { type: "Literal"; value: number | string | boolean; literalType: FormulaLiteralType } | { type: "NullLiteral" } | { type: "ArrayLiteral"; elements: FormulaAstNode[] } | { type: "Prop"; name: string } | { type: "Call"; name: string; args: FormulaAstNode[] } | { type: "BinaryOp"; op: string; left: FormulaAstNode; right: FormulaAstNode } | { type: "UnaryOp"; op: "-" | "not"; operand: FormulaAstNode } | { type: "If"; cond: FormulaAstNode; then: FormulaAstNode; else: FormulaAstNode };