@devaloop/devalang 0.0.1-alpha.12 → 0.0.1-alpha.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/.devalang +8 -9
  2. package/Cargo.toml +8 -3
  3. package/README.md +36 -34
  4. package/docs/CHANGELOG.md +65 -1
  5. package/docs/CONTRIBUTING.md +1 -0
  6. package/docs/ROADMAP.md +2 -2
  7. package/docs/TODO.md +6 -5
  8. package/examples/bank.deva +2 -4
  9. package/examples/function.deva +15 -0
  10. package/examples/index.deva +25 -14
  11. package/out-tsc/bin/devalang.exe +0 -0
  12. package/package.json +6 -6
  13. package/project-version.json +3 -3
  14. package/rust/cli/bank.rs +2 -1
  15. package/rust/cli/build.rs +76 -14
  16. package/rust/cli/check.rs +71 -8
  17. package/rust/cli/driver.rs +40 -28
  18. package/rust/cli/install.rs +22 -7
  19. package/rust/cli/login.rs +134 -0
  20. package/rust/cli/mod.rs +2 -1
  21. package/rust/cli/play.rs +45 -20
  22. package/rust/common/api.rs +8 -0
  23. package/rust/common/cdn.rs +2 -5
  24. package/rust/common/mod.rs +3 -1
  25. package/rust/common/sso.rs +8 -0
  26. package/rust/config/driver.rs +19 -1
  27. package/rust/config/loader.rs +56 -10
  28. package/rust/core/audio/engine.rs +254 -91
  29. package/rust/core/audio/interpreter/arrow_call.rs +34 -15
  30. package/rust/core/audio/interpreter/call.rs +72 -47
  31. package/rust/core/audio/interpreter/condition.rs +14 -12
  32. package/rust/core/audio/interpreter/driver.rs +90 -128
  33. package/rust/core/audio/interpreter/function.rs +21 -0
  34. package/rust/core/audio/interpreter/load.rs +1 -1
  35. package/rust/core/audio/interpreter/loop_.rs +24 -18
  36. package/rust/core/audio/interpreter/mod.rs +2 -1
  37. package/rust/core/audio/interpreter/sleep.rs +0 -6
  38. package/rust/core/audio/interpreter/spawn.rs +78 -60
  39. package/rust/core/audio/interpreter/trigger.rs +157 -70
  40. package/rust/core/audio/loader/trigger.rs +37 -4
  41. package/rust/core/audio/player.rs +20 -10
  42. package/rust/core/audio/renderer.rs +24 -25
  43. package/rust/core/builder/mod.rs +11 -6
  44. package/rust/core/debugger/mod.rs +2 -0
  45. package/rust/core/debugger/module.rs +47 -0
  46. package/rust/core/debugger/store.rs +25 -11
  47. package/rust/core/error/mod.rs +6 -0
  48. package/rust/core/lexer/handler/driver.rs +23 -1
  49. package/rust/core/lexer/handler/identifier.rs +1 -0
  50. package/rust/core/lexer/handler/indent.rs +16 -2
  51. package/rust/core/lexer/handler/mod.rs +1 -0
  52. package/rust/core/lexer/handler/parenthesis.rs +41 -0
  53. package/rust/core/lexer/token.rs +4 -0
  54. package/rust/core/mod.rs +2 -1
  55. package/rust/core/parser/driver.rs +47 -4
  56. package/rust/core/parser/handler/arrow_call.rs +78 -18
  57. package/rust/core/parser/handler/bank.rs +35 -7
  58. package/rust/core/parser/handler/dot.rs +81 -123
  59. package/rust/core/parser/handler/identifier/call.rs +69 -22
  60. package/rust/core/parser/handler/identifier/function.rs +92 -0
  61. package/rust/core/parser/handler/identifier/let_.rs +13 -19
  62. package/rust/core/parser/handler/identifier/mod.rs +1 -0
  63. package/rust/core/parser/handler/identifier/spawn.rs +74 -27
  64. package/rust/core/parser/statement.rs +16 -4
  65. package/rust/core/plugin/loader.rs +48 -0
  66. package/rust/core/plugin/mod.rs +1 -0
  67. package/rust/core/preprocessor/loader.rs +50 -32
  68. package/rust/core/preprocessor/module.rs +3 -1
  69. package/rust/core/preprocessor/processor.rs +26 -1
  70. package/rust/core/preprocessor/resolver/call.rs +61 -84
  71. package/rust/core/preprocessor/resolver/condition.rs +11 -6
  72. package/rust/core/preprocessor/resolver/driver.rs +52 -6
  73. package/rust/core/preprocessor/resolver/function.rs +78 -0
  74. package/rust/core/preprocessor/resolver/group.rs +43 -13
  75. package/rust/core/preprocessor/resolver/let_.rs +7 -10
  76. package/rust/core/preprocessor/resolver/mod.rs +2 -1
  77. package/rust/core/preprocessor/resolver/spawn.rs +64 -30
  78. package/rust/core/preprocessor/resolver/trigger.rs +7 -3
  79. package/rust/core/preprocessor/resolver/value.rs +10 -1
  80. package/rust/core/shared/value.rs +4 -1
  81. package/rust/core/store/function.rs +34 -0
  82. package/rust/core/store/global.rs +9 -10
  83. package/rust/core/store/mod.rs +2 -1
  84. package/rust/core/store/variable.rs +6 -0
  85. package/rust/installer/addon.rs +80 -0
  86. package/rust/installer/bank.rs +24 -14
  87. package/rust/installer/mod.rs +4 -1
  88. package/rust/installer/plugin.rs +55 -0
  89. package/rust/lib.rs +10 -7
  90. package/rust/main.rs +32 -9
  91. package/rust/utils/logger.rs +16 -0
  92. package/rust/utils/mod.rs +45 -1
  93. package/rust/utils/spinner.rs +2 -4
