259 lines
7.2 KiB
Rust
259 lines
7.2 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::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,
|
|
}
|
|
}
|
|
}
|