use crate::algorithm::Printer; use crate::path::PathKind; use crate::token::Token; use crate::INDENT; use proc_macro2::{Delimiter, Spacing, TokenStream}; use syn::{Ident, Macro, MacroDelimiter}; impl Printer { pub fn mac(&mut self, mac: &Macro, ident: Option<&Ident>, semicolon: bool) { if mac.path.is_ident("macro_rules") { if let Some(ident) = ident { self.macro_rules(ident, &mac.tokens); return; } } #[cfg(feature = "verbatim")] if ident.is_none() && self.standard_library_macro(mac, semicolon) { return; } self.path(&mac.path, PathKind::Simple); self.word("!"); if let Some(ident) = ident { self.nbsp(); self.ident(ident); } let (open, close, delimiter_break) = match mac.delimiter { MacroDelimiter::Paren(_) => ("(", ")", Self::zerobreak as fn(&mut Self)), MacroDelimiter::Brace(_) => (" {", "}", Self::hardbreak as fn(&mut Self)), MacroDelimiter::Bracket(_) => ("[", "]", Self::zerobreak as fn(&mut Self)), }; self.word(open); if !mac.tokens.is_empty() { self.cbox(INDENT); delimiter_break(self); self.ibox(0); self.macro_rules_tokens(mac.tokens.clone(), false); self.end(); delimiter_break(self); self.offset(-INDENT); self.end(); } self.word(close); if semicolon { match mac.delimiter { MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => self.word(";"), MacroDelimiter::Brace(_) => {} } } } fn macro_rules(&mut self, name: &Ident, rules: &TokenStream) { enum State { Start, Matcher, Equal, Greater, Expander, } use State::*; self.word("macro_rules! "); self.ident(name); self.word(" {"); self.cbox(INDENT); self.hardbreak_if_nonempty(); let mut state = State::Start; for tt in rules.clone() { let token = Token::from(tt); match (state, token) { (Start, Token::Group(delimiter, stream)) => { self.delimiter_open(delimiter); if !stream.is_empty() { self.cbox(INDENT); self.zerobreak(); self.ibox(0); self.macro_rules_tokens(stream, true); self.end(); self.zerobreak(); self.offset(-INDENT); self.end(); } self.delimiter_close(delimiter); state = Matcher; } (Matcher, Token::Punct('=', Spacing::Joint)) => { self.word(" ="); state = Equal; } (Equal, Token::Punct('>', Spacing::Alone)) => { self.word(">"); state = Greater; } (Greater, Token::Group(_delimiter, stream)) => { self.word(" {"); self.neverbreak(); if !stream.is_empty() { self.cbox(INDENT); self.hardbreak(); self.ibox(0); self.macro_rules_tokens(stream, false); self.end(); self.hardbreak(); self.offset(-INDENT); self.end(); } self.word("}"); state = Expander; } (Expander, Token::Punct(';', Spacing::Alone)) => { self.word(";"); self.hardbreak(); state = Start; } _ => unimplemented!("bad macro_rules syntax"), } } match state { Start => {} Expander => { self.word(";"); self.hardbreak(); } _ => self.hardbreak(), } self.offset(-INDENT); self.end(); self.word("}"); } pub fn macro_rules_tokens(&mut self, stream: TokenStream, matcher: bool) { #[derive(PartialEq)] enum State { Start, Dollar, DollarIdent, DollarIdentColon, DollarParen, DollarParenSep, Pound, PoundBang, Dot, Colon, Colon2, Ident, IdentBang, Delim, Other, } use State::*; let mut state = Start; let mut previous_is_joint = true; for tt in stream { let token = Token::from(tt); let (needs_space, next_state) = match (&state, &token) { (Dollar, Token::Ident(_)) => (false, if matcher { DollarIdent } else { Other }), (DollarIdent, Token::Punct(':', Spacing::Alone)) => (false, DollarIdentColon), (DollarIdentColon, Token::Ident(_)) => (false, Other), (DollarParen, Token::Punct('+' | '*' | '?', Spacing::Alone)) => (false, Other), (DollarParen, Token::Ident(_) | Token::Literal(_)) => (false, DollarParenSep), (DollarParen, Token::Punct(_, Spacing::Joint)) => (false, DollarParen), (DollarParen, Token::Punct(_, Spacing::Alone)) => (false, DollarParenSep), (DollarParenSep, Token::Punct('+' | '*', _)) => (false, Other), (Pound, Token::Punct('!', _)) => (false, PoundBang), (Dollar, Token::Group(Delimiter::Parenthesis, _)) => (false, DollarParen), (Pound | PoundBang, Token::Group(Delimiter::Bracket, _)) => (false, Other), (Ident, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => { (false, Delim) } (Ident, Token::Punct('!', Spacing::Alone)) => (false, IdentBang), (IdentBang, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => { (false, Other) } (Colon, Token::Punct(':', _)) => (false, Colon2), (_, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => (true, Delim), (_, Token::Group(Delimiter::Brace | Delimiter::None, _)) => (true, Other), (_, Token::Ident(ident)) if !is_keyword(ident) => { (state != Dot && state != Colon2, Ident) } (_, Token::Literal(_)) => (state != Dot, Ident), (_, Token::Punct(',' | ';', _)) => (false, Other), (_, Token::Punct('.', _)) if !matcher => (state != Ident && state != Delim, Dot), (_, Token::Punct(':', Spacing::Joint)) => (state != Ident, Colon), (_, Token::Punct('$', _)) => (true, Dollar), (_, Token::Punct('#', _)) => (true, Pound), (_, _) => (true, Other), }; if !previous_is_joint { if needs_space { self.space(); } else if let Token::Punct('.', _) = token { self.zerobreak(); } } previous_is_joint = match token { Token::Punct(_, Spacing::Joint) | Token::Punct('$', _) => true, _ => false, }; self.single_token( token, if matcher { |printer, stream| printer.macro_rules_tokens(stream, true) } else { |printer, stream| printer.macro_rules_tokens(stream, false) }, ); state = next_state; } } } fn is_keyword(ident: &Ident) -> bool { match ident.to_string().as_str() { "as" | "async" | "await" | "box" | "break" | "const" | "continue" | "crate" | "dyn" | "else" | "enum" | "extern" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut" | "pub" | "ref" | "return" | "static" | "struct" | "trait" | "type" | "unsafe" | "use" | "where" | "while" | "yield" => true, _ => false, } } #[cfg(feature = "verbatim")] mod standard_library { use crate::algorithm::Printer; use crate::iter::IterDelimited; use crate::path::PathKind; use crate::INDENT; use syn::ext::IdentExt; use syn::parse::{Parse, ParseStream, Parser, Result}; use syn::{ parenthesized, token, Attribute, Expr, ExprAssign, ExprPath, Ident, Lit, Macro, Pat, Path, Token, Type, Visibility, }; enum KnownMacro { Expr(Expr), Exprs(Vec), Cfg(Cfg), Matches(Matches), ThreadLocal(Vec), VecArray(Vec), VecRepeat { elem: Expr, n: Expr }, } enum Cfg { Eq(Ident, Option), Call(Ident, Vec), } struct Matches { expression: Expr, pattern: Pat, guard: Option, } struct ThreadLocal { attrs: Vec, vis: Visibility, name: Ident, ty: Type, init: Expr, } struct FormatArgs { format_string: Expr, args: Vec, } impl Parse for FormatArgs { fn parse(input: ParseStream) -> Result { let format_string: Expr = input.parse()?; let mut args = Vec::new(); while !input.is_empty() { input.parse::()?; if input.is_empty() { break; } let arg = if input.peek(Ident::peek_any) && input.peek2(Token![=]) && !input.peek2(Token![==]) { let key = input.call(Ident::parse_any)?; let eq_token: Token![=] = input.parse()?; let value: Expr = input.parse()?; Expr::Assign(ExprAssign { attrs: Vec::new(), left: Box::new(Expr::Path(ExprPath { attrs: Vec::new(), qself: None, path: Path::from(key), })), eq_token, right: Box::new(value), }) } else { input.parse()? }; args.push(arg); } Ok(FormatArgs { format_string, args, }) } } impl KnownMacro { fn parse_expr(input: ParseStream) -> Result { let expr: Expr = input.parse()?; Ok(KnownMacro::Expr(expr)) } fn parse_expr_comma(input: ParseStream) -> Result { let expr: Expr = input.parse()?; input.parse::>()?; Ok(KnownMacro::Exprs(vec![expr])) } fn parse_exprs(input: ParseStream) -> Result { let exprs = input.parse_terminated(Expr::parse, Token![,])?; Ok(KnownMacro::Exprs(Vec::from_iter(exprs))) } fn parse_assert(input: ParseStream) -> Result { let mut exprs = Vec::new(); let cond: Expr = input.parse()?; exprs.push(cond); if input.parse::>()?.is_some() && !input.is_empty() { let format_args: FormatArgs = input.parse()?; exprs.push(format_args.format_string); exprs.extend(format_args.args); } Ok(KnownMacro::Exprs(exprs)) } fn parse_assert_cmp(input: ParseStream) -> Result { let mut exprs = Vec::new(); let left: Expr = input.parse()?; exprs.push(left); input.parse::()?; let right: Expr = input.parse()?; exprs.push(right); if input.parse::>()?.is_some() && !input.is_empty() { let format_args: FormatArgs = input.parse()?; exprs.push(format_args.format_string); exprs.extend(format_args.args); } Ok(KnownMacro::Exprs(exprs)) } fn parse_cfg(input: ParseStream) -> Result { fn parse_single(input: ParseStream) -> Result { let ident: Ident = input.parse()?; if input.peek(token::Paren) && (ident == "all" || ident == "any") { let content; parenthesized!(content in input); let list = content.call(parse_multiple)?; Ok(Cfg::Call(ident, list)) } else if input.peek(token::Paren) && ident == "not" { let content; parenthesized!(content in input); let cfg = content.call(parse_single)?; content.parse::>()?; Ok(Cfg::Call(ident, vec![cfg])) } else if input.peek(Token![=]) { input.parse::()?; let string: Lit = input.parse()?; Ok(Cfg::Eq(ident, Some(string))) } else { Ok(Cfg::Eq(ident, None)) } } fn parse_multiple(input: ParseStream) -> Result> { let mut vec = Vec::new(); while !input.is_empty() { let cfg = input.call(parse_single)?; vec.push(cfg); if input.is_empty() { break; } input.parse::()?; } Ok(vec) } let cfg = input.call(parse_single)?; input.parse::>()?; Ok(KnownMacro::Cfg(cfg)) } fn parse_env(input: ParseStream) -> Result { let mut exprs = Vec::new(); let name: Expr = input.parse()?; exprs.push(name); if input.parse::>()?.is_some() && !input.is_empty() { let error_msg: Expr = input.parse()?; exprs.push(error_msg); input.parse::>()?; } Ok(KnownMacro::Exprs(exprs)) } fn parse_format_args(input: ParseStream) -> Result { let format_args: FormatArgs = input.parse()?; let mut exprs = format_args.args; exprs.insert(0, format_args.format_string); Ok(KnownMacro::Exprs(exprs)) } fn parse_matches(input: ParseStream) -> Result { let expression: Expr = input.parse()?; input.parse::()?; let pattern = input.call(Pat::parse_multi_with_leading_vert)?; let guard = if input.parse::>()?.is_some() { Some(input.parse()?) } else { None }; input.parse::>()?; Ok(KnownMacro::Matches(Matches { expression, pattern, guard, })) } fn parse_thread_local(input: ParseStream) -> Result { let mut items = Vec::new(); while !input.is_empty() { let attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; input.parse::()?; let name: Ident = input.parse()?; input.parse::()?; let ty: Type = input.parse()?; input.parse::()?; let init: Expr = input.parse()?; if input.is_empty() { break; } input.parse::()?; items.push(ThreadLocal { attrs, vis, name, ty, init, }); } Ok(KnownMacro::ThreadLocal(items)) } fn parse_vec(input: ParseStream) -> Result { if input.is_empty() { return Ok(KnownMacro::VecArray(Vec::new())); } let first: Expr = input.parse()?; if input.parse::>()?.is_some() { let len: Expr = input.parse()?; Ok(KnownMacro::VecRepeat { elem: first, n: len, }) } else { let mut vec = vec![first]; while !input.is_empty() { input.parse::()?; if input.is_empty() { break; } let next: Expr = input.parse()?; vec.push(next); } Ok(KnownMacro::VecArray(vec)) } } fn parse_write(input: ParseStream) -> Result { let mut exprs = Vec::new(); let dst: Expr = input.parse()?; exprs.push(dst); input.parse::()?; let format_args: FormatArgs = input.parse()?; exprs.push(format_args.format_string); exprs.extend(format_args.args); Ok(KnownMacro::Exprs(exprs)) } fn parse_writeln(input: ParseStream) -> Result { let mut exprs = Vec::new(); let dst: Expr = input.parse()?; exprs.push(dst); if input.parse::>()?.is_some() && !input.is_empty() { let format_args: FormatArgs = input.parse()?; exprs.push(format_args.format_string); exprs.extend(format_args.args); } Ok(KnownMacro::Exprs(exprs)) } } impl Printer { pub fn standard_library_macro(&mut self, mac: &Macro, mut semicolon: bool) -> bool { let name = mac.path.segments.last().unwrap().ident.to_string(); let parser = match name.as_str() { "addr_of" | "addr_of_mut" => KnownMacro::parse_expr, "assert" | "debug_assert" => KnownMacro::parse_assert, "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne" => { KnownMacro::parse_assert_cmp } "cfg" => KnownMacro::parse_cfg, "compile_error" | "include" | "include_bytes" | "include_str" | "option_env" => { KnownMacro::parse_expr_comma } "concat" | "concat_bytes" | "dbg" => KnownMacro::parse_exprs, "const_format_args" | "eprint" | "eprintln" | "format" | "format_args" | "format_args_nl" | "panic" | "print" | "println" | "todo" | "unimplemented" | "unreachable" => KnownMacro::parse_format_args, "env" => KnownMacro::parse_env, "matches" => KnownMacro::parse_matches, "thread_local" => KnownMacro::parse_thread_local, "vec" => KnownMacro::parse_vec, "write" => KnownMacro::parse_write, "writeln" => KnownMacro::parse_writeln, _ => return false, }; let known_macro = match parser.parse2(mac.tokens.clone()) { Ok(known_macro) => known_macro, Err(_) => return false, }; self.path(&mac.path, PathKind::Simple); self.word("!"); match &known_macro { KnownMacro::Expr(expr) => { self.word("("); self.cbox(INDENT); self.zerobreak(); self.expr(expr); self.zerobreak(); self.offset(-INDENT); self.end(); self.word(")"); } KnownMacro::Exprs(exprs) => { self.word("("); self.cbox(INDENT); self.zerobreak(); for elem in exprs.iter().delimited() { self.expr(&elem); self.trailing_comma(elem.is_last); } self.offset(-INDENT); self.end(); self.word(")"); } KnownMacro::Cfg(cfg) => { self.word("("); self.cfg(cfg); self.word(")"); } KnownMacro::Matches(matches) => { self.word("("); self.cbox(INDENT); self.zerobreak(); self.expr(&matches.expression); self.word(","); self.space(); self.pat(&matches.pattern); if let Some(guard) = &matches.guard { self.space(); self.word("if "); self.expr(guard); } self.zerobreak(); self.offset(-INDENT); self.end(); self.word(")"); } KnownMacro::ThreadLocal(items) => { self.word(" {"); self.cbox(INDENT); self.hardbreak_if_nonempty(); for item in items { self.outer_attrs(&item.attrs); self.cbox(0); self.visibility(&item.vis); self.word("static "); self.ident(&item.name); self.word(": "); self.ty(&item.ty); self.word(" = "); self.neverbreak(); self.expr(&item.init); self.word(";"); self.end(); self.hardbreak(); } self.offset(-INDENT); self.end(); self.word("}"); semicolon = false; } KnownMacro::VecArray(vec) => { self.word("["); self.cbox(INDENT); self.zerobreak(); for elem in vec.iter().delimited() { self.expr(&elem); self.trailing_comma(elem.is_last); } self.offset(-INDENT); self.end(); self.word("]"); } KnownMacro::VecRepeat { elem, n } => { self.word("["); self.cbox(INDENT); self.zerobreak(); self.expr(elem); self.word(";"); self.space(); self.expr(n); self.zerobreak(); self.offset(-INDENT); self.end(); self.word("]"); } } if semicolon { self.word(";"); } true } fn cfg(&mut self, cfg: &Cfg) { match cfg { Cfg::Eq(ident, value) => { self.ident(ident); if let Some(value) = value { self.word(" = "); self.lit(value); } } Cfg::Call(ident, args) => { self.ident(ident); self.word("("); self.cbox(INDENT); self.zerobreak(); for arg in args.iter().delimited() { self.cfg(&arg); self.trailing_comma(arg.is_last); } self.offset(-INDENT); self.end(); self.word(")"); } } } } }