about summary refs log tree commit diff
path: root/src/interpret
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2021-10-23 22:01:52 +0200
committerMel <einebeere@gmail.com>2021-10-23 22:01:52 +0200
commit8a6eb35a900081967db16d313ab7ed470de6570f (patch)
treed58dd702d8a742c7554545bc4e291480649e3663 /src/interpret
parentda14afd74e1659af6ce4553360ac5dd0ce933db8 (diff)
downloadrabbithole-8a6eb35a900081967db16d313ab7ed470de6570f.tar.zst
rabbithole-8a6eb35a900081967db16d313ab7ed470de6570f.zip
Loop expressions and concrete walker errors.
Diffstat (limited to 'src/interpret')
-rw-r--r--src/interpret/value.rs103
-rw-r--r--src/interpret/walker.rs97
2 files changed, 150 insertions, 50 deletions
diff --git a/src/interpret/value.rs b/src/interpret/value.rs
index 27f2668..86fe094 100644
--- a/src/interpret/value.rs
+++ b/src/interpret/value.rs
@@ -1,6 +1,6 @@
 use crate::parse::ast::nodes::FnNode;
-use anyhow::{anyhow, Result};
 use std::{cell::RefCell, fmt::Display, rc::Rc};
+use thiserror::Error;
 
 type Ref<T> = RefCell<Rc<T>>;
 
