use std::{ fmt::Display, ops::{Add, Div, Mul, Sub}, }; #[derive(Debug, Clone, PartialEq, Default)] pub enum Value { Int(i32), Float(f32), String(String), Bool(bool), Char(char), #[default] Nothing, Error, } impl Eq for Value {} // impl Value { // pub fn as_int(&self) -> Option { // match self { // Value::Int(v) => Some(*v), // _ => None, // } // } // pub fn as_float(&self) -> Option { // match self { // Value::Float(v) => Some(*v), // _ => None, // } // } // pub fn as_string(&self) -> Option { // match self { // Value::String(v) => Some(v.clone()), // _ => None, // } // } // pub fn as_bool(&self) -> Option { // match self { // Value::Bool(v) => Some(*v), // _ => None, // } // } // pub fn as_char(&self) -> Option { // match self { // Value::Char(v) => Some(*v), // _ => None, // } // } // } impl Value { pub fn is_truthy(&self) -> bool { match self { Value::Int(v) => *v != 0, Value::Float(v) => *v != 0.0, Value::String(v) => !v.is_empty(), Value::Bool(v) => *v, Value::Char(v) => *v != '\0', Value::Nothing => false, Value::Error => false, } } pub fn value_kind(&self) -> &'static str { match self { Value::Int(_) => "int", Value::Float(_) => "float", Value::String(_) => "string", Value::Bool(_) => "bool", Value::Char(_) => "char", Value::Nothing => "nothing", Value::Error => "error", } } } macro_rules! math_trait { ($trait_name:ty, $function_name:ident) => { impl $trait_name for Value { type Output = Value; fn $function_name(self, other: Value) -> Self::Output { match (self, other) { (Value::Int(lhs), Value::Int(rhs)) => Value::Int(lhs.$function_name(rhs)), (Value::Float(lhs), Value::Float(rhs)) => Value::Float(lhs.$function_name(rhs)), _ => Value::Error, } } } }; } math_trait!(Add, add); math_trait!(Sub, sub); math_trait!(Mul, mul); math_trait!(Div, div); impl Display for Value { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match self { Value::Int(v) => v.to_string(), Value::Float(v) => v.to_string(), Value::String(v) => v.clone(), Value::Bool(v) => v.to_string(), Value::Char(v) => v.to_string(), Value::Nothing => "()".to_string(), Value::Error => "Error".to_string(), } ) } } impl From<()> for Value { fn from(_: ()) -> Self { Value::Nothing } } pub trait FromValue { fn from_value(v: &Value) -> Option<&Self>; } macro_rules! from_impl { ($($type:ty => $variant:ident),+) => { $( impl From<$type> for Value { fn from(v: $type) -> Self { Value::$variant(v) } } impl FromValue for $type{ fn from_value(v: &Value) -> Option<&Self> { match v { Value::$variant(v) => Some(v), _ => None, } } } )+ }; } impl FromValue for Value { fn from_value(v: &Value) -> Option<&Self> { Some(v) } } from_impl!(i32 => Int, f32 => Float, String => String, bool => Bool, char => Char); impl From<&'_ str> for Value { fn from(v: &str) -> Self { Value::String(v.to_string()) } } impl FromValue for str { fn from_value(v: &Value) -> Option<&Self> { match v { Value::String(v) => Some(v.as_str()), _ => None, } } } impl PartialOrd for Value { fn partial_cmp(&self, other: &Self) -> Option { match (self, other) { (Value::Int(lhs), Value::Int(rhs)) => lhs.partial_cmp(rhs), (Value::Float(lhs), Value::Float(rhs)) => lhs.partial_cmp(rhs), (Value::String(lhs), Value::String(rhs)) => lhs.partial_cmp(rhs), (Value::Bool(lhs), Value::Bool(rhs)) => lhs.partial_cmp(rhs), (Value::Char(lhs), Value::Char(rhs)) => lhs.partial_cmp(rhs), (Value::Nothing, Value::Nothing) => Some(std::cmp::Ordering::Equal), (Value::Int(lhs), Value::Float(rhs)) => (*lhs as f32).partial_cmp(rhs), (Value::Float(lhs), Value::Int(rhs)) => lhs.partial_cmp(&(*rhs as f32)), _ => None, } } }