-
-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathremove_floor_division.rs
157 lines (130 loc) · 4.59 KB
/
remove_floor_division.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use std::{mem, ops};
use crate::nodes::{
BinaryOperator, Block, CompoundOperator, Expression, FieldExpression, FunctionCall,
LocalAssignStatement, Prefix, Statement,
};
use crate::process::{IdentifierTracker, NodeProcessor, NodeVisitor, ScopeVisitor};
use crate::rules::{
verify_no_rule_properties, Context, FlawlessRule, RemoveCompoundAssignment, RuleConfiguration,
RuleConfigurationError, RuleProperties,
};
struct RemoveFloorDivisionProcessor {
math_floor_identifier: String,
define_math_floor: bool,
identifier_tracker: IdentifierTracker,
}
impl ops::Deref for RemoveFloorDivisionProcessor {
type Target = IdentifierTracker;
fn deref(&self) -> &Self::Target {
&self.identifier_tracker
}
}
impl ops::DerefMut for RemoveFloorDivisionProcessor {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.identifier_tracker
}
}
const DEFAULT_MATH_LIBRARY: &str = "math";
const DEFAULT_MATH_FLOOR_NAME: &str = "floor";
impl RemoveFloorDivisionProcessor {
fn new(math_floor_identifier: impl Into<String>) -> Self {
Self {
math_floor_identifier: math_floor_identifier.into(),
define_math_floor: false,
identifier_tracker: Default::default(),
}
}
fn build_math_floor_call(&mut self, value: Expression) -> Expression {
FunctionCall::from_prefix(if self.is_identifier_used(DEFAULT_MATH_LIBRARY) {
self.define_math_floor = true;
Prefix::from_name(&self.math_floor_identifier)
} else {
FieldExpression::new(
Prefix::from_name(DEFAULT_MATH_LIBRARY),
DEFAULT_MATH_FLOOR_NAME,
)
.into()
})
.with_argument(value)
.into()
}
}
impl NodeProcessor for RemoveFloorDivisionProcessor {
fn process_statement(&mut self, statement: &mut Statement) {
match statement {
Statement::CompoundAssign(assign_statement)
if assign_statement.get_operator() == CompoundOperator::DoubleSlash =>
{
RemoveCompoundAssignment::default().replace_compound_assignment(statement);
}
_ => {}
}
}
fn process_expression(&mut self, expression: &mut Expression) {
if let Expression::Binary(binary) = expression {
if binary.operator() == BinaryOperator::DoubleSlash {
binary.set_operator(BinaryOperator::Slash);
let value = mem::replace(expression, Expression::nil());
*expression = self.build_math_floor_call(value);
}
}
}
}
pub const REMOVE_FLOOR_DIVISION_RULE_NAME: &str = "remove_floor_division";
/// A rule that removes interpolated strings.
#[derive(Debug, Default, PartialEq, Eq)]
pub struct RemoveFloorDivision {}
impl FlawlessRule for RemoveFloorDivision {
fn flawless_process(&self, block: &mut Block, _: &Context) {
const MATH_FLOOR_IDENTIFIER: &str = "__DARKLUA_MATH_FLOOR";
let mut processor = RemoveFloorDivisionProcessor::new(MATH_FLOOR_IDENTIFIER);
ScopeVisitor::visit_block(block, &mut processor);
if processor.define_math_floor {
block.insert_statement(
0,
LocalAssignStatement::from_variable(MATH_FLOOR_IDENTIFIER).with_value(
FieldExpression::new(
Prefix::from_name(DEFAULT_MATH_LIBRARY),
DEFAULT_MATH_FLOOR_NAME,
),
),
);
}
}
}
impl RuleConfiguration for RemoveFloorDivision {
fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
verify_no_rule_properties(&properties)?;
Ok(())
}
fn get_name(&self) -> &'static str {
REMOVE_FLOOR_DIVISION_RULE_NAME
}
fn serialize_to_properties(&self) -> RuleProperties {
RuleProperties::new()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::rules::Rule;
use insta::assert_json_snapshot;
fn new_rule() -> RemoveFloorDivision {
RemoveFloorDivision::default()
}
#[test]
fn serialize_default_rule() {
let rule: Box<dyn Rule> = Box::new(new_rule());
assert_json_snapshot!("default_remove_floor_division", rule);
}
#[test]
fn configure_with_extra_field_error() {
let result = json5::from_str::<Box<dyn Rule>>(
r#"{
rule: 'remove_floor_division',
prop: "something",
}"#,
);
pretty_assertions::assert_eq!(result.unwrap_err().to_string(), "unexpected field 'prop'");
}
}