stak_dynamic/
scheme_value.rs1use super::DynamicError;
2use alloc::string::String;
3use stak_vm::{Heap, Memory, Number, Type, Value};
4
5pub trait SchemeValue<H>: Sized {
7 fn from_scheme(memory: &Memory<H>, value: Value) -> Result<Option<Self>, DynamicError>;
9
10 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}