blob: 4bc41883018d70abbc3b6cea1977e53c289cc542 [file] [log] [blame] [edit]
//! Quasi-quoting without a Syntex dependency, intended for use with [Macros
//! 1.1](https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md).
//!
//! ```toml
//! [dependencies]
//! quote = "0.3"
//! ```
//!
//! ```rust,ignore
//! #[macro_use]
//! extern crate quote;
//! ```
//!
//! Interpolation is done with `#var`:
//!
//! ```text
//! let tokens = quote! {
//! struct SerializeWith #generics #where_clause {
//! value: &'a #field_ty,
//! phantom: ::std::marker::PhantomData<#item_ty>,
//! }
//!
//! impl #generics serde::Serialize for SerializeWith #generics #where_clause {
//! fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
//! where S: serde::Serializer
//! {
//! #path(self.value, s)
//! }
//! }
//!
//! SerializeWith {
//! value: #value,
//! phantom: ::std::marker::PhantomData::<#item_ty>,
//! }
//! };
//! ```
//!
//! Repetition is done using `#(...)*` or `#(...),*` very similar to `macro_rules!`:
//!
//! - `#(#var)*` - no separators
//! - `#(#var),*` - the character before the asterisk is used as a separator
//! - `#( struct #var; )*` - the repetition can contain other things
//! - `#( #k => println!("{}", #v), )*` - even multiple interpolations
//!
//! The return type of `quote!` is `quote::Tokens`. Tokens can be interpolated into
//! other quotes:
//!
//! ```text
//! let t = quote! { /* ... */ };
//! return quote! { /* ... */ #t /* ... */ };
//! ```
//!
//! Call `to_string()` or `as_str()` on a Tokens to get a `String` or `&str` of Rust
//! code.
//!
//! The `quote!` macro relies on deep recursion so some large invocations may fail
//! with "recursion limit reached" when you compile. If it fails, bump up the
//! recursion limit by adding `#![recursion_limit = "128"]` to your crate. An even
//! higher limit may be necessary for especially large invocations.
mod tokens;
pub use tokens::Tokens;
mod to_tokens;
pub use to_tokens::{ToTokens, ByteStr, Hex};
mod ident;
pub use ident::Ident;
/// The whole point.
#[macro_export]
macro_rules! quote {
() => {
$crate::Tokens::new()
};
($($tt:tt)+) => {
{
let mut _s = $crate::Tokens::new();
quote_each_token!(_s $($tt)*);
_s
}
};
}
// Extract the names of all #metavariables and pass them to the $finish macro.
//
// in: pounded_var_names!(then () a #b c #( #d )* #e)
// out: then!(() b d e)
#[macro_export]
#[doc(hidden)]
macro_rules! pounded_var_names {
($finish:ident ($($found:ident)*) # ( $($inner:tt)* ) $($rest:tt)*) => {
pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
};
($finish:ident ($($found:ident)*) # [ $($inner:tt)* ] $($rest:tt)*) => {
pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
};
($finish:ident ($($found:ident)*) # { $($inner:tt)* } $($rest:tt)*) => {
pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
};
($finish:ident ($($found:ident)*) # $first:ident $($rest:tt)*) => {
pounded_var_names!($finish ($($found)* $first) $($rest)*)
};
($finish:ident ($($found:ident)*) ( $($inner:tt)* ) $($rest:tt)*) => {
pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
};
($finish:ident ($($found:ident)*) [ $($inner:tt)* ] $($rest:tt)*) => {
pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
};
($finish:ident ($($found:ident)*) { $($inner:tt)* } $($rest:tt)*) => {
pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
};
($finish:ident ($($found:ident)*) $ignore:tt $($rest:tt)*) => {
pounded_var_names!($finish ($($found)*) $($rest)*)
};
($finish:ident ($($found:ident)*)) => {
$finish!(() $($found)*)
};
}
// in: nested_tuples_pat!(() a b c d e)
// out: ((((a b) c) d) e)
//
// in: nested_tuples_pat!(() a)
// out: a
#[macro_export]
#[doc(hidden)]
macro_rules! nested_tuples_pat {
(()) => {
&()
};
(() $first:ident $($rest:ident)*) => {
nested_tuples_pat!(($first) $($rest)*)
};
(($pat:pat) $first:ident $($rest:ident)*) => {
nested_tuples_pat!((($pat, $first)) $($rest)*)
};
(($done:pat)) => {
$done
};
}
// in: multi_zip_expr!(() a b c d e)
// out: a.into_iter().zip(b).zip(c).zip(d).zip(e)
//
// in: multi_zip_iter!(() a)
// out: a
#[macro_export]
#[doc(hidden)]
macro_rules! multi_zip_expr {
(()) => {
&[]
};
(() $single:ident) => {
$single
};
(() $first:ident $($rest:ident)*) => {
multi_zip_expr!(($first.into_iter()) $($rest)*)
};
(($zips:expr) $first:ident $($rest:ident)*) => {
multi_zip_expr!(($zips.zip($first)) $($rest)*)
};
(($done:expr)) => {
$done
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! quote_each_token {
($tokens:ident) => {};
($tokens:ident # ! $($rest:tt)*) => {
$tokens.append("#");
$tokens.append("!");
quote_each_token!($tokens $($rest)*);
};
($tokens:ident # ( $($inner:tt)* ) * $($rest:tt)*) => {
for pounded_var_names!(nested_tuples_pat () $($inner)*)
in pounded_var_names!(multi_zip_expr () $($inner)*) {
quote_each_token!($tokens $($inner)*);
}
quote_each_token!($tokens $($rest)*);
};
($tokens:ident # ( $($inner:tt)* ) $sep:tt * $($rest:tt)*) => {
for (_i, pounded_var_names!(nested_tuples_pat () $($inner)*))
in pounded_var_names!(multi_zip_expr () $($inner)*).into_iter().enumerate() {
if _i > 0 {
$tokens.append(stringify!($sep));
}
quote_each_token!($tokens $($inner)*);
}
quote_each_token!($tokens $($rest)*);
};
($tokens:ident # [ $($inner:tt)* ] $($rest:tt)*) => {
$tokens.append("#");
$tokens.append("[");
quote_each_token!($tokens $($inner)*);
$tokens.append("]");
quote_each_token!($tokens $($rest)*);
};
($tokens:ident # $first:ident $($rest:tt)*) => {
$crate::ToTokens::to_tokens(&$first, &mut $tokens);
quote_each_token!($tokens $($rest)*);
};
($tokens:ident ( $($first:tt)* ) $($rest:tt)*) => {
$tokens.append("(");
quote_each_token!($tokens $($first)*);
$tokens.append(")");
quote_each_token!($tokens $($rest)*);
};
($tokens:ident [ $($first:tt)* ] $($rest:tt)*) => {
$tokens.append("[");
quote_each_token!($tokens $($first)*);
$tokens.append("]");
quote_each_token!($tokens $($rest)*);
};
($tokens:ident { $($first:tt)* } $($rest:tt)*) => {
$tokens.append("{");
quote_each_token!($tokens $($first)*);
$tokens.append("}");
quote_each_token!($tokens $($rest)*);
};
($tokens:ident $first:tt $($rest:tt)*) => {
$tokens.append(stringify!($first));
quote_each_token!($tokens $($rest)*);
};
}