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

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 (62) hide show
  1. package/Cargo.toml +54 -53
  2. package/README.md +1 -14
  3. package/docs/CHANGELOG.md +26 -0
  4. package/docs/TODO.md +1 -1
  5. package/examples/index.deva +10 -13
  6. package/out-tsc/bin/devalang.exe +0 -0
  7. package/package.json +1 -1
  8. package/project-version.json +3 -3
  9. package/rust/cli/build.rs +25 -2
  10. package/rust/cli/check.rs +26 -3
  11. package/rust/cli/play.rs +1 -1
  12. package/rust/core/audio/engine.rs +126 -73
  13. package/rust/core/audio/interpreter/call.rs +72 -47
  14. package/rust/core/audio/interpreter/condition.rs +14 -12
  15. package/rust/core/audio/interpreter/driver.rs +84 -127
  16. package/rust/core/audio/interpreter/function.rs +21 -0
  17. package/rust/core/audio/interpreter/load.rs +1 -1
  18. package/rust/core/audio/interpreter/loop_.rs +24 -18
  19. package/rust/core/audio/interpreter/mod.rs +2 -1
  20. package/rust/core/audio/interpreter/sleep.rs +0 -6
  21. package/rust/core/audio/interpreter/spawn.rs +78 -60
  22. package/rust/core/audio/interpreter/trigger.rs +157 -70
  23. package/rust/core/audio/loader/trigger.rs +37 -4
  24. package/rust/core/audio/player.rs +20 -10
  25. package/rust/core/audio/renderer.rs +24 -25
  26. package/rust/core/debugger/mod.rs +2 -0
  27. package/rust/core/debugger/module.rs +47 -0
  28. package/rust/core/debugger/store.rs +25 -11
  29. package/rust/core/error/mod.rs +6 -0
  30. package/rust/core/lexer/handler/driver.rs +23 -1
  31. package/rust/core/lexer/handler/identifier.rs +1 -0
  32. package/rust/core/lexer/handler/mod.rs +1 -0
  33. package/rust/core/lexer/handler/parenthesis.rs +41 -0
  34. package/rust/core/lexer/token.rs +3 -0
  35. package/rust/core/parser/driver.rs +3 -1
  36. package/rust/core/parser/handler/dot.rs +64 -127
  37. package/rust/core/parser/handler/identifier/call.rs +69 -22
  38. package/rust/core/parser/handler/identifier/function.rs +92 -0
  39. package/rust/core/parser/handler/identifier/let_.rs +13 -19
  40. package/rust/core/parser/handler/identifier/mod.rs +1 -0
  41. package/rust/core/parser/handler/identifier/spawn.rs +74 -27
  42. package/rust/core/parser/statement.rs +16 -4
  43. package/rust/core/preprocessor/loader.rs +45 -29
  44. package/rust/core/preprocessor/module.rs +3 -1
  45. package/rust/core/preprocessor/processor.rs +26 -1
  46. package/rust/core/preprocessor/resolver/call.rs +61 -84
  47. package/rust/core/preprocessor/resolver/condition.rs +11 -6
  48. package/rust/core/preprocessor/resolver/driver.rs +52 -6
  49. package/rust/core/preprocessor/resolver/function.rs +78 -0
  50. package/rust/core/preprocessor/resolver/group.rs +43 -13
  51. package/rust/core/preprocessor/resolver/let_.rs +7 -10
  52. package/rust/core/preprocessor/resolver/mod.rs +2 -1
  53. package/rust/core/preprocessor/resolver/spawn.rs +64 -30
  54. package/rust/core/preprocessor/resolver/trigger.rs +7 -3
  55. package/rust/core/preprocessor/resolver/value.rs +10 -1
  56. package/rust/core/shared/value.rs +4 -1
  57. package/rust/core/store/function.rs +34 -0
  58. package/rust/core/store/global.rs +9 -10
  59. package/rust/core/store/mod.rs +2 -1
  60. package/rust/core/store/variable.rs +6 -0
  61. package/rust/lib.rs +10 -7
  62. package/rust/utils/mod.rs +45 -1
