Add source location tracking to AST nodes
The commit expands the source location tracking through the AST by: 1. Switching from raw Expr/Stmt nodes to AstNode wrappers containing source slices 2. Updating interpreter and parser to preserve location info through node traversal 3. Enhancing error reporting with richer source context information
This commit is contained in:
+80
-59
@@ -38,16 +38,16 @@ pub enum Expr {
|
||||
value: LiteralValue,
|
||||
},
|
||||
Binary {
|
||||
left: Box<Expr>,
|
||||
left: Box<AstNode<Expr>>,
|
||||
operator: TokenType,
|
||||
right: Box<Expr>,
|
||||
right: Box<AstNode<Expr>>,
|
||||
},
|
||||
Unary {
|
||||
operator: TokenType,
|
||||
operand: Box<Expr>,
|
||||
operand: Box<AstNode<Expr>>,
|
||||
},
|
||||
Grouping {
|
||||
expression: Box<Expr>,
|
||||
expression: Box<AstNode<Expr>>,
|
||||
},
|
||||
Variable {
|
||||
name: String,
|
||||
@@ -67,13 +67,13 @@ impl ExprPrint for Expr {
|
||||
operator,
|
||||
right,
|
||||
} => {
|
||||
format!("Binary ({} {} {})", left, operator, right)
|
||||
format!("Binary ({} {} {})", left.node, operator, right.node)
|
||||
}
|
||||
Expr::Unary { operator, operand } => {
|
||||
format!("Unary ({} {})", operator, operand)
|
||||
format!("Unary ({} {})", operator, operand.node)
|
||||
}
|
||||
Expr::Grouping { expression } => {
|
||||
format!("Grouping (group {})", expression)
|
||||
format!("Grouping (group {})", expression.node)
|
||||
}
|
||||
Expr::Variable { name } => {
|
||||
format!("Variable (variable {})", name)
|
||||
@@ -91,9 +91,9 @@ impl std::fmt::Display for Expr {
|
||||
left,
|
||||
operator,
|
||||
right,
|
||||
} => write!(f, "Binary ({} {} {})", left, operator, right),
|
||||
Expr::Unary { operator, operand } => write!(f, "Unary ({} {})", operator, operand),
|
||||
Expr::Grouping { expression } => write!(f, "Grouping (group {})", expression),
|
||||
} => write!(f, "Binary ({} {} {})", left.node, operator, right.node),
|
||||
Expr::Unary { operator, operand } => write!(f, "Unary ({} {})", operator, operand.node),
|
||||
Expr::Grouping { expression } => write!(f, "Grouping (group {})", expression.node),
|
||||
Expr::Variable { name } => write!(f, "Variable (variable {})", name),
|
||||
}
|
||||
}
|
||||
@@ -107,9 +107,9 @@ impl Debug for Expr {
|
||||
left,
|
||||
operator,
|
||||
right,
|
||||
} => write!(f, "({:?} {:?} {:?})", operator, left, right),
|
||||
Expr::Unary { operator, operand } => write!(f, "({:?} {:?})", operator, operand),
|
||||
Expr::Grouping { expression } => write!(f, "(group {:?})", expression),
|
||||
} => write!(f, "({:?} {:?} {:?})", operator, left.node, right.node),
|
||||
Expr::Unary { operator, operand } => write!(f, "({:?} {:?})", operator, operand.node),
|
||||
Expr::Grouping { expression } => write!(f, "(group {:?})", expression.node),
|
||||
Expr::Variable { name } => write!(f, "(variable {:?})", name),
|
||||
}
|
||||
}
|
||||
@@ -118,86 +118,96 @@ impl Debug for Expr {
|
||||
#[derive(Clone)]
|
||||
pub enum Stmt {
|
||||
Expression {
|
||||
expression: Box<Expr>,
|
||||
expression: Box<AstNode<Expr>>,
|
||||
},
|
||||
Print {
|
||||
expression: Box<Expr>,
|
||||
expression: Box<AstNode<Expr>>,
|
||||
},
|
||||
Stmt {
|
||||
expression: Box<Expr>,
|
||||
expression: Box<AstNode<Expr>>,
|
||||
},
|
||||
Var {
|
||||
name: String,
|
||||
initializer: Option<Box<Expr>>,
|
||||
initializer: Option<Box<AstNode<Expr>>>,
|
||||
},
|
||||
Assign {
|
||||
name: String,
|
||||
value: Box<Expr>,
|
||||
value: Box<AstNode<Expr>>,
|
||||
},
|
||||
Return {
|
||||
expression: Box<Expr>,
|
||||
expression: Box<AstNode<Expr>>,
|
||||
},
|
||||
Block {
|
||||
statements: Box<Vec<Stmt>>,
|
||||
statements: Box<Vec<AstNode<Stmt>>>,
|
||||
},
|
||||
If {
|
||||
condition: Box<Expr>,
|
||||
then_branch: Box<Stmt>,
|
||||
elif_branch: Vec<(Box<Expr>, Box<Stmt>)>,
|
||||
else_branch: Option<Box<Stmt>>,
|
||||
condition: Box<AstNode<Expr>>,
|
||||
then_branch: Box<AstNode<Stmt>>,
|
||||
elif_branch: Vec<(Box<AstNode<Expr>>, Box<AstNode<Stmt>>)>,
|
||||
else_branch: Option<Box<AstNode<Stmt>>>,
|
||||
},
|
||||
While {
|
||||
condition: Box<Expr>,
|
||||
body: Box<Stmt>,
|
||||
condition: Box<AstNode<Expr>>,
|
||||
body: Box<AstNode<Stmt>>,
|
||||
},
|
||||
For {
|
||||
variable: Box<Expr>,
|
||||
iterable: Box<Expr>,
|
||||
body: Box<Stmt>,
|
||||
variable: Box<AstNode<Expr>>,
|
||||
iterable: Box<AstNode<Expr>>,
|
||||
body: Box<AstNode<Stmt>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Display for Stmt {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Stmt::Expression { expression } => write!(f, "Expression ({})", expression),
|
||||
Stmt::Expression { expression } => write!(f, "Expression ({})", expression.node),
|
||||
Stmt::If {
|
||||
condition,
|
||||
then_branch,
|
||||
elif_branch,
|
||||
else_branch,
|
||||
} => {
|
||||
let mut result = format!("IF ({}) {{\n{}\n}}", condition, then_branch);
|
||||
let mut result = format!("IF ({}) {{\n{}\n}}", condition.node, then_branch.node);
|
||||
for (condition, branch) in elif_branch {
|
||||
result.push_str(&format!(" ELIF ({}) {{\n{}\n}}", condition, branch));
|
||||
result.push_str(&format!(
|
||||
" ELIF ({}) {{\n{}\n}}",
|
||||
condition.node, branch.node
|
||||
));
|
||||
}
|
||||
if let Some(else_branch) = else_branch {
|
||||
result.push_str(&format!(" ELSE {{\n{}\n}}", else_branch));
|
||||
result.push_str(&format!(" ELSE {{\n{}\n}}", else_branch.node));
|
||||
}
|
||||
write!(f, "IfStmt {}", result)
|
||||
}
|
||||
Stmt::Print { expression } => write!(f, "Print({});", expression),
|
||||
Stmt::Stmt { expression } => write!(f, "Stmt({});", expression),
|
||||
Stmt::Var { name, initializer } => {
|
||||
write!(f, "Var({} = {:?});", name, initializer)
|
||||
}
|
||||
Stmt::Assign { name, value } => write!(f, "Assign({} = {});", name, value),
|
||||
Stmt::Return { expression } => write!(f, "Return({});", expression),
|
||||
Stmt::Print { expression } => write!(f, "Print({});", expression.node),
|
||||
Stmt::Stmt { expression } => write!(f, "Stmt({});", expression.node),
|
||||
Stmt::Var { name, initializer } => match initializer {
|
||||
Some(init) => write!(f, "Var({} = {});", name, init.node),
|
||||
None => write!(f, "Var({});", name),
|
||||
},
|
||||
Stmt::Assign { name, value } => write!(f, "Assign({} = {});", name, value.node),
|
||||
Stmt::Return { expression } => write!(f, "Return({});", expression.node),
|
||||
Stmt::Block { statements } => write!(
|
||||
f,
|
||||
"Block([\n{}\n])",
|
||||
statements
|
||||
.iter()
|
||||
.map(|stmt| format!("\t \t{}", stmt))
|
||||
.map(|stmt| format!("\t \t{}", stmt.node))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
),
|
||||
Stmt::While { condition, body } => todo!(),
|
||||
Stmt::While { condition, body } => {
|
||||
write!(f, "While({}) {{\n{}\n}}", condition.node, body.node)
|
||||
}
|
||||
Stmt::For {
|
||||
variable,
|
||||
iterable,
|
||||
body,
|
||||
} => todo!(),
|
||||
} => write!(
|
||||
f,
|
||||
"For({} in {}) {{\n{}\n}}",
|
||||
variable.node, iterable.node, body.node
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,44 +215,55 @@ impl Display for Stmt {
|
||||
impl Debug for Stmt {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Stmt::Expression { expression } => write!(f, " Expression ({:?})", expression),
|
||||
Stmt::Expression { expression } => write!(f, " Expression ({:?})", expression.node),
|
||||
Stmt::If {
|
||||
condition,
|
||||
then_branch,
|
||||
elif_branch,
|
||||
else_branch,
|
||||
} => {
|
||||
let mut result = format!("IF ({:?}) {{\n{:?}\n}}", condition, then_branch);
|
||||
let mut result =
|
||||
format!("IF ({:?}) {{\n{:?}\n}}", condition.node, then_branch.node);
|
||||
for (condition, branch) in elif_branch {
|
||||
result.push_str(&format!(" ELIF ({:?}) {{\n{:?}\n}}", condition, branch));
|
||||
result.push_str(&format!(
|
||||
" ELIF ({:?}) {{\n{:?}\n}}",
|
||||
condition.node, branch.node
|
||||
));
|
||||
}
|
||||
if let Some(else_branch) = else_branch {
|
||||
result.push_str(&format!(" ELSE {{\n{:?}\n}}", else_branch));
|
||||
result.push_str(&format!(" ELSE {{\n{:?}\n}}", else_branch.node));
|
||||
}
|
||||
write!(f, "IfStmt {:?}", result)
|
||||
}
|
||||
Stmt::Print { expression } => write!(f, "Print({:?});", expression),
|
||||
Stmt::Stmt { expression } => write!(f, "Stmt({:?});", expression),
|
||||
Stmt::Var { name, initializer } => {
|
||||
write!(f, "Var({:?} = {:?});", name, initializer)
|
||||
}
|
||||
Stmt::Assign { name, value } => write!(f, "Assign({:?} = {:?});", name, value),
|
||||
Stmt::Return { expression } => write!(f, "Return({:?});", expression),
|
||||
Stmt::Print { expression } => write!(f, "Print({:?});", expression.node),
|
||||
Stmt::Stmt { expression } => write!(f, "Stmt({:?});", expression.node),
|
||||
Stmt::Var { name, initializer } => match initializer {
|
||||
Some(init) => write!(f, "Var({:?} = {:?});", name, init.node),
|
||||
None => write!(f, "Var({:?});", name),
|
||||
},
|
||||
Stmt::Assign { name, value } => write!(f, "Assign({:?} = {:?});", name, value.node),
|
||||
Stmt::Return { expression } => write!(f, "Return({:?});", expression.node),
|
||||
Stmt::Block { statements } => write!(
|
||||
f,
|
||||
"Block([\n{:?}\n])",
|
||||
statements
|
||||
.iter()
|
||||
.map(|stmt| format!("{:?}", stmt))
|
||||
.map(|stmt| format!("{:?}", stmt.node))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\t\t\n")
|
||||
),
|
||||
Stmt::While { condition, body } => todo!(),
|
||||
Stmt::While { condition, body } => {
|
||||
write!(f, "While({:?}) {{\n{:?}\n}}", condition.node, body.node)
|
||||
}
|
||||
Stmt::For {
|
||||
variable,
|
||||
iterable,
|
||||
body,
|
||||
} => todo!(),
|
||||
} => write!(
|
||||
f,
|
||||
"For({:?} in {:?}) {{\n{:?}\n}}",
|
||||
variable.node, iterable.node, body.node
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,12 +310,12 @@ impl AstNodeKind for Stmt {
|
||||
} => "if statement",
|
||||
Stmt::Print { expression: _ } => "print statement",
|
||||
Stmt::Stmt { expression: _ } => "statement",
|
||||
Stmt::While { condition, body } => todo!(),
|
||||
Stmt::While { condition, body } => "while statement",
|
||||
Stmt::For {
|
||||
variable,
|
||||
iterable,
|
||||
body,
|
||||
} => todo!(),
|
||||
} => "for statement",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ fn get_keyword_token(word: &str) -> Option<TokenType> {
|
||||
"for" => Some(TokenType::For),
|
||||
"fun" => Some(TokenType::Fun),
|
||||
"if" => Some(TokenType::If),
|
||||
"then" => Some(TokenType::Then),
|
||||
"elif" => Some(TokenType::Elif),
|
||||
"else" => Some(TokenType::Else),
|
||||
"or" => Some(TokenType::Or),
|
||||
|
||||
+303
-154
@@ -1,9 +1,10 @@
|
||||
use crate::{
|
||||
frontend::{
|
||||
ast::{AstNode, Expr, Stmt},
|
||||
source_registry::SourceSlice,
|
||||
tokens::{LiteralValue, Token, TokenType},
|
||||
},
|
||||
logging::display_ast::{pretty_print_with_config, PrettyConfig, PrettyPrint},
|
||||
logging::display_ast::{pretty_print_with_config, PrettyConfig},
|
||||
result::{LoxError, LoxResult},
|
||||
};
|
||||
|
||||
@@ -57,193 +58,284 @@ impl Parser {
|
||||
|
||||
fn statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
||||
match (&self.peek().token_type, &self.peek_next().token_type) {
|
||||
(TokenType::Var, _) => Ok(AstNode::new(
|
||||
self.var_statement()?,
|
||||
self.peek().source_slice.clone(),
|
||||
)),
|
||||
(TokenType::Print, _) => Ok(AstNode::new(
|
||||
self.print_statement()?,
|
||||
self.peek().source_slice.clone(),
|
||||
)),
|
||||
(TokenType::Return, _) => Ok(AstNode::new(
|
||||
self.return_statement()?,
|
||||
self.peek().source_slice.clone(),
|
||||
)),
|
||||
(TokenType::StartBlock, _) => Ok(AstNode::new(
|
||||
self.block_statement()?,
|
||||
self.peek().source_slice.clone(),
|
||||
)),
|
||||
(TokenType::Identifier, TokenType::Equal) => Ok(AstNode::new(
|
||||
self.assignment_statement()?,
|
||||
self.peek().source_slice.clone(),
|
||||
)),
|
||||
(TokenType::If, _) => Ok(AstNode::new(
|
||||
self.if_statement()?,
|
||||
self.peek().source_slice.clone(),
|
||||
)),
|
||||
(TokenType::While, _) => Ok(AstNode::new(
|
||||
self.while_statement()?,
|
||||
self.peek().source_slice.clone(),
|
||||
)),
|
||||
_ => Ok(AstNode::new(
|
||||
self.expression_statement()?,
|
||||
self.peek().source_slice.clone(),
|
||||
)),
|
||||
(TokenType::Var, _) => self.var_statement(),
|
||||
(TokenType::Print, _) => self.print_statement(),
|
||||
(TokenType::Return, _) => self.return_statement(),
|
||||
(TokenType::StartBlock, _) => self.block_statement(),
|
||||
(TokenType::Identifier, TokenType::Equal) => self.assignment_statement(),
|
||||
(TokenType::If, _) => self.if_statement(),
|
||||
(TokenType::While, _) => self.while_statement(),
|
||||
_ => self.expression_statement(),
|
||||
}
|
||||
}
|
||||
|
||||
fn while_statement(&mut self) -> LoxResult<Stmt> {
|
||||
fn while_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
||||
let start_slice = self.peek().source_slice.clone();
|
||||
self.advance();
|
||||
let condition = self.expression()?;
|
||||
let body = self.block_statement()?;
|
||||
Ok(Stmt::While {
|
||||
condition: Box::new(condition),
|
||||
body: Box::new(body),
|
||||
})
|
||||
let body = self.statement()?;
|
||||
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),
|
||||
},
|
||||
combined_slice,
|
||||
))
|
||||
}
|
||||
|
||||
fn if_statement(&mut self) -> LoxResult<Stmt> {
|
||||
self.advance();
|
||||
fn if_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
||||
let start_slice = self.peek().source_slice.clone();
|
||||
self.advance(); // consume 'if'
|
||||
let condition = self.expression()?;
|
||||
self.consume(TokenType::Then, "Expect 'then' after if condition.")?;
|
||||
let then_branch = self.statement()?;
|
||||
let mut elif_branches = Vec::new();
|
||||
let mut last_slice = then_branch.source_slice.clone();
|
||||
|
||||
while self.peek().token_type == TokenType::Elif {
|
||||
self.advance();
|
||||
let condition = self.expression()?;
|
||||
let then_branch = self.statement()?;
|
||||
elif_branches.push((Box::new(condition), Box::new(then_branch.node)));
|
||||
self.advance(); // consume 'elif'
|
||||
let elif_condition = self.expression()?;
|
||||
self.consume(TokenType::Then, "Expect 'then' after elif condition.")?;
|
||||
let elif_branch = self.statement()?;
|
||||
last_slice = elif_branch.source_slice.clone();
|
||||
elif_branches.push((Box::new(elif_condition), Box::new(elif_branch)));
|
||||
}
|
||||
|
||||
let else_branch = if self.peek().token_type == TokenType::Else {
|
||||
self.advance();
|
||||
Some(Box::new(self.statement()?.node))
|
||||
self.advance(); // consume 'else'
|
||||
let else_stmt = self.statement()?;
|
||||
last_slice = else_stmt.source_slice.clone();
|
||||
Some(Box::new(else_stmt))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Stmt::If {
|
||||
condition: Box::new(condition),
|
||||
then_branch: Box::new(then_branch.node),
|
||||
elif_branch: elif_branches,
|
||||
else_branch: else_branch,
|
||||
})
|
||||
|
||||
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,
|
||||
},
|
||||
combined_slice,
|
||||
))
|
||||
}
|
||||
|
||||
fn var_statement(&mut self) -> LoxResult<Stmt> {
|
||||
fn var_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
||||
let start_slice = self.peek().source_slice.clone();
|
||||
self.advance();
|
||||
let name = self.consume(TokenType::Identifier, "Expect variable name.")?;
|
||||
let name_lexeme = name.lexeme.clone();
|
||||
self.consume(TokenType::Equal, "Expect '=' after variable name.")?;
|
||||
let value = self.expression()?;
|
||||
self.consume(
|
||||
let semicolon = self.consume(
|
||||
TokenType::Semicolon,
|
||||
"Expect ';' after variable declaration.",
|
||||
)?;
|
||||
Ok(Stmt::Var {
|
||||
name: name_lexeme,
|
||||
initializer: Some(Box::new(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::Var {
|
||||
name: name_lexeme,
|
||||
initializer: Some(Box::new(value)),
|
||||
},
|
||||
combined_slice,
|
||||
))
|
||||
}
|
||||
|
||||
fn assignment_statement(&mut self) -> LoxResult<Stmt> {
|
||||
fn assignment_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
||||
let start_slice = self.peek().source_slice.clone();
|
||||
let name = self.consume(TokenType::Identifier, "Expect variable name.")?;
|
||||
let name_lexeme = name.lexeme.clone();
|
||||
self.consume(TokenType::Equal, "Expect '=' after variable name.")?;
|
||||
let value = self.expression()?;
|
||||
self.consume(
|
||||
let semicolon = self.consume(
|
||||
TokenType::Semicolon,
|
||||
"Expect ';' after variable declaration.",
|
||||
)?;
|
||||
Ok(Stmt::Assign {
|
||||
name: name_lexeme,
|
||||
value: Box::new(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::Assign {
|
||||
name: name_lexeme,
|
||||
value: Box::new(value),
|
||||
},
|
||||
combined_slice,
|
||||
))
|
||||
}
|
||||
|
||||
fn print_statement(&mut self) -> LoxResult<Stmt> {
|
||||
fn print_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
||||
// consume the print keyword
|
||||
let start_slice = self.peek().source_slice.clone();
|
||||
self.advance();
|
||||
let expr = self.expression()?;
|
||||
self.consume(TokenType::Semicolon, "Expect ';' after value.")?;
|
||||
Ok(Stmt::Print {
|
||||
expression: Box::new(expr),
|
||||
})
|
||||
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),
|
||||
},
|
||||
combined_slice,
|
||||
))
|
||||
}
|
||||
|
||||
fn block_statement(&mut self) -> LoxResult<Stmt> {
|
||||
fn block_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
||||
let start_slice = self.peek().source_slice.clone();
|
||||
self.advance();
|
||||
let mut statements = Vec::new();
|
||||
while self.peek().token_type != TokenType::EndBlock && !self.is_at_end() {
|
||||
let stmt = self.statement()?;
|
||||
statements.push(stmt.node.clone());
|
||||
if let Stmt::Expression { .. } = stmt.node {
|
||||
let should_break = matches!(stmt.node, Stmt::Expression { .. });
|
||||
statements.push(stmt);
|
||||
if should_break {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.consume(TokenType::EndBlock, "Expect 'end' after block.")?;
|
||||
Ok(Stmt::Block {
|
||||
statements: Box::new(statements),
|
||||
})
|
||||
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,
|
||||
);
|
||||
Ok(AstNode::new(
|
||||
Stmt::Block {
|
||||
statements: Box::new(statements),
|
||||
},
|
||||
combined_slice,
|
||||
))
|
||||
}
|
||||
|
||||
fn expression_statement(&mut self) -> LoxResult<Stmt> {
|
||||
fn expression_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
||||
let start_slice = self.peek().source_slice.clone();
|
||||
let expr = self.expression()?;
|
||||
if self.peek().token_type == TokenType::Semicolon {
|
||||
self.advance();
|
||||
return Ok(Stmt::Stmt {
|
||||
expression: Box::new(expr),
|
||||
});
|
||||
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(
|
||||
Stmt::Stmt {
|
||||
expression: Box::new(expr),
|
||||
},
|
||||
combined_slice,
|
||||
));
|
||||
}
|
||||
Ok(Stmt::Expression {
|
||||
expression: Box::new(expr),
|
||||
})
|
||||
// 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),
|
||||
},
|
||||
expr_slice,
|
||||
))
|
||||
}
|
||||
|
||||
fn return_statement(&mut self) -> LoxResult<Stmt> {
|
||||
fn return_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
||||
let start_slice = self.peek().source_slice.clone();
|
||||
self.advance();
|
||||
let expr = self.expression()?;
|
||||
self.consume(TokenType::Semicolon, "Expect ';' after return value.")?;
|
||||
Ok(Stmt::Return {
|
||||
expression: Box::new(expr),
|
||||
})
|
||||
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,
|
||||
);
|
||||
Ok(AstNode::new(
|
||||
Stmt::Return {
|
||||
expression: Box::new(expr),
|
||||
},
|
||||
combined_slice,
|
||||
))
|
||||
}
|
||||
|
||||
fn expression(&mut self) -> LoxResult<Expr> {
|
||||
fn expression(&mut self) -> LoxResult<AstNode<Expr>> {
|
||||
self.or_and()
|
||||
}
|
||||
|
||||
fn or_and(&mut self) -> LoxResult<Expr> {
|
||||
fn or_and(&mut self) -> LoxResult<AstNode<Expr>> {
|
||||
let mut expr = self.equality()?;
|
||||
|
||||
while [TokenType::Or, TokenType::And].contains(&self.peek().token_type) {
|
||||
let start_slice = expr.source_slice.clone();
|
||||
let operator = self.peek().token_type.clone();
|
||||
self.advance();
|
||||
let right = self.equality()?;
|
||||
expr = Expr::Binary {
|
||||
left: Box::new(expr),
|
||||
operator,
|
||||
right: Box::new(right),
|
||||
};
|
||||
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 equality(&mut self) -> LoxResult<Expr> {
|
||||
fn equality(&mut self) -> LoxResult<AstNode<Expr>> {
|
||||
let mut expr = self.comparison()?;
|
||||
|
||||
while [TokenType::BangEqual, TokenType::EqualEqual].contains(&self.peek().token_type) {
|
||||
let start_slice = expr.source_slice.clone();
|
||||
let operator = self.peek().token_type.clone();
|
||||
self.advance();
|
||||
let right = self.comparison()?;
|
||||
expr = Expr::Binary {
|
||||
left: Box::new(expr),
|
||||
operator,
|
||||
right: Box::new(right),
|
||||
};
|
||||
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 comparison(&mut self) -> LoxResult<Expr> {
|
||||
fn comparison(&mut self) -> LoxResult<AstNode<Expr>> {
|
||||
let mut expr = self.term()?;
|
||||
|
||||
while [
|
||||
@@ -254,121 +346,174 @@ impl Parser {
|
||||
]
|
||||
.contains(&self.peek().token_type)
|
||||
{
|
||||
let start_slice = expr.source_slice.clone();
|
||||
let operator = self.peek().token_type.clone();
|
||||
self.advance();
|
||||
let right = self.term()?;
|
||||
expr = Expr::Binary {
|
||||
left: Box::new(expr),
|
||||
operator,
|
||||
right: Box::new(right),
|
||||
};
|
||||
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 term(&mut self) -> LoxResult<Expr> {
|
||||
fn term(&mut self) -> LoxResult<AstNode<Expr>> {
|
||||
let mut expr = self.factor()?;
|
||||
|
||||
while [TokenType::Plus, TokenType::Minus].contains(&self.peek().token_type) {
|
||||
let start_slice = expr.source_slice.clone();
|
||||
let operator = self.peek().token_type.clone();
|
||||
self.advance();
|
||||
let right = self.factor()?;
|
||||
expr = Expr::Binary {
|
||||
left: Box::new(expr),
|
||||
operator,
|
||||
right: Box::new(right),
|
||||
};
|
||||
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 factor(&mut self) -> LoxResult<Expr> {
|
||||
fn factor(&mut self) -> LoxResult<AstNode<Expr>> {
|
||||
let mut expr = self.unary()?;
|
||||
|
||||
while [TokenType::Slash, TokenType::Star].contains(&self.peek().token_type) {
|
||||
let start_slice = expr.source_slice.clone();
|
||||
let operator = self.peek().token_type.clone();
|
||||
self.advance();
|
||||
let right = self.unary()?;
|
||||
expr = Expr::Binary {
|
||||
left: Box::new(expr),
|
||||
operator,
|
||||
right: Box::new(right),
|
||||
};
|
||||
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 unary(&mut self) -> LoxResult<Expr> {
|
||||
fn unary(&mut self) -> LoxResult<AstNode<Expr>> {
|
||||
if [TokenType::Bang, TokenType::Minus].contains(&self.peek().token_type) {
|
||||
let operator = self.peek().token_type.clone();
|
||||
let source_slice = self.peek().source_slice.clone();
|
||||
self.advance();
|
||||
let right = self.unary()?;
|
||||
return Ok(Expr::Unary {
|
||||
operator,
|
||||
operand: Box::new(right),
|
||||
});
|
||||
return Ok(AstNode::new(
|
||||
Expr::Unary {
|
||||
operator,
|
||||
operand: Box::new(right),
|
||||
},
|
||||
source_slice,
|
||||
));
|
||||
}
|
||||
|
||||
self.primary()
|
||||
}
|
||||
|
||||
fn primary(&mut self) -> LoxResult<Expr> {
|
||||
fn primary(&mut self) -> LoxResult<AstNode<Expr>> {
|
||||
match self.peek().token_type {
|
||||
TokenType::False => {
|
||||
let source_slice = self.peek().source_slice.clone();
|
||||
self.advance();
|
||||
Ok(Expr::Literal {
|
||||
value: LiteralValue::Boolean(false),
|
||||
})
|
||||
Ok(AstNode::new(
|
||||
Expr::Literal {
|
||||
value: LiteralValue::Boolean(false),
|
||||
},
|
||||
source_slice,
|
||||
))
|
||||
}
|
||||
TokenType::True => {
|
||||
let source_slice = self.peek().source_slice.clone();
|
||||
self.advance();
|
||||
Ok(Expr::Literal {
|
||||
value: LiteralValue::Boolean(true),
|
||||
})
|
||||
Ok(AstNode::new(
|
||||
Expr::Literal {
|
||||
value: LiteralValue::Boolean(true),
|
||||
},
|
||||
source_slice,
|
||||
))
|
||||
}
|
||||
TokenType::Nil => {
|
||||
let source_slice = self.peek().source_slice.clone();
|
||||
self.advance();
|
||||
Ok(Expr::Literal {
|
||||
value: LiteralValue::Nil,
|
||||
})
|
||||
Ok(AstNode::new(
|
||||
Expr::Literal {
|
||||
value: LiteralValue::Nil,
|
||||
},
|
||||
source_slice,
|
||||
))
|
||||
}
|
||||
TokenType::Number => {
|
||||
let source_slice = self.peek().source_slice.clone();
|
||||
let literal = self.peek().literal.clone();
|
||||
self.advance();
|
||||
let token = self.previous();
|
||||
if let Some(literal) = &token.literal {
|
||||
Ok(Expr::Literal {
|
||||
value: literal.clone(),
|
||||
})
|
||||
if let Some(literal) = literal {
|
||||
Ok(AstNode::new(Expr::Literal { value: literal }, source_slice))
|
||||
} else {
|
||||
Err(self.error(token, "Expected number literal").into())
|
||||
Err(self
|
||||
.error(self.previous(), "Expected number literal")
|
||||
.into())
|
||||
}
|
||||
}
|
||||
TokenType::String => {
|
||||
let source_slice = self.peek().source_slice.clone();
|
||||
let literal = self.peek().literal.clone();
|
||||
self.advance();
|
||||
let token = self.previous();
|
||||
if let Some(literal) = &token.literal {
|
||||
Ok(Expr::Literal {
|
||||
value: literal.clone(),
|
||||
})
|
||||
if let Some(literal) = literal {
|
||||
Ok(AstNode::new(Expr::Literal { value: literal }, source_slice))
|
||||
} else {
|
||||
Err(self.error(token, "Expected string literal").into())
|
||||
Err(self
|
||||
.error(self.previous(), "Expected string literal")
|
||||
.into())
|
||||
}
|
||||
}
|
||||
TokenType::LeftParen => {
|
||||
let source_slice = self.peek().source_slice.clone();
|
||||
self.advance();
|
||||
let expr = self.expression()?;
|
||||
self.consume(TokenType::RightParen, "Expect ')' after expression.")?;
|
||||
Ok(Expr::Grouping {
|
||||
expression: Box::new(expr),
|
||||
})
|
||||
Ok(AstNode::new(
|
||||
Expr::Grouping {
|
||||
expression: Box::new(expr),
|
||||
},
|
||||
source_slice,
|
||||
))
|
||||
}
|
||||
TokenType::Identifier => {
|
||||
let name = self.peek().lexeme.clone();
|
||||
let source_slice = self.peek().source_slice.clone();
|
||||
self.advance();
|
||||
Ok(Expr::Variable { name: name })
|
||||
Ok(AstNode::new(Expr::Variable { name }, source_slice))
|
||||
}
|
||||
_ => Err(self.error(self.peek(), "Expect expression.").into()),
|
||||
}
|
||||
@@ -386,7 +531,7 @@ impl Parser {
|
||||
if !self.is_at_end() {
|
||||
self.current += 1;
|
||||
}
|
||||
self.peek()
|
||||
self.previous()
|
||||
}
|
||||
|
||||
fn peek(&self) -> &Token {
|
||||
@@ -406,7 +551,11 @@ impl Parser {
|
||||
}
|
||||
|
||||
fn previous(&self) -> &Token {
|
||||
&self.tokens[self.current - 1]
|
||||
if self.current == 0 {
|
||||
&self.tokens[0]
|
||||
} else {
|
||||
&self.tokens[self.current - 1]
|
||||
}
|
||||
}
|
||||
|
||||
fn consume(&mut self, token_type: TokenType, message: &str) -> LoxResult<&Token> {
|
||||
|
||||
@@ -67,6 +67,7 @@ pub enum TokenType {
|
||||
For,
|
||||
While,
|
||||
If,
|
||||
Then,
|
||||
Elif,
|
||||
Else,
|
||||
Nil,
|
||||
@@ -103,6 +104,7 @@ impl fmt::Display for TokenType {
|
||||
TokenType::Fun => write!(f, "fun"),
|
||||
TokenType::For => write!(f, "for"),
|
||||
TokenType::If => write!(f, "if"),
|
||||
TokenType::Then => write!(f, "then"),
|
||||
TokenType::Nil => write!(f, "nil"),
|
||||
TokenType::Or => write!(f, "or"),
|
||||
TokenType::Print => write!(f, "print"),
|
||||
|
||||
Reference in New Issue
Block a user