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

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 (220) hide show
  1. package/.devalang +9 -10
  2. package/Cargo.toml +5 -4
  3. package/README.md +7 -5
  4. package/docs/CHANGELOG.md +42 -0
  5. package/docs/ROADMAP.md +5 -1
  6. package/docs/TODO.md +3 -14
  7. package/examples/bus.deva +10 -0
  8. package/examples/effect.deva +2 -0
  9. package/examples/filter.deva +11 -0
  10. package/examples/lfo.deva +9 -0
  11. package/examples/synth.deva +11 -1
  12. package/examples/synth_types.deva +17 -0
  13. package/out-tsc/core/functions/index.d.ts +5 -0
  14. package/out-tsc/core/functions/index.js +11 -0
  15. package/out-tsc/pkg/devalang_core.d.ts +2 -0
  16. package/out-tsc/pkg/devalang_core.js +17 -2
  17. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +8 -7
  18. package/package.json +1 -1
  19. package/project-version.json +3 -3
  20. package/rust/cli/bank/api.rs +122 -122
  21. package/rust/cli/bank/commands.rs +33 -2
  22. package/rust/cli/bank/mod.rs +29 -29
  23. package/rust/cli/build/commands.rs +53 -3
  24. package/rust/cli/build/mod.rs +2 -2
  25. package/rust/cli/build/process.rs +26 -7
  26. package/rust/cli/check/mod.rs +2 -2
  27. package/rust/cli/discover/commands.rs +253 -253
  28. package/rust/cli/discover/config.rs +111 -111
  29. package/rust/cli/discover/fs.rs +19 -19
  30. package/rust/cli/discover/install.rs +103 -103
  31. package/rust/cli/discover/metadata.rs +48 -48
  32. package/rust/cli/discover/mod.rs +5 -5
  33. package/rust/cli/install/addon.rs +118 -118
  34. package/rust/cli/install/bank.rs +22 -3
  35. package/rust/cli/install/commands.rs +35 -35
  36. package/rust/cli/install/mod.rs +4 -4
  37. package/rust/cli/install/plugin.rs +80 -61
  38. package/rust/cli/login/commands.rs +124 -124
  39. package/rust/cli/mod.rs +12 -12
  40. package/rust/cli/parser.rs +46 -1
  41. package/rust/cli/play/commands.rs +71 -20
  42. package/rust/cli/play/mod.rs +5 -5
  43. package/rust/cli/play/process.rs +14 -5
  44. package/rust/cli/play/realtime.rs +91 -91
  45. package/rust/cli/telemetry/commands.rs +22 -22
  46. package/rust/cli/telemetry/event_creator.rs +80 -80
  47. package/rust/cli/telemetry/mod.rs +3 -3
  48. package/rust/cli/telemetry/send.rs +51 -51
  49. package/rust/cli/template/commands.rs +69 -69
  50. package/rust/config/driver.rs +112 -103
  51. package/rust/config/mod.rs +3 -3
  52. package/rust/config/ops.rs +26 -26
  53. package/rust/config/settings.rs +101 -101
  54. package/rust/core/audio/engine/driver.rs +220 -0
  55. package/rust/core/audio/engine/export.rs +169 -0
  56. package/rust/core/audio/engine/helpers.rs +178 -170
  57. package/rust/core/audio/engine/mod.rs +51 -2
  58. package/rust/core/audio/engine/notes/dsp.rs +85 -0
  59. package/rust/core/audio/engine/notes/mod.rs +44 -0
  60. package/rust/core/audio/engine/notes/params.rs +294 -0
  61. package/rust/core/audio/engine/sample/insert.rs +199 -0
  62. package/rust/core/audio/engine/sample/mod.rs +40 -0
  63. package/rust/core/audio/engine/sample/padding.rs +170 -0
  64. package/rust/core/audio/evaluator/condition.rs +61 -0
  65. package/rust/core/audio/evaluator/mod.rs +9 -0
  66. package/rust/core/audio/{evaluator.rs → evaluator/numeric.rs} +1 -159
  67. package/rust/core/audio/evaluator/rhs.rs +16 -0
  68. package/rust/core/audio/evaluator/string_expr.rs +94 -0
  69. package/rust/core/audio/interpreter/driver.rs +55 -23
  70. package/rust/core/audio/interpreter/mod.rs +1 -13
  71. package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +175 -0
  72. package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +384 -0
  73. package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +2 -0
  74. package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +316 -0
  75. package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -0
  76. package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -0
  77. package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -0
  78. package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -0
  79. package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -0
  80. package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -0
  81. package/rust/core/audio/interpreter/{automate.rs → statements/automate.rs} +16 -18
  82. package/rust/core/audio/interpreter/{call.rs → statements/call.rs} +5 -4
  83. package/rust/core/audio/interpreter/{condition.rs → statements/condition.rs} +2 -1
  84. package/rust/core/audio/interpreter/{function.rs → statements/function.rs} +2 -4
  85. package/rust/core/audio/interpreter/{let_.rs → statements/let_.rs} +2 -4
  86. package/rust/core/audio/interpreter/{load.rs → statements/load.rs} +2 -4
  87. package/rust/core/audio/interpreter/{loop_.rs → statements/loop_.rs} +2 -1
  88. package/rust/core/audio/interpreter/statements/mod.rs +12 -0
  89. package/rust/core/audio/interpreter/{sleep.rs → statements/sleep.rs} +28 -28
  90. package/rust/core/audio/interpreter/{spawn.rs → statements/spawn.rs} +3 -2
  91. package/rust/core/audio/interpreter/{tempo.rs → statements/tempo.rs} +40 -40
  92. package/rust/core/audio/interpreter/{trigger.rs → statements/trigger.rs} +1 -1
  93. package/rust/core/audio/loader/trigger.rs +2 -1
  94. package/rust/core/audio/mod.rs +6 -7
  95. package/rust/core/audio/player.rs +70 -70
  96. package/rust/core/audio/special/easing.rs +189 -189
  97. package/rust/core/audio/special/env.rs +45 -45
  98. package/rust/core/audio/special/math.rs +134 -134
  99. package/rust/core/audio/special/mod.rs +9 -9
  100. package/rust/core/audio/special/modulator.rs +143 -143
  101. package/rust/core/builder/mod.rs +45 -2
  102. package/rust/core/debugger/lexer.rs +27 -27
  103. package/rust/core/debugger/{module.rs → logs.rs} +3 -6
  104. package/rust/core/debugger/mod.rs +30 -30
  105. package/rust/core/debugger/preprocessor.rs +27 -27
  106. package/rust/core/debugger/store.rs +2 -4
  107. package/rust/core/error/mod.rs +269 -269
  108. package/rust/core/lexer/driver.rs +59 -61
  109. package/rust/core/lexer/handler/arrow.rs +82 -82
  110. package/rust/core/lexer/handler/at.rs +21 -21
  111. package/rust/core/lexer/handler/brace.rs +41 -41
  112. package/rust/core/lexer/handler/colon.rs +21 -21
  113. package/rust/core/lexer/handler/comment.rs +30 -30
  114. package/rust/core/lexer/handler/dot.rs +21 -21
  115. package/rust/core/lexer/handler/driver.rs +337 -337
  116. package/rust/core/lexer/handler/identifier.rs +47 -47
  117. package/rust/core/lexer/handler/indent.rs +66 -66
  118. package/rust/core/lexer/handler/mod.rs +15 -15
  119. package/rust/core/lexer/handler/newline.rs +23 -23
  120. package/rust/core/lexer/handler/number.rs +31 -31
  121. package/rust/core/lexer/handler/operator.rs +46 -46
  122. package/rust/core/lexer/handler/parenthesis.rs +41 -41
  123. package/rust/core/lexer/handler/slash.rs +21 -21
  124. package/rust/core/lexer/handler/string.rs +63 -63
  125. package/rust/core/lexer/mod.rs +3 -3
  126. package/rust/core/mod.rs +0 -1
  127. package/rust/core/parser/driver/block.rs +111 -0
  128. package/rust/core/parser/driver/cursor.rs +82 -0
  129. package/rust/core/parser/driver/driver_impl.rs +139 -0
  130. package/rust/core/parser/driver/mod.rs +6 -0
  131. package/rust/core/parser/driver/parse_array.rs +120 -0
  132. package/rust/core/parser/driver/parse_map.rs +223 -0
  133. package/rust/core/parser/driver/parser.rs +160 -0
  134. package/rust/core/parser/handler/arrow_call.rs +28 -4
  135. package/rust/core/parser/handler/at.rs +279 -279
  136. package/rust/core/parser/handler/bank.rs +104 -104
  137. package/rust/core/parser/handler/condition.rs +83 -83
  138. package/rust/core/parser/handler/dot.rs +148 -148
  139. package/rust/core/parser/handler/identifier/automate.rs +254 -254
  140. package/rust/core/parser/handler/identifier/call.rs +91 -91
  141. package/rust/core/parser/handler/identifier/emit.rs +70 -70
  142. package/rust/core/parser/handler/identifier/function.rs +113 -113
  143. package/rust/core/parser/handler/identifier/group.rs +89 -89
  144. package/rust/core/parser/handler/identifier/let_.rs +173 -173
  145. package/rust/core/parser/handler/identifier/mod.rs +55 -55
  146. package/rust/core/parser/handler/identifier/on.rs +107 -107
  147. package/rust/core/parser/handler/identifier/print.rs +49 -49
  148. package/rust/core/parser/handler/identifier/sleep.rs +96 -43
  149. package/rust/core/parser/handler/identifier/spawn.rs +91 -91
  150. package/rust/core/parser/handler/identifier/synth.rs +135 -135
  151. package/rust/core/parser/handler/loop_.rs +194 -194
  152. package/rust/core/parser/handler/mod.rs +9 -9
  153. package/rust/core/parser/handler/pattern.rs +1 -1
  154. package/rust/core/parser/handler/tempo.rs +105 -57
  155. package/rust/core/parser/statement.rs +10 -11
  156. package/rust/core/plugin/loader.rs +1 -1
  157. package/rust/core/plugin/mod.rs +2 -2
  158. package/rust/core/plugin/runner/mod.rs +11 -0
  159. package/rust/core/plugin/{runner.rs → runner/non_wasm.rs} +297 -347
  160. package/rust/core/plugin/runner/wasm32.rs +43 -0
  161. package/rust/core/preprocessor/loader/inject.rs +278 -0
  162. package/rust/core/preprocessor/loader/loader_helpers.rs +110 -0
  163. package/rust/core/preprocessor/loader/mod.rs +235 -0
  164. package/rust/core/preprocessor/module.rs +2 -7
  165. package/rust/core/preprocessor/{processor.rs → processor/handlers.rs} +6 -13
  166. package/rust/core/preprocessor/processor/mod.rs +1 -0
  167. package/rust/core/preprocessor/resolver/bank.rs +49 -49
  168. package/rust/core/preprocessor/resolver/call.rs +124 -124
  169. package/rust/core/preprocessor/resolver/condition.rs +95 -95
  170. package/rust/core/preprocessor/resolver/driver.rs +324 -324
  171. package/rust/core/preprocessor/resolver/function.rs +2 -2
  172. package/rust/core/preprocessor/resolver/group.rs +46 -18
  173. package/rust/core/preprocessor/resolver/let_.rs +32 -32
  174. package/rust/core/preprocessor/resolver/loop_.rs +318 -318
  175. package/rust/core/preprocessor/resolver/mod.rs +16 -16
  176. package/rust/core/preprocessor/resolver/pattern.rs +83 -83
  177. package/rust/core/preprocessor/resolver/spawn.rs +99 -99
  178. package/rust/core/preprocessor/resolver/synth.rs +54 -54
  179. package/rust/core/preprocessor/resolver/tempo.rs +48 -48
  180. package/rust/core/preprocessor/resolver/trigger.rs +116 -116
  181. package/rust/core/preprocessor/resolver/value.rs +176 -176
  182. package/rust/core/store/global.rs +2 -6
  183. package/rust/core/store/mod.rs +1 -5
  184. package/rust/lib.rs +18 -3
  185. package/rust/main.rs +27 -3
  186. package/rust/types/Cargo.toml +1 -1
  187. package/rust/types/src/addons.rs +55 -55
  188. package/rust/types/src/config.rs +84 -74
  189. package/rust/types/src/lib.rs +15 -12
  190. package/rust/types/src/plugin.rs +20 -0
  191. package/rust/types/src/store.rs +139 -0
  192. package/rust/types/src/telemetry.rs +85 -85
  193. package/rust/utils/Cargo.toml +2 -2
  194. package/rust/utils/src/file.rs +94 -94
  195. package/rust/utils/src/first_usage.rs +97 -97
  196. package/rust/utils/src/lib.rs +9 -9
  197. package/rust/utils/src/logger.rs +200 -200
  198. package/rust/utils/src/path.rs +129 -88
  199. package/rust/utils/src/signature.rs +41 -41
  200. package/rust/utils/src/spinner.rs +20 -20
  201. package/rust/utils/src/version.rs +27 -27
  202. package/rust/utils/src/watcher.rs +46 -46
  203. package/rust/web/api.rs +5 -5
  204. package/rust/web/cdn.rs +34 -34
  205. package/rust/web/mod.rs +3 -3
  206. package/tests/integration.rs +21 -21
  207. package/typescript/core/functions/index.ts +11 -0
  208. package/typescript/pkg/devalang_core.ts +20 -4
  209. package/rust/core/audio/engine/sample.rs +0 -366
  210. package/rust/core/audio/engine/synth.rs +0 -325
  211. package/rust/core/audio/interpreter/arrow_call.rs +0 -311
  212. package/rust/core/audio/renderer.rs +0 -54
  213. package/rust/core/parser/driver.rs +0 -584
  214. package/rust/core/preprocessor/loader.rs +0 -637
  215. package/rust/core/store/export.rs +0 -28
  216. package/rust/core/store/function.rs +0 -40
  217. package/rust/core/store/import.rs +0 -28
  218. package/rust/core/store/variable.rs +0 -51
  219. package/rust/core/utils/mod.rs +0 -1
  220. package/rust/core/utils/path.rs +0 -37
