@devaloop/devalang 0.0.1-alpha.5 → 0.0.1-alpha.8
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 +45 -14
- package/docs/CHANGELOG.md +39 -0
- package/docs/ROADMAP.md +2 -2
- package/docs/SYNTAX.md +80 -18
- package/docs/TODO.md +12 -11
- package/examples/condition.deva +24 -0
- package/examples/group.deva +12 -0
- package/examples/index.deva +8 -9
- package/examples/loop.deva +15 -0
- package/examples/variables.deva +9 -0
- package/out-tsc/bin/devalang.exe +0 -0
- package/package.json +44 -44
- package/project-version.json +5 -5
- package/rust/cli/build.rs +6 -2
- package/rust/cli/play.rs +2 -1
- package/rust/core/audio/evaluator.rs +31 -0
- package/rust/core/audio/interpreter/call.rs +42 -0
- package/rust/core/audio/interpreter/condition.rs +65 -0
- package/rust/core/audio/interpreter/driver.rs +204 -0
- package/rust/core/audio/interpreter/let_.rs +19 -0
- package/rust/core/audio/interpreter/load.rs +18 -0
- package/rust/core/audio/interpreter/loop_.rs +59 -0
- package/rust/core/audio/interpreter/mod.rs +11 -0
- package/rust/core/audio/interpreter/sleep.rs +36 -0
- package/rust/core/audio/interpreter/spawn.rs +65 -0
- package/rust/core/audio/interpreter/tempo.rs +16 -0
- package/rust/core/audio/interpreter/trigger.rs +69 -0
- package/rust/core/audio/loader/mod.rs +1 -0
- package/rust/{audio/loader.rs → core/audio/loader/trigger.rs} +6 -0
- package/rust/{audio → core/audio}/mod.rs +2 -1
- package/rust/{audio/render.rs → core/audio/renderer.rs} +2 -2
- package/rust/core/builder/mod.rs +2 -1
- package/rust/core/debugger/lexer.rs +1 -1
- package/rust/core/debugger/mod.rs +1 -0
- package/rust/core/debugger/store.rs +25 -0
- package/rust/core/error/mod.rs +1 -1
- package/rust/core/lexer/handler/driver.rs +215 -0
- package/rust/core/lexer/handler/identifier.rs +2 -0
- package/rust/core/lexer/handler/indent.rs +1 -1
- package/rust/core/lexer/handler/mod.rs +3 -228
- package/rust/core/lexer/handler/operator.rs +44 -0
- package/rust/core/lexer/handler/string.rs +3 -6
- package/rust/core/lexer/mod.rs +1 -1
- package/rust/core/lexer/token.rs +36 -9
- package/rust/core/mod.rs +2 -1
- package/rust/core/parser/driver.rs +312 -0
- package/rust/core/parser/handler/at.rs +3 -7
- package/rust/core/parser/handler/bank.rs +5 -2
- package/rust/core/parser/handler/condition.rs +74 -0
- package/rust/core/parser/handler/dot.rs +1 -1
- package/rust/core/parser/handler/identifier.rs +130 -2
- package/rust/core/parser/handler/loop_.rs +1 -1
- package/rust/core/parser/handler/mod.rs +2 -1
- package/rust/core/parser/handler/tempo.rs +1 -1
- package/rust/core/parser/mod.rs +3 -237
- package/rust/core/parser/statement.rs +30 -33
- package/rust/core/preprocessor/loader.rs +7 -6
- package/rust/core/preprocessor/processor.rs +29 -3
- package/rust/core/preprocessor/resolver/bank.rs +10 -9
- package/rust/core/preprocessor/resolver/call.rs +53 -0
- package/rust/core/preprocessor/resolver/condition.rs +66 -0
- package/rust/core/preprocessor/resolver/driver.rs +182 -0
- package/rust/core/preprocessor/resolver/group.rs +118 -0
- package/rust/core/preprocessor/resolver/loop_.rs +3 -6
- package/rust/core/preprocessor/resolver/mod.rs +6 -147
- package/rust/core/preprocessor/resolver/spawn.rs +53 -0
- package/rust/core/preprocessor/resolver/trigger.rs +0 -3
- package/rust/lib.rs +0 -1
- package/rust/main.rs +0 -1
- package/examples/exported.deva +0 -7
- package/rust/audio/interpreter.rs +0 -143
- package/rust/core/lexer/handler/equal.rs +0 -32
- /package/rust/{audio → core/audio}/engine.rs +0 -0
- /package/rust/{audio → core/audio}/player.rs +0 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
lexer::token::{ Token, TokenKind },
|
|
3
|
+
parser::{
|
|
4
|
+
handler::{
|
|
5
|
+
at::parse_at_token,
|
|
6
|
+
bank::parse_bank_token,
|
|
7
|
+
condition::parse_condition_token,
|
|
8
|
+
dot::parse_dot_token,
|
|
9
|
+
identifier::parse_identifier_token,
|
|
10
|
+
loop_::parse_loop_token,
|
|
11
|
+
tempo::parse_tempo_token,
|
|
12
|
+
},
|
|
13
|
+
statement::{ Statement, StatementKind },
|
|
14
|
+
},
|
|
15
|
+
shared::value::Value,
|
|
16
|
+
store::global::GlobalStore,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
#[derive(Debug, Clone, PartialEq)]
|
|
20
|
+
pub struct Parser {
|
|
21
|
+
pub resolve_modules: bool,
|
|
22
|
+
pub tokens: Vec<Token>,
|
|
23
|
+
pub token_index: usize,
|
|
24
|
+
pub current_module: String,
|
|
25
|
+
pub previous: Option<Token>,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
impl Parser {
|
|
29
|
+
pub fn new() -> Self {
|
|
30
|
+
Parser {
|
|
31
|
+
resolve_modules: false,
|
|
32
|
+
tokens: Vec::new(),
|
|
33
|
+
token_index: 0,
|
|
34
|
+
current_module: String::new(),
|
|
35
|
+
previous: None,
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
pub fn set_current_module(&mut self, module_path: String) {
|
|
40
|
+
self.current_module = module_path;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
pub fn advance(&mut self) -> Option<&Token> {
|
|
44
|
+
if self.is_eof() {
|
|
45
|
+
return None;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
self.previous = self.tokens.get(self.token_index).cloned(); // mémorise avant de bouger
|
|
49
|
+
self.token_index += 1;
|
|
50
|
+
|
|
51
|
+
self.tokens.get(self.token_index - 1)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
pub fn peek_is(&self, expected: &str) -> bool {
|
|
55
|
+
self.peek().map_or(false, |t| t.lexeme == expected)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
pub fn advance_if(&mut self, kind: TokenKind) -> bool {
|
|
59
|
+
if self.match_token(kind) { true } else { false }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pub fn match_token(&mut self, kind: TokenKind) -> bool {
|
|
63
|
+
if let Some(tok) = self.peek() {
|
|
64
|
+
if tok.kind == kind {
|
|
65
|
+
self.advance();
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
false
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
pub fn previous_clone(&self) -> Option<Token> {
|
|
73
|
+
self.previous.clone()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
pub fn parse_block(
|
|
77
|
+
&self,
|
|
78
|
+
tokens: Vec<Token>,
|
|
79
|
+
global_store: &mut GlobalStore
|
|
80
|
+
) -> Vec<Statement> {
|
|
81
|
+
let mut inner_parser = Parser {
|
|
82
|
+
resolve_modules: self.resolve_modules,
|
|
83
|
+
tokens,
|
|
84
|
+
token_index: 0,
|
|
85
|
+
current_module: self.current_module.clone(),
|
|
86
|
+
previous: None,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
inner_parser.parse_tokens(inner_parser.tokens.clone(), global_store)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
pub fn parse_tokens(
|
|
93
|
+
&mut self,
|
|
94
|
+
tokens: Vec<Token>,
|
|
95
|
+
global_store: &mut GlobalStore
|
|
96
|
+
) -> Vec<Statement> {
|
|
97
|
+
self.tokens = tokens;
|
|
98
|
+
self.token_index = 0;
|
|
99
|
+
|
|
100
|
+
let mut statements = Vec::new();
|
|
101
|
+
|
|
102
|
+
while !self.is_eof() {
|
|
103
|
+
let token = match self.peek() {
|
|
104
|
+
Some(t) => t.clone(),
|
|
105
|
+
None => {
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
let mut statement = match &token.kind {
|
|
111
|
+
TokenKind::At => parse_at_token(self, global_store),
|
|
112
|
+
TokenKind::Identifier => parse_identifier_token(self, global_store),
|
|
113
|
+
TokenKind::Dot => parse_dot_token(self, global_store),
|
|
114
|
+
TokenKind::Tempo => parse_tempo_token(self, global_store),
|
|
115
|
+
TokenKind::Bank => parse_bank_token(self, global_store),
|
|
116
|
+
TokenKind::Loop => parse_loop_token(self, global_store),
|
|
117
|
+
TokenKind::If => parse_condition_token(self, global_store),
|
|
118
|
+
|
|
119
|
+
| TokenKind::Else // Ignore else, already handled in `parse_condition_token`
|
|
120
|
+
| TokenKind::Comment
|
|
121
|
+
| TokenKind::Equals
|
|
122
|
+
| TokenKind::Colon
|
|
123
|
+
| TokenKind::Number
|
|
124
|
+
| TokenKind::String
|
|
125
|
+
| TokenKind::LBrace
|
|
126
|
+
| TokenKind::RBrace
|
|
127
|
+
| TokenKind::Comma
|
|
128
|
+
| TokenKind::Newline
|
|
129
|
+
| TokenKind::Dedent
|
|
130
|
+
| TokenKind::Indent => {
|
|
131
|
+
self.advance();
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
TokenKind::EOF => {
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
_ => {
|
|
140
|
+
println!("Unhandled token: {:?}", token);
|
|
141
|
+
self.advance();
|
|
142
|
+
Statement::unknown()
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
statements.push(statement);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
statements
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
pub fn check_token(&self, kind: TokenKind) -> bool {
|
|
153
|
+
self.peek().map_or(false, |t| t.kind == kind)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
pub fn parse_map_value(&mut self) -> Option<Value> {
|
|
157
|
+
if !self.match_token(TokenKind::LBrace) {
|
|
158
|
+
return None;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let mut map = std::collections::HashMap::new();
|
|
162
|
+
|
|
163
|
+
while !self.check_token(TokenKind::RBrace) && !self.is_eof() {
|
|
164
|
+
let key = if let Some(token) = self.advance() {
|
|
165
|
+
token.lexeme.clone()
|
|
166
|
+
} else {
|
|
167
|
+
break;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
if !self.match_token(TokenKind::Colon) {
|
|
171
|
+
println!("Expected ':' after map key '{}'", key);
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let value = if let Some(token) = self.peek_clone() {
|
|
176
|
+
match token.kind {
|
|
177
|
+
TokenKind::String => {
|
|
178
|
+
self.advance();
|
|
179
|
+
Value::String(token.lexeme.clone())
|
|
180
|
+
}
|
|
181
|
+
TokenKind::Number => {
|
|
182
|
+
self.advance();
|
|
183
|
+
Value::Number(token.lexeme.parse().unwrap_or(0.0))
|
|
184
|
+
}
|
|
185
|
+
TokenKind::Identifier => {
|
|
186
|
+
self.advance();
|
|
187
|
+
Value::Identifier(token.lexeme.clone())
|
|
188
|
+
}
|
|
189
|
+
_ => {
|
|
190
|
+
println!("Unexpected token in map value: {:?}", token);
|
|
191
|
+
Value::Null
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
Value::Null
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
map.insert(key, value);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if !self.match_token(TokenKind::RBrace) {
|
|
202
|
+
println!("Expected '}}' at end of map");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
Some(Value::Map(map))
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
pub fn peek(&self) -> Option<&Token> {
|
|
209
|
+
self.tokens.get(self.token_index)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
pub fn peek_clone(&self) -> Option<Token> {
|
|
213
|
+
self.tokens.get(self.token_index).cloned()
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
pub fn expect(&mut self, kind: TokenKind) -> Result<&Token, String> {
|
|
217
|
+
let tok = self.advance().ok_or("Unexpected end of input")?;
|
|
218
|
+
if tok.kind == kind {
|
|
219
|
+
Ok(tok)
|
|
220
|
+
} else {
|
|
221
|
+
Err(format!("Expected {:?}, got {:?}", kind, tok.kind))
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
pub fn collect_block_tokens(&mut self, base_indent: usize) -> Vec<Token> {
|
|
226
|
+
let mut tokens = Vec::new();
|
|
227
|
+
|
|
228
|
+
while let Some(tok) = self.peek() {
|
|
229
|
+
if tok.indent <= base_indent && tok.kind != TokenKind::Newline {
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
tokens.push(self.advance().unwrap().clone());
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
tokens
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
pub fn collect_until<F>(&mut self, condition: F) -> Vec<Token> where F: Fn(&Token) -> bool {
|
|
239
|
+
let mut collected = Vec::new();
|
|
240
|
+
while let Some(token) = self.peek() {
|
|
241
|
+
if token.kind == TokenKind::Newline || token.kind == TokenKind::Indent {
|
|
242
|
+
self.advance(); // Skip newlines and indents
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if token.kind == TokenKind::EOF {
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
if condition(token) {
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
collected.push(self.advance().unwrap().clone());
|
|
252
|
+
}
|
|
253
|
+
collected
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
pub fn is_eof(&self) -> bool {
|
|
257
|
+
self.token_index >= self.tokens.len()
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
pub fn parse_block_until_next_else(
|
|
261
|
+
&mut self,
|
|
262
|
+
base_indent: usize,
|
|
263
|
+
global_store: &mut GlobalStore
|
|
264
|
+
) -> Vec<Statement> {
|
|
265
|
+
let mut block_tokens = Vec::new();
|
|
266
|
+
|
|
267
|
+
while let Some(tok) = self.peek() {
|
|
268
|
+
// Stop if we encounter an 'else' at same indent level
|
|
269
|
+
if tok.lexeme == "else" && tok.indent == base_indent {
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
block_tokens.push(self.advance().unwrap().clone());
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
self.parse_block(block_tokens, global_store)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
pub fn parse_condition_until_colon(&mut self) -> Option<Value> {
|
|
279
|
+
let tokens = self.collect_until(|t| t.kind == TokenKind::Colon);
|
|
280
|
+
if tokens.is_empty() {
|
|
281
|
+
return None;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
let condition = tokens
|
|
285
|
+
.iter()
|
|
286
|
+
.map(|t| t.lexeme.clone())
|
|
287
|
+
.collect::<Vec<_>>()
|
|
288
|
+
.join(" ");
|
|
289
|
+
|
|
290
|
+
Some(Value::String(condition))
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
pub fn parse_block_until_else_or_dedent(
|
|
294
|
+
&mut self,
|
|
295
|
+
base_indent: usize,
|
|
296
|
+
global_store: &mut GlobalStore
|
|
297
|
+
) -> Vec<Statement> {
|
|
298
|
+
let mut tokens = Vec::new();
|
|
299
|
+
|
|
300
|
+
while let Some(tok) = self.peek() {
|
|
301
|
+
if tok.lexeme == "else" && tok.indent == base_indent {
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
if tok.indent < base_indent && tok.kind != TokenKind::Newline {
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
tokens.push(self.advance().unwrap().clone());
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
self.parse_block(tokens, global_store)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
use crate::core::{
|
|
2
|
-
lexer::token::
|
|
3
|
-
parser::{
|
|
4
|
-
handler::identifier::parse_identifier_token,
|
|
5
|
-
statement::{ Statement, StatementKind },
|
|
6
|
-
Parser,
|
|
7
|
-
},
|
|
2
|
+
lexer::token::TokenKind,
|
|
3
|
+
parser::{ driver::Parser, statement::{ Statement, StatementKind } },
|
|
8
4
|
shared::value::Value,
|
|
9
5
|
store::global::GlobalStore,
|
|
10
6
|
};
|
|
11
|
-
pub fn parse_at_token(parser: &mut Parser,
|
|
7
|
+
pub fn parse_at_token(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
|
|
12
8
|
parser.advance(); // consume '@'
|
|
13
9
|
|
|
14
10
|
let Some(token) = parser.peek_clone() else {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
use crate::core::{
|
|
2
2
|
lexer::token::TokenKind,
|
|
3
|
-
parser::{ statement::{ Statement, StatementKind }, Parser },
|
|
3
|
+
parser::{ statement::{ Statement, StatementKind }, driver::Parser },
|
|
4
4
|
shared::value::Value,
|
|
5
5
|
store::global::GlobalStore,
|
|
6
6
|
};
|
|
@@ -25,7 +25,10 @@ pub fn parse_bank_token(parser: &mut Parser, _global_store: &mut GlobalStore) ->
|
|
|
25
25
|
_ => Value::Unknown,
|
|
26
26
|
}
|
|
27
27
|
} else {
|
|
28
|
-
return Statement::error(
|
|
28
|
+
return Statement::error(
|
|
29
|
+
bank_token,
|
|
30
|
+
"Expected identifier or number after 'bank'".to_string()
|
|
31
|
+
);
|
|
29
32
|
};
|
|
30
33
|
|
|
31
34
|
Statement {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
use crate::core::{
|
|
3
|
+
lexer::token::TokenKind,
|
|
4
|
+
parser::{statement::{Statement, StatementKind}, driver::Parser},
|
|
5
|
+
shared::value::Value,
|
|
6
|
+
store::global::GlobalStore,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
pub fn parse_condition_token(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
|
|
10
|
+
parser.advance(); // consume 'if'
|
|
11
|
+
let Some(if_token) = parser.previous_clone() else {
|
|
12
|
+
return Statement::unknown();
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
let Some(condition) = parser.parse_condition_until_colon() else {
|
|
16
|
+
return Statement::error(if_token, "Expected condition after 'if'".to_string());
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
parser.advance_if(TokenKind::Colon);
|
|
20
|
+
let base_indent = if_token.indent;
|
|
21
|
+
|
|
22
|
+
let if_body = parser.parse_block_until_else_or_dedent(base_indent, global_store);
|
|
23
|
+
|
|
24
|
+
let mut root_map = HashMap::new();
|
|
25
|
+
root_map.insert("condition".to_string(), condition);
|
|
26
|
+
root_map.insert("body".to_string(), Value::Block(if_body));
|
|
27
|
+
|
|
28
|
+
let mut current = &mut root_map;
|
|
29
|
+
|
|
30
|
+
// Loop for else / else if
|
|
31
|
+
while let Some(tok) = parser.peek_clone() {
|
|
32
|
+
// Only continue if we see `else` at same indent level
|
|
33
|
+
if tok.lexeme != "else" || tok.indent != base_indent {
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
parser.advance(); // consume 'else'
|
|
38
|
+
|
|
39
|
+
// Check if it's an 'else if'
|
|
40
|
+
let next_condition = if parser.peek_is("if") {
|
|
41
|
+
parser.advance(); // consume 'if'
|
|
42
|
+
let Some(cond) = parser.parse_condition_until_colon() else {
|
|
43
|
+
return Statement::error(tok.clone(), "Expected condition after 'else if'".to_string());
|
|
44
|
+
};
|
|
45
|
+
parser.advance_if(TokenKind::Colon);
|
|
46
|
+
Some(cond)
|
|
47
|
+
} else {
|
|
48
|
+
parser.advance_if(TokenKind::Colon);
|
|
49
|
+
None
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
let body = parser.parse_block_until_else_or_dedent(base_indent, global_store);
|
|
53
|
+
|
|
54
|
+
let mut next_map = HashMap::new();
|
|
55
|
+
if let Some(cond) = next_condition {
|
|
56
|
+
next_map.insert("condition".to_string(), cond);
|
|
57
|
+
}
|
|
58
|
+
next_map.insert("body".to_string(), Value::Block(body));
|
|
59
|
+
|
|
60
|
+
current.insert("next".to_string(), Value::Map(next_map));
|
|
61
|
+
current = match current.get_mut("next") {
|
|
62
|
+
Some(Value::Map(map)) => map,
|
|
63
|
+
_ => break,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
Statement {
|
|
68
|
+
kind: StatementKind::If,
|
|
69
|
+
value: Value::Map(root_map),
|
|
70
|
+
indent: if_token.indent,
|
|
71
|
+
line: if_token.line,
|
|
72
|
+
column: if_token.column,
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
use crate::core::{
|
|
2
2
|
lexer::token::{ Token, TokenKind },
|
|
3
|
-
parser::{ statement::{ Statement, StatementKind }, Parser },
|
|
3
|
+
parser::{ statement::{ Statement, StatementKind }, driver::Parser },
|
|
4
4
|
shared::value::Value,
|
|
5
5
|
store::global::GlobalStore,
|
|
6
6
|
};
|
|
7
7
|
use std::collections::HashMap;
|
|
8
8
|
|
|
9
|
-
pub fn parse_identifier_token(parser: &mut Parser,
|
|
9
|
+
pub fn parse_identifier_token(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
|
|
10
10
|
let Some(current_token) = parser.peek_clone() else {
|
|
11
11
|
return Statement::unknown();
|
|
12
12
|
};
|
|
@@ -111,6 +111,134 @@ pub fn parse_identifier_token(parser: &mut Parser, _global_store: &mut GlobalSto
|
|
|
111
111
|
line: current_token.line,
|
|
112
112
|
column: current_token.column,
|
|
113
113
|
};
|
|
114
|
+
} else if current_token.lexeme == "group" {
|
|
115
|
+
parser.advance(); // consume "group"
|
|
116
|
+
|
|
117
|
+
let Some(identifier_token) = parser.peek_clone() else {
|
|
118
|
+
return Statement::error(current_token, "Expected identifier after 'group'".to_string());
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
if
|
|
122
|
+
identifier_token.kind != TokenKind::Identifier &&
|
|
123
|
+
identifier_token.kind != TokenKind::String
|
|
124
|
+
{
|
|
125
|
+
return Statement::error(identifier_token, "Expected valid identifier".to_string());
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
parser.advance(); // consume identifier
|
|
129
|
+
|
|
130
|
+
let Some(colon_token) = parser.peek_clone() else {
|
|
131
|
+
return Statement::error(
|
|
132
|
+
identifier_token,
|
|
133
|
+
"Expected ':' after group identifier".to_string()
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
if colon_token.kind != TokenKind::Colon {
|
|
138
|
+
return Statement::error(
|
|
139
|
+
colon_token.clone(),
|
|
140
|
+
"Expected ':' after group identifier".to_string()
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
parser.advance(); // consume ':'
|
|
145
|
+
|
|
146
|
+
let base_indent = current_token.indent;
|
|
147
|
+
|
|
148
|
+
// Clone without consuming tokens
|
|
149
|
+
let mut index = parser.token_index;
|
|
150
|
+
let mut tokens_inside_group = Vec::new();
|
|
151
|
+
|
|
152
|
+
while index < parser.tokens.len() {
|
|
153
|
+
let token = parser.tokens[index].clone();
|
|
154
|
+
|
|
155
|
+
if token.indent <= base_indent && token.kind != TokenKind::Newline {
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
tokens_inside_group.push(token);
|
|
160
|
+
index += 1;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Advance index once to skip the processed tokens
|
|
164
|
+
parser.token_index = index;
|
|
165
|
+
|
|
166
|
+
let body = parser.parse_block(tokens_inside_group, global_store);
|
|
167
|
+
|
|
168
|
+
let mut value_map = HashMap::new();
|
|
169
|
+
value_map.insert("identifier".to_string(), Value::String(identifier_token.lexeme.clone()));
|
|
170
|
+
value_map.insert("body".to_string(), Value::Block(body));
|
|
171
|
+
|
|
172
|
+
return Statement {
|
|
173
|
+
kind: StatementKind::Group,
|
|
174
|
+
value: Value::Map(value_map),
|
|
175
|
+
indent: current_token.indent,
|
|
176
|
+
line: current_token.line,
|
|
177
|
+
column: current_token.column,
|
|
178
|
+
};
|
|
179
|
+
} else if current_token.lexeme == "call" {
|
|
180
|
+
parser.advance(); // consume "call"
|
|
181
|
+
|
|
182
|
+
let identifier = if let Some(token) = parser.peek_clone() {
|
|
183
|
+
if token.kind == TokenKind::Identifier {
|
|
184
|
+
parser.advance();
|
|
185
|
+
token.lexeme.clone()
|
|
186
|
+
} else {
|
|
187
|
+
return Statement::error(token, "Expected identifier after 'call'".to_string());
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
return Statement::error(current_token, "Expected identifier after 'call'".to_string());
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
return Statement {
|
|
194
|
+
kind: StatementKind::Call,
|
|
195
|
+
value: Value::String(identifier),
|
|
196
|
+
indent: current_token.indent,
|
|
197
|
+
line: current_token.line,
|
|
198
|
+
column: current_token.column,
|
|
199
|
+
};
|
|
200
|
+
} else if current_token.lexeme == "spawn" {
|
|
201
|
+
parser.advance(); // consume "spawn"
|
|
202
|
+
|
|
203
|
+
let identifier = if let Some(token) = parser.peek_clone() {
|
|
204
|
+
if token.kind == TokenKind::Identifier {
|
|
205
|
+
parser.advance();
|
|
206
|
+
token.lexeme.clone()
|
|
207
|
+
} else {
|
|
208
|
+
return Statement::error(token, "Expected identifier after 'spawn'".to_string());
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
return Statement::error(current_token, "Expected identifier after 'spawn'".to_string());
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
return Statement {
|
|
215
|
+
kind: StatementKind::Spawn,
|
|
216
|
+
value: Value::String(identifier),
|
|
217
|
+
indent: current_token.indent,
|
|
218
|
+
line: current_token.line,
|
|
219
|
+
column: current_token.column,
|
|
220
|
+
};
|
|
221
|
+
} else if current_token.lexeme == "sleep" {
|
|
222
|
+
parser.advance(); // consume "sleep"
|
|
223
|
+
|
|
224
|
+
let duration = if let Some(token) = parser.peek_clone() {
|
|
225
|
+
if token.kind == TokenKind::Number {
|
|
226
|
+
parser.advance();
|
|
227
|
+
token.lexeme.parse().unwrap_or(0.0)
|
|
228
|
+
} else {
|
|
229
|
+
return Statement::error(token, "Expected number after 'sleep'".to_string());
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
return Statement::error(current_token, "Expected number after 'sleep'".to_string());
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
return Statement {
|
|
236
|
+
kind: StatementKind::Sleep,
|
|
237
|
+
value: Value::Number(duration),
|
|
238
|
+
indent: current_token.indent,
|
|
239
|
+
line: current_token.line,
|
|
240
|
+
column: current_token.column,
|
|
241
|
+
};
|
|
114
242
|
} else {
|
|
115
243
|
// Unknown identifier handling
|
|
116
244
|
Statement {
|
|
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|
|
2
2
|
|
|
3
3
|
use crate::core::{
|
|
4
4
|
lexer::{ token::TokenKind },
|
|
5
|
-
parser::{ statement::{ Statement, StatementKind }, Parser },
|
|
5
|
+
parser::{ statement::{ Statement, StatementKind }, driver::Parser },
|
|
6
6
|
shared::value::Value,
|
|
7
7
|
store::global::GlobalStore,
|
|
8
8
|
};
|