darklua_core/rules/
convert_square_root_call.rs1use crate::nodes::{
2 BinaryExpression, BinaryOperator, Block, Expression, FunctionCall, Prefix, Statement,
3};
4use crate::process::{Evaluator, IdentifierTracker, NodeProcessor, NodeVisitor, ScopeVisitor};
5use crate::rules::{
6 Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties,
7};
8use crate::utils::{expressions_as_statement, preserve_arguments_side_effects};
9
10use super::verify_no_rule_properties;
11
12pub const CONVERT_SQUARE_ROOT_CALL_RULE_NAME: &str = "convert_square_root_call";
13
14const DEFAULT_MATH_LIBRARY: &str = "math";
15const DEFAULT_MATH_SQRT_NAME: &str = "sqrt";
16
17#[derive(Default)]
18struct Processor {
19 evaluator: Evaluator,
20 identifier_tracker: IdentifierTracker,
21}
22
23impl Processor {
24 fn new() -> Self {
25 Self::default()
26 }
27
28 fn is_math_sqrt_call(&self, call: &FunctionCall) -> bool {
29 if call.has_method() {
30 return false;
31 }
32
33 if call.get_arguments().len() != 1 {
34 return false;
35 }
36
37 if let Prefix::Field(field_expr) = call.get_prefix() {
38 if field_expr.get_field().get_name() != DEFAULT_MATH_SQRT_NAME {
39 return false;
40 }
41
42 if let Prefix::Identifier(identifier) = field_expr.get_prefix() {
43 if identifier.get_name() == DEFAULT_MATH_LIBRARY
44 && !self
45 .identifier_tracker
46 .is_identifier_used(DEFAULT_MATH_LIBRARY)
47 {
48 return true;
49 }
50 }
51 }
52
53 false
54 }
55}
56
57impl std::ops::Deref for Processor {
58 type Target = IdentifierTracker;
59
60 fn deref(&self) -> &Self::Target {
61 &self.identifier_tracker
62 }
63}
64
65impl std::ops::DerefMut for Processor {
66 fn deref_mut(&mut self) -> &mut Self::Target {
67 &mut self.identifier_tracker
68 }
69}
70
71impl NodeProcessor for Processor {
72 fn process_expression(&mut self, expression: &mut Expression) {
73 if let Expression::Call(call) = expression {
74 if self.is_math_sqrt_call(call) {
75 let arguments = call.get_arguments();
76 let expressions = arguments.clone().to_expressions();
77 if let Some(argument) = expressions.first() {
78 *expression = BinaryExpression::new(
79 BinaryOperator::Caret,
80 argument.clone(),
81 Expression::from(0.5),
82 )
83 .into();
84 }
85 }
86 }
87 }
88
89 fn process_statement(&mut self, statement: &mut Statement) {
90 if let Statement::Call(call) = statement {
91 if self.is_math_sqrt_call(call) {
92 let values = preserve_arguments_side_effects(&self.evaluator, call.get_arguments());
93
94 *statement = expressions_as_statement(values);
95 }
96 }
97 }
98}
99
100#[derive(Debug, Default)]
102pub struct ConvertSquareRootCall {}
103
104impl FlawlessRule for ConvertSquareRootCall {
105 fn flawless_process(&self, block: &mut Block, _: &Context) {
106 let mut processor = Processor::new();
107 ScopeVisitor::visit_block(block, &mut processor);
108 }
109}
110
111impl RuleConfiguration for ConvertSquareRootCall {
112 fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
113 verify_no_rule_properties(&properties)
114 }
115
116 fn get_name(&self) -> &'static str {
117 CONVERT_SQUARE_ROOT_CALL_RULE_NAME
118 }
119
120 fn serialize_to_properties(&self) -> RuleProperties {
121 RuleProperties::new()
122 }
123}
124
125#[cfg(test)]
126mod test {
127 use super::*;
128 use crate::rules::Rule;
129
130 use insta::assert_json_snapshot;
131
132 fn new_rule() -> ConvertSquareRootCall {
133 ConvertSquareRootCall::default()
134 }
135
136 #[test]
137 fn serialize_default_rule() {
138 let rule: Box<dyn Rule> = Box::new(new_rule());
139
140 assert_json_snapshot!(rule, @r###""convert_square_root_call""###);
141 }
142
143 #[test]
144 fn configure_with_extra_field_error() {
145 let result = json5::from_str::<Box<dyn Rule>>(
146 r#"{
147 rule: 'convert_square_root_call',
148 prop: "something",
149 }"#,
150 );
151 pretty_assertions::assert_eq!(result.unwrap_err().to_string(), "unexpected field 'prop'");
152 }
153}