From 5645282112c5b154f8a1721f1522d7b5c3ff7119 Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Mon, 14 Oct 2024 14:42:44 -0400 Subject: [PATCH] update: better error output on non-matching types with operator --- src/commands/eval/executor.rs | 59 +++++++++++----- src/commands/eval/mod.rs | 126 ++++++---------------------------- 2 files changed, 63 insertions(+), 122 deletions(-) diff --git a/src/commands/eval/executor.rs b/src/commands/eval/executor.rs index d253648..99ecbc2 100644 --- a/src/commands/eval/executor.rs +++ b/src/commands/eval/executor.rs @@ -1,5 +1,6 @@ use super::{Value, Type, FunctionDeclaration}; use super::parser::{ParseTree, ParseError}; +use super::tokenizer::Op; use std::collections::HashMap; use std::borrow::Cow; @@ -9,7 +10,7 @@ use std::error::Error; #[derive(Debug)] pub enum RuntimeError { ParseError(ParseError), - NoOverloadForTypes, + NoOverloadForTypes(String, Vec), ImmutableError(String), VariableUndefined(String), FunctionUndeclared(String), @@ -21,7 +22,9 @@ impl Display for RuntimeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::ParseError(e) => write!(f, "{e}"), - Self::NoOverloadForTypes => write!(f, "No overload of this operator exists for these operands"), + Self::NoOverloadForTypes(op, values) + => write!(f, "No overload of `{op}` exists for the operands `[{}]`", + values.iter().map(|x| format!("{}({x})", x.get_type())).collect::>().join(", ")), Self::ImmutableError(ident) => write!(f, "`{ident}` already exists and cannot be redefined"), Self::VariableUndefined(ident) => write!(f, "variable `{ident}` was not defined"), Self::FunctionUndeclared(ident) => write!(f, "function `{ident}` was not declared"), @@ -74,27 +77,47 @@ impl>> Executor { locals: &mut Cow>) -> Result { match tree { - ParseTree::Add(x, y) => (self.exec(*x, locals)? + self.exec(*y, locals)?) - .ok_or(RuntimeError::NoOverloadForTypes), - ParseTree::Sub(x, y) => (self.exec(*x, locals)? - self.exec(*y, locals)?) - .ok_or(RuntimeError::NoOverloadForTypes), - ParseTree::Mul(x, y) => (self.exec(*x, locals)? * self.exec(*y, locals)?) - .ok_or(RuntimeError::NoOverloadForTypes), - ParseTree::Div(x, y) => (self.exec(*x, locals)? / self.exec(*y, locals)?) - .ok_or(RuntimeError::NoOverloadForTypes), + ParseTree::Add(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { + (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x + y)), + (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x + y as f64)), + (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 + y)), + (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)), + (x, y) => Err(RuntimeError::NoOverloadForTypes("+".into(), vec![x, y])) + }, + ParseTree::Sub(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { + (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x - y)), + (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x - y as f64)), + (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 - y)), + (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x - y)), + (x, y) => Err(RuntimeError::NoOverloadForTypes("-".into(), vec![x, y])) + }, + ParseTree::Mul(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { + (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x * y)), + (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x * y as f64)), + (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 * y)), + (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x * y)), + (x, y) => Err(RuntimeError::NoOverloadForTypes("*".into(), vec![x, y])) + }, + ParseTree::Div(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { + (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x / y)), + (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x / y as f64)), + (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 / y)), + (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x / y)), + (x, y) => Err(RuntimeError::NoOverloadForTypes("*".into(), vec![x, y])) + }, ParseTree::Exp(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x.pow(y as u32))), (Value::Int(x), Value::Float(y)) => Ok(Value::Float((x as f64).powf(y))), (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x.powf(y as f64))), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x.powf(y))), - _ => Err(RuntimeError::NoOverloadForTypes), + (x, y) => Err(RuntimeError::NoOverloadForTypes("**".into(), vec![x, y])), }, ParseTree::Mod(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x % y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x % y as f64)), (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 % y)), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x % y)), - _ => Err(RuntimeError::NoOverloadForTypes), + (x, y) => Err(RuntimeError::NoOverloadForTypes("%".into(), vec![x, y])), }, ParseTree::EqualTo(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x == y)), @@ -103,39 +126,39 @@ impl>> Executor { (Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x == y)), (Value::Bool(x), Value::Bool(y)) => Ok(Value::Bool(x == y)), (Value::String(x), Value::String(y)) => Ok(Value::Bool(x == y)), - _ => Err(RuntimeError::NoOverloadForTypes) + (x, y) => Err(RuntimeError::NoOverloadForTypes("==".into(), vec![x, y])), }, ParseTree::GreaterThan(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x > y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool(x as f64 > y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x > y as f64)), (Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x > y)), - _ => Err(RuntimeError::NoOverloadForTypes) + (x, y) => Err(RuntimeError::NoOverloadForTypes(">".into(), vec![x, y])), }, ParseTree::GreaterThanOrEqualTo(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x >= y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool(x as f64 >= y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x >= y as f64)), (Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x >= y)), - _ => Err(RuntimeError::NoOverloadForTypes) + (x, y) => Err(RuntimeError::NoOverloadForTypes(">=".into(), vec![x, y])), }, ParseTree::LessThan(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x < y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool((x as f64) < y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x < y as f64)), (Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x < y)), - _ => Err(RuntimeError::NoOverloadForTypes) + (x, y) => Err(RuntimeError::NoOverloadForTypes("<".into(), vec![x, y])), }, ParseTree::LessThanOrEqualTo(x, y) => match (self.exec(*x, locals)?, self.exec(*y, locals)?) { (Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x <= y)), (Value::Int(x), Value::Float(y)) => Ok(Value::Bool(x as f64 <= y)), (Value::Float(x), Value::Int(y)) => Ok(Value::Bool(x <= y as f64)), (Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x <= y)), - _ => Err(RuntimeError::NoOverloadForTypes) + (x, y) => Err(RuntimeError::NoOverloadForTypes("<=".into(), vec![x, y])), }, ParseTree::Not(x) => match self.exec(*x, locals)? { Value::Bool(x) => Ok(Value::Bool(!x)), - _ => Err(RuntimeError::NoOverloadForTypes) + x => Err(RuntimeError::NoOverloadForTypes("not".into(), vec![x])) }, ParseTree::Equ(ident, body, scope) => { if self.globals.contains_key(&ident) || locals.contains_key(&ident) { diff --git a/src/commands/eval/mod.rs b/src/commands/eval/mod.rs index 5722a5b..9ffb8c8 100644 --- a/src/commands/eval/mod.rs +++ b/src/commands/eval/mod.rs @@ -39,7 +39,21 @@ enum Type { Function(Box, Vec), } -#[derive(Clone, Debug)] +impl Display for Type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", match self { + Self::Float => "Float".into(), + Self::Int => "Int".into(), + Self::Bool => "Bool".into(), + Self::String => "String".into(), + Self::Nil => "Nil".into(), + Self::Any => "Any".into(), + Self::Function(r, _) => format!("Function -> {}", *r) + }) + } +} + +#[derive(Clone, Debug, PartialEq)] pub enum Value { Float(f64), Int(i64), @@ -48,110 +62,14 @@ pub enum Value { Nil, } -impl std::ops::Add for Value { - type Output = Option; - - fn add(self, rhs: Self) -> Self::Output { +impl Value { + pub fn get_type(&self) -> Type { match self { - Self::Int(x) => { - match rhs { - Self::Int(y) => Some(Self::Int(x + y)), - Self::Float(y) => Some(Self::Float(x as f64 + y)), - _ => None, - } - } - Self::Float(x) => { - match rhs { - Self::Int(y) => Some(Self::Float(x + y as f64)), - Self::Float(y) => Some(Self::Float(x + y)), - _ => None, - } - } - Self::String(x) => { - match rhs { - Self::String(y) => Some(Self::String(format!("{x}{y}"))), - _ => None, - } - } - _ => None, - } - } -} - -impl std::ops::Sub for Value { - type Output = Option; - - fn sub(self, rhs: Self) -> Self::Output { - match self { - Self::Int(x) => { - match rhs { - Self::Int(y) => Some(Self::Int(x - y)), - Self::Float(y) => Some(Self::Float(x as f64 - y)), - _ => None, - } - } - Self::Float(x) => { - match rhs { - Self::Int(y) => Some(Self::Float(x - y as f64)), - Self::Float(y) => Some(Self::Float(x - y)), - _ => None, - } - } - _ => None, - } - } -} - -impl std::ops::Mul for Value { - type Output = Option; - - fn mul(self, rhs: Self) -> Self::Output { - match self { - Self::Int(x) => { - match rhs { - Self::Int(y) => Some(Self::Int(x * y)), - Self::Float(y) => Some(Self::Float(x as f64 * y)), - _ => None, - } - } - Self::Float(x) => { - match rhs { - Self::Int(y) => Some(Self::Float(x * y as f64)), - Self::Float(y) => Some(Self::Float(x * y)), - _ => None, - } - } - Self::String(x) => { - match rhs { - Self::Int(y) => Some(Self::String(x.repeat(y as usize))), - _ => None, - } - } - _ => None, - } - } -} - -impl std::ops::Div for Value { - type Output = Option; - - fn div(self, rhs: Self) -> Self::Output { - match self { - Self::Int(x) => { - match rhs { - Self::Int(y) => Some(Self::Int(x / y)), - Self::Float(y) => Some(Self::Float(x as f64 / y)), - _ => None, - } - } - Self::Float(x) => { - match rhs { - Self::Int(y) => Some(Self::Float(x / y as f64)), - Self::Float(y) => Some(Self::Float(x / y)), - _ => None, - } - } - _ => None, + Self::Float(_) => Type::Float, + Self::Int(_) => Type::Int, + Self::Bool(_) => Type::Bool, + Self::String(_) => Type::String, + Self::Nil => Type::Nil, } } }