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            let keyword_end_line = p.as_span().end_pos().line_col().0;
258            // Keyword consumed, check for expression
259            if let Some(expr_pair) = inner.next() {
260                // Only treat as `return <expr>` if the expression starts on the
261                // same line as `return`. Otherwise it's a bare `return` followed
262                // by dead code on the next line (the grammar greedily consumes it).
263                let expr_start_line = expr_pair.as_span().start_pos().line_col().0;
264                if expr_start_line > keyword_end_line {
265                    return Ok(Statement::Return(None, span));
266                }
267                let expr = expressions::parse_expression(expr_pair)?;
268                return Ok(Statement::Return(Some(expr), span));
269            } else {
270                return Ok(Statement::Return(None, span));
271            }
272        }
273    }
274
275    if let Some(expr_pair) = first {
276        // Return with expression
277        let expr = expressions::parse_expression(expr_pair)?;
278        Ok(Statement::Return(Some(expr), span))
279    } else {
280        // Return without expression
281        Ok(Statement::Return(None, span))
282    }
283}
284
285/// Parse a for loop
286pub fn parse_for_loop(pair: Pair<Rule>) -> Result<Statement> {
287    let pair_loc = pair_location(&pair);
288    let span = pair_span(&pair);
289    let mut inner = pair.into_inner();
290
291    // Parse for clause
292    let for_clause = inner.next().ok_or_else(|| ShapeError::ParseError {
293        message: "expected for clause".to_string(),
294        location: Some(pair_loc),
295    })?;
296    let init = parse_for_clause(for_clause)?;
297
298    // Parse body
299    let mut body = vec![];
300    for stmt_pair in inner {
301        if stmt_pair.as_rule() == Rule::statement {
302            body.push(parse_statement(stmt_pair)?);
303        }
304    }
305
306    Ok(Statement::For(
307        ForLoop {
308            init,
309            body,
310            is_async: false,
311        },
312        span,
313    ))
314}
315
316/// Parse a for clause (for x in expr or for init; cond; update)
317fn parse_for_clause(pair: Pair<Rule>) -> Result<ForInit> {
318    let pair_loc = pair_location(&pair);
319    let inner_str = pair.as_str();
320    let mut inner = pair.into_inner();
321
322    // Check if it's a for-in loop by looking for "in" keyword
323    if inner_str.contains(" in ") {
324        // for x in expr (or for {x, y} in expr with destructuring)
325        let pattern_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
326            message: "expected pattern in for-in loop".to_string(),
327            location: Some(pair_loc.clone()),
328        })?;
329        let pattern = super::items::parse_pattern(pattern_pair)?;
330        let iter_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
331            message: "expected iterable expression in for-in loop".to_string(),
332            location: Some(pair_loc),
333        })?;
334        let iter_expr = expressions::parse_expression(iter_pair)?;
335        Ok(ForInit::ForIn {
336            pattern,
337            iter: iter_expr,
338        })
339    } else {
340        // for (init; condition; update)
341        let init_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
342            message: "expected initialization in for loop".to_string(),
343            location: Some(pair_loc.clone()),
344        })?;
345        let init_decl = parse_variable_decl(init_pair)?;
346        let condition_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
347            message: "expected condition in for loop".to_string(),
348            location: Some(pair_loc.clone()),
349        })?;
350        let condition = expressions::parse_expression(condition_pair)?;
351        let update_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
352            message: "expected update expression in for loop".to_string(),
353            location: Some(pair_loc),
354        })?;
355        let update = expressions::parse_expression(update_pair)?;
356
357        Ok(ForInit::ForC {
358            init: Box::new(Statement::VariableDecl(init_decl, Span::DUMMY)),
359            condition,
360            update,
361        })
362    }
363}
364
365/// Parse a while loop
366pub fn parse_while_loop(pair: Pair<Rule>) -> Result<Statement> {
367    let pair_loc = pair_location(&pair);
368    let span = pair_span(&pair);
369    let mut inner = pair.into_inner();
370
371    // Parse condition
372    let condition_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
373        message: "expected condition in while loop".to_string(),
374        location: Some(pair_loc),
375    })?;
376    let condition = expressions::parse_expression(condition_pair)?;
377
378    // Parse body
379    let mut body = vec![];
380    for stmt_pair in inner {
381        if stmt_pair.as_rule() == Rule::statement {
382            body.push(parse_statement(stmt_pair)?);
383        }
384    }
385
386    Ok(Statement::While(WhileLoop { condition, body }, span))
387}
388
389/// Parse an if statement
390pub fn parse_if_stmt(pair: Pair<Rule>) -> Result<Statement> {
391    let pair_loc = pair_location(&pair);
392    let span = pair_span(&pair);
393    let mut inner = pair.into_inner();
394
395    // Parse condition
396    let condition_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
397        message: "expected condition in if statement".to_string(),
398        location: Some(pair_loc),
399    })?;
400    let condition = expressions::parse_expression(condition_pair)?;
401
402    // Parse then body
403    let mut then_body = vec![];
404    let mut else_body = None;
405
406    for part in inner {
407        match part.as_rule() {
408            Rule::statement => {
409                then_body.push(parse_statement(part)?);
410            }
411            Rule::else_clause => {
412                else_body = Some(parse_else_clause(part)?);
413            }
414            _ => {}
415        }
416    }
417
418    Ok(Statement::If(
419        IfStatement {
420            condition,
421            then_body,
422            else_body,
423        },
424        span,
425    ))
426}
427
428/// Parse an else clause (can be else {...} or else if (...) {...})
429fn parse_else_clause(pair: Pair<Rule>) -> Result<Vec<Statement>> {
430    let span = pair_span(&pair);
431    let mut inner = pair.into_inner();
432    let mut statements = vec![];
433
434    // Check if this is an else if
435    let first = inner.next();
436    if let Some(first_pair) = first {
437        match first_pair.as_rule() {
438            Rule::expression => {
439                // This is an else if - parse condition
440                let condition = expressions::parse_expression(first_pair)?;
441                let mut then_body = vec![];
442                let mut else_body = None;
443
444                // Parse the body and potential else clause
445                for part in inner {
446                    match part.as_rule() {
447                        Rule::statement => {
448                            then_body.push(parse_statement(part)?);
449                        }
450                        Rule::else_clause => {
451                            else_body = Some(parse_else_clause(part)?);
452                        }
453                        _ => {}
454                    }
455                }
456
457                // Create an if statement for the else if
458                statements.push(Statement::If(
459                    IfStatement {
460                        condition,
461                        then_body,
462                        else_body,
463                    },
464                    span,
465                ));
466            }
467            Rule::statement => {
468                // This is a regular else block - just parse statements
469                statements.push(parse_statement(first_pair)?);
470                for stmt_pair in inner {
471                    if stmt_pair.as_rule() == Rule::statement {
472                        statements.push(parse_statement(stmt_pair)?);
473                    }
474                }
475            }
476            _ => {}
477        }
478    }
479
480    Ok(statements)
481}
482
483/// Parse multiple statements (for function bodies)
484pub fn parse_statements(pairs: pest::iterators::Pairs<Rule>) -> Result<Vec<Statement>> {
485    let mut statements = vec![];
486
487    for pair in pairs {
488        if pair.as_rule() == Rule::statement {
489            statements.push(parse_statement(pair)?);
490        } else if pair.as_rule() == Rule::stmt_recovery {
491            let span = pair.as_span();
492            let text = pair.as_str().trim();
493            let preview = if text.len() > 40 {
494                format!("{}...", &text[..40])
495            } else {
496                text.to_string()
497            };
498            return Err(ShapeError::ParseError {
499                message: format!("Syntax error near: {}", preview),
500                location: Some(pair_location(&pair).with_length(span.end() - span.start())),
501            });
502        }
503    }
504
505    Ok(statements)
506}