diff options
| author | Mel <einebeere@gmail.com> | 2021-10-23 22:01:52 +0200 |
|---|---|---|
| committer | Mel <einebeere@gmail.com> | 2021-10-23 22:01:52 +0200 |
| commit | 8a6eb35a900081967db16d313ab7ed470de6570f (patch) | |
| tree | d58dd702d8a742c7554545bc4e291480649e3663 /src/interpret | |
| parent | da14afd74e1659af6ce4553360ac5dd0ce933db8 (diff) | |
| download | rabbithole-8a6eb35a900081967db16d313ab7ed470de6570f.tar.zst rabbithole-8a6eb35a900081967db16d313ab7ed470de6570f.zip | |
Loop expressions and concrete walker errors.
Diffstat (limited to 'src/interpret')
| -rw-r--r-- | src/interpret/value.rs | 103 | ||||
| -rw-r--r-- | src/interpret/walker.rs | 97 |
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>), +} |
