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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/.cargo/config.toml +2 -0
  2. package/.devalang +10 -4
  3. package/.github/workflows/ci.yml +103 -0
  4. package/Cargo.toml +80 -48
  5. package/README.md +135 -154
  6. package/docs/CHANGELOG.md +386 -1
  7. package/docs/CONTRIBUTING.md +101 -0
  8. package/docs/ROADMAP.md +10 -7
  9. package/docs/TODO.md +21 -9
  10. package/examples/automation.deva +42 -0
  11. package/examples/bank.deva +7 -0
  12. package/examples/duration.deva +9 -0
  13. package/examples/events.deva +12 -0
  14. package/examples/function.deva +15 -0
  15. package/examples/index.deva +57 -12
  16. package/examples/loop.deva +5 -12
  17. package/examples/pattern.deva +8 -0
  18. package/examples/plugin.deva +16 -0
  19. package/examples/variables.deva +1 -1
  20. package/out-tsc/bin/index.d.ts +2 -0
  21. package/out-tsc/bin/index.js +51 -7
  22. package/out-tsc/core/functions/index.d.ts +37 -0
  23. package/out-tsc/core/functions/index.js +76 -0
  24. package/out-tsc/core/index.d.ts +6 -0
  25. package/out-tsc/core/index.js +22 -0
  26. package/out-tsc/core/types/index.d.ts +4 -0
  27. package/out-tsc/core/types/index.js +20 -0
  28. package/out-tsc/core/types/plugin.d.ts +18 -0
  29. package/out-tsc/core/types/plugin.js +2 -0
  30. package/out-tsc/core/types/result.d.ts +27 -0
  31. package/out-tsc/core/types/result.js +2 -0
  32. package/out-tsc/core/types/statement.d.ts +106 -0
  33. package/out-tsc/core/types/statement.js +2 -0
  34. package/out-tsc/core/types/value.d.ts +43 -0
  35. package/out-tsc/core/types/value.js +2 -0
  36. package/out-tsc/index.d.ts +7 -0
  37. package/out-tsc/index.js +42 -1
  38. package/out-tsc/pkg/devalang_core.d.ts +13 -0
  39. package/out-tsc/pkg/devalang_core.js +50 -0
  40. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +33 -0
  41. package/out-tsc/scripts/copy-wasm-dts.d.ts +1 -0
  42. package/out-tsc/scripts/copy-wasm-dts.js +73 -0
  43. package/out-tsc/scripts/postinstall.d.ts +1 -0
  44. package/out-tsc/scripts/postinstall.js +83 -0
  45. package/out-tsc/scripts/version/bump.d.ts +1 -0
  46. package/out-tsc/scripts/version/fetch.d.ts +1 -0
  47. package/out-tsc/scripts/version/index.d.ts +1 -0
  48. package/out-tsc/scripts/version/sync.d.ts +1 -0
  49. package/package.json +28 -7
  50. package/project-version.json +4 -4
  51. package/rust/cli/bank/api.rs +122 -0
  52. package/rust/cli/bank/commands.rs +275 -0
  53. package/rust/cli/bank/mod.rs +29 -0
  54. package/rust/cli/build/commands.rs +103 -0
  55. package/rust/cli/build/mod.rs +2 -0
  56. package/rust/cli/build/process.rs +146 -0
  57. package/rust/cli/check/mod.rs +208 -0
  58. package/rust/cli/discover/commands.rs +253 -0
  59. package/rust/cli/discover/config.rs +111 -0
  60. package/rust/cli/discover/fs.rs +19 -0
  61. package/rust/cli/discover/install.rs +103 -0
  62. package/rust/cli/discover/metadata.rs +48 -0
  63. package/rust/cli/discover/mod.rs +5 -0
  64. package/rust/cli/{init.rs → init/commands.rs} +32 -23
  65. package/rust/cli/init/mod.rs +1 -0
  66. package/rust/cli/install/addon.rs +118 -0
  67. package/rust/cli/install/bank.rs +53 -0
  68. package/rust/cli/install/commands.rs +35 -0
  69. package/rust/cli/install/mod.rs +4 -0
  70. package/rust/cli/install/plugin.rs +61 -0
  71. package/rust/cli/login/commands.rs +124 -0
  72. package/rust/cli/login/mod.rs +1 -0
  73. package/rust/cli/mod.rs +12 -205
  74. package/rust/cli/parser.rs +314 -0
  75. package/rust/cli/play/commands.rs +324 -0
  76. package/rust/cli/play/io.rs +17 -0
  77. package/rust/cli/play/mod.rs +5 -0
  78. package/rust/cli/play/process.rs +150 -0
  79. package/rust/cli/play/realtime.rs +91 -0
  80. package/rust/cli/play/utils.rs +23 -0
  81. package/rust/cli/telemetry/commands.rs +22 -0
  82. package/rust/cli/telemetry/event_creator.rs +80 -0
  83. package/rust/cli/telemetry/mod.rs +3 -0
  84. package/rust/cli/telemetry/send.rs +51 -0
  85. package/rust/cli/{template.rs → template/commands.rs} +69 -57
  86. package/rust/cli/template/mod.rs +1 -0
  87. package/rust/cli/update/commands.rs +6 -0
  88. package/rust/cli/update/mod.rs +1 -0
  89. package/rust/config/driver.rs +103 -0
  90. package/rust/config/mod.rs +3 -16
  91. package/rust/config/ops.rs +26 -0
  92. package/rust/config/settings.rs +101 -0
  93. package/rust/core/audio/engine/helpers.rs +170 -0
  94. package/rust/core/audio/engine/mod.rs +7 -0
  95. package/rust/core/audio/engine/sample.rs +366 -0
  96. package/rust/core/audio/engine/synth.rs +325 -0
  97. package/rust/core/audio/evaluator.rs +310 -31
  98. package/rust/core/audio/interpreter/arrow_call.rs +311 -129
  99. package/rust/core/audio/interpreter/automate.rs +18 -0
  100. package/rust/core/audio/interpreter/call.rs +294 -64
  101. package/rust/core/audio/interpreter/condition.rs +71 -69
  102. package/rust/core/audio/interpreter/driver.rs +542 -216
  103. package/rust/core/audio/interpreter/function.rs +26 -0
  104. package/rust/core/audio/interpreter/let_.rs +38 -19
  105. package/rust/core/audio/interpreter/load.rs +19 -18
  106. package/rust/core/audio/interpreter/loop_.rs +114 -67
  107. package/rust/core/audio/interpreter/mod.rs +14 -12
  108. package/rust/core/audio/interpreter/sleep.rs +28 -36
  109. package/rust/core/audio/interpreter/spawn.rs +252 -66
  110. package/rust/core/audio/interpreter/tempo.rs +40 -16
  111. package/rust/core/audio/interpreter/trigger.rs +239 -69
  112. package/rust/core/audio/loader/mod.rs +1 -1
  113. package/rust/core/audio/loader/trigger.rs +97 -52
  114. package/rust/core/audio/mod.rs +7 -6
  115. package/rust/core/audio/player.rs +70 -54
  116. package/rust/core/audio/renderer.rs +54 -54
  117. package/rust/core/audio/special/easing.rs +189 -0
  118. package/rust/core/audio/special/env.rs +45 -0
  119. package/rust/core/audio/special/math.rs +134 -0
  120. package/rust/core/audio/special/mod.rs +9 -0
  121. package/rust/core/audio/special/modulator.rs +143 -0
  122. package/rust/core/builder/mod.rs +86 -80
  123. package/rust/core/debugger/lexer.rs +27 -27
  124. package/rust/core/debugger/mod.rs +30 -21
  125. package/rust/core/debugger/module.rs +55 -0
  126. package/rust/core/debugger/preprocessor.rs +27 -27
  127. package/rust/core/debugger/store.rs +40 -25
  128. package/rust/core/error/mod.rs +269 -60
  129. package/rust/core/lexer/driver.rs +61 -0
  130. package/rust/core/lexer/handler/arrow.rs +82 -31
  131. package/rust/core/lexer/handler/at.rs +21 -21
  132. package/rust/core/lexer/handler/brace.rs +41 -41
  133. package/rust/core/lexer/handler/colon.rs +21 -21
  134. package/rust/core/lexer/handler/comment.rs +30 -30
  135. package/rust/core/lexer/handler/dot.rs +21 -21
  136. package/rust/core/lexer/handler/driver.rs +337 -226
  137. package/rust/core/lexer/handler/identifier.rs +47 -41
  138. package/rust/core/lexer/handler/indent.rs +66 -52
  139. package/rust/core/lexer/handler/mod.rs +15 -14
  140. package/rust/core/lexer/handler/newline.rs +23 -23
  141. package/rust/core/lexer/handler/number.rs +31 -31
  142. package/rust/core/lexer/handler/operator.rs +46 -44
  143. package/rust/core/lexer/handler/parenthesis.rs +41 -0
  144. package/rust/core/lexer/handler/slash.rs +21 -0
  145. package/rust/core/lexer/handler/string.rs +63 -63
  146. package/rust/core/lexer/mod.rs +3 -51
  147. package/rust/core/lexer/token.rs +17 -12
  148. package/rust/core/mod.rs +10 -10
  149. package/rust/core/parser/driver.rs +584 -331
  150. package/rust/core/parser/handler/arrow_call.rs +253 -126
  151. package/rust/core/parser/handler/at.rs +279 -162
  152. package/rust/core/parser/handler/bank.rs +104 -41
  153. package/rust/core/parser/handler/condition.rs +83 -74
  154. package/rust/core/parser/handler/dot.rs +148 -112
  155. package/rust/core/parser/handler/identifier/automate.rs +254 -0
  156. package/rust/core/parser/handler/identifier/call.rs +91 -41
  157. package/rust/core/parser/handler/identifier/emit.rs +70 -0
  158. package/rust/core/parser/handler/identifier/function.rs +113 -0
  159. package/rust/core/parser/handler/identifier/group.rs +89 -75
  160. package/rust/core/parser/handler/identifier/let_.rs +173 -133
  161. package/rust/core/parser/handler/identifier/mod.rs +55 -51
  162. package/rust/core/parser/handler/identifier/on.rs +107 -0
  163. package/rust/core/parser/handler/identifier/print.rs +49 -0
  164. package/rust/core/parser/handler/identifier/sleep.rs +43 -33
  165. package/rust/core/parser/handler/identifier/spawn.rs +91 -41
  166. package/rust/core/parser/handler/identifier/synth.rs +135 -65
  167. package/rust/core/parser/handler/loop_.rs +194 -72
  168. package/rust/core/parser/handler/mod.rs +9 -8
  169. package/rust/core/parser/handler/pattern.rs +74 -0
  170. package/rust/core/parser/handler/tempo.rs +57 -47
  171. package/rust/core/parser/mod.rs +3 -4
  172. package/rust/core/parser/statement.rs +11 -96
  173. package/rust/core/plugin/loader.rs +137 -0
  174. package/rust/core/plugin/mod.rs +2 -0
  175. package/rust/core/plugin/runner.rs +347 -0
  176. package/rust/core/preprocessor/loader.rs +637 -193
  177. package/rust/core/preprocessor/mod.rs +4 -4
  178. package/rust/core/preprocessor/module.rs +60 -50
  179. package/rust/core/preprocessor/processor.rs +114 -76
  180. package/rust/core/preprocessor/resolver/bank.rs +49 -47
  181. package/rust/core/preprocessor/resolver/call.rs +124 -123
  182. package/rust/core/preprocessor/resolver/condition.rs +95 -92
  183. package/rust/core/preprocessor/resolver/driver.rs +324 -227
  184. package/rust/core/preprocessor/resolver/function.rs +69 -0
  185. package/rust/core/preprocessor/resolver/group.rs +94 -61
  186. package/rust/core/preprocessor/resolver/let_.rs +32 -31
  187. package/rust/core/preprocessor/resolver/loop_.rs +318 -91
  188. package/rust/core/preprocessor/resolver/mod.rs +16 -14
  189. package/rust/core/preprocessor/resolver/pattern.rs +83 -0
  190. package/rust/core/preprocessor/resolver/spawn.rs +99 -58
  191. package/rust/core/preprocessor/resolver/synth.rs +54 -50
  192. package/rust/core/preprocessor/resolver/tempo.rs +48 -49
  193. package/rust/core/preprocessor/resolver/trigger.rs +116 -112
  194. package/rust/core/preprocessor/resolver/value.rs +176 -78
  195. package/rust/core/store/export.rs +28 -28
  196. package/rust/core/store/function.rs +40 -0
  197. package/rust/core/store/global.rs +61 -39
  198. package/rust/core/store/import.rs +28 -28
  199. package/rust/core/store/mod.rs +5 -4
  200. package/rust/core/store/variable.rs +51 -28
  201. package/rust/core/utils/mod.rs +1 -2
  202. package/rust/core/utils/path.rs +37 -31
  203. package/rust/lib.rs +308 -117
  204. package/rust/main.rs +364 -65
  205. package/rust/types/Cargo.toml +11 -0
  206. package/rust/types/src/addons.rs +55 -0
  207. package/rust/types/src/ast.rs +202 -0
  208. package/rust/types/src/config.rs +74 -0
  209. package/rust/types/src/lib.rs +12 -0
  210. package/rust/types/src/telemetry.rs +85 -0
  211. package/rust/utils/Cargo.toml +26 -0
  212. package/rust/utils/src/error.rs +186 -0
  213. package/rust/utils/src/file.rs +94 -0
  214. package/rust/utils/src/first_usage.rs +97 -0
  215. package/rust/utils/{mod.rs → src/lib.rs} +9 -6
  216. package/rust/utils/{logger.rs → src/logger.rs} +200 -123
  217. package/rust/utils/src/path.rs +88 -0
  218. package/rust/utils/src/signature.rs +41 -0
  219. package/rust/utils/{spinner.rs → src/spinner.rs} +20 -21
  220. package/rust/utils/src/version.rs +27 -0
  221. package/rust/utils/{watcher.rs → src/watcher.rs} +46 -33
  222. package/rust/web/api.rs +5 -0
  223. package/rust/web/cdn.rs +34 -0
  224. package/rust/web/mod.rs +3 -0
  225. package/rust/web/sso.rs +5 -0
  226. package/templates/minimal/README.md +143 -127
  227. package/templates/welcome/README.md +143 -127
  228. package/templates/welcome/src/index.deva +56 -8
  229. package/templates/welcome/src/variables.deva +2 -4
  230. package/tests/integration.rs +21 -0
  231. package/tests/rust/cli_check_build.rs +21 -0
  232. package/tests/rust/cli_help.rs +12 -0
  233. package/tests/rust/cli_template_list.rs +10 -0
  234. package/tests/rust/cli_version.rs +11 -0
  235. package/tests/typescript/index.spec.ts +136 -0
  236. package/tests/typescript/playhead.spec.ts +36 -0
  237. package/tests/typescript/render_e2e.spec.ts +77 -0
  238. package/tsconfig.json +12 -10
  239. package/typescript/bin/index.ts +19 -5
  240. package/typescript/core/functions/index.ts +83 -0
  241. package/typescript/core/index.ts +6 -0
  242. package/typescript/core/types/index.ts +4 -0
  243. package/typescript/core/types/plugin.ts +19 -0
  244. package/typescript/core/types/result.ts +29 -0
  245. package/typescript/core/types/statement.ts +47 -0
  246. package/typescript/core/types/value.ts +29 -0
  247. package/typescript/index.ts +8 -1
  248. package/typescript/pkg/devalang_core.d.ts +4 -0
  249. package/typescript/pkg/devalang_core.ts +49 -0
  250. package/typescript/scripts/copy-wasm-dts.ts +41 -0
  251. package/typescript/scripts/postinstall.ts +85 -0
  252. package/typescript/scripts/version/bump.ts +0 -1
  253. package/typescript/scripts/version/index.ts +0 -1
  254. package/docs/COMMANDS.md +0 -85
  255. package/docs/CONFIG.md +0 -30
  256. package/docs/SYNTAX.md +0 -210
  257. package/out-tsc/bin/devalang.exe +0 -0
  258. package/out-tsc/scripts/postbuild.js +0 -11
  259. package/rust/cli/build.rs +0 -137
  260. package/rust/cli/check.rs +0 -117
  261. package/rust/cli/play.rs +0 -193
  262. package/rust/config/loader.rs +0 -13
  263. package/rust/core/audio/engine.rs +0 -203
  264. package/rust/core/shared/duration.rs +0 -8
  265. package/rust/core/shared/mod.rs +0 -2
  266. package/rust/core/shared/value.rs +0 -18
  267. package/rust/core/utils/validation.rs +0 -37
  268. package/rust/utils/file.rs +0 -35
  269. package/rust/utils/signature.rs +0 -17
  270. package/rust/utils/version.rs +0 -15
  271. package/typescript/scripts/postbuild.ts +0 -8
