use core::marker::PhantomData;
use crate::{
Cache, Emitter, Lexer,
emitter::Fatal,
lexer::{BlackHole, DefaultCache, InputContext},
};
pub trait ParseContext<'inp, L, Lang: ?Sized = ()> {
type Emitter: Emitter<'inp, L, Lang>
where
L: Lexer<'inp>;
type Cache: Cache<'inp, L>
where
L: Lexer<'inp>;
fn provide(self) -> InputContext<Self::Emitter, Self::Cache>
where
L: Lexer<'inp>;
}
impl<'inp, L, Lang: ?Sized> ParseContext<'inp, L, Lang> for ()
where
L: Lexer<'inp>,
Fatal<(), Lang>: Emitter<'inp, L, Lang>,
{
type Emitter = Fatal<(), Lang>;
type Cache = DefaultCache<'inp, L>;
#[cfg_attr(not(tarpaulin), inline(always))]
fn provide(self) -> InputContext<Self::Emitter, Self::Cache>
where
L: Lexer<'inp>,
{
InputContext::new(Fatal::of(), DefaultCache::<'inp, L>::new())
}
}
impl<'inp, L, Lang: ?Sized> ParseContext<'inp, L, Lang> for BlackHole
where
L: Lexer<'inp>,
Fatal<(), Lang>: Emitter<'inp, L, Lang>,
{
type Emitter = Fatal<(), Lang>;
type Cache = Self;
#[cfg_attr(not(tarpaulin), inline(always))]
fn provide(self) -> InputContext<Self::Emitter, Self::Cache>
where
L: Lexer<'inp>,
{
InputContext::new(Fatal::of(), Self)
}
}
impl<'inp, L, E, C, Lang: ?Sized> ParseContext<'inp, L, Lang> for (E, C)
where
L: Lexer<'inp>,
E: Emitter<'inp, L, Lang>,
C: Cache<'inp, L>,
{
type Emitter = E;
type Cache = C;
fn provide(self) -> InputContext<Self::Emitter, Self::Cache>
where
L: Lexer<'inp>,
{
InputContext::new(self.0, self.1)
}
}
pub type FatalContext<'inp, L, Error, Lang = ()> =
ParserContext<'inp, L, Fatal<Error, Lang>, DefaultCache<'inp, L>, Lang>;
pub struct ParserContext<'inp, L, E, C = DefaultCache<'inp, L>, Lang: ?Sized = ()>
where
L: Lexer<'inp>,
E: Emitter<'inp, L, Lang>,
C: Cache<'inp, L>,
{
emitter: E,
cache: Option<C::Options>,
_marker: PhantomData<&'inp L>,
_lang: PhantomData<Lang>,
}
impl<'inp, L, E, C> ParserContext<'inp, L, E, C>
where
L: Lexer<'inp>,
E: Emitter<'inp, L>,
C: Cache<'inp, L>,
{
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(emitter: E) -> Self {
Self::of(emitter)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_cache_options(emitter: E, options: C::Options) -> Self {
Self::with_cache_options_of(emitter, options)
}
}
impl<'inp, L, E, C, Lang: ?Sized> ParserContext<'inp, L, E, C, Lang>
where
L: Lexer<'inp>,
E: Emitter<'inp, L, Lang>,
C: Cache<'inp, L>,
{
const fn new_in(emitter: E, opts: Option<C::Options>) -> Self {
Self {
emitter,
cache: opts,
_marker: PhantomData,
_lang: PhantomData,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn of(emitter: E) -> Self {
Self::new_in(emitter, None)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_cache_options_of(emitter: E, options: C::Options) -> Self {
Self::new_in(emitter, Some(options))
}
}
impl<'inp, L, E, C, Lang: ?Sized> ParseContext<'inp, L, Lang> for ParserContext<'inp, L, E, C, Lang>
where
L: Lexer<'inp>,
E: Emitter<'inp, L, Lang>,
C: Cache<'inp, L>,
{
type Emitter = E;
type Cache = C;
#[cfg_attr(not(tarpaulin), inline(always))]
fn provide(self) -> InputContext<Self::Emitter, Self::Cache>
where
L: Lexer<'inp>,
{
match self.cache {
Some(options) => InputContext::new(self.emitter, C::with_options(options)),
None => InputContext::new(self.emitter, C::new()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lexer::DummyLexer;
#[test]
fn test_default_context() {
fn assert_context<'inp, Ctx>()
where
Ctx: ParseContext<'inp, DummyLexer>,
{
}
assert_context::<()>();
assert_context::<FatalContext<'_, DummyLexer, ()>>();
}
#[test]
fn test_custom_context() {
fn assert_context<'inp, Ctx>()
where
Ctx: ParseContext<'inp, DummyLexer>,
{
}
assert_context::<(Fatal<()>, DefaultCache<'_, DummyLexer>)>();
}
}