spaik/
subrs.rs

1//! Rust Subroutines for SPAIK LISP
2
3use crate::r8vm::R8VM;
4use crate::nkgc::{PV, SPV, Arena, ObjRef};
5use crate::error::{Error, ErrorKind};
6use crate::{nuke::*, SymID};
7use crate::fmt::{LispFmt, VisitSet};
8use std::convert::{TryInto, TryFrom};
9use std::fmt;
10
11/// The `mem` parameter is necessary here, because some of the conversions
12/// may need to create an SPV reference-counter
13pub trait FromLisp<T>: Sized {
14    #[allow(clippy::wrong_self_convention)]
15    fn from_lisp(self, mem: &mut Arena) -> Result<T, Error>;
16}
17
18/// The `mem` parameter is necessary here, because some of the conversions
19/// may need to do memory allocation.
20pub trait IntoLisp: Sized {
21    fn into_spv(self, mem: &mut Arena) -> Result<SPV, Error> {
22        let pv = self.into_pv(mem)?;
23        Ok(mem.make_extref(pv))
24    }
25
26    fn into_pv(self, mem: &mut Arena) -> Result<PV, Error>;
27}
28
29pub trait RefIntoLisp {
30    fn ref_into_spv(&self, mem: &mut Arena) -> Result<SPV, Error> {
31        let pv = self.ref_into_pv(mem)?;
32        Ok(mem.make_extref(pv))
33    }
34
35    fn ref_into_pv(&self, mem: &mut Arena) -> Result<PV, Error>;
36}
37
38impl<T> RefIntoLisp for T
39    where T: IntoLisp + Clone
40{
41    fn ref_into_pv(&self, mem: &mut Arena) -> Result<PV, Error> {
42        self.clone().into_pv(mem)
43    }
44}
45
46macro_rules! pv_convert {
47    ($pvt:ident, $($from_t:ty),*) => {
48        $(impl IntoLisp for $from_t {
49            fn into_pv(self, _: &mut Arena) -> Result<PV, Error> {
50                Ok(PV::$pvt(self.try_into()?))
51            }
52        })*
53        $(impl TryFrom<PV> for $from_t {
54            type Error = Error;
55            fn try_from(v: PV) -> Result<$from_t, Self::Error> {
56                if let PV::$pvt(x) = v {
57                    Ok(x.try_into()?)
58                } else {
59                    Err(Error::new(ErrorKind::TypeError {
60                        expect: PV::$pvt(Default::default()).bt_type_of(),
61                        got: v.bt_type_of(),
62                    }))
63                }
64            }
65        }
66        impl TryFrom<PV> for ObjRef<$from_t> {
67            type Error = Error;
68            #[inline(always)]
69            fn try_from(v: PV) -> Result<ObjRef<$from_t>, Self::Error> {
70                Ok(ObjRef(v.try_into()?))
71            }
72        })*
73    };
74}
75
76pv_convert!(Int,
77            i8, u8,
78            i16, u16,
79            i32, u32,
80            i64, u64,
81            i128, u128,
82            isize, usize);
83
84pv_convert!(Real,
85            f32);
86
87pv_convert!(Char,
88            char);
89
90impl IntoLisp for () {
91    fn into_pv(self, _: &mut Arena) -> Result<PV, Error> {
92        Ok(PV::Nil)
93    }
94}
95
96impl TryFrom<PV> for () {
97    type Error = Error;
98    fn try_from(v: PV) -> Result<(), Self::Error> {
99        if let PV::Nil = v {
100            Ok(())
101        } else {
102            Err(Error::new(ErrorKind::TypeError {
103                expect: PV::Nil.bt_type_of(),
104                got: v.bt_type_of(),
105            }))
106        }
107    }
108}
109
110/**
111 * A type that all SPAIK values may be converted to.
112 *
113 * ```rust
114 * use spaik::{Spaik, Ignore};
115 * let mut vm = Spaik::new_no_core();
116 * // Both of these succeed
117 * let _: Ignore = vm.eval("1").unwrap();
118 * let _: Ignore = vm.eval(r#"(concat "a" "b")"#).unwrap();
119 * ```
120 */
121pub struct Ignore;
122
123impl IntoLisp for Ignore {
124    fn into_pv(self, _: &mut Arena) -> Result<PV, Error> {
125        Ok(PV::Nil)
126    }
127}
128
129impl TryFrom<PV> for Ignore {
130    type Error = Error;
131    fn try_from(_v: PV) -> Result<Ignore, Self::Error> {
132        Ok(Ignore)
133    }
134}
135
136impl IntoLisp for bool {
137    fn into_pv(self, _: &mut Arena) -> Result<PV, Error> {
138        Ok(PV::Bool(self))
139    }
140}
141
142impl IntoLisp for &str {
143    fn into_pv(self, mem: &mut Arena) -> Result<PV, Error> {
144        Ok(mem.put_pv(self.to_string()))
145    }
146}
147
148impl IntoLisp for String {
149    fn into_pv(self, mem: &mut Arena) -> Result<PV, Error> {
150        Ok(mem.put_pv(self))
151    }
152}
153
154impl IntoLisp for SymID {
155    fn into_pv(self, _mem: &mut Arena) -> Result<PV, Error> {
156        Ok(PV::Sym(self))
157    }
158}
159
160impl<T> IntoLisp for Vec<T>
161    where T: IntoLisp
162{
163    fn into_pv(self, mem: &mut Arena) -> Result<PV, Error> {
164        let arr = self.into_iter()
165                      .map(|v| v.into_pv(mem))
166                      .collect::<Result<Vec<PV>, _>>()?;
167        Ok(mem.put_pv(arr))
168    }
169}
170
171impl<T> TryFrom<PV> for Vec<T>
172    where T: TryFrom<PV, Error=Error>
173{
174    type Error = Error;
175    fn try_from(v: PV) -> Result<Vec<T>, Self::Error> {
176        with_ref!(v, Vector(v) => {
177            (*v).iter().map(|&x| x.try_into())
178                    .collect::<Result<_, _>>()
179        })
180    }
181}
182
183/// NOTE: This is safe because while the `String` is shifted around by the
184/// GC mark-sweep phase, the actual allocated string contents are not.
185/// XXX: This definition also means that strings *must* be immutable.
186impl<'a> TryFrom<PV> for &'a str {
187    type Error = Error;
188    fn try_from(v: PV) -> Result<&'a str, Self::Error> {
189        with_ref!(v, String(s) => { Ok(&*s) })
190    }
191}
192
193impl<T, E> IntoLisp for Result<T, E>
194    where T: IntoLisp, E: Into<Error>
195{
196    fn into_pv(self, mem: &mut Arena) -> Result<PV, Error> {
197        match self {
198            Ok(v) => v.into_pv(mem),
199            Err(e) => Err(e.into()),
200        }
201    }
202}
203
204/**
205 * SAFETY: The call method may be passed an arg reference into the,
206 *         vm stack (which it gets a mutable reference to.) The call
207 *         method may not access `args` after mutating `vm`.
208 *         -
209 *         This invariant is ensured by the lispy proc-macro, which you
210 *         should use instead of implementing Subr yourself.
211*/
212#[cfg(not(feature = "freeze"))]
213pub unsafe trait Subr: CloneSubr + Send + 'static {
214    fn call(&mut self, vm: &mut R8VM, args: &[PV]) -> Result<PV, Error>;
215    fn name(&self) -> &'static str;
216}
217#[cfg(feature = "freeze")]
218pub unsafe trait Subr: CloneSubr + Send + 'static {
219    fn call(&mut self, vm: &mut R8VM, args: &[PV]) -> Result<PV, Error>;
220    fn name(&self) -> &'static str;
221}
222
223pub trait IntoSubr: Subr {
224    fn into_subr(self) -> Box<dyn Subr>;
225}
226
227impl<T> IntoSubr for T where T: Subr + Sized + 'static {
228    fn into_subr(self) -> Box<dyn Subr> {
229        Box::new(self)
230    }
231}
232
233impl fmt::Debug for Box<dyn Subr> {
234    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
235        write!(f, "(subr {})", self.name())
236    }
237}
238
239impl LispFmt for Box<dyn Subr> {
240    fn lisp_fmt(&self,
241                _: &mut VisitSet,
242                f: &mut fmt::Formatter<'_>) -> fmt::Result {
243        write!(f, "(subr {})", self.name())
244    }
245}
246
247pub trait CloneSubr {
248    fn clone_subr(&self) -> Box<dyn Subr>;
249}
250
251impl<T> CloneSubr for T
252    where T: Subr + Clone + 'static
253{
254    fn clone_subr(&self) -> Box<dyn Subr> {
255        Box::new(self.clone())
256    }
257}
258
259impl Clone for Box<dyn Subr> {
260    fn clone(&self) -> Box<dyn Subr> {
261        self.clone_subr()
262    }
263}
264
265impl IntoLisp for Box<dyn Subr> {
266    fn into_pv(self, mem: &mut Arena) -> Result<PV, Error> {
267        let p = mem.put(self);
268        Ok(NkAtom::make_ref(p))
269    }
270}