@devaloop/devalang 0.0.1-alpha.8 → 0.0.1-beta.1

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 (277) hide show
  1. package/.cargo/config.toml +2 -0
  2. package/.devalang +10 -4
  3. package/.github/workflows/ci.yml +103 -0
  4. package/Cargo.toml +80 -48
  5. package/README.md +135 -158
  6. package/docs/CHANGELOG.md +413 -1
  7. package/docs/CONTRIBUTING.md +101 -0
  8. package/docs/ROADMAP.md +10 -7
  9. package/docs/TODO.md +21 -9
  10. package/examples/automation.deva +42 -0
  11. package/examples/bank.deva +7 -0
  12. package/examples/condition.deva +8 -12
  13. package/examples/duration.deva +9 -0
  14. package/examples/events.deva +12 -0
  15. package/examples/function.deva +15 -0
  16. package/examples/group.deva +3 -3
  17. package/examples/index.deva +57 -10
  18. package/examples/loop.deva +7 -12
  19. package/examples/pattern.deva +8 -0
  20. package/examples/plugin.deva +16 -0
  21. package/examples/synth.deva +14 -0
  22. package/examples/variables.deva +2 -2
  23. package/out-tsc/bin/index.d.ts +2 -0
  24. package/out-tsc/bin/index.js +51 -7
  25. package/out-tsc/core/functions/index.d.ts +37 -0
  26. package/out-tsc/core/functions/index.js +76 -0
  27. package/out-tsc/core/index.d.ts +6 -0
  28. package/out-tsc/core/index.js +22 -0
  29. package/out-tsc/core/types/index.d.ts +4 -0
  30. package/out-tsc/core/types/index.js +20 -0
  31. package/out-tsc/core/types/plugin.d.ts +18 -0
  32. package/out-tsc/core/types/plugin.js +2 -0
  33. package/out-tsc/core/types/result.d.ts +27 -0
  34. package/out-tsc/core/types/result.js +2 -0
  35. package/out-tsc/core/types/statement.d.ts +106 -0
  36. package/out-tsc/core/types/statement.js +2 -0
  37. package/out-tsc/core/types/value.d.ts +43 -0
  38. package/out-tsc/core/types/value.js +2 -0
  39. package/out-tsc/index.d.ts +7 -0
  40. package/out-tsc/index.js +42 -1
  41. package/out-tsc/pkg/devalang_core.d.ts +13 -0
  42. package/out-tsc/pkg/devalang_core.js +50 -0
  43. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +33 -0
  44. package/out-tsc/scripts/copy-wasm-dts.d.ts +1 -0
  45. package/out-tsc/scripts/copy-wasm-dts.js +73 -0
  46. package/out-tsc/scripts/postinstall.d.ts +1 -0
  47. package/out-tsc/scripts/postinstall.js +83 -0
  48. package/out-tsc/scripts/version/bump.d.ts +1 -0
  49. package/out-tsc/scripts/version/fetch.d.ts +1 -0
  50. package/out-tsc/scripts/version/fetch.js +1 -5
  51. package/out-tsc/scripts/version/index.d.ts +1 -0
  52. package/out-tsc/scripts/version/sync.d.ts +1 -0
  53. package/package.json +28 -7
  54. package/project-version.json +4 -4
  55. package/rust/cli/bank/api.rs +122 -0
  56. package/rust/cli/bank/commands.rs +275 -0
  57. package/rust/cli/bank/mod.rs +29 -0
  58. package/rust/cli/build/commands.rs +103 -0
  59. package/rust/cli/build/mod.rs +2 -0
  60. package/rust/cli/build/process.rs +146 -0
  61. package/rust/cli/check/mod.rs +208 -0
  62. package/rust/cli/discover/commands.rs +253 -0
  63. package/rust/cli/discover/config.rs +111 -0
  64. package/rust/cli/discover/fs.rs +19 -0
  65. package/rust/cli/discover/install.rs +103 -0
  66. package/rust/cli/discover/metadata.rs +48 -0
  67. package/rust/cli/discover/mod.rs +5 -0
  68. package/rust/cli/{init.rs → init/commands.rs} +32 -23
  69. package/rust/cli/init/mod.rs +1 -0
  70. package/rust/cli/install/addon.rs +118 -0
  71. package/rust/cli/install/bank.rs +53 -0
  72. package/rust/cli/install/commands.rs +35 -0
  73. package/rust/cli/install/mod.rs +4 -0
  74. package/rust/cli/install/plugin.rs +61 -0
  75. package/rust/cli/login/commands.rs +124 -0
  76. package/rust/cli/login/mod.rs +1 -0
  77. package/rust/cli/mod.rs +12 -205
  78. package/rust/cli/parser.rs +314 -0
  79. package/rust/cli/play/commands.rs +324 -0
  80. package/rust/cli/play/io.rs +17 -0
  81. package/rust/cli/play/mod.rs +5 -0
  82. package/rust/cli/play/process.rs +150 -0
  83. package/rust/cli/play/realtime.rs +91 -0
  84. package/rust/cli/play/utils.rs +23 -0
  85. package/rust/cli/telemetry/commands.rs +22 -0
  86. package/rust/cli/telemetry/event_creator.rs +80 -0
  87. package/rust/cli/telemetry/mod.rs +3 -0
  88. package/rust/cli/telemetry/send.rs +51 -0
  89. package/rust/cli/{template.rs → template/commands.rs} +69 -57
  90. package/rust/cli/template/mod.rs +1 -0
  91. package/rust/cli/update/commands.rs +6 -0
  92. package/rust/cli/update/mod.rs +1 -0
  93. package/rust/config/driver.rs +103 -0
  94. package/rust/config/mod.rs +3 -16
  95. package/rust/config/ops.rs +26 -0
  96. package/rust/config/settings.rs +101 -0
  97. package/rust/core/audio/engine/helpers.rs +170 -0
  98. package/rust/core/audio/engine/mod.rs +7 -0
  99. package/rust/core/audio/engine/sample.rs +366 -0
  100. package/rust/core/audio/engine/synth.rs +325 -0
  101. package/rust/core/audio/evaluator.rs +310 -31
  102. package/rust/core/audio/interpreter/arrow_call.rs +311 -0
  103. package/rust/core/audio/interpreter/automate.rs +18 -0
  104. package/rust/core/audio/interpreter/call.rs +294 -42
  105. package/rust/core/audio/interpreter/condition.rs +71 -65
  106. package/rust/core/audio/interpreter/driver.rs +542 -204
  107. package/rust/core/audio/interpreter/function.rs +26 -0
  108. package/rust/core/audio/interpreter/let_.rs +38 -19
  109. package/rust/core/audio/interpreter/load.rs +19 -18
  110. package/rust/core/audio/interpreter/loop_.rs +114 -59
  111. package/rust/core/audio/interpreter/mod.rs +14 -11
  112. package/rust/core/audio/interpreter/sleep.rs +28 -36
  113. package/rust/core/audio/interpreter/spawn.rs +252 -65
  114. package/rust/core/audio/interpreter/tempo.rs +40 -16
  115. package/rust/core/audio/interpreter/trigger.rs +239 -69
  116. package/rust/core/audio/loader/mod.rs +1 -1
  117. package/rust/core/audio/loader/trigger.rs +97 -52
  118. package/rust/core/audio/mod.rs +7 -6
  119. package/rust/core/audio/player.rs +70 -54
  120. package/rust/core/audio/renderer.rs +54 -57
  121. package/rust/core/audio/special/easing.rs +189 -0
  122. package/rust/core/audio/special/env.rs +45 -0
  123. package/rust/core/audio/special/math.rs +134 -0
  124. package/rust/core/audio/special/mod.rs +9 -0
  125. package/rust/core/audio/special/modulator.rs +143 -0
  126. package/rust/core/builder/mod.rs +86 -80
  127. package/rust/core/debugger/lexer.rs +27 -27
  128. package/rust/core/debugger/mod.rs +30 -21
  129. package/rust/core/debugger/module.rs +55 -0
  130. package/rust/core/debugger/preprocessor.rs +27 -27
  131. package/rust/core/debugger/store.rs +40 -25
  132. package/rust/core/error/mod.rs +269 -60
  133. package/rust/core/lexer/driver.rs +61 -0
  134. package/rust/core/lexer/handler/arrow.rs +82 -0
  135. package/rust/core/lexer/handler/at.rs +21 -21
  136. package/rust/core/lexer/handler/brace.rs +41 -41
  137. package/rust/core/lexer/handler/colon.rs +21 -21
  138. package/rust/core/lexer/handler/comment.rs +30 -30
  139. package/rust/core/lexer/handler/dot.rs +21 -21
  140. package/rust/core/lexer/handler/driver.rs +337 -215
  141. package/rust/core/lexer/handler/identifier.rs +47 -40
  142. package/rust/core/lexer/handler/indent.rs +66 -52
  143. package/rust/core/lexer/handler/mod.rs +15 -13
  144. package/rust/core/lexer/handler/newline.rs +23 -23
  145. package/rust/core/lexer/handler/number.rs +31 -31
  146. package/rust/core/lexer/handler/operator.rs +46 -44
  147. package/rust/core/lexer/handler/parenthesis.rs +41 -0
  148. package/rust/core/lexer/handler/slash.rs +21 -0
  149. package/rust/core/lexer/handler/string.rs +63 -63
  150. package/rust/core/lexer/mod.rs +3 -30
  151. package/rust/core/lexer/token.rs +21 -12
  152. package/rust/core/mod.rs +10 -10
  153. package/rust/core/parser/driver.rs +584 -312
  154. package/rust/core/parser/handler/arrow_call.rs +253 -0
  155. package/rust/core/parser/handler/at.rs +279 -162
  156. package/rust/core/parser/handler/bank.rs +104 -41
  157. package/rust/core/parser/handler/condition.rs +83 -74
  158. package/rust/core/parser/handler/dot.rs +148 -112
  159. package/rust/core/parser/handler/identifier/automate.rs +254 -0
  160. package/rust/core/parser/handler/identifier/call.rs +91 -0
  161. package/rust/core/parser/handler/identifier/emit.rs +70 -0
  162. package/rust/core/parser/handler/identifier/function.rs +113 -0
  163. package/rust/core/parser/handler/identifier/group.rs +89 -0
  164. package/rust/core/parser/handler/identifier/let_.rs +173 -0
  165. package/rust/core/parser/handler/identifier/mod.rs +55 -0
  166. package/rust/core/parser/handler/identifier/on.rs +107 -0
  167. package/rust/core/parser/handler/identifier/print.rs +49 -0
  168. package/rust/core/parser/handler/identifier/sleep.rs +43 -0
  169. package/rust/core/parser/handler/identifier/spawn.rs +91 -0
  170. package/rust/core/parser/handler/identifier/synth.rs +135 -0
  171. package/rust/core/parser/handler/loop_.rs +194 -66
  172. package/rust/core/parser/handler/mod.rs +9 -7
  173. package/rust/core/parser/handler/pattern.rs +74 -0
  174. package/rust/core/parser/handler/tempo.rs +57 -47
  175. package/rust/core/parser/mod.rs +3 -4
  176. package/rust/core/parser/statement.rs +11 -88
  177. package/rust/core/plugin/loader.rs +137 -0
  178. package/rust/core/plugin/mod.rs +2 -0
  179. package/rust/core/plugin/runner.rs +347 -0
  180. package/rust/core/preprocessor/loader.rs +637 -179
  181. package/rust/core/preprocessor/mod.rs +4 -4
  182. package/rust/core/preprocessor/module.rs +60 -53
  183. package/rust/core/preprocessor/processor.rs +114 -67
  184. package/rust/core/preprocessor/resolver/bank.rs +49 -47
  185. package/rust/core/preprocessor/resolver/call.rs +124 -53
  186. package/rust/core/preprocessor/resolver/condition.rs +95 -66
  187. package/rust/core/preprocessor/resolver/driver.rs +324 -182
  188. package/rust/core/preprocessor/resolver/function.rs +69 -0
  189. package/rust/core/preprocessor/resolver/group.rs +94 -118
  190. package/rust/core/preprocessor/resolver/let_.rs +32 -0
  191. package/rust/core/preprocessor/resolver/loop_.rs +318 -145
  192. package/rust/core/preprocessor/resolver/mod.rs +16 -10
  193. package/rust/core/preprocessor/resolver/pattern.rs +83 -0
  194. package/rust/core/preprocessor/resolver/spawn.rs +99 -53
  195. package/rust/core/preprocessor/resolver/synth.rs +54 -0
  196. package/rust/core/preprocessor/resolver/tempo.rs +48 -49
  197. package/rust/core/preprocessor/resolver/trigger.rs +116 -111
  198. package/rust/core/preprocessor/resolver/value.rs +176 -0
  199. package/rust/core/store/export.rs +28 -28
  200. package/rust/core/store/function.rs +40 -0
  201. package/rust/core/store/global.rs +61 -39
  202. package/rust/core/store/import.rs +28 -28
  203. package/rust/core/store/mod.rs +5 -4
  204. package/rust/core/store/variable.rs +51 -28
  205. package/rust/core/utils/mod.rs +1 -2
  206. package/rust/core/utils/path.rs +37 -46
  207. package/rust/lib.rs +308 -117
  208. package/rust/main.rs +364 -65
  209. package/rust/types/Cargo.toml +11 -0
  210. package/rust/types/src/addons.rs +55 -0
  211. package/rust/types/src/ast.rs +202 -0
  212. package/rust/types/src/config.rs +74 -0
  213. package/rust/types/src/lib.rs +12 -0
  214. package/rust/types/src/telemetry.rs +85 -0
  215. package/rust/utils/Cargo.toml +26 -0
  216. package/rust/utils/src/error.rs +186 -0
  217. package/rust/utils/src/file.rs +94 -0
  218. package/rust/utils/src/first_usage.rs +97 -0
  219. package/rust/utils/{mod.rs → src/lib.rs} +9 -6
  220. package/rust/utils/{logger.rs → src/logger.rs} +200 -123
  221. package/rust/utils/src/path.rs +88 -0
  222. package/rust/utils/src/signature.rs +41 -0
  223. package/rust/utils/{spinner.rs → src/spinner.rs} +20 -21
  224. package/rust/utils/src/version.rs +27 -0
  225. package/rust/utils/{watcher.rs → src/watcher.rs} +46 -33
  226. package/rust/web/api.rs +5 -0
  227. package/rust/web/cdn.rs +34 -0
  228. package/rust/web/mod.rs +3 -0
  229. package/rust/web/sso.rs +5 -0
  230. package/templates/minimal/README.md +143 -127
  231. package/templates/welcome/README.md +143 -127
  232. package/templates/welcome/src/index.deva +56 -8
  233. package/templates/welcome/src/variables.deva +2 -4
  234. package/tests/integration.rs +21 -0
  235. package/tests/rust/cli_check_build.rs +21 -0
  236. package/tests/rust/cli_help.rs +12 -0
  237. package/tests/rust/cli_template_list.rs +10 -0
  238. package/tests/rust/cli_version.rs +11 -0
  239. package/tests/typescript/index.spec.ts +136 -0
  240. package/tests/typescript/playhead.spec.ts +36 -0
  241. package/tests/typescript/render_e2e.spec.ts +77 -0
  242. package/tsconfig.json +12 -10
  243. package/typescript/bin/index.ts +19 -5
  244. package/typescript/core/functions/index.ts +83 -0
  245. package/typescript/core/index.ts +6 -0
  246. package/typescript/core/types/index.ts +4 -0
  247. package/typescript/core/types/plugin.ts +19 -0
  248. package/typescript/core/types/result.ts +29 -0
  249. package/typescript/core/types/statement.ts +47 -0
  250. package/typescript/core/types/value.ts +29 -0
  251. package/typescript/index.ts +8 -1
  252. package/typescript/pkg/devalang_core.d.ts +4 -0
  253. package/typescript/pkg/devalang_core.ts +49 -0
  254. package/typescript/scripts/copy-wasm-dts.ts +41 -0
  255. package/typescript/scripts/postinstall.ts +85 -0
  256. package/typescript/scripts/version/bump.ts +0 -1
  257. package/typescript/scripts/version/fetch.ts +1 -6
  258. package/typescript/scripts/version/index.ts +0 -1
  259. package/docs/COMMANDS.md +0 -85
  260. package/docs/CONFIG.md +0 -30
  261. package/docs/SYNTAX.md +0 -210
  262. package/out-tsc/bin/devalang.exe +0 -0
  263. package/out-tsc/scripts/postbuild.js +0 -11
  264. package/rust/cli/build.rs +0 -137
  265. package/rust/cli/check.rs +0 -117
  266. package/rust/cli/play.rs +0 -193
  267. package/rust/config/loader.rs +0 -13
  268. package/rust/core/audio/engine.rs +0 -126
  269. package/rust/core/parser/handler/identifier.rs +0 -262
  270. package/rust/core/shared/duration.rs +0 -8
  271. package/rust/core/shared/mod.rs +0 -2
  272. package/rust/core/shared/value.rs +0 -18
  273. package/rust/core/utils/validation.rs +0 -35
  274. package/rust/utils/file.rs +0 -35
  275. package/rust/utils/signature.rs +0 -17
  276. package/rust/utils/version.rs +0 -15
  277. 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,126 +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::{ store::variable::VariableTable, utils::path::normalize_path };
