use crate::error::LoxError;
use crate::hash::hash_ptr;
use crate::private::Sealed;
use crate::LoxResult;
use crate::LoxValue;
use crate::async_types;
use std::fmt;
use std::fmt::Debug;
use std::hash::Hash;
use std::hash::Hasher;
use std::ptr;
use std::sync::Arc;
#[cfg(feature = "serde")]
use serde::Serialize;
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct LoxFn {
#[cfg_attr(feature = "serde", serde(skip_serializing))]
fun: Box<dyn Fn(LoxArgs) -> LoxResult + Send + Sync>,
params: Vec<&'static str>,
}
impl LoxFn {
pub fn new<F: Fn(LoxArgs) -> LoxResult + Send + Sync + 'static>(
fun: F,
params: Vec<&'static str>,
) -> Self {
Self {
fun: Box::new(fun),
params,
}
}
pub(super) fn call(&self, args: LoxArgs) -> LoxResult {
(self.fun)(args.check_arity(self.params.len())?)
}
pub(super) fn params(&self) -> &[&'static str] {
&self.params
}
}
impl Debug for LoxFn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LoxFn")
.field("params", &self.params)
.finish_non_exhaustive()
}
}
impl PartialEq for LoxFn {
fn eq(&self, other: &Self) -> bool {
let ptr1: *const _ = self.fun.as_ref();
let ptr2: *const _ = other.fun.as_ref();
ptr::eq(ptr1.cast::<()>(), ptr2.cast::<()>()) && self.params == other.params
}
}
impl Hash for LoxFn {
fn hash<H: Hasher>(&self, state: &mut H) {
hash_ptr(self.fun.as_ref(), state);
}
}
#[derive(Clone, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[doc(hidden)] pub enum LoxMethod {
Sync(Arc<LoxFn>),
Async(Arc<async_types::Coroutine>),
}
impl LoxMethod {
pub(super) fn params(&self) -> &[&'static str] {
match self {
LoxMethod::Sync(fun) => &fun.params,
LoxMethod::Async(fun) => fun.params(),
}
}
pub(super) fn get_sync(self) -> Option<Arc<LoxFn>> {
match self {
LoxMethod::Sync(fun) => Some(fun),
LoxMethod::Async(_) => None,
}
}
pub(super) fn call(&self, args: LoxArgs) -> LoxResult {
let args = args.check_arity(self.params().len() - 1)?;
match self {
LoxMethod::Sync(fun) => (fun.fun)(args),
LoxMethod::Async(fun) => Ok(LoxValue::Future(fun.start(args))),
}
}
}
#[doc(hidden)]
impl Debug for LoxMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LoxMethod::Sync(fun) => write!(f, "Sync({:#?})", fun.params),
LoxMethod::Async(fun) => write!(f, "Async({:#?})", fun.params()),
}
}
}
#[doc(hidden)] impl From<LoxFn> for LoxMethod {
fn from(value: LoxFn) -> Self {
LoxMethod::Sync(Arc::new(value))
}
}
#[doc(hidden)] impl From<Arc<LoxFn>> for LoxMethod {
fn from(value: Arc<LoxFn>) -> Self {
LoxMethod::Sync(value)
}
}
#[doc(hidden)] impl From<async_types::Coroutine> for LoxMethod {
fn from(value: async_types::Coroutine) -> Self {
LoxMethod::Async(Arc::new(value))
}
}
#[doc(hidden)] impl From<Arc<async_types::Coroutine>> for LoxMethod {
fn from(value: Arc<async_types::Coroutine>) -> Self {
LoxMethod::Async(value)
}
}
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct LoxArgs {
pub(crate) head: Option<LoxValue>,
pub(crate) main: Vec<LoxValue>,
}
impl LoxArgs {
pub const fn new(values: Vec<LoxValue>) -> LoxArgs {
LoxArgs {
head: None,
main: values,
}
}
pub fn drain(&mut self) -> impl Iterator<Item = LoxValue> + '_ {
self.head.take().into_iter().chain(self.main.drain(..))
}
pub fn len(&self) -> usize {
if self.head.is_some() {
self.main.len() + 1
} else {
self.main.len()
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get(&self, index: usize) -> Option<&LoxValue> {
self.head.as_ref().map_or_else(
|| self.main.get(index),
|head| {
if index == 0 {
Some(head)
} else {
self.main.get(index - 1)
}
},
)
}
pub fn extract<T: ConcreteLoxArgs>(self) -> Result<T, LoxError> {
T::extract_from_args(self)
}
pub(crate) fn check_arity(self, arity: usize) -> Result<LoxArgs, LoxError> {
if self.main.len() == arity {
Ok(self)
} else {
Err(LoxError::incorrect_arity(arity, self.main.len()))
}
}
pub(super) fn with_head(mut self, value: LoxValue) -> LoxArgs {
self.head = Some(value);
self
}
}
impl From<Vec<LoxValue>> for LoxArgs {
fn from(value: Vec<LoxValue>) -> Self {
LoxArgs::new(value)
}
}
impl<const N: usize> From<[LoxValue; N]> for LoxArgs {
fn from(value: [LoxValue; N]) -> Self {
value.to_vec().into()
}
}
pub trait ConcreteLoxArgs: Sealed + Sized {
#[allow(clippy::missing_errors_doc)]
fn extract_from_args(args: LoxArgs) -> Result<Self, LoxError>;
}
macro_rules! count {
( $start:ident $( $rest:ident )* ) => {
1 + count!( $( $rest )* )
};
() => {
0
};
}
macro_rules! impl_concrete_lox_args {
( $( $ty:ident )* ) => {
impl_concrete_lox_args! { | $( $ty )* }
};
( $( $start:ident )* | $next:ident $( $end:ident )* ) => {
impl_concrete_lox_args! { @ $( $start )* }
impl_concrete_lox_args! { $( $start )* $next | $( $end )* }
};
( $( $ty:ident )* | ) => {
impl_concrete_lox_args! { @ $( $ty )* }
};
( @ $( $ty:ident )* ) => {
impl<$( $ty: TryFrom<LoxValue>, )*> Sealed for ( $( $ty, )* )
where $( LoxError: From<<$ty as TryFrom<LoxValue>>::Error> ),* {}
impl<$( $ty ),*> ConcreteLoxArgs for ( $( $ty, )* )
where
$( $ty: TryFrom<LoxValue>, )*
$( LoxError: From<<$ty as TryFrom<LoxValue>>::Error> ),*
{
fn extract_from_args(mut args: LoxArgs) -> Result<Self, LoxError> {
const COUNT: usize = count!( $( $ty )* );
let len = args.main.len();
let mut drain = args.drain();
let result = ( $(
$ty::try_from(
drain
.next()
.ok_or(LoxError::incorrect_arity(COUNT, len))?
)?,
)* );
if drain.next().is_some() {
return Err(LoxError::incorrect_arity(COUNT, len));
}
Ok(result)
}
}
};
}
impl_concrete_lox_args! { T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17 T18 T19 T20 }