statements in rust
This commit is contained in:
parent
15b331f447
commit
a629ddca05
6 changed files with 434 additions and 194 deletions
66
rust/rox/src/environment.rs
Normal file
66
rust/rox/src/environment.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
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>,
|
||||
enclosing: Option<Box<Environment>>,
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
pub fn with_enclosing(enclosing: Environment) -> Self {
|
||||
Self {
|
||||
values: HashMap::default(),
|
||||
enclosing: Some(Box::new(enclosing)),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
enclosing.assign(token, value)
|
||||
} 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 {
|
||||
enclosing.get(token)
|
||||
} else {
|
||||
Err(EnvironmentError::UndefinedVariable(
|
||||
token.line,
|
||||
token.lexeme.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue