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>:
172    let count_str_ref = parts
173        .next()
174        .ok_or_else(|| anyhow!("loop requires a count"))?;
175    let count_str = count_str_ref.as_ref().trim_end_matches(':');
176
177    let count = if let Ok(num) = count_str.parse::<f32>() {
178        Value::Number(num)
179    } else {
180        Value::Identifier(count_str.to_string())
181    };
182
183    Ok(Statement::new(
184        StatementKind::Loop {
185            count,
186            body: Vec::new(), // Will be filled during indentation parsing
187        },
188        Value::Null,
189        0,
190        line_number,
191        1,
192    ))
193}
194
195/// Parse for statement
196pub fn parse_for(
197    parts: impl Iterator<Item = impl AsRef<str>>,
198    line_number: usize,
199) -> Result<Statement> {
200    // Parse: for <var> in <iterable>:
201    let parts_vec: Vec<String> = parts.map(|s| s.as_ref().to_string()).collect();
202
203    if parts_vec.is_empty() {
204        return Err(anyhow!("for loop requires a variable name"));
205    }
206
207    let variable = parts_vec[0].clone();
208
209    // Expect "in"
210    if parts_vec.len() < 2 || parts_vec[1] != "in" {
211        return Err(anyhow!("Expected 'in' after variable in for loop"));
212    }
213
214    // Parse iterable (array or identifier)
215    let iterable_str = parts_vec[2..].join(" ");
216    let iterable_str = iterable_str.trim_end_matches(':').trim();
217
218    let iterable = if iterable_str.starts_with('[') && iterable_str.ends_with(']') {
219        // Parse as array: [1, 2, 3] or range: [1..10]
220        parse_array_value(iterable_str)?
221    } else {
222        // Parse as identifier or number
223        if let Ok(num) = iterable_str.parse::<f32>() {
224            Value::Number(num)
225        } else {
226            Value::Identifier(iterable_str.to_string())
227        }
228    };
229
230    Ok(Statement::new(
231        StatementKind::For {
232            variable,
233            iterable,
234            body: Vec::new(), // Will be filled during indentation parsing
235        },
236        Value::Null,
237        0,
238        line_number,
239        1,
240    ))
241}
242
243/// Parse if statement
244pub fn parse_if(
245    parts: impl Iterator<Item = impl AsRef<str>>,
246    line_number: usize,
247) -> Result<Statement> {
248    // Parse: if <condition>:
249    let condition_str = parts
250        .map(|s| s.as_ref().to_string())
251        .collect::<Vec<_>>()
252        .join(" ");
253    let condition_str = condition_str.trim_end_matches(':').trim();
254
255    // Parse condition (for now, simple comparison)
256    let condition = parse_condition(condition_str)?;
257
258    Ok(Statement::new(
259        StatementKind::If {
260            condition,
261            body: Vec::new(), // Will be filled during indentation parsing
262            else_body: None,
263        },
264        Value::Null,
265        0,
266        line_number,
267        1,
268    ))
269}
270
271/// Parse else statement (can be "else if" or just "else")
272pub fn parse_else(line: &str, line_number: usize) -> Result<Statement> {
273    // Check if this is "else if" or just "else"
274    if line.trim().starts_with("else if") {
275        // Parse as "else if <condition>:"
276        let condition_str = line.trim().strip_prefix("else if").unwrap().trim();
277        let condition_str = condition_str.trim_end_matches(':').trim();
278        let condition = parse_condition(condition_str)?;
279
280        Ok(Statement::new(
281            StatementKind::If {
282                condition,
283                body: Vec::new(), // Will be filled during indentation parsing
284                else_body: None,
285            },
286            Value::Null,
287            0,
288            line_number,
289            1,
290        ))
291    } else {
292        // Just "else:"
293        Ok(Statement::new(
294            StatementKind::Comment, // We'll handle this specially during body parsing
295            Value::String("else".to_string()),
296            0,
297            line_number,
298            1,
299        ))
300    }
301}
302
303/// Parse call statement
304/// Supports:
305/// - call groupName
306/// - call target = "pattern" (inline pattern assignment)
307pub fn parse_call(
308    line: &str,
309    mut parts: impl Iterator<Item = impl AsRef<str>>,
310    line_number: usize,
311) -> Result<Statement> {
312    let first = parts
313        .next()
314        .ok_or_else(|| anyhow!("call requires a target"))?
315        .as_ref()
316        .to_string();
317
318    // Check if this is an inline pattern assignment: call target = "pattern"
319    if line.contains('=') {
320        // Split by = to get target and pattern
321        let eq_parts: Vec<&str> = line.splitn(2, '=').collect();
322        if eq_parts.len() == 2 {
323            // Extract target (remove "call " prefix)
324            let target = eq_parts[0]
325                .trim()
326                .strip_prefix("call")
327                .unwrap_or(eq_parts[0])
328                .trim()
329                .to_string();
330            let pattern = eq_parts[1].trim().trim_matches('"').to_string();
331
332            // Create an inline pattern statement
333            // Store pattern data in the Call's value field
334            let mut map = HashMap::new();
335            map.insert("inline_pattern".to_string(), Value::Boolean(true));
336            map.insert("target".to_string(), Value::String(target.clone()));
337            map.insert("pattern".to_string(), Value::String(pattern));
338
339            return Ok(Statement::new(
340                StatementKind::Call {
341                    name: target,
342                    args: Vec::new(),
343                },
344                Value::Map(map),
345                0,
346                line_number,
347                1,
348            ));
349        }
350    }
351
352    // Regular call statement (call groupName or call patternName)
353    Ok(Statement::new(
354        StatementKind::Call {
355            name: first,
356            args: Vec::new(),
357        },
358        Value::Null,
359        0,
360        line_number,
361        1,
362    ))
363}
364
365/// Parse spawn statement
366pub fn parse_spawn(
367    mut parts: impl Iterator<Item = impl AsRef<str>>,
368    line_number: usize,
369) -> Result<Statement> {
370    let name = parts
371        .next()
372        .ok_or_else(|| anyhow!("spawn requires a target"))?
373        .as_ref()
374        .to_string();
375
376    Ok(Statement::new(
377        StatementKind::Spawn {
378            name,
379            args: Vec::new(),
380        },
381        Value::Null,
382        0,
383        line_number,
384        1,
385    ))
386}
387
388/// Parse on statement (event handler)
389pub fn parse_on(
390    mut parts: impl Iterator<Item = impl AsRef<str>>,
391    line_number: usize,
392) -> Result<Statement> {
393    // Parse: on eventName: or on eventName once:
394    let event_name = parts
395        .next()
396        .ok_or_else(|| anyhow!("on statement requires an event name"))?
397        .as_ref()
398        .to_string();
399
400    // Check for "once" keyword
401    let next_word = parts.next();
402    let once = next_word.map(|w| w.as_ref() == "once").unwrap_or(false);
403
404    // Store once flag in args (temporary workaround until we have proper AST)
405    let args = if once {
406        Some(vec![Value::String("once".to_string())])
407    } else {
408        None
409    };
410
411    Ok(Statement::new(
412        StatementKind::On {
413            event: event_name,
414            args,
415            body: Vec::new(), // Will be filled during indentation parsing
416        },
417        Value::Null,
418        0,
419        line_number,
420        1,
421    ))
422}
423
424/// Parse emit statement (event emission)
425pub fn parse_emit(
426    line: &str,
427    mut parts: impl Iterator<Item = impl AsRef<str>>,
428    line_number: usize,
429) -> Result<Statement> {
430    // Parse: emit eventName { key: value, key: value }
431    let event_name = parts
432        .next()
433        .ok_or_else(|| anyhow!("emit statement requires an event name"))?
434        .as_ref()
435        .to_string();
436
437    // Parse payload if present
438    let remainder = line.splitn(2, &event_name).nth(1).unwrap_or("").trim();
439
440    let payload = if remainder.starts_with('{') && remainder.ends_with('}') {
441        // Parse as map: { key: value, key: value }
442        let inner = &remainder[1..remainder.len() - 1];
443        let mut map = HashMap::new();
444
445        // Split by comma and parse key-value pairs
446        for pair in inner.split(',') {
447            let parts: Vec<&str> = pair.split(':').collect();
448            if parts.len() == 2 {
449                let key = parts[0].trim().to_string();
450                let value_str = parts[1].trim();
451
452                // Parse value
453                let value = if value_str.starts_with('"') && value_str.ends_with('"') {
454                    Value::String(value_str.trim_matches('"').to_string())
455                } else if let Ok(num) = value_str.parse::<f32>() {
456                    Value::Number(num)
457                } else {
458                    Value::Identifier(value_str.to_string())
459                };
460
461                map.insert(key, value);
462            }
463        }
464
465        Some(Value::Map(map))
466    } else {
467        None
468    };
469
470    Ok(Statement::new(
471        StatementKind::Emit {
472            event: event_name,
473            payload,
474        },
475        Value::Null,
476        0,
477        line_number,
478        1,
479    ))
480}