@devaloop/devalang 0.0.1-alpha.16-hotfix.1 → 0.0.1-alpha.17

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 (235) hide show
  1. package/.cargo/config.toml +2 -0
  2. package/.devalang +6 -10
  3. package/.github/workflows/ci.yml +19 -8
  4. package/Cargo.toml +18 -2
  5. package/README.md +80 -33
  6. package/docs/CHANGELOG.md +56 -0
  7. package/docs/ROADMAP.md +6 -3
  8. package/examples/index.deva +52 -35
  9. package/out-tsc/bin/index.d.ts +2 -0
  10. package/out-tsc/core/functions/index.d.ts +37 -0
  11. package/out-tsc/core/functions/index.js +76 -0
  12. package/out-tsc/core/index.d.ts +6 -0
  13. package/out-tsc/core/index.js +22 -0
  14. package/out-tsc/core/types/index.d.ts +4 -0
  15. package/out-tsc/core/types/index.js +20 -0
  16. package/out-tsc/core/types/plugin.d.ts +18 -0
  17. package/out-tsc/core/types/plugin.js +2 -0
  18. package/out-tsc/core/types/result.d.ts +27 -0
  19. package/out-tsc/core/types/result.js +2 -0
  20. package/out-tsc/core/types/statement.d.ts +106 -0
  21. package/out-tsc/core/types/statement.js +2 -0
  22. package/out-tsc/core/types/value.d.ts +43 -0
  23. package/out-tsc/core/types/value.js +2 -0
  24. package/out-tsc/index.d.ts +7 -0
  25. package/out-tsc/index.js +41 -2
  26. package/out-tsc/pkg/devalang_core.d.ts +7 -0
  27. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +33 -0
  28. package/out-tsc/scripts/copy-wasm-dts.d.ts +1 -0
  29. package/out-tsc/scripts/copy-wasm-dts.js +73 -0
  30. package/out-tsc/scripts/postinstall.d.ts +1 -0
  31. package/out-tsc/scripts/postinstall.js +33 -23
  32. package/out-tsc/scripts/version/bump.d.ts +1 -0
  33. package/out-tsc/scripts/version/fetch.d.ts +1 -0
  34. package/out-tsc/scripts/version/index.d.ts +1 -0
  35. package/out-tsc/scripts/version/sync.d.ts +1 -0
  36. package/package.json +16 -4
  37. package/project-version.json +3 -3
  38. package/rust/cli/bank/api.rs +122 -0
  39. package/rust/cli/bank/commands.rs +275 -0
  40. package/rust/cli/bank/mod.rs +29 -0
  41. package/rust/cli/build/commands.rs +97 -0
  42. package/rust/cli/build/mod.rs +2 -0
  43. package/rust/cli/build/process.rs +146 -0
  44. package/rust/cli/{check.rs → check/mod.rs} +18 -31
  45. package/rust/cli/discover/commands.rs +253 -0
  46. package/rust/cli/discover/config.rs +111 -0
  47. package/rust/cli/discover/fs.rs +19 -0
  48. package/rust/cli/discover/install.rs +103 -0
  49. package/rust/cli/discover/metadata.rs +48 -0
  50. package/rust/cli/discover/mod.rs +5 -0
  51. package/rust/cli/{init.rs → init/commands.rs} +88 -87
  52. package/rust/cli/init/mod.rs +1 -0
  53. package/rust/{installer → cli/install}/addon.rs +5 -9
  54. package/rust/cli/install/bank.rs +53 -0
  55. package/rust/cli/{install.rs → install/commands.rs} +9 -9
  56. package/rust/{installer → cli/install}/mod.rs +2 -3
  57. package/rust/cli/install/plugin.rs +61 -0
  58. package/rust/cli/{login.rs → login/commands.rs} +8 -11
  59. package/rust/cli/login/mod.rs +1 -0
  60. package/rust/cli/mod.rs +2 -3
  61. package/rust/cli/{driver.rs → parser.rs} +19 -2
  62. package/rust/cli/play/commands.rs +324 -0
  63. package/rust/cli/play/io.rs +17 -0
  64. package/rust/cli/play/mod.rs +5 -0
  65. package/rust/cli/play/process.rs +150 -0
  66. package/rust/cli/play/realtime.rs +91 -0
  67. package/rust/cli/play/utils.rs +23 -0
  68. package/rust/cli/telemetry/commands.rs +22 -0
  69. package/rust/cli/telemetry/event_creator.rs +80 -0
  70. package/rust/cli/telemetry/mod.rs +3 -0
  71. package/rust/cli/telemetry/send.rs +51 -0
  72. package/rust/cli/{template.rs → template/commands.rs} +1 -1
  73. package/rust/cli/template/mod.rs +1 -0
  74. package/rust/cli/{update.rs → update/commands.rs} +6 -6
  75. package/rust/cli/update/mod.rs +1 -0
  76. package/rust/config/driver.rs +57 -72
  77. package/rust/config/mod.rs +1 -2
  78. package/rust/config/ops.rs +26 -0
  79. package/rust/config/settings.rs +60 -50
  80. package/rust/core/audio/engine/helpers.rs +146 -0
  81. package/rust/core/audio/engine/mod.rs +7 -0
  82. package/rust/core/audio/engine/sample.rs +298 -0
  83. package/rust/core/audio/engine/synth.rs +310 -0
  84. package/rust/core/audio/evaluator.rs +15 -12
  85. package/rust/core/audio/interpreter/arrow_call.rs +99 -24
  86. package/rust/core/audio/interpreter/call.rs +81 -60
  87. package/rust/core/audio/interpreter/condition.rs +3 -2
  88. package/rust/core/audio/interpreter/driver.rs +206 -151
  89. package/rust/core/audio/interpreter/let_.rs +1 -1
  90. package/rust/core/audio/interpreter/load.rs +2 -1
  91. package/rust/core/audio/interpreter/loop_.rs +7 -6
  92. package/rust/core/audio/interpreter/sleep.rs +2 -1
  93. package/rust/core/audio/interpreter/spawn.rs +45 -57
  94. package/rust/core/audio/interpreter/tempo.rs +31 -10
  95. package/rust/core/audio/interpreter/trigger.rs +2 -2
  96. package/rust/core/audio/loader/trigger.rs +4 -7
  97. package/rust/core/audio/player.rs +6 -0
  98. package/rust/core/audio/renderer.rs +5 -7
  99. package/rust/core/audio/special/env.rs +3 -1
  100. package/rust/core/audio/special/math.rs +4 -4
  101. package/rust/core/audio/special/modulator.rs +2 -2
  102. package/rust/core/builder/mod.rs +9 -3
  103. package/rust/core/debugger/lexer.rs +1 -1
  104. package/rust/core/debugger/mod.rs +6 -0
  105. package/rust/core/debugger/module.rs +4 -4
  106. package/rust/core/debugger/preprocessor.rs +1 -1
  107. package/rust/core/debugger/store.rs +2 -2
  108. package/rust/core/error/mod.rs +189 -0
  109. package/rust/core/lexer/handler/arrow.rs +1 -1
  110. package/rust/core/lexer/handler/at.rs +1 -1
  111. package/rust/core/lexer/handler/brace.rs +2 -2
  112. package/rust/core/lexer/handler/colon.rs +1 -1
  113. package/rust/core/lexer/handler/comment.rs +1 -1
  114. package/rust/core/lexer/handler/dot.rs +1 -1
  115. package/rust/core/lexer/handler/driver.rs +1 -1
  116. package/rust/core/lexer/handler/identifier.rs +1 -1
  117. package/rust/core/lexer/handler/mod.rs +1 -2
  118. package/rust/core/lexer/handler/number.rs +1 -1
  119. package/rust/core/lexer/handler/operator.rs +1 -1
  120. package/rust/core/lexer/handler/parenthesis.rs +2 -2
  121. package/rust/core/lexer/handler/slash.rs +1 -1
  122. package/rust/core/lexer/handler/string.rs +1 -1
  123. package/rust/core/lexer/mod.rs +22 -12
  124. package/rust/core/lexer/token.rs +90 -97
  125. package/rust/core/mod.rs +0 -1
  126. package/rust/core/parser/driver.rs +66 -13
  127. package/rust/core/parser/handler/arrow_call.rs +28 -8
  128. package/rust/core/parser/handler/at.rs +55 -21
  129. package/rust/core/parser/handler/bank.rs +14 -4
  130. package/rust/core/parser/handler/condition.rs +6 -3
  131. package/rust/core/parser/handler/dot.rs +2 -1
  132. package/rust/core/parser/handler/identifier/automate.rs +13 -16
  133. package/rust/core/parser/handler/identifier/call.rs +4 -4
  134. package/rust/core/parser/handler/identifier/emit.rs +9 -5
  135. package/rust/core/parser/handler/identifier/function.rs +20 -7
  136. package/rust/core/parser/handler/identifier/group.rs +11 -7
  137. package/rust/core/parser/handler/identifier/let_.rs +24 -9
  138. package/rust/core/parser/handler/identifier/mod.rs +6 -5
  139. package/rust/core/parser/handler/identifier/on.rs +16 -7
  140. package/rust/core/parser/handler/identifier/print.rs +6 -9
  141. package/rust/core/parser/handler/identifier/sleep.rs +12 -5
  142. package/rust/core/parser/handler/identifier/spawn.rs +4 -4
  143. package/rust/core/parser/handler/identifier/synth.rs +79 -9
  144. package/rust/core/parser/handler/loop_.rs +39 -14
  145. package/rust/core/parser/handler/tempo.rs +9 -5
  146. package/rust/core/parser/mod.rs +0 -1
  147. package/rust/core/parser/statement.rs +6 -137
  148. package/rust/core/plugin/loader.rs +41 -27
  149. package/rust/core/plugin/runner.rs +68 -17
  150. package/rust/core/preprocessor/loader.rs +155 -33
  151. package/rust/core/preprocessor/processor.rs +2 -2
  152. package/rust/core/preprocessor/resolver/bank.rs +6 -8
  153. package/rust/core/preprocessor/resolver/call.rs +20 -24
  154. package/rust/core/preprocessor/resolver/condition.rs +6 -8
  155. package/rust/core/preprocessor/resolver/driver.rs +14 -16
  156. package/rust/core/preprocessor/resolver/function.rs +6 -6
  157. package/rust/core/preprocessor/resolver/group.rs +6 -8
  158. package/rust/core/preprocessor/resolver/loop_.rs +8 -10
  159. package/rust/core/preprocessor/resolver/spawn.rs +19 -23
  160. package/rust/core/preprocessor/resolver/synth.rs +6 -8
  161. package/rust/core/preprocessor/resolver/tempo.rs +6 -8
  162. package/rust/core/preprocessor/resolver/trigger.rs +22 -19
  163. package/rust/core/preprocessor/resolver/value.rs +99 -4
  164. package/rust/core/store/export.rs +28 -28
  165. package/rust/core/store/function.rs +6 -0
  166. package/rust/core/store/global.rs +7 -1
  167. package/rust/core/store/import.rs +28 -28
  168. package/rust/core/store/variable.rs +1 -1
  169. package/rust/core/utils/mod.rs +0 -1
  170. package/rust/lib.rs +102 -9
  171. package/rust/main.rs +156 -45
  172. package/rust/types/Cargo.toml +8 -0
  173. package/rust/types/src/addons.rs +55 -0
  174. package/rust/types/src/ast.rs +198 -0
  175. package/rust/types/src/config.rs +74 -0
  176. package/rust/types/src/lib.rs +12 -0
  177. package/rust/types/src/telemetry.rs +85 -0
  178. package/rust/utils/Cargo.toml +23 -0
  179. package/rust/utils/{error.rs → src/error.rs} +186 -200
  180. package/rust/utils/src/file.rs +94 -0
  181. package/rust/utils/src/first_usage.rs +97 -0
  182. package/rust/utils/{mod.rs → src/lib.rs} +1 -1
  183. package/rust/utils/{logger.rs → src/logger.rs} +17 -12
  184. package/rust/utils/src/path.rs +88 -0
  185. package/rust/utils/src/signature.rs +41 -0
  186. package/rust/utils/{spinner.rs → src/spinner.rs} +3 -5
  187. package/rust/utils/src/version.rs +27 -0
  188. package/rust/utils/{watcher.rs → src/watcher.rs} +13 -1
  189. package/rust/web/api.rs +5 -0
  190. package/rust/web/cdn.rs +34 -0
  191. package/templates/minimal/README.md +98 -54
  192. package/templates/welcome/README.md +98 -54
  193. package/templates/welcome/src/index.deva +56 -8
  194. package/templates/welcome/src/variables.deva +2 -4
  195. package/tests/rust/TODO.md +0 -0
  196. package/tests/typescript/index.spec.ts +136 -0
  197. package/tests/typescript/playhead.spec.ts +36 -0
  198. package/tests/typescript/render_e2e.spec.ts +77 -0
  199. package/tsconfig.json +1 -1
  200. package/typescript/core/functions/index.ts +83 -0
  201. package/typescript/core/index.ts +6 -0
  202. package/typescript/core/types/index.ts +4 -0
  203. package/typescript/core/types/plugin.ts +19 -0
  204. package/typescript/core/types/result.ts +29 -0
  205. package/typescript/core/types/statement.ts +47 -0
  206. package/typescript/core/types/value.ts +29 -0
  207. package/typescript/index.ts +7 -2
  208. package/typescript/pkg/devalang_core.d.ts +4 -0
  209. package/typescript/scripts/copy-wasm-dts.ts +41 -0
  210. package/typescript/scripts/postinstall.ts +45 -32
  211. package/rust/cli/bank.rs +0 -462
  212. package/rust/cli/build.rs +0 -252
  213. package/rust/cli/generator.rs +0 -1
  214. package/rust/cli/play.rs +0 -1123
  215. package/rust/cli/telemetry.rs +0 -19
  216. package/rust/common/api.rs +0 -5
  217. package/rust/common/cdn.rs +0 -5
  218. package/rust/config/loader.rs +0 -165
  219. package/rust/config/stats.rs +0 -257
  220. package/rust/core/audio/engine.rs +0 -696
  221. package/rust/core/shared/bank.rs +0 -21
  222. package/rust/core/shared/duration.rs +0 -9
  223. package/rust/core/shared/mod.rs +0 -3
  224. package/rust/core/shared/value.rs +0 -35
  225. package/rust/core/utils/validation.rs +0 -35
  226. package/rust/installer/bank.rs +0 -62
  227. package/rust/installer/plugin.rs +0 -54
  228. package/rust/installer/utils.rs +0 -56
  229. package/rust/utils/file.rs +0 -38
  230. package/rust/utils/first_usage.rs +0 -76
  231. package/rust/utils/signature.rs +0 -19
  232. package/rust/utils/telemetry.rs +0 -292
  233. package/rust/utils/version.rs +0 -15
  234. /package/rust/{common → web}/mod.rs +0 -0
  235. /package/rust/{common → web}/sso.rs +0 -0
