@devaloop/devalang 0.0.1-alpha.9 → 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 (322) hide show
  1. package/.cargo/config.toml +2 -0
  2. package/.devalang +6 -1
  3. package/.github/workflows/ci.yml +103 -0
  4. package/Cargo.toml +81 -48
  5. package/README.md +137 -154
  6. package/docs/CHANGELOG.md +428 -1
  7. package/docs/CONTRIBUTING.md +101 -0
  8. package/docs/ROADMAP.md +14 -7
  9. package/docs/TODO.md +16 -15
  10. package/examples/automation.deva +42 -0
  11. package/examples/bank.deva +7 -0
  12. package/examples/bus.deva +10 -0
  13. package/examples/duration.deva +9 -0
  14. package/examples/effect.deva +2 -0
  15. package/examples/events.deva +12 -0
  16. package/examples/filter.deva +11 -0
  17. package/examples/function.deva +15 -0
  18. package/examples/index.deva +57 -12
  19. package/examples/lfo.deva +9 -0
  20. package/examples/loop.deva +5 -12
  21. package/examples/pattern.deva +8 -0
  22. package/examples/plugin.deva +16 -0
  23. package/examples/synth.deva +11 -1
  24. package/examples/synth_types.deva +17 -0
  25. package/examples/variables.deva +1 -1
  26. package/out-tsc/bin/index.d.ts +2 -0
  27. package/out-tsc/bin/index.js +51 -7
  28. package/out-tsc/core/functions/index.d.ts +42 -0
  29. package/out-tsc/core/functions/index.js +87 -0
  30. package/out-tsc/core/index.d.ts +6 -0
  31. package/out-tsc/core/index.js +22 -0
  32. package/out-tsc/core/types/index.d.ts +4 -0
  33. package/out-tsc/core/types/index.js +20 -0
  34. package/out-tsc/core/types/plugin.d.ts +18 -0
  35. package/out-tsc/core/types/plugin.js +2 -0
  36. package/out-tsc/core/types/result.d.ts +27 -0
  37. package/out-tsc/core/types/result.js +2 -0
  38. package/out-tsc/core/types/statement.d.ts +106 -0
  39. package/out-tsc/core/types/statement.js +2 -0
  40. package/out-tsc/core/types/value.d.ts +43 -0
  41. package/out-tsc/core/types/value.js +2 -0
  42. package/out-tsc/index.d.ts +7 -0
  43. package/out-tsc/index.js +42 -1
  44. package/out-tsc/pkg/devalang_core.d.ts +15 -0
  45. package/out-tsc/pkg/devalang_core.js +65 -0
  46. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +34 -0
  47. package/out-tsc/scripts/copy-wasm-dts.d.ts +1 -0
  48. package/out-tsc/scripts/copy-wasm-dts.js +73 -0
  49. package/out-tsc/scripts/postinstall.d.ts +1 -0
  50. package/out-tsc/scripts/postinstall.js +83 -0
  51. package/out-tsc/scripts/version/bump.d.ts +1 -0
  52. package/out-tsc/scripts/version/fetch.d.ts +1 -0
  53. package/out-tsc/scripts/version/index.d.ts +1 -0
  54. package/out-tsc/scripts/version/sync.d.ts +1 -0
  55. package/package.json +28 -7
  56. package/project-version.json +4 -4
  57. package/rust/cli/bank/api.rs +122 -0
  58. package/rust/cli/bank/commands.rs +306 -0
  59. package/rust/cli/bank/mod.rs +29 -0
  60. package/rust/cli/build/commands.rs +153 -0
  61. package/rust/cli/build/mod.rs +2 -0
  62. package/rust/cli/build/process.rs +165 -0
  63. package/rust/cli/check/mod.rs +208 -0
  64. package/rust/cli/discover/commands.rs +253 -0
  65. package/rust/cli/discover/config.rs +111 -0
  66. package/rust/cli/discover/fs.rs +19 -0
  67. package/rust/cli/discover/install.rs +103 -0
  68. package/rust/cli/discover/metadata.rs +48 -0
  69. package/rust/cli/discover/mod.rs +5 -0
  70. package/rust/cli/{init.rs → init/commands.rs} +32 -23
  71. package/rust/cli/init/mod.rs +1 -0
  72. package/rust/cli/install/addon.rs +118 -0
  73. package/rust/cli/install/bank.rs +72 -0
  74. package/rust/cli/install/commands.rs +35 -0
  75. package/rust/cli/install/mod.rs +4 -0
  76. package/rust/cli/install/plugin.rs +80 -0
  77. package/rust/cli/login/commands.rs +124 -0
  78. package/rust/cli/login/mod.rs +1 -0
  79. package/rust/cli/mod.rs +9 -202
  80. package/rust/cli/parser.rs +359 -0
  81. package/rust/cli/play/commands.rs +375 -0
  82. package/rust/cli/play/io.rs +17 -0
  83. package/rust/cli/play/mod.rs +5 -0
  84. package/rust/cli/play/process.rs +159 -0
  85. package/rust/cli/play/realtime.rs +91 -0
  86. package/rust/cli/play/utils.rs +23 -0
  87. package/rust/cli/telemetry/commands.rs +22 -0
  88. package/rust/cli/telemetry/event_creator.rs +80 -0
  89. package/rust/cli/telemetry/mod.rs +3 -0
  90. package/rust/cli/telemetry/send.rs +51 -0
  91. package/rust/cli/{template.rs → template/commands.rs} +17 -5
  92. package/rust/cli/template/mod.rs +1 -0
  93. package/rust/cli/update/commands.rs +6 -0
  94. package/rust/cli/update/mod.rs +1 -0
  95. package/rust/config/driver.rs +112 -0
  96. package/rust/config/mod.rs +3 -16
  97. package/rust/config/ops.rs +26 -0
  98. package/rust/config/settings.rs +101 -0
  99. package/rust/core/audio/engine/driver.rs +220 -0
  100. package/rust/core/audio/engine/export.rs +169 -0
  101. package/rust/core/audio/engine/helpers.rs +178 -0
  102. package/rust/core/audio/engine/mod.rs +56 -0
  103. package/rust/core/audio/engine/notes/dsp.rs +85 -0
  104. package/rust/core/audio/engine/notes/mod.rs +44 -0
  105. package/rust/core/audio/engine/notes/params.rs +294 -0
  106. package/rust/core/audio/engine/sample/insert.rs +199 -0
  107. package/rust/core/audio/engine/sample/mod.rs +40 -0
  108. package/rust/core/audio/engine/sample/padding.rs +170 -0
  109. package/rust/core/audio/evaluator/condition.rs +61 -0
  110. package/rust/core/audio/evaluator/mod.rs +9 -0
  111. package/rust/core/audio/evaluator/numeric.rs +152 -0
  112. package/rust/core/audio/evaluator/rhs.rs +16 -0
  113. package/rust/core/audio/evaluator/string_expr.rs +94 -0
  114. package/rust/core/audio/interpreter/driver.rs +574 -216
  115. package/rust/core/audio/interpreter/mod.rs +2 -12
  116. package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +175 -0
  117. package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +384 -0
  118. package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +2 -0
  119. package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +316 -0
  120. package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -0
  121. package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -0
  122. package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -0
  123. package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -0
  124. package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -0
  125. package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -0
  126. package/rust/core/audio/interpreter/statements/automate.rs +16 -0
  127. package/rust/core/audio/interpreter/statements/call.rs +295 -0
  128. package/rust/core/audio/interpreter/{condition.rs → statements/condition.rs} +72 -69
  129. package/rust/core/audio/interpreter/statements/function.rs +24 -0
  130. package/rust/core/audio/interpreter/statements/let_.rs +36 -0
  131. package/rust/core/audio/interpreter/statements/load.rs +17 -0
  132. package/rust/core/audio/interpreter/statements/loop_.rs +115 -0
  133. package/rust/core/audio/interpreter/statements/mod.rs +12 -0
  134. package/rust/core/audio/interpreter/statements/sleep.rs +28 -0
  135. package/rust/core/audio/interpreter/statements/spawn.rs +253 -0
  136. package/rust/core/audio/interpreter/statements/tempo.rs +40 -0
  137. package/rust/core/audio/interpreter/statements/trigger.rs +239 -0
  138. package/rust/core/audio/loader/mod.rs +1 -1
  139. package/rust/core/audio/loader/trigger.rs +98 -52
  140. package/rust/core/audio/mod.rs +2 -2
  141. package/rust/core/audio/player.rs +28 -12
  142. package/rust/core/audio/special/easing.rs +189 -0
  143. package/rust/core/audio/special/env.rs +45 -0
  144. package/rust/core/audio/special/math.rs +134 -0
  145. package/rust/core/audio/special/mod.rs +9 -0
  146. package/rust/core/audio/special/modulator.rs +143 -0
  147. package/rust/core/builder/mod.rs +129 -80
  148. package/rust/core/debugger/lexer.rs +4 -4
  149. package/rust/core/debugger/logs.rs +52 -0
  150. package/rust/core/debugger/mod.rs +11 -2
  151. package/rust/core/debugger/preprocessor.rs +4 -4
  152. package/rust/core/debugger/store.rs +38 -25
  153. package/rust/core/error/mod.rs +221 -12
  154. package/rust/core/lexer/driver.rs +59 -0
  155. package/rust/core/lexer/handler/arrow.rs +62 -11
  156. package/rust/core/lexer/handler/at.rs +5 -5
  157. package/rust/core/lexer/handler/brace.rs +11 -11
  158. package/rust/core/lexer/handler/colon.rs +5 -5
  159. package/rust/core/lexer/handler/comment.rs +3 -3
  160. package/rust/core/lexer/handler/dot.rs +6 -6
  161. package/rust/core/lexer/handler/driver.rs +143 -32
  162. package/rust/core/lexer/handler/identifier.rs +11 -5
  163. package/rust/core/lexer/handler/indent.rs +18 -4
  164. package/rust/core/lexer/handler/mod.rs +6 -5
  165. package/rust/core/lexer/handler/newline.rs +3 -3
  166. package/rust/core/lexer/handler/number.rs +5 -5
  167. package/rust/core/lexer/handler/operator.rs +5 -3
  168. package/rust/core/lexer/handler/parenthesis.rs +41 -0
  169. package/rust/core/lexer/handler/slash.rs +21 -0
  170. package/rust/core/lexer/handler/string.rs +3 -3
  171. package/rust/core/lexer/mod.rs +1 -49
  172. package/rust/core/lexer/token.rs +17 -12
  173. package/rust/core/mod.rs +9 -10
  174. package/rust/core/parser/driver/block.rs +111 -0
  175. package/rust/core/parser/driver/cursor.rs +82 -0
  176. package/rust/core/parser/driver/driver_impl.rs +139 -0
  177. package/rust/core/parser/driver/mod.rs +6 -0
  178. package/rust/core/parser/driver/parse_array.rs +120 -0
  179. package/rust/core/parser/driver/parse_map.rs +223 -0
  180. package/rust/core/parser/driver/parser.rs +160 -0
  181. package/rust/core/parser/handler/arrow_call.rs +277 -126
  182. package/rust/core/parser/handler/at.rs +142 -25
  183. package/rust/core/parser/handler/bank.rs +83 -20
  184. package/rust/core/parser/handler/condition.rs +14 -5
  185. package/rust/core/parser/handler/dot.rs +111 -75
  186. package/rust/core/parser/handler/identifier/automate.rs +254 -0
  187. package/rust/core/parser/handler/identifier/call.rs +74 -24
  188. package/rust/core/parser/handler/identifier/emit.rs +70 -0
  189. package/rust/core/parser/handler/identifier/function.rs +113 -0
  190. package/rust/core/parser/handler/identifier/group.rs +28 -14
  191. package/rust/core/parser/handler/identifier/let_.rs +61 -21
  192. package/rust/core/parser/handler/identifier/mod.rs +24 -20
  193. package/rust/core/parser/handler/identifier/on.rs +107 -0
  194. package/rust/core/parser/handler/identifier/print.rs +49 -0
  195. package/rust/core/parser/handler/identifier/sleep.rs +77 -14
  196. package/rust/core/parser/handler/identifier/spawn.rs +81 -31
  197. package/rust/core/parser/handler/identifier/synth.rs +102 -32
  198. package/rust/core/parser/handler/loop_.rs +144 -22
  199. package/rust/core/parser/handler/mod.rs +6 -5
  200. package/rust/core/parser/handler/pattern.rs +74 -0
  201. package/rust/core/parser/handler/tempo.rs +67 -9
  202. package/rust/core/parser/mod.rs +3 -4
  203. package/rust/core/parser/statement.rs +6 -92
  204. package/rust/core/plugin/loader.rs +137 -0
  205. package/rust/core/plugin/mod.rs +2 -0
  206. package/rust/core/plugin/runner/mod.rs +11 -0
  207. package/rust/core/plugin/runner/non_wasm.rs +297 -0
  208. package/rust/core/plugin/runner/wasm32.rs +43 -0
  209. package/rust/core/preprocessor/loader/inject.rs +278 -0
  210. package/rust/core/preprocessor/loader/loader_helpers.rs +110 -0
  211. package/rust/core/preprocessor/loader/mod.rs +235 -0
  212. package/rust/core/preprocessor/mod.rs +4 -4
  213. package/rust/core/preprocessor/module.rs +55 -50
  214. package/rust/core/preprocessor/processor/handlers.rs +107 -0
  215. package/rust/core/preprocessor/processor/mod.rs +1 -0
  216. package/rust/core/preprocessor/resolver/bank.rs +14 -12
  217. package/rust/core/preprocessor/resolver/call.rs +106 -105
  218. package/rust/core/preprocessor/resolver/condition.rs +13 -10
  219. package/rust/core/preprocessor/resolver/driver.rs +145 -48
  220. package/rust/core/preprocessor/resolver/function.rs +69 -0
  221. package/rust/core/preprocessor/resolver/group.rs +122 -61
  222. package/rust/core/preprocessor/resolver/let_.rs +13 -12
  223. package/rust/core/preprocessor/resolver/loop_.rs +240 -13
  224. package/rust/core/preprocessor/resolver/mod.rs +8 -6
  225. package/rust/core/preprocessor/resolver/pattern.rs +83 -0
  226. package/rust/core/preprocessor/resolver/spawn.rs +83 -42
  227. package/rust/core/preprocessor/resolver/synth.rs +15 -11
  228. package/rust/core/preprocessor/resolver/tempo.rs +13 -14
  229. package/rust/core/preprocessor/resolver/trigger.rs +32 -28
  230. package/rust/core/preprocessor/resolver/value.rs +111 -13
  231. package/rust/core/store/global.rs +57 -39
  232. package/rust/core/store/mod.rs +0 -3
  233. package/rust/lib.rs +323 -117
  234. package/rust/main.rs +388 -65
  235. package/rust/types/Cargo.toml +11 -0
  236. package/rust/types/src/addons.rs +55 -0
  237. package/rust/types/src/ast.rs +202 -0
  238. package/rust/types/src/config.rs +84 -0
  239. package/rust/types/src/lib.rs +15 -0
  240. package/rust/types/src/plugin.rs +20 -0
  241. package/rust/types/src/store.rs +139 -0
  242. package/rust/types/src/telemetry.rs +85 -0
  243. package/rust/utils/Cargo.toml +26 -0
  244. package/rust/utils/src/error.rs +186 -0
  245. package/rust/utils/src/file.rs +94 -0
  246. package/rust/utils/src/first_usage.rs +97 -0
  247. package/rust/utils/{mod.rs → src/lib.rs} +6 -3
  248. package/rust/utils/{logger.rs → src/logger.rs} +94 -17
  249. package/rust/utils/src/path.rs +129 -0
  250. package/rust/utils/src/signature.rs +41 -0
  251. package/rust/utils/{spinner.rs → src/spinner.rs} +7 -8
  252. package/rust/utils/src/version.rs +27 -0
  253. package/rust/utils/{watcher.rs → src/watcher.rs} +17 -4
  254. package/rust/web/api.rs +5 -0
  255. package/rust/web/cdn.rs +34 -0
  256. package/rust/web/mod.rs +3 -0
  257. package/rust/web/sso.rs +5 -0
  258. package/templates/minimal/README.md +143 -127
  259. package/templates/welcome/README.md +143 -127
  260. package/templates/welcome/src/index.deva +56 -8
  261. package/templates/welcome/src/variables.deva +2 -4
  262. package/tests/integration.rs +21 -0
  263. package/tests/rust/cli_check_build.rs +21 -0
  264. package/tests/rust/cli_help.rs +12 -0
  265. package/tests/rust/cli_template_list.rs +10 -0
  266. package/tests/rust/cli_version.rs +11 -0
  267. package/tests/typescript/index.spec.ts +136 -0
  268. package/tests/typescript/playhead.spec.ts +36 -0
  269. package/tests/typescript/render_e2e.spec.ts +77 -0
  270. package/tsconfig.json +12 -10
  271. package/typescript/bin/index.ts +19 -5
  272. package/typescript/core/functions/index.ts +94 -0
  273. package/typescript/core/index.ts +6 -0
  274. package/typescript/core/types/index.ts +4 -0
  275. package/typescript/core/types/plugin.ts +19 -0
  276. package/typescript/core/types/result.ts +29 -0
  277. package/typescript/core/types/statement.ts +47 -0
  278. package/typescript/core/types/value.ts +29 -0
  279. package/typescript/index.ts +8 -1
  280. package/typescript/pkg/devalang_core.d.ts +4 -0
  281. package/typescript/pkg/devalang_core.ts +65 -0
  282. package/typescript/scripts/copy-wasm-dts.ts +41 -0
  283. package/typescript/scripts/postinstall.ts +85 -0
  284. package/typescript/scripts/version/bump.ts +0 -1
  285. package/typescript/scripts/version/index.ts +0 -1
  286. package/docs/COMMANDS.md +0 -85
  287. package/docs/CONFIG.md +0 -30
  288. package/docs/SYNTAX.md +0 -210
  289. package/out-tsc/bin/devalang.exe +0 -0
  290. package/out-tsc/scripts/postbuild.js +0 -11
  291. package/rust/cli/build.rs +0 -137
  292. package/rust/cli/check.rs +0 -117
  293. package/rust/cli/play.rs +0 -193
  294. package/rust/config/loader.rs +0 -13
  295. package/rust/core/audio/engine.rs +0 -203
  296. package/rust/core/audio/evaluator.rs +0 -31
  297. package/rust/core/audio/interpreter/arrow_call.rs +0 -129
  298. package/rust/core/audio/interpreter/call.rs +0 -64
  299. package/rust/core/audio/interpreter/let_.rs +0 -19
  300. package/rust/core/audio/interpreter/load.rs +0 -18
  301. package/rust/core/audio/interpreter/loop_.rs +0 -67
  302. package/rust/core/audio/interpreter/sleep.rs +0 -36
  303. package/rust/core/audio/interpreter/spawn.rs +0 -66
  304. package/rust/core/audio/interpreter/tempo.rs +0 -16
  305. package/rust/core/audio/interpreter/trigger.rs +0 -69
  306. package/rust/core/audio/renderer.rs +0 -54
  307. package/rust/core/parser/driver.rs +0 -331
  308. package/rust/core/preprocessor/loader.rs +0 -193
  309. package/rust/core/preprocessor/processor.rs +0 -76
  310. package/rust/core/shared/duration.rs +0 -8
  311. package/rust/core/shared/mod.rs +0 -2
  312. package/rust/core/shared/value.rs +0 -18
  313. package/rust/core/store/export.rs +0 -28
  314. package/rust/core/store/import.rs +0 -28
  315. package/rust/core/store/variable.rs +0 -28
  316. package/rust/core/utils/mod.rs +0 -2
  317. package/rust/core/utils/path.rs +0 -31
  318. package/rust/core/utils/validation.rs +0 -37
  319. package/rust/utils/file.rs +0 -35
  320. package/rust/utils/signature.rs +0 -17
  321. package/rust/utils/version.rs +0 -15
  322. package/typescript/scripts/postbuild.ts +0 -8
