@devaloop/devalang 0.0.1-beta.1 → 0.0.1-beta.3

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 (207) hide show
  1. package/.devalang +9 -10
  2. package/Cargo.toml +84 -80
  3. package/README.md +10 -7
  4. package/docs/CHANGELOG.md +83 -0
  5. package/docs/ROADMAP.md +6 -2
  6. package/docs/TODO.md +3 -14
  7. package/examples/bus.deva +10 -0
  8. package/examples/chain.deva +19 -0
  9. package/examples/effect.deva +2 -0
  10. package/examples/filter.deva +11 -0
  11. package/examples/lfo.deva +9 -0
  12. package/examples/plugin.deva +10 -10
  13. package/examples/routing.deva +23 -0
  14. package/examples/synth.deva +11 -1
  15. package/examples/synth_types.deva +17 -0
  16. package/out-tsc/bin/project-version.json +6 -0
  17. package/out-tsc/core/functions/index.d.ts +5 -0
  18. package/out-tsc/core/functions/index.js +11 -0
  19. package/out-tsc/pkg/devalang_core.d.ts +2 -0
  20. package/out-tsc/pkg/devalang_core.js +17 -2
  21. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +1 -0
  22. package/out-tsc/scripts/version/copy-to-binary.d.ts +1 -0
  23. package/out-tsc/scripts/version/copy-to-binary.js +79 -0
  24. package/package.json +23 -10
  25. package/project-version.json +3 -3
  26. package/rust/bindings/Cargo.toml +9 -0
  27. package/rust/bindings/src/lib.rs +86 -0
  28. package/rust/cli/addon/commands.rs +35 -0
  29. package/rust/cli/addon/download.rs +234 -0
  30. package/rust/cli/addon/install.rs +33 -0
  31. package/rust/cli/addon/list.rs +224 -0
  32. package/rust/cli/addon/metadata.rs +124 -0
  33. package/rust/cli/addon/mod.rs +8 -0
  34. package/rust/cli/addon/remove.rs +271 -0
  35. package/rust/cli/addon/update.rs +305 -0
  36. package/rust/cli/{install/addon.rs → addon/utils.rs} +34 -43
  37. package/rust/cli/build/commands.rs +153 -103
  38. package/rust/cli/build/mod.rs +2 -2
  39. package/rust/cli/build/process.rs +165 -146
  40. package/rust/cli/check/mod.rs +208 -208
  41. package/rust/cli/discover/commands.rs +53 -31
  42. package/rust/cli/discover/config.rs +2 -4
  43. package/rust/cli/discover/install.rs +139 -28
  44. package/rust/cli/discover/metadata.rs +3 -3
  45. package/rust/cli/login/commands.rs +124 -124
  46. package/rust/cli/me/commands.rs +52 -0
  47. package/rust/cli/me/mod.rs +1 -0
  48. package/rust/cli/mod.rs +2 -2
  49. package/rust/cli/parser.rs +76 -70
  50. package/rust/cli/play/commands.rs +375 -324
  51. package/rust/cli/play/mod.rs +5 -5
  52. package/rust/cli/play/process.rs +159 -150
  53. package/rust/cli/play/realtime.rs +91 -91
  54. package/rust/cli/telemetry/commands.rs +22 -22
  55. package/rust/cli/telemetry/event_creator.rs +80 -80
  56. package/rust/cli/telemetry/mod.rs +3 -3
  57. package/rust/cli/telemetry/send.rs +51 -51
  58. package/rust/cli/template/commands.rs +69 -69
  59. package/rust/config/driver.rs +112 -103
  60. package/rust/config/mod.rs +3 -3
  61. package/rust/config/ops.rs +26 -26
  62. package/rust/config/settings.rs +101 -101
  63. package/rust/core/audio/engine/driver.rs +237 -0
  64. package/rust/core/audio/engine/export.rs +169 -0
  65. package/rust/core/audio/engine/helpers.rs +178 -170
  66. package/rust/core/audio/engine/mod.rs +56 -7
  67. package/rust/core/audio/engine/notes/dsp.rs +88 -0
  68. package/rust/core/audio/engine/notes/mod.rs +53 -0
  69. package/rust/core/audio/engine/notes/params.rs +294 -0
  70. package/rust/core/audio/engine/sample/insert.rs +300 -0
  71. package/rust/core/audio/engine/sample/mod.rs +40 -0
  72. package/rust/core/audio/engine/sample/padding.rs +170 -0
  73. package/rust/core/audio/evaluator/condition.rs +61 -0
  74. package/rust/core/audio/evaluator/mod.rs +9 -0
  75. package/rust/core/audio/{evaluator.rs → evaluator/numeric.rs} +152 -310
  76. package/rust/core/audio/evaluator/rhs.rs +16 -0
  77. package/rust/core/audio/evaluator/string_expr.rs +94 -0
  78. package/rust/core/audio/interpreter/driver.rs +574 -542
  79. package/rust/core/audio/interpreter/mod.rs +2 -14
  80. package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +179 -0
  81. package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +398 -0
  82. package/rust/core/audio/interpreter/statements/arrow_call/methods/effects.rs +323 -0
  83. package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +3 -0
  84. package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +371 -0
  85. package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -0
  86. package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -0
  87. package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -0
  88. package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -0
  89. package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -0
  90. package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -0
  91. package/rust/core/audio/interpreter/{automate.rs → statements/automate.rs} +2 -4
  92. package/rust/core/audio/interpreter/{call.rs → statements/call.rs} +36 -5
  93. package/rust/core/audio/interpreter/{condition.rs → statements/condition.rs} +72 -71
  94. package/rust/core/audio/interpreter/{function.rs → statements/function.rs} +24 -26
  95. package/rust/core/audio/interpreter/{let_.rs → statements/let_.rs} +36 -38
  96. package/rust/core/audio/interpreter/{load.rs → statements/load.rs} +17 -19
  97. package/rust/core/audio/interpreter/{loop_.rs → statements/loop_.rs} +115 -114
  98. package/rust/core/audio/interpreter/statements/mod.rs +12 -0
  99. package/rust/core/audio/interpreter/{sleep.rs → statements/sleep.rs} +28 -28
  100. package/rust/core/audio/interpreter/{spawn.rs → statements/spawn.rs} +54 -4
  101. package/rust/core/audio/interpreter/{tempo.rs → statements/tempo.rs} +40 -40
  102. package/rust/core/audio/interpreter/{trigger.rs → statements/trigger.rs} +242 -239
  103. package/rust/core/audio/loader/trigger.rs +98 -97
  104. package/rust/core/audio/mod.rs +6 -7
  105. package/rust/core/audio/special/easing.rs +189 -189
  106. package/rust/core/audio/special/env.rs +45 -45
  107. package/rust/core/audio/special/math.rs +134 -134
  108. package/rust/core/audio/special/modulator.rs +143 -143
  109. package/rust/core/builder/mod.rs +129 -86
  110. package/rust/core/debugger/{module.rs → logs.rs} +52 -55
  111. package/rust/core/debugger/mod.rs +30 -30
  112. package/rust/core/debugger/store.rs +38 -40
  113. package/rust/core/error/mod.rs +269 -269
  114. package/rust/core/lexer/driver.rs +2 -4
  115. package/rust/core/mod.rs +9 -10
  116. package/rust/core/parser/driver/block.rs +111 -0
  117. package/rust/core/parser/driver/cursor.rs +82 -0
  118. package/rust/core/parser/driver/driver_impl.rs +159 -0
  119. package/rust/core/parser/driver/mod.rs +6 -0
  120. package/rust/core/parser/driver/parse_array.rs +120 -0
  121. package/rust/core/parser/driver/parse_map.rs +247 -0
  122. package/rust/core/parser/driver/parser.rs +160 -0
  123. package/rust/core/parser/handler/arrow_call.rs +90 -15
  124. package/rust/core/parser/handler/at.rs +279 -279
  125. package/rust/core/parser/handler/bank.rs +104 -104
  126. package/rust/core/parser/handler/condition.rs +83 -83
  127. package/rust/core/parser/handler/dot.rs +148 -148
  128. package/rust/core/parser/handler/identifier/automate.rs +254 -254
  129. package/rust/core/parser/handler/identifier/call.rs +91 -91
  130. package/rust/core/parser/handler/identifier/emit.rs +70 -70
  131. package/rust/core/parser/handler/identifier/function.rs +113 -113
  132. package/rust/core/parser/handler/identifier/group.rs +89 -89
  133. package/rust/core/parser/handler/identifier/let_.rs +173 -173
  134. package/rust/core/parser/handler/identifier/mod.rs +55 -55
  135. package/rust/core/parser/handler/identifier/on.rs +107 -107
  136. package/rust/core/parser/handler/identifier/print.rs +49 -49
  137. package/rust/core/parser/handler/identifier/sleep.rs +96 -43
  138. package/rust/core/parser/handler/identifier/spawn.rs +91 -91
  139. package/rust/core/parser/handler/identifier/synth.rs +39 -3
  140. package/rust/core/parser/handler/loop_.rs +194 -194
  141. package/rust/core/parser/handler/pattern.rs +25 -2
  142. package/rust/core/parser/handler/tempo.rs +105 -57
  143. package/rust/core/parser/statement.rs +10 -11
  144. package/rust/core/plugin/loader.rs +137 -137
  145. package/rust/core/plugin/runner/mod.rs +11 -0
  146. package/rust/core/plugin/{runner.rs → runner/non_wasm.rs} +206 -72
  147. package/rust/core/plugin/runner/wasm32.rs +44 -0
  148. package/rust/core/preprocessor/loader/inject.rs +313 -0
  149. package/rust/core/preprocessor/loader/loader_helpers.rs +110 -0
  150. package/rust/core/preprocessor/loader/mod.rs +235 -0
  151. package/rust/core/preprocessor/module.rs +55 -60
  152. package/rust/core/preprocessor/{processor.rs → processor/handlers.rs} +107 -114
  153. package/rust/core/preprocessor/processor/mod.rs +1 -0
  154. package/rust/core/preprocessor/resolver/function.rs +69 -69
  155. package/rust/core/preprocessor/resolver/group.rs +122 -94
  156. package/rust/core/preprocessor/resolver/pattern.rs +14 -2
  157. package/rust/core/store/global.rs +57 -61
  158. package/rust/core/store/mod.rs +1 -5
  159. package/rust/lib.rs +323 -308
  160. package/rust/macros/Cargo.toml +14 -0
  161. package/rust/macros/src/lib.rs +52 -0
  162. package/rust/main.rs +336 -143
  163. package/rust/types/Cargo.toml +1 -1
  164. package/rust/types/src/addons.rs +57 -55
  165. package/rust/types/src/config.rs +82 -74
  166. package/rust/types/src/lib.rs +15 -12
  167. package/rust/types/src/plugin.rs +20 -0
  168. package/rust/types/src/store.rs +139 -0
  169. package/rust/types/src/telemetry.rs +85 -85
  170. package/rust/utils/Cargo.toml +5 -2
  171. package/rust/utils/src/file.rs +477 -94
  172. package/rust/utils/src/first_usage.rs +97 -97
  173. package/rust/utils/src/lib.rs +9 -9
  174. package/rust/utils/src/logger.rs +200 -200
  175. package/rust/utils/src/path.rs +158 -88
  176. package/rust/utils/src/signature.rs +41 -41
  177. package/rust/utils/src/spinner.rs +20 -20
  178. package/rust/utils/src/version.rs +58 -27
  179. package/rust/utils/src/watcher.rs +46 -46
  180. package/rust/web/api.rs +5 -5
  181. package/rust/web/auth.rs +5 -0
  182. package/rust/web/cdn.rs +34 -34
  183. package/rust/web/forge.rs +5 -0
  184. package/rust/web/mod.rs +2 -0
  185. package/tests/integration.rs +21 -21
  186. package/typescript/core/functions/index.ts +11 -0
  187. package/typescript/pkg/devalang_core.ts +20 -4
  188. package/typescript/scripts/version/copy-to-binary.ts +82 -0
  189. package/rust/cli/bank/api.rs +0 -122
  190. package/rust/cli/bank/commands.rs +0 -275
  191. package/rust/cli/bank/mod.rs +0 -29
  192. package/rust/cli/install/bank.rs +0 -53
  193. package/rust/cli/install/commands.rs +0 -35
  194. package/rust/cli/install/mod.rs +0 -4
  195. package/rust/cli/install/plugin.rs +0 -61
  196. package/rust/core/audio/engine/sample.rs +0 -366
  197. package/rust/core/audio/engine/synth.rs +0 -325
  198. package/rust/core/audio/interpreter/arrow_call.rs +0 -311
  199. package/rust/core/audio/renderer.rs +0 -54
  200. package/rust/core/parser/driver.rs +0 -584
  201. package/rust/core/preprocessor/loader.rs +0 -637
  202. package/rust/core/store/export.rs +0 -28
  203. package/rust/core/store/function.rs +0 -40
  204. package/rust/core/store/import.rs +0 -28
  205. package/rust/core/store/variable.rs +0 -51
  206. package/rust/core/utils/mod.rs +0 -1
  207. package/rust/core/utils/path.rs +0 -37
