@devaloop/devalang 0.0.1-alpha.11 → 0.0.1-alpha.13

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 (66) hide show
  1. package/.devalang +8 -8
  2. package/Cargo.toml +8 -8
  3. package/README.md +1 -14
  4. package/docs/CHANGELOG.md +44 -0
  5. package/docs/TODO.md +1 -1
  6. package/examples/index.deva +10 -11
  7. package/out-tsc/bin/devalang.exe +0 -0
  8. package/package.json +2 -1
  9. package/project-version.json +3 -3
  10. package/rust/cli/build.rs +25 -2
  11. package/rust/cli/check.rs +26 -3
  12. package/rust/cli/play.rs +1 -1
  13. package/rust/core/audio/engine.rs +207 -41
  14. package/rust/core/audio/interpreter/call.rs +72 -47
  15. package/rust/core/audio/interpreter/condition.rs +14 -12
  16. package/rust/core/audio/interpreter/driver.rs +84 -127
  17. package/rust/core/audio/interpreter/function.rs +21 -0
  18. package/rust/core/audio/interpreter/load.rs +1 -1
  19. package/rust/core/audio/interpreter/loop_.rs +24 -18
  20. package/rust/core/audio/interpreter/mod.rs +2 -1
  21. package/rust/core/audio/interpreter/sleep.rs +0 -6
  22. package/rust/core/audio/interpreter/spawn.rs +78 -60
  23. package/rust/core/audio/interpreter/trigger.rs +169 -61
  24. package/rust/core/audio/loader/trigger.rs +37 -4
  25. package/rust/core/audio/player.rs +20 -10
  26. package/rust/core/audio/renderer.rs +24 -25
  27. package/rust/core/debugger/mod.rs +2 -0
  28. package/rust/core/debugger/module.rs +47 -0
  29. package/rust/core/debugger/store.rs +25 -11
  30. package/rust/core/error/mod.rs +6 -0
  31. package/rust/core/lexer/handler/driver.rs +23 -1
  32. package/rust/core/lexer/handler/identifier.rs +1 -0
  33. package/rust/core/lexer/handler/mod.rs +1 -0
  34. package/rust/core/lexer/handler/parenthesis.rs +41 -0
  35. package/rust/core/lexer/token.rs +3 -0
  36. package/rust/core/parser/driver.rs +31 -3
  37. package/rust/core/parser/handler/dot.rs +65 -129
  38. package/rust/core/parser/handler/identifier/call.rs +69 -22
  39. package/rust/core/parser/handler/identifier/function.rs +92 -0
  40. package/rust/core/parser/handler/identifier/let_.rs +13 -19
  41. package/rust/core/parser/handler/identifier/mod.rs +1 -0
  42. package/rust/core/parser/handler/identifier/spawn.rs +74 -27
  43. package/rust/core/parser/statement.rs +16 -4
  44. package/rust/core/preprocessor/loader.rs +45 -29
  45. package/rust/core/preprocessor/module.rs +3 -1
  46. package/rust/core/preprocessor/processor.rs +26 -1
  47. package/rust/core/preprocessor/resolver/call.rs +61 -84
  48. package/rust/core/preprocessor/resolver/condition.rs +11 -6
  49. package/rust/core/preprocessor/resolver/driver.rs +52 -6
  50. package/rust/core/preprocessor/resolver/function.rs +78 -0
  51. package/rust/core/preprocessor/resolver/group.rs +43 -13
  52. package/rust/core/preprocessor/resolver/let_.rs +7 -10
  53. package/rust/core/preprocessor/resolver/mod.rs +2 -1
  54. package/rust/core/preprocessor/resolver/spawn.rs +64 -30
  55. package/rust/core/preprocessor/resolver/trigger.rs +7 -3
  56. package/rust/core/preprocessor/resolver/value.rs +10 -1
  57. package/rust/core/shared/value.rs +4 -1
  58. package/rust/core/store/function.rs +34 -0
  59. package/rust/core/store/global.rs +9 -10
  60. package/rust/core/store/mod.rs +2 -1
  61. package/rust/core/store/variable.rs +6 -0
  62. package/rust/installer/bank.rs +1 -1
  63. package/rust/installer/mod.rs +2 -1
  64. package/rust/lib.rs +10 -8
  65. package/rust/utils/mod.rs +44 -1
  66. /package/rust/{utils/installer.rs → installer/utils.rs} +0 -0
