@devaloop/devalang 0.0.1-alpha.11 → 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 (66) hide show
  1. package/.devalang +8 -8
  2. package/Cargo.toml +8 -8
  3. package/README.md +1 -14
  4. package/docs/CHANGELOG.md +44 -0
  5. package/docs/TODO.md +1 -1
  6. package/examples/index.deva +10 -11
  7. package/out-tsc/bin/devalang.exe +0 -0
  8. package/package.json +2 -1
  9. package/project-version.json +3 -3
  10. package/rust/cli/build.rs +25 -2
  11. package/rust/cli/check.rs +26 -3
  12. package/rust/cli/play.rs +1 -1
  13. package/rust/core/audio/engine.rs +207 -41
  14. package/rust/core/audio/interpreter/call.rs +72 -47
  15. package/rust/core/audio/interpreter/condition.rs +14 -12
  16. package/rust/core/audio/interpreter/driver.rs +84 -127
  17. package/rust/core/audio/interpreter/function.rs +21 -0
  18. package/rust/core/audio/interpreter/load.rs +1 -1
  19. package/rust/core/audio/interpreter/loop_.rs +24 -18
  20. package/rust/core/audio/interpreter/mod.rs +2 -1
  21. package/rust/core/audio/interpreter/sleep.rs +0 -6
  22. package/rust/core/audio/interpreter/spawn.rs +78 -60
  23. package/rust/core/audio/interpreter/trigger.rs +169 -61
  24. package/rust/core/audio/loader/trigger.rs +37 -4
  25. package/rust/core/audio/player.rs +20 -10
  26. package/rust/core/audio/renderer.rs +24 -25
  27. package/rust/core/debugger/mod.rs +2 -0
  28. package/rust/core/debugger/module.rs +47 -0
  29. package/rust/core/debugger/store.rs +25 -11
  30. package/rust/core/error/mod.rs +6 -0
  31. package/rust/core/lexer/handler/driver.rs +23 -1
  32. package/rust/core/lexer/handler/identifier.rs +1 -0
  33. package/rust/core/lexer/handler/mod.rs +1 -0
  34. package/rust/core/lexer/handler/parenthesis.rs +41 -0
  35. package/rust/core/lexer/token.rs +3 -0
  36. package/rust/core/parser/driver.rs +31 -3
  37. package/rust/core/parser/handler/dot.rs +65 -129
  38. package/rust/core/parser/handler/identifier/call.rs +69 -22
  39. package/rust/core/parser/handler/identifier/function.rs +92 -0
  40. package/rust/core/parser/handler/identifier/let_.rs +13 -19
  41. package/rust/core/parser/handler/identifier/mod.rs +1 -0
  42. package/rust/core/parser/handler/identifier/spawn.rs +74 -27
  43. package/rust/core/parser/statement.rs +16 -4
  44. package/rust/core/preprocessor/loader.rs +45 -29
  45. package/rust/core/preprocessor/module.rs +3 -1
  46. package/rust/core/preprocessor/processor.rs +26 -1
  47. package/rust/core/preprocessor/resolver/call.rs +61 -84
  48. package/rust/core/preprocessor/resolver/condition.rs +11 -6
  49. package/rust/core/preprocessor/resolver/driver.rs +52 -6
  50. package/rust/core/preprocessor/resolver/function.rs +78 -0
  51. package/rust/core/preprocessor/resolver/group.rs +43 -13
  52. package/rust/core/preprocessor/resolver/let_.rs +7 -10
  53. package/rust/core/preprocessor/resolver/mod.rs +2 -1
  54. package/rust/core/preprocessor/resolver/spawn.rs +64 -30
  55. package/rust/core/preprocessor/resolver/trigger.rs +7 -3
  56. package/rust/core/preprocessor/resolver/value.rs +10 -1
  57. package/rust/core/shared/value.rs +4 -1
  58. package/rust/core/store/function.rs +34 -0
  59. package/rust/core/store/global.rs +9 -10
  60. package/rust/core/store/mod.rs +2 -1
  61. package/rust/core/store/variable.rs +6 -0
  62. package/rust/installer/bank.rs +1 -1
  63. package/rust/installer/mod.rs +2 -1
  64. package/rust/lib.rs +10 -8
  65. package/rust/utils/mod.rs +44 -1
  66. /package/rust/{utils/installer.rs → installer/utils.rs} +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
  }
