koto_runtime/types/
value.rs

1//! The core value type used in the Koto runtime
2
3use crate::{KFunction, Ptr, Result, prelude::*};
4use std::{
5    fmt::{self, Write},
6    result::Result as StdResult,
7};
8
9/// The core Value type for Koto
10#[derive(Clone, Default)]
11pub enum KValue {
12    /// The default type representing the absence of a value
13    #[default]
14    Null,
15
16    /// A boolean, can be either true or false
17    Bool(bool),
18
19    /// A number, represented as either a signed 64 bit integer or float
20    Number(KNumber),
21
22    /// A range with start/end boundaries
23    Range(KRange),
24
25    /// The list type used in Koto
26    List(KList),
27
28    /// The tuple type used in Koto
29    Tuple(KTuple),
30
31    /// The hash map type used in Koto
32    Map(KMap),
33
34    /// The string type used in Koto
35    Str(KString),
36
37    /// A Koto function
38    Function(KFunction),
39
40    /// A function that's implemented outside of the Koto runtime
41    NativeFunction(KNativeFunction),
42
43    /// The iterator type used in Koto
44    Iterator(KIterator),
45
46    /// An object with behaviour defined via the [`KotoObject`] trait
47    Object(KObject),
48
49    /// A tuple of values that are packed into a contiguous series of registers
50    ///
51    /// Used as an optimization when multiple values are passed around without being assigned to a
52    /// single Tuple value.
53    ///
54    /// Note: this is intended for internal use only.
55    TemporaryTuple(RegisterSlice),
56}
57
58impl KValue {
59    /// Returns a recursive 'deep copy' of a Value
60    ///
61    /// This is used by `koto.deep_copy`.
62    pub fn deep_copy(&self) -> Result<KValue> {
63        let result = match &self {
64            KValue::List(l) => {
65                let result = l
66                    .data()
67                    .iter()
68                    .map(|v| v.deep_copy())
69                    .collect::<Result<_>>()?;
70                KList::with_data(result).into()
71            }
72            KValue::Tuple(t) => {
73                let result = t
74                    .iter()
75                    .map(|v| v.deep_copy())
76                    .collect::<Result<Vec<_>>>()?;
77                KValue::Tuple(result.into())
78            }
79            KValue::Map(m) => {
80                let data = m
81                    .data()
82                    .iter()
83                    .map(|(k, v)| v.deep_copy().map(|v| (k.clone(), v)))
84                    .collect::<Result<_>>()?;
85                let meta = m.meta_map().map(|meta| meta.borrow().clone());
86                KMap::with_contents(data, meta).into()
87            }
88            KValue::Iterator(i) => i.make_copy()?.into(),
89            KValue::Object(o) => o.try_borrow()?.copy().into(),
90            _ => self.clone(),
91        };
92
93        Ok(result)
94    }
95
96    /// Returns true if the value has function-like callable behaviour
97    pub fn is_callable(&self) -> bool {
98        use KValue::*;
99        match self {
100            Function(f) if f.flags.is_generator() => false,
101            Function(_) | NativeFunction(_) => true,
102            Map(m) => m.contains_meta_key(&MetaKey::Call),
103            Object(o) => o.try_borrow().is_ok_and(|o| o.is_callable()),
104            _ => false,
105        }
106    }
107
108    /// Returns true if the value is a generator function
109    pub fn is_generator(&self) -> bool {
110        matches!(self, KValue::Function(f) if f.flags.is_generator())
111    }
112
113    /// Returns true if the value is hashable
114    ///
115    /// Only hashable values are acceptable as map keys.
116    pub fn is_hashable(&self) -> bool {
117        use KValue::*;
118        match self {
119            Null | Bool(_) | Number(_) | Range(_) | Str(_) => true,
120            Tuple(t) => t.is_hashable(),
121            _ => false,
122        }
123    }
124
125    /// Returns true if the value supports `[]` indexing operations
126    pub fn is_indexable(&self) -> bool {
127        use KValue::*;
128        match self {
129            List(_) | Map(_) | Str(_) | Tuple(_) => true,
130            Object(o) => o.try_borrow().is_ok_and(|o| o.size().is_some()),
131            _ => false,
132        }
133    }
134
135    /// Returns true if a [KIterator] can be made from the value
136    pub fn is_iterable(&self) -> bool {
137        use KValue::*;
138        match self {
139            Range(_) | List(_) | Tuple(_) | Str(_) | Iterator(_) => true,
140            Map(m) => {
141                if m.meta_map().is_some() {
142                    m.contains_meta_key(&UnaryOp::Iterator.into())
143                        || m.contains_meta_key(&UnaryOp::Next.into())
144                } else {
145                    true
146                }
147            }
148            Object(o) => o
149                .try_borrow()
150                .is_ok_and(|o| !matches!(o.is_iterable(), IsIterable::NotIterable)),
151            _ => false,
152        }
153    }
154
155    /// Returns true if the values refer to the same underlying data
156    pub fn is_same_instance(&self, other: &Self) -> bool {
157        use KValue::*;
158        match (self, other) {
159            (Map(a), Map(b)) => a.is_same_instance(b),
160            (Object(a), Object(b)) => a.is_same_instance(b),
161            (List(a), List(b)) => a.is_same_instance(b),
162            (Tuple(a), Tuple(b)) => a.is_same_instance(b),
163            _ => false,
164        }
165    }
166
167    /// Returns the value's type as a [KString]
168    pub fn type_as_string(&self) -> KString {
169        use KValue::*;
170        match &self {
171            Null => TYPE_NULL.with(|x| x.clone()),
172            Bool(_) => TYPE_BOOL.with(|x| x.clone()),
173            Number(_) => TYPE_NUMBER.with(|x| x.clone()),
174            List(_) => TYPE_LIST.with(|x| x.clone()),
175            Range { .. } => TYPE_RANGE.with(|x| x.clone()),
176            Map(m) if m.meta_map().is_some() => m
177                .meta_type()
178                .unwrap_or_else(|| TYPE_OBJECT.with(|x| x.clone())),
179            Map(_) => TYPE_MAP.with(|x| x.clone()),
180            Str(_) => TYPE_STRING.with(|x| x.clone()),
181            Tuple(_) => TYPE_TUPLE.with(|x| x.clone()),
182            Function(f) if f.flags.is_generator() => TYPE_GENERATOR.with(|x| x.clone()),
183            Function(_) | NativeFunction(_) => TYPE_FUNCTION.with(|x| x.clone()),
184            Object(o) => o.try_borrow().map_or_else(
185                |_| "Error: object already borrowed".into(),
186                |o| o.type_string(),
187            ),
188            Iterator(_) => TYPE_ITERATOR.with(|x| x.clone()),
189            TemporaryTuple { .. } => TYPE_TEMPORARY_TUPLE.with(|x| x.clone()),
190        }
191    }
192
193    /// Renders the value into the provided display context
194    pub fn display(&self, ctx: &mut DisplayContext) -> Result<()> {
195        use KValue::*;
196        let _ = match self {
197            Null => write!(ctx, "null"),
198            Bool(b) => write!(ctx, "{b}"),
199            Number(n) => write!(ctx, "{n}"),
200            Range(r) => write!(ctx, "{r}"),
201            Function(f) => {
202                if ctx.debug_enabled() {
203                    write!(ctx, "|| (chunk: {}, ip: {})", Ptr::address(&f.chunk), f.ip)
204                } else {
205                    write!(ctx, "||")
206                }
207            }
208            NativeFunction(f) => {
209                if ctx.debug_enabled() {
210                    write!(ctx, "|| ({})", Ptr::address(&f.function))
211                } else {
212                    write!(ctx, "||")
213                }
214            }
215            Iterator(_) => write!(ctx, "Iterator"),
216            TemporaryTuple(RegisterSlice { start, count }) => {
217                write!(ctx, "TemporaryTuple [{start}..{}]", start + count)
218            }
219            Str(s) => {
220                if ctx.is_contained() || ctx.debug_enabled() {
221                    write!(ctx, "\'{s}\'")
222                } else {
223                    write!(ctx, "{s}")
224                }
225            }
226            List(l) => return l.display(ctx),
227            Tuple(t) => return t.display(ctx),
228            Map(m) => return m.display(ctx),
229            Object(o) => return o.try_borrow()?.display(ctx),
230        };
231        Ok(())
232    }
233}
234
235thread_local! {
236    static TYPE_NULL: KString = "Null".into();
237    static TYPE_BOOL: KString = "Bool".into();
238    static TYPE_NUMBER: KString = "Number".into();
239    static TYPE_LIST: KString = "List".into();
240    static TYPE_RANGE: KString = "Range".into();
241    static TYPE_MAP: KString = "Map".into();
242    static TYPE_OBJECT: KString = "Object".into();
243    static TYPE_STRING: KString = "String".into();
244    static TYPE_TUPLE: KString = "Tuple".into();
245    static TYPE_FUNCTION: KString = "Function".into();
246    static TYPE_GENERATOR: KString = "Generator".into();
247    static TYPE_ITERATOR: KString = "Iterator".into();
248    static TYPE_TEMPORARY_TUPLE: KString = "TemporaryTuple".into();
249}
250
251impl fmt::Debug for KValue {
252    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253        write!(f, "{}", self.type_as_string())
254    }
255}
256
257impl From<()> for KValue {
258    fn from(_: ()) -> Self {
259        Self::Null
260    }
261}
262
263impl From<bool> for KValue {
264    fn from(value: bool) -> Self {
265        Self::Bool(value)
266    }
267}
268
269impl From<KNumber> for KValue {
270    fn from(value: KNumber) -> Self {
271        Self::Number(value)
272    }
273}
274
275impl From<KRange> for KValue {
276    fn from(value: KRange) -> Self {
277        Self::Range(value)
278    }
279}
280
281impl From<&str> for KValue {
282    fn from(value: &str) -> Self {
283        Self::Str(value.into())
284    }
285}
286
287impl From<String> for KValue {
288    fn from(value: String) -> Self {
289        Self::Str(value.into())
290    }
291}
292
293impl From<KString> for KValue {
294    fn from(value: KString) -> Self {
295        Self::Str(value)
296    }
297}
298
299impl From<KList> for KValue {
300    fn from(value: KList) -> Self {
301        Self::List(value)
302    }
303}
304
305impl From<KTuple> for KValue {
306    fn from(value: KTuple) -> Self {
307        Self::Tuple(value)
308    }
309}
310
311impl From<KMap> for KValue {
312    fn from(value: KMap) -> Self {
313        Self::Map(value)
314    }
315}
316
317impl From<KObject> for KValue {
318    fn from(value: KObject) -> Self {
319        Self::Object(value)
320    }
321}
322
323impl From<KIterator> for KValue {
324    fn from(value: KIterator) -> Self {
325        Self::Iterator(value)
326    }
327}
328
329impl From<KNativeFunction> for KValue {
330    fn from(value: KNativeFunction) -> Self {
331        Self::NativeFunction(value)
332    }
333}
334
335impl<T> From<T> for KValue
336where
337    T: KotoFunction,
338{
339    fn from(value: T) -> Self {
340        Self::NativeFunction(KNativeFunction::new(value))
341    }
342}
343
344impl<T> From<Option<T>> for KValue
345where
346    T: Into<KValue>,
347{
348    fn from(value: Option<T>) -> Self {
349        match value {
350            Some(value) => value.into(),
351            None => KValue::Null,
352        }
353    }
354}
355
356/// A slice of a VM's register stack
357///
358/// See [Value::TemporaryTuple]
359#[allow(missing_docs)]
360#[derive(Clone, Copy, Debug, PartialEq, Eq)]
361pub struct RegisterSlice {
362    pub start: u8,
363    pub count: u8,
364}
365
366/// If conversion fails then the input value will be returned.
367impl TryFrom<KValue> for bool {
368    type Error = KValue;
369
370    fn try_from(value: KValue) -> StdResult<Self, KValue> {
371        if let KValue::Bool(b) = value {
372            Ok(b)
373        } else {
374            Err(value)
375        }
376    }
377}
378
379macro_rules! impl_try_from_value_string {
380    ($($type:ty),+) => {
381        $(
382            /// If conversion fails then the input value will be returned.
383            impl TryFrom<KValue> for $type {
384                type Error = KValue;
385
386                fn try_from(value: KValue) -> StdResult<Self, KValue> {
387                    if let KValue::Str(s) = value {
388                        Ok(s.as_str().into())
389                    } else {
390                        Err(value)
391                    }
392                }
393            }
394        )+
395    };
396}
397
398macro_rules! impl_try_from_value_string_ref {
399    ($($type:ty),+) => {
400        $(
401            /// If conversion fails then the input value will be returned.
402            impl<'a> TryFrom<&'a KValue> for $type {
403                type Error = &'a KValue;
404
405                fn try_from(value: &'a KValue) -> StdResult<Self, &'a KValue> {
406                    if let KValue::Str(s) = value {
407                        Ok(s.as_str().into())
408                    } else {
409                        Err(value)
410                    }
411                }
412            }
413        )+
414    };
415}
416
417macro_rules! impl_try_from_value_number {
418    ($($type:ty),+) => {
419        $(
420            /// If conversion fails then the input value will be returned.
421            ///
422            /// Note that number conversions are lossy. Out of range values will be saturated to the
423            /// bounds of the output type. Conversions follow the rules of the `as` operator.
424            impl TryFrom<KValue> for $type {
425                type Error = KValue;
426
427                fn try_from(value: KValue) -> StdResult<Self, KValue> {
428                    if let KValue::Number(n) = value {
429                        Ok(n.into())
430                    } else {
431                        Err(value)
432                    }
433                }
434            }
435        )+
436    };
437}
438
439impl_try_from_value_string!(String, Box<str>, std::rc::Rc<str>, std::sync::Arc<str>);
440impl_try_from_value_string_ref!(&'a str, std::borrow::Cow<'a, str>);
441impl_try_from_value_number!(
442    f32, f64, i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize
443);
444
445#[cfg(test)]
446mod tests {
447    use super::*;
448
449    #[test]
450    fn test_value_mem_size() {
451        // All KValue variants except for KValue::Function` should have a size of <= 16 bytes.
452        // KFunction has a size of 24 bytes, but is the single variant of that size,
453        // and has a niche which is then usable as the niche for KValue.
454        assert!(size_of::<KString>() <= 16);
455        assert!(size_of::<KList>() <= 16);
456        assert!(size_of::<KMap>() <= 16);
457        assert!(size_of::<KObject>() <= 16);
458        assert!(size_of::<KFunction>() <= 24);
459        assert!(size_of::<KValue>() <= 24);
460    }
461
462    #[test]
463    fn try_from_kvalue() {
464        assert_eq!(
465            &String::try_from(KValue::from("testing")).unwrap(),
466            "testing"
467        );
468
469        assert_eq!(i32::try_from(KValue::from(-123.45)).unwrap(), -123);
470
471        assert!(matches!(bool::try_from(KValue::Null), Err(KValue::Null)));
472    }
473}