@devaloop/devalang 0.0.1-beta.2 → 0.0.1-beta.3
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 +84 -81
- package/README.md +3 -2
- package/docs/CHANGELOG.md +41 -0
- package/docs/ROADMAP.md +3 -3
- package/examples/chain.deva +19 -0
- package/examples/plugin.deva +10 -10
- package/examples/routing.deva +23 -0
- package/out-tsc/bin/project-version.json +6 -0
- package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +8 -8
- package/out-tsc/scripts/version/copy-to-binary.d.ts +1 -0
- package/out-tsc/scripts/version/copy-to-binary.js +79 -0
- package/package.json +23 -10
- package/project-version.json +3 -3
- package/rust/bindings/Cargo.toml +9 -0
- package/rust/bindings/src/lib.rs +86 -0
- package/rust/cli/addon/commands.rs +35 -0
- package/rust/cli/addon/download.rs +234 -0
- package/rust/cli/addon/install.rs +33 -0
- package/rust/cli/addon/list.rs +224 -0
- package/rust/cli/addon/metadata.rs +124 -0
- package/rust/cli/addon/mod.rs +8 -0
- package/rust/cli/addon/remove.rs +271 -0
- package/rust/cli/addon/update.rs +305 -0
- package/rust/cli/{install/addon.rs → addon/utils.rs} +109 -118
- package/rust/cli/build/commands.rs +153 -153
- package/rust/cli/build/process.rs +165 -165
- package/rust/cli/check/mod.rs +208 -208
- package/rust/cli/discover/commands.rs +275 -253
- package/rust/cli/discover/config.rs +109 -111
- package/rust/cli/discover/fs.rs +19 -19
- package/rust/cli/discover/install.rs +214 -103
- package/rust/cli/discover/metadata.rs +48 -48
- package/rust/cli/discover/mod.rs +5 -5
- package/rust/cli/me/commands.rs +52 -0
- package/rust/cli/me/mod.rs +1 -0
- package/rust/cli/mod.rs +12 -12
- package/rust/cli/parser.rs +30 -69
- package/rust/cli/play/commands.rs +375 -375
- package/rust/cli/play/process.rs +159 -159
- package/rust/core/audio/engine/driver.rs +19 -2
- package/rust/core/audio/engine/export.rs +169 -169
- package/rust/core/audio/engine/mod.rs +56 -56
- package/rust/core/audio/engine/notes/dsp.rs +88 -85
- package/rust/core/audio/engine/notes/mod.rs +53 -44
- package/rust/core/audio/engine/notes/params.rs +294 -294
- package/rust/core/audio/engine/sample/insert.rs +148 -47
- package/rust/core/audio/engine/sample/mod.rs +40 -40
- package/rust/core/audio/engine/sample/padding.rs +170 -170
- package/rust/core/audio/evaluator/condition.rs +61 -61
- package/rust/core/audio/evaluator/numeric.rs +152 -152
- package/rust/core/audio/evaluator/rhs.rs +16 -16
- package/rust/core/audio/evaluator/string_expr.rs +94 -94
- package/rust/core/audio/interpreter/driver.rs +574 -574
- package/rust/core/audio/interpreter/mod.rs +2 -2
- package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +9 -5
- package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +398 -384
- package/rust/core/audio/interpreter/statements/arrow_call/methods/effects.rs +323 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +1 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +66 -11
- package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -3
- package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -192
- package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -24
- package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -116
- package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -97
- package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -100
- package/rust/core/audio/interpreter/statements/automate.rs +16 -16
- package/rust/core/audio/interpreter/statements/call.rs +31 -1
- package/rust/core/audio/interpreter/statements/condition.rs +72 -72
- package/rust/core/audio/interpreter/statements/function.rs +24 -24
- package/rust/core/audio/interpreter/statements/let_.rs +36 -36
- package/rust/core/audio/interpreter/statements/load.rs +17 -17
- package/rust/core/audio/interpreter/statements/loop_.rs +115 -115
- package/rust/core/audio/interpreter/statements/spawn.rs +51 -2
- package/rust/core/audio/interpreter/statements/trigger.rs +242 -239
- package/rust/core/audio/loader/trigger.rs +98 -98
- package/rust/core/audio/player.rs +70 -70
- package/rust/core/audio/special/mod.rs +9 -9
- package/rust/core/builder/mod.rs +129 -129
- package/rust/core/debugger/lexer.rs +27 -27
- package/rust/core/debugger/logs.rs +52 -52
- package/rust/core/debugger/preprocessor.rs +27 -27
- package/rust/core/debugger/store.rs +38 -38
- package/rust/core/lexer/driver.rs +59 -59
- package/rust/core/lexer/handler/arrow.rs +82 -82
- package/rust/core/lexer/handler/at.rs +21 -21
- package/rust/core/lexer/handler/brace.rs +41 -41
- package/rust/core/lexer/handler/colon.rs +21 -21
- package/rust/core/lexer/handler/comment.rs +30 -30
- package/rust/core/lexer/handler/dot.rs +21 -21
- package/rust/core/lexer/handler/driver.rs +337 -337
- package/rust/core/lexer/handler/identifier.rs +47 -47
- package/rust/core/lexer/handler/indent.rs +66 -66
- package/rust/core/lexer/handler/mod.rs +15 -15
- package/rust/core/lexer/handler/newline.rs +23 -23
- package/rust/core/lexer/handler/number.rs +31 -31
- package/rust/core/lexer/handler/operator.rs +46 -46
- package/rust/core/lexer/handler/parenthesis.rs +41 -41
- package/rust/core/lexer/handler/slash.rs +21 -21
- package/rust/core/lexer/handler/string.rs +63 -63
- package/rust/core/lexer/mod.rs +3 -3
- package/rust/core/mod.rs +9 -9
- package/rust/core/parser/driver/block.rs +111 -111
- package/rust/core/parser/driver/cursor.rs +82 -82
- package/rust/core/parser/driver/driver_impl.rs +21 -1
- package/rust/core/parser/driver/mod.rs +6 -6
- package/rust/core/parser/driver/parse_array.rs +120 -120
- package/rust/core/parser/driver/parse_map.rs +247 -223
- package/rust/core/parser/driver/parser.rs +160 -160
- package/rust/core/parser/handler/arrow_call.rs +65 -14
- package/rust/core/parser/handler/identifier/synth.rs +171 -135
- package/rust/core/parser/handler/mod.rs +9 -9
- package/rust/core/parser/handler/pattern.rs +24 -1
- package/rust/core/plugin/loader.rs +137 -137
- package/rust/core/plugin/mod.rs +2 -2
- package/rust/core/plugin/runner/non_wasm.rs +481 -297
- package/rust/core/plugin/runner/wasm32.rs +1 -0
- package/rust/core/preprocessor/loader/inject.rs +313 -278
- package/rust/core/preprocessor/loader/loader_helpers.rs +110 -110
- package/rust/core/preprocessor/loader/mod.rs +235 -235
- package/rust/core/preprocessor/module.rs +55 -55
- package/rust/core/preprocessor/processor/handlers.rs +107 -107
- package/rust/core/preprocessor/resolver/bank.rs +49 -49
- package/rust/core/preprocessor/resolver/call.rs +124 -124
- package/rust/core/preprocessor/resolver/condition.rs +95 -95
- package/rust/core/preprocessor/resolver/driver.rs +324 -324
- package/rust/core/preprocessor/resolver/function.rs +69 -69
- package/rust/core/preprocessor/resolver/group.rs +122 -122
- package/rust/core/preprocessor/resolver/let_.rs +32 -32
- package/rust/core/preprocessor/resolver/loop_.rs +318 -318
- package/rust/core/preprocessor/resolver/mod.rs +16 -16
- package/rust/core/preprocessor/resolver/pattern.rs +95 -83
- package/rust/core/preprocessor/resolver/spawn.rs +99 -99
- package/rust/core/preprocessor/resolver/synth.rs +54 -54
- package/rust/core/preprocessor/resolver/tempo.rs +48 -48
- package/rust/core/preprocessor/resolver/trigger.rs +116 -116
- package/rust/core/preprocessor/resolver/value.rs +176 -176
- package/rust/core/store/global.rs +57 -57
- package/rust/lib.rs +323 -323
- package/rust/macros/Cargo.toml +14 -0
- package/rust/macros/src/lib.rs +52 -0
- package/rust/main.rs +311 -142
- package/rust/types/Cargo.toml +1 -1
- package/rust/types/src/addons.rs +3 -1
- package/rust/types/src/config.rs +1 -3
- package/rust/utils/Cargo.toml +5 -2
- package/rust/utils/src/file.rs +397 -14
- package/rust/utils/src/path.rs +31 -2
- package/rust/utils/src/version.rs +38 -7
- package/rust/web/auth.rs +5 -0
- package/rust/web/forge.rs +5 -0
- package/rust/web/mod.rs +5 -3
- package/typescript/scripts/version/copy-to-binary.ts +82 -0
- package/rust/cli/bank/api.rs +0 -122
- package/rust/cli/bank/commands.rs +0 -306
- package/rust/cli/bank/mod.rs +0 -29
- package/rust/cli/install/bank.rs +0 -72
- package/rust/cli/install/commands.rs +0 -35
- package/rust/cli/install/mod.rs +0 -4
- package/rust/cli/install/plugin.rs +0 -80
|
@@ -1,318 +1,318 @@
|
|
|
1
|
-
use std::collections::HashMap;
|
|
2
|
-
|
|
3
|
-
use crate::core::{
|
|
4
|
-
parser::statement::{Statement, StatementKind},
|
|
5
|
-
preprocessor::{
|
|
6
|
-
module::Module,
|
|
7
|
-
resolver::{driver::resolve_statement, value::resolve_value},
|
|
8
|
-
},
|
|
9
|
-
store::global::GlobalStore,
|
|
10
|
-
};
|
|
11
|
-
use devalang_types::Value;
|
|
12
|
-
use devalang_utils::logger::Logger;
|
|
13
|
-
|
|
14
|
-
pub fn resolve_loop(
|
|
15
|
-
stmt: &Statement,
|
|
16
|
-
module: &Module,
|
|
17
|
-
path: &str,
|
|
18
|
-
global_store: &mut GlobalStore,
|
|
19
|
-
) -> Statement {
|
|
20
|
-
let logger = Logger::new();
|
|
21
|
-
|
|
22
|
-
let resolved_value = resolve_value(&stmt.value, module, global_store);
|
|
23
|
-
|
|
24
|
-
let Value::Map(value_map) = &resolved_value else {
|
|
25
|
-
return error_stmt(&logger, module, stmt, "Expected a map for loop value");
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
let mut resolved_map: HashMap<String, Value> = HashMap::new();
|
|
29
|
-
for (key, val) in value_map {
|
|
30
|
-
resolved_map.insert(key.clone(), resolve_value(val, module, global_store));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Foreach form takes precedence if present
|
|
34
|
-
if let (Some(Value::Identifier(var_name)), Some(array_val)) =
|
|
35
|
-
(resolved_map.get("foreach"), resolved_map.get("array"))
|
|
36
|
-
{
|
|
37
|
-
// Normalize array_val into an iterable Array
|
|
38
|
-
let resolved_array = match array_val {
|
|
39
|
-
Value::Array(items) => Value::Array(
|
|
40
|
-
items
|
|
41
|
-
.iter()
|
|
42
|
-
.map(|v| resolve_value(v, module, global_store))
|
|
43
|
-
.collect(),
|
|
44
|
-
),
|
|
45
|
-
Value::Number(n) => {
|
|
46
|
-
// Iterate 0..n-1
|
|
47
|
-
let count = (*n).max(0.0) as usize;
|
|
48
|
-
let mut items = Vec::with_capacity(count);
|
|
49
|
-
for i in 0..count {
|
|
50
|
-
items.push(Value::Number(i as f32));
|
|
51
|
-
}
|
|
52
|
-
Value::Array(items)
|
|
53
|
-
}
|
|
54
|
-
Value::String(s) => {
|
|
55
|
-
// Try to parse a simple comma-separated list: "a,b,c" -> ["a","b","c"]
|
|
56
|
-
// If numeric string: iterate 0..n-1
|
|
57
|
-
if let Ok(n) = s.parse::<f32>() {
|
|
58
|
-
let count = n.max(0.0) as usize;
|
|
59
|
-
let mut items = Vec::with_capacity(count);
|
|
60
|
-
for i in 0..count {
|
|
61
|
-
items.push(Value::Number(i as f32));
|
|
62
|
-
}
|
|
63
|
-
Value::Array(items)
|
|
64
|
-
} else if s.contains(',') {
|
|
65
|
-
let parts: Vec<Value> = s
|
|
66
|
-
.split(',')
|
|
67
|
-
.map(|p| Value::String(p.trim().to_string()))
|
|
68
|
-
.collect();
|
|
69
|
-
Value::Array(parts)
|
|
70
|
-
} else {
|
|
71
|
-
// Fallback: iterate characters
|
|
72
|
-
let parts: Vec<Value> =
|
|
73
|
-
s.chars().map(|c| Value::String(c.to_string())).collect();
|
|
74
|
-
Value::Array(parts)
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
Value::Identifier(name) => {
|
|
78
|
-
// Resolve identifier from module variables (already resolved map above)
|
|
79
|
-
let v = if let Some(v) = module.variable_table.get(name) {
|
|
80
|
-
v.clone()
|
|
81
|
-
} else {
|
|
82
|
-
Value::Null
|
|
83
|
-
};
|
|
84
|
-
match v {
|
|
85
|
-
Value::Array(items) => Value::Array(
|
|
86
|
-
items
|
|
87
|
-
.iter()
|
|
88
|
-
.map(|v| resolve_value(v, module, global_store))
|
|
89
|
-
.collect(),
|
|
90
|
-
),
|
|
91
|
-
Value::Number(n) => {
|
|
92
|
-
let count = n.max(0.0) as usize;
|
|
93
|
-
let mut items = Vec::with_capacity(count);
|
|
94
|
-
for i in 0..count {
|
|
95
|
-
items.push(Value::Number(i as f32));
|
|
96
|
-
}
|
|
97
|
-
Value::Array(items)
|
|
98
|
-
}
|
|
99
|
-
Value::String(s) => {
|
|
100
|
-
if let Ok(n) = s.parse::<f32>() {
|
|
101
|
-
let count = n.max(0.0) as usize;
|
|
102
|
-
let mut items = Vec::with_capacity(count);
|
|
103
|
-
for i in 0..count {
|
|
104
|
-
items.push(Value::Number(i as f32));
|
|
105
|
-
}
|
|
106
|
-
Value::Array(items)
|
|
107
|
-
} else if s.contains(',') {
|
|
108
|
-
let parts: Vec<Value> = s
|
|
109
|
-
.split(',')
|
|
110
|
-
.map(|p| Value::String(p.trim().to_string()))
|
|
111
|
-
.collect();
|
|
112
|
-
Value::Array(parts)
|
|
113
|
-
} else {
|
|
114
|
-
let parts: Vec<Value> =
|
|
115
|
-
s.chars().map(|c| Value::String(c.to_string())).collect();
|
|
116
|
-
Value::Array(parts)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
other => {
|
|
120
|
-
error_value(
|
|
121
|
-
&logger,
|
|
122
|
-
module,
|
|
123
|
-
stmt,
|
|
124
|
-
&format!(
|
|
125
|
-
"Foreach identifier '{}' resolves to unsupported value: {:?}",
|
|
126
|
-
name, other
|
|
127
|
-
),
|
|
128
|
-
);
|
|
129
|
-
Value::Array(vec![])
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
other => {
|
|
134
|
-
// Resolve and normalize if possible
|
|
135
|
-
let v = resolve_value(other, module, global_store);
|
|
136
|
-
match v {
|
|
137
|
-
Value::Array(items) => Value::Array(items),
|
|
138
|
-
Value::Number(n) => {
|
|
139
|
-
let count = n.max(0.0) as usize;
|
|
140
|
-
let mut items = Vec::with_capacity(count);
|
|
141
|
-
for i in 0..count {
|
|
142
|
-
items.push(Value::Number(i as f32));
|
|
143
|
-
}
|
|
144
|
-
Value::Array(items)
|
|
145
|
-
}
|
|
146
|
-
Value::String(s) => {
|
|
147
|
-
if let Ok(n) = s.parse::<f32>() {
|
|
148
|
-
let count = n.max(0.0) as usize;
|
|
149
|
-
let mut items = Vec::with_capacity(count);
|
|
150
|
-
for i in 0..count {
|
|
151
|
-
items.push(Value::Number(i as f32));
|
|
152
|
-
}
|
|
153
|
-
Value::Array(items)
|
|
154
|
-
} else if s.contains(',') {
|
|
155
|
-
let parts: Vec<Value> = s
|
|
156
|
-
.split(',')
|
|
157
|
-
.map(|p| Value::String(p.trim().to_string()))
|
|
158
|
-
.collect();
|
|
159
|
-
Value::Array(parts)
|
|
160
|
-
} else {
|
|
161
|
-
let parts: Vec<Value> =
|
|
162
|
-
s.chars().map(|c| Value::String(c.to_string())).collect();
|
|
163
|
-
Value::Array(parts)
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
other => {
|
|
167
|
-
error_value(
|
|
168
|
-
&logger,
|
|
169
|
-
module,
|
|
170
|
-
stmt,
|
|
171
|
-
&format!("Unsupported foreach array value: {:?}", other),
|
|
172
|
-
);
|
|
173
|
-
Value::Array(vec![])
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
let body_value = match resolved_map.get("body") {
|
|
180
|
-
Some(Value::Block(stmts)) => {
|
|
181
|
-
let resolved = stmts
|
|
182
|
-
.iter()
|
|
183
|
-
.map(|s| resolve_statement(s, module, path, global_store))
|
|
184
|
-
.collect();
|
|
185
|
-
Value::Block(resolved)
|
|
186
|
-
}
|
|
187
|
-
_ => {
|
|
188
|
-
error_value(&logger, module, stmt, "Invalid or missing loop body");
|
|
189
|
-
Value::Block(vec![])
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
let mut final_map = HashMap::new();
|
|
194
|
-
final_map.insert("foreach".to_string(), Value::Identifier(var_name.clone()));
|
|
195
|
-
final_map.insert("array".to_string(), resolved_array);
|
|
196
|
-
final_map.insert("body".to_string(), body_value);
|
|
197
|
-
|
|
198
|
-
return Statement {
|
|
199
|
-
kind: StatementKind::Loop,
|
|
200
|
-
value: Value::Map(final_map),
|
|
201
|
-
..stmt.clone()
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
let iterator_value = match resolved_map.get("iterator") {
|
|
206
|
-
Some(Value::Number(n)) => Value::Number(*n),
|
|
207
|
-
Some(Value::String(s)) => {
|
|
208
|
-
if let Ok(n) = s.parse::<f32>() {
|
|
209
|
-
Value::Number(n)
|
|
210
|
-
} else {
|
|
211
|
-
error_value(
|
|
212
|
-
&logger,
|
|
213
|
-
module,
|
|
214
|
-
stmt,
|
|
215
|
-
&format!("Loop iterator string not numeric: '{}'", s),
|
|
216
|
-
);
|
|
217
|
-
Value::Number(1.0)
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
Some(Value::Identifier(name)) => {
|
|
221
|
-
// Try resolving from module vars (may be number or numeric string)
|
|
222
|
-
if let Some(v) = module.variable_table.get(name) {
|
|
223
|
-
match v {
|
|
224
|
-
Value::Number(n) => Value::Number(*n),
|
|
225
|
-
Value::String(s) => {
|
|
226
|
-
if let Ok(n) = s.parse::<f32>() {
|
|
227
|
-
Value::Number(n)
|
|
228
|
-
} else {
|
|
229
|
-
error_value(
|
|
230
|
-
&logger,
|
|
231
|
-
module,
|
|
232
|
-
stmt,
|
|
233
|
-
&format!(
|
|
234
|
-
"Loop iterator '{}' resolves to non-numeric string: '{}'",
|
|
235
|
-
name, s
|
|
236
|
-
),
|
|
237
|
-
);
|
|
238
|
-
Value::Number(1.0)
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
other => {
|
|
242
|
-
error_value(
|
|
243
|
-
&logger,
|
|
244
|
-
module,
|
|
245
|
-
stmt,
|
|
246
|
-
&format!(
|
|
247
|
-
"Loop iterator '{}' resolves to non-number: {:?}",
|
|
248
|
-
name, other
|
|
249
|
-
),
|
|
250
|
-
);
|
|
251
|
-
Value::Number(1.0)
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
} else {
|
|
255
|
-
error_value(
|
|
256
|
-
&logger,
|
|
257
|
-
module,
|
|
258
|
-
stmt,
|
|
259
|
-
&format!("Loop iterator identifier '{}' not found", name),
|
|
260
|
-
);
|
|
261
|
-
Value::Number(1.0)
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
Some(other) => {
|
|
265
|
-
error_value(
|
|
266
|
-
&logger,
|
|
267
|
-
module,
|
|
268
|
-
stmt,
|
|
269
|
-
&format!("Loop iterator must be a number, found: {:?}", other),
|
|
270
|
-
);
|
|
271
|
-
Value::Number(1.0)
|
|
272
|
-
}
|
|
273
|
-
None => {
|
|
274
|
-
error_value(&logger, module, stmt, "Missing 'iterator' in loop");
|
|
275
|
-
Value::Number(1.0)
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
let body_value = match resolved_map.get("body") {
|
|
280
|
-
Some(Value::Block(stmts)) => {
|
|
281
|
-
let resolved = stmts
|
|
282
|
-
.iter()
|
|
283
|
-
.map(|s| resolve_statement(s, module, path, global_store))
|
|
284
|
-
.collect();
|
|
285
|
-
Value::Block(resolved)
|
|
286
|
-
}
|
|
287
|
-
_ => {
|
|
288
|
-
error_value(&logger, module, stmt, "Invalid or missing loop body");
|
|
289
|
-
Value::Block(vec![])
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
let mut final_map = HashMap::new();
|
|
294
|
-
final_map.insert("iterator".to_string(), iterator_value);
|
|
295
|
-
final_map.insert("body".to_string(), body_value);
|
|
296
|
-
|
|
297
|
-
Statement {
|
|
298
|
-
kind: StatementKind::Loop,
|
|
299
|
-
value: Value::Map(final_map),
|
|
300
|
-
..stmt.clone()
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
fn error_value(logger: &Logger, module: &Module, stmt: &Statement, msg: &str) {
|
|
305
|
-
let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
|
|
306
|
-
logger.log_error_with_stacktrace(msg, &stacktrace);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
fn error_stmt(logger: &Logger, module: &Module, stmt: &Statement, msg: &str) -> Statement {
|
|
310
|
-
error_value(logger, module, stmt, msg);
|
|
311
|
-
Statement {
|
|
312
|
-
kind: StatementKind::Error {
|
|
313
|
-
message: msg.to_string(),
|
|
314
|
-
},
|
|
315
|
-
value: Value::Null,
|
|
316
|
-
..stmt.clone()
|
|
317
|
-
}
|
|
318
|
-
}
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
|
|
3
|
+
use crate::core::{
|
|
4
|
+
parser::statement::{Statement, StatementKind},
|
|
5
|
+
preprocessor::{
|
|
6
|
+
module::Module,
|
|
7
|
+
resolver::{driver::resolve_statement, value::resolve_value},
|
|
8
|
+
},
|
|
9
|
+
store::global::GlobalStore,
|
|
10
|
+
};
|
|
11
|
+
use devalang_types::Value;
|
|
12
|
+
use devalang_utils::logger::Logger;
|
|
13
|
+
|
|
14
|
+
pub fn resolve_loop(
|
|
15
|
+
stmt: &Statement,
|
|
16
|
+
module: &Module,
|
|
17
|
+
path: &str,
|
|
18
|
+
global_store: &mut GlobalStore,
|
|
19
|
+
) -> Statement {
|
|
20
|
+
let logger = Logger::new();
|
|
21
|
+
|
|
22
|
+
let resolved_value = resolve_value(&stmt.value, module, global_store);
|
|
23
|
+
|
|
24
|
+
let Value::Map(value_map) = &resolved_value else {
|
|
25
|
+
return error_stmt(&logger, module, stmt, "Expected a map for loop value");
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
let mut resolved_map: HashMap<String, Value> = HashMap::new();
|
|
29
|
+
for (key, val) in value_map {
|
|
30
|
+
resolved_map.insert(key.clone(), resolve_value(val, module, global_store));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Foreach form takes precedence if present
|
|
34
|
+
if let (Some(Value::Identifier(var_name)), Some(array_val)) =
|
|
35
|
+
(resolved_map.get("foreach"), resolved_map.get("array"))
|
|
36
|
+
{
|
|
37
|
+
// Normalize array_val into an iterable Array
|
|
38
|
+
let resolved_array = match array_val {
|
|
39
|
+
Value::Array(items) => Value::Array(
|
|
40
|
+
items
|
|
41
|
+
.iter()
|
|
42
|
+
.map(|v| resolve_value(v, module, global_store))
|
|
43
|
+
.collect(),
|
|
44
|
+
),
|
|
45
|
+
Value::Number(n) => {
|
|
46
|
+
// Iterate 0..n-1
|
|
47
|
+
let count = (*n).max(0.0) as usize;
|
|
48
|
+
let mut items = Vec::with_capacity(count);
|
|
49
|
+
for i in 0..count {
|
|
50
|
+
items.push(Value::Number(i as f32));
|
|
51
|
+
}
|
|
52
|
+
Value::Array(items)
|
|
53
|
+
}
|
|
54
|
+
Value::String(s) => {
|
|
55
|
+
// Try to parse a simple comma-separated list: "a,b,c" -> ["a","b","c"]
|
|
56
|
+
// If numeric string: iterate 0..n-1
|
|
57
|
+
if let Ok(n) = s.parse::<f32>() {
|
|
58
|
+
let count = n.max(0.0) as usize;
|
|
59
|
+
let mut items = Vec::with_capacity(count);
|
|
60
|
+
for i in 0..count {
|
|
61
|
+
items.push(Value::Number(i as f32));
|
|
62
|
+
}
|
|
63
|
+
Value::Array(items)
|
|
64
|
+
} else if s.contains(',') {
|
|
65
|
+
let parts: Vec<Value> = s
|
|
66
|
+
.split(',')
|
|
67
|
+
.map(|p| Value::String(p.trim().to_string()))
|
|
68
|
+
.collect();
|
|
69
|
+
Value::Array(parts)
|
|
70
|
+
} else {
|
|
71
|
+
// Fallback: iterate characters
|
|
72
|
+
let parts: Vec<Value> =
|
|
73
|
+
s.chars().map(|c| Value::String(c.to_string())).collect();
|
|
74
|
+
Value::Array(parts)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
Value::Identifier(name) => {
|
|
78
|
+
// Resolve identifier from module variables (already resolved map above)
|
|
79
|
+
let v = if let Some(v) = module.variable_table.get(name) {
|
|
80
|
+
v.clone()
|
|
81
|
+
} else {
|
|
82
|
+
Value::Null
|
|
83
|
+
};
|
|
84
|
+
match v {
|
|
85
|
+
Value::Array(items) => Value::Array(
|
|
86
|
+
items
|
|
87
|
+
.iter()
|
|
88
|
+
.map(|v| resolve_value(v, module, global_store))
|
|
89
|
+
.collect(),
|
|
90
|
+
),
|
|
91
|
+
Value::Number(n) => {
|
|
92
|
+
let count = n.max(0.0) as usize;
|
|
93
|
+
let mut items = Vec::with_capacity(count);
|
|
94
|
+
for i in 0..count {
|
|
95
|
+
items.push(Value::Number(i as f32));
|
|
96
|
+
}
|
|
97
|
+
Value::Array(items)
|
|
98
|
+
}
|
|
99
|
+
Value::String(s) => {
|
|
100
|
+
if let Ok(n) = s.parse::<f32>() {
|
|
101
|
+
let count = n.max(0.0) as usize;
|
|
102
|
+
let mut items = Vec::with_capacity(count);
|
|
103
|
+
for i in 0..count {
|
|
104
|
+
items.push(Value::Number(i as f32));
|
|
105
|
+
}
|
|
106
|
+
Value::Array(items)
|
|
107
|
+
} else if s.contains(',') {
|
|
108
|
+
let parts: Vec<Value> = s
|
|
109
|
+
.split(',')
|
|
110
|
+
.map(|p| Value::String(p.trim().to_string()))
|
|
111
|
+
.collect();
|
|
112
|
+
Value::Array(parts)
|
|
113
|
+
} else {
|
|
114
|
+
let parts: Vec<Value> =
|
|
115
|
+
s.chars().map(|c| Value::String(c.to_string())).collect();
|
|
116
|
+
Value::Array(parts)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
other => {
|
|
120
|
+
error_value(
|
|
121
|
+
&logger,
|
|
122
|
+
module,
|
|
123
|
+
stmt,
|
|
124
|
+
&format!(
|
|
125
|
+
"Foreach identifier '{}' resolves to unsupported value: {:?}",
|
|
126
|
+
name, other
|
|
127
|
+
),
|
|
128
|
+
);
|
|
129
|
+
Value::Array(vec![])
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
other => {
|
|
134
|
+
// Resolve and normalize if possible
|
|
135
|
+
let v = resolve_value(other, module, global_store);
|
|
136
|
+
match v {
|
|
137
|
+
Value::Array(items) => Value::Array(items),
|
|
138
|
+
Value::Number(n) => {
|
|
139
|
+
let count = n.max(0.0) as usize;
|
|
140
|
+
let mut items = Vec::with_capacity(count);
|
|
141
|
+
for i in 0..count {
|
|
142
|
+
items.push(Value::Number(i as f32));
|
|
143
|
+
}
|
|
144
|
+
Value::Array(items)
|
|
145
|
+
}
|
|
146
|
+
Value::String(s) => {
|
|
147
|
+
if let Ok(n) = s.parse::<f32>() {
|
|
148
|
+
let count = n.max(0.0) as usize;
|
|
149
|
+
let mut items = Vec::with_capacity(count);
|
|
150
|
+
for i in 0..count {
|
|
151
|
+
items.push(Value::Number(i as f32));
|
|
152
|
+
}
|
|
153
|
+
Value::Array(items)
|
|
154
|
+
} else if s.contains(',') {
|
|
155
|
+
let parts: Vec<Value> = s
|
|
156
|
+
.split(',')
|
|
157
|
+
.map(|p| Value::String(p.trim().to_string()))
|
|
158
|
+
.collect();
|
|
159
|
+
Value::Array(parts)
|
|
160
|
+
} else {
|
|
161
|
+
let parts: Vec<Value> =
|
|
162
|
+
s.chars().map(|c| Value::String(c.to_string())).collect();
|
|
163
|
+
Value::Array(parts)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
other => {
|
|
167
|
+
error_value(
|
|
168
|
+
&logger,
|
|
169
|
+
module,
|
|
170
|
+
stmt,
|
|
171
|
+
&format!("Unsupported foreach array value: {:?}", other),
|
|
172
|
+
);
|
|
173
|
+
Value::Array(vec![])
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
let body_value = match resolved_map.get("body") {
|
|
180
|
+
Some(Value::Block(stmts)) => {
|
|
181
|
+
let resolved = stmts
|
|
182
|
+
.iter()
|
|
183
|
+
.map(|s| resolve_statement(s, module, path, global_store))
|
|
184
|
+
.collect();
|
|
185
|
+
Value::Block(resolved)
|
|
186
|
+
}
|
|
187
|
+
_ => {
|
|
188
|
+
error_value(&logger, module, stmt, "Invalid or missing loop body");
|
|
189
|
+
Value::Block(vec![])
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
let mut final_map = HashMap::new();
|
|
194
|
+
final_map.insert("foreach".to_string(), Value::Identifier(var_name.clone()));
|
|
195
|
+
final_map.insert("array".to_string(), resolved_array);
|
|
196
|
+
final_map.insert("body".to_string(), body_value);
|
|
197
|
+
|
|
198
|
+
return Statement {
|
|
199
|
+
kind: StatementKind::Loop,
|
|
200
|
+
value: Value::Map(final_map),
|
|
201
|
+
..stmt.clone()
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let iterator_value = match resolved_map.get("iterator") {
|
|
206
|
+
Some(Value::Number(n)) => Value::Number(*n),
|
|
207
|
+
Some(Value::String(s)) => {
|
|
208
|
+
if let Ok(n) = s.parse::<f32>() {
|
|
209
|
+
Value::Number(n)
|
|
210
|
+
} else {
|
|
211
|
+
error_value(
|
|
212
|
+
&logger,
|
|
213
|
+
module,
|
|
214
|
+
stmt,
|
|
215
|
+
&format!("Loop iterator string not numeric: '{}'", s),
|
|
216
|
+
);
|
|
217
|
+
Value::Number(1.0)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
Some(Value::Identifier(name)) => {
|
|
221
|
+
// Try resolving from module vars (may be number or numeric string)
|
|
222
|
+
if let Some(v) = module.variable_table.get(name) {
|
|
223
|
+
match v {
|
|
224
|
+
Value::Number(n) => Value::Number(*n),
|
|
225
|
+
Value::String(s) => {
|
|
226
|
+
if let Ok(n) = s.parse::<f32>() {
|
|
227
|
+
Value::Number(n)
|
|
228
|
+
} else {
|
|
229
|
+
error_value(
|
|
230
|
+
&logger,
|
|
231
|
+
module,
|
|
232
|
+
stmt,
|
|
233
|
+
&format!(
|
|
234
|
+
"Loop iterator '{}' resolves to non-numeric string: '{}'",
|
|
235
|
+
name, s
|
|
236
|
+
),
|
|
237
|
+
);
|
|
238
|
+
Value::Number(1.0)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
other => {
|
|
242
|
+
error_value(
|
|
243
|
+
&logger,
|
|
244
|
+
module,
|
|
245
|
+
stmt,
|
|
246
|
+
&format!(
|
|
247
|
+
"Loop iterator '{}' resolves to non-number: {:?}",
|
|
248
|
+
name, other
|
|
249
|
+
),
|
|
250
|
+
);
|
|
251
|
+
Value::Number(1.0)
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
error_value(
|
|
256
|
+
&logger,
|
|
257
|
+
module,
|
|
258
|
+
stmt,
|
|
259
|
+
&format!("Loop iterator identifier '{}' not found", name),
|
|
260
|
+
);
|
|
261
|
+
Value::Number(1.0)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
Some(other) => {
|
|
265
|
+
error_value(
|
|
266
|
+
&logger,
|
|
267
|
+
module,
|
|
268
|
+
stmt,
|
|
269
|
+
&format!("Loop iterator must be a number, found: {:?}", other),
|
|
270
|
+
);
|
|
271
|
+
Value::Number(1.0)
|
|
272
|
+
}
|
|
273
|
+
None => {
|
|
274
|
+
error_value(&logger, module, stmt, "Missing 'iterator' in loop");
|
|
275
|
+
Value::Number(1.0)
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
let body_value = match resolved_map.get("body") {
|
|
280
|
+
Some(Value::Block(stmts)) => {
|
|
281
|
+
let resolved = stmts
|
|
282
|
+
.iter()
|
|
283
|
+
.map(|s| resolve_statement(s, module, path, global_store))
|
|
284
|
+
.collect();
|
|
285
|
+
Value::Block(resolved)
|
|
286
|
+
}
|
|
287
|
+
_ => {
|
|
288
|
+
error_value(&logger, module, stmt, "Invalid or missing loop body");
|
|
289
|
+
Value::Block(vec![])
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
let mut final_map = HashMap::new();
|
|
294
|
+
final_map.insert("iterator".to_string(), iterator_value);
|
|
295
|
+
final_map.insert("body".to_string(), body_value);
|
|
296
|
+
|
|
297
|
+
Statement {
|
|
298
|
+
kind: StatementKind::Loop,
|
|
299
|
+
value: Value::Map(final_map),
|
|
300
|
+
..stmt.clone()
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
fn error_value(logger: &Logger, module: &Module, stmt: &Statement, msg: &str) {
|
|
305
|
+
let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
|
|
306
|
+
logger.log_error_with_stacktrace(msg, &stacktrace);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
fn error_stmt(logger: &Logger, module: &Module, stmt: &Statement, msg: &str) -> Statement {
|
|
310
|
+
error_value(logger, module, stmt, msg);
|
|
311
|
+
Statement {
|
|
312
|
+
kind: StatementKind::Error {
|
|
313
|
+
message: msg.to_string(),
|
|
314
|
+
},
|
|
315
|
+
value: Value::Null,
|
|
316
|
+
..stmt.clone()
|
|
317
|
+
}
|
|
318
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
pub mod driver;
|
|
2
|
-
|
|
3
|
-
pub mod value;
|
|
4
|
-
|
|
5
|
-
pub mod bank;
|
|
6
|
-
pub mod call;
|
|
7
|
-
pub mod condition;
|
|
8
|
-
pub mod function;
|
|
9
|
-
pub mod group;
|
|
10
|
-
pub mod let_;
|
|
11
|
-
pub mod loop_;
|
|
12
|
-
pub mod pattern;
|
|
13
|
-
pub mod spawn;
|
|
14
|
-
pub mod synth;
|
|
15
|
-
pub mod tempo;
|
|
16
|
-
pub mod trigger;
|
|
1
|
+
pub mod driver;
|
|
2
|
+
|
|
3
|
+
pub mod value;
|
|
4
|
+
|
|
5
|
+
pub mod bank;
|
|
6
|
+
pub mod call;
|
|
7
|
+
pub mod condition;
|
|
8
|
+
pub mod function;
|
|
9
|
+
pub mod group;
|
|
10
|
+
pub mod let_;
|
|
11
|
+
pub mod loop_;
|
|
12
|
+
pub mod pattern;
|
|
13
|
+
pub mod spawn;
|
|
14
|
+
pub mod synth;
|
|
15
|
+
pub mod tempo;
|
|
16
|
+
pub mod trigger;
|