@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
@@ -0,0 +1,45 @@
1
+ use devalang_types::Value;
2
+
3
+ use devalang_types::VariableTable;
4
+ use std::sync::OnceLock;
5
+ use std::time::{SystemTime, UNIX_EPOCH};
6
+
7
+ static SESSION_SEED: OnceLock<f32> = OnceLock::new();
8
+
9
+ pub fn get_session_seed() -> f32 {
10
+ *SESSION_SEED.get_or_init(|| {
11
+ let now = SystemTime::now()
12
+ .duration_since(UNIX_EPOCH)
13
+ .unwrap_or_default();
14
+ // Build a stable 0..1 seed from nanos
15
+ let nanos = now.subsec_nanos();
16
+ ((nanos as f32) / 1_000_000_000.0).clamp(0.0, 1.0)
17
+ })
18
+ }
19
+
20
+ // Resolve special environment variables like $env.bpm, $env.beat, $env.position
21
+ // For now, $env.position is treated as an alias of beat.
22
+ pub fn resolve_env_atom(atom: &str, bpm: f32, beat: f32) -> Option<f32> {
23
+ match atom {
24
+ "$env.bpm" => Some(bpm),
25
+ "$env.beat" => Some(beat),
26
+ "$env.position" => Some(beat),
27
+ // Optional seed for deterministic randomness
28
+ "$env.seed" => Some(get_session_seed()),
29
+ _ => None,
30
+ }
31
+ }
32
+
33
+ // Utility: resolve an identifier or numeric literal to f32 using the variable table
34
+ pub fn resolve_atom_or_var(atom: &str, vars: &VariableTable, bpm: f32, beat: f32) -> Option<f32> {
35
+ if let Some(v) = resolve_env_atom(atom, bpm, beat) {
36
+ return Some(v);
37
+ }
38
+ if let Ok(n) = atom.parse::<f32>() {
39
+ return Some(n);
40
+ }
41
+ if let Some(Value::Number(n)) = vars.get(atom) {
42
+ return Some(*n);
43
+ }
44
+ None
45
+ }
@@ -0,0 +1,134 @@
1
+ use devalang_types::VariableTable;
2
+ use devalang_utils::logger::{LogLevel, Logger};
3
+
4
+ // Parse comma-separated arguments at top level (no nested parentheses split)
5
+ fn parse_top_level_args(s: &str) -> Vec<&str> {
6
+ let mut args = Vec::new();
7
+ let mut depth = 0i32;
8
+ let mut start = 0usize;
9
+ for (i, ch) in s.char_indices() {
10
+ match ch {
11
+ '(' => depth += 1,
12
+ ')' => depth -= 1,
13
+ ',' if depth == 0 => {
14
+ args.push(s[start..i].trim());
15
+ start = i + 1;
16
+ }
17
+ _ => {}
18
+ }
19
+ }
20
+ let last = s[start..].trim();
21
+ if !last.is_empty() {
22
+ args.push(last);
23
+ }
24
+ args
25
+ }
26
+
27
+ fn eval_math_func(func: &str, args: &[f32], fallback_seed: f32) -> Option<f32> {
28
+ match func {
29
+ "sin" => args.first().copied().map(f32::sin),
30
+ "cos" => args.first().copied().map(f32::cos),
31
+ "random" => {
32
+ // deterministic pseudo-random based on provided seed or a fallback session seed
33
+ let seed = args.first().copied().unwrap_or(fallback_seed);
34
+ let x = (seed * 12.9898).sin() * 43_758.547;
35
+ Some((x.fract() * 2.0 - 1.0).clamp(-1.0, 1.0))
36
+ }
37
+ "lerp" => {
38
+ if args.len() >= 3 {
39
+ Some(args[0] + (args[1] - args[0]) * args[2])
40
+ } else {
41
+ None
42
+ }
43
+ }
44
+ _ => None,
45
+ }
46
+ }
47
+
48
+ // Find and evaluate the first $math.<fn>(...) occurrence in the string, replacing it with a number.
49
+ // Supports multi-argument functions by splitting on top-level commas.
50
+ pub fn find_and_eval_first_math_call<EvalFn>(
51
+ s: &str,
52
+ eval: EvalFn,
53
+ vars: &VariableTable,
54
+ bpm: f32,
55
+ beat: f32,
56
+ ) -> Option<String>
57
+ where
58
+ EvalFn: Fn(&str, &VariableTable, f32, f32) -> Option<f32>,
59
+ {
60
+ let logger = Logger::new();
61
+
62
+ let start = s.find("$math.")?;
63
+ let open_rel = match s[start..].find('(') {
64
+ Some(i) => i,
65
+ None => {
66
+ logger.log_message(
67
+ LogLevel::Error,
68
+ &format!("Malformed $math call: missing '(' in '{}'", s),
69
+ );
70
+ return None;
71
+ }
72
+ };
73
+ let open = start + open_rel;
74
+ if open <= start + 6 {
75
+ logger.log_message(
76
+ LogLevel::Error,
77
+ &format!("Malformed $math call: missing function name in '{}'", s),
78
+ );
79
+ return None;
80
+ }
81
+ let func = &s[start + 6..open];
82
+
83
+ // Find matching close parenthesis, handling nesting
84
+ let mut depth: i32 = 0;
85
+ let mut close_abs: Option<usize> = None;
86
+ for (i, ch) in s[open..].char_indices() {
87
+ match ch {
88
+ '(' => depth += 1,
89
+ ')' => {
90
+ depth -= 1;
91
+ if depth == 0 {
92
+ close_abs = Some(open + i);
93
+ break;
94
+ }
95
+ }
96
+ _ => {}
97
+ }
98
+ }
99
+ let close = match close_abs {
100
+ Some(c) => c,
101
+ None => {
102
+ logger.log_message(
103
+ LogLevel::Error,
104
+ &format!("Malformed $math call: missing closing ')' in '{}'", s),
105
+ );
106
+ return None;
107
+ }
108
+ };
109
+
110
+ let inner = &s[open + 1..close];
111
+ let raw_args = parse_top_level_args(inner);
112
+ let mut args: Vec<f32> = Vec::with_capacity(raw_args.len());
113
+ for a in raw_args {
114
+ if let Some(v) = eval(a, vars, bpm, beat) {
115
+ args.push(v);
116
+ } else {
117
+ logger.log_message(
118
+ LogLevel::Error,
119
+ &format!("Failed to evaluate argument '{}' for $math.{}", a, func),
120
+ );
121
+ return None;
122
+ }
123
+ }
124
+
125
+ // If no explicit seed is provided, use $env.seed via fallback
126
+ let fallback_seed = eval("$env.seed", vars, bpm, beat).unwrap_or(0.0);
127
+ let result = eval_math_func(func, &args, fallback_seed)?;
128
+
129
+ let mut replaced = String::new();
130
+ replaced.push_str(&s[..start]);
131
+ replaced.push_str(&result.to_string());
132
+ replaced.push_str(&s[close + 1..]);
133
+ Some(replaced)
134
+ }
@@ -0,0 +1,9 @@
1
+ pub mod easing;
2
+ pub mod env;
3
+ pub mod math;
4
+ pub mod modulator;
5
+
6
+ pub use easing::find_and_eval_first_easing_call;
7
+ pub use env::resolve_env_atom;
8
+ pub use math::find_and_eval_first_math_call;
9
+ pub use modulator::find_and_eval_first_mod_call;
@@ -0,0 +1,143 @@
1
+ use devalang_types::VariableTable;
2
+
3
+ fn lfo_sine(rate_per_beat: f32, beat: f32) -> f32 {
4
+ // Output in [-1,1]
5
+ (2.0 * std::f32::consts::PI * rate_per_beat * beat).sin()
6
+ }
7
+
8
+ fn lfo_triangle(rate_per_beat: f32, beat: f32) -> f32 {
9
+ // Triangle in [-1,1]
10
+ let phase = (rate_per_beat * beat).fract();
11
+ // Map [0,1]->[-1,1] tri
12
+ 4.0 * (phase - 0.5).abs() - 1.0
13
+ }
14
+
15
+ fn adsr_envelope_value_t(attack: f32, decay: f32, sustain: f32, release: f32, t: f32) -> f32 {
16
+ let a = attack.max(0.0);
17
+ let d = decay.max(0.0);
18
+ let r = release.max(0.0);
19
+ let s = sustain.clamp(0.0, 1.0);
20
+
21
+ // Normalize phases so that the whole ADSR spans t in [0,1]
22
+ let total = (a + d + r).max(1e-6);
23
+ let ap = a / total;
24
+ let dp = d / total;
25
+ let rp = r / total;
26
+
27
+ if t < ap {
28
+ // attack (0->1)
29
+ if ap > 0.0 { t / ap } else { 1.0 }
30
+ } else if t < ap + dp {
31
+ // decay (1->sustain)
32
+ let u = (t - ap) / dp.max(1e-6);
33
+ 1.0 - (1.0 - s) * u
34
+ } else if t < 1.0 - rp {
35
+ // sustain
36
+ s
37
+ } else {
38
+ // release (sustain->0)
39
+ let u = (t - (1.0 - rp)) / rp.max(1e-6);
40
+ s * (1.0 - u)
41
+ }
42
+ }
43
+
44
+ fn eval_mod_func(func: &str, args: &[f32], beat: f32) -> Option<f32> {
45
+ match func {
46
+ "lfo.sine" => {
47
+ let rate = args.first().copied().unwrap_or(1.0);
48
+ Some(lfo_sine(rate, beat))
49
+ }
50
+ "lfo.tri" | "lfo.triangle" => {
51
+ let rate = args.first().copied().unwrap_or(1.0);
52
+ Some(lfo_triangle(rate, beat))
53
+ }
54
+ // ADSR envelope normalized over t in [0,1]
55
+ // $mod.envelope(attack, decay, sustain, release, t)
56
+ "envelope" | "mod.envelope" => {
57
+ if args.len() >= 5 {
58
+ Some(adsr_envelope_value_t(
59
+ args[0],
60
+ args[1],
61
+ args[2],
62
+ args[3],
63
+ args[4].clamp(0.0, 1.0),
64
+ ))
65
+ } else {
66
+ None
67
+ }
68
+ }
69
+ _ => None,
70
+ }
71
+ }
72
+
73
+ fn parse_top_level_args(s: &str) -> Vec<&str> {
74
+ let mut args = Vec::new();
75
+ let mut depth = 0i32;
76
+ let mut start = 0usize;
77
+ for (i, ch) in s.char_indices() {
78
+ match ch {
79
+ '(' => depth += 1,
80
+ ')' => depth -= 1,
81
+ ',' if depth == 0 => {
82
+ args.push(s[start..i].trim());
83
+ start = i + 1;
84
+ }
85
+ _ => {}
86
+ }
87
+ }
88
+ let last = s[start..].trim();
89
+ if !last.is_empty() {
90
+ args.push(last);
91
+ }
92
+ args
93
+ }
94
+
95
+ // Find and evaluate the first $mod.<fn>(...) occurrence in the string.
96
+ pub fn find_and_eval_first_mod_call<EvalFn>(
97
+ s: &str,
98
+ eval: EvalFn,
99
+ vars: &VariableTable,
100
+ bpm: f32,
101
+ beat: f32,
102
+ ) -> Option<String>
103
+ where
104
+ EvalFn: Fn(&str, &VariableTable, f32, f32) -> Option<f32>,
105
+ {
106
+ let start = s.find("$mod.")?;
107
+ let open_rel = s[start..].find('(')?;
108
+ let open = start + open_rel;
109
+ let func = &s[start + 5..open];
110
+
111
+ // matching close
112
+ let mut depth: i32 = 0;
113
+ let mut close_abs: Option<usize> = None;
114
+ for (i, ch) in s[open..].char_indices() {
115
+ match ch {
116
+ '(' => depth += 1,
117
+ ')' => {
118
+ depth -= 1;
119
+ if depth == 0 {
120
+ close_abs = Some(open + i);
121
+ break;
122
+ }
123
+ }
124
+ _ => {}
125
+ }
126
+ }
127
+ let close = close_abs?;
128
+
129
+ let inner = &s[open + 1..close];
130
+ let raw_args = parse_top_level_args(inner);
131
+ let mut args: Vec<f32> = Vec::with_capacity(raw_args.len());
132
+ for a in raw_args {
133
+ args.push(eval(a, vars, bpm, beat)?);
134
+ }
135
+
136
+ let result = eval_mod_func(func, &args, beat)?;
137
+
138
+ let mut replaced = String::new();
139
+ replaced.push_str(&s[..start]);
140
+ replaced.push_str(&result.to_string());
141
+ replaced.push_str(&s[close + 1..]);
142
+ Some(replaced)
143
+ }
@@ -1,80 +1,129 @@
1
- use crate::core::audio::renderer::render_audio_with_modules;
2
- use crate::core::parser::statement::Statement;
3
- use crate::core::store::global::GlobalStore;
4
- use std::{ collections::HashMap, fs::create_dir_all };
5
- use std::io::Write;
6
-
7
- use crate::utils::logger::Logger;
8
-
9
- pub struct Builder {}
10
-
11
- impl Builder {
12
- pub fn new() -> Self {
13
- Builder {}
14
- }
15
-
16
- pub fn build_ast(&self, modules: &HashMap<String, Vec<Statement>>, out_dir: &str) {
17
- for (name, statements) in modules {
18
- let formatted_name = name.split("/").last().unwrap_or(name);
19
- let formatted_name = formatted_name.replace(".deva", "");
20
-
21
- create_dir_all(format!("{}/ast", out_dir)).expect("Failed to create AST directory");
22
-
23
- let file_path = format!("{}/ast/{}.json", out_dir, formatted_name);
24
- let mut file = std::fs::File::create(file_path).expect("Failed to create AST file");
25
-
26
- let content = serde_json
27
- ::to_string_pretty(&statements)
28
- .expect("Failed to serialize AST");
29
-
30
- file.write_all(content.as_bytes()).expect("Failed to write AST to file");
31
- }
32
- }
33
-
34
- pub fn build_audio(
35
- &self,
36
- modules: &HashMap<String, Vec<Statement>>,
37
- normalized_output_dir: &str,
38
- global_store: &mut GlobalStore
39
- ) {
40
- let logger = Logger::new();
41
-
42
- let audio_engines = render_audio_with_modules(
43
- modules.clone(),
44
- &normalized_output_dir,
45
- global_store
46
- );
47
-
48
- create_dir_all(format!("{}/audio", normalized_output_dir)).expect(
49
- "Failed to create audio directory"
50
- );
51
-
52
- for (module_name, mut audio_engine) in audio_engines {
53
- let formatted_module_name = module_name
54
- .split('/')
55
- .last()
56
- .unwrap_or(&module_name)
57
- .replace(".deva", "");
58
-
59
- let output_path = format!(
60
- "{}/audio/{}.wav",
61
- normalized_output_dir,
62
- formatted_module_name
63
- );
64
-
65
- match audio_engine.generate_wav_file(&output_path) {
66
- Ok(_) => {}
67
- Err(msg) => {
68
- logger.log_error_with_stacktrace(
69
- &format!(
70
- "Unable to generate WAV file for module '{}': {}",
71
- formatted_module_name,
72
- msg
73
- ),
74
- &module_name
75
- );
76
- }
77
- }
78
- }
79
- }
80
- }
1
+ use crate::core::audio::engine::render_audio_with_modules;
2
+ use crate::core::parser::statement::Statement;
3
+ use crate::core::store::global::GlobalStore;
4
+ use devalang_utils::logger::Logger;
5
+ use std::io::Write;
6
+ use std::{collections::HashMap, fs::create_dir_all};
7
+
8
+ pub struct Builder {}
9
+
10
+ impl Default for Builder {
11
+ fn default() -> Self {
12
+ Self::new()
13
+ }
14
+ }
15
+
16
+ impl Builder {
17
+ pub fn new() -> Self {
18
+ Builder {}
19
+ }
20
+
21
+ pub fn build_ast(
22
+ &self,
23
+ modules: &HashMap<String, Vec<Statement>>,
24
+ out_dir: &str,
25
+ compress: bool,
26
+ ) {
27
+ for (name, statements) in modules {
28
+ let formatted_name = name.split("/").last().unwrap_or(name);
29
+ let formatted_name = formatted_name.replace(".deva", "");
30
+
31
+ create_dir_all(format!("{}/ast", out_dir)).expect("Failed to create AST directory");
32
+
33
+ let file_path = format!("{}/ast/{}.json", out_dir, formatted_name);
34
+ let mut file = std::fs::File::create(file_path).expect("Failed to create AST file");
35
+ let content = if compress {
36
+ serde_json::to_string(&statements).expect("Failed to serialize AST")
37
+ } else {
38
+ serde_json::to_string_pretty(&statements).expect("Failed to serialize AST")
39
+ };
40
+
41
+ file.write_all(content.as_bytes())
42
+ .expect("Failed to write AST to file");
43
+ }
44
+ }
45
+
46
+ pub fn build_audio(
47
+ &self,
48
+ modules: &HashMap<String, Vec<Statement>>,
49
+ normalized_output_dir: &str,
50
+ global_store: &mut GlobalStore,
51
+ audio_format: Option<String>,
52
+ sample_rate: Option<u32>,
53
+ ) {
54
+ let logger = Logger::new();
55
+
56
+ let audio_engines =
57
+ render_audio_with_modules(modules.clone(), normalized_output_dir, global_store);
58
+
59
+ create_dir_all(format!("{}/audio", normalized_output_dir))
60
+ .expect("Failed to create audio directory");
61
+
62
+ for (module_name, mut audio_engine) in audio_engines {
63
+ let formatted_module_name = module_name
64
+ .split('/')
65
+ .next_back()
66
+ .unwrap_or(&module_name)
67
+ .replace(".deva", "");
68
+
69
+ let output_path = format!(
70
+ "{}/audio/{}.wav",
71
+ normalized_output_dir, formatted_module_name
72
+ );
73
+
74
+ match audio_engine.generate_wav_file(&output_path, audio_format.clone(), sample_rate) {
75
+ Ok(_) => {}
76
+ Err(msg) => {
77
+ logger.log_error_with_stacktrace(
78
+ &format!(
79
+ "Unable to generate WAV file for module '{}': {}",
80
+ formatted_module_name, msg
81
+ ),
82
+ &module_name,
83
+ );
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ pub fn build_midi(
90
+ &self,
91
+ modules: &HashMap<String, Vec<Statement>>,
92
+ normalized_output_dir: &str,
93
+ global_store: &mut GlobalStore,
94
+ ) {
95
+ let logger = Logger::new();
96
+
97
+ let audio_engines =
98
+ render_audio_with_modules(modules.clone(), normalized_output_dir, global_store);
99
+
100
+ create_dir_all(format!("{}/midi", normalized_output_dir))
101
+ .expect("Failed to create MIDI directory");
102
+
103
+ for (module_name, mut audio_engine) in audio_engines {
104
+ let formatted_module_name = module_name
105
+ .split('/')
106
+ .next_back()
107
+ .unwrap_or(&module_name)
108
+ .replace(".deva", "");
109
+
110
+ let output_path = format!(
111
+ "{}/midi/{}.mid",
112
+ normalized_output_dir, formatted_module_name
113
+ );
114
+
115
+ match audio_engine.generate_midi_file(&output_path, None, None) {
116
+ Ok(_) => {}
117
+ Err(msg) => {
118
+ logger.log_error_with_stacktrace(
119
+ &format!(
120
+ "Unable to generate MIDI file for module '{}': {}",
121
+ formatted_module_name, msg
122
+ ),
123
+ &module_name,
124
+ );
125
+ }
126
+ }
127
+ }
128
+ }
129
+ }
@@ -1,10 +1,10 @@
1
- use std::{ collections::HashMap, fs::create_dir_all };
2
- use crate::core::{ debugger::Debugger, lexer::token::Token };
1
+ use crate::core::{debugger::Debugger, lexer::token::Token};
2
+ use std::{collections::HashMap, fs::create_dir_all};
3
3
 
4
4
  pub fn write_lexer_log_file(
5
5
  output_dir: &str,
6
6
  file_name: &str,
7
- modules: HashMap<String, Vec<Token>>
7
+ modules: HashMap<String, Vec<Token>>,
8
8
  ) {
9
9
  let debugger = Debugger::new();
10
10
  let mut content = String::new();
@@ -20,7 +20,7 @@ pub fn write_lexer_log_file(
20
20
  content.push_str(&format!("{:?}\n", token));
21
21
  }
22
22
 
23
- content.push_str("\n");
23
+ content.push('\n');
24
24
  }
25
25
 
26
26
  debugger.write_log_file(&log_directory, file_name, &content);
@@ -0,0 +1,52 @@
1
+ use devalang_types::{FunctionTable, VariableTable};
2
+ use std::fs::create_dir_all;
3
+
4
+ pub fn write_module_variable_log_file(
5
+ output_dir: &str,
6
+ module_path: &str,
7
+ variable_table: &VariableTable,
8
+ ) {
9
+ let debugger = crate::core::debugger::Debugger::new();
10
+ let mut content = String::new();
11
+ let module_name = module_path
12
+ .split('/')
13
+ .next_back()
14
+ .unwrap_or("index")
15
+ .replace(".deva", "");
16
+
17
+ let log_directory = format!("{}/logs/modules/{}", output_dir, module_name);
18
+ create_dir_all(&log_directory).expect("Failed to create log directory");
19
+
20
+ for (var_name, var_data) in &variable_table.variables {
21
+ content.push_str(&format!("{:?} = {:?}\n", var_name, var_data));
22
+ }
23
+
24
+ content.push('\n');
25
+
26
+ debugger.write_log_file(&log_directory, "variables.log", &content);
27
+ }
28
+
29
+ pub fn write_module_function_log_file(
30
+ output_dir: &str,
31
+ module_path: &str,
32
+ function_table: &FunctionTable,
33
+ ) {
34
+ let debugger = crate::core::debugger::Debugger::new();
35
+ let mut content = String::new();
36
+ let module_name = module_path
37
+ .split('/')
38
+ .next_back()
39
+ .unwrap_or("index")
40
+ .replace(".deva", "");
41
+
42
+ let log_directory = format!("{}/logs/modules/{}", output_dir, module_name);
43
+ create_dir_all(&log_directory).expect("Failed to create log directory");
44
+
45
+ for (func_name, func_data) in &function_table.functions {
46
+ content.push_str(&format!("{:?} = {:?}\n", func_name, func_data));
47
+ }
48
+
49
+ content.push('\n');
50
+
51
+ debugger.write_log_file(&log_directory, "functions.log", &content);
52
+ }
@@ -1,11 +1,18 @@
1
- pub mod preprocessor;
2
1
  pub mod lexer;
2
+ pub mod logs;
3
+ pub mod preprocessor;
3
4
  pub mod store;
4
5
 
5
6
  use std::io::Write;
6
7
 
7
8
  pub struct Debugger {}
8
9
 
10
+ impl Default for Debugger {
11
+ fn default() -> Self {
12
+ Self::new()
13
+ }
14
+ }
15
+
9
16
  impl Debugger {
10
17
  pub fn new() -> Self {
11
18
  Debugger {}
@@ -13,9 +20,11 @@ impl Debugger {
13
20
 
14
21
  pub fn write_log_file(&self, path: &str, filename: &str, content: &str) {
15
22
  std::fs::create_dir_all(path).expect("Failed to create directory");
23
+
16
24
  let file_path = format!("{}/{}", path, filename);
17
25
  let mut file = std::fs::File::create(file_path).expect("Failed to create file");
18
26
 
19
- file.write_all(content.as_bytes()).expect("Failed to write to file");
27
+ file.write_all(content.as_bytes())
28
+ .expect("Failed to write to file");
20
29
  }
21
30
  }
@@ -1,10 +1,10 @@
1
- use std::{ collections::HashMap, fs::create_dir_all };
2
- use crate::core::{ debugger::Debugger, parser::statement::Statement };
1
+ use crate::core::{debugger::Debugger, parser::statement::Statement};
2
+ use std::{collections::HashMap, fs::create_dir_all};
3
3
 
4
4
  pub fn write_preprocessor_log_file(
5
5
  output_dir: &str,
6
6
  file_name: &str,
7
- modules: HashMap<String, Vec<Statement>>
7
+ modules: HashMap<String, Vec<Statement>>,
8
8
  ) {
9
9
  let debugger = Debugger::new();
10
10
  let mut content = String::new();
@@ -20,7 +20,7 @@ pub fn write_preprocessor_log_file(
20
20
  content.push_str(&format!("{:?}\n", stmt));
21
21
  }
22
22
 
23
- content.push_str("\n");
23
+ content.push('\n');
24
24
  }
25
25
 
26
26
  debugger.write_log_file(&log_directory, file_name, &content);