stak_dynamic/
scheme_value.rs

1use super::DynamicError;
2use alloc::string::String;
3use stak_vm::{Memory, Number, Type, Value};
4
5/// A trait to convert Rust values from and into Scheme values.
6pub trait SchemeValue: Sized {
7    /// Converts a Scheme value into a Rust value.
8    fn from_scheme(memory: &Memory, value: Value) -> Result<Option<Self>, DynamicError>;
9
10    /// Converts a Rust value into a Scheme value.
11    fn into_scheme(self, memory: &mut Memory) -> Result<Value, DynamicError>;
12}
13
14impl SchemeValue for bool {
15    fn from_scheme(memory: &Memory, value: Value) -> Result<Option<Self>, DynamicError> {
16        Ok(Some(value == memory.boolean(false)?.into()))
17    }
18
19    fn into_scheme(self, memory: &mut Memory) -> Result<Value, DynamicError> {
20        Ok(memory.boolean(self)?.into())
21    }
22}
23
24macro_rules! implement_integer {
25    ($type:ty) => {
26        impl SchemeValue for $type {
27            fn from_scheme(_memory: &Memory, value: Value) -> Result<Option<Self>, DynamicError> {
28                Ok(Some(value.assume_number().to_i64() as _))
29            }
30
31            fn into_scheme(self, _memory: &mut Memory) -> Result<Value, DynamicError> {
32                Ok(Number::from_i64(self as _).into())
33            }
34        }
35    };
36}
37
38implement_integer!(i8);
39implement_integer!(u8);
40implement_integer!(u16);
41implement_integer!(i16);
42implement_integer!(i32);
43implement_integer!(u32);
44implement_integer!(i64);
45implement_integer!(u64);
46implement_integer!(isize);
47implement_integer!(usize);
48
49macro_rules! implement_float {
50    ($type:ty) => {
51        impl SchemeValue for $type {
52            fn from_scheme(_memory: &Memory, value: Value) -> Result<Option<Self>, DynamicError> {
53                Ok(Some(value.assume_number().to_f64() as _))
54            }
55
56            fn into_scheme(self, _memory: &mut Memory) -> Result<Value, DynamicError> {
57                Ok(Number::from_f64(self as _).into())
58            }
59        }
60    };
61}
62
63implement_float!(f32);
64implement_float!(f64);
65
66impl SchemeValue for String {
67    fn from_scheme(memory: &Memory, value: Value) -> Result<Option<Self>, DynamicError> {
68        let cons = value.assume_cons();
69        let mut string = Self::with_capacity(memory.car(cons)?.assume_number().to_i64() as _);
70        let mut cons = memory.cdr(cons)?.assume_cons();
71
72        while cons != memory.null()? {
73            let Some(character) = char::from_u32(memory.car(cons)?.assume_number().to_i64() as _)
74            else {
75                return Ok(None);
76            };
77            string.push(character);
78            cons = memory.cdr(cons)?.assume_cons();
79        }
80
81        Ok(Some(string))
82    }
83
84    fn into_scheme(self, memory: &mut Memory) -> Result<Value, DynamicError> {
85        let mut length = 0;
86        let mut cons = memory.null()?;
87
88        for character in self.chars().rev() {
89            cons = memory.cons(Number::from_i64(character as _).into(), cons)?;
90            length += 1;
91        }
92
93        Ok(memory
94            .allocate(
95                Number::from_i64(length).into(),
96                cons.set_tag(Type::String as _).into(),
97            )?
98            .into())
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    mod string {
107        use super::*;
108
109        #[test]
110        fn ascii() {
111            let mut heap = [Default::default(); 256];
112            let mut memory = Memory::new(&mut heap).unwrap();
113            let string = "tomato";
114
115            let value = String::from(string).into_scheme(&mut memory).unwrap();
116
117            assert_eq!(
118                &String::from_scheme(&memory, value).unwrap().unwrap(),
119                string
120            );
121        }
122
123        #[test]
124        fn unicode() {
125            let mut heap = [Default::default(); 256];
126            let mut memory = Memory::new(&mut heap).unwrap();
127            let string = "γ‚ι˜ΏπŸ˜„";
128
129            let value = String::from(string).into_scheme(&mut memory).unwrap();
130
131            assert_eq!(
132                &String::from_scheme(&memory, value).unwrap().unwrap(),
133                string
134            );
135        }
136    }
137}