@devaloop/devalang 0.0.1-alpha.16-hotfix.3 → 0.0.1-alpha.18

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 (239) hide show
  1. package/.cargo/config.toml +2 -0
  2. package/.devalang +10 -10
  3. package/.github/workflows/ci.yml +0 -1
  4. package/Cargo.toml +18 -2
  5. package/README.md +82 -34
  6. package/docs/CHANGELOG.md +91 -0
  7. package/docs/ROADMAP.md +7 -4
  8. package/docs/TODO.md +1 -1
  9. package/examples/index.deva +55 -35
  10. package/examples/pattern.deva +5 -5
  11. package/out-tsc/bin/index.d.ts +2 -0
  12. package/out-tsc/core/functions/index.d.ts +37 -0
  13. package/out-tsc/core/functions/index.js +76 -0
  14. package/out-tsc/core/index.d.ts +6 -0
  15. package/out-tsc/core/index.js +22 -0
  16. package/out-tsc/core/types/index.d.ts +4 -0
  17. package/out-tsc/core/types/index.js +20 -0
  18. package/out-tsc/core/types/plugin.d.ts +18 -0
  19. package/out-tsc/core/types/plugin.js +2 -0
  20. package/out-tsc/core/types/result.d.ts +27 -0
  21. package/out-tsc/core/types/result.js +2 -0
  22. package/out-tsc/core/types/statement.d.ts +106 -0
  23. package/out-tsc/core/types/statement.js +2 -0
  24. package/out-tsc/core/types/value.d.ts +43 -0
  25. package/out-tsc/core/types/value.js +2 -0
  26. package/out-tsc/index.d.ts +7 -0
  27. package/out-tsc/index.js +41 -2
  28. package/out-tsc/pkg/devalang_core.d.ts +7 -0
  29. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +33 -0
  30. package/out-tsc/scripts/copy-wasm-dts.d.ts +1 -0
  31. package/out-tsc/scripts/copy-wasm-dts.js +73 -0
  32. package/out-tsc/scripts/postinstall.d.ts +1 -0
  33. package/out-tsc/scripts/postinstall.js +33 -23
  34. package/out-tsc/scripts/version/bump.d.ts +1 -0
  35. package/out-tsc/scripts/version/fetch.d.ts +1 -0
  36. package/out-tsc/scripts/version/index.d.ts +1 -0
  37. package/out-tsc/scripts/version/sync.d.ts +1 -0
  38. package/package.json +16 -4
  39. package/project-version.json +3 -3
  40. package/rust/cli/bank/api.rs +122 -0
  41. package/rust/cli/bank/commands.rs +275 -0
  42. package/rust/cli/bank/mod.rs +29 -0
  43. package/rust/cli/build/commands.rs +107 -0
  44. package/rust/cli/build/mod.rs +2 -0
  45. package/rust/cli/build/process.rs +146 -0
  46. package/rust/cli/{check.rs → check/mod.rs} +18 -31
  47. package/rust/cli/discover/commands.rs +253 -0
  48. package/rust/cli/discover/config.rs +111 -0
  49. package/rust/cli/discover/fs.rs +19 -0
  50. package/rust/cli/discover/install.rs +103 -0
  51. package/rust/cli/discover/metadata.rs +48 -0
  52. package/rust/cli/discover/mod.rs +5 -0
  53. package/rust/cli/{init.rs → init/commands.rs} +88 -87
  54. package/rust/cli/init/mod.rs +1 -0
  55. package/rust/cli/install/addon.rs +126 -0
  56. package/rust/cli/install/bank.rs +53 -0
  57. package/rust/cli/{install.rs → install/commands.rs} +9 -9
  58. package/rust/{installer → cli/install}/mod.rs +2 -3
  59. package/rust/cli/install/plugin.rs +61 -0
  60. package/rust/cli/{login.rs → login/commands.rs} +8 -11
  61. package/rust/cli/login/mod.rs +1 -0
  62. package/rust/cli/mod.rs +2 -2
  63. package/rust/cli/{driver.rs → parser.rs} +7 -2
  64. package/rust/cli/play/commands.rs +324 -0
  65. package/rust/cli/play/io.rs +17 -0
  66. package/rust/cli/play/mod.rs +5 -0
  67. package/rust/cli/play/process.rs +150 -0
  68. package/rust/cli/play/realtime.rs +91 -0
  69. package/rust/cli/play/utils.rs +23 -0
  70. package/rust/cli/{telemetry.rs → telemetry/commands.rs} +4 -4
  71. package/rust/cli/telemetry/event_creator.rs +80 -0
  72. package/rust/cli/telemetry/mod.rs +3 -0
  73. package/rust/cli/telemetry/send.rs +51 -0
  74. package/rust/cli/{template.rs → template/commands.rs} +1 -1
  75. package/rust/cli/template/mod.rs +1 -0
  76. package/rust/cli/{update.rs → update/commands.rs} +6 -6
  77. package/rust/cli/update/mod.rs +1 -0
  78. package/rust/config/driver.rs +57 -72
  79. package/rust/config/mod.rs +1 -2
  80. package/rust/config/ops.rs +26 -0
  81. package/rust/config/settings.rs +40 -42
  82. package/rust/core/audio/engine/helpers.rs +158 -0
  83. package/rust/core/audio/engine/mod.rs +7 -0
  84. package/rust/core/audio/engine/sample.rs +359 -0
  85. package/rust/core/audio/engine/synth.rs +325 -0
  86. package/rust/core/audio/evaluator.rs +68 -27
  87. package/rust/core/audio/interpreter/arrow_call.rs +113 -33
  88. package/rust/core/audio/interpreter/call.rs +232 -56
  89. package/rust/core/audio/interpreter/condition.rs +3 -2
  90. package/rust/core/audio/interpreter/driver.rs +206 -151
  91. package/rust/core/audio/interpreter/let_.rs +1 -1
  92. package/rust/core/audio/interpreter/load.rs +2 -1
  93. package/rust/core/audio/interpreter/loop_.rs +7 -6
  94. package/rust/core/audio/interpreter/sleep.rs +2 -1
  95. package/rust/core/audio/interpreter/spawn.rs +186 -54
  96. package/rust/core/audio/interpreter/tempo.rs +31 -10
  97. package/rust/core/audio/interpreter/trigger.rs +2 -2
  98. package/rust/core/audio/loader/trigger.rs +4 -7
  99. package/rust/core/audio/player.rs +6 -0
  100. package/rust/core/audio/renderer.rs +5 -7
  101. package/rust/core/audio/special/env.rs +3 -1
  102. package/rust/core/audio/special/math.rs +26 -6
  103. package/rust/core/audio/special/modulator.rs +2 -2
  104. package/rust/core/builder/mod.rs +9 -3
  105. package/rust/core/debugger/lexer.rs +1 -1
  106. package/rust/core/debugger/mod.rs +6 -0
  107. package/rust/core/debugger/module.rs +4 -4
  108. package/rust/core/debugger/preprocessor.rs +1 -1
  109. package/rust/core/debugger/store.rs +2 -2
  110. package/rust/core/error/mod.rs +189 -0
  111. package/rust/core/lexer/driver.rs +61 -0
  112. package/rust/core/lexer/handler/arrow.rs +1 -1
  113. package/rust/core/lexer/handler/at.rs +1 -1
  114. package/rust/core/lexer/handler/brace.rs +2 -2
  115. package/rust/core/lexer/handler/colon.rs +1 -1
  116. package/rust/core/lexer/handler/comment.rs +1 -1
  117. package/rust/core/lexer/handler/dot.rs +1 -1
  118. package/rust/core/lexer/handler/driver.rs +1 -1
  119. package/rust/core/lexer/handler/identifier.rs +4 -3
  120. package/rust/core/lexer/handler/mod.rs +1 -2
  121. package/rust/core/lexer/handler/number.rs +1 -1
  122. package/rust/core/lexer/handler/operator.rs +1 -1
  123. package/rust/core/lexer/handler/parenthesis.rs +2 -2
  124. package/rust/core/lexer/handler/slash.rs +1 -1
  125. package/rust/core/lexer/handler/string.rs +1 -1
  126. package/rust/core/lexer/mod.rs +1 -52
  127. package/rust/core/lexer/token.rs +91 -97
  128. package/rust/core/mod.rs +0 -1
  129. package/rust/core/parser/driver.rs +78 -22
  130. package/rust/core/parser/handler/arrow_call.rs +28 -8
  131. package/rust/core/parser/handler/at.rs +55 -21
  132. package/rust/core/parser/handler/bank.rs +14 -4
  133. package/rust/core/parser/handler/condition.rs +6 -3
  134. package/rust/core/parser/handler/dot.rs +5 -3
  135. package/rust/core/parser/handler/identifier/automate.rs +13 -16
  136. package/rust/core/parser/handler/identifier/call.rs +4 -4
  137. package/rust/core/parser/handler/identifier/emit.rs +9 -5
  138. package/rust/core/parser/handler/identifier/function.rs +20 -7
  139. package/rust/core/parser/handler/identifier/group.rs +11 -7
  140. package/rust/core/parser/handler/identifier/let_.rs +24 -9
  141. package/rust/core/parser/handler/identifier/mod.rs +6 -5
  142. package/rust/core/parser/handler/identifier/on.rs +16 -7
  143. package/rust/core/parser/handler/identifier/print.rs +6 -9
  144. package/rust/core/parser/handler/identifier/sleep.rs +12 -5
  145. package/rust/core/parser/handler/identifier/spawn.rs +4 -4
  146. package/rust/core/parser/handler/identifier/synth.rs +79 -9
  147. package/rust/core/parser/handler/loop_.rs +38 -13
  148. package/rust/core/parser/handler/mod.rs +1 -0
  149. package/rust/core/parser/handler/pattern.rs +74 -0
  150. package/rust/core/parser/handler/tempo.rs +9 -5
  151. package/rust/core/parser/mod.rs +0 -1
  152. package/rust/core/parser/statement.rs +6 -137
  153. package/rust/core/plugin/loader.rs +41 -27
  154. package/rust/core/plugin/runner.rs +68 -17
  155. package/rust/core/preprocessor/loader.rs +181 -99
  156. package/rust/core/preprocessor/processor.rs +9 -9
  157. package/rust/core/preprocessor/resolver/bank.rs +6 -8
  158. package/rust/core/preprocessor/resolver/call.rs +47 -23
  159. package/rust/core/preprocessor/resolver/condition.rs +6 -8
  160. package/rust/core/preprocessor/resolver/driver.rs +28 -28
  161. package/rust/core/preprocessor/resolver/function.rs +6 -6
  162. package/rust/core/preprocessor/resolver/group.rs +6 -8
  163. package/rust/core/preprocessor/resolver/loop_.rs +8 -10
  164. package/rust/core/preprocessor/resolver/mod.rs +1 -0
  165. package/rust/core/preprocessor/resolver/pattern.rs +75 -0
  166. package/rust/core/preprocessor/resolver/spawn.rs +45 -22
  167. package/rust/core/preprocessor/resolver/synth.rs +6 -8
  168. package/rust/core/preprocessor/resolver/tempo.rs +6 -8
  169. package/rust/core/preprocessor/resolver/trigger.rs +22 -19
  170. package/rust/core/preprocessor/resolver/value.rs +99 -4
  171. package/rust/core/store/export.rs +28 -28
  172. package/rust/core/store/function.rs +6 -0
  173. package/rust/core/store/global.rs +7 -1
  174. package/rust/core/store/import.rs +28 -28
  175. package/rust/core/store/variable.rs +16 -2
  176. package/rust/core/utils/mod.rs +0 -1
  177. package/rust/lib.rs +102 -9
  178. package/rust/main.rs +159 -45
  179. package/rust/types/Cargo.toml +11 -0
  180. package/rust/types/src/addons.rs +55 -0
  181. package/rust/types/src/ast.rs +202 -0
  182. package/rust/types/src/config.rs +74 -0
  183. package/rust/types/src/lib.rs +12 -0
  184. package/rust/types/src/telemetry.rs +85 -0
  185. package/rust/utils/Cargo.toml +26 -0
  186. package/rust/utils/{error.rs → src/error.rs} +186 -200
  187. package/rust/utils/src/file.rs +94 -0
  188. package/rust/utils/src/first_usage.rs +97 -0
  189. package/rust/utils/{mod.rs → src/lib.rs} +1 -1
  190. package/rust/utils/{logger.rs → src/logger.rs} +17 -12
  191. package/rust/utils/src/path.rs +88 -0
  192. package/rust/utils/src/signature.rs +41 -0
  193. package/rust/utils/{spinner.rs → src/spinner.rs} +3 -5
  194. package/rust/utils/src/version.rs +27 -0
  195. package/rust/utils/{watcher.rs → src/watcher.rs} +13 -1
  196. package/rust/web/cdn.rs +34 -0
  197. package/templates/minimal/README.md +98 -54
  198. package/templates/welcome/README.md +98 -54
  199. package/templates/welcome/src/index.deva +56 -8
  200. package/templates/welcome/src/variables.deva +2 -4
  201. package/tests/rust/TODO.md +0 -0
  202. package/tests/typescript/index.spec.ts +136 -0
  203. package/tests/typescript/playhead.spec.ts +36 -0
  204. package/tests/typescript/render_e2e.spec.ts +77 -0
  205. package/tsconfig.json +1 -1
  206. package/typescript/core/functions/index.ts +83 -0
  207. package/typescript/core/index.ts +6 -0
  208. package/typescript/core/types/index.ts +4 -0
  209. package/typescript/core/types/plugin.ts +19 -0
  210. package/typescript/core/types/result.ts +29 -0
  211. package/typescript/core/types/statement.ts +47 -0
  212. package/typescript/core/types/value.ts +29 -0
  213. package/typescript/index.ts +7 -2
  214. package/typescript/pkg/devalang_core.d.ts +4 -0
  215. package/typescript/scripts/copy-wasm-dts.ts +41 -0
  216. package/rust/cli/bank.rs +0 -462
  217. package/rust/cli/build.rs +0 -252
  218. package/rust/cli/play.rs +0 -1123
  219. package/rust/common/cdn.rs +0 -5
  220. package/rust/config/loader.rs +0 -165
  221. package/rust/config/stats.rs +0 -257
  222. package/rust/core/audio/engine.rs +0 -696
  223. package/rust/core/shared/bank.rs +0 -21
  224. package/rust/core/shared/duration.rs +0 -9
  225. package/rust/core/shared/mod.rs +0 -3
  226. package/rust/core/shared/value.rs +0 -35
  227. package/rust/core/utils/validation.rs +0 -35
  228. package/rust/installer/addon.rs +0 -84
  229. package/rust/installer/bank.rs +0 -62
  230. package/rust/installer/plugin.rs +0 -54
  231. package/rust/installer/utils.rs +0 -56
  232. package/rust/utils/file.rs +0 -38
  233. package/rust/utils/first_usage.rs +0 -83
  234. package/rust/utils/signature.rs +0 -19
  235. package/rust/utils/telemetry.rs +0 -292
  236. package/rust/utils/version.rs +0 -15
  237. /package/rust/{common → web}/api.rs +0 -0
  238. /package/rust/{common → web}/mod.rs +0 -0
  239. /package/rust/{common → web}/sso.rs +0 -0
