json_eval_rs/rlogic/evaluator/
arithmetic.rs

1use super::{Evaluator, types::*};
2use serde_json::Value;
3use super::super::compiled::CompiledLogic;
4use super::helpers;
5
6impl Evaluator {
7    /// Fast path for simple arithmetic without recursion (using f64)
8    #[inline(always)]
9    pub(super) fn eval_arithmetic_fast(&self, op: ArithOp, items: &[CompiledLogic], user_data: &Value, internal_context: &Value) -> Option<Value> {
10        // Only use fast path for simple cases (all literals or vars)
11        if items.len() > 20 || items.iter().any(|i| !matches!(i, CompiledLogic::Number(_) | CompiledLogic::Var(_, None))) {
12            return None;
13        }
14
15        let mut result = 0.0_f64;
16        for (idx, item) in items.iter().enumerate() {
17            let val = match item {
18                CompiledLogic::Number(n) => n.parse::<f64>().unwrap_or(0.0),
19                CompiledLogic::Var(name, _) => {
20                    // Try internal context first, then user data
21                    let v = helpers::get_var(internal_context, name)
22                        .or_else(|| helpers::get_var(user_data, name));
23                    if let Some(value) = v {
24                        helpers::to_f64(value)
25                    } else {
26                        0.0
27                    }
28                },
29                _ => return None,
30            };
31
32            if idx == 0 {
33                result = val;
34            } else {
35                result = match op {
36                    ArithOp::Add => result + val,
37                    ArithOp::Sub => result - val,
38                    ArithOp::Mul => result * val,
39                    ArithOp::Div => if val != 0.0 { result / val } else { return Some(Value::Null); },
40                };
41            }
42        }
43        Some(self.f64_to_json(result))
44    }
45
46    /// Evaluate binary arithmetic operation on two expressions
47    #[inline]
48    pub(super) fn eval_binary_arith<F>(&self, a: &CompiledLogic, b: &CompiledLogic, f: F, user_data: &Value, internal_context: &Value, depth: usize) -> Result<Value, String>
49    where F: FnOnce(f64, f64) -> Option<f64>
50    {
51        let val_a = self.evaluate_with_context(a, user_data, internal_context, depth + 1)?;
52        let val_b = self.evaluate_with_context(b, user_data, internal_context, depth + 1)?;
53        let num_a = helpers::to_f64(&val_a);
54        let num_b = helpers::to_f64(&val_b);
55        match f(num_a, num_b) {
56            Some(result) => Ok(self.f64_to_json(result)),
57            None => Ok(Value::Null)
58        }
59    }
60
61    /// Evaluate array arithmetic with fold operation
62    #[inline]
63    pub(super) fn eval_array_fold<F>(&self, items: &[CompiledLogic], initial: f64, f: F, user_data: &Value, internal_context: &Value, depth: usize) -> Result<Value, String>
64    where F: Fn(f64, f64) -> Option<f64>
65    {
66        let mut result = initial;
67        for item in items {
68            let val = self.evaluate_with_context(item, user_data, internal_context, depth + 1)?;
69            let num = helpers::to_f64(&val);
70            match f(result, num) {
71                Some(v) => result = v,
72                None => return Ok(Value::Null)
73            }
74        }
75        Ok(self.f64_to_json(result))
76    }
77
78    /// Flatten array values for arithmetic operations
79    pub(super) fn flatten_array_values(&self, items: &[CompiledLogic], user_data: &Value, internal_context: &Value, depth: usize) -> Result<Vec<f64>, String> {
80        let mut values = Vec::new();
81        for item in items {
82            let val = self.evaluate_with_context(item, user_data, internal_context, depth + 1)?;
83            if let Value::Array(arr) = val {
84                for elem in arr {
85                    values.push(helpers::to_f64(&elem));
86                }
87            } else {
88                values.push(helpers::to_f64(&val));
89            }
90        }
91        Ok(values)
92    }
93}