The spreadsheet engine that actually evaluates formulas. Parse, evaluate, and mutate Excel workbooks from Rust, Python, or the browser.
A permissively-licensed, production-grade spreadsheet engine with 320+ Excel-compatible functions, Apache Arrow storage, incremental dependency tracking, undo/redo, and dynamic array support. One Rust core, three language targets, MIT/Apache-2.0.
| 320+ Excel functions | Math, text, lookup (XLOOKUP, VLOOKUP), date/time, statistics, financial, database, engineering |
| Three language targets | Rust, Python (PyO3), and WASM (browser + Node) with consistent APIs |
| Arrow-powered storage | Apache Arrow columnar backing with spill overlays for efficient large-workbook evaluation |
| Dependency graph | Incremental recalculation, cycle detection, topological scheduling, optional parallel evaluation |
| Dynamic arrays | FILTER, UNIQUE, SORT, SORTBY, XLOOKUP with automatic spill semantics |
| Undo / redo | Transactional changelog with action grouping, rollback, and replay |
| File I/O | Load and write XLSX (calamine, umya), CSV, JSON — all behind feature flags |
| SheetPort | Treat any spreadsheet as a typed API with YAML manifests, schema validation, and batch evaluation |
| Deterministic mode | Inject clock, timezone, and RNG seed for reproducible evaluation (built for AI agents) |
📖 formualizer.dev — full documentation, interactive tools, and API reference.
- Quickstarts — get running in Rust, Python, or JS/WASM in minutes
- Function Reference — 320+ built-in functions with examples
- Formula Parser — interactive browser-based formula parser and AST inspector
- SheetPort Guide — treat spreadsheets as typed, deterministic APIs
- Core Concepts — dependency graph, evaluation pipeline, coercion rules
- Fintech & insurance teams replacing Excel VBA or server-side workbook evaluation with a fast, deterministic engine that doesn't require Excel installed.
- AI / agent builders who need programmatic spreadsheet manipulation with deterministic evaluation, auditable changelogs, and typed I/O via SheetPort.
- SaaS products embedding spreadsheet logic — pricing calculators, planning tools, configurators — without shipping a full spreadsheet UI.
- Data engineers extracting business logic trapped in spreadsheets into reproducible, testable pipelines.
use formualizer_workbook::Workbook;
use formualizer_common::LiteralValue;
let mut wb = Workbook::new();
wb.add_sheet("Sheet1")?;
// Populate data
wb.set_value("Sheet1", 1, 1, LiteralValue::Number(1000.0))?; // A1: principal
wb.set_value("Sheet1", 2, 1, LiteralValue::Number(0.05))?; // A2: rate
wb.set_value("Sheet1", 3, 1, LiteralValue::Number(12.0))?; // A3: periods
// Monthly payment formula
wb.set_formula("Sheet1", 1, 2, "=PMT(A2/12, A3, -A1)")?;
let payment = wb.evaluate_cell("Sheet1", 1, 2)?;
// => ~85.61# Cargo.toml
[dependencies]
formualizer = "0.3"pip install formualizerimport formualizer as fz
wb = fz.Workbook()
s = wb.sheet("Forecast")
# Load actuals
s.set_values_batch(1, 1, [
[fz.LiteralValue.text("Month"), fz.LiteralValue.text("Revenue"), fz.LiteralValue.text("Growth")],
[fz.LiteralValue.text("Jan"), fz.LiteralValue.number(50000.0), fz.LiteralValue.empty()],
[fz.LiteralValue.text("Feb"), fz.LiteralValue.number(53000.0), fz.LiteralValue.empty()],
[fz.LiteralValue.text("Mar"), fz.LiteralValue.number(58000.0), fz.LiteralValue.empty()],
])
# Add growth formulas
s.set_formula(3, 3, "=(B3-B2)/B2") # C3: Feb growth
s.set_formula(4, 3, "=(B4-B3)/B3") # C4: Mar growth
print(wb.evaluate_cell("Forecast", 3, 3)) # 0.06 (6%)
print(wb.evaluate_cell("Forecast", 4, 3)) # ~0.094 (9.4%)npm install formualizerimport init, { Workbook } from 'formualizer';
await init();
const wb = new Workbook();
wb.addSheet('Pricing');
wb.setValue('Pricing', 1, 1, 100); // base price
wb.setValue('Pricing', 2, 1, 0.15); // discount
wb.setFormula('Pricing', 1, 2, '=A1*(1-A2)');
console.log(await wb.evaluateCell('Pricing', 1, 2)); // 85You can register custom functions per workbook in Rust, Python, and JS/WASM.
- Rust:
register_custom_function/unregister_custom_function/list_custom_functions - Python:
register_function/unregister_function/list_functions - JS/WASM:
registerFunction/unregisterFunction/listFunctions
Semantics are consistent across hosts:
- Function names are case-insensitive (
my_fn,MY_FN, andMy_Fnrefer to the same function). - Custom functions are workbook-local and resolve before global built-ins.
- Overriding built-ins is blocked by default; opt in with
allow_override_builtin(Rust/Python) orallowOverrideBuiltin(JS). - Arguments are passed by value; range arguments are materialized as 2D arrays/lists.
- Returning an array spills into the grid using normal dynamic-array behavior.
- Callback failures become spreadsheet errors (
ExcelErrorin Rust,#VALUE!mapping for Python/JS exceptions).
Runnable examples:
- Rust callback custom function:
cargo run -p formualizer-workbook --example custom_function_registration - Python callback custom function:
python bindings/python/examples/custom_function_registration.py - JS/WASM callback custom function:
cd bindings/wasm && npm run build && node examples/custom-function-registration.mjs - Rust WASM plugin inspect catalog:
cargo run -p formualizer-workbook --features wasm_plugins --example wasm_plugin_inspect_catalog - Rust WASM plugin inspect + attach + bind:
cargo run -p formualizer-workbook --features wasm_runtime_wasmtime --example wasm_plugin_inspect_attach_bind - Rust WASM plugin directory attach:
cargo run -p formualizer-workbook --features wasm_runtime_wasmtime --example wasm_plugin_attach_dir
WASM plugin path (Rust workbook API):
- Effect-free inspect APIs are available:
inspect_wasm_module_bytesinspect_wasm_module_file(native only)inspect_wasm_modules_dir(native only)
- Explicit workbook-local attach/bind APIs are available:
attach_wasm_module_bytes/attach_wasm_module_file/attach_wasm_modules_dirbind_wasm_function
- Runtime behavior:
wasm_pluginsonly: runtime remains pending (#N/IMPLon bind)wasm_runtime_wasmtime(native):use_wasmtime_runtime()enables executable plugin bindings
| Library | Language | Parse | Evaluate | Write | Functions | Dep. graph | License |
|---|---|---|---|---|---|---|---|
| Formualizer | Rust / Python / WASM | Yes | Yes | Yes | 320+ | Yes (incremental) | MIT / Apache-2.0 |
| HyperFormula | JavaScript | Yes | Yes | No | ~400 | Yes | AGPL-3.0 (or commercial) |
| calamine | Rust | No | No | No | N/A | N/A | MIT / Apache-2.0 |
| openpyxl | Python | No | No | Yes | N/A | N/A | MIT |
| xlcalc | Python | Yes | Yes | No | ~50 | Partial | MIT |
| formulajs | JavaScript | No | Yes | No | ~100 | No | MIT |
- HyperFormula is the closest feature competitor, but its AGPL-3.0 license requires you to open-source your entire application or purchase a commercial license from Handsontable. Formualizer is permissively licensed with no strings attached.
- calamine is read-only — it extracts cached values from XLSX files but cannot evaluate formulas.
- openpyxl reads and writes XLSX but has no formula evaluation engine.
- xlcalc evaluates formulas but supports a fraction of Excel's function library and has limited dependency tracking.
- Formualizer is a complete, permissively-licensed engine: parse formulas, track dependencies, evaluate with 320+ functions, mutate workbooks, undo/redo — from Rust, Python, or the browser.
Formualizer is organized as a layered crate workspace. Pick the layer that fits your use case:
formualizer <-- recommended: batteries-included re-export
formualizer-workbook <-- high-level workbook API, sheets, undo/redo, I/O
formualizer-eval <-- calculation engine, dependency graph, built-ins
formualizer-parse <-- tokenizer, parser, AST, pretty-printer
formualizer-common <-- shared types (values, errors, references)
formualizer-sheetport <-- SheetPort runtime (spreadsheets as typed APIs)
| Crate | When to use it |
|---|---|
formualizer |
Default choice — re-exports workbook, engine, and SheetPort with feature flags |
formualizer-workbook |
You want the full workbook experience: sheets, I/O, undo/redo, batch operations |
formualizer-eval |
You own your own data model and want just the calculation engine with custom resolvers |
formualizer-parse |
You only need formula parsing, tokenization, AST analysis, or pretty-printing |
SheetPort lets you treat any spreadsheet as a deterministic function with typed inputs and outputs, defined by a YAML manifest:
from formualizer import SheetPortSession, Workbook
session = SheetPortSession.from_manifest_yaml(manifest_yaml, workbook)
# Write typed inputs — validated against schema
session.write_inputs({"loan_amount": 250000, "rate": 0.045, "term_months": 360})
# Evaluate and read typed outputs
result = session.evaluate_once(freeze_volatile=True)
print(result["monthly_payment"]) # deterministic, schema-validatedUse cases: financial model APIs, AI agent tool-use, configuration-driven business logic, batch scenario evaluation.
The evaluation engine is built on Apache Arrow columnar storage with:
- Incremental dependency graph (only recalculates what changed)
- CSR (Compressed Sparse Row) edge format for memory-efficient graphs
- Optional parallel evaluation via Rayon
- Warm-up planning for large workbooks
- Spill overlays for dynamic array results
Formal benchmarks are in progress.
| Target | Install | Docs |
|---|---|---|
| Rust | cargo add formualizer |
docs.rs · guide |
| Python | pip install formualizer |
README · guide |
| WASM | npm install formualizer |
README · guide |
Both Python and WASM bindings expose the same core API surface: tokenization, parsing, workbook operations, evaluation, undo/redo, and SheetPort.
Roadmap and active development are tracked via GitHub Issues, milestones, and pull requests.
Contributions are welcome. If you're looking for something to work on, browse open issues or open a new issue to discuss a proposal.
# Build and test
cargo test --workspace
cd bindings/python && maturin develop && pytest
cd bindings/wasm && wasm-pack build --target bundler && wasm-pack test --nodeDual-licensed under MIT or Apache-2.0, at your option.