@@ -1,7 +1,8 @@
1
+ use devalang_types::{Duration, Value};
2
+
1
3
  use crate::core::{
2
4
  audio::{engine::AudioEngine, interpreter::driver::execute_audio_block},
3
5
  parser::statement::{Statement, StatementKind},
4
- shared::value::Value,
5
6
  store::{function::FunctionTable, global::GlobalStore, variable::VariableTable},
6
7
  };
7
8
 
@@ -16,71 +17,152 @@ pub fn interprete_spawn_statement(
16
17
  max_end_time: f32,
17
18
  cursor_time: f32,
18
19
  ) -> (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
- // Function case
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()
20
+ if let StatementKind::Spawn { name, args } = &stmt.kind {
21
+ let mut local_engine = AudioEngine::new(audio_engine.module_name.clone());
22
+
23
+ // Function case
24
+ if let Some(func) = functions.functions.get(name) {
25
+ if func.parameters.len() != args.len() {
26
+ return (max_end_time, cursor_time);
27
+ }
28
+
29
+ let mut local_vars = VariableTable::with_parent(variable_table.clone());
30
+ for (param, arg) in func.parameters.iter().zip(args) {
31
+ local_vars.set(param.clone(), arg.clone());
32
+ }
33
+
34
+ let (spawn_max, _) = execute_audio_block(
35
+ &mut local_engine,
36
+ global_store,
37
+ local_vars,
38
+ functions.clone(),
39
+ &func.body,
40
+ base_bpm,
41
+ base_duration,
42
+ 0.0,
43
+ 0.0,
44
+ );
45
+
46
+ audio_engine.merge_with(local_engine);
47
+ return (spawn_max.max(max_end_time), cursor_time);
48
+ }
49
+
50
+ // Group case
51
+ if let Some(group_stmt) = find_group(name, variable_table, global_store) {
52
+ if let Value::Map(map) = &group_stmt.value {
53
+ if let Some(Value::Block(body)) = map.get("body") {
54
+ let (spawn_max, _) = execute_audio_block(
55
+ &mut local_engine,
56
+ global_store,
57
+ variable_table.clone(),
58
+ functions.clone(),
59
+ body,
60
+ base_bpm,
61
+ base_duration,
62
+ 0.0,
63
+ 0.0,
31
64
  );
32
- return (max_end_time, cursor_time);
65
+ audio_engine.merge_with(local_engine);
66
+ return (spawn_max.max(max_end_time), cursor_time);
33
67
  }
68
+ }
69
+ }
34
70
 
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());
71
+ // Pattern case: allow spawning a pattern similar to call
72
+ if let Some(pattern_stmt) = find_pattern(name, variable_table, global_store) {
73
+ if let Value::String(pat) = &pattern_stmt.value {
74
+ let mut target_entity = name.clone();
75
+ if let StatementKind::Pattern { name: _n, target } = &pattern_stmt.kind {
76
+ if let Some(t) = target {
77
+ target_entity = t.clone();
78
+ }
38
79
  }
39
80
 
40
- let (spawn_max, _) = execute_audio_block(
41
- &mut local_engine,
42
- global_store,
43
- local_vars,
44
- functions.clone(),
45
- &func.body,
46
- base_bpm,
47
- base_duration,
48
- 0.0,
49
- 0.0,
50
- );
81
+ let final_variable_table = if let Some(parent) = &variable_table.parent {
82
+ crate::core::store::variable::VariableTable {
83
+ variables: parent.variables.clone(),
84
+ parent: None,
85
+ }
86
+ } else {
87
+ variable_table.clone()
88
+ };
51
89
 
52
- audio_engine.merge_with(local_engine);
53
- return (spawn_max.max(max_end_time), cursor_time);
54
- }
90
+ let pattern_str: String = pat.chars().filter(|c| !c.is_whitespace()).collect();
91
+ if pattern_str.is_empty() {
92
+ return (max_end_time, cursor_time);
93
+ }
94
+
95
+ let step_count = pattern_str.len() as f32;
96
+ let total_bar = 4.0 * base_duration;
97
+ let step_duration = total_bar / step_count;
98
+
99
+ let mut updated_max = max_end_time;
100
+
101
+ for (i, ch) in pattern_str.chars().enumerate() {
102
+ if ch == '-' {
103
+ continue;
104
+ }
105
+
106
+ let event_time = cursor_time + (i as f32) * step_duration;
107
+
108
+ let mut trigger_val = Value::String(target_entity.clone());
109
+ if let Some(val) = variable_table.variables.get(&target_entity) {
110
+ match val {
111
+ Value::Identifier(id) => {
112
+ if let Some(parent) = &variable_table.parent {
113
+ if let Some(v) = parent.get(id) {
114
+ trigger_val = v.clone();
115
+ }
116
+ } else if let Some(v) = variable_table.get(id) {
117
+ trigger_val = v.clone();
118
+ }
119
+ }
120
+ Value::Map(map) => {
121
+ if let Some(Value::String(src)) = map.get("entity") {
122
+ trigger_val = Value::String(src.clone());
123
+ } else if let Some(Value::Identifier(src)) = map.get("entity") {
124
+ trigger_val = Value::Identifier(src.clone());
125
+ }
126
+ }
127
+ Value::Sample(sample_src) => {
128
+ trigger_val = Value::Sample(sample_src.clone());
129
+ }
130
+ _ => {}
131
+ }
132
+ }
55
133
 
56
- // Group case
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,
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);
134
+ let (src, sample_length) =
135
+ crate::core::audio::loader::trigger::load_trigger(&trigger_val, &Duration::Number(step_duration), &None, base_duration, final_variable_table.clone());
136
+
137
+ let play_length = step_duration.min(sample_length);
138
+
139
+ let trigger_src = match trigger_val.get("entity") {
140
+ Some(Value::String(s)) => s.clone(),
141
+ Some(Value::Identifier(id)) => id.clone(),
142
+ Some(Value::Statement(stmt)) => {
143
+ if let StatementKind::Trigger { entity, .. } = &stmt.kind {
144
+ entity.clone()
145
+ } else {
146
+ src.clone()
147
+ }
148
+ }
149
+ _ => src.clone(),
150
+ };
151
+
152
+ audio_engine.insert_sample(&trigger_src, event_time, play_length, None, &final_variable_table);
153
+
154
+ let end_time = event_time + play_length;
155
+ if end_time > updated_max {
156
+ updated_max = end_time;
73
157
  }
74
158
  }
75
- }
76
159
 
