expr-solver
A mathematical expression evaluator library written in Rust with support for custom functions, constants, and bytecode compilation.
Features
- Mathematical expressions - Arithmetic, comparisons, and built-in functions
- Flexible numeric types - Choose between fast f64 (default) or high-precision 128-bit Decimal
- Custom symbols - Register your own constants and functions
- Rich error messages - Syntax errors with source location highlighting
- Bytecode compilation - Compile expressions to portable binary format
- Stack-based VM - Efficient execution on a virtual machine
What's it For?
Need to evaluate math but don't want to embed an entire scripting language? You're in the right place. 🎯
Perfect for:
- Game engines - Calculate damage formulas, level requirements, or item stats without hardcoding values
- Configuration files - Let users write
price * 0.9instead of forcing them to update every value manually - Analytics dashboards - Enable power users to define custom metrics:
(revenue - cost) / users - Form calculators - Mortgage calculators, unit converters, or any user-facing math
- Educational tools - Math tutoring apps, graphing calculators, or homework helpers
- Learning compilers - Clean example of lexer, parser, and stack-based VM in ~2K lines of Rust
How It Works
Classic compiler pipeline with type-safe state transitions:
Input → Lexer → Parser → Compiler → Program<Compiled>
↓ link
Program<Linked> → Execute
The Program type uses Rust's type system to enforce correct usage at compile time. You cannot execute an unlinked program, and you cannot link a program twice.
Usage
As a Library
Add this to your Cargo.toml:
[]
= "1.2.0"
Numeric Type Selection:
The library supports two numeric backends via feature flags (mutually exclusive):
f64-floats(default) - Standard f64 floating-point arithmetic. Faster and simpler, allows Inf and NaN results.decimal-precision- 128-bit Decimal for high precision. No floating-point errors, checked arithmetic with overflow detection.
To use high-precision Decimal:
[]
= { = "1.2.0", = false, = ["decimal-precision"] }
To enable bytecode serialization with f64:
[]
= { = "1.2.0", = ["serialization"] }
To enable bytecode serialization with Decimal:
[]
= { = "1.2.0", = false, = ["decimal-precision", "serialization"] }
As a binary
Add this to your Cargo.toml:
[]
= "1.2.0"
Quick Evaluation
use eval;
// Simple one-liner
let result = eval.unwrap;
assert_eq!;
// With built-in functions
let result = eval.unwrap;
Custom Symbols
use ;
let mut table = stdlib;
table.add_const.unwrap;
table.add_func.unwrap;
let result = eval_with_table.unwrap;
assert_eq!;
Or with f64 (default):
use ;
let mut table = stdlib;
table.add_const.unwrap;
table.add_func.unwrap;
let result = eval_with_table.unwrap;
assert_eq!;
Compile Once, Execute Many Times
use ;
// Compile expression
let program = new_from_source.unwrap;
// Execute with different values
let mut table = new;
table.add_const.unwrap;
table.add_const.unwrap;
let linked = program.link.unwrap;
let result = linked.execute.unwrap; // 25.0
assert_eq!;
Numeric Precision
The library supports two numeric backends:
f64 (Default)
- Standard IEEE 754 double-precision floating-point
- Fast and efficient for most use cases
- Allows
InfandNaNresults (e.g.,1/0→Inf,sqrt(-1)→NaN) - Minimal error checking - only prevents panics
Decimal (Optional)
- 128-bit fixed-point arithmetic via
rust_decimal - Exact decimal representation (no 0.1 + 0.2 ≠ 0.3 issues)
- Checked arithmetic with overflow/underflow detection
- Domain validation (e.g.,
sqrt(-1)returns an error) - Ideal for financial calculations or when exact decimal precision is required
Choosing the right mode:
- Use f64 (default) for general-purpose math, scientific computing, or when performance is critical
- Use Decimal for financial applications, accounting, or when exact decimal representation is required
Built-in Functions
| Category | Functions |
|---|---|
| Arithmetic | abs, sign, floor, ceil, round, trunc, fract, mod, clamp |
| Trig | sin, cos, tan, asin, acos, atan, atan2 |
| Hyperbolic | sinh, cosh, tanh |
| Exp/Log | sqrt, cbrt, pow, exp, exp2, log, log2, log10, hypot |
| Variadic | min, max, sum, avg (1+ args) |
| Special | if(cond, then, else) |
Note: In
decimal-precisionmode, some operations (inverse trig,pow) use internal f64 conversion due torust_decimallimitations, which may introduce small precision loss.
Built-in Constants
pi, e, tau, ln2, ln10, sqrt2
All names are case-insensitive.
Operators
Arithmetic: +, -, *, /, ^ (power), ! (factorial), unary -
Comparison: ==, !=, <, <=, >, >= (returns 1 or 0)
Grouping: ( )
Command Line Usage
# Evaluate an expression
# Use the -e flag
# Define custom constants
# Compile to binary
# Execute compiled binary
# View assembly from expression or file
# Recompile bytecode (e.g., version migration)
# List available functions and constants
Testing
Run the test suite:
# Run all tests with default f64 mode
# Test with decimal-precision mode
# Test binary with f64 mode
License
This project is licensed under the MIT License.