feat: Add support for compound statements.
This commit adds parsing logic for compound statements.
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user