use crate::r8vm::{ArgSpec, R8VM};
use crate::nkgc::{PV, SPV, VLambda, Traceable, Arena};
use crate::error::{Error, ErrorKind};
use crate::nk::{NkAtom, NkRelocArray};
use crate::fmt::{LispFmt, VisitSet};
use crate::sym_db::SymDB;
use std::convert::{TryInto, TryFrom};
use std::fmt;
use std::ptr;
pub trait IntoLisp: Sized {
fn into_spv<'a>(self, mem: &mut Arena<'a>) -> Result<SPV<'a>, Error> {
let pv = self.into_pv(mem)?;
Ok(mem.make_extref(pv))
}
fn into_pv(self, mem: &mut Arena) -> Result<PV, Error>;
}
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 {
ty: ErrorKind::TypeError {
expect: PV::$pvt(Default::default()).type_of(),
got: v.type_of(),
op: Default::default(),
argn: 0
},
src: None
})
}
}
})*
};
}
pv_convert!(Int,
i8, u8,
i16, u16,
i32, u32,
i64, u64,
i128, u128);
pv_convert!(Real,
f32);
impl IntoLisp for bool {
fn into_pv(self, _: &mut Arena) -> Result<PV, Error> {
Ok(PV::Bool(self))
}
}
impl IntoLisp for String {
fn into_pv(self, mem: &mut Arena) -> Result<PV, Error> {
Ok(mem.put(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(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()),
}
}
}
pub unsafe trait Subr: CloneSubr {
fn call(&mut self, vm: &mut R8VM, args: &[PV]) -> Result<PV, Error>;
fn name(&self) -> &'static str;
}
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,
_: &dyn SymDB,
_: &mut VisitSet,
f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(subr {})", self.name())
}
}
impl Traceable for Box<dyn Subr> {
fn trace(&self, _gray: &mut Vec<*mut NkAtom>) {}
fn update_ptrs(&mut self, _reloc: &NkRelocArray) {}
}
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.alloc::<Self>();
unsafe { ptr::write(p, self) }
Ok(NkAtom::make_ref(p))
}
}
#[inline]
fn my_function(x: i32, y: i32) -> i32 {
println!("Inside my_function: {} {}", x, y);
let res = x + y.pow(2);
println!("res: {}", res);
res
}
#[allow(non_camel_case_types)]
#[derive(Clone)]
pub struct my_function_obj {}
impl my_function_obj {
#[inline]
pub fn new() -> Box<dyn Subr> {
Box::new(my_function_obj {})
}
}
unsafe impl Subr for my_function_obj {
fn call(&mut self, vm: &mut R8VM, args: &[PV]) -> Result<PV, Error> {
static SPEC: ArgSpec = ArgSpec::normal(2);
SPEC.check(Default::default(), args.len() as u16)?;
let x = args[0].try_into().map_err(|e: Error| e.argn(0))?;
let y = args[1].try_into().map_err(|e: Error| e.argn(1))?;
my_function(x, y).into_pv(&mut vm.mem)
}
fn name(&self) -> &'static str { "my-function" }
}
unsafe impl Subr for VLambda {
fn call(&mut self, vm: &mut R8VM, args: &[PV]) -> Result<PV, Error> {
if self.args.has_env() {
unimplemented!("Calling closures as Subr is not implemented.")
}
vm.raw_call(self.name, args)
}
fn name(&self) -> &'static str {
"lambda"
}
}