diff options
Diffstat (limited to 'src/interpret/scope.rs')
| -rw-r--r-- | src/interpret/scope.rs | 87 |
1 files changed, 76 insertions, 11 deletions
diff --git a/src/interpret/scope.rs b/src/interpret/scope.rs index d8b8f43..478b24d 100644 --- a/src/interpret/scope.rs +++ b/src/interpret/scope.rs @@ -1,9 +1,10 @@ use super::value::Value; use crate::parse::ast::nodes::Identifier; -use std::collections::HashMap; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; +use thiserror::Error; pub struct Scope { - scopes: Vec<HashMap<Identifier, Value>>, + scopes: Vec<HashMap<Identifier, AssignedValue>>, } impl Scope { @@ -19,11 +20,27 @@ impl Scope { self.scopes.pop(); } - pub fn set_var(&mut self, ident: &str, value: Value) { - for scope in self.scopes.iter_mut() { - if scope.contains_key(ident) { - scope.insert(ident.to_string(), value); - return; + pub fn set_var( + &mut self, + ident: &str, + value: Value, + is_constant: bool, + ) -> Result<(), ScopeError> { + let wrapped_value = if is_constant { + AssignedValue::Constant(value) + } else { + AssignedValue::Mutable(value) + }; + + let num_scopes = self.scopes.len(); + for scope in self.scopes.iter_mut().take(num_scopes - 1) { + if let Some(already_assigned_value) = scope.get(ident) { + if !already_assigned_value.is_constant() { + scope.insert(ident.to_string(), wrapped_value); + return Ok(()); + } else { + return Err(ScopeError::CantReassignConstant(ident.to_string())); + } } } @@ -31,15 +48,63 @@ impl Scope { .scopes .last_mut() .expect("Tried accessing scope after last frame is gone."); - inner_scope.insert(ident.to_string(), value); + + if let Some(already_assigned_value) = inner_scope.get(ident) { + if !already_assigned_value.is_constant() { + inner_scope.insert(ident.to_string(), wrapped_value); + Ok(()) + } else { + Err(ScopeError::CantReassignConstant(ident.to_string())) + } + } else { + inner_scope.insert(ident.to_string(), wrapped_value); + Ok(()) + } } - pub fn get_var(&self, ident: &str) -> Option<Value> { + pub fn get_var(&self, ident: &str) -> Result<Value, ScopeError> { for scope in self.scopes.iter().rev() { if let Some(value) = scope.get(ident) { - return Some(value.clone()); + return Ok(value.get_value()); } } - None + Err(ScopeError::UnknownIdentifier(ident.to_string())) + } +} + +#[derive(Debug)] +enum AssignedValue { + Constant(Value), + Mutable(Value), +} + +impl AssignedValue { + fn get_value(&self) -> Value { + match self { + Self::Mutable(value) => value.clone(), + Self::Constant(value) => match value { + Value::Array(reference) => { + let underlying_value = reference.borrow().clone(); + Value::Array(Rc::new(RefCell::new(underlying_value))) + } + Value::Fn(reference) => { + let underlying_value = reference.borrow().clone(); + Value::Fn(Rc::new(RefCell::new(underlying_value))) + } + x => x.clone(), + }, + } } + + fn is_constant(&self) -> bool { + matches!(self, AssignedValue::Constant(_)) + } +} + +#[derive(Error, Debug)] +pub enum ScopeError { + #[error("Unknown identifier: '{0}'.")] + UnknownIdentifier(Identifier), + #[error("Unable to re-assign the constant '{0}'.")] + CantReassignConstant(Identifier), } |
