use std::{ any::Any, cmp::Ordering, fmt::Display, ops::{Add, Div, Mul, Rem, Sub}, }; use crate::{env::Env, function::FunctionMap}; use self::custom::CustomValue; pub mod custom; #[derive(Debug, Clone, Default)] pub enum Value { Int(i32), Float(f32), String(String), Bool(bool), Char(char), Custom(Box), #[default] Nothing, Error, } impl Eq for Value {} 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::Custom(c) => c.to_bool(), Value::Nothing | Value::Error => false, } } pub fn value_kind(&self) -> &str { match self { Value::Int(_) => "int", Value::Float(_) => "float", Value::String(_) => "string", Value::Bool(_) => "bool", Value::Char(_) => "char", Value::Custom(c) => c.value_kind(), Value::Nothing => "nothing", Value::Error => "error", } } pub fn as_any(&self) -> &dyn Any { match self { Value::Int(i) => i, Value::Float(f) => f, Value::String(s) => s, Value::Bool(b) => b, Value::Char(c) => c, Value::Custom(c) => c.as_any(), Value::Nothing => &() as &dyn Any, Value::Error => &() as &dyn Any, } } pub fn as_t(&self) -> Option<&T> where T: Any + 'static, { self.as_any().downcast_ref::() } pub fn value_functions() -> FunctionMap { let mut map = FunctionMap::new(); map.insert_native("eq", Self::eq); map.insert_native("ne", Self::ne); map.insert_native("lt", Self::lt); map.insert_native("le", Self::le); map.insert_native("gt", Self::gt); map.insert_native("ge", Self::ge); map } pub fn inner_functions(&self) -> Option { if let Self::Custom(c) = self { c.functions() } else if let Self::Int(_) = self { let mut m = FunctionMap::new(); m.insert_native("is_42", |i: i32| i == 42); Some(m) } else { None } } pub fn call_method( &self, name: &str, env: &Env, _global_functions: &FunctionMap, args: &[Value], ) -> Value { if let Some(i) = self.inner_functions() { match i.call(name, env, args) { Ok(r) => return r, Err(e) => println!("{}", e), } } match Value::value_functions().call(name, env, args) { Ok(r) => return r, Err(e) => println!("{}", e), } Value::Nothing } } 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::Custom(lhs), rhs) => { lhs.$function_name(&rhs) } _ => Value::Error, } } } }; } math_trait!(Add, add); math_trait!(Sub, sub); math_trait!(Mul, mul); math_trait!(Div, div); math_trait!(Rem, rem); impl Display for Value { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Value::Custom(c) = self { c.format(f) } else { 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(), Value::Custom(_) => unreachable!(), } ) } } } impl From<()> for Value { fn from(_: ()) -> Self { Value::Nothing } } pub trait FromValue { fn from_value(v: &Value) -> Option where Self: Sized; } 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 { match v { Value::$variant(v) => Some(v.clone()), _ => None, } } } )+ }; } impl FromValue for Value { fn from_value(v: &Value) -> Option { Some(v.clone()) } } impl FromValue for T { fn from_value(v: &Value) -> Option { match v { Value::Custom(v) => v.as_any().downcast_ref().cloned(), _ => None, } } } impl From for Value { fn from(v: T) -> Self { Value::Custom(v.clone_box()) } } 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 PartialEq for Value { fn eq(&self, other: &Self) -> bool { match (self, other) { (Value::Int(lhs), Value::Int(rhs)) => lhs == rhs, (Value::Float(lhs), Value::Float(rhs)) => lhs == rhs, (Value::String(lhs), Value::String(rhs)) => lhs == rhs, (Value::Bool(lhs), Value::Bool(rhs)) => lhs == rhs, (Value::Char(lhs), Value::Char(rhs)) => lhs == rhs, (Value::Custom(lhs), other) => lhs.as_ref() == other, (other, Value::Custom(rhs)) => rhs.as_ref() == other, (Value::Nothing, Value::Nothing) | (Value::Error, Value::Error) => true, _ => false, } } } 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)), (Value::Custom(lhs), other) => lhs.as_ref().partial_cmp(other), (other, Value::Custom(rhs)) => rhs.as_ref().partial_cmp(other).map(Ordering::reverse), _ => None, } } }