use super::matcher::{FuncMatcher, Match, MatchError};
use super::vm::{Callable, Func, Script};
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::marker::PhantomData;
pub type ModuleList<'a, C> = Box<[FuncMatcher<'a, C>]>;
pub struct Cons<H, T> {
head: PhantomData<H>,
tail: PhantomData<T>,
}
pub struct Nil;
pub trait ModuleType<'a, C> {
type Error;
fn compile_line(ctx: &mut C, string: &'a str) -> Result<Func<'a>, Self::Error>;
fn compile(ctx: &mut C, string: &'a str) -> Result<Script<'a>, (usize, Self::Error)> {
let mut script = Vec::new();
for (line_num, line) in string
.lines()
.enumerate()
.map(|(i, s)| (i, s.trim()))
.filter(|(_, s)| !s.is_empty())
{
let func = Self::compile_line(ctx, line).map_err(|e| (line_num, e))?;
script.push(func);
}
Ok(script.into())
}
}
pub trait Module<'a, C> {
type Error;
fn compile_line(&self, ctx: &mut C, string: &'a str) -> Result<Func<'a>, Self::Error>;
fn compile(&self, ctx: &mut C, string: &'a str) -> Result<Script<'a>, (usize, Self::Error)> {
let mut script = Vec::new();
for (line_num, line) in string
.lines()
.enumerate()
.map(|(i, s)| (i, s.trim()))
.filter(|(_, s)| !s.is_empty())
{
let func = self.compile_line(ctx, line).map_err(|e| (line_num, e))?;
script.push(func);
}
Ok(script.into())
}
}
impl<'a, H, T, C> ModuleType<'a, C> for Cons<H, T>
where
H: 'a + Match<'a, C> + Callable,
T: ModuleType<'a, C, Error = MatchError>,
<T as ModuleType<'a, C>>::Error: Into<MatchError>,
{
type Error = MatchError;
fn compile_line(ctx: &mut C, string: &'a str) -> Result<Box<dyn Callable + 'a>, Self::Error> {
match H::match_str(ctx, string) {
Ok(matched) => Ok(Box::new(matched)),
Err(_) => T::compile_line(ctx, string),
}
}
}
impl<'a, C> ModuleType<'a, C> for Nil {
type Error = MatchError;
fn compile_line(_: &mut C, _: &'a str) -> Result<Box<dyn Callable + 'a>, Self::Error> {
Err(MatchError::UnexpectedEof)
}
}
impl<'a, C, T> Module<'a, C> for T
where
T: AsRef<[FuncMatcher<'a, C>]>,
{
type Error = MatchError;
fn compile_line(&self, ctx: &mut C, string: &'a str) -> Result<Func<'a>, Self::Error> {
for match_str in self.as_ref().into_iter() {
if let Ok(func) = match_str(ctx, string) {
return Ok(func);
}
}
Err(MatchError::UnexpectedEof)
}
}
#[macro_export]
macro_rules! mod_type {
() => {
$crate::module::Nil
};
($head:ty) => {
$crate::module::Cons<$head, $crate::module::Nil>
};
($head:ty, $($tail:ty),*) => {
$crate::module::Cons<$head, $crate::mod_type!($($tail),*)>
}
}
#[cfg(feature = "std")]
#[macro_export]
macro_rules! mod_list {
() => {
::std::boxed::Box::new([])
};
($ctx:ty => $($item:ty),*) => {
::std::boxed::Box::new([$(<$item as $crate::matcher::MatchFunc<$ctx>>::match_func),*])
}
}
#[cfg(not(feature = "std"))]
#[macro_export]
macro_rules! mod_list {
() => {
::alloc::boxed::Box::new([])
};
($ctx:ty => $($item:ty),*) => {
::alloc::boxed::Box::new([$(<$item as $crate::matcher::MatchFunc<$ctx>>::match_func),*])
}
}