package/rust/cli/play.rs DELETED
@@ -1,193 +0,0 @@
1
- use crate::{
2
- config::Config,
3
- core::{
4
- builder::Builder,
5
- debugger::{ lexer::write_lexer_log_file, preprocessor::write_preprocessor_log_file },
6
- preprocessor::loader::ModuleLoader,
7
- store::global::GlobalStore,
8
- utils::path::{ find_entry_file, normalize_path },
9
- },
10
- utils::{ logger::{ LogLevel, Logger }, spinner::with_spinner, watcher::watch_directory },
11
- };
12
-
13
- use std::{ path::Path, sync::mpsc::channel, thread, time::Duration };
14
- use std::fs;
15
- use std::collections::HashMap;
16
-
17
- #[cfg(feature = "cli")]
18
- pub fn handle_play_command(
19
- config: Option<Config>,
20
- entry: Option<String>,
21
- output: Option<String>,
22
- watch: bool,
23
- repeat: bool
24
- ) {
25
- use crate::core::audio::player::AudioPlayer;
26
-
27
- let logger = Logger::new();
28
-
29
- let entry_path = entry
30
- .or_else(|| config.as_ref().and_then(|c| c.defaults.entry.clone()))
31
- .unwrap_or_else(|| "".to_string());
32
-
33
- let output_path = output
34
- .or_else(|| config.as_ref().and_then(|c| c.defaults.output.clone()))
35
- .unwrap_or_else(|| "".to_string());
36
-
37
- let fetched_repeat = if repeat {
38
- true
39
- } else {
40
- config
41
- .as_ref()
42
- .and_then(|c| c.defaults.repeat)
43
- .unwrap_or(false)
44
- };
45
-
46
- if entry_path.is_empty() || output_path.is_empty() {
47
- logger.log_message(LogLevel::Error, "Entry or output path not specified.");
48
- std::process::exit(1);
49
- }
50
-
51
- let entry_file = find_entry_file(&entry_path).unwrap_or_else(|| {
52
- logger.log_message(LogLevel::Error, "index.deva not found");
53
- std::process::exit(1);
54
- });
55
-
56
- let audio_file = format!("{}/audio/index.wav", normalize_path(&output_path));
57
- let mut audio_player = AudioPlayer::new();
58
-
59
- if watch && fetched_repeat {
60
- logger.log_message(
61
- LogLevel::Error,
62
- "Watch and repeat cannot be used together. Use repeat instead."
63
- );
64
- std::process::exit(1);
65
- }
66
-
67
- if watch {
68
- let (tx, rx) = channel::<()>();
69
-
70
- // Thread 1 : Watcher sending changes
71
- let entry_clone = entry_path.clone();
72
- thread::spawn(move || {
73
- let _ = watch_directory(entry_clone, move || {
74
- let _ = tx.send(()); // signal a change
75
- });
76
- });
77
-
78
- // Main thread: build + play in a loop
79
- begin_play(&config, &entry_file, &output_path);
80
- audio_player.play_file_once(&audio_file);
81
-
82
- logger.log_message(LogLevel::Watcher, "Watching for changes... Press Ctrl+C to exit.");
83
-
84
- while let Ok(_) = rx.recv() {
85
- logger.log_message(LogLevel::Watcher, "Change detected, rebuilding...");
86
-
87
- begin_play(&config, &entry_file, &output_path);
88
-
89
- logger.log_message(LogLevel::Info, "🎵 Playback started (once mode)...");
90
-
91
- audio_player.play_file_once(&audio_file);
92
- }
93
- } else if fetched_repeat {
94
- // Initial build to start from a clean slate
95
- begin_play(&config, &entry_file, &output_path);
96
-
97
- logger.log_message(LogLevel::Info, "🎵 Playback started (repeat mode)...");
98
-
99
- let mut last_snapshot = snapshot_files(&entry_path);
100
- let mut audio_player = AudioPlayer::new();
101
- audio_player.play_file_once(&audio_file);
102
-
103
- loop {
104
- let current_snapshot = snapshot_files(&entry_path);
105
- let has_changed = files_changed(&last_snapshot, &current_snapshot);
106
-
107
- if has_changed {
108
- logger.log_message(LogLevel::Info, "Change detected, rebuilding in background...");
109
- let entry_file = entry_file.clone();
110
- let output_path = output_path.clone();
111
- let config_clone = config.clone();
112
-
113
- // Rebuild in a separate thread
114
- std::thread::spawn(move || {
115
- begin_play(&config_clone, &entry_file, &output_path);
116
- });
117
-
118
- last_snapshot = current_snapshot;
119
- }
120
-
121
- // Wait for the audio to finish without blocking the current playback
122
- audio_player.wait_until_end();
123
-
124
- // Then replay the audio (rebuilt or not)
125
- audio_player.play_file_once(&audio_file);
126
- }
127
- } else {
128
- // Single execution
129
- begin_play(&config, &entry_file, &output_path);
130
-
131
- logger.log_message(LogLevel::Info, "🎵 Playback started (once mode)...");
132
-
133
- audio_player.play_file_once(&audio_file);
134
- audio_player.wait_until_end();
135
- }
136
- }
137
-
138
- fn begin_play(config: &Option<Config>, entry_file: &str, output: &str) {
139
- let spinner = with_spinner("Building...", || {
140
- thread::sleep(Duration::from_millis(800));
141
- });
142
-
143
- let normalized_entry = normalize_path(entry_file);
144
- let normalized_output_dir = normalize_path(&output);
145
-
146
- let duration = std::time::Instant::now();
147
- let mut global_store = GlobalStore::new();
148
- let loader = ModuleLoader::new(&normalized_entry, &normalized_output_dir);
149
- let (modules_tokens, modules_statements) = loader.load_all_modules(&mut global_store);
150
-
151
- // SECTION Write logs
152
- write_lexer_log_file(&normalized_output_dir, "lexer_tokens.log", modules_tokens.clone());
153
- write_preprocessor_log_file(
154
- &normalized_output_dir,
155
- "resolved_statements.log",
156
- modules_statements.clone()
157
- );
158
-
159
- // SECTION Building AST and Audio
160
- let builder = Builder::new();
161
- builder.build_ast(&modules_statements, &output);
162
- builder.build_audio(&modules_statements, &output, &mut global_store);
163
-
164
- // SECTION Logging
165
- let logger = Logger::new();
166
- let success_message = format!(
167
- "Build completed successfully in {:.2?}. Output files written to: '{}'",
168
- duration.elapsed(),
169
- normalized_output_dir
170
- );
171
-
172
- logger.log_message(LogLevel::Success, &success_message);
173
- }
174
-
175
- fn snapshot_files<P: AsRef<Path>>(dir: P) -> HashMap<String, u64> {
176
- let mut map = HashMap::new();
177
- if let Ok(entries) = fs::read_dir(dir) {
178
- for entry in entries.flatten() {
179
- if let Ok(meta) = entry.metadata() {
180
- if let Ok(mtime) = meta.modified() {
181
- if let Ok(duration) = mtime.duration_since(std::time::UNIX_EPOCH) {
182
- map.insert(entry.path().display().to_string(), duration.as_secs());
183
- }
184
- }
185
- }
186
- }
187
- }
188
- map
189
- }
190
-
191
- fn files_changed(old: &HashMap<String, u64>, new: &HashMap<String, u64>) -> bool {
192
- old != new
193
- }
@@ -1,13 +0,0 @@
1
- use std::{ fs, path::Path };
2
- use crate::config::Config;
3
-
4
- pub fn load_config(path: Option<&Path>) -> Option<Config> {
5
- let config_path = path.unwrap_or_else(|| Path::new(".devalang"));
6
-
7
- if config_path.exists() {
8
- let content = fs::read_to_string(config_path).ok()?;
9
- toml::from_str(&content).ok()
10
- } else {
11
- None
12
- }
13
- }
@@ -1,203 +0,0 @@
1
- use std::{ collections::HashMap, fs::File, io::BufReader };
2
- use hound::{ SampleFormat, WavSpec, WavWriter };
3
- use rodio::{ Decoder, Source };
4
-
5
- use crate::core::{
6
- store::variable::VariableTable,
7
- utils::path::{ normalize_path, resolve_relative_path },
8
- };
9
-
10
- const SAMPLE_RATE: u32 = 44100;
11
- const CHANNELS: u16 = 2;
12
-
13
- #[derive(Debug, Clone, PartialEq)]
14
- pub struct AudioEngine {
15
- pub volume: f32,
16
- pub variables: VariableTable,
17
- pub buffer: Vec<i16>,
18
- pub module_name: String,
19
- }
20
-
21
- impl AudioEngine {
22
- pub fn new(module_name: String) -> Self {
23
- AudioEngine {
24
- volume: 1.0,
25
- buffer: vec![],
26
- variables: VariableTable::new(),
27
- module_name,
28
- }
29
- }
30
-
31
- pub fn mix(&mut self, other: &AudioEngine) {
32
- let max_len = self.buffer.len().max(other.buffer.len());
33
- self.buffer.resize(max_len, 0);
34
-
35
- for (i, &sample) in other.buffer.iter().enumerate() {
36
- self.buffer[i] = self.buffer[i].saturating_add(sample);
37
- }
38
- }
39
-
40
- pub fn merge_with(&mut self, other: AudioEngine) {
41
- if other.buffer.iter().all(|&s| s == 0) {
42
- eprintln!("⚠️ Skipping merge: other buffer is silent");
43
- return;
44
- }
45
-
46
- if self.buffer.iter().all(|&s| s == 0) {
47
- self.buffer = other.buffer;
48
- self.variables.variables.extend(other.variables.variables);
49
- return;
50
- }
51
-
52
- self.mix(&other);
53
- self.variables.variables.extend(other.variables.variables);
54
- }
55
-
56
- pub fn set_duration(&mut self, duration_secs: f32) {
57
- let total_samples = (duration_secs * (SAMPLE_RATE as f32) * (CHANNELS as f32)) as usize;
58
-
59
- if self.buffer.len() < total_samples {
60
- self.buffer.resize(total_samples, 0);
61
- }
62
- }
63
-
64
- pub fn set_variables(&mut self, variables: VariableTable) {
65
- self.variables = variables;
66
- }
67
-
68
- pub fn generate_wav_file(&mut self, output_dir: &String) -> Result<(), String> {
69
- if self.buffer.len() % (CHANNELS as usize) != 0 {
70
- self.buffer.push(0);
71
- println!("Completed buffer to respect stereo format.");
72
- }
73
-
74
- let spec = WavSpec {
75
- channels: CHANNELS,
76
- sample_rate: SAMPLE_RATE,
77
- bits_per_sample: 16,
78
- sample_format: SampleFormat::Int,
79
- };
80
-
81
- let mut writer = WavWriter::create(output_dir, spec).map_err(|e|
82
- format!("Error creating WAV file: {}", e)
83
- )?;
84
-
85
- for sample in &self.buffer {
86
- writer.write_sample(*sample).map_err(|e| format!("Error writing sample: {:?}", e))?;
87
- }
88
-
89
- writer.finalize().map_err(|e| format!("Error finalizing WAV: {:?}", e))?;
90
-
91
- Ok(())
92
- }
93
-
94
- pub fn insert_note(
95
- &mut self,
96
- waveform: String,
97
- freq: f32,
98
- amp: f32,
99
- start_time_ms: f32,
100
- duration_ms: f32
101
- ) {
102
- let sample_rate = SAMPLE_RATE as f32;
103
- let channels = CHANNELS as usize;
104
-
105
- let total_samples = ((duration_ms / 1000.0) * sample_rate) as usize;
106
- let start_sample = ((start_time_ms / 1000.0) * sample_rate) as usize;
107
- let amplitude = (i16::MAX as f32) * amp.clamp(0.0, 1.0);
108
-
109
- let mut samples = Vec::with_capacity(total_samples);
110
- let fade_len = (sample_rate * 0.01) as usize; // 10 ms fade
111
-
112
- for i in 0..total_samples {
113
- let t = ((start_sample + i) as f32) / sample_rate;
114
- let phase = 2.0 * std::f32::consts::PI * freq * t;
115
-
116
- let mut value = match waveform.as_str() {
117
- "sine" => phase.sin(),
118
- "square" => if phase.sin() >= 0.0 { 1.0 } else { -1.0 }
119
- "saw" => 2.0 * (freq * t - (freq * t + 0.5).floor()),
120
- "triangle" => (2.0 * (2.0 * (freq * t).fract() - 1.0)).abs() * 2.0 - 1.0,
121
- _ => 0.0,
122
- };
123
-
124
- // Fade in/out
125
- if i < fade_len {
126
- value *= (i as f32) / (fade_len as f32);
127
- } else if i >= total_samples - fade_len {
128
- value *= ((total_samples - i) as f32) / (fade_len as f32);
129
- }
130
-
131
- samples.push((value * amplitude) as i16);
132
- }
133
-
134
- // Convert to stereo
135
- let stereo_samples: Vec<i16> = samples
136
- .iter()
137
- .flat_map(|s| vec![*s, *s])
138
- .collect();
139
-
140
- let offset = start_sample * channels;
141
- let required_len = offset + stereo_samples.len();
142
-
143
- if self.buffer.len() < required_len {
144
- self.buffer.resize(required_len, 0);
145
- }
146
-
147
- for (i, sample) in stereo_samples.iter().enumerate() {
148
- if *sample != 0 {
149
- }
150
- self.buffer[offset + i] = self.buffer[offset + i].saturating_add(*sample);
151
- }
152
- }
153
-
154
- pub fn insert_sample(
155
- &mut self,
156
- filepath: &str,
157
- time_secs: f32,
158
- dur_sec: f32,
159
- effects: Option<HashMap<String, f32>>
160
- ) {
161
- let resolved = resolve_relative_path(&self.module_name.clone(), filepath);
162
-
163
- let file = BufReader::new(File::open(resolved).expect("Failed to open audio file"));
164
- let decoder = Decoder::new(file).expect("Failed to decode audio file");
165
-
166
- // Mono or stereo reading possible here, we will duplicate in L/R
167
- let max_mono_samples = (dur_sec * (SAMPLE_RATE as f32)) as usize;
168
- let samples: Vec<i16> = decoder.convert_samples().take(max_mono_samples).collect();
169
-
170
- if samples.is_empty() {
171
- eprintln!("No samples found in the audio file: {}", filepath);
172
- return;
173
- }
174
-
175
- // TODO Apply effects here if needed
176
- let offset = (time_secs * (SAMPLE_RATE as f32) * (CHANNELS as f32)) as usize;
177
- let required_len = offset + samples.len() * (CHANNELS as usize);
178
- let padded_required_len = if required_len % 2 == 1 {
179
- required_len + 1
180
- } else {
181
- required_len
182
- };
183
-
184
- self.buffer.resize(padded_required_len, 0);
185
- self.pad_samples(&samples, time_secs);
186
- }
187
-
188
- fn pad_samples(&mut self, samples: &[i16], time_secs: f32) {
189
- let offset = (time_secs * (SAMPLE_RATE as f32) * (CHANNELS as f32)) as usize;
190
-
191
- for (i, &sample) in samples.iter().enumerate() {
192
- let adjusted_sample = ((sample as f32) * self.volume).round() as i16;
193
-
194
- let left_pos = offset + i * 2;
195
- let right_pos = left_pos + 1;
196
-
197
- if right_pos < self.buffer.len() {
198
- self.buffer[left_pos] = self.buffer[left_pos].saturating_add(adjusted_sample); // gauche
199
- self.buffer[right_pos] = self.buffer[right_pos].saturating_add(adjusted_sample); // droite
200
- }
201
- }
202
- }
203
- }
@@ -1,31 +0,0 @@
1
- use crate::core::{ shared::value::Value, store::variable::VariableTable };
2
-
3
- pub fn evaluate_condition_string(expr: &str, vars: &VariableTable) -> bool {
4
- let tokens: Vec<&str> = expr.split_whitespace().collect();
5
- if tokens.len() != 3 {
6
- return false;
7
- }
8
-
9
- let left = tokens[0];
10
- let op = tokens[1];
11
- let right = tokens[2];
12
-
13
- let left_val = match vars.get(left) {
14
- Some(Value::Number(n)) => *n,
15
- _ => {
16
- return false;
17
- }
18
- };
19
-
20
- let right_val: f32 = right.parse().unwrap_or(0.0);
21
-
22
- match op {
23
- ">" => left_val > right_val,
24
- "<" => left_val < right_val,
25
- ">=" => left_val >= right_val,
26
- "<=" => left_val <= right_val,
27
- "==" => (left_val - right_val).abs() < f32::EPSILON,
28
- "!=" => (left_val - right_val).abs() > f32::EPSILON,
29
- _ => false,
30
- }
31
- }
@@ -1,129 +0,0 @@
1
- use crate::core::{
2
- audio::engine::AudioEngine,
3
- parser::statement::{ Statement, StatementKind },
4
- shared::value::Value,
5
- store::variable::VariableTable,
6
- };
7
-
8
- use std::collections::HashMap;
9
-
10
- pub fn interprete_call_arrow_statement(
11
- stmt: &Statement,
12
- audio_engine: &mut AudioEngine,
13
- variable_table: &VariableTable,
14
- base_bpm: f32,
15
- base_duration: f32,
16
- max_end_time: &mut f32,
17
- mut cursor_time: Option<&mut f32>,
18
- update_cursor: bool
19
- ) -> (f32, f32) {
20
- let cursor_copy = cursor_time
21
- .as_ref()
22
- .map(|c| **c)
23
- .unwrap_or(0.0);
24
-
25
- if let StatementKind::ArrowCall { target, method, args } = &stmt.kind {
26
- let Some(Value::Map(synth_map)) = variable_table.get(target) else {
27
- println!("❌ Synth '{}' not found in variable table", target);
28
- return (*max_end_time, cursor_copy);
29
- };
30
-
31
- let Some(Value::String(entity)) = synth_map.get("entity") else {
32
- println!("❌ Missing 'entity' key in synth '{}'.", target);
33
- return (*max_end_time, cursor_copy);
34
- };
35
-
36
- if entity != "synth" {
37
- println!("❌ '{}' is not a synth, entity is '{}'.", target, entity);
38
- return (*max_end_time, cursor_copy);
39
- }
40
-
41
- let Some(Value::Map(value_map)) = synth_map.get("value") else {
42
- println!("❌ Missing 'value' map in synth '{}'.", target);
43
- return (*max_end_time, cursor_copy);
44
- };
45
-
46
- let Some(Value::String(waveform)) = value_map.get("waveform") else {
47
- println!("❌ Missing or invalid 'waveform' in synth '{}'.", target);
48
- return (*max_end_time, cursor_copy);
49
- };
50
-
51
- let Some(Value::Map(params)) = value_map.get("parameters") else {
52
- println!("❌ Missing or invalid 'parameters' in synth '{}'.", target);
53
- return (*max_end_time, cursor_copy);
54
- };
55
-
56
- let freq = extract_f32(params, "freq").unwrap_or(440.0);
57
- let amp = extract_f32(params, "amp").unwrap_or(1.0);
58
-
59
- if method == "note" {
60
- let Some(Value::Identifier(note_name)) = args.get(0) else {
61
- println!("❌ Invalid or missing argument for 'note' method on '{}'.", target);
62
- return (*max_end_time, cursor_copy);
63
- };
64
-
65
- let mut final_note_params = HashMap::new();
66
- if let Some(Value::Map(note_params)) = args.get(1) {
67
- for (key, value) in note_params {
68
- final_note_params.insert(key.clone(), value.clone());
69
- }
70
- }
71
-
72
- let duration_ms = extract_f32(&final_note_params, "duration").unwrap_or(base_duration);
73
- let duration_secs = duration_ms / 1000.0;
74
-
75
- let final_freq = note_to_freq(note_name);
76
- let start_time = cursor_copy;
77
- let end_time = start_time + duration_secs;
78
-
79
- audio_engine.insert_note(
80
- waveform.clone(),
81
- final_freq,
82
- amp,
83
- start_time * 1000.0,
84
- duration_ms
85
- );
86
-
87
- *max_end_time = (*max_end_time).max(end_time);
88
-
89
- if update_cursor {
90
- if let Some(c) = cursor_time.as_mut() {
91
- **c = end_time;
92
- }
93
- }
94
-
95
- return (*max_end_time, end_time);
96
- } else {
97
- println!("❌ Unknown method '{}' on synth '{}'.", method, target);
98
- }
99
- }
100
-
101
- (*max_end_time, cursor_copy)
102
- }
103
-
104
- fn extract_f32(map: &HashMap<String, Value>, key: &str) -> Option<f32> {
105
- map.get(key).and_then(|v| {
106
- match v {
107
- Value::Number(n) => Some(*n),
108
- _ => None,
109
- }
110
- })
111
- }
112
-
113
- fn note_to_freq(note: &str) -> f32 {
114
- let notes = vec!["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
115
-
116
- if note.len() < 2 || note.len() > 3 {
117
- return 440.0;
118
- }
119
-
120
- let (name, octave_str) = note.split_at(note.len() - 1);
121
- let semitone = notes
122
- .iter()
123
- .position(|&n| n == name)
124
- .unwrap_or(9) as i32;
125
- let octave = octave_str.parse::<i32>().unwrap_or(4);
126
- let midi_note = (octave + 1) * 12 + semitone;
127
-
128
- 440.0 * (2.0_f32).powf(((midi_note as f32) - 69.0) / 12.0)
129
- }
@@ -1,64 +0,0 @@
1
- use crate::core::{
2
- audio::{ engine::AudioEngine, interpreter::{ driver::execute_audio_block } },
3
- parser::statement::{ Statement, StatementKind },
4
- shared::{ duration::Duration, value::Value },
5
- store::variable::VariableTable,
6
- };
7
-
8
- pub fn interprete_call_statement(
9
- stmt: &Statement,
10
- audio_engine: AudioEngine,
11
- variable_table: VariableTable,
12
- base_bpm: f32,
13
- base_duration: f32,
14
- max_end_time: f32,
15
- cursor_time: f32
16
- ) -> (AudioEngine, f32, f32) {
17
- match &stmt.value {
18
- Value::String(identifier) | Value::Identifier(identifier) => {
19
- if let Some(Value::Map(map)) = variable_table.clone().get(identifier) {
20
- if let Some(Value::Block(block)) = map.get("body") {
21
- let (eng, _, end_time) = execute_audio_block(
22
- audio_engine,
23
- variable_table,
24
- block.clone(),
25
- base_bpm,
26
- base_duration,
27
- max_end_time,
28
- cursor_time
29
- );
30
-
31
- return (eng, max_end_time.max(end_time), end_time);
32
- } else {
33
- eprintln!("❌ Group '{}' has no 'body' block", identifier);
34
- }
35
- } else {
36
- eprintln!("❌ Group '{}' not found or not a map", identifier);
37
- }
38
- }
39
-
40
- Value::Map(map) => {
41
- if let Some(Value::Block(block)) = map.get("body") {
42
- let (eng, _, end_time) = execute_audio_block(
43
- audio_engine,
44
- variable_table,
45
- block.clone(),
46
- base_bpm,
47
- base_duration,
48
- max_end_time,
49
- cursor_time
50
- );
51
-
52
- return (eng, max_end_time.max(end_time), end_time);
53
- } else {
54
- eprintln!("❌ Call map has no 'body' block");
55
- }
56
- }
57
-
58
- other => {
59
- eprintln!("❌ Invalid call statement: expected identifier or map, found {:?}", other);
60
- }
61
- }
62
-
63
- (audio_engine, max_end_time, cursor_time)
64
- }
@@ -1,19 +0,0 @@
1
- use crate::core::{
2
- audio::engine::AudioEngine,
3
- parser::statement::{ Statement, StatementKind },
4
- shared::value::Value,
5
- store::variable::VariableTable,
6
- };
7
-
8
- pub fn interprete_let_statement(
9
- stmt: &Statement,
10
- variable_table: &mut VariableTable
11
- ) -> Option<VariableTable> {
12
- if let StatementKind::Let { name } = &stmt.kind {
13
- variable_table.set(name.to_string(), stmt.value.clone());
14
-
15
- return Some(variable_table.clone())
16
- }
17
-
18
- None
19
- }
@@ -1,18 +0,0 @@
1
- use crate::core::{
2
- parser::statement::{ Statement, StatementKind },
3
- shared::value::Value,
4
- store::variable::VariableTable,
5
- };
6
-
7
- pub fn interprete_load_statement(
8
- stmt: &Statement,
9
- variable_table: &mut VariableTable
10
- ) -> Option<VariableTable> {
11
- if let StatementKind::Load { source, alias } = &stmt.kind {
12
- variable_table.set(alias.to_string(), Value::String(source.clone()));
13
-
14
- return Some(variable_table.clone());
15
- }
16
-
17
- None
18
- }