@devaloop/devalang 0.0.1-alpha.13 → 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 +2 -3
- package/Cargo.toml +58 -54
- package/README.md +59 -27
- package/docs/CHANGELOG.md +99 -2
- package/docs/CONTRIBUTING.md +1 -0
- package/docs/ROADMAP.md +3 -3
- package/docs/TODO.md +5 -4
- package/examples/automation.deva +44 -0
- package/examples/bank.deva +2 -4
- package/examples/function.deva +15 -0
- package/examples/index.deva +41 -11
- package/examples/plugin.deva +15 -0
- package/out-tsc/bin/devalang.exe +0 -0
- package/package.json +6 -6
- package/project-version.json +3 -3
- package/rust/cli/bank.rs +16 -16
- package/rust/cli/build.rs +69 -30
- package/rust/cli/check.rs +46 -6
- package/rust/cli/driver.rs +40 -28
- package/rust/cli/install.rs +22 -7
- package/rust/cli/login.rs +134 -0
- package/rust/cli/mod.rs +2 -1
- package/rust/cli/play.rs +44 -19
- package/rust/cli/update.rs +1 -1
- package/rust/common/api.rs +5 -0
- package/rust/common/cdn.rs +3 -9
- package/rust/common/mod.rs +3 -1
- package/rust/common/sso.rs +5 -0
- package/rust/config/driver.rs +19 -1
- package/rust/config/loader.rs +56 -10
- package/rust/core/audio/engine.rs +314 -63
- package/rust/core/audio/evaluator.rs +101 -0
- package/rust/core/audio/interpreter/arrow_call.rs +60 -15
- package/rust/core/audio/interpreter/automate.rs +18 -0
- package/rust/core/audio/interpreter/call.rs +4 -4
- package/rust/core/audio/interpreter/condition.rs +3 -3
- package/rust/core/audio/interpreter/driver.rs +68 -30
- 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 +4 -4
- 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/builder/mod.rs +11 -6
- 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/indent.rs +16 -2
- 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 +4 -0
- package/rust/core/mod.rs +2 -1
- package/rust/core/parser/driver.rs +134 -11
- package/rust/core/parser/handler/arrow_call.rs +141 -65
- package/rust/core/parser/handler/at.rs +1 -1
- package/rust/core/parser/handler/bank.rs +35 -7
- package/rust/core/parser/handler/dot.rs +43 -22
- 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/plugin/loader.rs +48 -0
- package/rust/core/plugin/mod.rs +1 -0
- package/rust/core/preprocessor/loader.rs +7 -5
- 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/addon.rs +80 -0
- package/rust/installer/bank.rs +25 -15
- package/rust/installer/mod.rs +4 -1
- package/rust/installer/plugin.rs +55 -0
- package/rust/main.rs +32 -10
- package/rust/utils/error.rs +51 -0
- package/rust/utils/logger.rs +20 -0
- package/rust/utils/mod.rs +1 -44
- package/rust/utils/spinner.rs +3 -5
|
@@ -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,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
use std::path::Path;
|
|
2
|
+
use serde::Deserialize;
|
|
3
|
+
|
|
4
|
+
#[derive(Debug, Deserialize, Clone)]
|
|
5
|
+
pub struct PluginInfo {
|
|
6
|
+
pub name: String,
|
|
7
|
+
pub version: Option<String>,
|
|
8
|
+
pub description: Option<String>,
|
|
9
|
+
pub author: Option<String>,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
#[derive(Debug, Deserialize, Clone)]
|
|
13
|
+
pub struct PluginFile {
|
|
14
|
+
pub plugin: PluginInfo,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
pub fn load_plugin(name: &str) -> Result<(PluginInfo, Vec<u8>), String> {
|
|
18
|
+
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
|
|
19
|
+
let plugin_dir = root.join(".deva").join("plugin").join(name);
|
|
20
|
+
let toml_path = plugin_dir.join("plugin.toml");
|
|
21
|
+
let wasm_path = plugin_dir.join(format!("{}_bg.wasm", name));
|
|
22
|
+
|
|
23
|
+
if !toml_path.exists() {
|
|
24
|
+
return Err(format!("❌ Plugin file not found: {}", toml_path.display()));
|
|
25
|
+
}
|
|
26
|
+
if !wasm_path.exists() {
|
|
27
|
+
return Err(format!("❌ Plugin wasm not found: {}", wasm_path.display()));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let toml_content = std::fs::read_to_string(&toml_path)
|
|
31
|
+
.map_err(|e| format!("Failed to read '{}': {}", toml_path.display(), e))?;
|
|
32
|
+
let plugin_file: PluginFile = toml::from_str(&toml_content)
|
|
33
|
+
.map_err(|e| format!("Failed to parse '{}': {}", toml_path.display(), e))?;
|
|
34
|
+
|
|
35
|
+
let wasm_bytes = std::fs::read(&wasm_path)
|
|
36
|
+
.map_err(|e| format!("Failed to read '{}': {}", wasm_path.display(), e))?;
|
|
37
|
+
|
|
38
|
+
Ok((plugin_file.plugin, wasm_bytes))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
pub fn load_plugin_from_uri(uri: &str) -> Result<(PluginInfo, Vec<u8>), String> {
|
|
42
|
+
if !uri.starts_with("devalang://plugin/") {
|
|
43
|
+
return Err("Invalid plugin URI".into());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let name = uri.trim_start_matches("devalang://plugin/");
|
|
47
|
+
load_plugin(name)
|
|
48
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pub mod loader;
|
|
@@ -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
|
|
@@ -254,6 +254,8 @@ impl ModuleLoader {
|
|
|
254
254
|
module: &mut Module,
|
|
255
255
|
bank_name: &str
|
|
256
256
|
) -> Result<Module, String> {
|
|
257
|
+
let alias = bank_name.split('.').last().unwrap_or(bank_name);
|
|
258
|
+
|
|
257
259
|
let bank_path = Path::new("./.deva/bank").join(bank_name);
|
|
258
260
|
let bank_toml_path = bank_path.join("bank.toml");
|
|
259
261
|
|
|
@@ -277,23 +279,23 @@ impl ModuleLoader {
|
|
|
277
279
|
|
|
278
280
|
bank_map.insert(bank_trigger.name.clone(), Value::String(bank_trigger_path.clone()));
|
|
279
281
|
|
|
280
|
-
if module.variable_table.variables.contains_key(
|
|
282
|
+
if module.variable_table.variables.contains_key(alias) {
|
|
281
283
|
eprintln!(
|
|
282
284
|
"⚠️ Trigger '{}' already defined in module '{}', skipping injection.",
|
|
283
|
-
|
|
285
|
+
alias,
|
|
284
286
|
module.path
|
|
285
287
|
);
|
|
286
288
|
continue;
|
|
287
289
|
}
|
|
288
290
|
|
|
289
291
|
module.variable_table.set(
|
|
290
|
-
format!("{}.{}",
|
|
292
|
+
format!("{}.{}", alias, bank_trigger.name),
|
|
291
293
|
Value::String(bank_trigger_path.clone())
|
|
292
294
|
);
|
|
293
295
|
}
|
|
294
296
|
|
|
295
297
|
// Inject the map under the bank name
|
|
296
|
-
module.variable_table.set(
|
|
298
|
+
module.variable_table.set(alias.to_string(), Value::Map(bank_map));
|
|
297
299
|
|
|
298
300
|
Ok(module.clone())
|
|
299
301
|
}
|
|
@@ -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
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
use std::path::Path;
|
|
2
|
+
use crate::{
|
|
3
|
+
common::{ api::get_api_url },
|
|
4
|
+
installer::{ bank::install_bank, plugin::install_plugin },
|
|
5
|
+
};
|
|
6
|
+
use dirs::home_dir;
|
|
7
|
+
|
|
8
|
+
#[derive(Debug, Clone)]
|
|
9
|
+
pub enum AddonType {
|
|
10
|
+
Bank,
|
|
11
|
+
Plugin,
|
|
12
|
+
Preset,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
pub async fn install_addon(
|
|
16
|
+
addon_type: AddonType,
|
|
17
|
+
name: &str,
|
|
18
|
+
target_dir: &Path
|
|
19
|
+
) -> Result<(), String> {
|
|
20
|
+
match addon_type {
|
|
21
|
+
AddonType::Bank => install_bank(name, target_dir).await,
|
|
22
|
+
AddonType::Plugin => install_plugin(name, target_dir).await,
|
|
23
|
+
AddonType::Preset => Err("Preset installation not implemented".into()),
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
pub async fn ask_api_for_signed_url(addon_type: AddonType, slug: &str) -> Result<String, String> {
|
|
28
|
+
let api_url = get_api_url();
|
|
29
|
+
|
|
30
|
+
let mut stored_token_path = home_dir().unwrap();
|
|
31
|
+
stored_token_path.push(".devalang");
|
|
32
|
+
stored_token_path.push("session_token.json");
|
|
33
|
+
|
|
34
|
+
let stored_token = std::fs::read_to_string(&stored_token_path).unwrap_or_default();
|
|
35
|
+
|
|
36
|
+
let request_url = format!(
|
|
37
|
+
"{}/v1/assets/url?type={}&slug={}&token={}",
|
|
38
|
+
api_url,
|
|
39
|
+
match addon_type {
|
|
40
|
+
AddonType::Bank => "bank",
|
|
41
|
+
AddonType::Plugin => "plugin",
|
|
42
|
+
AddonType::Preset => "preset",
|
|
43
|
+
},
|
|
44
|
+
slug,
|
|
45
|
+
stored_token
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
let mut headers = reqwest::header::HeaderMap::new();
|
|
49
|
+
|
|
50
|
+
headers.insert("Authorization", format!("Bearer {}", stored_token).parse().unwrap());
|
|
51
|
+
|
|
52
|
+
let client: reqwest::Client = reqwest::Client
|
|
53
|
+
::builder()
|
|
54
|
+
.default_headers(headers)
|
|
55
|
+
.build()
|
|
56
|
+
.map_err(|_| "Failed to build HTTP client".to_string())?;
|
|
57
|
+
|
|
58
|
+
let req = client
|
|
59
|
+
.get(&request_url)
|
|
60
|
+
.send().await
|
|
61
|
+
.map_err(|_| "Failed to receive response".to_string())?;
|
|
62
|
+
|
|
63
|
+
let response_body: serde_json::Value = req
|
|
64
|
+
.json().await
|
|
65
|
+
.map_err(|_| "Failed to read response body".to_string())?;
|
|
66
|
+
|
|
67
|
+
let signed_url: String = serde_json
|
|
68
|
+
::from_value(
|
|
69
|
+
response_body
|
|
70
|
+
.get("payload")
|
|
71
|
+
.cloned()
|
|
72
|
+
.unwrap_or_default()
|
|
73
|
+
.get("url")
|
|
74
|
+
.cloned()
|
|
75
|
+
.unwrap_or_default()
|
|
76
|
+
)
|
|
77
|
+
.map_err(|_| "Failed to parse response body".to_string())?;
|
|
78
|
+
|
|
79
|
+
Ok(signed_url)
|
|
80
|
+
}
|
package/rust/installer/bank.rs
CHANGED
|
@@ -1,33 +1,39 @@
|
|
|
1
1
|
use std::path::{ Path, PathBuf };
|
|
2
2
|
use crate::{
|
|
3
|
-
common::cdn::get_cdn_url,
|
|
4
3
|
config::loader::{ add_bank_to_config, load_config },
|
|
5
|
-
installer::
|
|
4
|
+
installer::{
|
|
5
|
+
addon::{ ask_api_for_signed_url, AddonType },
|
|
6
|
+
utils::{ download_file, extract_archive },
|
|
7
|
+
},
|
|
8
|
+
utils::logger::{ LogLevel, Logger },
|
|
6
9
|
};
|
|
7
10
|
|
|
8
11
|
pub async fn install_bank(name: &str, target_dir: &Path) -> Result<(), String> {
|
|
9
|
-
let
|
|
10
|
-
|
|
12
|
+
let logger = Logger::new();
|
|
13
|
+
|
|
14
|
+
let signed_url = ask_api_for_signed_url(AddonType::Bank, name).await?;
|
|
11
15
|
|
|
12
16
|
let bank_dir = target_dir.join("bank");
|
|
13
17
|
let archive_path = PathBuf::from(format!("./.deva/tmp/{}.devabank", name));
|
|
14
18
|
let extract_path = bank_dir.join(name);
|
|
15
19
|
|
|
20
|
+
download_file(&signed_url, &archive_path).await.map_err(|e|
|
|
21
|
+
format!("Failed to download: {}", e)
|
|
22
|
+
)?;
|
|
23
|
+
|
|
16
24
|
if extract_path.exists() {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
logger.log_message(
|
|
26
|
+
LogLevel::Warning,
|
|
27
|
+
&format!(
|
|
28
|
+
"Bank '{}' already exists at '{}'. Skipping install.",
|
|
29
|
+
name,
|
|
30
|
+
extract_path.display()
|
|
31
|
+
)
|
|
21
32
|
);
|
|
33
|
+
|
|
22
34
|
return Ok(());
|
|
23
35
|
}
|
|
24
36
|
|
|
25
|
-
download_file(&url, &archive_path).await.map_err(|e| format!("Failed to download: {}", e))?;
|
|
26
|
-
|
|
27
|
-
extract_archive(&archive_path, &extract_path).await.map_err(|e|
|
|
28
|
-
format!("Failed to extract: {}", e)
|
|
29
|
-
)?;
|
|
30
|
-
|
|
31
37
|
// Add the bank to the config
|
|
32
38
|
let root_dir = target_dir
|
|
33
39
|
.parent()
|
|
@@ -49,7 +55,11 @@ pub async fn install_bank(name: &str, target_dir: &Path) -> Result<(), String> {
|
|
|
49
55
|
|
|
50
56
|
let dependency_path = &format!("devalang://bank/{}", name);
|
|
51
57
|
|
|
52
|
-
|
|
58
|
+
extract_archive(&archive_path, &extract_path).await.map_err(|e|
|
|
59
|
+
format!("Failed to extract: {}", e)
|
|
60
|
+
)?;
|
|
61
|
+
|
|
62
|
+
add_bank_to_config(&mut config, &extract_path, dependency_path);
|
|
53
63
|
|
|
54
64
|
Ok(())
|
|
55
65
|
}
|
package/rust/installer/mod.rs
CHANGED