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_type_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` directive".to_string(),
359                location: None,
360            })?;
361            let type_pair = parts.next().ok_or_else(|| ShapeError::ParseError {
362                message: "expected type annotation in `set param` directive".to_string(),
363                location: None,
364            })?;
365            let type_annotation = crate::parser::types::parse_type_annotation(type_pair)?;
366            Ok(BlockItem::Statement(crate::ast::Statement::SetParamType {
367                param_name: param_pair.as_str().to_string(),
368                type_annotation,
369                span,
370            }))
371        }
372        Rule::set_return_stmt => {
373            let span = pair_span(&inner);
374            let mut parts = inner.into_inner();
375            let payload_pair = parts.next().ok_or_else(|| ShapeError::ParseError {
376                message: "expected type annotation or expression in `set return` directive"
377                    .to_string(),
378                location: None,
379            })?;
380            match payload_pair.as_rule() {
381                Rule::type_annotation => {
382                    let type_annotation =
383                        crate::parser::types::parse_type_annotation(payload_pair)?;
384                    Ok(BlockItem::Statement(crate::ast::Statement::SetReturnType {
385                        type_annotation,
386                        span,
387                    }))
388                }
389                Rule::set_return_expr_payload => {
390                    let expr_pair =
391                        payload_pair
392                            .into_inner()
393                            .next()
394                            .ok_or_else(|| ShapeError::ParseError {
395                                message:
396                                    "expected expression in parenthesized `set return` directive"
397                                        .to_string(),
398                                location: None,
399                            })?;
400                    let expression = super::super::parse_expression(expr_pair)?;
401                    Ok(BlockItem::Statement(crate::ast::Statement::SetReturnExpr {
402                        expression,
403                        span,
404                    }))
405                }
406                _ => Err(ShapeError::ParseError {
407                    message: "expected type annotation or expression in `set return` directive"
408                        .to_string(),
409                    location: None,
410                }),
411            }
412        }
413        Rule::replace_body_stmt => {
414            let span = pair_span(&inner);
415            let mut parts = inner.into_inner();
416            let Some(payload) = parts.next() else {
417                return Ok(BlockItem::Statement(crate::ast::Statement::ReplaceBody {
418                    body: Vec::new(),
419                    span,
420                }));
421            };
422            match payload.as_rule() {
423                Rule::replace_body_expr_payload => {
424                    let expr_pair =
425                        payload
426                            .into_inner()
427                            .next()
428                            .ok_or_else(|| ShapeError::ParseError {
429                                message:
430                                    "expected expression in parenthesized `replace body` directive"
431                                        .to_string(),
432                                location: None,
433                            })?;
434                    let expression = super::super::parse_expression(expr_pair)?;
435                    Ok(BlockItem::Statement(
436                        crate::ast::Statement::ReplaceBodyExpr { expression, span },
437                    ))
438                }
439                Rule::statement => {
440                    let mut body = Vec::new();
441                    body.push(crate::parser::statements::parse_statement(payload)?);
442                    body.extend(crate::parser::statements::parse_statements(parts)?);
443                    Ok(BlockItem::Statement(crate::ast::Statement::ReplaceBody {
444                        body,
445                        span,
446                    }))
447                }
448                _ => Err(ShapeError::ParseError {
449                    message: "expected body block or expression in `replace body` directive"
450                        .to_string(),
451                    location: None,
452                }),
453            }
454        }
455        Rule::replace_module_stmt => {
456            let span = pair_span(&inner);
457            let mut parts = inner.into_inner();
458            let payload = parts.next().ok_or_else(|| ShapeError::ParseError {
459                message: "expected expression payload in `replace module` directive".to_string(),
460                location: None,
461            })?;
462            if payload.as_rule() != Rule::replace_module_expr_payload {
463                return Err(ShapeError::ParseError {
464                    message: "expected parenthesized expression in `replace module` directive"
465                        .to_string(),
466                    location: None,
467                });
468            }
469            let expr_pair = payload
470                .into_inner()
471                .next()
472                .ok_or_else(|| ShapeError::ParseError {
473                    message: "expected expression in parenthesized `replace module` directive"
474                        .to_string(),
475                    location: None,
476                })?;
477            let expression = super::super::parse_expression(expr_pair)?;
478            Ok(BlockItem::Statement(
479                crate::ast::Statement::ReplaceModuleExpr { expression, span },
480            ))
481        }
482        // Nested function definition: desugar `fn name(params) { body }` inside a block
483        // to `let name = fn(params) { body }` (a VariableDecl with a FunctionExpr value).
484        Rule::function_def => {
485            let span = pair_span(&inner);
486            let func_def = crate::parser::functions::parse_function_def(inner)?;
487            let func_expr = Expr::FunctionExpr {
488                params: func_def.params,
489                return_type: func_def.return_type,
490                body: func_def.body,
491                span,
492            };
493            Ok(BlockItem::VariableDecl(crate::ast::VariableDecl {
494                kind: crate::ast::VarKind::Let,
495                is_mut: false,
496                pattern: crate::ast::DestructurePattern::Identifier(func_def.name, span),
497                type_annotation: None,
498                value: Some(func_expr),
499                ownership: Default::default(),
500            }))
501        }
502        _ => Err(ShapeError::ParseError {
503            message: format!("Unexpected block entry: {:?}", inner.as_rule()),
504            location: None,
505        }),
506    }
507}
508
509/// Parse async let expression: `async let name = expr`
510/// Spawns a task and binds a future handle to a local variable.
511pub fn parse_async_let_expr(pair: Pair<Rule>) -> Result<Expr> {
512    let span = pair_span(&pair);
513    let pair_loc = pair_location(&pair);
514    let mut inner = pair.into_inner();
515
516    let name_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
517        message: "expected variable name in async let".to_string(),
518        location: Some(pair_loc.clone()),
519    })?;
520    let name = name_pair.as_str().to_string();
521
522    let expr_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
523        message: "expected expression in async let".to_string(),
524        location: Some(pair_loc),
525    })?;
526    let expr = super::super::parse_expression(expr_pair)?;
527
528    Ok(Expr::AsyncLet(
529        Box::new(AsyncLetExpr {
530            name,
531            expr: Box::new(expr),
532            span,
533        }),
534        span,
535    ))
536}
537
538/// Parse async scope expression: `async scope { ... }`
539/// Cancellation boundary -- on scope exit, all pending tasks are cancelled in reverse order.
540pub fn parse_async_scope_expr(pair: Pair<Rule>) -> Result<Expr> {
541    let span = pair_span(&pair);
542    let pair_loc = pair_location(&pair);
543    let mut inner = pair.into_inner();
544
545    let body_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
546        message: "expected body in async scope".to_string(),
547        location: Some(pair_loc),
548    })?;
549    let body = parse_block_expr(body_pair)?;
550
551    Ok(Expr::AsyncScope(Box::new(body), span))
552}