mica_hl/value/
mod.rs

1mod raw;
2
3use std::any::Any;
4use std::borrow::Cow;
5use std::fmt;
6
7use mica_language::gc::Gc;
8use mica_language::value::{self, Closure, Dict, List, RawValue, Struct, Trait, UserData};
9
10use crate::{Error, Object};
11
12pub use raw::*;
13
14/// A GC'd type whose content cannot be safely accessed.
15#[doc(hidden)]
16pub struct Hidden<T>(pub(crate) Gc<T>);
17
18impl<T> Clone for Hidden<T> {
19   fn clone(&self) -> Self {
20      Self(self.0.clone())
21   }
22}
23
24/// A dynamically typed value.
25#[derive(Clone)]
26pub enum Value {
27   Nil,
28   False,
29   True,
30   Number(f64),
31   String(Gc<String>),
32   Function(Hidden<Closure>),
33   Struct(Hidden<Struct>),
34   Trait(Hidden<Trait>),
35   List(Hidden<List>),
36   Dict(Hidden<Dict>),
37   UserData(Gc<Box<dyn UserData>>),
38}
39
40impl Value {
41   /// Returns the name of this value's type.
42   pub fn type_name(&self) -> &str {
43      match self {
44         Value::Nil => "Nil",
45         Value::False => "False",
46         Value::True => "True",
47         Value::Number(_) => "Number",
48         Value::String(_) => "String",
49         Value::Function(_) => "Function",
50         // Hopefully this doesn't explode.
51         Value::Struct(s) => &unsafe { s.0.dtable() }.type_name,
52         Value::Trait(s) => &s.0.dtable().type_name,
53         Value::List(_) => "List",
54         Value::Dict(_) => "Dict",
55         Value::UserData(u) => &unsafe { u.dtable() }.type_name,
56      }
57   }
58
59   /// Returns whether the value is falsy.
60   pub fn is_falsy(&self) -> bool {
61      matches!(self, Self::Nil | Self::False)
62   }
63
64   /// Returns whether the value is truthy.
65   pub fn is_truthy(&self) -> bool {
66      !self.is_falsy()
67   }
68}
69
70impl fmt::Debug for Value {
71   fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72      fmt::Debug::fmt(&self.to_raw_unmanaged(), f)
73   }
74}
75
76impl fmt::Display for Value {
77   fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78      fmt::Display::fmt(&self.to_raw_unmanaged(), f)
79   }
80}
81
82/// **NOTE:** You should generally avoid dealing with raw values.
83#[doc(hidden)]
84impl From<RawValue> for Value {
85   fn from(raw: RawValue) -> Self {
86      Self::from_raw(raw)
87   }
88}
89
90/// The unit type translates to `Value::Nil`.
91impl From<()> for Value {
92   fn from(_: ()) -> Self {
93      Self::Nil
94   }
95}
96
97impl From<bool> for Value {
98   fn from(b: bool) -> Self {
99      match b {
100         true => Self::True,
101         false => Self::False,
102      }
103   }
104}
105
106macro_rules! value_from_number {
107   ($T:ty $(, $doc:literal)?) => {
108      $(#[doc = $doc])?
109      impl From<$T> for Value {
110         fn from(x: $T) -> Self {
111            Value::Number(x as f64)
112         }
113      }
114   };
115}
116
117value_from_number!(i8);
118value_from_number!(i16);
119value_from_number!(i32);
120value_from_number!(i64,   "**NOTE:** This is a lossy conversion, as an `f64` cannot represent the entire range of an `i64`.");
121value_from_number!(isize, "**NOTE:** This is a lossy conversion, as an `f64` cannot represent the entire range of an `isize`.");
122
123value_from_number!(u8);
124value_from_number!(u16);
125value_from_number!(u32);
126value_from_number!(u64,   "**NOTE:** This is a lossy conversion, as an `f64` cannot represent the entire range of a `u64`.");
127value_from_number!(usize, "**NOTE:** This is a lossy conversion, as an `f64` cannot represent the entire range of a `usize`.");
128
129value_from_number!(f32);
130value_from_number!(f64);
131
132impl From<char> for Value {
133   fn from(c: char) -> Self {
134      Self::from(c.to_string())
135   }
136}
137
138impl From<&str> for Value {
139   fn from(s: &str) -> Self {
140      Self::String(Gc::new(s.to_string()))
141   }
142}
143
144impl From<String> for Value {
145   fn from(s: String) -> Self {
146      Self::String(Gc::new(s))
147   }
148}
149
150impl From<Gc<String>> for Value {
151   fn from(s: Gc<String>) -> Self {
152      Self::String(s)
153   }
154}
155
156impl<T> From<Option<T>> for Value
157where
158   T: Into<Value>,
159{
160   fn from(opt: Option<T>) -> Self {
161      match opt {
162         Some(value) => value.into(),
163         None => Value::Nil,
164      }
165   }
166}
167
168/// **NOTE:** You should generally avoid dealing with raw values. This method in particular could
169/// cause you a bad time if you feed temporary `Value`s converted into `RawValue`s into the
170/// vector.
171#[doc(hidden)]
172impl From<Vec<RawValue>> for Value {
173   fn from(v: Vec<RawValue>) -> Self {
174      Value::List(Hidden(Gc::new(List::new(v))))
175   }
176}
177
178/// **NOTE:** Again, you should avoid dealing with raw values. See comment above
179/// (for `From<Vec<RawValue>>`).
180#[doc(hidden)]
181impl From<Dict> for Value {
182   fn from(d: Dict) -> Self {
183      Value::Dict(Hidden(Gc::new(d)))
184   }
185}
186
187impl<T> From<Object<T>> for Value
188where
189   T: Any,
190{
191   fn from(o: Object<T>) -> Self {
192      let user_data: Box<dyn value::UserData> = Box::new(o);
193      Self::UserData(Gc::new(user_data))
194   }
195}
196
197/// Implemented by types that can be constructed from [`Value`]s.
198pub trait TryFromValue
199where
200   Self: Sized,
201{
202   fn try_from_value(value: &Value) -> Result<Self, Error>;
203}
204
205fn type_mismatch(expected: impl Into<Cow<'static, str>>, got: &Value) -> Error {
206   Error::TypeMismatch {
207      expected: expected.into(),
208      got: got.type_name().to_string().into(),
209   }
210}
211
212impl TryFromValue for Value {
213   fn try_from_value(value: &Value) -> Result<Self, Error> {
214      Ok(value.clone())
215   }
216}
217
218/// **NOTE:** You should generally avoid dealing with raw values. This implementation is especially
219/// unsafe as the resulting RawValue is **unmanaged**, which means it may outlive the original value
220/// and cause memory safety issues.
221#[doc(hidden)]
222impl TryFromValue for RawValue {
223   fn try_from_value(value: &Value) -> Result<Self, Error> {
224      Ok(value.to_raw_unmanaged())
225   }
226}
227
228impl TryFromValue for () {
229   fn try_from_value(value: &Value) -> Result<Self, Error> {
230      if let Value::Nil = value {
231         Ok(())
232      } else {
233         Err(type_mismatch("Nil", value))
234      }
235   }
236}
237
238impl TryFromValue for bool {
239   fn try_from_value(value: &Value) -> Result<Self, Error> {
240      match value {
241         Value::True => Ok(true),
242         Value::False => Ok(false),
243         _ => Err(type_mismatch("Boolean", value)),
244      }
245   }
246}
247
248macro_rules! try_from_value_numeric {
249   ($T:ty) => {
250      impl TryFromValue for $T {
251         fn try_from_value(value: &Value) -> Result<Self, Error> {
252            if let Value::Number(number) = value {
253               Ok(*number as $T)
254            } else {
255               Err(type_mismatch("Number", value))
256            }
257         }
258      }
259   };
260}
261
262try_from_value_numeric!(u8);
263try_from_value_numeric!(u16);
264try_from_value_numeric!(u32);
265try_from_value_numeric!(u64);
266try_from_value_numeric!(usize);
267
268try_from_value_numeric!(i8);
269try_from_value_numeric!(i16);
270try_from_value_numeric!(i32);
271try_from_value_numeric!(i64);
272try_from_value_numeric!(isize);
273
274try_from_value_numeric!(f32);
275try_from_value_numeric!(f64);
276
277impl TryFromValue for Gc<String> {
278   fn try_from_value(value: &Value) -> Result<Self, Error> {
279      if let Value::String(s) = value {
280         Ok(Gc::clone(s))
281      } else {
282         Err(type_mismatch("String", value))
283      }
284   }
285}
286
287impl TryFromValue for String {
288   fn try_from_value(value: &Value) -> Result<Self, Error> {
289      <Gc<String>>::try_from_value(value).map(|s| s.to_string())
290   }
291}
292
293impl<T> TryFromValue for Option<T>
294where
295   T: TryFromValue,
296{
297   fn try_from_value(value: &Value) -> Result<Self, Error> {
298      match value {
299         Value::Nil => Ok(None),
300         _ => Ok(Some(T::try_from_value(value).map_err(|error| {
301            if let Error::TypeMismatch { expected, got } = error {
302               Error::TypeMismatch {
303                  expected: format!("{} or Nil", expected).into(),
304                  got,
305               }
306            } else {
307               unreachable!()
308            }
309         })?)),
310      }
311   }
312}