1use crate::ast::node::Node;
2use crate::Value::{Array, Bool, Bytes, DateTime, Duration, Float, Integer, KeyedMap, Map, Month, String, Weekday};
3use crate::{bail, Result, Rule};
4use crate::{ContextProvider, Environment, Value};
5use log::trace;
6use pest::iterators::Pair;
7use std::str::FromStr;
8
9#[derive(Debug, Clone, strum::EnumString, strum::Display)]
10pub enum Operator {
11 #[strum(serialize = "+")]
12 Add,
13 #[strum(serialize = "-")]
14 Subtract,
15 #[strum(serialize = "*")]
16 Multiply,
17 #[strum(serialize = "/")]
18 Divide,
19 #[strum(serialize = "%")]
20 Modulo,
21 #[strum(serialize = "^")]
22 Pow,
23 #[strum(serialize = "==")]
24 Equal,
25 #[strum(serialize = "!=")]
26 NotEqual,
27 #[strum(serialize = ">")]
28 GreaterThan,
29 #[strum(serialize = ">=")]
30 GreaterThanOrEqual,
31 #[strum(serialize = "<")]
32 LessThan,
33 #[strum(serialize = "<=")]
34 LessThanOrEqual,
35 #[strum(serialize = "&&", serialize = "and")]
36 And,
37 #[strum(serialize = "||", serialize = "or")]
38 Or,
39 #[strum(serialize = "in")]
40 In,
41 #[strum(serialize = "not in")]
42 NotIn,
43 #[strum(serialize = "contains")]
44 Contains,
45 #[strum(serialize = "startsWith")]
46 StartsWith,
47 #[strum(serialize = "endsWith")]
48 EndsWith,
49 #[strum(serialize = "matches")]
50 Matches,
51}
52
53impl From<Pair<'_, Rule>> for Operator {
54 fn from(pair: Pair<Rule>) -> Self {
55 trace!("[operator] {pair:?}");
56 if pair.as_rule() == Rule::not_in_op {
57 return Operator::NotIn;
58 }
59 match pair.as_str() {
60 "**" => Operator::Pow,
61 op => Operator::from_str(op).unwrap_or_else(|_| unreachable!("Invalid operator {op}")),
62 }
63 }
64}
65
66pub(crate) fn values_equal(left: &Value, right: &Value) -> bool {
67 match (left, right) {
68 (Integer(left), Float(right)) => *left as f64 == *right,
69 (Float(left), Integer(right)) => *left == *right as f64,
70 (Array(left), Array(right)) => {
71 left.len() == right.len()
72 && left
73 .iter()
74 .zip(right)
75 .all(|(left, right)| values_equal(left, right))
76 }
77 (Map(left), Map(right)) => {
78 left.len() == right.len()
79 && left.iter().all(|(key, left)| {
80 right
81 .get(key)
82 .is_some_and(|right| values_equal(left, right))
83 })
84 }
85 (KeyedMap(left), KeyedMap(right)) => {
86 left.len() == right.len()
87 && left.iter().all(|(left_key, left_value)| {
88 right.iter().any(|(right_key, right_value)| {
89 map_keys_equal(left_key, right_key)
90 && values_equal(left_value, right_value)
91 })
92 })
93 }
94 _ => left == right,
95 }
96}
97
98pub(crate) fn is_comparable_map_key(value: &Value) -> bool {
99 !matches!(
100 value,
101 Array(_) | Bytes(_) | Map(_) | KeyedMap(_)
102 )
103}
104
105pub(crate) fn map_keys_equal(left: &Value, right: &Value) -> bool {
106 std::mem::discriminant(left) == std::mem::discriminant(right) && left == right
107}
108
109impl Environment<'_> {
110 pub fn eval_operator(
111 &self,
112 ctx: &dyn ContextProvider,
113 operator: &Operator,
114 left: &Node,
115 right: &Node,
116 compiled_regex: Option<®ex::Regex>,
117 ) -> Result<Value> {
118 let left = self.eval_expr(ctx, left)?;
119 match &operator {
120 Operator::And => {
121 return match left {
122 Bool(false) => Ok(Bool(false)),
123 Bool(true) => match self.eval_expr(ctx, right)? {
124 Bool(value) => Ok(Bool(value)),
125 _ => bail!("Invalid operands for operator {operator}"),
126 },
127 _ => bail!("Invalid operands for operator {operator}"),
128 };
129 }
130 Operator::Or => {
131 return match left {
132 Bool(true) => Ok(Bool(true)),
133 Bool(false) => match self.eval_expr(ctx, right)? {
134 Bool(value) => Ok(Bool(value)),
135 _ => bail!("Invalid operands for operator {operator}"),
136 },
137 _ => bail!("Invalid operands for operator {operator}"),
138 };
139 }
140 _ => {}
141 }
142 let right = self.eval_expr(ctx, right)?;
143 let result = match operator {
144 Operator::Add => match (left, right) {
145 (Integer(left), Integer(right)) => left.wrapping_add(right).into(),
146 (Float(left), Float(right)) => (left + right).into(),
147 (Integer(left), Float(right)) => Float(left as f64 + right),
148 (Float(left), Integer(right)) => Float(left + right as f64),
149 (String(left), String(right)) => format!("{left}{right}").into(),
150 (DateTime(left), Duration(right)) => {
151 DateTime(left.checked_add_signed(chrono::Duration::nanoseconds(right))
152 .ok_or_else(|| crate::Error::ExprError("date out of range".into()))?)
153 }
154 (Duration(left), DateTime(right)) => {
155 DateTime(right.checked_add_signed(chrono::Duration::nanoseconds(left))
156 .ok_or_else(|| crate::Error::ExprError("date out of range".into()))?)
157 }
158 (Duration(left), Duration(right)) => Duration(left.wrapping_add(right)),
159 _ => bail!("Invalid operands for operator +"),
160 },
161 Operator::Subtract => match (left, right) {
162 (Integer(left), Integer(right)) => Integer(left.wrapping_sub(right)),
163 (Float(left), Float(right)) => Float(left - right),
164 (Integer(left), Float(right)) => Float(left as f64 - right),
165 (Float(left), Integer(right)) => Float(left - right as f64),
166 (DateTime(left), DateTime(right)) => Duration(
167 (left - right)
168 .num_nanoseconds()
169 .ok_or_else(|| crate::Error::ExprError("duration out of range".into()))?,
170 ),
171 (DateTime(left), Duration(right)) => {
172 DateTime(left.checked_sub_signed(chrono::Duration::nanoseconds(right))
173 .ok_or_else(|| crate::Error::ExprError("date out of range".into()))?)
174 }
175 (Duration(left), Duration(right)) => Duration(left.wrapping_sub(right)),
176 _ => bail!("Invalid operands for operator -"),
177 },
178 Operator::Multiply => match (left, right) {
179 (Integer(left), Integer(right)) => Integer(left.wrapping_mul(right)),
180 (Float(left), Float(right)) => Float(left * right),
181 (Integer(left), Float(right)) => Float(left as f64 * right),
182 (Float(left), Integer(right)) => Float(left * right as f64),
183 (Duration(left), Integer(right)) => Duration(left.wrapping_mul(right)),
184 (Integer(left), Duration(right)) => Duration(left.wrapping_mul(right)),
185 _ => bail!("Invalid operands for operator *"),
186 },
187 Operator::Divide => match (left, right) {
188 (Integer(left), Integer(right)) => Float(left as f64 / right as f64),
189 (Float(left), Float(right)) => Float(left / right),
190 (Integer(left), Float(right)) => Float(left as f64 / right),
191 (Float(left), Integer(right)) => Float(left / right as f64),
192 _ => bail!("Invalid operands for operator /"),
193 },
194 Operator::Modulo => match (left, right) {
195 (Integer(_), Integer(0)) => bail!("integer divide by zero"),
196 (Integer(left), Integer(right)) => Integer(left.wrapping_rem(right)),
197 _ => bail!("Invalid operands for operator %"),
198 },
199 Operator::Pow => match (left, right) {
200 (Integer(left), Integer(right)) => Float((left as f64).powf(right as f64)),
201 (Float(left), Float(right)) => Float(left.powf(right)),
202 (Integer(left), Float(right)) => Float((left as f64).powf(right)),
203 (Float(left), Integer(right)) => Float(left.powf(right as f64)),
204 _ => bail!("Invalid operands for operator {operator}"),
205 },
206 Operator::Equal => match (&left, &right) {
207 (Month(_) | Weekday(_), Integer(_))
208 | (Integer(_), Month(_) | Weekday(_)) => {
209 bail!("Invalid operands for operator {operator}")
210 }
211 (Map(_), KeyedMap(_)) | (KeyedMap(_), Map(_)) => {
212 bail!("Invalid operands for operator {operator}")
213 }
214 _ => Bool(values_equal(&left, &right)),
215 },
216 Operator::NotEqual => match (&left, &right) {
217 (Month(_) | Weekday(_), Integer(_))
218 | (Integer(_), Month(_) | Weekday(_)) => {
219 bail!("Invalid operands for operator {operator}")
220 }
221 (Map(_), KeyedMap(_)) | (KeyedMap(_), Map(_)) => {
222 bail!("Invalid operands for operator {operator}")
223 }
224 _ => Bool(!values_equal(&left, &right)),
225 },
226 Operator::GreaterThan => match (left, right) {
227 (Integer(left), Integer(right)) => (left > right).into(),
228 (Float(left), Float(right)) => (left > right).into(),
229 (Integer(left), Float(right)) => (left as f64 > right).into(),
230 (Float(left), Integer(right)) => (left > right as f64).into(),
231 (String(left), String(right)) => (left > right).into(),
232 (DateTime(left), DateTime(right)) => (left > right).into(),
233 (Duration(left), Duration(right)) => (left > right).into(),
234 _ => bail!("Invalid operands for operator {operator}"),
235 },
236 Operator::GreaterThanOrEqual => match (left, right) {
237 (Integer(left), Integer(right)) => (left >= right).into(),
238 (Float(left), Float(right)) => (left >= right).into(),
239 (Integer(left), Float(right)) => (left as f64 >= right).into(),
240 (Float(left), Integer(right)) => (left >= right as f64).into(),
241 (String(left), String(right)) => (left >= right).into(),
242 (DateTime(left), DateTime(right)) => (left >= right).into(),
243 (Duration(left), Duration(right)) => (left >= right).into(),
244 _ => bail!("Invalid operands for operator {operator}"),
245 },
246 Operator::LessThan => match (left, right) {
247 (Integer(left), Integer(right)) => (left < right).into(),
248 (Float(left), Float(right)) => (left < right).into(),
249 (Integer(left), Float(right)) => ((left as f64) < right).into(),
250 (Float(left), Integer(right)) => (left < right as f64).into(),
251 (String(left), String(right)) => (left < right).into(),
252 (DateTime(left), DateTime(right)) => (left < right).into(),
253 (Duration(left), Duration(right)) => (left < right).into(),
254 _ => bail!("Invalid operands for operator {operator}"),
255 },
256 Operator::LessThanOrEqual => match (left, right) {
257 (Integer(left), Integer(right)) => (left <= right).into(),
258 (Float(left), Float(right)) => (left <= right).into(),
259 (Integer(left), Float(right)) => (left as f64 <= right).into(),
260 (Float(left), Integer(right)) => (left <= right as f64).into(),
261 (String(left), String(right)) => (left <= right).into(),
262 (DateTime(left), DateTime(right)) => (left <= right).into(),
263 (Duration(left), Duration(right)) => (left <= right).into(),
264 _ => bail!("Invalid operands for operator {operator}"),
265 },
266 Operator::And | Operator::Or => unreachable!("handled before evaluating right operand"),
267 Operator::In => match (left, right) {
268 (String(left), Map(right)) => right.contains_key(&left).into(),
269 (left, KeyedMap(right)) => right
270 .iter()
271 .any(|(key, _)| map_keys_equal(&left, key))
272 .into(),
273 (left, Array(right)) => right
274 .iter()
275 .any(|right| values_equal(&left, right))
276 .into(),
277 _ => bail!("Invalid operands for operator {operator}"),
278 },
279 Operator::NotIn => match (left, right) {
280 (String(left), Map(right)) => (!right.contains_key(&left)).into(),
281 (left, KeyedMap(right)) => (!right
282 .iter()
283 .any(|(key, _)| map_keys_equal(&left, key)))
284 .into(),
285 (left, Array(right)) => (!right.iter().any(|right| values_equal(&left, right))).into(),
286 _ => bail!("Invalid operands for operator {operator}"),
287 },
288 Operator::Contains => match (left, right) {
289 (String(left), String(right)) => left.contains(&right).into(),
290 (Array(left), right) => left
291 .iter()
292 .any(|left| values_equal(left, &right))
293 .into(),
294 (Map(left), String(right)) => left.contains_key(&right).into(),
295 _ => bail!("Invalid operands for operator contains"),
296 },
297 Operator::StartsWith => match (left, right) {
298 (String(left), String(right)) => Bool(left.starts_with(&right)),
299 _ => bail!("Invalid operands for operator startsWith"),
300 },
301 Operator::EndsWith => match (left, right) {
302 (String(left), String(right)) => Bool(left.ends_with(&right)),
303 _ => bail!("Invalid operands for operator endsWith"),
304 },
305 Operator::Matches => match (left, right) {
306 (String(left), String(right)) => {
307 if let Some(regex) = compiled_regex {
308 Bool(regex.is_match(&left))
309 } else {
310 Bool(regex::Regex::new(&right)?.is_match(&left))
311 }
312 }
313 _ => bail!("Invalid operands for operator matches"),
314 },
315 };
316
317 Ok(result)
318 }
319}