crafting-interpreters/rust/rox/src/environment.rs

68 lines
2.3 KiB
Rust
Raw Normal View History

2025-02-12 13:10:07 +01:00
use std::{cell::RefCell, collections::HashMap, rc::Rc};
2025-02-12 10:30:51 +01:00
use thiserror::Error;
use crate::{token::Token, value::Value};
#[derive(Error, Debug)]
pub enum EnvironmentError {
#[error("line {0}: undefined variable: {1}")]
UndefinedVariable(usize, String),
}
/// Environment mapping variable names to their respective values. Can have an optional enclosing
/// environment. The outermost (global) environment has no enclosing one.
#[derive(Default, Debug, Clone)]
pub struct Environment {
values: HashMap<String, Value>,
2025-02-12 13:10:07 +01:00
enclosing: Option<Rc<RefCell<Environment>>>,
2025-02-12 10:30:51 +01:00
}
impl Environment {
2025-02-12 13:10:07 +01:00
/// Initialize a new environment with an enclosing one.
pub fn with_enclosing(enclosing: Rc<RefCell<Environment>>) -> Self {
2025-02-12 10:30:51 +01:00
Self {
values: HashMap::default(),
2025-02-12 13:10:07 +01:00
enclosing: Some(enclosing),
2025-02-12 10:30:51 +01:00
}
}
/// Define a new variable binding with a value.
///
/// Overwrite any binding of the same name.
pub fn define(&mut self, name: String, value: Value) {
self.values.insert(name, value);
}
/// Assign a new value to an existing (defined) variable. Error if there is no such variable in
/// this environment or any of the enclosing ones.
pub fn assign(&mut self, token: &Token, value: Value) -> Result<(), EnvironmentError> {
if self.values.contains_key(token.lexeme.as_str()) {
self.values.insert(token.lexeme.clone(), value);
Ok(())
} else if let Some(enclosing) = &mut self.enclosing {
2025-02-12 13:10:07 +01:00
enclosing.borrow_mut().assign(token, value)
2025-02-12 10:30:51 +01:00
} else {
Err(EnvironmentError::UndefinedVariable(
token.line,
token.lexeme.clone(),
))
}
}
/// Get the value of an existing (defined) variable. Error if there is no such variable in
/// this environment or any of the enclosing ones.
pub fn get(&self, token: &Token) -> Result<Value, EnvironmentError> {
if let Some(v) = self.values.get(token.lexeme.as_str()) {
Ok(v.clone())
} else if let Some(enclosing) = &self.enclosing {
2025-02-12 13:10:07 +01:00
enclosing.borrow().get(token)
2025-02-12 10:30:51 +01:00
} else {
Err(EnvironmentError::UndefinedVariable(
token.line,
token.lexeme.clone(),
))
}
}
}