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