devalang_core/core/preprocessor/
loader.rs1use std::{ collections::{ HashMap, HashSet }, path::Path };
2use crate::{
3 core::{
4 error::ErrorHandler,
5 lexer::{ token::Token, Lexer },
6 parser::{ driver::Parser, statement::{ Statement, StatementKind } },
7 preprocessor::{ module::Module, processor::process_modules },
8 shared::{ bank::BankFile, value::Value },
9 store::global::GlobalStore,
10 utils::path::normalize_path,
11 },
12 utils::logger::Logger,
13};
14use crate::core::preprocessor::resolver::driver::{
15 resolve_all_modules,
16 resolve_and_flatten_all_modules,
17};
18use crate::core::utils::path::resolve_relative_path;
19
20pub struct ModuleLoader {
21 pub entry: String,
22 pub output: String,
23 pub base_dir: String,
24}
25
26impl ModuleLoader {
27 pub fn new(entry: &str, output: &str) -> Self {
28 let base_dir = Path::new(entry)
29 .parent()
30 .unwrap_or(Path::new(""))
31 .to_string_lossy()
32 .replace('\\', "/");
33
34 Self {
35 entry: entry.to_string(),
36 output: output.to_string(),
37 base_dir: base_dir,
38 }
39 }
40
41 pub fn from_raw_source(
42 entry_path: &str,
43 output_path: &str,
44 content: &str,
45 global_store: &mut GlobalStore
46 ) -> Self {
47 let normalized_entry_path = normalize_path(entry_path);
48
49 let mut module = Module::new(&entry_path);
50 module.content = content.to_string();
51
52 global_store.insert_module(normalized_entry_path.to_string(), module);
53
54 Self {
55 entry: normalized_entry_path.to_string(),
56 output: output_path.to_string(),
57 base_dir: "".to_string(),
58 }
59 }
60
61 pub fn extract_statements_map(
62 &self,
63 global_store: &GlobalStore
64 ) -> HashMap<String, Vec<Statement>> {
65 global_store.modules
66 .iter()
67 .map(|(path, module)| (path.clone(), module.statements.clone()))
68 .collect()
69 }
70
71 pub fn load_single_module(&self, global_store: &mut GlobalStore) -> Result<Module, String> {
72 let mut module = global_store.modules
73 .remove(&self.entry)
74 .ok_or_else(|| format!("Module not found in store for path: {}", self.entry))?;
75
76 let lexer = Lexer::new();
78 let tokens = lexer
79 .lex_from_source(&module.content)
80 .map_err(|e| format!("Lexer failed: {}", e))?;
81
82 module.tokens = tokens.clone();
83
84 let mut parser = Parser::new();
86 parser.set_current_module(self.entry.clone());
87 let statements = parser.parse_tokens(tokens, global_store);
88 module.statements = statements;
89
90 if let Err(e) = self.inject_bank_triggers(&mut module, "808") {
92 return Err(format!("Failed to inject bank triggers: {}", e));
93 }
94
95 global_store.modules.insert(self.entry.clone(), module.clone());
96
97 let mut error_handler = ErrorHandler::new();
99 error_handler.detect_from_statements(&mut parser, &module.statements);
100
101 Ok(module)
102 }
103
104 pub fn load_wasm_module(&self, global_store: &mut GlobalStore) -> Result<(), String> {
105 let module = {
107 let module_ref = global_store.modules
108 .get(&self.entry)
109 .ok_or_else(|| format!("❌ Module not found for path: {}", self.entry))?;
110
111 Module::from_existing(&self.entry, module_ref.content.clone())
112 };
113
114 let lexer = Lexer::new();
116 let tokens = lexer
117 .lex_from_source(&module.content)
118 .map_err(|e| format!("Lexer failed: {}", e))?;
119
120 let mut parser = Parser::new();
122 parser.set_current_module(self.entry.clone());
123
124 let statements = parser.parse_tokens(tokens.clone(), global_store);
125
126 let mut updated_module = module;
127 updated_module.tokens = tokens;
128 updated_module.statements = statements;
129
130 if let Err(e) = self.inject_bank_triggers(&mut updated_module, "808") {
132 return Err(format!("Failed to inject bank triggers: {}", e));
133 }
134
135 let mut error_handler = ErrorHandler::new();
137 error_handler.detect_from_statements(&mut parser, &updated_module.statements);
138
139 global_store.modules.insert(self.entry.clone(), updated_module);
141
142 Ok(())
143 }
144
145 #[cfg(feature = "cli")]
146 pub fn load_all_modules(
147 &self,
148 global_store: &mut GlobalStore
149 ) -> (HashMap<String, Vec<Token>>, HashMap<String, Vec<Statement>>) {
150 let tokens_by_module = self.load_module_recursively(&self.entry, global_store);
152
153 process_modules(self, global_store);
155 resolve_all_modules(self, global_store);
156
157 let statements_by_module = resolve_and_flatten_all_modules(global_store);
159
160 (tokens_by_module, statements_by_module)
161 }
162
163 #[cfg(feature = "cli")]
164 fn load_module_recursively(
165 &self,
166 raw_path: &str,
167 global_store: &mut GlobalStore
168 ) -> HashMap<String, Vec<Token>> {
169 let path = normalize_path(raw_path);
170
171 if global_store.modules.contains_key(&path) {
173 return HashMap::new();
174 }
175
176 let lexer = Lexer::new();
177 let tokens = lexer.lex_tokens(&path);
178
179 let mut parser = Parser::new();
180 parser.set_current_module(path.clone());
181
182 let statements = parser.parse_tokens(tokens.clone(), global_store);
183
184 let mut module = Module::new(&path);
186 module.tokens = tokens.clone();
187 module.statements = statements.clone();
188
189 for bank_name in self.extract_bank_names(&statements) {
191 let _ = self.inject_bank_triggers(&mut module, &bank_name);
192 }
193
194 global_store.variables.variables.extend(module.variable_table.variables.clone());
196 global_store.functions.functions.extend(module.function_table.functions.clone());
197
198 global_store.insert_module(path.clone(), module);
200
201 self.load_module_imports(&path, global_store);
203
204 let mut error_handler = ErrorHandler::new();
206 error_handler.detect_from_statements(&mut parser, &statements);
207
208 if error_handler.has_errors() {
209 let logger = Logger::new();
210 for error in error_handler.get_errors() {
211 let trace = format!("{}:{}:{}", path, error.line, error.column);
212 logger.log_error_with_stacktrace(&error.message, &trace);
213 }
214 }
215
216 global_store.modules
218 .iter()
219 .map(|(p, m)| (p.clone(), m.tokens.clone()))
220 .collect()
221 }
222
223 #[cfg(feature = "cli")]
224 fn load_module_imports(&self, path: &String, global_store: &mut GlobalStore) {
225 let import_paths: Vec<String> = {
226 let current_module = match global_store.modules.get(path) {
227 Some(module) => module,
228 None => {
229 eprintln!("[warn] Cannot resolve imports: module '{}' not found in store", path);
230 return;
231 }
232 };
233
234 current_module.statements
235 .iter()
236 .filter_map(|stmt| {
237 if let StatementKind::Import { source, .. } = &stmt.kind {
238 Some(source.clone())
239 } else {
240 None
241 }
242 })
243 .collect()
244 };
245
246 for import_path in import_paths {
247 let resolved = resolve_relative_path(path, &import_path);
248 self.load_module_recursively(&resolved, global_store);
249 }
250 }
251
252 pub fn inject_bank_triggers(
253 &self,
254 module: &mut Module,
255 bank_name: &str
256 ) -> Result<Module, String> {
257 let alias = bank_name.split('.').last().unwrap_or(bank_name);
258
259 let bank_path = Path::new("./.deva/bank").join(bank_name);
260 let bank_toml_path = bank_path.join("bank.toml");
261
262 if !bank_toml_path.exists() {
263 return Ok(module.clone());
264 }
265
266 let content = std::fs
267 ::read_to_string(&bank_toml_path)
268 .map_err(|e| format!("Failed to read '{}': {}", bank_toml_path.display(), e))?;
269
270 let parsed_bankfile: BankFile = toml
271 ::from_str(&content)
272 .map_err(|e| format!("Failed to parse '{}': {}", bank_toml_path.display(), e))?;
273
274 let mut bank_map = HashMap::new();
275
276 for bank_trigger in parsed_bankfile.triggers.unwrap_or_default() {
277 let trigger_name = bank_trigger.name.clone().replace("./", "");
278 let bank_trigger_path = format!("devalang://bank/{}/{}", bank_name, trigger_name);
279
280 bank_map.insert(bank_trigger.name.clone(), Value::String(bank_trigger_path.clone()));
281
282 if module.variable_table.variables.contains_key(alias) {
283 eprintln!(
284 "⚠️ Trigger '{}' already defined in module '{}', skipping injection.",
285 alias,
286 module.path
287 );
288 continue;
289 }
290
291 module.variable_table.set(
292 format!("{}.{}", alias, bank_trigger.name),
293 Value::String(bank_trigger_path.clone())
294 );
295 }
296
297 module.variable_table.set(alias.to_string(), Value::Map(bank_map));
299
300 Ok(module.clone())
301 }
302
303 fn extract_bank_names(&self, statements: &[Statement]) -> HashSet<String> {
304 let mut banks = HashSet::new();
305
306 for stmt in statements {
307 match &stmt.kind {
308 StatementKind::Bank => {
310 if let Value::String(name) = &stmt.value {
311 banks.insert(name.clone());
312 }
313 if let Value::Number(num) = &stmt.value {
314 banks.insert(num.to_string());
315 }
316 if let Value::Identifier(name) = &stmt.value {
317 banks.insert(name.clone());
318 }
319 }
320 _ => {}
321 }
322 }
323
324 banks
325 }
326}