darklua_core/rules/
remove_continue.rs1use std::fmt::Debug;
2use std::mem;
3
4use crate::nodes::{
5 AssignStatement, Block, GenericForStatement, Identifier, IfStatement, LastStatement,
6 LocalAssignStatement, NumericForStatement, RepeatStatement, UnaryExpression, UnaryOperator,
7 WhileStatement,
8};
9use crate::process::{DefaultPostVisitor, NodePostProcessor, NodePostVisitor, NodeProcessor};
10use crate::rules::{Context, RuleConfiguration, RuleConfigurationError, RuleProperties};
11
12use super::{verify_no_rule_properties, FlawlessRule};
13
14#[derive(Default)]
15struct Processor {
16 loop_stack: Vec<Option<LoopData>>,
17 loop_identifier_count: u16,
18}
19
20struct LoopData {
21 has_continue_statement: bool,
22 loop_break_id: u16,
23}
24
25impl LoopData {
26 fn new(loop_break_id: u16) -> Self {
27 Self {
28 has_continue_statement: false,
29 loop_break_id,
30 }
31 }
32
33 fn get_identifier(&self) -> Identifier {
34 Identifier::new(format!("__DARKLUA_CONTINUE_{}", self.loop_break_id))
35 }
36}
37
38impl Processor {
39 fn push_loop(&mut self) {
40 self.loop_identifier_count += 1;
41 self.loop_stack
42 .push(Some(LoopData::new(self.loop_identifier_count)));
43 }
44
45 fn push_no_loop(&mut self) {
46 self.loop_stack.push(None);
47 }
48
49 fn wrap_loop_block_if_needed(&mut self, block: &mut Block) {
50 if let Some(loop_data) = self.loop_stack.pop().flatten() {
51 if !loop_data.has_continue_statement {
52 return;
53 }
54 let mut current_loop_block = mem::take(block);
55
56 if current_loop_block.get_last_statement().is_none() {
57 current_loop_block.push_statement(AssignStatement::from_variable(
58 loop_data.get_identifier(),
59 true,
60 ));
61 }
62
63 let new_block = Block::default()
64 .with_statement(
65 LocalAssignStatement::from_variable(loop_data.get_identifier())
66 .with_value(false),
67 )
68 .with_statement(RepeatStatement::new(current_loop_block, true))
69 .with_statement(IfStatement::create(
70 UnaryExpression::new(UnaryOperator::Not, loop_data.get_identifier()),
71 LastStatement::Break(None),
72 ));
73
74 *block = new_block;
75 }
76 }
77}
78
79impl NodeProcessor for Processor {
80 fn process_generic_for_statement(&mut self, _: &mut GenericForStatement) {
81 self.push_loop();
82 }
83
84 fn process_numeric_for_statement(&mut self, _: &mut NumericForStatement) {
85 self.push_loop();
86 }
87
88 fn process_repeat_statement(&mut self, _: &mut RepeatStatement) {
89 self.push_loop();
90 }
91
92 fn process_while_statement(&mut self, _: &mut WhileStatement) {
93 self.push_loop();
94 }
95
96 fn process_function_statement(&mut self, _: &mut crate::nodes::FunctionStatement) {
97 self.push_no_loop();
98 }
99
100 fn process_function_expression(&mut self, _: &mut crate::nodes::FunctionExpression) {
101 self.push_no_loop();
102 }
103
104 fn process_block(&mut self, block: &mut Block) {
105 let new_statement =
106 block
107 .mutate_last_statement()
108 .and_then(|last_statement| match last_statement {
109 LastStatement::Continue(continue_token) => {
110 if let Some(Some(loop_data)) = self.loop_stack.last_mut() {
111 if !loop_data.has_continue_statement {
112 loop_data.has_continue_statement = true;
113 }
114
115 *last_statement = LastStatement::Break(continue_token.take().map(
116 |mut continue_token| {
117 continue_token.replace_with_content("break");
118 continue_token
119 },
120 ));
121
122 Some(AssignStatement::from_variable(
123 loop_data.get_identifier(),
124 true,
125 ))
126 } else {
127 None
128 }
129 }
130 _ => None,
131 });
132
133 if let Some(statement) = new_statement {
134 block.push_statement(statement);
135 }
136 }
137}
138
139impl NodePostProcessor for Processor {
140 fn process_after_generic_for_statement(&mut self, statement: &mut GenericForStatement) {
141 self.wrap_loop_block_if_needed(statement.mutate_block());
142 }
143
144 fn process_after_numeric_for_statement(&mut self, statement: &mut NumericForStatement) {
145 self.wrap_loop_block_if_needed(statement.mutate_block());
146 }
147
148 fn process_after_repeat_statement(&mut self, statement: &mut RepeatStatement) {
149 self.wrap_loop_block_if_needed(statement.mutate_block());
150 }
151
152 fn process_after_while_statement(&mut self, statement: &mut WhileStatement) {
153 self.wrap_loop_block_if_needed(statement.mutate_block());
154 }
155
156 fn process_after_function_statement(&mut self, _: &mut crate::nodes::FunctionStatement) {
157 self.loop_stack.pop();
158 }
159
160 fn process_after_function_expression(&mut self, _: &mut crate::nodes::FunctionExpression) {
161 self.loop_stack.pop();
162 }
163}
164
165pub const REMOVE_CONTINUE_RULE_NAME: &str = "remove_continue";
166
167#[derive(Debug, Default, PartialEq, Eq)]
169pub struct RemoveContinue {}
170
171impl FlawlessRule for RemoveContinue {
172 fn flawless_process(&self, block: &mut Block, _: &Context) {
173 let mut processor = Processor::default();
174 DefaultPostVisitor::visit_block(block, &mut processor);
175 }
176}
177
178impl RuleConfiguration for RemoveContinue {
179 fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
180 verify_no_rule_properties(&properties)?;
181
182 Ok(())
183 }
184
185 fn get_name(&self) -> &'static str {
186 REMOVE_CONTINUE_RULE_NAME
187 }
188
189 fn serialize_to_properties(&self) -> RuleProperties {
190 RuleProperties::new()
191 }
192}
193
194#[cfg(test)]
195mod test {
196 use super::*;
197 use crate::rules::Rule;
198
199 use insta::assert_json_snapshot;
200
201 fn new_rule() -> RemoveContinue {
202 RemoveContinue::default()
203 }
204
205 #[test]
206 fn serialize_default_rule() {
207 let rule: Box<dyn Rule> = Box::new(new_rule());
208
209 assert_json_snapshot!("default_remove_continue", rule);
210 }
211
212 #[test]
213 fn configure_with_extra_field_error() {
214 let result = json5::from_str::<Box<dyn Rule>>(
215 r#"{
216 rule: 'remove_continue',
217 prop: "something",
218 }"#,
219 );
220 pretty_assertions::assert_eq!(result.unwrap_err().to_string(), "unexpected field 'prop'");
221 }
222}