devalang_core/core/preprocessor/resolver/
driver.rs

1use crate::core::{
2    parser::statement::{Statement, StatementKind},
3    preprocessor::{
4        loader::ModuleLoader,
5        module::Module,
6        resolver::{
7            bank::resolve_bank, call::resolve_call, condition::resolve_condition,
8        function::resolve_function, group::resolve_group, pattern::resolve_pattern, let_::resolve_let,
9            loop_::resolve_loop, spawn::resolve_spawn, tempo::resolve_tempo,
10            trigger::resolve_trigger,
11        },
12    },
13    store::global::GlobalStore,
14};
15use devalang_types::Value;
16use devalang_utils::logger::Logger;
17use devalang_utils::logger::LogLevel;
18use std::collections::HashMap;
19
20pub fn resolve_all_modules(module_loader: &ModuleLoader, global_store: &mut GlobalStore) {
21    for _module in global_store.clone().modules.values_mut() {
22        resolve_imports(module_loader, global_store);
23    }
24}
25
26pub fn resolve_statement(
27    stmt: &Statement,
28    module: &Module,
29    path: &str,
30    global_store: &mut GlobalStore,
31) -> Statement {
32    match &stmt.kind {
33        StatementKind::On { event, args, body } => {
34            let resolved_body: Vec<Statement> = body
35                .iter()
36                .map(|s| resolve_statement(s, module, path, global_store))
37                .collect();
38            Statement {
39                kind: StatementKind::On {
40                    event: event.clone(),
41                    args: args.clone(),
42                    body: resolved_body,
43                },
44                value: resolve_value(&stmt.value, module, global_store),
45                ..stmt.clone()
46            }
47        }
48        StatementKind::Emit { event, payload: _ } => Statement {
49            kind: StatementKind::Emit {
50                event: event.clone(),
51                payload: Some(resolve_value(&stmt.value, module, global_store)),
52            },
53            value: resolve_value(&stmt.value, module, global_store),
54            ..stmt.clone()
55        },
56        StatementKind::Trigger {
57            entity,
58            duration,
59            effects,
60        } => resolve_trigger(
61            stmt,
62            entity,
63            &mut duration.clone(),
64            effects.clone(),
65            module,
66            path,
67            global_store,
68        ),
69        StatementKind::If => resolve_condition(stmt, module, path, global_store),
70                StatementKind::Group => resolve_group(stmt, module, path, global_store),
71                StatementKind::Pattern { .. } => resolve_pattern(stmt, module, path, global_store),
72        StatementKind::Call { name, args } => {
73            resolve_call(stmt, name.clone(), args.clone(), module, path, global_store)
74        }
75        StatementKind::Spawn { name, args } => {
76            resolve_spawn(stmt, name.clone(), args.clone(), module, path, global_store)
77        }
78        StatementKind::Bank { .. } => resolve_bank(stmt, module, path, global_store),
79        StatementKind::Tempo => resolve_tempo(stmt, module, path, global_store),
80        StatementKind::Loop => resolve_loop(stmt, module, path, global_store),
81        StatementKind::Let { name, .. } => resolve_let(stmt, name, module, path, global_store),
82
83        _ => {
84            let resolved_value = resolve_value(&stmt.value, module, global_store);
85
86            Statement {
87                value: resolved_value,
88                ..stmt.clone()
89            }
90        }
91    }
92}
93
94fn resolve_value(value: &Value, module: &Module, global_store: &mut GlobalStore) -> Value {
95    let logger = Logger::new();
96    match value {
97        Value::Identifier(name) => {
98            if let Some(original_val) = module.variable_table.get(name) {
99                return resolve_value(original_val, module, global_store);
100            }
101
102            if let Some(export_val) = find_export_value(name, global_store) {
103                return resolve_value(&export_val, module, global_store);
104            }
105
106            // Leave unresolved identifiers as-is; they might be runtime-bound (e.g., foreach vars)
107            Value::Identifier(name.clone())
108        }
109
110        Value::String(s) => Value::String(s.clone()),
111
112        Value::Beat(beat_str) => {
113            logger.log_message(LogLevel::Warning, &format!("[warn] '{:?}': unresolved beat '{}'", module.path, beat_str));
114            Value::Beat(beat_str.clone())
115        }
116
117        Value::Map(map) => {
118            let mut resolved = HashMap::new();
119            for (k, v) in map {
120                resolved.insert(k.clone(), resolve_value(v, module, global_store));
121            }
122            Value::Map(resolved)
123        }
124
125        Value::Block(stmts) => {
126            let resolved_stmts = stmts
127                .iter()
128                .map(|stmt| resolve_statement(stmt, module, &module.path, global_store))
129                .collect();
130            Value::Block(resolved_stmts)
131        }
132
133        other => other.clone(),
134    }
135}
136
137fn find_export_value(name: &str, global_store: &GlobalStore) -> Option<Value> {
138    for module in global_store.modules.values() {
139        if let Some(val) = module.export_table.get_export(name) {
140            return Some(val.clone());
141        }
142    }
143    None
144}
145
146pub fn resolve_imports(_module_loader: &ModuleLoader, global_store: &mut GlobalStore) {
147    let logger = Logger::new();
148    for (module_path, module) in global_store.clone().modules.iter_mut() {
149        for (name, source_path) in &module.import_table.imports {
150            match source_path {
151                Value::String(source_path) => {
152                    if let Some(source_module) = global_store.modules.get(source_path) {
153                        if let Some(value) = source_module.export_table.get_export(name) {
154                            module.variable_table.set(name.clone(), value.clone());
155                        } else {
156                            logger.log_message(LogLevel::Warning, &format!("[warn] '{module_path}': '{name}' not found in exports of '{source_path}'"));
157                        }
158                    } else {
159                        logger.log_message(LogLevel::Warning, &format!("[warn] '{module_path}': cannot find source module '{source_path}'"));
160                    }
161                }
162                _ => {
163                    logger.log_message(LogLevel::Warning, &format!("[warn] '{module_path}': expected string for import source, found {:?}", source_path));
164                }
165            }
166        }
167    }
168}
169
170pub fn resolve_and_flatten_all_modules(
171    global_store: &mut GlobalStore,
172) -> HashMap<String, Vec<Statement>> {
173    let logger = Logger::new();
174    let snapshot = global_store.clone();
175
176    // 1. Imports resolution
177    for (module_path, module) in global_store.modules.iter_mut() {
178        for (name, source_path) in &module.import_table.imports {
179            if let Value::String(source_path_str) = source_path {
180                match snapshot.modules.get(source_path_str) {
181                    Some(source_module) => {
182                        if let Some(value) = source_module.export_table.get_export(name) {
183                            module.variable_table.set(name.clone(), value.clone());
184                        } else {
185                            logger.log_error_with_stacktrace(
186                                &format!("'{name}' not found in exports of '{source_path_str}'"),
187                                module_path,
188                            );
189                        }
190                    }
191                    None => {
192                        logger.log_error_with_stacktrace(
193                            &format!("Cannot find source module '{source_path_str}'"),
194                            module_path,
195                        );
196                    }
197                }
198            } else {
199                logger.log_error_with_stacktrace(
200                    &format!("Expected string for import source, found {:?}", source_path),
201                    module_path,
202                );
203            }
204        }
205    }
206
207    // 2. Statements resolution
208    let mut resolved_map: HashMap<String, Vec<Statement>> = HashMap::new();
209    for (path, module) in global_store.modules.clone() {
210        let mut resolved = Vec::new();
211
212        for stmt in &module.statements {
213            let stmt = stmt.clone();
214
215            match &stmt.kind {
216                StatementKind::Let { name } => {
217                    let resolved_stmt = resolve_let(&stmt, name, &module, &path, global_store);
218                    resolved.push(resolved_stmt);
219                }
220
221                StatementKind::Trigger {
222                    entity,
223                    duration,
224                    effects,
225                } => {
226                    let resolved_stmt = resolve_trigger(
227                        &stmt,
228                        entity.as_str(),
229                        &mut duration.clone(),
230                        effects.clone(),
231                        &module,
232                        &path,
233                        global_store,
234                    );
235                    resolved.push(resolved_stmt);
236                }
237
238                StatementKind::Loop => {
239                    let resolved_stmt = resolve_loop(&stmt, &module, &path, global_store);
240                    resolved.push(resolved_stmt);
241                }
242
243                StatementKind::Bank { .. } => {
244                    let resolved_stmt = resolve_bank(&stmt, &module, &path, global_store);
245                    resolved.push(resolved_stmt);
246                }
247
248                StatementKind::Tempo => {
249                    let resolved_stmt = resolve_tempo(&stmt, &module, &path, global_store);
250                    resolved.push(resolved_stmt);
251                }
252
253                StatementKind::Import { .. } | StatementKind::Export { .. } => {
254                    resolved.push(stmt.clone());
255                }
256
257                StatementKind::Call { name, args } => {
258                    let resolved_stmt = resolve_call(
259                        &stmt,
260                        name.clone(),
261                        args.clone(),
262                        &module,
263                        &path,
264                        global_store,
265                    );
266                    resolved.push(resolved_stmt);
267                }
268
269                StatementKind::Spawn { name, args } => {
270                    let resolved_stmt = resolve_spawn(
271                        &stmt,
272                        name.clone(),
273                        args.clone(),
274                        &module,
275                        &path,
276                        global_store,
277                    );
278                    resolved.push(resolved_stmt);
279                }
280
281                StatementKind::Group => {
282                    let resolved_stmt = resolve_group(&stmt, &module, &path, global_store);
283                    resolved.push(resolved_stmt);
284                }
285
286                StatementKind::Pattern { .. } => {
287                    let resolved_stmt = resolve_pattern(&stmt, &module, &path, global_store);
288                    resolved.push(resolved_stmt);
289                }
290
291                StatementKind::Function {
292                    name: _,
293                    parameters: _,
294                    body: _,
295                } => {
296                    let resolved_function = resolve_function(&stmt, &module, &path, global_store);
297                    resolved.push(resolved_function);
298                }
299
300                _ => {
301                    resolved.push(stmt);
302                }
303            }
304        }
305
306        resolved_map.insert(path.clone(), resolved);
307    }
308
309    resolved_map
310}