@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
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
lexer::token::{ Token, TokenKind },
|
|
3
|
+
parser::{ statement::{ Statement, StatementKind }, driver::Parser },
|
|
4
|
+
shared::value::Value,
|
|
5
|
+
store::global::GlobalStore,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
pub fn parse_call_token(
|
|
9
|
+
parser: &mut Parser,
|
|
10
|
+
current_token: Token,
|
|
11
|
+
global_store: &mut GlobalStore
|
|
12
|
+
) -> Statement {
|
|
13
|
+
parser.advance(); // consume "call"
|
|
14
|
+
|
|
15
|
+
let value = if let Some(token) = parser.peek_clone() {
|
|
16
|
+
parser.advance();
|
|
17
|
+
match token.kind {
|
|
18
|
+
TokenKind::Identifier => Value::Identifier(token.lexeme.clone()),
|
|
19
|
+
TokenKind::String => Value::String(token.lexeme.clone()),
|
|
20
|
+
_ => {
|
|
21
|
+
return Statement::error(
|
|
22
|
+
token,
|
|
23
|
+
"Expected identifier or string after 'call'".to_string()
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
return Statement::error(
|
|
29
|
+
current_token,
|
|
30
|
+
"Expected identifier or string after 'call'".to_string()
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return Statement {
|
|
35
|
+
kind: StatementKind::Call,
|
|
36
|
+
value,
|
|
37
|
+
indent: current_token.indent,
|
|
38
|
+
line: current_token.line,
|
|
39
|
+
column: current_token.column,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
lexer::token::{ Token, TokenKind },
|
|
3
|
+
parser::{ statement::{ Statement, StatementKind }, driver::Parser },
|
|
4
|
+
shared::value::Value,
|
|
5
|
+
store::global::GlobalStore,
|
|
6
|
+
};
|
|
7
|
+
use std::collections::HashMap;
|
|
8
|
+
|
|
9
|
+
pub fn parse_group_token(
|
|
10
|
+
parser: &mut Parser,
|
|
11
|
+
current_token: Token,
|
|
12
|
+
global_store: &mut GlobalStore
|
|
13
|
+
) -> Statement {
|
|
14
|
+
parser.advance(); // consume "group"
|
|
15
|
+
|
|
16
|
+
let Some(identifier_token) = parser.peek_clone() else {
|
|
17
|
+
return Statement::error(current_token, "Expected identifier after 'group'".to_string());
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
if identifier_token.kind != TokenKind::Identifier && identifier_token.kind != TokenKind::String {
|
|
21
|
+
return Statement::error(identifier_token, "Expected valid identifier".to_string());
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
parser.advance(); // consume identifier
|
|
25
|
+
|
|
26
|
+
let Some(colon_token) = parser.peek_clone() else {
|
|
27
|
+
return Statement::error(
|
|
28
|
+
identifier_token,
|
|
29
|
+
"Expected ':' after group identifier".to_string()
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
if colon_token.kind != TokenKind::Colon {
|
|
34
|
+
return Statement::error(
|
|
35
|
+
colon_token.clone(),
|
|
36
|
+
"Expected ':' after group identifier".to_string()
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
parser.advance(); // consume ':'
|
|
41
|
+
|
|
42
|
+
let base_indent = current_token.indent;
|
|
43
|
+
|
|
44
|
+
// Clone without consuming tokens
|
|
45
|
+
let mut index = parser.token_index;
|
|
46
|
+
let mut tokens_inside_group = Vec::new();
|
|
47
|
+
|
|
48
|
+
while index < parser.tokens.len() {
|
|
49
|
+
let token = parser.tokens[index].clone();
|
|
50
|
+
|
|
51
|
+
if token.indent <= base_indent && token.kind != TokenKind::Newline {
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
tokens_inside_group.push(token);
|
|
56
|
+
index += 1;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Advance index once to skip the processed tokens
|
|
60
|
+
parser.token_index = index;
|
|
61
|
+
|
|
62
|
+
let body = parser.parse_block(tokens_inside_group, global_store);
|
|
63
|
+
|
|
64
|
+
let mut value_map = HashMap::new();
|
|
65
|
+
value_map.insert("identifier".to_string(), Value::String(identifier_token.lexeme.clone()));
|
|
66
|
+
value_map.insert("body".to_string(), Value::Block(body));
|
|
67
|
+
|
|
68
|
+
return Statement {
|
|
69
|
+
kind: StatementKind::Group,
|
|
70
|
+
value: Value::Map(value_map),
|
|
71
|
+
indent: current_token.indent,
|
|
72
|
+
line: current_token.line,
|
|
73
|
+
column: current_token.column,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
|
|
3
|
+
use crate::core::{
|
|
4
|
+
lexer::token::{ Token, TokenKind },
|
|
5
|
+
parser::{
|
|
6
|
+
driver::Parser,
|
|
7
|
+
handler::identifier::synth::parse_synth_token,
|
|
8
|
+
statement::{ Statement, StatementKind },
|
|
9
|
+
},
|
|
10
|
+
shared::value::Value,
|
|
11
|
+
store::global::GlobalStore,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
pub fn parse_let_token(
|
|
15
|
+
parser: &mut Parser,
|
|
16
|
+
current_token: Token,
|
|
17
|
+
global_store: &mut GlobalStore
|
|
18
|
+
) -> Statement {
|
|
19
|
+
parser.advance(); // consume "let"
|
|
20
|
+
|
|
21
|
+
let identifier = if let Some(token) = parser.peek_clone() {
|
|
22
|
+
if token.kind == TokenKind::Identifier {
|
|
23
|
+
parser.advance();
|
|
24
|
+
token.lexeme.clone()
|
|
25
|
+
} else {
|
|
26
|
+
return Statement::error(token, "Expected identifier after 'let'".to_string());
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
return Statement::error(current_token, "Expected identifier after 'let'".to_string());
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
if !parser.match_token(TokenKind::Equals) {
|
|
33
|
+
return Statement::error(current_token, "Expected '=' after identifier".to_string());
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if let Some(token) = parser.peek_clone() {
|
|
37
|
+
if token.kind == TokenKind::Synth {
|
|
38
|
+
let synth_stmt = parse_synth_token(parser, token.clone(), global_store);
|
|
39
|
+
|
|
40
|
+
return Statement {
|
|
41
|
+
kind: StatementKind::Let { name: identifier },
|
|
42
|
+
value: synth_stmt.value,
|
|
43
|
+
indent: current_token.indent,
|
|
44
|
+
line: current_token.line,
|
|
45
|
+
column: current_token.column,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let value = match parser.peek_clone() {
|
|
51
|
+
Some(token) if token.kind == TokenKind::Identifier => {
|
|
52
|
+
parser.advance();
|
|
53
|
+
Value::Identifier(token.lexeme.clone())
|
|
54
|
+
}
|
|
55
|
+
Some(token) if token.kind == TokenKind::String => {
|
|
56
|
+
parser.advance();
|
|
57
|
+
Value::String(token.lexeme.clone())
|
|
58
|
+
}
|
|
59
|
+
Some(token) if token.kind == TokenKind::Number => {
|
|
60
|
+
parser.advance();
|
|
61
|
+
Value::Number(token.lexeme.parse().unwrap_or(0.0))
|
|
62
|
+
}
|
|
63
|
+
Some(token) if token.kind == TokenKind::Boolean => {
|
|
64
|
+
parser.advance();
|
|
65
|
+
Value::Boolean(token.lexeme.parse().unwrap_or(false))
|
|
66
|
+
}
|
|
67
|
+
Some(token) if token.kind == TokenKind::LBrace => {
|
|
68
|
+
parser.advance(); // consume '{'
|
|
69
|
+
let mut map = HashMap::new();
|
|
70
|
+
|
|
71
|
+
while let Some(key_token) = parser.peek_clone() {
|
|
72
|
+
if key_token.kind == TokenKind::RBrace {
|
|
73
|
+
parser.advance(); // consume '}'
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if key_token.kind != TokenKind::Identifier {
|
|
78
|
+
return Statement::error(token, "Expected key identifier in map".to_string());
|
|
79
|
+
}
|
|
80
|
+
parser.advance();
|
|
81
|
+
let key = key_token.lexeme.clone();
|
|
82
|
+
|
|
83
|
+
if !parser.match_token(TokenKind::Colon) {
|
|
84
|
+
let message = format!("Expected ':' after key '{}'", key);
|
|
85
|
+
return Statement::error(token, message);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let val = match parser.peek_clone() {
|
|
89
|
+
Some(t) if t.kind == TokenKind::Number => {
|
|
90
|
+
parser.advance();
|
|
91
|
+
Value::Number(t.lexeme.parse().unwrap_or(0.0))
|
|
92
|
+
}
|
|
93
|
+
Some(t) if t.kind == TokenKind::String => {
|
|
94
|
+
parser.advance();
|
|
95
|
+
Value::String(t.lexeme.clone())
|
|
96
|
+
}
|
|
97
|
+
Some(t) if t.kind == TokenKind::Identifier => {
|
|
98
|
+
parser.advance();
|
|
99
|
+
Value::Identifier(t.lexeme.clone())
|
|
100
|
+
}
|
|
101
|
+
_ => Value::Null,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
if val == Value::Null {
|
|
105
|
+
let message = format!("Invalid value for key '{}'", key);
|
|
106
|
+
return Statement::error(token, message);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
map.insert(key, val);
|
|
110
|
+
|
|
111
|
+
if let Some(t) = parser.peek() {
|
|
112
|
+
if t.kind == TokenKind::Comma {
|
|
113
|
+
parser.advance(); // skip comma
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
Value::Map(map)
|
|
119
|
+
}
|
|
120
|
+
other => {
|
|
121
|
+
let message = format!("Unexpected value token in let: {:?}", other);
|
|
122
|
+
return Statement::error(current_token, message);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
Statement {
|
|
127
|
+
kind: StatementKind::Let { name: identifier },
|
|
128
|
+
value,
|
|
129
|
+
indent: current_token.indent,
|
|
130
|
+
line: current_token.line,
|
|
131
|
+
column: current_token.column,
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
pub mod let_;
|
|
2
|
+
pub mod group;
|
|
3
|
+
pub mod call;
|
|
4
|
+
pub mod spawn;
|
|
5
|
+
pub mod sleep;
|
|
6
|
+
pub mod synth;
|
|
7
|
+
|
|
8
|
+
use crate::core::{
|
|
9
|
+
parser::{
|
|
10
|
+
driver::Parser,
|
|
11
|
+
handler::{
|
|
12
|
+
identifier::{
|
|
13
|
+
call::parse_call_token,
|
|
14
|
+
group::parse_group_token,
|
|
15
|
+
let_::parse_let_token,
|
|
16
|
+
sleep::parse_sleep_token,
|
|
17
|
+
spawn::parse_spawn_token,
|
|
18
|
+
synth::parse_synth_token
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
statement::Statement,
|
|
22
|
+
},
|
|
23
|
+
store::global::GlobalStore,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
pub fn parse_identifier_token(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
|
|
27
|
+
let Some(current_token) = parser.peek_clone() else {
|
|
28
|
+
return Statement::unknown();
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
let current_token_clone = current_token.clone();
|
|
32
|
+
let current_token_lexeme = current_token_clone.lexeme.clone();
|
|
33
|
+
|
|
34
|
+
let statement = match current_token_lexeme.as_str() {
|
|
35
|
+
"let" => parse_let_token(parser, current_token_clone, global_store),
|
|
36
|
+
"group" => parse_group_token(parser, current_token_clone, global_store),
|
|
37
|
+
"call" => parse_call_token(parser, current_token_clone, global_store),
|
|
38
|
+
"spawn" => parse_spawn_token(parser, current_token_clone, global_store),
|
|
39
|
+
"sleep" => parse_sleep_token(parser, current_token_clone, global_store),
|
|
40
|
+
"synth" => parse_synth_token(parser, current_token_clone, global_store),
|
|
41
|
+
_ => {
|
|
42
|
+
parser.advance(); // consume identifier
|
|
43
|
+
|
|
44
|
+
println!("Unrecognized identifier: {}", current_token_lexeme);
|
|
45
|
+
|
|
46
|
+
return Statement::error(current_token_clone, "Unexpected identifier".to_string());
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return statement;
|
|
51
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
lexer::token::{ Token, TokenKind },
|
|
3
|
+
parser::{ statement::{ Statement, StatementKind }, driver::Parser },
|
|
4
|
+
shared::value::Value,
|
|
5
|
+
store::global::GlobalStore,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
pub fn parse_sleep_token(
|
|
9
|
+
parser: &mut Parser,
|
|
10
|
+
current_token: Token,
|
|
11
|
+
global_store: &mut GlobalStore
|
|
12
|
+
) -> Statement {
|
|
13
|
+
parser.advance(); // consume "sleep"
|
|
14
|
+
|
|
15
|
+
let duration = if let Some(token) = parser.peek_clone() {
|
|
16
|
+
if token.kind == TokenKind::Number {
|
|
17
|
+
parser.advance();
|
|
18
|
+
token.lexeme.parse().unwrap_or(0.0)
|
|
19
|
+
} else {
|
|
20
|
+
return Statement::error(token, "Expected number after 'sleep'".to_string());
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
return Statement::error(current_token, "Expected number after 'sleep'".to_string());
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
return Statement {
|
|
27
|
+
kind: StatementKind::Sleep,
|
|
28
|
+
value: Value::Number(duration),
|
|
29
|
+
indent: current_token.indent,
|
|
30
|
+
line: current_token.line,
|
|
31
|
+
column: current_token.column,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
lexer::token::{ Token, TokenKind },
|
|
3
|
+
parser::{ statement::{ Statement, StatementKind }, driver::Parser },
|
|
4
|
+
shared::value::Value,
|
|
5
|
+
store::global::GlobalStore,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
pub fn parse_spawn_token(
|
|
9
|
+
parser: &mut Parser,
|
|
10
|
+
current_token: Token,
|
|
11
|
+
global_store: &mut GlobalStore
|
|
12
|
+
) -> Statement {
|
|
13
|
+
parser.advance(); // consume "spawn"
|
|
14
|
+
|
|
15
|
+
let value = if let Some(token) = parser.peek_clone() {
|
|
16
|
+
parser.advance();
|
|
17
|
+
match token.kind {
|
|
18
|
+
TokenKind::Identifier => Value::Identifier(token.lexeme.clone()),
|
|
19
|
+
TokenKind::String => Value::String(token.lexeme.clone()),
|
|
20
|
+
_ => {
|
|
21
|
+
return Statement::error(
|
|
22
|
+
token,
|
|
23
|
+
"Expected identifier or string after 'spawn'".to_string()
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
return Statement::error(
|
|
29
|
+
current_token,
|
|
30
|
+
"Expected identifier or string after 'spawn'".to_string()
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return Statement {
|
|
35
|
+
kind: StatementKind::Spawn,
|
|
36
|
+
value,
|
|
37
|
+
indent: current_token.indent,
|
|
38
|
+
line: current_token.line,
|
|
39
|
+
column: current_token.column,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
|
|
3
|
+
use crate::core::{
|
|
4
|
+
lexer::token::{ Token, TokenKind },
|
|
5
|
+
parser::{ driver::Parser, statement::{ Statement, StatementKind } },
|
|
6
|
+
shared::value::Value,
|
|
7
|
+
store::global::GlobalStore,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
pub fn parse_synth_token(
|
|
11
|
+
parser: &mut Parser,
|
|
12
|
+
current_token: Token,
|
|
13
|
+
global_store: &mut GlobalStore
|
|
14
|
+
) -> Statement {
|
|
15
|
+
parser.advance(); // consume 'synth'
|
|
16
|
+
|
|
17
|
+
let Some(synth_token) = parser.previous_clone() else {
|
|
18
|
+
return Statement::unknown();
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Expect an identifier (synth waveform)
|
|
22
|
+
let Some(identifier_token) = parser.peek_clone() else {
|
|
23
|
+
return Statement::error(synth_token, "Expected identifier after 'synth'".to_string());
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
let synth_waveform = identifier_token.lexeme.clone();
|
|
27
|
+
|
|
28
|
+
parser.advance(); // consume identifier
|
|
29
|
+
|
|
30
|
+
// Expect synth optional parameters map
|
|
31
|
+
let mut parameters = HashMap::new();
|
|
32
|
+
|
|
33
|
+
if let Some(params) = parser.parse_map_value() {
|
|
34
|
+
// If parameters are provided, we expect a map
|
|
35
|
+
if let Value::Map(map) = params {
|
|
36
|
+
parameters = map;
|
|
37
|
+
} else {
|
|
38
|
+
return Statement::error(synth_token, "Expected a map for synth parameters".to_string());
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
// If no parameters are provided, we can still create the statement with an empty map
|
|
42
|
+
parameters = HashMap::new();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
Statement {
|
|
46
|
+
kind: StatementKind::Synth,
|
|
47
|
+
value: Value::Map(
|
|
48
|
+
HashMap::from([
|
|
49
|
+
("entity".to_string(), Value::String("synth".to_string())),
|
|
50
|
+
(
|
|
51
|
+
"value".to_string(),
|
|
52
|
+
Value::Map(
|
|
53
|
+
HashMap::from([
|
|
54
|
+
("waveform".to_string(), Value::String(synth_waveform)),
|
|
55
|
+
("parameters".to_string(), Value::Map(parameters)),
|
|
56
|
+
])
|
|
57
|
+
),
|
|
58
|
+
),
|
|
59
|
+
])
|
|
60
|
+
),
|
|
61
|
+
indent: synth_token.indent,
|
|
62
|
+
line: synth_token.line,
|
|
63
|
+
column: synth_token.column,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -9,51 +9,57 @@ use crate::core::{
|
|
|
9
9
|
|
|
10
10
|
pub fn parse_loop_token(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
|
|
11
11
|
parser.advance(); // consume 'loop'
|
|
12
|
-
|
|
13
12
|
let Some(loop_token) = parser.previous_clone() else {
|
|
14
13
|
return Statement::unknown();
|
|
15
14
|
};
|
|
16
15
|
|
|
17
|
-
// Expect an identifier (iterator)
|
|
18
16
|
let Some(iterator_token) = parser.peek_clone() else {
|
|
19
|
-
return Statement::error(loop_token, "Expected identifier after 'loop'".to_string());
|
|
17
|
+
return Statement::error(loop_token, "Expected number or identifier after 'loop'".to_string());
|
|
20
18
|
};
|
|
21
19
|
|
|
22
|
-
let
|
|
23
|
-
|
|
20
|
+
let iterator_value = match iterator_token.kind {
|
|
21
|
+
TokenKind::Number => {
|
|
22
|
+
let val = iterator_token.lexeme.parse::<f32>().unwrap_or(1.0);
|
|
23
|
+
parser.advance();
|
|
24
|
+
Value::Number(val)
|
|
25
|
+
}
|
|
26
|
+
TokenKind::Identifier => {
|
|
27
|
+
let val = iterator_token.lexeme.clone();
|
|
28
|
+
parser.advance();
|
|
29
|
+
Value::Identifier(val)
|
|
30
|
+
}
|
|
31
|
+
_ => {
|
|
32
|
+
return Statement::error(
|
|
33
|
+
iterator_token.clone(),
|
|
34
|
+
"Expected a number or identifier as loop count".to_string()
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
24
38
|
|
|
25
39
|
// Expect colon
|
|
26
40
|
let Some(colon_token) = parser.peek_clone() else {
|
|
27
|
-
return Statement::error(iterator_token.clone(), "Expected ':' after
|
|
41
|
+
return Statement::error(iterator_token.clone(), "Expected ':' after loop count".to_string());
|
|
28
42
|
};
|
|
29
43
|
|
|
30
44
|
if colon_token.kind != TokenKind::Colon {
|
|
31
|
-
let message = format!("Expected ':' after
|
|
45
|
+
let message = format!("Expected ':' after loop count, got {:?}", colon_token.kind);
|
|
32
46
|
return Statement::error(colon_token.clone(), message);
|
|
33
47
|
}
|
|
34
48
|
|
|
35
49
|
parser.advance(); // consume ':'
|
|
36
50
|
|
|
37
|
-
// Collect
|
|
38
|
-
let tokens = parser.collect_until(
|
|
39
|
-
|t| (t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF)
|
|
40
|
-
);
|
|
51
|
+
// Collect body
|
|
52
|
+
let tokens = parser.collect_until(|t| t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF);
|
|
41
53
|
let loop_body = parser.parse_block(tokens.clone(), global_store);
|
|
42
54
|
|
|
43
|
-
// Peek for dedent
|
|
44
55
|
if let Some(token) = parser.peek() {
|
|
45
56
|
if token.kind == TokenKind::Dedent {
|
|
46
57
|
parser.advance();
|
|
47
|
-
} else {
|
|
48
|
-
// Unexpected token after loop body
|
|
49
58
|
}
|
|
50
|
-
} else {
|
|
51
|
-
// EOF or unexpected end of input
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
let mut value_map = HashMap::new();
|
|
55
|
-
|
|
56
|
-
value_map.insert("iterator".to_string(), Value::Identifier(iterator_name));
|
|
62
|
+
value_map.insert("iterator".to_string(), iterator_value);
|
|
57
63
|
value_map.insert("body".to_string(), Value::Block(loop_body.clone()));
|
|
58
64
|
|
|
59
65
|
Statement {
|