@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,10 +1,14 @@
1
+ use devalang_types::Value;
2
+
1
3
  use crate::core::{
2
4
  lexer::token::TokenKind,
3
- parser::{ driver::Parser, statement::{ Statement, StatementKind } },
4
- shared::value::Value,
5
+ parser::{
6
+ driver::parser::Parser,
7
+ statement::{Statement, StatementKind},
8
+ },
5
9
  store::global::GlobalStore,
6
10
  };
7
- pub fn parse_at_token(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
11
+ pub fn parse_at_token(parser: &mut Parser, _global_store: &mut GlobalStore) -> Statement {
8
12
  parser.advance(); // consume '@'
9
13
 
10
14
  let Some(token) = parser.peek_clone() else {
@@ -14,11 +18,96 @@ pub fn parse_at_token(parser: &mut Parser, global_store: &mut GlobalStore) -> St
14
18
  let keyword = token.lexeme.as_str();
15
19
 
16
20
  match keyword {
21
+ "use" => {
22
+ parser.advance(); // consume 'use'
23
+ let Some(use_token) = parser.previous_clone() else {
24
+ return Statement::unknown();
25
+ };
26
+
27
+ // Expect plugin author
28
+ let Some(author_token) = parser.peek_clone() else {
29
+ return crate::core::parser::statement::error_from_token(
30
+ use_token,
31
+ "Expected plugin author".to_string(),
32
+ );
33
+ };
34
+ if author_token.kind != TokenKind::Identifier {
35
+ return crate::core::parser::statement::error_from_token(
36
+ author_token,
37
+ "Expected identifier for plugin author".to_string(),
38
+ );
39
+ }
40
+ parser.advance(); // consume author
41
+
42
+ // Expect '.'
43
+ if !parser.match_token(TokenKind::Dot) {
44
+ return crate::core::parser::statement::error_from_token(
45
+ author_token,
46
+ "Expected '.' after plugin author".to_string(),
47
+ );
48
+ }
49
+
50
+ // Expect plugin name
51
+ let Some(plugin_token) = parser.peek_clone() else {
52
+ return crate::core::parser::statement::error_from_token(
53
+ author_token,
54
+ "Expected plugin name".to_string(),
55
+ );
56
+ };
57
+
58
+ let name = match plugin_token.kind {
59
+ TokenKind::Identifier | TokenKind::Number => {
60
+ parser.advance();
61
+ format!("{}.{}", author_token.lexeme, plugin_token.lexeme)
62
+ }
63
+ _ => {
64
+ return crate::core::parser::statement::error_from_token(
65
+ plugin_token,
66
+ "Expected identifier or number for plugin name".to_string(),
67
+ );
68
+ }
69
+ };
70
+
71
+ // Optional alias
72
+ let alias = if parser.match_token(TokenKind::As) {
73
+ let Some(alias_token) = parser.peek_clone() else {
74
+ return crate::core::parser::statement::error_from_token(
75
+ use_token,
76
+ "Expected identifier after 'as'".to_string(),
77
+ );
78
+ };
79
+ if alias_token.kind != TokenKind::Identifier {
80
+ return crate::core::parser::statement::error_from_token(
81
+ alias_token,
82
+ "Expected identifier after 'as'".to_string(),
83
+ );
84
+ }
85
+ parser.advance();
86
+ Some(alias_token.lexeme.clone())
87
+ } else {
88
+ None
89
+ };
90
+
91
+ Statement {
92
+ kind: StatementKind::Use {
93
+ name: name.clone(),
94
+ alias,
95
+ },
96
+ value: Value::Null,
97
+ indent: use_token.indent,
98
+ line: use_token.line,
99
+ column: use_token.column,
100
+ }
101
+ }
102
+
17
103
  "import" => {
18
104
  parser.advance(); // consume 'import'
19
105
 
20
106
  if !parser.match_token(TokenKind::LBrace) {
21
- return Statement::error(token, "Expected '{{' after 'import'".to_string());
107
+ return crate::core::parser::statement::error_from_token(
108
+ token,
109
+ "Expected '{{' after 'import'".to_string(),
110
+ );
22
111
  }
23
112
 
24
113
  let mut names = Vec::new();
@@ -36,31 +125,44 @@ pub fn parse_at_token(parser: &mut Parser, global_store: &mut GlobalStore) -> St
36
125
  break;
37
126
  }
38
127
  _ => {
39
- let message = format!(
40
- "Unexpected token in import list: {:?}",
41
- token.kind.clone()
128
+ let message =
129
+ format!("Unexpected token in import list: {:?}", token.kind.clone());
130
+ return crate::core::parser::statement::error_from_token(
131
+ token.clone(),
132
+ message,
42
133
  );
43
- return Statement::error(token.clone(), message);
44
134
  }
45
135
  }
46
136
  }
47
137
 
48
138
  let Some(from_token) = parser.peek_clone() else {
49
- return Statement::error(token, "Expected 'from' after import list".to_string());
139
+ return crate::core::parser::statement::error_from_token(
140
+ token,
141
+ "Expected 'from' after import list".to_string(),
142
+ );
50
143
  };
51
144
 
52
145
  if from_token.lexeme != "from" {
53
- return Statement::error(token, "Expected keyword 'from'".to_string());
146
+ return crate::core::parser::statement::error_from_token(
147
+ token,
148
+ "Expected keyword 'from'".to_string(),
149
+ );
54
150
  }
55
151
 
56
152
  parser.advance(); // consume 'from'
57
153
 
58
154
  let Some(source_token) = parser.peek() else {
59
- return Statement::error(token, "Expected string after 'from'".to_string());
155
+ return crate::core::parser::statement::error_from_token(
156
+ token,
157
+ "Expected string after 'from'".to_string(),
158
+ );
60
159
  };
61
160
 
62
161
  if source_token.kind != TokenKind::String {
63
- return Statement::error(token, "Expected string after 'from'".to_string());
162
+ return crate::core::parser::statement::error_from_token(
163
+ token,
164
+ "Expected string after 'from'".to_string(),
165
+ );
64
166
  }
65
167
 
66
168
  let source = source_token.lexeme.clone();
@@ -91,7 +193,10 @@ pub fn parse_at_token(parser: &mut Parser, global_store: &mut GlobalStore) -> St
91
193
  } else if token.kind == TokenKind::RBrace {
92
194
  break; // Stop at the closing brace
93
195
  } else {
94
- return Statement::error(token, "Unexpected token in export list".to_string());
196
+ return crate::core::parser::statement::error_from_token(
197
+ token,
198
+ "Unexpected token in export list".to_string(),
199
+ );
95
200
  }
96
201
  }
97
202
 
@@ -110,35 +215,47 @@ pub fn parse_at_token(parser: &mut Parser, global_store: &mut GlobalStore) -> St
110
215
  "load" => {
111
216
  parser.advance(); // consume 'load'
112
217
 
113
- // Exemple : @load "preset.mydeva"
218
+ // Example: @load "preset.mydeva"
114
219
  let Some(path_token) = parser.peek() else {
115
- return Statement::error(token, "Expected string after 'load'".to_string());
220
+ return crate::core::parser::statement::error_from_token(
221
+ token,
222
+ "Expected string after 'load'".to_string(),
223
+ );
116
224
  };
117
225
 
118
226
  if path_token.kind != TokenKind::String {
119
- return Statement::error(token, "Expected string after 'load'".to_string());
227
+ return crate::core::parser::statement::error_from_token(
228
+ token,
229
+ "Expected string after 'load'".to_string(),
230
+ );
120
231
  }
121
232
 
122
233
  let path = path_token.lexeme.clone();
123
234
 
124
235
  parser.advance(); // consume string
125
- parser.advance(); // consume 'as'
126
236
 
127
- let Some(as_token) = parser.peek_clone() else {
128
- return Statement::error(
237
+ if !parser.match_token(TokenKind::As) {
238
+ return crate::core::parser::statement::error_from_token(
239
+ token,
240
+ "Expected 'as' after path in load statement".to_string(),
241
+ );
242
+ }
243
+
244
+ let Some(alias_token) = parser.peek_clone() else {
245
+ return crate::core::parser::statement::error_from_token(
129
246
  token,
130
- "Expected 'as' after path in load statement".to_string()
247
+ "Expected identifier after 'as' in load statement".to_string(),
131
248
  );
132
249
  };
133
250
 
134
- if as_token.kind != TokenKind::Identifier {
135
- return Statement::error(
251
+ if alias_token.kind != TokenKind::Identifier {
252
+ return crate::core::parser::statement::error_from_token(
136
253
  token,
137
- "Expected identifier after 'as' in load statement".to_string()
254
+ "Expected identifier after 'as' in load statement".to_string(),
138
255
  );
139
256
  }
140
257
 
141
- let alias = as_token.lexeme.clone();
258
+ let alias = alias_token.lexeme.clone();
142
259
 
143
260
  parser.advance(); // consume identifier
144
261
 
@@ -156,7 +273,7 @@ pub fn parse_at_token(parser: &mut Parser, global_store: &mut GlobalStore) -> St
156
273
 
157
274
  _ => {
158
275
  let message = format!("Unknown keyword after '@' : {}", keyword);
159
- Statement::error(token, message)
276
+ crate::core::parser::statement::error_from_token(token, message)
160
277
  }
161
278
  }
162
279
  }
@@ -1,41 +1,104 @@
1
+ use devalang_types::Value;
2
+
1
3
  use crate::core::{
2
4
  lexer::token::TokenKind,
3
- parser::{ statement::{ Statement, StatementKind }, driver::Parser },
4
- shared::value::Value,
5
+ parser::{
6
+ driver::parser::Parser,
7
+ statement::{Statement, StatementKind},
8
+ },
5
9
  store::global::GlobalStore,
6
10
  };
7
11
 
8
12
  pub fn parse_bank_token(parser: &mut Parser, _global_store: &mut GlobalStore) -> Statement {
9
- parser.advance(); // consume 'bank'
13
+ // consume 'bank'
14
+ parser.advance();
10
15
 
11
- let Some(bank_token) = parser.previous_clone() else {
16
+ let Some(bank_tok) = parser.previous_clone() else {
12
17
  return Statement::unknown();
13
18
  };
14
19
 
15
- let bank_value = if let Some(token) = parser.peek_clone() {
16
- match token.kind {
17
- TokenKind::Identifier => {
20
+ // Parse bank name
21
+ let bank_value: Value = match parser.peek_clone() {
22
+ Some(tok) => match tok.kind {
23
+ TokenKind::Identifier | TokenKind::Number => {
24
+ // base name
18
25
  parser.advance();
19
- Value::Identifier(token.lexeme.clone())
26
+ let mut base = tok.lexeme.clone();
27
+ // optional .suffix (identifier or number)
28
+ if let Some(dot) = parser.peek_clone() {
29
+ if dot.kind == TokenKind::Dot {
30
+ // consume '.' and the following ident/number
31
+ parser.advance();
32
+ if let Some(suffix) = parser.peek_clone() {
33
+ match suffix.kind {
34
+ TokenKind::Identifier | TokenKind::Number => {
35
+ parser.advance();
36
+ base.push('.');
37
+ base.push_str(&suffix.lexeme);
38
+ Value::String(base)
39
+ }
40
+ _ => Value::Identifier(base),
41
+ }
42
+ } else {
43
+ Value::Identifier(base)
44
+ }
45
+ } else {
46
+ match tok.kind {
47
+ TokenKind::Identifier => Value::String(base),
48
+ TokenKind::Number => Value::Number(base.parse::<f32>().unwrap_or(0.0)),
49
+ _ => Value::Unknown,
50
+ }
51
+ }
52
+ } else {
53
+ match tok.kind {
54
+ TokenKind::Identifier => Value::String(base),
55
+ TokenKind::Number => Value::Number(base.parse::<f32>().unwrap_or(0.0)),
56
+ _ => Value::Unknown,
57
+ }
58
+ }
20
59
  }
21
- TokenKind::Number => {
60
+ TokenKind::String => {
22
61
  parser.advance();
23
- Value::Number(token.lexeme.parse::<f32>().unwrap_or(0.0))
62
+ Value::String(tok.lexeme.clone())
24
63
  }
25
64
  _ => Value::Unknown,
26
- }
27
- } else {
28
- return Statement::error(
29
- bank_token,
30
- "Expected identifier or number after 'bank'".to_string()
31
- );
65
+ },
66
+ None => Value::Unknown,
32
67
  };
33
68
 
69
+ if matches!(bank_value, Value::Unknown | Value::Null) {
70
+ return crate::core::parser::statement::error_from_token(
71
+ bank_tok,
72
+ "Expected a bank name".to_string(),
73
+ );
74
+ }
75
+
76
+ // Optional alias: as <identifier>
77
+ let mut alias: Option<String> = None;
78
+ if parser.peek_is("as") {
79
+ // consume 'as'
80
+ parser.advance();
81
+ let Some(next) = parser.peek_clone() else {
82
+ return crate::core::parser::statement::error_from_token(
83
+ bank_tok,
84
+ "Expected identifier after 'as'".to_string(),
85
+ );
86
+ };
87
+ if next.kind != TokenKind::Identifier {
88
+ return crate::core::parser::statement::error_from_token(
89
+ next,
90
+ "Expected identifier after 'as'".to_string(),
91
+ );
92
+ }
93
+ parser.advance();
94
+ alias = Some(next.lexeme.clone());
95
+ }
96
+
34
97
  Statement {
35
- kind: StatementKind::Bank,
98
+ kind: StatementKind::Bank { alias },
36
99
  value: bank_value,
37
- indent: bank_token.indent,
38
- line: bank_token.line,
39
- column: bank_token.column,
100
+ indent: bank_tok.indent,
101
+ line: bank_tok.line,
102
+ column: bank_tok.column,
40
103
  }
41
104
  }
@@ -1,10 +1,13 @@
1
- use std::collections::HashMap;
2
1
  use crate::core::{
3
2
  lexer::token::TokenKind,
4
- parser::{statement::{Statement, StatementKind}, driver::Parser},
5
- shared::value::Value,
3
+ parser::{
4
+ driver::parser::Parser,
5
+ statement::{Statement, StatementKind},
6
+ },
6
7
  store::global::GlobalStore,
7
8
  };
9
+ use devalang_types::Value;
10
+ use std::collections::HashMap;
8
11
 
9
12
  pub fn parse_condition_token(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
10
13
  parser.advance(); // consume 'if'
@@ -13,7 +16,10 @@ pub fn parse_condition_token(parser: &mut Parser, global_store: &mut GlobalStore
13
16
  };
14
17
 
15
18
  let Some(condition) = parser.parse_condition_until_colon() else {
16
- return Statement::error(if_token, "Expected condition after 'if'".to_string());
19
+ return crate::core::parser::statement::error_from_token(
20
+ if_token,
21
+ "Expected condition after 'if'".to_string(),
22
+ );
17
23
  };
18
24
 
19
25
  parser.advance_if(TokenKind::Colon);
@@ -40,7 +46,10 @@ pub fn parse_condition_token(parser: &mut Parser, global_store: &mut GlobalStore
40
46
  let next_condition = if parser.peek_is("if") {
41
47
  parser.advance(); // consume 'if'
42
48
  let Some(cond) = parser.parse_condition_until_colon() else {
43
- return Statement::error(tok.clone(), "Expected condition after 'else if'".to_string());
49
+ return crate::core::parser::statement::error_from_token(
50
+ tok.clone(),
51
+ "Expected condition after 'else if'".to_string(),
52
+ );
44
53
  };
45
54
  parser.advance_if(TokenKind::Colon);
46
55
  Some(cond)
@@ -1,100 +1,136 @@
1
+ use devalang_types::{Duration, Value};
2
+
1
3
  use crate::core::{
2
4
  lexer::token::TokenKind,
3
- parser::{ statement::{ Statement, StatementKind }, driver::Parser },
4
- shared::{ duration::Duration, value::Value },
5
- store::global::GlobalStore,
5
+ parser::{
6
+ driver::parser::Parser,
7
+ statement::{Statement, StatementKind},
8
+ },
6
9
  };
7
10
 
8
- pub fn parse_dot_token(parser: &mut Parser, _global_store: &mut GlobalStore) -> Statement {
9
- parser.advance(); // consume the dot token
10
-
11
+ pub fn parse_dot_token(
12
+ parser: &mut Parser,
13
+ _global_store: &mut crate::core::store::global::GlobalStore,
14
+ ) -> Statement {
15
+ parser.advance(); // consume '.'
16
+ let logger = devalang_utils::logger::Logger::new();
17
+ use devalang_utils::logger::LogLevel;
11
18
  let Some(dot_token) = parser.previous_clone() else {
12
19
  return Statement::unknown();
13
20
  };
14
21
 
15
- // .kick
16
- let Some(entity_token) = parser.peek_clone() else {
17
- return Statement {
18
- kind: StatementKind::Trigger {
19
- entity: String::new(),
20
- duration: Duration::Auto,
21
- },
22
- value: Value::Null,
23
- indent: dot_token.indent,
24
- line: dot_token.line,
25
- column: dot_token.column,
26
- };
27
- };
28
-
29
- parser.advance(); // consume entity
30
- let entity = entity_token.lexeme.clone();
22
+ // Parse a single entity (namespace-friendly, stops at newline)
23
+ let mut parts = Vec::new();
24
+ let current_line = dot_token.line;
25
+
26
+ while let Some(token) = parser.peek_clone() {
27
+ // Never cross a newline
28
+ if token.line != current_line {
29
+ break;
30
+ }
31
+ match token.kind {
32
+ TokenKind::Identifier | TokenKind::Number => {
33
+ parts.push(token.lexeme.clone());
34
+ parser.advance();
35
+ // The separator must be a '.' on the same line, otherwise stop
36
+ if let Some(next) = parser.peek_clone() {
37
+ if next.line != current_line || next.kind != TokenKind::Dot {
38
+ break;
39
+ }
40
+ } else {
41
+ break;
42
+ }
43
+ }
44
+ TokenKind::Dot => {
45
+ parser.advance();
46
+ }
47
+ TokenKind::Newline | TokenKind::EOF | TokenKind::Indent | TokenKind::Dedent => {
48
+ break; // Stop at newline or dedent
49
+ }
50
+ _ => {
51
+ break;
52
+ }
53
+ }
54
+ }
31
55
 
32
- // Check if there's a duration
33
- let next = parser.peek_clone();
56
+ // Build entity name properly
57
+ let entity = if !parts.is_empty() {
58
+ parts.join(".") // only join within the same line
59
+ } else {
60
+ logger.log_message(
61
+ LogLevel::Warning,
62
+ &format!("Empty entity after '.' at line {}", dot_token.line),
63
+ );
64
+ String::new()
65
+ };
34
66
 
35
- let (duration, value) = match next {
36
- // If no more tokens, it's just `.kick`
37
- None => (Duration::Auto, Value::Null),
67
+ // Optional duration and effects map
68
+ let mut duration = Duration::Auto;
69
+ let mut value = Value::Null;
38
70
 
39
- Some(token) =>
71
+ if let Some(token) = parser.peek_clone() {
72
+ // Duration and effects map are only valid on the same line
73
+ if token.line == current_line {
40
74
  match token.kind {
41
- TokenKind::Newline | TokenKind::EOF => { (Duration::Auto, Value::Null) }
42
-
43
75
  TokenKind::Number => {
44
- let duration_lexeme = token.lexeme.clone();
45
- parser.advance(); // consume duration
46
-
47
- // Try to parse optional value (ex: .kick 250 params)
48
- match parser.peek_clone() {
49
- Some(param_token) if param_token.kind == TokenKind::Identifier => {
50
- parser.advance();
51
- (
52
- parse_duration(duration_lexeme),
53
- Value::Identifier(param_token.lexeme.clone()),
54
- )
76
+ let numerator = token.lexeme.clone();
77
+ parser.advance();
78
+ if let Some(peek) = parser.peek_clone() {
79
+ if peek.line == current_line {
80
+ if let Some(TokenKind::Slash) = parser.peek_kind() {
81
+ parser.advance();
82
+ if let Some(denominator_token) = parser.peek_clone() {
83
+ if denominator_token.line == current_line
84
+ && denominator_token.kind == TokenKind::Number
85
+ {
86
+ let denominator = denominator_token.lexeme.clone();
87
+ parser.advance();
88
+ duration = Duration::Beat(format!(
89
+ "{}/{}",
90
+ numerator, denominator
91
+ ));
92
+ }
93
+ }
94
+ } else {
95
+ duration = parse_duration(numerator);
96
+ }
97
+ } else {
98
+ duration = parse_duration(numerator);
55
99
  }
56
-
57
- Some(param_token) if param_token.kind == TokenKind::LBrace => {
58
- // Handle value as Map
59
- let map = parser.parse_map_value(); // Assumes you have a helper for map
60
- (parse_duration(duration_lexeme), map.unwrap_or(Value::Null))
100
+ } else {
101
+ duration = parse_duration(numerator);
102
+ }
103
+ if let Some(next) = parser.peek_clone() {
104
+ if next.line == current_line && next.kind == TokenKind::LBrace {
105
+ value = parser.parse_map_value().unwrap_or(Value::Null);
61
106
  }
62
-
63
- _ => (parse_duration(duration_lexeme), Value::Null),
64
107
  }
65
108
  }
66
-
67
109
  TokenKind::Identifier => {
68
- let duration_lexeme = token.lexeme.clone();
69
- parser.advance(); // consume duration
70
-
71
- // Try to parse optional value (ex: .kick auto params)
72
- match parser.peek_clone() {
73
- Some(param_token) if param_token.kind == TokenKind::Identifier => {
74
- parser.advance();
75
- (
76
- parse_duration(duration_lexeme),
77
- Value::Identifier(param_token.lexeme.clone()),
78
- )
110
+ let id = token.lexeme.clone();
111
+ parser.advance();
112
+ duration = parse_duration(id);
113
+ if let Some(next) = parser.peek_clone() {
114
+ if next.line == current_line && next.kind == TokenKind::LBrace {
115
+ value = parser.parse_map_value().unwrap_or(Value::Null);
79
116
  }
80
-
81
- Some(param_token) if param_token.kind == TokenKind::LBrace => {
82
- // Handle value as Map
83
- let map = parser.parse_map_value(); // Assumes you have a helper for map
84
- (parse_duration(duration_lexeme), map.unwrap_or(Value::Null))
85
- }
86
-
87
- _ => (parse_duration(duration_lexeme), Value::Null),
88
117
  }
89
118
  }
90
-
91
- _ => (Duration::Auto, Value::Null),
119
+ TokenKind::LBrace => {
120
+ value = parser.parse_map_value().unwrap_or(Value::Null);
121
+ }
122
+ _ => {}
92
123
  }
93
- };
124
+ }
125
+ }
94
126
 
95
127
  Statement {
96
- kind: StatementKind::Trigger { entity, duration },
97
- value,
128
+ kind: StatementKind::Trigger {
129
+ entity,
130
+ duration,
131
+ effects: Some(value.clone()),
132
+ },
133
+ value: Value::Null,
98
134
  indent: dot_token.indent,
99
135
  line: dot_token.line,
100
136
  column: dot_token.column,
@@ -104,8 +140,8 @@ pub fn parse_dot_token(parser: &mut Parser, _global_store: &mut GlobalStore) ->
104
140
  fn parse_duration(s: String) -> Duration {
105
141
  if s == "auto" {
106
142
  Duration::Auto
107
- } else if s.parse::<f32>().is_ok() {
108
- Duration::Number(s.parse().unwrap())
143
+ } else if let Ok(num) = s.parse::<f32>() {
144
+ Duration::Number(num)
109
145
  } else {
110
146
  Duration::Identifier(s)
111
147
  }