use super::{ParseError, ParseErrorKind, ParseResult, Parser};
use crate::ast::*;
use crate::lexer::{Delimiter, Keyword, TokenKind};
impl<'a> Parser<'a> {
pub fn parse_block(&mut self) -> ParseResult<Block> {
let start = self.expect(&TokenKind::OpenDelim(Delimiter::Brace))?.span;
let mut stmts = Vec::new();
while !self.check(&TokenKind::CloseDelim(Delimiter::Brace)) && !self.is_eof() {
match self.parse_stmt() {
Ok(stmt) => stmts.push(stmt),
Err(e) => {
self.errors.push(e);
self.recover_to_stmt();
}
}
}
let end = self.expect(&TokenKind::CloseDelim(Delimiter::Brace))?.span;
let span = start.merge(&end);
Ok(Block {
stmts,
span,
id: NodeId::DUMMY,
})
}
pub fn parse_stmt(&mut self) -> ParseResult<Stmt> {
let attrs = self.parse_outer_attrs()?;
let start = self.current_span();
if self.is_item_start() {
let item = self.parse_item()?;
let span = item.span;
return Ok(Stmt::new(StmtKind::Item(Box::new(item)), span));
}
match self.current_kind().clone() {
TokenKind::Keyword(Keyword::Let) => self.parse_let_stmt(attrs),
TokenKind::Semi => {
self.advance();
Ok(Stmt::new(StmtKind::Empty, start))
}
_ => self.parse_expr_stmt(attrs),
}
}
fn parse_let_stmt(&mut self, attrs: Vec<Attribute>) -> ParseResult<Stmt> {
let start = self.expect_keyword(Keyword::Let)?;
let pattern = self.parse_pattern()?;
let ty = if self.eat(&TokenKind::Colon) {
Some(Box::new(self.parse_type()?))
} else {
None
};
let init = if self.eat(&TokenKind::Eq) {
let value = self.parse_expr()?;
let diverge = if self.eat_keyword(Keyword::Else) {
let block = self.parse_block()?;
let block_span = block.span;
let expr = Expr {
kind: ExprKind::Block(Box::new(block)),
span: block_span,
id: NodeId::DUMMY,
attrs: Vec::new(),
};
Some(Box::new(expr))
} else {
None
};
Some(LocalInit {
expr: Box::new(value),
diverge,
})
} else {
None
};
let end = self.expect(&TokenKind::Semi)?.span;
let span = start.merge(&end);
let local = Local {
attrs,
pattern,
ty,
init,
span,
id: NodeId::DUMMY,
};
Ok(Stmt::new(StmtKind::Local(Box::new(local)), span))
}
fn parse_expr_stmt(&mut self, _attrs: Vec<Attribute>) -> ParseResult<Stmt> {
let expr = self.parse_expr()?;
let start = expr.span;
let needs_semi = !self.expr_is_complete(&expr);
if self.eat(&TokenKind::Semi) {
let span = start.merge(&self.tokens[self.pos - 1].span);
Ok(Stmt::new(StmtKind::Semi(Box::new(expr)), span))
} else if needs_semi {
if self.check(&TokenKind::CloseDelim(Delimiter::Brace)) {
Ok(Stmt::new(StmtKind::Expr(Box::new(expr)), start))
} else {
Err(ParseError::new(
ParseErrorKind::ExpectedSemicolon,
self.current_span(),
))
}
} else {
Ok(Stmt::new(StmtKind::Expr(Box::new(expr)), start))
}
}
fn expr_is_complete(&self, expr: &Expr) -> bool {
matches!(
expr.kind,
ExprKind::If { .. }
| ExprKind::Match { .. }
| ExprKind::Loop { .. }
| ExprKind::While { .. }
| ExprKind::WhileLet { .. }
| ExprKind::For { .. }
| ExprKind::Block(_)
| ExprKind::Unsafe(_)
| ExprKind::Async { .. }
)
}
fn is_item_start(&self) -> bool {
if self.check_keyword(Keyword::Pub) {
return true;
}
if self.check(&TokenKind::Pound) && !matches!(self.peek().kind, TokenKind::Not) {
return true;
}
match self.current_kind() {
TokenKind::Keyword(kw) => match kw {
Keyword::Unsafe | Keyword::Async => {
matches!(
self.peek().kind,
TokenKind::Keyword(Keyword::Fn)
| TokenKind::Keyword(Keyword::Impl)
| TokenKind::Keyword(Keyword::Trait)
| TokenKind::Keyword(Keyword::Mod)
| TokenKind::Keyword(Keyword::Extern)
)
}
_ => matches!(
kw,
Keyword::Fn
| Keyword::Struct
| Keyword::Enum
| Keyword::Trait
| Keyword::Impl
| Keyword::Type
| Keyword::Const
| Keyword::Static
| Keyword::Mod
| Keyword::Use
| Keyword::Extern
| Keyword::Effect
| Keyword::Macro
),
},
_ => false,
}
}
pub fn parse_stmts(&mut self) -> ParseResult<Vec<Stmt>> {
let mut stmts = Vec::new();
while !self.check(&TokenKind::CloseDelim(Delimiter::Brace)) && !self.is_eof() {
match self.parse_stmt() {
Ok(stmt) => stmts.push(stmt),
Err(e) => {
self.errors.push(e);
self.recover_to_stmt();
}
}
}
Ok(stmts)
}
pub fn parse_block_or_expr(&mut self) -> ParseResult<Expr> {
if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) {
let block = self.parse_block()?;
let span = block.span;
Ok(Expr::new(ExprKind::Block(Box::new(block)), span))
} else {
self.parse_expr()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lexer::{Lexer, SourceFile as LexerSourceFile};
fn parse_stmt_from_str(s: &str) -> ParseResult<Stmt> {
let source = LexerSourceFile::new("test.quanta", format!("fn test() {{ {} }}", s));
let mut lexer = Lexer::new(&source);
let tokens = lexer.tokenize().unwrap();
let mut parser = Parser::new(&source, tokens);
parser.advance(); parser.advance(); parser.advance(); parser.advance(); parser.advance(); parser.parse_stmt()
}
#[test]
fn test_let_stmt() {
let result = parse_stmt_from_str("let x = 42;");
assert!(result.is_ok());
let result = parse_stmt_from_str("let x: i32 = 42;");
assert!(result.is_ok());
let result = parse_stmt_from_str("let x: i32;");
assert!(result.is_ok());
}
#[test]
fn test_expr_stmt() {
let result = parse_stmt_from_str("x + 1;");
assert!(result.is_ok());
}
}