From cede973c76e061e06c7bdaad3922be5d9305f950 Mon Sep 17 00:00:00 2001 From: Mel Date: Sun, 24 Oct 2021 23:04:22 +0200 Subject: Array element assignment and constants --- src/interpret/scope.rs | 87 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 11 deletions(-) (limited to 'src/interpret/scope.rs') 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>, + scopes: Vec>, } 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 { + pub fn get_var(&self, ident: &str) -> Result { 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), } -- cgit 1.4.1