Skip to main content

shape_ast/parser/
statements.rs

1//! Statement parsing for Shape
2
3use crate::error::{Result, ShapeError};
4use crate::parser::pair_location;
5use pest::iterators::Pair;
6
7use crate::ast::{Assignment, ForInit, ForLoop, IfStatement, Span, Statement, WhileLoop};
8use crate::parser::extensions::parse_extend_statement;
9use crate::parser::{Rule, expressions, pair_span, parse_variable_decl};
10
11/// Parse a statement
12pub fn parse_statement(pair: Pair<Rule>) -> Result<Statement> {
13    let pair_loc = pair_location(&pair);
14    let span = pair_span(&pair);
15    let inner = pair
16        .into_inner()
17        .next()
18        .ok_or_else(|| ShapeError::ParseError {
19            message: "expected statement content".to_string(),
20            location: Some(pair_loc.clone()),
21        })?;
22
23    match inner.as_rule() {
24        Rule::return_stmt => parse_return_stmt(inner),
25        Rule::break_stmt => Ok(Statement::Break(pair_span(&inner))),
26        Rule::continue_stmt => Ok(Statement::Continue(pair_span(&inner))),
27        Rule::variable_decl => {
28            let decl = parse_variable_decl(inner)?;
29            Ok(Statement::VariableDecl(decl, span))
30        }
31        Rule::assignment => {
32            let inner_loc = pair_location(&inner);
33            let inner_span = pair_span(&inner);
34            let mut inner = inner.into_inner();
35            let pattern_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
36                message: "expected pattern in assignment".to_string(),
37                location: Some(inner_loc.clone()),
38            })?;
39            let pattern = crate::parser::parse_pattern(pattern_pair)?;
40            let value_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
41                message: "expected value in assignment".to_string(),
42                location: Some(inner_loc),
43            })?;
44            let value = expressions::parse_expression(value_pair)?;
45            Ok(Statement::Assignment(
46                Assignment { pattern, value },
47                inner_span,
48            ))
49        }
50        Rule::expression_stmt => {
51            let inner_loc = pair_location(&inner);
52            let inner_span = pair_span(&inner);
53            let expr_pair = inner
54                .into_inner()
55                .next()
56                .ok_or_else(|| ShapeError::ParseError {
57                    message: "expected expression in statement".to_string(),
58                    location: Some(inner_loc),
59                })?;
60            let expr = expressions::parse_expression(expr_pair)?;
61            Ok(Statement::Expression(expr, inner_span))
62        }
63        Rule::for_loop => parse_for_loop(inner),
64        Rule::while_loop => parse_while_loop(inner),
65        Rule::if_stmt => parse_if_stmt(inner),
66        Rule::extend_statement => {
67            let ext = parse_extend_statement(inner)?;
68            Ok(Statement::Extend(ext, span))
69        }
70        Rule::function_def => {
71            // Desugar nested `fn name(params) { body }` to
72            // `let name = fn(params) { body }` (a VariableDecl statement).
73            let func_def = crate::parser::functions::parse_function_def(inner)?;
74            let func_expr = crate::ast::Expr::FunctionExpr {
75                params: func_def.params,
76                return_type: func_def.return_type,
77                body: func_def.body,
78                span,
79            };
80            Ok(Statement::VariableDecl(
81                crate::ast::VariableDecl {
82                    kind: crate::ast::VarKind::Let,
83                    is_mut: false,
84                    pattern: crate::ast::DestructurePattern::Identifier(func_def.name, span),
85                    type_annotation: None,
86                    value: Some(func_expr),
87                    ownership: Default::default(),
88                },
89                span,
90            ))
91        }
92        Rule::remove_target_stmt => Ok(Statement::RemoveTarget(pair_span(&inner))),
93        Rule::set_param_value_stmt => {
94            let inner_span = pair_span(&inner);
95            let mut inner_parts = inner.into_inner();
96            let param_pair = inner_parts.next().ok_or_else(|| ShapeError::ParseError {
97                message: "expected parameter name in `set param` value directive".to_string(),
98                location: Some(pair_loc.clone()),
99            })?;
100            let expr_pair = inner_parts.next().ok_or_else(|| ShapeError::ParseError {
101                message: "expected expression in `set param` value directive".to_string(),
102                location: Some(pair_loc.clone()),
103            })?;
104            let expression = crate::parser::expressions::parse_expression(expr_pair)?;
105            Ok(Statement::SetParamValue {
106                param_name: param_pair.as_str().to_string(),
107                expression,
108                span: inner_span,
109            })
110        }
111        Rule::set_param_type_stmt => {
112            let inner_span = pair_span(&inner);
113            let mut inner_parts = inner.into_inner();
114            let param_pair = inner_parts.next().ok_or_else(|| ShapeError::ParseError {
115                message: "expected parameter name in `set param` directive".to_string(),
116                location: Some(pair_loc.clone()),
117            })?;
118            let type_pair = inner_parts.next().ok_or_else(|| ShapeError::ParseError {
119                message: "expected type annotation in `set param` directive".to_string(),
120                location: Some(pair_loc.clone()),
121            })?;
122            let type_annotation = crate::parser::types::parse_type_annotation(type_pair)?;
123            Ok(Statement::SetParamType {
124                param_name: param_pair.as_str().to_string(),
125                type_annotation,
126                span: inner_span,
127            })
128        }
129        Rule::set_return_stmt => {
130            let inner_span = pair_span(&inner);
131            let mut inner_parts = inner.into_inner();
132            let payload_pair = inner_parts.next().ok_or_else(|| ShapeError::ParseError {
133                message: "expected type annotation or expression in `set return` directive"
134                    .to_string(),
135                location: Some(pair_loc.clone()),
136            })?;
137            match payload_pair.as_rule() {
138                Rule::type_annotation => {
139                    let type_annotation =
140                        crate::parser::types::parse_type_annotation(payload_pair)?;
141                    Ok(Statement::SetReturnType {
142                        type_annotation,
143                        span: inner_span,
144                    })
145                }
146                Rule::set_return_expr_payload => {
147                    let expr_pair =
148                        payload_pair
149                            .into_inner()
150                            .next()
151                            .ok_or_else(|| ShapeError::ParseError {
152                                message:
153                                    "expected expression in parenthesized `set return` directive"
154                                        .to_string(),
155                                location: Some(pair_loc.clone()),
156                            })?;
157                    let expression = expressions::parse_expression(expr_pair)?;
158                    Ok(Statement::SetReturnExpr {
159                        expression,
160                        span: inner_span,
161                    })
162                }
163                _ => Err(ShapeError::ParseError {
164                    message: "expected type annotation or expression in `set return` directive"
165                        .to_string(),
166                    location: Some(pair_loc),
167                }),
168            }
169        }
170        Rule::replace_body_stmt => {
171            let inner_span = pair_span(&inner);
172            let mut parts = inner.into_inner();
173            let Some(payload) = parts.next() else {
174                return Ok(Statement::ReplaceBody {
175                    body: Vec::new(),
176                    span: inner_span,
177                });
178            };
179            match payload.as_rule() {
180                Rule::replace_body_expr_payload => {
181                    let expr_pair =
182                        payload
183                            .into_inner()
184                            .next()
185                            .ok_or_else(|| ShapeError::ParseError {
186                                message:
187                                    "expected expression in parenthesized `replace body` directive"
188                                        .to_string(),
189                                location: Some(pair_loc.clone()),
190                            })?;
191                    let expression = expressions::parse_expression(expr_pair)?;
192                    Ok(Statement::ReplaceBodyExpr {
193                        expression,
194                        span: inner_span,
195                    })
196                }
197                Rule::statement => {
198                    let mut body = Vec::new();
199                    body.push(parse_statement(payload)?);
200                    body.extend(parse_statements(parts)?);
201                    Ok(Statement::ReplaceBody {
202                        body,
203                        span: inner_span,
204                    })
205                }
206                _ => Err(ShapeError::ParseError {
207                    message: "expected body block or expression in `replace body` directive"
208                        .to_string(),
209                    location: Some(pair_loc),
210                }),
211            }
212        }
213        Rule::replace_module_stmt => {
214            let inner_span = pair_span(&inner);
215            let mut parts = inner.into_inner();
216            let payload = parts.next().ok_or_else(|| ShapeError::ParseError {
217                message: "expected expression payload in `replace module` directive".to_string(),
218                location: Some(pair_loc.clone()),
219            })?;
220            if payload.as_rule() != Rule::replace_module_expr_payload {
221                return Err(ShapeError::ParseError {
222                    message: "expected parenthesized expression in `replace module` directive"
223                        .to_string(),
224                    location: Some(pair_loc),
225                });
226            }
227            let expr_pair = payload
228                .into_inner()
229                .next()
230                .ok_or_else(|| ShapeError::ParseError {
231                    message: "expected expression in parenthesized `replace module` directive"
232                        .to_string(),
233                    location: Some(pair_loc),
234                })?;
235            let expression = expressions::parse_expression(expr_pair)?;
236            Ok(Statement::ReplaceModuleExpr {
237                expression,
238                span: inner_span,
239            })
240        }
241        _ => Err(ShapeError::ParseError {
242            message: format!("Unexpected statement type: {:?}", inner.as_rule()),
243            location: None,
244        }),
245    }
246}
247
248/// Parse a return statement
249fn parse_return_stmt(pair: Pair<Rule>) -> Result<Statement> {
250    let span = pair_span(&pair);
251    let mut inner = pair.into_inner();
252
253    // Skip the return_keyword atomic token
254    let first = inner.next();
255    if let Some(ref p) = first {
256        if p.as_rule() == Rule::return_keyword {
257            // Keyword consumed, check for expression
258            if let Some(expr_pair) = inner.next() {
259                let expr = expressions::parse_expression(expr_pair)?;
260                return Ok(Statement::Return(Some(expr), span));
261            } else {
262                return Ok(Statement::Return(None, span));
263            }
264        }
265    }
266
267    if let Some(expr_pair) = first {
268        // Return with expression
269        let expr = expressions::parse_expression(expr_pair)?;
270        Ok(Statement::Return(Some(expr), span))
271    } else {
272        // Return without expression
273        Ok(Statement::Return(None, span))
274    }
275}
276
277/// Parse a for loop
278pub fn parse_for_loop(pair: Pair<Rule>) -> Result<Statement> {
279    let pair_loc = pair_location(&pair);
280    let span = pair_span(&pair);
281    let mut inner = pair.into_inner();
282
283    // Parse for clause
284    let for_clause = inner.next().ok_or_else(|| ShapeError::ParseError {
285        message: "expected for clause".to_string(),
286        location: Some(pair_loc),
287    })?;
288    let init = parse_for_clause(for_clause)?;
289
290    // Parse body
291    let mut body = vec![];
292    for stmt_pair in inner {
293        if stmt_pair.as_rule() == Rule::statement {
294            body.push(parse_statement(stmt_pair)?);
295        }
296    }
297
298    Ok(Statement::For(
299        ForLoop {
300            init,
301            body,
302            is_async: false,
303        },
304        span,
305    ))
306}
307
308/// Parse a for clause (for x in expr or for init; cond; update)
309fn parse_for_clause(pair: Pair<Rule>) -> Result<ForInit> {
310    let pair_loc = pair_location(&pair);
311    let inner_str = pair.as_str();
312    let mut inner = pair.into_inner();
313
314    // Check if it's a for-in loop by looking for "in" keyword
315    if inner_str.contains(" in ") {
316        // for x in expr (or for {x, y} in expr with destructuring)
317        let pattern_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
318            message: "expected pattern in for-in loop".to_string(),
319            location: Some(pair_loc.clone()),
320        })?;
321        let pattern = super::items::parse_pattern(pattern_pair)?;
322        let iter_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
323            message: "expected iterable expression in for-in loop".to_string(),
324            location: Some(pair_loc),
325        })?;
326        let iter_expr = expressions::parse_expression(iter_pair)?;
327        Ok(ForInit::ForIn {
328            pattern,
329            iter: iter_expr,
330        })
331    } else {
332        // for (init; condition; update)
333        let init_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
334            message: "expected initialization in for loop".to_string(),
335            location: Some(pair_loc.clone()),
336        })?;
337        let init_decl = parse_variable_decl(init_pair)?;
338        let condition_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
339            message: "expected condition in for loop".to_string(),
340            location: Some(pair_loc.clone()),
341        })?;
342        let condition = expressions::parse_expression(condition_pair)?;
343        let update_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
344            message: "expected update expression in for loop".to_string(),
345            location: Some(pair_loc),
346        })?;
347        let update = expressions::parse_expression(update_pair)?;
348
349        Ok(ForInit::ForC {
350            init: Box::new(Statement::VariableDecl(init_decl, Span::DUMMY)),
351            condition,
352            update,
353        })
354    }
355}
356
357/// Parse a while loop
358pub fn parse_while_loop(pair: Pair<Rule>) -> Result<Statement> {
359    let pair_loc = pair_location(&pair);
360    let span = pair_span(&pair);
361    let mut inner = pair.into_inner();
362
363    // Parse condition
364    let condition_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
365        message: "expected condition in while loop".to_string(),
366        location: Some(pair_loc),
367    })?;
368    let condition = expressions::parse_expression(condition_pair)?;
369
370    // Parse body
371    let mut body = vec![];
372    for stmt_pair in inner {
373        if stmt_pair.as_rule() == Rule::statement {
374            body.push(parse_statement(stmt_pair)?);
375        }
376    }
377
378    Ok(Statement::While(WhileLoop { condition, body }, span))
379}
380
381/// Parse an if statement
382pub fn parse_if_stmt(pair: Pair<Rule>) -> Result<Statement> {
383    let pair_loc = pair_location(&pair);
384    let span = pair_span(&pair);
385    let mut inner = pair.into_inner();
386
387    // Parse condition
388    let condition_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
389        message: "expected condition in if statement".to_string(),
390        location: Some(pair_loc),
391    })?;
392    let condition = expressions::parse_expression(condition_pair)?;
393
394    // Parse then body
395    let mut then_body = vec![];
396    let mut else_body = None;
397
398    for part in inner {
399        match part.as_rule() {
400            Rule::statement => {
401                then_body.push(parse_statement(part)?);
402            }
403            Rule::else_clause => {
404                else_body = Some(parse_else_clause(part)?);
405            }
406            _ => {}
407        }
408    }
409
410    Ok(Statement::If(
411        IfStatement {
412            condition,
413            then_body,
414            else_body,
415        },
416        span,
417    ))
418}
419
420/// Parse an else clause (can be else {...} or else if (...) {...})
421fn parse_else_clause(pair: Pair<Rule>) -> Result<Vec<Statement>> {
422    let span = pair_span(&pair);
423    let mut inner = pair.into_inner();
424    let mut statements = vec![];
425
426    // Check if this is an else if
427    let first = inner.next();
428    if let Some(first_pair) = first {
429        match first_pair.as_rule() {
430            Rule::expression => {
431                // This is an else if - parse condition
432                let condition = expressions::parse_expression(first_pair)?;
433                let mut then_body = vec![];
434                let mut else_body = None;
435
436                // Parse the body and potential else clause
437                for part in inner {
438                    match part.as_rule() {
439                        Rule::statement => {
440                            then_body.push(parse_statement(part)?);
441                        }
442                        Rule::else_clause => {
443                            else_body = Some(parse_else_clause(part)?);
444                        }
445                        _ => {}
446                    }
447                }
448
449                // Create an if statement for the else if
450                statements.push(Statement::If(
451                    IfStatement {
452                        condition,
453                        then_body,
454                        else_body,
455                    },
456                    span,
457                ));
458            }
459            Rule::statement => {
460                // This is a regular else block - just parse statements
461                statements.push(parse_statement(first_pair)?);
462                for stmt_pair in inner {
463                    if stmt_pair.as_rule() == Rule::statement {
464                        statements.push(parse_statement(stmt_pair)?);
465                    }
466                }
467            }
468            _ => {}
469        }
470    }
471
472    Ok(statements)
473}
474
475/// Parse multiple statements (for function bodies)
476pub fn parse_statements(pairs: pest::iterators::Pairs<Rule>) -> Result<Vec<Statement>> {
477    let mut statements = vec![];
478
479    for pair in pairs {
480        if pair.as_rule() == Rule::statement {
481            statements.push(parse_statement(pair)?);
482        } else if pair.as_rule() == Rule::stmt_recovery {
483            let span = pair.as_span();
484            let text = pair.as_str().trim();
485            let preview = if text.len() > 40 {
486                format!("{}...", &text[..40])
487            } else {
488                text.to_string()
489            };
490            return Err(ShapeError::ParseError {
491                message: format!("Syntax error near: {}", preview),
492                location: Some(pair_location(&pair).with_length(span.end() - span.start())),
493            });
494        }
495    }
496
497    Ok(statements)
498}