@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.
- package/Cargo.toml +1 -1
- package/README.md +4 -8
- package/docs/CHANGELOG.md +27 -0
- package/examples/condition.deva +8 -12
- package/examples/group.deva +3 -3
- package/examples/index.deva +10 -8
- package/examples/loop.deva +10 -8
- package/examples/synth.deva +14 -0
- package/examples/variables.deva +1 -1
- package/out-tsc/bin/devalang.exe +0 -0
- package/out-tsc/scripts/version/fetch.js +1 -5
- package/package.json +1 -1
- package/project-version.json +3 -3
- package/rust/core/audio/engine.rs +89 -12
- package/rust/core/audio/interpreter/arrow_call.rs +129 -0
- package/rust/core/audio/interpreter/call.rs +29 -7
- package/rust/core/audio/interpreter/condition.rs +5 -1
- package/rust/core/audio/interpreter/driver.rs +41 -29
- package/rust/core/audio/interpreter/loop_.rs +11 -3
- package/rust/core/audio/interpreter/mod.rs +1 -0
- package/rust/core/audio/interpreter/spawn.rs +43 -42
- package/rust/core/audio/interpreter/trigger.rs +1 -1
- package/rust/core/audio/renderer.rs +15 -18
- package/rust/core/lexer/handler/arrow.rs +31 -0
- package/rust/core/lexer/handler/driver.rs +12 -1
- package/rust/core/lexer/handler/identifier.rs +1 -0
- package/rust/core/lexer/handler/mod.rs +1 -0
- package/rust/core/lexer/mod.rs +24 -3
- package/rust/core/lexer/token.rs +4 -0
- package/rust/core/parser/driver.rs +23 -4
- package/rust/core/parser/handler/arrow_call.rs +126 -0
- package/rust/core/parser/handler/identifier/call.rs +41 -0
- package/rust/core/parser/handler/identifier/group.rs +75 -0
- package/rust/core/parser/handler/identifier/let_.rs +133 -0
- package/rust/core/parser/handler/identifier/mod.rs +51 -0
- package/rust/core/parser/handler/identifier/sleep.rs +33 -0
- package/rust/core/parser/handler/identifier/spawn.rs +41 -0
- package/rust/core/parser/handler/identifier/synth.rs +65 -0
- package/rust/core/parser/handler/loop_.rs +24 -18
- package/rust/core/parser/handler/mod.rs +2 -1
- package/rust/core/parser/statement.rs +8 -0
- package/rust/core/preprocessor/loader.rs +57 -43
- package/rust/core/preprocessor/module.rs +3 -6
- package/rust/core/preprocessor/processor.rs +13 -4
- package/rust/core/preprocessor/resolver/call.rs +99 -29
- package/rust/core/preprocessor/resolver/condition.rs +38 -12
- package/rust/core/preprocessor/resolver/driver.rs +74 -29
- package/rust/core/preprocessor/resolver/group.rs +24 -81
- package/rust/core/preprocessor/resolver/let_.rs +31 -0
- package/rust/core/preprocessor/resolver/loop_.rs +62 -116
- package/rust/core/preprocessor/resolver/mod.rs +5 -1
- package/rust/core/preprocessor/resolver/spawn.rs +41 -36
- package/rust/core/preprocessor/resolver/synth.rs +50 -0
- package/rust/core/preprocessor/resolver/trigger.rs +51 -50
- package/rust/core/preprocessor/resolver/value.rs +78 -0
- package/rust/core/utils/path.rs +17 -32
- package/rust/core/utils/validation.rs +30 -28
- package/typescript/scripts/version/fetch.ts +1 -6
- 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
|
|
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((
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
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::
|
|
157
|
-
let (
|
|
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
|
-
|
|
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::
|
|
172
|
-
|
|
186
|
+
StatementKind::ArrowCall { .. } => {
|
|
187
|
+
interprete_call_arrow_statement(
|
|
173
188
|
&stmt,
|
|
174
|
-
audio_engine
|
|
175
|
-
variable_table
|
|
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
|
|
48
|
+
engine,
|
|
41
49
|
variable_table.clone(),
|
|
42
50
|
loop_body.clone(),
|
|
43
51
|
base_bpm,
|
|
@@ -7,58 +7,59 @@ use crate::core::{
|
|
|
7
7
|
|
|
8
8
|
pub fn interprete_spawn_statement(
|
|
9
9
|
stmt: &Statement,
|
|
10
|
-
audio_engine:
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
30
|
+
_ => {
|
|
31
|
+
eprintln!("❌ Invalid spawn statement: expected identifier, found {:?}", stmt.value);
|
|
32
|
+
None
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
43
36
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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!("❌
|
|
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.
|
|
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
|
|
20
|
+
let mut global_max_end_time: f32 = 0.0;
|
|
21
|
+
let mut initial_engine = AudioEngine::new(module_name.clone());
|
|
22
22
|
|
|
23
|
-
// Apply
|
|
23
|
+
// Apply global variables to the initial engine
|
|
24
24
|
if let Some(module) = global_store.get_module(&module_name) {
|
|
25
|
-
|
|
25
|
+
initial_engine.set_variables(module.variable_table.clone());
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
//
|
|
29
|
-
let (mut
|
|
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
|
-
|
|
31
|
+
initial_engine,
|
|
32
32
|
module_name.clone(),
|
|
33
33
|
output_dir.to_string()
|
|
34
34
|
);
|
|
35
35
|
|
|
36
|
-
//
|
|
37
|
-
|
|
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)
|
|
41
|
+
&format!("Module '{}' ignored: silent buffer (no non-zero samples)", module_name)
|
|
47
42
|
);
|
|
48
|
-
|
|
49
43
|
continue;
|
|
50
44
|
}
|
|
51
45
|
|
|
52
|
-
//
|
|
53
|
-
|
|
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,
|
package/rust/core/lexer/mod.rs
CHANGED
|
@@ -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 =
|
|
26
|
+
let file_content =
|
|
27
|
+
fs::read_to_string(&resolved_path).expect("Failed to read the entrypoint file");
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
handle_content_lexing(file_content).expect("Failed to lex the content")
|
|
30
|
+
}
|
|
27
31
|
|
|
28
|
-
|
|
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
|
}
|
package/rust/core/lexer/token.rs
CHANGED
|
@@ -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::
|
|
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();
|
|
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
|
|
119
|
+
let statement = match &token.kind {
|
|
111
120
|
TokenKind::At => parse_at_token(self, global_store),
|
|
112
|
-
TokenKind::Identifier =>
|
|
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
|
+
}
|