rust_rule_engine/
expression.rs1use crate::types::Value;
7use crate::errors::{Result, RuleEngineError};
8use crate::engine::facts::Facts;
9
10pub fn evaluate_expression(expr: &str, facts: &Facts) -> Result<Value> {
14 let expr = expr.trim();
15
16 if let Some(pos) = find_operator(expr, &['+', '-']) {
24 let left = &expr[..pos].trim();
25 let op = &expr[pos..pos+1];
26 let right = &expr[pos+1..].trim();
27
28 let left_val = evaluate_expression(left, facts)?;
29 let right_val = evaluate_expression(right, facts)?;
30
31 return apply_operator(&left_val, op, &right_val);
32 }
33
34 if let Some(pos) = find_operator(expr, &['*', '/', '%']) {
36 let left = &expr[..pos].trim();
37 let op = &expr[pos..pos+1];
38 let right = &expr[pos+1..].trim();
39
40 let left_val = evaluate_expression(left, facts)?;
41 let right_val = evaluate_expression(right, facts)?;
42
43 return apply_operator(&left_val, op, &right_val);
44 }
45
46 if let Ok(int_val) = expr.parse::<i64>() {
51 return Ok(Value::Integer(int_val));
52 }
53
54 if let Ok(float_val) = expr.parse::<f64>() {
55 return Ok(Value::Number(float_val));
56 }
57
58 if let Some(value) = facts.get(expr) {
60 return Ok(value.clone());
61 }
62
63 Err(RuleEngineError::EvaluationError {
65 message: format!("Field '{}' not found in facts", expr),
66 })
67}
68
69fn find_operator(expr: &str, operators: &[char]) -> Option<usize> {
72 let mut paren_depth = 0;
73 let mut last_pos = None;
74
75 for (i, ch) in expr.chars().enumerate() {
76 match ch {
77 '(' => paren_depth += 1,
78 ')' => paren_depth -= 1,
79 _ if paren_depth == 0 && operators.contains(&ch) => {
80 last_pos = Some(i);
81 }
82 _ => {}
83 }
84 }
85
86 last_pos
87}
88
89fn apply_operator(left: &Value, op: &str, right: &Value) -> Result<Value> {
91 let left_num = value_to_number(left)?;
93 let right_num = value_to_number(right)?;
94
95 let result = match op {
96 "+" => left_num + right_num,
97 "-" => left_num - right_num,
98 "*" => left_num * right_num,
99 "/" => {
100 if right_num == 0.0 {
101 return Err(RuleEngineError::EvaluationError {
102 message: "Division by zero".to_string(),
103 });
104 }
105 left_num / right_num
106 }
107 "%" => left_num % right_num,
108 _ => {
109 return Err(RuleEngineError::EvaluationError {
110 message: format!("Unknown operator: {}", op),
111 });
112 }
113 };
114
115 if is_integer_value(left) && is_integer_value(right) && result.fract() == 0.0 {
117 Ok(Value::Integer(result as i64))
118 } else {
119 Ok(Value::Number(result))
120 }
121}
122
123fn value_to_number(value: &Value) -> Result<f64> {
125 match value {
126 Value::Integer(i) => Ok(*i as f64),
127 Value::Number(n) => Ok(*n),
128 Value::String(s) => {
129 s.parse::<f64>().map_err(|_| RuleEngineError::EvaluationError {
130 message: format!("Cannot convert '{}' to number", s),
131 })
132 }
133 _ => Err(RuleEngineError::EvaluationError {
134 message: format!("Cannot convert {:?} to number", value),
135 }),
136 }
137}
138
139fn is_integer_value(value: &Value) -> bool {
141 matches!(value, Value::Integer(_))
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn test_simple_arithmetic() {
150 let facts = Facts::new();
151
152 assert_eq!(
153 evaluate_expression("10 + 20", &facts).unwrap(),
154 Value::Integer(30)
155 );
156
157 assert_eq!(
158 evaluate_expression("100 - 25", &facts).unwrap(),
159 Value::Integer(75)
160 );
161
162 assert_eq!(
163 evaluate_expression("5 * 6", &facts).unwrap(),
164 Value::Integer(30)
165 );
166
167 assert_eq!(
168 evaluate_expression("100 / 4", &facts).unwrap(),
169 Value::Integer(25)
170 );
171 }
172
173 #[test]
174 fn test_field_references() {
175 let mut facts = Facts::new();
176 facts.set("Order.quantity", Value::Integer(10));
177 facts.set("Order.price", Value::Integer(100));
178
179 assert_eq!(
180 evaluate_expression("Order.quantity * Order.price", &facts).unwrap(),
181 Value::Integer(1000)
182 );
183 }
184
185 #[test]
186 fn test_mixed_operations() {
187 let mut facts = Facts::new();
188 facts.set("a", Value::Integer(10));
189 facts.set("b", Value::Integer(5));
190 facts.set("c", Value::Integer(2));
191
192 assert_eq!(
194 evaluate_expression("a + b * c", &facts).unwrap(),
195 Value::Integer(20)
196 );
197 }
198}