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) -> Option<Self>;
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) -> Option<Self> {
16        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) -> Option<Self> {
28                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) -> Option<Self> {
53                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) -> Option<Self> {
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            string.push(char::from_u32(
74                memory.car(cons).assume_number().to_i64() as _
75            )?);
76            cons = memory.cdr(cons).assume_cons();
77        }
78
79        Some(string)
80    }
81
82    fn into_scheme(self, memory: &mut Memory) -> Result<Value, DynamicError> {
83        let mut length = 0;
84        let mut cons = memory.null();
85
86        for character in self.chars().rev() {
87            cons = memory.cons(Number::from_i64(character as _).into(), cons)?;
88            length += 1;
89        }
90
91        Ok(memory
92            .allocate(
93                Number::from_i64(length).into(),
94                cons.set_tag(Type::String as _).into(),
95            )?
96            .into())
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    mod string {
105        use super::*;
106
107        #[test]
108        fn ascii() {
109            let mut heap = [Default::default(); 256];
110            let mut memory = Memory::new(&mut heap).unwrap();
111            let string = "tomato";
112
113            let value = String::from(string).into_scheme(&mut memory).unwrap();
114
115            assert_eq!(&String::from_scheme(&memory, value).unwrap(), string);
116        }
117
118        #[test]
119        fn unicode() {
120            let mut heap = [Default::default(); 256];
121            let mut memory = Memory::new(&mut heap).unwrap();
122            let string = "γ‚ι˜ΏπŸ˜„";
123
124            let value = String::from(string).into_scheme(&mut memory).unwrap();
125
126            assert_eq!(&String::from_scheme(&memory, value).unwrap(), string);
127        }
128    }
129}