@@ -1,72 +1,194 @@
1
- use std::collections::HashMap;
2
-
3
- use crate::core::{
4
- lexer::{ token::TokenKind },
5
- parser::{ statement::{ Statement, StatementKind }, driver::Parser },
6
- shared::value::Value,
7
- store::global::GlobalStore,
8
- };
9
-
10
- pub fn parse_loop_token(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
11
- parser.advance(); // consume 'loop'
12
- let Some(loop_token) = parser.previous_clone() else {
13
- return Statement::unknown();
14
- };
15
-
16
- let Some(iterator_token) = parser.peek_clone() else {
17
- return Statement::error(loop_token, "Expected number or identifier after 'loop'".to_string());
18
- };
19
-
20
- let iterator_value = match iterator_token.kind {
21
- TokenKind::Number => {
22
- let val = iterator_token.lexeme.parse::<f32>().unwrap_or(1.0);
23
- parser.advance();
24
- Value::Number(val)
25
- }
26
- TokenKind::Identifier => {
27
- let val = iterator_token.lexeme.clone();
28
- parser.advance();
29
- Value::Identifier(val)
30
- }
31
- _ => {
32
- return Statement::error(
33
- iterator_token.clone(),
34
- "Expected a number or identifier as loop count".to_string()
35
- );
36
- }
37
- };
38
-
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);
47
- }
48
-
49
- parser.advance(); // consume ':'
50
-
51
- // Collect body
52
- let tokens = parser.collect_until(|t| t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF);
53
- let loop_body = parser.parse_block(tokens.clone(), global_store);
54
-
55
- if let Some(token) = parser.peek() {
56
- if token.kind == TokenKind::Dedent {
57
- parser.advance();
58
- }
59
- }
60
-
61
- let mut value_map = HashMap::new();
62
- value_map.insert("iterator".to_string(), iterator_value);
63
- value_map.insert("body".to_string(), Value::Block(loop_body.clone()));
64
-
65
- Statement {
66
- kind: StatementKind::Loop,
67
- value: Value::Map(value_map),
68
- indent: loop_token.indent,
69
- line: loop_token.line,
70
- column: loop_token.column,
71
- }
72
- }
1
+ use devalang_types::Value;
2
+
3
+ use crate::core::{
4
+ lexer::token::TokenKind,
5
+ parser::{
6
+ driver::Parser,
7
+ statement::{Statement, StatementKind},
8
+ },
9
+ store::global::GlobalStore,
10
+ };
11
+
12
+ pub fn parse_loop_token(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
13
+ parser.advance(); // consume 'loop' or 'for' (aliased in lexer)
14
+ let Some(loop_token) = parser.previous_clone() else {
15
+ return Statement::unknown();
16
+ };
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>:
126
+ let Some(iterator_token) = parser.peek_clone() else {
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
+ );
133
+ };
134
+
135
+ let iterator_value = match iterator_token.kind {
136
+ TokenKind::Number => {
137
+ let val = iterator_token.lexeme.parse::<f32>().unwrap_or(1.0);
138
+ parser.advance();
139
+ Value::Number(val)
140
+ }
141
+ TokenKind::Identifier => {
142
+ let val = iterator_token.lexeme.clone();
143
+ parser.advance();
144
+ Value::Identifier(val)
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
+ }
152
+ _ => {
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(),
158
+ );
159
+ }
160
+ };
161
+
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
+ );
173
+ }
174
+
175
+ let tokens = parser.collect_until(|t| t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF);
176
+ let loop_body = parser.parse_block(tokens.clone(), global_store);
177
+ if let Some(token) = parser.peek() {
178
+ if token.kind == TokenKind::Dedent {
179
+ parser.advance();
180
+ }
181
+ }
182
+
183
+ let mut value_map = std::collections::HashMap::new();
184
+ value_map.insert("iterator".to_string(), iterator_value);
185
+ value_map.insert("body".to_string(), Value::Block(loop_body.clone()));
186
+
187
+ Statement {
188
+ kind: StatementKind::Loop,
189
+ value: Value::Map(value_map),
190
+ indent: loop_token.indent,
191
+ line: loop_token.line,
192
+ column: loop_token.column,
193
+ }
194
+ }
@@ -1,8 +1,9 @@
1
- pub mod at;
2
- pub mod identifier;
3
- pub mod dot;
4
- pub mod tempo;
5
- pub mod bank;
6
- pub mod loop_;
7
- pub mod condition;
8
- pub mod arrow_call;
1
+ pub mod arrow_call;
2
+ pub mod at;
3
+ pub mod bank;
4
+ pub mod condition;
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,
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,47 +1,57 @@
1
- use crate::core::{
2
- lexer::token::TokenKind,
3
- parser::{ statement::{ Statement, StatementKind }, driver::Parser },
4
- shared::value::Value,
5
- store::global::GlobalStore,
6
- };
7
-
8
- pub fn parse_tempo_token(parser: &mut Parser, _global_store: &mut GlobalStore) -> Statement {
9
- parser.advance(); // consume 'bpm'
10
-
11
- let Some(tempo_token) = parser.previous_clone() else {
12
- return Statement::unknown();
13
- };
14
-
15
- // Expect a number or identifier
16
- 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
- );
21
- };
22
-
23
- let value = match value_token.kind {
24
- TokenKind::Number => {
25
- parser.advance();
26
- Value::Number(value_token.lexeme.parse().unwrap_or(0.0))
27
- }
28
- TokenKind::Identifier => {
29
- parser.advance();
30
- Value::Identifier(value_token.lexeme.clone())
31
- }
32
- _ => {
33
- return Statement::error(
34
- value_token.clone(),
35
- format!("Expected a number or identifier after 'bpm', got {:?}", value_token.kind)
36
- );
37
- }
38
- };
39
-
40
- Statement {
41
- kind: StatementKind::Tempo,
42
- value,
43
- indent: tempo_token.indent,
44
- line: tempo_token.line,
45
- column: tempo_token.column,
46
- }
47
- }
1
+ use crate::core::{
2
+ lexer::token::TokenKind,
3
+ parser::{
4
+ driver::Parser,
5
+ statement::{Statement, StatementKind},
6
+ },
7
+ store::global::GlobalStore,
8
+ };
9
+ use devalang_types::Value;
10
+
11
+ pub fn parse_tempo_token(parser: &mut Parser, _global_store: &mut GlobalStore) -> Statement {
12
+ parser.advance(); // consume 'bpm'
13
+
14
+ let Some(tempo_token) = parser.previous_clone() else {
15
+ return Statement::unknown();
16
+ };
17
+
18
+ // Expect a number or identifier
19
+ let Some(value_token) = parser.peek_clone() else {
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(),
25
+ );
26
+ };
27
+
28
+ let value = match value_token.kind {
29
+ TokenKind::Number => {
30
+ parser.advance();
31
+ Value::Number(value_token.lexeme.parse().unwrap_or(0.0))
32
+ }
33
+ TokenKind::Identifier => {
34
+ parser.advance();
35
+ Value::Identifier(value_token.lexeme.clone())
36
+ }
37
+ _ => {
38
+ return Statement::error_with_pos(
39
+ value_token.indent,
40
+ value_token.line,
41
+ value_token.column,
42
+ format!(
43
+ "Expected a number or identifier after 'bpm', got {:?}",
44
+ value_token.kind
45
+ ),
46
+ );
47
+ }
48
+ };
49
+
50
+ Statement {
51
+ kind: StatementKind::Tempo,
52
+ value,
53
+ indent: tempo_token.indent,
54
+ line: tempo_token.line,
55
+ column: tempo_token.column,
56
+ }
57
+ }
@@ -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,11 @@
1
- use serde::{ Deserialize, Serialize };
2
- use crate::core::{ lexer::token::Token, shared::{ duration::Duration, value::Value } };
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,
11
- }
12
-
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
- }
33
- }
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
- }
1
+ use crate::core::lexer::token::Token;
2
+
3
+ pub use devalang_types::{Duration, Statement, StatementKind, Value};
4
+
5
+ pub fn unknown_from_token(token: &Token) -> Statement {
6
+ Statement::unknown_with_pos(token.indent, token.line, token.column)
7
+ }
8
+
9
+ pub fn error_from_token(token: Token, message: String) -> Statement {
10
+ Statement::error_with_pos(token.indent, token.line, token.column, message)
11
+ }