sunflower/src/value/mod.rs

262 lines
7.3 KiB
Rust

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<dyn CustomValue>),
#[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<T>(&self) -> Option<&T>
where
T: Any + 'static,
{
self.as_any().downcast_ref::<T>()
}
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<FunctionMap> {
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<Self>
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<Self> {
match v {
Value::$variant(v) => Some(v.clone()),
_ => None,
}
}
}
)+
};
}
impl FromValue for Value {
fn from_value(v: &Value) -> Option<Self> {
Some(v.clone())
}
}
impl<T: 'static + CustomValue + Clone> FromValue for T {
fn from_value(v: &Value) -> Option<Self> {
match v {
Value::Custom(v) => v.as_any().downcast_ref().cloned(),
_ => None,
}
}
}
impl<T: CustomValue> From<T> 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<std::cmp::Ordering> {
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,
}
}
}