spore_vm/val/
unsafe_val.rs

1use compact_str::CompactString;
2
3use crate::Vm;
4
5use super::{
6    bytecode::ByteCode, custom::CustomVal, formatter::ValFormatter, ListVal, NativeFunction,
7    StructVal, Symbol, ValId,
8};
9
10/// Contains a Spore value. The value is considered unsafe as it may be garbage collected.
11///
12/// # Safety
13/// The value is considered unsafe as some variants contain references that may be mutated or
14/// garbage collected by the VM. Unsafe fields contain a `Safety` section in their documentation.
15#[derive(Copy, Clone, Debug, Default, PartialEq)]
16pub enum UnsafeVal {
17    /// A type that contains a single value. Used to represent nothingness.
18    #[default]
19    Void,
20    /// Either true or false.
21    Bool(bool),
22    /// A 64 bit signed integer
23    Int(i64),
24    /// A 64 bit floating point number.
25    Float(f64),
26    /// A handle to a string within the VM.
27    ///
28    /// # Safety
29    /// May be garbage collected or mutated by the VM.
30    String(ValId<CompactString>),
31    /// A symbol.
32    Symbol(Symbol),
33    /// A handle to a box containing a mutable value.
34    ///
35    /// # Safety
36    /// May be garbage collected or mutated by the VM.
37    MutableBox(ValId<UnsafeVal>),
38    /// A handle to a list within the VM.
39    ///
40    /// # Safety
41    /// May be garbage collected or mutated by the VM.
42    List(ValId<ListVal>),
43    /// A handle to a struct.
44    Struct(ValId<StructVal>),
45    /// A handle to a function implemented in Spore's bytecode.
46    ///
47    /// # Safety
48    /// May be garbage collected or mutated by the VM.
49    ByteCodeFunction(ValId<ByteCode>),
50    /// A function implemented in Rust.
51    NativeFunction(NativeFunction),
52    /// A handle to a custom type.
53    ///
54    /// # Safety
55    /// May be garbage collected or mutated by the VM.
56    Custom(ValId<CustomVal>),
57}
58
59impl UnsafeVal {
60    /// The display name for the function type.
61    pub const FUNCTION_TYPE_NAME: &'static str = "function";
62    /// The display name for the boolean type.
63    pub const BOOL_TYPE_NAME: &'static str = "bool";
64    /// The display name for the integer type.
65    pub const INT_TYPE_NAME: &'static str = "int";
66    /// The display name for the float type.
67    pub const FLOAT_TYPE_NAME: &'static str = "float";
68    /// The display name for the void type.
69    pub const VOID_TYPE_NAME: &'static str = "void";
70    /// The display name for the symbol type.
71    pub const SYMBOL_TYPE_NAME: &'static str = "symbol";
72    /// The display name for the string type.
73    pub const STRING_TYPE_NAME: &'static str = "string";
74    /// The display name for the mutable box type.
75    pub const MUTABLE_BOX_TYPE_NAME: &'static str = "mutable-box";
76    /// The display name for the list type.
77    pub const LIST_TYPE_NAME: &'static str = "list";
78    /// The display name for the struct type.
79    pub const STRUCT_TYPE_NAME: &'static str = "struct";
80    /// The display name for the custom type.
81    pub const CUSTOM_TYPE_NAME: &'static str = "custom";
82
83    /// Get the display name for the type of `self`.
84    pub fn type_name(self) -> &'static str {
85        match self {
86            UnsafeVal::Void => UnsafeVal::VOID_TYPE_NAME,
87            UnsafeVal::Bool(_) => UnsafeVal::BOOL_TYPE_NAME,
88            UnsafeVal::Int(_) => UnsafeVal::INT_TYPE_NAME,
89            UnsafeVal::Float(_) => UnsafeVal::FLOAT_TYPE_NAME,
90            UnsafeVal::String(_) => UnsafeVal::STRING_TYPE_NAME,
91            UnsafeVal::Symbol(_) => UnsafeVal::SYMBOL_TYPE_NAME,
92            UnsafeVal::MutableBox(_) => UnsafeVal::MUTABLE_BOX_TYPE_NAME,
93            UnsafeVal::List(_) => UnsafeVal::LIST_TYPE_NAME,
94            UnsafeVal::Struct(_) => UnsafeVal::STRUCT_TYPE_NAME,
95            UnsafeVal::ByteCodeFunction(_) => UnsafeVal::FUNCTION_TYPE_NAME,
96            UnsafeVal::NativeFunction(_) => UnsafeVal::FUNCTION_TYPE_NAME,
97            UnsafeVal::Custom(_) => UnsafeVal::CUSTOM_TYPE_NAME,
98        }
99    }
100
101    /// Returns `false` if `self` is `void` or `false`. All other values return `true`.
102    pub fn is_truthy(self) -> bool {
103        !matches!(self, UnsafeVal::Void | UnsafeVal::Bool(false))
104    }
105
106    /// Get a display formatter for the current value.
107    pub fn formatted<'a>(&self, vm: &'a Vm) -> impl 'a + std::fmt::Display {
108        ValFormatter::new(vm, *self)
109    }
110
111    /// Get a display formatter for the current type. Unlike [Self::formatted], this will print
112    /// strings quoted. For example, `"hello"` will display as "hello" instead of hello.
113    pub fn format_quoted<'a>(&self, vm: &'a Vm) -> impl 'a + std::fmt::Display {
114        ValFormatter::new_quoted(vm, *self)
115    }
116}
117
118macro_rules! to_internal_val_impl {
119    ($rust_type:ty => $variant:ident) => {
120        impl From<$rust_type> for UnsafeVal {
121            fn from(v: $rust_type) -> UnsafeVal {
122                UnsafeVal::$variant(v)
123            }
124        }
125    };
126}
127
128to_internal_val_impl!(bool => Bool);
129to_internal_val_impl!(i64 => Int);
130to_internal_val_impl!(f64 => Float);
131to_internal_val_impl!(NativeFunction => NativeFunction);
132to_internal_val_impl!(ValId<CompactString> => String);
133to_internal_val_impl!(ValId<UnsafeVal> => MutableBox);
134to_internal_val_impl!(ValId<ListVal> => List);
135to_internal_val_impl!(ValId<StructVal> => Struct);
136to_internal_val_impl!(ValId<ByteCode> => ByteCodeFunction);
137to_internal_val_impl!(ValId<CustomVal> => Custom);
138
139impl From<()> for UnsafeVal {
140    fn from(_: ()) -> UnsafeVal {
141        UnsafeVal::Void
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    #[test]
150    fn internal_val_is_small() {
151        assert_eq!(
152            std::mem::size_of::<UnsafeVal>(),
153            2 * std::mem::size_of::<usize>()
154        );
155    }
156
157    #[test]
158    fn hacks_for_code_coverage() {
159        let vals = [
160            UnsafeVal::Void,
161            UnsafeVal::Bool(false),
162            UnsafeVal::Int(0),
163            UnsafeVal::Float(0.0),
164            UnsafeVal::String(Default::default()),
165            UnsafeVal::MutableBox(Default::default()),
166            UnsafeVal::List(Default::default()),
167            UnsafeVal::ByteCodeFunction(Default::default()),
168            UnsafeVal::NativeFunction(crate::builtins::numbers::add),
169            UnsafeVal::Custom(ValId {
170                vm_id: 0,
171                obj_id: 0,
172                idx: 0,
173                _marker: std::marker::PhantomData,
174            }),
175        ];
176        for v in vals {
177            assert_ne!(v.type_name(), "");
178        }
179    }
180}