formula¶
The Excel formula language used by EXCEL and HYBRID modes. Nothing on this path calls eval(): the source is tokenized, parsed into an AST, and walked by an evaluator that implements Excel's coercion and error-propagation rules.
Parser¶
parser
¶
Lexer¶
lexer
¶
tokenize
¶
tokenize(text: str) -> list[Token]
Source code in src/gridcalc/formula/lexer.py
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | |
Evaluator¶
evaluator
¶
Env
¶
Env(
cell_value: Callable[..., object],
builtins: dict[str, Callable[..., Any]],
named_ranges: dict[str, Node] | None = None,
py_registry: dict[str, Callable[..., Any]]
| None = None,
cell_is_formula: Callable[..., bool] | None = None,
cell_spill_value: Callable[..., object] | None = None,
cell_formula_text: Callable[..., object] | None = None,
)
Source code in src/gridcalc/formula/evaluator.py
push_scope
¶
Push a fresh local scope and return it so a caller can add
bindings incrementally. Must be paired with pop_scope.
Source code in src/gridcalc/formula/evaluator.py
lookup_local
¶
Resolve a LET-bound local. Returns (found, value) so a
legitimately-bound None is distinguishable from a miss.
Source code in src/gridcalc/formula/evaluator.py
eval_node
¶
eval_node(node: Node) -> Any
Evaluate a sub-expression AST node in this environment. Used by reference-aware functions to compute their non-reference arguments.
resolve_ref
¶
Resolve an AST node to a Reference, or None if it is not a reference. Cell/range refs resolve statically; a call (e.g. a nested OFFSET) is evaluated and accepted only if it yields a Reference.
Source code in src/gridcalc/formula/evaluator.py
Reference
dataclass
¶
A location (single cell or rectangular range), distinct from the
value(s) it points at. Produced by OFFSET and consumed by the
reference-aware functions (ROW/COLUMN/ROWS/COLUMNS/
FORMULATEXT/AREAS). Anywhere a plain value is expected -- a
normal function argument, an arithmetic operand, a formula's result --
a Reference materialises to a scalar (1x1) or a Vec, via _deref.
Coordinates are 0-based inclusive.
LambdaValue
¶
A first-class function produced by LAMBDA(param..., body).
Closes over the local scopes in effect where it was defined (a
shallow snapshot, since scopes are mutated and popped as evaluation
proceeds). Calling it swaps that captured scope stack in for the
duration of the body, then restores the caller's -- so lexical
scoping and re-entrancy both hold. refs_used and the range cache
stay on the shared Env so cell reads inside a lambda body are
still tracked as dependencies.
Source code in src/gridcalc/formula/evaluator.py
Dependencies¶
Static reference extraction, which is what makes topological recalculation possible.
deps
¶
Static dependency extraction over the formula AST.
Used by Grid to maintain forward/reverse dependency indexes for
topological recalc. Pure-AST analysis: no evaluation.
extract_refs
¶
extract_refs(
node: Node,
named_ranges: dict[str, Node] | None = None,
formula_sheet: str | None = None,
) -> set[tuple[str | None, int, int]]
Return the set of (sheet, col, row) cells that node reads.
Range references expand to the full rectangular set. Named ranges
are resolved through named_ranges; unknown names are ignored.
Sheet identity per ref
- if the ref carries an explicit sheet (
Sheet2!A1), use it; - otherwise the ref resolves against
formula_sheet(the sheet containing the formula). Whenformula_sheetis None, the returned key is(None, c, r)-- correct for the single-sheet case before phase 1's Sheet class lands and sufficient for any caller that doesn't differentiate sheets.
Does not detect dynamic-ref functions; use has_dynamic_refs.
Source code in src/gridcalc/formula/deps.py
has_dynamic_refs
¶
has_dynamic_refs(node: Node) -> bool
True if node contains a call whose read set depends on a value.
Cells matching this need always-recompute treatment in topo recalc.
Source code in src/gridcalc/formula/deps.py
Errors¶
errors
¶
ExcelError
¶
Bases: Enum
FormulaError
¶
Bases: Exception
parse_error_literal
¶
parse_error_literal(text: str) -> ExcelError | None
AST nodes¶
ast_nodes
¶
Node
module-attribute
¶
Node = (
Number
| String
| Bool
| ErrorLit
| CellRef
| RangeRef
| SpillRef
| Name
| Call
| PyCall
| Apply
| BinOp
| UnaryOp
| Percent
)