use crate::r8vm::R8VM;
use crate::nkgc::{PV, SPV, Arena, ObjRef};
use crate::error::{Error, ErrorKind};
use crate::{nuke::*, SymID};
use crate::fmt::{LispFmt, VisitSet};
use std::convert::{TryInto, TryFrom};
use std::fmt;
pub trait FromLisp<T>: Sized {
#[allow(clippy::wrong_self_convention)]
fn from_lisp(self, mem: &mut Arena) -> Result<T, Error>;
}
pub trait IntoLisp: Sized {
fn into_spv(self, mem: &mut Arena) -> Result<SPV, Error> {
let pv = self.into_pv(mem)?;
Ok(mem.make_extref(pv))
}
fn into_pv(self, mem: &mut Arena) -> Result<PV, Error>;
}
pub trait RefIntoLisp {
fn ref_into_spv(&self, mem: &mut Arena) -> Result<SPV, Error> {
let pv = self.ref_into_pv(mem)?;
Ok(mem.make_extref(pv))
}
fn ref_into_pv(&self, mem: &mut Arena) -> Result<PV, Error>;
}
impl<T> RefIntoLisp for T
where T: IntoLisp + Clone
{
fn ref_into_pv(&self, mem: &mut Arena) -> Result<PV, Error> {
self.clone().into_pv(mem)
}
}
macro_rules! pv_convert {
($pvt:ident, $($from_t:ty),*) => {
$(impl IntoLisp for $from_t {
fn into_pv(self, _: &mut Arena) -> Result<PV, Error> {
Ok(PV::$pvt(self.try_into()?))
}
})*
$(impl TryFrom<PV> for $from_t {
type Error = Error;
fn try_from(v: PV) -> Result<$from_t, Self::Error> {
if let PV::$pvt(x) = v {
Ok(x.try_into()?)
} else {
Err(Error::new(ErrorKind::TypeError {
expect: PV::$pvt(Default::default()).bt_type_of(),
got: v.bt_type_of(),
}))
}
}
}
impl TryFrom<PV> for ObjRef<$from_t> {
type Error = Error;
#[inline(always)]
fn try_from(v: PV) -> Result<ObjRef<$from_t>, Self::Error> {
Ok(ObjRef(v.try_into()?))
}
})*
};
}
pv_convert!(Int,
i8, u8,
i16, u16,
i32, u32,
i64, u64,
i128, u128,
isize, usize);
pv_convert!(Real,
f32);
pv_convert!(Char,
char);
impl IntoLisp for () {
fn into_pv(self, _: &mut Arena) -> Result<PV, Error> {
Ok(PV::Nil)
}
}
impl TryFrom<PV> for () {
type Error = Error;
fn try_from(v: PV) -> Result<(), Self::Error> {
if let PV::Nil = v {
Ok(())
} else {
Err(Error::new(ErrorKind::TypeError {
expect: PV::Nil.bt_type_of(),
got: v.bt_type_of(),
}))
}
}
}
pub struct Ignore;
impl IntoLisp for Ignore {
fn into_pv(self, _: &mut Arena) -> Result<PV, Error> {
Ok(PV::Nil)
}
}
impl TryFrom<PV> for Ignore {
type Error = Error;
fn try_from(_v: PV) -> Result<Ignore, Self::Error> {
Ok(Ignore)
}
}
impl IntoLisp for bool {
fn into_pv(self, _: &mut Arena) -> Result<PV, Error> {
Ok(PV::Bool(self))
}
}
impl IntoLisp for &str {
fn into_pv(self, mem: &mut Arena) -> Result<PV, Error> {
Ok(mem.put_pv(self.to_string()))
}
}
impl IntoLisp for String {
fn into_pv(self, mem: &mut Arena) -> Result<PV, Error> {
Ok(mem.put_pv(self))
}
}
impl IntoLisp for SymID {
fn into_pv(self, _mem: &mut Arena) -> Result<PV, Error> {
Ok(PV::Sym(self))
}
}
impl<T> IntoLisp for Vec<T>
where T: IntoLisp
{
fn into_pv(self, mem: &mut Arena) -> Result<PV, Error> {
let arr = self.into_iter()
.map(|v| v.into_pv(mem))
.collect::<Result<Vec<PV>, _>>()?;
Ok(mem.put_pv(arr))
}
}
impl<T> TryFrom<PV> for Vec<T>
where T: TryFrom<PV, Error=Error>
{
type Error = Error;
fn try_from(v: PV) -> Result<Vec<T>, Self::Error> {
with_ref!(v, Vector(v) => {
(*v).iter().map(|&x| x.try_into())
.collect::<Result<_, _>>()
})
}
}
impl<'a> TryFrom<PV> for &'a str {
type Error = Error;
fn try_from(v: PV) -> Result<&'a str, Self::Error> {
with_ref!(v, String(s) => { Ok(&*s) })
}
}
impl<T, E> IntoLisp for Result<T, E>
where T: IntoLisp, E: Into<Error>
{
fn into_pv(self, mem: &mut Arena) -> Result<PV, Error> {
match self {
Ok(v) => v.into_pv(mem),
Err(e) => Err(e.into()),
}
}
}
#[cfg(not(feature = "freeze"))]
pub unsafe trait Subr: CloneSubr + Send + 'static {
fn call(&mut self, vm: &mut R8VM, args: &[PV]) -> Result<PV, Error>;
fn name(&self) -> &'static str;
}
#[cfg(feature = "freeze")]
pub unsafe trait Subr: CloneSubr + Send + 'static {
fn call(&mut self, vm: &mut R8VM, args: &[PV]) -> Result<PV, Error>;
fn name(&self) -> &'static str;
}
pub trait IntoSubr: Subr {
fn into_subr(self) -> Box<dyn Subr>;
}
impl<T> IntoSubr for T where T: Subr + Sized + 'static {
fn into_subr(self) -> Box<dyn Subr> {
Box::new(self)
}
}
impl fmt::Debug for Box<dyn Subr> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "(subr {})", self.name())
}
}
impl LispFmt for Box<dyn Subr> {
fn lisp_fmt(&self,
_: &mut VisitSet,
f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(subr {})", self.name())
}
}
pub trait CloneSubr {
fn clone_subr(&self) -> Box<dyn Subr>;
}
impl<T> CloneSubr for T
where T: Subr + Clone + 'static
{
fn clone_subr(&self) -> Box<dyn Subr> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn Subr> {
fn clone(&self) -> Box<dyn Subr> {
self.clone_subr()
}
}
impl IntoLisp for Box<dyn Subr> {
fn into_pv(self, mem: &mut Arena) -> Result<PV, Error> {
let p = mem.put(self);
Ok(NkAtom::make_ref(p))
}
}