@@ -1,637 +0,0 @@
1
- #[cfg(feature = "cli")]
2
- use crate::core::preprocessor::resolver::driver::{
3
- resolve_all_modules, resolve_and_flatten_all_modules,
4
- };
5
- #[cfg(feature = "cli")]
6
- use crate::core::utils::path::resolve_relative_path;
7
- #[cfg_attr(not(feature = "cli"), allow(unused_imports))]
8
- use crate::core::{
9
- error::ErrorHandler,
10
- lexer::{driver::Lexer, token::Token},
11
- parser::{
12
- driver::Parser,
13
- statement::{Statement, StatementKind},
14
- },
15
- plugin::loader::load_plugin,
16
- preprocessor::{module::Module, processor::process_modules},
17
- store::global::GlobalStore,
18
- utils::path::normalize_path,
19
- };
20
- use devalang_types::{BankFile, Value};
21
- #[cfg(feature = "cli")]
22
- use devalang_utils::logger::{LogLevel, Logger};
23
- use std::{collections::HashMap, path::Path};
24
-
25
- pub struct ModuleLoader {
26
- pub entry: String,
27
- pub output: String,
28
- pub base_dir: String,
29
- }
30
-
31
- impl ModuleLoader {
32
- pub fn new(entry: &str, output: &str) -> Self {
33
- let base_dir = Path::new(entry)
34
- .parent()
35
- .unwrap_or(Path::new(""))
36
- .to_string_lossy()
37
- .replace('\\', "/");
38
-
39
- Self {
40
- entry: entry.to_string(),
41
- output: output.to_string(),
42
- base_dir,
43
- }
44
- }
45
-
46
- pub fn from_raw_source(
47
- entry_path: &str,
48
- output_path: &str,
49
- content: &str,
50
- global_store: &mut GlobalStore,
51
- ) -> Self {
52
- let normalized_entry_path = normalize_path(entry_path);
53
-
54
- let mut module = Module::new(entry_path);
55
- module.content = content.to_string();
56
-
57
- // Insert a module stub containing the provided content into the
58
- // global store. This is used by the WASM APIs and tests which
59
- // operate on in-memory sources instead of files on disk.
60
- global_store.insert_module(normalized_entry_path.to_string(), module);
61
-
62
- Self {
63
- entry: normalized_entry_path.to_string(),
64
- output: output_path.to_string(),
65
- base_dir: "".to_string(),
66
- }
67
- }
68
-
69
- pub fn extract_statements_map(
70
- &self,
71
- global_store: &GlobalStore,
72
- ) -> HashMap<String, Vec<Statement>> {
73
- global_store
74
- .modules
75
- .iter()
76
- .map(|(path, module)| (path.clone(), module.statements.clone()))
77
- .collect()
78
- }
79
-
80
- pub fn load_single_module(&self, global_store: &mut GlobalStore) -> Result<Module, String> {
81
- let mut module = global_store
82
- .modules
83
- .remove(&self.entry)
84
- .ok_or_else(|| format!("Module not found in store for path: {}", self.entry))?;
85
-
86
- // SECTION Lexing the module content
87
- let lexer = Lexer::new();
88
- let tokens = lexer
89
- .lex_from_source(&module.content)
90
- .map_err(|e| format!("Lexer failed: {}", e))?;
91
-
92
- module.tokens = tokens.clone();
93
-
94
- // SECTION Parsing tokens into statements
95
- let mut parser = Parser::new();
96
- parser.set_current_module(self.entry.clone());
97
- let statements = parser.parse_tokens(tokens, global_store);
98
- module.statements = statements;
99
-
100
- // SECTION Injecting bank triggers if any (legacy default for single-module run)
101
- if let Err(e) = self.inject_bank_triggers(&mut module, "808", None) {
102
- return Err(format!("Failed to inject bank triggers: {}", e));
103
- }
104
-
105
- for (plugin_name, alias) in self.extract_plugin_uses(&module.statements) {
106
- self.load_plugin_and_register(&mut module, &plugin_name, &alias, global_store);
107
- }
108
-
109
- global_store
110
- .modules
111
- .insert(self.entry.clone(), module.clone());
112
-
113
- // SECTION Error handling
114
- let mut error_handler = ErrorHandler::new();
115
- error_handler.detect_from_statements(&mut parser, &module.statements);
116
-
117
- Ok(module)
118
- }
119
-
120
- pub fn load_wasm_module(&self, global_store: &mut GlobalStore) -> Result<(), String> {
121
- // Step one : Load the module from the global store
122
- let module = {
123
- let module_ref = global_store
124
- .modules
125
- .get(&self.entry)
126
- .ok_or_else(|| format!("❌ Module not found for path: {}", self.entry))?;
127
-
128
- Module::from_existing(&self.entry, module_ref.content.clone())
129
- };
130
-
131
- // Step two : lexing
132
- let lexer = Lexer::new();
133
- let tokens = lexer
134
- .lex_from_source(&module.content)
135
- .map_err(|e| format!("Lexer failed: {}", e))?;
136
-
137
- // Step three : parsing
138
- let mut parser = Parser::new();
139
- parser.set_current_module(self.entry.clone());
140
-
141
- let statements = parser.parse_tokens(tokens.clone(), global_store);
142
-
143
- let mut updated_module = module;
144
- updated_module.tokens = tokens;
145
- updated_module.statements = statements;
146
-
147
- // Step four : Injecting bank triggers if any
148
- if let Err(e) = self.inject_bank_triggers(&mut updated_module, "808", None) {
149
- return Err(format!("Failed to inject bank triggers: {}", e));
150
- }
151
-
152
- // Insert the updated module into the global store before processing so
153
- // process_modules can operate on it and populate variable_table, imports,
154
- // and other derived structures.
155
- global_store
156
- .modules
157
- .insert(self.entry.clone(), updated_module.clone());
158
-
159
- // Process modules to populate module.variable_table, import/export tables,
160
- // and other derived structures so runtime execution can resolve groups/synths.
161
- process_modules(self, global_store);
162
-
163
- for (plugin_name, alias) in self.extract_plugin_uses(&updated_module.statements) {
164
- self.load_plugin_and_register(&mut updated_module, &plugin_name, &alias, global_store);
165
- }
166
-
167
- // Step four : error handling
168
- let mut error_handler = ErrorHandler::new();
169
- error_handler.detect_from_statements(&mut parser, &updated_module.statements);
170
-
171
- // Final step : also expose module-level variables and functions into the global store
172
- // so runtime evaluation (render_audio) can find group/synth definitions.
173
- // Use the module instance that was actually processed by `process_modules`
174
- // (it lives in `global_store.modules`) because `updated_module` is a local
175
- // clone and won't contain the mutations applied by `process_modules`.
176
- if let Some(stored_module) = global_store.modules.get(&self.entry) {
177
- global_store
178
- .variables
179
- .variables
180
- .extend(stored_module.variable_table.variables.clone());
181
- global_store
182
- .functions
183
- .functions
184
- .extend(stored_module.function_table.functions.clone());
185
- } else {
186
- // Fallback to the local updated_module if for any reason the module
187
- // wasn't inserted into the store (defensive programming).
188
- global_store
189
- .variables
190
- .variables
191
- .extend(updated_module.variable_table.variables.clone());
192
- global_store
193
- .functions
194
- .functions
195
- .extend(updated_module.function_table.functions.clone());
196
- }
197
-
198
- Ok(())
199
- }
200
-
201
- #[cfg(feature = "cli")]
202
- pub fn load_all_modules(
203
- &self,
204
- global_store: &mut GlobalStore,
205
- ) -> (HashMap<String, Vec<Token>>, HashMap<String, Vec<Statement>>) {
206
- // SECTION Load the entry module and its dependencies
207
- let tokens_by_module = self.load_module_recursively(&self.entry, global_store);
208
-
209
- // SECTION Process and resolve modules
210
- process_modules(self, global_store);
211
- resolve_all_modules(self, global_store);
212
-
213
- // SECTION Flatten all modules to get statements (+ injects)
214
- let statements_by_module = resolve_and_flatten_all_modules(global_store);
215
-
216
- (tokens_by_module, statements_by_module)
217
- }
218
-
219
- #[cfg(feature = "cli")]
220
- fn load_module_recursively(
221
- &self,
222
- raw_path: &str,
223
- global_store: &mut GlobalStore,
224
- ) -> HashMap<String, Vec<Token>> {
225
- let path = normalize_path(raw_path);
226
-
227
- // Check if already loaded
228
- if global_store.modules.contains_key(&path) {
229
- return HashMap::new();
230
- }
231
-
232
- let lexer = Lexer::new();
233
- let tokens = match lexer.lex_tokens(&path) {
234
- Ok(t) => t,
235
- Err(e) => {
236
- let logger = Logger::new();
237
- logger.log_message(LogLevel::Error, &format!("Failed to lex '{}': {}", path, e));
238
- return HashMap::new();
239
- }
240
- };
241
-
242
- let mut parser = Parser::new();
243
- parser.set_current_module(path.clone());
244
-
245
- let statements = parser.parse_tokens(tokens.clone(), global_store);
246
-
247
- // Insert module into store
248
- let mut module = Module::new(&path);
249
- module.tokens = tokens.clone();
250
- module.statements = statements.clone();
251
-
252
- // Inject triggers for each bank used in module, respecting aliases
253
- for (bank_name, alias_opt) in self.extract_bank_decls(&statements) {
254
- if let Err(e) = self.inject_bank_triggers(&mut module, &bank_name, alias_opt) {
255
- eprintln!("Failed to inject bank triggers for '{}': {}", bank_name, e);
256
- }
257
- }
258
-
259
- for (plugin_name, alias) in self.extract_plugin_uses(&statements) {
260
- self.load_plugin_and_register(&mut module, &plugin_name, &alias, global_store);
261
- }
262
-
263
- // Inject module variables and functions into global store
264
- global_store
265
- .variables
266
- .variables
267
- .extend(module.variable_table.variables.clone());
268
- global_store
269
- .functions
270
- .functions
271
- .extend(module.function_table.functions.clone());
272
-
273
- // Inject the module into the global store
274
- global_store.insert_module(path.clone(), module);
275
-
276
- // Load dependencies
277
- self.load_module_imports(&path, global_store);
278
-
279
- // Error handling (use the module now in the store to include injected errors)
280
- let mut error_handler = ErrorHandler::new();
281
- if let Some(current_module) = global_store.modules.get(&path) {
282
- error_handler.detect_from_statements(&mut parser, &current_module.statements);
283
- } else {
284
- error_handler.detect_from_statements(&mut parser, &statements);
285
- }
286
-
287
- if error_handler.has_errors() {
288
- let logger = Logger::new();
289
- for error in error_handler.get_errors() {
290
- let trace = format!("{}:{}:{}", path, error.line, error.column);
291
- logger.log_error_with_stacktrace(&error.message, &trace);
292
- }
293
- }
294
-
295
- // Return tokens per module
296
- global_store
297
- .modules
298
- .iter()
299
- .map(|(p, m)| (p.clone(), m.tokens.clone()))
300
- .collect()
301
- }
302
-
303
- #[cfg(feature = "cli")]
304
- fn load_module_imports(&self, path: &String, global_store: &mut GlobalStore) {
305
- let import_paths: Vec<String> = {
306
- let current_module = match global_store.modules.get(path) {
307
- Some(module) => module,
308
- None => {
309
- eprintln!(
310
- "[warn] Cannot resolve imports: module '{}' not found in store",
311
- path
312
- );
313
- return;
314
- }
315
- };
316
-
317
- current_module
318
- .statements
319
- .iter()
320
- .filter_map(|stmt| {
321
- if let StatementKind::Import { source, .. } = &stmt.kind {
322
- Some(source.clone())
323
- } else {
324
- None
325
- }
326
- })
327
- .collect()
328
- };
329
-
330
- for import_path in import_paths {
331
- let resolved = resolve_relative_path(path, &import_path);
332
- self.load_module_recursively(&resolved, global_store);
333
- }
334
- }
335
-
336
- pub fn inject_bank_triggers(
337
- &self,
338
- module: &mut Module,
339
- bank_name: &str,
340
- alias_override: Option<String>,
341
- ) -> Result<Module, String> {
342
- let default_alias = bank_name
343
- .split('.')
344
- .next_back()
345
- .unwrap_or(bank_name)
346
- .to_string();
347
- let alias_ref = alias_override.as_deref().unwrap_or(&default_alias);
348
-
349
- let bank_path = match devalang_utils::path::get_deva_dir() {
350
- Ok(dir) => dir.join("banks").join(bank_name),
351
- Err(_) => Path::new("./.deva").join("banks").join(bank_name),
352
- };
353
- let bank_toml_path = bank_path.join("bank.toml");
354
-
355
- if !bank_toml_path.exists() {
356
- return Ok(module.clone());
357
- }
358
-
359
- let content = std::fs::read_to_string(&bank_toml_path)
360
- .map_err(|e| format!("Failed to read '{}': {}", bank_toml_path.display(), e))?;
361
-
362
- let parsed_bankfile: BankFile = toml::from_str(&content)
363
- .map_err(|e| format!("Failed to parse '{}': {}", bank_toml_path.display(), e))?;
364
-
365
- let mut bank_map = HashMap::new();
366
-
367
- for bank_trigger in parsed_bankfile.triggers.unwrap_or_default() {
368
- // Use the configured path from the bank file as the entity reference so
369
- // that bank entries can point to files or nested paths. Clean common
370
- // local prefixes like "./" to keep the URI tidy.
371
- let entity_ref = bank_trigger
372
- .path
373
- .clone()
374
- .replace("\\", "/")
375
- .replace("./", "");
376
- let bank_trigger_path = format!("devalang://bank/{}/{}", bank_name, entity_ref);
377
-
378
- // Keep the trigger key as declared (bank_trigger.name) but expose its
379
- // value as a devalang://bank URI pointing to the configured path.
380
- bank_map.insert(
381
- bank_trigger.name.clone(),
382
- Value::String(bank_trigger_path.clone()),
383
- );
384
-
385
- if module.variable_table.variables.contains_key(alias_ref) {
386
- eprintln!(
387
- "⚠️ Trigger '{}' already defined in module '{}', skipping injection.",
388
- alias_ref, module.path
389
- );
390
- continue;
391
- }
392
-
393
- module.variable_table.set(
394
- format!("{}.{}", alias_ref, bank_trigger.name),
395
- Value::String(bank_trigger_path.clone()),
396
- );
397
- }
398
-
399
- // Inject the map under the bank name
400
- module
401
- .variable_table
402
- .set(alias_ref.to_string(), Value::Map(bank_map));
403
-
404
- Ok(module.clone())
405
- }
406
-
407
- #[cfg_attr(not(feature = "cli"), allow(dead_code))]
408
- fn extract_bank_decls(&self, statements: &[Statement]) -> Vec<(String, Option<String>)> {
409
- let mut banks = Vec::new();
410
-
411
- for stmt in statements {
412
- if let StatementKind::Bank { alias } = &stmt.kind {
413
- let name_opt = match &stmt.value {
414
- Value::String(s) => Some(s.clone()),
415
- Value::Identifier(s) => Some(s.clone()),
416
- Value::Number(n) => Some(n.to_string()),
417
- _ => None,
418
- };
419
- if let Some(name) = name_opt {
420
- banks.push((name, alias.clone()));
421
- }
422
- }
423
- }
424
-
425
- banks
426
- }
427
-
428
- fn extract_plugin_uses(&self, statements: &[Statement]) -> Vec<(String, String)> {
429
- let mut plugins = Vec::new();
430
-
431
- for stmt in statements {
432
- if let StatementKind::Use { name, alias } = &stmt.kind {
433
- let alias_name = alias
434
- .clone()
435
- .unwrap_or_else(|| name.split('.').next_back().unwrap_or(name).to_string());
436
- plugins.push((name.clone(), alias_name));
437
- }
438
- }
439
-
440
- plugins
441
- }
442
-
443
- fn load_plugin_and_register(
444
- &self,
445
- module: &mut Module,
446
- plugin_name: &str,
447
- alias: &str,
448
- global_store: &mut GlobalStore,
449
- ) {
450
- // plugin_name expected format: "author.name"
451
- let mut parts = plugin_name.split('.');
452
- let author = match parts.next() {
453
- Some(a) if !a.is_empty() => a,
454
- _ => {
455
- eprintln!("Invalid plugin name '{}': missing author", plugin_name);
456
- return;
457
- }
458
- };
459
- let name = match parts.next() {
460
- Some(n) if !n.is_empty() => n,
461
- _ => {
462
- eprintln!("Invalid plugin name '{}': missing name", plugin_name);
463
- return;
464
- }
465
- };
466
- if parts.next().is_some() {
467
- eprintln!(
468
- "Invalid plugin name '{}': expected <author>.<name>",
469
- plugin_name
470
- );
471
- return;
472
- }
473
-
474
- // Enforce presence in .devalang config when plugin exists locally
475
- // Build expected URI from author/name
476
- let expected_uri = format!("devalang://plugin/{}.{}", author, name);
477
-
478
- // Detect local presence (preferred and legacy layouts)
479
- let root = match devalang_utils::path::get_deva_dir() {
480
- Ok(dir) => dir,
481
- Err(_) => Path::new("./.deva").to_path_buf(),
482
- };
483
- let plugin_dir_preferred = root.join("plugins").join(format!("{}.{}", author, name));
484
- let toml_path_preferred = plugin_dir_preferred.join("plugin.toml");
485
- let plugin_dir_fallback = root.join("plugins").join(author).join(name);
486
- let toml_path_fallback = plugin_dir_fallback.join("plugin.toml");
487
- let exists_locally = toml_path_preferred.exists() || toml_path_fallback.exists();
488
-
489
- if exists_locally {
490
- // Load config and verify plugin is declared
491
- let cfg_opt = crate::config::ops::load_config(None);
492
- let mut declared = false;
493
- if let Some(cfg) = cfg_opt {
494
- if let Some(list) = cfg.plugins {
495
- declared = list.iter().any(|p| p.path == expected_uri);
496
- }
497
- }
498
- if !declared {
499
- // Inject a single, clear error into the module so it is reported once by the error handler
500
- module.statements.push(Statement {
501
- kind: StatementKind::Error {
502
- message: "plugin present in local files but missing in .devalang config"
503
- .to_string(),
504
- },
505
- value: Value::Null,
506
- indent: 0,
507
- line: 0,
508
- column: 0,
509
- });
510
- return;
511
- }
512
- }
513
-
514
- match load_plugin(author, name) {
515
- Ok((info, wasm)) => {
516
- let uri = format!("devalang://plugin/{}.{}", author, name);
517
- global_store
518
- .plugins
519
- .insert(format!("{}:{}", author, name), (info, wasm));
520
- // Set alias to URI, and inject exported variables
521
- module
522
- .variable_table
523
- .set(alias.to_string(), Value::String(uri.clone()));
524
- // Also expose alias at global level so runtime can resolve it
525
- global_store
526
- .variables
527
- .set(alias.to_string(), Value::String(uri.clone()));
528
-
529
- if let Some((plugin_info, _)) =
530
- global_store.plugins.get(&format!("{}:{}", author, name))
531
- {
532
- for exp in &plugin_info.exports {
533
- match exp.kind.as_str() {
534
- "number" => {
535
- if let Some(toml::Value::String(s)) = &exp.default {
536
- if let Ok(n) = s.parse::<f32>() {
537
- module.variable_table.set(
538
- format!("{}.{}", alias, exp.name),
539
- Value::Number(n),
540
- );
541
- }
542
- } else if let Some(toml::Value::Integer(i)) = &exp.default {
543
- module.variable_table.set(
544
- format!("{}.{}", alias, exp.name),
545
- Value::Number(*i as f32),
546
- );
547
- } else if let Some(toml::Value::Float(f)) = &exp.default {
548
- module.variable_table.set(
549
- format!("{}.{}", alias, exp.name),
550
- Value::Number(*f as f32),
551
- );
552
- }
553
- }
554
- "string" => {
555
- if let Some(toml::Value::String(s)) = &exp.default {
556
- module.variable_table.set(
557
- format!("{}.{}", alias, exp.name),
558
- Value::String(s.clone()),
559
- );
560
- }
561
- }
562
- "bool" => {
563
- if let Some(toml::Value::Boolean(b)) = &exp.default {
564
- module
565
- .variable_table
566
- .set(format!("{}.{}", alias, exp.name), Value::Boolean(*b));
567
- }
568
- }
569
- "synth" => {
570
- // Provide a discoverable marker: alias.<synthName> resolves to alias.synthName waveform string
571
- module.variable_table.set(
572
- format!("{}.{}", alias, exp.name),
573
- Value::String(format!("{}.{}", alias, exp.name)),
574
- );
575
- }
576
- _ => {
577
- // Fallback: if default is present, map it to a Value dynamically
578
- if let Some(def) = &exp.default {
579
- let val = match def {
580
- toml::Value::String(s) => Value::String(s.clone()),
581
- toml::Value::Integer(i) => Value::Number(*i as f32),
582
- toml::Value::Float(f) => Value::Number(*f as f32),
583
- toml::Value::Boolean(b) => Value::Boolean(*b),
584
- toml::Value::Array(arr) => Value::Array(
585
- arr.iter()
586
- .map(|v| match v {
587
- toml::Value::String(s) => {
588
- Value::String(s.clone())
589
- }
590
- toml::Value::Integer(i) => {
591
- Value::Number(*i as f32)
592
- }
593
- toml::Value::Float(f) => {
594
- Value::Number(*f as f32)
595
- }
596
- toml::Value::Boolean(b) => Value::Boolean(*b),
597
- _ => Value::Null,
598
- })
599
- .collect(),
600
- ),
601
- toml::Value::Table(t) => {
602
- let mut m = std::collections::HashMap::new();
603
- for (k, v) in t.iter() {
604
- let vv = match v {
605
- toml::Value::String(s) => {
606
- Value::String(s.clone())
607
- }
608
- toml::Value::Integer(i) => {
609
- Value::Number(*i as f32)
610
- }
611
- toml::Value::Float(f) => {
612
- Value::Number(*f as f32)
613
- }
614
- toml::Value::Boolean(b) => Value::Boolean(*b),
615
- _ => Value::Null,
616
- };
617
- m.insert(k.clone(), vv);
618
- }
619
- Value::Map(m)
620
- }
621
- _ => Value::Null,
622
- };
623
- if val != Value::Null {
624
- module
625
- .variable_table
626
- .set(format!("{}.{}", alias, exp.name), val);
627
- }
628
- }
629
- }
630
- }
631
- }
632
- }
633
- }
634
- Err(e) => eprintln!("Failed to load plugin {}: {}", plugin_name, e),
635
- }
636
- }
637
- }
@@ -1,28 +0,0 @@
1
- use std::collections::HashMap;
2
-
3
- use devalang_types::Value;
4
-
5
- #[derive(Debug, Default, Clone, PartialEq)]
6
- pub struct ExportTable {
7
- pub exports: HashMap<String, Value>,
8
- }
9
-
10
- impl ExportTable {
11
- pub fn new() -> Self {
12
- ExportTable {
13
- exports: HashMap::new(),
14
- }
15
- }
16
-
17
- pub fn add_export(&mut self, name: String, value: Value) {
18
- self.exports.insert(name, value);
19
- }
20
-
21
- pub fn get_export(&self, name: &str) -> Option<&Value> {
22
- self.exports.get(name)
23
- }
24
-
25
- pub fn remove_export(&mut self, name: &str) -> Option<Value> {
26
- self.exports.remove(name)
27
- }
28
- }