rew_compiler/
declarations.rs

1use regex::Regex;
2use std::collections::HashMap;
3
4#[derive(Debug, Clone)]
5/// Represents a code declaration with a trigger and replacement logic.
6/// 
7/// A declaration can be a constructor, macro, or definition, and it may also include conditions for matching.
8pub struct Declaration {
9  /// The string that triggers this declaration.
10  pub trigger: String,
11  /// The replacement string or code for this declaration.
12  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>, // New field for ONLYIF prev condition
20  pub condition_next: Option<String>, // New field for ONLYIF next condition
21}
22
23impl Declaration {
24  /// Creates a new `Declaration` instance by parsing the trigger and replacement strings.
25  /// 
26  /// # Arguments
27  /// * `trigger` - The identifier or pattern that triggers this declaration.
28  /// * `replacement` - The code or text that replaces the trigger.
29  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  // New method to parse ONLYIF conditions
51  fn parse_onlyif(replacement: &str) -> (String, Option<String>, Option<String>) {
52    // Check if ONLYIF is present in the replacement
53    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      // Parse the conditions
61      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      // Remove the ONLYIF part from the replacement
76      let replacement_text = re.replace(replacement, "").trim().to_string();
77
78      (replacement_text, prev_condition, next_condition)
79    } else {
80      // No ONLYIF condition found
81      (replacement.to_string(), None, None)
82    }
83  }
84}
85
86#[derive(Default, Clone)]
87/// Engine for managing and processing code declarations.
88pub struct DeclarationEngine {
89  /// Stores global-level declarations accessible across the entire project.
90  pub global_declarations: HashMap<String, Declaration>,
91}
92
93impl DeclarationEngine {
94  /// Parses a single line of code to extract declarations.
95  /// 
96  /// # Arguments
97  /// * `line` - A line of code potentially containing a declaration.
98  /// * `local_declarations` - A mutable reference to the local declarations map.
99  /// 
100  /// # Returns
101  /// * `true` if a declaration was successfully parsed, `false` otherwise.
102  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    // println!("{}", line);
110    // Check if this is a declaration
111    if tokens.len() < 4 {
112      return false;
113    }
114
115    if tokens[0].value != "declare" {
116      return false;
117    }
118
119    // Check for global marker (*)
120    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    // Skip whitespace
129    while token_index < tokens.len() && tokens[token_index].token_type == "WHITESPACE" {
130      token_index += 1;
131    }
132
133    // Get the trigger (should be a string)
134    if token_index >= tokens.len() || tokens[token_index].token_type != "STRING" {
135      return false;
136    }
137
138    // Extract the trigger without quotes
139    let trigger = tokens[token_index]
140      .value
141      .trim_matches('"')
142      .trim_matches('\'');
143    token_index += 1;
144
145    // Skip whitespace
146    while token_index < tokens.len() && tokens[token_index].token_type == "WHITESPACE" {
147      token_index += 1;
148    }
149
150    // Check for equals sign
151    if token_index >= tokens.len() || tokens[token_index].value != "=" {
152      return false;
153    }
154    token_index += 1;
155
156    // Skip whitespace
157    while token_index < tokens.len() && tokens[token_index].token_type == "WHITESPACE" {
158      token_index += 1;
159    }
160
161    // Get the replacement (everything until semicolon or end)
162    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    // println!("Parsed declaration: trigger={}, replacement={}", trigger, replacement);
169
170    // Create the declaration
171    let decl = Declaration::new(trigger, replacement.trim());
172
173    // Print the parsed declaration for debugging
174    // println!("Created declaration: trigger={}, replacement={}, prev={:?}, next={:?}",
175    //          decl.trigger, decl.replacement, decl.condition_prev, decl.condition_next);
176
177    // Generate a unique name for the declaration
178    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      // Handle multiline declarations
198      if in_multiline {
199        multiline_buffer.push_str(line);
200        multiline_buffer.push('\n');
201
202        if trimmed.ends_with(';') {
203          // End of multiline declaration
204          in_multiline = false;
205          self.parse_declaration(&multiline_buffer, &mut local_declarations);
206          multiline_buffer.clear();
207        }
208        continue;
209      }
210
211      // Check for declaration start
212      if trimmed.starts_with("#declare") || trimmed.starts_with("//declare") {
213        if trimmed.ends_with(';') {
214          // Single line declaration
215          self.parse_declaration(trimmed, &mut local_declarations);
216        } else {
217          // Start of multiline declaration
218          in_multiline = true;
219          multiline_buffer = trimmed.to_string();
220          multiline_buffer.push('\n');
221        }
222      }
223    }
224
225    // Handle any remaining multiline declaration
226    if in_multiline && !multiline_buffer.is_empty() {
227      self.parse_declaration(&multiline_buffer, &mut local_declarations);
228    }
229
230    local_declarations
231  }
232}