@devaloop/devalang 0.0.1-alpha.8 → 0.0.1-alpha.9

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 (59) hide show
  1. package/Cargo.toml +1 -1
  2. package/README.md +4 -8
  3. package/docs/CHANGELOG.md +27 -0
  4. package/examples/condition.deva +8 -12
  5. package/examples/group.deva +3 -3
  6. package/examples/index.deva +10 -8
  7. package/examples/loop.deva +10 -8
  8. package/examples/synth.deva +14 -0
  9. package/examples/variables.deva +1 -1
  10. package/out-tsc/bin/devalang.exe +0 -0
  11. package/out-tsc/scripts/version/fetch.js +1 -5
  12. package/package.json +1 -1
  13. package/project-version.json +3 -3
  14. package/rust/core/audio/engine.rs +89 -12
  15. package/rust/core/audio/interpreter/arrow_call.rs +129 -0
  16. package/rust/core/audio/interpreter/call.rs +29 -7
  17. package/rust/core/audio/interpreter/condition.rs +5 -1
  18. package/rust/core/audio/interpreter/driver.rs +41 -29
  19. package/rust/core/audio/interpreter/loop_.rs +11 -3
  20. package/rust/core/audio/interpreter/mod.rs +1 -0
  21. package/rust/core/audio/interpreter/spawn.rs +43 -42
  22. package/rust/core/audio/interpreter/trigger.rs +1 -1
  23. package/rust/core/audio/renderer.rs +15 -18
  24. package/rust/core/lexer/handler/arrow.rs +31 -0
  25. package/rust/core/lexer/handler/driver.rs +12 -1
  26. package/rust/core/lexer/handler/identifier.rs +1 -0
  27. package/rust/core/lexer/handler/mod.rs +1 -0
  28. package/rust/core/lexer/mod.rs +24 -3
  29. package/rust/core/lexer/token.rs +4 -0
  30. package/rust/core/parser/driver.rs +23 -4
  31. package/rust/core/parser/handler/arrow_call.rs +126 -0
  32. package/rust/core/parser/handler/identifier/call.rs +41 -0
  33. package/rust/core/parser/handler/identifier/group.rs +75 -0
  34. package/rust/core/parser/handler/identifier/let_.rs +133 -0
  35. package/rust/core/parser/handler/identifier/mod.rs +51 -0
  36. package/rust/core/parser/handler/identifier/sleep.rs +33 -0
  37. package/rust/core/parser/handler/identifier/spawn.rs +41 -0
  38. package/rust/core/parser/handler/identifier/synth.rs +65 -0
  39. package/rust/core/parser/handler/loop_.rs +24 -18
  40. package/rust/core/parser/handler/mod.rs +2 -1
  41. package/rust/core/parser/statement.rs +8 -0
  42. package/rust/core/preprocessor/loader.rs +57 -43
  43. package/rust/core/preprocessor/module.rs +3 -6
  44. package/rust/core/preprocessor/processor.rs +13 -4
  45. package/rust/core/preprocessor/resolver/call.rs +99 -29
  46. package/rust/core/preprocessor/resolver/condition.rs +38 -12
  47. package/rust/core/preprocessor/resolver/driver.rs +74 -29
  48. package/rust/core/preprocessor/resolver/group.rs +24 -81
  49. package/rust/core/preprocessor/resolver/let_.rs +31 -0
  50. package/rust/core/preprocessor/resolver/loop_.rs +62 -116
  51. package/rust/core/preprocessor/resolver/mod.rs +5 -1
  52. package/rust/core/preprocessor/resolver/spawn.rs +41 -36
  53. package/rust/core/preprocessor/resolver/synth.rs +50 -0
  54. package/rust/core/preprocessor/resolver/trigger.rs +51 -50
  55. package/rust/core/preprocessor/resolver/value.rs +78 -0
  56. package/rust/core/utils/path.rs +17 -32
  57. package/rust/core/utils/validation.rs +30 -28
  58. package/typescript/scripts/version/fetch.ts +1 -6
  59. package/rust/core/parser/handler/identifier.rs +0 -262
