about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/interpret/scope.rs87
-rw-r--r--src/interpret/value.rs45
-rw-r--r--src/interpret/walker.rs71
3 files changed, 160 insertions, 43 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),
 }
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<T> = RefCell<Rc<T>>;
+type ReferenceOnCopy<T> = Rc<RefCell<T>>;
 
 #[derive(Clone, Debug)]
 pub enum Value {
@@ -10,8 +10,8 @@ pub enum Value {
     Float(f64),
     Int(i64),
     Bool(bool),
-    Array(Vec<Value>),
-    Fn(Ref<FnNode>),
+    Array(ReferenceOnCopy<Vec<Value>>),
+    Fn(ReferenceOnCopy<FnNode>),
     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<Value, OperationError> {
+        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::<Vec<_>>()
                         .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<Value, WalkerError> {
+        // 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