use crate::parse::utils::is_valid_stream;
use proc_macro2::{TokenStream, TokenTree};
use syn::parse::{Parse, ParseStream};
use super::Combinator;
pub type CheckStreamFn = fn(ParseStream) -> bool;
#[derive(Clone, Copy)]
union CheckStreamFnPointer {
f: CheckStreamFn,
raw: *const (),
}
unsafe impl std::marker::Send for CheckStreamFnPointer {}
unsafe impl std::marker::Sync for CheckStreamFnPointer {}
#[derive(Clone)]
pub struct GroupDeterminer {
combinator: Option<Combinator>,
check_input_fn: CheckStreamFnPointer,
validate_parsed: bool,
length: u8,
}
#[macro_export]
macro_rules! define_group_determiners {
($($combinator: ident => $($token: expr),+ => $length: expr),+) => {{
[
$crate::define_determiner_with_no_group!(Token![,] => 0),
$(
$crate::define_group_determiner!(
$crate::chain::group::Combinator::$combinator => $($token),+ => $length
)
),*,
$crate::chain::group::GroupDeterminer::new_const(
None,
$crate::handler::Handler::peek_handler as *const (),
true,
0
)
]
}};
}
#[macro_export]
macro_rules! define_determiner_with_no_group {
($($token: expr),+ => $length: expr) => {{
let check_tokens = $crate::define_tokens_checker!($($token),+);
$crate::chain::group::GroupDeterminer::new_const(
None,
check_tokens as *const (),
true,
$length
)
}}
}
#[macro_export]
macro_rules! define_tokens_checker {
($token1: expr, $token2:expr, $token3:expr) => {{
fn check_tokens(input: ::syn::parse::ParseStream<'_>) -> bool {
input.peek($token1) && input.peek2($token2) && input.peek3($token3)
}
check_tokens
}};
($token1: expr, $token2:expr) => {{
fn check_tokens(input: ::syn::parse::ParseStream<'_>) -> bool {
input.peek($token1) && input.peek2($token2)
}
check_tokens
}};
($token: expr) => {{
fn check_tokens(input: ::syn::parse::ParseStream<'_>) -> bool { input.peek($token) }
check_tokens
}};
($($token: expr),+) => {{
fn check_tokens(input: ::syn::parse::ParseStream<'_>) -> bool {
let input = input.fork();
$(
input.peek($token) && $crate::parse::utils::skip(&input)
)&&+
}
check_tokens
}};
}
#[macro_export]
macro_rules! define_group_determiner {
($combinator: expr => $($tokens: expr),+ => $length: expr => $validate_parsed: expr) => {{
let check_tokens = $crate::define_tokens_checker!($($tokens),*);
$crate::chain::group::GroupDeterminer::new_const(
Some($combinator),
check_tokens as *const (),
$validate_parsed,
$length
)
}};
($combinator: expr => $($tokens: expr),+ => $length: expr) => {
$crate::define_group_determiner!(
$combinator => $($tokens),+ => $length => true
)
};
}
impl GroupDeterminer {
pub const fn new_const(
combinator: Option<Combinator>,
check_input_fn: *const (),
validate_parsed: bool,
length: u8,
) -> Self {
Self {
combinator,
check_input_fn: CheckStreamFnPointer {
raw: check_input_fn,
},
validate_parsed,
length,
}
}
pub fn new(
combinator: impl Into<Option<Combinator>>,
check_input_fn: CheckStreamFn,
validate_parsed: bool,
length: u8,
) -> Self {
Self {
combinator: combinator.into(),
check_input_fn: CheckStreamFnPointer { f: check_input_fn },
validate_parsed,
length,
}
}
pub fn combinator(&self) -> Option<Combinator> {
self.combinator
}
pub fn check_input(&self, input: ParseStream<'_>) -> bool {
(unsafe { self.check_input_fn.f })(input)
}
pub fn check_parsed<T: Parse>(&self, input: TokenStream) -> bool {
!self.validate_parsed || is_valid_stream::<T>(input)
}
pub fn erase_input<'b>(&self, input: ParseStream<'b>) -> syn::Result<ParseStream<'b>> {
for _ in 0..self.len() {
input.parse::<TokenTree>()?;
}
Ok(input)
}
pub fn len(&self) -> u8 {
self.length
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[cfg(test)]
mod tests {
use super::super::*;
use super::*;
#[test]
fn it_creates_comma_determiner() {
fn check_comma(input: ParseStream) -> bool {
input.peek(::syn::Token![,])
}
let first_comma_determiner = GroupDeterminer::new_const(
None, check_comma as *const (),
false,
1,
);
assert_eq!(first_comma_determiner.combinator(), None);
assert!(first_comma_determiner.check_parsed::<::syn::Expr>(::quote::quote! { , }));
assert_eq!(first_comma_determiner.len(), 1);
}
#[test]
fn it_creates_then_determiner() {
fn check_then(input: ParseStream) -> bool {
input.peek(::syn::Token![=>])
}
let then_determiner = GroupDeterminer::new(Combinator::Then, check_then, true, 2);
assert_eq!(then_determiner.combinator(), Some(Combinator::Then));
assert!(then_determiner.check_parsed::<::syn::Expr>(::quote::quote! { 23 }));
assert_eq!(then_determiner.len(), 2);
}
}