poco_scheme/
value.rs

1use std::{
2    fmt::{self, Display, Write},
3    rc::Rc,
4};
5
6use gc::{Finalize, Gc, GcCell};
7
8use crate::{ast::Lambda, util::ShowSlice, vm::Env};
9
10// Note that currently, this type is designed to be two machine words on a
11// 64-bit architecture. The size should be one machine word on both 32-bit and
12// 64-bit architectures, but achieving that would require going full-on unsafe,
13// so for now, we settle for 2 machine words.
14#[derive(Clone, PartialEq)]
15pub enum Value {
16    Fixnum(isize),
17    String(Box<String>),
18    Bool(bool),
19    Null,
20    Unspecified,
21    Cons(Gc<[Value; 2]>),
22    Symbol(Box<String>), // TODO: interning
23    PrimOp(&'static PrimOp),
24    Closure(Box<Closure>),
25    Exception(Box<Exception>),
26}
27
28#[derive(Debug, Clone, PartialEq)]
29pub struct Exception {
30    pub message: String,
31    pub irritants: Vec<Value>,
32}
33
34impl Display for Exception {
35    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36        f.write_str(&self.message)?;
37        if !self.irritants.is_empty() {
38            f.write_char(' ')?;
39            for (i, irritant) in self.irritants.iter().enumerate() {
40                if i + 1 == self.irritants.len() {
41                    write!(f, "{}", irritant)?;
42                } else {
43                    write!(f, "{} ", irritant)?;
44                }
45            }
46        }
47        Ok(())
48    }
49}
50
51#[derive(Clone)]
52pub struct PrimOp {
53    pub name: &'static str,
54    pub func: fn(&[Value]) -> Value,
55}
56
57impl PartialEq<PrimOp> for PrimOp {
58    fn eq(&self, other: &PrimOp) -> bool {
59        self as *const _ == other as *const _
60    }
61}
62
63impl PartialEq<Closure> for Closure {
64    fn eq(&self, _: &Closure) -> bool {
65        false
66    }
67}
68
69#[derive(Clone, Debug)]
70pub struct Closure {
71    pub lambda: Rc<Lambda>,
72    pub env: Gc<GcCell<Env>>,
73}
74
75impl Value {
76    pub fn into_result(self) -> Result<Value, Box<Exception>> {
77        match self {
78            Value::Exception(e) => Err(e),
79            _ => Ok(self),
80        }
81    }
82
83    pub fn list<I>(_elts: I) -> Self
84    where
85        I: IntoIterator,
86        I::Item: Into<Value>,
87    {
88        unimplemented!()
89    }
90
91    pub fn number<T>(n: T) -> Self
92    where
93        T: Into<isize>,
94    {
95        Value::Fixnum(n.into())
96    }
97
98    pub fn as_fixnum(&self) -> Option<isize> {
99        match self {
100            Value::Fixnum(n) => Some(*n),
101            _ => None,
102        }
103    }
104
105    pub fn is_true(&self) -> bool {
106        if let Value::Bool(v) = self {
107            *v
108        } else {
109            true
110        }
111    }
112    pub fn to_datum(&self) -> Option<lexpr::Value> {
113        use Value::*;
114        match self {
115            Null => Some(lexpr::Value::Null),
116            Unspecified => Some(lexpr::Value::Nil),
117            Bool(b) => Some((*b).into()),
118            Fixnum(n) => Some((*n as i64).into()),
119            String(s) => Some(s.as_str().into()),
120            Symbol(s) => Some(lexpr::Value::symbol(s.as_str())),
121            Cons(cell) => {
122                let cell = &*cell;
123                match (cell[0].to_datum(), cell[1].to_datum()) {
124                    (Some(car), Some(cdr)) => Some((car, cdr).into()),
125                    _ => None,
126                }
127            }
128            PrimOp(_) | Closure(_) | Exception(_) => None,
129        }
130    }
131}
132
133impl From<bool> for Value {
134    fn from(b: bool) -> Self {
135        Value::Bool(b)
136    }
137}
138
139impl<'a> From<&'a str> for Value {
140    fn from(s: &'a str) -> Self {
141        Value::String(Box::new(s.into()))
142    }
143}
144
145impl From<Box<Exception>> for Value {
146    fn from(e: Box<Exception>) -> Self {
147        Value::Exception(e)
148    }
149}
150
151impl From<&lexpr::Value> for Value {
152    fn from(v: &lexpr::Value) -> Self {
153        use lexpr::Value::*;
154        match v {
155            Bool(b) => Value::Bool(*b),
156            Number(n) => {
157                if let Some(n) = n.as_i64() {
158                    if n <= isize::max_value() as i64 {
159                        Value::Fixnum(n as isize)
160                    } else {
161                        unimplemented!()
162                    }
163                } else {
164                    unimplemented!()
165                }
166            }
167            String(s) => s.as_ref().into(),
168            Symbol(s) => Value::Symbol(Box::new(s.as_ref().to_owned())),
169            Cons(cell) => {
170                let (car, cdr) = cell.as_pair();
171                Value::Cons(Gc::new([car.into(), cdr.into()]))
172            }
173            Null => Value::Null,
174            Nil => Value::Unspecified,
175            _ => unimplemented!(),
176        }
177    }
178}
179
180impl fmt::Debug for Value {
181    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
182        // Should probably use a more "Rusty" representation
183        fmt::Display::fmt(self, f)
184    }
185}
186
187impl fmt::Display for Value {
188    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189        match self {
190            Value::Fixnum(n) => write!(f, "{}", n),
191            Value::Symbol(s) => write!(f, "{}", s),
192            Value::Bool(b) => f.write_str(if *b { "#t" } else { "#f" }),
193            Value::PrimOp(op) => write!(f, "#<prim-op {}>", op.name),
194            Value::Closure { .. } => write!(f, "#<closure>"),
195            Value::Null => write!(f, "()"),
196            Value::Unspecified => write!(f, "#<unspecified>"),
197            Value::Cons(cell) => write_cons(f, cell),
198            Value::String(s) => lexpr::Value::string(s.as_str()).fmt(f),
199            Value::Exception(e) => write!(
200                f,
201                "#<exception {} ({})>",
202                e.message,
203                ShowSlice(&e.irritants)
204            ),
205        }
206    }
207}
208
209fn write_cons(f: &mut fmt::Formatter, cell: &[Value; 2]) -> fmt::Result {
210    f.write_char('(')?;
211    cell[0].fmt(f)?;
212    let mut next = &cell[1];
213    loop {
214        match next {
215            Value::Null => break,
216            Value::Cons(cell) => {
217                f.write_char(' ')?;
218                cell[0].fmt(f)?;
219                next = &cell[1];
220            }
221            value => {
222                f.write_str(" . ")?;
223                value.fmt(f)?;
224                break;
225            }
226        }
227    }
228    f.write_char(')')?;
229    Ok(())
230}
231
232impl gc::Finalize for Value {
233    fn finalize(&self) {}
234}
235
236macro_rules! impl_value_trace_body {
237    ($this:ident, $method:ident) => {
238        match $this {
239            Value::Cons(cell) => {
240                cell[0].$method();
241                cell[1].$method();
242            }
243            Value::Closure(boxed) => {
244                let Closure { env, .. } = boxed.as_ref();
245                env.$method();
246            }
247            _ => {}
248        }
249    };
250}
251
252unsafe impl gc::Trace for Value {
253    unsafe fn trace(&self) {
254        impl_value_trace_body!(self, trace);
255    }
256    unsafe fn root(&self) {
257        impl_value_trace_body!(self, root);
258    }
259    unsafe fn unroot(&self) {
260        impl_value_trace_body!(self, unroot);
261    }
262    fn finalize_glue(&self) {
263        self.finalize();
264        impl_value_trace_body!(self, finalize_glue);
265    }
266}
267
268#[cfg(test)]
269mod tests {
270    use super::Value;
271    use std::mem;
272
273    #[test]
274    fn test_value_size() {
275        assert!(mem::size_of::<Value>() <= 2 * mem::size_of::<usize>());
276    }
277}