@@ -32,7 +32,7 @@ pub fn parse_identifier_token(parser: &mut Parser, global_store: &mut GlobalStor
32
32
  let current_token_clone = current_token.clone();
33
33
  let current_token_lexeme = current_token_clone.lexeme.clone();
34
34
 
35
- let statement = match current_token_lexeme.as_str() {
35
+ match current_token_lexeme.as_str() {
36
36
  "let" => parse_let_token(parser, current_token_clone, global_store),
37
37
  "group" => parse_group_token(parser, current_token_clone, global_store),
38
38
  "call" => parse_call_token(parser, current_token_clone, global_store),
@@ -46,9 +46,10 @@ pub fn parse_identifier_token(parser: &mut Parser, global_store: &mut GlobalStor
46
46
  _ => {
47
47
  parser.advance(); // consume identifier
48
48
 
49
- return Statement::error(current_token_clone, "Unexpected identifier".to_string());
49
+ crate::core::parser::statement::error_from_token(
50
+ current_token_clone,
51
+ "Unexpected identifier".to_string(),
52
+ )
50
53
  }
51
- };
52
-
53
- return statement;
54
+ }
54
55
  }
@@ -1,16 +1,14 @@
1
+ use devalang_types::Value;
2
+
1
3
  use crate::core::{
2
4
  lexer::token::TokenKind,
3
5
  parser::{
4
6
  driver::Parser,
5
7
  statement::{Statement, StatementKind},
6
8
  },
7
- shared::value::Value,
8
9
  store::global::GlobalStore,
9
10
  };
10
11
 
11
- // Syntax:
12
- // on <identifier>:
13
- // <indented block>
14
12
  pub fn parse_on_token(parser: &mut Parser, _global_store: &mut GlobalStore) -> Statement {
15
13
  // consume 'on'
16
14
  let on_tok = match parser.peek_clone() {
@@ -23,9 +21,17 @@ pub fn parse_on_token(parser: &mut Parser, _global_store: &mut GlobalStore) -> S
23
21
  let event_tok = match parser.peek_clone() {
24
22
  Some(tok) if tok.kind == TokenKind::Identifier => tok,
25
23
  Some(other) => {
26
- return Statement::error(other, "Expected event name after 'on'".to_string());
24
+ return crate::core::parser::statement::error_from_token(
25
+ other,
26
+ "Expected event name after 'on'".to_string(),
27
+ );
28
+ }
29
+ None => {
30
+ return crate::core::parser::statement::error_from_token(
31
+ on_tok,
32
+ "Expected event name after 'on'".to_string(),
33
+ );
27
34
  }
28
- None => return Statement::error(on_tok, "Expected event name after 'on'".to_string()),
29
35
  };
30
36
  let event_name = event_tok.lexeme.clone();
31
37
  parser.advance();
@@ -68,7 +74,10 @@ pub fn parse_on_token(parser: &mut Parser, _global_store: &mut GlobalStore) -> S
68
74
 
69
75
  // Expect ':' then block
70
76
  if parser.peek_kind() != Some(TokenKind::Colon) {
71
- return Statement::error(event_tok, "Expected ':' after event name".to_string());
77
+ return crate::core::parser::statement::error_from_token(
78
+ event_tok,
79
+ "Expected ':' after event name".to_string(),
80
+ );
72
81
  }
73
82
  parser.advance(); // consume ':'
74
83
 
@@ -6,6 +6,7 @@ use crate::core::{
6
6
  },
7
7
  store::global::GlobalStore,
8
8
  };
9
+ use devalang_types::Value;
9
10
 
10
11
  pub fn parse_print_token(
11
12
  parser: &mut Parser,
@@ -19,17 +20,13 @@ pub fn parse_print_token(
19
20
  // Accept: print <identifier|string|number|expression>
20
21
  let value = if collected.len() == 1 {
21
22
  match collected[0].kind {
22
- TokenKind::Identifier => {
23
- crate::core::shared::value::Value::Identifier(collected[0].lexeme.clone())
24
- }
25
- TokenKind::String => {
26
- crate::core::shared::value::Value::String(collected[0].lexeme.clone())
27
- }
23
+ TokenKind::Identifier => Value::Identifier(collected[0].lexeme.clone()),
24
+ TokenKind::String => Value::String(collected[0].lexeme.clone()),
28
25
  TokenKind::Number => {
29
26
  let n = collected[0].lexeme.parse::<f32>().unwrap_or(0.0);
30
- crate::core::shared::value::Value::Number(n)
27
+ Value::Number(n)
31
28
  }
32
- _ => crate::core::shared::value::Value::String(collected[0].lexeme.clone()),
29
+ _ => Value::String(collected[0].lexeme.clone()),
33
30
  }
34
31
  } else {
35
32
  // Join tokens with spaces to preserve readability for expressions/text
@@ -39,7 +36,7 @@ pub fn parse_print_token(
39
36
  .map(|t| t.lexeme.clone())
40
37
  .collect::<Vec<_>>()
41
38
  .join(" ");
42
- crate::core::shared::value::Value::String(text.trim().to_string())
39
+ Value::String(text.trim().to_string())
43
40
  };
44
41
 
45
42
  Statement {
@@ -1,10 +1,11 @@
1
+ use devalang_types::Value;
2
+
1
3
  use crate::core::{
2
4
  lexer::token::{Token, TokenKind},
3
5
  parser::{
4
6
  driver::Parser,
5
7
  statement::{Statement, StatementKind},
6
8
  },
7
- shared::value::Value,
8
9
  store::global::GlobalStore,
9
10
  };
10
11
 
@@ -20,17 +21,23 @@ pub fn parse_sleep_token(
20
21
  parser.advance();
21
22
  token.lexeme.parse().unwrap_or(0.0)
22
23
  } else {
23
- return Statement::error(token, "Expected number after 'sleep'".to_string());
24
+ return crate::core::parser::statement::error_from_token(
25
+ token,
26
+ "Expected number after 'sleep'".to_string(),
27
+ );
24
28
  }
25
29
  } else {
26
- return Statement::error(current_token, "Expected number after 'sleep'".to_string());
30
+ return crate::core::parser::statement::error_from_token(
31
+ current_token,
32
+ "Expected number after 'sleep'".to_string(),
33
+ );
27
34
  };
28
35
 
29
- return Statement {
36
+ Statement {
30
37
  kind: StatementKind::Sleep,
31
38
  value: Value::Number(duration),
32
39
  indent: current_token.indent,
33
40
  line: current_token.line,
34
41
  column: current_token.column,
35
- };
42
+ }
36
43
  }
@@ -4,9 +4,9 @@ use crate::core::{
4
4
  driver::Parser,
5
5
  statement::{Statement, StatementKind},
6
6
  },
7
- shared::value::Value,
8
7
  store::global::GlobalStore,
9
8
  };
9
+ use devalang_types::Value;
10
10
 
11
11
  pub fn parse_spawn_token(
12
12
  parser: &mut Parser,
@@ -19,7 +19,7 @@ pub fn parse_spawn_token(
19
19
  let name_token = match parser.peek_clone() {
20
20
  Some(t) => t,
21
21
  None => {
22
- return Statement::error(
22
+ return crate::core::parser::statement::error_from_token(
23
23
  current_token,
24
24
  "Expected function name after 'spawn'".to_string(),
25
25
  );
@@ -27,7 +27,7 @@ pub fn parse_spawn_token(
27
27
  };
28
28
 
29
29
  if name_token.kind != TokenKind::Identifier {
30
- return Statement::error(
30
+ return crate::core::parser::statement::error_from_token(
31
31
  name_token,
32
32
  "Expected function name to be an identifier".to_string(),
33
33
  );
@@ -68,7 +68,7 @@ pub fn parse_spawn_token(
68
68
  parser.advance(); // skip comma
69
69
  }
70
70
  _ => {
71
- return Statement::error(
71
+ return crate::core::parser::statement::error_from_token(
72
72
  token,
73
73
  "Unexpected token in spawn arguments".to_string(),
74
74
  );
@@ -1,12 +1,14 @@
1
1
  use std::collections::HashMap;
2
2
 
3
+ use devalang_types::Value;
4
+
3
5
  use crate::core::{
4
6
  lexer::token::Token,
5
7
  parser::{
6
8
  driver::Parser,
9
+ handler::dot::parse_dot_token,
7
10
  statement::{Statement, StatementKind},
8
11
  },
9
- shared::value::Value,
10
12
  store::global::GlobalStore,
11
13
  };
12
14
 
@@ -21,14 +23,81 @@ pub fn parse_synth_token(
21
23
  return Statement::unknown();
22
24
  };
23
25
 
24
- // Expect an identifier (synth waveform)
25
- let Some(identifier_token) = parser.peek_clone() else {
26
- return Statement::error(synth_token, "Expected identifier after 'synth'".to_string());
27
- };
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;
28
30
 
29
- 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
+ }
30
49
 
31
- 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
+ }
83
+
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
102
  // Expect synth optional parameters map
34
103
  let parameters = if let Some(params) = parser.parse_map_value() {
@@ -36,7 +105,7 @@ pub fn parse_synth_token(
36
105
  if let Value::Map(map) = params {
37
106
  map
38
107
  } else {
39
- return Statement::error(
108
+ return crate::core::parser::statement::error_from_token(
40
109
  synth_token,
41
110
  "Expected a map for synth parameters".to_string(),
42
111
  );
@@ -53,7 +122,8 @@ pub fn parse_synth_token(
53
122
  (
54
123
  "value".to_string(),
55
124
  Value::Map(HashMap::from([
56
- ("waveform".to_string(), Value::String(synth_waveform)),
125
+ // Store waveform as identifier to allow resolution from variables/exports
126
+ ("waveform".to_string(), Value::Identifier(synth_waveform)),
57
127
  ("parameters".to_string(), Value::Map(parameters)),
58
128
  ])),
59
129
  ),
@@ -1,10 +1,11 @@
1
+ use devalang_types::Value;
2
+
1
3
  use crate::core::{
2
4
  lexer::token::TokenKind,
3
5
  parser::{
4
6
  driver::Parser,
5
7
  statement::{Statement, StatementKind},
6
8
  },
7
- shared::value::Value,
8
9
  store::global::GlobalStore,
9
10
  };
10
11
 
@@ -20,7 +21,12 @@ pub fn parse_loop_token(parser: &mut Parser, global_store: &mut GlobalStore) ->
20
21
 
21
22
  // Peek next to decide
22
23
  let Some(next_token) = parser.peek_clone() else {
23
- return Statement::error(loop_token, "Expected iterator after loop/for".to_string());
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
+ );
24
30
  };
25
31
 
26
32
  // Try to detect 'for <ident> in [array]:' form
@@ -66,26 +72,35 @@ pub fn parse_loop_token(parser: &mut Parser, global_store: &mut GlobalStore) ->
66
72
  Value::Identifier(tok.lexeme.clone())
67
73
  }
68
74
  _ => {
69
- return Statement::error(
70
- loop_token,
75
+ return Statement::error_with_pos(
76
+ loop_token.indent,
77
+ loop_token.line,
78
+ loop_token.column,
71
79
  "Expected array, number, string or identifier after 'in'".to_string(),
72
80
  );
73
81
  }
74
82
  }
75
83
  } else {
76
- return Statement::error(
77
- loop_token,
84
+ return Statement::error_with_pos(
85
+ loop_token.indent,
86
+ loop_token.line,
87
+ loop_token.column,
78
88
  "Expected array, number, string or identifier after 'in'".to_string(),
79
89
  );
80
90
  };
81
91
 
82
92
  // Expect ':'
83
93
  if !parser.match_token(TokenKind::Colon) {
84
- return Statement::error(loop_token, "Expected ':' after foreach header".to_string());
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
+ );
85
100
  }
86
101
 
87
102
  let tokens =
88
- parser.collect_until(|t| t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF);
103
+ parser.collect_until(|t| (t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF));
89
104
  let loop_body = parser.parse_block(tokens.clone(), global_store);
90
105
  if let Some(token) = parser.peek() {
91
106
  if token.kind == TokenKind::Dedent {
@@ -109,8 +124,10 @@ pub fn parse_loop_token(parser: &mut Parser, global_store: &mut GlobalStore) ->
109
124
 
110
125
  // Fallback to legacy: loop <count>:
111
126
  let Some(iterator_token) = parser.peek_clone() else {
112
- return Statement::error(
113
- loop_token,
127
+ return Statement::error_with_pos(
128
+ loop_token.indent,
129
+ loop_token.line,
130
+ loop_token.column,
114
131
  "Expected number or identifier after 'loop'".to_string(),
115
132
  );
116
133
  };
@@ -133,8 +150,10 @@ pub fn parse_loop_token(parser: &mut Parser, global_store: &mut GlobalStore) ->
133
150
  Value::String(s)
134
151
  }
135
152
  _ => {
136
- return Statement::error(
137
- iterator_token.clone(),
153
+ return Statement::error_with_pos(
154
+ iterator_token.clone().indent,
155
+ iterator_token.clone().line,
156
+ iterator_token.clone().column,
138
157
  "Expected a number, string or identifier as loop count".to_string(),
139
158
  );
140
159
  }
@@ -145,10 +164,16 @@ pub fn parse_loop_token(parser: &mut Parser, global_store: &mut GlobalStore) ->
145
164
  "Expected ':' after loop count, got {:?}",
146
165
  parser.peek_kind()
147
166
  );
148
- return Statement::error(loop_token.clone(), message);
167
+ return Statement::error_with_pos(
168
+ loop_token.clone().indent,
169
+ loop_token.clone().line,
170
+ loop_token.clone().column,
171
+ message,
172
+ );
149
173
  }
150
174
 
151
- let tokens = parser.collect_until(|t| t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF);
175
+ let tokens =
176
+ parser.collect_until(|t| (t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF));
152
177
  let loop_body = parser.parse_block(tokens.clone(), global_store);
153
178
  if let Some(token) = parser.peek() {
154
179
  if token.kind == TokenKind::Dedent {
@@ -4,9 +4,9 @@ use crate::core::{
4
4
  driver::Parser,
5
5
  statement::{Statement, StatementKind},
6
6
  },
7
- shared::value::Value,
8
7
  store::global::GlobalStore,
9
8
  };
9
+ use devalang_types::Value;
10
10
 
11
11
  pub fn parse_tempo_token(parser: &mut Parser, _global_store: &mut GlobalStore) -> Statement {
12
12
  parser.advance(); // consume 'bpm'
@@ -17,8 +17,10 @@ pub fn parse_tempo_token(parser: &mut Parser, _global_store: &mut GlobalStore) -
17
17
 
18
18
  // Expect a number or identifier
19
19
  let Some(value_token) = parser.peek_clone() else {
20
- return Statement::error(
21
- tempo_token,
20
+ return Statement::error_with_pos(
21
+ tempo_token.indent,
22
+ tempo_token.line,
23
+ tempo_token.column,
22
24
  "Expected a number or identifier after 'bpm'".to_string(),
23
25
  );
24
26
  };
@@ -33,8 +35,10 @@ pub fn parse_tempo_token(parser: &mut Parser, _global_store: &mut GlobalStore) -
33
35
  Value::Identifier(value_token.lexeme.clone())
34
36
  }
35
37
  _ => {
36
- return Statement::error(
37
- value_token.clone(),
38
+ return Statement::error_with_pos(
39
+ value_token.indent,
40
+ value_token.line,
41
+ value_token.column,
38
42
  format!(
39
43
  "Expected a number or identifier after 'bpm', got {:?}",
40
44
  value_token.kind
@@ -1,4 +1,3 @@
1
1
  pub mod driver;
2
-
3
2
  pub mod handler;
4
3
  pub mod statement;
@@ -1,142 +1,11 @@
1
- use crate::core::{
2
- lexer::token::Token,
3
- shared::{duration::Duration, value::Value},
4
- };
5
- use serde::{Deserialize, Serialize};
1
+ use crate::core::lexer::token::Token;
6
2
 
7
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
8
- pub struct Statement {
9
- pub kind: StatementKind,
10
- pub value: Value,
11
- pub indent: usize,
12
- pub line: usize,
13
- pub column: usize,
14
- }
15
-
16
- impl Statement {
17
- pub fn unknown() -> Self {
18
- Statement {
19
- kind: StatementKind::Unknown,
20
- value: Value::Null,
21
- indent: 0,
22
- line: 0,
23
- column: 0,
24
- }
25
- }
26
-
27
- pub fn unknown_from_token(token: &Token) -> Self {
28
- Statement {
29
- kind: StatementKind::Unknown,
30
- value: Value::Null,
31
- indent: token.indent,
32
- line: token.line,
33
- column: token.column,
34
- }
35
- }
3
+ pub use devalang_types::{Duration, Statement, StatementKind, Value};
36
4
 
37
- pub fn error(token: Token, message: String) -> Self {
38
- Statement {
39
- kind: StatementKind::Error { message },
40
- value: Value::Null,
41
- indent: token.indent,
42
- line: token.line,
43
- column: token.column,
44
- }
45
- }
5
+ pub fn unknown_from_token(token: &Token) -> Statement {
6
+ Statement::unknown_with_pos(token.indent, token.line, token.column)
46
7
  }
47
8
 
48
- #[derive(Debug, Serialize, Clone, Deserialize, PartialEq)]
49
- pub enum StatementKind {
50
- // ───── Core Instructions ─────
51
- Tempo,
52
- Bank {
53
- alias: Option<String>,
54
- },
55
- Print,
56
- Load {
57
- source: String,
58
- alias: String,
59
- },
60
- Use {
61
- name: String,
62
- alias: Option<String>,
63
- },
64
- Let {
65
- name: String,
66
- },
67
- Automate {
68
- target: String,
69
- },
70
- ArrowCall {
71
- target: String,
72
- method: String,
73
- args: Vec<Value>,
74
- },
75
- Function {
76
- name: String,
77
- parameters: Vec<String>,
78
- body: Vec<Statement>,
79
- },
80
-
81
- // ───── Instruments ─────
82
- Synth,
83
-
84
- // ───── Playback / Scheduling ─────
85
- Trigger {
86
- entity: String,
87
- duration: Duration,
88
- effects: Option<Value>,
89
- },
90
- Sleep,
91
- Call {
92
- name: String,
93
- args: Vec<Value>,
94
- },
95
- Spawn {
96
- name: String,
97
- args: Vec<Value>,
98
- },
99
- Loop,
100
-
101
- // ───── Structure & Logic ─────
102
- Group,
103
-
104
- // ───── Module System ─────
105
- Include(String),
106
- Export {
107
- names: Vec<String>,
108
- source: String,
109
- },
110
- Import {
111
- names: Vec<String>,
112
- source: String,
113
- },
114
-
115
- // ───── Conditions ─────
116
- If,
117
- Else,
118
- ElseIf,
119
-
120
- // ───── Internal / Utility ─────
121
- Comment,
122
- Indent,
123
- Dedent,
124
- NewLine,
125
-
126
- // ───── Events / Live coding ─────
127
- On {
128
- event: String,
129
- args: Option<Vec<Value>>,
130
- body: Vec<Statement>,
131
- },
132
- Emit {
133
- event: String,
134
- payload: Option<Value>,
135
- },
136
-
137
- // ───── Error Handling ─────
138
- Unknown,
139
- Error {
140
- message: String,
141
- },
9
+ pub fn error_from_token(token: Token, message: String) -> Statement {
10
+ Statement::error_with_pos(token.indent, token.line, token.column, message)
142
11
  }