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