Skip to main content

rl_lang/parser/statements/
mod.rs

1//! Top-level statement dispatcher and block parser.
2//!
3//! [`parse_statement_to_ast`] is the main entry point called by the top-level
4//! parse loop in [`Parser::parse`]. It peeks at the current token and routes
5//! to the appropriate sub-parser. Anything that does not start with a known
6//! keyword is parsed as a bare expression statement.
7//!
8//! [`parse_statement_to_ast`]: Parser::parse_statement_to_ast
9//! [`Parser::parse`]: crate::parser::parser_logic::Parser::parse
10
11mod const_declaration;
12mod for_statement;
13mod function_declaration;
14mod if_statement;
15mod import_statement;
16mod match_statement;
17mod record_declaration;
18mod tag_declaration;
19mod variable_declaration;
20mod while_statement;
21
22use crate::{
23    ast::{
24        nodes::ExpressionKind,
25        statements::{FunctionAttribute, Statement, StatementKind},
26    },
27    lexer::tokentypes::TokenType,
28    parser::parser_logic::Parser,
29    utils::errors::Error,
30};
31
32impl Parser {
33    /// Dispatches the current token to the appropriate statement sub-parser.
34    ///
35    /// | Token | Action |
36    /// |---|---|
37    /// | `Newline` | skip - emits a no-op `Expression(0)` placeholder |
38    /// | `get` | [`parse_import`] |
39    /// | `dec` | [`parse_variable_declaration`] |
40    /// | `CONST` | [`parse_const_declaration`] |
41    /// | `while` | [`parse_while`] |
42    /// | `for` | [`parse_for`] |
43    /// | `if` | [`parse_if`] |
44    /// | `fn` | [`parse_function`] |
45    /// | `!#` | [`parse_entry_attribute`] |
46    /// | `return` | inline - parses optional return value |
47    /// | `break` | inline - emits [`StatementKind::Break`] |
48    /// | `continue` | inline - emits [`StatementKind::Continue`] |
49    /// | anything else | [`parse_expression`] wrapped in [`StatementKind::Expression`] |
50    ///
51    /// [`parse_import`]: Parser::parse_import
52    /// [`parse_variable_declaration`]: Parser::parse_variable_declartion
53    /// [`parse_const_declaration`]: Parser::parse_const_declartion
54    /// [`parse_while`]: Parser::parse_while
55    /// [`parse_for`]: Parser::parse_for
56    /// [`parse_if`]: Parser::parse_if
57    /// [`parse_function`]: Parser::parse_function
58    /// [`parse_entry_attribute`]: Parser::parse_entry_attribute
59    /// [`parse_expression`]: Parser::parse_expression
60    pub fn parse_statement_to_ast(&mut self) -> Result<Statement, Error> {
61        let start = self.peek_span();
62        match self.peek() {
63            TokenType::Newline => {
64                self.advance();
65                #[cfg(feature = "debug")]
66                log::info!("found newline while parsing... skipping");
67                let span = self.previous_span();
68                Ok(Statement::new(
69                    StatementKind::Expression(
70                        self.ast_arena.alloc_expr(ExpressionKind::Integer(0), span),
71                    ),
72                    span,
73                ))
74            }
75
76            TokenType::Get => {
77                self.advance();
78                #[cfg(feature = "debug")]
79                log::info!("found `get` for import while parsing");
80                self.parse_import(start)
81            }
82            TokenType::Dec => {
83                self.advance();
84                #[cfg(feature = "debug")]
85                log::info!("found `declaration` for variable while parsing");
86                self.parse_variable_declartion(start)
87            }
88            TokenType::Const => {
89                self.advance();
90                #[cfg(feature = "debug")]
91                log::info!("found `declaration` for constant while parsing");
92                self.parse_const_declartion(start)
93            }
94            TokenType::While => {
95                self.advance();
96                #[cfg(feature = "debug")]
97                log::info!("found `while` while parsing");
98                self.parse_while(start)
99            }
100            TokenType::For => {
101                self.advance();
102                #[cfg(feature = "debug")]
103                log::info!("found `for` while parsing");
104                self.parse_for(start)
105            }
106            TokenType::If => {
107                self.advance();
108                #[cfg(feature = "debug")]
109                log::info!("found `if` while parsing");
110                self.parse_if(start)
111            }
112
113            TokenType::Fn => {
114                self.advance();
115                #[cfg(feature = "debug")]
116                log::info!("found 'fn' while parsing");
117                self.parse_function(start, None)
118            }
119
120            TokenType::BangHash => {
121                self.advance();
122                self.parse_entry_attribute(start)
123            }
124            TokenType::Return => {
125                self.advance();
126                // parse the return value only when one is present on the same line
127                let expr = if !matches!(self.peek(), TokenType::Newline)
128                    && !matches!(self.peek(), TokenType::RightBrace)
129                    && !self.is_at_end()
130                {
131                    Some(self.parse_expression()?)
132                } else {
133                    None
134                };
135                let span = start.join(self.previous_span());
136                Ok(Statement::new(StatementKind::Return(expr), span))
137            }
138
139            TokenType::Break => {
140                self.advance();
141                let span = start.join(self.previous_span());
142                Ok(Statement::new(StatementKind::Break, span))
143            }
144
145            TokenType::Continue => {
146                self.advance();
147                let span = start.join(self.previous_span());
148                Ok(Statement::new(StatementKind::Continue, span))
149            }
150
151            TokenType::Match => {
152                self.advance();
153                self.parse_match(start)
154            }
155
156            TokenType::Record => {
157                self.advance();
158                #[cfg(feature = "debug")]
159                log::info!("found `record` while parsing");
160                self.parse_record_declaration(start)
161            }
162
163            TokenType::Tag => {
164                self.advance();
165                #[cfg(feature = "debug")]
166                log::info!("found `tag` while parsing");
167                self.parse_tag_declaration(start)
168            }
169
170            _ => {
171                #[cfg(feature = "debug")]
172                log::info!("parsing the current tokens as expression");
173                let expr = self.parse_expression()?;
174                let span = self.ast_arena.exprs.get(expr).span;
175                Ok(Statement::new(StatementKind::Expression(expr), span))
176            }
177        }
178    }
179
180    /// Parses a brace-delimited block `{ stmts* }` into a [`Vec<Statement>`].
181    ///
182    /// Consumes the opening `{`, then repeatedly calls [`parse_statement_to_ast`]
183    /// until `}` or [`TokenType::Eof`] is reached. Blank lines (newlines) inside
184    /// the block are skipped.
185    ///
186    /// # Errors
187    /// Returns an error if the opening `{` is missing.
188    ///
189    /// [`parse_statement_to_ast`]: Parser::parse_statement_to_ast
190    pub fn parse_block(&mut self) -> Result<Vec<Statement>, Error> {
191        if !self.match_type(&[TokenType::LeftBrace]) {
192            return Err(self.err("expected `{`", self.peek_span()));
193        }
194        let mut statements = Vec::new();
195
196        #[cfg(feature = "debug")]
197        log::info!("parsing body into statements");
198        while !self.match_type(&[TokenType::RightBrace, TokenType::Eof]) {
199            if matches!(self.peek(), TokenType::Newline) {
200                self.advance();
201                continue;
202            }
203            statements.push(self.parse_statement_to_ast()?);
204        }
205        Ok(statements)
206    }
207
208    /// Parses a `!#[entry]` attribute and the `fn` declaration that follows it.
209    ///
210    /// The `!#` token has already been consumed by [`parse_statement_to_ast`]
211    /// before this is called. Expects `[entry]` then a function declaration,
212    /// which is forwarded to [`parse_function`] with `is_entry = true`.
213    ///
214    /// # Errors
215    /// Returns an error if `[`, the `entry` identifier, `]`, or `fn` are missing
216    /// or in the wrong order.
217    ///
218    /// [`parse_function`]: Parser::parse_function
219    fn parse_entry_attribute(
220        &mut self,
221        start: crate::utils::span::Span,
222    ) -> Result<Statement, Error> {
223        if !self.match_type(&[TokenType::LeftBracket]) {
224            return Err(self.err("expected `[` after `!#`", self.peek_span()));
225        }
226
227        while self.match_type(&[TokenType::Newline]) {}
228
229        let attribute = match self.peek() {
230            TokenType::Identifier(name) if name == "entry" => {
231                self.advance();
232                FunctionAttribute::Entry
233            }
234            TokenType::Identifier(name) if name == "init" => {
235                self.advance();
236                FunctionAttribute::Init
237            }
238            TokenType::Identifier(name) if name == "final" => {
239                self.advance();
240                FunctionAttribute::Final
241            }
242            TokenType::Identifier(name) if name == "test" => {
243                self.advance();
244                FunctionAttribute::Test
245            }
246            _ => return Err(self.err("expected valid attribute", self.peek_span())),
247        };
248
249        while self.match_type(&[TokenType::Newline]) {}
250
251        if !self.match_type(&[TokenType::RightBracket]) {
252            return Err(self.err("expected `]` after entry attribute", self.peek_span()));
253        }
254
255        while self.match_type(&[TokenType::Newline]) {}
256
257        if !self.match_type(&[TokenType::Fn]) {
258            return Err(self.err(
259                "expected function declaration after `!#[<attribute>]`",
260                self.peek_span(),
261            ));
262        }
263        self.parse_function(start, Some(attribute))
264    }
265}