@devaloop/devalang 0.0.1-alpha.12 → 0.0.1-alpha.14

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 (93) hide show
  1. package/.devalang +8 -9
  2. package/Cargo.toml +8 -3
  3. package/README.md +36 -34
  4. package/docs/CHANGELOG.md +65 -1
  5. package/docs/CONTRIBUTING.md +1 -0
  6. package/docs/ROADMAP.md +2 -2
  7. package/docs/TODO.md +6 -5
  8. package/examples/bank.deva +2 -4
  9. package/examples/function.deva +15 -0
  10. package/examples/index.deva +25 -14
  11. package/out-tsc/bin/devalang.exe +0 -0
  12. package/package.json +6 -6
  13. package/project-version.json +3 -3
  14. package/rust/cli/bank.rs +2 -1
  15. package/rust/cli/build.rs +76 -14
  16. package/rust/cli/check.rs +71 -8
  17. package/rust/cli/driver.rs +40 -28
  18. package/rust/cli/install.rs +22 -7
  19. package/rust/cli/login.rs +134 -0
  20. package/rust/cli/mod.rs +2 -1
  21. package/rust/cli/play.rs +45 -20
  22. package/rust/common/api.rs +8 -0
  23. package/rust/common/cdn.rs +2 -5
  24. package/rust/common/mod.rs +3 -1
  25. package/rust/common/sso.rs +8 -0
  26. package/rust/config/driver.rs +19 -1
  27. package/rust/config/loader.rs +56 -10
  28. package/rust/core/audio/engine.rs +254 -91
  29. package/rust/core/audio/interpreter/arrow_call.rs +34 -15
  30. package/rust/core/audio/interpreter/call.rs +72 -47
  31. package/rust/core/audio/interpreter/condition.rs +14 -12
  32. package/rust/core/audio/interpreter/driver.rs +90 -128
  33. package/rust/core/audio/interpreter/function.rs +21 -0
  34. package/rust/core/audio/interpreter/load.rs +1 -1
  35. package/rust/core/audio/interpreter/loop_.rs +24 -18
  36. package/rust/core/audio/interpreter/mod.rs +2 -1
  37. package/rust/core/audio/interpreter/sleep.rs +0 -6
  38. package/rust/core/audio/interpreter/spawn.rs +78 -60
  39. package/rust/core/audio/interpreter/trigger.rs +157 -70
  40. package/rust/core/audio/loader/trigger.rs +37 -4
  41. package/rust/core/audio/player.rs +20 -10
  42. package/rust/core/audio/renderer.rs +24 -25
  43. package/rust/core/builder/mod.rs +11 -6
  44. package/rust/core/debugger/mod.rs +2 -0
  45. package/rust/core/debugger/module.rs +47 -0
  46. package/rust/core/debugger/store.rs +25 -11
  47. package/rust/core/error/mod.rs +6 -0
  48. package/rust/core/lexer/handler/driver.rs +23 -1
  49. package/rust/core/lexer/handler/identifier.rs +1 -0
  50. package/rust/core/lexer/handler/indent.rs +16 -2
  51. package/rust/core/lexer/handler/mod.rs +1 -0
  52. package/rust/core/lexer/handler/parenthesis.rs +41 -0
  53. package/rust/core/lexer/token.rs +4 -0
  54. package/rust/core/mod.rs +2 -1
  55. package/rust/core/parser/driver.rs +47 -4
  56. package/rust/core/parser/handler/arrow_call.rs +78 -18
  57. package/rust/core/parser/handler/bank.rs +35 -7
  58. package/rust/core/parser/handler/dot.rs +81 -123
  59. package/rust/core/parser/handler/identifier/call.rs +69 -22
  60. package/rust/core/parser/handler/identifier/function.rs +92 -0
  61. package/rust/core/parser/handler/identifier/let_.rs +13 -19
  62. package/rust/core/parser/handler/identifier/mod.rs +1 -0
  63. package/rust/core/parser/handler/identifier/spawn.rs +74 -27
  64. package/rust/core/parser/statement.rs +16 -4
  65. package/rust/core/plugin/loader.rs +48 -0
  66. package/rust/core/plugin/mod.rs +1 -0
  67. package/rust/core/preprocessor/loader.rs +50 -32
  68. package/rust/core/preprocessor/module.rs +3 -1
  69. package/rust/core/preprocessor/processor.rs +26 -1
  70. package/rust/core/preprocessor/resolver/call.rs +61 -84
  71. package/rust/core/preprocessor/resolver/condition.rs +11 -6
  72. package/rust/core/preprocessor/resolver/driver.rs +52 -6
  73. package/rust/core/preprocessor/resolver/function.rs +78 -0
  74. package/rust/core/preprocessor/resolver/group.rs +43 -13
  75. package/rust/core/preprocessor/resolver/let_.rs +7 -10
  76. package/rust/core/preprocessor/resolver/mod.rs +2 -1
  77. package/rust/core/preprocessor/resolver/spawn.rs +64 -30
  78. package/rust/core/preprocessor/resolver/trigger.rs +7 -3
  79. package/rust/core/preprocessor/resolver/value.rs +10 -1
  80. package/rust/core/shared/value.rs +4 -1
  81. package/rust/core/store/function.rs +34 -0
  82. package/rust/core/store/global.rs +9 -10
  83. package/rust/core/store/mod.rs +2 -1
  84. package/rust/core/store/variable.rs +6 -0
  85. package/rust/installer/addon.rs +80 -0
  86. package/rust/installer/bank.rs +24 -14
  87. package/rust/installer/mod.rs +4 -1
  88. package/rust/installer/plugin.rs +55 -0
  89. package/rust/lib.rs +10 -7
  90. package/rust/main.rs +32 -9
  91. package/rust/utils/logger.rs +16 -0
  92. package/rust/utils/mod.rs +45 -1
  93. package/rust/utils/spinner.rs +2 -4
