about summary refs log tree commit diff
path: root/src/interpret/operator.rs
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2021-11-20 00:31:28 +0100
committerMel <einebeere@gmail.com>2021-11-20 00:31:28 +0100
commit395d086f0dce355ccdcf3da149c309826c539b48 (patch)
tree888ad59d8fcebcec0c04bcfd13364889c5047349 /src/interpret/operator.rs
parent334f70f5a2f63ec636ac1a8bc375ce51effba424 (diff)
downloadrabbithole-395d086f0dce355ccdcf3da149c309826c539b48.tar.zst
rabbithole-395d086f0dce355ccdcf3da149c309826c539b48.zip
Runtime types
Diffstat (limited to 'src/interpret/operator.rs')
-rw-r--r--src/interpret/operator.rs300
1 files changed, 300 insertions, 0 deletions
diff --git a/src/interpret/operator.rs b/src/interpret/operator.rs
new file mode 100644
index 0000000..78ebe75
--- /dev/null
+++ b/src/interpret/operator.rs
@@ -0,0 +1,300 @@
+use thiserror::Error;
+
+use crate::{parse::ast::expression::Expression, types::bag::TypeBag};
+
+use super::{
+    value::{Value, ValueVariant},
+    walker::{Walker, WalkerError},
+};
+
+pub struct ValueOperator<'types> {
+    types: &'types TypeBag,
+}
+
+impl<'t> ValueOperator<'t> {
+    pub fn new(types: &'t TypeBag) -> Self {
+        ValueOperator { types }
+    }
+
+    pub fn add(&self, lhs: Value, rhs: Value) -> Result<Value, OperationError> {
+        match lhs.variant {
+            ValueVariant::Str(ref l) => match rhs.variant {
+                ValueVariant::Str(r) => Ok(Value::str(format!("{}{}", l, r), self.types)),
+                ValueVariant::Float(r) => Ok(Value::str(format!("{}{}", l, r), self.types)),
+                ValueVariant::Int(r) => Ok(Value::str(format!("{}{}", l, r), self.types)),
+                _ => Err(OperationError::AddTypes(lhs, rhs)),
+            },
+            ValueVariant::Float(l) => match rhs.variant {
+                ValueVariant::Str(r) => Ok(Value::str(l.to_string() + &r, self.types)),
+                ValueVariant::Float(r) => Ok(Value::float(l + r, self.types)),
+                ValueVariant::Int(r) => Ok(Value::float(l + r as f64, self.types)),
+                _ => Err(OperationError::AddTypes(lhs, rhs)),
+            },
+            ValueVariant::Int(l) => match rhs.variant {
+                ValueVariant::Str(r) => Ok(Value::str(l.to_string() + &r, self.types)),
+                ValueVariant::Float(r) => Ok(Value::float(l as f64 + r, self.types)),
+                ValueVariant::Int(r) => Ok(Value::int(l + r, self.types)),
+                _ => Err(OperationError::AddTypes(lhs, rhs)),
+            },
+            _ => Err(OperationError::AddTypes(lhs, rhs)),
+        }
+    }
+
+    pub fn sub(&self, lhs: Value, rhs: Value) -> Result<Value, OperationError> {
+        match lhs.variant {
+            ValueVariant::Float(l) => match rhs.variant {
+                ValueVariant::Float(r) => Ok(Value::float(l - r, self.types)),
+                ValueVariant::Int(r) => Ok(Value::float(l - r as f64, self.types)),
+                _ => Err(OperationError::SubTypes(lhs, rhs)),
+            },
+            ValueVariant::Int(l) => match rhs.variant {
+                ValueVariant::Float(r) => Ok(Value::float(l as f64 - r, self.types)),
+                ValueVariant::Int(r) => Ok(Value::int(l - r, self.types)),
+                _ => Err(OperationError::SubTypes(lhs, rhs)),
+            },
+            _ => Err(OperationError::SubTypes(lhs, rhs)),
+        }
+    }
+
+    pub fn mul(&self, lhs: Value, rhs: Value) -> Result<Value, OperationError> {
+        match lhs.variant {
+            ValueVariant::Float(l) => match rhs.variant {
+                ValueVariant::Float(r) => Ok(Value::float(l * r, self.types)),
+                ValueVariant::Int(r) => Ok(Value::float(l * r as f64, self.types)),
+                _ => Err(OperationError::MulTypes(lhs, rhs)),
+            },
+            ValueVariant::Int(l) => match rhs.variant {
+                ValueVariant::Float(r) => Ok(Value::float(l as f64 * r, self.types)),
+                ValueVariant::Int(r) => Ok(Value::int(l * r, self.types)),
+                _ => Err(OperationError::MulTypes(lhs, rhs)),
+            },
+            _ => Err(OperationError::MulTypes(lhs, rhs)),
+        }
+    }
+
+    pub fn div(&self, lhs: Value, rhs: Value) -> Result<Value, OperationError> {
+        match lhs.variant {
+            ValueVariant::Float(l) => match rhs.variant {
+                ValueVariant::Float(r) => Ok(Value::float(l / r, self.types)),
+                ValueVariant::Int(r) => Ok(Value::float(l / r as f64, self.types)),
+                _ => Err(OperationError::DivTypes(lhs, rhs)),
+            },
+            ValueVariant::Int(l) => match rhs.variant {
+                ValueVariant::Float(r) => Ok(Value::float(l as f64 / r, self.types)),
+                ValueVariant::Int(r) => Ok(Value::float(l as f64 / r as f64, self.types)),
+                _ => Err(OperationError::DivTypes(lhs, rhs)),
+            },
+            _ => Err(OperationError::DivTypes(lhs, rhs)),
+        }
+    }
+
+    pub fn eq(&self, lhs: Value, rhs: Value) -> Result<Value, OperationError> {
+        match lhs.variant {
+            ValueVariant::Str(l) => match rhs.variant {
+                ValueVariant::Str(r) => Ok(Value::bool(l == r, self.types)),
+                _ => Ok(Value::bool(false, self.types)),
+            },
+            ValueVariant::Float(l) => match rhs.variant {
+                ValueVariant::Float(r) => Ok(Value::bool(l == r, self.types)),
+                _ => Ok(Value::bool(false, self.types)),
+            },
+            ValueVariant::Int(l) => match rhs.variant {
+                ValueVariant::Int(r) => Ok(Value::bool(l == r, self.types)),
+                _ => Ok(Value::bool(false, self.types)),
+            },
+            ValueVariant::Bool(l) => match rhs.variant {
+                ValueVariant::Bool(r) => Ok(Value::bool(l == r, self.types)),
+                _ => Ok(Value::bool(false, self.types)),
+            },
+            _ => Ok(Value::bool(false, self.types)),
+        }
+    }
+
+    pub fn neq(&self, lhs: Value, rhs: Value) -> Result<Value, OperationError> {
+        if let Ok(Value {
+            variant: ValueVariant::Bool(value),
+            ..
+        }) = self.eq(lhs, rhs)
+        {
+            Ok(Value::bool(!value, self.types))
+        } else {
+            unreachable!()
+        }
+    }
+
+    pub fn gt(&self, lhs: Value, rhs: Value) -> Result<Value, OperationError> {
+        match lhs.variant {
+            ValueVariant::Float(r) => match rhs.variant {
+                ValueVariant::Float(l) => Ok(Value::bool(r > l, self.types)),
+                ValueVariant::Int(l) => Ok(Value::bool(r > l as f64, self.types)),
+                _ => Err(OperationError::CompareTypes(lhs, rhs)),
+            },
+            ValueVariant::Int(r) => match rhs.variant {
+                ValueVariant::Float(l) => Ok(Value::bool(r as f64 > l, self.types)),
+                ValueVariant::Int(l) => Ok(Value::bool(r > l, self.types)),
+                _ => Err(OperationError::CompareTypes(lhs, rhs)),
+            },
+            _ => Err(OperationError::CompareTypes(lhs, rhs)),
+        }
+    }
+
+    pub fn gte(&self, lhs: Value, rhs: Value) -> Result<Value, OperationError> {
+        match lhs.variant {
+            ValueVariant::Float(r) => match rhs.variant {
+                ValueVariant::Float(l) => Ok(Value::bool(r >= l, self.types)),
+                ValueVariant::Int(l) => Ok(Value::bool(r >= l as f64, self.types)),
+                _ => Err(OperationError::CompareTypes(lhs, rhs)),
+            },
+            ValueVariant::Int(r) => match rhs.variant {
+                ValueVariant::Float(l) => Ok(Value::bool(r as f64 >= l, self.types)),
+                ValueVariant::Int(l) => Ok(Value::bool(r >= l, self.types)),
+                _ => Err(OperationError::CompareTypes(lhs, rhs)),
+            },
+            _ => Err(OperationError::CompareTypes(lhs, rhs)),
+        }
+    }
+
+    pub fn neg(&self, val: Value) -> Result<Value, OperationError> {
+        match val.variant {
+            ValueVariant::Float(float) => Ok(Value::float(-float, self.types)),
+            ValueVariant::Int(int) => Ok(Value::int(-int, self.types)),
+            _ => Err(OperationError::NegType(val)),
+        }
+    }
+
+    pub fn not(&self, val: Value) -> Result<Value, OperationError> {
+        match val.variant {
+            ValueVariant::Bool(bool) => Ok(Value::bool(bool, self.types)),
+            _ => Err(OperationError::NotType(val)),
+        }
+    }
+
+    pub fn subscript(&self, val: Value, index: Value) -> Result<Value, OperationError> {
+        let index = match index.variant {
+            ValueVariant::Int(i) => i,
+            _ => return Err(OperationError::ArrayIndexType(index)),
+        };
+
+        match val.variant {
+            ValueVariant::Array(a) => {
+                let array = a.borrow();
+                if index < 0 || index as usize >= array.len() {
+                    Err(OperationError::ArrayIndexOutOfRange {
+                        index,
+                        length: array.len(),
+                    })
+                } else {
+                    Ok(array[index as usize].clone())
+                }
+            }
+            // Maybe allow string subscripts?
+            _ => Err(OperationError::ArrayType(val)),
+        }
+    }
+
+    pub fn subscript_assign(
+        &self,
+        val: &mut Value,
+        index: Value,
+        value: Value,
+    ) -> Result<Value, OperationError> {
+        let index = match index.variant {
+            ValueVariant::Int(i) => i,
+            _ => return Err(OperationError::ArrayIndexType(index)),
+        };
+
+        match &val.variant {
+            ValueVariant::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())
+                }
+            }
+            _ => Err(OperationError::ArrayType(val.clone())),
+        }
+    }
+
+    pub fn call(&self, val: &Value, arguments: Vec<Value>) -> Result<Value, WalkerError> {
+        let called = match &val.variant {
+            ValueVariant::Fn(i) => i,
+            _ => {
+                return Err(WalkerError::OperationError(OperationError::CallableType(
+                    val.clone(),
+                )))
+            }
+        }
+        .borrow();
+
+        // FIXME: Currently closures are able to re-assign values from the upper scopes.
+        // This is good behaviour, until a closure re-assigns a value that was declared after
+        // the closure even existed.
+        // Minimal reproducible example:
+        // ```rh
+        // closure = fn { y = 10; };
+        // y = 1;
+        // closure();
+        // print y;
+        // ```
+        // Expected: 1
+        // Actual: 10
+        let mut scope = called.scope.clone();
+        scope.nest();
+
+        let parameters = &called.node.header.parameters;
+
+        if parameters.len() != arguments.len() {
+            return Err(WalkerError::OperationError(
+                OperationError::WrongArgumentCount(parameters.len(), arguments.len()),
+            ));
+        }
+
+        for (argument, parameter) in arguments.into_iter().zip(parameters.iter()) {
+            scope.set_var_shadowed(&parameter.identifier, argument);
+        }
+
+        // Yes, we create a new walker for every function call,
+        // it's *way* easier that way.
+        let mut walker = Walker::new(scope, self.types.clone());
+        let result = walker.walk_expression(&Expression::Block(Box::new(called.node.body.clone())));
+
+        if let Err(WalkerError::Return(returned)) = result {
+            Ok(returned)
+        } else {
+            result
+        }
+    }
+}
+
+#[derive(Error, Debug)]
+pub enum OperationError {
+    #[error("Can't add value '{0}' of type '{}' to value '{1}' of type '{}'.", .0.type_name(), .1.type_name())]
+    AddTypes(Value, Value),
+    #[error("Can't subtract value '{1}' of type '{}' from value '{0}' of type '{}'.", .1.type_name(), .0.type_name())]
+    SubTypes(Value, Value),
+    #[error("Can't multiply value '{0}' of type '{}' with value '{1}' of type '{}'.", .0.type_name(), .1.type_name())]
+    MulTypes(Value, Value),
+    #[error("Can't divide value '{0}' of type '{}' by value '{1}' of type '{}'.", .0.type_name(), .1.type_name())]
+    DivTypes(Value, Value),
+    #[error("Can't compare value '{0}' of type '{}' with value '{1}' of type '{}'.", .0.type_name(), .1.type_name())]
+    CompareTypes(Value, Value),
+    #[error("Can't negate value '{0}' of type '{}'.", .0.type_name())]
+    NegType(Value),
+    #[error("Can't flip value '{0}' of type '{}'.", .0.type_name())]
+    NotType(Value),
+    #[error("Can't use value '{0}' of type '{}' as a subsript index.", .0.type_name())]
+    ArrayIndexType(Value),
+    #[error("Can't subscript value '{0}' of type '{}'.", .0.type_name())]
+    ArrayType(Value),
+    #[error("Array index '{index}' out of range for array of length '{length}'.")]
+    ArrayIndexOutOfRange { index: i64, length: usize },
+    #[error("Can't call value '{0}' of type '{}'.", .0.type_name())]
+    CallableType(Value),
+    #[error("Function expects {0} arguments, but {1} were given.")]
+    WrongArgumentCount(usize, usize),
+}