@devaloop/devalang 0.0.1-alpha.14 → 0.0.1-alpha.15
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 +2 -2
- package/README.md +31 -14
- package/docs/CHANGELOG.md +59 -0
- package/docs/ROADMAP.md +1 -1
- package/examples/automation.deva +44 -0
- package/examples/index.deva +41 -25
- package/examples/plugin.deva +15 -0
- package/out-tsc/bin/devalang.exe +0 -0
- package/package.json +1 -1
- package/project-version.json +3 -3
- package/rust/cli/bank.rs +14 -15
- package/rust/cli/check.rs +1 -1
- package/rust/cli/play.rs +1 -1
- package/rust/cli/update.rs +1 -1
- package/rust/common/api.rs +3 -6
- package/rust/common/cdn.rs +3 -6
- package/rust/common/sso.rs +3 -6
- package/rust/core/audio/engine.rs +215 -74
- package/rust/core/audio/evaluator.rs +101 -0
- package/rust/core/audio/interpreter/arrow_call.rs +27 -1
- package/rust/core/audio/interpreter/automate.rs +18 -0
- package/rust/core/audio/interpreter/call.rs +2 -2
- package/rust/core/audio/interpreter/condition.rs +3 -3
- package/rust/core/audio/interpreter/driver.rs +49 -16
- package/rust/core/audio/interpreter/let_.rs +14 -7
- package/rust/core/audio/interpreter/loop_.rs +39 -6
- package/rust/core/audio/interpreter/mod.rs +2 -1
- package/rust/core/audio/interpreter/sleep.rs +2 -4
- package/rust/core/audio/interpreter/spawn.rs +2 -2
- package/rust/core/audio/loader/trigger.rs +2 -5
- package/rust/core/audio/mod.rs +2 -1
- package/rust/core/audio/renderer.rs +1 -1
- package/rust/core/audio/special/easing.rs +120 -0
- package/rust/core/audio/special/env.rs +41 -0
- package/rust/core/audio/special/math.rs +92 -0
- package/rust/core/audio/special/mod.rs +9 -0
- package/rust/core/audio/special/modulator.rs +120 -0
- package/rust/core/debugger/store.rs +1 -1
- package/rust/core/error/mod.rs +4 -1
- package/rust/core/lexer/handler/arrow.rs +60 -9
- package/rust/core/lexer/handler/at.rs +4 -4
- package/rust/core/lexer/handler/brace.rs +8 -8
- package/rust/core/lexer/handler/colon.rs +4 -4
- package/rust/core/lexer/handler/comment.rs +2 -2
- package/rust/core/lexer/handler/dot.rs +4 -4
- package/rust/core/lexer/handler/driver.rs +42 -13
- package/rust/core/lexer/handler/identifier.rs +5 -4
- package/rust/core/lexer/handler/newline.rs +1 -1
- package/rust/core/lexer/handler/number.rs +3 -3
- package/rust/core/lexer/handler/operator.rs +3 -1
- package/rust/core/lexer/handler/parenthesis.rs +8 -8
- package/rust/core/lexer/handler/slash.rs +5 -5
- package/rust/core/lexer/handler/string.rs +1 -1
- package/rust/core/lexer/mod.rs +1 -1
- package/rust/core/lexer/token.rs +3 -0
- package/rust/core/parser/driver.rs +94 -12
- package/rust/core/parser/handler/arrow_call.rs +105 -89
- package/rust/core/parser/handler/at.rs +1 -1
- package/rust/core/parser/handler/dot.rs +3 -3
- package/rust/core/parser/handler/identifier/automate.rs +194 -0
- package/rust/core/parser/handler/identifier/function.rs +2 -3
- package/rust/core/parser/handler/identifier/let_.rs +16 -0
- package/rust/core/parser/handler/identifier/mod.rs +14 -10
- package/rust/core/parser/handler/identifier/print.rs +29 -0
- package/rust/core/parser/handler/identifier/sleep.rs +1 -1
- package/rust/core/parser/handler/identifier/synth.rs +7 -9
- package/rust/core/parser/handler/loop_.rs +60 -43
- package/rust/core/parser/statement.rs +5 -0
- package/rust/core/preprocessor/loader.rs +1 -1
- package/rust/core/preprocessor/processor.rs +4 -4
- package/rust/core/preprocessor/resolver/bank.rs +1 -2
- package/rust/core/preprocessor/resolver/call.rs +19 -18
- package/rust/core/preprocessor/resolver/driver.rs +7 -5
- package/rust/core/preprocessor/resolver/function.rs +3 -13
- package/rust/core/preprocessor/resolver/loop_.rs +31 -1
- package/rust/core/preprocessor/resolver/spawn.rs +3 -22
- package/rust/core/preprocessor/resolver/tempo.rs +1 -1
- package/rust/core/preprocessor/resolver/trigger.rs +2 -3
- package/rust/core/preprocessor/resolver/value.rs +6 -12
- package/rust/core/shared/bank.rs +1 -1
- package/rust/core/utils/path.rs +1 -1
- package/rust/core/utils/validation.rs +0 -1
- package/rust/installer/bank.rs +1 -1
- package/rust/installer/plugin.rs +2 -2
- package/rust/main.rs +0 -1
- package/rust/utils/error.rs +51 -0
- package/rust/utils/logger.rs +4 -0
- package/rust/utils/mod.rs +1 -44
- package/rust/utils/spinner.rs +1 -1
|
@@ -5,21 +5,23 @@ pub mod spawn;
|
|
|
5
5
|
pub mod sleep;
|
|
6
6
|
pub mod synth;
|
|
7
7
|
pub mod function;
|
|
8
|
+
pub mod automate;
|
|
9
|
+
pub mod print;
|
|
8
10
|
|
|
9
11
|
use crate::core::{
|
|
10
12
|
parser::{
|
|
11
13
|
driver::Parser,
|
|
12
|
-
handler::{
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
handler::identifier::{
|
|
15
|
+
automate::parse_automate_token,
|
|
16
|
+
call::parse_call_token,
|
|
17
|
+
group::parse_group_token,
|
|
18
|
+
let_::parse_let_token,
|
|
19
|
+
print::parse_print_token,
|
|
20
|
+
sleep::parse_sleep_token,
|
|
21
|
+
spawn::parse_spawn_token,
|
|
22
|
+
synth::parse_synth_token,
|
|
21
23
|
},
|
|
22
|
-
statement::Statement,
|
|
24
|
+
statement::{ Statement },
|
|
23
25
|
},
|
|
24
26
|
store::global::GlobalStore,
|
|
25
27
|
};
|
|
@@ -39,6 +41,8 @@ pub fn parse_identifier_token(parser: &mut Parser, global_store: &mut GlobalStor
|
|
|
39
41
|
"spawn" => parse_spawn_token(parser, current_token_clone, global_store),
|
|
40
42
|
"sleep" => parse_sleep_token(parser, current_token_clone, global_store),
|
|
41
43
|
"synth" => parse_synth_token(parser, current_token_clone, global_store),
|
|
44
|
+
"automate" => parse_automate_token(parser, current_token_clone, global_store),
|
|
45
|
+
"print" => parse_print_token(parser, current_token_clone, global_store),
|
|
42
46
|
_ => {
|
|
43
47
|
parser.advance(); // consume identifier
|
|
44
48
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
lexer::token::{ Token, TokenKind },
|
|
3
|
+
parser::{ driver::Parser, statement::{ Statement, StatementKind } },
|
|
4
|
+
store::global::GlobalStore,
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
pub fn parse_print_token(
|
|
8
|
+
parser: &mut Parser,
|
|
9
|
+
current_token: Token,
|
|
10
|
+
_global_store: &mut GlobalStore
|
|
11
|
+
) -> Statement {
|
|
12
|
+
// consume 'print'
|
|
13
|
+
parser.advance();
|
|
14
|
+
|
|
15
|
+
let collected = parser.collect_until(|t| matches!(t.kind, TokenKind::Newline | TokenKind::EOF));
|
|
16
|
+
// If single identifier, store as Identifier; else store as String of concatenated lexemes
|
|
17
|
+
let value = if collected.len() == 1 && collected[0].kind == TokenKind::Identifier {
|
|
18
|
+
crate::core::shared::value::Value::Identifier(collected[0].lexeme.clone())
|
|
19
|
+
} else {
|
|
20
|
+
let mut text = String::new();
|
|
21
|
+
for t in collected.iter() {
|
|
22
|
+
if matches!(t.kind, TokenKind::Newline | TokenKind::EOF) { break; }
|
|
23
|
+
text.push_str(&t.lexeme);
|
|
24
|
+
}
|
|
25
|
+
crate::core::shared::value::Value::String(text.trim().to_string())
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
Statement { kind: StatementKind::Print, value, indent: current_token.indent, line: current_token.line, column: current_token.column }
|
|
29
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use std::collections::HashMap;
|
|
2
2
|
|
|
3
3
|
use crate::core::{
|
|
4
|
-
lexer::token::
|
|
4
|
+
lexer::token::Token,
|
|
5
5
|
parser::{ driver::Parser, statement::{ Statement, StatementKind } },
|
|
6
6
|
shared::value::Value,
|
|
7
7
|
store::global::GlobalStore,
|
|
@@ -9,8 +9,8 @@ use crate::core::{
|
|
|
9
9
|
|
|
10
10
|
pub fn parse_synth_token(
|
|
11
11
|
parser: &mut Parser,
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
_current_token: Token,
|
|
13
|
+
_global_store: &mut GlobalStore
|
|
14
14
|
) -> Statement {
|
|
15
15
|
parser.advance(); // consume 'synth'
|
|
16
16
|
|
|
@@ -28,19 +28,17 @@ pub fn parse_synth_token(
|
|
|
28
28
|
parser.advance(); // consume identifier
|
|
29
29
|
|
|
30
30
|
// Expect synth optional parameters map
|
|
31
|
-
let
|
|
32
|
-
|
|
33
|
-
if let Some(params) = parser.parse_map_value() {
|
|
31
|
+
let parameters = if let Some(params) = parser.parse_map_value() {
|
|
34
32
|
// If parameters are provided, we expect a map
|
|
35
33
|
if let Value::Map(map) = params {
|
|
36
|
-
|
|
34
|
+
map
|
|
37
35
|
} else {
|
|
38
36
|
return Statement::error(synth_token, "Expected a map for synth parameters".to_string());
|
|
39
37
|
}
|
|
40
38
|
} else {
|
|
41
39
|
// If no parameters are provided, we can still create the statement with an empty map
|
|
42
|
-
|
|
43
|
-
}
|
|
40
|
+
HashMap::new()
|
|
41
|
+
};
|
|
44
42
|
|
|
45
43
|
Statement {
|
|
46
44
|
kind: StatementKind::Synth,
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
use std::collections::HashMap;
|
|
2
|
-
|
|
3
1
|
use crate::core::{
|
|
4
2
|
lexer::{ token::TokenKind },
|
|
5
3
|
parser::{ statement::{ Statement, StatementKind }, driver::Parser },
|
|
@@ -8,65 +6,84 @@ use crate::core::{
|
|
|
8
6
|
};
|
|
9
7
|
|
|
10
8
|
pub fn parse_loop_token(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
|
|
11
|
-
parser.advance(); // consume 'loop'
|
|
9
|
+
parser.advance(); // consume 'loop' or 'for' (aliased in lexer)
|
|
12
10
|
let Some(loop_token) = parser.previous_clone() else {
|
|
13
11
|
return Statement::unknown();
|
|
14
12
|
};
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
// Support two forms:
|
|
15
|
+
// 1) loop <count>:
|
|
16
|
+
// 2) for <ident> in [a,b,c]:
|
|
17
|
+
|
|
18
|
+
// Peek next to decide
|
|
19
|
+
let Some(next_token) = parser.peek_clone() else {
|
|
20
|
+
return Statement::error(loop_token, "Expected iterator after loop/for".to_string());
|
|
18
21
|
};
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
// Try to detect 'for <ident> in [array]:' form
|
|
24
|
+
let mut foreach_ident: Option<String> = None;
|
|
25
|
+
if let TokenKind::Identifier = next_token.kind {
|
|
26
|
+
// Could be either count identifier (old form) or foreach variable
|
|
27
|
+
// Look ahead for 'in'
|
|
28
|
+
let name = next_token.lexeme.clone();
|
|
29
|
+
// don't consume yet; we'll branch
|
|
30
|
+
if let Some(t2) = parser.peek_nth(1) {
|
|
31
|
+
if t2.kind == TokenKind::Identifier && t2.lexeme == "in" {
|
|
32
|
+
// foreach form
|
|
33
|
+
foreach_ident = Some(name);
|
|
34
|
+
// consume ident and 'in'
|
|
35
|
+
parser.advance();
|
|
36
|
+
parser.advance();
|
|
37
|
+
}
|
|
30
38
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if let Some(var_name) = foreach_ident {
|
|
42
|
+
// Expect array literal
|
|
43
|
+
let array_val = if let Some(v) = parser.parse_array_value() { v } else {
|
|
44
|
+
return Statement::error(loop_token, "Expected array literal after 'in'".to_string());
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Expect ':'
|
|
48
|
+
if !parser.match_token(TokenKind::Colon) {
|
|
49
|
+
return Statement::error(loop_token, "Expected ':' after foreach header".to_string());
|
|
36
50
|
}
|
|
51
|
+
|
|
52
|
+
let tokens = parser.collect_until(|t| t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF);
|
|
53
|
+
let loop_body = parser.parse_block(tokens.clone(), global_store);
|
|
54
|
+
if let Some(token) = parser.peek() { if token.kind == TokenKind::Dedent { parser.advance(); } }
|
|
55
|
+
|
|
56
|
+
let mut value_map = std::collections::HashMap::new();
|
|
57
|
+
value_map.insert("foreach".to_string(), Value::Identifier(var_name));
|
|
58
|
+
value_map.insert("array".to_string(), array_val);
|
|
59
|
+
value_map.insert("body".to_string(), Value::Block(loop_body.clone()));
|
|
60
|
+
|
|
61
|
+
return Statement { kind: StatementKind::Loop, value: Value::Map(value_map), indent: loop_token.indent, line: loop_token.line, column: loop_token.column };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Fallback to legacy: loop <count>:
|
|
65
|
+
let Some(iterator_token) = parser.peek_clone() else {
|
|
66
|
+
return Statement::error(loop_token, "Expected number or identifier after 'loop'".to_string());
|
|
37
67
|
};
|
|
38
68
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
69
|
+
let iterator_value = match iterator_token.kind {
|
|
70
|
+
TokenKind::Number => { let val = iterator_token.lexeme.parse::<f32>().unwrap_or(1.0); parser.advance(); Value::Number(val) }
|
|
71
|
+
TokenKind::Identifier => { let val = iterator_token.lexeme.clone(); parser.advance(); Value::Identifier(val) }
|
|
72
|
+
_ => { return Statement::error(iterator_token.clone(), "Expected a number or identifier as loop count".to_string()); }
|
|
42
73
|
};
|
|
43
74
|
|
|
44
|
-
if
|
|
45
|
-
let message = format!("Expected ':' after loop count, got {:?}",
|
|
46
|
-
return Statement::error(
|
|
75
|
+
if !parser.match_token(TokenKind::Colon) {
|
|
76
|
+
let message = format!("Expected ':' after loop count, got {:?}", parser.peek_kind());
|
|
77
|
+
return Statement::error(loop_token.clone(), message);
|
|
47
78
|
}
|
|
48
79
|
|
|
49
|
-
parser.advance(); // consume ':'
|
|
50
|
-
|
|
51
|
-
// Collect body
|
|
52
80
|
let tokens = parser.collect_until(|t| t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF);
|
|
53
81
|
let loop_body = parser.parse_block(tokens.clone(), global_store);
|
|
82
|
+
if let Some(token) = parser.peek() { if token.kind == TokenKind::Dedent { parser.advance(); } }
|
|
54
83
|
|
|
55
|
-
|
|
56
|
-
if token.kind == TokenKind::Dedent {
|
|
57
|
-
parser.advance();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
let mut value_map = HashMap::new();
|
|
84
|
+
let mut value_map = std::collections::HashMap::new();
|
|
62
85
|
value_map.insert("iterator".to_string(), iterator_value);
|
|
63
86
|
value_map.insert("body".to_string(), Value::Block(loop_body.clone()));
|
|
64
87
|
|
|
65
|
-
Statement {
|
|
66
|
-
kind: StatementKind::Loop,
|
|
67
|
-
value: Value::Map(value_map),
|
|
68
|
-
indent: loop_token.indent,
|
|
69
|
-
line: loop_token.line,
|
|
70
|
-
column: loop_token.column,
|
|
71
|
-
}
|
|
88
|
+
Statement { kind: StatementKind::Loop, value: Value::Map(value_map), indent: loop_token.indent, line: loop_token.line, column: loop_token.column }
|
|
72
89
|
}
|
|
@@ -37,6 +37,7 @@ pub enum StatementKind {
|
|
|
37
37
|
// ───── Core Instructions ─────
|
|
38
38
|
Tempo,
|
|
39
39
|
Bank,
|
|
40
|
+
Print,
|
|
40
41
|
Load {
|
|
41
42
|
source: String,
|
|
42
43
|
alias: String,
|
|
@@ -44,6 +45,10 @@ pub enum StatementKind {
|
|
|
44
45
|
Let {
|
|
45
46
|
name: String,
|
|
46
47
|
},
|
|
48
|
+
// Automation of parameters over time (percent-based envelopes)
|
|
49
|
+
Automate {
|
|
50
|
+
target: String,
|
|
51
|
+
},
|
|
47
52
|
ArrowCall {
|
|
48
53
|
target: String,
|
|
49
54
|
method: String,
|
|
@@ -188,7 +188,7 @@ impl ModuleLoader {
|
|
|
188
188
|
|
|
189
189
|
// Inject triggers for each bank used in module
|
|
190
190
|
for bank_name in self.extract_bank_names(&statements) {
|
|
191
|
-
self.inject_bank_triggers(&mut module, &bank_name);
|
|
191
|
+
let _ = self.inject_bank_triggers(&mut module, &bank_name);
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
// Inject module variables and functions into global store
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
use std::{ collections::HashMap, path::Path };
|
|
2
2
|
|
|
3
3
|
use crate::core::{
|
|
4
|
-
parser::
|
|
5
|
-
preprocessor::
|
|
4
|
+
parser::statement::StatementKind,
|
|
5
|
+
preprocessor::loader::ModuleLoader,
|
|
6
6
|
shared::value::Value,
|
|
7
7
|
store::global::GlobalStore,
|
|
8
8
|
utils::path::{ normalize_path, resolve_relative_path },
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
pub fn process_modules(
|
|
11
|
+
pub fn process_modules(_module_loader: &ModuleLoader, global_store: &mut GlobalStore) {
|
|
12
12
|
for module in global_store.modules.values_mut() {
|
|
13
13
|
for stmt in &module.statements {
|
|
14
14
|
match &stmt.kind {
|
|
@@ -52,7 +52,7 @@ pub fn process_modules(module_loader: &ModuleLoader, global_store: &mut GlobalSt
|
|
|
52
52
|
);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
StatementKind::Export { names, source } => {
|
|
55
|
+
StatementKind::Export { names, source: _ } => {
|
|
56
56
|
for name in names {
|
|
57
57
|
if let Some(val) = module.variable_table.get(name) {
|
|
58
58
|
module.export_table.add_export(name.clone(), val.clone());
|
|
@@ -11,12 +11,11 @@ use crate::{
|
|
|
11
11
|
pub fn resolve_bank(
|
|
12
12
|
stmt: &Statement,
|
|
13
13
|
module: &Module,
|
|
14
|
-
|
|
14
|
+
_path: &str,
|
|
15
15
|
_global_store: &GlobalStore
|
|
16
16
|
) -> Statement {
|
|
17
17
|
let mut new_stmt = stmt.clone();
|
|
18
18
|
let logger = Logger::new();
|
|
19
|
-
|
|
20
19
|
match &stmt.value {
|
|
21
20
|
Value::Identifier(ident) => {
|
|
22
21
|
if let Some(val) = module.variable_table.get(ident) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use crate::{
|
|
2
2
|
core::{
|
|
3
3
|
parser::statement::{ Statement, StatementKind },
|
|
4
|
-
preprocessor::
|
|
4
|
+
preprocessor::module::Module,
|
|
5
5
|
shared::value::Value,
|
|
6
6
|
store::global::GlobalStore,
|
|
7
7
|
},
|
|
@@ -13,7 +13,7 @@ pub fn resolve_call(
|
|
|
13
13
|
name: String,
|
|
14
14
|
args: Vec<Value>,
|
|
15
15
|
module: &Module,
|
|
16
|
-
|
|
16
|
+
_path: &str,
|
|
17
17
|
global_store: &mut GlobalStore
|
|
18
18
|
) -> Statement {
|
|
19
19
|
let logger = Logger::new();
|
|
@@ -78,23 +78,24 @@ pub fn resolve_call(
|
|
|
78
78
|
..stmt.clone()
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
-
_ =>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
fn error_stmt(logger: &Logger, module: &Module, stmt: &Statement, message: &str) -> Statement {
|
|
90
|
-
let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
|
|
91
|
-
logger.log_message(LogLevel::Error, &format!("{message}\n → at {stacktrace}"));
|
|
81
|
+
_ => {
|
|
82
|
+
let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
|
|
83
|
+
logger.log_message(
|
|
84
|
+
LogLevel::Error,
|
|
85
|
+
&format!(
|
|
86
|
+
"Expected StatementKind::Call in resolve_call()\n → at {stacktrace}"
|
|
87
|
+
)
|
|
88
|
+
);
|
|
92
89
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
Statement {
|
|
91
|
+
kind: StatementKind::Error {
|
|
92
|
+
message: "Expected StatementKind::Call in resolve_call()".to_string(),
|
|
93
|
+
},
|
|
94
|
+
value: Value::Null,
|
|
95
|
+
..stmt.clone()
|
|
96
|
+
}
|
|
96
97
|
},
|
|
97
|
-
value: Value::Null,
|
|
98
|
-
..stmt.clone()
|
|
99
98
|
}
|
|
100
99
|
}
|
|
100
|
+
|
|
101
|
+
// (removed unused helpers get_group_body, error_stmt)
|
|
@@ -25,7 +25,7 @@ use crate::{
|
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
pub fn resolve_all_modules(module_loader: &ModuleLoader, global_store: &mut GlobalStore) {
|
|
28
|
-
for
|
|
28
|
+
for _module in global_store.clone().modules.values_mut() {
|
|
29
29
|
resolve_imports(module_loader, global_store);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
@@ -80,10 +80,12 @@ fn resolve_value(value: &Value, module: &Module, global_store: &mut GlobalStore)
|
|
|
80
80
|
return resolve_value(&export_val, module, global_store);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
Value::
|
|
83
|
+
// Leave unresolved identifiers as-is; they might be runtime-bound (e.g., foreach vars)
|
|
84
|
+
Value::Identifier(name.clone())
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
Value::String(s) => Value::String(s.clone()),
|
|
88
|
+
|
|
87
89
|
Value::Beat(beat_str) => {
|
|
88
90
|
println!("[warn] '{:?}': unresolved beat '{}'", module.path, beat_str);
|
|
89
91
|
Value::Beat(beat_str.clone())
|
|
@@ -118,7 +120,7 @@ fn find_export_value(name: &str, global_store: &GlobalStore) -> Option<Value> {
|
|
|
118
120
|
None
|
|
119
121
|
}
|
|
120
122
|
|
|
121
|
-
pub fn resolve_imports(
|
|
123
|
+
pub fn resolve_imports(_module_loader: &ModuleLoader, global_store: &mut GlobalStore) {
|
|
122
124
|
for (module_path, module) in global_store.clone().modules.iter_mut() {
|
|
123
125
|
for (name, source_path) in &module.import_table.imports {
|
|
124
126
|
match source_path {
|
|
@@ -260,7 +262,7 @@ pub fn resolve_and_flatten_all_modules(
|
|
|
260
262
|
resolved.push(resolved_stmt);
|
|
261
263
|
}
|
|
262
264
|
|
|
263
|
-
StatementKind::Function { name, parameters, body } => {
|
|
265
|
+
StatementKind::Function { name: _, parameters: _, body: _ } => {
|
|
264
266
|
let resolved_function = resolve_function(&stmt, &module, &path, global_store);
|
|
265
267
|
resolved.push(resolved_function);
|
|
266
268
|
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
use std::collections::HashMap;
|
|
2
1
|
|
|
3
2
|
use crate::{
|
|
4
3
|
core::{
|
|
5
4
|
parser::statement::{ Statement, StatementKind },
|
|
6
5
|
preprocessor::{ module::Module, resolver::driver::resolve_statement },
|
|
7
6
|
shared::value::Value,
|
|
8
|
-
store::{ function::FunctionDef, global::GlobalStore
|
|
7
|
+
store::{ function::FunctionDef, global::GlobalStore },
|
|
9
8
|
},
|
|
10
|
-
|
|
9
|
+
|
|
11
10
|
};
|
|
12
11
|
|
|
13
12
|
pub fn resolve_function(
|
|
@@ -66,13 +65,4 @@ fn resolve_block_statements(
|
|
|
66
65
|
.collect()
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
|
|
70
|
-
let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
|
|
71
|
-
logger.log_error_with_stacktrace(&message, &stacktrace);
|
|
72
|
-
|
|
73
|
-
Statement {
|
|
74
|
-
kind: StatementKind::Error { message },
|
|
75
|
-
value: Value::Null,
|
|
76
|
-
..stmt.clone()
|
|
77
|
-
}
|
|
78
|
-
}
|
|
68
|
+
// (removed unused helper type_error)
|
|
@@ -32,6 +32,36 @@ pub fn resolve_loop(
|
|
|
32
32
|
resolved_map.insert(key.clone(), resolve_value(val, module, global_store));
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
// Foreach form takes precedence if present
|
|
36
|
+
if let (Some(Value::Identifier(var_name)), Some(array_val)) = (resolved_map.get("foreach"), resolved_map.get("array")) {
|
|
37
|
+
// Resolve array elements
|
|
38
|
+
let resolved_array = match array_val {
|
|
39
|
+
Value::Array(items) => Value::Array(items.iter().map(|v| resolve_value(v, module, global_store)).collect()),
|
|
40
|
+
other => resolve_value(other, module, global_store),
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
let body_value = match resolved_map.get("body") {
|
|
44
|
+
Some(Value::Block(stmts)) => {
|
|
45
|
+
let resolved = stmts
|
|
46
|
+
.iter()
|
|
47
|
+
.map(|s| resolve_statement(s, module, path, global_store))
|
|
48
|
+
.collect();
|
|
49
|
+
Value::Block(resolved)
|
|
50
|
+
}
|
|
51
|
+
_ => {
|
|
52
|
+
error_value(&logger, module, stmt, "Invalid or missing loop body");
|
|
53
|
+
Value::Block(vec![])
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
let mut final_map = HashMap::new();
|
|
58
|
+
final_map.insert("foreach".to_string(), Value::Identifier(var_name.clone()));
|
|
59
|
+
final_map.insert("array".to_string(), resolved_array);
|
|
60
|
+
final_map.insert("body".to_string(), body_value);
|
|
61
|
+
|
|
62
|
+
return Statement { kind: StatementKind::Loop, value: Value::Map(final_map), ..stmt.clone() };
|
|
63
|
+
}
|
|
64
|
+
|
|
35
65
|
let iterator_value = match resolved_map.get("iterator") {
|
|
36
66
|
Some(Value::Number(n)) => Value::Number(*n),
|
|
37
67
|
Some(other) => {
|
|
@@ -49,7 +79,7 @@ pub fn resolve_loop(
|
|
|
49
79
|
}
|
|
50
80
|
};
|
|
51
81
|
|
|
52
|
-
let body_value = match resolved_map.
|
|
82
|
+
let body_value = match resolved_map.get("body") {
|
|
53
83
|
Some(Value::Block(stmts)) => {
|
|
54
84
|
let resolved = stmts
|
|
55
85
|
.iter()
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
use crate::{
|
|
2
2
|
core::{
|
|
3
3
|
parser::statement::{ Statement, StatementKind },
|
|
4
|
-
preprocessor::
|
|
5
|
-
module::Module,
|
|
6
|
-
resolver::driver::resolve_statement,
|
|
7
|
-
resolver::value::resolve_value,
|
|
8
|
-
},
|
|
4
|
+
preprocessor::module::Module,
|
|
9
5
|
shared::value::Value,
|
|
10
6
|
store::global::GlobalStore,
|
|
11
7
|
},
|
|
@@ -17,7 +13,7 @@ pub fn resolve_spawn(
|
|
|
17
13
|
name: String,
|
|
18
14
|
args: Vec<Value>,
|
|
19
15
|
module: &Module,
|
|
20
|
-
|
|
16
|
+
_path: &str,
|
|
21
17
|
global_store: &mut GlobalStore
|
|
22
18
|
) -> Statement {
|
|
23
19
|
let logger = Logger::new();
|
|
@@ -74,19 +70,4 @@ pub fn resolve_spawn(
|
|
|
74
70
|
}
|
|
75
71
|
}
|
|
76
72
|
|
|
77
|
-
|
|
78
|
-
if let Value::Block(body) = &stmt_box.value { body.clone() } else { vec![] }
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
fn error_stmt(logger: &Logger, module: &Module, stmt: &Statement, message: &str) -> Statement {
|
|
82
|
-
let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
|
|
83
|
-
logger.log_message(LogLevel::Error, &format!("{message}\n → at {stacktrace}"));
|
|
84
|
-
|
|
85
|
-
Statement {
|
|
86
|
-
kind: StatementKind::Error {
|
|
87
|
-
message: message.to_string(),
|
|
88
|
-
},
|
|
89
|
-
value: Value::Null,
|
|
90
|
-
..stmt.clone()
|
|
91
|
-
}
|
|
92
|
-
}
|
|
73
|
+
// (removed unused helpers get_group_body, error_stmt)
|
|
@@ -14,7 +14,7 @@ pub fn resolve_trigger(
|
|
|
14
14
|
stmt: &Statement,
|
|
15
15
|
entity: &str,
|
|
16
16
|
duration: &mut Duration,
|
|
17
|
-
|
|
17
|
+
_effects: Option<Value>,
|
|
18
18
|
module: &Module,
|
|
19
19
|
path: &str,
|
|
20
20
|
global_store: &GlobalStore
|
|
@@ -22,7 +22,6 @@ pub fn resolve_trigger(
|
|
|
22
22
|
let logger = Logger::new();
|
|
23
23
|
|
|
24
24
|
let mut final_duration = duration.clone();
|
|
25
|
-
let mut final_value = stmt.value.clone();
|
|
26
25
|
|
|
27
26
|
// Duration resolution
|
|
28
27
|
if let Duration::Identifier(ident) = duration {
|
|
@@ -43,7 +42,7 @@ pub fn resolve_trigger(
|
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
// Params value resolution
|
|
46
|
-
final_value = match &stmt.value {
|
|
45
|
+
let final_value = match &stmt.value {
|
|
47
46
|
Value::Identifier(ident) => {
|
|
48
47
|
println!("Resolving identifier: {}", ident);
|
|
49
48
|
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
use std::collections::HashMap;
|
|
2
2
|
|
|
3
|
-
use crate::{
|
|
4
|
-
core::{
|
|
5
|
-
parser::statement::{ Statement, StatementKind },
|
|
3
|
+
use crate::core::{
|
|
6
4
|
preprocessor::{ module::Module, resolver::driver::resolve_statement },
|
|
7
5
|
shared::value::Value,
|
|
8
|
-
store::
|
|
9
|
-
}
|
|
10
|
-
utils::logger::{ LogLevel, Logger },
|
|
11
|
-
};
|
|
6
|
+
store::global::GlobalStore,
|
|
7
|
+
};
|
|
12
8
|
|
|
13
9
|
fn find_export_value(name: &str, global_store: &GlobalStore) -> Option<Value> {
|
|
14
10
|
for (_path, module) in &global_store.modules {
|
|
@@ -23,8 +19,7 @@ fn find_export_value(name: &str, global_store: &GlobalStore) -> Option<Value> {
|
|
|
23
19
|
pub fn resolve_value(value: &Value, module: &Module, global_store: &mut GlobalStore) -> Value {
|
|
24
20
|
match value {
|
|
25
21
|
Value::String(s) => {
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
// Keep raw strings as-is; they may be runtime-evaluated (e.g., expressions)
|
|
28
23
|
Value::String(s.clone())
|
|
29
24
|
},
|
|
30
25
|
|
|
@@ -37,9 +32,8 @@ pub fn resolve_value(value: &Value, module: &Module, global_store: &mut GlobalSt
|
|
|
37
32
|
return resolve_value(&export_val, module, global_store);
|
|
38
33
|
}
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Value::Null
|
|
35
|
+
// Leave unresolved identifiers as-is; may be runtime-bound (e.g., foreach variable)
|
|
36
|
+
Value::Identifier(name.clone())
|
|
43
37
|
}
|
|
44
38
|
|
|
45
39
|
Value::Map(map) => {
|
package/rust/core/shared/bank.rs
CHANGED
package/rust/core/utils/path.rs
CHANGED
package/rust/installer/bank.rs
CHANGED
|
@@ -59,7 +59,7 @@ pub async fn install_bank(name: &str, target_dir: &Path) -> Result<(), String> {
|
|
|
59
59
|
format!("Failed to extract: {}", e)
|
|
60
60
|
)?;
|
|
61
61
|
|
|
62
|
-
add_bank_to_config(&mut config, &extract_path,
|
|
62
|
+
add_bank_to_config(&mut config, &extract_path, dependency_path);
|
|
63
63
|
|
|
64
64
|
Ok(())
|
|
65
65
|
}
|