@devaloop/devalang 0.0.1-alpha.3 → 0.0.1-alpha.5
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 +1 -1
- package/Cargo.toml +9 -7
- package/README.md +49 -25
- package/docs/CHANGELOG.md +30 -0
- package/docs/COMMANDS.md +31 -0
- package/docs/CONFIG.md +6 -4
- package/docs/ROADMAP.md +4 -4
- package/docs/TODO.md +4 -4
- package/examples/index.deva +9 -2
- package/examples/samples/hat-808.wav +0 -0
- package/out-tsc/bin/devalang.exe +0 -0
- package/package.json +44 -42
- package/project-version.json +6 -6
- package/rust/audio/engine.rs +126 -0
- package/rust/audio/interpreter.rs +143 -0
- package/rust/audio/loader.rs +46 -0
- package/rust/audio/mod.rs +5 -0
- package/rust/audio/player.rs +54 -0
- package/rust/audio/render.rs +57 -0
- package/rust/cli/build.rs +17 -5
- package/rust/cli/check.rs +2 -1
- package/rust/cli/init.rs +4 -2
- package/rust/cli/mod.rs +29 -0
- package/rust/cli/play.rs +192 -0
- package/rust/cli/template.rs +2 -1
- package/rust/config/loader.rs +0 -1
- package/rust/config/mod.rs +3 -2
- package/rust/core/builder/mod.rs +54 -6
- package/rust/core/debugger/lexer.rs +20 -5
- package/rust/core/debugger/preprocessor.rs +9 -5
- package/rust/core/lexer/handler/mod.rs +2 -2
- package/rust/core/lexer/handler/newline.rs +5 -1
- package/rust/core/lexer/mod.rs +10 -5
- package/rust/core/parser/handler/loop_.rs +11 -0
- package/rust/core/parser/mod.rs +0 -1
- package/rust/core/preprocessor/loader.rs +89 -16
- package/rust/core/preprocessor/module.rs +2 -0
- package/rust/core/preprocessor/resolver/bank.rs +46 -0
- package/rust/core/preprocessor/resolver/loop_.rs +148 -0
- package/rust/core/preprocessor/resolver/mod.rs +151 -0
- package/rust/core/preprocessor/resolver/tempo.rs +49 -0
- package/rust/core/preprocessor/resolver/trigger.rs +114 -0
- package/rust/lib.rs +118 -0
- package/rust/main.rs +8 -0
- package/rust/utils/logger.rs +45 -6
- package/rust/utils/spinner.rs +2 -0
- package/rust/utils/watcher.rs +10 -2
- package/templates/minimal/.devalang +2 -1
- package/templates/minimal/README.md +202 -0
- package/templates/welcome/.devalang +2 -1
- package/templates/welcome/README.md +48 -31
- package/rust/core/preprocessor/resolver.rs +0 -372
package/rust/core/builder/mod.rs
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
use crate::core::parser::statement::Statement;
|
|
1
|
+
use crate::{ audio::render::render_audio_with_modules, core::parser::statement::Statement };
|
|
2
|
+
use crate::core::store::global::GlobalStore;
|
|
2
3
|
use std::{ collections::HashMap, fs::create_dir_all };
|
|
3
4
|
use std::io::Write;
|
|
4
5
|
|
|
6
|
+
use crate::utils::logger::Logger;
|
|
7
|
+
|
|
5
8
|
pub struct Builder {}
|
|
6
9
|
|
|
7
10
|
impl Builder {
|
|
@@ -9,16 +12,14 @@ impl Builder {
|
|
|
9
12
|
Builder {}
|
|
10
13
|
}
|
|
11
14
|
|
|
12
|
-
pub fn build_ast(&self, modules: &HashMap<String, Vec<Statement
|
|
13
|
-
let output_path = "./output";
|
|
14
|
-
|
|
15
|
+
pub fn build_ast(&self, modules: &HashMap<String, Vec<Statement>>, out_dir: &str) {
|
|
15
16
|
for (name, statements) in modules {
|
|
16
17
|
let formatted_name = name.split("/").last().unwrap_or(name);
|
|
17
18
|
let formatted_name = formatted_name.replace(".deva", "");
|
|
18
19
|
|
|
19
|
-
create_dir_all(format!("{}/ast",
|
|
20
|
+
create_dir_all(format!("{}/ast", out_dir)).expect("Failed to create AST directory");
|
|
20
21
|
|
|
21
|
-
let file_path = format!("{}/ast/{}.json",
|
|
22
|
+
let file_path = format!("{}/ast/{}.json", out_dir, formatted_name);
|
|
22
23
|
let mut file = std::fs::File::create(file_path).expect("Failed to create AST file");
|
|
23
24
|
|
|
24
25
|
let content = serde_json
|
|
@@ -28,4 +29,51 @@ impl Builder {
|
|
|
28
29
|
file.write_all(content.as_bytes()).expect("Failed to write AST to file");
|
|
29
30
|
}
|
|
30
31
|
}
|
|
32
|
+
|
|
33
|
+
pub fn build_audio(
|
|
34
|
+
&self,
|
|
35
|
+
modules: &HashMap<String, Vec<Statement>>,
|
|
36
|
+
normalized_output_dir: &str,
|
|
37
|
+
global_store: &mut GlobalStore
|
|
38
|
+
) {
|
|
39
|
+
let logger = Logger::new();
|
|
40
|
+
|
|
41
|
+
let audio_engines = render_audio_with_modules(
|
|
42
|
+
modules.clone(),
|
|
43
|
+
&normalized_output_dir,
|
|
44
|
+
global_store
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
create_dir_all(format!("{}/audio", normalized_output_dir)).expect(
|
|
48
|
+
"Failed to create audio directory"
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
for (module_name, mut audio_engine) in audio_engines {
|
|
52
|
+
let formatted_module_name = module_name
|
|
53
|
+
.split('/')
|
|
54
|
+
.last()
|
|
55
|
+
.unwrap_or(&module_name)
|
|
56
|
+
.replace(".deva", "");
|
|
57
|
+
|
|
58
|
+
let output_path = format!(
|
|
59
|
+
"{}/audio/{}.wav",
|
|
60
|
+
normalized_output_dir,
|
|
61
|
+
formatted_module_name
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
match audio_engine.generate_wav_file(&output_path) {
|
|
65
|
+
Ok(_) => {}
|
|
66
|
+
Err(msg) => {
|
|
67
|
+
logger.log_error_with_stacktrace(
|
|
68
|
+
&format!(
|
|
69
|
+
"Unable to generate WAV file for module '{}': {}",
|
|
70
|
+
formatted_module_name,
|
|
71
|
+
msg
|
|
72
|
+
),
|
|
73
|
+
&module_name
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
31
79
|
}
|
|
@@ -1,12 +1,27 @@
|
|
|
1
|
-
use
|
|
1
|
+
use std::{ collections::HashMap, fs::create_dir_all };
|
|
2
|
+
use crate::core::{ debugger::Debugger, lexer::token::Token, parser::statement::Statement };
|
|
2
3
|
|
|
3
|
-
pub fn write_lexer_log_file(
|
|
4
|
+
pub fn write_lexer_log_file(
|
|
5
|
+
output_dir: &str,
|
|
6
|
+
file_name: &str,
|
|
7
|
+
modules: HashMap<String, Vec<Token>>
|
|
8
|
+
) {
|
|
4
9
|
let debugger = Debugger::new();
|
|
5
10
|
let mut content = String::new();
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
let log_directory = format!("{}/logs", output_dir);
|
|
13
|
+
|
|
14
|
+
create_dir_all(&log_directory).expect("Failed to create log directory");
|
|
15
|
+
|
|
16
|
+
for (path, tokens) in modules {
|
|
17
|
+
content.push_str(&format!("--- Resolved Tokens for {} ---\n", path));
|
|
18
|
+
|
|
19
|
+
for token in tokens {
|
|
20
|
+
content.push_str(&format!("{:?}\n", token));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
content.push_str("\n");
|
|
9
24
|
}
|
|
10
25
|
|
|
11
|
-
debugger.write_log_file(
|
|
26
|
+
debugger.write_log_file(&log_directory, file_name, &content);
|
|
12
27
|
}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
use std::collections::HashMap;
|
|
1
|
+
use std::{ collections::HashMap, fs::create_dir_all };
|
|
2
2
|
use crate::core::{ debugger::Debugger, parser::statement::Statement };
|
|
3
3
|
|
|
4
4
|
pub fn write_preprocessor_log_file(
|
|
5
5
|
output_dir: &str,
|
|
6
6
|
file_name: &str,
|
|
7
|
-
|
|
7
|
+
modules: HashMap<String, Vec<Statement>>
|
|
8
8
|
) {
|
|
9
9
|
let debugger = Debugger::new();
|
|
10
10
|
let mut content = String::new();
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
let log_directory = format!("{}/logs", output_dir);
|
|
13
|
+
|
|
14
|
+
create_dir_all(&log_directory).expect("Failed to create log directory");
|
|
15
|
+
|
|
16
|
+
for (path, stmts) in modules {
|
|
13
17
|
content.push_str(&format!("--- Resolved Statements for {} ---\n", path));
|
|
14
|
-
|
|
18
|
+
|
|
15
19
|
for stmt in stmts {
|
|
16
20
|
content.push_str(&format!("{:?}\n", stmt));
|
|
17
21
|
}
|
|
@@ -19,5 +23,5 @@ pub fn write_preprocessor_log_file(
|
|
|
19
23
|
content.push_str("\n");
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
debugger.write_log_file(
|
|
26
|
+
debugger.write_log_file(&log_directory, file_name, &content);
|
|
23
27
|
}
|
|
@@ -42,7 +42,7 @@ fn advance_char<I: Iterator<Item = char>>(
|
|
|
42
42
|
Some(c)
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
pub fn handle_content_lexing(content: String) -> Vec<Token> {
|
|
45
|
+
pub fn handle_content_lexing(content: String) -> Result<Vec<Token>, String> {
|
|
46
46
|
let mut tokens = Vec::new();
|
|
47
47
|
|
|
48
48
|
let mut line = 1;
|
|
@@ -234,5 +234,5 @@ pub fn handle_content_lexing(content: String) -> Vec<Token> {
|
|
|
234
234
|
// );
|
|
235
235
|
// }
|
|
236
236
|
|
|
237
|
-
tokens
|
|
237
|
+
Ok(tokens)
|
|
238
238
|
}
|
package/rust/core/lexer/mod.rs
CHANGED
|
@@ -2,7 +2,10 @@ pub mod handler;
|
|
|
2
2
|
pub mod token;
|
|
3
3
|
|
|
4
4
|
use std::fs;
|
|
5
|
-
use crate::core::{
|
|
5
|
+
use crate::core::{
|
|
6
|
+
lexer::{ handler::handle_content_lexing, token::Token },
|
|
7
|
+
utils::path::normalize_path,
|
|
8
|
+
};
|
|
6
9
|
|
|
7
10
|
pub struct Lexer {}
|
|
8
11
|
|
|
@@ -11,14 +14,16 @@ impl Lexer {
|
|
|
11
14
|
Lexer {}
|
|
12
15
|
}
|
|
13
16
|
|
|
17
|
+
pub fn lex_from_source(&self, source: &str) -> Result<Vec<Token>, String> {
|
|
18
|
+
handle_content_lexing(source.to_string())
|
|
19
|
+
}
|
|
20
|
+
|
|
14
21
|
pub fn lex_tokens(&self, entrypoint: &str) -> Vec<Token> {
|
|
15
22
|
let path = normalize_path(entrypoint);
|
|
16
23
|
|
|
17
|
-
let file_content = fs
|
|
18
|
-
::read_to_string(&path)
|
|
19
|
-
.expect("Failed to read the entrypoint file");
|
|
24
|
+
let file_content = fs::read_to_string(&path).expect("Failed to read the entrypoint file");
|
|
20
25
|
|
|
21
|
-
let tokens = handle_content_lexing(file_content);
|
|
26
|
+
let tokens = handle_content_lexing(file_content).expect("Failed to lex the content");
|
|
22
27
|
|
|
23
28
|
tokens
|
|
24
29
|
}
|
|
@@ -40,6 +40,17 @@ pub fn parse_loop_token(parser: &mut Parser, global_store: &mut GlobalStore) ->
|
|
|
40
40
|
);
|
|
41
41
|
let loop_body = parser.parse_block(tokens.clone(), global_store);
|
|
42
42
|
|
|
43
|
+
// Peek for dedent
|
|
44
|
+
if let Some(token) = parser.peek() {
|
|
45
|
+
if token.kind == TokenKind::Dedent {
|
|
46
|
+
parser.advance();
|
|
47
|
+
} else {
|
|
48
|
+
// Unexpected token after loop body
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
// EOF or unexpected end of input
|
|
52
|
+
}
|
|
53
|
+
|
|
43
54
|
let mut value_map = HashMap::new();
|
|
44
55
|
|
|
45
56
|
value_map.insert("iterator".to_string(), Value::Identifier(iterator_name));
|
package/rust/core/parser/mod.rs
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
use std::collections::HashMap;
|
|
2
|
-
|
|
3
2
|
use crate::{
|
|
4
3
|
core::{
|
|
5
|
-
debugger::{ lexer::write_lexer_log_file, preprocessor::write_preprocessor_log_file },
|
|
6
4
|
error::ErrorHandler,
|
|
7
|
-
lexer::Lexer,
|
|
5
|
+
lexer::{ token::Token, Lexer },
|
|
8
6
|
parser::{ statement::{ Statement, StatementKind }, Parser },
|
|
9
7
|
preprocessor::{
|
|
10
8
|
module::Module,
|
|
@@ -12,8 +10,9 @@ use crate::{
|
|
|
12
10
|
resolver::{ resolve_all_modules, resolve_and_flatten_all_modules },
|
|
13
11
|
},
|
|
14
12
|
store::global::GlobalStore,
|
|
13
|
+
utils::path::normalize_path,
|
|
15
14
|
},
|
|
16
|
-
utils::logger::
|
|
15
|
+
utils::logger::Logger,
|
|
17
16
|
};
|
|
18
17
|
|
|
19
18
|
pub struct ModuleLoader {
|
|
@@ -29,26 +28,90 @@ impl ModuleLoader {
|
|
|
29
28
|
}
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
pub fn
|
|
31
|
+
pub fn from_raw_source(
|
|
32
|
+
entry_path: &str,
|
|
33
|
+
output_path: &str,
|
|
34
|
+
content: &str,
|
|
35
|
+
global_store: &mut GlobalStore
|
|
36
|
+
) -> Self {
|
|
37
|
+
let normalized_entry_path = normalize_path(entry_path);
|
|
38
|
+
|
|
39
|
+
let mut module = Module::new(&entry_path);
|
|
40
|
+
module.content = content.to_string();
|
|
41
|
+
|
|
42
|
+
println!("Loading module from raw source: {}", normalized_entry_path);
|
|
43
|
+
|
|
44
|
+
global_store.insert_module(normalized_entry_path.to_string(), module);
|
|
45
|
+
|
|
46
|
+
Self {
|
|
47
|
+
entry: normalized_entry_path.to_string(),
|
|
48
|
+
output: output_path.to_string(),
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
pub fn extract_statements_map(
|
|
53
|
+
&self,
|
|
54
|
+
global_store: &GlobalStore
|
|
55
|
+
) -> HashMap<String, Vec<Statement>> {
|
|
56
|
+
global_store.modules
|
|
57
|
+
.iter()
|
|
58
|
+
.map(|(path, module)| (path.clone(), module.statements.clone()))
|
|
59
|
+
.collect()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pub fn load_single_module(&self, global_store: &mut GlobalStore) -> Result<Module, String> {
|
|
63
|
+
let mut module = global_store.modules
|
|
64
|
+
.remove(&self.entry)
|
|
65
|
+
.ok_or_else(|| format!("Module not found in store for path: {}", self.entry))?;
|
|
66
|
+
|
|
67
|
+
// SECTION Lexing the module content
|
|
68
|
+
let lexer = Lexer::new();
|
|
69
|
+
let tokens = lexer
|
|
70
|
+
.lex_from_source(&module.content)
|
|
71
|
+
.map_err(|e| format!("Lexer failed: {}", e))?;
|
|
72
|
+
|
|
73
|
+
module.tokens = tokens.clone();
|
|
74
|
+
|
|
75
|
+
// SECTION Parsing tokens into statements
|
|
76
|
+
let mut parser = Parser::new();
|
|
77
|
+
parser.set_current_module(self.entry.clone());
|
|
78
|
+
let statements = parser.parse_tokens(tokens, global_store);
|
|
79
|
+
module.statements = statements;
|
|
80
|
+
|
|
81
|
+
// SECTION Error handling
|
|
82
|
+
let mut error_handler = ErrorHandler::new();
|
|
83
|
+
error_handler.detect_from_statements(&mut parser, &module.statements);
|
|
84
|
+
|
|
85
|
+
global_store.modules.insert(self.entry.clone(), module.clone());
|
|
86
|
+
|
|
87
|
+
Ok(module)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
#[cfg(feature = "cli")]
|
|
91
|
+
pub fn load_all_modules(
|
|
92
|
+
&self,
|
|
93
|
+
global_store: &mut GlobalStore
|
|
94
|
+
) -> (HashMap<String, Vec<Token>>, HashMap<String, Vec<Statement>>) {
|
|
33
95
|
// SECTION Load the entry module and its dependencies
|
|
34
|
-
self.load_module_recursively(&self.entry, global_store);
|
|
96
|
+
let tokens_by_module = self.load_module_recursively(&self.entry, global_store);
|
|
35
97
|
|
|
36
98
|
// SECTION Process and resolve modules
|
|
37
99
|
process_modules(self, global_store);
|
|
38
100
|
resolve_all_modules(self, global_store);
|
|
39
101
|
|
|
40
|
-
let
|
|
41
|
-
|
|
42
|
-
// SECTION Write resolved statements to log file
|
|
43
|
-
write_preprocessor_log_file(&self.output, "resolved_statements.log", resolved.clone());
|
|
102
|
+
let statemnts_by_module = resolve_and_flatten_all_modules(global_store);
|
|
44
103
|
|
|
45
|
-
|
|
46
|
-
resolved
|
|
104
|
+
(tokens_by_module, statemnts_by_module)
|
|
47
105
|
}
|
|
48
106
|
|
|
49
|
-
|
|
107
|
+
#[cfg(feature = "cli")]
|
|
108
|
+
fn load_module_recursively(
|
|
109
|
+
&self,
|
|
110
|
+
path: &str,
|
|
111
|
+
global_store: &mut GlobalStore
|
|
112
|
+
) -> HashMap<String, Vec<Token>> {
|
|
50
113
|
if global_store.modules.contains_key(path) {
|
|
51
|
-
return;
|
|
114
|
+
return HashMap::new();
|
|
52
115
|
}
|
|
53
116
|
|
|
54
117
|
let lexer = Lexer::new();
|
|
@@ -75,15 +138,25 @@ impl ModuleLoader {
|
|
|
75
138
|
|
|
76
139
|
// SECTION Module creation
|
|
77
140
|
let mut module = Module::new(path);
|
|
78
|
-
module.tokens = tokens;
|
|
79
|
-
module.statements = statements;
|
|
141
|
+
module.tokens = tokens.clone();
|
|
142
|
+
module.statements = statements.clone();
|
|
80
143
|
|
|
81
144
|
global_store.insert_module(path.to_string(), module);
|
|
82
145
|
|
|
83
146
|
// Then load the imports recursively
|
|
84
147
|
self.load_module_imports(&path.to_string(), global_store);
|
|
148
|
+
|
|
149
|
+
// Return all tokens by module
|
|
150
|
+
let mut tokens_by_module = HashMap::new();
|
|
151
|
+
|
|
152
|
+
global_store.modules.iter().for_each(|(path, module)| {
|
|
153
|
+
tokens_by_module.insert(path.clone(), module.tokens.clone());
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
tokens_by_module
|
|
85
157
|
}
|
|
86
158
|
|
|
159
|
+
#[cfg(feature = "cli")]
|
|
87
160
|
fn load_module_imports(&self, path: &String, global_store: &mut GlobalStore) {
|
|
88
161
|
let imports = global_store.modules
|
|
89
162
|
.get(path)
|
|
@@ -18,6 +18,7 @@ pub struct Module {
|
|
|
18
18
|
pub variable_table: VariableTable,
|
|
19
19
|
pub export_table: ExportTable,
|
|
20
20
|
pub import_table: ImportTable,
|
|
21
|
+
pub content: String,
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
impl Module {
|
|
@@ -30,6 +31,7 @@ impl Module {
|
|
|
30
31
|
export_table: ExportTable::new(),
|
|
31
32
|
import_table: ImportTable::new(),
|
|
32
33
|
resolved: false,
|
|
34
|
+
content: String::new(),
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
use crate::{core::{
|
|
2
|
+
parser::statement::{Statement, StatementKind},
|
|
3
|
+
preprocessor::module::Module,
|
|
4
|
+
shared::value::Value,
|
|
5
|
+
store::global::GlobalStore,
|
|
6
|
+
}, utils::logger::Logger};
|
|
7
|
+
|
|
8
|
+
pub fn resolve_bank(
|
|
9
|
+
stmt: &Statement,
|
|
10
|
+
module: &Module,
|
|
11
|
+
path: &str,
|
|
12
|
+
_global_store: &GlobalStore
|
|
13
|
+
) -> Statement {
|
|
14
|
+
let mut new_stmt = stmt.clone();
|
|
15
|
+
let logger = Logger::new();
|
|
16
|
+
|
|
17
|
+
match &stmt.value {
|
|
18
|
+
Value::Identifier(ident) => {
|
|
19
|
+
if let Some(val) = module.variable_table.get(ident) {
|
|
20
|
+
new_stmt.value = val.clone();
|
|
21
|
+
} else {
|
|
22
|
+
let message = format!("Bank identifier '{ident}' not found in variable table");
|
|
23
|
+
logger.log_error_with_stacktrace(&message, &module.path);
|
|
24
|
+
new_stmt.kind = StatementKind::Error {
|
|
25
|
+
message: message.clone(),
|
|
26
|
+
};
|
|
27
|
+
new_stmt.value = Value::Null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
Value::String(_) => {
|
|
32
|
+
// Pas de résolution nécessaire
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
other => {
|
|
36
|
+
let message = format!("Expected a string or identifier for bank, found {:?}", other);
|
|
37
|
+
logger.log_error_with_stacktrace(&message, &module.path);
|
|
38
|
+
new_stmt.kind = StatementKind::Error {
|
|
39
|
+
message: "Expected a string or identifier for bank".to_string(),
|
|
40
|
+
};
|
|
41
|
+
new_stmt.value = Value::Null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
new_stmt
|
|
46
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
use crate::{
|
|
2
|
+
core::{
|
|
3
|
+
parser::statement::{ Statement, StatementKind },
|
|
4
|
+
preprocessor::{ module::Module, resolver::trigger::resolve_trigger },
|
|
5
|
+
shared::value::Value,
|
|
6
|
+
store::global::GlobalStore,
|
|
7
|
+
},
|
|
8
|
+
utils::logger::Logger,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
pub fn resolve_loop(
|
|
12
|
+
stmt: &Statement,
|
|
13
|
+
module: &Module,
|
|
14
|
+
path: &str,
|
|
15
|
+
global_store: &GlobalStore
|
|
16
|
+
) -> Statement {
|
|
17
|
+
let logger = Logger::new();
|
|
18
|
+
|
|
19
|
+
// Vérifie que stmt.value est bien une Map
|
|
20
|
+
if let Value::Map(value_map) = &stmt.value {
|
|
21
|
+
// Résolution de l'iterator
|
|
22
|
+
let iterator_value = match value_map.get("iterator") {
|
|
23
|
+
Some(Value::Identifier(ident)) => {
|
|
24
|
+
match module.variable_table.get(ident) {
|
|
25
|
+
Some(Value::Number(n)) => Value::Number(*n),
|
|
26
|
+
Some(_) => {
|
|
27
|
+
log_type_error(
|
|
28
|
+
&logger,
|
|
29
|
+
module,
|
|
30
|
+
stmt,
|
|
31
|
+
format!("Loop iterator '{ident}' must resolve to a number")
|
|
32
|
+
);
|
|
33
|
+
Value::Null
|
|
34
|
+
}
|
|
35
|
+
None => {
|
|
36
|
+
// Value is not a variable so we assume it's a number
|
|
37
|
+
if let Ok(n) = ident.parse::<f32>() {
|
|
38
|
+
Value::Number(n)
|
|
39
|
+
} else {
|
|
40
|
+
log_type_error(
|
|
41
|
+
&logger,
|
|
42
|
+
module,
|
|
43
|
+
stmt,
|
|
44
|
+
format!("Loop iterator '{ident}' is not a valid number")
|
|
45
|
+
);
|
|
46
|
+
Value::Null
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
Some(Value::Number(n)) => Value::Number(*n),
|
|
52
|
+
Some(other) => {
|
|
53
|
+
log_type_error(
|
|
54
|
+
&logger,
|
|
55
|
+
module,
|
|
56
|
+
stmt,
|
|
57
|
+
format!("Unexpected value for loop iterator: {:?}", other)
|
|
58
|
+
);
|
|
59
|
+
Value::Null
|
|
60
|
+
}
|
|
61
|
+
None => {
|
|
62
|
+
log_type_error(
|
|
63
|
+
&logger,
|
|
64
|
+
module,
|
|
65
|
+
stmt,
|
|
66
|
+
"Missing 'iterator' key in loop statement map".to_string()
|
|
67
|
+
);
|
|
68
|
+
Value::Null
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Résolution du body
|
|
73
|
+
let body_value = match value_map.get("body") {
|
|
74
|
+
Some(Value::Block(block)) => {
|
|
75
|
+
let mut resolved_block = Vec::new();
|
|
76
|
+
for ref statement in block.clone() {
|
|
77
|
+
match &statement.kind {
|
|
78
|
+
StatementKind::Trigger { entity, duration } => {
|
|
79
|
+
let resolved = resolve_trigger(
|
|
80
|
+
&mut statement.clone(),
|
|
81
|
+
&entity,
|
|
82
|
+
&mut duration.clone(),
|
|
83
|
+
module,
|
|
84
|
+
path,
|
|
85
|
+
global_store
|
|
86
|
+
);
|
|
87
|
+
resolved_block.push(resolved);
|
|
88
|
+
}
|
|
89
|
+
_ => {
|
|
90
|
+
println!("Unhandled loop body statement: {:?}", statement);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
Value::Block(resolved_block)
|
|
95
|
+
}
|
|
96
|
+
Some(other) => {
|
|
97
|
+
log_type_error(
|
|
98
|
+
&logger,
|
|
99
|
+
module,
|
|
100
|
+
stmt,
|
|
101
|
+
format!("Unexpected value for loop body: {:?}", other)
|
|
102
|
+
);
|
|
103
|
+
Value::Null
|
|
104
|
+
}
|
|
105
|
+
None => {
|
|
106
|
+
log_type_error(
|
|
107
|
+
&logger,
|
|
108
|
+
module,
|
|
109
|
+
stmt,
|
|
110
|
+
"Missing 'body' key in loop statement map".to_string()
|
|
111
|
+
);
|
|
112
|
+
Value::Null
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// ✅ Reconstruit proprement la valeur résolue
|
|
117
|
+
let mut resolved_map = std::collections::HashMap::new();
|
|
118
|
+
resolved_map.insert("iterator".to_string(), iterator_value);
|
|
119
|
+
resolved_map.insert("body".to_string(), body_value);
|
|
120
|
+
|
|
121
|
+
// ✅ Reconstruit le StatementLoop à partir des éléments résolus
|
|
122
|
+
Statement {
|
|
123
|
+
kind: StatementKind::Loop,
|
|
124
|
+
value: Value::Map(resolved_map),
|
|
125
|
+
..stmt.clone()
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
log_type_error(
|
|
129
|
+
&logger,
|
|
130
|
+
module,
|
|
131
|
+
stmt,
|
|
132
|
+
format!("Expected a map for loop value, found {:?}", stmt.value)
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
Statement {
|
|
136
|
+
kind: StatementKind::Error {
|
|
137
|
+
message: "Expected a map for loop value".to_string(),
|
|
138
|
+
},
|
|
139
|
+
value: Value::Null,
|
|
140
|
+
..stmt.clone()
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
fn log_type_error(logger: &Logger, module: &Module, stmt: &Statement, message: String) {
|
|
146
|
+
let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
|
|
147
|
+
logger.log_error_with_stacktrace(&message, &stacktrace);
|
|
148
|
+
}
|