@devaloop/devalang 0.0.1-alpha.3 → 0.0.1-alpha.4
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 +3 -3
- package/README.md +42 -25
- package/docs/CHANGELOG.md +17 -0
- package/docs/COMMANDS.md +31 -0
- package/docs/CONFIG.md +6 -4
- package/docs/ROADMAP.md +1 -1
- package/docs/TODO.md +4 -4
- package/examples/index.deva +7 -1
- package/examples/samples/hat-808.wav +0 -0
- package/out-tsc/bin/devalang.exe +0 -0
- package/package.json +41 -41
- package/project-version.json +6 -6
- package/rust/audio/engine.rs +130 -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 -6
- package/rust/cli/mod.rs +29 -0
- package/rust/cli/play.rs +191 -0
- package/rust/config/mod.rs +3 -2
- package/rust/core/builder/mod.rs +48 -0
- package/rust/core/debugger/lexer.rs +20 -5
- package/rust/core/debugger/preprocessor.rs +9 -5
- package/rust/core/preprocessor/loader.rs +26 -15
- package/rust/core/preprocessor/resolver/bank.rs +46 -0
- package/rust/core/preprocessor/resolver/loop_.rs +143 -0
- package/rust/core/preprocessor/resolver/mod.rs +152 -0
- package/rust/core/preprocessor/resolver/tempo.rs +49 -0
- package/rust/core/preprocessor/resolver/trigger.rs +114 -0
- package/rust/main.rs +6 -0
- package/rust/utils/watcher.rs +10 -2
- package/rust/core/preprocessor/resolver.rs +0 -372
|
@@ -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,143 @@
|
|
|
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
|
+
log_type_error(
|
|
37
|
+
&logger,
|
|
38
|
+
module,
|
|
39
|
+
stmt,
|
|
40
|
+
format!("Loop iterator '{ident}' not found")
|
|
41
|
+
);
|
|
42
|
+
Value::Null
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
Some(Value::Number(n)) => Value::Number(*n),
|
|
47
|
+
Some(other) => {
|
|
48
|
+
log_type_error(
|
|
49
|
+
&logger,
|
|
50
|
+
module,
|
|
51
|
+
stmt,
|
|
52
|
+
format!("Unexpected value for loop iterator: {:?}", other)
|
|
53
|
+
);
|
|
54
|
+
Value::Null
|
|
55
|
+
}
|
|
56
|
+
None => {
|
|
57
|
+
log_type_error(
|
|
58
|
+
&logger,
|
|
59
|
+
module,
|
|
60
|
+
stmt,
|
|
61
|
+
"Missing 'iterator' key in loop statement map".to_string()
|
|
62
|
+
);
|
|
63
|
+
Value::Null
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Résolution du body
|
|
68
|
+
let body_value = match value_map.get("body") {
|
|
69
|
+
Some(Value::Block(block)) => {
|
|
70
|
+
let mut resolved_block = Vec::new();
|
|
71
|
+
for ref statement in block.clone() {
|
|
72
|
+
match &statement.kind {
|
|
73
|
+
StatementKind::Trigger { entity, duration } => {
|
|
74
|
+
let resolved = resolve_trigger(
|
|
75
|
+
&mut statement.clone(),
|
|
76
|
+
&entity,
|
|
77
|
+
&mut duration.clone(),
|
|
78
|
+
module,
|
|
79
|
+
path,
|
|
80
|
+
global_store
|
|
81
|
+
);
|
|
82
|
+
resolved_block.push(resolved);
|
|
83
|
+
}
|
|
84
|
+
_ => {
|
|
85
|
+
println!("Unhandled loop body statement: {:?}", statement);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
Value::Block(resolved_block)
|
|
90
|
+
}
|
|
91
|
+
Some(other) => {
|
|
92
|
+
log_type_error(
|
|
93
|
+
&logger,
|
|
94
|
+
module,
|
|
95
|
+
stmt,
|
|
96
|
+
format!("Unexpected value for loop body: {:?}", other)
|
|
97
|
+
);
|
|
98
|
+
Value::Null
|
|
99
|
+
}
|
|
100
|
+
None => {
|
|
101
|
+
log_type_error(
|
|
102
|
+
&logger,
|
|
103
|
+
module,
|
|
104
|
+
stmt,
|
|
105
|
+
"Missing 'body' key in loop statement map".to_string()
|
|
106
|
+
);
|
|
107
|
+
Value::Null
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// ✅ Reconstruit proprement la valeur résolue
|
|
112
|
+
let mut resolved_map = std::collections::HashMap::new();
|
|
113
|
+
resolved_map.insert("iterator".to_string(), iterator_value);
|
|
114
|
+
resolved_map.insert("body".to_string(), body_value);
|
|
115
|
+
|
|
116
|
+
// ✅ Reconstruit le StatementLoop à partir des éléments résolus
|
|
117
|
+
Statement {
|
|
118
|
+
kind: StatementKind::Loop,
|
|
119
|
+
value: Value::Map(resolved_map),
|
|
120
|
+
..stmt.clone()
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
log_type_error(
|
|
124
|
+
&logger,
|
|
125
|
+
module,
|
|
126
|
+
stmt,
|
|
127
|
+
format!("Expected a map for loop value, found {:?}", stmt.value)
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
Statement {
|
|
131
|
+
kind: StatementKind::Error {
|
|
132
|
+
message: "Expected a map for loop value".to_string(),
|
|
133
|
+
},
|
|
134
|
+
value: Value::Null,
|
|
135
|
+
..stmt.clone()
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
fn log_type_error(logger: &Logger, module: &Module, stmt: &Statement, message: String) {
|
|
141
|
+
let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
|
|
142
|
+
logger.log_error_with_stacktrace(&message, &stacktrace);
|
|
143
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
pub mod trigger;
|
|
2
|
+
pub mod loop_;
|
|
3
|
+
pub mod bank;
|
|
4
|
+
pub mod tempo;
|
|
5
|
+
|
|
6
|
+
use std::collections::HashMap;
|
|
7
|
+
use crate::{
|
|
8
|
+
core::{
|
|
9
|
+
parser::statement::{ self, Statement, StatementKind },
|
|
10
|
+
preprocessor::{
|
|
11
|
+
loader::ModuleLoader,
|
|
12
|
+
resolver::{
|
|
13
|
+
bank::resolve_bank,
|
|
14
|
+
loop_::resolve_loop,
|
|
15
|
+
tempo::resolve_tempo,
|
|
16
|
+
trigger::resolve_trigger,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
shared::{ duration::Duration, value::Value },
|
|
20
|
+
store::global::GlobalStore,
|
|
21
|
+
utils::validation::{ is_valid_entity, is_valid_identifier },
|
|
22
|
+
},
|
|
23
|
+
utils::logger::Logger,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
pub fn resolve_all_modules(module_loader: &ModuleLoader, global_store: &mut GlobalStore) {
|
|
27
|
+
for module in global_store.clone().modules.values_mut() {
|
|
28
|
+
resolve_imports(module_loader, global_store);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
pub fn resolve_imports(module_loader: &ModuleLoader, global_store: &mut GlobalStore) {
|
|
33
|
+
for (module_path, module) in global_store.clone().modules.iter_mut() {
|
|
34
|
+
for (name, source_path) in &module.import_table.imports {
|
|
35
|
+
match source_path {
|
|
36
|
+
Value::String(source_path) => {
|
|
37
|
+
if let Some(source_module) = global_store.modules.get(source_path) {
|
|
38
|
+
if let Some(value) = source_module.export_table.get_export(name) {
|
|
39
|
+
module.variable_table.set(name.clone(), value.clone());
|
|
40
|
+
} else {
|
|
41
|
+
println!(
|
|
42
|
+
"[warn] '{module_path}': '{name}' not found in exports of '{source_path}'"
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
println!(
|
|
47
|
+
"[warn] '{module_path}': cannot find source module '{source_path}'"
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
_ => {
|
|
52
|
+
println!(
|
|
53
|
+
"[warn] '{module_path}': expected string for import source, found {:?}",
|
|
54
|
+
source_path
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pub fn resolve_and_flatten_all_modules(
|
|
63
|
+
global_store: &mut GlobalStore
|
|
64
|
+
) -> HashMap<String, Vec<Statement>> {
|
|
65
|
+
let logger = Logger::new();
|
|
66
|
+
let snapshot = global_store.clone(); // pour éviter les emprunts mutables
|
|
67
|
+
|
|
68
|
+
// 1. Résolution des imports
|
|
69
|
+
for (module_path, module) in global_store.modules.iter_mut() {
|
|
70
|
+
for (name, source_path) in &module.import_table.imports {
|
|
71
|
+
if let Value::String(source_path_str) = source_path {
|
|
72
|
+
match snapshot.modules.get(source_path_str) {
|
|
73
|
+
Some(source_module) => {
|
|
74
|
+
if let Some(value) = source_module.export_table.get_export(name) {
|
|
75
|
+
module.variable_table.set(name.clone(), value.clone());
|
|
76
|
+
} else {
|
|
77
|
+
logger.log_error_with_stacktrace(
|
|
78
|
+
&format!("'{name}' not found in exports of '{source_path_str}'"),
|
|
79
|
+
module_path
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
None => {
|
|
84
|
+
logger.log_error_with_stacktrace(
|
|
85
|
+
&format!("Cannot find source module '{source_path_str}'"),
|
|
86
|
+
module_path
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
logger.log_error_with_stacktrace(
|
|
92
|
+
&format!("Expected string for import source, found {:?}", source_path),
|
|
93
|
+
module_path
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 2. Résolution des statements
|
|
100
|
+
let mut resolved_map = HashMap::new();
|
|
101
|
+
let store_snapshot = global_store.clone();
|
|
102
|
+
|
|
103
|
+
for (path, module) in &store_snapshot.modules {
|
|
104
|
+
let mut resolved = Vec::new();
|
|
105
|
+
|
|
106
|
+
for stmt in &module.statements {
|
|
107
|
+
let mut stmt = stmt.clone();
|
|
108
|
+
|
|
109
|
+
match &stmt.kind {
|
|
110
|
+
StatementKind::Trigger { entity, duration } => {
|
|
111
|
+
let resolved_stmt = resolve_trigger(
|
|
112
|
+
&stmt,
|
|
113
|
+
entity.as_str(),
|
|
114
|
+
&mut duration.clone(),
|
|
115
|
+
&module,
|
|
116
|
+
&path,
|
|
117
|
+
&store_snapshot
|
|
118
|
+
);
|
|
119
|
+
resolved.push(resolved_stmt);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
StatementKind::Loop => {
|
|
123
|
+
let resolved_stmt = resolve_loop(&stmt, &module, &path, &store_snapshot);
|
|
124
|
+
resolved.push(resolved_stmt);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
StatementKind::Bank => {
|
|
128
|
+
let resolved_stmt = resolve_bank(&stmt, &module, &path, &store_snapshot);
|
|
129
|
+
resolved.push(resolved_stmt);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
StatementKind::Tempo => {
|
|
133
|
+
let resolved_stmt = resolve_tempo(&stmt, &module, &path, &store_snapshot);
|
|
134
|
+
resolved.push(resolved_stmt);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
StatementKind::Import { .. } | StatementKind::Export { .. } => {
|
|
138
|
+
// Rien à faire
|
|
139
|
+
resolved.push(stmt.clone());
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
_ => {
|
|
143
|
+
resolved.push(stmt);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
resolved_map.insert(path.clone(), resolved);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
resolved_map
|
|
152
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
};
|
|
10
|
+
|
|
11
|
+
pub fn resolve_tempo(
|
|
12
|
+
stmt: &Statement,
|
|
13
|
+
module: &Module,
|
|
14
|
+
path: &str,
|
|
15
|
+
_global_store: &GlobalStore
|
|
16
|
+
) -> Statement {
|
|
17
|
+
let mut new_stmt = stmt.clone();
|
|
18
|
+
let logger = Logger::new();
|
|
19
|
+
|
|
20
|
+
match &stmt.value {
|
|
21
|
+
Value::Identifier(ident) => {
|
|
22
|
+
if let Some(val) = module.variable_table.get(ident) {
|
|
23
|
+
new_stmt.value = val.clone();
|
|
24
|
+
} else {
|
|
25
|
+
let message = format!("Tempo identifier '{ident}' not found in variable table");
|
|
26
|
+
logger.log_error_with_stacktrace(&message, &module.path);
|
|
27
|
+
new_stmt.kind = StatementKind::Error {
|
|
28
|
+
message,
|
|
29
|
+
};
|
|
30
|
+
new_stmt.value = Value::Null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Value::Number(_) => {
|
|
35
|
+
// Already resolved, no modification needed
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
other => {
|
|
39
|
+
let message = format!("Expected a number or identifier for tempo, found {:?}", other);
|
|
40
|
+
logger.log_error_with_stacktrace(&message, &module.path);
|
|
41
|
+
new_stmt.kind = StatementKind::Error {
|
|
42
|
+
message: "Expected a number or identifier for tempo".to_string(),
|
|
43
|
+
};
|
|
44
|
+
new_stmt.value = Value::Null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
new_stmt
|
|
49
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
|
|
3
|
+
use crate::{
|
|
4
|
+
core::{
|
|
5
|
+
parser::statement::{ Statement, StatementKind },
|
|
6
|
+
preprocessor::module::Module,
|
|
7
|
+
shared::{ duration::Duration, value::Value },
|
|
8
|
+
store::global::GlobalStore,
|
|
9
|
+
utils::validation::is_valid_entity,
|
|
10
|
+
},
|
|
11
|
+
utils::logger::Logger,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
pub fn resolve_trigger(
|
|
15
|
+
stmt: &Statement,
|
|
16
|
+
entity: &str,
|
|
17
|
+
duration: &mut Duration,
|
|
18
|
+
module: &Module,
|
|
19
|
+
path: &str,
|
|
20
|
+
global_store: &GlobalStore
|
|
21
|
+
) -> Statement {
|
|
22
|
+
let logger = Logger::new();
|
|
23
|
+
|
|
24
|
+
let mut final_duration = duration.clone();
|
|
25
|
+
let mut final_value = stmt.value.clone();
|
|
26
|
+
|
|
27
|
+
if !is_valid_entity(entity, module, global_store) {
|
|
28
|
+
let message = format!("Invalid entity '{}', expected a valid identifier", entity);
|
|
29
|
+
let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
|
|
30
|
+
logger.log_error_with_stacktrace(&message, &stacktrace);
|
|
31
|
+
|
|
32
|
+
return Statement {
|
|
33
|
+
kind: stmt.kind.clone(),
|
|
34
|
+
value: Value::Null,
|
|
35
|
+
line: stmt.line,
|
|
36
|
+
column: stmt.column,
|
|
37
|
+
indent: stmt.indent,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ✅ Résolution de duration si c'est un identifiant
|
|
42
|
+
if let Duration::Identifier(ident) = duration {
|
|
43
|
+
if let Some(val) = module.variable_table.get(ident) {
|
|
44
|
+
match val {
|
|
45
|
+
Value::Number(num) => {
|
|
46
|
+
final_duration = Duration::Number(*num);
|
|
47
|
+
}
|
|
48
|
+
Value::String(s) => {
|
|
49
|
+
final_duration = Duration::Identifier(s.clone());
|
|
50
|
+
}
|
|
51
|
+
Value::Identifier(id) if id == "auto" => {
|
|
52
|
+
final_duration = Duration::Auto;
|
|
53
|
+
}
|
|
54
|
+
_ => {}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ✅ Résolution de value (params, effets)
|
|
60
|
+
final_value = match &stmt.value {
|
|
61
|
+
Value::Identifier(ident) => {
|
|
62
|
+
match module.variable_table.get(ident) {
|
|
63
|
+
Some(val) => val.clone(),
|
|
64
|
+
None => {
|
|
65
|
+
let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
|
|
66
|
+
let message = format!(
|
|
67
|
+
"'{path}': value identifier '{ident}' not found in variable table"
|
|
68
|
+
);
|
|
69
|
+
logger.log_error_with_stacktrace(&message, &stacktrace);
|
|
70
|
+
Value::Null
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
Value::Map(map) => {
|
|
75
|
+
let mut resolved_map = HashMap::new();
|
|
76
|
+
for (k, v) in map.iter() {
|
|
77
|
+
let resolved_v = match v {
|
|
78
|
+
Value::Identifier(id) => {
|
|
79
|
+
module.variable_table.get(id).cloned().unwrap_or(Value::Null)
|
|
80
|
+
}
|
|
81
|
+
other => other.clone(),
|
|
82
|
+
};
|
|
83
|
+
resolved_map.insert(k.clone(), resolved_v);
|
|
84
|
+
}
|
|
85
|
+
Value::Map(resolved_map)
|
|
86
|
+
}
|
|
87
|
+
other => other.clone(),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// ✅ On reconstruit le Statement avec Trigger résolu
|
|
91
|
+
if let StatementKind::Trigger { entity, .. } = &stmt.kind {
|
|
92
|
+
return Statement {
|
|
93
|
+
kind: StatementKind::Trigger {
|
|
94
|
+
entity: entity.to_string(),
|
|
95
|
+
duration: final_duration,
|
|
96
|
+
},
|
|
97
|
+
value: final_value,
|
|
98
|
+
line: stmt.line,
|
|
99
|
+
column: stmt.column,
|
|
100
|
+
indent: stmt.indent,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return Statement {
|
|
105
|
+
kind: StatementKind::Trigger {
|
|
106
|
+
entity: entity.to_string(),
|
|
107
|
+
duration: final_duration,
|
|
108
|
+
},
|
|
109
|
+
value: final_value,
|
|
110
|
+
line: stmt.line,
|
|
111
|
+
column: stmt.column,
|
|
112
|
+
indent: stmt.indent,
|
|
113
|
+
};
|
|
114
|
+
}
|
package/rust/main.rs
CHANGED
|
@@ -2,6 +2,7 @@ pub mod core;
|
|
|
2
2
|
pub mod cli;
|
|
3
3
|
pub mod utils;
|
|
4
4
|
pub mod config;
|
|
5
|
+
pub mod audio;
|
|
5
6
|
|
|
6
7
|
use std::io;
|
|
7
8
|
use cli::{ Cli };
|
|
@@ -11,6 +12,7 @@ use crate::{
|
|
|
11
12
|
build::handle_build_command,
|
|
12
13
|
check::handle_check_command,
|
|
13
14
|
init::handle_init_command,
|
|
15
|
+
play::handle_play_command,
|
|
14
16
|
template::{ handle_template_info_command, handle_template_list_command },
|
|
15
17
|
Commands,
|
|
16
18
|
TemplateCommand,
|
|
@@ -51,6 +53,10 @@ fn main() -> io::Result<()> {
|
|
|
51
53
|
handle_build_command(config, entry, output, watch);
|
|
52
54
|
}
|
|
53
55
|
|
|
56
|
+
Commands::Play { entry, output, watch, repeat } => {
|
|
57
|
+
handle_play_command(config, entry, output, watch, repeat);
|
|
58
|
+
}
|
|
59
|
+
|
|
54
60
|
_ => {}
|
|
55
61
|
}
|
|
56
62
|
|
package/rust/utils/watcher.rs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
use notify::{ Watcher, RecursiveMode, Config, RecommendedWatcher };
|
|
2
2
|
use std::sync::mpsc::channel;
|
|
3
3
|
|
|
4
|
+
use std::time::{ Duration, Instant };
|
|
5
|
+
|
|
4
6
|
pub fn watch_directory<F>(entry: String, callback: F) -> notify::Result<()>
|
|
5
7
|
where F: Fn() + Send + 'static
|
|
6
8
|
{
|
|
@@ -9,13 +11,19 @@ pub fn watch_directory<F>(entry: String, callback: F) -> notify::Result<()>
|
|
|
9
11
|
let mut watcher: RecommendedWatcher = Watcher::new(tx, Config::default())?;
|
|
10
12
|
watcher.watch(&entry.as_ref(), RecursiveMode::Recursive)?;
|
|
11
13
|
|
|
14
|
+
let mut last_trigger = Instant::now();
|
|
15
|
+
|
|
12
16
|
loop {
|
|
13
17
|
match rx.recv() {
|
|
14
18
|
Ok(_) => {
|
|
15
|
-
|
|
19
|
+
let now = Instant::now();
|
|
20
|
+
if now.duration_since(last_trigger) > Duration::from_millis(200) {
|
|
21
|
+
callback();
|
|
22
|
+
last_trigger = now;
|
|
23
|
+
}
|
|
16
24
|
}
|
|
17
25
|
Err(e) => {
|
|
18
|
-
eprintln!("Channel error
|
|
26
|
+
eprintln!("Channel error: {:?}", e);
|
|
19
27
|
break;
|
|
20
28
|
}
|
|
21
29
|
}
|