• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::algorithm::Printer;
2 use crate::path::PathKind;
3 use crate::token::Token;
4 use crate::INDENT;
5 use proc_macro2::{Delimiter, Spacing, TokenStream};
6 use syn::{Ident, Macro, MacroDelimiter};
7 
8 impl Printer {
mac(&mut self, mac: &Macro, ident: Option<&Ident>, semicolon: bool)9     pub fn mac(&mut self, mac: &Macro, ident: Option<&Ident>, semicolon: bool) {
10         if mac.path.is_ident("macro_rules") {
11             if let Some(ident) = ident {
12                 self.macro_rules(ident, &mac.tokens);
13                 return;
14             }
15         }
16         self.path(&mac.path, PathKind::Simple);
17         self.word("!");
18         if let Some(ident) = ident {
19             self.nbsp();
20             self.ident(ident);
21         }
22         let (open, close, delimiter_break) = match mac.delimiter {
23             MacroDelimiter::Paren(_) => ("(", ")", Self::zerobreak as fn(&mut Self)),
24             MacroDelimiter::Brace(_) => (" {", "}", Self::hardbreak as fn(&mut Self)),
25             MacroDelimiter::Bracket(_) => ("[", "]", Self::zerobreak as fn(&mut Self)),
26         };
27         self.word(open);
28         if !mac.tokens.is_empty() {
29             self.cbox(INDENT);
30             delimiter_break(self);
31             self.ibox(0);
32             self.macro_rules_tokens(mac.tokens.clone(), false);
33             self.end();
34             delimiter_break(self);
35             self.offset(-INDENT);
36             self.end();
37         }
38         self.word(close);
39         if semicolon {
40             match mac.delimiter {
41                 MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => self.word(";"),
42                 MacroDelimiter::Brace(_) => {}
43             }
44         }
45     }
46 
macro_rules(&mut self, name: &Ident, rules: &TokenStream)47     fn macro_rules(&mut self, name: &Ident, rules: &TokenStream) {
48         enum State {
49             Start,
50             Matcher,
51             Equal,
52             Greater,
53             Expander,
54         }
55 
56         use State::*;
57 
58         self.word("macro_rules! ");
59         self.ident(name);
60         self.word(" {");
61         self.cbox(INDENT);
62         self.hardbreak_if_nonempty();
63         let mut state = State::Start;
64         for tt in rules.clone() {
65             let token = Token::from(tt);
66             match (state, token) {
67                 (Start, Token::Group(delimiter, stream)) => {
68                     self.delimiter_open(delimiter);
69                     if !stream.is_empty() {
70                         self.cbox(INDENT);
71                         self.zerobreak();
72                         self.ibox(0);
73                         self.macro_rules_tokens(stream, true);
74                         self.end();
75                         self.zerobreak();
76                         self.offset(-INDENT);
77                         self.end();
78                     }
79                     self.delimiter_close(delimiter);
80                     state = Matcher;
81                 }
82                 (Matcher, Token::Punct('=', Spacing::Joint)) => {
83                     self.word(" =");
84                     state = Equal;
85                 }
86                 (Equal, Token::Punct('>', Spacing::Alone)) => {
87                     self.word(">");
88                     state = Greater;
89                 }
90                 (Greater, Token::Group(_delimiter, stream)) => {
91                     self.word(" {");
92                     self.neverbreak();
93                     if !stream.is_empty() {
94                         self.cbox(INDENT);
95                         self.hardbreak();
96                         self.ibox(0);
97                         self.macro_rules_tokens(stream, false);
98                         self.end();
99                         self.hardbreak();
100                         self.offset(-INDENT);
101                         self.end();
102                     }
103                     self.word("}");
104                     state = Expander;
105                 }
106                 (Expander, Token::Punct(';', Spacing::Alone)) => {
107                     self.word(";");
108                     self.hardbreak();
109                     state = Start;
110                 }
111                 _ => unimplemented!("bad macro_rules syntax"),
112             }
113         }
114         match state {
115             Start => {}
116             Expander => {
117                 self.word(";");
118                 self.hardbreak();
119             }
120             _ => self.hardbreak(),
121         }
122         self.offset(-INDENT);
123         self.end();
124         self.word("}");
125     }
126 
macro_rules_tokens(&mut self, stream: TokenStream, matcher: bool)127     pub fn macro_rules_tokens(&mut self, stream: TokenStream, matcher: bool) {
128         #[derive(PartialEq)]
129         enum State {
130             Start,
131             Dollar,
132             DollarIdent,
133             DollarIdentColon,
134             DollarParen,
135             DollarParenSep,
136             Pound,
137             PoundBang,
138             Dot,
139             Colon,
140             Colon2,
141             Ident,
142             IdentBang,
143             Delim,
144             Other,
145         }
146 
147         use State::*;
148 
149         let mut state = Start;
150         let mut previous_is_joint = true;
151         for tt in stream {
152             let token = Token::from(tt);
153             let (needs_space, next_state) = match (&state, &token) {
154                 (Dollar, Token::Ident(_)) => (false, if matcher { DollarIdent } else { Other }),
155                 (DollarIdent, Token::Punct(':', Spacing::Alone)) => (false, DollarIdentColon),
156                 (DollarIdentColon, Token::Ident(_)) => (false, Other),
157                 (DollarParen, Token::Punct('+' | '*' | '?', Spacing::Alone)) => (false, Other),
158                 (DollarParen, Token::Ident(_) | Token::Literal(_)) => (false, DollarParenSep),
159                 (DollarParen, Token::Punct(_, Spacing::Joint)) => (false, DollarParen),
160                 (DollarParen, Token::Punct(_, Spacing::Alone)) => (false, DollarParenSep),
161                 (DollarParenSep, Token::Punct('+' | '*', _)) => (false, Other),
162                 (Pound, Token::Punct('!', _)) => (false, PoundBang),
163                 (Dollar, Token::Group(Delimiter::Parenthesis, _)) => (false, DollarParen),
164                 (Pound | PoundBang, Token::Group(Delimiter::Bracket, _)) => (false, Other),
165                 (Ident, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => {
166                     (false, Delim)
167                 }
168                 (Ident, Token::Punct('!', Spacing::Alone)) => (false, IdentBang),
169                 (IdentBang, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => {
170                     (false, Other)
171                 }
172                 (Colon, Token::Punct(':', _)) => (false, Colon2),
173                 (_, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => (true, Delim),
174                 (_, Token::Group(Delimiter::Brace | Delimiter::None, _)) => (true, Other),
175                 (_, Token::Ident(ident)) if !is_keyword(ident) => {
176                     (state != Dot && state != Colon2, Ident)
177                 }
178                 (_, Token::Literal(_)) => (state != Dot, Ident),
179                 (_, Token::Punct(',' | ';', _)) => (false, Other),
180                 (_, Token::Punct('.', _)) if !matcher => (state != Ident && state != Delim, Dot),
181                 (_, Token::Punct(':', Spacing::Joint)) => (state != Ident, Colon),
182                 (_, Token::Punct('$', _)) => (true, Dollar),
183                 (_, Token::Punct('#', _)) => (true, Pound),
184                 (_, _) => (true, Other),
185             };
186             if !previous_is_joint {
187                 if needs_space {
188                     self.space();
189                 } else if let Token::Punct('.', _) = token {
190                     self.zerobreak();
191                 }
192             }
193             previous_is_joint = match token {
194                 Token::Punct(_, Spacing::Joint) | Token::Punct('$', _) => true,
195                 _ => false,
196             };
197             self.single_token(
198                 token,
199                 if matcher {
200                     |printer, stream| printer.macro_rules_tokens(stream, true)
201                 } else {
202                     |printer, stream| printer.macro_rules_tokens(stream, false)
203                 },
204             );
205             state = next_state;
206         }
207     }
208 }
209 
is_keyword(ident: &Ident) -> bool210 fn is_keyword(ident: &Ident) -> bool {
211     match ident.to_string().as_str() {
212         "as" | "async" | "await" | "box" | "break" | "const" | "continue" | "crate" | "dyn"
213         | "else" | "enum" | "extern" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop"
214         | "macro" | "match" | "mod" | "move" | "mut" | "pub" | "ref" | "return" | "static"
215         | "struct" | "trait" | "type" | "unsafe" | "use" | "where" | "while" | "yield" => true,
216         _ => false,
217     }
218 }
219