darklua_core/rules/
compute_expression.rs

1use crate::nodes::{BinaryOperator, Block, Expression};
2use crate::process::{DefaultVisitor, Evaluator, NodeProcessor, NodeVisitor};
3use crate::rules::{
4    Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties,
5};
6
7use super::verify_no_rule_properties;
8
9#[derive(Debug, Clone, Default)]
10struct Computer {
11    evaluator: Evaluator,
12}
13
14impl Computer {
15    fn replace_with(&mut self, expression: &Expression) -> Option<Expression> {
16        match expression {
17            Expression::Unary(_) => {
18                if !self.evaluator.has_side_effects(expression) {
19                    self.evaluator.evaluate(expression).to_expression()
20                } else {
21                    None
22                }
23            }
24            Expression::Binary(binary) => {
25                if !self.evaluator.has_side_effects(expression) {
26                    self.evaluator
27                        .evaluate(expression)
28                        .to_expression()
29                        .or_else(|| {
30                            match binary.operator() {
31                                BinaryOperator::And => {
32                                    self.evaluator.evaluate(binary.left()).is_truthy().map(
33                                        |is_truthy| {
34                                            if is_truthy {
35                                                binary.right().clone()
36                                            } else {
37                                                binary.left().clone()
38                                            }
39                                        },
40                                    )
41                                }
42                                BinaryOperator::Or => {
43                                    self.evaluator.evaluate(binary.left()).is_truthy().map(
44                                        |is_truthy| {
45                                            if is_truthy {
46                                                binary.left().clone()
47                                            } else {
48                                                binary.right().clone()
49                                            }
50                                        },
51                                    )
52                                }
53                                _ => None,
54                            }
55                            .map(|mut expression| {
56                                self.process_expression(&mut expression);
57                                expression
58                            })
59                        })
60                } else {
61                    match binary.operator() {
62                        BinaryOperator::And => {
63                            if !self.evaluator.has_side_effects(binary.left()) {
64                                self.evaluator.evaluate(binary.left()).is_truthy().map(
65                                    |is_truthy| {
66                                        if is_truthy {
67                                            binary.right().clone()
68                                        } else {
69                                            binary.left().clone()
70                                        }
71                                    },
72                                )
73                            } else {
74                                None
75                            }
76                        }
77                        BinaryOperator::Or => {
78                            if !self.evaluator.has_side_effects(binary.left()) {
79                                self.evaluator.evaluate(binary.left()).is_truthy().map(
80                                    |is_truthy| {
81                                        if is_truthy {
82                                            binary.left().clone()
83                                        } else {
84                                            binary.right().clone()
85                                        }
86                                    },
87                                )
88                            } else {
89                                None
90                            }
91                        }
92                        _ => None,
93                    }
94                }
95            }
96            Expression::If(_) => {
97                if !self.evaluator.has_side_effects(expression) {
98                    self.evaluator.evaluate(expression).to_expression()
99                } else {
100                    None
101                }
102            }
103            _ => None,
104        }
105    }
106}
107
108impl NodeProcessor for Computer {
109    fn process_expression(&mut self, expression: &mut Expression) {
110        if let Some(replace_with) = self.replace_with(expression) {
111            *expression = replace_with;
112        }
113    }
114}
115
116pub const COMPUTE_EXPRESSIONS_RULE_NAME: &str = "compute_expression";
117
118/// A rule that compute expressions that do not have any side-effects.
119#[derive(Debug, Default, PartialEq, Eq)]
120pub struct ComputeExpression {}
121
122impl FlawlessRule for ComputeExpression {
123    fn flawless_process(&self, block: &mut Block, _: &Context) {
124        let mut processor = Computer::default();
125        DefaultVisitor::visit_block(block, &mut processor);
126    }
127}
128
129impl RuleConfiguration for ComputeExpression {
130    fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
131        verify_no_rule_properties(&properties)?;
132
133        Ok(())
134    }
135
136    fn get_name(&self) -> &'static str {
137        COMPUTE_EXPRESSIONS_RULE_NAME
138    }
139
140    fn serialize_to_properties(&self) -> RuleProperties {
141        RuleProperties::new()
142    }
143}
144
145#[cfg(test)]
146mod test {
147    use super::*;
148    use crate::rules::Rule;
149
150    use insta::assert_json_snapshot;
151
152    fn new_rule() -> ComputeExpression {
153        ComputeExpression::default()
154    }
155
156    #[test]
157    fn serialize_default_rule() {
158        let rule: Box<dyn Rule> = Box::new(new_rule());
159
160        assert_json_snapshot!("default_compute_expression", rule);
161    }
162}