aboutsummaryrefslogtreecommitdiff
path: root/src/interpret
diff options
context:
space:
mode:
Diffstat (limited to 'src/interpret')
-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