1use crate::{
2 cons::{Cons, Tag},
3 number::Number,
4 value_inner,
5};
6use core::fmt::{self, Display, Formatter};
7
8#[derive(Copy, Clone, Debug)]
10pub struct Value(u64);
11
12#[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 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 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 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 pub const fn assume_cons(self) -> Cons {
50 debug_assert!(self.is_cons());
51
52 Cons::from_raw(self.0)
53 }
54
55 pub const fn assume_number(self) -> Number {
57 debug_assert!(self.is_number());
58
59 Number::from_raw(self.0)
60 }
61
62 pub const fn is_cons(&self) -> bool {
64 value_inner::is_cons(self.0)
65 }
66
67 pub const fn is_number(&self) -> bool {
69 !self.is_cons()
70 }
71
72 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 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}