@@ -2,6 +2,7 @@ use crate::core::{
2
2
  audio::{
3
3
  engine::AudioEngine,
4
4
  interpreter::{
5
+ arrow_call::interprete_call_arrow_statement,
5
6
  call::interprete_call_statement,
6
7
  condition::interprete_condition_statement,
7
8
  let_::interprete_let_statement,
@@ -29,7 +30,7 @@ pub fn run_audio_program(
29
30
  let variable_table = audio_engine.variables.clone();
30
31
 
31
32
  let (updated_audio_engine, base_bpm, max_end_time) = execute_audio_block(
32
- audio_engine.clone(),
33
+ audio_engine,
33
34
  variable_table.clone(),
34
35
  statements.clone(),
35
36
  base_bpm.clone(),
@@ -50,6 +51,8 @@ pub fn execute_audio_block(
50
51
  mut max_end_time: f32,
51
52
  mut cursor_time: f32
52
53
  ) -> (AudioEngine, f32, f32) {
54
+ let initial_cursor_time = cursor_time;
55
+
53
56
  for stmt in statements {
54
57
  match &stmt.kind {
55
58
  StatementKind::Load { .. } => {
@@ -108,26 +111,37 @@ pub fn execute_audio_block(
108
111
  }
109
112
 
110
113
  StatementKind::Spawn => {
114
+ let mut temp_engine = AudioEngine::new(audio_engine.module_name.clone());
115
+
111
116
  if
112
- let Some((new_cursor_time, new_max_end_time, updated_engine)) =
113
- interprete_spawn_statement(
114
- &stmt,
115
- &mut audio_engine,
116
- &variable_table,
117
- base_bpm,
118
- base_duration,
119
- cursor_time,
120
- max_end_time
121
- )
117
+ let Some((_cur, _max, updated_engine)) = interprete_spawn_statement(
118
+ &stmt,
119
+ temp_engine,
120
+ &variable_table,
121
+ base_bpm,
122
+ base_duration,
123
+ initial_cursor_time,
124
+ max_end_time
125
+ )
122
126
  {
123
- cursor_time = new_cursor_time;
124
- max_end_time = new_max_end_time;
125
- audio_engine = updated_engine;
126
- } else {
127
- eprintln!("❌ Failed to interpret spawn statement: {:?}", stmt);
127
+ audio_engine.merge_with(updated_engine);
128
128
  }
129
129
  }
130
130
 
131
+ StatementKind::Call => {
132
+ let (call_engine, new_max, new_cursor) = interprete_call_statement(
133
+ &stmt,
134
+ audio_engine.clone(),
135
+ variable_table.clone(),
136
+ base_bpm,
137
+ base_duration,
138
+ max_end_time,
139
+ cursor_time
140
+ );
141
+
142
+ audio_engine.merge_with(call_engine);
143
+ }
144
+
131
145
  StatementKind::Sleep => {
132
146
  let (new_cursor, new_max) = interprete_sleep_statement(
133
147
  &stmt,
@@ -153,8 +167,8 @@ pub fn execute_audio_block(
153
167
  max_end_time = new_max;
154
168
  }
155
169
 
156
- StatementKind::Call => {
157
- let (call_engine, new_max, new_cursor) = interprete_call_statement(
170
+ StatementKind::If | StatementKind::ElseIf | StatementKind::Else => {
171
+ let (condition_engine, new_max, new_cursor) = interprete_condition_statement(
158
172
  &stmt,
159
173
  audio_engine.clone(),
160
174
  variable_table.clone(),
@@ -163,25 +177,23 @@ pub fn execute_audio_block(
163
177
  max_end_time,
164
178
  cursor_time
165
179
  );
166
- audio_engine = call_engine;
180
+
181
+ audio_engine = condition_engine;
167
182
  cursor_time = new_cursor;
168
183
  max_end_time = new_max;
169
184
  }
170
185
 
171
- StatementKind::If | StatementKind::ElseIf | StatementKind::Else => {
172
- let (condition_engine, new_max, new_cursor) = interprete_condition_statement(
186
+ StatementKind::ArrowCall { .. } => {
187
+ interprete_call_arrow_statement(
173
188
  &stmt,
174
- audio_engine.clone(),
175
- variable_table.clone(),
189
+ &mut audio_engine,
190
+ &variable_table,
176
191
  base_bpm,
177
192
  base_duration,
178
- max_end_time,
179
- cursor_time
193
+ &mut max_end_time,
194
+ Some(&mut cursor_time),
195
+ true
180
196
  );
181
-
182
- audio_engine = condition_engine;
183
- cursor_time = new_cursor;
184
- max_end_time = new_max;
185
197
  }
186
198
 
187
199
  | StatementKind::Bank
@@ -17,8 +17,16 @@ pub fn interprete_loop_statement(
17
17
  if let Value::Map(loop_value) = &stmt.value {
18
18
  let loop_count = match loop_value.get("iterator") {
19
19
  Some(Value::Number(n)) => *n as usize,
20
+ Some(Value::Identifier(ident)) => {
21
+ if let Some(Value::Number(n)) = variable_table.get(ident) {
22
+ *n as usize
23
+ } else {
24
+ eprintln!("❌ Loop iterator must be a number, found: {:?}", ident);
25
+ return (audio_engine, max_end_time, cursor_time);
26
+ }
27
+ }
20
28
  _ => {
21
- eprintln!("❌ Loop iterator must be a number");
29
+ eprintln!("❌ Loop iterator must be a number, found: {:?}", loop_value.get("iterator"));
22
30
  return (audio_engine, max_end_time, cursor_time);
23
31
  }
24
32
  };
@@ -26,7 +34,7 @@ pub fn interprete_loop_statement(
26
34
  let loop_body = match loop_value.get("body") {
27
35
  Some(Value::Block(body)) => body.clone(),
28
36
  _ => {
29
- eprintln!("❌ Loop body must be a block");
37
+ eprintln!("❌ Loop body must be a block, found: {:?}", loop_value.get("body"));
30
38
  return (audio_engine, max_end_time, cursor_time);
31
39
  }
32
40
  };
@@ -37,7 +45,7 @@ pub fn interprete_loop_statement(
37
45
 
38
46
  for _ in 0..loop_count {
39
47
  let (eng, _, end_time) = execute_audio_block(
40
- engine.clone(),
48
+ engine,
41
49
  variable_table.clone(),
42
50
  loop_body.clone(),
43
51
  base_bpm,
@@ -9,3 +9,4 @@ pub mod sleep;
9
9
  pub mod loop_;
10
10
  pub mod call;
11
11
  pub mod condition;
12
+ pub mod arrow_call;
@@ -7,58 +7,59 @@ use crate::core::{
7
7
 
8
8
  pub fn interprete_spawn_statement(
9
9
  stmt: &Statement,
10
- audio_engine: &mut AudioEngine,
10
+ audio_engine: AudioEngine,
11
11
  variable_table: &VariableTable,
12
12
  base_bpm: f32,
13
13
  base_duration: f32,
14
14
  cursor_time: f32,
15
15
  max_end_time: f32
16
16
  ) -> Option<(f32, f32, AudioEngine)> {
17
- if let Value::String(identifier) = &stmt.value {
18
- match variable_table.get(identifier) {
19
- Some(Value::Map(map)) => {
20
- if let Some(Value::Block(block)) = map.get("body") {
21
- let mut local_max = cursor_time;
22
- let mut updated_engine = audio_engine.clone();
23
-
24
- for inner_stmt in block {
25
- let (inner_engine, _, inner_end_time) = execute_audio_block(
26
- updated_engine.clone(),
27
- variable_table.clone(),
28
- vec![inner_stmt.clone()],
29
- base_bpm,
30
- base_duration,
31
- max_end_time,
32
- cursor_time
33
- );
34
-
35
- updated_engine = inner_engine;
36
-
37
- if inner_end_time > local_max {
38
- local_max = inner_end_time;
39
- }
40
- }
17
+ match &stmt.value {
18
+ Value::String(identifier) | Value::Identifier(identifier) => {
19
+ handle_spawn_identifier(
20
+ identifier,
21
+ audio_engine,
22
+ variable_table,
23
+ base_bpm,
24
+ base_duration,
25
+ cursor_time,
26
+ max_end_time
27
+ )
28
+ }
41
29
 
42
- let new_max_end_time = local_max.max(max_end_time);
30
+ _ => {
31
+ eprintln!("❌ Invalid spawn statement: expected identifier, found {:?}", stmt.value);
32
+ None
33
+ }
34
+ }
35
+ }
43
36
 
44
- return Some((local_max, new_max_end_time, updated_engine));
45
- } else {
46
- eprintln!("❌ Cannot spawn '{}': no valid body block", identifier);
47
- }
48
- }
49
- Some(other) => {
50
- eprintln!(
51
- "❌ Cannot spawn '{}': expected map with block, got {:?}",
52
- identifier,
53
- other
54
- );
55
- }
56
- None => {
57
- eprintln!("❌ Cannot spawn '{}': not found in variable table", identifier);
58
- }
37
+ fn handle_spawn_identifier(
38
+ identifier: &str,
39
+ audio_engine: AudioEngine,
40
+ variable_table: &VariableTable,
41
+ base_bpm: f32,
42
+ base_duration: f32,
43
+ cursor_time: f32,
44
+ max_end_time: f32
45
+ ) -> Option<(f32, f32, AudioEngine)> {
46
+ if let Some(Value::Map(map)) = variable_table.get(identifier) {
47
+ if let Some(Value::Block(block)) = map.get("body") {
48
+ let (eng, _, end_time) = execute_audio_block(
49
+ audio_engine.clone(),
50
+ variable_table.clone(),
51
+ block.clone(),
52
+ base_bpm,
53
+ base_duration,
54
+ max_end_time,
55
+ cursor_time
56
+ );
57
+ return Some((max_end_time.max(end_time), end_time, eng));
58
+ } else {
59
+ eprintln!("❌ Spawn group '{}' has no 'body' block", identifier);
59
60
  }
60
61
  } else {
61
- eprintln!("❌ Invalid spawn statement: expected a string identifier, got {:?}", stmt.value);
62
+ eprintln!("❌ Spawn group '{}' not found or not a map", identifier);
62
63
  }
63
64
 
64
65
  None
@@ -54,7 +54,7 @@ pub fn interprete_trigger_statement(
54
54
  );
55
55
 
56
56
  let mut updated_engine = audio_engine.clone();
57
- updated_engine.insert(&src, cursor_time, duration_final, None);
57
+ updated_engine.insert_sample(&src, cursor_time, duration_final, None);
58
58
 
59
59
  let new_cursor_time = cursor_time + duration_final;
60
60
  let new_max_end_time = new_cursor_time.max(max_end_time);
@@ -17,40 +17,37 @@ pub fn render_audio_with_modules(
17
17
  let mut result = HashMap::new();
18
18
 
19
19
  for (module_name, statements) in modules {
20
- let mut global_max_end_time = 0.0;
21
- let mut audio_engine = AudioEngine::new();
20
+ let mut global_max_end_time: f32 = 0.0;
21
+ let mut initial_engine = AudioEngine::new(module_name.clone());
22
22
 
23
- // Apply the module's variable table if it exists
23
+ // Apply global variables to the initial engine
24
24
  if let Some(module) = global_store.get_module(&module_name) {
25
- audio_engine.set_variables(module.variable_table.clone());
25
+ initial_engine.set_variables(module.variable_table.clone());
26
26
  }
27
27
 
28
- // Interpret the statements to fill the audio buffer
29
- let (mut audio_engine, module_base_bpm, module_max_end_time) = run_audio_program(
28
+ // interprete statements to fill the audio buffer
29
+ let (mut updated_engine, _bpm, module_max_end_time) = run_audio_program(
30
30
  &statements,
31
- audio_engine,
31
+ initial_engine,
32
32
  module_name.clone(),
33
33
  output_dir.to_string()
34
34
  );
35
35
 
36
- // Calculate the module's maximum duration
37
- global_max_end_time = module_max_end_time.max(global_max_end_time);
38
- audio_engine.set_duration(global_max_end_time);
39
-
40
- // Check if the buffer contains at least one non-zero sample
41
- if audio_engine.buffer.iter().all(|&s| s == 0) {
36
+ // Verify if the buffer is silent (all samples are zero)
37
+ if updated_engine.buffer.iter().all(|&s| s == 0) {
42
38
  let logger = Logger::new();
43
-
44
39
  logger.log_message(
45
40
  LogLevel::Warning,
46
- format!("Module '{}' ignored: silent buffer (no non-zero samples)", module_name).as_str()
41
+ &format!("Module '{}' ignored: silent buffer (no non-zero samples)", module_name)
47
42
  );
48
-
49
43
  continue;
50
44
  }
51
45
 
52
- // Insert only if the module produces sound
53
- result.insert(module_name, audio_engine);
46
+ // Determines the maximum end time for the module
47
+ global_max_end_time = global_max_end_time.max(module_max_end_time);
48
+ updated_engine.set_duration(global_max_end_time);
49
+
50
+ result.insert(module_name, updated_engine);
54
51
  }
55
52
 
56
53
  result
@@ -0,0 +1,31 @@
1
+ use crate::core::lexer::token::{ Token, TokenKind };
2
+
3
+ pub fn handle_arrow_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
+ let mut arrow_call = char.to_string();
13
+
14
+ while let Some(&c) = chars.peek() {
15
+ if c == '>' {
16
+ chars.next();
17
+ arrow_call.push(c);
18
+ *column += 1;
19
+ } else {
20
+ break;
21
+ }
22
+ }
23
+
24
+ tokens.push(Token {
25
+ kind: TokenKind::Arrow,
26
+ lexeme: arrow_call,
27
+ line: *line,
28
+ column: *column,
29
+ indent: *current_indent,
30
+ });
31
+ }
@@ -1,6 +1,6 @@
1
1
  use crate::core::lexer::{
2
2
  handler::{
3
- 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, 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, string::handle_string_lexer
4
4
  },
5
5
  token::{ Token, TokenKind },
6
6
  };
@@ -97,6 +97,17 @@ pub fn handle_content_lexing(content: String) -> Result<Vec<Token>, String> {
97
97
  &mut column
98
98
  );
99
99
  }
100
+ '-' => {
101
+ handle_arrow_lexer(
102
+ ch,
103
+ &mut chars,
104
+ &mut current_indent,
105
+ &mut indent_stack,
106
+ &mut tokens,
107
+ &mut line,
108
+ &mut column
109
+ );
110
+ }
100
111
  '{' => {
101
112
  handle_lbrace_lexer(
102
113
  ch,
@@ -27,6 +27,7 @@ pub fn handle_identifier_lexer(
27
27
  "bank" => TokenKind::Bank,
28
28
  "bpm" => TokenKind::Tempo,
29
29
  "loop" => TokenKind::Loop,
30
+ "synth" => TokenKind::Synth,
30
31
  _ => TokenKind::Identifier,
31
32
  };
32
33
 
@@ -11,3 +11,4 @@ pub mod newline;
11
11
  pub mod number;
12
12
  pub mod indent;
13
13
  pub mod string;
14
+ pub mod arrow;
@@ -2,6 +2,7 @@ pub mod handler;
2
2
  pub mod token;
3
3
 
4
4
  use std::fs;
5
+ use std::path::{Path, PathBuf};
5
6
  use crate::core::{
6
7
  lexer::{ handler::driver::handle_content_lexing, token::Token },
7
8
  utils::path::normalize_path,
@@ -20,11 +21,31 @@ impl Lexer {
20
21
 
21
22
  pub fn lex_tokens(&self, entrypoint: &str) -> Vec<Token> {
22
23
  let path = normalize_path(entrypoint);
24
+ let resolved_path = Self::resolve_entry_path(&path);
23
25
 
24
- let file_content = fs::read_to_string(&path).expect("Failed to read the entrypoint file");
26
+ let file_content =
27
+ fs::read_to_string(&resolved_path).expect("Failed to read the entrypoint file");
25
28
 
26
- let tokens = handle_content_lexing(file_content).expect("Failed to lex the content");
29
+ handle_content_lexing(file_content).expect("Failed to lex the content")
30
+ }
27
31
 
28
- tokens
32
+ fn resolve_entry_path(path: &str) -> String {
33
+ let candidate = Path::new(path);
34
+
35
+ if candidate.is_dir() {
36
+ let index_path = candidate.join("index.deva");
37
+ if index_path.exists() {
38
+ return index_path.to_string_lossy().replace("\\", "/");
39
+ } else {
40
+ panic!(
41
+ "Expected 'index.deva' in directory '{}', but it was not found",
42
+ path
43
+ );
44
+ }
45
+ } else if candidate.is_file() {
46
+ return path.to_string();
47
+ } else {
48
+ panic!("Provided entrypoint '{}' is not a valid file or directory", path);
49
+ }
29
50
  }
30
51
  }
@@ -30,11 +30,15 @@ pub enum TokenKind {
30
30
  Bank,
31
31
  Loop,
32
32
 
33
+ // ───── Instruments ─────
34
+ Synth,
35
+
33
36
  // ───── Literals ─────
34
37
  Identifier,
35
38
  Number,
36
39
  String,
37
40
  Boolean,
41
+ Arrow,
38
42
 
39
43
  // ───── Structures ─────
40
44
  Map,
@@ -2,6 +2,7 @@ use crate::core::{
2
2
  lexer::token::{ Token, TokenKind },
3
3
  parser::{
4
4
  handler::{
5
+ arrow_call::parse_arrow_call,
5
6
  at::parse_at_token,
6
7
  bank::parse_bank_token,
7
8
  condition::parse_condition_token,
@@ -10,7 +11,7 @@ use crate::core::{
10
11
  loop_::parse_loop_token,
11
12
  tempo::parse_tempo_token,
12
13
  },
13
- statement::{ Statement, StatementKind },
14
+ statement::Statement,
14
15
  },
15
16
  shared::value::Value,
16
17
  store::global::GlobalStore,
@@ -45,7 +46,7 @@ impl Parser {
45
46
  return None;
46
47
  }
47
48
 
48
- self.previous = self.tokens.get(self.token_index).cloned(); // mémorise avant de bouger
49
+ self.previous = self.tokens.get(self.token_index).cloned();
49
50
  self.token_index += 1;
50
51
 
51
52
  self.tokens.get(self.token_index - 1)
@@ -55,6 +56,14 @@ impl Parser {
55
56
  self.peek().map_or(false, |t| t.lexeme == expected)
56
57
  }
57
58
 
59
+ pub fn peek_nth(&self, n: usize) -> Option<&Token> {
60
+ if self.token_index + n < self.tokens.len() {
61
+ self.tokens.get(self.token_index + n)
62
+ } else {
63
+ None
64
+ }
65
+ }
66
+
58
67
  pub fn advance_if(&mut self, kind: TokenKind) -> bool {
59
68
  if self.match_token(kind) { true } else { false }
60
69
  }
@@ -107,9 +116,19 @@ impl Parser {
107
116
  }
108
117
  };
109
118
 
110
- let mut statement = match &token.kind {
119
+ let statement = match &token.kind {
111
120
  TokenKind::At => parse_at_token(self, global_store),
112
- TokenKind::Identifier => parse_identifier_token(self, global_store),
121
+ TokenKind::Identifier => {
122
+ if let Some(next) = self.peek_nth(1).cloned() {
123
+ if next.kind == TokenKind::Arrow {
124
+ parse_arrow_call(self, global_store)
125
+ } else {
126
+ parse_identifier_token(self, global_store)
127
+ }
128
+ } else {
129
+ parse_identifier_token(self, global_store)
130
+ }
131
+ }
113
132
  TokenKind::Dot => parse_dot_token(self, global_store),
114
133
  TokenKind::Tempo => parse_tempo_token(self, global_store),
115
134
  TokenKind::Bank => parse_bank_token(self, global_store),
@@ -0,0 +1,126 @@
1
+ use crate::core::{
2
+ lexer::token::TokenKind,
3
+ parser::{ driver::Parser, statement::{ Statement, StatementKind } },
4
+ shared::value::Value,
5
+ store::global::GlobalStore,
6
+ };
7
+
8
+ pub fn parse_arrow_call(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
9
+ let Some(target_token) = parser.peek_clone() else {
10
+ return Statement::unknown();
11
+ };
12
+
13
+ if target_token.kind != TokenKind::Identifier {
14
+ parser.advance(); // consume target token
15
+ return Statement::unknown();
16
+ }
17
+
18
+ let Some(arrow_token) = parser.peek_nth(1).cloned() else {
19
+ parser.advance(); // consume arrow token
20
+ return Statement {
21
+ kind: StatementKind::Unknown,
22
+ value: Value::String(target_token.lexeme.clone()),
23
+ indent: target_token.indent,
24
+ line: target_token.line,
25
+ column: target_token.column,
26
+ };
27
+ };
28
+
29
+ if arrow_token.kind != TokenKind::Arrow {
30
+ parser.advance(); // consume method token
31
+ return Statement {
32
+ kind: StatementKind::Unknown,
33
+ value: Value::String(target_token.lexeme.clone()),
34
+ indent: target_token.indent,
35
+ line: target_token.line,
36
+ column: target_token.column,
37
+ };
38
+ }
39
+
40
+ // We have a valid arrow call, so we consume the arrow token
41
+ let Some(method_token) = parser.peek_nth(2).cloned() else {
42
+ parser.advance();
43
+ return Statement::unknown();
44
+ };
45
+
46
+ if method_token.kind != TokenKind::Identifier {
47
+ parser.advance();
48
+ return Statement::unknown();
49
+ }
50
+
51
+ // Consume the tokens for target, arrow, and method
52
+ parser.advance(); // target
53
+ parser.advance(); // ->
54
+ parser.advance(); // method
55
+
56
+ let mut args = Vec::new();
57
+
58
+ while let Some(token) = parser.peek_clone() {
59
+ if token.kind == TokenKind::Newline || token.kind == TokenKind::EOF {
60
+ break;
61
+ }
62
+
63
+ parser.advance();
64
+
65
+ let value = match token.kind {
66
+ TokenKind::Identifier => Value::Identifier(token.lexeme.clone()),
67
+ TokenKind::String => Value::String(token.lexeme.clone()),
68
+ TokenKind::Number => Value::Number(token.lexeme.parse::<f32>().unwrap_or(0.0)),
69
+ TokenKind::LBrace => {
70
+ // Handle map literal
71
+ let mut map = std::collections::HashMap::new();
72
+ while let Some(inner_token) = parser.peek_clone() {
73
+ if inner_token.kind == TokenKind::RBrace {
74
+ parser.advance(); // consume RBrace
75
+ break;
76
+ }
77
+ if inner_token.kind == TokenKind::Newline || inner_token.kind == TokenKind::EOF {
78
+ break;
79
+ }
80
+ parser.advance(); // consume key token
81
+ let key = inner_token.lexeme.clone();
82
+
83
+ if let Some(colon_token) = parser.peek_clone() {
84
+ if colon_token.kind == TokenKind::Colon {
85
+ parser.advance(); // consume colon
86
+ if let Some(value_token) = parser.peek_clone() {
87
+ parser.advance(); // consume value token
88
+ let value = match value_token.kind {
89
+ TokenKind::Identifier =>
90
+ Value::Identifier(value_token.lexeme.clone()),
91
+ TokenKind::String => Value::String(value_token.lexeme.clone()),
92
+ TokenKind::Number =>
93
+ Value::Number(
94
+ value_token.lexeme.parse::<f32>().unwrap_or(0.0)
95
+ ),
96
+ TokenKind::Boolean =>
97
+ Value::Boolean(
98
+ value_token.lexeme.parse::<bool>().unwrap_or(false)
99
+ ),
100
+ _ => Value::Unknown,
101
+ };
102
+ map.insert(key, value);
103
+ }
104
+ }
105
+ }
106
+ }
107
+ Value::Map(map)
108
+ }
109
+ _ => Value::Unknown,
110
+ };
111
+
112
+ args.push(value);
113
+ }
114
+
115
+ Statement {
116
+ kind: StatementKind::ArrowCall {
117
+ target: target_token.lexeme.clone(),
118
+ method: method_token.lexeme.clone(),
119
+ args,
120
+ },
121
+ value: Value::Null,
122
+ indent: target_token.indent,
123
+ line: target_token.line,
124
+ column: target_token.column,
125
+ }
126
+ }