@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.
- package/.devalang +8 -9
- package/Cargo.toml +8 -3
- package/README.md +36 -34
- package/docs/CHANGELOG.md +65 -1
- package/docs/CONTRIBUTING.md +1 -0
- package/docs/ROADMAP.md +2 -2
- package/docs/TODO.md +6 -5
- package/examples/bank.deva +2 -4
- package/examples/function.deva +15 -0
- package/examples/index.deva +25 -14
- package/out-tsc/bin/devalang.exe +0 -0
- package/package.json +6 -6
- package/project-version.json +3 -3
- package/rust/cli/bank.rs +2 -1
- package/rust/cli/build.rs +76 -14
- package/rust/cli/check.rs +71 -8
- package/rust/cli/driver.rs +40 -28
- package/rust/cli/install.rs +22 -7
- package/rust/cli/login.rs +134 -0
- package/rust/cli/mod.rs +2 -1
- package/rust/cli/play.rs +45 -20
- package/rust/common/api.rs +8 -0
- package/rust/common/cdn.rs +2 -5
- package/rust/common/mod.rs +3 -1
- package/rust/common/sso.rs +8 -0
- package/rust/config/driver.rs +19 -1
- package/rust/config/loader.rs +56 -10
- package/rust/core/audio/engine.rs +254 -91
- package/rust/core/audio/interpreter/arrow_call.rs +34 -15
- package/rust/core/audio/interpreter/call.rs +72 -47
- package/rust/core/audio/interpreter/condition.rs +14 -12
- package/rust/core/audio/interpreter/driver.rs +90 -128
- package/rust/core/audio/interpreter/function.rs +21 -0
- package/rust/core/audio/interpreter/load.rs +1 -1
- package/rust/core/audio/interpreter/loop_.rs +24 -18
- package/rust/core/audio/interpreter/mod.rs +2 -1
- package/rust/core/audio/interpreter/sleep.rs +0 -6
- package/rust/core/audio/interpreter/spawn.rs +78 -60
- package/rust/core/audio/interpreter/trigger.rs +157 -70
- package/rust/core/audio/loader/trigger.rs +37 -4
- package/rust/core/audio/player.rs +20 -10
- package/rust/core/audio/renderer.rs +24 -25
- package/rust/core/builder/mod.rs +11 -6
- package/rust/core/debugger/mod.rs +2 -0
- package/rust/core/debugger/module.rs +47 -0
- package/rust/core/debugger/store.rs +25 -11
- package/rust/core/error/mod.rs +6 -0
- package/rust/core/lexer/handler/driver.rs +23 -1
- package/rust/core/lexer/handler/identifier.rs +1 -0
- package/rust/core/lexer/handler/indent.rs +16 -2
- package/rust/core/lexer/handler/mod.rs +1 -0
- package/rust/core/lexer/handler/parenthesis.rs +41 -0
- package/rust/core/lexer/token.rs +4 -0
- package/rust/core/mod.rs +2 -1
- package/rust/core/parser/driver.rs +47 -4
- package/rust/core/parser/handler/arrow_call.rs +78 -18
- package/rust/core/parser/handler/bank.rs +35 -7
- package/rust/core/parser/handler/dot.rs +81 -123
- package/rust/core/parser/handler/identifier/call.rs +69 -22
- package/rust/core/parser/handler/identifier/function.rs +92 -0
- package/rust/core/parser/handler/identifier/let_.rs +13 -19
- package/rust/core/parser/handler/identifier/mod.rs +1 -0
- package/rust/core/parser/handler/identifier/spawn.rs +74 -27
- package/rust/core/parser/statement.rs +16 -4
- package/rust/core/plugin/loader.rs +48 -0
- package/rust/core/plugin/mod.rs +1 -0
- package/rust/core/preprocessor/loader.rs +50 -32
- package/rust/core/preprocessor/module.rs +3 -1
- package/rust/core/preprocessor/processor.rs +26 -1
- package/rust/core/preprocessor/resolver/call.rs +61 -84
- package/rust/core/preprocessor/resolver/condition.rs +11 -6
- package/rust/core/preprocessor/resolver/driver.rs +52 -6
- package/rust/core/preprocessor/resolver/function.rs +78 -0
- package/rust/core/preprocessor/resolver/group.rs +43 -13
- package/rust/core/preprocessor/resolver/let_.rs +7 -10
- package/rust/core/preprocessor/resolver/mod.rs +2 -1
- package/rust/core/preprocessor/resolver/spawn.rs +64 -30
- package/rust/core/preprocessor/resolver/trigger.rs +7 -3
- package/rust/core/preprocessor/resolver/value.rs +10 -1
- package/rust/core/shared/value.rs +4 -1
- package/rust/core/store/function.rs +34 -0
- package/rust/core/store/global.rs +9 -10
- package/rust/core/store/mod.rs +2 -1
- package/rust/core/store/variable.rs +6 -0
- package/rust/installer/addon.rs +80 -0
- package/rust/installer/bank.rs +24 -14
- package/rust/installer/mod.rs +4 -1
- package/rust/installer/plugin.rs +55 -0
- package/rust/lib.rs +10 -7
- package/rust/main.rs +32 -9
- package/rust/utils/logger.rs +16 -0
- package/rust/utils/mod.rs +45 -1
- package/rust/utils/spinner.rs +2 -4
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
use std::collections::HashMap;
|
|
2
|
-
|
|
3
2
|
use crate::{
|
|
4
3
|
core::{
|
|
5
4
|
audio::{ engine::AudioEngine, interpreter::driver::run_audio_program },
|
|
@@ -18,36 +17,36 @@ pub fn render_audio_with_modules(
|
|
|
18
17
|
|
|
19
18
|
for (module_name, statements) in modules {
|
|
20
19
|
let mut global_max_end_time: f32 = 0.0;
|
|
21
|
-
let mut
|
|
20
|
+
let mut audio_engine = AudioEngine::new(module_name.clone());
|
|
22
21
|
|
|
23
22
|
// Apply global variables to the initial engine
|
|
24
23
|
if let Some(module) = global_store.get_module(&module_name) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
// Verify if the buffer is silent (all samples are zero)
|
|
37
|
-
if updated_engine.buffer.iter().all(|&s| s == 0) {
|
|
38
|
-
let logger = Logger::new();
|
|
39
|
-
logger.log_message(
|
|
40
|
-
LogLevel::Warning,
|
|
41
|
-
&format!("Module '{}' ignored: silent buffer (no non-zero samples)", module_name)
|
|
24
|
+
// interprete statements to fill the audio buffer
|
|
25
|
+
let (module_max_end_time, cursor_time) = run_audio_program(
|
|
26
|
+
&statements,
|
|
27
|
+
&mut audio_engine,
|
|
28
|
+
module_name.clone(),
|
|
29
|
+
output_dir.to_string(),
|
|
30
|
+
module.variable_table.clone(),
|
|
31
|
+
module.function_table.clone(),
|
|
32
|
+
global_store
|
|
42
33
|
);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
34
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
35
|
+
// Verify if the buffer is silent (all samples are zero)
|
|
36
|
+
if audio_engine.buffer.iter().all(|&s| s == 0) {
|
|
37
|
+
let logger = Logger::new();
|
|
38
|
+
logger.log_message(
|
|
39
|
+
LogLevel::Warning,
|
|
40
|
+
&format!("Module '{}' ignored: silent buffer (no non-zero samples)", module_name)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Determines the maximum end time for the module
|
|
45
|
+
global_max_end_time = global_max_end_time.max(module_max_end_time);
|
|
46
|
+
audio_engine.set_duration(global_max_end_time);
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
result.insert(module_name, audio_engine);
|
|
49
|
+
}
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
result
|
package/rust/core/builder/mod.rs
CHANGED
|
@@ -3,7 +3,6 @@ use crate::core::parser::statement::Statement;
|
|
|
3
3
|
use crate::core::store::global::GlobalStore;
|
|
4
4
|
use std::{ collections::HashMap, fs::create_dir_all };
|
|
5
5
|
use std::io::Write;
|
|
6
|
-
|
|
7
6
|
use crate::utils::logger::Logger;
|
|
8
7
|
|
|
9
8
|
pub struct Builder {}
|
|
@@ -13,7 +12,12 @@ impl Builder {
|
|
|
13
12
|
Builder {}
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
pub fn build_ast(
|
|
15
|
+
pub fn build_ast(
|
|
16
|
+
&self,
|
|
17
|
+
modules: &HashMap<String, Vec<Statement>>,
|
|
18
|
+
out_dir: &str,
|
|
19
|
+
compress: bool
|
|
20
|
+
) {
|
|
17
21
|
for (name, statements) in modules {
|
|
18
22
|
let formatted_name = name.split("/").last().unwrap_or(name);
|
|
19
23
|
let formatted_name = formatted_name.replace(".deva", "");
|
|
@@ -22,10 +26,11 @@ impl Builder {
|
|
|
22
26
|
|
|
23
27
|
let file_path = format!("{}/ast/{}.json", out_dir, formatted_name);
|
|
24
28
|
let mut file = std::fs::File::create(file_path).expect("Failed to create AST file");
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.expect("Failed to serialize AST")
|
|
29
|
+
let content = if compress {
|
|
30
|
+
serde_json::to_string(&statements).expect("Failed to serialize AST")
|
|
31
|
+
} else {
|
|
32
|
+
serde_json::to_string_pretty(&statements).expect("Failed to serialize AST")
|
|
33
|
+
};
|
|
29
34
|
|
|
30
35
|
file.write_all(content.as_bytes()).expect("Failed to write AST to file");
|
|
31
36
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
pub mod preprocessor;
|
|
2
2
|
pub mod lexer;
|
|
3
3
|
pub mod store;
|
|
4
|
+
pub mod module;
|
|
4
5
|
|
|
5
6
|
use std::io::Write;
|
|
6
7
|
|
|
@@ -13,6 +14,7 @@ impl Debugger {
|
|
|
13
14
|
|
|
14
15
|
pub fn write_log_file(&self, path: &str, filename: &str, content: &str) {
|
|
15
16
|
std::fs::create_dir_all(path).expect("Failed to create directory");
|
|
17
|
+
|
|
16
18
|
let file_path = format!("{}/{}", path, filename);
|
|
17
19
|
let mut file = std::fs::File::create(file_path).expect("Failed to create file");
|
|
18
20
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
use std::{ fs::create_dir_all };
|
|
2
|
+
use crate::core::{
|
|
3
|
+
debugger::Debugger,
|
|
4
|
+
store::{ function::FunctionTable, variable::VariableTable },
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
pub fn write_module_variable_log_file(
|
|
8
|
+
output_dir: &str,
|
|
9
|
+
module_path: &str,
|
|
10
|
+
variable_table: &VariableTable
|
|
11
|
+
) {
|
|
12
|
+
let debugger = Debugger::new();
|
|
13
|
+
let mut content = String::new();
|
|
14
|
+
let module_name = module_path.split('/').last().unwrap_or("index").replace(".deva", "");
|
|
15
|
+
|
|
16
|
+
let log_directory = format!("{}/logs/modules/{}", output_dir, module_name);
|
|
17
|
+
create_dir_all(&log_directory).expect("Failed to create log directory");
|
|
18
|
+
|
|
19
|
+
for (var_name, var_data) in &variable_table.variables {
|
|
20
|
+
content.push_str(&format!("{:?} = {:?}\n", var_name, var_data));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
content.push_str("\n");
|
|
24
|
+
|
|
25
|
+
debugger.write_log_file(&log_directory, "variables.log", &content);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
pub fn write_module_function_log_file(
|
|
29
|
+
output_dir: &str,
|
|
30
|
+
module_path: &str,
|
|
31
|
+
function_table: &FunctionTable
|
|
32
|
+
) {
|
|
33
|
+
let debugger = Debugger::new();
|
|
34
|
+
let mut content = String::new();
|
|
35
|
+
let module_name = module_path.split('/').last().unwrap_or("index").replace(".deva", "");
|
|
36
|
+
|
|
37
|
+
let log_directory = format!("{}/logs/modules/{}", output_dir, module_name);
|
|
38
|
+
create_dir_all(&log_directory).expect("Failed to create log directory");
|
|
39
|
+
|
|
40
|
+
for (func_name, func_data) in &function_table.functions {
|
|
41
|
+
content.push_str(&format!("{:?} = {:?}\n", func_name, func_data));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
content.push_str("\n");
|
|
45
|
+
|
|
46
|
+
debugger.write_log_file(&log_directory, "functions.log", &content);
|
|
47
|
+
}
|
|
@@ -1,25 +1,39 @@
|
|
|
1
|
-
use std::{
|
|
2
|
-
use crate::core::{
|
|
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
|
|
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 (
|
|
12
|
-
content.push_str(&format!("
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
19
|
-
|
|
27
|
+
let log_directory = format!("{}/logs", output_dir);
|
|
28
|
+
create_dir_all(&log_directory).expect("Failed to create log directory");
|
|
20
29
|
|
|
21
|
-
|
|
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
|
}
|
package/rust/core/error/mod.rs
CHANGED
|
@@ -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,
|
|
@@ -16,10 +16,24 @@ pub fn handle_indent_lexer(
|
|
|
16
16
|
*current_indent += 1;
|
|
17
17
|
chars.next();
|
|
18
18
|
col += 1;
|
|
19
|
+
tokens.push(Token {
|
|
20
|
+
kind: TokenKind::Whitespace,
|
|
21
|
+
lexeme: " ".to_string(),
|
|
22
|
+
line: *line,
|
|
23
|
+
column: col,
|
|
24
|
+
indent: *current_indent,
|
|
25
|
+
});
|
|
19
26
|
} else if c == '\t' {
|
|
20
27
|
*current_indent += 4;
|
|
21
28
|
chars.next();
|
|
22
29
|
col += 4;
|
|
30
|
+
tokens.push(Token {
|
|
31
|
+
kind: TokenKind::Whitespace,
|
|
32
|
+
lexeme: "\t".to_string(),
|
|
33
|
+
line: *line,
|
|
34
|
+
column: col,
|
|
35
|
+
indent: *current_indent,
|
|
36
|
+
});
|
|
23
37
|
} else {
|
|
24
38
|
break;
|
|
25
39
|
}
|
|
@@ -32,7 +46,7 @@ pub fn handle_indent_lexer(
|
|
|
32
46
|
indent_stack.push(*current_indent);
|
|
33
47
|
tokens.push(Token {
|
|
34
48
|
kind: TokenKind::Indent,
|
|
35
|
-
lexeme: String::
|
|
49
|
+
lexeme: String::from("<INDENT>"),
|
|
36
50
|
line: *line,
|
|
37
51
|
column: *column,
|
|
38
52
|
indent: *current_indent,
|
|
@@ -42,7 +56,7 @@ pub fn handle_indent_lexer(
|
|
|
42
56
|
indent_stack.pop();
|
|
43
57
|
tokens.push(Token {
|
|
44
58
|
kind: TokenKind::Dedent,
|
|
45
|
-
lexeme: String::
|
|
59
|
+
lexeme: String::from("<DEDENT>"),
|
|
46
60
|
line: *line,
|
|
47
61
|
column: *column,
|
|
48
62
|
indent: *current_indent,
|
|
@@ -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
|
+
}
|
package/rust/core/lexer/token.rs
CHANGED
|
@@ -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, // '
|
|
@@ -81,6 +84,7 @@ pub enum TokenKind {
|
|
|
81
84
|
ElseIf,
|
|
82
85
|
|
|
83
86
|
// ───── Special / Internal ─────
|
|
87
|
+
Whitespace,
|
|
84
88
|
Unknown,
|
|
85
89
|
Error(String),
|
|
86
90
|
EOF,
|
package/rust/core/mod.rs
CHANGED
|
@@ -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
|
},
|
|
@@ -107,7 +107,11 @@ impl Parser {
|
|
|
107
107
|
tokens: Vec<Token>,
|
|
108
108
|
global_store: &mut GlobalStore
|
|
109
109
|
) -> Vec<Statement> {
|
|
110
|
-
|
|
110
|
+
// Filtrer les tokens Whitespace et Newline avant parsing
|
|
111
|
+
self.tokens = tokens
|
|
112
|
+
.into_iter()
|
|
113
|
+
.filter(|t| t.kind != TokenKind::Whitespace && t.kind != TokenKind::Newline)
|
|
114
|
+
.collect();
|
|
111
115
|
self.token_index = 0;
|
|
112
116
|
|
|
113
117
|
let mut statements = Vec::new();
|
|
@@ -120,6 +124,11 @@ impl Parser {
|
|
|
120
124
|
}
|
|
121
125
|
};
|
|
122
126
|
|
|
127
|
+
if token.kind == TokenKind::Newline {
|
|
128
|
+
self.advance();
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
123
132
|
let statement = match &token.kind {
|
|
124
133
|
TokenKind::At => parse_at_token(self, global_store),
|
|
125
134
|
TokenKind::Identifier => {
|
|
@@ -138,6 +147,7 @@ impl Parser {
|
|
|
138
147
|
TokenKind::Bank => parse_bank_token(self, global_store),
|
|
139
148
|
TokenKind::Loop => parse_loop_token(self, global_store),
|
|
140
149
|
TokenKind::If => parse_condition_token(self, global_store),
|
|
150
|
+
TokenKind::Function => parse_function_token(self, global_store),
|
|
141
151
|
|
|
142
152
|
| TokenKind::Else // Ignore else, already handled in `parse_condition_token`
|
|
143
153
|
| TokenKind::Comment
|
|
@@ -148,7 +158,6 @@ impl Parser {
|
|
|
148
158
|
| TokenKind::LBrace
|
|
149
159
|
| TokenKind::RBrace
|
|
150
160
|
| TokenKind::Comma
|
|
151
|
-
| TokenKind::Newline
|
|
152
161
|
| TokenKind::Dedent
|
|
153
162
|
| TokenKind::Indent => {
|
|
154
163
|
self.advance();
|
|
@@ -188,17 +197,50 @@ impl Parser {
|
|
|
188
197
|
let mut map = std::collections::HashMap::new();
|
|
189
198
|
|
|
190
199
|
while !self.check_token(TokenKind::RBrace) && !self.is_eof() {
|
|
200
|
+
// Skip newlines, whitespace, indent, dedent before the key
|
|
201
|
+
while
|
|
202
|
+
self.check_token(TokenKind::Newline) ||
|
|
203
|
+
self.check_token(TokenKind::Whitespace) ||
|
|
204
|
+
self.check_token(TokenKind::Indent) ||
|
|
205
|
+
self.check_token(TokenKind::Dedent)
|
|
206
|
+
{
|
|
207
|
+
self.advance();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Check if we are at the closing brace of the map
|
|
211
|
+
if self.check_token(TokenKind::RBrace) {
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
|
|
191
215
|
let key = if let Some(token) = self.advance() {
|
|
192
|
-
token.
|
|
216
|
+
match token.kind {
|
|
217
|
+
| TokenKind::Whitespace
|
|
218
|
+
| TokenKind::Indent
|
|
219
|
+
| TokenKind::Dedent
|
|
220
|
+
| TokenKind::Newline => {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
_ => token.lexeme.clone(),
|
|
224
|
+
}
|
|
193
225
|
} else {
|
|
194
226
|
break;
|
|
195
227
|
};
|
|
196
228
|
|
|
229
|
+
// Skip newlines and whitespace before colon
|
|
230
|
+
while self.check_token(TokenKind::Newline) || self.check_token(TokenKind::Whitespace) {
|
|
231
|
+
self.advance();
|
|
232
|
+
}
|
|
233
|
+
|
|
197
234
|
if !self.match_token(TokenKind::Colon) {
|
|
198
235
|
println!("Expected ':' after map key '{}'", key);
|
|
199
236
|
break;
|
|
200
237
|
}
|
|
201
238
|
|
|
239
|
+
// Skip newlines and whitespace before value
|
|
240
|
+
while self.check_token(TokenKind::Newline) || self.check_token(TokenKind::Whitespace) {
|
|
241
|
+
self.advance();
|
|
242
|
+
}
|
|
243
|
+
|
|
202
244
|
let value = if let Some(token) = self.peek_clone() {
|
|
203
245
|
match token.kind {
|
|
204
246
|
TokenKind::String => {
|
|
@@ -303,6 +345,7 @@ impl Parser {
|
|
|
303
345
|
}
|
|
304
346
|
collected.push(self.advance().unwrap().clone());
|
|
305
347
|
}
|
|
348
|
+
|
|
306
349
|
collected
|
|
307
350
|
}
|
|
308
351
|
|
|
@@ -54,11 +54,43 @@ pub fn parse_arrow_call(parser: &mut Parser, global_store: &mut GlobalStore) ->
|
|
|
54
54
|
parser.advance(); // method
|
|
55
55
|
|
|
56
56
|
let mut args = Vec::new();
|
|
57
|
+
let mut paren_depth = 0;
|
|
58
|
+
let mut map_depth = 0;
|
|
57
59
|
|
|
58
60
|
while let Some(token) = parser.peek_clone() {
|
|
59
61
|
if token.kind == TokenKind::Newline || token.kind == TokenKind::EOF {
|
|
60
62
|
break;
|
|
61
63
|
}
|
|
64
|
+
if token.kind == TokenKind::LParen {
|
|
65
|
+
paren_depth += 1;
|
|
66
|
+
}
|
|
67
|
+
if token.kind == TokenKind::RParen {
|
|
68
|
+
if paren_depth > 0 {
|
|
69
|
+
paren_depth -= 1;
|
|
70
|
+
parser.advance();
|
|
71
|
+
if paren_depth == 0 {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
continue;
|
|
75
|
+
} else {
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if token.kind == TokenKind::LBrace {
|
|
80
|
+
map_depth += 1;
|
|
81
|
+
}
|
|
82
|
+
if token.kind == TokenKind::RBrace {
|
|
83
|
+
if map_depth > 0 {
|
|
84
|
+
map_depth -= 1;
|
|
85
|
+
parser.advance();
|
|
86
|
+
if map_depth == 0 {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
continue;
|
|
90
|
+
} else {
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
62
94
|
|
|
63
95
|
parser.advance();
|
|
64
96
|
|
|
@@ -79,49 +111,68 @@ pub fn parse_arrow_call(parser: &mut Parser, global_store: &mut GlobalStore) ->
|
|
|
79
111
|
}
|
|
80
112
|
parser.advance(); // consume key token
|
|
81
113
|
let key = inner_token.lexeme.clone();
|
|
82
|
-
|
|
83
114
|
if let Some(colon_token) = parser.peek_clone() {
|
|
84
115
|
if colon_token.kind == TokenKind::Colon {
|
|
85
116
|
parser.advance(); // consume colon
|
|
86
117
|
if let Some(value_token) = parser.peek_clone() {
|
|
87
118
|
parser.advance(); // consume value token
|
|
88
119
|
let value = match value_token.kind {
|
|
89
|
-
TokenKind::Identifier =>
|
|
90
|
-
|
|
120
|
+
TokenKind::Identifier => {
|
|
121
|
+
// Interpret bare true/false as booleans
|
|
122
|
+
if value_token.lexeme == "true" {
|
|
123
|
+
Value::Boolean(true)
|
|
124
|
+
} else if value_token.lexeme == "false" {
|
|
125
|
+
Value::Boolean(false)
|
|
126
|
+
} else {
|
|
127
|
+
Value::Identifier(value_token.lexeme.clone())
|
|
128
|
+
}
|
|
129
|
+
},
|
|
91
130
|
TokenKind::String => Value::String(value_token.lexeme.clone()),
|
|
92
131
|
TokenKind::Number => {
|
|
132
|
+
// Support decimals (e.g., 0.8) and beats (e.g., 1/4)
|
|
93
133
|
if let Some(TokenKind::Slash) = parser.peek_kind() {
|
|
94
|
-
|
|
134
|
+
// Beat fraction
|
|
135
|
+
parser.advance(); // consume '/'
|
|
95
136
|
if let Some(denominator_token) = parser.peek_clone() {
|
|
96
137
|
if denominator_token.kind == TokenKind::Number {
|
|
97
138
|
parser.advance(); // consume denominator
|
|
98
|
-
let denominator =
|
|
99
|
-
|
|
100
|
-
Value::Beat(
|
|
101
|
-
format!(
|
|
102
|
-
"{}/{}",
|
|
103
|
-
value_token.lexeme,
|
|
104
|
-
denominator
|
|
105
|
-
)
|
|
106
|
-
)
|
|
139
|
+
let denominator = denominator_token.lexeme.clone();
|
|
140
|
+
Value::Beat(format!("{}/{}", value_token.lexeme, denominator))
|
|
107
141
|
} else {
|
|
108
142
|
Value::Unknown
|
|
109
143
|
}
|
|
110
144
|
} else {
|
|
111
145
|
Value::Unknown
|
|
112
146
|
}
|
|
147
|
+
} else if let Some(next) = parser.peek_clone() {
|
|
148
|
+
// Decimal number handling: NUMBER '.' NUMBER -> f32
|
|
149
|
+
if next.kind == TokenKind::Dot {
|
|
150
|
+
// consume '.'
|
|
151
|
+
parser.advance();
|
|
152
|
+
if let Some(after_dot) = parser.peek_clone() {
|
|
153
|
+
if after_dot.kind == TokenKind::Number {
|
|
154
|
+
parser.advance(); // consume fractional digits
|
|
155
|
+
let combined = format!("{}.{}", value_token.lexeme, after_dot.lexeme);
|
|
156
|
+
Value::Number(combined.parse::<f32>().unwrap_or(0.0))
|
|
157
|
+
} else {
|
|
158
|
+
// Lone dot without number, fallback to integer part
|
|
159
|
+
Value::Number(value_token.lexeme.parse::<f32>().unwrap_or(0.0))
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
Value::Number(value_token.lexeme.parse::<f32>().unwrap_or(0.0))
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
// Regular integer number
|
|
166
|
+
Value::Number(value_token.lexeme.parse::<f32>().unwrap_or(0.0))
|
|
167
|
+
}
|
|
113
168
|
} else {
|
|
114
|
-
|
|
115
|
-
Value::Number(
|
|
116
|
-
value_token.lexeme.parse::<f32>().unwrap_or(0.0)
|
|
117
|
-
)
|
|
169
|
+
Value::Number(value_token.lexeme.parse::<f32>().unwrap_or(0.0))
|
|
118
170
|
}
|
|
119
171
|
}
|
|
120
172
|
TokenKind::Boolean =>
|
|
121
173
|
Value::Boolean(
|
|
122
174
|
value_token.lexeme.parse::<bool>().unwrap_or(false)
|
|
123
175
|
),
|
|
124
|
-
|
|
125
176
|
_ => Value::Unknown,
|
|
126
177
|
};
|
|
127
178
|
map.insert(key, value);
|
|
@@ -135,6 +186,15 @@ pub fn parse_arrow_call(parser: &mut Parser, global_store: &mut GlobalStore) ->
|
|
|
135
186
|
};
|
|
136
187
|
|
|
137
188
|
args.push(value);
|
|
189
|
+
|
|
190
|
+
// Stop if we reach the end of the statement
|
|
191
|
+
if
|
|
192
|
+
paren_depth == 0 &&
|
|
193
|
+
map_depth == 0 &&
|
|
194
|
+
(token.kind == TokenKind::RParen || token.kind == TokenKind::RBrace)
|
|
195
|
+
{
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
138
198
|
}
|
|
139
199
|
|
|
140
200
|
Statement {
|