@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.
- package/.devalang +8 -8
- package/Cargo.toml +8 -8
- package/README.md +1 -14
- package/docs/CHANGELOG.md +44 -0
- package/docs/TODO.md +1 -1
- package/examples/index.deva +10 -11
- package/out-tsc/bin/devalang.exe +0 -0
- package/package.json +2 -1
- package/project-version.json +3 -3
- package/rust/cli/build.rs +25 -2
- package/rust/cli/check.rs +26 -3
- package/rust/cli/play.rs +1 -1
- package/rust/core/audio/engine.rs +207 -41
- 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 +84 -127
- 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 +169 -61
- 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/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/mod.rs +1 -0
- package/rust/core/lexer/handler/parenthesis.rs +41 -0
- package/rust/core/lexer/token.rs +3 -0
- package/rust/core/parser/driver.rs +31 -3
- package/rust/core/parser/handler/dot.rs +65 -129
- 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/preprocessor/loader.rs +45 -29
- 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/bank.rs +1 -1
- package/rust/installer/mod.rs +2 -1
- package/rust/lib.rs +10 -8
- package/rust/utils/mod.rs +44 -1
- /package/rust/{utils/installer.rs → installer/utils.rs} +0 -0
|
@@ -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,
|
|
@@ -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, // '
|
|
@@ -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
|
-
|
|
210
|
-
|
|
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::{
|
|
4
|
-
shared::{
|
|
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(
|
|
9
|
-
parser
|
|
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
|
|
17
|
+
// Parse a single entity (namespace-friendly, stops at newline)
|
|
16
18
|
let mut parts = Vec::new();
|
|
17
19
|
|
|
18
|
-
|
|
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
|
-
|
|
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();
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
123
|
-
|
|
49
|
+
// Optional duration and effects map
|
|
50
|
+
let mut duration = Duration::Auto;
|
|
51
|
+
let mut value = Value::Null;
|
|
124
52
|
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
(duration, val)
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
duration = parse_duration(numerator);
|
|
137
69
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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 {
|
|
163
|
-
|
|
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::{
|
|
3
|
-
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
|
-
|
|
11
|
+
_global_store: &mut GlobalStore
|
|
12
12
|
) -> Statement {
|
|
13
13
|
parser.advance(); // consume "call"
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
if name_token.kind != TokenKind::Identifier {
|
|
28
27
|
return Statement::error(
|
|
29
|
-
|
|
30
|
-
"Expected
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
+
}
|