77
- eprintln!("❌ Function or group '{}' not found", name);
160
+ audio_engine.merge_with(local_engine);
161
+ return (updated_max.max(max_end_time), cursor_time);
162
+ }
78
163
  }
79
164
 
80
- _ => eprintln!(
81
- "❌ interprete_spawn_statement expected Spawn, got {:?}",
82
- stmt.kind
83
- ),
165
+ // Function or group not found
84
166
  }
85
167
 
86
168
  (max_end_time, cursor_time)
@@ -103,3 +185,53 @@ fn find_group<'a>(
103
185
  }
104
186
  None
105
187
  }
188
+
189
+ fn find_pattern(
190
+ name: &str,
191
+ variable_table: &VariableTable,
192
+ global_store: &GlobalStore,
193
+ ) -> Option<Statement> {
194
+ use crate::core::parser::statement::Statement;
195
+ use crate::core::parser::statement::StatementKind;
196
+
197
+ if let Some(Value::Statement(stmt_box)) = variable_table.get(name) {
198
+ if let StatementKind::Pattern { .. } = stmt_box.kind {
199
+ return Some(*stmt_box.clone());
200
+ }
201
+ }
202
+
203
+ if let Some(val) = global_store.variables.variables.get(name) {
204
+ match val {
205
+ Value::Statement(stmt_box) => {
206
+ if let StatementKind::Pattern { .. } = stmt_box.kind {
207
+ return Some(*stmt_box.clone());
208
+ }
209
+ }
210
+ Value::Map(map) => {
211
+ if let Some(Value::String(_pat)) = map.get("pattern") {
212
+ // Rebuild a Pattern statement from stored map if possible
213
+ let stmt = Statement {
214
+ kind: StatementKind::Pattern {
215
+ name: name.to_string(),
216
+ target: map.get("target").and_then(|v| match v {
217
+ Value::String(s) => Some(s.clone()),
218
+ _ => None,
219
+ }),
220
+ },
221
+ value: Value::String(map.get("pattern").and_then(|v| match v {
222
+ Value::String(s) => Some(s.clone()),
223
+ _ => None,
224
+ }).unwrap_or_default()),
225
+ indent: 0,
226
+ line: 0,
227
+ column: 0,
228
+ };
229
+ return Some(stmt);
230
+ }
231
+ }
232
+ _ => {}
233
+ }
234
+ }
235
+
236
+ None
237
+ }
@@ -1,17 +1,38 @@
1
- use crate::core::{
2
- parser::statement::{Statement, StatementKind},
3
- shared::value::Value,
4
- };
1
+ use crate::core::parser::statement::{Statement, StatementKind};
2
+ use devalang_types::Value;
3
+ use devalang_utils::logger::{LogLevel, Logger};
5
4
 