@@ -14,13 +14,41 @@ pub fn parse_bank_token(parser: &mut Parser, _global_store: &mut GlobalStore) ->
14
14
 
15
15
  let bank_value = if let Some(token) = parser.peek_clone() {
16
16
  match token.kind {
17
- TokenKind::Identifier => {
18
- parser.advance();
19
- Value::Identifier(token.lexeme.clone())
20
- }
21
- TokenKind::Number => {
22
- parser.advance();
23
- Value::Number(token.lexeme.parse::<f32>().unwrap_or(0.0))
17
+ TokenKind::Identifier | TokenKind::Number => {
18
+ parser.advance(); // consume identifier or number
19
+
20
+ let mut value = token.lexeme.clone();
21
+
22
+ // Support namespaced banks: <author>.<bank_name>
23
+ if let Some(next) = parser.peek_clone() {
24
+ if next.kind == TokenKind::Dot {
25
+ parser.advance(); // consume '.'
26
+ if let Some(last) = parser.peek_clone() {
27
+ match last.kind {
28
+ TokenKind::Identifier | TokenKind::Number => {
29
+ parser.advance();
30
+ value = format!("{}.{}", value, last.lexeme);
31
+ Value::String(value)
32
+ }
33
+ _ => Value::Unknown,
34
+ }
35
+ } else {
36
+ Value::Unknown
37
+ }
38
+ } else {
39
+ match token.kind {
40
+ TokenKind::Identifier => Value::Identifier(value),
41
+ TokenKind::Number => Value::Number(value.parse::<f32>().unwrap_or(0.0)),
42
+ _ => Value::Unknown,
43
+ }
44
+ }
45
+ } else {
46
+ match token.kind {
47
+ TokenKind::Identifier => Value::Identifier(value),
48
+ TokenKind::Number => Value::Number(value.parse::<f32>().unwrap_or(0.0)),
49
+ _ => Value::Unknown,
50
+ }
51
+ }
24
52
  }
25
53
  _ => Value::Unknown,
26
54
  }
@@ -1,165 +1,123 @@
1
1
  use crate::core::{
2
2
  lexer::token::TokenKind,
3
- parser::{ statement::{ Statement, StatementKind }, driver::Parser },
4
- shared::{ duration::Duration, value::Value },
5
- store::global::GlobalStore,
3
+ parser::{driver::Parser, statement::{Statement, StatementKind}},
4
+ shared::{duration::Duration, value::Value},
6
5
  };
7
6
 
8
- pub fn parse_dot_token(parser: &mut Parser, _global_store: &mut GlobalStore) -> Statement {
9
- parser.advance(); // consume the first dot
7
+ pub fn parse_dot_token(
8
+ parser: &mut Parser,
9
+ _global_store: &mut crate::core::store::global::GlobalStore
10
+ ) -> Statement {
11
+ parser.advance(); // consume '.'
10
12
 
11
13
  let Some(dot_token) = parser.previous_clone() else {
12
14
  return Statement::unknown();
13
15
  };
14
16
 
15
- // Parse namespaced identifier: .808.kick.snare
17
+ // Parse a single entity (namespace-friendly, stops at newline)
16
18
  let mut parts = Vec::new();
19
+ let current_line = dot_token.line;
17
20
 
18
21
  while let Some(token) = parser.peek_clone() {
22
+ // Ne jamais traverser une nouvelle ligne
23
+ if token.line != current_line {
24
+ break;
25
+ }
19
26
  match token.kind {
20
- TokenKind::Number => {
21
- // Stop if it's part of a duration
22
- if let Some(TokenKind::Slash) = parser.peek_nth_kind(1) {
23
- break;
24
- }
25
-
27
+ TokenKind::Identifier | TokenKind::Number => {
26
28
  parts.push(token.lexeme.clone());
27
29
  parser.advance();
28
- }
29
-
30
- TokenKind::Identifier => {
31
- // Stop parsing entity name if next token is ':' or if already have one ident and current might be a param
32
- if parts.len() >= 1 {
33
- break; // we've already got the entity
34
- }
35
-
36
- if token.lexeme == "auto" {
30
+ // Le séparateur doit être un '.' sur la même ligne, sinon on s'arrête
31
+ if let Some(next) = parser.peek_clone() {
32
+ if next.line != current_line || next.kind != TokenKind::Dot {
33
+ break;
34
+ }
35
+ } else {
37
36
  break;
38
37
  }
39
-
40
- parts.push(token.lexeme.clone());
41
- parser.advance();
42
38
  }
43
-
44
39
  TokenKind::Dot => {
45
- parser.advance(); // continue chaining
40
+ parser.advance();
41
+ }
42
+ TokenKind::Newline | TokenKind::EOF | TokenKind::Indent | TokenKind::Dedent => {
43
+ break; // Stop at newline or dedent
46
44
  }
47
-
48
45
  _ => {
49
46
  break;
50
47
  }
51
48
  }
52
49
  }
53
50
 
54
- let entity = if parts.len() == 1 { parts[0].clone() } else { parts[..=1].join(".") };
55
-
56
- if entity.is_empty() {
57
- return Statement {
58
- kind: StatementKind::Trigger {
59
- entity: String::new(),
60
- duration: Duration::Auto,
61
- },
62
- value: Value::Null,
63
- indent: dot_token.indent,
64
- line: dot_token.line,
65
- column: dot_token.column,
66
- };
67
- }
68
-
69
- // Check if there's a duration
70
- let next = parser.peek_clone();
51
+ // Build entity name properly
52
+ let entity = if !parts.is_empty() {
53
+ parts.join(".") // only join within the same line
54
+ } else {
55
+ eprintln!("⚠️ Empty entity after '.' at line {}", dot_token.line);
56
+ String::new()
57
+ };
71
58
 
72
- let (duration, value) = match next {
73
- None => (Duration::Auto, Value::Null),
59
+ // Optional duration and effects map
60
+ let mut duration = Duration::Auto;
61
+ let mut value = Value::Null;
74
62
 
75
- Some(token) =>
63
+ if let Some(token) = parser.peek_clone() {
64
+ // La durée et la map d'effets ne sont valides que sur la même ligne
65
+ if token.line == current_line {
76
66
  match token.kind {
77
- TokenKind::Newline | TokenKind::EOF => (Duration::Auto, Value::Null),
78
-
79
- TokenKind::Number => {
80
- let numerator = token.lexeme.clone();
81
- parser.advance(); // consume numerator
82
-
83
- if let Some(TokenKind::Slash) = parser.peek_kind() {
84
- parser.advance(); // consume slash
85
-
86
- if let Some(denominator_token) = parser.peek_clone() {
87
- if denominator_token.kind == TokenKind::Number {
88
- let denominator = denominator_token.lexeme.clone();
89
- parser.advance(); // consume denominator
90
-
91
- let beat_str = format!("{}/{}", numerator, denominator);
92
- let beat_duration = Duration::Beat(beat_str);
93
-
94
- let val = match parser.peek_clone() {
95
- Some(param_token) if
96
- param_token.kind == TokenKind::Identifier
97
- => {
67
+ TokenKind::Number => {
68
+ let numerator = token.lexeme.clone();
69
+ parser.advance();
70
+ if let Some(peek) = parser.peek_clone() {
71
+ if peek.line == current_line {
72
+ if let Some(TokenKind::Slash) = parser.peek_kind() {
73
+ parser.advance();
74
+ if let Some(denominator_token) = parser.peek_clone() {
75
+ if denominator_token.line == current_line && denominator_token.kind == TokenKind::Number {
76
+ let denominator = denominator_token.lexeme.clone();
98
77
  parser.advance();
99
- Value::Identifier(param_token.lexeme.clone())
100
- }
101
- Some(param_token) if param_token.kind == TokenKind::LBrace => {
102
- parser.parse_map_value().unwrap_or(Value::Null)
78
+ duration = Duration::Beat(format!("{}/{}", numerator, denominator));
103
79
  }
104
- _ => Value::Null,
105
- };
106
-
107
- return Statement {
108
- kind: StatementKind::Trigger {
109
- entity,
110
- duration: beat_duration,
111
- },
112
- value: val,
113
- indent: dot_token.indent,
114
- line: dot_token.line,
115
- column: dot_token.column,
116
- };
80
+ }
81
+ } else {
82
+ duration = parse_duration(numerator);
117
83
  }
84
+ } else {
85
+ duration = parse_duration(numerator);
118
86
  }
87
+ } else {
88
+ duration = parse_duration(numerator);
119
89
  }
120
-
121
- // fallback: simple numeric duration
122
- let duration = parse_duration(numerator);
123
-
124
- let val = match parser.peek_clone() {
125
- Some(param_token) if param_token.kind == TokenKind::Identifier => {
126
- parser.advance();
127
- Value::Identifier(param_token.lexeme.clone())
128
- }
129
- Some(param_token) if param_token.kind == TokenKind::LBrace => {
130
- parser.parse_map_value().unwrap_or(Value::Null)
131
- }
132
- _ => Value::Null,
133
- };
134
-
135
- (duration, val)
136
- }
137
-
138
- TokenKind::Identifier => {
139
- let duration_lexeme = token.lexeme.clone();
140
- parser.advance(); // consume duration
141
-
142
- let val = match parser.peek_clone() {
143
- Some(param_token) if param_token.kind == TokenKind::Identifier => {
144
- parser.advance();
145
- Value::Identifier(param_token.lexeme.clone())
90
+ if let Some(next) = parser.peek_clone() {
91
+ if next.line == current_line && next.kind == TokenKind::LBrace {
92
+ value = parser.parse_map_value().unwrap_or(Value::Null);
146
93
  }
147
- Some(param_token) if param_token.kind == TokenKind::LBrace => {
148
- parser.parse_map_value().unwrap_or(Value::Null)
94
+ }
95
+ }
96
+ TokenKind::Identifier => {
97
+ let id = token.lexeme.clone();
98
+ parser.advance();
99
+ duration = parse_duration(id);
100
+ if let Some(next) = parser.peek_clone() {
101
+ if next.line == current_line && next.kind == TokenKind::LBrace {
102
+ value = parser.parse_map_value().unwrap_or(Value::Null);
149
103
  }
150
- _ => Value::Null,
151
- };
152
-
153
- (parse_duration(duration_lexeme), val)
154
- }
155
-
156
- _ => (Duration::Auto, Value::Null),
104
+ }
157
105
  }
158
- };
106
+ TokenKind::LBrace => {
107
+ value = parser.parse_map_value().unwrap_or(Value::Null);
108
+ }
109
+ _ => {}
110
+ }
111
+ }
112
+ }
159
113
 
160
114
  Statement {
161
- kind: StatementKind::Trigger { entity, duration },
162
- value,
115
+ kind: StatementKind::Trigger {
116
+ entity,
117
+ duration,
118
+ effects: Some(value.clone()),
119
+ },
120
+ value: Value::Null,
163
121
  indent: dot_token.indent,
164
122
  line: dot_token.line,
165
123
  column: dot_token.column,
@@ -1,6 +1,6 @@
1
1
  use crate::core::{
2
- lexer::token::{ Token, TokenKind },
3
- parser::{ statement::{ Statement, StatementKind }, driver::Parser },
2
+ lexer::token::{Token, TokenKind},
3
+ parser::{statement::{Statement, StatementKind}, driver::Parser},
4
4
  shared::value::Value,
5
5
  store::global::GlobalStore,
6
6
  };
@@ -8,34 +8,81 @@ use crate::core::{
8
8
  pub fn parse_call_token(
9
9
  parser: &mut Parser,
10
10
  current_token: Token,
11
- global_store: &mut GlobalStore
11
+ _global_store: &mut GlobalStore
12
12
  ) -> Statement {
13
13
  parser.advance(); // consume "call"
14
14
 
15
- let value = if let Some(token) = parser.peek_clone() {
16
- parser.advance();
17
- match token.kind {
18
- TokenKind::Identifier => Value::Identifier(token.lexeme.clone()),
19
- TokenKind::String => Value::String(token.lexeme.clone()),
20
- _ => {
21
- return Statement::error(
22
- token,
23
- "Expected identifier or string after 'call'".to_string()
24
- );
25
- }
15
+ // Expect function name
16
+ let name_token = match parser.peek_clone() {
17
+ Some(t) => t,
18
+ None => {
19
+ return Statement::error(
20
+ current_token,
21
+ "Expected function name after 'call'".to_string()
22
+ );
26
23
  }
27
- } else {
24
+ };
25
+
26
+ if name_token.kind != TokenKind::Identifier {
28
27
  return Statement::error(
29
- current_token,
30
- "Expected identifier or string after 'call'".to_string()
28
+ name_token,
29
+ "Expected function name to be an identifier".to_string()
31
30
  );
32
- };
31
+ }
32
+
33
+ let func_name = name_token.lexeme.clone();
34
+ parser.advance(); // consume function name
35
+
36
+ // Expect '('
37
+ let mut args: Vec<Value> = Vec::new();
38
+ if let Some(open_paren) = parser.peek_clone() {
39
+ if open_paren.kind == TokenKind::LParen {
40
+ parser.advance(); // consume '('
33
41
 
34
- return Statement {
35
- kind: StatementKind::Call,
36
- value,
42
+ // Collect args until ')'
43
+ while let Some(token) = parser.peek_clone() {
44
+ if token.kind == TokenKind::RParen {
45
+ parser.advance(); // consume ')'
46
+ break;
47
+ }
48
+
49
+ match token.kind {
50
+ TokenKind::Number => {
51
+ if let Ok(num) = token.lexeme.parse::<f32>() {
52
+ args.push(Value::Number(num));
53
+ }
54
+ parser.advance();
55
+ }
56
+ TokenKind::String => {
57
+ args.push(Value::String(token.lexeme.clone()));
58
+ parser.advance();
59
+ }
60
+ TokenKind::Identifier => {
61
+ args.push(Value::Identifier(token.lexeme.clone()));
62
+ parser.advance();
63
+ }
64
+ TokenKind::Comma => {
65
+ parser.advance(); // skip comma
66
+ }
67
+ _ => {
68
+ return Statement::error(
69
+ token,
70
+ "Unexpected token in call arguments".to_string()
71
+ );
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+
78
+ Statement {
79
+ kind: StatementKind::Call {
80
+ name: func_name,
81
+ args,
82
+ },
83
+ value: Value::Null,
37
84
  indent: current_token.indent,
38
85
  line: current_token.line,
39
86
  column: current_token.column,
40
- };
87
+ }
41
88
  }
@@ -0,0 +1,92 @@
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_function_token(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
9
+ parser.advance(); // consume 'fn'
10
+
11
+ let fn_token = match parser.previous_clone() {
12
+ Some(tok) => tok,
13
+ None => return Statement::unknown(),
14
+ };
15
+
16
+ let name_token = match parser.peek_clone() {
17
+ Some(tok) => tok,
18
+ None => return Statement::error(fn_token, "Expected function name after 'fn'".to_string()),
19
+ };
20
+
21
+ if name_token.kind != TokenKind::Identifier {
22
+ return Statement::error(
23
+ name_token.clone(),
24
+ "Expected function name to be an identifier".to_string()
25
+ );
26
+ }
27
+
28
+ let function_name = name_token.lexeme.clone();
29
+ parser.advance(); // consume function name
30
+
31
+ let mut parameters = Vec::new();
32
+ let mut body = Vec::new();
33
+
34
+ // Expect '('
35
+ if parser.peek_kind() != Some(TokenKind::LParen) {
36
+ return Statement::error(name_token.clone(), "Expected '(' after function name".to_string());
37
+ }
38
+ parser.advance(); // consume '('
39
+
40
+ // Parse parameters until ')'
41
+ let tokens = parser.collect_until(|t| t.kind == TokenKind::RParen || t.kind == TokenKind::EOF);
42
+ for token in tokens {
43
+ if token.kind == TokenKind::Identifier {
44
+ parameters.push(token.lexeme.clone());
45
+ }
46
+ }
47
+
48
+ if parser.peek_kind() == Some(TokenKind::RParen) {
49
+ parser.advance(); // consume ')'
50
+ } else {
51
+ return Statement::error(name_token.clone(), "Expected ')' after parameters".to_string());
52
+ }
53
+
54
+ // Expect colon
55
+ if parser.peek_kind() != Some(TokenKind::Colon) {
56
+ return Statement::error(name_token.clone(), "Expected ':' after ')'".to_string());
57
+ }
58
+ parser.advance(); // consume ':'
59
+
60
+ // Collect ALL tokens indented after this line until Dedent
61
+ let base_indent = fn_token.indent;
62
+ let mut body_tokens = Vec::new();
63
+
64
+ while let Some(tok) = parser.peek() {
65
+ if tok.kind == TokenKind::Dedent && tok.indent <= base_indent {
66
+ break;
67
+ }
68
+ body_tokens.push(parser.advance().unwrap().clone());
69
+ }
70
+
71
+ // arse those tokens into block statements
72
+ body = parser.parse_block(body_tokens.clone(), global_store);
73
+
74
+ // Skip Dedent if present
75
+ if let Some(tok) = parser.peek() {
76
+ if tok.kind == TokenKind::Dedent {
77
+ parser.advance();
78
+ }
79
+ }
80
+
81
+ Statement {
82
+ kind: StatementKind::Function {
83
+ name: function_name.clone(),
84
+ parameters: parameters.clone(),
85
+ body: body.clone(),
86
+ },
87
+ value: Value::Null,
88
+ indent: fn_token.indent,
89
+ line: fn_token.line,
90
+ column: fn_token.column,
91
+ }
92
+ }
@@ -4,7 +4,7 @@ use crate::core::{
4
4
  lexer::token::{ Token, TokenKind },
5
5
  parser::{
6
6
  driver::Parser,
7
- handler::identifier::synth::parse_synth_token,
7
+ handler::{ dot::parse_dot_token, identifier::synth::parse_synth_token },
8
8
  statement::{ Statement, StatementKind },
9
9
  },
10
10
  shared::value::Value,
@@ -33,21 +33,15 @@ pub fn parse_let_token(
33
33
  return Statement::error(current_token, "Expected '=' after identifier".to_string());
34
34
  }
35
35
 
36
- if let Some(token) = parser.peek_clone() {
37
- if token.kind == TokenKind::Synth {
36
+ let value = match parser.peek_clone() {
37
+ Some(token) if token.kind == TokenKind::Dot => {
38
+ let dot_stmt = parse_dot_token(parser, global_store);
39
+ Value::Statement(Box::new(dot_stmt))
40
+ }
41
+ Some(token) if token.kind == TokenKind::Synth => {
38
42
  let synth_stmt = parse_synth_token(parser, token.clone(), global_store);
39
-
40
- return Statement {
41
- kind: StatementKind::Let { name: identifier },
42
- value: synth_stmt.value,
43
- indent: current_token.indent,
44
- line: current_token.line,
45
- column: current_token.column,
46
- };
43
+ Value::Statement(Box::new(synth_stmt))
47
44
  }
48
- }
49
-
50
- let value = match parser.peek_clone() {
51
45
  Some(token) if token.kind == TokenKind::Identifier => {
52
46
  parser.advance();
53
47
  Value::Identifier(token.lexeme.clone())
@@ -65,7 +59,8 @@ pub fn parse_let_token(
65
59
  Value::Boolean(token.lexeme.parse().unwrap_or(false))
66
60
  }
67
61
  Some(token) if token.kind == TokenKind::LBrace => {
68
- parser.advance(); // consume '{'
62
+ parser.advance();
63
+
69
64
  let mut map = HashMap::new();
70
65
 
71
66
  while let Some(key_token) = parser.peek_clone() {
@@ -117,15 +112,14 @@ pub fn parse_let_token(
117
112
 
118
113
  Value::Map(map)
119
114
  }
120
- other => {
121
- let message = format!("Unexpected value token in let: {:?}", other);
122
- return Statement::error(current_token, message);
115
+ _ => {
116
+ return Statement::error(current_token, "Unhandled value type after '='".to_string());
123
117
  }
124
118
  };
125
119
 
126
120
  Statement {
127
121
  kind: StatementKind::Let { name: identifier },
128
- value,
122
+ value: value,
129
123
  indent: current_token.indent,
130
124
  line: current_token.line,
131
125
  column: current_token.column,
@@ -4,6 +4,7 @@ pub mod call;
4
4
  pub mod spawn;
5
5
  pub mod sleep;
6
6
  pub mod synth;
7
+ pub mod function;
7
8
 
8
9
  use crate::core::{
9
10
  parser::{