dalbit_core/modifiers/
remove_redeclared_keys.rs

1use darklua_core::nodes::{
2    AssignStatement, Block, Expression, FunctionCall, FunctionExpression, Identifier,
3    IndexExpression, LocalAssignStatement, ParentheseExpression, ReturnStatement, Statement,
4    StringExpression, TableEntry, TableExpression,
5};
6use darklua_core::process::{DefaultVisitor, Evaluator, LuaValue, NodeProcessor, NodeVisitor};
7use darklua_core::rules::{Context, RuleConfiguration, RuleConfigurationError, RuleProperties};
8
9use super::runtime_identifier::RuntimeIdentifierBuilder;
10use darklua_core::rules::{Rule, RuleProcessResult};
11
12#[derive(Default)]
13struct Processor {
14    evaluator: Evaluator,
15    table_identifier: String,
16    skip_next_table_exp: bool,
17}
18
19impl Processor {
20    fn skip(&mut self, active: bool) {
21        self.skip_next_table_exp = active;
22    }
23}
24
25use std::collections::HashMap;
26use std::fmt::Debug;
27
28impl NodeProcessor for Processor {
29    fn process_expression(&mut self, exp: &mut Expression) {
30        if let Expression::Table(table_exp) = exp {
31            if self.skip_next_table_exp {
32                self.skip(false);
33                return;
34            }
35            let entries = table_exp.mutate_entries();
36            let mut numeral_table = HashMap::new();
37            let mut str_table = HashMap::new();
38            let mut num_index: usize = 0;
39            let mut side_effect_stmts: Vec<Statement> = Vec::new();
40
41            for (i, entry) in entries.iter().enumerate() {
42                match entry {
43                    TableEntry::Index(index_entry) => {
44                        let value = self.evaluator.evaluate(index_entry.get_key());
45                        match value {
46                            LuaValue::Number(lua_index) => {
47                                if lua_index.fract() == 0.0 && lua_index > 0.0 {
48                                    let key = (lua_index as usize) - 1;
49                                    if side_effect_stmts.is_empty() {
50                                        numeral_table.insert(key, i);
51                                    } else {
52                                        let assignment = AssignStatement::from_variable(
53                                            IndexExpression::new(
54                                                Identifier::new(self.table_identifier.as_str()),
55                                                key + 1,
56                                            ),
57                                            index_entry.get_value().clone(),
58                                        );
59                                        side_effect_stmts.push(assignment.into());
60                                    }
61                                }
62                            }
63                            LuaValue::String(key) => {
64                                if side_effect_stmts.is_empty() {
65                                    str_table.insert(key, i);
66                                } else {
67                                    let assignment = AssignStatement::from_variable(
68                                        IndexExpression::new(
69                                            Identifier::new(self.table_identifier.as_str()),
70                                            StringExpression::from_value(key),
71                                        ),
72                                        index_entry.get_value().clone(),
73                                    );
74                                    side_effect_stmts.push(assignment.into());
75                                }
76                            }
77                            LuaValue::Unknown => {
78                                let assignment = AssignStatement::from_variable(
79                                    IndexExpression::new(
80                                        Identifier::new(self.table_identifier.as_str()),
81                                        index_entry.get_key().clone(),
82                                    ),
83                                    index_entry.get_value().clone(),
84                                );
85                                side_effect_stmts.push(assignment.into());
86                            }
87                            _ => (),
88                        }
89                    }
90                    TableEntry::Value(_) => {
91                        numeral_table.insert(num_index, i);
92                        num_index += 1;
93                    }
94                    TableEntry::Field(field_entry) => {
95                        let key = field_entry.get_field().get_name();
96                        str_table.insert(key.to_owned(), i);
97                    }
98                }
99            }
100
101            let mut keys: Vec<_> = numeral_table.keys().collect();
102            keys.sort();
103            let mut new_entries: Vec<TableEntry> = Vec::new();
104
105            for i in keys {
106                let v = numeral_table[i];
107                let entry = &entries[v];
108                let new_entry = match entry {
109                    TableEntry::Index(index_entry) => {
110                        if *i <= num_index {
111                            Some(TableEntry::Value(index_entry.get_value().clone()))
112                        } else {
113                            Some(TableEntry::Index(index_entry.clone()))
114                        }
115                    }
116                    TableEntry::Value(exp) => Some(TableEntry::Value(exp.clone())),
117                    _ => None,
118                };
119                if let Some(new_entry) = new_entry {
120                    new_entries.push(new_entry);
121                }
122            }
123
124            for (_, v) in str_table {
125                let entry = &entries[v];
126                new_entries.push(entry.clone());
127            }
128
129            entries.clear();
130            for ent in new_entries {
131                entries.push(ent);
132            }
133
134            if !side_effect_stmts.is_empty() {
135                let var = Identifier::new(self.table_identifier.as_str());
136                let table_stmt = TableExpression::new(entries.clone());
137                self.skip(true);
138                let local_assign_stmt =
139                    LocalAssignStatement::new(vec![var.clone().into()], vec![table_stmt.into()]);
140                side_effect_stmts.insert(0, local_assign_stmt.into());
141                let return_stmt = ReturnStatement::one(var);
142                let func_block = Block::new(side_effect_stmts, Some(return_stmt.into()));
143                let func = Expression::Function(FunctionExpression::from_block(func_block));
144                let parenthese_func = ParentheseExpression::new(func);
145                let func_call = FunctionCall::from_prefix(parenthese_func);
146                let call_exp = Expression::Call(Box::new(func_call));
147                *exp = call_exp;
148            }
149        }
150    }
151}
152
153pub const REMOVE_REDECLARED_KEYS_RULE_NAME: &str = "remove_redeclared_keys";
154
155/// A rule that removes redeclared keys in table and organize the components of a mixed table
156#[derive(Debug, PartialEq, Eq)]
157pub struct RemoveRedeclaredKeys {
158    runtime_identifier_format: String,
159}
160
161impl Default for RemoveRedeclaredKeys {
162    fn default() -> Self {
163        Self {
164            runtime_identifier_format: "_DARKLUA_REMOVE_REDECLARED_KEYS_{name}{hash}".to_string(),
165        }
166    }
167}
168
169impl Rule for RemoveRedeclaredKeys {
170    fn process(&self, block: &mut Block, _: &Context) -> RuleProcessResult {
171        let var_builder = RuntimeIdentifierBuilder::new(
172            self.runtime_identifier_format.as_str(),
173            format!("{block:?}").as_bytes(),
174            None,
175        )?;
176        let mut processor = Processor {
177            evaluator: Evaluator::default(),
178            table_identifier: var_builder.build("tbl")?,
179            skip_next_table_exp: false,
180        };
181        DefaultVisitor::visit_block(block, &mut processor);
182        Ok(())
183    }
184}
185
186impl RuleConfiguration for RemoveRedeclaredKeys {
187    fn configure(&mut self, _: RuleProperties) -> Result<(), RuleConfigurationError> {
188        Ok(())
189    }
190
191    fn get_name(&self) -> &'static str {
192        REMOVE_REDECLARED_KEYS_RULE_NAME
193    }
194
195    fn serialize_to_properties(&self) -> RuleProperties {
196        RuleProperties::new()
197    }
198}