darklua_core/process/evaluator/
mod.rs

1mod lua_value;
2
3pub use lua_value::*;
4
5use crate::nodes::*;
6
7/// A struct to convert an Expression node into a LuaValue object.
8#[derive(Debug, Clone, Default, PartialEq, Eq)]
9pub struct Evaluator {
10    pure_metamethods: bool,
11}
12
13impl Evaluator {
14    /// When evaluating expressions related to tables, this value tells the evaluator if
15    /// metamethods can have side effects. For example, indexing a normal table in Lua does not
16    /// have any side effects, but if the table is a metatable, it's `__index` metamethod can
17    /// possibly have side effects (since it can be a function call).
18    pub fn assume_pure_metamethods(mut self) -> Self {
19        self.pure_metamethods = true;
20        self
21    }
22
23    pub fn evaluate(&self, expression: &Expression) -> LuaValue {
24        match expression {
25            Expression::False(_) => LuaValue::False,
26            Expression::Function(_) => LuaValue::Function,
27            Expression::Nil(_) => LuaValue::Nil,
28            Expression::Number(number) => LuaValue::from(number.compute_value()),
29            Expression::String(string) => LuaValue::from(string.get_value()),
30            Expression::Table(_) => LuaValue::Table,
31            Expression::True(_) => LuaValue::True,
32            Expression::Binary(binary) => self.evaluate_binary(binary),
33            Expression::Unary(unary) => self.evaluate_unary(unary),
34            Expression::Parenthese(parenthese) => {
35                // when the evaluator will be able to manage tuples, keep only the first element
36                // of the tuple here (or coerce the tuple to `nil` if it is empty)
37                self.evaluate(parenthese.inner_expression())
38            }
39            Expression::If(if_expression) => self.evaluate_if(if_expression),
40            Expression::InterpolatedString(interpolated_string) => {
41                let mut result = String::new();
42                for segment in interpolated_string.iter_segments() {
43                    match segment {
44                        InterpolationSegment::String(string) => {
45                            result.push_str(string.get_value());
46                        }
47                        InterpolationSegment::Value(value) => {
48                            match self.evaluate(value.get_expression()) {
49                                LuaValue::False => {
50                                    result.push_str("false");
51                                }
52                                LuaValue::True => {
53                                    result.push_str("true");
54                                }
55                                LuaValue::Nil => {
56                                    result.push_str("nil");
57                                }
58                                LuaValue::String(string) => {
59                                    result.push_str(&string);
60                                }
61                                LuaValue::Function
62                                | LuaValue::Number(_)
63                                | LuaValue::Table
64                                | LuaValue::Unknown => return LuaValue::Unknown,
65                            }
66                        }
67                    }
68                }
69                LuaValue::String(result)
70            }
71            Expression::TypeCast(type_cast) => self.evaluate(type_cast.get_expression()),
72            Expression::Call(_)
73            | Expression::Field(_)
74            | Expression::Identifier(_)
75            | Expression::Index(_)
76            | Expression::VariableArguments(_) => LuaValue::Unknown,
77        }
78    }
79
80    #[allow(clippy::only_used_in_recursion)]
81    pub fn can_return_multiple_values(&self, expression: &Expression) -> bool {
82        match expression {
83            Expression::Call(_)
84            | Expression::Field(_)
85            | Expression::Index(_)
86            | Expression::Unary(_)
87            | Expression::VariableArguments(_) => true,
88            Expression::Binary(binary) => {
89                !matches!(binary.operator(), BinaryOperator::And | BinaryOperator::Or)
90            }
91            Expression::False(_)
92            | Expression::Function(_)
93            | Expression::Identifier(_)
94            | Expression::If(_)
95            | Expression::Nil(_)
96            | Expression::Number(_)
97            | Expression::Parenthese(_)
98            | Expression::String(_)
99            | Expression::InterpolatedString(_)
100            | Expression::Table(_)
101            | Expression::True(_) => false,
102            Expression::TypeCast(type_cast) => {
103                self.can_return_multiple_values(type_cast.get_expression())
104            }
105        }
106    }
107
108    pub fn has_side_effects(&self, expression: &Expression) -> bool {
109        match expression {
110            Expression::False(_)
111            | Expression::Function(_)
112            | Expression::Identifier(_)
113            | Expression::Nil(_)
114            | Expression::Number(_)
115            | Expression::String(_)
116            | Expression::True(_)
117            | Expression::VariableArguments(_) => false,
118            Expression::If(if_expression) => self.if_expression_has_side_effects(if_expression),
119            Expression::Binary(binary) => {
120                let left = binary.left();
121                let right = binary.right();
122
123                let left_value = self.evaluate(left);
124                let left_side_effect = self.has_side_effects(binary.left());
125
126                match binary.operator() {
127                    BinaryOperator::And => {
128                        if left_value.is_truthy().unwrap_or(true) {
129                            left_side_effect || self.has_side_effects(binary.right())
130                        } else {
131                            left_side_effect
132                        }
133                    }
134                    BinaryOperator::Or => {
135                        if left_value.is_truthy().unwrap_or(false) {
136                            left_side_effect
137                        } else {
138                            left_side_effect || self.has_side_effects(binary.right())
139                        }
140                    }
141                    _ => {
142                        if self.pure_metamethods {
143                            left_side_effect || self.has_side_effects(binary.right())
144                        } else {
145                            self.maybe_metatable(&left_value)
146                                || self.maybe_metatable(&self.evaluate(right))
147                                || self.has_side_effects(left)
148                                || self.has_side_effects(right)
149                        }
150                    }
151                }
152            }
153            Expression::Unary(unary) => {
154                if self.pure_metamethods || matches!(unary.operator(), UnaryOperator::Not) {
155                    self.has_side_effects(unary.get_expression())
156                } else {
157                    let sub_expression = unary.get_expression();
158
159                    self.maybe_metatable(&self.evaluate(sub_expression))
160                        || self.has_side_effects(sub_expression)
161                }
162            }
163            Expression::Field(field) => self.field_has_side_effects(field),
164            Expression::Index(index) => self.index_has_side_effects(index),
165            Expression::Parenthese(parenthese) => {
166                self.has_side_effects(parenthese.inner_expression())
167            }
168            Expression::Table(table) => table
169                .get_entries()
170                .iter()
171                .any(|entry| self.table_entry_has_side_effects(entry)),
172            Expression::Call(call) => self.call_has_side_effects(call),
173            Expression::InterpolatedString(interpolated_string) => interpolated_string
174                .iter_segments()
175                .any(|segment| match segment {
176                    InterpolationSegment::String(_) => false,
177                    InterpolationSegment::Value(value) => {
178                        self.has_side_effects(value.get_expression())
179                    }
180                }),
181            Expression::TypeCast(type_cast) => self.has_side_effects(type_cast.get_expression()),
182        }
183    }
184
185    fn if_expression_has_side_effects(&self, if_expression: &IfExpression) -> bool {
186        if self.has_side_effects(if_expression.get_condition()) {
187            return true;
188        }
189
190        let condition = self.evaluate(if_expression.get_condition());
191
192        if let Some(truthy) = condition.is_truthy() {
193            if truthy {
194                self.has_side_effects(if_expression.get_result())
195            } else {
196                for branch in if_expression.iter_branches() {
197                    if self.has_side_effects(branch.get_condition()) {
198                        return true;
199                    }
200
201                    let branch_condition = self.evaluate(branch.get_condition());
202
203                    if let Some(truthy) = branch_condition.is_truthy() {
204                        if truthy {
205                            return self.has_side_effects(branch.get_result());
206                        }
207                    } else if self.has_side_effects(branch.get_result()) {
208                        return true;
209                    }
210                }
211
212                self.has_side_effects(if_expression.get_else_result())
213            }
214        } else {
215            if self.has_side_effects(if_expression.get_result()) {
216                return true;
217            }
218
219            for branch in if_expression.iter_branches() {
220                if self.has_side_effects(branch.get_condition())
221                    || self.has_side_effects(branch.get_result())
222                {
223                    return true;
224                }
225            }
226
227            self.has_side_effects(if_expression.get_else_result())
228        }
229    }
230
231    #[inline]
232    fn call_has_side_effects(&self, _call: &FunctionCall) -> bool {
233        true
234    }
235
236    #[inline]
237    fn table_entry_has_side_effects(&self, entry: &TableEntry) -> bool {
238        match entry {
239            TableEntry::Field(entry) => self.has_side_effects(entry.get_value()),
240            TableEntry::Index(entry) => {
241                self.has_side_effects(entry.get_key()) || self.has_side_effects(entry.get_value())
242            }
243            TableEntry::Value(value) => self.has_side_effects(value),
244        }
245    }
246
247    #[inline]
248    fn field_has_side_effects(&self, field: &FieldExpression) -> bool {
249        !self.pure_metamethods || self.prefix_has_side_effects(field.get_prefix())
250    }
251
252    #[inline]
253    fn index_has_side_effects(&self, index: &IndexExpression) -> bool {
254        !self.pure_metamethods
255            || self.has_side_effects(index.get_index())
256            || self.prefix_has_side_effects(index.get_prefix())
257    }
258
259    fn prefix_has_side_effects(&self, prefix: &Prefix) -> bool {
260        match prefix {
261            Prefix::Call(call) => self.call_has_side_effects(call),
262            Prefix::Field(field) => self.field_has_side_effects(field),
263            Prefix::Identifier(_) => false,
264            Prefix::Index(index) => self.index_has_side_effects(index),
265            Prefix::Parenthese(sub_expression) => {
266                self.has_side_effects(sub_expression.inner_expression())
267            }
268        }
269    }
270
271    #[inline]
272    fn maybe_metatable(&self, value: &LuaValue) -> bool {
273        match value {
274            LuaValue::False
275            | LuaValue::Function
276            | LuaValue::Nil
277            | LuaValue::Number(_)
278            | LuaValue::String(_)
279            | LuaValue::Table
280            | LuaValue::True => false,
281            LuaValue::Unknown => true,
282        }
283    }
284
285    fn evaluate_binary(&self, expression: &BinaryExpression) -> LuaValue {
286        match expression.operator() {
287            BinaryOperator::And => self
288                .evaluate(expression.left())
289                .map_if_truthy(|_| self.evaluate(expression.right())),
290            BinaryOperator::Or => self
291                .evaluate(expression.left())
292                .map_if_truthy_else(|left| left, || self.evaluate(expression.right())),
293            BinaryOperator::Equal => self.evaluate_equal(
294                &self.evaluate(expression.left()),
295                &self.evaluate(expression.right()),
296            ),
297            BinaryOperator::NotEqual => {
298                let result = self.evaluate_equal(
299                    &self.evaluate(expression.left()),
300                    &self.evaluate(expression.right()),
301                );
302
303                match result {
304                    LuaValue::True => LuaValue::False,
305                    LuaValue::False => LuaValue::True,
306                    _ => LuaValue::Unknown,
307                }
308            }
309            BinaryOperator::Plus => self.evaluate_math(expression, |a, b| a + b),
310            BinaryOperator::Minus => self.evaluate_math(expression, |a, b| a - b),
311            BinaryOperator::Asterisk => self.evaluate_math(expression, |a, b| a * b),
312            BinaryOperator::Slash => self.evaluate_math(expression, |a, b| a / b),
313            BinaryOperator::DoubleSlash => self.evaluate_math(expression, |a, b| (a / b).floor()),
314            BinaryOperator::Caret => self.evaluate_math(expression, |a, b| a.powf(b)),
315            BinaryOperator::Percent => {
316                self.evaluate_math(expression, |a, b| a - b * (a / b).floor())
317            }
318            BinaryOperator::Concat => {
319                match (
320                    self.evaluate(expression.left()).string_coercion(),
321                    self.evaluate(expression.right()).string_coercion(),
322                ) {
323                    (LuaValue::String(mut left), LuaValue::String(right)) => {
324                        left.push_str(&right);
325                        LuaValue::String(left)
326                    }
327                    _ => LuaValue::Unknown,
328                }
329            }
330            BinaryOperator::LowerThan => self.evaluate_relational(expression, |a, b| a < b),
331            BinaryOperator::LowerOrEqualThan => self.evaluate_relational(expression, |a, b| a <= b),
332            BinaryOperator::GreaterThan => self.evaluate_relational(expression, |a, b| a > b),
333            BinaryOperator::GreaterOrEqualThan => {
334                self.evaluate_relational(expression, |a, b| a >= b)
335            }
336        }
337    }
338
339    fn evaluate_equal(&self, left: &LuaValue, right: &LuaValue) -> LuaValue {
340        match (left, right) {
341            (LuaValue::Unknown, _) | (_, LuaValue::Unknown) => LuaValue::Unknown,
342            (LuaValue::True, LuaValue::True)
343            | (LuaValue::False, LuaValue::False)
344            | (LuaValue::Nil, LuaValue::Nil) => LuaValue::True,
345            (LuaValue::Number(a), LuaValue::Number(b)) => {
346                LuaValue::from((a - b).abs() < f64::EPSILON)
347            }
348            (LuaValue::String(a), LuaValue::String(b)) => LuaValue::from(a == b),
349            _ => LuaValue::False,
350        }
351    }
352
353    fn evaluate_math<F>(&self, expression: &BinaryExpression, operation: F) -> LuaValue
354    where
355        F: Fn(f64, f64) -> f64,
356    {
357        let left = self.evaluate(expression.left()).number_coercion();
358
359        if let LuaValue::Number(left) = left {
360            let right = self.evaluate(expression.right()).number_coercion();
361
362            if let LuaValue::Number(right) = right {
363                LuaValue::Number(operation(left, right))
364            } else {
365                LuaValue::Unknown
366            }
367        } else {
368            LuaValue::Unknown
369        }
370    }
371
372    fn evaluate_relational<F>(&self, expression: &BinaryExpression, operation: F) -> LuaValue
373    where
374        F: Fn(f64, f64) -> bool,
375    {
376        let left = self.evaluate(expression.left());
377
378        match left {
379            LuaValue::Number(left) => {
380                let right = self.evaluate(expression.right());
381
382                if let LuaValue::Number(right) = right {
383                    if operation(left, right) {
384                        LuaValue::True
385                    } else {
386                        LuaValue::False
387                    }
388                } else {
389                    LuaValue::Unknown
390                }
391            }
392            LuaValue::String(left) => {
393                let right = self.evaluate(expression.right());
394
395                if let LuaValue::String(right) = right {
396                    self.compare_strings(&left, &right, expression.operator())
397                } else {
398                    LuaValue::Unknown
399                }
400            }
401            _ => LuaValue::Unknown,
402        }
403    }
404
405    fn compare_strings(&self, left: &str, right: &str, operator: BinaryOperator) -> LuaValue {
406        LuaValue::from(match operator {
407            BinaryOperator::Equal => left == right,
408            BinaryOperator::NotEqual => left != right,
409            BinaryOperator::LowerThan => left < right,
410            BinaryOperator::LowerOrEqualThan => left <= right,
411            BinaryOperator::GreaterThan => left > right,
412            BinaryOperator::GreaterOrEqualThan => left >= right,
413            _ => return LuaValue::Unknown,
414        })
415    }
416
417    fn evaluate_unary(&self, expression: &UnaryExpression) -> LuaValue {
418        match expression.operator() {
419            UnaryOperator::Not => self
420                .evaluate(expression.get_expression())
421                .is_truthy()
422                .map(|value| LuaValue::from(!value))
423                .unwrap_or(LuaValue::Unknown),
424            UnaryOperator::Minus => {
425                match self.evaluate(expression.get_expression()).number_coercion() {
426                    LuaValue::Number(value) => LuaValue::from(-value),
427                    _ => LuaValue::Unknown,
428                }
429            }
430            _ => LuaValue::Unknown,
431        }
432    }
433
434    fn evaluate_if(&self, expression: &IfExpression) -> LuaValue {
435        let condition = self.evaluate(expression.get_condition());
436
437        if let Some(truthy) = condition.is_truthy() {
438            if truthy {
439                self.evaluate(expression.get_result())
440            } else {
441                for branch in expression.iter_branches() {
442                    let branch_condition = self.evaluate(branch.get_condition());
443                    if let Some(truthy) = branch_condition.is_truthy() {
444                        if truthy {
445                            return self.evaluate(branch.get_result());
446                        }
447                    } else {
448                        return LuaValue::Unknown;
449                    }
450                }
451
452                self.evaluate(expression.get_else_result())
453            }
454        } else {
455            LuaValue::Unknown
456        }
457    }
458}
459
460#[cfg(test)]
461mod test {
462    use super::*;
463
464    macro_rules! evaluate_expressions {
465        ($($name:ident ($expression:expr) => $value:expr),* $(,)?) => {
466            $(
467                #[test]
468                fn $name() {
469                    assert_eq!($value, Evaluator::default().evaluate(&$expression.into()));
470                }
471            )*
472        };
473    }
474
475    evaluate_expressions!(
476        true_expression(Expression::from(true)) => LuaValue::True,
477        false_expression(Expression::from(false)) => LuaValue::False,
478        nil_expression(Expression::nil()) => LuaValue::Nil,
479        number_expression(DecimalNumber::new(0.0)) => LuaValue::Number(0.0),
480        number_expression_negative_zero(DecimalNumber::new(-0.0)) => LuaValue::Number(-0.0),
481        string_expression(StringExpression::from_value("foo")) => LuaValue::String("foo".to_owned()),
482        empty_interpolated_string_expression(InterpolatedStringExpression::empty()) => LuaValue::String("".to_owned()),
483        interpolated_string_expression_with_one_string(InterpolatedStringExpression::empty().with_segment("hello"))
484            => LuaValue::String("hello".to_owned()),
485        interpolated_string_expression_with_multiple_string_segments(
486            InterpolatedStringExpression::empty()
487                .with_segment("hello")
488                .with_segment("-")
489                .with_segment("bye")
490        ) => LuaValue::String("hello-bye".to_owned()),
491        interpolated_string_expression_with_true_segment(
492            InterpolatedStringExpression::empty().with_segment(Expression::from(true))
493        ) => LuaValue::String("true".to_owned()),
494        interpolated_string_expression_with_false_segment(
495            InterpolatedStringExpression::empty().with_segment(Expression::from(false))
496        ) => LuaValue::String("false".to_owned()),
497        interpolated_string_expression_with_nil_segment(
498            InterpolatedStringExpression::empty().with_segment(Expression::nil())
499        ) => LuaValue::String("nil".to_owned()),
500        interpolated_string_expression_with_mixed_segments(
501            InterpolatedStringExpression::empty()
502                .with_segment("variable = ")
503                .with_segment(Expression::from(true))
504                .with_segment("?")
505        ) => LuaValue::String("variable = true?".to_owned()),
506        interpolated_string_expression_with_mixed_segments_unknown(
507            InterpolatedStringExpression::empty()
508                .with_segment("variable = ")
509                .with_segment(Expression::identifier("test"))
510                .with_segment("!")
511        ) => LuaValue::Unknown,
512        true_wrapped_in_parens(ParentheseExpression::new(true)) => LuaValue::True,
513        false_wrapped_in_parens(ParentheseExpression::new(false)) => LuaValue::False,
514        nil_wrapped_in_parens(ParentheseExpression::new(Expression::nil())) => LuaValue::Nil,
515        number_wrapped_in_parens(ParentheseExpression::new(DecimalNumber::new(0.0)))
516            => LuaValue::Number(0.0),
517        string_wrapped_in_parens(ParentheseExpression::new(StringExpression::from_value("foo")))
518            => LuaValue::from("foo"),
519        table_expression(TableExpression::default()) => LuaValue::Table,
520        if_expression_always_true(IfExpression::new(true, 1.0, 0.0)) => LuaValue::from(1.0),
521        if_expression_always_false(IfExpression::new(false, 1.0, 0.0)) => LuaValue::from(0.0),
522        if_expression_unknown_condition(IfExpression::new(Expression::identifier("test"), 1.0, 0.0))
523            => LuaValue::Unknown,
524        if_expression_elseif_always_true(IfExpression::new(false, 1.0, 0.0).with_branch(true, 2.0))
525            => LuaValue::from(2.0),
526        if_expression_elseif_always_false(IfExpression::new(false, 1.0, 0.0).with_branch(false, 2.0))
527            => LuaValue::from(0.0),
528    );
529
530    mod binary_expressions {
531        use super::*;
532
533        macro_rules! evaluate_binary_expressions {
534            ($($name:ident ($operator:expr, $left:expr, $right:expr) => $expect:expr),* $(,)?) => {
535                $(
536                    #[test]
537                    fn $name() {
538                        let binary = BinaryExpression::new($operator, $left, $right);
539
540                        let result = Evaluator::default().evaluate(&binary.into());
541
542                        match (&$expect, &result) {
543                            (LuaValue::Number(expect_float), LuaValue::Number(result))=> {
544                                if expect_float.is_nan() {
545                                    assert!(result.is_nan(), "{} should be NaN", result);
546                                } else if expect_float.is_infinite() {
547                                    assert!(result.is_infinite(), "{} should be infinite", result);
548                                    assert_eq!(expect_float.is_sign_positive(), result.is_sign_positive());
549                                } else {
550                                    assert!(
551                                        (expect_float - result).abs() < f64::EPSILON,
552                                        "{} does not approximate {}", result, expect_float
553                                    );
554                                    assert!(
555                                        expect_float.is_sign_positive() == result.is_sign_positive(),
556                                        "{} should be of the same sign as {}", result, expect_float
557                                    );
558                                }
559                            }
560                            _ => {
561                                assert_eq!($expect, result);
562                            }
563                        }
564                    }
565                )*
566            };
567        }
568
569        evaluate_binary_expressions!(
570            true_and_number(
571                BinaryOperator::And,
572                true,
573                Expression::Number(DecimalNumber::new(0.0).into())
574            ) => LuaValue::Number(0.0),
575            true_and_true(
576                BinaryOperator::And,
577                true,
578                true
579            ) => LuaValue::True,
580            true_and_false(
581                BinaryOperator::And,
582                true,
583                false
584            ) => LuaValue::False,
585            true_and_nil(
586                BinaryOperator::And,
587                true,
588                Expression::nil()
589            ) => LuaValue::Nil,
590            true_and_string(
591                BinaryOperator::And,
592                true,
593                Expression::String(StringExpression::from_value("foo"))
594            ) => LuaValue::String("foo".to_owned()),
595            true_and_table(
596                BinaryOperator::And,
597                true,
598                TableExpression::default()
599            ) => LuaValue::Table,
600            nil_and_true(
601                BinaryOperator::And,
602                Expression::nil(),
603                true
604            ) => LuaValue::Nil,
605            false_and_true(
606                BinaryOperator::And,
607                false,
608                true
609            ) => LuaValue::False,
610            true_or_number(
611                BinaryOperator::Or,
612                true,
613                Expression::Number(DecimalNumber::new(0.0).into())
614            ) => LuaValue::True,
615            true_or_true(
616                BinaryOperator::Or,
617                true,
618                true
619            ) => LuaValue::True,
620            true_or_false(
621                BinaryOperator::Or,
622                true,
623                false
624            ) => LuaValue::True,
625            true_or_nil(
626                BinaryOperator::Or,
627                true,
628                Expression::nil()
629            ) => LuaValue::True,
630            true_or_string(
631                BinaryOperator::Or,
632                true,
633                Expression::String(StringExpression::from_value("foo"))
634            ) => LuaValue::True,
635            nil_or_true(
636                BinaryOperator::Or,
637                Expression::nil(),
638                true
639            ) => LuaValue::True,
640            nil_or_false(
641                BinaryOperator::Or,
642                Expression::nil(),
643                false
644            ) => LuaValue::False,
645            nil_or_nil(
646                BinaryOperator::Or,
647                Expression::nil(),
648                Expression::nil()
649            ) => LuaValue::Nil,
650            one_plus_two(
651                BinaryOperator::Plus,
652                Expression::from(1.0),
653                Expression::from(2.0)
654            ) => LuaValue::Number(3.0),
655            one_minus_two(
656                BinaryOperator::Minus,
657                Expression::from(1.0),
658                Expression::from(2.0)
659            ) => LuaValue::Number(-1.0),
660            three_times_four(
661                BinaryOperator::Asterisk,
662                Expression::from(3.0),
663                Expression::from(4.0)
664            ) => LuaValue::Number(12.0),
665            twelve_divided_by_four(
666                BinaryOperator::Slash,
667                Expression::from(12.0),
668                Expression::from(4.0)
669            ) => LuaValue::Number(3.0),
670            one_divided_by_zero(
671                BinaryOperator::Slash,
672                Expression::from(1.0),
673                Expression::from(0.0)
674            ) => LuaValue::Number(f64::INFINITY),
675            negative_zero_plus_negative_zero(
676                BinaryOperator::Plus,
677                Expression::from(-0.0),
678                Expression::from(-0.0)
679            ) => LuaValue::Number(-0.0),
680            negative_zero_minus_zero(
681                BinaryOperator::Minus,
682                Expression::from(-0.0),
683                Expression::from(0.0)
684            ) => LuaValue::Number(-0.0),
685            zero_divided_by_zero(
686                BinaryOperator::Slash,
687                Expression::from(0.0),
688                Expression::from(0.0)
689            ) => LuaValue::Number(f64::NAN),
690            twelve_floor_division_by_four(
691                BinaryOperator::DoubleSlash,
692                Expression::from(12.0),
693                Expression::from(4.0)
694            ) => LuaValue::Number(3.0),
695            eleven_floor_division_by_three(
696                BinaryOperator::DoubleSlash,
697                Expression::from(11.0),
698                Expression::from(3.0)
699            ) => LuaValue::Number(3.0),
700            one_floor_division_by_zero(
701                BinaryOperator::DoubleSlash,
702                Expression::from(1.0),
703                Expression::from(0.0)
704            ) => LuaValue::Number(f64::INFINITY),
705            minus_one_floor_division_by_zero(
706                BinaryOperator::DoubleSlash,
707                Expression::from(-1.0),
708                Expression::from(0.0)
709            ) => LuaValue::Number(f64::NEG_INFINITY),
710            zero_floor_division_by_zero(
711                BinaryOperator::DoubleSlash,
712                Expression::from(0.0),
713                Expression::from(0.0)
714            ) => LuaValue::Number(f64::NAN),
715            five_mod_two(
716                BinaryOperator::Percent,
717                Expression::from(5.0),
718                Expression::from(2.0)
719            ) => LuaValue::Number(1.0),
720            minus_five_mod_two(
721                BinaryOperator::Percent,
722                Expression::from(-5.0),
723                Expression::from(2.0)
724            ) => LuaValue::Number(1.0),
725            minus_five_mod_minus_two(
726                BinaryOperator::Percent,
727                Expression::from(-5.0),
728                Expression::from(-2.0)
729            ) => LuaValue::Number(-1.0),
730            five_point_two_mod_two(
731                BinaryOperator::Percent,
732                Expression::from(5.5),
733                Expression::from(2.0)
734            ) => LuaValue::Number(1.5),
735            five_pow_two(
736                BinaryOperator::Caret,
737                Expression::from(5.0),
738                Expression::from(2.0)
739            ) => LuaValue::Number(25.0),
740            string_number_plus_string_number(
741                BinaryOperator::Plus,
742                StringExpression::from_value("2"),
743                StringExpression::from_value("3")
744            ) => LuaValue::Number(5.0),
745            concat_strings(
746                BinaryOperator::Concat,
747                StringExpression::from_value("2"),
748                StringExpression::from_value("3")
749            ) => LuaValue::from("23"),
750            concat_string_with_number(
751                BinaryOperator::Concat,
752                StringExpression::from_value("foo"),
753                11.0
754            ) => LuaValue::from("foo11"),
755            concat_number_with_string(
756                BinaryOperator::Concat,
757                11.0,
758                StringExpression::from_value("foo")
759            ) => LuaValue::from("11foo"),
760            concat_number_with_number(
761                BinaryOperator::Concat,
762                11.0,
763                33.0
764            ) => LuaValue::from("1133"),
765            concat_number_with_negative_number(
766                BinaryOperator::Concat,
767                11.0,
768                -33.0
769            ) => LuaValue::from("11-33"),
770            concat_empty_strings(
771                BinaryOperator::Concat,
772                StringExpression::empty(),
773                StringExpression::empty()
774            ) => LuaValue::from(""),
775            number_lower_than_string(
776                BinaryOperator::LowerThan,
777                1.0,
778                StringExpression::empty()
779            ) => LuaValue::Unknown,
780            number_string_greater_than_number(
781                BinaryOperator::GreaterThan,
782                StringExpression::from_value("100"),
783                1.0
784            ) => LuaValue::Unknown,
785            number_string_greater_or_equal_than_number(
786                BinaryOperator::GreaterOrEqualThan,
787                StringExpression::from_value("100"),
788                100.0
789            ) => LuaValue::Unknown,
790            number_lower_or_equal_than_number_string(
791                BinaryOperator::GreaterOrEqualThan,
792                100.0,
793                StringExpression::from_value("100")
794            ) => LuaValue::Unknown,
795        );
796
797        macro_rules! evaluate_equality {
798            ($($name:ident ($left:expr, $right:expr) => $value:expr),* $(,)?) => {
799                $(
800                    mod $name {
801                        use super::*;
802
803                        #[test]
804                        fn equal() {
805                            let binary = BinaryExpression::new(
806                                BinaryOperator::Equal,
807                                $left,
808                                $right,
809                            );
810
811                            assert_eq!($value, Evaluator::default().evaluate(&binary.into()));
812
813                            let binary = BinaryExpression::new(
814                                BinaryOperator::Equal,
815                                $right,
816                                $left,
817                            );
818
819                            assert_eq!($value, Evaluator::default().evaluate(&binary.into()));
820                        }
821
822                        #[test]
823                        fn not_equal() {
824                            let value = match $value {
825                                LuaValue::True => LuaValue::False,
826                                LuaValue::False => LuaValue::True,
827                                _ => LuaValue::Unknown
828                            };
829                            let binary = BinaryExpression::new(
830                                BinaryOperator::NotEqual,
831                                $left,
832                                $right,
833                            );
834
835                            assert_eq!(value, Evaluator::default().evaluate(&binary.into()));
836
837                            let binary = BinaryExpression::new(
838                                BinaryOperator::NotEqual,
839                                $right,
840                                $left,
841                            );
842
843                            assert_eq!(value, Evaluator::default().evaluate(&binary.into()));
844                        }
845                    }
846                )*
847            };
848        }
849
850        evaluate_equality!(
851            true_true(Expression::from(true), Expression::from(true)) => LuaValue::True,
852            false_false(Expression::from(false), Expression::from(false)) => LuaValue::True,
853            nil_nil(Expression::nil(), Expression::nil()) => LuaValue::True,
854            same_strings(
855                StringExpression::from_value("foo"),
856                StringExpression::from_value("foo")
857            ) => LuaValue::True,
858            same_numbers(
859                Expression::Number(DecimalNumber::new(0.0).into()),
860                Expression::Number(DecimalNumber::new(0.0).into())
861            ) => LuaValue::True,
862            true_false(Expression::from(true), Expression::from(false)) => LuaValue::False,
863            true_nil(Expression::from(true), Expression::from(false)) => LuaValue::False,
864            different_numbers(
865                Expression::Number(DecimalNumber::new(1.0).into()),
866                Expression::Number(DecimalNumber::new(10.0).into())
867            ) => LuaValue::False,
868            different_strings(
869                StringExpression::from_value("foo"),
870                StringExpression::from_value("bar")
871            ) => LuaValue::False,
872        );
873
874        macro_rules! evaluate_equality_with_relational_operators {
875            ($($name:ident => $value:expr),* $(,)?) => {
876                $(
877                    mod $name {
878                        use super::*;
879
880                        #[test]
881                        fn lower() {
882                            let value: Expression = $value.into();
883                            let binary = BinaryExpression::new(BinaryOperator::LowerThan, value.clone(), value);
884                            assert_eq!(LuaValue::False, Evaluator::default().evaluate(&binary.into()));
885                        }
886
887                        #[test]
888                        fn lower_or_equal() {
889                            let value: Expression = $value.into();
890                            let binary = BinaryExpression::new(BinaryOperator::LowerOrEqualThan, value.clone(), value);
891                            assert_eq!(LuaValue::True, Evaluator::default().evaluate(&binary.into()));
892                        }
893
894                        #[test]
895                        fn greater() {
896                            let value: Expression = $value.into();
897                            let binary = BinaryExpression::new(BinaryOperator::GreaterThan, value.clone(), value);
898                            assert_eq!(LuaValue::False, Evaluator::default().evaluate(&binary.into()));
899                        }
900
901                        #[test]
902                        fn greater_or_equal() {
903                            let value: Expression = $value.into();
904                            let binary = BinaryExpression::new(BinaryOperator::GreaterOrEqualThan, value.clone(), value);
905                            assert_eq!(LuaValue::True, Evaluator::default().evaluate(&binary.into()));
906                        }
907                    }
908                )*
909            };
910        }
911
912        evaluate_equality_with_relational_operators!(
913            zero => 1.0,
914            one => 1.0,
915            hundred => 100.0,
916            string => StringExpression::from_value("var"),
917        );
918
919        macro_rules! evaluate_strict_relational_operators {
920            ($($name_lower:ident($lower:expr) < $name_greater:ident($greater:expr)),* $(,)?) => {
921                mod lower_or_greater_than {
922                    use super::*;
923                    paste::paste! {
924
925                    $(
926                        #[test]
927                        fn [<$name_lower _lower_than_ $name_greater>]() {
928                            let binary = BinaryExpression::new(
929                                BinaryOperator::LowerThan,
930                                $lower,
931                                $greater,
932                            );
933                            assert_eq!(LuaValue::True, Evaluator::default().evaluate(&binary.into()));
934                        }
935
936                        #[test]
937                        fn [<$name_lower _lower_or_equal_than_ $name_greater>]() {
938                            let binary = BinaryExpression::new(
939                                BinaryOperator::LowerOrEqualThan,
940                                $lower,
941                                $greater,
942                            );
943                            assert_eq!(LuaValue::True, Evaluator::default().evaluate(&binary.into()));
944                        }
945
946                        #[test]
947                        fn [<$name_lower _greater_than_ $name_greater>]() {
948                            let binary = BinaryExpression::new(
949                                BinaryOperator::GreaterThan,
950                                $lower,
951                                $greater,
952                            );
953                            assert_eq!(LuaValue::False, Evaluator::default().evaluate(&binary.into()));
954                        }
955
956                        #[test]
957                        fn [<$name_lower _greater_or_equal_than_ $name_greater>]() {
958                            let binary = BinaryExpression::new(
959                                BinaryOperator::GreaterOrEqualThan,
960                                $lower,
961                                $greater,
962                            );
963                            assert_eq!(LuaValue::False, Evaluator::default().evaluate(&binary.into()));
964                        }
965
966                        #[test]
967                        fn [<$name_greater _lower_than_ $name_lower>]() {
968                            let binary = BinaryExpression::new(
969                                BinaryOperator::LowerThan,
970                                $greater,
971                                $lower,
972                            );
973                            assert_eq!(LuaValue::False, Evaluator::default().evaluate(&binary.into()));
974                        }
975
976                        #[test]
977                        fn [<$name_greater _lower_or_equal_than_ $name_lower>]() {
978                            let binary = BinaryExpression::new(
979                                BinaryOperator::LowerOrEqualThan,
980                                $greater,
981                                $lower,
982                            );
983                            assert_eq!(LuaValue::False, Evaluator::default().evaluate(&binary.into()));
984                        }
985
986                        #[test]
987                        fn [<$name_greater _greater_than_ $name_lower>]() {
988                            let binary = BinaryExpression::new(
989                                BinaryOperator::GreaterThan,
990                                $greater,
991                                $lower,
992                            );
993                            assert_eq!(LuaValue::True, Evaluator::default().evaluate(&binary.into()));
994                        }
995
996                        #[test]
997                        fn [<$name_greater _greater_or_equal_than_ $name_lower>]() {
998                            let binary = BinaryExpression::new(
999                                BinaryOperator::GreaterOrEqualThan,
1000                                $greater,
1001                                $lower,
1002                            );
1003                            assert_eq!(LuaValue::True, Evaluator::default().evaluate(&binary.into()));
1004                        }
1005                    )*
1006
1007                    }
1008                }
1009            };
1010        }
1011
1012        evaluate_strict_relational_operators!(
1013            one(1.0) < hundred(100.0),
1014            minus_15(-15.0) < minus_2_5(-2.5),
1015            string_a(StringExpression::from_value("a"))
1016                < string_b(StringExpression::from_value("b")),
1017            string_a(StringExpression::from_value("a"))
1018                < string_aa(StringExpression::from_value("aa")),
1019            string_1(StringExpression::from_value("1"))
1020                < string_a(StringExpression::from_value("a")),
1021            string_111(StringExpression::from_value("111"))
1022                < string_a(StringExpression::from_value("a")),
1023            empty_string(StringExpression::from_value(""))
1024                < string_colon(StringExpression::from_value(":")),
1025        );
1026    }
1027
1028    mod unary_expressions {
1029        use super::*;
1030        use UnaryOperator::*;
1031
1032        macro_rules! evaluate_unary_expressions {
1033            ($($name:ident ($operator:expr, $input:expr) => $value:expr),*) => {
1034                $(
1035                    #[test]
1036                    fn $name() {
1037                        let unary = UnaryExpression::new($operator, $input);
1038                        assert_eq!($value, Evaluator::default().evaluate(&unary.into()));
1039                    }
1040                )*
1041            };
1042        }
1043
1044        evaluate_unary_expressions!(
1045            not_true(Not, Expression::from(true)) => LuaValue::False,
1046            not_false(Not, Expression::from(false)) => LuaValue::True,
1047            not_nil(Not, Expression::nil()) => LuaValue::True,
1048            not_table(Not, TableExpression::default()) => LuaValue::False,
1049            not_string(Not, StringExpression::from_value("foo")) => LuaValue::False,
1050            not_number(
1051                Not,
1052                Expression::Number(DecimalNumber::new(10.0).into())
1053            ) => LuaValue::False,
1054            not_identifier(Not, Expression::identifier("foo")) => LuaValue::Unknown,
1055            minus_one(Minus, DecimalNumber::new(1.0)) => LuaValue::from(-1.0),
1056            minus_zero(Minus, DecimalNumber::new(-0.0)) => LuaValue::from(-0.0),
1057            minus_negative_number(Minus, DecimalNumber::new(-5.0)) => LuaValue::from(5.0),
1058            minus_string_converted_to_number(Minus, StringExpression::from_value("1")) => LuaValue::from(-1.0)
1059        );
1060    }
1061
1062    macro_rules! has_side_effects {
1063        ($($name:ident => $expression:expr),* $(,)?) => {
1064            $(
1065                #[test]
1066                fn $name() {
1067                    assert!(Evaluator::default().has_side_effects(&$expression.into()));
1068                }
1069            )*
1070        };
1071    }
1072
1073    macro_rules! has_no_side_effects {
1074        ($($name:ident => $expression:expr),* $(,)?) => {
1075            $(
1076                #[test]
1077                fn $name() {
1078                    assert!(!Evaluator::default().has_side_effects(&$expression.into()));
1079                }
1080            )*
1081        };
1082    }
1083
1084    has_side_effects!(
1085        call_to_unknown_function => FunctionCall::from_name("foo"),
1086        binary_true_and_call => BinaryExpression::new(
1087            BinaryOperator::And,
1088            Expression::from(true),
1089            FunctionCall::from_name("foo"),
1090        ),
1091        binary_false_or_call => BinaryExpression::new(
1092            BinaryOperator::Or,
1093            Expression::from(false),
1094            FunctionCall::from_name("var"),
1095        ),
1096        addition_unknown_variable_and_number => BinaryExpression::new(
1097            BinaryOperator::Plus,
1098            Expression::identifier("var"),
1099            1.0,
1100        ),
1101        addition_number_with_unknown_variable => BinaryExpression::new(
1102            BinaryOperator::Plus,
1103            1.0,
1104            Expression::identifier("var"),
1105        ),
1106        unary_minus_on_variable => UnaryExpression::new(UnaryOperator::Minus, Identifier::new("var")),
1107        length_on_variable => UnaryExpression::new(UnaryOperator::Length, Identifier::new("var")),
1108        field_index => FieldExpression::new(Identifier::new("var"), "field"),
1109        table_value_with_call_in_entry => TableExpression::default()
1110            .append_array_value(FunctionCall::from_name("call")),
1111
1112        interpolated_string_with_function_call => InterpolatedStringExpression::empty()
1113            .with_segment(Expression::from(FunctionCall::from_name("foo"))),
1114    );
1115
1116    has_no_side_effects!(
1117        true_value => Expression::from(true),
1118        false_value => Expression::from(false),
1119        nil_value => Expression::nil(),
1120        table_value => TableExpression::default(),
1121        number_value => Expression::Number(DecimalNumber::new(0.0).into()),
1122        string_value => StringExpression::from_value(""),
1123        empty_interpolated_string_value => InterpolatedStringExpression::empty(),
1124        interpolated_string_with_true_value => InterpolatedStringExpression::empty()
1125            .with_segment(Expression::from(true)),
1126        identifier => Expression::identifier("foo"),
1127        identifier_in_parentheses => Expression::identifier("foo").in_parentheses(),
1128        binary_false_and_call => BinaryExpression::new(
1129            BinaryOperator::And,
1130            Expression::from(false),
1131            FunctionCall::from_name("foo"),
1132        ),
1133        binary_true_or_call => BinaryExpression::new(
1134            BinaryOperator::Or,
1135            Expression::from(true),
1136            FunctionCall::from_name("foo"),
1137        ),
1138        not_variable => UnaryExpression::new(UnaryOperator::Not, Identifier::new("var")),
1139    );
1140
1141    mod assume_pure_metamethods {
1142        use super::*;
1143
1144        macro_rules! has_no_side_effects {
1145            ($($name:ident => $expression:expr),* $(,)?) => {
1146                $(
1147                    #[test]
1148                    fn $name() {
1149                        let evaluator = Evaluator::default().assume_pure_metamethods();
1150                        assert!(!evaluator.has_side_effects(&$expression.into()));
1151                    }
1152                )*
1153            };
1154        }
1155
1156        has_no_side_effects!(
1157            addition_unknown_variable_and_number => BinaryExpression::new(
1158                BinaryOperator::Plus,
1159                Expression::identifier("foo"),
1160                1.0,
1161            ),
1162            addition_number_with_unknown_variable => BinaryExpression::new(
1163                BinaryOperator::Plus,
1164                1.0,
1165                Expression::identifier("foo"),
1166            ),
1167            unary_minus_on_variable => UnaryExpression::new(UnaryOperator::Minus, Identifier::new("var")),
1168            length_on_variable => UnaryExpression::new(UnaryOperator::Length, Identifier::new("var")),
1169            not_on_variable => UnaryExpression::new(UnaryOperator::Not, Identifier::new("var")),
1170            field_index => FieldExpression::new(Identifier::new("var"), "field"),
1171        );
1172    }
1173}