@@ -15,79 +15,79 @@ pub enum Value {
 }
 
 impl Value {
-    pub fn add(self, rhs: Value) -> Result<Value> {
+    pub fn add(self, rhs: Value) -> Result<Value, OperationError> {
         match self {
-            Value::Str(l) => match rhs {
-                Value::Str(r) => Ok(Value::Str(l + &r)),
-                Value::Float(r) => Ok(Value::Str(l + &r.to_string())),
-                Value::Int(r) => Ok(Value::Str(l + &r.to_string())),
-                _ => Err(anyhow!("Right operand can't be added.")),
+            Value::Str(ref l) => match rhs {
+                Value::Str(r) => Ok(Value::Str(format!("{}{}", l, r))),
+                Value::Float(r) => Ok(Value::Str(format!("{}{}", l, r))),
+                Value::Int(r) => Ok(Value::Str(format!("{}{}", l, r))),
+                r => Err(OperationError::AddTypes(self, r)),
             },
             Value::Float(l) => match rhs {
                 Value::Str(r) => Ok(Value::Str(l.to_string() + &r)),
                 Value::Float(r) => Ok(Value::Float(l + r)),
                 Value::Int(r) => Ok(Value::Float(l + r as f64)),
-                _ => Err(anyhow!("Right operand can't be added.")),
+                r => Err(OperationError::AddTypes(self, r)),
             },
             Value::Int(l) => match rhs {
                 Value::Str(r) => Ok(Value::Str(l.to_string() + &r)),
                 Value::Float(r) => Ok(Value::Float(l as f64 + r)),
                 Value::Int(r) => Ok(Value::Int(l + r)),
-                _ => Err(anyhow!("Right operand can't be added.")),
+                r => Err(OperationError::AddTypes(self, r)),
             },
-            _ => Err(anyhow!("Left operand can't be added.")),
+            _ => Err(OperationError::AddTypes(self, rhs)),
         }
     }
 
-    pub fn sub(self, rhs: Value) -> Result<Value> {
+    pub fn sub(self, rhs: Value) -> Result<Value, OperationError> {
         match self {
             Value::Float(l) => match rhs {
                 Value::Float(r) => Ok(Value::Float(l - r)),
                 Value::Int(r) => Ok(Value::Float(l - r as f64)),
-                _ => Err(anyhow!("Right operand can't be substracted.")),
+                r => Err(OperationError::SubTypes(self, r)),
             },
             Value::Int(l) => match rhs {
                 Value::Float(r) => Ok(Value::Float(l as f64 - r)),
                 Value::Int(r) => Ok(Value::Int(l - r)),
-                _ => Err(anyhow!("Right operand can't be substracted.")),
+                r => Err(OperationError::SubTypes(self, r)),
             },
-            _ => Err(anyhow!("Left operand can't be substracted from.")),
+            _ => Err(OperationError::SubTypes(self, rhs)),
         }
     }
 
-    pub fn mul(self, rhs: Value) -> Result<Value> {
+    pub fn mul(self, rhs: Value) -> Result<Value, OperationError> {
         match self {
             Value::Float(l) => match rhs {
                 Value::Float(r) => Ok(Value::Float(l * r)),
                 Value::Int(r) => Ok(Value::Float(l * r as f64)),
-                _ => Err(anyhow!("Right operand can't be multiplied.")),
+                r => Err(OperationError::MulTypes(self, r)),
             },
             Value::Int(l) => match rhs {
                 Value::Float(r) => Ok(Value::Float(l as f64 * r)),
                 Value::Int(r) => Ok(Value::Int(l * r)),
-                _ => Err(anyhow!("Right operand can't be multiplied.")),
+                r => Err(OperationError::MulTypes(self, r)),
             },
-            _ => Err(anyhow!("Left operand can't be multiplied.")),
+            _ => Err(OperationError::MulTypes(self, rhs)),
         }
     }
 
-    pub fn div(self, rhs: Value) -> Result<Value> {
+    pub fn div(self, rhs: Value) -> Result<Value, OperationError> {
         match self {
             Value::Float(l) => match rhs {
                 Value::Float(r) => Ok(Value::Float(l / r)),
                 Value::Int(r) => Ok(Value::Float(l / r as f64)),
-                _ => Err(anyhow!("Right operand can't be multiplied.")),
+                r => Err(OperationError::DivTypes(self, r)),
             },
             Value::Int(l) => match rhs {
                 Value::Float(r) => Ok(Value::Float(l as f64 / r)),
                 Value::Int(r) => Ok(Value::Float(l as f64 / r as f64)),
-                _ => Err(anyhow!("Right operand can't be multiplied.")),
+                r => Err(OperationError::DivTypes(self, r)),
             },
-            _ => Err(anyhow!("Left operand can't be multiplied.")),
+            _ => Err(OperationError::DivTypes(self, rhs)),
         }
     }
 
-    pub fn eq(self, rhs: Value) -> Result<Value> {
+    pub fn eq(self, rhs: Value) -> Result<Value, OperationError> {
         match self {
             Value::Str(l) => match rhs {
                 Value::Str(r) => Ok(Value::Bool(l == r)),
@@ -109,7 +109,7 @@ impl Value {
         }
     }
 
-    pub fn neq(self, rhs: Value) -> Result<Value> {
+    pub fn neq(self, rhs: Value) -> Result<Value, OperationError> {
         if let Ok(Value::Bool(value)) = self.eq(rhs) {
             Ok(Value::Bool(!value))
         } else {
@@ -117,50 +117,63 @@ impl Value {
         }
     }
 
-    pub fn gt(self, rhs: Value) -> Result<Value> {
+    pub fn gt(self, rhs: Value) -> Result<Value, OperationError> {
         match self {
             Value::Float(r) => match rhs {
                 Value::Float(l) => Ok(Value::Bool(r > l)),
                 Value::Int(l) => Ok(Value::Bool(r > l as f64)),
-                _ => Err(anyhow!("Right operand can't be compared.")),
+                r => Err(OperationError::CompareTypes(self, r)),
             },
             Value::Int(r) => match rhs {
                 Value::Float(l) => Ok(Value::Bool(r as f64 > l)),
                 Value::Int(l) => Ok(Value::Bool(r > l)),
-                _ => Err(anyhow!("Right operand can't be compared.")),
+                r => Err(OperationError::CompareTypes(self, r)),
             },
-            _ => Err(anyhow!("Left operand can't be compared.")),
+            _ => Err(OperationError::CompareTypes(self, rhs)),
         }
     }
 
-    pub fn gte(self, rhs: Value) -> Result<Value> {
+    pub fn gte(self, rhs: Value) -> Result<Value, OperationError> {
         match self {
             Value::Float(r) => match rhs {
                 Value::Float(l) => Ok(Value::Bool(r >= l)),
                 Value::Int(l) => Ok(Value::Bool(r >= l as f64)),
-                _ => Err(anyhow!("Right operand can't be compared.")),
+                r => Err(OperationError::CompareTypes(self, r)),
             },
             Value::Int(r) => match rhs {
                 Value::Float(l) => Ok(Value::Bool(r as f64 >= l)),
                 Value::Int(l) => Ok(Value::Bool(r >= l)),
-                _ => Err(anyhow!("Right operand can't be compared.")),
+                r => Err(OperationError::CompareTypes(self, r)),
             },
-            _ => Err(anyhow!("Left operand can't be compared.")),
+            _ => Err(OperationError::CompareTypes(self, rhs)),
         }
     }
 
-    pub fn neg(self) -> Result<Value> {
+    pub fn neg(self) -> Result<Value, OperationError> {
         match self {
             Value::Float(float) => Ok(Value::Float(-float)),
             Value::Int(int) => Ok(Value::Int(-int)),
-            _ => Err(anyhow!("Can't negate value.")),
+            _ => Err(OperationError::NegType(self)),
         }
     }
 
-    pub fn not(self) -> Result<Value> {
+    pub fn not(self) -> Result<Value, OperationError> {
         match self {
             Value::Bool(bool) => Ok(Value::Bool(bool)),
-            _ => Err(anyhow!("Can't flip non-bool value.")),
+            _ => Err(OperationError::NotType(self)),
+        }
+    }
+}
+
+impl Value {
+    pub fn type_name(&self) -> &'static str {
+        match self {
+            Value::Str(_) => "Str",
+            Value::Float(_) => "Float",
+            Value::Int(_) => "Int",
+            Value::Bool(_) => "Bool",
+            Value::Fn(_) => "Fn",
+            Value::Void => "Void",
         }
     }
 }
@@ -177,3 +190,21 @@ impl Display for Value {
         }
     }
 }
+
+#[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),
+}
diff --git a/src/interpret/walker.rs b/src/interpret/walker.rs
index 7684747..db37a26 100644
--- a/src/interpret/walker.rs
+++ b/src/interpret/walker.rs
@@ -2,13 +2,16 @@ use std::{cell::RefCell, rc::Rc};
 
 use crate::parse::ast::{
     expression::Expression,
-    nodes::{BinaryOperator as BinOp, BlockNode, Literal, UnaryOperator as UnOp},
+    nodes::{BinaryOperator as BinOp, BlockNode, Identifier, Literal, UnaryOperator as UnOp},
     statement::Statement,
     Program,
 };
-use anyhow::{anyhow, Result};
+use thiserror::Error;
 
-use super::{scope::Scope, value::Value};
+use super::{
+    scope::Scope,
+    value::{OperationError, Value},
+};
 
 pub struct Walker {
     scope: Scope,
@@ -28,7 +31,7 @@ impl Walker {
         }
     }
 
-    fn walk_statement(&mut self, statement: &Statement) -> Result<Option<Value>> {
+    fn walk_statement(&mut self, statement: &Statement) -> Result<Option<Value>, WalkerError> {
         let result = match statement {
             Statement::Expression(node) => {
                 self.walk_expression(node)?;
@@ -40,12 +43,23 @@ impl Walker {
                 None
             }
             Statement::Return(node) => Some(self.walk_expression(node)?),
+            Statement::Break(node) => {
+                let returned = if let Some(expression) = node {
+                    Some(self.walk_expression(expression)?)
+                } else {
+                    None
+                };
+                // If there's a loop above us it will catch this error.
+                return Err(WalkerError::LoopBreak(returned));
+            }
+            // Same here.
+            Statement::Continue => return Err(WalkerError::LoopContinue),
         };
 
         Ok(result)
     }
 
-    pub fn walk_expression(&mut self, node: &Expression) -> Result<Value> {
+    pub fn walk_expression(&mut self, node: &Expression) -> Result<Value, WalkerError> {
         match node {
             Expression::Binary { left, op, right } => {
                 // Assignment
@@ -79,7 +93,8 @@ impl Walker {
                     // No structure access yet.
                     BinOp::Dot => todo!(),
                     _ => unreachable!(),
-                }?;
+                }
+                .map_err(WalkerError::OperationError)?;
 
                 Ok(new_value)
             }
@@ -87,9 +102,10 @@ impl Walker {
                 let value = self.walk_expression(right)?;
 
                 let new_value = match op {
-                    UnOp::Minus => value.neg()?,
-                    UnOp::Not => value.not()?,
-                };
+                    UnOp::Minus => value.neg(),
+                    UnOp::Not => value.not(),
+                }
+                .map_err(WalkerError::OperationError)?;
 
                 Ok(new_value)
             }
@@ -116,9 +132,7 @@ impl Walker {
                             return self.walk_block(&conditional.block);
                         }
                     } else {
-                        return Err(anyhow!(
-                            "If and Elif expressions can only take boolean values as conditions."
-                        ));
+                        return Err(WalkerError::WrongIfConditionType);
                     }
                 }
 
@@ -128,17 +142,53 @@ impl Walker {
                     Ok(Value::Void)
                 }
             }
+            Expression::Loop(loop_node) => {
+                if let Some(condition) = &loop_node.condition {
+                    loop {
+                        if let Value::Bool(should_repeat) = self.walk_expression(condition)? {
+                            if should_repeat {
+                                match self.walk_block(&loop_node.body) {
+                                    Err(WalkerError::LoopBreak(loop_value)) => {
+                                        return Ok(loop_value.unwrap_or(Value::Void));
+                                    }
+                                    // Do nothing for continue and continue looping of course, you dummy.
+                                    Err(WalkerError::LoopContinue) => {}
+                                    // This is probably an actual error.
+                                    Err(x) => return Err(x),
+                                    _ => {}
+                                }
+                            } else {
+                                return Ok(Value::Void);
+                            }
+                        } else {
+                            return Err(WalkerError::WrongLoopConditionType);
+                        }
+                    }
+                } else {
+                    // Same as above.
+                    loop {
+                        match self.walk_block(&loop_node.body) {
+                            Err(WalkerError::LoopBreak(loop_value)) => {
+                                break Ok(loop_value.unwrap_or(Value::Void));
+                            }
+                            Err(WalkerError::LoopContinue) => {}
+                            Err(x) => return Err(x),
+                            _ => {}
+                        }
+                    }
+                }
+            }
             Expression::Identifier(ident) => {
                 if let Some(value) = self.scope.get_var(ident) {
                     Ok(value)
                 } else {
-                    Err(anyhow!("Unknown identifier: {}.", ident))
+                    Err(WalkerError::UnknownIdentifier(ident.clone()))
                 }
             }
         }
     }
 
-    fn walk_block(&mut self, block: &BlockNode) -> Result<Value> {
+    fn walk_block(&mut self, block: &BlockNode) -> Result<Value, WalkerError> {
         self.scope.nest();
 
         for statement in block.statements.iter() {
@@ -156,3 +206,22 @@ impl Walker {
         result
     }
 }
+
+// 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(transparent)]
+    OperationError(OperationError),
+    // These are used for loop control flow and are only errors
+    // if continue and break statements are used outside of loops.
+    #[error("Continue statements are only valid inside loops.")]
+    LoopContinue,
+    #[error("Break statements are only valid inside loops.")]
+    LoopBreak(Option<Value>),
+}