Skip to content

Embeddable spreadsheet engine — parse, evaluate & mutate Excel workbooks from Rust, Python, or the browser. Arrow-powered, 320+ functions.

Notifications You must be signed in to change notification settings

PSU3D0/formualizer

Repository files navigation

Formualizer

Arrow Powered CI Python Coverage Rust Core Coverage crates.io PyPI npm Documentation License: MIT/Apache-2.0

Formualizer banner


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.


Highlights

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)

Documentation

📖 formualizer.dev — full documentation, interactive tools, and API reference.

Who is this for?

  • 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.

Quick start

Rust

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"

Python

pip install formualizer
import 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%)

WASM (browser / Node)

npm install formualizer
import 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)); // 85

Custom functions (workbook-local)

You 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, and My_Fn refer 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) or allowOverrideBuiltin (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 (ExcelError in 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_bytes
    • inspect_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_dir
    • bind_wasm_function
  • Runtime behavior:
    • wasm_plugins only: runtime remains pending (#N/IMPL on bind)
    • wasm_runtime_wasmtime (native): use_wasmtime_runtime() enables executable plugin bindings

How is this different?

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.

Architecture

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: spreadsheets as typed APIs

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-validated

Use cases: financial model APIs, AI agent tool-use, configuration-driven business logic, batch scenario evaluation.

Performance

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.

Bindings

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

Roadmap and active development are tracked via GitHub Issues, milestones, and pull requests.

Contributing

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 --node

License

Dual-licensed under MIT or Apache-2.0, at your option.

About

Embeddable spreadsheet engine — parse, evaluate & mutate Excel workbooks from Rust, Python, or the browser. Arrow-powered, 320+ functions.

Topics

Resources

Contributing

Stars

Watchers

Forks

Packages