use core::{
any::{type_name, Any},
fmt,
};
use arithmetic_parser::Location;
pub use self::{
function::{CallContext, Function, InterpretedFn, NativeFn},
object::Object,
tuple::Tuple,
};
use crate::{
alloc::{Arc, Vec},
fns,
};
mod function;
mod object;
mod ops;
mod tuple;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ValueType {
Prim,
Bool,
Function,
Tuple(usize),
Object,
Array,
Ref,
}
impl fmt::Display for ValueType {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Prim => formatter.write_str("primitive value"),
Self::Bool => formatter.write_str("boolean value"),
Self::Function => formatter.write_str("function"),
Self::Tuple(1) => formatter.write_str("tuple with 1 element"),
Self::Object => formatter.write_str("object"),
Self::Tuple(size) => write!(formatter, "tuple with {size} elements"),
Self::Array => formatter.write_str("array"),
Self::Ref => formatter.write_str("reference"),
}
}
}
#[derive(Clone)]
pub struct OpaqueRef {
value: Arc<dyn Any>,
type_name: &'static str,
dyn_eq: fn(&dyn Any, &dyn Any) -> bool,
dyn_fmt: fn(&dyn Any, &mut fmt::Formatter<'_>) -> fmt::Result,
}
impl OpaqueRef {
#[allow(clippy::missing_panics_doc)] pub fn new<T>(value: T) -> Self
where
T: Any + fmt::Debug + PartialEq,
{
Self {
value: Arc::new(value),
type_name: type_name::<T>(),
dyn_eq: |this, other| {
let this_cast = this.downcast_ref::<T>().unwrap();
other
.downcast_ref::<T>()
.map_or(false, |other_cast| other_cast == this_cast)
},
dyn_fmt: |this, formatter| {
let this_cast = this.downcast_ref::<T>().unwrap();
fmt::Debug::fmt(this_cast, formatter)
},
}
}
#[allow(clippy::missing_panics_doc)] pub fn with_identity_eq<T: Any>(value: T) -> Self {
Self {
value: Arc::new(value),
type_name: type_name::<T>(),
dyn_eq: |this, other| {
let this_data = (this as *const dyn Any).cast::<()>();
let other_data = (other as *const dyn Any).cast::<()>();
this_data == other_data
},
dyn_fmt: |this, formatter| fmt::Debug::fmt(&this.type_id(), formatter),
}
}
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
self.value.downcast_ref()
}
}
impl PartialEq for OpaqueRef {
fn eq(&self, other: &Self) -> bool {
(self.dyn_eq)(self.value.as_ref(), other.value.as_ref())
}
}
impl fmt::Debug for OpaqueRef {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_tuple("OpaqueRef")
.field(&self.value.as_ref())
.finish()
}
}
impl fmt::Display for OpaqueRef {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}::", self.type_name)?;
(self.dyn_fmt)(self.value.as_ref(), formatter)
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum Value<T> {
Prim(T),
Bool(bool),
Function(Function<T>),
Tuple(Tuple<T>),
Object(Object<T>),
Ref(OpaqueRef),
}
pub type SpannedValue<T> = Location<Value<T>>;
impl<T> Value<T> {
pub fn native_fn(function: impl NativeFn<T> + 'static) -> Self {
Self::Function(Function::Native(Arc::new(function)))
}
pub fn wrapped_fn<const CTX: bool, Args, F>(fn_to_wrap: F) -> Self
where
fns::FnWrapper<Args, F, CTX>: NativeFn<T> + 'static,
{
let wrapped = fns::wrap::<CTX, Args, _>(fn_to_wrap);
Self::native_fn(wrapped)
}
pub(crate) fn interpreted_fn(function: InterpretedFn<T>) -> Self {
Self::Function(Function::Interpreted(Arc::new(function)))
}
pub const fn void() -> Self {
Self::Tuple(Tuple::void())
}
pub fn opaque_ref(value: impl Any + fmt::Debug + PartialEq) -> Self {
Self::Ref(OpaqueRef::new(value))
}
pub fn value_type(&self) -> ValueType {
match self {
Self::Prim(_) => ValueType::Prim,
Self::Bool(_) => ValueType::Bool,
Self::Function(_) => ValueType::Function,
Self::Tuple(elements) => ValueType::Tuple(elements.len()),
Self::Object(_) => ValueType::Object,
Self::Ref(_) => ValueType::Ref,
}
}
pub fn is_void(&self) -> bool {
matches!(self, Self::Tuple(tuple) if tuple.is_empty())
}
pub fn is_function(&self) -> bool {
matches!(self, Self::Function(_))
}
pub(crate) fn as_object(&self) -> Option<&Object<T>> {
match self {
Self::Object(object) => Some(object),
_ => None,
}
}
}
impl<T> From<Vec<Self>> for Value<T> {
fn from(elements: Vec<Self>) -> Self {
Self::Tuple(Tuple::from(elements))
}
}
impl<T: Clone> From<&Value<T>> for Value<T> {
fn from(reference: &Value<T>) -> Self {
reference.clone()
}
}
impl<T: PartialEq> PartialEq for Value<T> {
fn eq(&self, rhs: &Self) -> bool {
match (self, rhs) {
(Self::Prim(this), Self::Prim(other)) => this == other,
(Self::Bool(this), Self::Bool(other)) => this == other,
(Self::Tuple(this), Self::Tuple(other)) => this == other,
(Self::Object(this), Self::Object(other)) => this == other,
(Self::Function(this), Self::Function(other)) => this == other,
(Self::Ref(this), Self::Ref(other)) => this == other,
_ => false,
}
}
}
impl<T: fmt::Display> fmt::Display for Value<T> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Prim(value) => fmt::Display::fmt(value, formatter),
Self::Bool(true) => formatter.write_str("true"),
Self::Bool(false) => formatter.write_str("false"),
Self::Ref(opaque_ref) => fmt::Display::fmt(opaque_ref, formatter),
Self::Function(function) => fmt::Display::fmt(function, formatter),
Self::Object(object) => fmt::Display::fmt(object, formatter),
Self::Tuple(tuple) => fmt::Display::fmt(tuple, formatter),
}
}
}
#[cfg(test)]
mod tests {
use core::cmp::Ordering;
use super::*;
#[test]
fn opaque_ref_equality() {
let value = Value::<f32>::opaque_ref(Ordering::Less);
let same_value = Value::<f32>::opaque_ref(Ordering::Less);
assert_eq!(value, same_value);
assert_eq!(value, value.clone());
let other_value = Value::<f32>::opaque_ref(Ordering::Greater);
assert_ne!(value, other_value);
}
#[test]
fn opaque_ref_formatting() {
let value = OpaqueRef::new(Ordering::Less);
assert_eq!(value.to_string(), "core::cmp::Ordering::Less");
}
}