@devaloop/devalang 0.0.1-alpha.1 → 0.0.1-alpha.11
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 +9 -0
- package/Cargo.toml +15 -6
- package/README.md +79 -81
- package/docs/CHANGELOG.md +213 -0
- package/docs/ROADMAP.md +11 -8
- package/docs/TODO.md +32 -29
- package/examples/bank.deva +9 -0
- package/examples/condition.deva +20 -0
- package/examples/duration.deva +9 -0
- package/examples/group.deva +12 -0
- package/examples/index.deva +12 -5
- package/examples/loop.deva +16 -0
- package/examples/samples/hat-808.wav +0 -0
- package/examples/synth.deva +14 -0
- package/examples/variables.deva +9 -0
- package/out-tsc/bin/devalang.exe +0 -0
- package/out-tsc/scripts/version/fetch.js +1 -5
- package/package.json +5 -4
- package/project-version.json +3 -3
- package/rust/cli/bank.rs +455 -0
- package/rust/cli/build.rs +114 -28
- package/rust/cli/check.rs +96 -103
- package/rust/cli/driver.rs +280 -0
- package/rust/cli/init.rs +79 -0
- package/rust/cli/install.rs +17 -0
- package/rust/cli/mod.rs +8 -1
- package/rust/cli/play.rs +193 -0
- package/rust/cli/template.rs +57 -0
- package/rust/cli/update.rs +4 -0
- package/rust/common/cdn.rs +11 -0
- package/rust/common/mod.rs +1 -0
- package/rust/config/driver.rs +76 -0
- package/rust/config/loader.rs +110 -0
- package/rust/config/mod.rs +2 -0
- package/rust/core/audio/engine.rs +242 -0
- package/rust/core/audio/evaluator.rs +31 -0
- package/rust/core/audio/interpreter/arrow_call.rs +142 -0
- package/rust/core/audio/interpreter/call.rs +70 -0
- package/rust/core/audio/interpreter/condition.rs +69 -0
- package/rust/core/audio/interpreter/driver.rs +236 -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 +67 -0
- package/rust/core/audio/interpreter/mod.rs +12 -0
- package/rust/core/audio/interpreter/sleep.rs +36 -0
- package/rust/core/audio/interpreter/spawn.rs +84 -0
- package/rust/core/audio/interpreter/tempo.rs +16 -0
- package/rust/core/audio/interpreter/trigger.rs +102 -0
- package/rust/core/audio/loader/mod.rs +1 -0
- package/rust/core/audio/loader/trigger.rs +64 -0
- package/rust/core/audio/mod.rs +6 -0
- package/rust/core/audio/player.rs +54 -0
- package/rust/core/audio/renderer.rs +54 -0
- package/rust/core/builder/mod.rs +70 -27
- package/rust/core/debugger/lexer.rs +27 -0
- package/rust/core/debugger/mod.rs +13 -49
- package/rust/core/debugger/preprocessor.rs +27 -0
- package/rust/core/debugger/store.rs +25 -0
- package/rust/core/error/mod.rs +60 -0
- package/rust/core/lexer/handler/arrow.rs +31 -0
- package/rust/core/lexer/handler/at.rs +21 -0
- package/rust/core/lexer/handler/brace.rs +41 -0
- package/rust/core/lexer/handler/colon.rs +21 -0
- package/rust/core/lexer/handler/comment.rs +30 -0
- package/rust/core/lexer/handler/dot.rs +21 -0
- package/rust/core/lexer/handler/driver.rs +241 -0
- package/rust/core/lexer/handler/identifier.rs +41 -0
- package/rust/core/lexer/handler/indent.rs +52 -0
- package/rust/core/lexer/handler/mod.rs +15 -0
- package/rust/core/lexer/handler/newline.rs +23 -0
- package/rust/core/lexer/handler/number.rs +31 -0
- package/rust/core/lexer/handler/operator.rs +44 -0
- package/rust/core/lexer/handler/slash.rs +21 -0
- package/rust/core/lexer/handler/string.rs +63 -0
- package/rust/core/lexer/mod.rs +37 -319
- package/rust/core/lexer/token.rs +87 -0
- package/rust/core/mod.rs +6 -2
- package/rust/core/parser/driver.rs +339 -0
- package/rust/core/parser/handler/arrow_call.rs +151 -0
- package/rust/core/parser/handler/at.rs +162 -0
- package/rust/core/parser/handler/bank.rs +41 -0
- package/rust/core/parser/handler/condition.rs +74 -0
- package/rust/core/parser/handler/dot.rs +178 -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 +72 -0
- package/rust/core/parser/handler/mod.rs +8 -0
- package/rust/core/parser/handler/tempo.rs +47 -0
- package/rust/core/parser/mod.rs +3 -200
- package/rust/core/parser/statement.rs +96 -0
- package/rust/core/preprocessor/loader.rs +308 -0
- package/rust/core/preprocessor/mod.rs +2 -24
- package/rust/core/preprocessor/module.rs +42 -56
- package/rust/core/preprocessor/processor.rs +76 -0
- package/rust/core/preprocessor/resolver/bank.rs +41 -51
- package/rust/core/preprocessor/resolver/call.rs +123 -0
- package/rust/core/preprocessor/resolver/condition.rs +92 -0
- package/rust/core/preprocessor/resolver/driver.rs +232 -0
- package/rust/core/preprocessor/resolver/group.rs +61 -0
- package/rust/core/preprocessor/resolver/let_.rs +31 -0
- package/rust/core/preprocessor/resolver/loop_.rs +76 -67
- package/rust/core/preprocessor/resolver/mod.rs +12 -111
- package/rust/core/preprocessor/resolver/spawn.rs +58 -0
- package/rust/core/preprocessor/resolver/synth.rs +50 -0
- package/rust/core/preprocessor/resolver/tempo.rs +40 -61
- package/rust/core/preprocessor/resolver/trigger.rs +90 -154
- package/rust/core/preprocessor/resolver/value.rs +78 -0
- package/rust/core/shared/bank.rs +21 -0
- package/rust/core/shared/duration.rs +9 -0
- package/rust/core/shared/mod.rs +3 -0
- package/rust/core/shared/value.rs +29 -0
- package/rust/core/store/export.rs +28 -0
- package/rust/core/store/global.rs +39 -0
- package/rust/core/store/import.rs +28 -0
- package/rust/core/store/mod.rs +4 -0
- package/rust/core/store/variable.rs +28 -0
- package/rust/core/utils/mod.rs +2 -0
- package/rust/core/utils/path.rs +31 -0
- package/rust/core/utils/validation.rs +37 -0
- package/rust/installer/bank.rs +55 -0
- package/rust/installer/mod.rs +1 -0
- package/rust/lib.rs +162 -1
- package/rust/main.rs +104 -31
- package/rust/utils/file.rs +35 -0
- package/rust/utils/installer.rs +56 -0
- package/rust/utils/logger.rs +108 -34
- package/rust/utils/mod.rs +5 -3
- package/rust/utils/{loader.rs → spinner.rs} +2 -0
- package/rust/utils/watcher.rs +33 -0
- package/templates/minimal/.devalang +5 -0
- package/templates/minimal/README.md +202 -0
- package/templates/minimal/src/index.deva +2 -0
- package/templates/welcome/.devalang +5 -0
- package/templates/welcome/README.md +202 -0
- package/templates/welcome/samples/kick-808.wav +0 -0
- package/templates/welcome/src/index.deva +13 -0
- package/templates/welcome/src/variables.deva +5 -0
- package/typescript/scripts/version/fetch.ts +1 -6
- package/docs/COMMANDS.md +0 -31
- package/docs/SYNTAX.md +0 -148
- package/examples/exported.deva +0 -7
- package/rust/audio/mod.rs +0 -1
- package/rust/cli/new.rs +0 -1
- package/rust/core/parser/at.rs +0 -142
- package/rust/core/parser/bank.rs +0 -42
- package/rust/core/parser/dot.rs +0 -107
- package/rust/core/parser/identifer.rs +0 -91
- package/rust/core/parser/loop_.rs +0 -62
- package/rust/core/parser/tempo.rs +0 -42
- package/rust/core/parser/variable.rs +0 -129
- package/rust/core/preprocessor/dependencies.rs +0 -54
- package/rust/core/preprocessor/resolver/at.rs +0 -24
- package/rust/core/types/cli.rs +0 -160
- package/rust/core/types/mod.rs +0 -7
- package/rust/core/types/module.rs +0 -41
- package/rust/core/types/parser.rs +0 -73
- package/rust/core/types/statement.rs +0 -105
- package/rust/core/types/store.rs +0 -116
- package/rust/core/types/token.rs +0 -83
- package/rust/core/types/variable.rs +0 -32
- package/rust/runner/executer.rs +0 -44
- package/rust/runner/mod.rs +0 -1
- package/rust/utils/path.rs +0 -46
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
lexer::token::TokenKind,
|
|
3
|
+
parser::{ statement::{ Statement, StatementKind }, driver::Parser },
|
|
4
|
+
shared::{ duration::Duration, value::Value },
|
|
5
|
+
store::global::GlobalStore,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
pub fn parse_dot_token(parser: &mut Parser, _global_store: &mut GlobalStore) -> Statement {
|
|
9
|
+
parser.advance(); // consume the first dot
|
|
10
|
+
|
|
11
|
+
let Some(dot_token) = parser.previous_clone() else {
|
|
12
|
+
return Statement::unknown();
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// Parse namespaced identifier: .808.kick.snare
|
|
16
|
+
let mut parts = Vec::new();
|
|
17
|
+
|
|
18
|
+
loop {
|
|
19
|
+
let Some(token) = parser.peek_clone() else {
|
|
20
|
+
break;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
match token.kind {
|
|
24
|
+
// Stop if we encounter a likely duration keyword
|
|
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
|
+
|
|
31
|
+
parts.push(token.lexeme.clone());
|
|
32
|
+
parser.advance();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
TokenKind::Identifier => {
|
|
36
|
+
// Stop if it's the duration keyword "auto"
|
|
37
|
+
if token.lexeme == "auto" {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
parts.push(token.lexeme.clone());
|
|
42
|
+
parser.advance();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
TokenKind::Dot => {
|
|
46
|
+
parser.advance(); // consume dot
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
_ => {
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let entity = parts.join(".");
|
|
56
|
+
|
|
57
|
+
if entity.is_empty() {
|
|
58
|
+
return Statement {
|
|
59
|
+
kind: StatementKind::Trigger {
|
|
60
|
+
entity: String::new(),
|
|
61
|
+
duration: Duration::Auto,
|
|
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
|
+
}
|
|
121
|
+
|
|
122
|
+
// fallback: simple numeric duration
|
|
123
|
+
let duration = parse_duration(numerator);
|
|
124
|
+
|
|
125
|
+
let val = match parser.peek_clone() {
|
|
126
|
+
Some(param_token) if param_token.kind == TokenKind::Identifier => {
|
|
127
|
+
parser.advance();
|
|
128
|
+
Value::Identifier(param_token.lexeme.clone())
|
|
129
|
+
}
|
|
130
|
+
Some(param_token) if param_token.kind == TokenKind::LBrace => {
|
|
131
|
+
parser.parse_map_value().unwrap_or(Value::Null)
|
|
132
|
+
}
|
|
133
|
+
_ => Value::Null,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
(duration, val)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
TokenKind::Identifier => {
|
|
140
|
+
let duration_lexeme = token.lexeme.clone();
|
|
141
|
+
parser.advance(); // consume duration
|
|
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)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
_ => (Duration::Auto, Value::Null),
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
Statement {
|
|
162
|
+
kind: StatementKind::Trigger { entity, duration },
|
|
163
|
+
value,
|
|
164
|
+
indent: dot_token.indent,
|
|
165
|
+
line: dot_token.line,
|
|
166
|
+
column: dot_token.column,
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
fn parse_duration(s: String) -> Duration {
|
|
171
|
+
if s == "auto" {
|
|
172
|
+
Duration::Auto
|
|
173
|
+
} else if let Ok(num) = s.parse::<f32>() {
|
|
174
|
+
Duration::Number(num)
|
|
175
|
+
} else {
|
|
176
|
+
Duration::Identifier(s)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -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
|
+
}
|