Skip to main content

nifty_syntax/parser/
stmt.rs

1//! Statements and blocks.
2
3use super::expr::TrailingBlocks;
4use super::{ParseResult, Parser, Reported};
5use crate::ast::{AssignOp, Block, ElseBranch, IfStmt, Iterable, Stmt, StmtKind};
6use crate::token::TokenKind;
7
8impl Parser<'_> {
9    pub(super) fn parse_block(&mut self) -> ParseResult<Block> {
10        let start = self.expect(TokenKind::LBrace)?.span.start;
11        let mut stmts = Vec::new();
12        loop {
13            self.skip_newlines();
14            match self.peek() {
15                TokenKind::RBrace => {
16                    self.bump();
17                    break;
18                }
19                TokenKind::Eof => return self.unexpected("`}`"),
20                _ => match self.parse_stmt() {
21                    Ok(stmt) => {
22                        stmts.push(stmt);
23                        self.finish_line();
24                    }
25                    Err(Reported) => self.recover_to_line_end(),
26                },
27            }
28        }
29        Ok(Block {
30            stmts,
31            span: self.span_from(start),
32        })
33    }
34
35    pub(super) fn parse_stmt(&mut self) -> ParseResult<Stmt> {
36        let start = self.current().span.start;
37        let kind = match self.peek() {
38            TokenKind::Let => self.parse_let()?,
39            TokenKind::If => StmtKind::If(self.parse_if()?),
40            TokenKind::For => self.parse_for()?,
41            TokenKind::Return => self.parse_return()?,
42            _ => self.parse_expr_or_assign()?,
43        };
44        Ok(Stmt {
45            kind,
46            span: self.span_from(start),
47        })
48    }
49
50    fn parse_let(&mut self) -> ParseResult<StmtKind> {
51        self.bump();
52        let name = self.expect_ident()?;
53        let ty = self.parse_type_annotation()?;
54        self.expect(TokenKind::Eq)?;
55        let value = self.parse_expr(TrailingBlocks::Allowed)?;
56        Ok(StmtKind::Let { name, ty, value })
57    }
58
59    fn parse_if(&mut self) -> ParseResult<IfStmt> {
60        self.bump();
61        let condition = self.parse_expr(TrailingBlocks::Forbidden)?;
62        let then_block = self.parse_block()?;
63        let else_branch = match self.eat(TokenKind::Else) {
64            None => None,
65            Some(_) if self.at(TokenKind::If) => Some(ElseBranch::If(Box::new(self.parse_if()?))),
66            Some(_) => Some(ElseBranch::Block(self.parse_block()?)),
67        };
68        Ok(IfStmt {
69            condition,
70            then_block,
71            else_branch,
72        })
73    }
74
75    fn parse_for(&mut self) -> ParseResult<StmtKind> {
76        self.bump();
77        let first = self.expect_ident()?;
78        let (mut index, item) = match self.eat(TokenKind::Comma) {
79            Some(_) => (Some(first), self.expect_ident()?),
80            None => (None, first),
81        };
82        self.expect(TokenKind::In)?;
83        let iterable = self.parse_iterable()?;
84        if let (Some(extra), Iterable::Range { .. }) = (&index, &iterable) {
85            self.report(
86                format!(
87                    "a range loop has no separate index; `{}` already counts, as in `for {} in 0..n`",
88                    item.name, item.name
89                ),
90                extra.span,
91            );
92            index = None;
93        }
94        let body = self.parse_block()?;
95        Ok(StmtKind::For {
96            index,
97            item,
98            iterable,
99            body,
100        })
101    }
102
103    fn parse_iterable(&mut self) -> ParseResult<Iterable> {
104        let start = self.parse_expr(TrailingBlocks::Forbidden)?;
105        if self.eat(TokenKind::DotDot).is_none() {
106            return Ok(Iterable::List(start));
107        }
108        let end = self.parse_expr(TrailingBlocks::Forbidden)?;
109        Ok(Iterable::Range { start, end })
110    }
111
112    fn parse_return(&mut self) -> ParseResult<StmtKind> {
113        self.bump();
114        let value = match self.peek() {
115            TokenKind::Newline | TokenKind::RBrace | TokenKind::Eof => None,
116            _ => Some(self.parse_expr(TrailingBlocks::Allowed)?),
117        };
118        Ok(StmtKind::Return(value))
119    }
120
121    fn parse_expr_or_assign(&mut self) -> ParseResult<StmtKind> {
122        let target = self.parse_expr(TrailingBlocks::Allowed)?;
123        let Some(op) = assign_op(self.peek()) else {
124            return Ok(StmtKind::Expr(target));
125        };
126        self.bump();
127        self.skip_newlines();
128        let value = self.parse_expr(TrailingBlocks::Allowed)?;
129        Ok(StmtKind::Assign { target, op, value })
130    }
131}
132
133fn assign_op(kind: TokenKind) -> Option<AssignOp> {
134    Some(match kind {
135        TokenKind::Eq => AssignOp::Set,
136        TokenKind::PlusEq => AssignOp::Add,
137        TokenKind::MinusEq => AssignOp::Sub,
138        TokenKind::StarEq => AssignOp::Mul,
139        TokenKind::SlashEq => AssignOp::Div,
140        _ => return None,
141    })
142}