@devaloop/devalang 0.0.1-alpha.14 → 0.0.1-alpha.16

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.
Files changed (177) hide show
  1. package/.devalang +10 -8
  2. package/.github/workflows/ci.yml +92 -0
  3. package/Cargo.toml +60 -58
  4. package/README.md +32 -15
  5. package/docs/CHANGELOG.md +93 -1
  6. package/docs/CONTRIBUTING.md +101 -1
  7. package/docs/ROADMAP.md +2 -2
  8. package/docs/TODO.md +1 -1
  9. package/examples/automation.deva +42 -0
  10. package/examples/bank.deva +4 -4
  11. package/examples/events.deva +12 -0
  12. package/examples/function.deva +4 -4
  13. package/examples/index.deva +39 -25
  14. package/examples/loop.deva +5 -11
  15. package/examples/pattern.deva +8 -0
  16. package/examples/plugin.deva +16 -0
  17. package/examples/variables.deva +1 -1
  18. package/out-tsc/bin/index.js +51 -7
  19. package/out-tsc/index.js +3 -1
  20. package/out-tsc/scripts/postbuild.js +9 -10
  21. package/out-tsc/scripts/postinstall.js +49 -0
  22. package/package.json +12 -4
  23. package/project-version.json +3 -3
  24. package/rust/cli/bank.rs +462 -456
  25. package/rust/cli/build.rs +252 -199
  26. package/rust/cli/check.rs +221 -180
  27. package/rust/cli/driver.rs +297 -292
  28. package/rust/cli/generator.rs +1 -0
  29. package/rust/cli/init.rs +87 -79
  30. package/rust/cli/install.rs +35 -32
  31. package/rust/cli/login.rs +127 -134
  32. package/rust/cli/mod.rs +13 -11
  33. package/rust/cli/play.rs +1123 -218
  34. package/rust/cli/telemetry.rs +19 -0
  35. package/rust/cli/template.rs +69 -57
  36. package/rust/cli/update.rs +6 -4
  37. package/rust/common/api.rs +5 -8
  38. package/rust/common/cdn.rs +3 -6
  39. package/rust/common/mod.rs +3 -3
  40. package/rust/common/sso.rs +3 -6
  41. package/rust/config/driver.rs +118 -94
  42. package/rust/config/loader.rs +165 -156
  43. package/rust/config/mod.rs +4 -2
  44. package/rust/config/settings.rs +91 -0
  45. package/rust/config/stats.rs +257 -0
  46. package/rust/core/audio/engine.rs +696 -518
  47. package/rust/core/audio/evaluator.rs +263 -31
  48. package/rust/core/audio/interpreter/arrow_call.rs +198 -161
  49. package/rust/core/audio/interpreter/automate.rs +18 -0
  50. package/rust/core/audio/interpreter/call.rs +98 -95
  51. package/rust/core/audio/interpreter/condition.rs +70 -71
  52. package/rust/core/audio/interpreter/driver.rs +487 -198
  53. package/rust/core/audio/interpreter/function.rs +26 -21
  54. package/rust/core/audio/interpreter/let_.rs +38 -19
  55. package/rust/core/audio/interpreter/load.rs +18 -18
  56. package/rust/core/audio/interpreter/loop_.rs +113 -73
  57. package/rust/core/audio/interpreter/mod.rs +14 -13
  58. package/rust/core/audio/interpreter/sleep.rs +27 -30
  59. package/rust/core/audio/interpreter/spawn.rs +105 -102
  60. package/rust/core/audio/interpreter/tempo.rs +19 -16
  61. package/rust/core/audio/interpreter/trigger.rs +239 -210
  62. package/rust/core/audio/loader/mod.rs +1 -1
  63. package/rust/core/audio/loader/trigger.rs +100 -97
  64. package/rust/core/audio/mod.rs +7 -6
  65. package/rust/core/audio/player.rs +64 -64
  66. package/rust/core/audio/renderer.rs +56 -53
  67. package/rust/core/audio/special/easing.rs +189 -0
  68. package/rust/core/audio/special/env.rs +43 -0
  69. package/rust/core/audio/special/math.rs +102 -0
  70. package/rust/core/audio/special/mod.rs +9 -0
  71. package/rust/core/audio/special/modulator.rs +143 -0
  72. package/rust/core/builder/mod.rs +80 -85
  73. package/rust/core/debugger/lexer.rs +27 -27
  74. package/rust/core/debugger/mod.rs +24 -23
  75. package/rust/core/debugger/module.rs +55 -47
  76. package/rust/core/debugger/preprocessor.rs +27 -27
  77. package/rust/core/debugger/store.rs +40 -39
  78. package/rust/core/error/mod.rs +80 -66
  79. package/rust/core/lexer/handler/arrow.rs +82 -31
  80. package/rust/core/lexer/handler/at.rs +21 -21
  81. package/rust/core/lexer/handler/brace.rs +41 -41
  82. package/rust/core/lexer/handler/colon.rs +21 -21
  83. package/rust/core/lexer/handler/comment.rs +30 -30
  84. package/rust/core/lexer/handler/dot.rs +21 -21
  85. package/rust/core/lexer/handler/driver.rs +337 -263
  86. package/rust/core/lexer/handler/identifier.rs +46 -42
  87. package/rust/core/lexer/handler/indent.rs +66 -66
  88. package/rust/core/lexer/handler/mod.rs +16 -16
  89. package/rust/core/lexer/handler/newline.rs +23 -23
  90. package/rust/core/lexer/handler/number.rs +31 -31
  91. package/rust/core/lexer/handler/operator.rs +46 -44
  92. package/rust/core/lexer/handler/parenthesis.rs +41 -41
  93. package/rust/core/lexer/handler/slash.rs +21 -21
  94. package/rust/core/lexer/handler/string.rs +63 -63
  95. package/rust/core/lexer/mod.rs +54 -51
  96. package/rust/core/lexer/token.rs +97 -91
  97. package/rust/core/mod.rs +11 -11
  98. package/rust/core/parser/driver.rs +513 -408
  99. package/rust/core/parser/handler/arrow_call.rs +233 -211
  100. package/rust/core/parser/handler/at.rs +245 -162
  101. package/rust/core/parser/handler/bank.rs +94 -69
  102. package/rust/core/parser/handler/condition.rs +80 -74
  103. package/rust/core/parser/handler/dot.rs +143 -135
  104. package/rust/core/parser/handler/identifier/automate.rs +257 -0
  105. package/rust/core/parser/handler/identifier/call.rs +91 -88
  106. package/rust/core/parser/handler/identifier/emit.rs +66 -0
  107. package/rust/core/parser/handler/identifier/function.rs +100 -92
  108. package/rust/core/parser/handler/identifier/group.rs +85 -75
  109. package/rust/core/parser/handler/identifier/let_.rs +158 -127
  110. package/rust/core/parser/handler/identifier/mod.rs +54 -52
  111. package/rust/core/parser/handler/identifier/on.rs +98 -0
  112. package/rust/core/parser/handler/identifier/print.rs +52 -0
  113. package/rust/core/parser/handler/identifier/sleep.rs +36 -33
  114. package/rust/core/parser/handler/identifier/spawn.rs +91 -88
  115. package/rust/core/parser/handler/identifier/synth.rs +65 -65
  116. package/rust/core/parser/handler/loop_.rs +170 -72
  117. package/rust/core/parser/handler/mod.rs +8 -8
  118. package/rust/core/parser/handler/tempo.rs +53 -47
  119. package/rust/core/parser/mod.rs +4 -4
  120. package/rust/core/parser/statement.rs +142 -108
  121. package/rust/core/plugin/loader.rs +123 -48
  122. package/rust/core/plugin/mod.rs +2 -1
  123. package/rust/core/plugin/runner.rs +296 -0
  124. package/rust/core/preprocessor/loader.rs +515 -326
  125. package/rust/core/preprocessor/mod.rs +4 -4
  126. package/rust/core/preprocessor/module.rs +60 -58
  127. package/rust/core/preprocessor/processor.rs +99 -101
  128. package/rust/core/preprocessor/resolver/bank.rs +51 -49
  129. package/rust/core/preprocessor/resolver/call.rs +100 -100
  130. package/rust/core/preprocessor/resolver/condition.rs +97 -97
  131. package/rust/core/preprocessor/resolver/driver.rs +310 -278
  132. package/rust/core/preprocessor/resolver/function.rs +69 -78
  133. package/rust/core/preprocessor/resolver/group.rs +96 -91
  134. package/rust/core/preprocessor/resolver/let_.rs +32 -28
  135. package/rust/core/preprocessor/resolver/loop_.rs +320 -91
  136. package/rust/core/preprocessor/resolver/mod.rs +15 -15
  137. package/rust/core/preprocessor/resolver/spawn.rs +76 -92
  138. package/rust/core/preprocessor/resolver/synth.rs +56 -50
  139. package/rust/core/preprocessor/resolver/tempo.rs +50 -49
  140. package/rust/core/preprocessor/resolver/trigger.rs +113 -116
  141. package/rust/core/preprocessor/resolver/value.rs +81 -87
  142. package/rust/core/shared/bank.rs +1 -1
  143. package/rust/core/shared/duration.rs +9 -9
  144. package/rust/core/shared/mod.rs +3 -3
  145. package/rust/core/shared/value.rs +35 -32
  146. package/rust/core/store/function.rs +34 -34
  147. package/rust/core/store/global.rs +55 -38
  148. package/rust/core/store/mod.rs +5 -5
  149. package/rust/core/store/variable.rs +37 -34
  150. package/rust/core/utils/mod.rs +2 -2
  151. package/rust/core/utils/path.rs +37 -31
  152. package/rust/core/utils/validation.rs +35 -37
  153. package/rust/installer/addon.rs +84 -80
  154. package/rust/installer/bank.rs +62 -65
  155. package/rust/installer/mod.rs +5 -5
  156. package/rust/installer/plugin.rs +54 -55
  157. package/rust/installer/utils.rs +56 -56
  158. package/rust/lib.rs +156 -164
  159. package/rust/main.rs +250 -145
  160. package/rust/utils/error.rs +200 -0
  161. package/rust/utils/file.rs +38 -35
  162. package/rust/utils/first_usage.rs +76 -0
  163. package/rust/utils/logger.rs +195 -139
  164. package/rust/utils/mod.rs +9 -50
  165. package/rust/utils/signature.rs +19 -17
  166. package/rust/utils/spinner.rs +22 -19
  167. package/rust/utils/telemetry.rs +292 -0
  168. package/rust/utils/watcher.rs +34 -33
  169. package/templates/minimal/README.md +97 -121
  170. package/templates/welcome/README.md +97 -121
  171. package/typescript/bin/index.ts +19 -5
  172. package/typescript/index.ts +3 -1
  173. package/typescript/scripts/postbuild.ts +10 -6
  174. package/typescript/scripts/postinstall.ts +56 -0
  175. package/typescript/scripts/version/bump.ts +0 -1
  176. package/typescript/scripts/version/index.ts +0 -1
  177. package/out-tsc/bin/devalang.exe +0 -0
