2025-10-03 19:07:12 +02:00
|
|
|
use crate::{
|
2025-10-07 14:28:24 +02:00
|
|
|
common::{
|
2025-10-03 19:07:12 +02:00
|
|
|
ast::{AstNode, Expr, Stmt},
|
2025-10-07 14:28:24 +02:00
|
|
|
base_value::{BaseValue, LoxFunction},
|
2026-02-11 16:35:05 +01:00
|
|
|
lox_result::{parse_error, runtime_error, LoxError, LoxResult},
|
2025-10-07 14:28:24 +02:00
|
|
|
},
|
|
|
|
|
frontend::{
|
|
|
|
|
source_registry::SourceSlice,
|
|
|
|
|
tokens::{Token, TokenType},
|
2025-10-03 19:07:12 +02:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pub struct Parser {
|
|
|
|
|
tokens: Vec<Token>,
|
|
|
|
|
current: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Parser {
|
|
|
|
|
pub fn new(tokens: Vec<Token>) -> Self {
|
|
|
|
|
Self { tokens, current: 0 }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn parse(&mut self) -> LoxResult<Vec<AstNode<Stmt>>> {
|
|
|
|
|
let mut statements = Vec::new();
|
|
|
|
|
let mut errors = Vec::new();
|
|
|
|
|
while !self.is_at_end() {
|
2026-02-11 16:35:05 +01:00
|
|
|
match self.statement(None) {
|
2025-10-03 19:07:12 +02:00
|
|
|
Ok(stmt) => {
|
|
|
|
|
statements.push(stmt.clone());
|
|
|
|
|
}
|
|
|
|
|
Err(err) => {
|
|
|
|
|
errors.push(err);
|
|
|
|
|
self.synchronize();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !errors.is_empty() {
|
|
|
|
|
return Err(errors.into_iter().next().unwrap());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(statements)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-11 16:35:05 +01:00
|
|
|
fn statement(&mut self, label: Option<String>) -> LoxResult<AstNode<Stmt>> {
|
2025-10-03 19:07:12 +02:00
|
|
|
match (&self.peek().token_type, &self.peek_next().token_type) {
|
2025-10-04 19:02:33 +02:00
|
|
|
(TokenType::Print, _) => self.print_statement(),
|
2026-02-11 16:35:05 +01:00
|
|
|
(TokenType::Return, _) => self.return_statement(label),
|
|
|
|
|
(TokenType::Break, _) => self.return_statement(Some("loop".to_string())),
|
|
|
|
|
(TokenType::StartBlock, _) => self.block_statement(label),
|
2025-10-06 18:52:32 +02:00
|
|
|
(TokenType::Var, TokenType::Identifier) => {
|
|
|
|
|
self.advance();
|
|
|
|
|
self.var_statement()
|
|
|
|
|
}
|
|
|
|
|
(TokenType::Identifier, TokenType::Colon) => self.var_statement(),
|
2025-10-04 19:02:33 +02:00
|
|
|
(TokenType::If, _) => self.if_statement(),
|
|
|
|
|
(TokenType::While, _) => self.while_statement(),
|
2025-10-04 21:05:00 +02:00
|
|
|
(TokenType::For, _) => self.for_statement(),
|
2025-10-04 19:02:33 +02:00
|
|
|
_ => self.expression_statement(),
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-04 21:05:00 +02:00
|
|
|
fn for_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
|
|
|
|
let start_slice = self.peek().source_slice.clone();
|
2025-10-06 18:52:32 +02:00
|
|
|
self.advance(); // consume 'for'
|
|
|
|
|
|
|
|
|
|
let variable = self.var_statement()?;
|
|
|
|
|
let condition = self.expression()?;
|
|
|
|
|
self.consume(TokenType::Semicolon, "Expected ';' after for condition")?;
|
2026-06-30 14:05:46 +02:00
|
|
|
let increment = self.expression_statement()?;
|
2026-02-11 16:35:05 +01:00
|
|
|
let body = self.statement(Some("loop".to_string()))?;
|
2025-10-06 18:52:32 +02:00
|
|
|
|
|
|
|
|
let end_slice = self.previous().source_slice.clone();
|
2025-10-04 21:05:00 +02:00
|
|
|
let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
start_slice.source_id,
|
|
|
|
|
start_slice.start_position,
|
|
|
|
|
end_slice.end_position,
|
|
|
|
|
);
|
2025-10-06 18:52:32 +02:00
|
|
|
|
2025-10-04 21:05:00 +02:00
|
|
|
Ok(AstNode::new(
|
|
|
|
|
Stmt::For {
|
|
|
|
|
variable: Box::new(variable),
|
2025-10-06 18:52:32 +02:00
|
|
|
condition: Box::new(condition),
|
|
|
|
|
increment: Box::new(increment),
|
2025-10-04 21:05:00 +02:00
|
|
|
body: Box::new(body),
|
2025-10-10 10:18:02 +02:00
|
|
|
return_value: Box::new(BaseValue::Nil),
|
2025-10-04 21:05:00 +02:00
|
|
|
},
|
|
|
|
|
combined_slice,
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-06 18:52:32 +02:00
|
|
|
// fn for_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
|
|
|
|
// let start_slice = self.peek().source_slice.clone();
|
|
|
|
|
// self.advance();
|
|
|
|
|
// let variable = if let Token {
|
|
|
|
|
// token_type: TokenType::Identifier,
|
|
|
|
|
// literal: Some(variable_name),
|
|
|
|
|
// source_slice,
|
|
|
|
|
// ..
|
|
|
|
|
// } = self.peek()
|
|
|
|
|
// {
|
|
|
|
|
// AstNode {
|
|
|
|
|
// node: Expr::Literal {
|
|
|
|
|
// value: variable_name.clone(),
|
|
|
|
|
// },
|
|
|
|
|
// source_slice: source_slice.clone(),
|
|
|
|
|
// }
|
|
|
|
|
// } else {
|
|
|
|
|
// return parse_error(
|
|
|
|
|
// self.peek().source_slice.clone(),
|
|
|
|
|
// "Expect variable name after 'for' keyword.",
|
|
|
|
|
// );
|
|
|
|
|
// };
|
|
|
|
|
// self.advance();
|
|
|
|
|
// self.consume(TokenType::In, "Expect 'in' after for variable.")?;
|
|
|
|
|
// let iterable = self.expression()?;
|
2026-02-11 16:35:05 +01:00
|
|
|
// let body = self.statement(None)?;
|
2025-10-06 18:52:32 +02:00
|
|
|
// let end_slice = body.source_slice.clone();
|
|
|
|
|
// let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
// start_slice.source_id,
|
|
|
|
|
// start_slice.start_position,
|
|
|
|
|
// end_slice.end_position,
|
|
|
|
|
// );
|
|
|
|
|
// Ok(AstNode::new(
|
|
|
|
|
// Stmt::For {
|
|
|
|
|
// variable: Box::new(variable),
|
|
|
|
|
// iterable: Box::new(iterable),
|
|
|
|
|
// body: Box::new(body),
|
|
|
|
|
// },
|
|
|
|
|
// combined_slice,
|
|
|
|
|
// ))
|
|
|
|
|
// }
|
|
|
|
|
|
2025-10-04 19:02:33 +02:00
|
|
|
fn while_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
|
|
|
|
let start_slice = self.peek().source_slice.clone();
|
2025-10-03 19:40:25 +02:00
|
|
|
self.advance();
|
|
|
|
|
let condition = self.expression()?;
|
2026-02-11 16:35:05 +01:00
|
|
|
let body = self.statement(Some("loop".to_string()))?;
|
2025-10-04 19:02:33 +02:00
|
|
|
let end_slice = body.source_slice.clone();
|
|
|
|
|
let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
start_slice.source_id,
|
|
|
|
|
start_slice.start_position,
|
|
|
|
|
end_slice.end_position,
|
|
|
|
|
);
|
|
|
|
|
Ok(AstNode::new(
|
|
|
|
|
Stmt::While {
|
|
|
|
|
condition: Box::new(condition),
|
|
|
|
|
body: Box::new(body),
|
2025-10-10 10:18:02 +02:00
|
|
|
return_value: Box::new(BaseValue::Nil),
|
2025-10-04 19:02:33 +02:00
|
|
|
},
|
|
|
|
|
combined_slice,
|
|
|
|
|
))
|
2025-10-03 19:40:25 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-04 19:02:33 +02:00
|
|
|
fn if_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
|
|
|
|
let start_slice = self.peek().source_slice.clone();
|
|
|
|
|
self.advance(); // consume 'if'
|
2025-10-03 19:07:12 +02:00
|
|
|
let condition = self.expression()?;
|
2025-10-04 19:02:33 +02:00
|
|
|
self.consume(TokenType::Then, "Expect 'then' after if condition.")?;
|
2026-02-11 16:35:05 +01:00
|
|
|
let then_branch = self.statement(None)?;
|
2025-10-03 19:07:12 +02:00
|
|
|
let mut elif_branches = Vec::new();
|
2025-10-04 19:02:33 +02:00
|
|
|
let mut last_slice = then_branch.source_slice.clone();
|
|
|
|
|
|
2025-10-03 19:07:12 +02:00
|
|
|
while self.peek().token_type == TokenType::Elif {
|
2025-10-04 19:02:33 +02:00
|
|
|
self.advance(); // consume 'elif'
|
|
|
|
|
let elif_condition = self.expression()?;
|
|
|
|
|
self.consume(TokenType::Then, "Expect 'then' after elif condition.")?;
|
2026-02-11 16:35:05 +01:00
|
|
|
let elif_branch = self.statement(None)?;
|
2025-10-04 19:02:33 +02:00
|
|
|
last_slice = elif_branch.source_slice.clone();
|
|
|
|
|
elif_branches.push((Box::new(elif_condition), Box::new(elif_branch)));
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
2025-10-04 19:02:33 +02:00
|
|
|
|
2025-10-03 19:07:12 +02:00
|
|
|
let else_branch = if self.peek().token_type == TokenType::Else {
|
2025-10-04 19:02:33 +02:00
|
|
|
self.advance(); // consume 'else'
|
2026-02-11 16:35:05 +01:00
|
|
|
let else_stmt = self.statement(None)?;
|
2025-10-04 19:02:33 +02:00
|
|
|
last_slice = else_stmt.source_slice.clone();
|
|
|
|
|
Some(Box::new(else_stmt))
|
2025-10-03 19:07:12 +02:00
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
2025-10-04 19:02:33 +02:00
|
|
|
|
|
|
|
|
let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
start_slice.source_id,
|
|
|
|
|
start_slice.start_position,
|
|
|
|
|
last_slice.end_position,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Ok(AstNode::new(
|
|
|
|
|
Stmt::If {
|
|
|
|
|
condition: Box::new(condition),
|
|
|
|
|
then_branch: Box::new(then_branch),
|
|
|
|
|
elif_branch: elif_branches,
|
|
|
|
|
else_branch: else_branch,
|
2025-10-10 10:18:02 +02:00
|
|
|
return_value: Box::new(BaseValue::Nil),
|
2025-10-04 19:02:33 +02:00
|
|
|
},
|
|
|
|
|
combined_slice,
|
|
|
|
|
))
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-04 19:02:33 +02:00
|
|
|
fn var_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
|
|
|
|
let start_slice = self.peek().source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
let name = self.consume(TokenType::Identifier, "Expect variable name.")?;
|
|
|
|
|
let name_lexeme = name.lexeme.clone();
|
2025-10-06 18:52:32 +02:00
|
|
|
|
2025-10-07 14:28:24 +02:00
|
|
|
let mut _type_annotation = BaseValue::Nil;
|
2025-10-06 18:52:32 +02:00
|
|
|
self.consume(TokenType::Colon, "Expect column")?;
|
2026-06-29 20:47:59 +02:00
|
|
|
if self.peek().token_type == TokenType::Identifier {
|
|
|
|
|
// TODO manage type annotation
|
|
|
|
|
self.consume(TokenType::Identifier, "Expect type name.")?;
|
|
|
|
|
}
|
2025-10-06 18:52:32 +02:00
|
|
|
|
|
|
|
|
match self.peek().token_type {
|
|
|
|
|
TokenType::Equal | TokenType::Identifier => {
|
|
|
|
|
self.variable_declaration(start_slice, name_lexeme)
|
|
|
|
|
}
|
|
|
|
|
TokenType::Colon => self.function_declaration(start_slice, name_lexeme),
|
|
|
|
|
_ => runtime_error(start_slice, "this is not supposed to be here"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn function_declaration(
|
|
|
|
|
&mut self,
|
|
|
|
|
start_slice: SourceSlice,
|
|
|
|
|
name_lexeme: String,
|
|
|
|
|
) -> LoxResult<AstNode<Stmt>> {
|
|
|
|
|
self.advance();
|
|
|
|
|
self.consume(TokenType::Fn, "Expect 'fn' after '::'")?;
|
|
|
|
|
self.consume(TokenType::LeftParen, "Expect '(' after 'fn' ")?;
|
|
|
|
|
let mut parameters: Vec<(String, String)> = vec![];
|
|
|
|
|
while self.peek().token_type != TokenType::RightParen {
|
|
|
|
|
let expr = self
|
|
|
|
|
.consume(TokenType::Identifier, "Expect identifier after '('")?
|
|
|
|
|
.clone();
|
|
|
|
|
if self.peek().token_type == TokenType::Colon {
|
|
|
|
|
self.advance();
|
|
|
|
|
let type_ =
|
|
|
|
|
self.consume(TokenType::Identifier, "Expected identifier after ':' ")?;
|
|
|
|
|
parameters.push((expr.lexeme, type_.lexeme.clone()));
|
|
|
|
|
} else {
|
|
|
|
|
parameters.push((expr.lexeme, "Any".to_string()));
|
|
|
|
|
}
|
|
|
|
|
if self.peek().token_type == TokenType::Comma {
|
|
|
|
|
self.advance();
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-07 14:28:24 +02:00
|
|
|
self.consume(TokenType::RightParen, "Expected ')' after parameters")?;
|
2025-10-06 18:52:32 +02:00
|
|
|
let end_position = self.peek().source_slice.clone();
|
|
|
|
|
let combine_position = SourceSlice {
|
|
|
|
|
source_id: start_slice.source_id,
|
|
|
|
|
start_position: start_slice.start_position.clone(),
|
|
|
|
|
end_position: end_position.end_position,
|
|
|
|
|
};
|
2025-10-07 14:28:24 +02:00
|
|
|
|
2026-06-30 09:47:22 +02:00
|
|
|
let mut guard: Option<Box<AstNode<Expr>>> = None;
|
2025-10-07 14:28:24 +02:00
|
|
|
if self.peek().token_type == TokenType::LeftBrace {
|
|
|
|
|
self.consume(TokenType::LeftBrace, "Expected '{' after guard expression")?;
|
2026-06-30 09:47:22 +02:00
|
|
|
guard = Some(Box::new(self.expression()?));
|
2025-10-06 18:52:32 +02:00
|
|
|
self.consume(TokenType::RightBrace, "Expected '}' after guard expression")?;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-11 16:35:05 +01:00
|
|
|
let body = self.statement(None)?;
|
2026-06-30 14:05:46 +02:00
|
|
|
let node = AstNode::new(
|
|
|
|
|
Expr::Literal {
|
2025-10-07 14:28:24 +02:00
|
|
|
value: BaseValue::Function(LoxFunction {
|
2025-10-06 18:52:32 +02:00
|
|
|
parameters,
|
2025-10-10 10:18:02 +02:00
|
|
|
return_type: None,
|
2025-10-06 18:52:32 +02:00
|
|
|
body,
|
|
|
|
|
closure: None,
|
|
|
|
|
guard,
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-06-30 14:05:46 +02:00
|
|
|
combine_position.clone(),
|
|
|
|
|
);
|
2025-10-06 18:52:32 +02:00
|
|
|
Ok(AstNode::new(
|
|
|
|
|
Stmt::VarDeclaration {
|
|
|
|
|
name: name_lexeme,
|
|
|
|
|
initializer: Some(Box::new(node)),
|
2025-10-10 10:18:02 +02:00
|
|
|
return_value: Box::new(BaseValue::Nil),
|
2025-10-06 18:52:32 +02:00
|
|
|
},
|
|
|
|
|
combine_position.clone(),
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn variable_declaration(
|
|
|
|
|
&mut self,
|
|
|
|
|
start_slice: SourceSlice,
|
|
|
|
|
name_lexeme: String,
|
|
|
|
|
) -> LoxResult<AstNode<Stmt>> {
|
|
|
|
|
if self.peek().token_type == TokenType::Identifier {
|
|
|
|
|
self.advance();
|
|
|
|
|
if self.peek().token_type == TokenType::Identifier {
|
|
|
|
|
self.advance();
|
|
|
|
|
}
|
|
|
|
|
// todo: make type annotation
|
|
|
|
|
}
|
2026-06-30 14:05:46 +02:00
|
|
|
let mut value = AstNode::new(
|
|
|
|
|
Expr::Literal {
|
2025-10-07 14:28:24 +02:00
|
|
|
value: BaseValue::Nil,
|
2025-10-06 18:52:32 +02:00
|
|
|
},
|
2026-06-30 14:05:46 +02:00
|
|
|
start_slice.clone(),
|
|
|
|
|
);
|
2025-10-06 18:52:32 +02:00
|
|
|
if self.peek().token_type == TokenType::Equal {
|
|
|
|
|
self.advance();
|
|
|
|
|
value = self.expression()?;
|
|
|
|
|
}
|
2025-10-04 19:02:33 +02:00
|
|
|
let semicolon = self.consume(
|
2025-10-03 19:07:12 +02:00
|
|
|
TokenType::Semicolon,
|
|
|
|
|
"Expect ';' after variable declaration.",
|
|
|
|
|
)?;
|
2025-10-04 19:02:33 +02:00
|
|
|
let end_slice = semicolon.source_slice.clone();
|
|
|
|
|
let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
start_slice.source_id,
|
|
|
|
|
start_slice.start_position,
|
|
|
|
|
end_slice.end_position,
|
|
|
|
|
);
|
|
|
|
|
Ok(AstNode::new(
|
2025-10-06 18:52:32 +02:00
|
|
|
Stmt::VarDeclaration {
|
2025-10-04 19:02:33 +02:00
|
|
|
name: name_lexeme,
|
|
|
|
|
initializer: Some(Box::new(value)),
|
2025-10-10 10:18:02 +02:00
|
|
|
return_value: Box::new(BaseValue::Nil),
|
2025-10-04 19:02:33 +02:00
|
|
|
},
|
|
|
|
|
combined_slice,
|
|
|
|
|
))
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-04 19:02:33 +02:00
|
|
|
fn print_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
2025-10-03 19:07:12 +02:00
|
|
|
// consume the print keyword
|
2025-10-04 19:02:33 +02:00
|
|
|
let start_slice = self.peek().source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
self.advance();
|
|
|
|
|
let expr = self.expression()?;
|
2025-10-04 19:02:33 +02:00
|
|
|
let semicolon = self.consume(TokenType::Semicolon, "Expect ';' after value.")?;
|
|
|
|
|
let end_slice = semicolon.source_slice.clone();
|
|
|
|
|
let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
start_slice.source_id,
|
|
|
|
|
start_slice.start_position,
|
|
|
|
|
end_slice.end_position,
|
|
|
|
|
);
|
|
|
|
|
Ok(AstNode::new(
|
|
|
|
|
Stmt::Print {
|
|
|
|
|
expression: Box::new(expr),
|
2025-10-10 10:18:02 +02:00
|
|
|
return_value: Box::new(BaseValue::Nil),
|
2025-10-04 19:02:33 +02:00
|
|
|
},
|
|
|
|
|
combined_slice,
|
|
|
|
|
))
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
2026-02-11 16:35:05 +01:00
|
|
|
fn block_statement(&mut self, label: Option<String>) -> LoxResult<AstNode<Stmt>> {
|
2025-10-04 19:02:33 +02:00
|
|
|
let start_slice = self.peek().source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
self.advance();
|
|
|
|
|
let mut statements = Vec::new();
|
2026-02-11 16:35:05 +01:00
|
|
|
|
2025-10-03 19:07:12 +02:00
|
|
|
while self.peek().token_type != TokenType::EndBlock && !self.is_at_end() {
|
2026-02-11 16:35:05 +01:00
|
|
|
let stmt = self.statement(label.clone())?;
|
2025-10-04 19:02:33 +02:00
|
|
|
statements.push(stmt);
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
2025-10-04 19:02:33 +02:00
|
|
|
let end_token = self.consume(TokenType::EndBlock, "Expect 'end' after block.")?;
|
|
|
|
|
let end_slice = end_token.source_slice.clone();
|
|
|
|
|
let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
start_slice.source_id,
|
|
|
|
|
start_slice.start_position,
|
|
|
|
|
end_slice.end_position,
|
|
|
|
|
);
|
2026-02-11 16:35:05 +01:00
|
|
|
let label_str = if let Some(label) = label {
|
|
|
|
|
label
|
|
|
|
|
} else {
|
|
|
|
|
String::default()
|
|
|
|
|
};
|
2025-10-04 19:02:33 +02:00
|
|
|
Ok(AstNode::new(
|
|
|
|
|
Stmt::Block {
|
|
|
|
|
statements: Box::new(statements),
|
2026-02-11 16:35:05 +01:00
|
|
|
label: label_str,
|
2025-10-10 10:18:02 +02:00
|
|
|
return_value: Box::new(BaseValue::Nil),
|
2025-10-04 19:02:33 +02:00
|
|
|
},
|
|
|
|
|
combined_slice,
|
|
|
|
|
))
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-04 19:02:33 +02:00
|
|
|
fn expression_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
|
|
|
|
let start_slice = self.peek().source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
let expr = self.expression()?;
|
|
|
|
|
if self.peek().token_type == TokenType::Semicolon {
|
2025-10-04 19:02:33 +02:00
|
|
|
let semicolon = self.advance();
|
|
|
|
|
let end_slice = semicolon.source_slice.clone();
|
|
|
|
|
let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
start_slice.source_id,
|
|
|
|
|
start_slice.start_position,
|
|
|
|
|
end_slice.end_position,
|
|
|
|
|
);
|
|
|
|
|
return Ok(AstNode::new(
|
2025-10-10 10:18:02 +02:00
|
|
|
Stmt::Expression {
|
2025-10-04 19:02:33 +02:00
|
|
|
expression: Box::new(expr),
|
2025-10-10 10:18:02 +02:00
|
|
|
return_value: Box::new(BaseValue::Nil),
|
2025-10-04 19:02:33 +02:00
|
|
|
},
|
|
|
|
|
combined_slice,
|
|
|
|
|
));
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
2025-10-04 19:02:33 +02:00
|
|
|
// Use the expression's source slice for expression statements without semicolon
|
|
|
|
|
let expr_slice = expr.source_slice.clone();
|
|
|
|
|
Ok(AstNode::new(
|
|
|
|
|
Stmt::Expression {
|
|
|
|
|
expression: Box::new(expr),
|
2025-10-10 10:18:02 +02:00
|
|
|
return_value: Box::new(BaseValue::Nil),
|
2025-10-04 19:02:33 +02:00
|
|
|
},
|
|
|
|
|
expr_slice,
|
|
|
|
|
))
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
2026-02-11 16:35:05 +01:00
|
|
|
fn return_statement(&mut self, label: Option<String>) -> LoxResult<AstNode<Stmt>> {
|
2025-10-04 19:02:33 +02:00
|
|
|
let start_slice = self.peek().source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
self.advance();
|
2026-02-11 16:35:05 +01:00
|
|
|
let expr = match self.expression() {
|
|
|
|
|
Ok(expr) => expr,
|
|
|
|
|
Err(LoxError::ParseError {
|
|
|
|
|
message,
|
|
|
|
|
source_slice,
|
|
|
|
|
}) => {
|
|
|
|
|
if message == "Expect expression." {
|
2026-06-30 14:05:46 +02:00
|
|
|
AstNode::new(
|
|
|
|
|
Expr::Literal {
|
2026-02-11 16:35:05 +01:00
|
|
|
value: BaseValue::Nil,
|
|
|
|
|
},
|
2026-06-30 14:05:46 +02:00
|
|
|
start_slice.clone(),
|
|
|
|
|
)
|
2026-02-11 16:35:05 +01:00
|
|
|
} else {
|
|
|
|
|
return Err(LoxError::ParseError {
|
|
|
|
|
message,
|
|
|
|
|
source_slice,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(err) => return Err(err),
|
|
|
|
|
};
|
2025-10-04 19:02:33 +02:00
|
|
|
let semicolon = self.consume(TokenType::Semicolon, "Expect ';' after return value.")?;
|
|
|
|
|
let end_slice = semicolon.source_slice.clone();
|
|
|
|
|
let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
start_slice.source_id,
|
|
|
|
|
start_slice.start_position,
|
|
|
|
|
end_slice.end_position,
|
|
|
|
|
);
|
2026-02-11 16:35:05 +01:00
|
|
|
let label_str = if let Some(label) = label {
|
|
|
|
|
label
|
|
|
|
|
} else {
|
|
|
|
|
String::default()
|
|
|
|
|
};
|
2025-10-04 19:02:33 +02:00
|
|
|
Ok(AstNode::new(
|
|
|
|
|
Stmt::Return {
|
|
|
|
|
expression: Box::new(expr),
|
2025-10-10 10:18:02 +02:00
|
|
|
return_value: Box::new(BaseValue::Nil),
|
2026-02-11 16:35:05 +01:00
|
|
|
label: label_str,
|
2025-10-04 19:02:33 +02:00
|
|
|
},
|
|
|
|
|
combined_slice,
|
|
|
|
|
))
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-04 19:02:33 +02:00
|
|
|
fn expression(&mut self) -> LoxResult<AstNode<Expr>> {
|
2026-06-30 14:05:46 +02:00
|
|
|
self.assignment()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn assignment(&mut self) -> LoxResult<AstNode<Expr>> {
|
|
|
|
|
let expr = self.or_and()?;
|
|
|
|
|
if self.peek().token_type == TokenType::Equal {
|
|
|
|
|
self.advance(); // consume '='
|
|
|
|
|
// Right-associative: `a = b = c` parses as `a = (b = c)`.
|
|
|
|
|
let value = self.assignment()?;
|
|
|
|
|
let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
expr.source_slice.source_id,
|
|
|
|
|
expr.source_slice.start_position.clone(),
|
|
|
|
|
value.source_slice.end_position.clone(),
|
|
|
|
|
);
|
|
|
|
|
let target_slice = expr.source_slice.clone();
|
|
|
|
|
match expr.node {
|
|
|
|
|
Expr::Identifier { name } => Ok(AstNode::new(
|
|
|
|
|
Expr::Assign {
|
|
|
|
|
name,
|
|
|
|
|
value: Box::new(value),
|
|
|
|
|
},
|
|
|
|
|
combined_slice,
|
|
|
|
|
)),
|
|
|
|
|
_ => parse_error(target_slice, "Invalid assignment target."),
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Ok(expr)
|
|
|
|
|
}
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-04 19:02:33 +02:00
|
|
|
fn or_and(&mut self) -> LoxResult<AstNode<Expr>> {
|
2025-10-07 14:28:24 +02:00
|
|
|
let mut expr = self.logical_is()?;
|
2025-10-03 19:07:12 +02:00
|
|
|
|
|
|
|
|
while [TokenType::Or, TokenType::And].contains(&self.peek().token_type) {
|
2025-10-07 14:28:24 +02:00
|
|
|
let start_slice = expr.source_slice.clone();
|
|
|
|
|
let operator = self.peek().token_type.clone();
|
|
|
|
|
self.advance();
|
|
|
|
|
let right = self.logical_is()?;
|
|
|
|
|
let end_slice = right.source_slice.clone();
|
|
|
|
|
let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
start_slice.source_id,
|
|
|
|
|
start_slice.start_position,
|
|
|
|
|
end_slice.end_position,
|
|
|
|
|
);
|
|
|
|
|
expr = AstNode::new(
|
|
|
|
|
Expr::Binary {
|
|
|
|
|
left: Box::new(expr),
|
|
|
|
|
operator,
|
|
|
|
|
right: Box::new(right),
|
|
|
|
|
},
|
|
|
|
|
combined_slice,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(expr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn logical_is(&mut self) -> LoxResult<AstNode<Expr>> {
|
|
|
|
|
let mut expr = self.equality()?;
|
|
|
|
|
|
|
|
|
|
while self.peek().token_type == TokenType::Is {
|
2025-10-04 19:02:33 +02:00
|
|
|
let start_slice = expr.source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
let operator = self.peek().token_type.clone();
|
|
|
|
|
self.advance();
|
|
|
|
|
let right = self.equality()?;
|
2025-10-04 19:02:33 +02:00
|
|
|
let end_slice = right.source_slice.clone();
|
|
|
|
|
let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
start_slice.source_id,
|
|
|
|
|
start_slice.start_position,
|
|
|
|
|
end_slice.end_position,
|
|
|
|
|
);
|
|
|
|
|
expr = AstNode::new(
|
|
|
|
|
Expr::Binary {
|
|
|
|
|
left: Box::new(expr),
|
|
|
|
|
operator,
|
|
|
|
|
right: Box::new(right),
|
|
|
|
|
},
|
|
|
|
|
combined_slice,
|
|
|
|
|
);
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(expr)
|
|
|
|
|
}
|
2025-10-07 14:28:24 +02:00
|
|
|
|
2025-10-04 19:02:33 +02:00
|
|
|
fn equality(&mut self) -> LoxResult<AstNode<Expr>> {
|
2025-10-03 19:07:12 +02:00
|
|
|
let mut expr = self.comparison()?;
|
|
|
|
|
|
|
|
|
|
while [TokenType::BangEqual, TokenType::EqualEqual].contains(&self.peek().token_type) {
|
2025-10-04 19:02:33 +02:00
|
|
|
let start_slice = expr.source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
let operator = self.peek().token_type.clone();
|
|
|
|
|
self.advance();
|
|
|
|
|
let right = self.comparison()?;
|
2025-10-04 19:02:33 +02:00
|
|
|
let end_slice = right.source_slice.clone();
|
|
|
|
|
let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
start_slice.source_id,
|
|
|
|
|
start_slice.start_position,
|
|
|
|
|
end_slice.end_position,
|
|
|
|
|
);
|
|
|
|
|
expr = AstNode::new(
|
|
|
|
|
Expr::Binary {
|
|
|
|
|
left: Box::new(expr),
|
|
|
|
|
operator,
|
|
|
|
|
right: Box::new(right),
|
|
|
|
|
},
|
|
|
|
|
combined_slice,
|
|
|
|
|
);
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(expr)
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-04 19:02:33 +02:00
|
|
|
fn comparison(&mut self) -> LoxResult<AstNode<Expr>> {
|
2025-10-03 19:07:12 +02:00
|
|
|
let mut expr = self.term()?;
|
|
|
|
|
|
|
|
|
|
while [
|
|
|
|
|
TokenType::Greater,
|
|
|
|
|
TokenType::GreaterEqual,
|
|
|
|
|
TokenType::Less,
|
|
|
|
|
TokenType::LessEqual,
|
|
|
|
|
]
|
|
|
|
|
.contains(&self.peek().token_type)
|
|
|
|
|
{
|
2025-10-04 19:02:33 +02:00
|
|
|
let start_slice = expr.source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
let operator = self.peek().token_type.clone();
|
|
|
|
|
self.advance();
|
|
|
|
|
let right = self.term()?;
|
2025-10-04 19:02:33 +02:00
|
|
|
let end_slice = right.source_slice.clone();
|
|
|
|
|
let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
start_slice.source_id,
|
|
|
|
|
start_slice.start_position,
|
|
|
|
|
end_slice.end_position,
|
|
|
|
|
);
|
|
|
|
|
expr = AstNode::new(
|
|
|
|
|
Expr::Binary {
|
|
|
|
|
left: Box::new(expr),
|
|
|
|
|
operator,
|
|
|
|
|
right: Box::new(right),
|
|
|
|
|
},
|
|
|
|
|
combined_slice,
|
|
|
|
|
);
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(expr)
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-04 19:02:33 +02:00
|
|
|
fn term(&mut self) -> LoxResult<AstNode<Expr>> {
|
2025-10-03 19:07:12 +02:00
|
|
|
let mut expr = self.factor()?;
|
|
|
|
|
|
|
|
|
|
while [TokenType::Plus, TokenType::Minus].contains(&self.peek().token_type) {
|
2025-10-04 19:02:33 +02:00
|
|
|
let start_slice = expr.source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
let operator = self.peek().token_type.clone();
|
|
|
|
|
self.advance();
|
|
|
|
|
let right = self.factor()?;
|
2025-10-04 19:02:33 +02:00
|
|
|
let end_slice = right.source_slice.clone();
|
|
|
|
|
let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
start_slice.source_id,
|
|
|
|
|
start_slice.start_position,
|
|
|
|
|
end_slice.end_position,
|
|
|
|
|
);
|
|
|
|
|
expr = AstNode::new(
|
|
|
|
|
Expr::Binary {
|
|
|
|
|
left: Box::new(expr),
|
|
|
|
|
operator,
|
|
|
|
|
right: Box::new(right),
|
|
|
|
|
},
|
|
|
|
|
combined_slice,
|
|
|
|
|
);
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(expr)
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-04 19:02:33 +02:00
|
|
|
fn factor(&mut self) -> LoxResult<AstNode<Expr>> {
|
2025-10-03 19:07:12 +02:00
|
|
|
let mut expr = self.unary()?;
|
|
|
|
|
|
|
|
|
|
while [TokenType::Slash, TokenType::Star].contains(&self.peek().token_type) {
|
2025-10-04 19:02:33 +02:00
|
|
|
let start_slice = expr.source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
let operator = self.peek().token_type.clone();
|
|
|
|
|
self.advance();
|
|
|
|
|
let right = self.unary()?;
|
2025-10-04 19:02:33 +02:00
|
|
|
let end_slice = right.source_slice.clone();
|
|
|
|
|
let combined_slice = SourceSlice::from_positions(
|
|
|
|
|
start_slice.source_id,
|
|
|
|
|
start_slice.start_position,
|
|
|
|
|
end_slice.end_position,
|
|
|
|
|
);
|
|
|
|
|
expr = AstNode::new(
|
|
|
|
|
Expr::Binary {
|
|
|
|
|
left: Box::new(expr),
|
|
|
|
|
operator,
|
|
|
|
|
right: Box::new(right),
|
|
|
|
|
},
|
|
|
|
|
combined_slice,
|
|
|
|
|
);
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(expr)
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-04 19:02:33 +02:00
|
|
|
fn unary(&mut self) -> LoxResult<AstNode<Expr>> {
|
2025-10-03 19:07:12 +02:00
|
|
|
if [TokenType::Bang, TokenType::Minus].contains(&self.peek().token_type) {
|
|
|
|
|
let operator = self.peek().token_type.clone();
|
2025-10-04 19:02:33 +02:00
|
|
|
let source_slice = self.peek().source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
self.advance();
|
|
|
|
|
let right = self.unary()?;
|
2025-10-04 19:02:33 +02:00
|
|
|
return Ok(AstNode::new(
|
|
|
|
|
Expr::Unary {
|
|
|
|
|
operator,
|
|
|
|
|
operand: Box::new(right),
|
|
|
|
|
},
|
|
|
|
|
source_slice,
|
|
|
|
|
));
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-06 18:52:32 +02:00
|
|
|
self.call()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn call(&mut self) -> LoxResult<AstNode<Expr>> {
|
|
|
|
|
let mut expr = self.primary()?;
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
if self.peek().token_type == TokenType::LeftParen {
|
|
|
|
|
expr = self.finish_call(expr)?;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(expr)
|
|
|
|
|
}
|
|
|
|
|
fn finish_call(&mut self, callee: AstNode<Expr>) -> LoxResult<AstNode<Expr>> {
|
|
|
|
|
let start_slice = callee.source_slice.start_position.clone();
|
|
|
|
|
self.advance(); // Consume '('
|
|
|
|
|
|
|
|
|
|
let mut arguments = Vec::new();
|
|
|
|
|
|
|
|
|
|
if self.peek().token_type != TokenType::RightParen {
|
|
|
|
|
loop {
|
|
|
|
|
arguments.push(self.expression()?);
|
|
|
|
|
if self.peek().token_type == TokenType::Comma {
|
|
|
|
|
self.advance();
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.consume(TokenType::RightParen, "Expect ')' after arguments.")?;
|
|
|
|
|
|
|
|
|
|
// Estendi il source_slice per includere le parentesi di chiusura
|
|
|
|
|
let end_slice = self.previous().source_slice.end_position.clone();
|
|
|
|
|
let full_slice = SourceSlice {
|
|
|
|
|
source_id: callee.source_slice.source_id.clone(),
|
|
|
|
|
start_position: start_slice.clone(),
|
|
|
|
|
end_position: end_slice.clone(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Ok(AstNode::new(
|
|
|
|
|
Expr::Call {
|
|
|
|
|
callee: Box::new(callee),
|
|
|
|
|
arguments,
|
|
|
|
|
},
|
|
|
|
|
full_slice,
|
|
|
|
|
))
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-04 19:02:33 +02:00
|
|
|
fn primary(&mut self) -> LoxResult<AstNode<Expr>> {
|
2025-10-03 19:07:12 +02:00
|
|
|
match self.peek().token_type {
|
|
|
|
|
TokenType::False => {
|
2025-10-04 19:02:33 +02:00
|
|
|
let source_slice = self.peek().source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
self.advance();
|
2025-10-04 19:02:33 +02:00
|
|
|
Ok(AstNode::new(
|
|
|
|
|
Expr::Literal {
|
2025-10-07 14:28:24 +02:00
|
|
|
value: BaseValue::Boolean(false),
|
2025-10-04 19:02:33 +02:00
|
|
|
},
|
|
|
|
|
source_slice,
|
|
|
|
|
))
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
TokenType::True => {
|
2025-10-04 19:02:33 +02:00
|
|
|
let source_slice = self.peek().source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
self.advance();
|
2025-10-04 19:02:33 +02:00
|
|
|
Ok(AstNode::new(
|
|
|
|
|
Expr::Literal {
|
2025-10-07 14:28:24 +02:00
|
|
|
value: BaseValue::Boolean(true),
|
2025-10-04 19:02:33 +02:00
|
|
|
},
|
|
|
|
|
source_slice,
|
|
|
|
|
))
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
TokenType::Nil => {
|
2025-10-04 19:02:33 +02:00
|
|
|
let source_slice = self.peek().source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
self.advance();
|
2025-10-04 19:02:33 +02:00
|
|
|
Ok(AstNode::new(
|
|
|
|
|
Expr::Literal {
|
2025-10-07 14:28:24 +02:00
|
|
|
value: BaseValue::Nil,
|
2025-10-04 19:02:33 +02:00
|
|
|
},
|
|
|
|
|
source_slice,
|
|
|
|
|
))
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
TokenType::Number => {
|
2025-10-04 19:02:33 +02:00
|
|
|
let source_slice = self.peek().source_slice.clone();
|
|
|
|
|
let literal = self.peek().literal.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
self.advance();
|
2025-10-04 19:02:33 +02:00
|
|
|
if let Some(literal) = literal {
|
|
|
|
|
Ok(AstNode::new(Expr::Literal { value: literal }, source_slice))
|
2025-10-03 19:07:12 +02:00
|
|
|
} else {
|
2025-10-06 18:52:32 +02:00
|
|
|
parse_error(self.peek().source_slice.clone(), "Expected number literal")
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
TokenType::String => {
|
2025-10-04 19:02:33 +02:00
|
|
|
let source_slice = self.peek().source_slice.clone();
|
|
|
|
|
let literal = self.peek().literal.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
self.advance();
|
2025-10-04 19:02:33 +02:00
|
|
|
if let Some(literal) = literal {
|
|
|
|
|
Ok(AstNode::new(Expr::Literal { value: literal }, source_slice))
|
2025-10-03 19:07:12 +02:00
|
|
|
} else {
|
2025-10-06 18:52:32 +02:00
|
|
|
parse_error(self.peek().source_slice.clone(), "Expected string literal")
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
TokenType::LeftParen => {
|
2025-10-04 19:02:33 +02:00
|
|
|
let source_slice = self.peek().source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
self.advance();
|
|
|
|
|
let expr = self.expression()?;
|
|
|
|
|
self.consume(TokenType::RightParen, "Expect ')' after expression.")?;
|
2025-10-04 19:02:33 +02:00
|
|
|
Ok(AstNode::new(
|
|
|
|
|
Expr::Grouping {
|
|
|
|
|
expression: Box::new(expr),
|
|
|
|
|
},
|
|
|
|
|
source_slice,
|
|
|
|
|
))
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
TokenType::Identifier => {
|
|
|
|
|
let name = self.peek().lexeme.clone();
|
2025-10-04 19:02:33 +02:00
|
|
|
let source_slice = self.peek().source_slice.clone();
|
2025-10-03 19:07:12 +02:00
|
|
|
self.advance();
|
2025-10-06 18:52:32 +02:00
|
|
|
Ok(AstNode::new(Expr::Identifier { name }, source_slice))
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
2025-10-06 18:52:32 +02:00
|
|
|
_ => parse_error(self.peek().source_slice.clone(), "Expect expression."),
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_at_end(&self) -> bool {
|
|
|
|
|
if self.current >= self.tokens.len() {
|
|
|
|
|
true
|
|
|
|
|
} else {
|
|
|
|
|
self.tokens[self.current].token_type == TokenType::Eof
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn advance(&mut self) -> &Token {
|
|
|
|
|
if !self.is_at_end() {
|
|
|
|
|
self.current += 1;
|
|
|
|
|
}
|
2025-10-04 19:02:33 +02:00
|
|
|
self.previous()
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn peek(&self) -> &Token {
|
|
|
|
|
if self.is_at_end() {
|
|
|
|
|
&self.tokens.last().unwrap()
|
|
|
|
|
} else {
|
|
|
|
|
&self.tokens[self.current]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn peek_next(&self) -> &Token {
|
|
|
|
|
if self.is_at_end() {
|
|
|
|
|
&self.peek()
|
|
|
|
|
} else {
|
|
|
|
|
&self.tokens[self.current + 1]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn previous(&self) -> &Token {
|
2025-10-04 19:02:33 +02:00
|
|
|
if self.current == 0 {
|
|
|
|
|
&self.tokens[0]
|
|
|
|
|
} else {
|
|
|
|
|
&self.tokens[self.current - 1]
|
|
|
|
|
}
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn consume(&mut self, token_type: TokenType, message: &str) -> LoxResult<&Token> {
|
|
|
|
|
if self.peek().token_type == token_type {
|
|
|
|
|
self.advance();
|
|
|
|
|
Ok(self.previous())
|
|
|
|
|
} else {
|
2025-10-06 18:52:32 +02:00
|
|
|
return parse_error(self.peek().source_slice.clone(), message);
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn synchronize(&mut self) {
|
|
|
|
|
self.advance();
|
|
|
|
|
|
|
|
|
|
while !self.is_at_end() {
|
|
|
|
|
if self.previous().token_type == TokenType::Semicolon {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match self.peek().token_type {
|
|
|
|
|
TokenType::Class
|
|
|
|
|
| TokenType::Fun
|
|
|
|
|
| TokenType::For
|
|
|
|
|
| TokenType::If
|
|
|
|
|
| TokenType::While
|
|
|
|
|
| TokenType::Print
|
|
|
|
|
| TokenType::Return => return,
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.advance();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-06-29 20:47:59 +02:00
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
use crate::common::base_value::Number;
|
|
|
|
|
use crate::frontend::lexer::Lexer;
|
|
|
|
|
|
|
|
|
|
/// Lex and parse `src`, returning the parser's result.
|
|
|
|
|
fn parse_source(src: &str) -> LoxResult<Vec<AstNode<Stmt>>> {
|
|
|
|
|
let tokens = Lexer::new(src.to_string(), 0)
|
|
|
|
|
.scans_tokens()
|
|
|
|
|
.expect("source should lex without errors");
|
|
|
|
|
Parser::new(tokens).parse()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Parse `src`, panicking if parsing fails.
|
|
|
|
|
fn parse_ok(src: &str) -> Vec<AstNode<Stmt>> {
|
|
|
|
|
parse_source(src).expect("expected source to parse")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Extract the inner expression of an expression statement.
|
|
|
|
|
fn expression_of(stmt: &AstNode<Stmt>) -> &AstNode<Expr> {
|
|
|
|
|
match &stmt.node {
|
|
|
|
|
Stmt::Expression { expression, .. } => expression,
|
|
|
|
|
other => panic!("expected expression statement, got {:?}", other),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parses_number_literal_expression() {
|
|
|
|
|
let stmts = parse_ok("42;");
|
|
|
|
|
assert_eq!(stmts.len(), 1);
|
|
|
|
|
match &expression_of(&stmts[0]).node {
|
|
|
|
|
Expr::Literal { value } => {
|
|
|
|
|
assert_eq!(*value, BaseValue::Number(Number::I32(42)));
|
|
|
|
|
}
|
|
|
|
|
other => panic!("expected literal, got {:?}", other),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn respects_multiplication_precedence_over_addition() {
|
|
|
|
|
// 1 + 2 * 3 should parse as 1 + (2 * 3)
|
|
|
|
|
let stmts = parse_ok("1 + 2 * 3;");
|
|
|
|
|
match &expression_of(&stmts[0]).node {
|
|
|
|
|
Expr::Binary {
|
|
|
|
|
operator, right, ..
|
|
|
|
|
} => {
|
|
|
|
|
assert_eq!(*operator, TokenType::Plus);
|
|
|
|
|
match &right.node {
|
|
|
|
|
Expr::Binary { operator, .. } => {
|
|
|
|
|
assert_eq!(*operator, TokenType::Star)
|
|
|
|
|
}
|
|
|
|
|
other => panic!("expected nested binary, got {:?}", other),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
other => panic!("expected binary expression, got {:?}", other),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parses_unary_negation() {
|
|
|
|
|
let stmts = parse_ok("-5;");
|
|
|
|
|
match &expression_of(&stmts[0]).node {
|
|
|
|
|
Expr::Unary { operator, .. } => assert_eq!(*operator, TokenType::Minus),
|
|
|
|
|
other => panic!("expected unary expression, got {:?}", other),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parses_grouping_to_change_precedence() {
|
|
|
|
|
// (1 + 2) * 3 should have a grouping on the left of the multiply.
|
|
|
|
|
let stmts = parse_ok("(1 + 2) * 3;");
|
|
|
|
|
match &expression_of(&stmts[0]).node {
|
|
|
|
|
Expr::Binary { operator, left, .. } => {
|
|
|
|
|
assert_eq!(*operator, TokenType::Star);
|
|
|
|
|
assert!(matches!(left.node, Expr::Grouping { .. }));
|
|
|
|
|
}
|
|
|
|
|
other => panic!("expected binary expression, got {:?}", other),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parses_comparison_expression() {
|
|
|
|
|
let stmts = parse_ok("1 < 2;");
|
|
|
|
|
match &expression_of(&stmts[0]).node {
|
|
|
|
|
Expr::Binary { operator, .. } => assert_eq!(*operator, TokenType::Less),
|
|
|
|
|
other => panic!("expected binary expression, got {:?}", other),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parses_print_statement() {
|
|
|
|
|
let stmts = parse_ok("print 1;");
|
|
|
|
|
assert!(matches!(stmts[0].node, Stmt::Print { .. }));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parses_var_declaration_with_initializer() {
|
|
|
|
|
let stmts = parse_ok("var x: Int = 5;");
|
|
|
|
|
match &stmts[0].node {
|
|
|
|
|
Stmt::VarDeclaration {
|
|
|
|
|
name, initializer, ..
|
|
|
|
|
} => {
|
|
|
|
|
assert_eq!(name, "x");
|
|
|
|
|
assert!(initializer.is_some());
|
|
|
|
|
}
|
|
|
|
|
other => panic!("expected var declaration, got {:?}", other),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parses_assignment_statement() {
|
|
|
|
|
let stmts = parse_ok("x = 5;");
|
|
|
|
|
match &stmts[0].node {
|
2026-06-30 14:05:46 +02:00
|
|
|
Stmt::Expression { expression, .. } => match &expression.node {
|
|
|
|
|
Expr::Assign { name, .. } => assert_eq!(name, "x"),
|
|
|
|
|
other => panic!("expected assign expression, got {:?}", other),
|
|
|
|
|
},
|
|
|
|
|
other => panic!("expected expression statement, got {:?}", other),
|
2026-06-29 20:47:59 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parses_block_statement() {
|
|
|
|
|
let stmts = parse_ok("do print 1; print 2; end");
|
|
|
|
|
match &stmts[0].node {
|
|
|
|
|
Stmt::Block { statements, .. } => assert_eq!(statements.len(), 2),
|
|
|
|
|
other => panic!("expected block statement, got {:?}", other),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parses_if_statement() {
|
|
|
|
|
let stmts = parse_ok("if true then print 1;");
|
|
|
|
|
assert!(matches!(stmts[0].node, Stmt::If { .. }));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parses_while_statement() {
|
|
|
|
|
let stmts = parse_ok("while true do print 1; end");
|
|
|
|
|
assert!(matches!(stmts[0].node, Stmt::While { .. }));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn errors_on_missing_semicolon_after_print() {
|
|
|
|
|
assert!(parse_source("print 1").is_err());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn errors_on_unclosed_grouping() {
|
|
|
|
|
assert!(parse_source("(1 + 2;").is_err());
|
|
|
|
|
}
|
|
|
|
|
}
|