expr-solver-lib 1.0.3

A simple math expression solver library
Documentation
[![Rust](https://github.com/albeva/expr-solver/actions/workflows/rust.yml/badge.svg?branch=main)](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

| Function                    | Arguments | Description                              | Notes                           |
|-----------------------------|-----------|------------------------------------------|---------------------------------|
| **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

| Constant | Value | Description |
|----------|-------|-------------|
| `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

| Operator | Type | Associativity | Precedence | Description |
|----------|------|---------------|------------|-------------|
| `!` | 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.