stak_dynamic/
scheme_value.rs

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