@@ -1,3 +1,6 @@
1
+ use rayon::prelude::*;
2
+ use std::sync::{ Arc, Mutex };
3
+
1
4
  use crate::core::{
2
5
  audio::{
3
6
  engine::AudioEngine,
@@ -5,6 +8,7 @@ use crate::core::{
5
8
  arrow_call::interprete_call_arrow_statement,
6
9
  call::interprete_call_statement,
7
10
  condition::interprete_condition_statement,
11
+ function::interprete_function_statement,
8
12
  let_::interprete_let_statement,
9
13
  load::interprete_load_statement,
10
14
  loop_::interprete_loop_statement,
@@ -15,37 +19,26 @@ use crate::core::{
15
19
  },
16
20
  },
17
21
  parser::statement::{ Statement, StatementKind },
18
- store::variable::VariableTable,
22
+ store::{ function::FunctionTable, global::GlobalStore, variable::VariableTable },
19
23
  };
20
24
 
21
25
  pub fn run_audio_program(
22
26
  statements: &Vec<Statement>,
23
- mut audio_engine: AudioEngine,
27
+ audio_engine: &mut AudioEngine,
24
28
  entry: String,
25
- output: String
26
- ) -> (AudioEngine, f32, f32) {
29
+ output: String,
30
+ mut module_variables: VariableTable,
31
+ mut module_functions: FunctionTable,
32
+ global_store: &mut GlobalStore
33
+ ) -> (f32, f32) {
27
34
  let mut base_bpm = 120.0;
28
35
  let mut base_duration = 60.0 / base_bpm;
29
36
 
30
- let mut variable_table = audio_engine.variables.clone();
31
-
32
- for stmt in statements {
33
- if let StatementKind::Let { .. } = stmt.kind {
34
- if
35
- let Some(new_table) =
36
- interprete_let_statement(
37
- stmt,
38
- &mut variable_table
39
- )
40
- {
41
- variable_table = new_table;
42
- }
43
- }
44
- }
45
-
46
- let (updated_audio_engine, base_bpm, max_end_time) = execute_audio_block(
37
+ let (max_end_time, cursor_time) = execute_audio_block(
47
38
  audio_engine,
48
- variable_table,
39
+ global_store,
40
+ global_store.variables.clone(),
41
+ global_store.functions.clone(),
49
42
  statements.clone(),
50
43
  base_bpm,
51
44
  base_duration,
@@ -53,112 +46,64 @@ pub fn run_audio_program(
53
46
  0.0
54
47
  );
55
48
 
56
- (updated_audio_engine, base_bpm, max_end_time)
49
+ (max_end_time, cursor_time)
57
50
  }
58
51
 
59
52
  pub fn execute_audio_block(
60
- mut audio_engine: AudioEngine,
61
- mut variable_table: VariableTable,
62
- mut statements: Vec<Statement>,
53
+ audio_engine: &mut AudioEngine,
54
+ global_store: &GlobalStore,
55
+ variable_table: VariableTable,
56
+ functions_table: FunctionTable,
57
+ statements: Vec<Statement>,
63
58
  mut base_bpm: f32,
64
59
  mut base_duration: f32,
65
60
  mut max_end_time: f32,
66
61
  mut cursor_time: f32
67
- ) -> (AudioEngine, f32, f32) {
68
- let initial_cursor_time = cursor_time;
62
+ ) -> (f32, f32) {
63
+ let (spawns, others): (Vec<_>, Vec<_>) = statements
64
+ .into_iter()
65
+ .partition(|stmt| matches!(stmt.kind, StatementKind::Spawn { .. }));
69
66
 
70
- for stmt in statements.clone() {
67
+ // Execute sequential statements first
68
+ for stmt in others {
71
69
  match &stmt.kind {
72
70
  StatementKind::Load { .. } => {
73
71
  if
74
- let Some(new_variable_table) = interprete_load_statement(
72
+ let Some(new_table) = interprete_load_statement(
75
73
  &stmt,
76
- &mut variable_table
74
+ &mut variable_table.clone()
77
75
  )
78
76
  {
79
- variable_table = new_variable_table;
80
- } else {
81
- eprintln!("❌ Failed to interpret load statement: {:?}", stmt);
77
+ // Extend the variable_table if necessary
82
78
  }
83
79
  }
84
-
85
80
  StatementKind::Let { .. } => {
86
- if
87
- let Some(new_variable_table) = interprete_let_statement(
88
- &stmt,
89
- &mut variable_table
90
- )
91
- {
92
- variable_table = new_variable_table;
93
- } else {
94
- eprintln!("❌ Failed to interpret let statement: {:?}", stmt);
95
- }
81
+ interprete_let_statement(&stmt, &mut variable_table.clone());
82
+ }
83
+ StatementKind::Function { .. } => {
84
+ interprete_function_statement(&stmt, &mut functions_table.clone());
96
85
  }
97
-
98
86
  StatementKind::Tempo => {
99
87
  if let Some((new_bpm, new_duration)) = interprete_tempo_statement(&stmt) {
100
88
  base_bpm = new_bpm;
101
89
  base_duration = new_duration;
102
- } else {
103
- eprintln!("❌ Failed to interpret tempo statement: {:?}", stmt);
104
90
  }
105
91
  }
106
-
107
92
  StatementKind::Trigger { .. } => {
108
93
  if
109
- let Some((new_cursor_time, new_max_end_time, updated_engine)) =
110
- interprete_trigger_statement(
111
- &stmt,
112
- &mut audio_engine,
113
- &variable_table,
114
- base_duration,
115
- cursor_time,
116
- max_end_time
117
- )
118
- {
119
- cursor_time = new_cursor_time;
120
- max_end_time = new_max_end_time;
121
- audio_engine = updated_engine;
122
- } else {
123
- eprintln!("❌ Failed to interpret trigger statement: {:?}", stmt);
124
- }
125
- }
126
-
127
- StatementKind::Spawn => {
128
- let mut temp_engine = AudioEngine::new(audio_engine.module_name.clone());
129
-
130
- if
131
- let Some((_cur, _max, updated_engine)) = interprete_spawn_statement(
94
+ let Some((new_cursor, new_max, _)) = interprete_trigger_statement(
132
95
  &stmt,
133
- temp_engine,
96
+ audio_engine,
134
97
  &variable_table,
135
- base_bpm,
136
98
  base_duration,
137
- initial_cursor_time,
99
+ cursor_time,
138
100
  max_end_time
139
101
  )
140
102
  {
141
- audio_engine.merge_with(updated_engine);
103
+ cursor_time = new_cursor;
104
+ max_end_time = new_max;
142
105
  }
143
106
  }
144
-
145
- StatementKind::Call => {
146
- let (call_engine, new_max, end_time, new_cursor) = interprete_call_statement(
147
- &stmt,
148
- audio_engine.clone(),
149
- variable_table.clone(),
150
- base_bpm,
151
- base_duration,
152
- max_end_time,
153
- cursor_time,
154
- &statements
155
- );
156
-
157
- audio_engine.merge_with(call_engine);
158
- cursor_time = new_cursor;
159
- max_end_time = new_max;
160
- }
161
-
162
107
  StatementKind::Sleep => {
163
108
  let (new_cursor, new_max) = interprete_sleep_statement(
164
109
  &stmt,
@@ -168,42 +113,40 @@ pub fn execute_audio_block(
168
113
  cursor_time = new_cursor;
169
114
  max_end_time = new_max;
170
115
  }
171
-
172
116
  StatementKind::Loop => {
173
- let (loop_engine, new_max, new_cursor) = interprete_loop_statement(
117
+ let (new_max, new_cursor) = interprete_loop_statement(
174
118
  &stmt,
175
- audio_engine.clone(),
176
- variable_table.clone(),
119
+ audio_engine,
120
+ global_store,
121
+ &variable_table,
122
+ &functions_table,
177
123
  base_bpm,
178
124
  base_duration,
179
125
  max_end_time,
180
126
  cursor_time
181
127
  );
182
- audio_engine = loop_engine;
183
128
  cursor_time = new_cursor;
184
129
  max_end_time = new_max;
185
130
  }
186
-
187
- StatementKind::If | StatementKind::ElseIf | StatementKind::Else => {
188
- let (condition_engine, new_max, new_cursor) = interprete_condition_statement(
131
+ StatementKind::Call { .. } => {
132
+ let (new_max, new_cursor) = interprete_call_statement(
189
133
  &stmt,
190
- audio_engine.clone(),
191
- variable_table.clone(),
134
+ audio_engine,
135
+ &variable_table,
136
+ &functions_table,
137
+ global_store,
192
138
  base_bpm,
193
139
  base_duration,
194
140
  max_end_time,
195
- cursor_time
141
+ cursor_time,
196
142
  );
197
-
198
- audio_engine = condition_engine;
199
143
  cursor_time = new_cursor;
200
144
  max_end_time = new_max;
201
145
  }
202
-
203
146
  StatementKind::ArrowCall { .. } => {
204
- let (new_max_end_time, new_cursor_time) = interprete_call_arrow_statement(
147
+ let (new_max, new_cursor) = interprete_call_arrow_statement(
205
148
  &stmt,
206
- &mut audio_engine,
149
+ audio_engine,
207
150
  &variable_table,
208
151
  base_bpm,
209
152
  base_duration,
@@ -211,26 +154,40 @@ pub fn execute_audio_block(
211
154
  Some(&mut cursor_time),
212
155
  true
213
156
  );
214
-
215
- cursor_time = new_cursor_time;
216
- max_end_time = new_max_end_time;
217
- }
218
-
219
- | StatementKind::Bank
220
- | StatementKind::Import { .. }
221
- | StatementKind::Export { .. }
222
- | StatementKind::Group
223
- | StatementKind::Unknown => {
224
- // NOTE: Ignoring unsupported statement kinds for now.
225
- }
226
-
227
- _ => {
228
- eprintln!("Unsupported audio statement kind: {:?}", stmt);
157
+ cursor_time = new_cursor;
158
+ max_end_time = new_max;
229
159
  }
160
+ _ => {}
230
161
  }
231
162
  }
232
163
 
233
- audio_engine.set_variables(variable_table);
164
+ // Execute parallel spawns (collect results)
165
+ let spawn_results: Vec<(AudioEngine, f32)> = spawns
166
+ .par_iter()
167
+ .map(|stmt| {
168
+ let mut local_engine = AudioEngine::new(audio_engine.module_name.clone());
169
+ let (spawn_max, _) = interprete_spawn_statement(
170
+ stmt,
171
+ &mut local_engine,
172
+ &variable_table,
173
+ &functions_table,
174
+ global_store,
175
+ base_bpm,
176
+ base_duration,
177
+ 0.0,
178
+ 0.0
179
+ );
180
+ (local_engine, spawn_max)
181
+ })
182
+ .collect();
183
+
184
+ // Finally, merge results from all spawns
185
+ for (local_engine, spawn_max) in spawn_results {
186
+ audio_engine.merge_with(local_engine);
187
+ if spawn_max > max_end_time {
188
+ max_end_time = spawn_max;
189
+ }
190
+ }
234
191
 
235
- (audio_engine, base_bpm, max_end_time)
192
+ (max_end_time.max(cursor_time), cursor_time)
236
193
  }
@@ -0,0 +1,21 @@
1
+ use crate::core::{
2
+ parser::statement::{ Statement, StatementKind },
3
+ store::function::{ FunctionDef, FunctionTable },
4
+ };
5
+
6
+ pub fn interprete_function_statement(
7
+ stmt: &Statement,
8
+ functions_table: &mut FunctionTable
9
+ ) -> Option<FunctionTable> {
10
+ if let StatementKind::Function { name, parameters, body } = &stmt.kind {
11
+ functions_table.add_function(FunctionDef {
12
+ name: name.clone(),
13
+ parameters: parameters.clone(),
14
+ body: body.clone(),
15
+ });
16
+
17
+ return Some(functions_table.clone());
18
+ }
19
+
20
+ None
21
+ }
@@ -9,7 +9,7 @@ pub fn interprete_load_statement(
9
9
  variable_table: &mut VariableTable
10
10
  ) -> Option<VariableTable> {
11
11
  if let StatementKind::Load { source, alias } = &stmt.kind {
12
- variable_table.set(alias.to_string(), Value::String(source.clone()));
12
+ variable_table.set(alias.to_string(), Value::Sample(source.clone()));
13
13
 
14
14
  return Some(variable_table.clone());
15
15
  }
@@ -2,18 +2,20 @@ use crate::core::{
2
2
  audio::{ engine::AudioEngine, interpreter::driver::execute_audio_block },
3
3
  parser::statement::{ Statement, StatementKind },
4
4
  shared::{ duration::Duration, value::Value },
5
- store::variable::VariableTable,
5
+ store::{function::FunctionTable, global::GlobalStore, variable::VariableTable},
6
6
  };
7
7
 
8
8
  pub fn interprete_loop_statement(
9
9
  stmt: &Statement,
10
- audio_engine: AudioEngine,
11
- variable_table: VariableTable,
10
+ audio_engine: &mut AudioEngine,
11
+ global_store: &GlobalStore,
12
+ variable_table: &VariableTable,
13
+ functions_table: &FunctionTable,
12
14
  base_bpm: f32,
13
15
  base_duration: f32,
14
16
  max_end_time: f32,
15
17
  cursor_time: f32
16
- ) -> (AudioEngine, f32, f32) {
18
+ ) -> (f32, f32) {
17
19
  if let Value::Map(loop_value) = &stmt.value {
18
20
  let loop_count = match loop_value.get("iterator") {
19
21
  Some(Value::Number(n)) => *n as usize,
@@ -22,12 +24,15 @@ pub fn interprete_loop_statement(
22
24
  *n as usize
23
25
  } else {
24
26
  eprintln!("❌ Loop iterator must be a number, found: {:?}", ident);
25
- return (audio_engine, max_end_time, cursor_time);
27
+ return (max_end_time, cursor_time);
26
28
  }
27
29
  }
28
30
  _ => {
29
- eprintln!("❌ Loop iterator must be a number, found: {:?}", loop_value.get("iterator"));
30
- return (audio_engine, max_end_time, cursor_time);
31
+ eprintln!(
32
+ "❌ Loop iterator must be a number, found: {:?}",
33
+ loop_value.get("iterator")
34
+ );
35
+ return (max_end_time, cursor_time);
31
36
  }
32
37
  };
33
38
 
@@ -35,7 +40,7 @@ pub fn interprete_loop_statement(
35
40
  Some(Value::Block(body)) => body.clone(),
36
41
  _ => {
37
42
  eprintln!("❌ Loop body must be a block, found: {:?}", loop_value.get("body"));
38
- return (audio_engine, max_end_time, cursor_time);
43
+ return (max_end_time, cursor_time);
39
44
  }
40
45
  };
41
46
 
@@ -43,10 +48,12 @@ pub fn interprete_loop_statement(
43
48
  let mut cur_time = cursor_time;
44
49
  let mut max_time = max_end_time;
45
50
 
46
- for _ in 0..loop_count {
47
- let (eng, _, end_time) = execute_audio_block(
48
- engine,
51
+ for i in 0..loop_count {
52
+ let (block_end_time, cursor_time) = execute_audio_block(
53
+ &mut engine,
54
+ global_store,
49
55
  variable_table.clone(),
56
+ functions_table.clone(),
50
57
  loop_body.clone(),
51
58
  base_bpm,
52
59
  base_duration,
@@ -54,14 +61,13 @@ pub fn interprete_loop_statement(
54
61
  cur_time
55
62
  );
56
63
 
57
- engine = eng;
58
- cur_time = end_time;
59
- max_time = max_time.max(end_time);
64
+ cur_time = block_end_time;
65
+ max_time = max_time.max(cur_time);
60
66
  }
61
67
 
62
- (engine, max_time, cur_time)
63
- } else {
64
- eprintln!("❌ Loop statement value is not a map");
65
- (audio_engine, max_end_time, cursor_time)
68
+ return (max_time, cur_time);
66
69
  }
70
+
71
+ eprintln!("❌ Loop statement value is not a map");
72
+ (max_end_time, cursor_time)
67
73
  }
@@ -9,4 +9,5 @@ pub mod sleep;
9
9
  pub mod loop_;
10
10
  pub mod call;
11
11
  pub mod condition;
12
- pub mod arrow_call;
12
+ pub mod arrow_call;
13
+ pub mod function;
@@ -18,12 +18,6 @@ pub fn interprete_sleep_statement(
18
18
  0.0
19
19
  })
20
20
  }
21
- Value::String(s) if s.ends_with("s") => {
22
- s.trim_end_matches("s").parse::<f32>().unwrap_or_else(|_| {
23
- eprintln!("❌ Invalid sleep value (s): {}", s);
24
- 0.0
25
- })
26
- }
27
21
  other => {
28
22
  eprintln!("❌ Invalid sleep value: {:?}", other);
29
23
  0.0
@@ -1,84 +1,102 @@
1
1
  use crate::core::{
2
- audio::{ engine::AudioEngine, interpreter::driver::execute_audio_block },
3
- parser::statement::Statement,
2
+ audio::{engine::AudioEngine, interpreter::driver::execute_audio_block},
3
+ parser::statement::{Statement, StatementKind},
4
4
  shared::value::Value,
5
- store::variable::VariableTable,
5
+ store::{function::FunctionTable, global::GlobalStore, variable::VariableTable},
6
6
  };
7
7
 
8
8
  pub fn interprete_spawn_statement(
9
9
  stmt: &Statement,
10
- audio_engine: AudioEngine,
10
+ audio_engine: &mut AudioEngine,
11
11
  variable_table: &VariableTable,
12
+ functions: &FunctionTable,
13
+ global_store: &GlobalStore,
12
14
  base_bpm: f32,
13
15
  base_duration: f32,
16
+ max_end_time: f32,
14
17
  cursor_time: f32,
15
- max_end_time: f32
16
- ) -> Option<(f32, f32, AudioEngine)> {
17
- match &stmt.value {
18
- Value::String(identifier) | Value::Identifier(identifier) => {
19
- handle_spawn_identifier(
20
- identifier,
21
- audio_engine,
22
- variable_table,
23
- base_bpm,
24
- base_duration,
25
- cursor_time,
26
- max_end_time
27
- )
28
- }
18
+ ) -> (f32, f32) {
19
+ match &stmt.kind {
20
+ StatementKind::Spawn { name, args } => {
21
+ let mut local_engine = AudioEngine::new(audio_engine.module_name.clone());
22
+
23
+ // ✅ 1. Cas : fonction
24
+ if let Some(func) = functions.functions.get(name) {
25
+ if func.parameters.len() != args.len() {
26
+ eprintln!(
27
+ "❌ Function '{}' expects {} args, got {}",
28
+ name,
29
+ func.parameters.len(),
30
+ args.len()
31
+ );
32
+ return (max_end_time, cursor_time);
33
+ }
29
34
 
30
- Value::Map(map) => {
31
- if let Some(Value::Block(block)) = map.get("body") {
32
- let (eng, _, end_time) = execute_audio_block(
33
- audio_engine.clone(),
34
- variable_table.clone(),
35
- block.clone(),
35
+ let mut local_vars = VariableTable::with_parent(variable_table.clone());
36
+ for (param, arg) in func.parameters.iter().zip(args) {
37
+ local_vars.set(param.clone(), arg.clone());
38
+ }
39
+
40
+ let (spawn_max, _) = execute_audio_block(
41
+ &mut local_engine,
42
+ global_store,
43
+ local_vars,
44
+ functions.clone(),
45
+ func.body.clone(),
36
46
  base_bpm,
37
47
  base_duration,
38
- max_end_time,
39
- cursor_time
48
+ 0.0,
49
+ 0.0,
40
50
  );
41
- return Some((max_end_time.max(end_time), end_time, eng));
42
- } else {
43
- eprintln!("❌ Spawn map has no 'body' block");
51
+
52
+ audio_engine.merge_with(local_engine);
53
+ return (spawn_max.max(max_end_time), cursor_time);
54
+ }
55
+
56
+ // ✅ 2. Cas : group dans variable_table ou global_store
57
+ if let Some(group_stmt) = find_group(name, variable_table, global_store) {
58
+ if let Value::Map(map) = &group_stmt.value {
59
+ if let Some(Value::Block(body)) = map.get("body") {
60
+ let (spawn_max, _) = execute_audio_block(
61
+ &mut local_engine,
62
+ global_store,
63
+ variable_table.clone(),
64
+ functions.clone(),
65
+ body.clone(),
66
+ base_bpm,
67
+ base_duration,
68
+ 0.0,
69
+ 0.0,
70
+ );
71
+ audio_engine.merge_with(local_engine);
72
+ return (spawn_max.max(max_end_time), cursor_time);
73
+ }
74
+ }
44
75
  }
45
- None
46
- }
47
76
 
48
- _ => {
49
- eprintln!("❌ Invalid spawn statement: expected identifier, found {:?}", stmt.value);
50
- None
77
+ eprintln!("❌ Function or group '{}' not found", name);
51
78
  }
79
+
80
+ _ => eprintln!("❌ interprete_spawn_statement expected Spawn, got {:?}", stmt.kind),
52
81
  }
82
+
83
+ (max_end_time, cursor_time)
53
84
  }
54
85
 
55
- fn handle_spawn_identifier(
56
- identifier: &str,
57
- audio_engine: AudioEngine,
58
- variable_table: &VariableTable,
59
- base_bpm: f32,
60
- base_duration: f32,
61
- cursor_time: f32,
62
- max_end_time: f32
63
- ) -> Option<(f32, f32, AudioEngine)> {
64
- if let Some(Value::Map(map)) = variable_table.get(identifier) {
65
- if let Some(Value::Block(block)) = map.get("body") {
66
- let (eng, _, end_time) = execute_audio_block(
67
- audio_engine.clone(),
68
- variable_table.clone(),
69
- block.clone(),
70
- base_bpm,
71
- base_duration,
72
- max_end_time,
73
- cursor_time
74
- );
75
- return Some((max_end_time.max(end_time), end_time, eng));
76
- } else {
77
- eprintln!("❌ Spawn group '{}' has no 'body' block", identifier);
86
+ fn find_group<'a>(
87
+ name: &str,
88
+ variable_table: &'a VariableTable,
89
+ global_store: &'a GlobalStore,
90
+ ) -> Option<&'a Statement> {
91
+ if let Some(Value::Statement(stmt_box)) = variable_table.get(name) {
92
+ if let StatementKind::Group = stmt_box.kind {
93
+ return Some(stmt_box);
94
+ }
95
+ }
96
+ if let Some(Value::Statement(stmt_box)) = global_store.variables.variables.get(name) {
97
+ if let StatementKind::Group = stmt_box.kind {
98
+ return Some(stmt_box);
78
99
  }
79
- } else {
80
- eprintln!("❌ Spawn group '{}' not found or not a map", identifier);
81
100
  }
82
-
83
101
  None
84
102
  }