Skip to content

Bindings ​

Bindings expose workbook operations to host environments while keeping calculation in the core. Each binding speaks the host language idiom on one side and the C ABI on the other.

Glossary: binding

A thin per-host layer that translates host types (Uint8Array, bytes, bytearray, std::span) into engine inputs and engine outputs back into host types. Bindings do not implement formula semantics — they shape data and manage lifetimes.

Host typesBindingsBoundaryCoreUint8Array /Bufferbytes / bytearraystd::span / void*JSON inputsWASMNative NodePythonCLIMCPC ABI (only stable boundary)C++17 core — parser / evaluator / calc graph / file IOHost typesBindingsBoundaryCoreUint8Array / Bufferbytes / bytearraystd::span / void*JSON inputsWASMNative NodePythonCLIMCPC ABI (only stable boundary)C++17 core — parser /evaluator / calc graph / file IO

Responsibilities ​

  • Convert host buffers to workbook bytes accepted by the C ABI.
  • Manage workbook handles and lifetimes (delete() in WASM, context manager in Python, dispose() plus GC fallback in Native Node, process lifetime in CLI / MCP).
  • Convert spreadsheet values to host values, preserving ValueKind so cell errors remain values rather than exceptions.
  • Surface calculation and IO errors without losing spreadsheet error values.
  • Expose the function and structural API documented under API per host.

Non-responsibilities ​

Bindings should not:

  • implement formula semantics (the parser / evaluator lives in the core),
  • duplicate workbook parsing (the OOXML / XLSB readers live in the core),
  • add profile-specific behavior (profiles are workbook state, set through the C ABI),
  • introduce a separate dirty-set, dependency graph, or recalc scheduler (all of those belong to the core).

Don't reimplement Excel in the binding

If a binding is tempted to special-case a function result or coerce a value differently than the core, the right fix is in the core: add the case to the evaluator, add an oracle golden, and let every binding pick up the change at once. Per-binding patches drift fast.

What this looks like in practice ​

BindingHost idioms it handlesWhat it never touches
WASMUint8Array, status envelopes, ValueKind enum, async module factoryFunction semantics, file parsing, calc graph
Native NodeN-API Buffer, sync API shape, dispose() and memoryUsage()Function semantics, file parsing, calc graph
Pythonbytes, context manager, FormulonError, Value.to_python()Function semantics, file parsing, calc graph
CLIargv, stdin / stdout / stderr, exit codesFunction semantics, file parsing, calc graph
MCPJSON inputs / outputs, session map, allowlist dispatchFunction semantics, file parsing, calc graph