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