sunflower/src/value.rs

186 lines
4.9 KiB
Rust

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<i32> {
// match self {
// Value::Int(v) => Some(*v),
// _ => None,
// }
// }
// pub fn as_float(&self) -> Option<f32> {
// match self {
// Value::Float(v) => Some(*v),
// _ => None,
// }
// }
// pub fn as_string(&self) -> Option<String> {
// match self {
// Value::String(v) => Some(v.clone()),
// _ => None,
// }
// }
// pub fn as_bool(&self) -> Option<bool> {
// match self {
// Value::Bool(v) => Some(*v),
// _ => None,
// }
// }
// pub fn as_char(&self) -> Option<char> {
// 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<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)),
_ => None,
}
}
}