@@ -1,3 +1,5 @@
1
+ use std::collections::HashMap;
2
+
1
3
  use crate::core::{
2
4
  audio::{ engine::AudioEngine, loader::trigger::load_trigger },
3
5
  parser::statement::{ Statement, StatementKind },
@@ -13,90 +15,196 @@ pub fn interprete_trigger_statement(
13
15
  cursor_time: f32,
14
16
  max_end_time: f32
15
17
  ) -> Option<(f32, f32, AudioEngine)> {
16
- if let StatementKind::Trigger { entity, duration } = &stmt.kind {
17
- if let Some(trigger_val) = resolve_namespaced_variable(entity, variable_table) {
18
- let duration_secs = match duration {
19
- Duration::Number(n) => *n,
20
-
21
- Duration::Identifier(id) => {
22
- if id == "auto" {
23
- 1.0
18
+ if let StatementKind::Trigger { entity, duration, effects } = &stmt.kind {
19
+ let mut trigger_val = Value::String(entity.clone());
20
+ let mut trigger_src = String::new();
21
+
22
+ match variable_table.variables.get(entity) {
23
+ Some(Value::Identifier(id)) => {
24
+ // Get real value from global variable table
25
+ if let Some(global_table) = &variable_table.parent {
26
+ if let Some(val) = global_table.get(id) {
27
+ trigger_val = val.clone();
24
28
  } else {
25
- match variable_table.get(id) {
26
- Some(Value::Number(n)) => *n,
27
- Some(Value::Identifier(other)) if other == "auto" => 1.0,
28
- Some(other) => {
29
- eprintln!(
30
- "❌ Invalid duration reference '{}': expected number, got {:?}",
31
- id,
32
- other
33
- );
34
- return None;
35
- }
36
- None => {
37
- eprintln!("❌ Duration identifier '{}' not found", id);
38
- return None;
39
- }
40
- }
29
+ eprintln!("❌ Trigger entity '{}' not found in global variable table", id);
30
+ return None;
41
31
  }
32
+ } else if let Some(val) = variable_table.get(id) {
33
+ trigger_val = val.clone();
34
+ } else {
35
+ eprintln!("❌ Trigger entity '{}' not found in variable table", id);
36
+ return None;
42
37
  }
38
+ }
39
+ Some(Value::Sample(sample_src)) => {
40
+ // If the entity is a sample, we use its path directly
41
+ trigger_src = sample_src.clone();
42
+ }
43
+ Some(Value::Map(map)) => {
44
+ // If the entity is a map, we assume it contains an "entity" key
45
+ if let Some(Value::String(src)) = map.get("entity") {
46
+ trigger_val = Value::String(src.clone());
47
+ } else if let Some(Value::Identifier(src)) = map.get("entity") {
48
+ trigger_val = Value::Identifier(src.clone());
49
+ } else {
50
+ eprintln!(
51
+ "❌ Trigger map must contain an 'entity' key with a string or identifier value."
52
+ );
53
+ return None;
54
+ }
55
+ }
56
+ _ => {
57
+ trigger_val = Value::String(entity.clone());
58
+ }
59
+ }
43
60
 
44
- Duration::Beat(beat_str) => {
45
- // Assuming beat_str is in the format "numerator/denominator"
46
- let parts: Vec<&str> = beat_str.split('/').collect();
61
+ let duration_secs = match duration {
62
+ Duration::Number(n) => *n,
47
63
 
48
- if parts.len() != 2 {
49
- eprintln!("❌ Invalid beat duration format: {}", beat_str);
50
- return None;
64
+ Duration::Identifier(id) => {
65
+ if id == "auto" {
66
+ 1.0
67
+ } else {
68
+ match variable_table.get(id) {
69
+ Some(Value::Number(n)) => *n,
70
+ Some(Value::Identifier(other)) if other == "auto" => 1.0,
71
+ Some(other) => {
72
+ eprintln!(
73
+ "❌ Invalid duration reference '{}': expected number, got {:?}",
74
+ id,
75
+ other
76
+ );
77
+ return None;
78
+ }
79
+ None => {
80
+ eprintln!("❌ Duration identifier '{}' not found", id);
81
+ return None;
82
+ }
51
83
  }
84
+ }
85
+ }
52
86
 
53
- let numerator: f32 = parts[0].parse().unwrap_or(1.0);
54
- let denominator: f32 = parts[1].parse().unwrap_or(1.0);
55
- numerator / denominator
87
+ Duration::Beat(beat_str) => {
88
+ let parts: Vec<&str> = beat_str.split('/').collect();
89
+ if parts.len() != 2 {
90
+ eprintln!("❌ Invalid beat duration format: {}", beat_str);
91
+ return None;
56
92
  }
57
93
 
58
- Duration::Auto => 1.0,
59
- };
94
+ let numerator: f32 = parts[0].parse().unwrap_or(1.0);
95
+ let denominator: f32 = parts[1].parse().unwrap_or(4.0);
60
96
 
61
- let duration_final = duration_secs * base_duration;
97
+ let beats = (numerator / denominator) * 4.0;
62
98
 
63
- let (src, _) = load_trigger(
64
- trigger_val,
65
- duration,
66
- base_duration,
67
- variable_table.clone()
68
- );
99
+ beats * base_duration
100
+ }
69
101
 
70
- let mut updated_engine = audio_engine.clone();
71
- updated_engine.insert_sample(&src, cursor_time, duration_final, None);
102
+ Duration::Auto => base_duration,
103
+ };
72
104
 
73
- let new_cursor_time = cursor_time + duration_final;
74
- let new_max_end_time = new_cursor_time.max(max_end_time);
105
+ let final_variable_table = if let Some(parent) = &variable_table.parent {
106
+ VariableTable {
107
+ variables: parent.variables.clone(),
108
+ parent: None,
109
+ }
110
+ } else {
111
+ variable_table.clone()
112
+ };
113
+
114
+ let (src, sample_length) = load_trigger(
115
+ &trigger_val,
116
+ duration,
117
+ effects,
118
+ base_duration,
119
+ final_variable_table.clone()
120
+ );
121
+
122
+ if trigger_src.is_empty() {
123
+ trigger_src = src;
124
+ }
75
125
 
76
- return Some((new_cursor_time, new_max_end_time, updated_engine));
126
+ let effects = extract_effects(stmt.value.clone());
127
+ let one_shot = effects
128
+ .as_ref()
129
+ .and_then(|map| map.get("one_shot"))
130
+ .and_then(|v| {
131
+ match v {
132
+ Value::Identifier(id) if id == "true" => Some(true),
133
+ Value::String(s) if s == "true" => Some(true),
134
+ _ => None,
135
+ }
136
+ })
137
+ .unwrap_or(false);
138
+
139
+ let play_length = if one_shot {
140
+ sample_length // play entire sample
77
141
  } else {
78
- eprintln!("❌ Unknown trigger entity: {}", entity);
142
+ duration_secs.min(sample_length)
143
+ };
144
+
145
+ let trigger_src = match trigger_val.get("entity") {
146
+ Some(Value::String(src)) => src.clone(),
147
+ Some(Value::Identifier(id)) => id.clone(),
148
+ Some(Value::Statement(stmt)) => {
149
+ if let StatementKind::Trigger { entity, .. } = &stmt.kind {
150
+ entity.clone()
151
+ } else {
152
+ eprintln!("❌ Invalid trigger statement in map: expected 'Trigger' kind");
153
+ return None;
154
+ }
155
+ }
156
+ _ => trigger_src,
157
+ };
158
+
159
+ if let Some(effects_map) = effects {
160
+ audio_engine.insert_sample(
161
+ &trigger_src,
162
+ cursor_time,
163
+ play_length,
164
+ Some(effects_map),
165
+ &final_variable_table
166
+ );
167
+ } else {
168
+ audio_engine.insert_sample(
169
+ &trigger_src,
170
+ cursor_time,
171
+ play_length,
172
+ None,
173
+ &final_variable_table
174
+ );
79
175
  }
176
+
177
+ let new_cursor_time = cursor_time + duration_secs; // advance by beat duration
178
+ let new_max_end_time = (cursor_time + play_length).max(max_end_time);
179
+
180
+ let updated_engine = audio_engine.clone();
181
+
182
+ return Some((new_cursor_time, new_max_end_time, updated_engine));
80
183
  }
81
184
 
82
185
  None
83
186
  }
84
187
 
85
- fn resolve_namespaced_variable<'a>(path: &str, variables: &'a VariableTable) -> Option<&'a Value> {
86
- let mut current: Option<&Value> = None;
188
+ fn extract_effects(value: Value) -> Option<HashMap<String, Value>> {
189
+ if let Value::Map(map) = value {
190
+ let mut effects = HashMap::new();
87
191
 
88
- for (i, part) in path.split('.').enumerate() {
89
- if i == 0 {
90
- current = variables.get(part);
91
- } else {
92
- current = match current {
93
- Some(Value::Map(map)) => map.get(part),
94
- _ => {
95
- return None;
192
+ for (key, val) in map {
193
+ if key == "effects" {
194
+ if let Value::Map(effect_map) = val {
195
+ for (effect_key, effect_val) in effect_map {
196
+ effects.insert(effect_key, effect_val);
197
+ }
198
+ } else {
199
+ return None; // effects must be a map
96
200
  }
97
- };
201
+ } else {
202
+ return Some(effects);
203
+ }
98
204
  }
99
- }
100
205
 
101
- current
206
+ Some(effects)
207
+ } else {
208
+ None
209
+ }
102
210
  }
@@ -1,8 +1,13 @@
1
- use crate::core::{ shared::{ duration::Duration, value::Value }, store::variable::VariableTable };
1
+ use crate::core::{
2
+ parser::statement::StatementKind,
3
+ shared::{ duration::Duration, value::Value },
4
+ store::variable::VariableTable,
5
+ };
2
6
 
3
7
  pub fn load_trigger(
4
8
  trigger: &Value,
5
9
  duration: &Duration,
10
+ effects: &Option<Value>,
6
11
  base_duration: f32,
7
12
  variable_table: VariableTable
8
13
  ) -> (String, f32) {
@@ -13,8 +18,36 @@ pub fn load_trigger(
13
18
  Value::String(src) => {
14
19
  trigger_path = src.to_string();
15
20
  }
21
+ Value::Identifier(src) => {
22
+ trigger_path = src.to_string();
23
+ }
24
+
25
+ Value::Map(map) => {
26
+ if let Some(Value::String(src)) = map.get("entity") {
27
+ trigger_path = format!("devalang://bank/{}", src.to_string());
28
+ } else if let Some(Value::Identifier(src)) = map.get("entity") {
29
+ trigger_path = format!("devalang://bank/{}", src.to_string());
30
+ } else {
31
+ eprintln!(
32
+ "❌ Trigger map must contain an 'entity' key with a string or identifier value."
33
+ );
34
+ }
35
+ }
36
+ Value::Sample(src) => {
37
+ trigger_path = src.to_string();
38
+ }
39
+ Value::Statement(stmt) => {
40
+ if let StatementKind::Trigger { entity, duration, effects } = &stmt.kind {
41
+ trigger_path = entity.clone();
42
+ } else {
43
+ eprintln!("❌ Trigger statement must be of type 'Trigger', found: {:?}", stmt.kind);
44
+ }
45
+ }
16
46
  _ => {
17
- eprintln!("❌ Invalid trigger type. Expected a text variable.");
47
+ eprintln!(
48
+ "❌ Invalid trigger type. Expected a string or identifier variable, found: {:?}",
49
+ trigger
50
+ );
18
51
  }
19
52
  }
20
53
 
@@ -45,11 +78,11 @@ pub fn load_trigger(
45
78
 
46
79
  Duration::Beat(beat_str) => {
47
80
  let parts: Vec<&str> = beat_str.split('/').collect();
48
-
81
+
49
82
  if parts.len() == 2 {
50
83
  let numerator: f32 = parts[0].parse().unwrap_or(1.0);
51
84
  let denominator: f32 = parts[1].parse().unwrap_or(1.0);
52
- duration_as_secs = numerator / denominator * base_duration;
85
+ duration_as_secs = (numerator / denominator) * base_duration;
53
86
  } else {
54
87
  eprintln!("❌ Invalid beat duration format: {}", beat_str);
55
88
  }
@@ -21,23 +21,33 @@ impl AudioPlayer {
21
21
  }
22
22
  }
23
23
 
24
- fn load_source(&self, path: &str) -> impl Source<Item = f32> + Send + 'static {
25
- let file = File::open(path).unwrap();
26
- let reader = BufReader::new(file);
27
-
28
- Decoder::new(reader).unwrap().convert_samples()
24
+ fn load_source(&self, path: &str) -> Option<impl Source<Item = f32> + Send + 'static> {
25
+ if let Ok(file) = File::open(path) {
26
+ let reader = BufReader::new(file);
27
+ match Decoder::new(reader) {
28
+ Ok(decoder) => Some(decoder.convert_samples()),
29
+ Err(e) => {
30
+ eprintln!("❌ Failed to decode audio file '{}': {}", path, e);
31
+ None
32
+ }
33
+ }
34
+ } else {
35
+ eprintln!("❌ Could not open audio file: {}", path);
36
+ None
37
+ }
29
38
  }
30
39
 
31
40
  pub fn play_file_once(&mut self, path: &str) {
32
41
  self.sink.stop();
33
42
  self.sink = Sink::try_new(&self.handle).unwrap();
34
-
35
43
  self.sink.set_volume(1.0);
36
44
 
37
- let source = self.load_source(path);
38
-
39
- self.sink.append(source);
40
- self.last_path = Some(path.to_string());
45
+ if let Some(source) = self.load_source(path) {
46
+ self.sink.append(source);
47
+ self.last_path = Some(path.to_string());
48
+ } else {
49
+ eprintln!("⚠️ Skipping playback: failed to load '{}'", path);
50
+ }
41
51
  }
42
52
 
43
53
  pub fn replay_last(&mut self) {
@@ -1,5 +1,4 @@
1
1
  use std::collections::HashMap;
2
-
3
2
  use crate::{
4
3
  core::{
5
4
  audio::{ engine::AudioEngine, interpreter::driver::run_audio_program },
@@ -18,36 +17,36 @@ pub fn render_audio_with_modules(
18
17
 
19
18
  for (module_name, statements) in modules {
20
19
  let mut global_max_end_time: f32 = 0.0;
21
- let mut initial_engine = AudioEngine::new(module_name.clone());
20
+ let mut audio_engine = AudioEngine::new(module_name.clone());
22
21
 
23
22
  // Apply global variables to the initial engine
24
23
  if let Some(module) = global_store.get_module(&module_name) {
25
- initial_engine.set_variables(module.variable_table.clone());
26
- }
27
-
28
- // interprete statements to fill the audio buffer
29
- let (mut updated_engine, _bpm, module_max_end_time) = run_audio_program(
30
- &statements,
31
- initial_engine,
32
- module_name.clone(),
33
- output_dir.to_string()
34
- );
35
-
36
- // Verify if the buffer is silent (all samples are zero)
37
- if updated_engine.buffer.iter().all(|&s| s == 0) {
38
- let logger = Logger::new();
39
- logger.log_message(
40
- LogLevel::Warning,
41
- &format!("Module '{}' ignored: silent buffer (no non-zero samples)", module_name)
24
+ // interprete statements to fill the audio buffer
25
+ let (module_max_end_time, cursor_time) = run_audio_program(
26
+ &statements,
27
+ &mut audio_engine,
28
+ module_name.clone(),
29
+ output_dir.to_string(),
30
+ module.variable_table.clone(),
31
+ module.function_table.clone(),
32
+ global_store
42
33
  );
43
- continue;
44
- }
45
34
 
46
- // Determines the maximum end time for the module
47
- global_max_end_time = global_max_end_time.max(module_max_end_time);
48
- updated_engine.set_duration(global_max_end_time);
35
+ // Verify if the buffer is silent (all samples are zero)
36
+ if audio_engine.buffer.iter().all(|&s| s == 0) {
37
+ let logger = Logger::new();
38
+ logger.log_message(
39
+ LogLevel::Warning,
40
+ &format!("Module '{}' ignored: silent buffer (no non-zero samples)", module_name)
41
+ );
42
+ }
43
+
44
+ // Determines the maximum end time for the module
45
+ global_max_end_time = global_max_end_time.max(module_max_end_time);
46
+ audio_engine.set_duration(global_max_end_time);
49
47
 
50
- result.insert(module_name, updated_engine);
48
+ result.insert(module_name, audio_engine);
49
+ }
51
50
  }
52
51
 
53
52
  result
@@ -1,6 +1,7 @@
1
1
  pub mod preprocessor;
2
2
  pub mod lexer;
3
3
  pub mod store;
4
+ pub mod module;
4
5
 
5
6
  use std::io::Write;
6
7
 
@@ -13,6 +14,7 @@ impl Debugger {
13
14
 
14
15
  pub fn write_log_file(&self, path: &str, filename: &str, content: &str) {
15
16
  std::fs::create_dir_all(path).expect("Failed to create directory");
17
+
16
18
  let file_path = format!("{}/{}", path, filename);
17
19
  let mut file = std::fs::File::create(file_path).expect("Failed to create file");
18
20
 
@@ -0,0 +1,47 @@
1
+ use std::{ fs::create_dir_all };
2
+ use crate::core::{
3
+ debugger::Debugger,
4
+ store::{ function::FunctionTable, variable::VariableTable },
5
+ };
6
+
7
+ pub fn write_module_variable_log_file(
8
+ output_dir: &str,
9
+ module_path: &str,
10
+ variable_table: &VariableTable
11
+ ) {
12
+ let debugger = Debugger::new();
13
+ let mut content = String::new();
14
+ let module_name = module_path.split('/').last().unwrap_or("index").replace(".deva", "");
15
+
16
+ let log_directory = format!("{}/logs/modules/{}", output_dir, module_name);
17
+ create_dir_all(&log_directory).expect("Failed to create log directory");
18
+
19
+ for (var_name, var_data) in &variable_table.variables {
20
+ content.push_str(&format!("{:?} = {:?}\n", var_name, var_data));
21
+ }
22
+
23
+ content.push_str("\n");
24
+
25
+ debugger.write_log_file(&log_directory, "variables.log", &content);
26
+ }
27
+
28
+ pub fn write_module_function_log_file(
29
+ output_dir: &str,
30
+ module_path: &str,
31
+ function_table: &FunctionTable
32
+ ) {
33
+ let debugger = Debugger::new();
34
+ let mut content = String::new();
35
+ let module_name = module_path.split('/').last().unwrap_or("index").replace(".deva", "");
36
+
37
+ let log_directory = format!("{}/logs/modules/{}", output_dir, module_name);
38
+ create_dir_all(&log_directory).expect("Failed to create log directory");
39
+
40
+ for (func_name, func_data) in &function_table.functions {
41
+ content.push_str(&format!("{:?} = {:?}\n", func_name, func_data));
42
+ }
43
+
44
+ content.push_str("\n");
45
+
46
+ debugger.write_log_file(&log_directory, "functions.log", &content);
47
+ }