@@ -1,210 +1,239 @@
1
- use std::collections::HashMap;
2
-
3
- use crate::core::{
4
- audio::{ engine::AudioEngine, loader::trigger::load_trigger },
5
- parser::statement::{ Statement, StatementKind },
6
- shared::{ duration::Duration, value::Value },
7
- store::variable::VariableTable,
8
- };
9
-
10
- pub fn interprete_trigger_statement(
11
- stmt: &Statement,
12
- audio_engine: &mut AudioEngine,
13
- variable_table: &VariableTable,
14
- base_duration: f32,
15
- cursor_time: f32,
16
- max_end_time: f32
17
- ) -> Option<(f32, f32, AudioEngine)> {
18
- if let StatementKind::Trigger { entity, duration, effects } = &stmt.kind {
19
- let mut trigger_val = Value::String(entity.clone());
20
- let mut trigger_src = String::new();
21
-
22
- match variable_table.variables.get(entity) {
23
- Some(Value::Identifier(id)) => {
24
- // Get real value from global variable table
25
- if let Some(global_table) = &variable_table.parent {
26
- if let Some(val) = global_table.get(id) {
27
- trigger_val = val.clone();
28
- } else {
29
- eprintln!("❌ Trigger entity '{}' not found in global variable table", id);
30
- return None;
31
- }
32
- } else if let Some(val) = variable_table.get(id) {
33
- trigger_val = val.clone();
34
- } else {
35
- eprintln!("❌ Trigger entity '{}' not found in variable table", id);
36
- return None;
37
- }
38
- }
39
- Some(Value::Sample(sample_src)) => {
40
- // If the entity is a sample, we use its path directly
41
- trigger_src = sample_src.clone();
42
- }
43
- Some(Value::Map(map)) => {
44
- // If the entity is a map, we assume it contains an "entity" key
45
- if let Some(Value::String(src)) = map.get("entity") {
46
- trigger_val = Value::String(src.clone());
47
- } else if let Some(Value::Identifier(src)) = map.get("entity") {
48
- trigger_val = Value::Identifier(src.clone());
49
- } else {
50
- eprintln!(
51
- "❌ Trigger map must contain an 'entity' key with a string or identifier value."
52
- );
53
- return None;
54
- }
55
- }
56
- _ => {
57
- trigger_val = Value::String(entity.clone());
58
- }
59
- }
60
-
61
- let duration_secs = match duration {
62
- Duration::Number(n) => *n,
63
-
64
- Duration::Identifier(id) => {
65
- if id == "auto" {
66
- 1.0
67
- } else {
68
- match variable_table.get(id) {
69
- Some(Value::Number(n)) => *n,
70
- Some(Value::Identifier(other)) if other == "auto" => 1.0,
71
- Some(other) => {
72
- eprintln!(
73
- "❌ Invalid duration reference '{}': expected number, got {:?}",
74
- id,
75
- other
76
- );
77
- return None;
78
- }
79
- None => {
80
- eprintln!("❌ Duration identifier '{}' not found", id);
81
- return None;
82
- }
83
- }
84
- }
85
- }
86
-
87
- Duration::Beat(beat_str) => {
88
- let parts: Vec<&str> = beat_str.split('/').collect();
89
- if parts.len() != 2 {
90
- eprintln!("❌ Invalid beat duration format: {}", beat_str);
91
- return None;
92
- }
93
-
94
- let numerator: f32 = parts[0].parse().unwrap_or(1.0);
95
- let denominator: f32 = parts[1].parse().unwrap_or(4.0);
96
-
97
- let beats = (numerator / denominator) * 4.0;
98
-
99
- beats * base_duration
100
- }
101
-
102
- Duration::Auto => base_duration,
103
- };
104
-
105
- let final_variable_table = if let Some(parent) = &variable_table.parent {
106
- VariableTable {
107
- variables: parent.variables.clone(),
108
- parent: None,
109
- }
110
- } else {
111
- variable_table.clone()
112
- };
113
-
114
- let (src, sample_length) = load_trigger(
115
- &trigger_val,
116
- duration,
117
- effects,
118
- base_duration,
119
- final_variable_table.clone()
120
- );
121
-
122
- if trigger_src.is_empty() {
123
- trigger_src = src;
124
- }
125
-
126
- let effects = extract_effects(stmt.value.clone());
127
- let one_shot = effects
128
- .as_ref()
129
- .and_then(|map| map.get("one_shot"))
130
- .and_then(|v| {
131
- match v {
132
- Value::Identifier(id) if id == "true" => Some(true),
133
- Value::String(s) if s == "true" => Some(true),
134
- _ => None,
135
- }
136
- })
137
- .unwrap_or(false);
138
-
139
- let play_length = if one_shot {
140
- sample_length // play entire sample
141
- } else {
142
- duration_secs.min(sample_length)
143
- };
144
-
145
- let trigger_src = match trigger_val.get("entity") {
146
- Some(Value::String(src)) => src.clone(),
147
- Some(Value::Identifier(id)) => id.clone(),
148
- Some(Value::Statement(stmt)) => {
149
- if let StatementKind::Trigger { entity, .. } = &stmt.kind {
150
- entity.clone()
151
- } else {
152
- eprintln!("❌ Invalid trigger statement in map: expected 'Trigger' kind");
153
- return None;
154
- }
155
- }
156
- _ => trigger_src,
157
- };
158
-
159
- if let Some(effects_map) = effects {
160
- audio_engine.insert_sample(
161
- &trigger_src,
162
- cursor_time,
163
- play_length,
164
- Some(effects_map),
165
- &final_variable_table
166
- );
167
- } else {
168
- audio_engine.insert_sample(
169
- &trigger_src,
170
- cursor_time,
171
- play_length,
172
- None,
173
- &final_variable_table
174
- );
175
- }
176
-
177
- let new_cursor_time = cursor_time + duration_secs; // advance by beat duration
178
- let new_max_end_time = (cursor_time + play_length).max(max_end_time);
179
-
180
- let updated_engine = audio_engine.clone();
181
-
182
- return Some((new_cursor_time, new_max_end_time, updated_engine));
183
- }
184
-
185
- None
186
- }
187
-
188
- fn extract_effects(value: Value) -> Option<HashMap<String, Value>> {
189
- if let Value::Map(map) = value {
190
- let mut effects = HashMap::new();
191
-
192
- for (key, val) in map {
193
- if key == "effects" {
194
- if let Value::Map(effect_map) = val {
195
- for (effect_key, effect_val) in effect_map {
196
- effects.insert(effect_key, effect_val);
197
- }
198
- } else {
199
- return None; // effects must be a map
200
- }
201
- } else {
202
- return Some(effects);
203
- }
204
- }
205
-
206
- Some(effects)
207
- } else {
208
- None
209
- }
210
- }
1
+ use std::collections::HashMap;
2
+
3
+ use crate::core::{
4
+ audio::{engine::AudioEngine, loader::trigger::load_trigger},
5
+ parser::statement::{Statement, StatementKind},
6
+ shared::{duration::Duration, value::Value},
7
+ store::variable::VariableTable,
8
+ };
9
+ use crate::utils::logger::Logger;
10
+
11
+ pub fn interprete_trigger_statement(
12
+ stmt: &Statement,
13
+ audio_engine: &mut AudioEngine,
14
+ variable_table: &VariableTable,
15
+ base_duration: f32,
16
+ cursor_time: f32,
17
+ max_end_time: f32,
18
+ ) -> Option<(f32, f32, AudioEngine)> {
19
+ if let StatementKind::Trigger {
20
+ entity,
21
+ duration,
22
+ effects,
23
+ } = &stmt.kind
24
+ {
25
+ let mut trigger_val = Value::String(entity.clone());
26
+ let mut trigger_src = String::new();
27
+
28
+ match variable_table.variables.get(entity) {
29
+ Some(Value::Identifier(id)) => {
30
+ // Get real value from global variable table
31
+ if let Some(global_table) = &variable_table.parent {
32
+ if let Some(val) = global_table.get(id) {
33
+ trigger_val = val.clone();
34
+ } else {
35
+ eprintln!(
36
+ "❌ Trigger entity '{}' not found in global variable table",
37
+ id
38
+ );
39
+ return None;
40
+ }
41
+ } else if let Some(val) = variable_table.get(id) {
42
+ trigger_val = val.clone();
43
+ } else {
44
+ eprintln!("❌ Trigger entity '{}' not found in variable table", id);
45
+ return None;
46
+ }
47
+ }
48
+ Some(Value::Sample(sample_src)) => {
49
+ // If the entity is a sample, we use its path directly
50
+ trigger_src = sample_src.clone();
51
+ }
52
+ Some(Value::Map(map)) => {
53
+ // If the entity is a map, we assume it contains an "entity" key
54
+ if let Some(Value::String(src)) = map.get("entity") {
55
+ trigger_val = Value::String(src.clone());
56
+ } else if let Some(Value::Identifier(src)) = map.get("entity") {
57
+ trigger_val = Value::Identifier(src.clone());
58
+ } else {
59
+ eprintln!(
60
+ "❌ Trigger map must contain an 'entity' key with a string or identifier value."
61
+ );
62
+ return None;
63
+ }
64
+ }
65
+ _ => {
66
+ trigger_val = Value::String(entity.clone());
67
+ }
68
+ }
69
+
70
+ // If trigger could not be resolved to a known mapping or explicit path, abort early
71
+ if let Value::String(s) = &trigger_val {
72
+ let is_protocol = s.starts_with("devalang://");
73
+ let is_var = variable_table.get(s).is_some()
74
+ || variable_table
75
+ .parent
76
+ .as_ref()
77
+ .and_then(|p| p.get(s))
78
+ .is_some();
79
+ let looks_like_path = s.contains('/')
80
+ || s.ends_with(".wav")
81
+ || s.ends_with(".mp3")
82
+ || s.ends_with(".ogg");
83
+ if !is_protocol && !is_var && !looks_like_path {
84
+ let logger = Logger::new();
85
+ logger.log_error_with_stacktrace(
86
+ &format!("unknown trigger: {}", s),
87
+ &format!("{}:{}:{}", audio_engine.module_name, stmt.line, stmt.column),
88
+ );
89
+ return None;
90
+ }
91
+ }
92
+
93
+ let duration_secs = match duration {
94
+ Duration::Number(n) => *n,
95
+
96
+ Duration::Identifier(id) => {
97
+ if id == "auto" {
98
+ 1.0
99
+ } else {
100
+ match variable_table.get(id) {
101
+ Some(Value::Number(n)) => *n,
102
+ Some(Value::Identifier(other)) if other == "auto" => 1.0,
103
+ Some(other) => {
104
+ eprintln!(
105
+ "❌ Invalid duration reference '{}': expected number, got {:?}",
106
+ id, other
107
+ );
108
+ return None;
109
+ }
110
+ None => {
111
+ eprintln!("❌ Duration identifier '{}' not found", id);
112
+ return None;
113
+ }
114
+ }
115
+ }
116
+ }
117
+
118
+ Duration::Beat(beat_str) => {
119
+ let parts: Vec<&str> = beat_str.split('/').collect();
120
+ if parts.len() != 2 {
121
+ eprintln!("❌ Invalid beat duration format: {}", beat_str);
122
+ return None;
123
+ }
124
+
125
+ let numerator: f32 = parts[0].parse().unwrap_or(1.0);
126
+ let denominator: f32 = parts[1].parse().unwrap_or(4.0);
127
+
128
+ let beats = (numerator / denominator) * 4.0;
129
+
130
+ beats * base_duration
131
+ }
132
+
133
+ Duration::Auto => base_duration,
134
+ };
135
+
136
+ let final_variable_table = if let Some(parent) = &variable_table.parent {
137
+ VariableTable {
138
+ variables: parent.variables.clone(),
139
+ parent: None,
140
+ }
141
+ } else {
142
+ variable_table.clone()
143
+ };
144
+
145
+ let (src, sample_length) = load_trigger(
146
+ &trigger_val,
147
+ duration,
148
+ effects,
149
+ base_duration,
150
+ final_variable_table.clone(),
151
+ );
152
+
153
+ if trigger_src.is_empty() {
154
+ trigger_src = src;
155
+ }
156
+
157
+ let effects = extract_effects(stmt.value.clone());
158
+ let one_shot = effects
159
+ .as_ref()
160
+ .and_then(|map| map.get("one_shot"))
161
+ .and_then(|v| match v {
162
+ Value::Identifier(id) if id == "true" => Some(true),
163
+ Value::String(s) if s == "true" => Some(true),
164
+ _ => None,
165
+ })
166
+ .unwrap_or(false);
167
+
168
+ let play_length = if one_shot {
169
+ sample_length // play entire sample
170
+ } else {
171
+ duration_secs.min(sample_length)
172
+ };
173
+
174
+ let trigger_src = match trigger_val.get("entity") {
175
+ Some(Value::String(src)) => src.clone(),
176
+ Some(Value::Identifier(id)) => id.clone(),
177
+ Some(Value::Statement(stmt)) => {
178
+ if let StatementKind::Trigger { entity, .. } = &stmt.kind {
179
+ entity.clone()
180
+ } else {
181
+ eprintln!("❌ Invalid trigger statement in map: expected 'Trigger' kind");
182
+ return None;
183
+ }
184
+ }
185
+ _ => trigger_src,
186
+ };
187
+
188
+ if let Some(effects_map) = effects {
189
+ audio_engine.insert_sample(
190
+ &trigger_src,
191
+ cursor_time,
192
+ play_length,
193
+ Some(effects_map),
194
+ &final_variable_table,
195
+ );
196
+ } else {
197
+ audio_engine.insert_sample(
198
+ &trigger_src,
199
+ cursor_time,
200
+ play_length,
201
+ None,
202
+ &final_variable_table,
203
+ );
204
+ }
205
+
206
+ let new_cursor_time = cursor_time + duration_secs; // advance by beat duration
207
+ let new_max_end_time = (cursor_time + play_length).max(max_end_time);
208
+
209
+ let updated_engine = audio_engine.clone();
210
+
211
+ return Some((new_cursor_time, new_max_end_time, updated_engine));
212
+ }
213
+
214
+ None
215
+ }
216
+
217
+ fn extract_effects(value: Value) -> Option<HashMap<String, Value>> {
218
+ if let Value::Map(map) = value {
219
+ let mut effects = HashMap::new();
220
+
221
+ for (key, val) in map {
222
+ if key == "effects" {
223
+ if let Value::Map(effect_map) = val {
224
+ for (effect_key, effect_val) in effect_map {
225
+ effects.insert(effect_key, effect_val);
226
+ }
227
+ } else {
228
+ return None; // effects must be a map
229
+ }
230
+ } else {
231
+ return Some(effects);
232
+ }
233
+ }
234
+
235
+ Some(effects)
236
+ } else {
237
+ None
238
+ }
239
+ }
@@ -1 +1 @@
1
- pub mod trigger;
1
+ pub mod trigger;
@@ -1,97 +1,100 @@
1
- use crate::core::{
2
- parser::statement::StatementKind,
3
- shared::{ duration::Duration, value::Value },
4
- store::variable::VariableTable,
5
- };
6
-
7
- pub fn load_trigger(
8
- trigger: &Value,
9
- duration: &Duration,
10
- effects: &Option<Value>,
11
- base_duration: f32,
12
- variable_table: VariableTable
13
- ) -> (String, f32) {
14
- let mut trigger_path = String::new();
15
- let mut duration_as_secs = 0.0;
16
-
17
- match trigger {
18
- Value::String(src) => {
19
- trigger_path = src.to_string();
20
- }
21
- Value::Identifier(src) => {
22
- trigger_path = src.to_string();
23
- }
24
-
25
- Value::Map(map) => {
26
- if let Some(Value::String(src)) = map.get("entity") {
27
- trigger_path = format!("devalang://bank/{}", src.to_string());
28
- } else if let Some(Value::Identifier(src)) = map.get("entity") {
29
- trigger_path = format!("devalang://bank/{}", src.to_string());
30
- } else {
31
- eprintln!(
32
- "❌ Trigger map must contain an 'entity' key with a string or identifier value."
33
- );
34
- }
35
- }
36
- Value::Sample(src) => {
37
- trigger_path = src.to_string();
38
- }
39
- Value::Statement(stmt) => {
40
- if let StatementKind::Trigger { entity, duration, effects } = &stmt.kind {
41
- trigger_path = entity.clone();
42
- } else {
43
- eprintln!("❌ Trigger statement must be of type 'Trigger', found: {:?}", stmt.kind);
44
- }
45
- }
46
- _ => {
47
- eprintln!(
48
- "❌ Invalid trigger type. Expected a string or identifier variable, found: {:?}",
49
- trigger
50
- );
51
- }
52
- }
53
-
54
- match duration {
55
- Duration::Identifier(duration_identifier) => {
56
- if duration_identifier == "auto" {
57
- duration_as_secs = base_duration;
58
- } else if let Some(Value::Number(num)) = variable_table.get(duration_identifier) {
59
- duration_as_secs = *num;
60
- } else if let Some(Value::String(num_str)) = variable_table.get(duration_identifier) {
61
- duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
62
- } else if
63
- let Some(Value::Identifier(num_str)) = variable_table.get(duration_identifier)
64
- {
65
- duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
66
- } else {
67
- eprintln!("❌ Invalid duration identifier: {}", duration_identifier);
68
- }
69
- }
70
-
71
- Duration::Number(num) => {
72
- duration_as_secs = *num;
73
- }
74
-
75
- Duration::Auto => {
76
- duration_as_secs = base_duration;
77
- }
78
-
79
- Duration::Beat(beat_str) => {
80
- let parts: Vec<&str> = beat_str.split('/').collect();
81
-
82
- if parts.len() == 2 {
83
- let numerator: f32 = parts[0].parse().unwrap_or(1.0);
84
- let denominator: f32 = parts[1].parse().unwrap_or(1.0);
85
- duration_as_secs = (numerator / denominator) * base_duration;
86
- } else {
87
- eprintln!("❌ Invalid beat duration format: {}", beat_str);
88
- }
89
- }
90
-
91
- _ => {
92
- eprintln!("❌ Invalid duration type. Expected an identifier.");
93
- }
94
- }
95
-
96
- (trigger_path, duration_as_secs)
97
- }
1
+ use crate::core::{
2
+ parser::statement::StatementKind,
3
+ shared::{duration::Duration, value::Value},
4
+ store::variable::VariableTable,
5
+ };
6
+
7
+ pub fn load_trigger(
8
+ trigger: &Value,
9
+ duration: &Duration,
10
+ _effects: &Option<Value>,
11
+ base_duration: f32,
12
+ variable_table: VariableTable,
13
+ ) -> (String, f32) {
14
+ let mut trigger_path = String::new();
15
+ let mut duration_as_secs = 0.0;
16
+
17
+ match trigger {
18
+ Value::String(src) => {
19
+ trigger_path = src.to_string();
20
+ }
21
+ Value::Identifier(src) => {
22
+ trigger_path = src.to_string();
23
+ }
24
+
25
+ Value::Map(map) => {
26
+ if let Some(Value::String(src)) = map.get("entity") {
27
+ trigger_path = format!("devalang://bank/{}", src.to_string());
28
+ } else if let Some(Value::Identifier(src)) = map.get("entity") {
29
+ trigger_path = format!("devalang://bank/{}", src.to_string());
30
+ } else {
31
+ eprintln!(
32
+ "❌ Trigger map must contain an 'entity' key with a string or identifier value."
33
+ );
34
+ }
35
+ }
36
+ Value::Sample(src) => {
37
+ trigger_path = src.to_string();
38
+ }
39
+ Value::Statement(stmt) => {
40
+ if let StatementKind::Trigger {
41
+ entity,
42
+ duration: _,
43
+ effects: _,
44
+ } = &stmt.kind
45
+ {
46
+ trigger_path = entity.clone();
47
+ } else {
48
+ eprintln!(
49
+ "❌ Trigger statement must be of type 'Trigger', found: {:?}",
50
+ stmt.kind
51
+ );
52
+ }
53
+ }
54
+ _ => {
55
+ eprintln!(
56
+ "❌ Invalid trigger type. Expected a string or identifier variable, found: {:?}",
57
+ trigger
58
+ );
59
+ }
60
+ }
61
+
62
+ match duration {
63
+ Duration::Identifier(duration_identifier) => {
64
+ if duration_identifier == "auto" {
65
+ duration_as_secs = base_duration;
66
+ } else if let Some(Value::Number(num)) = variable_table.get(duration_identifier) {
67
+ duration_as_secs = *num;
68
+ } else if let Some(Value::String(num_str)) = variable_table.get(duration_identifier) {
69
+ duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
70
+ } else if let Some(Value::Identifier(num_str)) = variable_table.get(duration_identifier)
71
+ {
72
+ duration_as_secs = num_str.parse::<f32>().unwrap_or(base_duration);
73
+ } else {
74
+ eprintln!("❌ Invalid duration identifier: {}", duration_identifier);
75
+ }
76
+ }
77
+
78
+ Duration::Number(num) => {
79
+ duration_as_secs = *num;
80
+ }
81
+
82
+ Duration::Auto => {
83
+ duration_as_secs = base_duration;
84
+ }
85
+
86
+ Duration::Beat(beat_str) => {
87
+ let parts: Vec<&str> = beat_str.split('/').collect();
88
+
89
+ if parts.len() == 2 {
90
+ let numerator: f32 = parts[0].parse().unwrap_or(1.0);
91
+ let denominator: f32 = parts[1].parse().unwrap_or(1.0);
92
+ duration_as_secs = (numerator / denominator) * base_duration;
93
+ } else {
94
+ eprintln!("❌ Invalid beat duration format: {}", beat_str);
95
+ }
96
+ }
97
+ }
98
+
99
+ (trigger_path, duration_as_secs)
100
+ }