@devaloop/devalang 0.0.1-alpha.1 → 0.0.1-alpha.11

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 (168) hide show
  1. package/.devalang +9 -0
  2. package/Cargo.toml +15 -6
  3. package/README.md +79 -81
  4. package/docs/CHANGELOG.md +213 -0
  5. package/docs/ROADMAP.md +11 -8
  6. package/docs/TODO.md +32 -29
  7. package/examples/bank.deva +9 -0
  8. package/examples/condition.deva +20 -0
  9. package/examples/duration.deva +9 -0
  10. package/examples/group.deva +12 -0
  11. package/examples/index.deva +12 -5
  12. package/examples/loop.deva +16 -0
  13. package/examples/samples/hat-808.wav +0 -0
  14. package/examples/synth.deva +14 -0
  15. package/examples/variables.deva +9 -0
  16. package/out-tsc/bin/devalang.exe +0 -0
  17. package/out-tsc/scripts/version/fetch.js +1 -5
  18. package/package.json +5 -4
  19. package/project-version.json +3 -3
  20. package/rust/cli/bank.rs +455 -0
  21. package/rust/cli/build.rs +114 -28
  22. package/rust/cli/check.rs +96 -103
  23. package/rust/cli/driver.rs +280 -0
  24. package/rust/cli/init.rs +79 -0
  25. package/rust/cli/install.rs +17 -0
  26. package/rust/cli/mod.rs +8 -1
  27. package/rust/cli/play.rs +193 -0
  28. package/rust/cli/template.rs +57 -0
  29. package/rust/cli/update.rs +4 -0
  30. package/rust/common/cdn.rs +11 -0
  31. package/rust/common/mod.rs +1 -0
  32. package/rust/config/driver.rs +76 -0
  33. package/rust/config/loader.rs +110 -0
  34. package/rust/config/mod.rs +2 -0
  35. package/rust/core/audio/engine.rs +242 -0
  36. package/rust/core/audio/evaluator.rs +31 -0
  37. package/rust/core/audio/interpreter/arrow_call.rs +142 -0
  38. package/rust/core/audio/interpreter/call.rs +70 -0
  39. package/rust/core/audio/interpreter/condition.rs +69 -0
  40. package/rust/core/audio/interpreter/driver.rs +236 -0
  41. package/rust/core/audio/interpreter/let_.rs +19 -0
  42. package/rust/core/audio/interpreter/load.rs +18 -0
  43. package/rust/core/audio/interpreter/loop_.rs +67 -0
  44. package/rust/core/audio/interpreter/mod.rs +12 -0
  45. package/rust/core/audio/interpreter/sleep.rs +36 -0
  46. package/rust/core/audio/interpreter/spawn.rs +84 -0
  47. package/rust/core/audio/interpreter/tempo.rs +16 -0
  48. package/rust/core/audio/interpreter/trigger.rs +102 -0
  49. package/rust/core/audio/loader/mod.rs +1 -0
  50. package/rust/core/audio/loader/trigger.rs +64 -0
  51. package/rust/core/audio/mod.rs +6 -0
  52. package/rust/core/audio/player.rs +54 -0
  53. package/rust/core/audio/renderer.rs +54 -0
  54. package/rust/core/builder/mod.rs +70 -27
  55. package/rust/core/debugger/lexer.rs +27 -0
  56. package/rust/core/debugger/mod.rs +13 -49
  57. package/rust/core/debugger/preprocessor.rs +27 -0
  58. package/rust/core/debugger/store.rs +25 -0
  59. package/rust/core/error/mod.rs +60 -0
  60. package/rust/core/lexer/handler/arrow.rs +31 -0
  61. package/rust/core/lexer/handler/at.rs +21 -0
  62. package/rust/core/lexer/handler/brace.rs +41 -0
  63. package/rust/core/lexer/handler/colon.rs +21 -0
  64. package/rust/core/lexer/handler/comment.rs +30 -0
  65. package/rust/core/lexer/handler/dot.rs +21 -0
  66. package/rust/core/lexer/handler/driver.rs +241 -0
  67. package/rust/core/lexer/handler/identifier.rs +41 -0
  68. package/rust/core/lexer/handler/indent.rs +52 -0
  69. package/rust/core/lexer/handler/mod.rs +15 -0
  70. package/rust/core/lexer/handler/newline.rs +23 -0
  71. package/rust/core/lexer/handler/number.rs +31 -0
  72. package/rust/core/lexer/handler/operator.rs +44 -0
  73. package/rust/core/lexer/handler/slash.rs +21 -0
  74. package/rust/core/lexer/handler/string.rs +63 -0
  75. package/rust/core/lexer/mod.rs +37 -319
  76. package/rust/core/lexer/token.rs +87 -0
  77. package/rust/core/mod.rs +6 -2
  78. package/rust/core/parser/driver.rs +339 -0
  79. package/rust/core/parser/handler/arrow_call.rs +151 -0
  80. package/rust/core/parser/handler/at.rs +162 -0
  81. package/rust/core/parser/handler/bank.rs +41 -0
  82. package/rust/core/parser/handler/condition.rs +74 -0
  83. package/rust/core/parser/handler/dot.rs +178 -0
  84. package/rust/core/parser/handler/identifier/call.rs +41 -0
  85. package/rust/core/parser/handler/identifier/group.rs +75 -0
  86. package/rust/core/parser/handler/identifier/let_.rs +133 -0
  87. package/rust/core/parser/handler/identifier/mod.rs +51 -0
  88. package/rust/core/parser/handler/identifier/sleep.rs +33 -0
  89. package/rust/core/parser/handler/identifier/spawn.rs +41 -0
  90. package/rust/core/parser/handler/identifier/synth.rs +65 -0
  91. package/rust/core/parser/handler/loop_.rs +72 -0
  92. package/rust/core/parser/handler/mod.rs +8 -0
  93. package/rust/core/parser/handler/tempo.rs +47 -0
  94. package/rust/core/parser/mod.rs +3 -200
  95. package/rust/core/parser/statement.rs +96 -0
  96. package/rust/core/preprocessor/loader.rs +308 -0
  97. package/rust/core/preprocessor/mod.rs +2 -24
  98. package/rust/core/preprocessor/module.rs +42 -56
  99. package/rust/core/preprocessor/processor.rs +76 -0
  100. package/rust/core/preprocessor/resolver/bank.rs +41 -51
  101. package/rust/core/preprocessor/resolver/call.rs +123 -0
  102. package/rust/core/preprocessor/resolver/condition.rs +92 -0
  103. package/rust/core/preprocessor/resolver/driver.rs +232 -0
  104. package/rust/core/preprocessor/resolver/group.rs +61 -0
  105. package/rust/core/preprocessor/resolver/let_.rs +31 -0
  106. package/rust/core/preprocessor/resolver/loop_.rs +76 -67
  107. package/rust/core/preprocessor/resolver/mod.rs +12 -111
  108. package/rust/core/preprocessor/resolver/spawn.rs +58 -0
  109. package/rust/core/preprocessor/resolver/synth.rs +50 -0
  110. package/rust/core/preprocessor/resolver/tempo.rs +40 -61
  111. package/rust/core/preprocessor/resolver/trigger.rs +90 -154
  112. package/rust/core/preprocessor/resolver/value.rs +78 -0
  113. package/rust/core/shared/bank.rs +21 -0
  114. package/rust/core/shared/duration.rs +9 -0
  115. package/rust/core/shared/mod.rs +3 -0
  116. package/rust/core/shared/value.rs +29 -0
  117. package/rust/core/store/export.rs +28 -0
  118. package/rust/core/store/global.rs +39 -0
  119. package/rust/core/store/import.rs +28 -0
  120. package/rust/core/store/mod.rs +4 -0
  121. package/rust/core/store/variable.rs +28 -0
  122. package/rust/core/utils/mod.rs +2 -0
  123. package/rust/core/utils/path.rs +31 -0
  124. package/rust/core/utils/validation.rs +37 -0
  125. package/rust/installer/bank.rs +55 -0
  126. package/rust/installer/mod.rs +1 -0
  127. package/rust/lib.rs +162 -1
  128. package/rust/main.rs +104 -31
  129. package/rust/utils/file.rs +35 -0
  130. package/rust/utils/installer.rs +56 -0
  131. package/rust/utils/logger.rs +108 -34
  132. package/rust/utils/mod.rs +5 -3
  133. package/rust/utils/{loader.rs → spinner.rs} +2 -0
  134. package/rust/utils/watcher.rs +33 -0
  135. package/templates/minimal/.devalang +5 -0
  136. package/templates/minimal/README.md +202 -0
  137. package/templates/minimal/src/index.deva +2 -0
  138. package/templates/welcome/.devalang +5 -0
  139. package/templates/welcome/README.md +202 -0
  140. package/templates/welcome/samples/kick-808.wav +0 -0
  141. package/templates/welcome/src/index.deva +13 -0
  142. package/templates/welcome/src/variables.deva +5 -0
  143. package/typescript/scripts/version/fetch.ts +1 -6
  144. package/docs/COMMANDS.md +0 -31
  145. package/docs/SYNTAX.md +0 -148
  146. package/examples/exported.deva +0 -7
  147. package/rust/audio/mod.rs +0 -1
  148. package/rust/cli/new.rs +0 -1
  149. package/rust/core/parser/at.rs +0 -142
  150. package/rust/core/parser/bank.rs +0 -42
  151. package/rust/core/parser/dot.rs +0 -107
  152. package/rust/core/parser/identifer.rs +0 -91
  153. package/rust/core/parser/loop_.rs +0 -62
  154. package/rust/core/parser/tempo.rs +0 -42
  155. package/rust/core/parser/variable.rs +0 -129
  156. package/rust/core/preprocessor/dependencies.rs +0 -54
  157. package/rust/core/preprocessor/resolver/at.rs +0 -24
  158. package/rust/core/types/cli.rs +0 -160
  159. package/rust/core/types/mod.rs +0 -7
  160. package/rust/core/types/module.rs +0 -41
  161. package/rust/core/types/parser.rs +0 -73
  162. package/rust/core/types/statement.rs +0 -105
  163. package/rust/core/types/store.rs +0 -116
  164. package/rust/core/types/token.rs +0 -83
  165. package/rust/core/types/variable.rs +0 -32
  166. package/rust/runner/executer.rs +0 -44
  167. package/rust/runner/mod.rs +0 -1
  168. package/rust/utils/path.rs +0 -46
