use std::rc::Rc;
use debris_error::{LangError, LangErrorKind, LangResult, Result};
use crate::{
class::ClassRef,
objects::{
obj_bool_static::ObjStaticBool, obj_function::FunctionContext,
obj_int_static::ObjStaticInt, obj_null::ObjNull, obj_string::ObjString,
},
type_context::TypeContext,
ObjectPayload, ObjectRef, ValidPayload,
};
pub struct DebrisFunctionInterface(NormalizedFunction);
impl DebrisFunctionInterface {
pub(crate) fn call(&self, function_ctx: &mut FunctionContext) -> Result<ObjectRef> {
let raw_value = self.call_raw(function_ctx);
self.handle_raw_result(function_ctx, raw_value)
.map_err(|kind| LangError::new(kind, function_ctx.span).into())
}
pub(crate) fn call_raw(
&self,
function_ctx: &mut FunctionContext,
) -> Option<LangResult<ObjectRef>> {
(self.0.inner_fn)(function_ctx)
}
pub fn handle_raw_result(
&self,
function_ctx: &FunctionContext,
value: Option<LangResult<ObjectRef>>,
) -> LangResult<ObjectRef> {
match value {
Some(val) => val,
None => Err(LangErrorKind::UnexpectedOverload {
expected: (self.0.required_parameter_fn)(function_ctx.type_ctx()).map_or_else(
|| vec![vec!["<Any>".to_string()]],
|overloads| {
overloads
.into_iter()
.map(|params| {
params.into_iter().map(|param| param.to_string()).collect()
})
.collect()
},
),
parameters: function_ctx
.parameters
.iter()
.map(|param| param.class.to_string())
.collect(),
function_definition_span: None,
}),
}
}
}
impl From<NormalizedFunction> for DebrisFunctionInterface {
fn from(value: NormalizedFunction) -> Self {
DebrisFunctionInterface(value)
}
}
type NormalizedFnSig = dyn Fn(&mut FunctionContext) -> Option<LangResult<ObjectRef>>;
pub struct NormalizedFunction {
inner_fn: Box<NormalizedFnSig>,
required_parameter_fn: Box<dyn Fn(&TypeContext) -> Option<Vec<Vec<ClassRef>>>>,
}
pub fn make_overload(functions: Vec<NormalizedFunction>) -> NormalizedFunction {
let functions = Rc::new(functions);
let functions_2 = Rc::clone(&functions);
NormalizedFunction {
inner_fn: Box::new(move |ctx| {
for function in functions.as_ref() {
if let Some(result) = (function.inner_fn)(ctx) {
return Some(result);
}
}
None
}),
required_parameter_fn: Box::new(move |type_ctx| {
let mut values = Vec::with_capacity(functions_2.len());
for function in functions_2.as_ref() {
values.extend(
(function.required_parameter_fn)(type_ctx).unwrap_or_else(|| vec![vec![]]),
);
}
Some(values)
}),
}
}
pub trait ValidReturnType {
fn into_result(self, ctx: &mut FunctionContext) -> Option<LangResult<ObjectRef>>;
}
impl<T> ValidReturnType for T
where
T: ObjectPayload,
{
fn into_result(self, ctx: &mut FunctionContext) -> Option<LangResult<ObjectRef>> {
Some(Ok(self.into_object(ctx.type_ctx())))
}
}
impl<T> ValidReturnType for LangResult<T>
where
T: ObjectPayload,
{
fn into_result(self, ctx: &mut FunctionContext) -> Option<LangResult<ObjectRef>> {
Some(self.map(|value| value.into_object(ctx.type_ctx())))
}
}
impl<T> ValidReturnType for Option<LangResult<T>>
where
T: ObjectPayload,
{
fn into_result(self, ctx: &mut FunctionContext) -> Option<LangResult<ObjectRef>> {
self.map(|res| res.map(|val| val.into_object(ctx.type_ctx())))
}
}
impl ValidReturnType for Option<LangResult<ObjectRef>> {
fn into_result(self, _ctx: &mut FunctionContext) -> Option<LangResult<ObjectRef>> {
self
}
}
impl ValidReturnType for LangResult<ObjectRef> {
fn into_result(self, _ctx: &mut FunctionContext) -> Option<LangResult<ObjectRef>> {
Some(self)
}
}
impl ValidReturnType for ObjectRef {
fn into_result(self, _ctx: &mut FunctionContext) -> Option<LangResult<ObjectRef>> {
Some(Ok(self))
}
}
macro_rules! impl_map_valid_return_type {
($from:ty, $to:ty) => {
impl ValidReturnType for $from {
fn into_result(self, ctx: &mut FunctionContext) -> Option<LangResult<ObjectRef>> {
Some(Ok(
<$to as From<$from>>::from(self).into_object(&ctx.type_ctx())
))
}
}
impl ValidReturnType for LangResult<$from> {
fn into_result(self, ctx: &mut FunctionContext) -> Option<LangResult<ObjectRef>> {
Some(match self {
Ok(value) => Ok(<$to as From<$from>>::from(value).into_object(&ctx.type_ctx())),
Err(err) => Err(err),
})
}
}
impl ValidReturnType for Option<$from> {
fn into_result(self, ctx: &mut FunctionContext) -> Option<LangResult<ObjectRef>> {
self.map(|value| Ok(<$to as From<$from>>::from(value).into_object(&ctx.type_ctx())))
}
}
impl ValidReturnType for Option<LangResult<$from>> {
fn into_result(self, ctx: &mut FunctionContext) -> Option<LangResult<ObjectRef>> {
self.map(|res| match res {
Ok(value) => Ok(<$to as From<$from>>::from(value).into_object(&ctx.type_ctx())),
Err(err) => Err(err),
})
}
}
};
}
impl_map_valid_return_type!((), ObjNull);
impl_map_valid_return_type!(i32, ObjStaticInt);
impl_map_valid_return_type!(bool, ObjStaticBool);
impl_map_valid_return_type!(Rc<str>, ObjString);
pub trait ToFunctionInterface<Params, Return>: 'static {
fn to_normalized_function(self) -> NormalizedFunction;
}
impl ToFunctionInterface<(), ()> for NormalizedFunction {
fn to_normalized_function(self) -> NormalizedFunction {
self
}
}
impl<Function, Return> ToFunctionInterface<(&mut FunctionContext<'_, '_, '_>, &[ObjectRef]), Return>
for Function
where
Function: Fn(&mut FunctionContext, &[ObjectRef]) -> Return + 'static,
Return: ValidReturnType,
{
fn to_normalized_function(self) -> NormalizedFunction {
NormalizedFunction {
inner_fn: Box::new(move |ctx: &mut FunctionContext| {
(self)(ctx, ctx.parameters).into_result(ctx)
}),
required_parameter_fn: Box::new(|_ctx| None),
}
}
}
macro_rules! count {
() => (0_usize);
( $x:tt $($xs:tt)* ) => (1_usize + count!($($xs)*));
}
macro_rules! impl_to_function_interface {
($($xs:ident),*) => {
impl<Function, Return, $($xs),*> ToFunctionInterface<(&mut FunctionContext<'_, '_, '_>, $(&$xs),*), Return> for Function
where
Function: Fn(&mut FunctionContext, $(&$xs),*) -> Return + 'static,
Return: ValidReturnType,
$($xs: ObjectPayload),*
{
impl_to_function_interface!(impl_inner, [$($xs),*], ,);
}
impl<Function, Return, $($xs),*> ToFunctionInterface<(&FunctionContext<'_, '_, '_>, $(&$xs),*), Return> for Function
where
Function: Fn(&FunctionContext, $(&$xs),*) -> Return + 'static,
Return: ValidReturnType,
$($xs: ObjectPayload),*
{
impl_to_function_interface!(impl_inner, [$($xs),*], ,);
}
#[allow(unused_parens)]
impl<Function, Return, $($xs),*> ToFunctionInterface<($(&$xs),*), Return> for Function
where
Function: Fn($(&$xs),*) -> Return + 'static,
Return: ValidReturnType,
$($xs: ObjectPayload),*
{
impl_to_function_interface!(impl_inner, [$($xs),*],);
}
};
(impl_inner, [$($xs:ident),*], $($use_ctx:tt)?) => {
fn to_normalized_function(self) -> NormalizedFunction {
let inner_fn = Box::new(move |ctx: &mut FunctionContext| {
let iter = ctx.parameters.iter();
let self_val = if ctx.parameters.len() + 1 == count!($($xs)*) {
ctx.self_val.clone()
} else {
None
};
#[allow(unused_variables, unused_mut)]
let mut iter = self_val.iter().chain(iter);
let temp_slice: [ObjectRef; count!($($xs)*)] = [$(impl_to_function_interface!(maybe_promote, ctx, iter.next(), $xs)),*];
if iter.next().is_some() {
return None;
}
#[allow(unused_variables, unused_mut)]
let mut temp_values = temp_slice.iter();
(self)($(ctx $use_ctx)? $(
impl_to_function_interface!(verify_type, temp_values.next(), $xs)?
),*).into_result(ctx)
});
NormalizedFunction {
inner_fn,
required_parameter_fn: Box::new(#[allow(unused_variables)] |type_ctx| Some(vec![vec![$($xs::static_class(type_ctx)),*]]))
}
}
};
(maybe_promote, $ctx:expr, $val:expr, $typ:ident) => {{
let value = $val?;
if value.downcast_payload::<$typ>().is_some() {
value.clone()
} else {
match $ctx.promote_obj(value.clone(), $ctx.type_ctx().static_class_obj::<$typ>()) {
Some(Ok(value)) => value,
other => return other,
}
}
}};
(verify_type, $val:expr, $typ:ident) => {
$val.and_then(|value| value.downcast_payload::<$typ>())
};
}
impl_to_function_interface!();
impl_to_function_interface!(A);
impl_to_function_interface!(A, B);
impl_to_function_interface!(A, B, C);
impl_to_function_interface!(A, B, C, D);
impl_to_function_interface!(A, B, C, D, E);
impl_to_function_interface!(A, B, C, D, E, F);
impl_to_function_interface!(A, B, C, D, E, F, G);
impl_to_function_interface!(A, B, C, D, E, F, G, H);
pub trait DowncastArray<'a, T> {
fn downcast_array(&'a self) -> Option<T>;
}
macro_rules! impl_downcast_array {
($($xs:ident),*) => {
impl<'a, $($xs),*> DowncastArray<'a, ($(&'a $xs),*,)> for [ObjectRef]
where
$(
$xs: ObjectPayload
),*
{
#[allow(non_snake_case)]
fn downcast_array(&'a self) -> Option<($(&'a $xs),*,)> {
match &self {
[$($xs),*] => {
$(
let $xs = $xs.downcast_payload()?;
)*
Some(($($xs),*,))
}
_ => None,
}
}
}
};
}
impl_downcast_array!(A);
impl_downcast_array!(A, B);
impl_downcast_array!(A, B, C);
impl_downcast_array!(A, B, C, D);
impl_downcast_array!(A, B, C, D, E);
impl_downcast_array!(A, B, C, D, E, F);
impl_downcast_array!(A, B, C, D, E, F, G);
impl_downcast_array!(A, B, C, D, E, F, G, H);
impl_downcast_array!(A, B, C, D, E, F, G, H, I);
impl_downcast_array!(A, B, C, D, E, F, G, H, I, J);