[](https://github.com/albeva/expr-solver/actions/workflows/rust.yml)
# 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
- **128-bit decimal precision** - No floating-point errors using `rust_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
## How It Works
The library implements a classic compiler pipeline:
```
Source → Lexer → Parser → AST → Semantic Analysis → IR → Bytecode → VM
```
1. **Lexer** - Tokenizes the input string into operators, numbers, and identifiers
2. **Parser** - Uses operator precedence climbing to build an Abstract Syntax Tree (AST)
3. **Semantic Analysis** - Resolves symbols and validates function arities
4. **IR Builder** - Converts the AST into stack-based bytecode instructions
5. **Virtual Machine** - Executes the bytecode on a stack-based VM
This architecture allows for:
- Separating parsing from execution
- Compiling expressions once and running them multiple times
- Serializing compiled bytecode to disk for later use
## Usage
### As a Library
Add this to your `Cargo.toml`:
```toml
[dependencies]
expr-solver-lib = "1.0.3"
```
### As a binary
Add this to your `Cargo.toml`:
```toml
[dependencies]
expr-solver-bin = "1.0.3"
```
### Basic Example
```rust
use expr_solver::Eval;
fn main() {
// Quick one-liner evaluation
match Eval::evaluate("2+3*4") {
Ok(result) => println!("Result: {}", result),
Err(e) => eprintln!("Error: {}", e),
}
// Or create an evaluator instance for more control
let mut eval = Eval::new("sqrt(16) + pi");
match eval.run() {
Ok(result) => println!("Result: {}", result),
Err(e) => eprintln!("Error: {}", e),
}
}
```
### Advanced Example
```rust
use expr_solver::{Eval, SymTable};
use rust_decimal_macros::dec;
fn main() {
// Create a custom symbol table
let mut table = SymTable::stdlib();
table.add_const("x", dec!(10)).unwrap();
table.add_func("double", 1, false, |args| Ok(args[0] * dec!(2))).unwrap();
// Evaluate with custom symbols
let mut eval = Eval::with_table("double(x) + sqrt(25)", table);
let result = eval.run().unwrap();
println!("Result: {}", result); // 25
}
```
### Compile and Execute
```rust
use expr_solver::Eval;
use std::path::PathBuf;
fn main() {
// Compile expression to bytecode
let mut eval = Eval::new("2 + 3 * 4");
eval.compile_to_file(&PathBuf::from("expr.bin")).unwrap();
// Load and execute the compiled bytecode
let mut eval = Eval::new_from_file(PathBuf::from("expr.bin"));
let result = eval.run().unwrap();
println!("Result: {}", result); // 14
}
```
### Viewing Assembly
You can inspect the generated bytecode as human-readable assembly:
```rust
use expr_solver::Eval;
fn main() {
let mut eval = Eval::new("2 + 3 * 4");
println!("{}", eval.get_assembly().unwrap());
}
```
Output:
```asm
; VERSION 1.0.2
0000 PUSH 2
0001 PUSH 3
0002 PUSH 4
0003 MUL
0004 ADD
```
The assembly shows the stack-based bytecode instructions that will be executed by the VM.
## Precision and Data Types
All calculations are performed using **128-bit `Decimal`** type from the `rust_decimal` crate, providing exact decimal arithmetic without floating-point errors.
> **Note**: Some trigonometric and hyperbolic functions (`asin`, `acos`, `atan`, `atan2`, `sinh`, `cosh`, `tanh`, `cbrt`, `exp2`, `log2`, `hypot`) internally convert to/from `f64` for computation, which may introduce minor precision differences. All constants (`pi`, `e`, `tau`, `ln2`, `ln10`, `sqrt2`) are computed using native `Decimal` operations for maximum precision.
## Built-in Functions
| **Arithmetic** | | | |
| `abs(x)` | 1 | Absolute value | |
| `sign(x)` | 1 | Sign (-1, 0, or 1) | |
| `floor(x)` | 1 | Round down to integer | |
| `ceil(x)` | 1 | Round up to integer | |
| `round(x)` | 1 | Round to nearest integer | |
| `trunc(x)` | 1 | Truncate to integer | |
| `fract(x)` | 1 | Fractional part | |
| `mod(x, y)` | 2 | Remainder of x/y | |
| `clamp(x, min, max)` | 3 | Constrain value between bounds | |
| **Trigonometry** | | | |
| `sin(x)` | 1 | Sine | |
| `cos(x)` | 1 | Cosine | |
| `tan(x)` | 1 | Tangent | |
| `asin(x)` | 1 | Arcsine | Uses f64 internally |
| `acos(x)` | 1 | Arccosine | Uses f64 internally |
| `atan(x)` | 1 | Arctangent | Uses f64 internally |
| `atan2(y, x)` | 2 | Two-argument arctangent | Uses f64 internally |
| **Hyperbolic** | | | |
| `sinh(x)` | 1 | Hyperbolic sine | Uses f64 internally |
| `cosh(x)` | 1 | Hyperbolic cosine | Uses f64 internally |
| `tanh(x)` | 1 | Hyperbolic tangent | Uses f64 internally |
| **Exponential/Logarithmic** | | | |
| `sqrt(x)` | 1 | Square root | |
| `cbrt(x)` | 1 | Cube root | Uses f64 internally |
| `pow(x, y)` | 2 | x raised to power y | |
| `exp(x)` | 1 | e raised to power x | |
| `exp2(x)` | 1 | 2 raised to power x | Uses f64 internally |
| `log(x)` | 1 | Natural logarithm | |
| `log2(x)` | 1 | Base-2 logarithm | Uses f64 internally |
| `log10(x)` | 1 | Base-10 logarithm | |
| `hypot(x, y)` | 2 | Euclidean distance √(x²+y²) | Uses f64 internally |
| **Variadic** | | | |
| `min(x, ...)` | 1+ | Minimum value | Accepts any number of arguments |
| `max(x, ...)` | 1+ | Maximum value | Accepts any number of arguments |
| `sum(x, ...)` | 1+ | Sum of values | Accepts any number of arguments |
| `avg(x, ...)` | 1+ | Average of values | Accepts any number of arguments |
| **Special** | | | |
| `if(cond, t, f)` | 3 | Conditional: returns t if cond≠0, else f | |
## Built-in Constants
| `pi` | 3.14159... | π (pi) |
| `e` | 2.71828... | Euler's number |
| `tau` | 6.28318... | 2π (tau) |
| `ln2` | 0.69314... | Natural logarithm of 2 |
| `ln10` | 2.30258... | Natural logarithm of 10 |
| `sqrt2` | 1.41421... | Square root of 2 |
> **Note**: All function and constant names are case-insensitive.
## Supported Operators
| `!` | Postfix Unary | Left | 6 | Factorial |
| `^` | Binary | Right | 5 | Exponentiation |
| `-` | Prefix Unary | Right | 4 | Negation |
| `*`, `/` | Binary | Left | 3 | Multiplication, Division |
| `+`, `-` | Binary | Left | 2 | Addition, Subtraction |
| `==`, `!=`, `<`, `<=`, `>`, `>=` | Binary | Left | 1 | Comparisons (return 1 or 0) |
| `()` | Grouping | - | - | Parentheses for grouping |
## Command Line Usage
```bash
# Evaluate an expression
expr-solver "2 + 3 * 4"
# Use the -e flag
expr-solver -e "sin(pi/2)"
# Define custom constants
expr-solver -D x=10 -D y=20 "x + y"
# Compile to binary
expr-solver -e "2+3*4" -o expr.bin
# Execute compiled binary
expr-solver -i expr.bin
# View assembly
expr-solver -e "2+3" -a
# List available functions and constants
expr-solver -t
```
## Testing
Run the test suite:
```bash
# Run all tests
cargo test
```
## License
This project is licensed under the MIT License.