@@ -0,0 +1,236 @@
1
+ use crate::core::{
2
+ audio::{
3
+ engine::AudioEngine,
4
+ interpreter::{
5
+ arrow_call::interprete_call_arrow_statement,
6
+ call::interprete_call_statement,
7
+ condition::interprete_condition_statement,
8
+ let_::interprete_let_statement,
9
+ load::interprete_load_statement,
10
+ loop_::interprete_loop_statement,
11
+ sleep::interprete_sleep_statement,
12
+ spawn::interprete_spawn_statement,
13
+ tempo::interprete_tempo_statement,
14
+ trigger::interprete_trigger_statement,
15
+ },
16
+ },
17
+ parser::statement::{ Statement, StatementKind },
18
+ store::variable::VariableTable,
19
+ };
20
+
21
+ pub fn run_audio_program(
22
+ statements: &Vec<Statement>,
23
+ mut audio_engine: AudioEngine,
24
+ entry: String,
25
+ output: String
26
+ ) -> (AudioEngine, f32, f32) {
27
+ let mut base_bpm = 120.0;
28
+ let mut base_duration = 60.0 / base_bpm;
29
+
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(
47
+ audio_engine,
48
+ variable_table,
49
+ statements.clone(),
50
+ base_bpm,
51
+ base_duration,
52
+ 0.0,
53
+ 0.0
54
+ );
55
+
56
+ (updated_audio_engine, base_bpm, max_end_time)
57
+ }
58
+
59
+ pub fn execute_audio_block(
60
+ mut audio_engine: AudioEngine,
61
+ mut variable_table: VariableTable,
62
+ mut statements: Vec<Statement>,
63
+ mut base_bpm: f32,
64
+ mut base_duration: f32,
65
+ mut max_end_time: f32,
66
+ mut cursor_time: f32
67
+ ) -> (AudioEngine, f32, f32) {
68
+ let initial_cursor_time = cursor_time;
69
+
70
+ for stmt in statements.clone() {
71
+ match &stmt.kind {
72
+ StatementKind::Load { .. } => {
73
+ if
74
+ let Some(new_variable_table) = interprete_load_statement(
75
+ &stmt,
76
+ &mut variable_table
77
+ )
78
+ {
79
+ variable_table = new_variable_table;
80
+ } else {
81
+ eprintln!("❌ Failed to interpret load statement: {:?}", stmt);
82
+ }
83
+ }
84
+
85
+ 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
+ }
96
+ }
97
+
98
+ StatementKind::Tempo => {
99
+ if let Some((new_bpm, new_duration)) = interprete_tempo_statement(&stmt) {
100
+ base_bpm = new_bpm;
101
+ base_duration = new_duration;
102
+ } else {
103
+ eprintln!("❌ Failed to interpret tempo statement: {:?}", stmt);
104
+ }
105
+ }
106
+
107
+ StatementKind::Trigger { .. } => {
108
+ 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(
132
+ &stmt,
133
+ temp_engine,
134
+ &variable_table,
135
+ base_bpm,
136
+ base_duration,
137
+ initial_cursor_time,
138
+ max_end_time
139
+ )
140
+ {
141
+ audio_engine.merge_with(updated_engine);
142
+ }
143
+ }
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
+ StatementKind::Sleep => {
163
+ let (new_cursor, new_max) = interprete_sleep_statement(
164
+ &stmt,
165
+ cursor_time,
166
+ max_end_time
167
+ );
168
+ cursor_time = new_cursor;
169
+ max_end_time = new_max;
170
+ }
171
+
172
+ StatementKind::Loop => {
173
+ let (loop_engine, new_max, new_cursor) = interprete_loop_statement(
174
+ &stmt,
175
+ audio_engine.clone(),
176
+ variable_table.clone(),
177
+ base_bpm,
178
+ base_duration,
179
+ max_end_time,
180
+ cursor_time
181
+ );
182
+ audio_engine = loop_engine;
183
+ cursor_time = new_cursor;
184
+ max_end_time = new_max;
185
+ }
186
+
187
+ StatementKind::If | StatementKind::ElseIf | StatementKind::Else => {
188
+ let (condition_engine, new_max, new_cursor) = interprete_condition_statement(
189
+ &stmt,
190
+ audio_engine.clone(),
191
+ variable_table.clone(),
192
+ base_bpm,
193
+ base_duration,
194
+ max_end_time,
195
+ cursor_time
196
+ );
197
+
198
+ audio_engine = condition_engine;
199
+ cursor_time = new_cursor;
200
+ max_end_time = new_max;
201
+ }
202
+
203
+ StatementKind::ArrowCall { .. } => {
204
+ let (new_max_end_time, new_cursor_time) = interprete_call_arrow_statement(
205
+ &stmt,
206
+ &mut audio_engine,
207
+ &variable_table,
208
+ base_bpm,
209
+ base_duration,
210
+ &mut max_end_time,
211
+ Some(&mut cursor_time),
212
+ true
213
+ );
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);
229
+ }
230
+ }
231
+ }
232
+
233
+ audio_engine.set_variables(variable_table);
234
+
235
+ (audio_engine, base_bpm, max_end_time)
236
+ }
@@ -0,0 +1,19 @@
1
+ use crate::core::{
2
+ audio::engine::AudioEngine,
3
+ parser::statement::{ Statement, StatementKind },
4
+ shared::value::Value,
5
+ store::variable::VariableTable,
6
+ };
7
+
8
+ pub fn interprete_let_statement(
9
+ stmt: &Statement,
10
+ variable_table: &mut VariableTable
11
+ ) -> Option<VariableTable> {
12
+ if let StatementKind::Let { name } = &stmt.kind {
13
+ variable_table.set(name.to_string(), stmt.value.clone());
14
+
15
+ return Some(variable_table.clone())
16
+ }
17
+
18
+ None
19
+ }
@@ -0,0 +1,18 @@
1
+ use crate::core::{
2
+ parser::statement::{ Statement, StatementKind },
3
+ shared::value::Value,
4
+ store::variable::VariableTable,
5
+ };
6
+
7
+ pub fn interprete_load_statement(
8
+ stmt: &Statement,
9
+ variable_table: &mut VariableTable
10
+ ) -> Option<VariableTable> {
11
+ if let StatementKind::Load { source, alias } = &stmt.kind {
12
+ variable_table.set(alias.to_string(), Value::String(source.clone()));
13
+
14
+ return Some(variable_table.clone());
15
+ }
16
+
17
+ None
18
+ }
@@ -0,0 +1,67 @@
1
+ use crate::core::{
2
+ audio::{ engine::AudioEngine, interpreter::driver::execute_audio_block },
3
+ parser::statement::{ Statement, StatementKind },
4
+ shared::{ duration::Duration, value::Value },
5
+ store::variable::VariableTable,
6
+ };
7
+
8
+ pub fn interprete_loop_statement(
9
+ stmt: &Statement,
10
+ audio_engine: AudioEngine,
11
+ variable_table: VariableTable,
12
+ base_bpm: f32,
13
+ base_duration: f32,
14
+ max_end_time: f32,
15
+ cursor_time: f32
16
+ ) -> (AudioEngine, f32, f32) {
17
+ if let Value::Map(loop_value) = &stmt.value {
18
+ let loop_count = match loop_value.get("iterator") {
19
+ Some(Value::Number(n)) => *n as usize,
20
+ Some(Value::Identifier(ident)) => {
21
+ if let Some(Value::Number(n)) = variable_table.get(ident) {
22
+ *n as usize
23
+ } else {
24
+ eprintln!("❌ Loop iterator must be a number, found: {:?}", ident);
25
+ return (audio_engine, max_end_time, cursor_time);
26
+ }
27
+ }
28
+ _ => {
29
+ eprintln!("❌ Loop iterator must be a number, found: {:?}", loop_value.get("iterator"));
30
+ return (audio_engine, max_end_time, cursor_time);
31
+ }
32
+ };
33
+
34
+ let loop_body = match loop_value.get("body") {
35
+ Some(Value::Block(body)) => body.clone(),
36
+ _ => {
37
+ eprintln!("❌ Loop body must be a block, found: {:?}", loop_value.get("body"));
38
+ return (audio_engine, max_end_time, cursor_time);
39
+ }
40
+ };
41
+
42
+ let mut engine = audio_engine;
43
+ let mut cur_time = cursor_time;
44
+ let mut max_time = max_end_time;
45
+
46
+ for _ in 0..loop_count {
47
+ let (eng, _, end_time) = execute_audio_block(
48
+ engine,
49
+ variable_table.clone(),
50
+ loop_body.clone(),
51
+ base_bpm,
52
+ base_duration,
53
+ max_time,
54
+ cur_time
55
+ );
56
+
57
+ engine = eng;
58
+ cur_time = end_time;
59
+ max_time = max_time.max(end_time);
60
+ }
61
+
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)
66
+ }
67
+ }
@@ -0,0 +1,12 @@
1
+ pub mod driver;
2
+
3
+ pub mod load;
4
+ pub mod let_;
5
+ pub mod tempo;
6
+ pub mod trigger;
7
+ pub mod spawn;
8
+ pub mod sleep;
9
+ pub mod loop_;
10
+ pub mod call;
11
+ pub mod condition;
12
+ pub mod arrow_call;
@@ -0,0 +1,36 @@
1
+ use crate::core::{
2
+ audio::{ engine::AudioEngine, loader::trigger::load_trigger },
3
+ parser::statement::{ Statement, StatementKind },
4
+ shared::{ duration::Duration, value::Value },
5
+ store::variable::VariableTable,
6
+ };
7
+
8
+ pub fn interprete_sleep_statement(
9
+ stmt: &Statement,
10
+ cursor_time: f32,
11
+ max_end_time: f32,
12
+ ) -> (f32, f32) {
13
+ let duration_secs = match &stmt.value {
14
+ Value::Number(ms) => *ms / 1000.0,
15
+ Value::String(s) if s.ends_with("ms") => {
16
+ s.trim_end_matches("ms").parse::<f32>().map(|ms| ms / 1000.0).unwrap_or_else(|_| {
17
+ eprintln!("❌ Invalid sleep value (ms): {}", s);
18
+ 0.0
19
+ })
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
+ other => {
28
+ eprintln!("❌ Invalid sleep value: {:?}", other);
29
+ 0.0
30
+ }
31
+ };
32
+
33
+ let new_cursor = cursor_time + duration_secs;
34
+ let new_max = max_end_time.max(new_cursor);
35
+ (new_cursor, new_max)
36
+ }
@@ -0,0 +1,84 @@
1
+ use crate::core::{
2
+ audio::{ engine::AudioEngine, interpreter::driver::execute_audio_block },
3
+ parser::statement::Statement,
4
+ shared::value::Value,
5
+ store::variable::VariableTable,
6
+ };
7
+
8
+ pub fn interprete_spawn_statement(
9
+ stmt: &Statement,
10
+ audio_engine: AudioEngine,
11
+ variable_table: &VariableTable,
12
+ base_bpm: f32,
13
+ base_duration: f32,
14
+ 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
+ }
29
+
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(),
36
+ base_bpm,
37
+ base_duration,
38
+ max_end_time,
39
+ cursor_time
40
+ );
41
+ return Some((max_end_time.max(end_time), end_time, eng));
42
+ } else {
43
+ eprintln!("❌ Spawn map has no 'body' block");
44
+ }
45
+ None
46
+ }
47
+
48
+ _ => {
49
+ eprintln!("❌ Invalid spawn statement: expected identifier, found {:?}", stmt.value);
50
+ None
51
+ }
52
+ }
53
+ }
54
+
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);
78
+ }
79
+ } else {
80
+ eprintln!("❌ Spawn group '{}' not found or not a map", identifier);
81
+ }
82
+
83
+ None
84
+ }
@@ -0,0 +1,16 @@
1
+ use crate::core::{ parser::statement::{ Statement, StatementKind }, shared::value::Value };
2
+
3
+ pub fn interprete_tempo_statement(stmt: &Statement) -> Option<(f32, f32)> {
4
+ if let StatementKind::Tempo = &stmt.kind {
5
+ if let Value::Number(bpm) = &stmt.value {
6
+ let bpm = *bpm as f32;
7
+ let duration = 60.0 / bpm;
8
+
9
+ return Some((bpm, duration));
10
+ } else {
11
+ eprintln!("❌ Invalid tempo value: {:?}", stmt.value);
12
+ }
13
+ }
14
+
15
+ None
16
+ }
@@ -0,0 +1,102 @@
1
+ use crate::core::{
2
+ audio::{ engine::AudioEngine, loader::trigger::load_trigger },
3
+ parser::statement::{ Statement, StatementKind },
4
+ shared::{ duration::Duration, value::Value },
5
+ store::variable::VariableTable,
6
+ };
7
+
8
+ pub fn interprete_trigger_statement(
9
+ stmt: &Statement,
10
+ audio_engine: &mut AudioEngine,
11
+ variable_table: &VariableTable,
12
+ base_duration: f32,
13
+ cursor_time: f32,
14
+ max_end_time: f32
15
+ ) -> 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
24
+ } 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
+ }
41
+ }
42
+ }
43
+
44
+ Duration::Beat(beat_str) => {
45
+ // Assuming beat_str is in the format "numerator/denominator"
46
+ let parts: Vec<&str> = beat_str.split('/').collect();
47
+
48
+ if parts.len() != 2 {
49
+ eprintln!("❌ Invalid beat duration format: {}", beat_str);
50
+ return None;
51
+ }
52
+
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
56
+ }
57
+
58
+ Duration::Auto => 1.0,
59
+ };
60
+
61
+ let duration_final = duration_secs * base_duration;
62
+
63
+ let (src, _) = load_trigger(
64
+ trigger_val,
65
+ duration,
66
+ base_duration,
67
+ variable_table.clone()
68
+ );
69
+
70
+ let mut updated_engine = audio_engine.clone();
71
+ updated_engine.insert_sample(&src, cursor_time, duration_final, None);
72
+
73
+ let new_cursor_time = cursor_time + duration_final;
74
+ let new_max_end_time = new_cursor_time.max(max_end_time);
75
+
76
+ return Some((new_cursor_time, new_max_end_time, updated_engine));
77
+ } else {
78
+ eprintln!("❌ Unknown trigger entity: {}", entity);
79
+ }
80
+ }
81
+
82
+ None
83
+ }
84
+
85
+ fn resolve_namespaced_variable<'a>(path: &str, variables: &'a VariableTable) -> Option<&'a Value> {
86
+ let mut current: Option<&Value> = None;
87
+
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;
96
+ }
97
+ };
98
+ }
99
+ }
100
+
101
+ current
102
+ }
@@ -0,0 +1 @@
1
+ pub mod trigger;
@@ -0,0 +1,64 @@
1
+ use crate::core::{ shared::{ duration::Duration, value::Value }, store::variable::VariableTable };
2
+
3
+ pub fn load_trigger(
4
+ trigger: &Value,
5
+ duration: &Duration,
6
+ base_duration: f32,
7
+ variable_table: VariableTable
8
+ ) -> (String, f32) {
9
+ let mut trigger_path = String::new();
10
+ let mut duration_as_secs = 0.0;
11
+
12
+ match trigger {
13
+ Value::String(src) => {
14
+ trigger_path = src.to_string();
15
+ }
16
+ _ => {
17
+ eprintln!("❌ Invalid trigger type. Expected a text variable.");
18
+ }
19
+ }
20
+
21
+ match duration {
22
+ Duration::Identifier(duration_identifier) => {
23
+ if duration_identifier == "auto" {
24
+ duration_as_secs = base_duration;
25
+ } else if let Some(Value::Number(num)) = variable_table.get(duration_identifier) {
26
+ duration_as_secs = *num;
27
+ } else if let Some(Value::String(num_str)) = variable_table.get(duration_identifier) {
28
+ duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
29
+ } else if
30
+ let Some(Value::Identifier(num_str)) = variable_table.get(duration_identifier)
31
+ {
32
+ duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
33
+ } else {
34
+ eprintln!("❌ Invalid duration identifier: {}", duration_identifier);
35
+ }
36
+ }
37
+
38
+ Duration::Number(num) => {
39
+ duration_as_secs = *num;
40
+ }
41
+
42
+ Duration::Auto => {
43
+ duration_as_secs = base_duration;
44
+ }
45
+
46
+ Duration::Beat(beat_str) => {
47
+ let parts: Vec<&str> = beat_str.split('/').collect();
48
+
49
+ if parts.len() == 2 {
50
+ let numerator: f32 = parts[0].parse().unwrap_or(1.0);
51
+ let denominator: f32 = parts[1].parse().unwrap_or(1.0);
52
+ duration_as_secs = numerator / denominator * base_duration;
53
+ } else {
54
+ eprintln!("❌ Invalid beat duration format: {}", beat_str);
55
+ }
56
+ }
57
+
58
+ _ => {
59
+ eprintln!("❌ Invalid duration type. Expected an identifier.");
60
+ }
61
+ }
62
+
63
+ (trigger_path, duration_as_secs)
64
+ }
@@ -0,0 +1,6 @@
1
+ pub mod engine;
2
+ pub mod interpreter;
3
+ pub mod loader;
4
+ pub mod player;
5
+ pub mod renderer;
6
+ pub mod evaluator;