@@ -8,34 +8,81 @@ use crate::core::{
8
8
  pub fn parse_spawn_token(
9
9
  parser: &mut Parser,
10
10
  current_token: Token,
11
- global_store: &mut GlobalStore
11
+ _global_store: &mut GlobalStore
12
12
  ) -> Statement {
13
- parser.advance(); // consume "spawn"
14
-
15
- let value = if let Some(token) = parser.peek_clone() {
16
- parser.advance();
17
- match token.kind {
18
- TokenKind::Identifier => Value::Identifier(token.lexeme.clone()),
19
- TokenKind::String => Value::String(token.lexeme.clone()),
20
- _ => {
21
- return Statement::error(
22
- token,
23
- "Expected identifier or string after 'spawn'".to_string()
24
- );
25
- }
26
- }
27
- } else {
13
+ parser.advance(); // consume "spawn"
14
+
15
+ // Expect function name
16
+ let name_token = match parser.peek_clone() {
17
+ Some(t) => t,
18
+ None => {
28
19
  return Statement::error(
29
20
  current_token,
30
- "Expected identifier or string after 'spawn'".to_string()
21
+ "Expected function name after 'spawn'".to_string()
31
22
  );
32
- };
33
-
34
- return Statement {
35
- kind: StatementKind::Spawn,
36
- value,
37
- indent: current_token.indent,
38
- line: current_token.line,
39
- column: current_token.column,
40
- };
41
- }
23
+ }
24
+ };
25
+
26
+ if name_token.kind != TokenKind::Identifier {
27
+ return Statement::error(
28
+ name_token,
29
+ "Expected function name to be an identifier".to_string()
30
+ );
31
+ }
32
+
33
+ let func_name = name_token.lexeme.clone();
34
+ parser.advance(); // consume function name
35
+
36
+ // Expect '('
37
+ let mut args: Vec<Value> = Vec::new();
38
+ if let Some(open_paren) = parser.peek_clone() {
39
+ if open_paren.kind == TokenKind::LParen {
40
+ parser.advance(); // consume '('
41
+
42
+ // Collect args until ')'
43
+ while let Some(token) = parser.peek_clone() {
44
+ if token.kind == TokenKind::RParen {
45
+ parser.advance(); // consume ')'
46
+ break;
47
+ }
48
+
49
+ match token.kind {
50
+ TokenKind::Number => {
51
+ if let Ok(num) = token.lexeme.parse::<f32>() {
52
+ args.push(Value::Number(num));
53
+ }
54
+ parser.advance();
55
+ }
56
+ TokenKind::String => {
57
+ args.push(Value::String(token.lexeme.clone()));
58
+ parser.advance();
59
+ }
60
+ TokenKind::Identifier => {
61
+ args.push(Value::Identifier(token.lexeme.clone()));
62
+ parser.advance();
63
+ }
64
+ TokenKind::Comma => {
65
+ parser.advance(); // skip comma
66
+ }
67
+ _ => {
68
+ return Statement::error(
69
+ token,
70
+ "Unexpected token in spawn arguments".to_string()
71
+ );
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+
78
+ Statement {
79
+ kind: StatementKind::Spawn {
80
+ name: func_name,
81
+ args,
82
+ },
83
+ value: Value::Null,
84
+ indent: current_token.indent,
85
+ line: current_token.line,
86
+ column: current_token.column,
87
+ }
88
+ }
@@ -49,6 +49,11 @@ pub enum StatementKind {
49
49
  method: String,
50
50
  args: Vec<Value>,
51
51
  },
52
+ Function {
53
+ name: String,
54
+ parameters: Vec<String>,
55
+ body: Vec<Statement>,
56
+ },
52
57
 
53
58
  // ───── Instruments ─────
54
59
  Synth,
@@ -57,12 +62,19 @@ pub enum StatementKind {
57
62
  Trigger {
58
63
  entity: String,
59
64
  duration: Duration,
65
+ effects: Option<Value>,
60
66
  },
61
67
  Sleep,
62
- Call,
63
- Spawn,
68
+ Call {
69
+ name: String,
70
+ args: Vec<Value>,
71
+ },
72
+ Spawn {
73
+ name: String,
74
+ args: Vec<Value>,
75
+ },
64
76
  Loop,
65
-
77
+
66
78
  // ───── Structure & Logic ─────
67
79
  Group,
68
80
 
@@ -93,4 +105,4 @@ pub enum StatementKind {
93
105
  Error {
94
106
  message: String,
95
107
  },
96
- }
108
+ }
@@ -0,0 +1,48 @@
1
+ use std::path::Path;
2
+ use serde::Deserialize;
3
+
4
+ #[derive(Debug, Deserialize, Clone)]
5
+ pub struct PluginInfo {
6
+ pub name: String,
7
+ pub version: Option<String>,
8
+ pub description: Option<String>,
9
+ pub author: Option<String>,
10
+ }
11
+
12
+ #[derive(Debug, Deserialize, Clone)]
13
+ pub struct PluginFile {
14
+ pub plugin: PluginInfo,
15
+ }
16
+
17
+ pub fn load_plugin(name: &str) -> Result<(PluginInfo, Vec<u8>), String> {
18
+ let root = Path::new(env!("CARGO_MANIFEST_DIR"));
19
+ let plugin_dir = root.join(".deva").join("plugin").join(name);
20
+ let toml_path = plugin_dir.join("plugin.toml");
21
+ let wasm_path = plugin_dir.join(format!("{}_bg.wasm", name));
22
+
23
+ if !toml_path.exists() {
24
+ return Err(format!("❌ Plugin file not found: {}", toml_path.display()));
25
+ }
26
+ if !wasm_path.exists() {
27
+ return Err(format!("❌ Plugin wasm not found: {}", wasm_path.display()));
28
+ }
29
+
30
+ let toml_content = std::fs::read_to_string(&toml_path)
31
+ .map_err(|e| format!("Failed to read '{}': {}", toml_path.display(), e))?;
32
+ let plugin_file: PluginFile = toml::from_str(&toml_content)
33
+ .map_err(|e| format!("Failed to parse '{}': {}", toml_path.display(), e))?;
34
+
35
+ let wasm_bytes = std::fs::read(&wasm_path)
36
+ .map_err(|e| format!("Failed to read '{}': {}", wasm_path.display(), e))?;
37
+
38
+ Ok((plugin_file.plugin, wasm_bytes))
39
+ }
40
+
41
+ pub fn load_plugin_from_uri(uri: &str) -> Result<(PluginInfo, Vec<u8>), String> {
42
+ if !uri.starts_with("devalang://plugin/") {
43
+ return Err("Invalid plugin URI".into());
44
+ }
45
+
46
+ let name = uri.trim_start_matches("devalang://plugin/");
47
+ load_plugin(name)
48
+ }
@@ -0,0 +1 @@
1
+ pub mod loader;
@@ -127,6 +127,11 @@ impl ModuleLoader {
127
127
  updated_module.tokens = tokens;
128
128
  updated_module.statements = statements;
129
129
 
130
+ // Step four : Injecting bank triggers if any
131
+ if let Err(e) = self.inject_bank_triggers(&mut updated_module, "808") {
132
+ return Err(format!("Failed to inject bank triggers: {}", e));
133
+ }
134
+
130
135
  // Step four : error handling
131
136
  let mut error_handler = ErrorHandler::new();
132
137
  error_handler.detect_from_statements(&mut parser, &updated_module.statements);
@@ -143,16 +148,16 @@ impl ModuleLoader {
143
148
  global_store: &mut GlobalStore
144
149
  ) -> (HashMap<String, Vec<Token>>, HashMap<String, Vec<Statement>>) {
145
150
  // SECTION Load the entry module and its dependencies
146
-
147
151
  let tokens_by_module = self.load_module_recursively(&self.entry, global_store);
148
152
 
149
153
  // SECTION Process and resolve modules
150
154
  process_modules(self, global_store);
151
155
  resolve_all_modules(self, global_store);
152
156
 
153
- let statemnts_by_module = resolve_and_flatten_all_modules(global_store);
157
+ // SECTION Flatten all modules to get statements (+ injects)
158
+ let statements_by_module = resolve_and_flatten_all_modules(global_store);
154
159
 
155
- (tokens_by_module, statemnts_by_module)
160
+ (tokens_by_module, statements_by_module)
156
161
  }
157
162
 
158
163
  #[cfg(feature = "cli")]
@@ -182,12 +187,15 @@ impl ModuleLoader {
182
187
  module.statements = statements.clone();
183
188
 
184
189
  // Inject triggers for each bank used in module
185
- for bank_name in ModuleLoader::extract_bank_names(&statements) {
186
- if let Err(e) = self.inject_bank_triggers(&mut module, &bank_name) {
187
- return HashMap::new(); // Return empty map on error
188
- }
190
+ for bank_name in self.extract_bank_names(&statements) {
191
+ self.inject_bank_triggers(&mut module, &bank_name);
189
192
  }
190
193
 
194
+ // Inject module variables and functions into global store
195
+ global_store.variables.variables.extend(module.variable_table.variables.clone());
196
+ global_store.functions.functions.extend(module.function_table.functions.clone());
197
+
198
+ // Inject the module into the global store
191
199
  global_store.insert_module(path.clone(), module);
192
200
 
193
201
  // Load dependencies
@@ -241,65 +249,75 @@ impl ModuleLoader {
241
249
  }
242
250
  }
243
251
 
244
- pub fn inject_bank_triggers(&self, module: &mut Module, bank_name: &str) -> Result<(), String> {
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
+
245
259
  let bank_path = Path::new("./.deva/bank").join(bank_name);
246
- let bank_file_path = bank_path.join("bank.toml");
260
+ let bank_toml_path = bank_path.join("bank.toml");
247
261
 
248
- if !bank_file_path.exists() {
249
- return Ok(()); // Pas d'erreur si la banque n'existe pas encore
262
+ if !bank_toml_path.exists() {
263
+ return Ok(module.clone());
250
264
  }
251
265
 
252
266
  let content = std::fs
253
- ::read_to_string(&bank_file_path)
254
- .map_err(|e| format!("Failed to read '{}': {}", bank_file_path.display(), e))?;
267
+ ::read_to_string(&bank_toml_path)
268
+ .map_err(|e| format!("Failed to read '{}': {}", bank_toml_path.display(), e))?;
255
269
 
256
- let parsed: BankFile = toml
270
+ let parsed_bankfile: BankFile = toml
257
271
  ::from_str(&content)
258
- .map_err(|e| format!("Failed to parse '{}': {}", bank_file_path.display(), e))?;
272
+ .map_err(|e| format!("Failed to parse '{}': {}", bank_toml_path.display(), e))?;
259
273
 
260
274
  let mut bank_map = HashMap::new();
261
275
 
262
- for bank_trigger in parsed.triggers.unwrap_or_default() {
276
+ for bank_trigger in parsed_bankfile.triggers.unwrap_or_default() {
263
277
  let trigger_name = bank_trigger.name.clone().replace("./", "");
264
278
  let bank_trigger_path = format!("devalang://bank/{}/{}", bank_name, trigger_name);
265
279
 
266
280
  bank_map.insert(bank_trigger.name.clone(), Value::String(bank_trigger_path.clone()));
267
281
 
268
- if module.variable_table.variables.contains_key(bank_name) {
282
+ if module.variable_table.variables.contains_key(alias) {
269
283
  eprintln!(
270
284
  "⚠️ Trigger '{}' already defined in module '{}', skipping injection.",
271
- bank_name, module.path
285
+ alias,
286
+ module.path
272
287
  );
273
288
  continue;
274
289
  }
275
290
 
276
291
  module.variable_table.set(
277
- format!("{}.{}", bank_name, bank_trigger.name),
292
+ format!("{}.{}", alias, bank_trigger.name),
278
293
  Value::String(bank_trigger_path.clone())
279
294
  );
280
295
  }
281
296
 
282
297
  // Inject the map under the bank name
283
- module.variable_table.set(bank_name.to_string(), Value::Map(bank_map));
298
+ module.variable_table.set(alias.to_string(), Value::Map(bank_map));
284
299
 
285
- Ok(())
300
+ Ok(module.clone())
286
301
  }
287
302
 
288
- fn extract_bank_names(statements: &[Statement]) -> HashSet<String> {
303
+ fn extract_bank_names(&self, statements: &[Statement]) -> HashSet<String> {
289
304
  let mut banks = HashSet::new();
290
305
 
291
306
  for stmt in statements {
292
- if let StatementKind::Trigger { entity, .. } = &stmt.kind {
293
- let parts: Vec<&str> = entity.split('.').collect();
294
- if parts.len() >= 2 {
295
- banks.insert(parts[0].to_string()); // "808.kick" → "808"
296
- }
297
- }
298
-
299
- if let StatementKind::Bank = &stmt.kind {
300
- if let Value::String(name) = &stmt.value {
301
- banks.insert(name.clone());
307
+ match &stmt.kind {
308
+ // Extract only bank declarations
309
+ 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
+ }
302
319
  }
320
+ _ => {}
303
321
  }
304
322
  }
305
323
 
@@ -1,7 +1,7 @@
1
1
  use crate::core::{
2
2
  lexer::token::Token,
3
3
  parser::statement::Statement,
4
- store::{ export::ExportTable, import::ImportTable, variable::VariableTable },
4
+ store::{ export::ExportTable, function::FunctionTable, import::ImportTable, variable::VariableTable },
5
5
  };
6
6
 
7
7
  #[derive(Debug, Clone)]
@@ -11,6 +11,7 @@ pub struct Module {
11
11
  pub tokens: Vec<Token>,
12
12
  pub statements: Vec<Statement>,
13
13
  pub variable_table: VariableTable,
14
+ pub function_table: FunctionTable,
14
15
  pub export_table: ExportTable,
15
16
  pub import_table: ImportTable,
16
17
  pub content: String,
@@ -24,6 +25,7 @@ impl Module {
24
25
  tokens: Vec::new(),
25
26
  statements: Vec::new(),
26
27
  variable_table: VariableTable::new(),
28
+ function_table: FunctionTable::new(),
27
29
  export_table: ExportTable::new(),
28
30
  import_table: ImportTable::new(),
29
31
  resolved: false,
@@ -5,7 +5,7 @@ use crate::core::{
5
5
  preprocessor::{ loader::ModuleLoader, resolver::group },
6
6
  shared::value::Value,
7
7
  store::global::GlobalStore,
8
- utils::path::{normalize_path, resolve_relative_path},
8
+ utils::path::{ normalize_path, resolve_relative_path },
9
9
  };
10
10
 
11
11
  pub fn process_modules(module_loader: &ModuleLoader, global_store: &mut GlobalStore) {
@@ -13,6 +13,31 @@ pub fn process_modules(module_loader: &ModuleLoader, global_store: &mut GlobalSt
13
13
  for stmt in &module.statements {
14
14
  match &stmt.kind {
15
15
  StatementKind::Let { name } => {
16
+ if let Value::Null = stmt.value {
17
+ eprintln!("❌ Variable '{}' is declared but not initialized.", name);
18
+
19
+ module.variable_table.variables.insert(
20
+ name.clone(),
21
+ Value::StatementKind(Box::new(stmt.kind.clone()))
22
+ );
23
+
24
+ continue;
25
+ }
26
+
27
+ if module.variable_table.get(name).is_some() {
28
+ eprintln!("❌ Variable '{}' is already defined in this scope.", name);
29
+ continue;
30
+ }
31
+
32
+ if let Some(module_variable) = module.variable_table.variables.get(name) {
33
+ eprintln!(
34
+ "❌ Variable '{}' is already defined globally with value: {:?}",
35
+ name,
36
+ module_variable
37
+ );
38
+ continue;
39
+ }
40
+
16
41
  module.variable_table.variables.insert(name.clone(), stmt.value.clone());
17
42
  }
18
43
 
@@ -10,103 +10,80 @@ use crate::{
10
10
 
11
11
  pub fn resolve_call(
12
12
  stmt: &Statement,
13
+ name: String,
14
+ args: Vec<Value>,
13
15
  module: &Module,
14
16
  path: &str,
15
17
  global_store: &mut GlobalStore
16
18
  ) -> Statement {
17
19
  let logger = Logger::new();
18
20
 
19
- match &stmt.value {
20
- Value::Identifier(ident) => {
21
- match module.variable_table.get(ident) {
22
- Some(Value::String(group_name)) =>
23
- resolve_group_by_name(group_name, stmt, module, path, global_store, &logger),
24
- Some(Value::Map(group_map)) =>
25
- resolved_call(stmt, group_map, module, path, global_store),
26
- Some(other) =>
27
- error_stmt(
28
- &logger,
29
- module,
30
- stmt,
31
- &format!(
32
- "Identifier '{ident}' must resolve to a group name or map, found {:?}",
33
- other
34
- )
35
- ),
36
- None =>
37
- error_stmt(
38
- &logger,
39
- module,
40
- stmt,
41
- &format!("Identifier '{ident}' not found in variable table")
42
- ),
43
- }
44
- }
21
+ match &stmt.kind {
22
+ StatementKind::Call { .. } => {
23
+ // Check if it's a function
24
+ if let Some(func) = global_store.functions.functions.get(&name) {
25
+ let mut call_map = std::collections::HashMap::new();
26
+ call_map.insert("name".to_string(), Value::Identifier(name.clone()));
27
+ call_map.insert(
28
+ "parameters".to_string(),
29
+ Value::Array(
30
+ func.parameters
31
+ .iter()
32
+ .map(|p| Value::Identifier(p.clone()))
33
+ .collect()
34
+ )
35
+ );
36
+ call_map.insert("args".to_string(), Value::Array(args.clone()));
37
+ call_map.insert("body".to_string(), Value::Block(func.body.clone()));
45
38
 
46
- Value::String(name) =>
47
- resolve_group_by_name(name, stmt, module, path, global_store, &logger),
39
+ return Statement {
40
+ kind: StatementKind::Call { name, args },
41
+ value: Value::Map(call_map),
42
+ ..stmt.clone()
43
+ };
44
+ }
48
45
 
49
- Value::Map(group_map) => resolved_call(stmt, group_map, module, path, global_store),
46
+ // Otherwise, check if it's a variable (e.g. group)
47
+ if let Some(variable) = global_store.variables.variables.get(&name) {
48
+ if let Value::Statement(stmt_box) = variable {
49
+ if let StatementKind::Group = stmt_box.kind {
50
+ if let Value::Map(map) = &stmt_box.value {
51
+ if let Some(Value::Block(body)) = map.get("body") {
52
+ let mut resolved_map = std::collections::HashMap::new();
53
+ resolved_map.insert(
54
+ "identifier".to_string(),
55
+ Value::String(name.clone())
56
+ );
57
+ resolved_map.insert("args".to_string(), Value::Array(args.clone()));
58
+ resolved_map.insert("body".to_string(), Value::Block(body.clone()));
50
59
 
51
- other =>
52
- error_stmt(
53
- &logger,
54
- module,
55
- stmt,
56
- &format!("Call expects a group name as string or identifier, found {:?}", other)
57
- ),
58
- }
59
- }
60
+ return Statement {
61
+ kind: StatementKind::Call { name, args },
62
+ value: Value::Map(resolved_map),
63
+ ..stmt.clone()
64
+ };
65
+ }
66
+ }
67
+ }
68
+ }
69
+ }
60
70
 
61
- fn resolve_group_by_name(
62
- name: &str,
63
- stmt: &Statement,
64
- module: &Module,
65
- path: &str,
66
- global_store: &mut GlobalStore,
67
- logger: &Logger
68
- ) -> Statement {
69
- match module.variable_table.get(name) {
70
- Some(Value::Map(group_map)) => resolved_call(stmt, group_map, module, path, global_store),
71
- Some(other) =>
72
- error_stmt(
73
- logger,
74
- module,
75
- stmt,
76
- &format!("Expected a group for '{}', but found {:?}", name, other)
77
- ),
78
- None =>
79
- error_stmt(
80
- logger,
81
- module,
82
- stmt,
83
- &format!("Group '{}' not found in module '{}'", name, module.path)
84
- ),
71
+ // Otherwise, log an error
72
+ logger.log_message(LogLevel::Error, &format!("Function or group '{}' not found", name));
73
+ Statement {
74
+ kind: StatementKind::Error {
75
+ message: format!("Function or group '{}' not found", name),
76
+ },
77
+ value: Value::Null,
78
+ ..stmt.clone()
79
+ }
80
+ }
81
+ _ => error_stmt(&logger, module, stmt, "Expected StatementKind::Call in resolve_call()"),
85
82
  }
86
83
  }
87
84
 
88
- fn resolved_call(
89
- stmt: &Statement,
90
- group_map: &std::collections::HashMap<String, Value>,
91
- module: &Module,
92
- path: &str,
93
- global_store: &mut GlobalStore
94
- ) -> Statement {
95
- let mut cloned_map = group_map.clone();
96
-
97
- if let Some(Value::Block(stmts)) = group_map.get("body") {
98
- let resolved = stmts
99
- .iter()
100
- .map(|s| resolve_statement(s, module, path, global_store))
101
- .collect();
102
- cloned_map.insert("body".to_string(), Value::Block(resolved));
103
- }
104
-
105
- Statement {
106
- kind: StatementKind::Call,
107
- value: Value::Map(cloned_map),
108
- ..stmt.clone()
109
- }
85
+ fn get_group_body(stmt_box: &Statement) -> Vec<Statement> {
86
+ if let Value::Block(body) = &stmt_box.value { body.clone() } else { vec![] }
110
87
  }
111
88
 
112
89
  fn error_stmt(logger: &Logger, module: &Module, stmt: &Statement, message: &str) -> Statement {
@@ -1,7 +1,7 @@
1
1
  use crate::{
2
2
  core::{
3
- parser::statement::{Statement, StatementKind},
4
- preprocessor::{module::Module, resolver::driver::resolve_statement},
3
+ parser::statement::{ Statement, StatementKind },
4
+ preprocessor::{ module::Module, resolver::driver::resolve_statement },
5
5
  shared::value::Value,
6
6
  store::global::GlobalStore,
7
7
  },
@@ -12,12 +12,17 @@ pub fn resolve_condition(
12
12
  stmt: &Statement,
13
13
  module: &Module,
14
14
  path: &str,
15
- global_store: &mut GlobalStore,
15
+ global_store: &mut GlobalStore
16
16
  ) -> Statement {
17
17
  let logger = Logger::new();
18
18
 
19
19
  let Value::Map(condition_map) = &stmt.value else {
20
- return type_error(&logger, module, stmt, "Expected a map in condition statement".to_string());
20
+ return type_error(
21
+ &logger,
22
+ module,
23
+ stmt,
24
+ "Expected a map in condition statement".to_string()
25
+ );
21
26
  };
22
27
 
23
28
  let mut resolved_map = condition_map.clone();
@@ -60,13 +65,13 @@ fn resolve_block_statements(
60
65
  body: &[Statement],
61
66
  module: &Module,
62
67
  path: &str,
63
- global_store: &mut GlobalStore,
68
+ global_store: &mut GlobalStore
64
69
  ) -> Vec<Statement> {
65
70
  body.iter()
66
71
  .flat_map(|stmt| {
67
72
  let resolved = resolve_statement(stmt, module, path, global_store);
68
73
 
69
- if let StatementKind::Call = resolved.kind {
74
+ if let StatementKind::Call { .. } = resolved.kind {
70
75
  if let Value::Block(inner) = &resolved.value {
71
76
  return inner
72
77
  .iter()