@@ -0,0 +1,44 @@
1
+ use std::collections::HashMap;
2
+
3
+ pub struct WasmPluginRunner;
4
+
5
+ impl WasmPluginRunner {
6
+ pub fn new() -> Self {
7
+ WasmPluginRunner
8
+ }
9
+
10
+ pub fn process_in_place(&self, _wasm_bytes: &[u8], _buffer: &mut [f32]) -> Result<(), String> {
11
+ Err("Wasm plugin execution is not available in wasm builds".to_string())
12
+ }
13
+
14
+ pub fn render_note_in_place(
15
+ &self,
16
+ _wasm_bytes: &[u8],
17
+ _buffer: &mut [f32],
18
+ _synth_name: Option<&str>,
19
+ _freq: f32,
20
+ _amp: f32,
21
+ _duration_ms: i32,
22
+ _sample_rate: i32,
23
+ _channels: i32,
24
+ ) -> Result<(), String> {
25
+ Err("Wasm plugin rendering is not available in wasm builds".to_string())
26
+ }
27
+
28
+ pub fn render_note_with_params_in_place(
29
+ &self,
30
+ _wasm_bytes: &[u8],
31
+ _buffer: &mut [f32],
32
+ _synth_name: Option<&str>,
33
+ _freq: f32,
34
+ _amp: f32,
35
+ _duration_ms: i32,
36
+ _sample_rate: i32,
37
+ _channels: i32,
38
+ _params_num: &HashMap<String, f32>,
39
+ _params_str: Option<&HashMap<String, String>>,
40
+ _exported_names: Option<&[String]>,
41
+ ) -> Result<(), String> {
42
+ Err("Wasm plugin rendering is not available in wasm builds".to_string())
43
+ }
44
+ }
@@ -0,0 +1,313 @@
1
+ use crate::core::parser::statement::Statement;
2
+ use crate::core::plugin::loader::load_plugin;
3
+ use crate::core::preprocessor::module::Module;
4
+ use crate::core::store::global::GlobalStore;
5
+ use devalang_types::Value;
6
+ use std::{collections::HashMap, path::Path};
7
+
8
+ pub fn inject_bank_triggers(
9
+ module: &mut Module,
10
+ bank_name: &str,
11
+ alias_override: Option<String>,
12
+ ) -> Result<(), String> {
13
+ let default_alias = bank_name
14
+ .split('.')
15
+ .next_back()
16
+ .unwrap_or(bank_name)
17
+ .to_string();
18
+ let alias_ref = alias_override.as_deref().unwrap_or(&default_alias);
19
+
20
+ let root = match devalang_utils::path::get_deva_dir() {
21
+ Ok(dir) => dir,
22
+ Err(_) => Path::new("./.deva").to_path_buf(),
23
+ };
24
+
25
+ // Try both plural and singular folder names and both layouts (flat and nested)
26
+ let mut parsed_bankfile_opt: Option<devalang_types::BankFile> = None;
27
+ let sds = ["banks", "bank"];
28
+ for sd in &sds {
29
+ // candidate: .deva/<sd>/<bank_name>/bank.toml (flat dir name)
30
+ let candidate1 = root.join(sd).join(bank_name).join("bank.toml");
31
+ if candidate1.exists() {
32
+ let content = std::fs::read_to_string(&candidate1)
33
+ .map_err(|e| format!("Failed to read '{}': {}", candidate1.display(), e))?;
34
+ if let Ok(parsed) = toml::from_str::<devalang_types::BankFile>(&content) {
35
+ parsed_bankfile_opt = Some(parsed);
36
+ break;
37
+ }
38
+ }
39
+
40
+ // If bank_name uses dot notation, also try nested layout: .deva/<sd>/<publisher>/<name>/bank.toml
41
+ if bank_name.contains('.') {
42
+ let mut it = bank_name.splitn(2, '.');
43
+ let pubr = it.next().unwrap_or("");
44
+ let nm = it.next().unwrap_or("");
45
+ let candidate2 = root.join(sd).join(pubr).join(nm).join("bank.toml");
46
+ if candidate2.exists() {
47
+ let content = std::fs::read_to_string(&candidate2)
48
+ .map_err(|e| format!("Failed to read '{}': {}", candidate2.display(), e))?;
49
+ if let Ok(parsed) = toml::from_str::<devalang_types::BankFile>(&content) {
50
+ parsed_bankfile_opt = Some(parsed);
51
+ break;
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ let parsed_bankfile = match parsed_bankfile_opt {
58
+ Some(p) => p,
59
+ None => return Ok(()),
60
+ };
61
+
62
+ let mut bank_map = HashMap::new();
63
+
64
+ for bank_trigger in parsed_bankfile.triggers.unwrap_or_default() {
65
+ let entity_ref = bank_trigger
66
+ .path
67
+ .clone()
68
+ .replace("\\", "/")
69
+ .replace("./", "");
70
+ let bank_trigger_path = format!("devalang://bank/{}/{}", bank_name, entity_ref);
71
+
72
+ bank_map.insert(
73
+ bank_trigger.name.clone(),
74
+ Value::String(bank_trigger_path.clone()),
75
+ );
76
+
77
+ if module.variable_table.variables.contains_key(alias_ref) {
78
+ eprintln!(
79
+ "⚠️ Trigger '{}' already defined in module '{}', skipping injection.",
80
+ alias_ref, module.path
81
+ );
82
+ continue;
83
+ }
84
+
85
+ module.variable_table.set(
86
+ format!("{}.{}", alias_ref, bank_trigger.name),
87
+ Value::String(bank_trigger_path.clone()),
88
+ );
89
+ }
90
+
91
+ module
92
+ .variable_table
93
+ .set(alias_ref.to_string(), Value::Map(bank_map));
94
+
95
+ Ok(())
96
+ }
97
+
98
+ pub fn extract_bank_decls(statements: &[Statement]) -> Vec<(String, Option<String>)> {
99
+ let mut banks = Vec::new();
100
+
101
+ for stmt in statements {
102
+ if let crate::core::parser::statement::StatementKind::Bank { alias } = &stmt.kind {
103
+ let name_opt = match &stmt.value {
104
+ Value::String(s) => Some(s.clone()),
105
+ Value::Identifier(s) => Some(s.clone()),
106
+ Value::Number(n) => Some(n.to_string()),
107
+ _ => None,
108
+ };
109
+ if let Some(name) = name_opt {
110
+ banks.push((name, alias.clone()));
111
+ }
112
+ }
113
+ }
114
+
115
+ banks
116
+ }
117
+
118
+ pub fn extract_plugin_uses(statements: &[Statement]) -> Vec<(String, String)> {
119
+ let mut plugins = Vec::new();
120
+
121
+ for stmt in statements {
122
+ if let crate::core::parser::statement::StatementKind::Use { name, alias } = &stmt.kind {
123
+ let alias_name = alias
124
+ .clone()
125
+ .unwrap_or_else(|| name.split('.').next_back().unwrap_or(name).to_string());
126
+ plugins.push((name.clone(), alias_name));
127
+ }
128
+ }
129
+
130
+ plugins
131
+ }
132
+
133
+ pub fn load_plugin_and_register(
134
+ module: &mut Module,
135
+ plugin_name: &str,
136
+ alias: &str,
137
+ global_store: &mut GlobalStore,
138
+ ) {
139
+ let mut parts = plugin_name.split('.');
140
+ let author = match parts.next() {
141
+ Some(a) if !a.is_empty() => a,
142
+ _ => {
143
+ eprintln!("Invalid plugin name '{}': missing author", plugin_name);
144
+ return;
145
+ }
146
+ };
147
+ let name = match parts.next() {
148
+ Some(n) if !n.is_empty() => n,
149
+ _ => {
150
+ eprintln!("Invalid plugin name '{}': missing name", plugin_name);
151
+ return;
152
+ }
153
+ };
154
+ if parts.next().is_some() {
155
+ eprintln!(
156
+ "Invalid plugin name '{}': expected <author>.<name>",
157
+ plugin_name
158
+ );
159
+ return;
160
+ }
161
+
162
+ let expected_uri = format!("devalang://plugin/{}/{}", author, name);
163
+
164
+ let root = match devalang_utils::path::get_deva_dir() {
165
+ Ok(dir) => dir,
166
+ Err(_) => Path::new("./.deva").to_path_buf(),
167
+ };
168
+ // Test both 'plugins' and 'plugin' folders
169
+ let mut exists_locally = false;
170
+ for sd in &["plugins", "plugin"] {
171
+ let plugin_dir_preferred = root.join(sd).join(format!("{}/{}", author, name));
172
+ let toml_path_preferred = plugin_dir_preferred.join("plugin.toml");
173
+ let plugin_dir_fallback = root.join(sd).join(author).join(name);
174
+ let toml_path_fallback = plugin_dir_fallback.join("plugin.toml");
175
+ if toml_path_preferred.exists() || toml_path_fallback.exists() {
176
+ exists_locally = true;
177
+ break;
178
+ }
179
+ }
180
+
181
+ if exists_locally {
182
+ let cfg_opt = crate::config::ops::load_config(None);
183
+ let mut declared = false;
184
+ if let Some(cfg) = cfg_opt {
185
+ if let Some(list) = cfg.plugins {
186
+ declared = list.iter().any(|p| p.path == expected_uri);
187
+ }
188
+ }
189
+ if !declared {
190
+ module
191
+ .statements
192
+ .push(crate::core::parser::statement::Statement {
193
+ kind: crate::core::parser::statement::StatementKind::Error {
194
+ message: "plugin present in local files but missing in .devalang config"
195
+ .to_string(),
196
+ },
197
+ value: Value::Null,
198
+ indent: 0,
199
+ line: 0,
200
+ column: 0,
201
+ });
202
+ return;
203
+ }
204
+ }
205
+
206
+ match load_plugin(author, name) {
207
+ Ok((info, wasm)) => {
208
+ // keep dotted form for config/expected URIs, but inject a slash form into variables
209
+ let _uri_dot = format!("devalang://plugin/{}.{}", author, name);
210
+ let uri_inject = format!("devalang://plugin/{}/{}", author, name);
211
+ global_store
212
+ .plugins
213
+ .insert(format!("{}:{}", author, name), (info, wasm));
214
+ module
215
+ .variable_table
216
+ .set(alias.to_string(), Value::String(uri_inject.clone()));
217
+ global_store
218
+ .variables
219
+ .set(alias.to_string(), Value::String(uri_inject.clone()));
220
+
221
+ if let Some((plugin_info, _)) =
222
+ global_store.plugins.get(&format!("{}:{}", author, name))
223
+ {
224
+ for exp in &plugin_info.exports {
225
+ match exp.kind.as_str() {
226
+ "number" => {
227
+ if let Some(toml::Value::String(s)) = &exp.default {
228
+ if let Ok(n) = s.parse::<f32>() {
229
+ module
230
+ .variable_table
231
+ .set(format!("{}.{}", alias, exp.name), Value::Number(n));
232
+ }
233
+ } else if let Some(toml::Value::Integer(i)) = &exp.default {
234
+ module.variable_table.set(
235
+ format!("{}.{}", alias, exp.name),
236
+ Value::Number(*i as f32),
237
+ );
238
+ } else if let Some(toml::Value::Float(f)) = &exp.default {
239
+ module.variable_table.set(
240
+ format!("{}.{}", alias, exp.name),
241
+ Value::Number(*f as f32),
242
+ );
243
+ }
244
+ }
245
+ "string" => {
246
+ if let Some(toml::Value::String(s)) = &exp.default {
247
+ module.variable_table.set(
248
+ format!("{}.{}", alias, exp.name),
249
+ Value::String(s.clone()),
250
+ );
251
+ }
252
+ }
253
+ "bool" => {
254
+ if let Some(toml::Value::Boolean(b)) = &exp.default {
255
+ module
256
+ .variable_table
257
+ .set(format!("{}.{}", alias, exp.name), Value::Boolean(*b));
258
+ }
259
+ }
260
+ "synth" => {
261
+ module.variable_table.set(
262
+ format!("{}.{}", alias, exp.name),
263
+ Value::String(format!("{}.{}", alias, exp.name)),
264
+ );
265
+ }
266
+ _ => {
267
+ if let Some(def) = &exp.default {
268
+ let val = match def {
269
+ toml::Value::String(s) => Value::String(s.clone()),
270
+ toml::Value::Integer(i) => Value::Number(*i as f32),
271
+ toml::Value::Float(f) => Value::Number(*f as f32),
272
+ toml::Value::Boolean(b) => Value::Boolean(*b),
273
+ toml::Value::Array(arr) => Value::Array(
274
+ arr.iter()
275
+ .map(|v| match v {
276
+ toml::Value::String(s) => Value::String(s.clone()),
277
+ toml::Value::Integer(i) => Value::Number(*i as f32),
278
+ toml::Value::Float(f) => Value::Number(*f as f32),
279
+ toml::Value::Boolean(b) => Value::Boolean(*b),
280
+ _ => Value::Null,
281
+ })
282
+ .collect(),
283
+ ),
284
+ toml::Value::Table(t) => {
285
+ let mut m = std::collections::HashMap::new();
286
+ for (k, v) in t.iter() {
287
+ let vv = match v {
288
+ toml::Value::String(s) => Value::String(s.clone()),
289
+ toml::Value::Integer(i) => Value::Number(*i as f32),
290
+ toml::Value::Float(f) => Value::Number(*f as f32),
291
+ toml::Value::Boolean(b) => Value::Boolean(*b),
292
+ _ => Value::Null,
293
+ };
294
+ m.insert(k.clone(), vv);
295
+ }
296
+ Value::Map(m)
297
+ }
298
+ _ => Value::Null,
299
+ };
300
+ if val != Value::Null {
301
+ module
302
+ .variable_table
303
+ .set(format!("{}.{}", alias, exp.name), val);
304
+ }
305
+ }
306
+ }
307
+ }
308
+ }
309
+ }
310
+ }
311
+ Err(e) => eprintln!("Failed to load plugin {}: {}", plugin_name, e),
312
+ }
313
+ }
@@ -0,0 +1,110 @@
1
+ use crate::core::preprocessor::loader::inject;
2
+ use crate::core::{
3
+ lexer::{driver::Lexer, token::Token},
4
+ parser::driver::parser::Parser,
5
+ preprocessor::module::Module,
6
+ store::global::GlobalStore,
7
+ };
8
+ use devalang_utils::logger::{LogLevel, Logger};
9
+ use devalang_utils::path::{normalize_path, resolve_relative_path};
10
+ use std::collections::HashMap;
11
+
12
+ pub fn load_module_recursively(
13
+ raw_path: &str,
14
+ global_store: &mut GlobalStore,
15
+ ) -> HashMap<String, Vec<Token>> {
16
+ let path = normalize_path(raw_path);
17
+
18
+ // Check if already loaded
19
+ if global_store.modules.contains_key(&path) {
20
+ return HashMap::new();
21
+ }
22
+
23
+ let lexer = Lexer::new();
24
+ let tokens = match lexer.lex_tokens(&path) {
25
+ Ok(t) => t,
26
+ Err(e) => {
27
+ let logger = Logger::new();
28
+ logger.log_message(LogLevel::Error, &format!("Failed to lex '{}': {}", path, e));
29
+ return HashMap::new();
30
+ }
31
+ };
32
+
33
+ let mut parser = Parser::new();
34
+ parser.set_current_module(path.clone());
35
+
36
+ let statements = parser.parse_tokens(tokens.clone(), global_store);
37
+
38
+ // Insert module into store
39
+ let mut module = Module::new(&path);
40
+ module.tokens = tokens.clone();
41
+ module.statements = statements.clone();
42
+
43
+ // Inject triggers for each bank used in module, respecting aliases
44
+ for (bank_name, alias_opt) in inject::extract_bank_decls(&statements) {
45
+ if let Err(e) = inject::inject_bank_triggers(&mut module, &bank_name, alias_opt) {
46
+ eprintln!("Failed to inject bank triggers for '{}': {}", bank_name, e);
47
+ }
48
+ }
49
+
50
+ for (plugin_name, alias) in inject::extract_plugin_uses(&statements) {
51
+ inject::load_plugin_and_register(&mut module, &plugin_name, &alias, global_store);
52
+ }
53
+
54
+ // Inject module variables and functions into global store
55
+ global_store
56
+ .variables
57
+ .variables
58
+ .extend(module.variable_table.variables.clone());
59
+ global_store
60
+ .functions
61
+ .functions
62
+ .extend(module.function_table.functions.clone());
63
+
64
+ // Inject the module into the global store
65
+ global_store.insert_module(path.clone(), module);
66
+
67
+ // Load dependencies
68
+ load_module_imports(&path, global_store);
69
+
70
+ // Return tokens per module
71
+ global_store
72
+ .modules
73
+ .iter()
74
+ .map(|(p, m)| (p.clone(), m.tokens.clone()))
75
+ .collect()
76
+ }
77
+
78
+ pub fn load_module_imports(path: &String, global_store: &mut GlobalStore) {
79
+ let import_paths: Vec<String> = {
80
+ let current_module = match global_store.modules.get(path) {
81
+ Some(module) => module,
82
+ None => {
83
+ eprintln!(
84
+ "[warn] Cannot resolve imports: module '{}' not found in store",
85
+ path
86
+ );
87
+ return;
88
+ }
89
+ };
90
+
91
+ current_module
92
+ .statements
93
+ .iter()
94
+ .filter_map(|stmt| {
95
+ if let crate::core::parser::statement::StatementKind::Import { source, .. } =
96
+ &stmt.kind
97
+ {
98
+ Some(source.clone())
99
+ } else {
100
+ None
101
+ }
102
+ })
103
+ .collect()
104
+ };
105
+
106
+ for import_path in import_paths {
107
+ let resolved = resolve_relative_path(path, &import_path);
108
+ load_module_recursively(&resolved, global_store);
109
+ }
110
+ }