implement chapter 11 in rust

This commit is contained in:
Sebastian Hugentobler 2025-05-25 10:52:20 +02:00
parent 8860a1c639
commit a25b6d1e92
Signed by: shu
SSH key fingerprint: SHA256:ppcx6MlixdNZd5EUM1nkHOKoyQYoJwzuQKXM6J/t66M
16 changed files with 470 additions and 32 deletions

View file

@ -8,6 +8,8 @@ use crate::{token::Token, value::Value};
pub enum EnvironmentError {
#[error("line {0}: undefined variable: {1}")]
UndefinedVariable(usize, String),
#[error("invalid environment distance")]
InvalidDistance,
}
/// Environment mapping variable names to their respective values. Can have an optional enclosing
@ -50,6 +52,18 @@ impl Environment {
}
}
pub fn assign_at(
&mut self,
distance: usize,
token: &Token,
value: Value,
) -> Result<(), EnvironmentError> {
self.ancestor(distance)?
.values
.insert(token.lexeme.clone(), value);
Ok(())
}
/// 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> {
@ -64,4 +78,29 @@ impl Environment {
))
}
}
pub fn get_at(&self, distance: usize, token: &Token) -> Result<Value, EnvironmentError> {
if let Some(v) = self.ancestor(distance)?.values.get(token.lexeme.as_str()) {
Ok(v.clone())
} else {
Err(EnvironmentError::UndefinedVariable(
token.line,
token.lexeme.clone(),
))
}
}
fn ancestor(&self, distance: usize) -> Result<Environment, EnvironmentError> {
let mut environment = self.clone();
for _ in 0..distance {
let enclosing = match &environment.enclosing {
Some(enc) => enc.borrow().clone(),
None => return Err(EnvironmentError::InvalidDistance),
};
environment = enclosing;
}
Ok(environment)
}
}