darklua_core/rules/
compute_expression.rs1use 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#[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}