@devaloop/devalang 0.0.1-alpha.7 → 0.0.1-alpha.9
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 +31 -16
- package/docs/CHANGELOG.md +49 -2
- package/docs/ROADMAP.md +2 -2
- package/docs/SYNTAX.md +41 -7
- package/docs/TODO.md +3 -3
- package/examples/condition.deva +20 -0
- package/examples/group.deva +3 -3
- package/examples/index.deva +9 -8
- package/examples/loop.deva +10 -8
- package/examples/synth.deva +14 -0
- package/examples/variables.deva +2 -2
- package/out-tsc/bin/devalang.exe +0 -0
- package/out-tsc/scripts/version/fetch.js +1 -5
- package/package.json +1 -1
- package/project-version.json +3 -3
- package/rust/cli/build.rs +6 -1
- package/rust/core/audio/engine.rs +89 -12
- package/rust/core/audio/evaluator.rs +31 -0
- package/rust/core/audio/interpreter/arrow_call.rs +129 -0
- package/rust/core/audio/interpreter/call.rs +64 -0
- package/rust/core/audio/interpreter/condition.rs +69 -0
- package/rust/core/audio/interpreter/driver.rs +216 -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 +67 -0
- package/rust/core/audio/interpreter/mod.rs +12 -0
- package/rust/core/audio/interpreter/sleep.rs +36 -0
- package/rust/core/audio/interpreter/spawn.rs +66 -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/renderer.rs +54 -0
- 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/arrow.rs +31 -0
- package/rust/core/lexer/handler/driver.rs +226 -0
- package/rust/core/lexer/handler/identifier.rs +3 -0
- package/rust/core/lexer/handler/mod.rs +4 -227
- package/rust/core/lexer/handler/operator.rs +44 -0
- package/rust/core/lexer/mod.rs +25 -4
- package/rust/core/lexer/token.rs +40 -9
- package/rust/core/parser/driver.rs +331 -0
- package/rust/core/parser/handler/arrow_call.rs +126 -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/call.rs +41 -0
- package/rust/core/parser/handler/identifier/group.rs +75 -0
- package/rust/core/parser/handler/identifier/let_.rs +133 -0
- package/rust/core/parser/handler/identifier/mod.rs +51 -0
- package/rust/core/parser/handler/identifier/sleep.rs +33 -0
- package/rust/core/parser/handler/identifier/spawn.rs +41 -0
- package/rust/core/parser/handler/identifier/synth.rs +65 -0
- package/rust/core/parser/handler/loop_.rs +25 -19
- package/rust/core/parser/handler/mod.rs +3 -1
- package/rust/core/parser/handler/tempo.rs +1 -1
- package/rust/core/parser/mod.rs +3 -237
- package/rust/core/parser/statement.rs +36 -35
- package/rust/core/preprocessor/loader.rs +64 -49
- package/rust/core/preprocessor/module.rs +3 -6
- package/rust/core/preprocessor/processor.rs +13 -4
- package/rust/core/preprocessor/resolver/call.rs +123 -0
- package/rust/core/preprocessor/resolver/condition.rs +92 -0
- package/rust/core/preprocessor/resolver/driver.rs +227 -0
- package/rust/core/preprocessor/resolver/group.rs +35 -87
- package/rust/core/preprocessor/resolver/let_.rs +31 -0
- package/rust/core/preprocessor/resolver/loop_.rs +62 -116
- package/rust/core/preprocessor/resolver/mod.rs +9 -153
- package/rust/core/preprocessor/resolver/spawn.rs +58 -0
- package/rust/core/preprocessor/resolver/synth.rs +50 -0
- package/rust/core/preprocessor/resolver/trigger.rs +51 -50
- package/rust/core/preprocessor/resolver/value.rs +78 -0
- package/rust/core/utils/path.rs +17 -32
- package/rust/core/utils/validation.rs +30 -28
- package/typescript/scripts/version/fetch.ts +1 -6
- package/rust/core/audio/interpreter.rs +0 -317
- package/rust/core/audio/render.rs +0 -53
- package/rust/core/lexer/handler/equal.rs +0 -32
- package/rust/core/parser/handler/identifier.rs +0 -260
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
use crate::core::{ shared::value::Value, store::variable::VariableTable };
|
|
2
|
+
|
|
3
|
+
pub fn evaluate_condition_string(expr: &str, vars: &VariableTable) -> bool {
|
|
4
|
+
let tokens: Vec<&str> = expr.split_whitespace().collect();
|
|
5
|
+
if tokens.len() != 3 {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let left = tokens[0];
|
|
10
|
+
let op = tokens[1];
|
|
11
|
+
let right = tokens[2];
|
|
12
|
+
|
|
13
|
+
let left_val = match vars.get(left) {
|
|
14
|
+
Some(Value::Number(n)) => *n,
|
|
15
|
+
_ => {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
let right_val: f32 = right.parse().unwrap_or(0.0);
|
|
21
|
+
|
|
22
|
+
match op {
|
|
23
|
+
">" => left_val > right_val,
|
|
24
|
+
"<" => left_val < right_val,
|
|
25
|
+
">=" => left_val >= right_val,
|
|
26
|
+
"<=" => left_val <= right_val,
|
|
27
|
+
"==" => (left_val - right_val).abs() < f32::EPSILON,
|
|
28
|
+
"!=" => (left_val - right_val).abs() > f32::EPSILON,
|
|
29
|
+
_ => false,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
audio::engine::AudioEngine,
|
|
3
|
+
parser::statement::{ Statement, StatementKind },
|
|
4
|
+
shared::value::Value,
|
|
5
|
+
store::variable::VariableTable,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
use std::collections::HashMap;
|
|
9
|
+
|
|
10
|
+
pub fn interprete_call_arrow_statement(
|
|
11
|
+
stmt: &Statement,
|
|
12
|
+
audio_engine: &mut AudioEngine,
|
|
13
|
+
variable_table: &VariableTable,
|
|
14
|
+
base_bpm: f32,
|
|
15
|
+
base_duration: f32,
|
|
16
|
+
max_end_time: &mut f32,
|
|
17
|
+
mut cursor_time: Option<&mut f32>,
|
|
18
|
+
update_cursor: bool
|
|
19
|
+
) -> (f32, f32) {
|
|
20
|
+
let cursor_copy = cursor_time
|
|
21
|
+
.as_ref()
|
|
22
|
+
.map(|c| **c)
|
|
23
|
+
.unwrap_or(0.0);
|
|
24
|
+
|
|
25
|
+
if let StatementKind::ArrowCall { target, method, args } = &stmt.kind {
|
|
26
|
+
let Some(Value::Map(synth_map)) = variable_table.get(target) else {
|
|
27
|
+
println!("❌ Synth '{}' not found in variable table", target);
|
|
28
|
+
return (*max_end_time, cursor_copy);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
let Some(Value::String(entity)) = synth_map.get("entity") else {
|
|
32
|
+
println!("❌ Missing 'entity' key in synth '{}'.", target);
|
|
33
|
+
return (*max_end_time, cursor_copy);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
if entity != "synth" {
|
|
37
|
+
println!("❌ '{}' is not a synth, entity is '{}'.", target, entity);
|
|
38
|
+
return (*max_end_time, cursor_copy);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let Some(Value::Map(value_map)) = synth_map.get("value") else {
|
|
42
|
+
println!("❌ Missing 'value' map in synth '{}'.", target);
|
|
43
|
+
return (*max_end_time, cursor_copy);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
let Some(Value::String(waveform)) = value_map.get("waveform") else {
|
|
47
|
+
println!("❌ Missing or invalid 'waveform' in synth '{}'.", target);
|
|
48
|
+
return (*max_end_time, cursor_copy);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
let Some(Value::Map(params)) = value_map.get("parameters") else {
|
|
52
|
+
println!("❌ Missing or invalid 'parameters' in synth '{}'.", target);
|
|
53
|
+
return (*max_end_time, cursor_copy);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
let freq = extract_f32(params, "freq").unwrap_or(440.0);
|
|
57
|
+
let amp = extract_f32(params, "amp").unwrap_or(1.0);
|
|
58
|
+
|
|
59
|
+
if method == "note" {
|
|
60
|
+
let Some(Value::Identifier(note_name)) = args.get(0) else {
|
|
61
|
+
println!("❌ Invalid or missing argument for 'note' method on '{}'.", target);
|
|
62
|
+
return (*max_end_time, cursor_copy);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
let mut final_note_params = HashMap::new();
|
|
66
|
+
if let Some(Value::Map(note_params)) = args.get(1) {
|
|
67
|
+
for (key, value) in note_params {
|
|
68
|
+
final_note_params.insert(key.clone(), value.clone());
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let duration_ms = extract_f32(&final_note_params, "duration").unwrap_or(base_duration);
|
|
73
|
+
let duration_secs = duration_ms / 1000.0;
|
|
74
|
+
|
|
75
|
+
let final_freq = note_to_freq(note_name);
|
|
76
|
+
let start_time = cursor_copy;
|
|
77
|
+
let end_time = start_time + duration_secs;
|
|
78
|
+
|
|
79
|
+
audio_engine.insert_note(
|
|
80
|
+
waveform.clone(),
|
|
81
|
+
final_freq,
|
|
82
|
+
amp,
|
|
83
|
+
start_time * 1000.0,
|
|
84
|
+
duration_ms
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
*max_end_time = (*max_end_time).max(end_time);
|
|
88
|
+
|
|
89
|
+
if update_cursor {
|
|
90
|
+
if let Some(c) = cursor_time.as_mut() {
|
|
91
|
+
**c = end_time;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return (*max_end_time, end_time);
|
|
96
|
+
} else {
|
|
97
|
+
println!("❌ Unknown method '{}' on synth '{}'.", method, target);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
(*max_end_time, cursor_copy)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fn extract_f32(map: &HashMap<String, Value>, key: &str) -> Option<f32> {
|
|
105
|
+
map.get(key).and_then(|v| {
|
|
106
|
+
match v {
|
|
107
|
+
Value::Number(n) => Some(*n),
|
|
108
|
+
_ => None,
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
fn note_to_freq(note: &str) -> f32 {
|
|
114
|
+
let notes = vec!["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
|
|
115
|
+
|
|
116
|
+
if note.len() < 2 || note.len() > 3 {
|
|
117
|
+
return 440.0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let (name, octave_str) = note.split_at(note.len() - 1);
|
|
121
|
+
let semitone = notes
|
|
122
|
+
.iter()
|
|
123
|
+
.position(|&n| n == name)
|
|
124
|
+
.unwrap_or(9) as i32;
|
|
125
|
+
let octave = octave_str.parse::<i32>().unwrap_or(4);
|
|
126
|
+
let midi_note = (octave + 1) * 12 + semitone;
|
|
127
|
+
|
|
128
|
+
440.0 * (2.0_f32).powf(((midi_note as f32) - 69.0) / 12.0)
|
|
129
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
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_call_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
|
+
match &stmt.value {
|
|
18
|
+
Value::String(identifier) | Value::Identifier(identifier) => {
|
|
19
|
+
if let Some(Value::Map(map)) = variable_table.clone().get(identifier) {
|
|
20
|
+
if let Some(Value::Block(block)) = map.get("body") {
|
|
21
|
+
let (eng, _, end_time) = execute_audio_block(
|
|
22
|
+
audio_engine,
|
|
23
|
+
variable_table,
|
|
24
|
+
block.clone(),
|
|
25
|
+
base_bpm,
|
|
26
|
+
base_duration,
|
|
27
|
+
max_end_time,
|
|
28
|
+
cursor_time
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return (eng, max_end_time.max(end_time), end_time);
|
|
32
|
+
} else {
|
|
33
|
+
eprintln!("❌ Group '{}' has no 'body' block", identifier);
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
eprintln!("❌ Group '{}' not found or not a map", identifier);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
Value::Map(map) => {
|
|
41
|
+
if let Some(Value::Block(block)) = map.get("body") {
|
|
42
|
+
let (eng, _, end_time) = execute_audio_block(
|
|
43
|
+
audio_engine,
|
|
44
|
+
variable_table,
|
|
45
|
+
block.clone(),
|
|
46
|
+
base_bpm,
|
|
47
|
+
base_duration,
|
|
48
|
+
max_end_time,
|
|
49
|
+
cursor_time
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return (eng, max_end_time.max(end_time), end_time);
|
|
53
|
+
} else {
|
|
54
|
+
eprintln!("❌ Call map has no 'body' block");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
other => {
|
|
59
|
+
eprintln!("❌ Invalid call statement: expected identifier or map, found {:?}", other);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
(audio_engine, max_end_time, cursor_time)
|
|
64
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
audio::{
|
|
3
|
+
engine::AudioEngine,
|
|
4
|
+
evaluator::evaluate_condition_string,
|
|
5
|
+
interpreter::driver::execute_audio_block,
|
|
6
|
+
},
|
|
7
|
+
parser::statement::Statement,
|
|
8
|
+
shared::value::Value,
|
|
9
|
+
store::variable::VariableTable,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
pub fn interprete_condition_statement(
|
|
13
|
+
stmt: &Statement,
|
|
14
|
+
audio_engine: AudioEngine,
|
|
15
|
+
variable_table: VariableTable,
|
|
16
|
+
base_bpm: f32,
|
|
17
|
+
base_duration: f32,
|
|
18
|
+
max_end_time: f32,
|
|
19
|
+
cursor_time: f32
|
|
20
|
+
) -> (AudioEngine, f32, f32) {
|
|
21
|
+
let mut engine = audio_engine.clone();
|
|
22
|
+
let mut vars = variable_table.clone();
|
|
23
|
+
let mut cur_time = cursor_time;
|
|
24
|
+
let mut max_time = max_end_time;
|
|
25
|
+
|
|
26
|
+
let mut current = stmt.value.clone();
|
|
27
|
+
|
|
28
|
+
loop {
|
|
29
|
+
let Value::Map(map) = current else {
|
|
30
|
+
break;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
let should_execute = match map.get("condition") {
|
|
34
|
+
Some(Value::Boolean(b)) => *b,
|
|
35
|
+
Some(Value::String(expr)) => evaluate_condition_string(expr, &vars),
|
|
36
|
+
Some(_) => false,
|
|
37
|
+
None => true,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
if should_execute {
|
|
41
|
+
if let Some(Value::Block(block)) = map.get("body") {
|
|
42
|
+
let (new_engine, _, new_max) = execute_audio_block(
|
|
43
|
+
engine,
|
|
44
|
+
vars,
|
|
45
|
+
block.clone(),
|
|
46
|
+
base_bpm,
|
|
47
|
+
base_duration,
|
|
48
|
+
max_time,
|
|
49
|
+
cur_time
|
|
50
|
+
);
|
|
51
|
+
return (new_engine, new_max, new_max);
|
|
52
|
+
} else {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Advance to the next condition
|
|
58
|
+
match map.get("next") {
|
|
59
|
+
Some(Value::Map(next_map)) => {
|
|
60
|
+
current = Value::Map(next_map.clone());
|
|
61
|
+
}
|
|
62
|
+
_ => {
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
(audio_engine, max_end_time, cursor_time)
|
|
69
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
use crate::core::{
|
|
2
|
+
audio::{
|
|
3
|
+
engine::AudioEngine,
|
|
4
|
+
interpreter::{
|
|
5
|
+
arrow_call::interprete_call_arrow_statement,
|
|
6
|
+
call::interprete_call_statement,
|
|
7
|
+
condition::interprete_condition_statement,
|
|
8
|
+
let_::interprete_let_statement,
|
|
9
|
+
load::interprete_load_statement,
|
|
10
|
+
loop_::interprete_loop_statement,
|
|
11
|
+
sleep::interprete_sleep_statement,
|
|
12
|
+
spawn::interprete_spawn_statement,
|
|
13
|
+
tempo::interprete_tempo_statement,
|
|
14
|
+
trigger::interprete_trigger_statement,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
parser::statement::{ Statement, StatementKind },
|
|
18
|
+
store::variable::VariableTable,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
pub fn run_audio_program(
|
|
22
|
+
statements: &Vec<Statement>,
|
|
23
|
+
audio_engine: AudioEngine,
|
|
24
|
+
entry: String,
|
|
25
|
+
output: String
|
|
26
|
+
) -> (AudioEngine, f32, f32) {
|
|
27
|
+
let mut base_bpm = 120.0;
|
|
28
|
+
let mut base_duration = 60.0 / base_bpm;
|
|
29
|
+
|
|
30
|
+
let variable_table = audio_engine.variables.clone();
|
|
31
|
+
|
|
32
|
+
let (updated_audio_engine, base_bpm, max_end_time) = execute_audio_block(
|
|
33
|
+
audio_engine,
|
|
34
|
+
variable_table.clone(),
|
|
35
|
+
statements.clone(),
|
|
36
|
+
base_bpm.clone(),
|
|
37
|
+
base_duration.clone(),
|
|
38
|
+
0.0,
|
|
39
|
+
0.0
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
(updated_audio_engine, base_bpm, max_end_time)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
pub fn execute_audio_block(
|
|
46
|
+
mut audio_engine: AudioEngine,
|
|
47
|
+
mut variable_table: VariableTable,
|
|
48
|
+
mut statements: Vec<Statement>,
|
|
49
|
+
mut base_bpm: f32,
|
|
50
|
+
mut base_duration: f32,
|
|
51
|
+
mut max_end_time: f32,
|
|
52
|
+
mut cursor_time: f32
|
|
53
|
+
) -> (AudioEngine, f32, f32) {
|
|
54
|
+
let initial_cursor_time = cursor_time;
|
|
55
|
+
|
|
56
|
+
for stmt in statements {
|
|
57
|
+
match &stmt.kind {
|
|
58
|
+
StatementKind::Load { .. } => {
|
|
59
|
+
if
|
|
60
|
+
let Some(new_variable_table) = interprete_load_statement(
|
|
61
|
+
&stmt,
|
|
62
|
+
&mut variable_table
|
|
63
|
+
)
|
|
64
|
+
{
|
|
65
|
+
variable_table = new_variable_table;
|
|
66
|
+
} else {
|
|
67
|
+
eprintln!("❌ Failed to interpret load statement: {:?}", stmt);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
StatementKind::Let { .. } => {
|
|
72
|
+
if
|
|
73
|
+
let Some(new_variable_table) = interprete_let_statement(
|
|
74
|
+
&stmt,
|
|
75
|
+
&mut variable_table
|
|
76
|
+
)
|
|
77
|
+
{
|
|
78
|
+
variable_table = new_variable_table;
|
|
79
|
+
} else {
|
|
80
|
+
eprintln!("❌ Failed to interpret let statement: {:?}", stmt);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
StatementKind::Tempo => {
|
|
85
|
+
if let Some((new_bpm, new_duration)) = interprete_tempo_statement(&stmt) {
|
|
86
|
+
base_bpm = new_bpm;
|
|
87
|
+
base_duration = new_duration;
|
|
88
|
+
} else {
|
|
89
|
+
eprintln!("❌ Failed to interpret tempo statement: {:?}", stmt);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
StatementKind::Trigger { .. } => {
|
|
94
|
+
if
|
|
95
|
+
let Some((new_cursor_time, new_max_end_time, updated_engine)) =
|
|
96
|
+
interprete_trigger_statement(
|
|
97
|
+
&stmt,
|
|
98
|
+
&mut audio_engine,
|
|
99
|
+
&variable_table,
|
|
100
|
+
base_duration,
|
|
101
|
+
cursor_time,
|
|
102
|
+
max_end_time
|
|
103
|
+
)
|
|
104
|
+
{
|
|
105
|
+
cursor_time = new_cursor_time;
|
|
106
|
+
max_end_time = new_max_end_time;
|
|
107
|
+
audio_engine = updated_engine;
|
|
108
|
+
} else {
|
|
109
|
+
eprintln!("❌ Failed to interpret trigger statement: {:?}", stmt);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
StatementKind::Spawn => {
|
|
114
|
+
let mut temp_engine = AudioEngine::new(audio_engine.module_name.clone());
|
|
115
|
+
|
|
116
|
+
if
|
|
117
|
+
let Some((_cur, _max, updated_engine)) = interprete_spawn_statement(
|
|
118
|
+
&stmt,
|
|
119
|
+
temp_engine,
|
|
120
|
+
&variable_table,
|
|
121
|
+
base_bpm,
|
|
122
|
+
base_duration,
|
|
123
|
+
initial_cursor_time,
|
|
124
|
+
max_end_time
|
|
125
|
+
)
|
|
126
|
+
{
|
|
127
|
+
audio_engine.merge_with(updated_engine);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
StatementKind::Call => {
|
|
132
|
+
let (call_engine, new_max, new_cursor) = interprete_call_statement(
|
|
133
|
+
&stmt,
|
|
134
|
+
audio_engine.clone(),
|
|
135
|
+
variable_table.clone(),
|
|
136
|
+
base_bpm,
|
|
137
|
+
base_duration,
|
|
138
|
+
max_end_time,
|
|
139
|
+
cursor_time
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
audio_engine.merge_with(call_engine);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
StatementKind::Sleep => {
|
|
146
|
+
let (new_cursor, new_max) = interprete_sleep_statement(
|
|
147
|
+
&stmt,
|
|
148
|
+
cursor_time,
|
|
149
|
+
max_end_time
|
|
150
|
+
);
|
|
151
|
+
cursor_time = new_cursor;
|
|
152
|
+
max_end_time = new_max;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
StatementKind::Loop => {
|
|
156
|
+
let (loop_engine, new_max, new_cursor) = interprete_loop_statement(
|
|
157
|
+
&stmt,
|
|
158
|
+
audio_engine.clone(),
|
|
159
|
+
variable_table.clone(),
|
|
160
|
+
base_bpm,
|
|
161
|
+
base_duration,
|
|
162
|
+
max_end_time,
|
|
163
|
+
cursor_time
|
|
164
|
+
);
|
|
165
|
+
audio_engine = loop_engine;
|
|
166
|
+
cursor_time = new_cursor;
|
|
167
|
+
max_end_time = new_max;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
StatementKind::If | StatementKind::ElseIf | StatementKind::Else => {
|
|
171
|
+
let (condition_engine, new_max, new_cursor) = interprete_condition_statement(
|
|
172
|
+
&stmt,
|
|
173
|
+
audio_engine.clone(),
|
|
174
|
+
variable_table.clone(),
|
|
175
|
+
base_bpm,
|
|
176
|
+
base_duration,
|
|
177
|
+
max_end_time,
|
|
178
|
+
cursor_time
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
audio_engine = condition_engine;
|
|
182
|
+
cursor_time = new_cursor;
|
|
183
|
+
max_end_time = new_max;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
StatementKind::ArrowCall { .. } => {
|
|
187
|
+
interprete_call_arrow_statement(
|
|
188
|
+
&stmt,
|
|
189
|
+
&mut audio_engine,
|
|
190
|
+
&variable_table,
|
|
191
|
+
base_bpm,
|
|
192
|
+
base_duration,
|
|
193
|
+
&mut max_end_time,
|
|
194
|
+
Some(&mut cursor_time),
|
|
195
|
+
true
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
| StatementKind::Bank
|
|
200
|
+
| StatementKind::Import { .. }
|
|
201
|
+
| StatementKind::Export { .. }
|
|
202
|
+
| StatementKind::Group
|
|
203
|
+
| StatementKind::Unknown => {
|
|
204
|
+
// NOTE: Ignoring unsupported statement kinds for now.
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
_ => {
|
|
208
|
+
eprintln!("Unsupported audio statement kind: {:?}", stmt);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
audio_engine.set_variables(variable_table);
|
|
214
|
+
|
|
215
|
+
(audio_engine, base_bpm, max_end_time)
|
|
216
|
+
}
|
|
@@ -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,67 @@
|
|
|
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
|
+
Some(Value::Identifier(ident)) => {
|
|
21
|
+
if let Some(Value::Number(n)) = variable_table.get(ident) {
|
|
22
|
+
*n as usize
|
|
23
|
+
} else {
|
|
24
|
+
eprintln!("❌ Loop iterator must be a number, found: {:?}", ident);
|
|
25
|
+
return (audio_engine, max_end_time, cursor_time);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
_ => {
|
|
29
|
+
eprintln!("❌ Loop iterator must be a number, found: {:?}", loop_value.get("iterator"));
|
|
30
|
+
return (audio_engine, max_end_time, cursor_time);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
let loop_body = match loop_value.get("body") {
|
|
35
|
+
Some(Value::Block(body)) => body.clone(),
|
|
36
|
+
_ => {
|
|
37
|
+
eprintln!("❌ Loop body must be a block, found: {:?}", loop_value.get("body"));
|
|
38
|
+
return (audio_engine, max_end_time, cursor_time);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
let mut engine = audio_engine;
|
|
43
|
+
let mut cur_time = cursor_time;
|
|
44
|
+
let mut max_time = max_end_time;
|
|
45
|
+
|
|
46
|
+
for _ in 0..loop_count {
|
|
47
|
+
let (eng, _, end_time) = execute_audio_block(
|
|
48
|
+
engine,
|
|
49
|
+
variable_table.clone(),
|
|
50
|
+
loop_body.clone(),
|
|
51
|
+
base_bpm,
|
|
52
|
+
base_duration,
|
|
53
|
+
max_time,
|
|
54
|
+
cur_time
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
engine = eng;
|
|
58
|
+
cur_time = end_time;
|
|
59
|
+
max_time = max_time.max(end_time);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
(engine, max_time, cur_time)
|
|
63
|
+
} else {
|
|
64
|
+
eprintln!("❌ Loop statement value is not a map");
|
|
65
|
+
(audio_engine, max_end_time, cursor_time)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -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
|
+
}
|