@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
@@ -1,16 +1,21 @@
1
1
  use std::collections::HashMap;
2
2
 
3
+ use devalang_types::Value;
4
+
3
5
  use crate::core::{
4
- lexer::token::{ Token, TokenKind },
5
- parser::{ driver::Parser, statement::{ Statement, StatementKind } },
6
- shared::value::Value,
6
+ lexer::token::Token,
7
+ parser::{
8
+ driver::parser::Parser,
9
+ handler::dot::parse_dot_token,
10
+ statement::{Statement, StatementKind},
11
+ },
7
12
  store::global::GlobalStore,
8
13
  };
9
14
 
10
15
  pub fn parse_synth_token(
11
16
  parser: &mut Parser,
12
- current_token: Token,
13
- global_store: &mut GlobalStore
17
+ _current_token: Token,
18
+ _global_store: &mut GlobalStore,
14
19
  ) -> Statement {
15
20
  parser.advance(); // consume 'synth'
16
21
 
@@ -18,46 +23,111 @@ pub fn parse_synth_token(
18
23
  return Statement::unknown();
19
24
  };
20
25
 
21
- // Expect an identifier (synth waveform)
22
- let Some(identifier_token) = parser.peek_clone() else {
23
- return Statement::error(synth_token, "Expected identifier after 'synth'".to_string());
24
- };
26
+ // Expect a provider/waveform identifier (can be dotted: alias.synth)
27
+ // Also accept a dot-led entity by delegating to the dot parser (e.g. .module.export)
28
+ let synth_waveform = if let Some(first_token) = parser.peek_clone() {
29
+ use crate::core::lexer::token::TokenKind;
25
30
 
26
- let synth_waveform = identifier_token.lexeme.clone();
31
+ if first_token.kind == TokenKind::Dot {
32
+ // Parse dot-entity and extract its entity string
33
+ let dot_stmt = parse_dot_token(parser, _global_store);
34
+ // Extract entity if the parsed statement is a Trigger
35
+ match dot_stmt.kind {
36
+ StatementKind::Trigger { entity, .. } => entity,
37
+ _ => String::new(),
38
+ }
39
+ } else {
40
+ if first_token.kind != crate::core::lexer::token::TokenKind::Identifier
41
+ && first_token.kind != crate::core::lexer::token::TokenKind::Number
42
+ && first_token.kind != crate::core::lexer::token::TokenKind::Synth
43
+ {
44
+ return crate::core::parser::statement::error_from_token(
45
+ first_token.clone(),
46
+ "Expected identifier after 'synth'".to_string(),
47
+ );
48
+ }
27
49
 
28
- parser.advance(); // consume identifier
50
+ // Collect dotted parts on the same line
51
+ let mut parts: Vec<String> = Vec::new();
52
+ let current_line = first_token.line;
53
+ loop {
54
+ let Some(tok) = parser.peek_clone() else {
55
+ break;
56
+ };
57
+ if tok.line != current_line {
58
+ break;
59
+ }
60
+ match tok.kind {
61
+ crate::core::lexer::token::TokenKind::Identifier
62
+ | crate::core::lexer::token::TokenKind::Number
63
+ | crate::core::lexer::token::TokenKind::Synth => {
64
+ parts.push(tok.lexeme.clone());
65
+ parser.advance();
66
+ // If next isn't a dot on same line, stop
67
+ if let Some(next) = parser.peek_clone() {
68
+ if !(next.line == current_line
69
+ && next.kind == crate::core::lexer::token::TokenKind::Dot)
70
+ {
71
+ break;
72
+ }
73
+ } else {
74
+ break;
75
+ }
76
+ }
77
+ crate::core::lexer::token::TokenKind::Dot => {
78
+ parser.advance();
79
+ }
80
+ _ => break,
81
+ }
82
+ }
29
83
 
30
- // Expect synth optional parameters map
31
- let mut parameters = HashMap::new();
84
+ parts.join(".")
85
+ }
86
+ } else {
87
+ return crate::core::parser::statement::error_from_token(
88
+ synth_token,
89
+ "Expected identifier after 'synth'".to_string(),
90
+ );
91
+ };
92
+
93
+ // Skip formatting before optional parameters map
94
+ while parser.check_token(crate::core::lexer::token::TokenKind::Newline)
95
+ || parser.check_token(crate::core::lexer::token::TokenKind::Indent)
96
+ || parser.check_token(crate::core::lexer::token::TokenKind::Dedent)
97
+ || parser.check_token(crate::core::lexer::token::TokenKind::Whitespace)
98
+ {
99
+ parser.advance();
100
+ }
32
101
 
33
- if let Some(params) = parser.parse_map_value() {
102
+ // Expect synth optional parameters map
103
+ let parameters = if let Some(params) = parser.parse_map_value() {
34
104
  // If parameters are provided, we expect a map
35
105
  if let Value::Map(map) = params {
36
- parameters = map;
106
+ map
37
107
  } else {
38
- return Statement::error(synth_token, "Expected a map for synth parameters".to_string());
108
+ return crate::core::parser::statement::error_from_token(
109
+ synth_token,
110
+ "Expected a map for synth parameters".to_string(),
111
+ );
39
112
  }
40
113
  } else {
41
114
  // If no parameters are provided, we can still create the statement with an empty map
42
- parameters = HashMap::new();
43
- }
115
+ HashMap::new()
116
+ };
44
117
 
45
118
  Statement {
46
119
  kind: StatementKind::Synth,
47
- value: Value::Map(
48
- HashMap::from([
49
- ("entity".to_string(), Value::String("synth".to_string())),
50
- (
51
- "value".to_string(),
52
- Value::Map(
53
- HashMap::from([
54
- ("waveform".to_string(), Value::String(synth_waveform)),
55
- ("parameters".to_string(), Value::Map(parameters)),
56
- ])
57
- ),
58
- ),
59
- ])
60
- ),
120
+ value: Value::Map(HashMap::from([
121
+ ("entity".to_string(), Value::String("synth".to_string())),
122
+ (
123
+ "value".to_string(),
124
+ Value::Map(HashMap::from([
125
+ // Store waveform as identifier to allow resolution from variables/exports
126
+ ("waveform".to_string(), Value::Identifier(synth_waveform)),
127
+ ("parameters".to_string(), Value::Map(parameters)),
128
+ ])),
129
+ ),
130
+ ])),
61
131
  indent: synth_token.indent,
62
132
  line: synth_token.line,
63
133
  column: synth_token.column,
@@ -1,20 +1,135 @@
1
- use std::collections::HashMap;
1
+ use devalang_types::Value;
2
2
 
3
3
  use crate::core::{
4
- lexer::{ token::TokenKind },
5
- parser::{ statement::{ Statement, StatementKind }, driver::Parser },
6
- shared::value::Value,
4
+ lexer::token::TokenKind,
5
+ parser::{
6
+ driver::parser::Parser,
7
+ statement::{Statement, StatementKind},
8
+ },
7
9
  store::global::GlobalStore,
8
10
  };
9
11
 
10
12
  pub fn parse_loop_token(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
11
- parser.advance(); // consume 'loop'
13
+ parser.advance(); // consume 'loop' or 'for' (aliased in lexer)
12
14
  let Some(loop_token) = parser.previous_clone() else {
13
15
  return Statement::unknown();
14
16
  };
15
17
 
18
+ // Support two forms:
19
+ // 1) loop <count>:
20
+ // 2) for <ident> in [a,b,c]:
21
+
22
+ // Peek next to decide
23
+ let Some(next_token) = parser.peek_clone() else {
24
+ return Statement::error_with_pos(
25
+ loop_token.indent,
26
+ loop_token.line,
27
+ loop_token.column,
28
+ "Expected iterator after loop/for".to_string(),
29
+ );
30
+ };
31
+
32
+ // Try to detect 'for <ident> in [array]:' form
33
+ let mut foreach_ident: Option<String> = None;
34
+ if let TokenKind::Identifier = next_token.kind {
35
+ // Could be either count identifier (old form) or foreach variable
36
+ // Look ahead for 'in'
37
+ let name = next_token.lexeme.clone();
38
+ // don't consume yet; we'll branch
39
+ if let Some(t2) = parser.peek_nth(1) {
40
+ if t2.kind == TokenKind::Identifier && t2.lexeme == "in" {
41
+ // foreach form
42
+ foreach_ident = Some(name);
43
+ // consume ident and 'in'
44
+ parser.advance();
45
+ parser.advance();
46
+ }
47
+ }
48
+ }
49
+
50
+ if let Some(var_name) = foreach_ident {
51
+ // Expect [array] OR number OR string OR identifier after 'in'
52
+ let array_val = if let Some(tok) = parser.peek_clone() {
53
+ match tok.kind {
54
+ TokenKind::LBracket => {
55
+ if let Some(v) = parser.parse_array_value() {
56
+ v
57
+ } else {
58
+ Value::Array(vec![])
59
+ }
60
+ }
61
+ TokenKind::Number => {
62
+ parser.advance();
63
+ let n = tok.lexeme.parse::<f32>().unwrap_or(0.0);
64
+ Value::Number(n)
65
+ }
66
+ TokenKind::String => {
67
+ parser.advance();
68
+ Value::String(tok.lexeme.clone())
69
+ }
70
+ TokenKind::Identifier => {
71
+ parser.advance();
72
+ Value::Identifier(tok.lexeme.clone())
73
+ }
74
+ _ => {
75
+ return Statement::error_with_pos(
76
+ loop_token.indent,
77
+ loop_token.line,
78
+ loop_token.column,
79
+ "Expected array, number, string or identifier after 'in'".to_string(),
80
+ );
81
+ }
82
+ }
83
+ } else {
84
+ return Statement::error_with_pos(
85
+ loop_token.indent,
86
+ loop_token.line,
87
+ loop_token.column,
88
+ "Expected array, number, string or identifier after 'in'".to_string(),
89
+ );
90
+ };
91
+
92
+ // Expect ':'
93
+ if !parser.match_token(TokenKind::Colon) {
94
+ return Statement::error_with_pos(
95
+ loop_token.indent,
96
+ loop_token.line,
97
+ loop_token.column,
98
+ "Expected ':' after foreach header".to_string(),
99
+ );
100
+ }
101
+
102
+ let tokens =
103
+ parser.collect_until(|t| t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF);
104
+ let loop_body = parser.parse_block(tokens.clone(), global_store);
105
+ if let Some(token) = parser.peek() {
106
+ if token.kind == TokenKind::Dedent {
107
+ parser.advance();
108
+ }
109
+ }
110
+
111
+ let mut value_map = std::collections::HashMap::new();
112
+ value_map.insert("foreach".to_string(), Value::Identifier(var_name));
113
+ value_map.insert("array".to_string(), array_val);
114
+ value_map.insert("body".to_string(), Value::Block(loop_body.clone()));
115
+
116
+ return Statement {
117
+ kind: StatementKind::Loop,
118
+ value: Value::Map(value_map),
119
+ indent: loop_token.indent,
120
+ line: loop_token.line,
121
+ column: loop_token.column,
122
+ };
123
+ }
124
+
125
+ // Fallback to legacy: loop <count>:
16
126
  let Some(iterator_token) = parser.peek_clone() else {
17
- return Statement::error(loop_token, "Expected number or identifier after 'loop'".to_string());
127
+ return Statement::error_with_pos(
128
+ loop_token.indent,
129
+ loop_token.line,
130
+ loop_token.column,
131
+ "Expected number or identifier after 'loop'".to_string(),
132
+ );
18
133
  };
19
134
 
20
135
  let iterator_value = match iterator_token.kind {
@@ -28,37 +143,44 @@ pub fn parse_loop_token(parser: &mut Parser, global_store: &mut GlobalStore) ->
28
143
  parser.advance();
29
144
  Value::Identifier(val)
30
145
  }
146
+ TokenKind::String => {
147
+ // strings that are numeric (e.g. "10")
148
+ let s = iterator_token.lexeme.clone();
149
+ parser.advance();
150
+ Value::String(s)
151
+ }
31
152
  _ => {
32
- return Statement::error(
33
- iterator_token.clone(),
34
- "Expected a number or identifier as loop count".to_string()
153
+ return Statement::error_with_pos(
154
+ iterator_token.clone().indent,
155
+ iterator_token.clone().line,
156
+ iterator_token.clone().column,
157
+ "Expected a number, string or identifier as loop count".to_string(),
35
158
  );
36
159
  }
37
160
  };
38
161
 
39
- // Expect colon
40
- let Some(colon_token) = parser.peek_clone() else {
41
- return Statement::error(iterator_token.clone(), "Expected ':' after loop count".to_string());
42
- };
43
-
44
- if colon_token.kind != TokenKind::Colon {
45
- let message = format!("Expected ':' after loop count, got {:?}", colon_token.kind);
46
- return Statement::error(colon_token.clone(), message);
162
+ if !parser.match_token(TokenKind::Colon) {
163
+ let message = format!(
164
+ "Expected ':' after loop count, got {:?}",
165
+ parser.peek_kind()
166
+ );
167
+ return Statement::error_with_pos(
168
+ loop_token.clone().indent,
169
+ loop_token.clone().line,
170
+ loop_token.clone().column,
171
+ message,
172
+ );
47
173
  }
48
174
 
49
- parser.advance(); // consume ':'
50
-
51
- // Collect body
52
175
  let tokens = parser.collect_until(|t| t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF);
53
176
  let loop_body = parser.parse_block(tokens.clone(), global_store);
54
-
55
177
  if let Some(token) = parser.peek() {
56
178
  if token.kind == TokenKind::Dedent {
57
179
  parser.advance();
58
180
  }
59
181
  }
60
182
 
61
- let mut value_map = HashMap::new();
183
+ let mut value_map = std::collections::HashMap::new();
62
184
  value_map.insert("iterator".to_string(), iterator_value);
63
185
  value_map.insert("body".to_string(), Value::Block(loop_body.clone()));
64
186
 
@@ -1,8 +1,9 @@
1
+ pub mod arrow_call;
1
2
  pub mod at;
2
- pub mod identifier;
3
- pub mod dot;
4
- pub mod tempo;
5
3
  pub mod bank;
6
- pub mod loop_;
7
4
  pub mod condition;
8
- pub mod arrow_call;
5
+ pub mod dot;
6
+ pub mod identifier;
7
+ pub mod loop_;
8
+ pub mod pattern;
9
+ pub mod tempo;
@@ -0,0 +1,74 @@
1
+ use devalang_types::Value;
2
+
3
+ use crate::core::{
4
+ lexer::token::TokenKind,
5
+ parser::{
6
+ driver::parser::Parser,
7
+ statement::{Statement, StatementKind},
8
+ },
9
+ store::global::GlobalStore,
10
+ };
11
+
12
+ pub fn parse_pattern_token(parser: &mut Parser, _global_store: &mut GlobalStore) -> Statement {
13
+ // consume 'pattern'
14
+ parser.advance();
15
+
16
+ let Some(tok) = parser.previous_clone() else {
17
+ return Statement::unknown();
18
+ };
19
+
20
+ // Parse pattern name
21
+ let mut name = String::new();
22
+ if let Some(next) = parser.peek_clone() {
23
+ if next.kind == TokenKind::Identifier {
24
+ parser.advance();
25
+ name = next.lexeme.clone();
26
+ }
27
+ }
28
+
29
+ // optional 'with <target>' sequence
30
+ let mut target: Option<String> = None;
31
+ if parser.peek_is("with") {
32
+ parser.advance(); // consume 'with'
33
+ if let Some(tok2) = parser.peek_clone() {
34
+ // target can be identifier or dotted identifier
35
+ if tok2.kind == TokenKind::Identifier {
36
+ parser.advance();
37
+ let mut base = tok2.lexeme.clone();
38
+ if let Some(dot) = parser.peek_clone() {
39
+ if dot.kind == TokenKind::Dot {
40
+ parser.advance();
41
+ if let Some(suf) = parser.peek_clone() {
42
+ if suf.kind == TokenKind::Identifier || suf.kind == TokenKind::Number {
43
+ parser.advance();
44
+ base.push('.');
45
+ base.push_str(&suf.lexeme);
46
+ }
47
+ }
48
+ }
49
+ }
50
+ target = Some(base);
51
+ }
52
+ }
53
+ }
54
+
55
+ // optional '=' and pattern string
56
+ let mut value: Value = Value::Null;
57
+ if parser.peek_is("=") {
58
+ parser.advance();
59
+ if let Some(tok3) = parser.peek_clone() {
60
+ if tok3.kind == TokenKind::String {
61
+ parser.advance();
62
+ value = Value::String(tok3.lexeme.clone());
63
+ }
64
+ }
65
+ }
66
+
67
+ Statement {
68
+ kind: StatementKind::Pattern { name, target },
69
+ value,
70
+ indent: tok.indent,
71
+ line: tok.line,
72
+ column: tok.column,
73
+ }
74
+ }
@@ -1,9 +1,12 @@
1
1
  use crate::core::{
2
2
  lexer::token::TokenKind,
3
- parser::{ statement::{ Statement, StatementKind }, driver::Parser },
4
- shared::value::Value,
3
+ parser::{
4
+ driver::parser::Parser,
5
+ statement::{Statement, StatementKind},
6
+ },
5
7
  store::global::GlobalStore,
6
8
  };
9
+ use devalang_types::Value;
7
10
 
8
11
  pub fn parse_tempo_token(parser: &mut Parser, _global_store: &mut GlobalStore) -> Statement {
9
12
  parser.advance(); // consume 'bpm'
@@ -14,25 +17,80 @@ pub fn parse_tempo_token(parser: &mut Parser, _global_store: &mut GlobalStore) -
14
17
 
15
18
  // Expect a number or identifier
16
19
  let Some(value_token) = parser.peek_clone() else {
17
- return Statement::error(
18
- tempo_token,
19
- "Expected a number or identifier after 'bpm'".to_string()
20
+ return Statement::error_with_pos(
21
+ tempo_token.indent,
22
+ tempo_token.line,
23
+ tempo_token.column,
24
+ "Expected a number or identifier after 'bpm'".to_string(),
20
25
  );
21
26
  };
22
27
 
23
28
  let value = match value_token.kind {
24
29
  TokenKind::Number => {
30
+ // support decimals and fraction forms
31
+ let mut num = value_token.lexeme.clone();
25
32
  parser.advance();
26
- Value::Number(value_token.lexeme.parse().unwrap_or(0.0))
33
+ if let Some(dot) = parser.peek_clone() {
34
+ if dot.kind == TokenKind::Dot {
35
+ if let Some(next) = parser.peek_nth(1).cloned() {
36
+ if next.kind == TokenKind::Number {
37
+ parser.advance();
38
+ parser.advance();
39
+ num.push('.');
40
+ num.push_str(&next.lexeme);
41
+ }
42
+ }
43
+ }
44
+ }
45
+
46
+ if let Some(slash) = parser.peek_clone() {
47
+ if slash.kind == TokenKind::Slash {
48
+ parser.advance();
49
+ if let Some(den) = parser.peek_clone() {
50
+ if den.kind == TokenKind::Number || den.kind == TokenKind::Identifier {
51
+ let frac = format!("{}/{}", num, den.lexeme);
52
+ parser.advance();
53
+ Value::Duration(devalang_types::Duration::Beat(frac))
54
+ } else {
55
+ return Statement::error_with_pos(
56
+ slash.indent,
57
+ slash.line,
58
+ slash.column,
59
+ "Expected denominator after '/' in bpm".to_string(),
60
+ );
61
+ }
62
+ } else {
63
+ return Statement::error_with_pos(
64
+ slash.indent,
65
+ slash.line,
66
+ slash.column,
67
+ "Expected denominator after '/' in bpm".to_string(),
68
+ );
69
+ }
70
+ } else {
71
+ Value::Number(num.parse().unwrap_or(0.0))
72
+ }
73
+ } else {
74
+ Value::Number(num.parse().unwrap_or(0.0))
75
+ }
27
76
  }
28
77
  TokenKind::Identifier => {
29
78
  parser.advance();
30
79
  Value::Identifier(value_token.lexeme.clone())
31
80
  }
81
+ TokenKind::String => {
82
+ parser.advance();
83
+ Value::String(value_token.lexeme.clone())
84
+ }
32
85
  _ => {
33
- return Statement::error(
34
- value_token.clone(),
35
- format!("Expected a number or identifier after 'bpm', got {:?}", value_token.kind)
86
+ return Statement::error_with_pos(
87
+ value_token.indent,
88
+ value_token.line,
89
+ value_token.column,
90
+ format!(
91
+ "Expected a number, string or identifier after 'bpm', got {:?}",
92
+ value_token.kind
93
+ ),
36
94
  );
37
95
  }
38
96
  };
@@ -1,4 +1,3 @@
1
- pub mod driver;
2
-
3
- pub mod statement;
4
- pub mod handler;
1
+ pub mod driver;
2
+ pub mod handler;
3
+ pub mod statement;
@@ -1,96 +1,10 @@
1
- use serde::{ Deserialize, Serialize };
2
- use crate::core::{ lexer::token::Token, shared::{ duration::Duration, value::Value } };
1
+ use crate::core::lexer::token::Token;
2
+ pub use devalang_types::{Duration, Statement, StatementKind, Value};
3
3
 
4
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5
- pub struct Statement {
6
- pub kind: StatementKind,
7
- pub value: Value,
8
- pub indent: usize,
9
- pub line: usize,
10
- pub column: usize,
4
+ pub fn unknown_from_token(token: &Token) -> Statement {
5
+ Statement::unknown_with_pos(token.indent, token.line, token.column)
11
6
  }
12
7
 
13
- impl Statement {
14
- pub fn unknown() -> Self {
15
- Statement {
16
- kind: StatementKind::Unknown,
17
- value: Value::Null,
18
- indent: 0,
19
- line: 0,
20
- column: 0,
21
- }
22
- }
23
-
24
- pub fn error(token: Token, message: String) -> Self {
25
- Statement {
26
- kind: StatementKind::Error { message },
27
- value: Value::Null,
28
- indent: token.indent,
29
- line: token.line,
30
- column: token.column,
31
- }
32
- }
8
+ pub fn error_from_token(token: Token, message: String) -> Statement {
9
+ Statement::error_with_pos(token.indent, token.line, token.column, message)
33
10
  }
34
-
35
- #[derive(Debug, Serialize, Clone, Deserialize, PartialEq)]
36
- pub enum StatementKind {
37
- // ───── Core Instructions ─────
38
- Tempo,
39
- Bank,
40
- Load {
41
- source: String,
42
- alias: String,
43
- },
44
- Let {
45
- name: String,
46
- },
47
- ArrowCall {
48
- target: String,
49
- method: String,
50
- args: Vec<Value>,
51
- },
52
-
53
- // ───── Instruments ─────
54
- Synth,
55
-
56
- // ───── Playback / Scheduling ─────
57
- Trigger {
58
- entity: String,
59
- duration: Duration,
60
- },
61
- Sleep,
62
- Call,
63
- Spawn,
64
- Loop,
65
-
66
- // ───── Structure & Logic ─────
67
- Group,
68
-
69
- // ───── Module System ─────
70
- Include(String),
71
- Export {
72
- names: Vec<String>,
73
- source: String,
74
- },
75
- Import {
76
- names: Vec<String>,
77
- source: String,
78
- },
79
-
80
- // ───── Conditions ─────
81
- If,
82
- Else,
83
- ElseIf,
84
-
85
- // ───── Internal / Utility ─────
86
- Comment,
87
- Indent,
88
- Dedent,
89
- NewLine,
90
-
91
- // ───── Error Handling ─────
92
- Unknown,
93
- Error {
94
- message: String,
95
- },
96
- }