6
5
  pub fn interprete_tempo_statement(stmt: &Statement) -> Option<(f32, f32)> {
7
6
  if let StatementKind::Tempo = &stmt.kind {
8
- if let Value::Number(bpm) = &stmt.value {
9
- let bpm = *bpm as f32;
10
- let duration = 60.0 / bpm;
7
+ match &stmt.value {
8
+ Value::Number(bpm) => {
9
+ let bpm = { *bpm };
10
+ let duration = 60.0 / bpm;
11
11
 
12
- return Some((bpm, duration));
13
- } else {
14
- eprintln!("❌ Invalid tempo value: {:?}", stmt.value);
12
+ return Some((bpm, duration));
13
+ }
14
+
15
+ Value::String(bpm_str) => {
16
+ if let Ok(bpm) = bpm_str.parse::<f32>() {
17
+ let duration = 60.0 / bpm;
18
+ return Some((bpm, duration));
19
+ }
20
+ }
21
+
22
+ Value::Identifier(bpm_ident) => {
23
+ if let Ok(bpm) = bpm_ident.parse::<f32>() {
24
+ let duration = 60.0 / bpm;
25
+ return Some((bpm, duration));
26
+ }
27
+ }
28
+
29
+ _ => {
30
+ let logger = Logger::new();
31
+ logger.log_message(
32
+ LogLevel::Warning,
33
+ format!("Invalid tempo value: {:?}", stmt.value).as_str(),
34
+ );
35
+ }
15
36
  }
16
37
  }
17
38
 
@@ -3,10 +3,10 @@ use std::collections::HashMap;
3
3
  use crate::core::{
4
4
  audio::{engine::AudioEngine, loader::trigger::load_trigger},
5
5
  parser::statement::{Statement, StatementKind},
6
- shared::{duration::Duration, value::Value},
7
6
  store::variable::VariableTable,
8
7
  };
9
- use crate::utils::logger::Logger;
8
+ use devalang_types::{Duration, Value};
9
+ use devalang_utils::logger::Logger;
10
10
 
11
11
  pub fn interprete_trigger_statement(
12
12
  stmt: &Statement,
@@ -1,8 +1,5 @@
1
- use crate::core::{
2
- parser::statement::StatementKind,
3
- shared::{duration::Duration, value::Value},
4
- store::variable::VariableTable,
5
- };
1
+ use crate::core::{parser::statement::StatementKind, store::variable::VariableTable};
2
+ use devalang_types::{Duration, Value};
6
3
 
7
4
  pub fn load_trigger(
8
5
  trigger: &Value,
@@ -24,9 +21,9 @@ pub fn load_trigger(
24
21
 
25
22
  Value::Map(map) => {
26
23
  if let Some(Value::String(src)) = map.get("entity") {
27
- trigger_path = format!("devalang://bank/{}", src.to_string());
24
+ trigger_path = format!("devalang://bank/{}", src);
28
25
  } else if let Some(Value::Identifier(src)) = map.get("entity") {
29
- trigger_path = format!("devalang://bank/{}", src.to_string());
26
+ trigger_path = format!("devalang://bank/{}", src);
30
27
  } else {
31
28
  eprintln!(
32
29
  "❌ Trigger map must contain an 'entity' key with a string or identifier value."
@@ -8,6 +8,12 @@ pub struct AudioPlayer {
8
8
  last_path: Option<String>,
9
9
  }
10
10
 
11
+ impl Default for AudioPlayer {
12
+ fn default() -> Self {
13
+ Self::new()
14
+ }
15
+ }
16
+
11
17
  impl AudioPlayer {
12
18
  pub fn new() -> Self {
13
19
  let (stream, handle) = OutputStream::try_default().unwrap();
@@ -1,11 +1,9 @@
1
- use crate::{
2
- core::{
3
- audio::{engine::AudioEngine, interpreter::driver::run_audio_program},
4
- parser::statement::Statement,
5
- store::global::GlobalStore,
6
- },
7
- utils::logger::{LogLevel, Logger},
1
+ use crate::core::{
2
+ audio::{engine::AudioEngine, interpreter::driver::run_audio_program},
3
+ parser::statement::Statement,
4
+ store::global::GlobalStore,
8
5
  };
6
+ use devalang_utils::logger::{LogLevel, Logger};
9
7
  use std::collections::HashMap;
10
8
 
11
9
  pub fn render_audio_with_modules(
@@ -1,3 +1,5 @@
1
+ use devalang_types::Value;
2
+
1
3
  use crate::core::store::variable::VariableTable;
2
4
  use std::sync::OnceLock;
3
5
  use std::time::{SystemTime, UNIX_EPOCH};
@@ -36,7 +38,7 @@ pub fn resolve_atom_or_var(atom: &str, vars: &VariableTable, bpm: f32, beat: f32
36
38
  if let Ok(n) = atom.parse::<f32>() {
37
39
  return Some(n);
38
40
  }
39
- if let Some(crate::core::shared::value::Value::Number(n)) = vars.get(atom) {
41
+ if let Some(Value::Number(n)) = vars.get(atom) {
40
42
  return Some(*n);
41
43
  }
42
44
  None
@@ -1,4 +1,5 @@
1
1
  use crate::core::store::variable::VariableTable;
2
+ use devalang_utils::logger::{Logger, LogLevel};
2
3
 
3
4
  // Parse comma-separated arguments at top level (no nested parentheses split)
4
5
  fn parse_top_level_args(s: &str) -> Vec<&str> {
@@ -25,12 +26,12 @@ fn parse_top_level_args(s: &str) -> Vec<&str> {
25
26
 
26
27
  fn eval_math_func(func: &str, args: &[f32], fallback_seed: f32) -> Option<f32> {
27
28
  match func {
28
- "sin" => args.get(0).copied().map(f32::sin),
29
- "cos" => args.get(0).copied().map(f32::cos),
29
+ "sin" => args.first().copied().map(f32::sin),
30
+ "cos" => args.first().copied().map(f32::cos),
30
31
  "random" => {
31
32
  // deterministic pseudo-random based on provided seed or a fallback session seed
32
- let seed = args.get(0).copied().unwrap_or(fallback_seed);
33
- let x = (seed * 12.9898).sin() * 43758.5453;
33
+ let seed = args.first().copied().unwrap_or(fallback_seed);
34
+ let x = (seed * 12.9898).sin() * 43_758.547;
34
35
  Some((x.fract() * 2.0 - 1.0).clamp(-1.0, 1.0))
35
36
  }
36
37
  "lerp" => {
@@ -56,9 +57,21 @@ pub fn find_and_eval_first_math_call<EvalFn>(
56
57
  where
57
58
  EvalFn: Fn(&str, &VariableTable, f32, f32) -> Option<f32>,
58
59
  {
60
+ let logger = Logger::new();
61
+
59
62
  let start = s.find("$math.")?;
60
- let open_rel = s[start..].find('(')?;
63
+ let open_rel = match s[start..].find('(') {
64
+ Some(i) => i,
65
+ None => {
66
+ logger.log_message(LogLevel::Error, &format!("Malformed $math call: missing '(' in '{}'", s));
67
+ return None;
68
+ }
69
+ };
61
70
  let open = start + open_rel;
71
+ if open <= start + 6 {
72
+ logger.log_message(LogLevel::Error, &format!("Malformed $math call: missing function name in '{}'", s));
73
+ return None;
74
+ }
62
75
  let func = &s[start + 6..open];
63
76
 
64
77
  // Find matching close parenthesis, handling nesting
@@ -77,7 +90,13 @@ where
77
90
  _ => {}
78
91
  }
79
92
  }
80
- let close = close_abs?;
93
+ let close = match close_abs {
94
+ Some(c) => c,
95
+ None => {
96
+ logger.log_message(LogLevel::Error, &format!("Malformed $math call: missing closing ')' in '{}'", s));
97
+ return None;
98
+ }
99
+ };
81
100
 
82
101
  let inner = &s[open + 1..close];
83
102
  let raw_args = parse_top_level_args(inner);
@@ -86,6 +105,7 @@ where
86
105
  if let Some(v) = eval(a, vars, bpm, beat) {
87
106
  args.push(v);
88
107
  } else {
108
+ logger.log_message(LogLevel::Error, &format!("Failed to evaluate argument '{}' for $math.{}", a, func));
89
109
  return None;
90
110
  }
91
111
  }
@@ -44,11 +44,11 @@ fn adsr_envelope_value_t(attack: f32, decay: f32, sustain: f32, release: f32, t:
44
44
  fn eval_mod_func(func: &str, args: &[f32], beat: f32) -> Option<f32> {
45
45
  match func {
46
46
  "lfo.sine" => {
47
- let rate = args.get(0).copied().unwrap_or(1.0);
47
+ let rate = args.first().copied().unwrap_or(1.0);
48
48
  Some(lfo_sine(rate, beat))
49
49
  }
50
50
  "lfo.tri" | "lfo.triangle" => {
51
- let rate = args.get(0).copied().unwrap_or(1.0);
51
+ let rate = args.first().copied().unwrap_or(1.0);
52
52
  Some(lfo_triangle(rate, beat))
53
53
  }
54
54
  // ADSR envelope normalized over t in [0,1]
@@ -1,12 +1,18 @@
1
1
  use crate::core::audio::renderer::render_audio_with_modules;
2
2
  use crate::core::parser::statement::Statement;
3
3
  use crate::core::store::global::GlobalStore;
4
- use crate::utils::logger::Logger;
4
+ use devalang_utils::logger::Logger;
5
5
  use std::io::Write;
6
6
  use std::{collections::HashMap, fs::create_dir_all};
7
7
 
8
8
  pub struct Builder {}
9
9
 
10
+ impl Default for Builder {
11
+ fn default() -> Self {
12
+ Self::new()
13
+ }
14
+ }
15
+
10
16
  impl Builder {
11
17
  pub fn new() -> Self {
12
18
  Builder {}
@@ -46,7 +52,7 @@ impl Builder {
46
52
  let logger = Logger::new();
47
53
 
48
54
  let audio_engines =
49
- render_audio_with_modules(modules.clone(), &normalized_output_dir, global_store);
55
+ render_audio_with_modules(modules.clone(), normalized_output_dir, global_store);
50
56
 
51
57
  create_dir_all(format!("{}/audio", normalized_output_dir))
52
58
  .expect("Failed to create audio directory");
@@ -54,7 +60,7 @@ impl Builder {
54
60
  for (module_name, mut audio_engine) in audio_engines {
55
61
  let formatted_module_name = module_name
56
62
  .split('/')
57
- .last()
63
+ .next_back()
58
64
  .unwrap_or(&module_name)
59
65
  .replace(".deva", "");
60
66
 
@@ -20,7 +20,7 @@ pub fn write_lexer_log_file(
20
20
  content.push_str(&format!("{:?}\n", token));
21
21
  }
22
22
 
23
- content.push_str("\n");
23
+ content.push('\n');
24
24
  }
25
25
 
26
26
  debugger.write_log_file(&log_directory, file_name, &content);
@@ -7,6 +7,12 @@ use std::io::Write;
7
7
 
8
8
  pub struct Debugger {}
9
9
 
10
+ impl Default for Debugger {
11
+ fn default() -> Self {
12
+ Self::new()
13
+ }
14
+ }
15
+
10
16
  impl Debugger {
11
17
  pub fn new() -> Self {
12
18
  Debugger {}
@@ -13,7 +13,7 @@ pub fn write_module_variable_log_file(
13
13
  let mut content = String::new();
14
14
  let module_name = module_path
15
15
  .split('/')
16
- .last()
16
+ .next_back()
17
17
  .unwrap_or("index")
18
18
  .replace(".deva", "");
19
19
 
@@ -24,7 +24,7 @@ pub fn write_module_variable_log_file(
24
24
  content.push_str(&format!("{:?} = {:?}\n", var_name, var_data));
25
25
  }
26
26
 
27
- content.push_str("\n");
27
+ content.push('\n');
28
28
 
29
29
  debugger.write_log_file(&log_directory, "variables.log", &content);
30
30
  }
@@ -38,7 +38,7 @@ pub fn write_module_function_log_file(
38
38
  let mut content = String::new();
39
39
  let module_name = module_path
40
40
  .split('/')
41
- .last()
41
+ .next_back()
42
42
  .unwrap_or("index")
43
43
  .replace(".deva", "");
44
44
 
@@ -49,7 +49,7 @@ pub fn write_module_function_log_file(
49
49
  content.push_str(&format!("{:?} = {:?}\n", func_name, func_data));
50
50
  }
51
51
 
52
- content.push_str("\n");
52
+ content.push('\n');
53
53
 
54
54
  debugger.write_log_file(&log_directory, "functions.log", &content);
55
55
  }
@@ -20,7 +20,7 @@ pub fn write_preprocessor_log_file(
20
20
  content.push_str(&format!("{:?}\n", stmt));
21
21
  }
22
22
 
23
- content.push_str("\n");
23
+ content.push('\n');
24
24
  }
25
25
 
26
26
  debugger.write_log_file(&log_directory, file_name, &content);
@@ -15,7 +15,7 @@ pub fn write_variables_log_file(output_dir: &str, file_name: &str, variables: Va
15
15
  content.push_str(&format!("{:?} = {:?}\n", var_name, var_data));
16
16
  }
17
17
 
18
- content.push_str("\n");
18
+ content.push('\n');
19
19
 
20
20
  debugger.write_log_file(&log_directory, file_name, &content);
21
21
  }
@@ -34,7 +34,7 @@ pub fn write_function_log_file(output_dir: &str, file_name: &str, functions: Fun
34
34
  ));
35
35
  }
36
36
 
37
- content.push_str("\n");
37
+ content.push('\n');
38
38
 
39
39
  debugger.write_log_file(&log_directory, file_name, &content);
40
40
  }