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 = Vec::new();
42                for segment in interpolated_string.iter_segments() {
43                    match segment {
44                        InterpolationSegment::String(string) => {
45                            result.extend_from_slice(string.get_value());
46                        }
47                        InterpolationSegment::Value(value) => {
48                            match self.evaluate(value.get_expression()) {
49                                LuaValue::False => {
50                                    result.extend_from_slice(b"false");
51                                }
52                                LuaValue::True => {
53                                    result.extend_from_slice(b"true");
54                                }
55                                LuaValue::Nil => {
56                                    result.extend_from_slice(b"nil");
57                                }
58                                LuaValue::String(string) => {
59                                    result.extend_from_slice(&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.extend_from_slice(&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: &[u8], right: &[u8], 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            UnaryOperator::Length => self.evaluate(expression.get_expression()).length(),
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::from("foo"),
482        empty_interpolated_string_expression(InterpolatedStringExpression::empty()) => LuaValue::from(""),
483        interpolated_string_expression_with_one_string(InterpolatedStringExpression::empty().with_segment("hello"))
484            => LuaValue::from("hello"),
485        interpolated_string_expression_with_multiple_string_segments(
486            InterpolatedStringExpression::empty()
487                .with_segment("hello")
488                .with_segment("-")
489                .with_segment("bye")
490        ) => LuaValue::from("hello-bye"),
491        interpolated_string_expression_with_true_segment(
492            InterpolatedStringExpression::empty().with_segment(Expression::from(true))
493        ) => LuaValue::from("true"),
494        interpolated_string_expression_with_false_segment(
495            InterpolatedStringExpression::empty().with_segment(Expression::from(false))
496        ) => LuaValue::from("false"),
497        interpolated_string_expression_with_nil_segment(
498            InterpolatedStringExpression::empty().with_segment(Expression::nil())
499        ) => LuaValue::from("nil"),
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::from("variable = true?"),
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        length_empty_string(UnaryExpression::new(UnaryOperator::Length, StringExpression::empty()))
529            => LuaValue::Number(0.0),
530        length_single_char_string(UnaryExpression::new(UnaryOperator::Length, StringExpression::from_value("a")))
531            => LuaValue::Number(1.0),
532        length_short_string(UnaryExpression::new(UnaryOperator::Length, StringExpression::from_value("hello")))
533            => LuaValue::Number(5.0),
534        length_unknown_expression(UnaryExpression::new(UnaryOperator::Length, Expression::identifier("var")))
535            => LuaValue::Unknown,
536        length_number_expression(UnaryExpression::new(UnaryOperator::Length, Expression::from(42.0)))
537            => LuaValue::Unknown,
538        length_nil_expression(UnaryExpression::new(UnaryOperator::Length, Expression::nil()))
539            => LuaValue::Unknown,
540    );
541
542    mod binary_expressions {
543        use super::*;
544
545        macro_rules! evaluate_binary_expressions {
546            ($($name:ident ($operator:expr, $left:expr, $right:expr) => $expect:expr),* $(,)?) => {
547                $(
548                    #[test]
549                    fn $name() {
550                        let binary = BinaryExpression::new($operator, $left, $right);
551
552                        let result = Evaluator::default().evaluate(&binary.into());
553
554                        match (&$expect, &result) {
555                            (LuaValue::Number(expect_float), LuaValue::Number(result))=> {
556                                if expect_float.is_nan() {
557                                    assert!(result.is_nan(), "{} should be NaN", result);
558                                } else if expect_float.is_infinite() {
559                                    assert!(result.is_infinite(), "{} should be infinite", result);
560                                    assert_eq!(expect_float.is_sign_positive(), result.is_sign_positive());
561                                } else {
562                                    assert!(
563                                        (expect_float - result).abs() < f64::EPSILON,
564                                        "{} does not approximate {}", result, expect_float
565                                    );
566                                    assert!(
567                                        expect_float.is_sign_positive() == result.is_sign_positive(),
568                                        "{} should be of the same sign as {}", result, expect_float
569                                    );
570                                }
571                            }
572                            _ => {
573                                assert_eq!($expect, result);
574                            }
575                        }
576                    }
577                )*
578            };
579        }
580
581        evaluate_binary_expressions!(
582            true_and_number(
583                BinaryOperator::And,
584                true,
585                Expression::Number(DecimalNumber::new(0.0).into())
586            ) => LuaValue::Number(0.0),
587            true_and_true(
588                BinaryOperator::And,
589                true,
590                true
591            ) => LuaValue::True,
592            true_and_false(
593                BinaryOperator::And,
594                true,
595                false
596            ) => LuaValue::False,
597            true_and_nil(
598                BinaryOperator::And,
599                true,
600                Expression::nil()
601            ) => LuaValue::Nil,
602            true_and_string(
603                BinaryOperator::And,
604                true,
605                Expression::String(StringExpression::from_value("foo"))
606            ) => LuaValue::from("foo"),
607            true_and_table(
608                BinaryOperator::And,
609                true,
610                TableExpression::default()
611            ) => LuaValue::Table,
612            nil_and_true(
613                BinaryOperator::And,
614                Expression::nil(),
615                true
616            ) => LuaValue::Nil,
617            false_and_true(
618                BinaryOperator::And,
619                false,
620                true
621            ) => LuaValue::False,
622            true_or_number(
623                BinaryOperator::Or,
624                true,
625                Expression::Number(DecimalNumber::new(0.0).into())
626            ) => LuaValue::True,
627            true_or_true(
628                BinaryOperator::Or,
629                true,
630                true
631            ) => LuaValue::True,
632            true_or_false(
633                BinaryOperator::Or,
634                true,
635                false
636            ) => LuaValue::True,
637            true_or_nil(
638                BinaryOperator::Or,
639                true,
640                Expression::nil()
641            ) => LuaValue::True,
642            true_or_string(
643                BinaryOperator::Or,
644                true,
645                Expression::String(StringExpression::from_value("foo"))
646            ) => LuaValue::True,
647            nil_or_true(
648                BinaryOperator::Or,
649                Expression::nil(),
650                true
651            ) => LuaValue::True,
652            nil_or_false(
653                BinaryOperator::Or,
654                Expression::nil(),
655                false
656            ) => LuaValue::False,
657            nil_or_nil(
658                BinaryOperator::Or,
659                Expression::nil(),
660                Expression::nil()
661            ) => LuaValue::Nil,
662            one_plus_two(
663                BinaryOperator::Plus,
664                Expression::from(1.0),
665                Expression::from(2.0)
666            ) => LuaValue::Number(3.0),
667            one_minus_two(
668                BinaryOperator::Minus,
669                Expression::from(1.0),
670                Expression::from(2.0)
671            ) => LuaValue::Number(-1.0),
672            three_times_four(
673                BinaryOperator::Asterisk,
674                Expression::from(3.0),
675                Expression::from(4.0)
676            ) => LuaValue::Number(12.0),
677            twelve_divided_by_four(
678                BinaryOperator::Slash,
679                Expression::from(12.0),
680                Expression::from(4.0)
681            ) => LuaValue::Number(3.0),
682            one_divided_by_zero(
683                BinaryOperator::Slash,
684                Expression::from(1.0),
685                Expression::from(0.0)
686            ) => LuaValue::Number(f64::INFINITY),
687            negative_zero_plus_negative_zero(
688                BinaryOperator::Plus,
689                Expression::from(-0.0),
690                Expression::from(-0.0)
691            ) => LuaValue::Number(-0.0),
692            negative_zero_minus_zero(
693                BinaryOperator::Minus,
694                Expression::from(-0.0),
695                Expression::from(0.0)
696            ) => LuaValue::Number(-0.0),
697            zero_divided_by_zero(
698                BinaryOperator::Slash,
699                Expression::from(0.0),
700                Expression::from(0.0)
701            ) => LuaValue::Number(f64::NAN),
702            twelve_floor_division_by_four(
703                BinaryOperator::DoubleSlash,
704                Expression::from(12.0),
705                Expression::from(4.0)
706            ) => LuaValue::Number(3.0),
707            eleven_floor_division_by_three(
708                BinaryOperator::DoubleSlash,
709                Expression::from(11.0),
710                Expression::from(3.0)
711            ) => LuaValue::Number(3.0),
712            one_floor_division_by_zero(
713                BinaryOperator::DoubleSlash,
714                Expression::from(1.0),
715                Expression::from(0.0)
716            ) => LuaValue::Number(f64::INFINITY),
717            minus_one_floor_division_by_zero(
718                BinaryOperator::DoubleSlash,
719                Expression::from(-1.0),
720                Expression::from(0.0)
721            ) => LuaValue::Number(f64::NEG_INFINITY),
722            zero_floor_division_by_zero(
723                BinaryOperator::DoubleSlash,
724                Expression::from(0.0),
725                Expression::from(0.0)
726            ) => LuaValue::Number(f64::NAN),
727            five_mod_two(
728                BinaryOperator::Percent,
729                Expression::from(5.0),
730                Expression::from(2.0)
731            ) => LuaValue::Number(1.0),
732            minus_five_mod_two(
733                BinaryOperator::Percent,
734                Expression::from(-5.0),
735                Expression::from(2.0)
736            ) => LuaValue::Number(1.0),
737            minus_five_mod_minus_two(
738                BinaryOperator::Percent,
739                Expression::from(-5.0),
740                Expression::from(-2.0)
741            ) => LuaValue::Number(-1.0),
742            five_point_two_mod_two(
743                BinaryOperator::Percent,
744                Expression::from(5.5),
745                Expression::from(2.0)
746            ) => LuaValue::Number(1.5),
747            five_pow_two(
748                BinaryOperator::Caret,
749                Expression::from(5.0),
750                Expression::from(2.0)
751            ) => LuaValue::Number(25.0),
752            string_number_plus_string_number(
753                BinaryOperator::Plus,
754                StringExpression::from_value("2"),
755                StringExpression::from_value("3")
756            ) => LuaValue::Number(5.0),
757            concat_strings(
758                BinaryOperator::Concat,
759                StringExpression::from_value("2"),
760                StringExpression::from_value("3")
761            ) => LuaValue::from("23"),
762            concat_string_with_number(
763                BinaryOperator::Concat,
764                StringExpression::from_value("foo"),
765                11.0
766            ) => LuaValue::from("foo11"),
767            concat_number_with_string(
768                BinaryOperator::Concat,
769                11.0,
770                StringExpression::from_value("foo")
771            ) => LuaValue::from("11foo"),
772            concat_number_with_number(
773                BinaryOperator::Concat,
774                11.0,
775                33.0
776            ) => LuaValue::from("1133"),
777            concat_number_with_negative_number(
778                BinaryOperator::Concat,
779                11.0,
780                -33.0
781            ) => LuaValue::from("11-33"),
782            concat_empty_strings(
783                BinaryOperator::Concat,
784                StringExpression::empty(),
785                StringExpression::empty()
786            ) => LuaValue::from(""),
787            number_lower_than_string(
788                BinaryOperator::LowerThan,
789                1.0,
790                StringExpression::empty()
791            ) => LuaValue::Unknown,
792            number_string_greater_than_number(
793                BinaryOperator::GreaterThan,
794                StringExpression::from_value("100"),
795                1.0
796            ) => LuaValue::Unknown,
797            number_string_greater_or_equal_than_number(
798                BinaryOperator::GreaterOrEqualThan,
799                StringExpression::from_value("100"),
800                100.0
801            ) => LuaValue::Unknown,
802            number_lower_or_equal_than_number_string(
803                BinaryOperator::GreaterOrEqualThan,
804                100.0,
805                StringExpression::from_value("100")
806            ) => LuaValue::Unknown,
807        );
808
809        macro_rules! evaluate_equality {
810            ($($name:ident ($left:expr, $right:expr) => $value:expr),* $(,)?) => {
811                $(
812                    mod $name {
813                        use super::*;
814
815                        #[test]
816                        fn equal() {
817                            let binary = BinaryExpression::new(
818                                BinaryOperator::Equal,
819                                $left,
820                                $right,
821                            );
822
823                            assert_eq!($value, Evaluator::default().evaluate(&binary.into()));
824
825                            let binary = BinaryExpression::new(
826                                BinaryOperator::Equal,
827                                $right,
828                                $left,
829                            );
830
831                            assert_eq!($value, Evaluator::default().evaluate(&binary.into()));
832                        }
833
834                        #[test]
835                        fn not_equal() {
836                            let value = match $value {
837                                LuaValue::True => LuaValue::False,
838                                LuaValue::False => LuaValue::True,
839                                _ => LuaValue::Unknown
840                            };
841                            let binary = BinaryExpression::new(
842                                BinaryOperator::NotEqual,
843                                $left,
844                                $right,
845                            );
846
847                            assert_eq!(value, Evaluator::default().evaluate(&binary.into()));
848
849                            let binary = BinaryExpression::new(
850                                BinaryOperator::NotEqual,
851                                $right,
852                                $left,
853                            );
854
855                            assert_eq!(value, Evaluator::default().evaluate(&binary.into()));
856                        }
857                    }
858                )*
859            };
860        }
861
862        evaluate_equality!(
863            true_true(Expression::from(true), Expression::from(true)) => LuaValue::True,
864            false_false(Expression::from(false), Expression::from(false)) => LuaValue::True,
865            nil_nil(Expression::nil(), Expression::nil()) => LuaValue::True,
866            same_strings(
867                StringExpression::from_value("foo"),
868                StringExpression::from_value("foo")
869            ) => LuaValue::True,
870            same_numbers(
871                Expression::Number(DecimalNumber::new(0.0).into()),
872                Expression::Number(DecimalNumber::new(0.0).into())
873            ) => LuaValue::True,
874            true_false(Expression::from(true), Expression::from(false)) => LuaValue::False,
875            true_nil(Expression::from(true), Expression::from(false)) => LuaValue::False,
876            different_numbers(
877                Expression::Number(DecimalNumber::new(1.0).into()),
878                Expression::Number(DecimalNumber::new(10.0).into())
879            ) => LuaValue::False,
880            different_strings(
881                StringExpression::from_value("foo"),
882                StringExpression::from_value("bar")
883            ) => LuaValue::False,
884        );
885
886        macro_rules! evaluate_equality_with_relational_operators {
887            ($($name:ident => $value:expr),* $(,)?) => {
888                $(
889                    mod $name {
890                        use super::*;
891
892                        #[test]
893                        fn lower() {
894                            let value: Expression = $value.into();
895                            let binary = BinaryExpression::new(BinaryOperator::LowerThan, value.clone(), value);
896                            assert_eq!(LuaValue::False, Evaluator::default().evaluate(&binary.into()));
897                        }
898
899                        #[test]
900                        fn lower_or_equal() {
901                            let value: Expression = $value.into();
902                            let binary = BinaryExpression::new(BinaryOperator::LowerOrEqualThan, value.clone(), value);
903                            assert_eq!(LuaValue::True, Evaluator::default().evaluate(&binary.into()));
904                        }
905
906                        #[test]
907                        fn greater() {
908                            let value: Expression = $value.into();
909                            let binary = BinaryExpression::new(BinaryOperator::GreaterThan, value.clone(), value);
910                            assert_eq!(LuaValue::False, Evaluator::default().evaluate(&binary.into()));
911                        }
912
913                        #[test]
914                        fn greater_or_equal() {
915                            let value: Expression = $value.into();
916                            let binary = BinaryExpression::new(BinaryOperator::GreaterOrEqualThan, value.clone(), value);
917                            assert_eq!(LuaValue::True, Evaluator::default().evaluate(&binary.into()));
918                        }
919                    }
920                )*
921            };
922        }
923
924        evaluate_equality_with_relational_operators!(
925            zero => 1.0,
926            one => 1.0,
927            hundred => 100.0,
928            string => StringExpression::from_value("var"),
929        );
930
931        macro_rules! evaluate_strict_relational_operators {
932            ($($name_lower:ident($lower:expr) < $name_greater:ident($greater:expr)),* $(,)?) => {
933                mod lower_or_greater_than {
934                    use super::*;
935                    paste::paste! {
936
937                    $(
938                        #[test]
939                        fn [<$name_lower _lower_than_ $name_greater>]() {
940                            let binary = BinaryExpression::new(
941                                BinaryOperator::LowerThan,
942                                $lower,
943                                $greater,
944                            );
945                            assert_eq!(LuaValue::True, Evaluator::default().evaluate(&binary.into()));
946                        }
947
948                        #[test]
949                        fn [<$name_lower _lower_or_equal_than_ $name_greater>]() {
950                            let binary = BinaryExpression::new(
951                                BinaryOperator::LowerOrEqualThan,
952                                $lower,
953                                $greater,
954                            );
955                            assert_eq!(LuaValue::True, Evaluator::default().evaluate(&binary.into()));
956                        }
957
958                        #[test]
959                        fn [<$name_lower _greater_than_ $name_greater>]() {
960                            let binary = BinaryExpression::new(
961                                BinaryOperator::GreaterThan,
962                                $lower,
963                                $greater,
964                            );
965                            assert_eq!(LuaValue::False, Evaluator::default().evaluate(&binary.into()));
966                        }
967
968                        #[test]
969                        fn [<$name_lower _greater_or_equal_than_ $name_greater>]() {
970                            let binary = BinaryExpression::new(
971                                BinaryOperator::GreaterOrEqualThan,
972                                $lower,
973                                $greater,
974                            );
975                            assert_eq!(LuaValue::False, Evaluator::default().evaluate(&binary.into()));
976                        }
977
978                        #[test]
979                        fn [<$name_greater _lower_than_ $name_lower>]() {
980                            let binary = BinaryExpression::new(
981                                BinaryOperator::LowerThan,
982                                $greater,
983                                $lower,
984                            );
985                            assert_eq!(LuaValue::False, Evaluator::default().evaluate(&binary.into()));
986                        }
987
988                        #[test]
989                        fn [<$name_greater _lower_or_equal_than_ $name_lower>]() {
990                            let binary = BinaryExpression::new(
991                                BinaryOperator::LowerOrEqualThan,
992                                $greater,
993                                $lower,
994                            );
995                            assert_eq!(LuaValue::False, Evaluator::default().evaluate(&binary.into()));
996                        }
997
998                        #[test]
999                        fn [<$name_greater _greater_than_ $name_lower>]() {
1000                            let binary = BinaryExpression::new(
1001                                BinaryOperator::GreaterThan,
1002                                $greater,
1003                                $lower,
1004                            );
1005                            assert_eq!(LuaValue::True, Evaluator::default().evaluate(&binary.into()));
1006                        }
1007
1008                        #[test]
1009                        fn [<$name_greater _greater_or_equal_than_ $name_lower>]() {
1010                            let binary = BinaryExpression::new(
1011                                BinaryOperator::GreaterOrEqualThan,
1012                                $greater,
1013                                $lower,
1014                            );
1015                            assert_eq!(LuaValue::True, Evaluator::default().evaluate(&binary.into()));
1016                        }
1017                    )*
1018
1019                    }
1020                }
1021            };
1022        }
1023
1024        evaluate_strict_relational_operators!(
1025            one(1.0) < hundred(100.0),
1026            minus_15(-15.0) < minus_2_5(-2.5),
1027            string_a(StringExpression::from_value("a"))
1028                < string_b(StringExpression::from_value("b")),
1029            string_a(StringExpression::from_value("a"))
1030                < string_aa(StringExpression::from_value("aa")),
1031            string_1(StringExpression::from_value("1"))
1032                < string_a(StringExpression::from_value("a")),
1033            string_111(StringExpression::from_value("111"))
1034                < string_a(StringExpression::from_value("a")),
1035            empty_string(StringExpression::from_value(""))
1036                < string_colon(StringExpression::from_value(":")),
1037        );
1038    }
1039
1040    mod unary_expressions {
1041        use super::*;
1042        use UnaryOperator::*;
1043
1044        macro_rules! evaluate_unary_expressions {
1045            ($($name:ident ($operator:expr, $input:expr) => $value:expr),*) => {
1046                $(
1047                    #[test]
1048                    fn $name() {
1049                        let unary = UnaryExpression::new($operator, $input);
1050                        assert_eq!($value, Evaluator::default().evaluate(&unary.into()));
1051                    }
1052                )*
1053            };
1054        }
1055
1056        evaluate_unary_expressions!(
1057            not_true(Not, Expression::from(true)) => LuaValue::False,
1058            not_false(Not, Expression::from(false)) => LuaValue::True,
1059            not_nil(Not, Expression::nil()) => LuaValue::True,
1060            not_table(Not, TableExpression::default()) => LuaValue::False,
1061            not_string(Not, StringExpression::from_value("foo")) => LuaValue::False,
1062            not_number(
1063                Not,
1064                Expression::Number(DecimalNumber::new(10.0).into())
1065            ) => LuaValue::False,
1066            not_identifier(Not, Expression::identifier("foo")) => LuaValue::Unknown,
1067            minus_one(Minus, DecimalNumber::new(1.0)) => LuaValue::from(-1.0),
1068            minus_zero(Minus, DecimalNumber::new(-0.0)) => LuaValue::from(-0.0),
1069            minus_negative_number(Minus, DecimalNumber::new(-5.0)) => LuaValue::from(5.0),
1070            minus_string_converted_to_number(Minus, StringExpression::from_value("1")) => LuaValue::from(-1.0)
1071        );
1072    }
1073
1074    macro_rules! has_side_effects {
1075        ($($name:ident => $expression:expr),* $(,)?) => {
1076            $(
1077                #[test]
1078                fn $name() {
1079                    assert!(Evaluator::default().has_side_effects(&$expression.into()));
1080                }
1081            )*
1082        };
1083    }
1084
1085    macro_rules! has_no_side_effects {
1086        ($($name:ident => $expression:expr),* $(,)?) => {
1087            $(
1088                #[test]
1089                fn $name() {
1090                    assert!(!Evaluator::default().has_side_effects(&$expression.into()));
1091                }
1092            )*
1093        };
1094    }
1095
1096    has_side_effects!(
1097        call_to_unknown_function => FunctionCall::from_name("foo"),
1098        binary_true_and_call => BinaryExpression::new(
1099            BinaryOperator::And,
1100            Expression::from(true),
1101            FunctionCall::from_name("foo"),
1102        ),
1103        binary_false_or_call => BinaryExpression::new(
1104            BinaryOperator::Or,
1105            Expression::from(false),
1106            FunctionCall::from_name("var"),
1107        ),
1108        addition_unknown_variable_and_number => BinaryExpression::new(
1109            BinaryOperator::Plus,
1110            Expression::identifier("var"),
1111            1.0,
1112        ),
1113        addition_number_with_unknown_variable => BinaryExpression::new(
1114            BinaryOperator::Plus,
1115            1.0,
1116            Expression::identifier("var"),
1117        ),
1118        unary_minus_on_variable => UnaryExpression::new(UnaryOperator::Minus, Identifier::new("var")),
1119        length_on_variable => UnaryExpression::new(UnaryOperator::Length, Identifier::new("var")),
1120        field_index => FieldExpression::new(Identifier::new("var"), "field"),
1121        table_value_with_call_in_entry => TableExpression::default()
1122            .append_array_value(FunctionCall::from_name("call")),
1123
1124        interpolated_string_with_function_call => InterpolatedStringExpression::empty()
1125            .with_segment(Expression::from(FunctionCall::from_name("foo"))),
1126    );
1127
1128    has_no_side_effects!(
1129        true_value => Expression::from(true),
1130        false_value => Expression::from(false),
1131        nil_value => Expression::nil(),
1132        table_value => TableExpression::default(),
1133        number_value => Expression::Number(DecimalNumber::new(0.0).into()),
1134        string_value => StringExpression::from_value(""),
1135        empty_interpolated_string_value => InterpolatedStringExpression::empty(),
1136        interpolated_string_with_true_value => InterpolatedStringExpression::empty()
1137            .with_segment(Expression::from(true)),
1138        identifier => Expression::identifier("foo"),
1139        identifier_in_parentheses => Expression::identifier("foo").in_parentheses(),
1140        binary_false_and_call => BinaryExpression::new(
1141            BinaryOperator::And,
1142            Expression::from(false),
1143            FunctionCall::from_name("foo"),
1144        ),
1145        binary_true_or_call => BinaryExpression::new(
1146            BinaryOperator::Or,
1147            Expression::from(true),
1148            FunctionCall::from_name("foo"),
1149        ),
1150        not_variable => UnaryExpression::new(UnaryOperator::Not, Identifier::new("var")),
1151    );
1152
1153    mod assume_pure_metamethods {
1154        use super::*;
1155
1156        macro_rules! has_no_side_effects {
1157            ($($name:ident => $expression:expr),* $(,)?) => {
1158                $(
1159                    #[test]
1160                    fn $name() {
1161                        let evaluator = Evaluator::default().assume_pure_metamethods();
1162                        assert!(!evaluator.has_side_effects(&$expression.into()));
1163                    }
1164                )*
1165            };
1166        }
1167
1168        has_no_side_effects!(
1169            addition_unknown_variable_and_number => BinaryExpression::new(
1170                BinaryOperator::Plus,
1171                Expression::identifier("foo"),
1172                1.0,
1173            ),
1174            addition_number_with_unknown_variable => BinaryExpression::new(
1175                BinaryOperator::Plus,
1176                1.0,
1177                Expression::identifier("foo"),
1178            ),
1179            unary_minus_on_variable => UnaryExpression::new(UnaryOperator::Minus, Identifier::new("var")),
1180            length_on_variable => UnaryExpression::new(UnaryOperator::Length, Identifier::new("var")),
1181            not_on_variable => UnaryExpression::new(UnaryOperator::Not, Identifier::new("var")),
1182            field_index => FieldExpression::new(Identifier::new("var"), "field"),
1183        );
1184    }
1185}