@devaloop/devalang 0.0.1-alpha.12 → 0.0.1-alpha.14
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 -9
- package/Cargo.toml +8 -3
- package/README.md +36 -34
- package/docs/CHANGELOG.md +65 -1
- package/docs/CONTRIBUTING.md +1 -0
- package/docs/ROADMAP.md +2 -2
- package/docs/TODO.md +6 -5
- package/examples/bank.deva +2 -4
- package/examples/function.deva +15 -0
- package/examples/index.deva +25 -14
- package/out-tsc/bin/devalang.exe +0 -0
- package/package.json +6 -6
- package/project-version.json +3 -3
- package/rust/cli/bank.rs +2 -1
- package/rust/cli/build.rs +76 -14
- package/rust/cli/check.rs +71 -8
- 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 +45 -20
- package/rust/common/api.rs +8 -0
- package/rust/common/cdn.rs +2 -5
- package/rust/common/mod.rs +3 -1
- package/rust/common/sso.rs +8 -0
- package/rust/config/driver.rs +19 -1
- package/rust/config/loader.rs +56 -10
- package/rust/core/audio/engine.rs +254 -91
- package/rust/core/audio/interpreter/arrow_call.rs +34 -15
- package/rust/core/audio/interpreter/call.rs +72 -47
- package/rust/core/audio/interpreter/condition.rs +14 -12
- package/rust/core/audio/interpreter/driver.rs +90 -128
- package/rust/core/audio/interpreter/function.rs +21 -0
- package/rust/core/audio/interpreter/load.rs +1 -1
- package/rust/core/audio/interpreter/loop_.rs +24 -18
- package/rust/core/audio/interpreter/mod.rs +2 -1
- package/rust/core/audio/interpreter/sleep.rs +0 -6
- package/rust/core/audio/interpreter/spawn.rs +78 -60
- package/rust/core/audio/interpreter/trigger.rs +157 -70
- package/rust/core/audio/loader/trigger.rs +37 -4
- package/rust/core/audio/player.rs +20 -10
- package/rust/core/audio/renderer.rs +24 -25
- package/rust/core/builder/mod.rs +11 -6
- package/rust/core/debugger/mod.rs +2 -0
- package/rust/core/debugger/module.rs +47 -0
- package/rust/core/debugger/store.rs +25 -11
- package/rust/core/error/mod.rs +6 -0
- package/rust/core/lexer/handler/driver.rs +23 -1
- package/rust/core/lexer/handler/identifier.rs +1 -0
- package/rust/core/lexer/handler/indent.rs +16 -2
- package/rust/core/lexer/handler/mod.rs +1 -0
- package/rust/core/lexer/handler/parenthesis.rs +41 -0
- package/rust/core/lexer/token.rs +4 -0
- package/rust/core/mod.rs +2 -1
- package/rust/core/parser/driver.rs +47 -4
- package/rust/core/parser/handler/arrow_call.rs +78 -18
- package/rust/core/parser/handler/bank.rs +35 -7
- package/rust/core/parser/handler/dot.rs +81 -123
- package/rust/core/parser/handler/identifier/call.rs +69 -22
- package/rust/core/parser/handler/identifier/function.rs +92 -0
- package/rust/core/parser/handler/identifier/let_.rs +13 -19
- package/rust/core/parser/handler/identifier/mod.rs +1 -0
- package/rust/core/parser/handler/identifier/spawn.rs +74 -27
- package/rust/core/parser/statement.rs +16 -4
- package/rust/core/plugin/loader.rs +48 -0
- package/rust/core/plugin/mod.rs +1 -0
- package/rust/core/preprocessor/loader.rs +50 -32
- package/rust/core/preprocessor/module.rs +3 -1
- package/rust/core/preprocessor/processor.rs +26 -1
- package/rust/core/preprocessor/resolver/call.rs +61 -84
- package/rust/core/preprocessor/resolver/condition.rs +11 -6
- package/rust/core/preprocessor/resolver/driver.rs +52 -6
- package/rust/core/preprocessor/resolver/function.rs +78 -0
- package/rust/core/preprocessor/resolver/group.rs +43 -13
- package/rust/core/preprocessor/resolver/let_.rs +7 -10
- package/rust/core/preprocessor/resolver/mod.rs +2 -1
- package/rust/core/preprocessor/resolver/spawn.rs +64 -30
- package/rust/core/preprocessor/resolver/trigger.rs +7 -3
- package/rust/core/preprocessor/resolver/value.rs +10 -1
- package/rust/core/shared/value.rs +4 -1
- package/rust/core/store/function.rs +34 -0
- package/rust/core/store/global.rs +9 -10
- package/rust/core/store/mod.rs +2 -1
- package/rust/core/store/variable.rs +6 -0
- package/rust/installer/addon.rs +80 -0
- package/rust/installer/bank.rs +24 -14
- package/rust/installer/mod.rs +4 -1
- package/rust/installer/plugin.rs +55 -0
- package/rust/lib.rs +10 -7
- package/rust/main.rs +32 -9
- package/rust/utils/logger.rs +16 -0
- package/rust/utils/mod.rs +45 -1
- package/rust/utils/spinner.rs +2 -4
|
@@ -8,34 +8,81 @@ use crate::core::{
|
|
|
8
8
|
pub fn parse_spawn_token(
|
|
9
9
|
parser: &mut Parser,
|
|
10
10
|
current_token: Token,
|
|
11
|
-
|
|
11
|
+
_global_store: &mut GlobalStore
|
|
12
12
|
) -> Statement {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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 {
|
|
13
|
+
parser.advance(); // consume "spawn"
|
|
14
|
+
|
|
15
|
+
// Expect function name
|
|
16
|
+
let name_token = match parser.peek_clone() {
|
|
17
|
+
Some(t) => t,
|
|
18
|
+
None => {
|
|
28
19
|
return Statement::error(
|
|
29
20
|
current_token,
|
|
30
|
-
"Expected
|
|
21
|
+
"Expected function name after 'spawn'".to_string()
|
|
31
22
|
);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
if name_token.kind != TokenKind::Identifier {
|
|
27
|
+
return Statement::error(
|
|
28
|
+
name_token,
|
|
29
|
+
"Expected function name to be an identifier".to_string()
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let func_name = name_token.lexeme.clone();
|
|
34
|
+
parser.advance(); // consume function name
|
|
35
|
+
|
|
36
|
+
// Expect '('
|
|
37
|
+
let mut args: Vec<Value> = Vec::new();
|
|
38
|
+
if let Some(open_paren) = parser.peek_clone() {
|
|
39
|
+
if open_paren.kind == TokenKind::LParen {
|
|
40
|
+
parser.advance(); // consume '('
|
|
41
|
+
|
|
42
|
+
// Collect args until ')'
|
|
43
|
+
while let Some(token) = parser.peek_clone() {
|
|
44
|
+
if token.kind == TokenKind::RParen {
|
|
45
|
+
parser.advance(); // consume ')'
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
match token.kind {
|
|
50
|
+
TokenKind::Number => {
|
|
51
|
+
if let Ok(num) = token.lexeme.parse::<f32>() {
|
|
52
|
+
args.push(Value::Number(num));
|
|
53
|
+
}
|
|
54
|
+
parser.advance();
|
|
55
|
+
}
|
|
56
|
+
TokenKind::String => {
|
|
57
|
+
args.push(Value::String(token.lexeme.clone()));
|
|
58
|
+
parser.advance();
|
|
59
|
+
}
|
|
60
|
+
TokenKind::Identifier => {
|
|
61
|
+
args.push(Value::Identifier(token.lexeme.clone()));
|
|
62
|
+
parser.advance();
|
|
63
|
+
}
|
|
64
|
+
TokenKind::Comma => {
|
|
65
|
+
parser.advance(); // skip comma
|
|
66
|
+
}
|
|
67
|
+
_ => {
|
|
68
|
+
return Statement::error(
|
|
69
|
+
token,
|
|
70
|
+
"Unexpected token in spawn arguments".to_string()
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
Statement {
|
|
79
|
+
kind: StatementKind::Spawn {
|
|
80
|
+
name: func_name,
|
|
81
|
+
args,
|
|
82
|
+
},
|
|
83
|
+
value: Value::Null,
|
|
84
|
+
indent: current_token.indent,
|
|
85
|
+
line: current_token.line,
|
|
86
|
+
column: current_token.column,
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -49,6 +49,11 @@ pub enum StatementKind {
|
|
|
49
49
|
method: String,
|
|
50
50
|
args: Vec<Value>,
|
|
51
51
|
},
|
|
52
|
+
Function {
|
|
53
|
+
name: String,
|
|
54
|
+
parameters: Vec<String>,
|
|
55
|
+
body: Vec<Statement>,
|
|
56
|
+
},
|
|
52
57
|
|
|
53
58
|
// ───── Instruments ─────
|
|
54
59
|
Synth,
|
|
@@ -57,12 +62,19 @@ pub enum StatementKind {
|
|
|
57
62
|
Trigger {
|
|
58
63
|
entity: String,
|
|
59
64
|
duration: Duration,
|
|
65
|
+
effects: Option<Value>,
|
|
60
66
|
},
|
|
61
67
|
Sleep,
|
|
62
|
-
Call
|
|
63
|
-
|
|
68
|
+
Call {
|
|
69
|
+
name: String,
|
|
70
|
+
args: Vec<Value>,
|
|
71
|
+
},
|
|
72
|
+
Spawn {
|
|
73
|
+
name: String,
|
|
74
|
+
args: Vec<Value>,
|
|
75
|
+
},
|
|
64
76
|
Loop,
|
|
65
|
-
|
|
77
|
+
|
|
66
78
|
// ───── Structure & Logic ─────
|
|
67
79
|
Group,
|
|
68
80
|
|
|
@@ -93,4 +105,4 @@ pub enum StatementKind {
|
|
|
93
105
|
Error {
|
|
94
106
|
message: String,
|
|
95
107
|
},
|
|
96
|
-
}
|
|
108
|
+
}
|
|
@@ -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;
|
|
@@ -127,6 +127,11 @@ impl ModuleLoader {
|
|
|
127
127
|
updated_module.tokens = tokens;
|
|
128
128
|
updated_module.statements = statements;
|
|
129
129
|
|
|
130
|
+
// Step four : Injecting bank triggers if any
|
|
131
|
+
if let Err(e) = self.inject_bank_triggers(&mut updated_module, "808") {
|
|
132
|
+
return Err(format!("Failed to inject bank triggers: {}", e));
|
|
133
|
+
}
|
|
134
|
+
|
|
130
135
|
// Step four : error handling
|
|
131
136
|
let mut error_handler = ErrorHandler::new();
|
|
132
137
|
error_handler.detect_from_statements(&mut parser, &updated_module.statements);
|
|
@@ -143,16 +148,16 @@ impl ModuleLoader {
|
|
|
143
148
|
global_store: &mut GlobalStore
|
|
144
149
|
) -> (HashMap<String, Vec<Token>>, HashMap<String, Vec<Statement>>) {
|
|
145
150
|
// SECTION Load the entry module and its dependencies
|
|
146
|
-
|
|
147
151
|
let tokens_by_module = self.load_module_recursively(&self.entry, global_store);
|
|
148
152
|
|
|
149
153
|
// SECTION Process and resolve modules
|
|
150
154
|
process_modules(self, global_store);
|
|
151
155
|
resolve_all_modules(self, global_store);
|
|
152
156
|
|
|
153
|
-
|
|
157
|
+
// SECTION Flatten all modules to get statements (+ injects)
|
|
158
|
+
let statements_by_module = resolve_and_flatten_all_modules(global_store);
|
|
154
159
|
|
|
155
|
-
(tokens_by_module,
|
|
160
|
+
(tokens_by_module, statements_by_module)
|
|
156
161
|
}
|
|
157
162
|
|
|
158
163
|
#[cfg(feature = "cli")]
|
|
@@ -182,12 +187,15 @@ impl ModuleLoader {
|
|
|
182
187
|
module.statements = statements.clone();
|
|
183
188
|
|
|
184
189
|
// Inject triggers for each bank used in module
|
|
185
|
-
for bank_name in
|
|
186
|
-
|
|
187
|
-
return HashMap::new(); // Return empty map on error
|
|
188
|
-
}
|
|
190
|
+
for bank_name in self.extract_bank_names(&statements) {
|
|
191
|
+
self.inject_bank_triggers(&mut module, &bank_name);
|
|
189
192
|
}
|
|
190
193
|
|
|
194
|
+
// Inject module variables and functions into global store
|
|
195
|
+
global_store.variables.variables.extend(module.variable_table.variables.clone());
|
|
196
|
+
global_store.functions.functions.extend(module.function_table.functions.clone());
|
|
197
|
+
|
|
198
|
+
// Inject the module into the global store
|
|
191
199
|
global_store.insert_module(path.clone(), module);
|
|
192
200
|
|
|
193
201
|
// Load dependencies
|
|
@@ -241,65 +249,75 @@ impl ModuleLoader {
|
|
|
241
249
|
}
|
|
242
250
|
}
|
|
243
251
|
|
|
244
|
-
pub fn inject_bank_triggers(
|
|
252
|
+
pub fn inject_bank_triggers(
|
|
253
|
+
&self,
|
|
254
|
+
module: &mut Module,
|
|
255
|
+
bank_name: &str
|
|
256
|
+
) -> Result<Module, String> {
|
|
257
|
+
let alias = bank_name.split('.').last().unwrap_or(bank_name);
|
|
258
|
+
|
|
245
259
|
let bank_path = Path::new("./.deva/bank").join(bank_name);
|
|
246
|
-
let
|
|
260
|
+
let bank_toml_path = bank_path.join("bank.toml");
|
|
247
261
|
|
|
248
|
-
if !
|
|
249
|
-
return Ok(());
|
|
262
|
+
if !bank_toml_path.exists() {
|
|
263
|
+
return Ok(module.clone());
|
|
250
264
|
}
|
|
251
265
|
|
|
252
266
|
let content = std::fs
|
|
253
|
-
::read_to_string(&
|
|
254
|
-
.map_err(|e| format!("Failed to read '{}': {}",
|
|
267
|
+
::read_to_string(&bank_toml_path)
|
|
268
|
+
.map_err(|e| format!("Failed to read '{}': {}", bank_toml_path.display(), e))?;
|
|
255
269
|
|
|
256
|
-
let
|
|
270
|
+
let parsed_bankfile: BankFile = toml
|
|
257
271
|
::from_str(&content)
|
|
258
|
-
.map_err(|e| format!("Failed to parse '{}': {}",
|
|
272
|
+
.map_err(|e| format!("Failed to parse '{}': {}", bank_toml_path.display(), e))?;
|
|
259
273
|
|
|
260
274
|
let mut bank_map = HashMap::new();
|
|
261
275
|
|
|
262
|
-
for bank_trigger in
|
|
276
|
+
for bank_trigger in parsed_bankfile.triggers.unwrap_or_default() {
|
|
263
277
|
let trigger_name = bank_trigger.name.clone().replace("./", "");
|
|
264
278
|
let bank_trigger_path = format!("devalang://bank/{}/{}", bank_name, trigger_name);
|
|
265
279
|
|
|
266
280
|
bank_map.insert(bank_trigger.name.clone(), Value::String(bank_trigger_path.clone()));
|
|
267
281
|
|
|
268
|
-
if module.variable_table.variables.contains_key(
|
|
282
|
+
if module.variable_table.variables.contains_key(alias) {
|
|
269
283
|
eprintln!(
|
|
270
284
|
"⚠️ Trigger '{}' already defined in module '{}', skipping injection.",
|
|
271
|
-
|
|
285
|
+
alias,
|
|
286
|
+
module.path
|
|
272
287
|
);
|
|
273
288
|
continue;
|
|
274
289
|
}
|
|
275
290
|
|
|
276
291
|
module.variable_table.set(
|
|
277
|
-
format!("{}.{}",
|
|
292
|
+
format!("{}.{}", alias, bank_trigger.name),
|
|
278
293
|
Value::String(bank_trigger_path.clone())
|
|
279
294
|
);
|
|
280
295
|
}
|
|
281
296
|
|
|
282
297
|
// Inject the map under the bank name
|
|
283
|
-
module.variable_table.set(
|
|
298
|
+
module.variable_table.set(alias.to_string(), Value::Map(bank_map));
|
|
284
299
|
|
|
285
|
-
Ok(())
|
|
300
|
+
Ok(module.clone())
|
|
286
301
|
}
|
|
287
302
|
|
|
288
|
-
fn extract_bank_names(statements: &[Statement]) -> HashSet<String> {
|
|
303
|
+
fn extract_bank_names(&self, statements: &[Statement]) -> HashSet<String> {
|
|
289
304
|
let mut banks = HashSet::new();
|
|
290
305
|
|
|
291
306
|
for stmt in statements {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
307
|
+
match &stmt.kind {
|
|
308
|
+
// Extract only bank declarations
|
|
309
|
+
StatementKind::Bank => {
|
|
310
|
+
if let Value::String(name) = &stmt.value {
|
|
311
|
+
banks.insert(name.clone());
|
|
312
|
+
}
|
|
313
|
+
if let Value::Number(num) = &stmt.value {
|
|
314
|
+
banks.insert(num.to_string());
|
|
315
|
+
}
|
|
316
|
+
if let Value::Identifier(name) = &stmt.value {
|
|
317
|
+
banks.insert(name.clone());
|
|
318
|
+
}
|
|
302
319
|
}
|
|
320
|
+
_ => {}
|
|
303
321
|
}
|
|
304
322
|
}
|
|
305
323
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use crate::core::{
|
|
2
2
|
lexer::token::Token,
|
|
3
3
|
parser::statement::Statement,
|
|
4
|
-
store::{ export::ExportTable, import::ImportTable, variable::VariableTable },
|
|
4
|
+
store::{ export::ExportTable, function::FunctionTable, import::ImportTable, variable::VariableTable },
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
#[derive(Debug, Clone)]
|
|
@@ -11,6 +11,7 @@ pub struct Module {
|
|
|
11
11
|
pub tokens: Vec<Token>,
|
|
12
12
|
pub statements: Vec<Statement>,
|
|
13
13
|
pub variable_table: VariableTable,
|
|
14
|
+
pub function_table: FunctionTable,
|
|
14
15
|
pub export_table: ExportTable,
|
|
15
16
|
pub import_table: ImportTable,
|
|
16
17
|
pub content: String,
|
|
@@ -24,6 +25,7 @@ impl Module {
|
|
|
24
25
|
tokens: Vec::new(),
|
|
25
26
|
statements: Vec::new(),
|
|
26
27
|
variable_table: VariableTable::new(),
|
|
28
|
+
function_table: FunctionTable::new(),
|
|
27
29
|
export_table: ExportTable::new(),
|
|
28
30
|
import_table: ImportTable::new(),
|
|
29
31
|
resolved: false,
|
|
@@ -5,7 +5,7 @@ use crate::core::{
|
|
|
5
5
|
preprocessor::{ loader::ModuleLoader, resolver::group },
|
|
6
6
|
shared::value::Value,
|
|
7
7
|
store::global::GlobalStore,
|
|
8
|
-
utils::path::{normalize_path, resolve_relative_path},
|
|
8
|
+
utils::path::{ normalize_path, resolve_relative_path },
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
pub fn process_modules(module_loader: &ModuleLoader, global_store: &mut GlobalStore) {
|
|
@@ -13,6 +13,31 @@ pub fn process_modules(module_loader: &ModuleLoader, global_store: &mut GlobalSt
|
|
|
13
13
|
for stmt in &module.statements {
|
|
14
14
|
match &stmt.kind {
|
|
15
15
|
StatementKind::Let { name } => {
|
|
16
|
+
if let Value::Null = stmt.value {
|
|
17
|
+
eprintln!("❌ Variable '{}' is declared but not initialized.", name);
|
|
18
|
+
|
|
19
|
+
module.variable_table.variables.insert(
|
|
20
|
+
name.clone(),
|
|
21
|
+
Value::StatementKind(Box::new(stmt.kind.clone()))
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if module.variable_table.get(name).is_some() {
|
|
28
|
+
eprintln!("❌ Variable '{}' is already defined in this scope.", name);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if let Some(module_variable) = module.variable_table.variables.get(name) {
|
|
33
|
+
eprintln!(
|
|
34
|
+
"❌ Variable '{}' is already defined globally with value: {:?}",
|
|
35
|
+
name,
|
|
36
|
+
module_variable
|
|
37
|
+
);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
16
41
|
module.variable_table.variables.insert(name.clone(), stmt.value.clone());
|
|
17
42
|
}
|
|
18
43
|
|
|
@@ -10,103 +10,80 @@ use crate::{
|
|
|
10
10
|
|
|
11
11
|
pub fn resolve_call(
|
|
12
12
|
stmt: &Statement,
|
|
13
|
+
name: String,
|
|
14
|
+
args: Vec<Value>,
|
|
13
15
|
module: &Module,
|
|
14
16
|
path: &str,
|
|
15
17
|
global_store: &mut GlobalStore
|
|
16
18
|
) -> Statement {
|
|
17
19
|
let logger = Logger::new();
|
|
18
20
|
|
|
19
|
-
match &stmt.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
None =>
|
|
37
|
-
error_stmt(
|
|
38
|
-
&logger,
|
|
39
|
-
module,
|
|
40
|
-
stmt,
|
|
41
|
-
&format!("Identifier '{ident}' not found in variable table")
|
|
42
|
-
),
|
|
43
|
-
}
|
|
44
|
-
}
|
|
21
|
+
match &stmt.kind {
|
|
22
|
+
StatementKind::Call { .. } => {
|
|
23
|
+
// Check if it's a function
|
|
24
|
+
if let Some(func) = global_store.functions.functions.get(&name) {
|
|
25
|
+
let mut call_map = std::collections::HashMap::new();
|
|
26
|
+
call_map.insert("name".to_string(), Value::Identifier(name.clone()));
|
|
27
|
+
call_map.insert(
|
|
28
|
+
"parameters".to_string(),
|
|
29
|
+
Value::Array(
|
|
30
|
+
func.parameters
|
|
31
|
+
.iter()
|
|
32
|
+
.map(|p| Value::Identifier(p.clone()))
|
|
33
|
+
.collect()
|
|
34
|
+
)
|
|
35
|
+
);
|
|
36
|
+
call_map.insert("args".to_string(), Value::Array(args.clone()));
|
|
37
|
+
call_map.insert("body".to_string(), Value::Block(func.body.clone()));
|
|
45
38
|
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
return Statement {
|
|
40
|
+
kind: StatementKind::Call { name, args },
|
|
41
|
+
value: Value::Map(call_map),
|
|
42
|
+
..stmt.clone()
|
|
43
|
+
};
|
|
44
|
+
}
|
|
48
45
|
|
|
49
|
-
|
|
46
|
+
// Otherwise, check if it's a variable (e.g. group)
|
|
47
|
+
if let Some(variable) = global_store.variables.variables.get(&name) {
|
|
48
|
+
if let Value::Statement(stmt_box) = variable {
|
|
49
|
+
if let StatementKind::Group = stmt_box.kind {
|
|
50
|
+
if let Value::Map(map) = &stmt_box.value {
|
|
51
|
+
if let Some(Value::Block(body)) = map.get("body") {
|
|
52
|
+
let mut resolved_map = std::collections::HashMap::new();
|
|
53
|
+
resolved_map.insert(
|
|
54
|
+
"identifier".to_string(),
|
|
55
|
+
Value::String(name.clone())
|
|
56
|
+
);
|
|
57
|
+
resolved_map.insert("args".to_string(), Value::Array(args.clone()));
|
|
58
|
+
resolved_map.insert("body".to_string(), Value::Block(body.clone()));
|
|
50
59
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
+
return Statement {
|
|
61
|
+
kind: StatementKind::Call { name, args },
|
|
62
|
+
value: Value::Map(resolved_map),
|
|
63
|
+
..stmt.clone()
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
60
70
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
error_stmt(
|
|
73
|
-
logger,
|
|
74
|
-
module,
|
|
75
|
-
stmt,
|
|
76
|
-
&format!("Expected a group for '{}', but found {:?}", name, other)
|
|
77
|
-
),
|
|
78
|
-
None =>
|
|
79
|
-
error_stmt(
|
|
80
|
-
logger,
|
|
81
|
-
module,
|
|
82
|
-
stmt,
|
|
83
|
-
&format!("Group '{}' not found in module '{}'", name, module.path)
|
|
84
|
-
),
|
|
71
|
+
// Otherwise, log an error
|
|
72
|
+
logger.log_message(LogLevel::Error, &format!("Function or group '{}' not found", name));
|
|
73
|
+
Statement {
|
|
74
|
+
kind: StatementKind::Error {
|
|
75
|
+
message: format!("Function or group '{}' not found", name),
|
|
76
|
+
},
|
|
77
|
+
value: Value::Null,
|
|
78
|
+
..stmt.clone()
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
_ => error_stmt(&logger, module, stmt, "Expected StatementKind::Call in resolve_call()"),
|
|
85
82
|
}
|
|
86
83
|
}
|
|
87
84
|
|
|
88
|
-
fn
|
|
89
|
-
|
|
90
|
-
group_map: &std::collections::HashMap<String, Value>,
|
|
91
|
-
module: &Module,
|
|
92
|
-
path: &str,
|
|
93
|
-
global_store: &mut GlobalStore
|
|
94
|
-
) -> Statement {
|
|
95
|
-
let mut cloned_map = group_map.clone();
|
|
96
|
-
|
|
97
|
-
if let Some(Value::Block(stmts)) = group_map.get("body") {
|
|
98
|
-
let resolved = stmts
|
|
99
|
-
.iter()
|
|
100
|
-
.map(|s| resolve_statement(s, module, path, global_store))
|
|
101
|
-
.collect();
|
|
102
|
-
cloned_map.insert("body".to_string(), Value::Block(resolved));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
Statement {
|
|
106
|
-
kind: StatementKind::Call,
|
|
107
|
-
value: Value::Map(cloned_map),
|
|
108
|
-
..stmt.clone()
|
|
109
|
-
}
|
|
85
|
+
fn get_group_body(stmt_box: &Statement) -> Vec<Statement> {
|
|
86
|
+
if let Value::Block(body) = &stmt_box.value { body.clone() } else { vec![] }
|
|
110
87
|
}
|
|
111
88
|
|
|
112
89
|
fn error_stmt(logger: &Logger, module: &Module, stmt: &Statement, message: &str) -> Statement {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use crate::{
|
|
2
2
|
core::{
|
|
3
|
-
parser::statement::{Statement, StatementKind},
|
|
4
|
-
preprocessor::{module::Module, resolver::driver::resolve_statement},
|
|
3
|
+
parser::statement::{ Statement, StatementKind },
|
|
4
|
+
preprocessor::{ module::Module, resolver::driver::resolve_statement },
|
|
5
5
|
shared::value::Value,
|
|
6
6
|
store::global::GlobalStore,
|
|
7
7
|
},
|
|
@@ -12,12 +12,17 @@ pub fn resolve_condition(
|
|
|
12
12
|
stmt: &Statement,
|
|
13
13
|
module: &Module,
|
|
14
14
|
path: &str,
|
|
15
|
-
global_store: &mut GlobalStore
|
|
15
|
+
global_store: &mut GlobalStore
|
|
16
16
|
) -> Statement {
|
|
17
17
|
let logger = Logger::new();
|
|
18
18
|
|
|
19
19
|
let Value::Map(condition_map) = &stmt.value else {
|
|
20
|
-
return type_error(
|
|
20
|
+
return type_error(
|
|
21
|
+
&logger,
|
|
22
|
+
module,
|
|
23
|
+
stmt,
|
|
24
|
+
"Expected a map in condition statement".to_string()
|
|
25
|
+
);
|
|
21
26
|
};
|
|
22
27
|
|
|
23
28
|
let mut resolved_map = condition_map.clone();
|
|
@@ -60,13 +65,13 @@ fn resolve_block_statements(
|
|
|
60
65
|
body: &[Statement],
|
|
61
66
|
module: &Module,
|
|
62
67
|
path: &str,
|
|
63
|
-
global_store: &mut GlobalStore
|
|
68
|
+
global_store: &mut GlobalStore
|
|
64
69
|
) -> Vec<Statement> {
|
|
65
70
|
body.iter()
|
|
66
71
|
.flat_map(|stmt| {
|
|
67
72
|
let resolved = resolve_statement(stmt, module, path, global_store);
|
|
68
73
|
|
|
69
|
-
if let StatementKind::Call = resolved.kind {
|
|
74
|
+
if let StatementKind::Call { .. } = resolved.kind {
|
|
70
75
|
if let Value::Block(inner) = &resolved.value {
|
|
71
76
|
return inner
|
|
72
77
|
.iter()
|