David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 1 | use super::*; |
| 2 | |
David Tolnay | 9c76bcb | 2017-12-26 23:14:59 -0500 | [diff] [blame^] | 3 | use proc_macro2::{TokenNode, TokenTree, Delimiter}; |
| 4 | |
| 5 | #[cfg(feature = "extra-traits")] |
| 6 | use std::hash::{Hash, Hasher}; |
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. |
David Tolnay | 9c76bcb | 2017-12-26 23:14:59 -0500 | [diff] [blame^] | 12 | pub struct Macro #manual_extra_traits { |
Alex Crichton | 62a0a59 | 2017-05-22 13:58:53 -0700 | [diff] [blame] | 13 | pub path: Path, |
David Tolnay | f8db7ba | 2017-11-11 22:52:16 -0800 | [diff] [blame] | 14 | pub bang_token: Token![!], |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 15 | pub tokens: Vec<TokenTree>, |
Alex Crichton | 62a0a59 | 2017-05-22 13:58:53 -0700 | [diff] [blame] | 16 | } |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 17 | } |
| 18 | |
David Tolnay | 9c76bcb | 2017-12-26 23:14:59 -0500 | [diff] [blame^] | 19 | #[cfg(feature = "extra-traits")] |
| 20 | impl Eq for Macro {} |
| 21 | |
| 22 | #[cfg(feature = "extra-traits")] |
| 23 | impl PartialEq for Macro { |
| 24 | fn eq(&self, other: &Self) -> bool { |
| 25 | self.path == other.path |
| 26 | && self.bang_token == other.bang_token |
| 27 | && SliceTokenTreeHelper(&self.tokens) == SliceTokenTreeHelper(&other.tokens) |
| 28 | } |
| 29 | } |
| 30 | |
| 31 | #[cfg(feature = "extra-traits")] |
| 32 | impl Hash for Macro { |
| 33 | fn hash<H>(&self, state: &mut H) |
| 34 | where H: Hasher |
| 35 | { |
| 36 | self.path.hash(state); |
| 37 | self.bang_token.hash(state); |
| 38 | SliceTokenTreeHelper(&self.tokens).hash(state); |
| 39 | } |
| 40 | } |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 41 | |
David Tolnay | decf28d | 2017-11-11 11:56:45 -0800 | [diff] [blame] | 42 | impl Macro { |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 43 | pub fn is_braced(&self) -> bool { |
| 44 | match self.tokens.last() { |
David Tolnay | 9c76bcb | 2017-12-26 23:14:59 -0500 | [diff] [blame^] | 45 | Some(t) => is_braced(t), |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 46 | None => false, |
| 47 | } |
Alex Crichton | 62a0a59 | 2017-05-22 13:58:53 -0700 | [diff] [blame] | 48 | } |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 49 | } |
| 50 | |
David Tolnay | 9c76bcb | 2017-12-26 23:14:59 -0500 | [diff] [blame^] | 51 | pub fn is_braced(tt: &TokenTree) -> bool { |
| 52 | match tt.kind { |
| 53 | TokenNode::Group(Delimiter::Brace, _) => true, |
| 54 | _ => false, |
Alex Crichton | 62a0a59 | 2017-05-22 13:58:53 -0700 | [diff] [blame] | 55 | } |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 56 | } |
| 57 | |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 58 | #[cfg(feature = "extra-traits")] |
David Tolnay | 9c76bcb | 2017-12-26 23:14:59 -0500 | [diff] [blame^] | 59 | pub struct TokenTreeHelper<'a>(pub &'a TokenTree); |
| 60 | |
| 61 | #[cfg(feature = "extra-traits")] |
| 62 | impl<'a> PartialEq for TokenTreeHelper<'a> { |
| 63 | fn eq(&self, other: &Self) -> bool { |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame] | 64 | use proc_macro2::Spacing; |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 65 | |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 66 | match (&self.0.kind, &other.0.kind) { |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame] | 67 | (&TokenNode::Group(d1, ref s1), &TokenNode::Group(d2, ref s2)) => { |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 68 | match (d1, d2) { |
| 69 | (Delimiter::Parenthesis, Delimiter::Parenthesis) | |
| 70 | (Delimiter::Brace, Delimiter::Brace) | |
David Tolnay | bb4ca9f | 2017-12-26 12:28:58 -0500 | [diff] [blame] | 71 | (Delimiter::Bracket, Delimiter::Bracket) | |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 72 | (Delimiter::None, Delimiter::None) => {} |
| 73 | _ => return false, |
| 74 | } |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 75 | |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 76 | let s1 = s1.clone().into_iter(); |
| 77 | let mut s2 = s2.clone().into_iter(); |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 78 | |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 79 | for item1 in s1 { |
| 80 | let item2 = match s2.next() { |
| 81 | Some(item) => item, |
| 82 | None => return false, |
| 83 | }; |
David Tolnay | 9c76bcb | 2017-12-26 23:14:59 -0500 | [diff] [blame^] | 84 | if TokenTreeHelper(&item1) != TokenTreeHelper(&item2) { |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 85 | return false |
| 86 | } |
| 87 | } |
| 88 | s2.next().is_none() |
| 89 | } |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame] | 90 | (&TokenNode::Op(o1, k1), &TokenNode::Op(o2, k2)) => { |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 91 | o1 == o2 && match (k1, k2) { |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame] | 92 | (Spacing::Alone, Spacing::Alone) | |
| 93 | (Spacing::Joint, Spacing::Joint) => true, |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 94 | _ => false, |
| 95 | } |
| 96 | } |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame] | 97 | (&TokenNode::Literal(ref l1), &TokenNode::Literal(ref l2)) => { |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 98 | l1.to_string() == l2.to_string() |
| 99 | } |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame] | 100 | (&TokenNode::Term(ref s1), &TokenNode::Term(ref s2)) => { |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 101 | s1.as_str() == s2.as_str() |
| 102 | } |
| 103 | _ => false, |
| 104 | } |
Alex Crichton | 62a0a59 | 2017-05-22 13:58:53 -0700 | [diff] [blame] | 105 | } |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 106 | } |
| 107 | |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 108 | #[cfg(feature = "extra-traits")] |
David Tolnay | 9c76bcb | 2017-12-26 23:14:59 -0500 | [diff] [blame^] | 109 | impl<'a> Hash for TokenTreeHelper<'a> { |
| 110 | fn hash<H: Hasher>(&self, h: &mut H) { |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame] | 111 | use proc_macro2::Spacing; |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 112 | |
| 113 | match self.0.kind { |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame] | 114 | TokenNode::Group(delim, ref stream) => { |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 115 | 0u8.hash(h); |
| 116 | match delim { |
| 117 | Delimiter::Parenthesis => 0u8.hash(h), |
| 118 | Delimiter::Brace => 1u8.hash(h), |
| 119 | Delimiter::Bracket => 2u8.hash(h), |
| 120 | Delimiter::None => 3u8.hash(h), |
| 121 | } |
| 122 | |
David Tolnay | bb4ca9f | 2017-12-26 12:28:58 -0500 | [diff] [blame] | 123 | for item in stream.clone() { |
David Tolnay | 9c76bcb | 2017-12-26 23:14:59 -0500 | [diff] [blame^] | 124 | TokenTreeHelper(&item).hash(h); |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 125 | } |
| 126 | 0xffu8.hash(h); // terminator w/ a variant we don't normally hash |
| 127 | } |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame] | 128 | TokenNode::Op(op, kind) => { |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 129 | 1u8.hash(h); |
| 130 | op.hash(h); |
| 131 | match kind { |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame] | 132 | Spacing::Alone => 0u8.hash(h), |
| 133 | Spacing::Joint => 1u8.hash(h), |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 134 | } |
| 135 | } |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame] | 136 | TokenNode::Literal(ref lit) => (2u8, lit.to_string()).hash(h), |
| 137 | TokenNode::Term(ref word) => (3u8, word.as_str()).hash(h), |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 138 | } |
Alex Crichton | 62a0a59 | 2017-05-22 13:58:53 -0700 | [diff] [blame] | 139 | } |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 140 | } |
| 141 | |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 142 | #[cfg(feature = "extra-traits")] |
David Tolnay | 9c76bcb | 2017-12-26 23:14:59 -0500 | [diff] [blame^] | 143 | pub struct SliceTokenTreeHelper<'a>(pub &'a [TokenTree]); |
| 144 | |
| 145 | #[cfg(feature = "extra-traits")] |
| 146 | impl<'a> PartialEq for SliceTokenTreeHelper<'a> { |
| 147 | fn eq(&self, other: &Self) -> bool { |
| 148 | if self.0.len() != other.0.len() { |
| 149 | return false; |
| 150 | } |
| 151 | for (a, b) in self.0.iter().zip(other.0) { |
| 152 | if TokenTreeHelper(a) != TokenTreeHelper(b) { |
| 153 | return false; |
| 154 | } |
| 155 | } |
| 156 | true |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | #[cfg(feature = "extra-traits")] |
| 161 | impl<'a> Hash for SliceTokenTreeHelper<'a> { |
| 162 | fn hash<H: Hasher>(&self, state: &mut H) { |
| 163 | self.0.len().hash(state); |
| 164 | for tt in self.0 { |
| 165 | TokenTreeHelper(tt).hash(state); |
| 166 | } |
Alex Crichton | 62a0a59 | 2017-05-22 13:58:53 -0700 | [diff] [blame] | 167 | } |
David Tolnay | 84aa075 | 2016-10-02 23:01:13 -0700 | [diff] [blame] | 168 | } |
| 169 | |
| 170 | #[cfg(feature = "parsing")] |
| 171 | pub mod parsing { |
| 172 | use super::*; |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 173 | |
Alex Crichton | f9e8f1a | 2017-07-05 18:20:44 -0700 | [diff] [blame] | 174 | use proc_macro2::{TokenNode, TokenTree}; |
David Tolnay | c5ab8c6 | 2017-12-26 16:43:39 -0500 | [diff] [blame] | 175 | use synom::Synom; |
| 176 | use cursor::Cursor; |
| 177 | use {PResult, parse_error}; |
David Tolnay | 84aa075 | 2016-10-02 23:01:13 -0700 | [diff] [blame] | 178 | |
David Tolnay | decf28d | 2017-11-11 11:56:45 -0800 | [diff] [blame] | 179 | impl Synom for Macro { |
Michael Layzell | 92639a5 | 2017-06-01 00:07:44 -0400 | [diff] [blame] | 180 | named!(parse -> Self, do_parse!( |
| 181 | what: syn!(Path) >> |
David Tolnay | f8db7ba | 2017-11-11 22:52:16 -0800 | [diff] [blame] | 182 | bang: punct!(!) >> |
David Tolnay | 9c76bcb | 2017-12-26 23:14:59 -0500 | [diff] [blame^] | 183 | body: call!(parse_tt_delimited) >> |
David Tolnay | decf28d | 2017-11-11 11:56:45 -0800 | [diff] [blame] | 184 | (Macro { |
Michael Layzell | 92639a5 | 2017-06-01 00:07:44 -0400 | [diff] [blame] | 185 | path: what, |
| 186 | bang_token: bang, |
| 187 | tokens: vec![body], |
| 188 | }) |
| 189 | )); |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 190 | } |
| 191 | |
David Tolnay | 9c76bcb | 2017-12-26 23:14:59 -0500 | [diff] [blame^] | 192 | pub fn parse_tt_list(input: Cursor) -> PResult<Vec<TokenTree>> { |
| 193 | Ok((Cursor::empty(), input.token_stream().into_iter().collect())) |
| 194 | } |
Alex Crichton | 954046c | 2017-05-30 21:49:42 -0700 | [diff] [blame] | 195 | |
David Tolnay | 9c76bcb | 2017-12-26 23:14:59 -0500 | [diff] [blame^] | 196 | pub fn parse_tt_delimited(input: Cursor) -> PResult<TokenTree> { |
| 197 | match input.token_tree() { |
| 198 | Some((rest, token @ TokenTree { kind: TokenNode::Group(..), .. })) => { |
| 199 | Ok((rest, token)) |
Alex Crichton | 954046c | 2017-05-30 21:49:42 -0700 | [diff] [blame] | 200 | } |
David Tolnay | 9c76bcb | 2017-12-26 23:14:59 -0500 | [diff] [blame^] | 201 | _ => parse_error(), |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 202 | } |
| 203 | } |
David Tolnay | f4bbbd9 | 2016-09-23 14:41:55 -0700 | [diff] [blame] | 204 | } |
David Tolnay | f8cf963 | 2016-10-02 23:15:25 -0700 | [diff] [blame] | 205 | |
| 206 | #[cfg(feature = "printing")] |
| 207 | mod printing { |
| 208 | use super::*; |
| 209 | use quote::{Tokens, ToTokens}; |
| 210 | |
David Tolnay | decf28d | 2017-11-11 11:56:45 -0800 | [diff] [blame] | 211 | impl ToTokens for Macro { |
David Tolnay | f8cf963 | 2016-10-02 23:15:25 -0700 | [diff] [blame] | 212 | fn to_tokens(&self, tokens: &mut Tokens) { |
| 213 | self.path.to_tokens(tokens); |
Alex Crichton | ccbb45d | 2017-05-23 10:58:24 -0700 | [diff] [blame] | 214 | self.bang_token.to_tokens(tokens); |
| 215 | tokens.append_all(&self.tokens); |
David Tolnay | f8cf963 | 2016-10-02 23:15:25 -0700 | [diff] [blame] | 216 | } |
| 217 | } |
David Tolnay | f8cf963 | 2016-10-02 23:15:25 -0700 | [diff] [blame] | 218 | } |