@devaloop/devalang 0.0.1-alpha.7 → 0.0.1-alpha.8
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 +1 -1
- package/README.md +29 -10
- package/docs/CHANGELOG.md +22 -2
- package/docs/ROADMAP.md +2 -2
- package/docs/SYNTAX.md +41 -7
- package/docs/TODO.md +3 -3
- package/examples/condition.deva +24 -0
- package/examples/index.deva +4 -5
- package/examples/variables.deva +1 -1
- package/out-tsc/bin/devalang.exe +0 -0
- package/package.json +1 -1
- package/project-version.json +3 -3
- package/rust/cli/build.rs +6 -1
- package/rust/core/audio/evaluator.rs +31 -0
- package/rust/core/audio/interpreter/call.rs +42 -0
- package/rust/core/audio/interpreter/condition.rs +65 -0
- package/rust/core/audio/interpreter/driver.rs +204 -0
- package/rust/core/audio/interpreter/let_.rs +19 -0
- package/rust/core/audio/interpreter/load.rs +18 -0
- package/rust/core/audio/interpreter/loop_.rs +59 -0
- package/rust/core/audio/interpreter/mod.rs +11 -0
- package/rust/core/audio/interpreter/sleep.rs +36 -0
- package/rust/core/audio/interpreter/spawn.rs +65 -0
- package/rust/core/audio/interpreter/tempo.rs +16 -0
- package/rust/core/audio/interpreter/trigger.rs +69 -0
- package/rust/core/audio/loader/mod.rs +1 -0
- package/rust/core/audio/{loader.rs → loader/trigger.rs} +3 -1
- package/rust/core/audio/mod.rs +2 -1
- package/rust/core/audio/{render.rs → renderer.rs} +6 -2
- package/rust/core/builder/mod.rs +1 -1
- package/rust/core/debugger/lexer.rs +1 -1
- package/rust/core/debugger/mod.rs +1 -0
- package/rust/core/debugger/store.rs +25 -0
- package/rust/core/error/mod.rs +1 -1
- package/rust/core/lexer/handler/driver.rs +215 -0
- package/rust/core/lexer/handler/identifier.rs +2 -0
- package/rust/core/lexer/handler/mod.rs +3 -227
- package/rust/core/lexer/handler/operator.rs +44 -0
- package/rust/core/lexer/mod.rs +1 -1
- package/rust/core/lexer/token.rs +36 -9
- package/rust/core/parser/driver.rs +312 -0
- package/rust/core/parser/handler/at.rs +3 -7
- package/rust/core/parser/handler/bank.rs +5 -2
- package/rust/core/parser/handler/condition.rs +74 -0
- package/rust/core/parser/handler/dot.rs +1 -1
- package/rust/core/parser/handler/identifier.rs +38 -36
- package/rust/core/parser/handler/loop_.rs +1 -1
- package/rust/core/parser/handler/mod.rs +2 -1
- package/rust/core/parser/handler/tempo.rs +1 -1
- package/rust/core/parser/mod.rs +3 -237
- package/rust/core/parser/statement.rs +29 -36
- package/rust/core/preprocessor/loader.rs +7 -6
- package/rust/core/preprocessor/processor.rs +1 -1
- package/rust/core/preprocessor/resolver/call.rs +53 -0
- package/rust/core/preprocessor/resolver/condition.rs +66 -0
- package/rust/core/preprocessor/resolver/driver.rs +182 -0
- package/rust/core/preprocessor/resolver/group.rs +89 -84
- package/rust/core/preprocessor/resolver/mod.rs +5 -153
- package/rust/core/preprocessor/resolver/spawn.rs +53 -0
- package/rust/core/audio/interpreter.rs +0 -317
- package/rust/core/lexer/handler/equal.rs +0 -32
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
audio::{
|
|
3
|
+
engine::AudioEngine,
|
|
4
|
+
interpreter::{
|
|
5
|
+
call::interprete_call_statement,
|
|
6
|
+
condition::interprete_condition_statement,
|
|
7
|
+
let_::interprete_let_statement,
|
|
8
|
+
load::interprete_load_statement,
|
|
9
|
+
loop_::interprete_loop_statement,
|
|
10
|
+
sleep::interprete_sleep_statement,
|
|
11
|
+
spawn::interprete_spawn_statement,
|
|
12
|
+
tempo::interprete_tempo_statement,
|
|
13
|
+
trigger::interprete_trigger_statement,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
parser::statement::{ Statement, StatementKind },
|
|
17
|
+
store::variable::VariableTable,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
pub fn run_audio_program(
|
|
21
|
+
statements: &Vec<Statement>,
|
|
22
|
+
audio_engine: AudioEngine,
|
|
23
|
+
entry: String,
|
|
24
|
+
output: String
|
|
25
|
+
) -> (AudioEngine, f32, f32) {
|
|
26
|
+
let mut base_bpm = 120.0;
|
|
27
|
+
let mut base_duration = 60.0 / base_bpm;
|
|
28
|
+
|
|
29
|
+
let variable_table = audio_engine.variables.clone();
|
|
30
|
+
|
|
31
|
+
let (updated_audio_engine, base_bpm, max_end_time) = execute_audio_block(
|
|
32
|
+
audio_engine.clone(),
|
|
33
|
+
variable_table.clone(),
|
|
34
|
+
statements.clone(),
|
|
35
|
+
base_bpm.clone(),
|
|
36
|
+
base_duration.clone(),
|
|
37
|
+
0.0,
|
|
38
|
+
0.0
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
(updated_audio_engine, base_bpm, max_end_time)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
pub fn execute_audio_block(
|
|
45
|
+
mut audio_engine: AudioEngine,
|
|
46
|
+
mut variable_table: VariableTable,
|
|
47
|
+
mut statements: Vec<Statement>,
|
|
48
|
+
mut base_bpm: f32,
|
|
49
|
+
mut base_duration: f32,
|
|
50
|
+
mut max_end_time: f32,
|
|
51
|
+
mut cursor_time: f32
|
|
52
|
+
) -> (AudioEngine, f32, f32) {
|
|
53
|
+
for stmt in statements {
|
|
54
|
+
match &stmt.kind {
|
|
55
|
+
StatementKind::Load { .. } => {
|
|
56
|
+
if
|
|
57
|
+
let Some(new_variable_table) = interprete_load_statement(
|
|
58
|
+
&stmt,
|
|
59
|
+
&mut variable_table
|
|
60
|
+
)
|
|
61
|
+
{
|
|
62
|
+
variable_table = new_variable_table;
|
|
63
|
+
} else {
|
|
64
|
+
eprintln!("❌ Failed to interpret load statement: {:?}", stmt);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
StatementKind::Let { .. } => {
|
|
69
|
+
if
|
|
70
|
+
let Some(new_variable_table) = interprete_let_statement(
|
|
71
|
+
&stmt,
|
|
72
|
+
&mut variable_table
|
|
73
|
+
)
|
|
74
|
+
{
|
|
75
|
+
variable_table = new_variable_table;
|
|
76
|
+
} else {
|
|
77
|
+
eprintln!("❌ Failed to interpret let statement: {:?}", stmt);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
StatementKind::Tempo => {
|
|
82
|
+
if let Some((new_bpm, new_duration)) = interprete_tempo_statement(&stmt) {
|
|
83
|
+
base_bpm = new_bpm;
|
|
84
|
+
base_duration = new_duration;
|
|
85
|
+
} else {
|
|
86
|
+
eprintln!("❌ Failed to interpret tempo statement: {:?}", stmt);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
StatementKind::Trigger { .. } => {
|
|
91
|
+
if
|
|
92
|
+
let Some((new_cursor_time, new_max_end_time, updated_engine)) =
|
|
93
|
+
interprete_trigger_statement(
|
|
94
|
+
&stmt,
|
|
95
|
+
&mut audio_engine,
|
|
96
|
+
&variable_table,
|
|
97
|
+
base_duration,
|
|
98
|
+
cursor_time,
|
|
99
|
+
max_end_time
|
|
100
|
+
)
|
|
101
|
+
{
|
|
102
|
+
cursor_time = new_cursor_time;
|
|
103
|
+
max_end_time = new_max_end_time;
|
|
104
|
+
audio_engine = updated_engine;
|
|
105
|
+
} else {
|
|
106
|
+
eprintln!("❌ Failed to interpret trigger statement: {:?}", stmt);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
StatementKind::Spawn => {
|
|
111
|
+
if
|
|
112
|
+
let Some((new_cursor_time, new_max_end_time, updated_engine)) =
|
|
113
|
+
interprete_spawn_statement(
|
|
114
|
+
&stmt,
|
|
115
|
+
&mut audio_engine,
|
|
116
|
+
&variable_table,
|
|
117
|
+
base_bpm,
|
|
118
|
+
base_duration,
|
|
119
|
+
cursor_time,
|
|
120
|
+
max_end_time
|
|
121
|
+
)
|
|
122
|
+
{
|
|
123
|
+
cursor_time = new_cursor_time;
|
|
124
|
+
max_end_time = new_max_end_time;
|
|
125
|
+
audio_engine = updated_engine;
|
|
126
|
+
} else {
|
|
127
|
+
eprintln!("❌ Failed to interpret spawn statement: {:?}", stmt);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
StatementKind::Sleep => {
|
|
132
|
+
let (new_cursor, new_max) = interprete_sleep_statement(
|
|
133
|
+
&stmt,
|
|
134
|
+
cursor_time,
|
|
135
|
+
max_end_time
|
|
136
|
+
);
|
|
137
|
+
cursor_time = new_cursor;
|
|
138
|
+
max_end_time = new_max;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
StatementKind::Loop => {
|
|
142
|
+
let (loop_engine, new_max, new_cursor) = interprete_loop_statement(
|
|
143
|
+
&stmt,
|
|
144
|
+
audio_engine.clone(),
|
|
145
|
+
variable_table.clone(),
|
|
146
|
+
base_bpm,
|
|
147
|
+
base_duration,
|
|
148
|
+
max_end_time,
|
|
149
|
+
cursor_time
|
|
150
|
+
);
|
|
151
|
+
audio_engine = loop_engine;
|
|
152
|
+
cursor_time = new_cursor;
|
|
153
|
+
max_end_time = new_max;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
StatementKind::Call => {
|
|
157
|
+
let (call_engine, new_max, new_cursor) = interprete_call_statement(
|
|
158
|
+
&stmt,
|
|
159
|
+
audio_engine.clone(),
|
|
160
|
+
variable_table.clone(),
|
|
161
|
+
base_bpm,
|
|
162
|
+
base_duration,
|
|
163
|
+
max_end_time,
|
|
164
|
+
cursor_time
|
|
165
|
+
);
|
|
166
|
+
audio_engine = call_engine;
|
|
167
|
+
cursor_time = new_cursor;
|
|
168
|
+
max_end_time = new_max;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
StatementKind::If | StatementKind::ElseIf | StatementKind::Else => {
|
|
172
|
+
let (condition_engine, new_max, new_cursor) = interprete_condition_statement(
|
|
173
|
+
&stmt,
|
|
174
|
+
audio_engine.clone(),
|
|
175
|
+
variable_table.clone(),
|
|
176
|
+
base_bpm,
|
|
177
|
+
base_duration,
|
|
178
|
+
max_end_time,
|
|
179
|
+
cursor_time
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
audio_engine = condition_engine;
|
|
183
|
+
cursor_time = new_cursor;
|
|
184
|
+
max_end_time = new_max;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
| StatementKind::Bank
|
|
188
|
+
| StatementKind::Import { .. }
|
|
189
|
+
| StatementKind::Export { .. }
|
|
190
|
+
| StatementKind::Group
|
|
191
|
+
| StatementKind::Unknown => {
|
|
192
|
+
// NOTE: Ignoring unsupported statement kinds for now.
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
_ => {
|
|
196
|
+
eprintln!("Unsupported audio statement kind: {:?}", stmt);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
audio_engine.set_variables(variable_table);
|
|
202
|
+
|
|
203
|
+
(audio_engine, base_bpm, max_end_time)
|
|
204
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
audio::engine::AudioEngine,
|
|
3
|
+
parser::statement::{ Statement, StatementKind },
|
|
4
|
+
shared::value::Value,
|
|
5
|
+
store::variable::VariableTable,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
pub fn interprete_let_statement(
|
|
9
|
+
stmt: &Statement,
|
|
10
|
+
variable_table: &mut VariableTable
|
|
11
|
+
) -> Option<VariableTable> {
|
|
12
|
+
if let StatementKind::Let { name } = &stmt.kind {
|
|
13
|
+
variable_table.set(name.to_string(), stmt.value.clone());
|
|
14
|
+
|
|
15
|
+
return Some(variable_table.clone())
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
None
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
parser::statement::{ Statement, StatementKind },
|
|
3
|
+
shared::value::Value,
|
|
4
|
+
store::variable::VariableTable,
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
pub fn interprete_load_statement(
|
|
8
|
+
stmt: &Statement,
|
|
9
|
+
variable_table: &mut VariableTable
|
|
10
|
+
) -> Option<VariableTable> {
|
|
11
|
+
if let StatementKind::Load { source, alias } = &stmt.kind {
|
|
12
|
+
variable_table.set(alias.to_string(), Value::String(source.clone()));
|
|
13
|
+
|
|
14
|
+
return Some(variable_table.clone());
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
None
|
|
18
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
audio::{ engine::AudioEngine, interpreter::driver::execute_audio_block },
|
|
3
|
+
parser::statement::{ Statement, StatementKind },
|
|
4
|
+
shared::{ duration::Duration, value::Value },
|
|
5
|
+
store::variable::VariableTable,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
pub fn interprete_loop_statement(
|
|
9
|
+
stmt: &Statement,
|
|
10
|
+
audio_engine: AudioEngine,
|
|
11
|
+
variable_table: VariableTable,
|
|
12
|
+
base_bpm: f32,
|
|
13
|
+
base_duration: f32,
|
|
14
|
+
max_end_time: f32,
|
|
15
|
+
cursor_time: f32
|
|
16
|
+
) -> (AudioEngine, f32, f32) {
|
|
17
|
+
if let Value::Map(loop_value) = &stmt.value {
|
|
18
|
+
let loop_count = match loop_value.get("iterator") {
|
|
19
|
+
Some(Value::Number(n)) => *n as usize,
|
|
20
|
+
_ => {
|
|
21
|
+
eprintln!("❌ Loop iterator must be a number");
|
|
22
|
+
return (audio_engine, max_end_time, cursor_time);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
let loop_body = match loop_value.get("body") {
|
|
27
|
+
Some(Value::Block(body)) => body.clone(),
|
|
28
|
+
_ => {
|
|
29
|
+
eprintln!("❌ Loop body must be a block");
|
|
30
|
+
return (audio_engine, max_end_time, cursor_time);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
let mut engine = audio_engine;
|
|
35
|
+
let mut cur_time = cursor_time;
|
|
36
|
+
let mut max_time = max_end_time;
|
|
37
|
+
|
|
38
|
+
for _ in 0..loop_count {
|
|
39
|
+
let (eng, _, end_time) = execute_audio_block(
|
|
40
|
+
engine.clone(),
|
|
41
|
+
variable_table.clone(),
|
|
42
|
+
loop_body.clone(),
|
|
43
|
+
base_bpm,
|
|
44
|
+
base_duration,
|
|
45
|
+
max_time,
|
|
46
|
+
cur_time
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
engine = eng;
|
|
50
|
+
cur_time = end_time;
|
|
51
|
+
max_time = max_time.max(end_time);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
(engine, max_time, cur_time)
|
|
55
|
+
} else {
|
|
56
|
+
eprintln!("❌ Loop statement value is not a map");
|
|
57
|
+
(audio_engine, max_end_time, cursor_time)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
audio::{ engine::AudioEngine, loader::trigger::load_trigger },
|
|
3
|
+
parser::statement::{ Statement, StatementKind },
|
|
4
|
+
shared::{ duration::Duration, value::Value },
|
|
5
|
+
store::variable::VariableTable,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
pub fn interprete_sleep_statement(
|
|
9
|
+
stmt: &Statement,
|
|
10
|
+
cursor_time: f32,
|
|
11
|
+
max_end_time: f32,
|
|
12
|
+
) -> (f32, f32) {
|
|
13
|
+
let duration_secs = match &stmt.value {
|
|
14
|
+
Value::Number(ms) => *ms / 1000.0,
|
|
15
|
+
Value::String(s) if s.ends_with("ms") => {
|
|
16
|
+
s.trim_end_matches("ms").parse::<f32>().map(|ms| ms / 1000.0).unwrap_or_else(|_| {
|
|
17
|
+
eprintln!("❌ Invalid sleep value (ms): {}", s);
|
|
18
|
+
0.0
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
Value::String(s) if s.ends_with("s") => {
|
|
22
|
+
s.trim_end_matches("s").parse::<f32>().unwrap_or_else(|_| {
|
|
23
|
+
eprintln!("❌ Invalid sleep value (s): {}", s);
|
|
24
|
+
0.0
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
other => {
|
|
28
|
+
eprintln!("❌ Invalid sleep value: {:?}", other);
|
|
29
|
+
0.0
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
let new_cursor = cursor_time + duration_secs;
|
|
34
|
+
let new_max = max_end_time.max(new_cursor);
|
|
35
|
+
(new_cursor, new_max)
|
|
36
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
audio::{ engine::AudioEngine, interpreter::driver::execute_audio_block },
|
|
3
|
+
parser::statement::Statement,
|
|
4
|
+
shared::value::Value,
|
|
5
|
+
store::variable::VariableTable,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
pub fn interprete_spawn_statement(
|
|
9
|
+
stmt: &Statement,
|
|
10
|
+
audio_engine: &mut AudioEngine,
|
|
11
|
+
variable_table: &VariableTable,
|
|
12
|
+
base_bpm: f32,
|
|
13
|
+
base_duration: f32,
|
|
14
|
+
cursor_time: f32,
|
|
15
|
+
max_end_time: f32
|
|
16
|
+
) -> Option<(f32, f32, AudioEngine)> {
|
|
17
|
+
if let Value::String(identifier) = &stmt.value {
|
|
18
|
+
match variable_table.get(identifier) {
|
|
19
|
+
Some(Value::Map(map)) => {
|
|
20
|
+
if let Some(Value::Block(block)) = map.get("body") {
|
|
21
|
+
let mut local_max = cursor_time;
|
|
22
|
+
let mut updated_engine = audio_engine.clone();
|
|
23
|
+
|
|
24
|
+
for inner_stmt in block {
|
|
25
|
+
let (inner_engine, _, inner_end_time) = execute_audio_block(
|
|
26
|
+
updated_engine.clone(),
|
|
27
|
+
variable_table.clone(),
|
|
28
|
+
vec![inner_stmt.clone()],
|
|
29
|
+
base_bpm,
|
|
30
|
+
base_duration,
|
|
31
|
+
max_end_time,
|
|
32
|
+
cursor_time
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
updated_engine = inner_engine;
|
|
36
|
+
|
|
37
|
+
if inner_end_time > local_max {
|
|
38
|
+
local_max = inner_end_time;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let new_max_end_time = local_max.max(max_end_time);
|
|
43
|
+
|
|
44
|
+
return Some((local_max, new_max_end_time, updated_engine));
|
|
45
|
+
} else {
|
|
46
|
+
eprintln!("❌ Cannot spawn '{}': no valid body block", identifier);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
Some(other) => {
|
|
50
|
+
eprintln!(
|
|
51
|
+
"❌ Cannot spawn '{}': expected map with block, got {:?}",
|
|
52
|
+
identifier,
|
|
53
|
+
other
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
None => {
|
|
57
|
+
eprintln!("❌ Cannot spawn '{}': not found in variable table", identifier);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
eprintln!("❌ Invalid spawn statement: expected a string identifier, got {:?}", stmt.value);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
None
|
|
65
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
use crate::core::{ parser::statement::{ Statement, StatementKind }, shared::value::Value };
|
|
2
|
+
|
|
3
|
+
pub fn interprete_tempo_statement(stmt: &Statement) -> Option<(f32, f32)> {
|
|
4
|
+
if let StatementKind::Tempo = &stmt.kind {
|
|
5
|
+
if let Value::Number(bpm) = &stmt.value {
|
|
6
|
+
let bpm = *bpm as f32;
|
|
7
|
+
let duration = 60.0 / bpm;
|
|
8
|
+
|
|
9
|
+
return Some((bpm, duration));
|
|
10
|
+
} else {
|
|
11
|
+
eprintln!("❌ Invalid tempo value: {:?}", stmt.value);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
None
|
|
16
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
audio::{ engine::AudioEngine, loader::trigger::load_trigger },
|
|
3
|
+
parser::statement::{ Statement, StatementKind },
|
|
4
|
+
shared::{ duration::Duration, value::Value },
|
|
5
|
+
store::variable::VariableTable,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
pub fn interprete_trigger_statement(
|
|
9
|
+
stmt: &Statement,
|
|
10
|
+
audio_engine: &mut AudioEngine,
|
|
11
|
+
variable_table: &VariableTable,
|
|
12
|
+
base_duration: f32,
|
|
13
|
+
cursor_time: f32,
|
|
14
|
+
max_end_time: f32
|
|
15
|
+
) -> Option<(f32, f32, AudioEngine)> {
|
|
16
|
+
if let StatementKind::Trigger { entity, duration } = &stmt.kind {
|
|
17
|
+
if let Some(trigger_val) = variable_table.get(entity) {
|
|
18
|
+
let duration_secs = match duration {
|
|
19
|
+
Duration::Number(n) => *n,
|
|
20
|
+
|
|
21
|
+
Duration::Identifier(id) => {
|
|
22
|
+
if id == "auto" {
|
|
23
|
+
1.0
|
|
24
|
+
} else {
|
|
25
|
+
match variable_table.get(id) {
|
|
26
|
+
Some(Value::Number(n)) => *n,
|
|
27
|
+
Some(Value::Identifier(other)) if other == "auto" => 1.0,
|
|
28
|
+
Some(other) => {
|
|
29
|
+
eprintln!(
|
|
30
|
+
"❌ Invalid duration reference '{}': expected number, got {:?}",
|
|
31
|
+
id,
|
|
32
|
+
other
|
|
33
|
+
);
|
|
34
|
+
return None;
|
|
35
|
+
}
|
|
36
|
+
None => {
|
|
37
|
+
eprintln!("❌ Duration identifier '{}' not found", id);
|
|
38
|
+
return None;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
Duration::Auto => 1.0,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
let duration_final = duration_secs * base_duration;
|
|
48
|
+
|
|
49
|
+
let (src, _) = load_trigger(
|
|
50
|
+
trigger_val,
|
|
51
|
+
duration,
|
|
52
|
+
base_duration,
|
|
53
|
+
variable_table.clone()
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
let mut updated_engine = audio_engine.clone();
|
|
57
|
+
updated_engine.insert(&src, cursor_time, duration_final, None);
|
|
58
|
+
|
|
59
|
+
let new_cursor_time = cursor_time + duration_final;
|
|
60
|
+
let new_max_end_time = new_cursor_time.max(max_end_time);
|
|
61
|
+
|
|
62
|
+
return Some((new_cursor_time, new_max_end_time, updated_engine));
|
|
63
|
+
} else {
|
|
64
|
+
eprintln!("❌ Unknown trigger entity: {}", entity);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
None
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pub mod trigger;
|
|
@@ -26,7 +26,9 @@ pub fn load_trigger(
|
|
|
26
26
|
duration_as_secs = *num;
|
|
27
27
|
} else if let Some(Value::String(num_str)) = variable_table.get(duration_identifier) {
|
|
28
28
|
duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
|
|
29
|
-
} else if
|
|
29
|
+
} else if
|
|
30
|
+
let Some(Value::Identifier(num_str)) = variable_table.get(duration_identifier)
|
|
31
|
+
{
|
|
30
32
|
duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
|
|
31
33
|
} else {
|
|
32
34
|
eprintln!("❌ Invalid duration identifier: {}", duration_identifier);
|
package/rust/core/audio/mod.rs
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
use std::collections::HashMap;
|
|
2
2
|
|
|
3
3
|
use crate::{
|
|
4
|
-
core::{
|
|
4
|
+
core::{
|
|
5
|
+
audio::{ engine::AudioEngine, interpreter::driver::run_audio_program },
|
|
6
|
+
parser::statement::Statement,
|
|
7
|
+
store::global::GlobalStore,
|
|
8
|
+
},
|
|
5
9
|
utils::logger::{ LogLevel, Logger },
|
|
6
10
|
};
|
|
7
11
|
|
|
@@ -22,7 +26,7 @@ pub fn render_audio_with_modules(
|
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
// Interpret the statements to fill the audio buffer
|
|
25
|
-
let (mut audio_engine, module_base_bpm, module_max_end_time) =
|
|
29
|
+
let (mut audio_engine, module_base_bpm, module_max_end_time) = run_audio_program(
|
|
26
30
|
&statements,
|
|
27
31
|
audio_engine,
|
|
28
32
|
module_name.clone(),
|
package/rust/core/builder/mod.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use crate::core::audio::
|
|
1
|
+
use crate::core::audio::renderer::render_audio_with_modules;
|
|
2
2
|
use crate::core::parser::statement::Statement;
|
|
3
3
|
use crate::core::store::global::GlobalStore;
|
|
4
4
|
use std::{ collections::HashMap, fs::create_dir_all };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
use std::{ collections::HashMap, fs::create_dir_all };
|
|
2
|
+
use crate::core::{ debugger::Debugger, preprocessor::module::Module };
|
|
3
|
+
|
|
4
|
+
pub fn write_store_log_file(output_dir: &str, file_name: &str, modules: HashMap<String, Module>) {
|
|
5
|
+
let debugger = Debugger::new();
|
|
6
|
+
let mut content = String::new();
|
|
7
|
+
|
|
8
|
+
let log_directory = format!("{}/logs", output_dir);
|
|
9
|
+
create_dir_all(&log_directory).expect("Failed to create log directory");
|
|
10
|
+
|
|
11
|
+
for (path, module) in modules {
|
|
12
|
+
content.push_str(&format!("--- Module: {} ---\n", path));
|
|
13
|
+
|
|
14
|
+
for (index, var_value) in module.variable_table.variables.iter().enumerate() {
|
|
15
|
+
let var_name = var_value.0.clone();
|
|
16
|
+
let var_data = var_value.1;
|
|
17
|
+
|
|
18
|
+
content.push_str(&format!("{}: {:?} = {:?}\n", index + 1, var_name, var_data));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
content.push_str("\n");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
debugger.write_log_file(&log_directory, file_name, &content);
|
|
25
|
+
}
|
package/rust/core/error/mod.rs
CHANGED