devalang_wasm/language/syntax/parser/driver/statements/
core.rs1use super::super::duration::parse_duration_token;
2use super::super::helpers::{parse_array_value, parse_synth_definition};
3use crate::language::syntax::ast::{Statement, StatementKind, Value};
4use anyhow::{Result, anyhow};
6use std::iter::Iterator;
7
8pub fn parse_tempo(
10 mut parts: impl Iterator<Item = impl AsRef<str>>,
11 line_number: usize,
12) -> Result<Statement> {
13 let value = parts
14 .next()
15 .ok_or_else(|| anyhow!("tempo declaration requires a value"))?;
16 let bpm: f32 = value
17 .as_ref()
18 .parse()
19 .map_err(|_| anyhow!("invalid tempo value: '{}'", value.as_ref()))?;
20 Ok(Statement::tempo(bpm, line_number, 1))
21}
22
23pub fn parse_print(line: &str, line_number: usize) -> Result<Statement> {
25 let message = line
26 .strip_prefix("print")
27 .ok_or_else(|| {
28 anyhow!(
29 "Invalid print statement: expected 'print' keyword at line {}",
30 line_number
31 )
32 })?
33 .trim();
34
35 if message.starts_with('"') && message.ends_with('"') && message.len() >= 2 {
38 let cleaned = message[1..message.len() - 1].to_string();
39 Ok(Statement::new(
40 StatementKind::Print,
41 Value::String(cleaned),
42 0,
43 line_number,
44 1,
45 ))
46 } else if let Ok(num) = message.parse::<f32>() {
47 Ok(Statement::new(
48 StatementKind::Print,
49 Value::Number(num),
50 0,
51 line_number,
52 1,
53 ))
54 } else {
55 if message.contains('+') {
60 let parts: Vec<Value> = message
61 .split('+')
62 .map(|p| p.trim())
63 .filter(|s| !s.is_empty())
64 .map(|tok| {
65 crate::language::syntax::parser::driver::helpers::parse_single_arg(tok)
66 .unwrap_or(Value::Identifier(tok.to_string()))
67 })
68 .collect();
69
70 Ok(Statement::new(
71 StatementKind::Print,
72 Value::Array(parts),
73 0,
74 line_number,
75 1,
76 ))
77 } else {
78 let val = crate::language::syntax::parser::driver::helpers::parse_single_arg(message)?;
80 Ok(Statement::new(StatementKind::Print, val, 0, line_number, 1))
81 }
82 }
83}
84
85pub fn parse_sleep(
87 mut parts: impl Iterator<Item = impl AsRef<str>>,
88 line_number: usize,
89) -> Result<Statement> {
90 let value = parts
91 .next()
92 .ok_or_else(|| anyhow!("sleep instruction requires a duration"))?;
93 let duration = parse_duration_token(value.as_ref())?;
94 Ok(Statement::new(
95 StatementKind::Sleep,
96 Value::Duration(duration),
97 0,
98 line_number,
99 1,
100 ))
101}
102
103pub fn parse_bank(
105 mut parts: impl Iterator<Item = impl AsRef<str>>,
106 line_number: usize,
107) -> Result<Statement> {
108 let name = parts
109 .next()
110 .ok_or_else(|| anyhow!("bank declaration requires a name"))?
111 .as_ref()
112 .to_string();
113
114 let alias = if let Some(word) = parts.next() {
115 if word.as_ref() == "as" {
116 parts.next().map(|v| v.as_ref().to_string())
117 } else {
118 None
119 }
120 } else {
121 None
122 };
123
124 Ok(Statement::new(
125 StatementKind::Bank { name, alias },
126 Value::Null,
127 0,
128 line_number,
129 1,
130 ))
131}
132
133pub fn parse_let(
135 line: &str,
136 mut parts: impl Iterator<Item = impl AsRef<str>>,
137 line_number: usize,
138) -> Result<Statement> {
139 let name = parts
140 .next()
141 .ok_or_else(|| anyhow!("let statement requires a name"))?
142 .as_ref()
143 .to_string();
144
145 let remainder = line
146 .splitn(2, '=')
147 .nth(1)
148 .map(|r| r.trim().to_string())
149 .unwrap_or_default();
150
151 let value = if remainder.is_empty() {
152 None
153 } else if remainder.starts_with("synth ") {
154 if remainder.contains("->") {
156 let stmt =
157 crate::language::syntax::parser::driver::parse_arrow_call(&remainder, line_number)?;
158 let mut synth_map = std::collections::HashMap::new();
159 let first_part = remainder.split("->").next().unwrap_or("synth");
160 let synth_def_val = parse_synth_definition(first_part)?;
161 let mut param_map_present = false;
162 if let Value::Map(m) = synth_def_val {
163 param_map_present = m
164 .iter()
165 .any(|(k, v)| k != "type" && k != "waveform" && matches!(v, Value::Map(_)));
166 synth_map = m;
167 }
168
169 if let Value::Map(chain_container) = stmt.value {
170 let mut effects_arr: Vec<Value> = Vec::new();
171
172 if let Some(Value::String(first_method)) = chain_container.get("method") {
173 let mut handled_as_synth_param = false;
174 if let Some(Value::Array(args_arr)) = chain_container.get("args") {
175 if first_method == "type" {
176 if let Some(first) = args_arr.get(0) {
177 match first {
178 Value::String(s) => {
179 synth_map.insert(
180 "synth_type".to_string(),
181 Value::String(s.clone()),
182 );
183 handled_as_synth_param = true;
184 }
185 Value::Identifier(id) => {
186 synth_map.insert(
187 "synth_type".to_string(),
188 Value::String(id.clone()),
189 );
190 handled_as_synth_param = true;
191 }
192 _ => {}
193 }
194 }
195 } else if first_method == "adsr" {
196 if let Some(first) = args_arr.get(0) {
197 if let Value::Map(arg_map) = first {
198 for (k, v) in arg_map.iter() {
199 if ["attack", "decay", "sustain", "release"]
200 .contains(&k.as_str())
201 {
202 synth_map.insert(k.clone(), v.clone());
203 }
204 }
205 handled_as_synth_param = true;
206 }
207 }
208 }
209 }
210
211 if !handled_as_synth_param {
212 let mut eff_map = std::collections::HashMap::new();
213 eff_map.insert("type".to_string(), Value::String(first_method.clone()));
214 if let Some(Value::Array(args_arr)) = chain_container.get("args") {
215 if let Some(first) = args_arr.get(0) {
216 if let Value::Map(arg_map) = first {
217 for (k, v) in arg_map.iter() {
218 eff_map.insert(k.clone(), v.clone());
219 }
220 } else {
221 eff_map.insert("value".to_string(), first.clone());
222 }
223 }
224 }
225 effects_arr.push(Value::Map(eff_map));
226 }
227 }
228
229 if let Some(Value::Array(chain_arr)) = chain_container.get("chain") {
230 for call_val in chain_arr.iter() {
231 if let Value::Map(call_map) = call_val {
232 if let Some(Value::String(mname)) = call_map.get("method") {
233 if mname == "type" {
234 if let Some(Value::Array(args_arr)) = call_map.get("args") {
235 if let Some(first) = args_arr.get(0) {
236 match first {
237 Value::String(s) => {
238 synth_map.insert(
239 "synth_type".to_string(),
240 Value::String(s.clone()),
241 );
242 continue;
243 }
244 Value::Identifier(id) => {
245 synth_map.insert(
246 "synth_type".to_string(),
247 Value::String(id.clone()),
248 );
249 continue;
250 }
251 _ => {}
252 }
253 }
254 }
255 }
256 if mname == "adsr" {
257 if let Some(Value::Array(args_arr)) = call_map.get("args") {
258 if let Some(first) = args_arr.get(0) {
259 if let Value::Map(arg_map) = first {
260 for (k, v) in arg_map.iter() {
261 if ["attack", "decay", "sustain", "release"]
262 .contains(&k.as_str())
263 {
264 synth_map.insert(k.clone(), v.clone());
265 }
266 }
267 continue;
268 }
269 }
270 }
271 }
272 let mut eff_map = std::collections::HashMap::new();
273 eff_map.insert("type".to_string(), Value::String(mname.clone()));
274 if let Some(Value::Array(args_arr)) = call_map.get("args") {
275 if let Some(first) = args_arr.get(0) {
276 if let Value::Map(arg_map) = first {
277 for (k, v) in arg_map.iter() {
278 eff_map.insert(k.clone(), v.clone());
279 }
280 } else {
281 eff_map.insert("value".to_string(), first.clone());
282 }
283 }
284 }
285 effects_arr.push(Value::Map(eff_map));
286 }
287 }
288 }
289 }
290
291 if !effects_arr.is_empty() {
292 synth_map.insert("chain".to_string(), Value::Array(effects_arr));
293 }
294 }
295
296 if param_map_present && synth_map.contains_key("chain") {
297 eprintln!(
298 "DEPRECATION: chained params for synth with param map are deprecated — both will be merged, but prefer chained params."
299 );
300 }
301
302 Some(Value::Map(synth_map))
303 } else {
304 Some(parse_synth_definition(&remainder)?)
305 }
306 } else if remainder.starts_with('[') && remainder.ends_with(']') {
307 Some(parse_array_value(&remainder)?)
308 } else if remainder.starts_with('.') {
309 let stmt = crate::language::syntax::parser::driver::trigger::parse_trigger_line(
310 &remainder,
311 line_number,
312 )?;
313 Some(crate::language::syntax::ast::Value::Statement(Box::new(
314 stmt,
315 )))
316 } else {
317 let lower = remainder.to_lowercase();
318 if lower == "true" {
319 Some(Value::Boolean(true))
320 } else if lower == "false" {
321 Some(Value::Boolean(false))
322 } else if let Ok(num) = remainder.parse::<f32>() {
323 Some(Value::Number(num))
324 } else {
325 Some(Value::Identifier(remainder))
326 }
327 };
328
329 Ok(Statement::new(
330 StatementKind::Let { name, value },
331 Value::Null,
332 0,
333 line_number,
334 1,
335 ))
336}
337
338pub fn parse_var(
340 line: &str,
341 mut parts: impl Iterator<Item = impl AsRef<str>>,
342 line_number: usize,
343) -> Result<Statement> {
344 let name = parts
345 .next()
346 .ok_or_else(|| anyhow!("var statement requires a name"))?
347 .as_ref()
348 .to_string();
349
350 let remainder = line
351 .splitn(2, '=')
352 .nth(1)
353 .map(|r| r.trim().to_string())
354 .unwrap_or_default();
355
356 let value = if remainder.is_empty() {
357 None
358 } else {
359 let lower = remainder.to_lowercase();
361 if lower == "true" {
362 Some(Value::Boolean(true))
363 } else if lower == "false" {
364 Some(Value::Boolean(false))
365 } else if let Ok(num) = remainder.parse::<f32>() {
366 Some(Value::Number(num))
367 } else {
368 Some(Value::Identifier(remainder))
369 }
370 };
371
372 Ok(Statement::new(
373 StatementKind::Var { name, value },
374 Value::Null,
375 0,
376 line_number,
377 1,
378 ))
379}
380
381pub fn parse_const(
383 line: &str,
384 mut parts: impl Iterator<Item = impl AsRef<str>>,
385 line_number: usize,
386) -> Result<Statement> {
387 let name = parts
388 .next()
389 .ok_or_else(|| anyhow!("const statement requires a name"))?
390 .as_ref()
391 .to_string();
392
393 let remainder = line
394 .splitn(2, '=')
395 .nth(1)
396 .map(|r| r.trim().to_string())
397 .unwrap_or_default();
398
399 if remainder.is_empty() {
400 return Err(anyhow!("const declaration requires initialization"));
401 }
402
403 let value = {
405 if remainder.starts_with("synth ") {
408 if remainder.contains("->") {
409 let stmt = crate::language::syntax::parser::driver::parse_arrow_call(
410 &remainder,
411 line_number,
412 )?;
413 Some(stmt.value)
414 } else {
415 Some(parse_synth_definition(&remainder)?)
416 }
417 } else if remainder.starts_with('[') && remainder.ends_with(']') {
418 Some(parse_array_value(&remainder)?)
419 } else if remainder.starts_with('.') {
420 let stmt = crate::language::syntax::parser::driver::trigger::parse_trigger_line(
421 &remainder,
422 line_number,
423 )?;
424 Some(crate::language::syntax::ast::Value::Statement(Box::new(
425 stmt,
426 )))
427 } else {
428 let lower = remainder.to_lowercase();
429 if lower == "true" {
430 Some(Value::Boolean(true))
431 } else if lower == "false" {
432 Some(Value::Boolean(false))
433 } else if let Ok(num) = remainder.parse::<f32>() {
434 Some(Value::Number(num))
435 } else {
436 Some(Value::Identifier(remainder))
437 }
438 }
439 };
440
441 Ok(Statement::new(
442 StatementKind::Const { name, value },
443 Value::Null,
444 0,
445 line_number,
446 1,
447 ))
448}
449
450pub fn parse_return(line: &str, line_number: usize) -> Result<Statement> {
452 let remainder = line
454 .strip_prefix("return")
455 .ok_or_else(|| anyhow!("invalid return statement"))?
456 .trim();
457
458 if remainder.is_empty() {
459 Ok(Statement::new(
460 StatementKind::Return { value: None },
461 Value::Null,
462 0,
463 line_number,
464 1,
465 ))
466 } else {
467 let val = crate::language::syntax::parser::driver::helpers::parse_single_arg(remainder)?;
469 Ok(Statement::new(
470 StatementKind::Return {
471 value: Some(Box::new(val)),
472 },
473 Value::Null,
474 0,
475 line_number,
476 1,
477 ))
478 }
479}