Compare commits

..

1 Commits

Author SHA1 Message Date
cde0ff5582 feat: Add support for compound statements.
This commit adds parsing logic for compound statements.
2026-03-12 21:42:40 +01:00
4 changed files with 56 additions and 3 deletions

View File

@@ -234,6 +234,15 @@ pub enum StatementKind<P: Phase> {
value: Option<Expression<P>>, value: Option<Expression<P>>,
}, },
/// 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<Statement<P>>,
},
/// A bare expression statement, e.g. `f(x);`. /// A bare expression statement, e.g. `f(x);`.
/// ///
/// The trailing `;` is not stored in the node but is included in /// The trailing `;` is not stored in the node but is included in

View File

@@ -34,5 +34,10 @@ fn main() {
Ok(ast) => println!("{ast:#?}"), Ok(ast) => println!("{ast:#?}"),
Err(diag) => diag.report(file, &content), Err(diag) => diag.report(file, &content),
} }
parser
.errors
.into_iter()
.for_each(|diag| diag.report(file, &content));
} }
} }

View File

@@ -24,7 +24,7 @@ pub struct Parser<'src> {
/// Diagnostics accumulated during parsing. Non-fatal errors are pushed here /// Diagnostics accumulated during parsing. Non-fatal errors are pushed here
/// so that the parser can attempt to continue and surface multiple issues /// so that the parser can attempt to continue and surface multiple issues
/// in a single pass. /// in a single pass.
errors: Vec<Diagnostic>, pub errors: Vec<Diagnostic>,
} }
impl<'src> Parser<'src> { impl<'src> Parser<'src> {
@@ -110,12 +110,14 @@ impl<'src> Parser<'src> {
/// Dispatches to the appropriate specialised parser based on the leading /// Dispatches to the appropriate specialised parser based on the leading
/// token: /// token:
/// - `let` → [`parse_let_statement`](Self::parse_let_statement) /// - `let` → [`parse_let_statement`](Self::parse_let_statement)
/// - `{` → [`parse_compound_statement`](Self::parse_compound_statement)
/// - anything else → an expression followed by a mandatory `;` /// - anything else → an expression followed by a mandatory `;`
pub fn parse_statement(&mut self) -> Result<ast::ParsedStatement, Diagnostic> { pub fn parse_statement(&mut self) -> Result<ast::ParsedStatement, Diagnostic> {
let peek = self.peek_no_eof()?; let peek = self.peek_no_eof()?;
match peek.kind { match peek.kind {
TokenKind::KwLet => self.parse_let_statement(), TokenKind::KwLet => self.parse_let_statement(),
TokenKind::LCurly => self.parse_compound_statement(),
_ => { _ => {
let expr = self.parse_expression(0)?; let expr = self.parse_expression(0)?;
@@ -171,6 +173,36 @@ impl<'src> Parser<'src> {
}) })
} }
/// Parses a braced block of statements: `{ <stmt>* }`.
///
/// 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<ast::ParsedStatement, Diagnostic> {
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 /// Parses a type annotation, e.g. `u8`, `i64`, `bool`, or a user-defined
/// named type. /// named type.
/// ///
@@ -193,7 +225,11 @@ impl<'src> Parser<'src> {
TokenKind::Identifier => ast::TypeKind::Named(peek.text.to_string()), 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; let span = self.advance().span;

View File

@@ -1 +1,4 @@
let test: i32 = foo.bar - 5 as i32; {
let a : 0;
f();
}