stak_vm/
value.rs

1use crate::{
2    cons::{Cons, Tag},
3    number::Number,
4    value_inner,
5};
6use core::fmt::{self, Display, Formatter};
7
8/// A value.
9#[derive(Copy, Clone, Debug)]
10pub struct Value(u64);
11
12/// A typed value.
13#[derive(Copy, Clone, Debug, PartialEq)]
14#[cfg_attr(not(feature = "float"), derive(Eq))]
15pub enum TypedValue {
16    Cons(Cons),
17    Number(Number),
18}
19
20impl Value {
21    /// Converts a value to a cons.
22    #[inline]
23    pub const fn to_cons(self) -> Option<Cons> {
24        if let TypedValue::Cons(cons) = self.to_typed() {
25            Some(cons)
26        } else {
27            None
28        }
29    }
30
31    /// Converts a value to a number.
32    #[inline]
33    pub const fn to_number(self) -> Option<Number> {
34        if let TypedValue::Number(number) = self.to_typed() {
35            Some(number)
36        } else {
37            None
38        }
39    }
40
41    /// Converts a value to a typed value.
42    #[inline]
43    pub const fn to_typed(self) -> TypedValue {
44        if self.is_cons() {
45            TypedValue::Cons(self.assume_cons())
46        } else {
47            TypedValue::Number(self.assume_number())
48        }
49    }
50
51    /// Converts a value to a cons assuming its type.
52    #[inline]
53    pub const fn assume_cons(self) -> Cons {
54        debug_assert!(self.is_cons());
55
56        Cons::from_raw(self.0)
57    }
58
59    /// Converts a value to a number assuming its type.
60    #[inline]
61    pub const fn assume_number(self) -> Number {
62        debug_assert!(self.is_number());
63
64        Number::from_raw(self.0)
65    }
66
67    /// Checks if it is a cons.
68    #[inline]
69    pub const fn is_cons(&self) -> bool {
70        value_inner::is_cons(self.0)
71    }
72
73    /// Checks if it is a number.
74    #[inline]
75    pub const fn is_number(&self) -> bool {
76        !self.is_cons()
77    }
78
79    /// Returns a tag.
80    #[inline]
81    pub const fn tag(self) -> Tag {
82        if let Some(cons) = self.to_cons() {
83            cons.tag()
84        } else {
85            0
86        }
87    }
88
89    /// Sets a tag.
90    #[inline]
91    pub const fn set_tag(self, tag: Tag) -> Self {
92        if let Some(cons) = self.to_cons() {
93            Self::from_cons(cons.set_tag(tag))
94        } else {
95            self
96        }
97    }
98
99    const fn from_cons(cons: Cons) -> Self {
100        Self(cons.to_raw())
101    }
102}
103
104impl Default for Value {
105    #[inline]
106    fn default() -> Self {
107        Number::default().into()
108    }
109}
110
111impl PartialEq for Value {
112    #[inline]
113    fn eq(&self, other: &Self) -> bool {
114        self.is_cons() && self.to_cons() == other.to_cons()
115            || self.is_number() && self.to_number() == other.to_number()
116    }
117}
118
119impl Eq for Value {}
120
121impl From<Cons> for Value {
122    #[inline]
123    fn from(cons: Cons) -> Self {
124        Self(cons.to_raw())
125    }
126}
127
128impl From<Number> for Value {
129    #[inline]
130    fn from(number: Number) -> Self {
131        Self(number.to_raw())
132    }
133}
134
135impl Display for Value {
136    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
137        match self.to_typed() {
138            TypedValue::Cons(cons) => write!(formatter, "{cons}"),
139            TypedValue::Number(number) => write!(formatter, "{number}"),
140        }
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147    use crate::{Type, cons::NEVER};
148
149    #[test]
150    fn convert_cons() {
151        let cons = Cons::new(42);
152
153        assert_eq!(Value::from(cons).to_cons().unwrap(), cons);
154    }
155
156    #[test]
157    fn convert_tagged_cons() {
158        const TAG: Tag = 0b111;
159
160        let cons = Cons::new(42).set_tag(TAG);
161        let converted = Value::from(cons).to_cons().unwrap();
162
163        assert_eq!(converted, cons);
164        assert_eq!(converted.tag(), TAG);
165    }
166
167    #[test]
168    fn convert_number() {
169        let number = Number::from_i64(42);
170
171        assert_eq!(Value::from(number).to_number().unwrap(), number);
172    }
173
174    #[test]
175    fn convert_moved() {
176        assert_eq!(Value::from(NEVER).to_cons().unwrap(), NEVER);
177    }
178
179    #[test]
180    fn get_tag_from_number() {
181        let tag = Value::from(Number::from_i64(42)).tag();
182
183        assert_eq!(tag, Default::default());
184        assert_eq!(tag, Type::default() as Tag);
185    }
186
187    #[test]
188    fn set_tag_to_number() {
189        let value = Value::from(Number::from_i64(42)).set_tag(0b111);
190
191        assert_eq!(value.tag(), Default::default());
192        assert_eq!(value.to_number(), Some(Number::from_i64(42)));
193    }
194}