devalang_wasm/language/syntax/parser/driver/statements/
structure.rs

1use super::super::helpers::{parse_array_value, parse_condition};
2use crate::language::syntax::ast::{Statement, StatementKind, Value};
3/// Structure statement parsing: group, pattern, loop, for, if, on, emit, call, spawn
4use anyhow::{Result, anyhow};
5use std::collections::HashMap;
6use std::iter::Iterator;
7
8/// Parse pattern statement
9/// Supports:
10/// - pattern name with target = "pattern"
11/// - pattern name with target { options } = "pattern"
12pub fn parse_pattern(
13    mut parts: impl Iterator<Item = impl AsRef<str>>,
14    line_number: usize,
15) -> Result<Statement> {
16    let name = parts
17        .next()
18        .ok_or_else(|| anyhow!("pattern requires a name"))?
19        .as_ref()
20        .to_string();
21
22    let mut target = None;
23    let mut pattern_str = None;
24    let mut options: HashMap<String, Value> = HashMap::new();
25
26    // Check for "with" keyword
27    if let Some(word) = parts.next() {
28        if word.as_ref() == "with" {
29            // Next is the target
30            target = parts.next().map(|v| v.as_ref().to_string());
31
32            // Collect remaining parts to check for options block or pattern
33            let rest: Vec<String> = parts.map(|s| s.as_ref().to_string()).collect();
34            let joined = rest.join(" ");
35
36            // Check if there's an options block: { key: value, key: value }
37            if let Some(brace_start) = joined.find('{') {
38                if let Some(brace_end) = joined.rfind('}') {
39                    // Extract options block
40                    let options_str = &joined[brace_start + 1..brace_end];
41
42                    // Parse options (key: value pairs)
43                    for pair in options_str.split(',') {
44                        let parts: Vec<&str> = pair.split(':').collect();
45                        if parts.len() == 2 {
46                            let key = parts[0].trim().to_string();
47                            let value_str = parts[1].trim();
48
49                            // Parse value
50                            let value = if let Ok(num) = value_str.parse::<f32>() {
51                                Value::Number(num)
52                            } else if value_str == "true" {
53                                Value::Boolean(true)
54                            } else if value_str == "false" {
55                                Value::Boolean(false)
56                            } else if value_str.starts_with('"') && value_str.ends_with('"') {
57                                Value::String(value_str.trim_matches('"').to_string())
58                            } else {
59                                Value::Identifier(value_str.to_string())
60                            };
61
62                            options.insert(key, value);
63                        }
64                    }
65
66                    // Check for "=" and pattern after the brace
67                    let after_brace = joined[brace_end + 1..].trim();
68                    if let Some(eq_pos) = after_brace.find('=') {
69                        let pattern_part = after_brace[eq_pos + 1..].trim();
70                        pattern_str = Some(pattern_part.trim_matches('"').to_string());
71                    }
72                } else {
73                    return Err(anyhow!("Unclosed brace in pattern options"));
74                }
75            } else {
76                // No options block, check for "=" and pattern directly
77                if let Some(eq_pos) = joined.find('=') {
78                    let pattern_part = joined[eq_pos + 1..].trim();
79                    pattern_str = Some(pattern_part.trim_matches('"').to_string());
80                }
81            }
82        }
83    }
84
85    // Store pattern string and options in value
86    let value = if !options.is_empty() {
87        let mut map = options;
88        if let Some(pat) = pattern_str {
89            map.insert("pattern".to_string(), Value::String(pat));
90        }
91        Value::Map(map)
92    } else {
93        pattern_str.map(Value::String).unwrap_or(Value::Null)
94    };
95
96    Ok(Statement::new(
97        StatementKind::Pattern { name, target },
98        value,
99        0,
100        line_number,
101        1,
102    ))
103}
104
105/// Parse group statement
106pub fn parse_group(
107    mut parts: impl Iterator<Item = impl AsRef<str>>,
108    line_number: usize,
109) -> Result<Statement> {
110    let name = parts
111        .next()
112        .ok_or_else(|| anyhow!("group requires a name"))?
113        .as_ref()
114        .trim_end_matches(':')
115        .to_string();
116
117    Ok(Statement::new(
118        StatementKind::Group {
119            name: name.clone(),
120            body: Vec::new(),
121        },
122        Value::Identifier(name),
123        0,
124        line_number,
125        1,
126    ))
127}
128
129/// Parse automate statement: automate <target> [mode <note|global>]:
130pub fn parse_automate(
131    mut parts: impl Iterator<Item = impl AsRef<str>>,
132    line_number: usize,
133) -> Result<Statement> {
134    // First part is the target
135    let target = parts
136        .next()
137        .ok_or_else(|| anyhow!("automate requires a target"))?
138        .as_ref()
139        .trim_end_matches(':')
140        .to_string();
141
142    // Optional mode: 'mode note' or 'mode global'
143    let mut mode: Option<String> = None;
144    if let Some(word) = parts.next() {
145        if word.as_ref() == "mode" {
146            if let Some(m) = parts.next() {
147                mode = Some(m.as_ref().trim_end_matches(':').to_string());
148            }
149        }
150    }
151
152    let mut map = HashMap::new();
153    if let Some(m) = mode {
154        map.insert("mode".to_string(), Value::String(m));
155    }
156
157    Ok(Statement::new(
158        StatementKind::Automate { target },
159        Value::Map(map),
160        0,
161        line_number,
162        1,
163    ))
164}
165
166/// Parse loop statement
167pub fn parse_loop(
168    mut parts: impl Iterator<Item = impl AsRef<str>>,
169    line_number: usize,
170) -> Result<Statement> {
171    // Parse: loop <count>:  OR plain `loop:` (no count)
172    // If no count is provided we store Value::Null to indicate an unbounded loop
173    let count_opt = parts.next();
174
175    let count = if let Some(count_str_ref) = count_opt {
176        let count_str = count_str_ref.as_ref().trim_end_matches(':');
177        // Support forms:
178        // - loop pass:
179        // - loop pass():
180        // - loop pass(500):
181        // - loop 4:
182        if let Ok(num) = count_str.parse::<f32>() {
183            Value::Number(num)
184        } else if count_str.contains('(') && count_str.ends_with(')') {
185            // Parse as a call-like value (e.g., pass(500))
186            if let Some(open_idx) = count_str.find('(') {
187                let name = count_str[..open_idx].to_string();
188                let inside = &count_str[open_idx + 1..count_str.len() - 1];
189                let args = if inside.trim().is_empty() {
190                    Vec::new()
191                } else {
192                    crate::language::syntax::parser::driver::parse_function_args(inside)?
193                };
194                Value::Call { name, args }
195            } else {
196                Value::Identifier(count_str.to_string())
197            }
198        } else {
199            Value::Identifier(count_str.to_string())
200        }
201    } else {
202        // No explicit count -> mark as Null (infinite/indefinite)
203        Value::Null
204    };
205
206    Ok(Statement::new(
207        StatementKind::Loop {
208            count,
209            body: Vec::new(), // Will be filled during indentation parsing
210        },
211        Value::Null,
212        0,
213        line_number,
214        1,
215    ))
216}
217
218/// Parse for statement
219pub fn parse_for(
220    parts: impl Iterator<Item = impl AsRef<str>>,
221    line_number: usize,
222) -> Result<Statement> {
223    // Parse: for <var> in <iterable>:
224    let parts_vec: Vec<String> = parts.map(|s| s.as_ref().to_string()).collect();
225
226    if parts_vec.is_empty() {
227        return Err(anyhow!("for loop requires a variable name"));
228    }
229
230    let variable = parts_vec[0].clone();
231
232    // Expect "in"
233    if parts_vec.len() < 2 || parts_vec[1] != "in" {
234        return Err(anyhow!("Expected 'in' after variable in for loop"));
235    }
236
237    // Parse iterable (array or identifier)
238    let iterable_str = parts_vec[2..].join(" ");
239    let iterable_str = iterable_str.trim_end_matches(':').trim();
240
241    let iterable = if iterable_str.starts_with('[') && iterable_str.ends_with(']') {
242        // Parse as array: [1, 2, 3] or range: [1..10]
243        parse_array_value(iterable_str)?
244    } else {
245        // Parse as identifier or number
246        if let Ok(num) = iterable_str.parse::<f32>() {
247            Value::Number(num)
248        } else {
249            Value::Identifier(iterable_str.to_string())
250        }
251    };
252
253    Ok(Statement::new(
254        StatementKind::For {
255            variable,
256            iterable,
257            body: Vec::new(), // Will be filled during indentation parsing
258        },
259        Value::Null,
260        0,
261        line_number,
262        1,
263    ))
264}
265
266/// Parse function statement: function name(arg1, arg2, ...):
267pub fn parse_function(line: &str, line_number: usize) -> Result<Statement> {
268    // Expect form: function <name>(arg1, arg2, ...):
269    let after_kw = line
270        .trim()
271        .strip_prefix("function")
272        .ok_or_else(|| anyhow!("function parsing error"))?
273        .trim();
274
275    // Find name and args parentheses
276    if let Some(paren_idx) = after_kw.find('(') {
277        let name = after_kw[..paren_idx].trim().to_string();
278        if let Some(close_idx) = after_kw.rfind(')') {
279            let args_str = &after_kw[paren_idx + 1..close_idx];
280            // Parse parameter names: split by ',' and trim
281            let params: Vec<String> = if args_str.trim().is_empty() {
282                Vec::new()
283            } else {
284                args_str
285                    .split(',')
286                    .map(|s| s.trim().trim_end_matches(':').to_string())
287                    .filter(|s| !s.is_empty())
288                    .collect()
289            };
290
291            return Ok(Statement::new(
292                StatementKind::Function {
293                    name: name.clone(),
294                    parameters: params,
295                    body: Vec::new(),
296                },
297                Value::Identifier(name),
298                0,
299                line_number,
300                1,
301            ));
302        }
303    }
304
305    Err(anyhow!("Invalid function declaration"))
306}
307
308/// Parse if statement
309pub fn parse_if(
310    parts: impl Iterator<Item = impl AsRef<str>>,
311    line_number: usize,
312) -> Result<Statement> {
313    // Parse: if <condition>:
314    let condition_str = parts
315        .map(|s| s.as_ref().to_string())
316        .collect::<Vec<_>>()
317        .join(" ");
318    let condition_str = condition_str.trim_end_matches(':').trim();
319
320    // Parse condition (for now, simple comparison)
321    let condition = parse_condition(condition_str)?;
322
323    Ok(Statement::new(
324        StatementKind::If {
325            condition,
326            body: Vec::new(), // Will be filled during indentation parsing
327            else_body: None,
328        },
329        Value::Null,
330        0,
331        line_number,
332        1,
333    ))
334}
335
336/// Parse else statement (can be "else if" or just "else")
337pub fn parse_else(line: &str, line_number: usize) -> Result<Statement> {
338    // Check if this is "else if" or just "else"
339    if line.trim().starts_with("else if") {
340        // Parse as "else if <condition>:"
341        let condition_str = line.trim().strip_prefix("else if").unwrap().trim();
342        let condition_str = condition_str.trim_end_matches(':').trim();
343        let condition = parse_condition(condition_str)?;
344
345        // Mark this If as originating from an 'else if' so the outer parser
346        // post-process can attach it as an else-branch to the previous If.
347        Ok(Statement::new(
348            StatementKind::If {
349                condition,
350                body: Vec::new(), // Will be filled during indentation parsing
351                else_body: None,
352            },
353            Value::String("else-if".to_string()),
354            0,
355            line_number,
356            1,
357        ))
358    } else {
359        // Just "else:"
360        // Return a lightweight marker (Comment with value "else") so the
361        // outer parser can detect and attach the following indented block
362        // as the else-body of the preceding If.
363        Ok(Statement::new(
364            StatementKind::Comment, // We'll handle this specially during body parsing
365            Value::String("else".to_string()),
366            0,
367            line_number,
368            1,
369        ))
370    }
371}
372
373/// Parse call statement
374/// Supports:
375/// - call groupName
376/// - call target = "pattern" (inline pattern assignment)
377pub fn parse_call(
378    line: &str,
379    mut parts: impl Iterator<Item = impl AsRef<str>>,
380    line_number: usize,
381) -> Result<Statement> {
382    // Determine the call name. If the call uses parentheses (call name(arg1, arg2))
383    // extract the name from the text before the first '(', otherwise fall back to
384    // the whitespace-split first token.
385    let first_token = parts
386        .next()
387        .ok_or_else(|| anyhow!("call requires a target"))?
388        .as_ref()
389        .to_string();
390    let mut call_name = first_token.clone();
391    if line.find('(').is_some() {
392        // Extract the substring between 'call' and the first '(' as the name
393        if let Some(after_call) = line.trim().strip_prefix("call") {
394            let snippet = after_call.trim();
395            if let Some(pidx) = snippet.find('(') {
396                call_name = snippet[..pidx].trim().to_string();
397            }
398        }
399    }
400
401    // Check if this is an inline pattern assignment: call target = "pattern"
402    if line.contains('=') {
403        // Split by = to get target and pattern
404        let eq_parts: Vec<&str> = line.splitn(2, '=').collect();
405        if eq_parts.len() == 2 {
406            // Extract target (remove "call " prefix)
407            let target = eq_parts[0]
408                .trim()
409                .strip_prefix("call")
410                .unwrap_or(eq_parts[0])
411                .trim()
412                .to_string();
413            let pattern = eq_parts[1].trim().trim_matches('"').to_string();
414
415            // Create an inline pattern statement
416            // Store pattern data in the Call's value field
417            let mut map = HashMap::new();
418            map.insert("inline_pattern".to_string(), Value::Boolean(true));
419            map.insert("target".to_string(), Value::String(target.clone()));
420            map.insert("pattern".to_string(), Value::String(pattern));
421
422            return Ok(Statement::new(
423                StatementKind::Call {
424                    name: target,
425                    args: Vec::new(),
426                },
427                Value::Map(map),
428                0,
429                line_number,
430                1,
431            ));
432        }
433    }
434
435    // Regular call statement (call groupName or call patternName)
436    // Support call with arguments: call name(arg1, arg2)
437    if let Some(paren_idx) = line.find('(') {
438        if let Some(close_idx) = line.rfind(')') {
439            let args_str = &line[paren_idx + 1..close_idx];
440            let args = if args_str.trim().is_empty() {
441                Vec::new()
442            } else {
443                // Reuse parse_function_args from parent module
444                crate::language::syntax::parser::driver::parse_function_args(args_str)?
445            };
446
447            return Ok(Statement::new(
448                StatementKind::Call {
449                    name: call_name,
450                    args,
451                },
452                Value::Null,
453                0,
454                line_number,
455                1,
456            ));
457        }
458    }
459
460    Ok(Statement::new(
461        StatementKind::Call {
462            name: call_name,
463            args: Vec::new(),
464        },
465        Value::Null,
466        0,
467        line_number,
468        1,
469    ))
470}
471
472/// Parse break statement
473pub fn parse_break(
474    _parts: impl Iterator<Item = impl AsRef<str>>,
475    line_number: usize,
476) -> Result<Statement> {
477    Ok(Statement::new(
478        StatementKind::Break,
479        Value::Null,
480        0,
481        line_number,
482        1,
483    ))
484}
485
486/// Parse spawn statement
487pub fn parse_spawn(
488    mut parts: impl Iterator<Item = impl AsRef<str>>,
489    line_number: usize,
490) -> Result<Statement> {
491    let name = parts
492        .next()
493        .ok_or_else(|| anyhow!("spawn requires a target"))?
494        .as_ref()
495        .to_string();
496
497    Ok(Statement::new(
498        StatementKind::Spawn {
499            name,
500            args: Vec::new(),
501        },
502        Value::Null,
503        0,
504        line_number,
505        1,
506    ))
507}
508
509/// Parse on statement (event handler)
510pub fn parse_on(
511    mut parts: impl Iterator<Item = impl AsRef<str>>,
512    line_number: usize,
513) -> Result<Statement> {
514    // Parse: on eventName: or on eventName once:
515    // Accept forms like: on beat: | on beat once: | on beat(4): | on beat(4) once:
516    let raw = parts
517        .next()
518        .ok_or_else(|| anyhow!("on statement requires an event name"))?
519        .as_ref()
520        .to_string();
521
522    // Parse optional interval within parentheses, e.g. beat(4)
523    let mut event_name = raw.clone();
524    let mut args_vec: Vec<Value> = Vec::new();
525    if let Some(open_idx) = raw.find('(') {
526        if raw.ends_with(')') {
527            let base = raw[..open_idx].to_string();
528            let inside = &raw[open_idx + 1..raw.len() - 1];
529            if let Ok(n) = inside.trim().parse::<f32>() {
530                args_vec.push(Value::Number(n));
531            }
532            event_name = base;
533        }
534    }
535
536    // Check for "once" keyword as next token
537    let next_word = parts.next();
538    let once = next_word.map(|w| w.as_ref() == "once").unwrap_or(false);
539    if once {
540        args_vec.push(Value::String("once".to_string()));
541    }
542
543    let args = if !args_vec.is_empty() {
544        Some(args_vec)
545    } else {
546        None
547    };
548
549    Ok(Statement::new(
550        StatementKind::On {
551            event: event_name,
552            args,
553            body: Vec::new(), // Will be filled during indentation parsing
554        },
555        Value::Null,
556        0,
557        line_number,
558        1,
559    ))
560}
561
562/// Parse emit statement (event emission)
563pub fn parse_emit(
564    line: &str,
565    mut parts: impl Iterator<Item = impl AsRef<str>>,
566    line_number: usize,
567) -> Result<Statement> {
568    // Parse: emit eventName { key: value, key: value }
569    let event_name = parts
570        .next()
571        .ok_or_else(|| anyhow!("emit statement requires an event name"))?
572        .as_ref()
573        .to_string();
574
575    // Parse payload if present
576    let remainder = line.splitn(2, &event_name).nth(1).unwrap_or("").trim();
577
578    let payload = if remainder.starts_with('{') && remainder.ends_with('}') {
579        // Parse as map: { key: value, key: value }
580        let inner = &remainder[1..remainder.len() - 1];
581        let mut map = HashMap::new();
582
583        // Split by comma and parse key-value pairs
584        for pair in inner.split(',') {
585            let parts: Vec<&str> = pair.split(':').collect();
586            if parts.len() == 2 {
587                let key = parts[0].trim().to_string();
588                let value_str = parts[1].trim();
589
590                // Parse value
591                let value = if value_str.starts_with('"') && value_str.ends_with('"') {
592                    Value::String(value_str.trim_matches('"').to_string())
593                } else if let Ok(num) = value_str.parse::<f32>() {
594                    Value::Number(num)
595                } else {
596                    Value::Identifier(value_str.to_string())
597                };
598
599                map.insert(key, value);
600            }
601        }
602
603        Some(Value::Map(map))
604    } else {
605        None
606    };
607
608    Ok(Statement::new(
609        StatementKind::Emit {
610            event: event_name,
611            payload,
612        },
613        Value::Null,
614        0,
615        line_number,
616        1,
617    ))
618}