rew_compiler/
declarations.rs1use regex::Regex;
2use std::collections::HashMap;
3
4#[derive(Debug, Clone)]
5pub struct Declaration {
9 pub trigger: String,
11 pub replacement: String,
13 #[allow(unused)]
14 pub is_definition: bool,
15 #[allow(unused)]
16 pub is_constructor: bool,
17 #[allow(unused)]
18 pub is_macro: bool,
19 pub condition_prev: Option<String>, pub condition_next: Option<String>, }
22
23impl Declaration {
24 pub fn new(trigger: &str, replacement: &str) -> Self {
30 let is_definition = trigger.starts_with('=');
31 let is_constructor = trigger.ends_with('*');
32 let is_macro = trigger.ends_with('!');
33
34 let (replacement_text, condition_prev, condition_next) = Self::parse_onlyif(replacement);
35
36 Self {
37 trigger: trigger
38 .trim_end_matches(['*', '!'].as_ref())
39 .trim_start_matches(['='].as_ref())
40 .to_string(),
41 replacement: replacement_text,
42 is_definition,
43 is_constructor,
44 is_macro,
45 condition_prev,
46 condition_next,
47 }
48 }
49
50 fn parse_onlyif(replacement: &str) -> (String, Option<String>, Option<String>) {
52 let re = Regex::new(r"ONLYIF\(([^)]+)\)").unwrap();
54
55 if let Some(caps) = re.captures(replacement) {
56 let conditions = &caps[1];
57 let mut prev_condition = None;
58 let mut next_condition = None;
59
60 for condition in conditions.split(',') {
62 let parts: Vec<&str> = condition.split('=').collect();
63 if parts.len() == 2 {
64 let key = parts[0].trim();
65 let value = parts[1].trim().trim_matches('"').trim_matches('\'');
66
67 if key == "prev" {
68 prev_condition = Some(value.to_string());
69 } else if key == "next" {
70 next_condition = Some(value.to_string());
71 }
72 }
73 }
74
75 let replacement_text = re.replace(replacement, "").trim().to_string();
77
78 (replacement_text, prev_condition, next_condition)
79 } else {
80 (replacement.to_string(), None, None)
82 }
83 }
84}
85
86#[derive(Default, Clone)]
87pub struct DeclarationEngine {
89 pub global_declarations: HashMap<String, Declaration>,
91}
92
93impl DeclarationEngine {
94 pub fn parse_declaration(
103 &mut self,
104 line: &str,
105 local_declarations: &mut HashMap<String, Declaration>,
106 ) -> bool {
107 let tokens =
108 crate::compiler::tokenize_coffee_script(line.replace("#", "").replace("//", "").as_str());
109 if tokens.len() < 4 {
112 return false;
113 }
114
115 if tokens[0].value != "declare" {
116 return false;
117 }
118
119 let mut token_index = 1;
121 let is_global = if token_index < tokens.len() && tokens[token_index].value == "*" {
122 token_index += 1;
123 true
124 } else {
125 false
126 };
127
128 while token_index < tokens.len() && tokens[token_index].token_type == "WHITESPACE" {
130 token_index += 1;
131 }
132
133 if token_index >= tokens.len() || tokens[token_index].token_type != "STRING" {
135 return false;
136 }
137
138 let trigger = tokens[token_index]
140 .value
141 .trim_matches('"')
142 .trim_matches('\'');
143 token_index += 1;
144
145 while token_index < tokens.len() && tokens[token_index].token_type == "WHITESPACE" {
147 token_index += 1;
148 }
149
150 if token_index >= tokens.len() || tokens[token_index].value != "=" {
152 return false;
153 }
154 token_index += 1;
155
156 while token_index < tokens.len() && tokens[token_index].token_type == "WHITESPACE" {
158 token_index += 1;
159 }
160
161 let mut replacement = String::new();
163 while token_index < tokens.len() && tokens[token_index].value != ";" {
164 replacement.push_str(&tokens[token_index].value);
165 token_index += 1;
166 }
167
168 let decl = Declaration::new(trigger, replacement.trim());
172
173 let name = format!("decl_{}", uuid::Uuid::new_v4().to_string().replace("-", ""));
179
180 if is_global {
181 self.global_declarations.insert(name, decl);
182 } else {
183 local_declarations.insert(name, decl);
184 }
185
186 true
187 }
188
189 pub fn process_script(&mut self, script: &str) -> HashMap<String, Declaration> {
190 let mut local_declarations = HashMap::new();
191 let mut in_multiline = false;
192 let mut multiline_buffer = String::new();
193
194 for line in script.lines() {
195 let trimmed = line.trim();
196
197 if in_multiline {
199 multiline_buffer.push_str(line);
200 multiline_buffer.push('\n');
201
202 if trimmed.ends_with(';') {
203 in_multiline = false;
205 self.parse_declaration(&multiline_buffer, &mut local_declarations);
206 multiline_buffer.clear();
207 }
208 continue;
209 }
210
211 if trimmed.starts_with("#declare") || trimmed.starts_with("//declare") {
213 if trimmed.ends_with(';') {
214 self.parse_declaration(trimmed, &mut local_declarations);
216 } else {
217 in_multiline = true;
219 multiline_buffer = trimmed.to_string();
220 multiline_buffer.push('\n');
221 }
222 }
223 }
224
225 if in_multiline && !multiline_buffer.is_empty() {
227 self.parse_declaration(&multiline_buffer, &mut local_declarations);
228 }
229
230 local_declarations
231 }
232}