Skip to main content

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, RuleMetadata, 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    metadata: RuleMetadata,
122}
123
124impl FlawlessRule for ComputeExpression {
125    fn flawless_process(&self, block: &mut Block, _: &Context) {
126        let mut processor = Computer::default();
127        DefaultVisitor::visit_block(block, &mut processor);
128    }
129}
130
131impl RuleConfiguration for ComputeExpression {
132    fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
133        verify_no_rule_properties(&properties)?;
134
135        Ok(())
136    }
137
138    fn get_name(&self) -> &'static str {
139        COMPUTE_EXPRESSIONS_RULE_NAME
140    }
141
142    fn serialize_to_properties(&self) -> RuleProperties {
143        RuleProperties::new()
144    }
145
146    fn set_metadata(&mut self, metadata: RuleMetadata) {
147        self.metadata = metadata;
148    }
149
150    fn metadata(&self) -> &RuleMetadata {
151        &self.metadata
152    }
153}
154
155#[cfg(test)]
156mod test {
157    use super::*;
158    use crate::rules::Rule;
159
160    use insta::assert_json_snapshot;
161
162    fn new_rule() -> ComputeExpression {
163        ComputeExpression::default()
164    }
165
166    #[test]
167    fn serialize_default_rule() {
168        let rule: Box<dyn Rule> = Box::new(new_rule());
169
170        assert_json_snapshot!(rule, @r###""compute_expression""###);
171    }
172}