1use crate::env::Env;
2use std::sync::{Arc, Mutex};
3use tidepool_repr::{CoreExpr, DataConId, Literal, VarId};
4
5pub type SharedByteArray = Arc<Mutex<Vec<u8>>>;
10
11#[derive(Debug, Clone)]
13pub enum Value {
14 Lit(Literal),
16 Con(DataConId, Vec<Value>),
18 Closure(Env, VarId, CoreExpr),
20 ThunkRef(ThunkId),
22 JoinCont(Vec<VarId>, CoreExpr, Env),
24 ConFun(DataConId, usize, Vec<Value>),
27 ByteArray(SharedByteArray),
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33pub struct ThunkId(pub u32);
34
35impl std::fmt::Display for ThunkId {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 write!(f, "<thunk#{}>", self.0)
38 }
39}
40
41impl std::fmt::Display for Value {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 match self {
44 Value::Lit(lit) => match lit {
45 Literal::LitInt(n) => write!(f, "{}", n),
46 Literal::LitWord(n) => write!(f, "{}", n),
47 Literal::LitChar(c) => write!(f, "'{}'", c.escape_default()),
48 Literal::LitString(bs) => match std::str::from_utf8(bs) {
49 Ok(s) => write!(f, "{:?}", s),
50 Err(_) => write!(f, "<bytes len={}>", bs.len()),
51 },
52 Literal::LitFloat(bits) => match u32::try_from(*bits) {
53 Ok(bits32) => write!(f, "{}", f32::from_bits(bits32)),
54 Err(_) => write!(f, "<invalid f32 bits=0x{:016x}>", *bits),
55 },
56 Literal::LitDouble(bits) => write!(f, "{}", f64::from_bits(*bits)),
57 },
58 Value::Con(id, fields) => {
59 write!(f, "<Con#{}>", id.0)?;
60 for field in fields {
61 write!(f, " {}", field)?;
62 }
63 Ok(())
64 }
65 Value::Closure(..) => write!(f, "<closure>"),
66 Value::ThunkRef(id) => write!(f, "{}", id),
67 Value::JoinCont(..) => write!(f, "<join>"),
68 Value::ConFun(id, arity, args) => {
69 write!(f, "<partial Con#{} {}/{}>", id.0, args.len(), arity)
70 }
71 Value::ByteArray(ba) => match ba.lock() {
72 Ok(bytes) => write!(f, "<ByteArray# len={}>", bytes.len()),
73 Err(_) => write!(f, "<ByteArray# poisoned>"),
74 },
75 }
76 }
77}
78
79impl Value {
80 pub fn node_count(&self) -> usize {
82 match self {
83 Value::Con(_, fields) => 1 + fields.iter().map(|f| f.node_count()).sum::<usize>(),
84 _ => 1,
85 }
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use tidepool_repr::{CoreFrame, RecursiveTree};
93
94 #[test]
95 fn test_value_display() {
96 let env = Env::new();
97 let expr = RecursiveTree {
98 nodes: vec![CoreFrame::Var(VarId(0))],
99 };
100
101 assert_eq!(Value::Lit(Literal::LitInt(42)).to_string(), "42");
102 assert_eq!(Value::Lit(Literal::LitChar('x')).to_string(), "'x'");
103 assert_eq!(Value::Lit(Literal::LitChar('\n')).to_string(), r"'\n'");
104 assert_eq!(
105 Value::Lit(Literal::LitString(b"hello".to_vec())).to_string(),
106 "\"hello\""
107 );
108 assert_eq!(
109 Value::Lit(Literal::LitString(b"with \"quotes\"".to_vec())).to_string(),
110 "\"with \\\"quotes\\\"\""
111 );
112 assert_eq!(Value::Lit(Literal::from(3.14f64)).to_string(), "3.14");
113 assert_eq!(
114 Value::Lit(Literal::LitFloat(0xFFFF_FFFF_FFFF_FFFF)).to_string(),
115 "<invalid f32 bits=0xffffffffffffffff>"
116 );
117
118 assert_eq!(Value::Con(DataConId(1), vec![]).to_string(), "<Con#1>");
119 assert_eq!(
120 Value::Con(DataConId(1), vec![Value::Lit(Literal::LitInt(42))]).to_string(),
121 "<Con#1> 42"
122 );
123 assert_eq!(
124 Value::Con(
125 DataConId(1),
126 vec![
127 Value::Lit(Literal::LitInt(42)),
128 Value::Lit(Literal::LitString(b"hi".to_vec()))
129 ]
130 )
131 .to_string(),
132 "<Con#1> 42 \"hi\""
133 );
134
135 assert_eq!(
136 Value::Closure(env.clone(), VarId(0), expr.clone()).to_string(),
137 "<closure>"
138 );
139 assert_eq!(Value::ThunkRef(ThunkId(123)).to_string(), "<thunk#123>");
140 assert_eq!(
141 Value::JoinCont(vec![VarId(1)], expr, env).to_string(),
142 "<join>"
143 );
144
145 assert_eq!(
146 Value::ConFun(DataConId(1), 2, vec![Value::Lit(Literal::LitInt(42))]).to_string(),
147 "<partial Con#1 1/2>"
148 );
149 }
150
151 #[test]
152 fn test_value_construction() {
153 let env = Env::new();
154 let lit = Value::Lit(Literal::LitInt(42));
155 let con = Value::Con(DataConId(1), vec![lit.clone()]);
156
157 let expr = RecursiveTree {
158 nodes: vec![CoreFrame::Var(VarId(0))],
159 };
160 let closure = Value::Closure(env.clone(), VarId(0), expr.clone());
161 let thunk = Value::ThunkRef(ThunkId(0));
162 let join = Value::JoinCont(vec![VarId(1)], expr, env);
163
164 match lit {
165 Value::Lit(_) => (),
166 _ => panic!("Expected Lit"),
167 }
168 match con {
169 Value::Con(_, _) => (),
170 _ => panic!("Expected Con"),
171 }
172 match closure {
173 Value::Closure(_, _, _) => (),
174 _ => panic!("Expected Closure"),
175 }
176 match thunk {
177 Value::ThunkRef(_) => (),
178 _ => panic!("Expected ThunkRef"),
179 }
180 match join {
181 Value::JoinCont(_, _, _) => (),
182 _ => panic!("Expected JoinCont"),
183 }
184 }
185
186 #[test]
187 fn test_closure_clone() {
188 let env = Env::new();
189 let expr = RecursiveTree {
190 nodes: vec![CoreFrame::Var(VarId(0))],
191 };
192 let closure = Value::Closure(env, VarId(0), expr);
193 let _cloned = closure.clone();
194 }
195}