@devaloop/devalang 0.0.1-alpha.4 → 0.0.1-alpha.7
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 +7 -5
- package/README.md +26 -7
- package/docs/CHANGELOG.md +34 -2
- package/docs/ROADMAP.md +3 -3
- package/docs/SYNTAX.md +42 -14
- package/docs/TODO.md +9 -8
- package/examples/group.deva +12 -0
- package/examples/index.deva +10 -9
- package/examples/loop.deva +15 -0
- package/examples/variables.deva +9 -0
- package/out-tsc/bin/devalang.exe +0 -0
- package/package.json +43 -41
- package/project-version.json +5 -5
- package/rust/cli/build.rs +4 -4
- package/rust/cli/check.rs +2 -1
- package/rust/cli/init.rs +4 -2
- package/rust/cli/play.rs +5 -3
- package/rust/cli/template.rs +2 -1
- package/rust/config/loader.rs +0 -1
- package/rust/{audio → core/audio}/engine.rs +1 -5
- package/rust/core/audio/interpreter.rs +317 -0
- package/rust/{audio → core/audio}/loader.rs +4 -0
- package/rust/{audio → core/audio}/render.rs +1 -5
- package/rust/core/builder/mod.rs +9 -8
- package/rust/core/lexer/handler/indent.rs +1 -1
- package/rust/core/lexer/handler/mod.rs +3 -4
- package/rust/core/lexer/handler/newline.rs +5 -1
- package/rust/core/lexer/handler/string.rs +3 -6
- package/rust/core/lexer/mod.rs +10 -5
- package/rust/core/mod.rs +2 -1
- package/rust/core/parser/handler/identifier.rs +127 -1
- package/rust/core/parser/handler/loop_.rs +11 -0
- package/rust/core/parser/mod.rs +0 -1
- package/rust/core/parser/statement.rs +9 -5
- package/rust/core/preprocessor/loader.rs +65 -3
- package/rust/core/preprocessor/module.rs +2 -0
- package/rust/core/preprocessor/processor.rs +28 -2
- package/rust/core/preprocessor/resolver/bank.rs +10 -9
- package/rust/core/preprocessor/resolver/group.rs +113 -0
- package/rust/core/preprocessor/resolver/loop_.rs +15 -13
- package/rust/core/preprocessor/resolver/mod.rs +11 -5
- package/rust/core/preprocessor/resolver/trigger.rs +0 -3
- package/rust/lib.rs +117 -0
- package/rust/main.rs +2 -1
- package/rust/utils/logger.rs +45 -6
- package/rust/utils/spinner.rs +2 -0
- 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/examples/exported.deva +0 -7
- package/rust/audio/interpreter.rs +0 -143
- /package/rust/{audio → core/audio}/mod.rs +0 -0
- /package/rust/{audio → core/audio}/player.rs +0 -0
|
@@ -51,12 +51,16 @@ pub enum StatementKind {
|
|
|
51
51
|
// Loop statements
|
|
52
52
|
Loop,
|
|
53
53
|
|
|
54
|
+
// Group statements
|
|
55
|
+
Group,
|
|
56
|
+
|
|
57
|
+
// Special statements
|
|
58
|
+
Call,
|
|
59
|
+
Spawn,
|
|
60
|
+
Sleep,
|
|
61
|
+
|
|
54
62
|
// Conditional statements
|
|
55
|
-
// If
|
|
56
|
-
// // condition: ConditionParts,
|
|
57
|
-
// condition_state: bool,
|
|
58
|
-
// body: Vec<Statement>,
|
|
59
|
-
// },
|
|
63
|
+
// If
|
|
60
64
|
|
|
61
65
|
// Keyword statements
|
|
62
66
|
Tempo,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
use std::collections::HashMap;
|
|
2
|
-
|
|
3
2
|
use crate::{
|
|
4
3
|
core::{
|
|
5
4
|
error::ErrorHandler,
|
|
@@ -11,8 +10,9 @@ use crate::{
|
|
|
11
10
|
resolver::{ resolve_all_modules, resolve_and_flatten_all_modules },
|
|
12
11
|
},
|
|
13
12
|
store::global::GlobalStore,
|
|
13
|
+
utils::path::normalize_path,
|
|
14
14
|
},
|
|
15
|
-
utils::logger::
|
|
15
|
+
utils::logger::Logger,
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
pub struct ModuleLoader {
|
|
@@ -28,7 +28,67 @@ impl ModuleLoader {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
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(
|
|
32
92
|
&self,
|
|
33
93
|
global_store: &mut GlobalStore
|
|
34
94
|
) -> (HashMap<String, Vec<Token>>, HashMap<String, Vec<Statement>>) {
|
|
@@ -44,6 +104,7 @@ impl ModuleLoader {
|
|
|
44
104
|
(tokens_by_module, statemnts_by_module)
|
|
45
105
|
}
|
|
46
106
|
|
|
107
|
+
#[cfg(feature = "cli")]
|
|
47
108
|
fn load_module_recursively(
|
|
48
109
|
&self,
|
|
49
110
|
path: &str,
|
|
@@ -95,6 +156,7 @@ impl ModuleLoader {
|
|
|
95
156
|
tokens_by_module
|
|
96
157
|
}
|
|
97
158
|
|
|
159
|
+
#[cfg(feature = "cli")]
|
|
98
160
|
fn load_module_imports(&self, path: &String, global_store: &mut GlobalStore) {
|
|
99
161
|
let imports = global_store.modules
|
|
100
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
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
|
|
1
3
|
use crate::core::{
|
|
2
4
|
parser::{ statement::StatementKind, Parser },
|
|
3
|
-
preprocessor::loader::ModuleLoader,
|
|
5
|
+
preprocessor::{ loader::ModuleLoader, resolver::group },
|
|
4
6
|
shared::value::Value,
|
|
5
7
|
store::global::GlobalStore,
|
|
6
8
|
};
|
|
@@ -33,7 +35,31 @@ pub fn process_modules(module_loader: &ModuleLoader, global_store: &mut GlobalSt
|
|
|
33
35
|
module.import_table.add_import(name.clone(), Value::String(source.clone()));
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
|
-
|
|
38
|
+
|
|
39
|
+
StatementKind::Group => {
|
|
40
|
+
if let Value::Map(map) = &stmt.value {
|
|
41
|
+
if
|
|
42
|
+
let (Some(Value::String(name)), Some(Value::Block(body))) = (
|
|
43
|
+
map.get("identifier"),
|
|
44
|
+
map.get("body"),
|
|
45
|
+
)
|
|
46
|
+
{
|
|
47
|
+
let mut stored_map = HashMap::new();
|
|
48
|
+
|
|
49
|
+
stored_map.insert(
|
|
50
|
+
"identifier".to_string(),
|
|
51
|
+
Value::String(name.clone())
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
stored_map.insert("body".to_string(), Value::Block(body.clone()));
|
|
55
|
+
|
|
56
|
+
module.variable_table.set(name.to_string(), Value::Map(stored_map));
|
|
57
|
+
} else {
|
|
58
|
+
eprintln!("❌ Invalid group definition: {:?}", stmt.value);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
37
63
|
_ => {}
|
|
38
64
|
}
|
|
39
65
|
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
use crate::{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
use crate::{
|
|
2
|
+
core::{
|
|
3
|
+
parser::statement::{ Statement, StatementKind },
|
|
4
|
+
preprocessor::module::Module,
|
|
5
|
+
shared::value::Value,
|
|
6
|
+
store::global::GlobalStore,
|
|
7
|
+
},
|
|
8
|
+
utils::logger::Logger,
|
|
9
|
+
};
|
|
7
10
|
|
|
8
11
|
pub fn resolve_bank(
|
|
9
12
|
stmt: &Statement,
|
|
@@ -28,9 +31,7 @@ pub fn resolve_bank(
|
|
|
28
31
|
}
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
Value::String(_) => {
|
|
32
|
-
// Pas de résolution nécessaire
|
|
33
|
-
}
|
|
34
|
+
Value::String(_) => {}
|
|
34
35
|
|
|
35
36
|
other => {
|
|
36
37
|
let message = format!("Expected a string or identifier for bank, found {:?}", other);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
|
|
3
|
+
use crate::{
|
|
4
|
+
core::{
|
|
5
|
+
parser::statement::{ Statement, StatementKind },
|
|
6
|
+
preprocessor::{ module::Module, resolver::trigger::resolve_trigger },
|
|
7
|
+
shared::value::Value,
|
|
8
|
+
store::global::GlobalStore,
|
|
9
|
+
},
|
|
10
|
+
utils::logger::Logger,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
pub fn resolve_group(
|
|
14
|
+
stmt: &Statement,
|
|
15
|
+
module: &Module,
|
|
16
|
+
path: &str,
|
|
17
|
+
global_store: &GlobalStore
|
|
18
|
+
) -> Statement {
|
|
19
|
+
let logger = Logger::new();
|
|
20
|
+
|
|
21
|
+
if let Value::Map(value_map) = &stmt.value {
|
|
22
|
+
let group_name = match value_map.get("identifier") {
|
|
23
|
+
Some(Value::String(name)) => name.clone(),
|
|
24
|
+
Some(other) => {
|
|
25
|
+
log_type_error(
|
|
26
|
+
&logger,
|
|
27
|
+
module,
|
|
28
|
+
stmt,
|
|
29
|
+
format!("Group name must be a string, found {:?}", other)
|
|
30
|
+
);
|
|
31
|
+
return stmt.clone();
|
|
32
|
+
}
|
|
33
|
+
None => {
|
|
34
|
+
log_type_error(&logger, module, stmt, "Group name is required".to_string());
|
|
35
|
+
return stmt.clone();
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
let body_value = match value_map.get("body") {
|
|
40
|
+
Some(Value::Block(block)) => {
|
|
41
|
+
let mut resolved_block = Vec::new();
|
|
42
|
+
for ref statement in block.clone() {
|
|
43
|
+
match &statement.kind {
|
|
44
|
+
StatementKind::Trigger { entity, duration } => {
|
|
45
|
+
let resolved = resolve_trigger(
|
|
46
|
+
&mut statement.clone(),
|
|
47
|
+
&entity,
|
|
48
|
+
&mut duration.clone(),
|
|
49
|
+
module,
|
|
50
|
+
path,
|
|
51
|
+
global_store
|
|
52
|
+
);
|
|
53
|
+
resolved_block.push(resolved);
|
|
54
|
+
}
|
|
55
|
+
_ => {
|
|
56
|
+
println!("Unhandled group body statement: {:?}", statement);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
Value::Block(resolved_block)
|
|
61
|
+
}
|
|
62
|
+
Some(other) => {
|
|
63
|
+
log_type_error(
|
|
64
|
+
&logger,
|
|
65
|
+
module,
|
|
66
|
+
stmt,
|
|
67
|
+
format!("Unexpected value for group body: {:?}", other)
|
|
68
|
+
);
|
|
69
|
+
Value::Null
|
|
70
|
+
}
|
|
71
|
+
None => {
|
|
72
|
+
log_type_error(
|
|
73
|
+
&logger,
|
|
74
|
+
module,
|
|
75
|
+
stmt,
|
|
76
|
+
"Missing 'body' key in group statement map".to_string()
|
|
77
|
+
);
|
|
78
|
+
Value::Null
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
let mut resolved_map = HashMap::new();
|
|
83
|
+
|
|
84
|
+
resolved_map.insert("identifier".to_string(), Value::String(group_name));
|
|
85
|
+
resolved_map.insert("body".to_string(), body_value);
|
|
86
|
+
|
|
87
|
+
return Statement {
|
|
88
|
+
kind: StatementKind::Group,
|
|
89
|
+
value: Value::Map(resolved_map),
|
|
90
|
+
..stmt.clone()
|
|
91
|
+
};
|
|
92
|
+
} else {
|
|
93
|
+
log_type_error(
|
|
94
|
+
&logger,
|
|
95
|
+
module,
|
|
96
|
+
stmt,
|
|
97
|
+
format!("Expected Map for group statement, found {:?}", stmt.value)
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
Statement {
|
|
101
|
+
kind: StatementKind::Error {
|
|
102
|
+
message: "Expected a map for group statement".to_string(),
|
|
103
|
+
},
|
|
104
|
+
value: Value::Null,
|
|
105
|
+
..stmt.clone()
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fn log_type_error(logger: &Logger, module: &Module, stmt: &Statement, message: String) {
|
|
111
|
+
let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
|
|
112
|
+
logger.log_error_with_stacktrace(&message, &stacktrace);
|
|
113
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
|
|
1
3
|
use crate::{
|
|
2
4
|
core::{
|
|
3
5
|
parser::statement::{ Statement, StatementKind },
|
|
@@ -16,9 +18,7 @@ pub fn resolve_loop(
|
|
|
16
18
|
) -> Statement {
|
|
17
19
|
let logger = Logger::new();
|
|
18
20
|
|
|
19
|
-
// Vérifie que stmt.value est bien une Map
|
|
20
21
|
if let Value::Map(value_map) = &stmt.value {
|
|
21
|
-
// Résolution de l'iterator
|
|
22
22
|
let iterator_value = match value_map.get("iterator") {
|
|
23
23
|
Some(Value::Identifier(ident)) => {
|
|
24
24
|
match module.variable_table.get(ident) {
|
|
@@ -33,13 +33,18 @@ pub fn resolve_loop(
|
|
|
33
33
|
Value::Null
|
|
34
34
|
}
|
|
35
35
|
None => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
+
}
|
|
43
48
|
}
|
|
44
49
|
}
|
|
45
50
|
}
|
|
@@ -64,7 +69,6 @@ pub fn resolve_loop(
|
|
|
64
69
|
}
|
|
65
70
|
};
|
|
66
71
|
|
|
67
|
-
// Résolution du body
|
|
68
72
|
let body_value = match value_map.get("body") {
|
|
69
73
|
Some(Value::Block(block)) => {
|
|
70
74
|
let mut resolved_block = Vec::new();
|
|
@@ -108,12 +112,10 @@ pub fn resolve_loop(
|
|
|
108
112
|
}
|
|
109
113
|
};
|
|
110
114
|
|
|
111
|
-
|
|
112
|
-
let mut resolved_map = std::collections::HashMap::new();
|
|
115
|
+
let mut resolved_map = HashMap::new();
|
|
113
116
|
resolved_map.insert("iterator".to_string(), iterator_value);
|
|
114
117
|
resolved_map.insert("body".to_string(), body_value);
|
|
115
118
|
|
|
116
|
-
// ✅ Reconstruit le StatementLoop à partir des éléments résolus
|
|
117
119
|
Statement {
|
|
118
120
|
kind: StatementKind::Loop,
|
|
119
121
|
value: Value::Map(resolved_map),
|
|
@@ -2,6 +2,7 @@ pub mod trigger;
|
|
|
2
2
|
pub mod loop_;
|
|
3
3
|
pub mod bank;
|
|
4
4
|
pub mod tempo;
|
|
5
|
+
pub mod group;
|
|
5
6
|
|
|
6
7
|
use std::collections::HashMap;
|
|
7
8
|
use crate::{
|
|
@@ -11,6 +12,7 @@ use crate::{
|
|
|
11
12
|
loader::ModuleLoader,
|
|
12
13
|
resolver::{
|
|
13
14
|
bank::resolve_bank,
|
|
15
|
+
group::resolve_group,
|
|
14
16
|
loop_::resolve_loop,
|
|
15
17
|
tempo::resolve_tempo,
|
|
16
18
|
trigger::resolve_trigger,
|
|
@@ -63,9 +65,9 @@ pub fn resolve_and_flatten_all_modules(
|
|
|
63
65
|
global_store: &mut GlobalStore
|
|
64
66
|
) -> HashMap<String, Vec<Statement>> {
|
|
65
67
|
let logger = Logger::new();
|
|
66
|
-
let snapshot = global_store.clone();
|
|
68
|
+
let snapshot = global_store.clone();
|
|
67
69
|
|
|
68
|
-
// 1.
|
|
70
|
+
// 1. Imports resolution
|
|
69
71
|
for (module_path, module) in global_store.modules.iter_mut() {
|
|
70
72
|
for (name, source_path) in &module.import_table.imports {
|
|
71
73
|
if let Value::String(source_path_str) = source_path {
|
|
@@ -96,8 +98,8 @@ pub fn resolve_and_flatten_all_modules(
|
|
|
96
98
|
}
|
|
97
99
|
}
|
|
98
100
|
|
|
99
|
-
// 2.
|
|
100
|
-
let mut resolved_map = HashMap::new();
|
|
101
|
+
// 2. Statements resolution
|
|
102
|
+
let mut resolved_map: HashMap<String, Vec<Statement>> = HashMap::new();
|
|
101
103
|
let store_snapshot = global_store.clone();
|
|
102
104
|
|
|
103
105
|
for (path, module) in &store_snapshot.modules {
|
|
@@ -135,10 +137,14 @@ pub fn resolve_and_flatten_all_modules(
|
|
|
135
137
|
}
|
|
136
138
|
|
|
137
139
|
StatementKind::Import { .. } | StatementKind::Export { .. } => {
|
|
138
|
-
// Rien à faire
|
|
139
140
|
resolved.push(stmt.clone());
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
StatementKind::Group => {
|
|
144
|
+
let resolved_stmt = resolve_group(&stmt, &module, &path, &store_snapshot);
|
|
145
|
+
resolved.push(resolved_stmt);
|
|
146
|
+
}
|
|
147
|
+
|
|
142
148
|
_ => {
|
|
143
149
|
resolved.push(stmt);
|
|
144
150
|
}
|
|
@@ -38,7 +38,6 @@ pub fn resolve_trigger(
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
// ✅ Résolution de duration si c'est un identifiant
|
|
42
41
|
if let Duration::Identifier(ident) = duration {
|
|
43
42
|
if let Some(val) = module.variable_table.get(ident) {
|
|
44
43
|
match val {
|
|
@@ -56,7 +55,6 @@ pub fn resolve_trigger(
|
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
// ✅ Résolution de value (params, effets)
|
|
60
58
|
final_value = match &stmt.value {
|
|
61
59
|
Value::Identifier(ident) => {
|
|
62
60
|
match module.variable_table.get(ident) {
|
|
@@ -87,7 +85,6 @@ pub fn resolve_trigger(
|
|
|
87
85
|
other => other.clone(),
|
|
88
86
|
};
|
|
89
87
|
|
|
90
|
-
// ✅ On reconstruit le Statement avec Trigger résolu
|
|
91
88
|
if let StatementKind::Trigger { entity, .. } = &stmt.kind {
|
|
92
89
|
return Statement {
|
|
93
90
|
kind: StatementKind::Trigger {
|
package/rust/lib.rs
CHANGED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
pub mod core;
|
|
2
|
+
pub mod utils;
|
|
3
|
+
pub mod config;
|
|
4
|
+
|
|
5
|
+
use serde::{ Deserialize, Serialize };
|
|
6
|
+
use wasm_bindgen::prelude::*;
|
|
7
|
+
use serde_wasm_bindgen::to_value;
|
|
8
|
+
|
|
9
|
+
use crate::core::{
|
|
10
|
+
parser::statement::{ Statement, StatementKind },
|
|
11
|
+
preprocessor::loader::ModuleLoader,
|
|
12
|
+
shared::value::Value,
|
|
13
|
+
store::global::GlobalStore,
|
|
14
|
+
utils::path::normalize_path,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
#[derive(Serialize, Deserialize)]
|
|
18
|
+
struct ParseResult {
|
|
19
|
+
ok: bool,
|
|
20
|
+
ast: String,
|
|
21
|
+
errors: Vec<ErrorResult>,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#[derive(Serialize, Deserialize)]
|
|
25
|
+
struct ErrorResult {
|
|
26
|
+
message: String,
|
|
27
|
+
line: usize,
|
|
28
|
+
column: usize,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#[wasm_bindgen]
|
|
32
|
+
pub fn parse(entry_path: &str, source: &str) -> Result<JsValue, JsValue> {
|
|
33
|
+
let statements = parse_internal_from_string(entry_path, source);
|
|
34
|
+
|
|
35
|
+
match statements {
|
|
36
|
+
Ok(value) => {
|
|
37
|
+
let ast_string = value;
|
|
38
|
+
to_value(&ast_string).map_err(|e|
|
|
39
|
+
JsValue::from_str(&format!("Error converting AST to JS value: {}", e))
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
Err(e) => { Err(JsValue::from_str(&format!("Error: {}", e))) }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fn parse_internal_from_string(virtual_path: &str, source: &str) -> Result<ParseResult, String> {
|
|
47
|
+
let entry_path = normalize_path(virtual_path);
|
|
48
|
+
let output_path = normalize_path("./temp");
|
|
49
|
+
|
|
50
|
+
let mut global_store = GlobalStore::new();
|
|
51
|
+
let loader = ModuleLoader::from_raw_source(
|
|
52
|
+
&entry_path,
|
|
53
|
+
&output_path,
|
|
54
|
+
source,
|
|
55
|
+
&mut global_store
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
let module = loader
|
|
59
|
+
.load_single_module(&mut global_store)
|
|
60
|
+
.map_err(|e| format!("Error loading module: {}", e))?;
|
|
61
|
+
|
|
62
|
+
let raw_ast = ast_to_string(module.statements.clone());
|
|
63
|
+
|
|
64
|
+
let found_errors = collect_errors_recursively(&module.statements);
|
|
65
|
+
|
|
66
|
+
let result = ParseResult {
|
|
67
|
+
ok: true,
|
|
68
|
+
ast: raw_ast,
|
|
69
|
+
errors: found_errors,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
Ok(result)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
fn collect_errors_recursively(statements: &[Statement]) -> Vec<ErrorResult> {
|
|
76
|
+
let mut errors: Vec<ErrorResult> = Vec::new();
|
|
77
|
+
|
|
78
|
+
for stmt in statements {
|
|
79
|
+
match &stmt.kind {
|
|
80
|
+
StatementKind::Unknown => {
|
|
81
|
+
errors.push(ErrorResult {
|
|
82
|
+
message: format!("Unknown statement at line {}:{}", stmt.line, stmt.column),
|
|
83
|
+
line: stmt.line,
|
|
84
|
+
column: stmt.column,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
StatementKind::Error { message } => {
|
|
88
|
+
errors.push(ErrorResult {
|
|
89
|
+
message: message.clone(),
|
|
90
|
+
line: stmt.line,
|
|
91
|
+
column: stmt.column,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
StatementKind::Loop => {
|
|
95
|
+
if let Some(body_statements) = extract_loop_body_statements(&stmt.value) {
|
|
96
|
+
errors.extend(collect_errors_recursively(body_statements));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
_ => {}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
errors
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
fn extract_loop_body_statements(value: &Value) -> Option<&[Statement]> {
|
|
107
|
+
if let Value::Map(map) = value {
|
|
108
|
+
if let Some(Value::Block(statements)) = map.get("body") {
|
|
109
|
+
return Some(statements);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
None
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fn ast_to_string(statements: Vec<Statement>) -> String {
|
|
116
|
+
serde_json::to_string_pretty(&statements).expect("Failed to serialize AST")
|
|
117
|
+
}
|
package/rust/main.rs
CHANGED
package/rust/utils/logger.rs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
use
|
|
1
|
+
#[cfg(feature = "cli")]
|
|
2
|
+
use crossterm::style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor};
|
|
3
|
+
use std::fmt::Write;
|
|
3
4
|
|
|
4
5
|
#[derive(Debug, Clone, PartialEq)]
|
|
5
6
|
pub enum LogLevel {
|
|
@@ -12,24 +13,43 @@ pub enum LogLevel {
|
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
#[derive(Debug, Clone)]
|
|
15
|
-
pub struct Logger
|
|
16
|
+
pub struct Logger;
|
|
16
17
|
|
|
17
18
|
impl Logger {
|
|
18
19
|
pub fn new() -> Self {
|
|
19
|
-
Logger
|
|
20
|
+
Logger
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
// --- log_message ---
|
|
24
|
+
|
|
25
|
+
#[cfg(feature = "cli")]
|
|
22
26
|
pub fn log_message(&self, level: LogLevel, message: &str) {
|
|
23
27
|
let formatted_status = self.format_status(level);
|
|
24
28
|
println!("🦊 {} {} {}", self.language_signature(), formatted_status, message);
|
|
25
29
|
}
|
|
26
30
|
|
|
31
|
+
#[cfg(not(feature = "cli"))]
|
|
32
|
+
pub fn log_message(&self, _level: LogLevel, _message: &str) {
|
|
33
|
+
// no-op for WASM
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// --- log_error_with_stacktrace ---
|
|
37
|
+
|
|
38
|
+
#[cfg(feature = "cli")]
|
|
27
39
|
pub fn log_error_with_stacktrace(&self, message: &str, stacktrace: &str) {
|
|
28
40
|
let formatted_status = self.format_status(LogLevel::Error);
|
|
29
41
|
println!("🦊 {} {} {}", self.language_signature(), formatted_status, message);
|
|
30
42
|
println!(" ↳ {}", stacktrace);
|
|
31
43
|
}
|
|
32
44
|
|
|
45
|
+
#[cfg(not(feature = "cli"))]
|
|
46
|
+
pub fn log_error_with_stacktrace(&self, _message: &str, _stacktrace: &str) {
|
|
47
|
+
// no-op for WASM
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// --- language_signature ---
|
|
51
|
+
|
|
52
|
+
#[cfg(feature = "cli")]
|
|
33
53
|
fn language_signature(&self) -> String {
|
|
34
54
|
let mut s = String::new();
|
|
35
55
|
|
|
@@ -43,12 +63,19 @@ impl Logger {
|
|
|
43
63
|
|
|
44
64
|
write!(&mut s, "{}", SetForegroundColor(Color::Grey)).unwrap();
|
|
45
65
|
s.push(']');
|
|
46
|
-
|
|
47
66
|
write!(&mut s, "{}", ResetColor).unwrap();
|
|
48
67
|
|
|
49
68
|
s
|
|
50
69
|
}
|
|
51
70
|
|
|
71
|
+
#[cfg(not(feature = "cli"))]
|
|
72
|
+
fn language_signature(&self) -> String {
|
|
73
|
+
"[Devalang]".to_string()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// --- format_status ---
|
|
77
|
+
|
|
78
|
+
#[cfg(feature = "cli")]
|
|
52
79
|
fn format_status(&self, level: LogLevel) -> String {
|
|
53
80
|
let mut s = String::new();
|
|
54
81
|
|
|
@@ -76,9 +103,21 @@ impl Logger {
|
|
|
76
103
|
s.push_str(status);
|
|
77
104
|
write!(&mut s, "{}", SetAttribute(Attribute::Reset)).unwrap();
|
|
78
105
|
s.push(']');
|
|
79
|
-
|
|
80
106
|
write!(&mut s, "{}", ResetColor).unwrap();
|
|
81
107
|
|
|
82
108
|
s
|
|
83
109
|
}
|
|
110
|
+
|
|
111
|
+
#[cfg(not(feature = "cli"))]
|
|
112
|
+
fn format_status(&self, level: LogLevel) -> String {
|
|
113
|
+
match level {
|
|
114
|
+
LogLevel::Success => "[SUCCESS]",
|
|
115
|
+
LogLevel::Error => "[ERROR]",
|
|
116
|
+
LogLevel::Info => "[INFO]",
|
|
117
|
+
LogLevel::Warning => "[WARNING]",
|
|
118
|
+
LogLevel::Watcher => "[WATCHER]",
|
|
119
|
+
LogLevel::Debug => "[DEBUG]",
|
|
120
|
+
}
|
|
121
|
+
.to_string()
|
|
122
|
+
}
|
|
84
123
|
}
|