use std::{fmt, marker::PhantomData};
use crate::{Object, RuntimeError, Value, ValueRef, bytecode::FunctionId};
#[derive(Debug, Clone, Copy)]
pub struct UserFunction(FunctionId);
impl UserFunction {
pub fn new(id: FunctionId) -> Self {
Self(id)
}
pub fn id(&self) -> FunctionId {
self.0
}
}
impl Object for UserFunction {
fn debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "UserFunction({:?})", self.0)
}
}
pub struct NativeFunction {
pub name: String,
pub func: Box<dyn Function + Send + Sync + 'static>,
}
impl NativeFunction {
pub fn new(name: impl ToString, func: Box<dyn Function + Send + Sync + 'static>) -> Self {
Self {
name: name.to_string(),
func,
}
}
}
impl fmt::Debug for NativeFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<NativeFunction`{}`>", self.name)
}
}
impl Object for NativeFunction {
fn call(&mut self, args: &[ValueRef]) -> Result<Option<Value>, RuntimeError> {
(self.func).call(args)
}
}
pub trait Function: Send + 'static {
fn call(&mut self, args: &[ValueRef]) -> Result<Option<Value>, RuntimeError>;
}
pub struct IntoFunction<F, Args> {
func: F,
_marker: PhantomData<fn(Args) -> ()>,
}
impl<F, Args> Function for IntoFunction<F, Args>
where
F: Callable<Args> + Clone + Send,
Args: 'static,
{
fn call(&mut self, args: &[ValueRef]) -> Result<Option<Value>, RuntimeError> {
self.func.call(args)
}
}
pub trait IntoRet {
fn into_ret(self) -> Result<Option<Value>, RuntimeError>;
}
impl<T: Object> IntoRet for T {
fn into_ret(self) -> Result<Option<Value>, RuntimeError> {
Ok(Some(Value::new(self)))
}
}
impl<T: Object> IntoRet for Result<T, RuntimeError> {
fn into_ret(self) -> Result<Option<Value>, RuntimeError> {
self.map(|v| Some(Value::new(v)))
}
}
pub trait FromValue: Sized {
fn from_value(value: &ValueRef) -> Result<Self, RuntimeError>;
}
impl<T> FromValue for T
where
T: Object + Clone,
{
fn from_value(value: &ValueRef) -> Result<T, RuntimeError> {
let v = value.value();
let value = v
.downcast_ref::<T>()
.ok_or(RuntimeError::invalid_type::<T>(value))?;
Ok(value.clone())
}
}
pub trait Callable<Args>: Clone + Sized + 'static {
fn call(&mut self, args: &[ValueRef]) -> Result<Option<Value>, RuntimeError>;
fn into_function(self) -> IntoFunction<Self, Args> {
IntoFunction {
func: self,
_marker: PhantomData,
}
}
}
impl<F, Ret> Callable<&[ValueRef]> for F
where
F: Fn(&[ValueRef]) -> Ret + Clone + 'static,
Ret: IntoRet,
{
fn call(&mut self, args: &[ValueRef]) -> Result<Option<Value>, RuntimeError> {
(self)(args).into_ret()
}
}
impl<F, Ret> Callable<()> for F
where
F: Fn() -> Ret + Clone + 'static,
Ret: IntoRet,
{
fn call(&mut self, _args: &[ValueRef]) -> Result<Option<Value>, RuntimeError> {
self().into_ret()
}
}
macro_rules! impl_callable {
($($idx: expr => $arg: ident),+) => {
#[allow(non_snake_case)]
impl<F, Ret, $($arg,)*> Callable<($($arg,)*)> for F
where
F: Fn($($arg,)*) -> Ret + Clone + 'static,
Ret: IntoRet,
$( $arg: FromValue + 'static, )*
{
fn call(&mut self, args: &[ValueRef]) -> Result<Option<Value>, RuntimeError> {
$(
let $arg = <$arg>::from_value(args.get($idx).ok_or(RuntimeError::invalid_argument::<$arg>($idx, "NoThing"))?)?;
)*
(self)($($arg,)*).into_ret()
}
}
}
}
impl_callable!(0=>T0);
impl_callable!(0=>T0, 1=>T1);
impl_callable!(0=>T0, 1=>T1, 2=>T2);
impl_callable!(0=>T0, 1=>T1, 2=>T2, 3=>T3);
impl_callable!(0=>T0, 1=>T1, 2=>T2, 3=>T3, 4=>T4);
impl_callable!(0=>T0, 1=>T1, 2=>T2, 3=>T3, 4=>T4, 5=>T5);
impl_callable!(0=>T0, 1=>T1, 2=>T2, 3=>T3, 4=>T4, 5=>T5, 6=>T6);
impl_callable!(0=>T0, 1=>T1, 2=>T2, 3=>T3, 4=>T4, 5=>T5, 6=>T6, 7=>T7);
impl_callable!(0=>T0, 1=>T1, 2=>T2, 3=>T3, 4=>T4, 5=>T5, 6=>T6, 7=>T7, 8=>T8);
impl_callable!(0=>T0, 1=>T1, 2=>T2, 3=>T3, 4=>T4, 5=>T5, 6=>T6, 7=>T7, 8=>T8, 9=>T9);
impl_callable!(0=>T0, 1=>T1, 2=>T2, 3=>T3, 4=>T4, 5=>T5, 6=>T6, 7=>T7, 8=>T8, 9=>T9, 10=>T10);
impl_callable!(0=>T0, 1=>T1, 2=>T2, 3=>T3, 4=>T4, 5=>T5, 6=>T6, 7=>T7, 8=>T8, 9=>T9, 10=>T10, 11=>T11);
impl_callable!(0=>T0, 1=>T1, 2=>T2, 3=>T3, 4=>T4, 5=>T5, 6=>T6, 7=>T7, 8=>T8, 9=>T9, 10=>T10, 11=>T11, 12=>T12);
impl_callable!(0=>T0, 1=>T1, 2=>T2, 3=>T3, 4=>T4, 5=>T5, 6=>T6, 7=>T7, 8=>T8, 9=>T9, 10=>T10, 11=>T11, 12=>T12, 13=>T13);
impl_callable!(0=>T0, 1=>T1, 2=>T2, 3=>T3, 4=>T4, 5=>T5, 6=>T6, 7=>T7, 8=>T8, 9=>T9, 10=>T10, 11=>T11, 12=>T12, 13=>T13, 14=>T14);
impl_callable!(0=>T0, 1=>T1, 2=>T2, 3=>T3, 4=>T4, 5=>T5, 6=>T6, 7=>T7, 8=>T8, 9=>T9, 10=>T10, 11=>T11, 12=>T12, 13=>T13, 14=>T14, 15=>T15);