From cde0ff5582c27caf15d7581bc42ff9a1e70a0eb8 Mon Sep 17 00:00:00 2001 From: Jooris Hadeler Date: Thu, 12 Mar 2026 21:42:40 +0100 Subject: [PATCH] feat: Add support for compound statements. This commit adds parsing logic for compound statements. --- src/ast.rs | 9 +++++++++ src/main.rs | 5 +++++ src/parser.rs | 40 ++++++++++++++++++++++++++++++++++++++-- test.bky | 5 ++++- 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 7e73509..84f81d9 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -234,6 +234,15 @@ pub enum StatementKind { value: Option>, }, + /// A braced block of statements, e.g. `{ let x = 1; f(x); }`. + /// + /// Compound statements introduce a new scope and can appear anywhere a + /// statement is expected. + Compound { + /// The statements contained within the block, in source order. + inner: Vec>, + }, + /// A bare expression statement, e.g. `f(x);`. /// /// The trailing `;` is not stored in the node but is included in diff --git a/src/main.rs b/src/main.rs index 45e62f1..113626b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,5 +34,10 @@ fn main() { Ok(ast) => println!("{ast:#?}"), Err(diag) => diag.report(file, &content), } + + parser + .errors + .into_iter() + .for_each(|diag| diag.report(file, &content)); } } diff --git a/src/parser.rs b/src/parser.rs index 35c0d16..67bc36a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -24,7 +24,7 @@ pub struct Parser<'src> { /// Diagnostics accumulated during parsing. Non-fatal errors are pushed here /// so that the parser can attempt to continue and surface multiple issues /// in a single pass. - errors: Vec, + pub errors: Vec, } impl<'src> Parser<'src> { @@ -110,12 +110,14 @@ impl<'src> Parser<'src> { /// Dispatches to the appropriate specialised parser based on the leading /// token: /// - `let` → [`parse_let_statement`](Self::parse_let_statement) + /// - `{` → [`parse_compound_statement`](Self::parse_compound_statement) /// - anything else → an expression followed by a mandatory `;` pub fn parse_statement(&mut self) -> Result { let peek = self.peek_no_eof()?; match peek.kind { TokenKind::KwLet => self.parse_let_statement(), + TokenKind::LCurly => self.parse_compound_statement(), _ => { let expr = self.parse_expression(0)?; @@ -171,6 +173,36 @@ impl<'src> Parser<'src> { }) } + /// Parses a braced block of statements: `{ * }`. + /// + /// Each inner statement is parsed with [`parse_statement`](Self::parse_statement). + /// If a statement fails, the diagnostic is pushed onto [`errors`](Parser::errors) + /// and [`synchronize`](Self::synchronize) is called so that parsing can + /// continue with the next statement. The block span runs from `{` to `}`. + fn parse_compound_statement(&mut self) -> Result { + let lcurly_token = self.expect(TokenKind::LCurly)?; + let mut inner = Vec::new(); + + while !self.is_at_eof() && !self.is_peek(TokenKind::RCurly) { + match self.parse_statement() { + Ok(stmt) => inner.push(stmt), + Err(diag) => { + self.errors.push(diag); + self.synchronize(); + } + } + } + + let rcurly_token = self.expect(TokenKind::RCurly)?; + let span = lcurly_token.span.extend(rcurly_token.span); + + Ok(ast::ParsedStatement { + kind: ast::StatementKind::Compound { inner }, + span, + extra: (), + }) + } + /// Parses a type annotation, e.g. `u8`, `i64`, `bool`, or a user-defined /// named type. /// @@ -193,7 +225,11 @@ impl<'src> Parser<'src> { TokenKind::Identifier => ast::TypeKind::Named(peek.text.to_string()), - _ => return Err(Diagnostic::new(Severity::Error, "expected a type")), + _ => { + return Err( + Diagnostic::new(Severity::Error, "expected a type").with_span(peek.span) + ); + } }; let span = self.advance().span; diff --git a/test.bky b/test.bky index d75e696..ce0d482 100644 --- a/test.bky +++ b/test.bky @@ -1 +1,4 @@ -let test: i32 = foo.bar - 5 as i32; \ No newline at end of file +{ + let a : 0; + f(); +} \ No newline at end of file