statements in rust

This commit is contained in:
Sebastian Hugentobler 2025-02-12 10:30:51 +01:00
parent 15b331f447
commit a629ddca05
6 changed files with 434 additions and 194 deletions

View file

@ -3,6 +3,7 @@ use tracing::error;
use crate::{
expression::Expression,
statement::Statement,
token::{
self, Token,
TokenType::{self, *},
@ -13,14 +14,24 @@ use crate::{
pub enum ParserError {
#[error("empty token stream")]
NoTokens,
#[error("line {0}: expected expression")]
#[error("[line {0}] expected expression")]
ExpressionExpected(usize),
#[error("line {0}: expected ')' after expression.")]
#[error("[line {0}] expected ')' after expression.")]
ParenAfterExpression(usize),
#[error("Out of bounds access at index {0}.")]
#[error("[Out of bounds access at index {0}.")]
OutOfBoundsAccess(usize),
#[error("line {0}: literal expected.")]
#[error("[line {0}] literal expected.")]
LiteralExpected(usize),
#[error("[line {0}] expected ';' after value.")]
SemicolonAfterValueExpected(usize),
#[error("[line {0}] expected ';' after expression.")]
SemicolonAfterExpressionExpected(usize),
#[error("[line {0}] expected variable name.")]
VariableNameExpected(usize),
#[error("[line {0}] invalid assignment target.")]
InvalidAssignmentTarget(usize),
#[error("[line {0}] expected '}}' after block.")]
RightBraceAfterBlockExpected(usize),
}
/// Parse the Lox language.
@ -43,6 +54,23 @@ impl Parser {
})
}
/// Parse all tokens to a list of statements for execution.
fn run(&mut self) -> Result<Vec<Statement>, ParserError> {
let mut statements = Vec::new();
while !self.is_at_end() {
match self.declaration() {
Ok(x) => statements.push(x),
Err(e) => {
error!("{e}");
self.synchronize();
}
}
}
Ok(statements)
}
/// Check if any of the provided types match the type of the current token.
///
/// If so, advance the current token.
@ -117,7 +145,99 @@ impl Parser {
/// expression -> equality ;
fn expression(&mut self) -> Result<Expression, ParserError> {
self.equality()
self.assignment()
}
fn declaration(&mut self) -> Result<Statement, ParserError> {
if self.matches(&[Var]) {
self.var_declaration()
} else {
self.statement()
}
}
fn statement(&mut self) -> Result<Statement, ParserError> {
if self.matches(&[Print]) {
self.print_statement()
} else if self.matches(&[LeftBrace]) {
Ok(Statement::Block(self.block()?))
} else {
self.expression_statement()
}
}
fn print_statement(&mut self) -> Result<Statement, ParserError> {
let value = self.expression()?;
let line = self.current_token.line;
self.consume(&Semicolon)
.ok_or(ParserError::SemicolonAfterValueExpected(line))?;
Ok(Statement::Print(value))
}
fn var_declaration(&mut self) -> Result<Statement, ParserError> {
let line = self.current_token.line;
let name = self
.consume(&Identifier)
.ok_or(ParserError::VariableNameExpected(line))?
.clone();
let initializer = if self.matches(&[Equal]) {
Some(self.expression()?)
} else {
None
};
self.consume(&Semicolon)
.ok_or(ParserError::SemicolonAfterExpressionExpected(line))?;
Ok(Statement::Var {
name,
initializer: Box::new(initializer),
})
}
fn expression_statement(&mut self) -> Result<Statement, ParserError> {
let expr = self.expression()?;
let line = self.current_token.line;
self.consume(&Semicolon)
.ok_or(ParserError::SemicolonAfterExpressionExpected(line))?;
Ok(Statement::Expression(expr))
}
fn block(&mut self) -> Result<Vec<Statement>, ParserError> {
let mut statements = Vec::new();
while !self.check(&RightBrace) && !self.is_at_end() {
statements.push(self.declaration()?);
}
let line = self.previous()?.line;
self.consume(&RightBrace)
.ok_or(ParserError::RightBraceAfterBlockExpected(line))?;
Ok(statements)
}
fn assignment(&mut self) -> Result<Expression, ParserError> {
let expr = self.equality()?;
if self.matches(&[Equal]) {
let equals = self.previous()?.clone();
let value = self.assignment()?;
if let Expression::Variable { name } = expr {
Ok(Expression::Assign {
name,
value: Box::new(value),
})
} else {
Err(ParserError::InvalidAssignmentTarget(equals.line))
}
} else {
Ok(expr)
}
}
/// equality -> comparison ( ( "!=" | "==" ) comparison )* ;
@ -169,6 +289,9 @@ impl Parser {
Ok(Expression::Literal {
value: token::Literal::Nil,
})
} else if self.matches(&[Identifier]) {
let prev = self.previous()?.clone();
Ok(Expression::Variable { name: prev })
} else if self.matches(&[Number, String]) {
let prev = self.previous()?;
let value = prev
@ -205,106 +328,7 @@ impl Parser {
}
/// Try to parse the provided tokens into an AST.
pub fn generate_ast(tokens: Vec<Token>) -> Result<Expression, ParserError> {
pub fn ast(tokens: Vec<Token>) -> Result<Vec<Statement>, ParserError> {
let mut parser = Parser::new(tokens)?;
parser.expression()
}
#[cfg(test)]
mod tests {
use crate::{
expression::Expression,
token::{Literal, Token, TokenType},
};
use super::generate_ast;
#[test]
fn simple_expression() {
let ast = generate_ast(vec![
Token {
token_type: TokenType::Number,
lexeme: "3".into(),
literal: Some(Literal::Number(3.0)),
line: 1,
},
Token {
token_type: TokenType::Star,
lexeme: "*".into(),
literal: None,
line: 1,
},
Token {
token_type: TokenType::Number,
lexeme: "4".into(),
literal: Some(Literal::Number(4.0)),
line: 1,
},
Token {
token_type: TokenType::Plus,
lexeme: "+".into(),
literal: None,
line: 1,
},
Token {
token_type: TokenType::Number,
lexeme: "2".into(),
literal: Some(Literal::Number(2.0)),
line: 1,
},
Token {
token_type: TokenType::Star,
lexeme: "*".into(),
literal: None,
line: 1,
},
Token {
token_type: TokenType::Number,
lexeme: "6".into(),
literal: Some(Literal::Number(6.0)),
line: 1,
},
])
.unwrap();
assert_eq!(
ast,
Expression::Binary {
left: Box::new(Expression::Binary {
left: Box::new(Expression::Literal {
value: Literal::Number(3.0)
}),
operator: Token {
token_type: TokenType::Star,
lexeme: "*".into(),
literal: None,
line: 1
},
right: Box::new(Expression::Literal {
value: Literal::Number(4.0)
})
}),
operator: Token {
token_type: TokenType::Plus,
lexeme: "+".into(),
literal: None,
line: 1
},
right: Box::new(Expression::Binary {
left: Box::new(Expression::Literal {
value: Literal::Number(2.0)
}),
operator: Token {
token_type: TokenType::Star,
lexeme: "*".into(),
literal: None,
line: 1
},
right: Box::new(Expression::Literal {
value: Literal::Number(6.0)
})
})
}
)
}
parser.run()
}