Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 1 | #[cfg(feature = "extra-traits")] |
| 2 | use std::fmt; |
| 3 | |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 4 | use super::*; |
| 5 | |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame^] | 6 | use proc_macro2::{TokenNode, Delimiter}; |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 7 | |
Alex Crichton | 62a0a59 | 2017-05-22 13:58:53 -0700 | [diff] [blame] | 8 | ast_struct! { |
| 9 | /// Represents a macro invocation. The Path indicates which macro |
| 10 | /// is being invoked, and the vector of token-trees contains the source |
| 11 | /// of the macro invocation. |
Alex Crichton | 62a0a59 | 2017-05-22 13:58:53 -0700 | [diff] [blame] | 12 | pub struct Mac { |
| 13 | pub path: Path, |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 14 | pub bang_token: tokens::Bang, |
David Tolnay | 570695e | 2017-06-03 16:15:13 -0700 | [diff] [blame] | 15 | /// The `example` in `macro_rules! example { ... }`. |
| 16 | pub ident: Option<Ident>, |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 17 | pub tokens: Vec<TokenTree>, |
Alex Crichton | 62a0a59 | 2017-05-22 13:58:53 -0700 | [diff] [blame] | 18 | } |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 19 | } |
| 20 | |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 21 | #[cfg_attr(feature = "clone-impls", derive(Clone))] |
| 22 | pub struct TokenTree(pub proc_macro2::TokenTree); |
| 23 | |
| 24 | impl Mac { |
| 25 | pub fn is_braced(&self) -> bool { |
| 26 | match self.tokens.last() { |
| 27 | Some(t) => t.is_braced(), |
| 28 | None => false, |
| 29 | } |
Alex Crichton | 62a0a59 | 2017-05-22 13:58:53 -0700 | [diff] [blame] | 30 | } |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 31 | } |
| 32 | |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 33 | impl TokenTree { |
| 34 | pub fn is_braced(&self) -> bool { |
| 35 | match self.0.kind { |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame^] | 36 | TokenNode::Group(Delimiter::Brace, _) => true, |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 37 | _ => false, |
| 38 | } |
Alex Crichton | 62a0a59 | 2017-05-22 13:58:53 -0700 | [diff] [blame] | 39 | } |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 40 | } |
| 41 | |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 42 | #[cfg(feature = "extra-traits")] |
| 43 | impl PartialEq for TokenTree { |
| 44 | fn eq(&self, other: &TokenTree) -> bool { |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame^] | 45 | use proc_macro2::Spacing; |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 46 | |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 47 | match (&self.0.kind, &other.0.kind) { |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame^] | 48 | (&TokenNode::Group(d1, ref s1), &TokenNode::Group(d2, ref s2)) => { |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 49 | match (d1, d2) { |
| 50 | (Delimiter::Parenthesis, Delimiter::Parenthesis) | |
| 51 | (Delimiter::Brace, Delimiter::Brace) | |
| 52 | (Delimiter::Bracket, Delimiter::Bracket) => {} |
| 53 | (Delimiter::None, Delimiter::None) => {} |
| 54 | _ => return false, |
| 55 | } |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 56 | |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 57 | let s1 = s1.clone().into_iter(); |
| 58 | let mut s2 = s2.clone().into_iter(); |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 59 | |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 60 | for item1 in s1 { |
| 61 | let item2 = match s2.next() { |
| 62 | Some(item) => item, |
| 63 | None => return false, |
| 64 | }; |
| 65 | if TokenTree(item1) != TokenTree(item2) { |
| 66 | return false |
| 67 | } |
| 68 | } |
| 69 | s2.next().is_none() |
| 70 | } |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame^] | 71 | (&TokenNode::Op(o1, k1), &TokenNode::Op(o2, k2)) => { |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 72 | o1 == o2 && match (k1, k2) { |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame^] | 73 | (Spacing::Alone, Spacing::Alone) | |
| 74 | (Spacing::Joint, Spacing::Joint) => true, |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 75 | _ => false, |
| 76 | } |
| 77 | } |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame^] | 78 | (&TokenNode::Literal(ref l1), &TokenNode::Literal(ref l2)) => { |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 79 | l1.to_string() == l2.to_string() |
| 80 | } |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame^] | 81 | (&TokenNode::Term(ref s1), &TokenNode::Term(ref s2)) => { |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 82 | s1.as_str() == s2.as_str() |
| 83 | } |
| 84 | _ => false, |
| 85 | } |
Alex Crichton | 62a0a59 | 2017-05-22 13:58:53 -0700 | [diff] [blame] | 86 | } |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 87 | } |
| 88 | |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 89 | #[cfg(feature = "extra-traits")] |
| 90 | impl Eq for TokenTree {} |
| 91 | |
| 92 | #[cfg(feature = "extra-traits")] |
| 93 | impl ::std::hash::Hash for TokenTree { |
| 94 | fn hash<H: ::std::hash::Hasher>(&self, h: &mut H) { |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame^] | 95 | use proc_macro2::Spacing; |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 96 | |
| 97 | match self.0.kind { |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame^] | 98 | TokenNode::Group(delim, ref stream) => { |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 99 | 0u8.hash(h); |
| 100 | match delim { |
| 101 | Delimiter::Parenthesis => 0u8.hash(h), |
| 102 | Delimiter::Brace => 1u8.hash(h), |
| 103 | Delimiter::Bracket => 2u8.hash(h), |
| 104 | Delimiter::None => 3u8.hash(h), |
| 105 | } |
| 106 | |
| 107 | for item in stream.clone().into_iter() { |
| 108 | TokenTree(item).hash(h); |
| 109 | } |
| 110 | 0xffu8.hash(h); // terminator w/ a variant we don't normally hash |
| 111 | } |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame^] | 112 | TokenNode::Op(op, kind) => { |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 113 | 1u8.hash(h); |
| 114 | op.hash(h); |
| 115 | match kind { |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame^] | 116 | Spacing::Alone => 0u8.hash(h), |
| 117 | Spacing::Joint => 1u8.hash(h), |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 118 | } |
| 119 | } |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame^] | 120 | TokenNode::Literal(ref lit) => (2u8, lit.to_string()).hash(h), |
| 121 | TokenNode::Term(ref word) => (3u8, word.as_str()).hash(h), |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 122 | } |
Alex Crichton | 62a0a59 | 2017-05-22 13:58:53 -0700 | [diff] [blame] | 123 | } |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 124 | } |
| 125 | |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 126 | #[cfg(feature = "extra-traits")] |
| 127 | impl fmt::Debug for TokenTree { |
| 128 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 129 | self.0.to_string().fmt(f) |
Alex Crichton | 62a0a59 | 2017-05-22 13:58:53 -0700 | [diff] [blame] | 130 | } |
David Tolnay | 84aa075 | 2016-10-02 23:01:13 -0700 | [diff] [blame] | 131 | } |
| 132 | |
| 133 | #[cfg(feature = "parsing")] |
| 134 | pub mod parsing { |
| 135 | use super::*; |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 136 | |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame^] | 137 | use proc_macro2::{TokenNode, TokenTree}; |
Alex Crichton | 954046c | 2017-05-30 21:49:42 -0700 | [diff] [blame] | 138 | use synom::tokens::*; |
Michael Layzell | 92639a5 | 2017-06-01 00:07:44 -0400 | [diff] [blame] | 139 | use synom::{Synom, PResult, Cursor, parse_error}; |
David Tolnay | 84aa075 | 2016-10-02 23:01:13 -0700 | [diff] [blame] | 140 | |
Alex Crichton | 954046c | 2017-05-30 21:49:42 -0700 | [diff] [blame] | 141 | impl Synom for Mac { |
Michael Layzell | 92639a5 | 2017-06-01 00:07:44 -0400 | [diff] [blame] | 142 | named!(parse -> Self, do_parse!( |
| 143 | what: syn!(Path) >> |
| 144 | bang: syn!(Bang) >> |
| 145 | body: call!(::TokenTree::parse_delimited) >> |
| 146 | (Mac { |
| 147 | path: what, |
| 148 | bang_token: bang, |
David Tolnay | 570695e | 2017-06-03 16:15:13 -0700 | [diff] [blame] | 149 | ident: None, |
Michael Layzell | 92639a5 | 2017-06-01 00:07:44 -0400 | [diff] [blame] | 150 | tokens: vec![body], |
| 151 | }) |
| 152 | )); |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 153 | } |
| 154 | |
Alex Crichton | 954046c | 2017-05-30 21:49:42 -0700 | [diff] [blame] | 155 | impl ::TokenTree { |
Michael Layzell | 92639a5 | 2017-06-01 00:07:44 -0400 | [diff] [blame] | 156 | pub fn parse_list(input: Cursor) -> PResult<Vec<Self>> { |
Michael Layzell | 589a8f4 | 2017-06-02 19:47:01 -0400 | [diff] [blame] | 157 | Ok((Cursor::empty(), input.token_stream().into_iter().map(::TokenTree).collect())) |
Alex Crichton | 954046c | 2017-05-30 21:49:42 -0700 | [diff] [blame] | 158 | } |
| 159 | |
Michael Layzell | 92639a5 | 2017-06-01 00:07:44 -0400 | [diff] [blame] | 160 | pub fn parse_delimited(input: Cursor) -> PResult<Self> { |
Michael Layzell | 589a8f4 | 2017-06-02 19:47:01 -0400 | [diff] [blame] | 161 | match input.token_tree() { |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame^] | 162 | Some((rest, token @ TokenTree { kind: TokenNode::Group(..), .. })) => { |
Michael Layzell | 589a8f4 | 2017-06-02 19:47:01 -0400 | [diff] [blame] | 163 | Ok((rest, ::TokenTree(token))) |
Alex Crichton | 954046c | 2017-05-30 21:49:42 -0700 | [diff] [blame] | 164 | } |
Michael Layzell | 92639a5 | 2017-06-01 00:07:44 -0400 | [diff] [blame] | 165 | _ => parse_error(), |
Alex Crichton | 954046c | 2017-05-30 21:49:42 -0700 | [diff] [blame] | 166 | } |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 167 | } |
| 168 | } |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 169 | } |
David Tolnay | f8cf963 | 2016-10-02 23:15:25 -0700 | [diff] [blame] | 170 | |
| 171 | #[cfg(feature = "printing")] |
| 172 | mod printing { |
| 173 | use super::*; |
| 174 | use quote::{Tokens, ToTokens}; |
| 175 | |
| 176 | impl ToTokens for Mac { |
| 177 | fn to_tokens(&self, tokens: &mut Tokens) { |
| 178 | self.path.to_tokens(tokens); |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 179 | self.bang_token.to_tokens(tokens); |
| 180 | tokens.append_all(&self.tokens); |
David Tolnay | f8cf963 | 2016-10-02 23:15:25 -0700 | [diff] [blame] | 181 | } |
| 182 | } |
| 183 | |
| 184 | impl ToTokens for TokenTree { |
| 185 | fn to_tokens(&self, tokens: &mut Tokens) { |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 186 | self.0.to_tokens(tokens); |
David Tolnay | f8cf963 | 2016-10-02 23:15:25 -0700 | [diff] [blame] | 187 | } |
| 188 | } |
| 189 | } |