1mod lua_value;
2
3pub use lua_value::*;
4
5use crate::nodes::*;
6
7#[derive(Debug, Clone, Default, PartialEq, Eq)]
9pub struct Evaluator {
10 pure_metamethods: bool,
11}
12
13impl Evaluator {
14 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 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}