Skip to main content

rl_lang/parser/statements/
for_statement.rs

1//! `for` loop parser - three distinct syntaxes.
2//!
3//! rl-lang supports three `for` forms:
4//!
5//! ```text
6//! // 1. C-style: initializer, condition, increment inside brackets
7//! for [int i = 0, i < 10, i += 1] { … }
8//!
9//! // 2. Range iteration: integer variable over a literal range
10//! for x in 0..10 { … }
11//!
12//! // 3. Foreach: variable over an array variable or expression
13//! for item in my_array { … }
14//! ```
15//!
16//! The leading token after `for` disambiguates the form:
17//! - `[` -> C-style ([`StatementKind::For`])
18//! - `identifier` -> range or foreach ([`StatementKind::ForRange`] / [`StatementKind::ForEach`])
19//! - anything else -> error
20
21use crate::{
22    ast::{
23        nodes::ExpressionKind,
24        statements::{Statement, StatementKind},
25    },
26    lexer::tokentypes::TokenType,
27    parser::parser_logic::Parser,
28    utils::errors::Error,
29};
30
31impl Parser {
32    /// Parses a `for` statement in one of its three forms.
33    ///
34    /// Called after `for` has been consumed. Dispatches on the next token:
35    ///
36    /// - **`[`** - C-style loop. Reads `[T i = init, condition, increment]`
37    ///   then a body block. Produces [`StatementKind::For`].
38    ///
39    /// - **identifier** - inspects the token after the identifier name:
40    ///   - followed by `in N..M` or `in [items]` -> [`StatementKind::ForRange`]
41    ///     with a pre-evaluated [`StatementKind::Range`] of `i64` values.
42    ///   - followed by `in <identifier>` -> [`StatementKind::ForEach`], iterating
43    ///     over an array expression at runtime.
44    ///
45    /// # Errors
46    /// Returns an error for invalid range elements (non-integer), missing
47    /// commas in C-style headers, or unrecognised `for` syntax.
48    pub fn parse_for(&mut self, start: crate::utils::span::Span) -> Result<Statement, Error> {
49        if matches!(self.peek(), TokenType::LeftBracket) {
50            // C-style: for [T i = init, cond, incr] { … }
51            self.advance();
52            while self.match_type(&[TokenType::Newline]) {}
53            let init_start = self.peek_span();
54            let initializer = Box::new(self.parse_variable_declartion(init_start)?);
55            while self.match_type(&[TokenType::Newline]) {}
56            self.match_type(&[TokenType::Comma]);
57            while self.match_type(&[TokenType::Newline]) {}
58            let condition = self.parse_expression()?;
59            while self.match_type(&[TokenType::Newline]) {}
60            self.match_type(&[TokenType::Comma]);
61            while self.match_type(&[TokenType::Newline]) {}
62            let increment = self.parse_expression()?;
63            while self.match_type(&[TokenType::Newline]) {}
64            self.match_type(&[TokenType::RightBracket]);
65            while self.match_type(&[TokenType::Newline]) {}
66            let body = self.parse_block()?;
67            let span = start.join(self.previous_span());
68            Ok(Statement::new(
69                StatementKind::For {
70                    initializer,
71                    condition,
72                    increment,
73                    body,
74                },
75                span,
76            ))
77        } else if matches!(self.peek(), TokenType::Identifier(_)) {
78            // range or foreach: for <ident> in …
79            while self.match_type(&[TokenType::Newline]) {}
80            let ident_expr = self.parse_expression()?;
81            let ident_id = self.ast_arena.exprs.get(ident_expr).clone();
82            let variable_name = match &ident_id.kind {
83                ExpressionKind::Identifier(name) => name,
84                _ => return Err(self.err("for-range expects identifier", ident_id.span)),
85            };
86            while self.match_type(&[TokenType::Newline]) {}
87            self.match_type(&[TokenType::In]);
88
89            while self.match_type(&[TokenType::Newline]) {}
90            let range = if (matches!(self.peek(), TokenType::NumberLiteral(_))
91                || (matches!(self.peek(), TokenType::ByteLiteral(_))))
92            {
93                // literal range: N..M  (integers or bytes, evaluated at parse time)
94                let start_expr = self.parse_expression()?;
95                while self.match_type(&[TokenType::Newline]) {}
96                let start_id = self.ast_arena.exprs.get(start_expr);
97                let range_start = match start_id.kind {
98                    ExpressionKind::Integer(i) => i,
99                    ExpressionKind::Byte(b) => b as i64,
100                    _ => return Err(self.err("range should be integers only", start_id.span)),
101                };
102                self.match_type(&[TokenType::DotDot]);
103                let end_expr = self.parse_expression()?;
104                let end_id = self.ast_arena.exprs.get(end_expr);
105                let range_end = match end_id.kind {
106                    ExpressionKind::Integer(i) => i,
107                    ExpressionKind::Byte(b) => b as i64,
108                    _ => return Err(self.err("range should be integers only", end_id.span)),
109                };
110                let range_vec: Vec<i64> = (range_start..range_end).collect();
111                let span = start.join(self.previous_span());
112                Box::new(Statement::new(StatementKind::Range(range_vec), span))
113            } else if self.match_type(&[TokenType::LeftBracket]) {
114                // inline array literal: [1, 2, 3] (integers only, evaluated at parse time)
115                let mut items = Vec::new();
116                while self.match_type(&[TokenType::Newline]) {}
117                while self.peek() != TokenType::RightBracket {
118                    let value = self.parse_expression()?;
119                    items.push(value);
120                    while self.match_type(&[TokenType::Newline]) {}
121                    if self.peek() == TokenType::RightBracket {
122                        break;
123                    }
124                    while self.match_type(&[TokenType::Newline]) {}
125                    if !self.match_type(&[TokenType::Comma]) {
126                        return Err(self.err("expected ',' between list items", self.peek_span()));
127                    }
128                }
129                while self.match_type(&[TokenType::Newline]) {}
130                self.match_type(&[TokenType::RightBracket]);
131                let mut iterable_list = Vec::new();
132                for item in items {
133                    let item_id = self.ast_arena.exprs.get(item);
134                    match item_id.kind {
135                        ExpressionKind::Integer(i) => iterable_list.push(i),
136                        ExpressionKind::Byte(b) => iterable_list.push(b as i64),
137                        _ => return Err(self.err("list items must be integers", item_id.span)),
138                    }
139                }
140                let span = start.join(self.previous_span());
141                Box::new(Statement::new(StatementKind::Range(iterable_list), span))
142            } else {
143                if matches!(self.peek(), TokenType::Identifier(_)) {
144                    // foreach: for item in some_array_var_or_expr
145                    while self.match_type(&[TokenType::Newline]) {}
146                    let iterable_expression = self.parse_expression()?;
147                    while self.match_type(&[TokenType::Newline]) {}
148                    let body = self.parse_block()?;
149                    let span = start.join(self.previous_span());
150                    return Ok(Statement::new(
151                        StatementKind::ForEach {
152                            variable: variable_name.clone(),
153                            iterable: iterable_expression,
154                            body,
155                        },
156                        span,
157                    ));
158                }
159                return Err(self.err(
160                    "expected range (e.g. 1..10), array literal ([1, 2, 3], or array variable",
161                    self.peek_span(),
162                ));
163            };
164
165            while self.match_type(&[TokenType::Newline]) {}
166            let body = self.parse_block()?;
167            let span = start.join(self.previous_span());
168            Ok(Statement::new(
169                StatementKind::ForRange {
170                    variable: variable_name.clone(),
171                    range,
172                    body,
173                },
174                span,
175            ))
176        } else {
177            Err(self.err("wrong usage of for", self.peek_span()))
178        }
179    }
180}