Refactor environment to use stack-based scoping

The commit rewrites the environment to use a stack of hash maps for
managing variable scopes. This replaces the old parent-reference
approach with a simpler and more efficient stack-based model.

Key changes: - Rename Environment to EnvironmentStack - Store scopes in
a Vec of HashMaps - Add push/pop scope operations for block handling -
Update interpreter to properly manage scope lifetimes - Clean up error
handling with helper functions
This commit is contained in:
Giulio Agostini
2025-10-04 21:05:00 +02:00
parent 41253e932a
commit 827349cbad
11 changed files with 165 additions and 243 deletions
+50 -80
View File
@@ -1,5 +1,5 @@
use crate::{
backend::environment::Environment,
backend::environment::EnvironmentStack,
frontend::{
ast::{AstNode, AstNodeKind, Expr, Stmt},
source_registry::SourceSlice,
@@ -8,7 +8,7 @@ use crate::{
result::{LoxError, LoxResult},
};
use std::{
fmt::{format, Debug, Display},
fmt::{Debug, Display},
ops::{Add, Div, Mul, Neg, Not, Rem, Sub},
};
@@ -169,59 +169,17 @@ impl From<LiteralValue> for bool {
}
}
pub struct Interpreter<'a> {
enviorment: &'a mut Environment<'a>,
}
impl<'a> Interpreter<'a> {
pub fn new(env: &'a mut Environment<'a>) -> Self {
Self { enviorment: env }
}
fn interpret_binary(
&mut self,
left: AstNode<Expr>,
operator: TokenType,
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.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)),
TokenType::BangEqual => Ok(LiteralValue::Boolean(left_value != right_value)),
TokenType::Greater => Ok(LiteralValue::Boolean(left_value > right_value)),
TokenType::GreaterEqual => Ok(LiteralValue::Boolean(left_value >= right_value)),
TokenType::Less => Ok(LiteralValue::Boolean(left_value < right_value)),
TokenType::LessEqual => Ok(LiteralValue::Boolean(left_value <= right_value)),
TokenType::Percent => left_value % right_value,
TokenType::And => Ok(LiteralValue::Boolean(
left_value.is_truthy() && right_value.is_truthy(),
)),
TokenType::Or => Ok(LiteralValue::Boolean(
left_value.is_truthy() || right_value.is_truthy(),
)),
_ => Err(LoxError::RuntimeError {
source_slice: SourceSlice::default(), // todo change this to the actual source slice
message: format!("Unsupported binary operator {}", operator),
}),
}
}
pub struct Interpreter {
enviorment: EnvironmentStack,
}
pub trait EvaluateInterpreter<T> {
fn interpret(&mut self, stmt: T) -> LoxResult<LiteralValue>;
}
impl<'a, R: AstNodeKind + Clone + Debug + Display> EvaluateInterpreter<AstNode<R>>
for Interpreter<'a>
impl<'a, R: AstNodeKind + Clone + Debug + Display> EvaluateInterpreter<AstNode<R>> for Interpreter
where
Interpreter<'a>: EvaluateInterpreter<R>,
Interpreter: EvaluateInterpreter<R>,
{
fn interpret(&mut self, stmt: AstNode<R>) -> LoxResult<LiteralValue> {
match self.interpret(stmt.node.clone()) {
@@ -235,7 +193,7 @@ where
}
// Direct Expr evaluation to avoid infinite recursion
impl<'a> EvaluateInterpreter<Expr> for Interpreter<'a> {
impl EvaluateInterpreter<Expr> for Interpreter {
fn interpret(&mut self, expr: Expr) -> LoxResult<LiteralValue> {
match expr {
Expr::Literal { value } => Ok(value),
@@ -258,7 +216,7 @@ impl<'a> EvaluateInterpreter<Expr> for Interpreter<'a> {
}
}
impl<'a> EvaluateInterpreter<AstNode<Stmt>> for Interpreter<'a> {
impl EvaluateInterpreter<AstNode<Stmt>> for Interpreter {
fn interpret(&mut self, node: AstNode<Stmt>) -> LoxResult<LiteralValue> {
let stmt = node.node;
let _source_slice = node.source_slice;
@@ -266,7 +224,13 @@ impl<'a> EvaluateInterpreter<AstNode<Stmt>> for Interpreter<'a> {
}
}
impl<'a> Interpreter<'a> {
impl Interpreter {
pub fn new() -> Self {
Self {
enviorment: EnvironmentStack::new(),
}
}
fn evaluate_binary(
&mut self,
left: LiteralValue,
@@ -311,30 +275,7 @@ impl<'a> Interpreter<'a> {
println!("print interpreter: \t{}", value);
Ok(LiteralValue::Nil)
}
Stmt::Block { statements } => {
let (elements, final_expr) = match statements.split_last() {
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
}
};
// Ora elements è sempre disponibile
for statement in elements.iter() {
self.interpret((*statement).clone())?;
}
// Gestisci l'espressione finale se presente
match final_expr {
Some(expr) => self.interpret(*expr.clone()),
None => Ok(LiteralValue::Nil),
}
}
Stmt::Block { statements } => self.evaluate_block(*statements),
Stmt::Stmt { expression } => self.interpret(*expression.clone()),
Stmt::Return { expression } => self.interpret(*expression),
Stmt::Var { name, initializer } => {
@@ -396,11 +337,40 @@ impl<'a> Interpreter<'a> {
}
Ok(LiteralValue::Nil)
}
Stmt::For {
variable,
iterable,
body,
} => todo!(),
Stmt::For { .. } => todo!(),
}
}
fn evaluate_block(&mut self, statements: Vec<AstNode<Stmt>>) -> LoxResult<LiteralValue> {
self.enviorment.push_new_scope();
let (elements, final_expr) = match statements.split_last() {
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
}
};
// Ora elements è sempre disponibile
for statement in elements.iter() {
self.interpret((*statement).clone())?;
}
// Gestisci l'espressione finale se presente
match final_expr {
Some(expr) => {
let res = self.interpret(*expr.clone());
self.enviorment.pop_scope();
res
}
None => {
self.enviorment.pop_scope();
Ok(LiteralValue::Nil)
}
}
}
}