formula
Zero-dependency spreadsheet formula parser and evaluator.
A pure-TypeScript engine that turns a =-prefixed string into a computed value,
resolving cell references through a callback you provide. It has no dependencies
and no opinion about where your grid lives — it powers the
bulk-editor's formulas but works standalone.
Installation
pnpm add @fluixi/formula
Usage
import { evaluateFormula } from '@fluixi/formula';
const grid: Record<string, string> = { A1: '2', A2: '3', A3: '=A1+A2' };
const resolve = (addr: string) => grid[addr] ?? '';
evaluateFormula('=SUM(A1:A3)', { resolve }); // "10"
evaluateFormula('=IF(A1>A2,"hi","lo")', { resolve }); // "lo"
evaluateFormula('plain text', { resolve }); // "plain text" — unchanged
Input that doesn't start with = is returned unchanged, so evaluateFormula
drops straight into anywhere expecting an identity value transform.
Supported syntax
- Operators —
+ - * / ^, string concat&, comparisons= <> < > <= >=, unary+/-, postfix%. Power is right-associative. - References — A1 addresses (
B3) and ranges (A1:B3), resolved throughresolve. Ranges expand to individual cells. - Literals — numbers,
"double-quoted strings"(""escapes a quote),TRUE/FALSE. - Functions —
SUM,AVERAGE/AVG,MIN,MAX,COUNT,COUNTA,PRODUCT,ROUND,ABS,SQRT,FLOOR,CEILING,INT,MOD,POWER,IF,AND,OR,NOT,CONCAT/CONCATENATE,LEN,UPPER,LOWER,TRIM. Names are case-insensitive.
Errors
Problems surface as spreadsheet-style strings rather than exceptions, and propagate through operators and across cell references:
| Error | Meaning |
|---|---|
#DIV/0! |
Division by zero. |
#NAME? |
Unknown function. |
#VALUE! |
A value couldn't be coerced (e.g. text used as a number). |
#NUM! |
Numeric overflow / invalid result. |
#CYCLE! |
Circular reference between cells. |
#ERROR! |
Syntax error. |
A referenced cell that already holds an error passes it along, so an error shows at its source and everywhere downstream.
Custom functions
Pass extra functions (merged over the built-ins, matched case-insensitively).
Each receives already-evaluated arguments; a range arrives as a RangeValue.
evaluateFormula('=DOUBLE(21)', {
resolve,
functions: { double: (args) => (typeof args[0] === 'number' ? args[0] * 2 : 0) },
}); // "42"
With bulk-editor
The bulk-editor ships a one-line adapter, formulaEvaluator(),
that wires this engine into its evaluate hook — the editor's cell resolver
handles A1 addressing and circular-reference detection for you:
import { BulkEditor, formulaEvaluator } from '@fluixi/bulk-editor';
<BulkEditor columns={columns} rows={rows} evaluate={formulaEvaluator()} />
API
Beyond evaluateFormula, the package exports its building blocks: parse
(string → AST), tokenize, the A1 helpers (colToLabel, labelToCol,
parseAddress, expandRange), the BUILTINS registry, and the value helpers
(ERR, isError, isRange, makeError).
Part of the Fluixi UI component library. Made with ☕ by the Fluixi team.