extern crate proc_macro;
use ::proc_macro::{*, TokenTree as TT};
use ::core::iter;
macro_rules! matches {(
$expr:expr,
$(|)? $($pat:pat)|+
$(if $guard:expr)?
$(,)?
) => (match $expr {
$(| $pat)+
$(if $guard)?
=> true,
| _
=> false,
})}
#[proc_macro] pub
fn item (input: TokenStream)
-> TokenStream
{
match try_map(input) {
| Ok(it) => it,
| Err((ref err_msg, span)) => compile_error(err_msg, span),
}
}
fn compile_error(err_msg: &'_ str, span: Span)
-> TokenStream
{
macro_rules! spanned {($expr:expr) => ({
let mut it = $expr;
it.set_span(span);
it
})}
Vec::<TokenTree>::into_iter(vec![
TT::Ident(Ident::new("compile_error", span)),
TT::Punct(spanned!(Punct::new('!', Spacing::Alone))),
TT::Group(spanned!(Group::new(
Delimiter::Brace,
iter::once(TT::Literal(
spanned!(Literal::string(err_msg))
)).collect(),
))),
])
.collect()
}
fn try_map (input: TokenStream)
-> Result<TokenStream, (&'static str, Span)>
{
input.into_iter().map(|mut tt| Ok({
if let TT::Group(ref group) = tt {
let span = tt.span();
tt = TT::Group(Group::new(
group.delimiter(),
try_map(group.stream())?
));
tt.set_span(span);
}
match tt {
| TT::Group(ref group)
if true
&& group.delimiter() == Delimiter::Bracket
&& matches!(
group.stream().into_iter().next(),
Some(TT::Punct(p))
if p.as_char() == '<'
,
)
=> {
let mut tokens: Box<dyn Iterator<Item = TokenTree>> =
Box::new(group.stream().into_iter())
;
let _ = tokens.next();
let ref mut ident = String::new();
loop { match tokens.next() {
| None
=> {
return Err((
"Missing `>` before the closing `]`",
tt.span(),
));
},
| Some(TT::Punct(p))
if p.as_char() == '>'
=> if let Some(unexpected_tt) = tokens.next() {
return Err((
"Unexpected trailing token after the terminating `>`",
unexpected_tt.span(),
));
} else {
break;
},
| Some(TT::Literal(lit))
=> {
let ref s = lit.to_string();
if s.chars().all(|c| matches!(c,
'a' ..= 'z' |
'A' ..= 'Z' |
'0' ..= '9' |
'_'
))
{
ident.push_str(s);
} else {
return Err((
"Cannot be converted into an identifier",
lit.span(),
));
}
},
| Some(TT::Ident(it))
=> {
ident.push_str(&it.to_string());
},
| Some(TT::Group(it))
if it.delimiter() == Delimiter::None
=> {
tokens = Box::new(it.stream().into_iter().chain(tokens));
continue;
},
| Some(it @ TT::Group(_))
=> {
return Err((
"Unexpected group",
it.span(),
));
},
| Some(it @ TT::Punct(_))
=> {
return Err((
"Unexpected punct",
it.span(),
));
},
}}
return Ok(TT::Ident(Ident::new(ident, tt.span())));
},
| _ => {},
}
tt
})).collect()
}
#[doc(hidden)]
#[proc_macro_derive(__expr_hack__)] pub
fn __expr_hack__ (input: TokenStream)
-> TokenStream
{
let mut tokens = input.into_iter();
let _ = tokens.by_ref().take(2).for_each(drop);
let mut tokens = if let Some(TT::Group(it)) = tokens.next() { it } else {
panic!()
}.stream().into_iter();
let _ = tokens.by_ref().take(2).for_each(drop);
let mut tokens = if let Some(TT::Group(it)) = tokens.next() { it } else {
panic!()
}.stream().into_iter();
let _ = tokens.by_ref().take(2).for_each(drop);
let input = if let Some(TT::Group(it)) = tokens.next() { it } else {
panic!()
}.stream();
let ret = match try_map(input) {
| Ok(it) => it,
| Err((ref err_msg, span)) => return compile_error(err_msg, span),
};
let span = Span::call_site();
Vec::<TokenTree>::into_iter(vec![
TT::Ident(Ident::new("macro_rules", span)),
TT::Punct(Punct::new('!', Spacing::Alone)),
TT::Ident(Ident::new("__mini_paste__Hack__", span)),
TT::Group(Group::new(
Delimiter::Brace,
Vec::<TokenTree>::into_iter(vec![
TT::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
TT::Punct(Punct::new('=', Spacing::Joint)),
TT::Punct(Punct::new('>', Spacing::Alone)),
TT::Group(Group::new(
Delimiter::Parenthesis,
ret,
)),
]).collect(),
)),
])
.collect()
}