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 ++++++++++++++++++++++++++++++++++++++++++------- src/interpret/value.rs | 45 ++++++++++++++++++++----- src/interpret/walker.rs | 71 ++++++++++++++++++++++++++-------------- 3 files changed, 160 insertions(+), 43 deletions(-) (limited to 'src') 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), } diff --git a/src/interpret/value.rs b/src/interpret/value.rs index 1a7422b..f923742 100644 --- a/src/interpret/value.rs +++ b/src/interpret/value.rs @@ -2,7 +2,7 @@ use crate::parse::ast::nodes::FnNode; use std::{cell::RefCell, fmt::Display, rc::Rc}; use thiserror::Error; -type Ref = RefCell>; +type ReferenceOnCopy = Rc>; #[derive(Clone, Debug)] pub enum Value { @@ -10,8 +10,8 @@ pub enum Value { Float(f64), Int(i64), Bool(bool), - Array(Vec), - Fn(Ref), + Array(ReferenceOnCopy>), + Fn(ReferenceOnCopy), Void, } @@ -173,17 +173,45 @@ impl Value { match self { Value::Array(a) => { - if index < 0 || index as usize >= a.len() { + let array = a.borrow(); + if index < 0 || index as usize >= array.len() { Err(OperationError::ArrayIndexOutOfRange { index, - length: a.len(), + length: array.len(), }) } else { - Ok(a[index as usize].clone()) + Ok(array[index as usize].clone()) } } // Maybe allow string subscripts? - x => Err(OperationError::ArrayType(x)), + x => Err(OperationError::ArrayType(x.clone())), + } + } + + pub fn subscript_assign( + &mut self, + index: Value, + value: Value, + ) -> Result { + let index = match index { + Value::Int(i) => i, + i => return Err(OperationError::ArrayIndexType(i)), + }; + + match self { + Value::Array(a) => { + let mut array = a.borrow_mut(); + if index < 0 || index as usize >= array.len() { + Err(OperationError::ArrayIndexOutOfRange { + index, + length: array.len(), + }) + } else { + array[index as usize] = value; + Ok(array[index as usize].clone()) + } + } + x => Err(OperationError::ArrayType(x.clone())), } } } @@ -213,7 +241,8 @@ impl Display for Value { write!( f, "[{}]", - a.iter() + a.borrow() + .iter() .map(|v| format!("{}", v)) .collect::>() .join(", ") diff --git a/src/interpret/walker.rs b/src/interpret/walker.rs index 0177777..b8a2e68 100644 --- a/src/interpret/walker.rs +++ b/src/interpret/walker.rs @@ -2,14 +2,14 @@ use std::{cell::RefCell, rc::Rc}; use crate::parse::ast::{ expression::Expression, - nodes::{BinaryOperator as BinOp, BlockNode, Identifier, SimpleLiteral, UnaryOperator as UnOp}, + nodes::{BinaryOperator as BinOp, BlockNode, SimpleLiteral, UnaryOperator as UnOp}, statement::Statement, Program, }; use thiserror::Error; use super::{ - scope::Scope, + scope::{Scope, ScopeError}, value::{OperationError, Value}, }; @@ -63,16 +63,10 @@ impl Walker { match node { Expression::Binary { left, op, right } => { // Assignment - // No difference between assignments for now. - if let BinOp::ConstAssign | BinOp::Assign = op { - let identifier = match left.as_ref() { - Expression::Identifier(i) => i, - _ => todo!("Lvalues can only be identifiers."), - }; - - let value = self.walk_expression(right)?; - self.scope.set_var(identifier, value.clone()); - return Ok(value); + match op { + BinOp::ConstAssign => return self.assing_to_lvalue(left, right, true), + BinOp::Assign => return self.assing_to_lvalue(left, right, false), + _ => {} } let left = self.walk_expression(left)?; @@ -90,8 +84,7 @@ impl Walker { BinOp::Gte => left.gte(right), BinOp::Lt => right.gt(left), BinOp::Lte => right.gte(left), - // No structure access yet. - BinOp::Dot => todo!(), + BinOp::Dot => todo!("Structures not implemented yet."), _ => unreachable!(), } .map_err(WalkerError::OperationError) @@ -109,7 +102,10 @@ impl Walker { Expression::ArrayAccess(node) => { let array = self.walk_expression(&node.array)?; let index = self.walk_expression(&node.index)?; - array.subscript(index).map_err(WalkerError::OperationError) + array + .subscript(index) + .map(|v| v.clone()) + .map_err(WalkerError::OperationError) } Expression::MemberAccess(_) => todo!("Structures not implemented yet."), Expression::Group(node) => self.walk_expression(node), @@ -118,7 +114,7 @@ impl Walker { for expression in &node.elements { elements.push(self.walk_expression(expression)?); } - Ok(Value::Array(elements)) + Ok(Value::Array(Rc::new(RefCell::new(elements)))) } Expression::SimpleLiteral(token) => { let value = match token { @@ -133,7 +129,7 @@ impl Walker { Expression::Block(block) => self.walk_block(block.as_ref()), Expression::FnLiteral(fn_node) => { let node = fn_node.as_ref().clone(); - Ok(Value::Fn(RefCell::new(Rc::new(node)))) + Ok(Value::Fn(Rc::new(RefCell::new(node)))) } Expression::If(if_node) => { for conditional in &if_node.conditionals { @@ -189,11 +185,7 @@ impl Walker { } } Expression::Identifier(ident) => { - if let Some(value) = self.scope.get_var(ident) { - Ok(value) - } else { - Err(WalkerError::UnknownIdentifier(ident.clone())) - } + self.scope.get_var(ident).map_err(WalkerError::ScopeError) } } } @@ -215,17 +207,48 @@ impl Walker { result } + + pub fn assing_to_lvalue( + &mut self, + lvalue: &Expression, + rvalue: &Expression, + is_constant: bool, + ) -> Result { + // Maybe other expressions could also be l-values, but these are fine for now. + match lvalue { + Expression::MemberAccess(_) => todo!("Structures not implemented yet."), + Expression::ArrayAccess(node) => { + let mut array = self.walk_expression(&node.array)?; + let index = self.walk_expression(&node.index)?; + let value = self.walk_expression(rvalue)?; + + array + .subscript_assign(index, value) + .map_err(WalkerError::OperationError) + } + Expression::Identifier(ident) => { + let value = self.walk_expression(rvalue)?; + self.scope + .set_var(ident, value.clone(), is_constant) + .map_err(WalkerError::ScopeError)?; + return Ok(value); + } + _ => Err(WalkerError::NonLValueAssignment), + } + } } // TODO: Add source maps to the errors. #[derive(Error, Debug)] pub enum WalkerError { - #[error("Unknown identifier '{0}'")] - UnknownIdentifier(Identifier), #[error("Loop expressions can only take boolean values as conditions.")] WrongLoopConditionType, #[error("If and Elif expressions can only take boolean values as conditions.")] WrongIfConditionType, + #[error("Can only assign to identifiers and member or array access results.")] + NonLValueAssignment, + #[error(transparent)] + ScopeError(ScopeError), #[error(transparent)] OperationError(OperationError), // These are used for loop control flow and are only errors -- cgit 1.4.1