1fad3a1d3Sopenharmony_ciuse super::*; 2fad3a1d3Sopenharmony_ciuse crate::token::{Brace, Bracket, Paren}; 3fad3a1d3Sopenharmony_ciuse proc_macro2::extra::DelimSpan; 4fad3a1d3Sopenharmony_ci#[cfg(any(feature = "parsing", feature = "printing"))] 5fad3a1d3Sopenharmony_ciuse proc_macro2::Delimiter; 6fad3a1d3Sopenharmony_ciuse proc_macro2::TokenStream; 7fad3a1d3Sopenharmony_ci#[cfg(feature = "parsing")] 8fad3a1d3Sopenharmony_ciuse proc_macro2::TokenTree; 9fad3a1d3Sopenharmony_ci 10fad3a1d3Sopenharmony_ci#[cfg(feature = "parsing")] 11fad3a1d3Sopenharmony_ciuse crate::parse::{Parse, ParseStream, Parser, Result}; 12fad3a1d3Sopenharmony_ci 13fad3a1d3Sopenharmony_ciast_struct! { 14fad3a1d3Sopenharmony_ci /// A macro invocation: `println!("{}", mac)`. 15fad3a1d3Sopenharmony_ci #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] 16fad3a1d3Sopenharmony_ci pub struct Macro { 17fad3a1d3Sopenharmony_ci pub path: Path, 18fad3a1d3Sopenharmony_ci pub bang_token: Token![!], 19fad3a1d3Sopenharmony_ci pub delimiter: MacroDelimiter, 20fad3a1d3Sopenharmony_ci pub tokens: TokenStream, 21fad3a1d3Sopenharmony_ci } 22fad3a1d3Sopenharmony_ci} 23fad3a1d3Sopenharmony_ci 24fad3a1d3Sopenharmony_ciast_enum! { 25fad3a1d3Sopenharmony_ci /// A grouping token that surrounds a macro body: `m!(...)` or `m!{...}` or `m![...]`. 26fad3a1d3Sopenharmony_ci #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] 27fad3a1d3Sopenharmony_ci pub enum MacroDelimiter { 28fad3a1d3Sopenharmony_ci Paren(Paren), 29fad3a1d3Sopenharmony_ci Brace(Brace), 30fad3a1d3Sopenharmony_ci Bracket(Bracket), 31fad3a1d3Sopenharmony_ci } 32fad3a1d3Sopenharmony_ci} 33fad3a1d3Sopenharmony_ci 34fad3a1d3Sopenharmony_ciimpl MacroDelimiter { 35fad3a1d3Sopenharmony_ci pub fn span(&self) -> &DelimSpan { 36fad3a1d3Sopenharmony_ci match self { 37fad3a1d3Sopenharmony_ci MacroDelimiter::Paren(token) => &token.span, 38fad3a1d3Sopenharmony_ci MacroDelimiter::Brace(token) => &token.span, 39fad3a1d3Sopenharmony_ci MacroDelimiter::Bracket(token) => &token.span, 40fad3a1d3Sopenharmony_ci } 41fad3a1d3Sopenharmony_ci } 42fad3a1d3Sopenharmony_ci} 43fad3a1d3Sopenharmony_ci 44fad3a1d3Sopenharmony_ciimpl Macro { 45fad3a1d3Sopenharmony_ci /// Parse the tokens within the macro invocation's delimiters into a syntax 46fad3a1d3Sopenharmony_ci /// tree. 47fad3a1d3Sopenharmony_ci /// 48fad3a1d3Sopenharmony_ci /// This is equivalent to `syn::parse2::<T>(mac.tokens)` except that it 49fad3a1d3Sopenharmony_ci /// produces a more useful span when `tokens` is empty. 50fad3a1d3Sopenharmony_ci /// 51fad3a1d3Sopenharmony_ci /// # Example 52fad3a1d3Sopenharmony_ci /// 53fad3a1d3Sopenharmony_ci /// ``` 54fad3a1d3Sopenharmony_ci /// use syn::{parse_quote, Expr, ExprLit, Ident, Lit, LitStr, Macro, Token}; 55fad3a1d3Sopenharmony_ci /// use syn::ext::IdentExt; 56fad3a1d3Sopenharmony_ci /// use syn::parse::{Error, Parse, ParseStream, Result}; 57fad3a1d3Sopenharmony_ci /// use syn::punctuated::Punctuated; 58fad3a1d3Sopenharmony_ci /// 59fad3a1d3Sopenharmony_ci /// // The arguments expected by libcore's format_args macro, and as a 60fad3a1d3Sopenharmony_ci /// // result most other formatting and printing macros like println. 61fad3a1d3Sopenharmony_ci /// // 62fad3a1d3Sopenharmony_ci /// // println!("{} is {number:.prec$}", "x", prec=5, number=0.01) 63fad3a1d3Sopenharmony_ci /// struct FormatArgs { 64fad3a1d3Sopenharmony_ci /// format_string: Expr, 65fad3a1d3Sopenharmony_ci /// positional_args: Vec<Expr>, 66fad3a1d3Sopenharmony_ci /// named_args: Vec<(Ident, Expr)>, 67fad3a1d3Sopenharmony_ci /// } 68fad3a1d3Sopenharmony_ci /// 69fad3a1d3Sopenharmony_ci /// impl Parse for FormatArgs { 70fad3a1d3Sopenharmony_ci /// fn parse(input: ParseStream) -> Result<Self> { 71fad3a1d3Sopenharmony_ci /// let format_string: Expr; 72fad3a1d3Sopenharmony_ci /// let mut positional_args = Vec::new(); 73fad3a1d3Sopenharmony_ci /// let mut named_args = Vec::new(); 74fad3a1d3Sopenharmony_ci /// 75fad3a1d3Sopenharmony_ci /// format_string = input.parse()?; 76fad3a1d3Sopenharmony_ci /// while !input.is_empty() { 77fad3a1d3Sopenharmony_ci /// input.parse::<Token![,]>()?; 78fad3a1d3Sopenharmony_ci /// if input.is_empty() { 79fad3a1d3Sopenharmony_ci /// break; 80fad3a1d3Sopenharmony_ci /// } 81fad3a1d3Sopenharmony_ci /// if input.peek(Ident::peek_any) && input.peek2(Token![=]) { 82fad3a1d3Sopenharmony_ci /// while !input.is_empty() { 83fad3a1d3Sopenharmony_ci /// let name: Ident = input.call(Ident::parse_any)?; 84fad3a1d3Sopenharmony_ci /// input.parse::<Token![=]>()?; 85fad3a1d3Sopenharmony_ci /// let value: Expr = input.parse()?; 86fad3a1d3Sopenharmony_ci /// named_args.push((name, value)); 87fad3a1d3Sopenharmony_ci /// if input.is_empty() { 88fad3a1d3Sopenharmony_ci /// break; 89fad3a1d3Sopenharmony_ci /// } 90fad3a1d3Sopenharmony_ci /// input.parse::<Token![,]>()?; 91fad3a1d3Sopenharmony_ci /// } 92fad3a1d3Sopenharmony_ci /// break; 93fad3a1d3Sopenharmony_ci /// } 94fad3a1d3Sopenharmony_ci /// positional_args.push(input.parse()?); 95fad3a1d3Sopenharmony_ci /// } 96fad3a1d3Sopenharmony_ci /// 97fad3a1d3Sopenharmony_ci /// Ok(FormatArgs { 98fad3a1d3Sopenharmony_ci /// format_string, 99fad3a1d3Sopenharmony_ci /// positional_args, 100fad3a1d3Sopenharmony_ci /// named_args, 101fad3a1d3Sopenharmony_ci /// }) 102fad3a1d3Sopenharmony_ci /// } 103fad3a1d3Sopenharmony_ci /// } 104fad3a1d3Sopenharmony_ci /// 105fad3a1d3Sopenharmony_ci /// // Extract the first argument, the format string literal, from an 106fad3a1d3Sopenharmony_ci /// // invocation of a formatting or printing macro. 107fad3a1d3Sopenharmony_ci /// fn get_format_string(m: &Macro) -> Result<LitStr> { 108fad3a1d3Sopenharmony_ci /// let args: FormatArgs = m.parse_body()?; 109fad3a1d3Sopenharmony_ci /// match args.format_string { 110fad3a1d3Sopenharmony_ci /// Expr::Lit(ExprLit { lit: Lit::Str(lit), .. }) => Ok(lit), 111fad3a1d3Sopenharmony_ci /// other => { 112fad3a1d3Sopenharmony_ci /// // First argument was not a string literal expression. 113fad3a1d3Sopenharmony_ci /// // Maybe something like: println!(concat!(...), ...) 114fad3a1d3Sopenharmony_ci /// Err(Error::new_spanned(other, "format string must be a string literal")) 115fad3a1d3Sopenharmony_ci /// } 116fad3a1d3Sopenharmony_ci /// } 117fad3a1d3Sopenharmony_ci /// } 118fad3a1d3Sopenharmony_ci /// 119fad3a1d3Sopenharmony_ci /// fn main() { 120fad3a1d3Sopenharmony_ci /// let invocation = parse_quote! { 121fad3a1d3Sopenharmony_ci /// println!("{:?}", Instant::now()) 122fad3a1d3Sopenharmony_ci /// }; 123fad3a1d3Sopenharmony_ci /// let lit = get_format_string(&invocation).unwrap(); 124fad3a1d3Sopenharmony_ci /// assert_eq!(lit.value(), "{:?}"); 125fad3a1d3Sopenharmony_ci /// } 126fad3a1d3Sopenharmony_ci /// ``` 127fad3a1d3Sopenharmony_ci #[cfg(feature = "parsing")] 128fad3a1d3Sopenharmony_ci #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] 129fad3a1d3Sopenharmony_ci pub fn parse_body<T: Parse>(&self) -> Result<T> { 130fad3a1d3Sopenharmony_ci self.parse_body_with(T::parse) 131fad3a1d3Sopenharmony_ci } 132fad3a1d3Sopenharmony_ci 133fad3a1d3Sopenharmony_ci /// Parse the tokens within the macro invocation's delimiters using the 134fad3a1d3Sopenharmony_ci /// given parser. 135fad3a1d3Sopenharmony_ci #[cfg(feature = "parsing")] 136fad3a1d3Sopenharmony_ci #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] 137fad3a1d3Sopenharmony_ci pub fn parse_body_with<F: Parser>(&self, parser: F) -> Result<F::Output> { 138fad3a1d3Sopenharmony_ci let scope = self.delimiter.span().close(); 139fad3a1d3Sopenharmony_ci crate::parse::parse_scoped(parser, scope, self.tokens.clone()) 140fad3a1d3Sopenharmony_ci } 141fad3a1d3Sopenharmony_ci} 142fad3a1d3Sopenharmony_ci 143fad3a1d3Sopenharmony_ci#[cfg(feature = "parsing")] 144fad3a1d3Sopenharmony_cipub(crate) fn parse_delimiter(input: ParseStream) -> Result<(MacroDelimiter, TokenStream)> { 145fad3a1d3Sopenharmony_ci input.step(|cursor| { 146fad3a1d3Sopenharmony_ci if let Some((TokenTree::Group(g), rest)) = cursor.token_tree() { 147fad3a1d3Sopenharmony_ci let span = g.delim_span(); 148fad3a1d3Sopenharmony_ci let delimiter = match g.delimiter() { 149fad3a1d3Sopenharmony_ci Delimiter::Parenthesis => MacroDelimiter::Paren(Paren(span)), 150fad3a1d3Sopenharmony_ci Delimiter::Brace => MacroDelimiter::Brace(Brace(span)), 151fad3a1d3Sopenharmony_ci Delimiter::Bracket => MacroDelimiter::Bracket(Bracket(span)), 152fad3a1d3Sopenharmony_ci Delimiter::None => { 153fad3a1d3Sopenharmony_ci return Err(cursor.error("expected delimiter")); 154fad3a1d3Sopenharmony_ci } 155fad3a1d3Sopenharmony_ci }; 156fad3a1d3Sopenharmony_ci Ok(((delimiter, g.stream()), rest)) 157fad3a1d3Sopenharmony_ci } else { 158fad3a1d3Sopenharmony_ci Err(cursor.error("expected delimiter")) 159fad3a1d3Sopenharmony_ci } 160fad3a1d3Sopenharmony_ci }) 161fad3a1d3Sopenharmony_ci} 162fad3a1d3Sopenharmony_ci 163fad3a1d3Sopenharmony_ci#[cfg(feature = "parsing")] 164fad3a1d3Sopenharmony_cipub(crate) mod parsing { 165fad3a1d3Sopenharmony_ci use super::*; 166fad3a1d3Sopenharmony_ci use crate::parse::{Parse, ParseStream, Result}; 167fad3a1d3Sopenharmony_ci 168fad3a1d3Sopenharmony_ci #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] 169fad3a1d3Sopenharmony_ci impl Parse for Macro { 170fad3a1d3Sopenharmony_ci fn parse(input: ParseStream) -> Result<Self> { 171fad3a1d3Sopenharmony_ci let tokens; 172fad3a1d3Sopenharmony_ci Ok(Macro { 173fad3a1d3Sopenharmony_ci path: input.call(Path::parse_mod_style)?, 174fad3a1d3Sopenharmony_ci bang_token: input.parse()?, 175fad3a1d3Sopenharmony_ci delimiter: { 176fad3a1d3Sopenharmony_ci let (delimiter, content) = parse_delimiter(input)?; 177fad3a1d3Sopenharmony_ci tokens = content; 178fad3a1d3Sopenharmony_ci delimiter 179fad3a1d3Sopenharmony_ci }, 180fad3a1d3Sopenharmony_ci tokens, 181fad3a1d3Sopenharmony_ci }) 182fad3a1d3Sopenharmony_ci } 183fad3a1d3Sopenharmony_ci } 184fad3a1d3Sopenharmony_ci} 185fad3a1d3Sopenharmony_ci 186fad3a1d3Sopenharmony_ci#[cfg(feature = "printing")] 187fad3a1d3Sopenharmony_cimod printing { 188fad3a1d3Sopenharmony_ci use super::*; 189fad3a1d3Sopenharmony_ci use proc_macro2::TokenStream; 190fad3a1d3Sopenharmony_ci use quote::ToTokens; 191fad3a1d3Sopenharmony_ci 192fad3a1d3Sopenharmony_ci impl MacroDelimiter { 193fad3a1d3Sopenharmony_ci pub(crate) fn surround(&self, tokens: &mut TokenStream, inner: TokenStream) { 194fad3a1d3Sopenharmony_ci let (delim, span) = match self { 195fad3a1d3Sopenharmony_ci MacroDelimiter::Paren(paren) => (Delimiter::Parenthesis, paren.span), 196fad3a1d3Sopenharmony_ci MacroDelimiter::Brace(brace) => (Delimiter::Brace, brace.span), 197fad3a1d3Sopenharmony_ci MacroDelimiter::Bracket(bracket) => (Delimiter::Bracket, bracket.span), 198fad3a1d3Sopenharmony_ci }; 199fad3a1d3Sopenharmony_ci token::printing::delim(delim, span.join(), tokens, inner); 200fad3a1d3Sopenharmony_ci } 201fad3a1d3Sopenharmony_ci } 202fad3a1d3Sopenharmony_ci 203fad3a1d3Sopenharmony_ci #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] 204fad3a1d3Sopenharmony_ci impl ToTokens for Macro { 205fad3a1d3Sopenharmony_ci fn to_tokens(&self, tokens: &mut TokenStream) { 206fad3a1d3Sopenharmony_ci self.path.to_tokens(tokens); 207fad3a1d3Sopenharmony_ci self.bang_token.to_tokens(tokens); 208fad3a1d3Sopenharmony_ci self.delimiter.surround(tokens, self.tokens.clone()); 209fad3a1d3Sopenharmony_ci } 210fad3a1d3Sopenharmony_ci } 211fad3a1d3Sopenharmony_ci} 212