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
+24 -23
View File
@@ -2,43 +2,44 @@ use std::collections::HashMap;
use crate::{
frontend::{source_registry::SourceSlice, tokens::LiteralValue},
result::{LoxError, LoxResult},
result::{runtime_error, LoxResult},
};
pub struct Environment<'a> {
values: HashMap<String, LiteralValue>,
parent: Option<&'a Environment<'a>>,
#[derive(Debug, Clone)]
pub struct EnvironmentStack {
stack: Vec<HashMap<String, LiteralValue>>,
}
impl<'a> Environment<'a> {
impl EnvironmentStack {
pub fn new() -> Self {
Environment {
values: HashMap::new(),
parent: None,
EnvironmentStack {
stack: vec![HashMap::new()],
}
}
pub fn new_with_parent(parent: &'a Environment<'a>) -> Self {
Environment {
values: HashMap::new(),
parent: Some(parent),
}
pub fn push_new_scope(&mut self) {
self.stack.push(HashMap::new());
}
pub fn pop_scope(&mut self) {
self.stack.pop();
}
pub fn get(&self, name: &str) -> LoxResult<LiteralValue> {
match self.values.get(name) {
Some(value) => Ok(value.clone()),
None => match self.parent {
Some(parent) => parent.get(name),
None => Err(LoxError::RuntimeError {
source_slice: SourceSlice::default(), // todo change this to the actual source slice
message: format!("Undefined variable '{}'", name),
}),
},
let size = self.stack.len();
for i in (0..size).rev() {
if let Some(value) = self.stack[i].get(name) {
return Ok(value.clone());
}
}
runtime_error(
SourceSlice::default(), // todo change this to the actual source slice
format!("Undefined variable '{}'", name),
)
}
pub fn set(&mut self, name: String, value: LiteralValue) {
self.values.insert(name, value);
self.stack.last_mut().unwrap().insert(name, value);
}
}