Calibre.FormulaEngine 0.3.0
Calibre.FormulaEngine
Domain-neutral formula calculation engine as a pure .NET library (net10.0, zero runtime dependencies). It knows nothing about salary, leave, loans, or any other domain — consumers (payroll, attendance, lending, accounting) layer their own contracts and content on top.
Pipeline
DSL text → FormulaParser ┐
Excel text → ExcelFormulaParser ┴→ AST (canonical JSON form) → dependency extraction
→ DAG build + topological sort + cycle rejection
→ compile to expression-tree delegates → cached execution (+ calculation trace)
Two authoring surfaces, one AST. FormulaParser reads the engine's own DSL and is frozen.
ExcelFormulaParser reads spreadsheet formula-bar text (dialect excel/1) so a rule pastes in
unchanged: percent literals become PCT, AND/OR become operator chains, ROUNDUP becomes
ROUND's AWAY_FROM_ZERO mode, CEILING(x,1) becomes CEIL. ExcelFormulaPrinter renders any
printable AST back as that text, byte-round-trip guaranteed. Neither surface adds a node kind, an
operator or a function, so canonical JSON and hashes are the same as they always were.
The AST is the canonical artifact: formula text pretty-prints from it, builder UIs emit it, the validator validates it, hosts store and hash it. See Calibre.FormulaEngine.Serialization.AstJsonSerializer for the canonical JSON contract and AstHash for content hashing.
Design laws (identity of this package)
- Pure — no database, no clock, no network, no file or config I/O. The host hydrates an
EvaluationContext(values, quantities, policies, rates, slab tables, dates, resolvers); the engine only computes. Same inputs + same versions = same outputs, forever. - Zero persistence — not even repository interfaces. Hosts own storage.
- Zero domain concepts — no "employee", no "salary". Ever.
- Decimal arithmetic only, explicit rounding at defined points.
- Not Turing-complete — no loops, no mutation, no state. Formulas express math over resolved data; imperative data resolution is the host's job.
- Strict SemVer — the public API surface and the canonical JSON form are contracts; breaking either is a major version bump.
Build
dotnet build Calibre.FormulaEngine.slnx
dotnet test Calibre.FormulaEngine.slnx
dotnet pack src/Calibre.FormulaEngine -c Release -o artifacts
Status
| Stage | State |
|---|---|
| AST node model + canonical JSON + hashing | ✅ (released 0.1.0) |
| Evaluation context object model | ✅ (released 0.1.0) |
| Parser (DSL → AST) | ✅ |
| Dependency extractor, DAG builder, cycle detection | ✅ |
| Expression-tree compiler + delegate cache | ✅ |
| Validator (schema/type checks, percent discipline, lint hooks) + test-vector runner | ✅ |
Excel surface excel/1 (parser + printer), ROUND mode AWAY_FROM_ZERO, context function allow-list (FE015) |
✅ (0.3.0) |
The full pipeline is implemented. Formula contexts are host-declared: ValidationSchema.Functions
is an allow-list of function names, and a call outside it is FE015 — the engine never names a
surface or a domain.
No packages depend on Calibre.FormulaEngine.
.NET 10.0
- No dependencies.