1use std::sync::Arc;
8
9#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
15pub enum Literal {
16 Bool(bool),
18 Int(i64),
20 Float(f64),
22 Str(String),
24 Bytes(Vec<u8>),
26 Null,
28 Record(Vec<(Arc<str>, Self)>),
30 List(Vec<Self>),
32 Closure {
38 param: Arc<str>,
40 body: Box<crate::Expr>,
42 env: crate::Env,
47 },
48}
49
50impl Literal {
51 #[must_use]
53 pub const fn type_name(&self) -> &'static str {
54 match self {
55 Self::Bool(_) => "bool",
56 Self::Int(_) => "int",
57 Self::Float(_) => "float",
58 Self::Str(_) => "string",
59 Self::Bytes(_) => "bytes",
60 Self::Null => "null",
61 Self::Record(_) => "record",
62 Self::List(_) => "list",
63 Self::Closure { .. } => "function",
64 }
65 }
66
67 #[must_use]
69 pub const fn is_null(&self) -> bool {
70 matches!(self, Self::Null)
71 }
72
73 #[must_use]
75 pub const fn as_bool(&self) -> Option<bool> {
76 match self {
77 Self::Bool(b) => Some(*b),
78 _ => None,
79 }
80 }
81
82 #[must_use]
84 pub const fn as_int(&self) -> Option<i64> {
85 match self {
86 Self::Int(n) => Some(*n),
87 _ => None,
88 }
89 }
90
91 #[must_use]
93 pub const fn as_float(&self) -> Option<f64> {
94 match self {
95 Self::Float(f) => Some(*f),
96 _ => None,
97 }
98 }
99
100 #[must_use]
102 pub fn as_str(&self) -> Option<&str> {
103 match self {
104 Self::Str(s) => Some(s),
105 _ => None,
106 }
107 }
108
109 #[must_use]
111 pub fn as_record(&self) -> Option<&[(Arc<str>, Self)]> {
112 match self {
113 Self::Record(fields) => Some(fields),
114 _ => None,
115 }
116 }
117
118 #[must_use]
120 pub fn as_list(&self) -> Option<&[Self]> {
121 match self {
122 Self::List(items) => Some(items),
123 _ => None,
124 }
125 }
126
127 #[must_use]
129 pub fn field(&self, name: &str) -> Option<&Self> {
130 match self {
131 Self::Record(fields) => fields.iter().find(|(k, _)| &**k == name).map(|(_, v)| v),
132 _ => None,
133 }
134 }
135}
136
137impl PartialEq for Literal {
140 fn eq(&self, other: &Self) -> bool {
141 match (self, other) {
142 (Self::Bool(a), Self::Bool(b)) => a == b,
143 (Self::Int(a), Self::Int(b)) => a == b,
144 (Self::Float(a), Self::Float(b)) => a.to_bits() == b.to_bits(),
145 (Self::Str(a), Self::Str(b)) => a == b,
146 (Self::Bytes(a), Self::Bytes(b)) => a == b,
147 (Self::Null, Self::Null) => true,
148 (Self::Record(a), Self::Record(b)) => a == b,
149 (Self::List(a), Self::List(b)) => a == b,
150 (
151 Self::Closure {
152 param: p1,
153 body: b1,
154 env: e1,
155 },
156 Self::Closure {
157 param: p2,
158 body: b2,
159 env: e2,
160 },
161 ) => p1 == p2 && b1 == b2 && e1 == e2,
162 _ => false,
163 }
164 }
165}
166
167impl Eq for Literal {}
168
169impl std::hash::Hash for Literal {
170 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
171 std::mem::discriminant(self).hash(state);
172 match self {
173 Self::Bool(b) => b.hash(state),
174 Self::Int(n) => n.hash(state),
175 Self::Float(f) => f.to_bits().hash(state),
176 Self::Str(s) => s.hash(state),
177 Self::Bytes(b) => b.hash(state),
178 Self::Null => {}
179 Self::Record(fields) => fields.hash(state),
180 Self::List(items) => items.hash(state),
181 Self::Closure { param, body, env } => {
182 param.hash(state);
183 body.hash(state);
184 env.hash(state);
185 }
186 }
187 }
188}
189
190impl std::fmt::Display for Literal {
191 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192 match self {
193 Self::Bool(b) => write!(f, "{b}"),
194 Self::Int(n) => write!(f, "{n}"),
195 Self::Float(v) => write!(f, "{v}"),
196 Self::Str(s) => write!(f, "\"{s}\""),
197 Self::Bytes(b) => write!(f, "<{} bytes>", b.len()),
198 Self::Null => write!(f, "null"),
199 Self::Record(fields) => {
200 write!(f, "{{ ")?;
201 for (i, (k, v)) in fields.iter().enumerate() {
202 if i > 0 {
203 write!(f, ", ")?;
204 }
205 write!(f, "{k}: {v}")?;
206 }
207 write!(f, " }}")
208 }
209 Self::List(items) => {
210 write!(f, "[")?;
211 for (i, v) in items.iter().enumerate() {
212 if i > 0 {
213 write!(f, ", ")?;
214 }
215 write!(f, "{v}")?;
216 }
217 write!(f, "]")
218 }
219 Self::Closure { param, .. } => write!(f, "<closure λ{param}>"),
220 }
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn float_equality_uses_bits() {
230 let a = Literal::Float(f64::NAN);
232 let b = Literal::Float(f64::NAN);
233 assert_eq!(a, b);
234 }
235
236 #[test]
237 fn type_names() {
238 assert_eq!(Literal::Bool(true).type_name(), "bool");
239 assert_eq!(Literal::Int(42).type_name(), "int");
240 assert_eq!(Literal::Null.type_name(), "null");
241 assert_eq!(Literal::Record(vec![]).type_name(), "record");
242 assert_eq!(Literal::List(vec![]).type_name(), "list");
243 }
244
245 #[test]
246 fn record_field_lookup() {
247 let rec = Literal::Record(vec![
248 (Arc::from("name"), Literal::Str("alice".into())),
249 (Arc::from("age"), Literal::Int(30)),
250 ]);
251 assert_eq!(rec.field("name"), Some(&Literal::Str("alice".into())));
252 assert_eq!(rec.field("age"), Some(&Literal::Int(30)));
253 assert_eq!(rec.field("missing"), None);
254 }
255}