cao_lang/
value.rs

1use crate::prelude::CaoLangTable;
2use crate::vm::runtime::cao_lang_object::{CaoLangObject, CaoLangObjectBody};
3use std::convert::{From, TryFrom};
4use std::ops::{Add, Div, Mul, Sub};
5use std::ptr::NonNull;
6
7#[derive(Default, Clone, Copy)]
8pub enum Value {
9    #[default]
10    Nil,
11    Object(NonNull<CaoLangObject>),
12    Integer(i64),
13    Real(f64),
14}
15
16impl std::fmt::Debug for Value {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        match self {
19            Self::Nil => write!(f, "Nil"),
20            Self::Object(arg0) => f
21                .debug_tuple("Object")
22                .field(&arg0)
23                .field(unsafe { arg0.as_ref() })
24                .finish(),
25            Self::Integer(arg0) => f.debug_tuple("Integer").field(arg0).finish(),
26            Self::Real(arg0) => f.debug_tuple("Real").field(arg0).finish(),
27        }
28    }
29}
30
31impl PartialOrd for Value {
32    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
33        let (this, other) = self.try_cast_match(*other);
34        match (this, other) {
35            (Value::Object(a), Value::Object(b)) => unsafe { a.as_ref().partial_cmp(b.as_ref()) },
36            (Value::Integer(a), Value::Integer(b)) => a.partial_cmp(&b),
37            (Value::Real(a), Value::Real(b)) => a.partial_cmp(&b),
38            _ => None,
39        }
40    }
41}
42
43impl std::hash::Hash for Value {
44    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
45        match self {
46            Value::Nil => 0u8.hash(state),
47            Value::Integer(i) => {
48                i.hash(state);
49            }
50            Value::Real(f) => {
51                f.to_bits().hash(state);
52            }
53            Value::Object(o) => unsafe {
54                o.as_ref().hash(state);
55            },
56        }
57    }
58}
59
60impl PartialEq for Value {
61    fn eq(&self, other: &Self) -> bool {
62        match (*self, *other) {
63            (Value::Nil, Value::Nil) => true,
64            (Value::Object(lhs), Value::Object(rhs)) => unsafe { lhs.as_ref().eq(rhs.as_ref()) },
65            (Value::Integer(lhs), Value::Integer(rhs)) => lhs == rhs,
66            (Value::Real(lhs), Value::Real(rhs)) => lhs == rhs,
67            _ => false,
68        }
69    }
70}
71
72impl Eq for Value {}
73
74/// Intended for saving `Values` after the program has finished executing
75///
76/// ```
77/// use cao_lang::prelude::*;
78///
79/// let mut vm = Vm::new(()).unwrap();
80/// // init an object `val` with 1 entry {'pog': 42}
81/// let mut obj = vm.init_table().unwrap();
82/// let pog = vm.init_string("pog").unwrap();
83/// obj.as_table_mut()
84///     .unwrap()
85///     .insert(Value::Object(pog.into_inner()), 42)
86///     .unwrap();
87/// let val = Value::Object(obj.into_inner());
88///
89/// let owned = OwnedValue::try_from(val).unwrap();
90///
91/// // (de)serialize the owned object...
92///
93/// // new vm instance
94/// let mut vm = Vm::new(()).unwrap();
95/// let loaded = vm.insert_value(&owned).unwrap();
96///
97/// # // check the contents
98/// # let loaded_table = vm.get_table(loaded).unwrap();
99/// # assert_eq!(loaded_table.len(), 1);
100/// # for (k, v) in loaded_table.iter() {
101/// #     let k = unsafe { k.as_str().unwrap() };
102/// #     let v = v.as_int().unwrap();
103
104/// #     assert_eq!(k, "pog");
105/// #     assert_eq!(v, 42);
106/// # }
107/// ```
108#[derive(Default, Debug, Clone)]
109#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
110pub enum OwnedValue {
111    #[default]
112    Nil,
113    String(String),
114    Table(Vec<OwnedEntry>),
115    Integer(i64),
116    Real(f64),
117}
118
119#[derive(Debug, Clone)]
120#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
121pub struct OwnedEntry {
122    pub key: OwnedValue,
123    pub value: OwnedValue,
124}
125
126impl TryFrom<Value> for OwnedValue {
127    type Error = Value;
128
129    fn try_from(v: Value) -> Result<Self, Self::Error> {
130        let res = match v {
131            Value::Nil => Self::Nil,
132            Value::Object(ptr) => unsafe {
133                match &ptr.as_ref().body {
134                    CaoLangObjectBody::Table(t) => {
135                        let mut entries = Vec::with_capacity(t.len());
136                        for (k, v) in t.iter() {
137                            entries.push(OwnedEntry {
138                                key: (*k).try_into()?,
139                                value: (*v).try_into()?,
140                            });
141                        }
142                        Self::Table(entries)
143                    }
144                    CaoLangObjectBody::String(s) => Self::String(s.as_str().to_owned()),
145                    CaoLangObjectBody::Function(_)
146                    | CaoLangObjectBody::Closure(_)
147                    | CaoLangObjectBody::NativeFunction(_)
148                    | CaoLangObjectBody::Upvalue(_) => {
149                        return Err(v);
150                    }
151                }
152            },
153            Value::Integer(x) => Self::Integer(x),
154            Value::Real(x) => Self::Real(x),
155        };
156        Ok(res)
157    }
158}
159
160impl Value {
161    #[inline]
162    pub fn as_bool(self) -> bool {
163        match self {
164            Value::Object(i) => unsafe { !i.as_ref().is_empty() },
165            Value::Integer(i) => i != 0,
166            Value::Real(i) => i != 0.0,
167            Value::Nil => false,
168        }
169    }
170
171    /// Typename of this value
172    pub fn type_name(self) -> &'static str {
173        match self {
174            Value::Nil => "Nil",
175            Value::Object(o) => unsafe { o.as_ref().type_name() },
176            Value::Integer(_) => "Integer",
177            Value::Real(_) => "Real",
178        }
179    }
180
181    #[inline]
182    pub fn is_float(self) -> bool {
183        matches!(self, Value::Real(_))
184    }
185
186    /// # Safety
187    ///
188    /// Must be called with ptr obtained from a `string_literal` instruction, before the last `clear`!
189    ///
190    /// The Vm that allocated the string must still be in memory!
191    ///
192    /// # Return
193    ///
194    /// Returns `None` if the value is not a string, or points to an invalid string
195    pub unsafe fn as_str<'a>(self) -> Option<&'a str> {
196        match self {
197            Value::Object(o) => unsafe { o.as_ref().as_str() },
198            _ => None,
199        }
200    }
201
202    /// # Safety
203    ///
204    /// Must be called with ptr obtained from a vm , before the last `clear`!
205    ///
206    /// The Vm that allocated the table must still be in memory!
207    ///
208    /// # Return
209    ///
210    /// Returns `None` if the value is not a table, or points to an invalid table
211    pub unsafe fn as_table<'a>(self) -> Option<&'a CaoLangTable> {
212        match self {
213            Value::Object(table) => table.as_ref().as_table(),
214            _ => None,
215        }
216    }
217
218    pub fn as_int(self) -> Option<i64> {
219        match self {
220            Value::Integer(x) => Some(x),
221            _ => None,
222        }
223    }
224
225    pub fn as_real(self) -> Option<f64> {
226        match self {
227            Value::Real(x) => Some(x),
228            _ => None,
229        }
230    }
231
232    #[inline]
233    pub fn is_obj(self) -> bool {
234        matches!(self, Value::Object(_))
235    }
236
237    #[inline]
238    pub fn is_integer(self) -> bool {
239        matches!(self, Value::Integer(_))
240    }
241
242    #[inline]
243    pub fn is_null(self) -> bool {
244        matches!(self, Value::Nil)
245    }
246
247    /// return the original pair if casting can't be performed
248    fn try_cast_match(self, other: Self) -> (Self, Self) {
249        if self.is_float() || other.is_float() {
250            if let Ok(a) = f64::try_from(self) {
251                if let Ok(b) = f64::try_from(other) {
252                    return (Value::Real(a), Value::Real(b));
253                }
254            }
255        }
256        if self.is_integer() || other.is_integer() {
257            if let Ok(a) = i64::try_from(self) {
258                if let Ok(b) = i64::try_from(other) {
259                    return (Value::Integer(a), Value::Integer(b));
260                }
261            }
262        }
263        (self, other)
264    }
265}
266
267impl TryFrom<Value> for &str {
268    type Error = Value;
269
270    fn try_from(value: Value) -> Result<Self, Self::Error> {
271        match value {
272            Value::Object(o) => unsafe { o.as_ref().as_str().ok_or(value) },
273            _ => Err(value),
274        }
275    }
276}
277
278impl From<Value> for bool {
279    fn from(s: Value) -> Self {
280        s.as_bool()
281    }
282}
283
284impl TryFrom<Value> for *mut CaoLangTable {
285    type Error = Value;
286
287    fn try_from(v: Value) -> Result<Self, Value> {
288        match v {
289            Value::Object(mut p) => unsafe {
290                match p.as_mut().as_table_mut() {
291                    Some(t) => Ok(t as *mut _),
292                    _ => Err(v),
293                }
294            },
295            _ => Err(v),
296        }
297    }
298}
299
300impl TryFrom<Value> for &CaoLangTable {
301    type Error = Value;
302
303    fn try_from(v: Value) -> Result<Self, Value> {
304        match v {
305            Value::Object(p) => unsafe { p.as_ref().as_table().ok_or(v) },
306            _ => Err(v),
307        }
308    }
309}
310
311impl TryFrom<Value> for &mut CaoLangTable {
312    type Error = Value;
313
314    fn try_from(v: Value) -> Result<Self, Value> {
315        match v {
316            Value::Object(mut p) => unsafe { p.as_mut().as_table_mut().ok_or(v) },
317            _ => Err(v),
318        }
319    }
320}
321
322impl TryFrom<Value> for i64 {
323    type Error = Value;
324
325    fn try_from(v: Value) -> Result<Self, Value> {
326        match v {
327            Value::Integer(i) => Ok(i),
328            Value::Real(r) => Ok(r as i64),
329            Value::Object(o) => Ok(unsafe { o.as_ref().len() as i64 }),
330            Value::Nil => Ok(0),
331        }
332    }
333}
334
335impl TryFrom<Value> for f64 {
336    type Error = Value;
337
338    fn try_from(v: Value) -> Result<Self, Value> {
339        match v {
340            Value::Real(i) => Ok(i),
341            Value::Integer(i) => Ok(i as f64),
342            Value::Object(o) => Ok(unsafe { o.as_ref().len() as f64 }),
343            Value::Nil => Ok(0.0),
344        }
345    }
346}
347
348impl From<i64> for Value {
349    fn from(i: i64) -> Self {
350        Value::Integer(i)
351    }
352}
353
354impl From<bool> for Value {
355    fn from(i: bool) -> Self {
356        Value::Integer(i as i64)
357    }
358}
359
360macro_rules! binary_op {
361    ($a: expr, $b: expr, $op: tt) => {
362        {
363        let (a, b) = $a.try_cast_match($b);
364        match (a, b) {
365            (Value::Integer(a), Value::Integer(b)) => {
366                    Value::Integer(a $op b)
367            }
368            (Value::Real(a), Value::Real(b)) => Value::Real(a $op b),
369            _ => Value::Nil
370        }
371        }
372    }
373}
374
375impl Add for Value {
376    type Output = Self;
377
378    fn add(self, other: Self) -> Self {
379        binary_op!(self, other, +)
380    }
381}
382
383impl Sub for Value {
384    type Output = Self;
385
386    fn sub(self, other: Self) -> Self {
387        binary_op!(self, other, -)
388    }
389}
390
391impl Mul for Value {
392    type Output = Self;
393
394    fn mul(self, other: Self) -> Self {
395        binary_op!(self, other, *)
396    }
397}
398
399impl Div for Value {
400    type Output = Self;
401
402    fn div(self, other: Self) -> Self {
403        let (a, b) = self.try_cast_match(other);
404        match (a, b) {
405            (Value::Integer(a), Value::Integer(b)) => Value::Real(a as f64 / b as f64),
406            (Value::Real(a), Value::Real(b)) => Value::Real(a / b),
407            _ => Value::Nil,
408        }
409    }
410}
411
412impl std::borrow::Borrow<str> for Value {
413    fn borrow(&self) -> &str {
414        match self {
415            Value::Object(s) => unsafe { s.as_ref().as_str().unwrap_or("") },
416            _ => "",
417        }
418    }
419}
420
421impl std::borrow::Borrow<i64> for Value {
422    fn borrow(&self) -> &i64 {
423        match self {
424            Value::Integer(i) => i,
425            _ => &(!0),
426        }
427    }
428}
429
430/// We can't implement `TryFrom<Value>` for `Option<T>`'s, you can use this wrapper in functions to
431/// take an optional value
432pub struct Nilable<T>(pub Option<T>);
433
434impl<T> TryFrom<Value> for Nilable<T>
435where
436    T: TryFrom<Value>,
437{
438    type Error = Value;
439
440    fn try_from(value: Value) -> Result<Self, Self::Error> {
441        match value {
442            Value::Nil => Ok(Nilable(None)),
443            _ => {
444                let res = value.try_into().map_err(|_| value)?;
445                Ok(Nilable(Some(res)))
446            }
447        }
448    }
449}