1use hashbrown::HashMap;
4
5use core::{
6 any::{type_name, Any},
7 fmt,
8};
9
10use crate::{
11 alloc::{vec, Rc, String, Vec},
12 fns,
13};
14use arithmetic_parser::{MaybeSpanned, StripCode};
15
16mod env;
17mod function;
18mod ops;
19mod variable_map;
20
21pub use self::{
22 env::Environment,
23 function::{CallContext, Function, InterpretedFn, NativeFn},
24 variable_map::{Assertions, Comparisons, Prelude, VariableMap},
25};
26
27#[derive(Debug, Clone, Copy, PartialEq)]
29#[non_exhaustive]
30pub enum ValueType {
31 Prim,
33 Bool,
35 Function,
37 Tuple(usize),
39 Object,
41 Array,
46 Ref,
48}
49
50impl fmt::Display for ValueType {
51 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
52 match self {
53 Self::Prim => formatter.write_str("primitive value"),
54 Self::Bool => formatter.write_str("boolean value"),
55 Self::Function => formatter.write_str("function"),
56 Self::Tuple(1) => formatter.write_str("tuple with 1 element"),
57 Self::Object => formatter.write_str("object"),
58 Self::Tuple(size) => write!(formatter, "tuple with {} elements", size),
59 Self::Array => formatter.write_str("array"),
60 Self::Ref => formatter.write_str("reference"),
61 }
62 }
63}
64
65pub struct OpaqueRef {
76 value: Rc<dyn Any>,
77 type_name: &'static str,
78 dyn_eq: fn(&dyn Any, &dyn Any) -> bool,
79 dyn_fmt: fn(&dyn Any, &mut fmt::Formatter<'_>) -> fmt::Result,
80}
81
82#[allow(renamed_and_removed_lints, clippy::unknown_clippy_lints)]
83impl OpaqueRef {
86 #[allow(clippy::missing_panics_doc)] pub fn new<T>(value: T) -> Self
91 where
92 T: Any + fmt::Debug + PartialEq,
93 {
94 Self {
95 value: Rc::new(value),
96 type_name: type_name::<T>(),
97
98 dyn_eq: |this, other| {
99 let this_cast = this.downcast_ref::<T>().unwrap();
100 other
101 .downcast_ref::<T>()
102 .map_or(false, |other_cast| other_cast == this_cast)
103 },
104 dyn_fmt: |this, formatter| {
105 let this_cast = this.downcast_ref::<T>().unwrap();
106 fmt::Debug::fmt(this_cast, formatter)
107 },
108 }
109 }
110
111 #[allow(clippy::missing_panics_doc)] pub fn with_identity_eq<T>(value: T) -> Self
117 where
118 T: Any + fmt::Debug,
119 {
120 Self {
121 value: Rc::new(value),
122 type_name: type_name::<T>(),
123
124 dyn_eq: |this, other| {
125 let this_data = (this as *const dyn Any).cast::<()>();
126 let other_data = (other as *const dyn Any).cast::<()>();
127 this_data == other_data
128 },
129 dyn_fmt: |this, formatter| {
130 let this_cast = this.downcast_ref::<T>().unwrap();
131 fmt::Debug::fmt(this_cast, formatter)
132 },
133 }
134 }
135
136 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
138 self.value.downcast_ref()
139 }
140}
141
142impl Clone for OpaqueRef {
143 fn clone(&self) -> Self {
144 Self {
145 value: Rc::clone(&self.value),
146 type_name: self.type_name,
147 dyn_eq: self.dyn_eq,
148 dyn_fmt: self.dyn_fmt,
149 }
150 }
151}
152
153impl PartialEq for OpaqueRef {
154 fn eq(&self, other: &Self) -> bool {
155 (self.dyn_eq)(self.value.as_ref(), other.value.as_ref())
156 }
157}
158
159impl fmt::Debug for OpaqueRef {
160 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
161 formatter
162 .debug_tuple("OpaqueRef")
163 .field(&self.value.as_ref())
164 .finish()
165 }
166}
167
168impl fmt::Display for OpaqueRef {
169 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
170 write!(formatter, "{}::", self.type_name)?;
171 (self.dyn_fmt)(self.value.as_ref(), formatter)
172 }
173}
174
175#[derive(Debug)]
177#[non_exhaustive]
178pub enum Value<'a, T> {
179 Prim(T),
185 Bool(bool),
187 Function(Function<'a, T>),
189 Tuple(Vec<Value<'a, T>>),
191 Object(HashMap<String, Value<'a, T>>),
193 Ref(OpaqueRef),
195}
196
197pub type SpannedValue<'a, T> = MaybeSpanned<'a, Value<'a, T>>;
199
200impl<'a, T> Value<'a, T> {
201 pub fn native_fn(function: impl NativeFn<T> + 'static) -> Self {
203 Self::Function(Function::Native(Rc::new(function)))
204 }
205
206 pub fn wrapped_fn<Args, F>(fn_to_wrap: F) -> Self
214 where
215 fns::FnWrapper<Args, F>: NativeFn<T> + 'static,
216 {
217 let wrapped = fns::wrap::<Args, _>(fn_to_wrap);
218 Self::native_fn(wrapped)
219 }
220
221 pub(crate) fn interpreted_fn(function: InterpretedFn<'a, T>) -> Self {
223 Self::Function(Function::Interpreted(Rc::new(function)))
224 }
225
226 pub fn void() -> Self {
228 Self::Tuple(vec![])
229 }
230
231 pub fn opaque_ref(value: impl Any + fmt::Debug + PartialEq) -> Self {
233 Self::Ref(OpaqueRef::new(value))
234 }
235
236 pub fn value_type(&self) -> ValueType {
238 match self {
239 Self::Prim(_) => ValueType::Prim,
240 Self::Bool(_) => ValueType::Bool,
241 Self::Function(_) => ValueType::Function,
242 Self::Tuple(elements) => ValueType::Tuple(elements.len()),
243 Self::Object(_) => ValueType::Object,
244 Self::Ref(_) => ValueType::Ref,
245 }
246 }
247
248 pub fn is_void(&self) -> bool {
250 matches!(self, Self::Tuple(tuple) if tuple.is_empty())
251 }
252
253 pub fn is_function(&self) -> bool {
255 matches!(self, Self::Function(_))
256 }
257}
258
259impl<T: Clone> Clone for Value<'_, T> {
260 fn clone(&self) -> Self {
261 match self {
262 Self::Prim(lit) => Self::Prim(lit.clone()),
263 Self::Bool(bool) => Self::Bool(*bool),
264 Self::Function(function) => Self::Function(function.clone()),
265 Self::Tuple(tuple) => Self::Tuple(tuple.clone()),
266 Self::Object(fields) => Self::Object(fields.clone()),
267 Self::Ref(reference) => Self::Ref(reference.clone()),
268 }
269 }
270}
271
272impl<T: 'static + Clone> StripCode for Value<'_, T> {
273 type Stripped = Value<'static, T>;
274
275 fn strip_code(self) -> Self::Stripped {
276 match self {
277 Self::Prim(lit) => Value::Prim(lit),
278 Self::Bool(bool) => Value::Bool(bool),
279 Self::Function(function) => Value::Function(function.strip_code()),
280 Self::Tuple(tuple) => {
281 Value::Tuple(tuple.into_iter().map(StripCode::strip_code).collect())
282 }
283 Self::Object(fields) => Value::Object(
284 fields
285 .into_iter()
286 .map(|(name, value)| (name, value.strip_code()))
287 .collect(),
288 ),
289 Self::Ref(reference) => Value::Ref(reference),
290 }
291 }
292}
293
294impl<'a, T: Clone> From<&Value<'a, T>> for Value<'a, T> {
295 fn from(reference: &Value<'a, T>) -> Self {
296 reference.clone()
297 }
298}
299
300impl<T: PartialEq> PartialEq for Value<'_, T> {
301 fn eq(&self, rhs: &Self) -> bool {
302 match (self, rhs) {
303 (Self::Prim(this), Self::Prim(other)) => this == other,
304 (Self::Bool(this), Self::Bool(other)) => this == other,
305 (Self::Tuple(this), Self::Tuple(other)) => this == other,
306 (Self::Object(this), Self::Object(other)) => this == other,
307 (Self::Function(this), Self::Function(other)) => this.is_same_function(other),
308 (Self::Ref(this), Self::Ref(other)) => this == other,
309 _ => false,
310 }
311 }
312}
313
314impl<T: fmt::Display> fmt::Display for Value<'_, T> {
315 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
316 match self {
317 Self::Prim(value) => fmt::Display::fmt(value, formatter),
318 Self::Bool(true) => formatter.write_str("true"),
319 Self::Bool(false) => formatter.write_str("false"),
320 Self::Ref(opaque_ref) => fmt::Display::fmt(opaque_ref, formatter),
321 Self::Function(_) => formatter.write_str("[function]"),
322 Self::Object(fields) => {
323 formatter.write_str("#{ ")?;
324 for (name, value) in fields.iter() {
325 write!(formatter, "{} = {}; ", name, value)?;
326 }
327 formatter.write_str("}")
328 }
329 Self::Tuple(elements) => {
330 formatter.write_str("(")?;
331 for (i, element) in elements.iter().enumerate() {
332 fmt::Display::fmt(element, formatter)?;
333 if i + 1 < elements.len() {
334 formatter.write_str(", ")?;
335 }
336 }
337 formatter.write_str(")")
338 }
339 }
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346
347 use core::cmp::Ordering;
348
349 #[test]
350 fn opaque_ref_equality() {
351 let value = Value::<f32>::opaque_ref(Ordering::Less);
352 let same_value = Value::<f32>::opaque_ref(Ordering::Less);
353 assert_eq!(value, same_value);
354 assert_eq!(value, value.clone());
355 let other_value = Value::<f32>::opaque_ref(Ordering::Greater);
356 assert_ne!(value, other_value);
357 }
358
359 #[test]
360 fn opaque_ref_formatting() {
361 let value = OpaqueRef::new(Ordering::Less);
362 assert_eq!(value.to_string(), "core::cmp::Ordering::Less");
363 }
364}