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