1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! Bytecode virtual machine for mathexpr.
//!
//! This module provides the VM that executes compiled bytecode.
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use crate::compiler::{CompiledExpr, Instruction};
use crate::error::EvalError;
impl CompiledExpr {
/// Evaluate the compiled expression with the given variable values.
///
/// # Arguments
///
/// * `vars` - Variable values in the same order as `var_names` passed to `compile`
///
/// # Returns
///
/// * `Ok(f64)` - The result of the evaluation
/// * `Err(EvalError)` - If evaluation fails (e.g., division by zero)
///
/// # Panics
///
/// Panics if the number of variables doesn't match what was compiled.
///
/// # Example
///
/// ```
/// use mathexpr::{parse, CompiledExpr};
///
/// let ast = parse("x + y").unwrap();
/// let compiled = CompiledExpr::compile(&ast, &["x", "y"]).unwrap();
/// let result = compiled.eval(&[3.0, 4.0]).unwrap();
/// assert_eq!(result, 7.0);
/// ```
#[inline]
pub fn eval(&self, vars: &[f64]) -> Result<f64, EvalError> {
self.eval_with_current(0.0, vars)
}
/// Evaluate the compiled expression with a current value and variable values.
///
/// # Arguments
///
/// * `current` - The current/input value (accessible as `_` in the expression)
/// * `vars` - Variable values in the same order as `var_names` passed to `compile`
///
/// # Returns
///
/// * `Ok(f64)` - The result of the evaluation
/// * `Err(EvalError)` - If evaluation fails (e.g., division by zero)
///
/// # Example
///
/// ```
/// use mathexpr::{parse, CompiledExpr};
///
/// let ast = parse("_ * scale").unwrap();
/// let compiled = CompiledExpr::compile(&ast, &["scale"]).unwrap();
/// let result = compiled.eval_with_current(5.0, &[2.0]).unwrap();
/// assert_eq!(result, 10.0);
/// ```
#[inline]
pub fn eval_with_current(&self, current: f64, vars: &[f64]) -> Result<f64, EvalError> {
debug_assert_eq!(
vars.len(),
self.variable_indices.len(),
"Variable count mismatch: expected {}, got {}",
self.variable_indices.len(),
vars.len()
);
let mut stack = Vec::with_capacity(16);
for inst in &self.instructions {
match inst {
Instruction::LoadConst(n) => stack.push(*n),
Instruction::LoadCurrent => stack.push(current),
Instruction::LoadVar(idx) => {
stack.push(vars[*idx]);
}
Instruction::Negate => {
let v = stack.pop().unwrap();
stack.push(-v);
}
Instruction::BinOp(op) => {
let r = stack.pop().unwrap();
let l = stack.pop().unwrap();
stack.push(op.eval(l, r)?);
}
Instruction::Call(func) => {
let arity = func.arity();
let result = match arity {
0 => func.eval(&[])?,
1 => {
let a = stack.pop().unwrap();
func.eval(&[a])?
}
2 => {
let b = stack.pop().unwrap();
let a = stack.pop().unwrap();
func.eval(&[a, b])?
}
3 => {
let c = stack.pop().unwrap();
let b = stack.pop().unwrap();
let a = stack.pop().unwrap();
func.eval(&[a, b, c])?
}
_ => {
// General case for functions with more arguments
let mut args = Vec::with_capacity(arity);
for _ in 0..arity {
args.push(stack.pop().unwrap());
}
args.reverse();
func.eval(&args)?
}
};
stack.push(result);
}
}
}
Ok(stack.pop().unwrap())
}
}