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:
+98
-28
@@ -19,6 +19,13 @@ fn error(message: String) -> LoxError {
|
||||
}
|
||||
}
|
||||
|
||||
fn error_at(source_slice: SourceSlice, message: String) -> LoxError {
|
||||
LoxError::RuntimeError {
|
||||
source_slice,
|
||||
message,
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for LiteralValue {
|
||||
type Output = LiteralValue;
|
||||
|
||||
@@ -68,6 +75,25 @@ impl Add for LiteralValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl LiteralValue {
|
||||
pub fn add_with_source(
|
||||
self,
|
||||
other: LiteralValue,
|
||||
source_slice: SourceSlice,
|
||||
) -> LoxResult<LiteralValue> {
|
||||
match (self, other) {
|
||||
(LiteralValue::Number(a), LiteralValue::Number(b)) => Ok(LiteralValue::Number(a + b)),
|
||||
(LiteralValue::String(a), LiteralValue::String(b)) => {
|
||||
Ok(LiteralValue::String(format!("{}{}", a, b)))
|
||||
}
|
||||
_ => Err(error_at(
|
||||
source_slice,
|
||||
"Cannot add non-numeric values".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for LiteralValue {
|
||||
type Output = LoxResult<LiteralValue>;
|
||||
|
||||
@@ -154,15 +180,16 @@ impl<'a> Interpreter<'a> {
|
||||
|
||||
fn interpret_binary(
|
||||
&mut self,
|
||||
left: Expr,
|
||||
left: AstNode<Expr>,
|
||||
operator: TokenType,
|
||||
right: Expr,
|
||||
right: AstNode<Expr>,
|
||||
source_slice: SourceSlice,
|
||||
) -> LoxResult<LiteralValue> {
|
||||
let left_value = self.interpret(left)?;
|
||||
let right_value = self.interpret(right)?;
|
||||
match operator {
|
||||
TokenType::Minus => left_value - right_value,
|
||||
TokenType::Plus => left_value + right_value,
|
||||
TokenType::Plus => left_value.add_with_source(right_value, source_slice.clone()),
|
||||
TokenType::Slash => left_value / right_value,
|
||||
TokenType::Star => left_value * right_value,
|
||||
TokenType::EqualEqual => Ok(LiteralValue::Boolean(left_value == right_value)),
|
||||
@@ -207,31 +234,76 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// Direct Expr evaluation to avoid infinite recursion
|
||||
impl<'a> EvaluateInterpreter<Expr> for Interpreter<'a> {
|
||||
fn interpret(&mut self, stmt: Expr) -> LoxResult<LiteralValue> {
|
||||
match stmt {
|
||||
Expr::Literal { value } => Ok(value.clone()),
|
||||
fn interpret(&mut self, expr: Expr) -> LoxResult<LiteralValue> {
|
||||
match expr {
|
||||
Expr::Literal { value } => Ok(value),
|
||||
Expr::Variable { name } => self.enviorment.get(&name),
|
||||
Expr::Binary {
|
||||
left,
|
||||
operator,
|
||||
right,
|
||||
} => self.interpret_binary(*left, operator, *right),
|
||||
} => {
|
||||
let left_val = self.interpret(*left)?;
|
||||
let right_val = self.interpret(*right)?;
|
||||
self.evaluate_binary(left_val, operator, right_val)
|
||||
}
|
||||
Expr::Unary { operator, operand } => {
|
||||
let right = self.interpret(*operand)?;
|
||||
match operator {
|
||||
TokenType::Minus => Ok((-right)?),
|
||||
TokenType::Bang => Ok(!right),
|
||||
_ => Err(error("Unsupported unary operator".to_string())),
|
||||
}
|
||||
let operand_val = self.interpret(*operand)?;
|
||||
self.evaluate_unary(operator, operand_val)
|
||||
}
|
||||
Expr::Grouping { expression } => self.interpret(*expression),
|
||||
Expr::Variable { name } => self.enviorment.get(&name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> EvaluateInterpreter<Stmt> for Interpreter<'a> {
|
||||
fn interpret(&mut self, stmt: Stmt) -> LoxResult<LiteralValue> {
|
||||
impl<'a> EvaluateInterpreter<AstNode<Stmt>> for Interpreter<'a> {
|
||||
fn interpret(&mut self, node: AstNode<Stmt>) -> LoxResult<LiteralValue> {
|
||||
let stmt = node.node;
|
||||
let _source_slice = node.source_slice;
|
||||
self.interpret_stmt_inner(stmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Interpreter<'a> {
|
||||
fn evaluate_binary(
|
||||
&mut self,
|
||||
left: LiteralValue,
|
||||
operator: TokenType,
|
||||
right: LiteralValue,
|
||||
) -> LoxResult<LiteralValue> {
|
||||
match operator {
|
||||
TokenType::Plus => left + right,
|
||||
TokenType::Minus => left - right,
|
||||
TokenType::Star => left * right,
|
||||
TokenType::Slash => left / right,
|
||||
TokenType::Greater => Ok(LiteralValue::Boolean(left > right)),
|
||||
TokenType::GreaterEqual => Ok(LiteralValue::Boolean(left >= right)),
|
||||
TokenType::Less => Ok(LiteralValue::Boolean(left < right)),
|
||||
TokenType::LessEqual => Ok(LiteralValue::Boolean(left <= right)),
|
||||
TokenType::EqualEqual => Ok(LiteralValue::Boolean(left == right)),
|
||||
TokenType::BangEqual => Ok(LiteralValue::Boolean(left != right)),
|
||||
_ => Err(error(format!(
|
||||
"Unsupported binary operator: {:?}",
|
||||
operator
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate_unary(
|
||||
&mut self,
|
||||
operator: TokenType,
|
||||
operand: LiteralValue,
|
||||
) -> LoxResult<LiteralValue> {
|
||||
match operator {
|
||||
TokenType::Minus => -operand,
|
||||
TokenType::Bang => Ok(!operand),
|
||||
_ => Err(error(format!("Unsupported unary operator: {:?}", operator))),
|
||||
}
|
||||
}
|
||||
|
||||
fn interpret_stmt_inner(&mut self, stmt: Stmt) -> LoxResult<LiteralValue> {
|
||||
match stmt {
|
||||
Stmt::Expression { expression } => self.interpret(*expression),
|
||||
Stmt::Print { expression } => {
|
||||
@@ -241,11 +313,12 @@ impl<'a> EvaluateInterpreter<Stmt> for Interpreter<'a> {
|
||||
}
|
||||
Stmt::Block { statements } => {
|
||||
let (elements, final_expr) = match statements.split_last() {
|
||||
Some((Stmt::Expression { expression }, body)) => (body, Some(expression)),
|
||||
Some((Stmt::Return { expression }, body)) => (body, Some(expression)),
|
||||
Some((_last_stmt, _body)) => {
|
||||
(statements.as_slice(), None) // Non è un'Expression finale
|
||||
}
|
||||
Some((stmt, body)) => match &stmt.node {
|
||||
Stmt::Expression { expression } => (body, Some(expression)),
|
||||
Stmt::Return { expression } => (body, Some(expression)),
|
||||
_ => (statements.as_slice(), None),
|
||||
},
|
||||
|
||||
None => {
|
||||
(&[][..], None) // Blocco vuoto
|
||||
}
|
||||
@@ -258,14 +331,11 @@ impl<'a> EvaluateInterpreter<Stmt> for Interpreter<'a> {
|
||||
|
||||
// Gestisci l'espressione finale se presente
|
||||
match final_expr {
|
||||
Some(expr) => self.interpret((**expr).clone()),
|
||||
Some(expr) => self.interpret(*expr.clone()),
|
||||
None => Ok(LiteralValue::Nil),
|
||||
}
|
||||
}
|
||||
Stmt::Stmt { expression } => {
|
||||
let _ = self.interpret(*expression);
|
||||
Ok(LiteralValue::Nil)
|
||||
}
|
||||
Stmt::Stmt { expression } => self.interpret(*expression.clone()),
|
||||
Stmt::Return { expression } => self.interpret(*expression),
|
||||
Stmt::Var { name, initializer } => {
|
||||
let value = if let Some(expr) = initializer {
|
||||
@@ -277,8 +347,8 @@ impl<'a> EvaluateInterpreter<Stmt> for Interpreter<'a> {
|
||||
return Ok(LiteralValue::Nil);
|
||||
}
|
||||
Stmt::Assign { name, value } => {
|
||||
let value = self.interpret(*value)?;
|
||||
self.enviorment.set(name.clone(), value);
|
||||
let result = self.interpret(*value)?;
|
||||
self.enviorment.set(name.clone(), result);
|
||||
Ok(LiteralValue::Nil)
|
||||
}
|
||||
Stmt::If {
|
||||
|
||||
Reference in New Issue
Block a user