Skip to main content

shape_ast/parser/expressions/control_flow/
loops.rs

1//! Loop and flow control expression parsing
2//!
3//! This module handles parsing of loop constructs and flow control:
4//! - While loops
5//! - For loops
6//! - Infinite loops
7//! - Let expressions
8//! - Break expressions
9//! - Return expressions
10//! - Block expressions
11
12use crate::ast::{
13    Assignment, AsyncLetExpr, BlockExpr, BlockItem, Expr, ForExpr, IfStatement, LetExpr, LoopExpr,
14    Span, Statement, WhileExpr,
15};
16use crate::error::{Result, ShapeError};
17use crate::parser::Rule;
18use pest::iterators::Pair;
19
20use super::super::super::pair_span;
21use super::pattern_matching::parse_pattern;
22use crate::parser::pair_location;
23
24/// Parse while expression
25pub fn parse_while_expr(pair: Pair<Rule>) -> Result<Expr> {
26    let span = pair_span(&pair);
27    let pair_loc = pair_location(&pair);
28    let mut inner = pair.into_inner();
29
30    let condition_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
31        message: "expected condition in while expression".to_string(),
32        location: Some(pair_loc.clone()),
33    })?;
34    let condition = super::super::parse_expression(condition_pair)?;
35    let body_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
36        message: "expected body in while expression".to_string(),
37        location: Some(pair_loc),
38    })?;
39    let body = parse_block_expr(body_pair)?;
40
41    Ok(Expr::While(
42        Box::new(WhileExpr {
43            condition: Box::new(condition),
44            body: Box::new(body),
45        }),
46        span,
47    ))
48}
49
50/// Parse for expression (including `for await x in stream { }`)
51pub fn parse_for_expr(pair: Pair<Rule>) -> Result<Expr> {
52    let span = pair_span(&pair);
53    let pair_loc = pair_location(&pair);
54    // Detect `for await` by checking the raw text
55    let is_async = pair.as_str().trim_start().starts_with("for") && pair.as_str().contains("await");
56    let mut inner = pair.into_inner();
57
58    let for_clause = inner.next().ok_or_else(|| ShapeError::ParseError {
59        message: "expected for clause".to_string(),
60        location: Some(pair_loc.clone()),
61    })?;
62    let clause_loc = pair_location(&for_clause);
63    let mut clause_inner = for_clause.into_inner();
64    let pattern_pair = clause_inner.next().ok_or_else(|| ShapeError::ParseError {
65        message: "expected pattern in for loop".to_string(),
66        location: Some(clause_loc.clone()),
67    })?;
68    let pattern = parse_pattern(pattern_pair)?;
69    let iterable_pair = clause_inner.next().ok_or_else(|| ShapeError::ParseError {
70        message: "expected iterable expression in for loop".to_string(),
71        location: Some(clause_loc),
72    })?;
73    let iterable = super::super::parse_expression(iterable_pair)?;
74
75    let body_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
76        message: "expected body in for expression".to_string(),
77        location: Some(pair_loc),
78    })?;
79    let body = parse_block_expr(body_pair)?;
80
81    Ok(Expr::For(
82        Box::new(ForExpr {
83            pattern,
84            iterable: Box::new(iterable),
85            body: Box::new(body),
86            is_async,
87        }),
88        span,
89    ))
90}
91
92/// Parse loop expression
93pub fn parse_loop_expr(pair: Pair<Rule>) -> Result<Expr> {
94    let span = pair_span(&pair);
95    let pair_loc = pair_location(&pair);
96    let mut inner = pair.into_inner();
97    let body_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
98        message: "expected body in loop expression".to_string(),
99        location: Some(pair_loc),
100    })?;
101    let body = parse_block_expr(body_pair)?;
102
103    Ok(Expr::Loop(
104        Box::new(LoopExpr {
105            body: Box::new(body),
106        }),
107        span,
108    ))
109}
110
111/// Parse let expression
112pub fn parse_let_expr(pair: Pair<Rule>) -> Result<Expr> {
113    let span = pair_span(&pair);
114    let pair_loc = pair_location(&pair);
115    let mut inner = pair.into_inner();
116
117    let pattern_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
118        message: "expected pattern in let expression".to_string(),
119        location: Some(pair_loc.clone()),
120    })?;
121    let pattern = parse_pattern(pattern_pair)?;
122
123    // Check if there's an initializer
124    let mut value = None;
125    let mut body_expr = None;
126
127    for next_pair in inner {
128        if next_pair.as_rule() == Rule::expression {
129            if value.is_none() {
130                value = Some(Box::new(super::super::parse_expression(next_pair)?));
131            } else {
132                body_expr = Some(super::super::parse_expression(next_pair)?);
133            }
134        }
135    }
136
137    let body = body_expr.ok_or_else(|| ShapeError::ParseError {
138        message: "let expression missing body".to_string(),
139        location: Some(pair_loc),
140    })?;
141
142    Ok(Expr::Let(
143        Box::new(LetExpr {
144            pattern,
145            type_annotation: None, // Add missing field
146            value,
147            body: Box::new(body),
148        }),
149        span,
150    ))
151}
152
153/// Parse break expression
154pub fn parse_break_expr(pair: Pair<Rule>) -> Result<Expr> {
155    let span = pair_span(&pair);
156    let mut inner = pair.into_inner();
157    let value = if let Some(expr) = inner.next() {
158        Some(Box::new(super::super::parse_expression(expr)?))
159    } else {
160        None
161    };
162    Ok(Expr::Break(value, span))
163}
164
165/// Parse return expression
166pub fn parse_return_expr(pair: Pair<Rule>) -> Result<Expr> {
167    let span = pair_span(&pair);
168    let mut inner = pair.into_inner();
169    let value = if let Some(expr) = inner.next() {
170        Some(Box::new(super::super::parse_expression(expr)?))
171    } else {
172        None
173    };
174    Ok(Expr::Return(value, span))
175}
176
177/// Parse block expression
178pub fn parse_block_expr(pair: Pair<Rule>) -> Result<Expr> {
179    let span = pair_span(&pair);
180    let mut items = Vec::new();
181
182    if let Some(block_items) = pair.into_inner().next() {
183        // Collect all inner pairs to analyze them
184        let inner_pairs: Vec<_> = block_items.into_inner().collect();
185
186        // Process each item, tracking whether it's a statement (with semicolon) or final expression
187        for item_pair in inner_pairs {
188            match item_pair.as_rule() {
189                Rule::block_statement => {
190                    // This is a statement with a semicolon - parse it
191                    let inner = item_pair.into_inner().next().unwrap();
192                    let block_item = parse_block_entry(inner)?;
193                    items.push(block_item);
194                }
195                Rule::block_item => {
196                    // This is the final expression without semicolon - the block's value
197                    let inner = item_pair.into_inner().next().unwrap();
198                    let block_item = parse_block_entry(inner)?;
199                    // Convert tail-position if-statement to a conditional expression
200                    // so the block evaluates to the if's value.
201                    let block_item = if_stmt_to_tail_expr(block_item);
202                    items.push(block_item);
203                }
204                _ => {} // Skip other tokens
205            }
206        }
207    }
208
209    // Empty blocks evaluate to Unit
210    if items.is_empty() {
211        return Ok(Expr::Unit(span));
212    }
213
214    // Promote the last item to a tail expression when it is a statement
215    // that can produce a value (e.g. an if-statement).  The PEG grammar
216    // `(block_statement ~ ";"?)* ~ block_item?` may consume a trailing
217    // if-statement as a `block_statement` (with the optional semicolon
218    // matching nothing), so the `block_item` -> `if_stmt_to_tail_expr`
219    // path is never reached.  Fix: apply the same promotion to the last
220    // collected item regardless of how the grammar matched it.
221    if let Some(last) = items.pop() {
222        items.push(if_stmt_to_tail_expr(last));
223    }
224
225    Ok(Expr::Block(BlockExpr { items }, span))
226}
227
228/// Convert a tail-position `if` statement into a conditional expression so the
229/// block evaluates to the value of the `if` rather than discarding it.
230///
231/// Only `BlockItem::Statement(Statement::If(..))` is converted; every other
232/// variant is returned unchanged.
233fn if_stmt_to_tail_expr(item: BlockItem) -> BlockItem {
234    match item {
235        BlockItem::Statement(Statement::If(if_stmt, span)) => {
236            BlockItem::Expression(if_stmt_to_conditional(if_stmt, span))
237        }
238        other => other,
239    }
240}
241
242/// Recursively convert an `IfStatement` (statement form) into an
243/// `Expr::Conditional` (expression form) by wrapping each branch's
244/// `Vec<Statement>` in a `Block` expression.
245fn if_stmt_to_conditional(if_stmt: IfStatement, span: Span) -> Expr {
246    let then_expr = stmts_to_block_expr(if_stmt.then_body, span);
247
248    let else_expr = if_stmt.else_body.map(|stmts| {
249        // An `else if` is represented as a single Statement::If inside the vec.
250        if stmts.len() == 1 {
251            if matches!(stmts.first(), Some(Statement::If(..))) {
252                let stmt = stmts.into_iter().next().unwrap();
253                if let Statement::If(nested_if, nested_span) = stmt {
254                    return Box::new(if_stmt_to_conditional(nested_if, nested_span));
255                }
256                unreachable!();
257            }
258        }
259        Box::new(stmts_to_block_expr(stmts, span))
260    });
261
262    Expr::Conditional {
263        condition: Box::new(if_stmt.condition),
264        then_expr: Box::new(then_expr),
265        else_expr,
266        span,
267    }
268}
269
270/// Wrap a `Vec<Statement>` as a block expression whose last entry
271/// is promoted to `BlockItem::Expression` when possible, so it
272/// produces a value on the stack.
273fn stmts_to_block_expr(stmts: Vec<Statement>, span: Span) -> Expr {
274    if stmts.is_empty() {
275        return Expr::Unit(span);
276    }
277    let len = stmts.len();
278    let mut items: Vec<BlockItem> = Vec::with_capacity(len);
279    for (i, s) in stmts.into_iter().enumerate() {
280        let is_last = i == len - 1;
281        if is_last {
282            match s {
283                // Promote trailing expression-statement to a block expression item
284                Statement::Expression(expr, _) => {
285                    items.push(BlockItem::Expression(expr));
286                }
287                // Promote trailing nested if to a conditional expression (recursively)
288                Statement::If(nested_if, nested_span) => {
289                    items.push(BlockItem::Expression(if_stmt_to_conditional(
290                        nested_if,
291                        nested_span,
292                    )));
293                }
294                other => {
295                    items.push(BlockItem::Statement(other));
296                }
297            }
298        } else {
299            items.push(BlockItem::Statement(s));
300        }
301    }
302    Expr::Block(BlockExpr { items }, span)
303}
304
305fn parse_block_entry(inner: Pair<Rule>) -> Result<BlockItem> {
306    match inner.as_rule() {
307        Rule::return_stmt => {
308            let return_span = pair_span(&inner);
309            let value = inner
310                .into_inner()
311                .filter(|p| p.as_rule() != Rule::return_keyword)
312                .next()
313                .map(|expr_pair| super::super::parse_expression(expr_pair))
314                .transpose()?
315                .map(Box::new);
316            Ok(BlockItem::Expression(Expr::Return(value, return_span)))
317        }
318        Rule::variable_decl => {
319            let decl = crate::parser::parse_variable_decl(inner)?;
320            Ok(BlockItem::VariableDecl(decl))
321        }
322        Rule::assignment => {
323            let mut inner = inner.into_inner();
324            let pattern = crate::parser::parse_pattern(inner.next().unwrap())?;
325            let value = super::super::parse_expression(inner.next().unwrap())?;
326            Ok(BlockItem::Assignment(Assignment { pattern, value }))
327        }
328        Rule::expression => {
329            let expr = super::super::parse_expression(inner)?;
330            Ok(BlockItem::Expression(expr))
331        }
332        Rule::if_stmt => {
333            let stmt = crate::parser::statements::parse_if_stmt(inner)?;
334            Ok(BlockItem::Statement(stmt))
335        }
336        Rule::for_loop => {
337            let stmt = crate::parser::statements::parse_for_loop(inner)?;
338            Ok(BlockItem::Statement(stmt))
339        }
340        Rule::while_loop => {
341            let stmt = crate::parser::statements::parse_while_loop(inner)?;
342            Ok(BlockItem::Statement(stmt))
343        }
344        Rule::extend_statement => {
345            let span = pair_span(&inner);
346            let ext = crate::parser::extensions::parse_extend_statement(inner)?;
347            Ok(BlockItem::Statement(crate::ast::Statement::Extend(
348                ext, span,
349            )))
350        }
351        Rule::remove_target_stmt => Ok(BlockItem::Statement(crate::ast::Statement::RemoveTarget(
352            pair_span(&inner),
353        ))),
354        Rule::set_param_value_stmt => {
355            let span = pair_span(&inner);
356            let mut parts = inner.into_inner();
357            let param_pair = parts.next().ok_or_else(|| ShapeError::ParseError {
358                message: "expected parameter name in `set param` value directive".to_string(),
359                location: None,
360            })?;
361            let expr_pair = parts.next().ok_or_else(|| ShapeError::ParseError {
362                message: "expected expression in `set param` value directive".to_string(),
363                location: None,
364            })?;
365            let expression = super::super::parse_expression(expr_pair)?;
366            Ok(BlockItem::Statement(crate::ast::Statement::SetParamValue {
367                param_name: param_pair.as_str().to_string(),
368                expression,
369                span,
370            }))
371        }
372        Rule::set_param_type_stmt => {
373            let span = pair_span(&inner);
374            let mut parts = inner.into_inner();
375            let param_pair = parts.next().ok_or_else(|| ShapeError::ParseError {
376                message: "expected parameter name in `set param` directive".to_string(),
377                location: None,
378            })?;
379            let type_pair = parts.next().ok_or_else(|| ShapeError::ParseError {
380                message: "expected type annotation in `set param` directive".to_string(),
381                location: None,
382            })?;
383            let type_annotation = crate::parser::types::parse_type_annotation(type_pair)?;
384            Ok(BlockItem::Statement(crate::ast::Statement::SetParamType {
385                param_name: param_pair.as_str().to_string(),
386                type_annotation,
387                span,
388            }))
389        }
390        Rule::set_return_stmt => {
391            let span = pair_span(&inner);
392            let mut parts = inner.into_inner();
393            let payload_pair = parts.next().ok_or_else(|| ShapeError::ParseError {
394                message: "expected type annotation or expression in `set return` directive"
395                    .to_string(),
396                location: None,
397            })?;
398            match payload_pair.as_rule() {
399                Rule::type_annotation => {
400                    let type_annotation =
401                        crate::parser::types::parse_type_annotation(payload_pair)?;
402                    Ok(BlockItem::Statement(crate::ast::Statement::SetReturnType {
403                        type_annotation,
404                        span,
405                    }))
406                }
407                Rule::set_return_expr_payload => {
408                    let expr_pair =
409                        payload_pair
410                            .into_inner()
411                            .next()
412                            .ok_or_else(|| ShapeError::ParseError {
413                                message:
414                                    "expected expression in parenthesized `set return` directive"
415                                        .to_string(),
416                                location: None,
417                            })?;
418                    let expression = super::super::parse_expression(expr_pair)?;
419                    Ok(BlockItem::Statement(crate::ast::Statement::SetReturnExpr {
420                        expression,
421                        span,
422                    }))
423                }
424                _ => Err(ShapeError::ParseError {
425                    message: "expected type annotation or expression in `set return` directive"
426                        .to_string(),
427                    location: None,
428                }),
429            }
430        }
431        Rule::replace_body_stmt => {
432            let span = pair_span(&inner);
433            let mut parts = inner.into_inner();
434            let Some(payload) = parts.next() else {
435                return Ok(BlockItem::Statement(crate::ast::Statement::ReplaceBody {
436                    body: Vec::new(),
437                    span,
438                }));
439            };
440            match payload.as_rule() {
441                Rule::replace_body_expr_payload => {
442                    let expr_pair =
443                        payload
444                            .into_inner()
445                            .next()
446                            .ok_or_else(|| ShapeError::ParseError {
447                                message:
448                                    "expected expression in parenthesized `replace body` directive"
449                                        .to_string(),
450                                location: None,
451                            })?;
452                    let expression = super::super::parse_expression(expr_pair)?;
453                    Ok(BlockItem::Statement(
454                        crate::ast::Statement::ReplaceBodyExpr { expression, span },
455                    ))
456                }
457                Rule::statement => {
458                    let mut body = Vec::new();
459                    body.push(crate::parser::statements::parse_statement(payload)?);
460                    body.extend(crate::parser::statements::parse_statements(parts)?);
461                    Ok(BlockItem::Statement(crate::ast::Statement::ReplaceBody {
462                        body,
463                        span,
464                    }))
465                }
466                _ => Err(ShapeError::ParseError {
467                    message: "expected body block or expression in `replace body` directive"
468                        .to_string(),
469                    location: None,
470                }),
471            }
472        }
473        Rule::replace_module_stmt => {
474            let span = pair_span(&inner);
475            let mut parts = inner.into_inner();
476            let payload = parts.next().ok_or_else(|| ShapeError::ParseError {
477                message: "expected expression payload in `replace module` directive".to_string(),
478                location: None,
479            })?;
480            if payload.as_rule() != Rule::replace_module_expr_payload {
481                return Err(ShapeError::ParseError {
482                    message: "expected parenthesized expression in `replace module` directive"
483                        .to_string(),
484                    location: None,
485                });
486            }
487            let expr_pair = payload
488                .into_inner()
489                .next()
490                .ok_or_else(|| ShapeError::ParseError {
491                    message: "expected expression in parenthesized `replace module` directive"
492                        .to_string(),
493                    location: None,
494                })?;
495            let expression = super::super::parse_expression(expr_pair)?;
496            Ok(BlockItem::Statement(
497                crate::ast::Statement::ReplaceModuleExpr { expression, span },
498            ))
499        }
500        // Nested function definition: desugar `fn name(params) { body }` inside a block
501        // to `let name = fn(params) { body }` (a VariableDecl with a FunctionExpr value).
502        Rule::function_def => {
503            let span = pair_span(&inner);
504            let func_def = crate::parser::functions::parse_function_def(inner)?;
505            let func_expr = Expr::FunctionExpr {
506                params: func_def.params,
507                return_type: func_def.return_type,
508                body: func_def.body,
509                span,
510            };
511            Ok(BlockItem::VariableDecl(crate::ast::VariableDecl {
512                kind: crate::ast::VarKind::Let,
513                is_mut: false,
514                pattern: crate::ast::DestructurePattern::Identifier(func_def.name, span),
515                type_annotation: None,
516                value: Some(func_expr),
517                ownership: Default::default(),
518            }))
519        }
520        _ => Err(ShapeError::ParseError {
521            message: format!("Unexpected block entry: {:?}", inner.as_rule()),
522            location: None,
523        }),
524    }
525}
526
527/// Parse async let expression: `async let name = expr`
528/// Spawns a task and binds a future handle to a local variable.
529pub fn parse_async_let_expr(pair: Pair<Rule>) -> Result<Expr> {
530    let span = pair_span(&pair);
531    let pair_loc = pair_location(&pair);
532    let mut inner = pair.into_inner();
533
534    let name_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
535        message: "expected variable name in async let".to_string(),
536        location: Some(pair_loc.clone()),
537    })?;
538    let name = name_pair.as_str().to_string();
539
540    let expr_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
541        message: "expected expression in async let".to_string(),
542        location: Some(pair_loc),
543    })?;
544    let expr = super::super::parse_expression(expr_pair)?;
545
546    Ok(Expr::AsyncLet(
547        Box::new(AsyncLetExpr {
548            name,
549            expr: Box::new(expr),
550            span,
551        }),
552        span,
553    ))
554}
555
556/// Parse async scope expression: `async scope { ... }`
557/// Cancellation boundary -- on scope exit, all pending tasks are cancelled in reverse order.
558pub fn parse_async_scope_expr(pair: Pair<Rule>) -> Result<Expr> {
559    let span = pair_span(&pair);
560    let pair_loc = pair_location(&pair);
561    let mut inner = pair.into_inner();
562
563    let body_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
564        message: "expected body in async scope".to_string(),
565        location: Some(pair_loc),
566    })?;
567    let body = parse_block_expr(body_pair)?;
568
569    Ok(Expr::AsyncScope(Box::new(body), span))
570}