arithmetic_eval/values/
mod.rs

1//! Values used by the interpreter.
2
3use core::{
4    any::{type_name, Any},
5    fmt,
6};
7
8use arithmetic_parser::Location;
9
10pub use self::{
11    function::{CallContext, Function, InterpretedFn, NativeFn},
12    object::Object,
13    tuple::Tuple,
14};
15use crate::{
16    alloc::{Arc, Vec},
17    fns,
18};
19
20mod function;
21mod object;
22mod ops;
23mod tuple;
24
25/// Possible high-level types of [`Value`]s.
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
27#[non_exhaustive]
28pub enum ValueType {
29    /// Primitive type other than `Bool`ean.
30    Prim,
31    /// Boolean value.
32    Bool,
33    /// Function value.
34    Function,
35    /// Tuple of a specific size.
36    Tuple(usize),
37    /// Object.
38    Object,
39    /// Array (a tuple of arbitrary size).
40    ///
41    /// This variant is never returned from [`Value::value_type()`]; at the same time, it is
42    /// used for error reporting etc.
43    Array,
44    /// Opaque reference to a value.
45    Ref,
46}
47
48impl fmt::Display for ValueType {
49    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
50        match self {
51            Self::Prim => formatter.write_str("primitive value"),
52            Self::Bool => formatter.write_str("boolean value"),
53            Self::Function => formatter.write_str("function"),
54            Self::Tuple(1) => formatter.write_str("tuple with 1 element"),
55            Self::Object => formatter.write_str("object"),
56            Self::Tuple(size) => write!(formatter, "tuple with {size} elements"),
57            Self::Array => formatter.write_str("array"),
58            Self::Ref => formatter.write_str("reference"),
59        }
60    }
61}
62
63/// Opaque reference to a native value.
64///
65/// The references cannot be created by interpreted code, but can be used as function args
66/// or return values of native functions. References are [`Arc`]'d, thus can easily be cloned.
67///
68/// References are comparable among each other:
69///
70/// - If the wrapped value implements [`PartialEq`], this implementation will be used
71///   for comparison.
72/// - If `PartialEq` is not implemented, the comparison is by the `Arc` pointer.
73#[derive(Clone)]
74pub struct OpaqueRef {
75    value: Arc<dyn Any>,
76    type_name: &'static str,
77    dyn_eq: fn(&dyn Any, &dyn Any) -> bool,
78    dyn_fmt: fn(&dyn Any, &mut fmt::Formatter<'_>) -> fmt::Result,
79}
80
81impl OpaqueRef {
82    /// Creates a reference to `value` that implements equality comparison.
83    ///
84    /// Prefer using this method if the wrapped type implements [`PartialEq`].
85    #[allow(clippy::missing_panics_doc)] // false positive; `unwrap()`s never panic
86    pub fn new<T>(value: T) -> Self
87    where
88        T: Any + fmt::Debug + PartialEq,
89    {
90        Self {
91            value: Arc::new(value),
92            type_name: type_name::<T>(),
93
94            dyn_eq: |this, other| {
95                let this_cast = this.downcast_ref::<T>().unwrap();
96                other.downcast_ref::<T>() == Some(this_cast)
97            },
98            dyn_fmt: |this, formatter| {
99                let this_cast = this.downcast_ref::<T>().unwrap();
100                fmt::Debug::fmt(this_cast, formatter)
101            },
102        }
103    }
104
105    /// Creates a reference to `value` with the identity comparison: values are considered
106    /// equal iff they point to the same data.
107    ///
108    /// Prefer [`Self::new()`] when possible.
109    #[allow(clippy::missing_panics_doc)] // false positive; `unwrap()`s never panic
110    pub fn with_identity_eq<T: Any>(value: T) -> Self {
111        Self {
112            value: Arc::new(value),
113            type_name: type_name::<T>(),
114
115            dyn_eq: |this, other| {
116                let this_data = (this as *const dyn Any).cast::<()>();
117                let other_data = (other as *const dyn Any).cast::<()>();
118                this_data == other_data
119            },
120            dyn_fmt: |this, formatter| fmt::Debug::fmt(&this.type_id(), formatter),
121        }
122    }
123
124    /// Tries to downcast this reference to a specific type.
125    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
126        self.value.downcast_ref()
127    }
128}
129
130impl PartialEq for OpaqueRef {
131    fn eq(&self, other: &Self) -> bool {
132        (self.dyn_eq)(self.value.as_ref(), other.value.as_ref())
133    }
134}
135
136impl fmt::Debug for OpaqueRef {
137    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
138        formatter
139            .debug_tuple("OpaqueRef")
140            .field(&self.value.as_ref())
141            .finish()
142    }
143}
144
145impl fmt::Display for OpaqueRef {
146    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
147        write!(formatter, "{}::", self.type_name)?;
148        (self.dyn_fmt)(self.value.as_ref(), formatter)
149    }
150}
151
152/// Values produced by expressions during their interpretation.
153#[derive(Debug, Clone)]
154#[non_exhaustive]
155pub enum Value<T> {
156    /// Primitive value, such as a number. This does not include Boolean values,
157    /// which are a separate variant.
158    ///
159    /// Literals must necessarily map to primitive values, but there may be some primitive values
160    /// not representable as literals.
161    Prim(T),
162    /// Boolean value.
163    Bool(bool),
164    /// Function.
165    Function(Function<T>),
166    /// Tuple of zero or more values.
167    Tuple(Tuple<T>),
168    /// Object with zero or more named fields.
169    Object(Object<T>),
170    /// Opaque reference to a native value.
171    Ref(OpaqueRef),
172}
173
174/// Value together with a span that has produced it.
175pub type SpannedValue<T> = Location<Value<T>>;
176
177impl<T> Value<T> {
178    /// Creates a value for a native function.
179    pub fn native_fn(function: impl NativeFn<T> + 'static) -> Self {
180        Self::Function(Function::Native(Arc::new(function)))
181    }
182
183    /// Creates a [wrapped function](fns::FnWrapper).
184    ///
185    /// Calling this method is equivalent to [`wrap`](fns::wrap)ping a function and calling
186    /// [`Self::native_fn()`] on it. Thanks to type inference magic, the Rust compiler
187    /// will usually be able to extract the `Args` type param from the function definition,
188    /// provided that type of function arguments and its return type are defined explicitly
189    /// or can be unequivocally inferred from the declaration.
190    pub fn wrapped_fn<const CTX: bool, Args, F>(fn_to_wrap: F) -> Self
191    where
192        fns::FnWrapper<Args, F, CTX>: NativeFn<T> + 'static,
193    {
194        let wrapped = fns::wrap::<CTX, Args, _>(fn_to_wrap);
195        Self::native_fn(wrapped)
196    }
197
198    /// Creates a value for an interpreted function.
199    pub(crate) fn interpreted_fn(function: InterpretedFn<T>) -> Self {
200        Self::Function(Function::Interpreted(Arc::new(function)))
201    }
202
203    /// Creates a void value (an empty tuple).
204    pub const fn void() -> Self {
205        Self::Tuple(Tuple::void())
206    }
207
208    /// Creates a reference to a native variable.
209    pub fn opaque_ref(value: impl Any + fmt::Debug + PartialEq) -> Self {
210        Self::Ref(OpaqueRef::new(value))
211    }
212
213    /// Returns the type of this value.
214    pub fn value_type(&self) -> ValueType {
215        match self {
216            Self::Prim(_) => ValueType::Prim,
217            Self::Bool(_) => ValueType::Bool,
218            Self::Function(_) => ValueType::Function,
219            Self::Tuple(elements) => ValueType::Tuple(elements.len()),
220            Self::Object(_) => ValueType::Object,
221            Self::Ref(_) => ValueType::Ref,
222        }
223    }
224
225    /// Checks if this value is void (an empty tuple).
226    pub fn is_void(&self) -> bool {
227        matches!(self, Self::Tuple(tuple) if tuple.is_empty())
228    }
229
230    /// Checks if this value is a function.
231    pub fn is_function(&self) -> bool {
232        matches!(self, Self::Function(_))
233    }
234
235    pub(crate) fn as_object(&self) -> Option<&Object<T>> {
236        match self {
237            Self::Object(object) => Some(object),
238            _ => None,
239        }
240    }
241}
242
243impl<T> From<Vec<Self>> for Value<T> {
244    fn from(elements: Vec<Self>) -> Self {
245        Self::Tuple(Tuple::from(elements))
246    }
247}
248
249impl<T: Clone> From<&Value<T>> for Value<T> {
250    fn from(reference: &Value<T>) -> Self {
251        reference.clone()
252    }
253}
254
255impl<T: PartialEq> PartialEq for Value<T> {
256    fn eq(&self, rhs: &Self) -> bool {
257        match (self, rhs) {
258            (Self::Prim(this), Self::Prim(other)) => this == other,
259            (Self::Bool(this), Self::Bool(other)) => this == other,
260            (Self::Tuple(this), Self::Tuple(other)) => this == other,
261            (Self::Object(this), Self::Object(other)) => this == other,
262            (Self::Function(this), Self::Function(other)) => this == other,
263            (Self::Ref(this), Self::Ref(other)) => this == other,
264            _ => false,
265        }
266    }
267}
268
269impl<T: fmt::Display> fmt::Display for Value<T> {
270    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
271        match self {
272            Self::Prim(value) => fmt::Display::fmt(value, formatter),
273            Self::Bool(true) => formatter.write_str("true"),
274            Self::Bool(false) => formatter.write_str("false"),
275            Self::Ref(opaque_ref) => fmt::Display::fmt(opaque_ref, formatter),
276            Self::Function(function) => fmt::Display::fmt(function, formatter),
277            Self::Object(object) => fmt::Display::fmt(object, formatter),
278            Self::Tuple(tuple) => fmt::Display::fmt(tuple, formatter),
279        }
280    }
281}
282
283#[cfg(test)]
284mod tests {
285    use core::cmp::Ordering;
286
287    use super::*;
288
289    #[test]
290    fn opaque_ref_equality() {
291        let value = Value::<f32>::opaque_ref(Ordering::Less);
292        let same_value = Value::<f32>::opaque_ref(Ordering::Less);
293        assert_eq!(value, same_value);
294        assert_eq!(value, value.clone());
295        let other_value = Value::<f32>::opaque_ref(Ordering::Greater);
296        assert_ne!(value, other_value);
297    }
298
299    #[test]
300    fn opaque_ref_formatting() {
301        let value = OpaqueRef::new(Ordering::Less);
302        assert_eq!(value.to_string(), "core::cmp::Ordering::Less");
303    }
304}