mathengine_evaluator/
lib.rs

1use mathengine_lexer::Operation;
2use mathengine_parser::{
3    Expression,
4    types::{Number, UnitValue, Value},
5};
6
7pub mod error;
8pub use error::EvalError;
9
10pub fn evaluate(expr: &Expression) -> Result<Value, EvalError> {
11    match expr {
12        Expression::Number(n) => Ok(Value::Number(Number::from(*n))),
13        Expression::UnitValue { value, unit } => {
14            Ok(Value::UnitValue(UnitValue::new(*value, unit.clone())))
15        }
16        Expression::Unit(_unit) => Err(EvalError::InvalidUnitExpression {
17            message: "Cannot evaluate a unit without a value".to_string(),
18        }),
19        Expression::Binary { op, left, right } => match op {
20            Operation::Convert => {
21                let left_val = evaluate(left)?;
22                let (value, from_unit) = match left_val {
23                    Value::UnitValue(uv) => (uv.value(), uv.unit().to_string()),
24                    _ => {
25                        return Err(EvalError::InvalidUnitExpression {
26                            message: "Left side of conversion must be a unit value".to_string(),
27                        });
28                    }
29                };
30
31                let to_unit = match right.as_ref() {
32                    Expression::Unit(u) => u,
33                    _ => {
34                        return Err(EvalError::InvalidUnitExpression {
35                            message: "Right side of conversion must be a unit".to_string(),
36                        });
37                    }
38                };
39
40                // Use the new UnitValue conversion method
41                let unit_value = UnitValue::new(value, from_unit.clone());
42                let converted = unit_value.convert_to(to_unit)?;
43
44                Ok(Value::UnitValue(converted))
45            }
46            _ => {
47                let left_val = evaluate(left)?;
48                let right_val = evaluate(right)?;
49
50                // Check for division by zero before delegating to operators
51                if let Operation::Divide = op {
52                    match &right_val {
53                        Value::Number(n) if n.0 == 0.0 => return Err(EvalError::DivisionByZero),
54                        _ => {}
55                    }
56                }
57
58                let result = match op {
59                    Operation::Add => left_val + right_val,
60                    Operation::Subtract => left_val - right_val,
61                    Operation::Multiply => left_val * right_val,
62                    Operation::Divide => left_val / right_val,
63                    Operation::Power => {
64                        // Power is not implemented via operators yet, handle specially
65                        match (left_val, right_val) {
66                            (Value::Number(l), Value::Number(r)) => {
67                                Value::Number(Number::from(l.0.powf(r.0)))
68                            }
69                            _ => {
70                                return Err(EvalError::UnsupportedOperation {
71                                    operation: "power".to_string(),
72                                    operand_type: "non-numeric values".to_string(),
73                                });
74                            }
75                        }
76                    }
77                    Operation::Convert => {
78                        return Err(EvalError::UnsupportedOperation {
79                            operation: "convert".to_string(),
80                            operand_type: "binary operation".to_string(),
81                        });
82                    }
83                };
84
85                Ok(result)
86            }
87        },
88        Expression::Unary { op, operand } => {
89            let val = evaluate(operand)?;
90            match op {
91                Operation::Subtract => match val {
92                    Value::Number(n) => Ok(Value::Number(-n)),
93                    Value::UnitValue(_) => Err(EvalError::UnsupportedOperation {
94                        operation: "negate".to_string(),
95                        operand_type: "unit value".to_string(),
96                    }),
97                },
98                _ => Err(EvalError::UnsupportedOperation {
99                    operation: format!("{:?}", op),
100                    operand_type: "unary operand".to_string(),
101                }),
102            }
103        }
104    }
105}
106