use proc_macro::{Punct, Spacing, Span, TokenStream};
#[cfg(doc)]
use crate::{Verbatim, quote, verbatim};
use crate::{IntoTokens, TokenQueue};
#[cfg_attr(docsrs, doc(cfg(feature = "quote")))]
#[macro_export]
macro_rules! quote {
($($t:tt)*) => {
$crate::Transcriber::from_fn(
|_q| {
#[allow(unused_imports)]
use $crate::{IntoTokens as _, ඞ_macro_exports::{self as m, proc_macro, core, Spec, SpecLiteralQuote as _}};
$crate::ඞ_macro_quote_extend_impl! { _q $({$t})* };
},
)
};
}
#[must_use = "`Transcriber`s are lazily evaluated. See `TokenQueue::extend_from`."]
#[derive(Clone, Copy)]
pub struct Transcriber<F> {
f: F,
span: Option<Span>,
}
impl<F> Transcriber<F>
where
F: FnOnce(&mut TokenQueue),
{
pub fn from_fn(f: F) -> Transcriber<F> {
Transcriber { f, span: None }
}
pub fn into_fn(self) -> F {
self.f
}
pub fn with_span(mut self, span: Span) -> Transcriber<F> {
self.span = Some(span);
self
}
}
impl<F> IntoTokens for Transcriber<F>
where
F: FnOnce(&mut TokenQueue),
{
fn extend_tokens(self, q: &mut TokenQueue) {
if let Some(span) = self.span {
q.set_tracked_span(span);
}
(self.f)(q);
#[allow(clippy::redundant_pattern_matching, reason = "dude. lay off it.")]
if let Some(_) = self.span {
q.unset_tracked_span();
}
}
}
impl<F> From<Transcriber<F>> for TokenStream
where
F: FnOnce(&mut TokenQueue),
{
fn from(value: Transcriber<F>) -> TokenStream {
TokenStream::from(value.into_tokens())
}
}
#[derive(Debug, Clone, Copy)]
pub struct Dr;
impl IntoTokens for Dr {
fn extend_tokens(self, q: &mut TokenQueue) {
q.push(Punct::new('$', Spacing::Alone));
}
fn queue_size_hint(&self) -> (usize, Option<usize>) {
(1, Some(1))
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "quote")))]
#[macro_export]
macro_rules! verbatim {
($lt:lifetime) => {
$crate::Verbatim::new(
$crate::ඞ_macro_exports::core::stringify!($lt),
$crate::VerbatimKind::Lifetime,
)
};
($id:ident) => {
$crate::Verbatim::new(
$crate::ඞ_macro_exports::core::stringify!($lt),
$crate::VerbatimKind::Ident,
)
};
($lit:literal) => {
$crate::Verbatim::new(
$crate::ඞ_macro_exports::core::stringify!($lt),
$crate::VerbatimKind::Literal,
)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! ඞ_macro_quote_extend_impl {
($q:ident) => {};
($q:ident {$}) => {
core::compile_error!("invalid quasi-quoting syntax: `$` cannot trail the input.");
};
($q:ident $t:tt) => {
$crate::ඞ_macro_quote_tt_impl! { $q $t };
};
($q:ident {$} {$n:ident}) => {
$n.extend_tokens($q);
};
($q:ident $($t:tt)*) => {
$crate::ඞ_macro_quote_parse_matrix! {
ඞ_macro_quote_emit
$q
{ _ _ _ _ _ $($t)* }
{ _ _ _ _ $($t)* _ }
{ _ _ _ $($t)* _ _ }
{ _ _ $($t)* _ _ _ }
{ _ $($t)* _ _ _ _ }
{ $($t)* _ _ _ _ _ }
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! ඞ_macro_quote_emit {
(tt $q:ident $t:tt) => {
$crate::ඞ_macro_quote_tt_impl! { $q $t };
};
(embed $q:ident $n:ident) => {
$n.extend_tokens($q);
};
(rep $cx:tt $n:ident $($t:tt)*) => {
core::compile_error!("i'm working on it trust bro");
};
(seprep $cx:tt $n:ident p:tt $($t:tt)*) => {
core::compile_error!("i'm working on it trust bro");
};
(reserved $cx:tt $t:tt) => {
core::compile_error!(core::concat!(
"invalid quasi-quoting syntax: `",
core::stringify!($t),
"` following `@` is reserved.\n\
help: use `@@` to quote a single `@` symbol.\n\
help: see `vermouth::quote` for documentation.",
));
};
(triple_at $cx:tt) => {
core::compile_error!("the syntax `@@@` is not supported by `vermouth::quote`.");
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! ඞ_macro_quote_parse_window {
($m:ident $cx:tt {$} {$} {$} $_3:tt $_4:tt $_5:tt) => {
$crate::$m! { triple_at $cx }
};
($m:ident $cx:tt {$} {$} $a:tt $b:tt $c:tt $d:tt) => {
$crate::$m! { tt $cx {$} }
$crate::ඞ_macro_quote_parse_window! { $m $cx _ _ _ _ _ $a }
$crate::ඞ_macro_quote_parse_window! { $m $cx _ _ _ _ $a $b }
$crate::ඞ_macro_quote_parse_window! { $m $cx _ _ _ $a $b $c }
$crate::ඞ_macro_quote_parse_window! { $m $cx _ _ $a $b $c $d }
};
($m:ident $cx:tt $_0:tt {$} {$n:ident} {($($t:tt)*)} {*} $a:tt) => {
$crate::$m! { rep $cx $n $($t)* }
$crate::ඞ_macro_quote_parse_window! { $q _ _ _ _ _ $a }
};
($m:ident $cx:tt $_0:tt {$} {$n:ident} {($($t:tt)*)} {p:tt} {*}) => {
$crate::$m! { seprep $cx $n p $($t)* }
};
($m:ident $cx:tt $_0:tt {$} {$n:ident} $a:tt $b:tt $c:tt) => {
$crate::$m! { embed $cx $n };
$crate::ඞ_macro_quote_parse_window! { $m $cx _ _ _ _ _ $a }
$crate::ඞ_macro_quote_parse_window! { $m $cx _ _ _ _ $a $b }
$crate::ඞ_macro_quote_parse_window! { $m $cx _ _ _ $a $b $c }
};
($m:ident $cx:tt $_0:tt {$} {$} $a:tt $b:tt $c:tt) => {};
($m:ident $cx:tt $_0:tt {$} $t:tt $_3:tt $_4:tt $_5:tt) => {
$crate::$m! { reserved $cx $t }
};
($m:ident $cx:tt $a:tt $b:tt {$} $_3:tt $_4:tt $_5:tt) => {
$crate::ඞ_macro_quote_parse_window! { $m $cx $a $b _ _ _ _ }
};
($m:ident $cx:tt $a:tt $b:tt $c:tt {$} $_4:tt $_5:tt) => {
$crate::ඞ_macro_quote_parse_window! { $m $cx $a $b $c _ _ _ }
};
($m:ident $cx:tt $a:tt $b:tt $c:tt $d:tt {$} $_5:tt) => {
$crate::ඞ_macro_quote_parse_window! { $m $cx $a $b $c $d _ _ }
};
($m:ident $cx:tt $a:tt $b:tt $c:tt $d:tt $e:tt {$}) => {
$crate::ඞ_macro_quote_parse_window! { $m $cx $a $b $c $d $e _ }
};
($m:ident $cx:tt $_0:tt $_1:tt $_2:tt $_3:tt $_4:tt $t:tt) => {
$crate::$m! { tt $cx $t }
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! ඞ_macro_quote_parse_matrix {
(
$m:ident
$cx:tt
{ $($a:tt)* }
{ $($b:tt)* }
{ $($c:tt)* }
{ $($d:tt)* }
{ $($e:tt)* }
{ $($f:tt)* }
) => {
$(
$crate::ඞ_macro_quote_parse_window! { $m $cx $a $b $c $d $e $f }
)*
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! ඞ_macro_quote_punct_seq {
($_:tt $($char:literal)+) => {
&[
$($char,)+
]
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! ඞ_macro_quote_tt_impl {
($q:ident _) => {};
($q:ident {_}) => {
m::push_underscore($q);
};
($q:ident {()}) => {
m::push_empty_group($q, proc_macro::Delimiter::Parenthesis);
};
($q:ident {{}}) => {
m::push_empty_group($q, proc_macro::Delimiter::Brace);
};
($q:ident {[]}) => {
m::push_empty_group($q, proc_macro::Delimiter::Bracket);
};
($q:ident {($($t:tt)*)}) => {
$crate::TokenQueue::open_substream($q);
$crate::ඞ_macro_quote_extend_impl! { $q $({$t})* };
$crate::TokenQueue::close_substream_and_push_as_group($q, proc_macro::Delimiter::Parenthesis);
};
($q:ident {{$($t:tt)*}}) => {
$crate::TokenQueue::open_substream($q);
$crate::ඞ_macro_quote_extend_impl! { $q $({$t})* };
$crate::TokenQueue::close_substream_and_push_as_group($q, proc_macro::Delimiter::Brace);
};
($q:ident {[$($t:tt)*]}) => {
$crate::TokenQueue::open_substream($q);
$crate::ඞ_macro_quote_extend_impl! { $q $({$t})* };
$crate::TokenQueue::close_substream_and_push_as_group($q, proc_macro::Delimiter::Bracket);
};
($q:ident {$id:ident}) => {
$crate::TokenQueue::push($q, const {
m::parse_ident(stringify!($id))
});
};
($q:ident {$lit:literal}) => {
m::Spec::new(&$lit).ඞ_lit_quote::<{ m::parse_lit_regime(core::stringify!($lit)) }>(
&$lit,
core::stringify!($lit),
$q
);
};
($q:ident {$lt:lifetime}) => {
$crate::TokenQueue::push($q, const {
m::parse_lifetime(stringify!($lt))
});
};
($q:ident {$p:tt}) => {
m::push_punct(
$q,
$crate::punct_decompose!(
expand = $crate::ඞ_macro_quote_punct_seq,
fallback = {
core::compile_error!(
core::concat!(
"unrecognised token: ",
core::stringify!($p),
)
);
},
$p
)
);
};
}