6
-
7
- const SAMPLE_RATE: u32 = 44100;
8
- const CHANNELS: u16 = 2;
9
-
10
- #[derive(Debug, Clone, PartialEq)]
11
- pub struct AudioEngine {
12
- pub volume: f32,
13
- pub variables: VariableTable,
14
- pub buffer: Vec<i16>,
15
- }
16
-
17
- impl AudioEngine {
18
- pub fn new() -> Self {
19
- AudioEngine {
20
- volume: 1.0,
21
- buffer: vec![],
22
- variables: VariableTable::new(),
23
- }
24
- }
25
-
26
- pub fn mix(&mut self, other: &AudioEngine) {
27
- let max_len = self.buffer.len().max(other.buffer.len());
28
- self.buffer.resize(max_len, 0);
29
-
30
- for (i, &sample) in other.buffer.iter().enumerate() {
31
- self.buffer[i] = self.buffer[i].saturating_add(sample);
32
- }
33
- }
34
-
35
- pub fn set_duration(&mut self, duration_secs: f32) {
36
- let mut total_samples = (duration_secs * (SAMPLE_RATE as f32) * (CHANNELS as f32)) as usize;
37
-
38
- if total_samples % (CHANNELS as usize) != 0 {
39
- total_samples += 1;
40
- }
41
-
42
- self.buffer.resize(total_samples, 0);
43
- }
44
-
45
- pub fn set_variables(&mut self, variables: VariableTable) {
46
- self.variables = variables;
47
- }
48
-
49
- pub fn generate_wav_file(&mut self, output_dir: &String) -> Result<(), String> {
50
- if self.buffer.len() % (CHANNELS as usize) != 0 {
51
- self.buffer.push(0);
52
- println!("Completed buffer to respect stereo format.");
53
- }
54
-
55
- let spec = WavSpec {
56
- channels: CHANNELS,
57
- sample_rate: SAMPLE_RATE,
58
- bits_per_sample: 16,
59
- sample_format: SampleFormat::Int,
60
- };
61
-
62
- let mut writer = WavWriter::create(output_dir, spec).map_err(|e|
63
- format!("Error creating WAV file: {}", e)
64
- )?;
65
-
66
- for sample in &self.buffer {
67
- writer.write_sample(*sample).map_err(|e| format!("Error writing sample: {:?}", e))?;
68
- }
69
-
70
- writer.finalize().map_err(|e| format!("Error finalizing WAV: {:?}", e))?;
71
-
72
- Ok(())
73
- }
74
-
75
- pub fn insert(
76
- &mut self,
77
- filepath: &str,
78
- time_secs: f32,
79
- dur_sec: f32,
80
- effects: Option<HashMap<String, f32>>
81
- ) {
82
- let normalized_filepath = normalize_path(filepath);
83
-
84
- let file = BufReader::new(
85
- File::open(normalized_filepath).expect("Failed to open audio file")
86
- );
87
- let decoder = Decoder::new(file).expect("Failed to decode audio file");
88
-
89
- // Mono or stereo reading possible here, we will duplicate in L/R
90
- let max_mono_samples = (dur_sec * (SAMPLE_RATE as f32)) as usize;
91
- let samples: Vec<i16> = decoder.convert_samples().take(max_mono_samples).collect();
92
-
93
- if samples.is_empty() {
94
- eprintln!("No samples found in the audio file: {}", filepath);
95
- return;
96
- }
97
-
98
- // TODO Apply effects here if needed
99
- let offset = (time_secs * (SAMPLE_RATE as f32) * (CHANNELS as f32)) as usize;
100
- let required_len = offset + samples.len() * (CHANNELS as usize);
101
- let padded_required_len = if required_len % 2 == 1 {
102
- required_len + 1
103
- } else {
104
- required_len
105
- };
106
-
107
- self.buffer.resize(padded_required_len, 0);
108
- self.pad_samples(&samples, time_secs);
109
- }
110
-
111
- fn pad_samples(&mut self, samples: &[i16], time_secs: f32) {
112
- let offset = (time_secs * (SAMPLE_RATE as f32) * (CHANNELS as f32)) as usize;
113
-
114
- for (i, &sample) in samples.iter().enumerate() {
115
- let adjusted_sample = ((sample as f32) * self.volume).round() as i16;
116
-
117
- let left_pos = offset + i * 2;
118
- let right_pos = left_pos + 1;
119
-
120
- if right_pos < self.buffer.len() {
121
- self.buffer[left_pos] = self.buffer[left_pos].saturating_add(adjusted_sample); // gauche
122
- self.buffer[right_pos] = self.buffer[right_pos].saturating_add(adjusted_sample); // droite
123
- }
124
- }
125
- }
126
- }
@@ -1,262 +0,0 @@
1
- use crate::core::{
2
- lexer::token::{ Token, TokenKind },
3
- parser::{ statement::{ Statement, StatementKind }, driver::Parser },
4
- shared::value::Value,
5
- store::global::GlobalStore,
6
- };
7
- use std::collections::HashMap;
8
-
9
- pub fn parse_identifier_token(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
10
- let Some(current_token) = parser.peek_clone() else {
11
- return Statement::unknown();
12
- };
13
-
14
- if current_token.lexeme == "let" {
15
- parser.advance(); // consume "let"
16
-
17
- let identifier = if let Some(token) = parser.peek_clone() {
18
- if token.kind == TokenKind::Identifier {
19
- parser.advance();
20
- token.lexeme.clone()
21
- } else {
22
- return Statement::error(token, "Expected identifier after 'let'".to_string());
23
- }
24
- } else {
25
- return Statement::error(current_token, "Expected identifier after 'let'".to_string());
26
- };
27
-
28
- if !parser.match_token(TokenKind::Equals) {
29
- return Statement::error(current_token, "Expected '=' after identifier".to_string());
30
- }
31
-
32
- let value = match parser.peek_clone() {
33
- Some(token) if token.kind == TokenKind::Identifier => {
34
- parser.advance();
35
- Value::Identifier(token.lexeme.clone())
36
- }
37
- Some(token) if token.kind == TokenKind::String => {
38
- parser.advance();
39
- Value::String(token.lexeme.clone())
40
- }
41
- Some(token) if token.kind == TokenKind::Number => {
42
- parser.advance();
43
- Value::Number(token.lexeme.parse().unwrap_or(0.0))
44
- }
45
- Some(token) if token.kind == TokenKind::LBrace => {
46
- parser.advance(); // consume '{'
47
- let mut map = HashMap::new();
48
-
49
- while let Some(key_token) = parser.peek_clone() {
50
- if key_token.kind == TokenKind::RBrace {
51
- parser.advance(); // consume '}'
52
- break;
53
- }
54
-
55
- if key_token.kind != TokenKind::Identifier {
56
- return Statement::error(
57
- token,
58
- "Expected key identifier in map".to_string()
59
- );
60
- }
61
- parser.advance();
62
- let key = key_token.lexeme.clone();
63
-
64
- if !parser.match_token(TokenKind::Colon) {
65
- let message = format!("Expected ':' after key '{}'", key);
66
- return Statement::error(token, message);
67
- }
68
-
69
- let val = match parser.peek_clone() {
70
- Some(t) if t.kind == TokenKind::Number => {
71
- parser.advance();
72
- Value::Number(t.lexeme.parse().unwrap_or(0.0))
73
- }
74
- Some(t) if t.kind == TokenKind::String => {
75
- parser.advance();
76
- Value::String(t.lexeme.clone())
77
- }
78
- Some(t) if t.kind == TokenKind::Identifier => {
79
- parser.advance();
80
- Value::Identifier(t.lexeme.clone())
81
- }
82
- _ => { Value::Null }
83
- };
84
-
85
- if val == Value::Null {
86
- let message = format!("Invalid value for key '{}'", key);
87
- return Statement::error(token, message);
88
- }
89
-
90
- map.insert(key, val);
91
-
92
- if let Some(t) = parser.peek() {
93
- if t.kind == TokenKind::Comma {
94
- parser.advance(); // skip comma
95
- }
96
- }
97
- }
98
-
99
- Value::Map(map)
100
- }
101
- other => {
102
- let message = format!("Unexpected value token in let: {:?}", other);
103
- return Statement::error(current_token, message);
104
- }
105
- };
106
-
107
- return Statement {
108
- kind: StatementKind::Let { name: identifier },
109
- value,
110
- indent: current_token.indent,
111
- line: current_token.line,
112
- column: current_token.column,
113
- };
114
- } else if current_token.lexeme == "group" {
115
- parser.advance(); // consume "group"
116
-
117
- let Some(identifier_token) = parser.peek_clone() else {
118
- return Statement::error(current_token, "Expected identifier after 'group'".to_string());
119
- };
120
-
121
- if
122
- identifier_token.kind != TokenKind::Identifier &&
123
- identifier_token.kind != TokenKind::String
124
- {
125
- return Statement::error(identifier_token, "Expected valid identifier".to_string());
126
- }
127
-
128
- parser.advance(); // consume identifier
129
-
130
- let Some(colon_token) = parser.peek_clone() else {
131
- return Statement::error(
132
- identifier_token,
133
- "Expected ':' after group identifier".to_string()
134
- );
135
- };
136
-
137
- if colon_token.kind != TokenKind::Colon {
138
- return Statement::error(
139
- colon_token.clone(),
140
- "Expected ':' after group identifier".to_string()
141
- );
142
- }
143
-
144
- parser.advance(); // consume ':'
145
-
146
- let base_indent = current_token.indent;
147
-
148
- // Clone without consuming tokens
149
- let mut index = parser.token_index;
150
- let mut tokens_inside_group = Vec::new();
151
-
152
- while index < parser.tokens.len() {
153
- let token = parser.tokens[index].clone();
154
-
155
- if token.indent <= base_indent && token.kind != TokenKind::Newline {
156
- break;
157
- }
158
-
159
- tokens_inside_group.push(token);
160
- index += 1;
161
- }
162
-
163
- // Advance index once to skip the processed tokens
164
- parser.token_index = index;
165
-
166
- let body = parser.parse_block(tokens_inside_group, global_store);
167
-
168
- let mut value_map = HashMap::new();
169
- value_map.insert("identifier".to_string(), Value::String(identifier_token.lexeme.clone()));
170
- value_map.insert("body".to_string(), Value::Block(body));
171
-
172
- return Statement {
173
- kind: StatementKind::Group,
174
- value: Value::Map(value_map),
175
- indent: current_token.indent,
176
- line: current_token.line,
177
- column: current_token.column,
178
- };
179
- } else if current_token.lexeme == "call" {
180
- parser.advance(); // consume "call"
181
-
182
- let identifier = if let Some(token) = parser.peek_clone() {
183
- if token.kind == TokenKind::Identifier {
184
- parser.advance();
185
- token.lexeme.clone()
186
- } else {
187
- return Statement::error(token, "Expected identifier after 'call'".to_string());
188
- }
189
- } else {
190
- return Statement::error(current_token, "Expected identifier after 'call'".to_string());
191
- };
192
-
193
- return Statement {
194
- kind: StatementKind::Call,
195
- value: Value::String(identifier),
196
- indent: current_token.indent,
197
- line: current_token.line,
198
- column: current_token.column,
199
- };
200
- } else if current_token.lexeme == "spawn" {
201
- parser.advance(); // consume "spawn"
202
-
203
- let identifier = if let Some(token) = parser.peek_clone() {
204
- if token.kind == TokenKind::Identifier {
205
- parser.advance();
206
- token.lexeme.clone()
207
- } else {
208
- return Statement::error(token, "Expected identifier after 'spawn'".to_string());
209
- }
210
- } else {
211
- return Statement::error(current_token, "Expected identifier after 'spawn'".to_string());
212
- };
213
-
214
- return Statement {
215
- kind: StatementKind::Spawn,
216
- value: Value::String(identifier),
217
- indent: current_token.indent,
218
- line: current_token.line,
219
- column: current_token.column,
220
- };
221
- } else if current_token.lexeme == "sleep" {
222
- parser.advance(); // consume "sleep"
223
-
224
- let duration = if let Some(token) = parser.peek_clone() {
225
- if token.kind == TokenKind::Number {
226
- parser.advance();
227
- token.lexeme.parse().unwrap_or(0.0)
228
- } else {
229
- return Statement::error(token, "Expected number after 'sleep'".to_string());
230
- }
231
- } else {
232
- return Statement::error(current_token, "Expected number after 'sleep'".to_string());
233
- };
234
-
235
- return Statement {
236
- kind: StatementKind::Sleep,
237
- value: Value::Number(duration),
238
- indent: current_token.indent,
239
- line: current_token.line,
240
- column: current_token.column,
241
- };
242
- } else {
243
- // Unknown identifier handling
244
- Statement {
245
- kind: StatementKind::Unknown,
246
- value: Value::String(current_token.lexeme.clone()),
247
- indent: current_token.indent,
248
- line: current_token.line,
249
- column: current_token.column,
250
- };
251
- }
252
-
253
- parser.advance(); // unknown identifier fallback
254
-
255
- Statement {
256
- kind: StatementKind::Unknown,
257
- value: Value::String(current_token.lexeme.clone()),
258
- indent: current_token.indent,
259
- line: current_token.line,
260
- column: current_token.column,
261
- }
262
- }
@@ -1,8 +0,0 @@
1
- use serde::{ Deserialize, Serialize };
2
-
3
- #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
4
- pub enum Duration {
5
- Number(f32),
6
- Identifier(String),
7
- Auto,
8
- }
@@ -1,2 +0,0 @@
1
- pub mod value;
2
- pub mod duration;
@@ -1,18 +0,0 @@
1
- use std::collections::HashMap;
2
- use serde::{ Deserialize, Serialize };
3
-
4
- use crate::core::parser::statement::Statement;
5
-
6
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
7
- pub enum Value {
8
- Boolean(bool),
9
- Number(f32),
10
- Identifier(String),
11
- String(String),
12
- Array(Vec<Value>),
13
- Map(HashMap<String, Value>),
14
- Block(Vec<Statement>),
15
- Sample(String),
16
- Unknown,
17
- Null,
18
- }
@@ -1,35 +0,0 @@
1
- use crate::core::{ preprocessor::module::Module, shared::value::Value, store::global::GlobalStore };
2
-
3
- pub fn is_valid_entity(entity: &str, module: &Module, global_store: &GlobalStore) -> bool {
4
- let built_ins = ["kick", "snare", "hat", "clap"];
5
-
6
- if built_ins.contains(&entity) {
7
- return true;
8
- }
9
-
10
- if let Some(val) = module.variable_table.get(entity) {
11
- match val {
12
- Value::Sample(_) => true,
13
- _ => false,
14
- }
15
- } else {
16
- false
17
- }
18
- }
19
-
20
- pub fn is_valid_identifier(ident: &str, module: &Module) -> bool {
21
- let built_ins = ["auto"];
22
-
23
- if built_ins.contains(&ident) {
24
- return true;
25
- }
26
-
27
- if let Some(val) = module.variable_table.get(ident) {
28
- match val {
29
- Value::Identifier(_) => true,
30
- _ => false,
31
- }
32
- } else {
33
- false
34
- }
35
- }
@@ -1,35 +0,0 @@
1
- use std::{ fs::{ self }, path::Path };
2
- use include_dir::{ Dir, DirEntry };
3
-
4
- pub fn copy_dir_recursive(dir: &Dir, target_root: &Path, base_path: &Path) {
5
- for entry in dir.entries() {
6
- match entry {
7
- DirEntry::Dir(subdir) => {
8
- copy_dir_recursive(subdir, target_root, base_path);
9
- }
10
- DirEntry::File(file) => {
11
- let rel_path = file.path().strip_prefix(base_path).unwrap();
12
- let dest_path = target_root.join(rel_path);
13
-
14
- if let Some(parent) = dest_path.parent() {
15
- fs::create_dir_all(parent).unwrap();
16
- }
17
-
18
- fs::write(&dest_path, file.contents()).expect("Error writing file");
19
- }
20
- }
21
- }
22
- }
23
-
24
- pub fn format_file_size(bytes: u64) -> String {
25
- const KB: u64 = 1024;
26
- const MB: u64 = 1024 * 1024;
27
-
28
- if bytes >= MB {
29
- format!("{:.2} Mb", (bytes as f64) / (MB as f64))
30
- } else if bytes >= KB {
31
- format!("{:.2} Kb", (bytes as f64) / (KB as f64))
32
- } else {
33
- format!("{} bytes", bytes)
34
- }
35
- }