@@ -1,25 +1,39 @@
1
- use std::{ collections::HashMap, fs::create_dir_all };
2
- use crate::core::{ debugger::Debugger, preprocessor::module::Module };
1
+ use std::{ fs::create_dir_all };
2
+ use crate::core::{
3
+ debugger::Debugger,
4
+ store::{ function::FunctionTable, variable::VariableTable },
5
+ };
3
6
 
4
- pub fn write_store_log_file(output_dir: &str, file_name: &str, modules: HashMap<String, Module>) {
7
+ pub fn write_variables_log_file(output_dir: &str, file_name: &str, variables: VariableTable) {
5
8
  let debugger = Debugger::new();
6
9
  let mut content = String::new();
7
10
 
8
11
  let log_directory = format!("{}/logs", output_dir);
9
12
  create_dir_all(&log_directory).expect("Failed to create log directory");
10
13
 
11
- for (path, module) in modules {
12
- content.push_str(&format!("--- Module: {} ---\n", path));
14
+ for (var_name, var_data) in variables.variables {
15
+ content.push_str(&format!("{:?} = {:?}\n", var_name, var_data));
16
+ }
17
+
18
+ content.push_str("\n");
19
+
20
+ debugger.write_log_file(&log_directory, file_name, &content);
21
+ }
13
22
 
14
- for (index, var_value) in module.variable_table.variables.iter().enumerate() {
15
- let var_name = var_value.0.clone();
16
- let var_data = var_value.1;
23
+ pub fn write_function_log_file(output_dir: &str, file_name: &str, functions: FunctionTable) {
24
+ let debugger = Debugger::new();
25
+ let mut content = String::new();
17
26
 
18
- content.push_str(&format!("{}: {:?} = {:?}\n", index + 1, var_name, var_data));
19
- }
27
+ let log_directory = format!("{}/logs", output_dir);
28
+ create_dir_all(&log_directory).expect("Failed to create log directory");
20
29
 
21
- content.push_str("\n");
30
+ for (index, function) in functions.functions {
31
+ content.push_str(
32
+ &format!("'{}' = [{:?}] => {:?}\n", function.name, function.parameters, function.body)
33
+ );
22
34
  }
23
35
 
36
+ content.push_str("\n");
37
+
24
38
  debugger.write_log_file(&log_directory, file_name, &content);
25
39
  }
@@ -4,6 +4,12 @@ pub struct ErrorHandler {
4
4
  errors: Vec<Error>,
5
5
  }
6
6
 
7
+ pub struct ErrorResult {
8
+ pub message: String,
9
+ pub line: usize,
10
+ pub column: usize,
11
+ }
12
+
7
13
  pub struct Error {
8
14
  pub message: String,
9
15
  pub line: usize,
@@ -1,6 +1,6 @@
1
1
  use crate::core::lexer::{
2
2
  handler::{
3
- arrow::handle_arrow_lexer, at::handle_at_lexer, brace::{ handle_lbrace_lexer, handle_rbrace_lexer }, colon::handle_colon_lexer, comment::handle_comment_lexer, dot::handle_dot_lexer, identifier::handle_identifier_lexer, indent::handle_indent_lexer, newline::handle_newline_lexer, number::handle_number_lexer, operator::handle_operator_lexer, slash::handle_slash_lexer, string::handle_string_lexer
3
+ arrow::handle_arrow_lexer, at::handle_at_lexer, brace::{ handle_lbrace_lexer, handle_rbrace_lexer }, colon::handle_colon_lexer, comment::handle_comment_lexer, dot::handle_dot_lexer, identifier::handle_identifier_lexer, indent::handle_indent_lexer, newline::handle_newline_lexer, number::handle_number_lexer, operator::handle_operator_lexer, parenthesis::{handle_lparen_lexer, handle_rparen_lexer}, slash::handle_slash_lexer, string::handle_string_lexer
4
4
  },
5
5
  token::{ Token, TokenKind },
6
6
  };
@@ -145,6 +145,28 @@ pub fn handle_content_lexing(content: String) -> Result<Vec<Token>, String> {
145
145
  &mut column
146
146
  );
147
147
  }
148
+ '(' => {
149
+ handle_lparen_lexer(
150
+ ch,
151
+ &mut chars,
152
+ &mut current_indent,
153
+ &mut indent_stack,
154
+ &mut tokens,
155
+ &mut line,
156
+ &mut column
157
+ );
158
+ }
159
+ ')' => {
160
+ handle_rparen_lexer(
161
+ ch,
162
+ &mut chars,
163
+ &mut current_indent,
164
+ &mut indent_stack,
165
+ &mut tokens,
166
+ &mut line,
167
+ &mut column
168
+ );
169
+ }
148
170
  '.' => {
149
171
  handle_dot_lexer(
150
172
  ch,
@@ -28,6 +28,7 @@ pub fn handle_identifier_lexer(
28
28
  "bpm" => TokenKind::Tempo,
29
29
  "loop" => TokenKind::Loop,
30
30
  "synth" => TokenKind::Synth,
31
+ "fn" => TokenKind::Function,
31
32
  _ => TokenKind::Identifier,
32
33
  };
33
34
 
@@ -13,3 +13,4 @@ pub mod indent;
13
13
  pub mod string;
14
14
  pub mod arrow;
15
15
  pub mod slash;
16
+ pub mod parenthesis;
@@ -0,0 +1,41 @@
1
+ use crate::core::lexer::token::{ Token, TokenKind };
2
+
3
+ pub fn handle_rparen_lexer(
4
+ char: char,
5
+ chars: &mut std::iter::Peekable<std::str::Chars>,
6
+ current_indent: &mut usize,
7
+ indent_stack: &mut Vec<usize>,
8
+ tokens: &mut Vec<Token>,
9
+ line: &mut usize,
10
+ column: &mut usize
11
+ ) {
12
+ tokens.push(Token {
13
+ kind: TokenKind::RParen,
14
+ lexeme: char.to_string(),
15
+ line: *line,
16
+ column: *column,
17
+ indent: *current_indent,
18
+ });
19
+
20
+ *column += 1;
21
+ }
22
+
23
+ pub fn handle_lparen_lexer(
24
+ char: char,
25
+ chars: &mut std::iter::Peekable<std::str::Chars>,
26
+ current_indent: &mut usize,
27
+ indent_stack: &mut Vec<usize>,
28
+ tokens: &mut Vec<Token>,
29
+ line: &mut usize,
30
+ column: &mut usize
31
+ ) {
32
+ tokens.push(Token {
33
+ kind: TokenKind::LParen,
34
+ lexeme: char.to_string(),
35
+ line: *line,
36
+ column: *column,
37
+ indent: *current_indent,
38
+ });
39
+
40
+ *column += 1;
41
+ }
@@ -29,6 +29,7 @@ pub enum TokenKind {
29
29
  Tempo,
30
30
  Bank,
31
31
  Loop,
32
+ Function,
32
33
 
33
34
  // ───── Instruments ─────
34
35
  Synth,
@@ -64,6 +65,8 @@ pub enum TokenKind {
64
65
  RBrace, // }
65
66
  LBracket, // [
66
67
  RBracket, // ]
68
+ LParen, // (
69
+ RParen, // )
67
70
 
68
71
  // ───── Quotes ─────
69
72
  Quote, // '
@@ -7,7 +7,7 @@ use crate::core::{
7
7
  bank::parse_bank_token,
8
8
  condition::parse_condition_token,
9
9
  dot::parse_dot_token,
10
- identifier::parse_identifier_token,
10
+ identifier::{function::parse_function_token, parse_identifier_token},
11
11
  loop_::parse_loop_token,
12
12
  tempo::parse_tempo_token,
13
13
  },
@@ -138,6 +138,7 @@ impl Parser {
138
138
  TokenKind::Bank => parse_bank_token(self, global_store),
139
139
  TokenKind::Loop => parse_loop_token(self, global_store),
140
140
  TokenKind::If => parse_condition_token(self, global_store),
141
+ TokenKind::Function => parse_function_token(self, global_store),
141
142
 
142
143
  | TokenKind::Else // Ignore else, already handled in `parse_condition_token`
143
144
  | TokenKind::Comment
@@ -206,9 +207,35 @@ impl Parser {
206
207
  Value::String(token.lexeme.clone())
207
208
  }
208
209
  TokenKind::Number => {
209
- self.advance();
210
- Value::Number(token.lexeme.parse().unwrap_or(0.0))
210
+ let mut number_str = token.lexeme.clone();
211
+ self.advance(); // consume the first number
212
+
213
+ if let Some(dot_token) = self.peek_clone() {
214
+ if dot_token.kind == TokenKind::Dot {
215
+ self.advance(); // consume the dot
216
+
217
+ if let Some(decimal_token) = self.peek_clone() {
218
+ if decimal_token.kind == TokenKind::Number {
219
+ self.advance(); // consume the number after the dot
220
+ number_str.push('.');
221
+ number_str.push_str(&decimal_token.lexeme);
222
+ } else {
223
+ println!(
224
+ "Expected number after dot, got {:?}",
225
+ decimal_token
226
+ );
227
+ return Some(Value::Null);
228
+ }
229
+ } else {
230
+ println!("Expected number after dot, but reached EOF");
231
+ return Some(Value::Null);
232
+ }
233
+ }
234
+ }
235
+
236
+ Value::Number(number_str.parse::<f32>().unwrap_or(0.0))
211
237
  }
238
+
212
239
  TokenKind::Identifier => {
213
240
  self.advance();
214
241
  Value::Identifier(token.lexeme.clone())
@@ -277,6 +304,7 @@ impl Parser {
277
304
  }
278
305
  collected.push(self.advance().unwrap().clone());
279
306
  }
307
+
280
308
  collected
281
309
  }
282
310
 
@@ -1,166 +1,102 @@
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();
17
19
 
18
- loop {
19
- let Some(token) = parser.peek_clone() else {
20
- break;
21
- };
22
-
20
+ while let Some(token) = parser.peek_clone() {
23
21
  match token.kind {
24
- // Stop if we encounter a likely duration keyword
25
- TokenKind::Number => {
26
- // If there's a slash after the number, it's probably a fraction (1/4)
27
- if let Some(TokenKind::Slash) = parser.peek_nth_kind(1) {
28
- break;
29
- }
30
-
22
+ TokenKind::Identifier | TokenKind::Number => {
31
23
  parts.push(token.lexeme.clone());
32
24
  parser.advance();
33
- }
34
-
35
- TokenKind::Identifier => {
36
- // Stop if it's the duration keyword "auto"
37
- if token.lexeme == "auto" {
25
+ if parser.peek_kind() != Some(TokenKind::Dot) {
38
26
  break;
39
27
  }
40
-
41
- parts.push(token.lexeme.clone());
42
- parser.advance();
43
28
  }
44
-
45
29
  TokenKind::Dot => {
46
- parser.advance(); // consume dot
30
+ parser.advance();
31
+ }
32
+ TokenKind::Newline | TokenKind::EOF | TokenKind::Indent | TokenKind::Dedent => {
33
+ break; // Stop at newline or dedent
47
34
  }
48
-
49
35
  _ => {
50
36
  break;
51
37
  }
52
38
  }
53
39
  }
54
40
 
55
- let entity = parts.join(".");
56
-
57
- if entity.is_empty() {
58
- return Statement {
59
- kind: StatementKind::Trigger {
60
- entity: String::new(),
61
- duration: Duration::Auto,
62
- },
63
- value: Value::Null,
64
- indent: dot_token.indent,
65
- line: dot_token.line,
66
- column: dot_token.column,
67
- };
68
- }
69
-
70
- // Check if there's a duration
71
- let next = parser.peek_clone();
72
-
73
- let (duration, value) = match next {
74
- None => (Duration::Auto, Value::Null),
75
-
76
- Some(token) =>
77
- match token.kind {
78
- TokenKind::Newline | TokenKind::EOF => (Duration::Auto, Value::Null),
79
-
80
- TokenKind::Number => {
81
- let numerator = token.lexeme.clone();
82
- parser.advance(); // consume numerator
83
-
84
- if let Some(TokenKind::Slash) = parser.peek_kind() {
85
- parser.advance(); // consume slash
86
-
87
- if let Some(denominator_token) = parser.peek_clone() {
88
- if denominator_token.kind == TokenKind::Number {
89
- let denominator = denominator_token.lexeme.clone();
90
- parser.advance(); // consume denominator
91
-
92
- let beat_str = format!("{}/{}", numerator, denominator);
93
- let beat_duration = Duration::Beat(beat_str);
94
-
95
- let val = match parser.peek_clone() {
96
- Some(param_token) if
97
- param_token.kind == TokenKind::Identifier
98
- => {
99
- parser.advance();
100
- Value::Identifier(param_token.lexeme.clone())
101
- }
102
- Some(param_token) if param_token.kind == TokenKind::LBrace => {
103
- parser.parse_map_value().unwrap_or(Value::Null)
104
- }
105
- _ => Value::Null,
106
- };
107
-
108
- return Statement {
109
- kind: StatementKind::Trigger {
110
- entity,
111
- duration: beat_duration,
112
- },
113
- value: val,
114
- indent: dot_token.indent,
115
- line: dot_token.line,
116
- column: dot_token.column,
117
- };
118
- }
119
- }
120
- }
41
+ // Build entity name properly
42
+ let entity = if !parts.is_empty() {
43
+ parts.join(".") // only join within the same line
44
+ } else {
45
+ eprintln!("⚠️ Empty entity after '.' at line {}", dot_token.line);
46
+ String::new()
47
+ };
121
48
 
122
- // fallback: simple numeric duration
123
- let duration = parse_duration(numerator);
49
+ // Optional duration and effects map
50
+ let mut duration = Duration::Auto;
51
+ let mut value = Value::Null;
124
52
 
125
- let val = match parser.peek_clone() {
126
- Some(param_token) if param_token.kind == TokenKind::Identifier => {
53
+ if let Some(token) = parser.peek_clone() {
54
+ match token.kind {
55
+ TokenKind::Number => {
56
+ let numerator = token.lexeme.clone();
57
+ parser.advance();
58
+ if let Some(TokenKind::Slash) = parser.peek_kind() {
59
+ parser.advance();
60
+ if let Some(denominator_token) = parser.peek_clone() {
61
+ if denominator_token.kind == TokenKind::Number {
62
+ let denominator = denominator_token.lexeme.clone();
127
63
  parser.advance();
128
- Value::Identifier(param_token.lexeme.clone())
129
- }
130
- Some(param_token) if param_token.kind == TokenKind::LBrace => {
131
- parser.parse_map_value().unwrap_or(Value::Null)
64
+ duration = Duration::Beat(format!("{}/{}", numerator, denominator));
132
65
  }
133
- _ => Value::Null,
134
- };
135
-
136
- (duration, val)
66
+ }
67
+ } else {
68
+ duration = parse_duration(numerator);
137
69
  }
138
-
139
- TokenKind::Identifier => {
140
- let duration_lexeme = token.lexeme.clone();
141
- parser.advance(); // consume duration
142
-
143
- let val = match parser.peek_clone() {
144
- Some(param_token) if param_token.kind == TokenKind::Identifier => {
145
- parser.advance();
146
- Value::Identifier(param_token.lexeme.clone())
147
- }
148
- Some(param_token) if param_token.kind == TokenKind::LBrace => {
149
- parser.parse_map_value().unwrap_or(Value::Null)
150
- }
151
- _ => Value::Null,
152
- };
153
-
154
- (parse_duration(duration_lexeme), val)
70
+ if let Some(next) = parser.peek_clone() {
71
+ if next.kind == TokenKind::LBrace {
72
+ value = parser.parse_map_value().unwrap_or(Value::Null);
73
+ }
155
74
  }
156
-
157
- _ => (Duration::Auto, Value::Null),
158
75
  }
159
- };
76
+ TokenKind::Identifier => {
77
+ let id = token.lexeme.clone();
78
+ parser.advance();
79
+ duration = parse_duration(id);
80
+ if let Some(next) = parser.peek_clone() {
81
+ if next.kind == TokenKind::LBrace {
82
+ value = parser.parse_map_value().unwrap_or(Value::Null);
83
+ }
84
+ }
85
+ }
86
+ TokenKind::LBrace => {
87
+ value = parser.parse_map_value().unwrap_or(Value::Null);
88
+ }
89
+ _ => {}
90
+ }
91
+ }
160
92
 
161
93
  Statement {
162
- kind: StatementKind::Trigger { entity, duration },
163
- value,
94
+ kind: StatementKind::Trigger {
95
+ entity,
96
+ duration,
97
+ effects: Some(value.clone()),
98
+ },
99
+ value: Value::Null,
164
100
  indent: dot_token.indent,
165
101
  line: dot_token.line,
166
102
  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
+ }