blob: bde82dcafbc6b9094eaf517c1c92c0e2618d79e8 [file] [log] [blame]
David Tolnay55535012018-01-05 16:39:23 -08001// Copyright 2018 Syn Developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
David Tolnaycc543712018-01-08 11:29:54 -08009#[cfg(feature = "parsing")]
David Tolnaydfc886b2018-01-06 08:03:09 -080010use buffer::Cursor;
David Tolnaycc543712018-01-08 11:29:54 -080011#[cfg(feature = "parsing")]
David Tolnay203557a2017-12-27 23:59:33 -050012use synom::PResult;
David Tolnaycc543712018-01-08 11:29:54 -080013#[cfg(feature = "parsing")]
David Tolnay61037c62018-01-05 16:21:03 -080014use token::{Brace, Bracket, Paren};
David Tolnaycc543712018-01-08 11:29:54 -080015#[cfg(feature = "parsing")]
16use {parse_error, MacroDelimiter};
David Tolnaye0824032017-12-27 15:25:56 -050017
David Tolnayc43b44e2017-12-30 23:55:54 -050018#[cfg(feature = "extra-traits")]
19use std::hash::{Hash, Hasher};
20
David Tolnaycc543712018-01-08 11:29:54 -080021#[cfg(any(feature = "parsing", feature = "extra-traits"))]
Alex Crichton9a4dca22018-03-28 06:32:19 -070022use proc_macro2::{Delimiter, TokenStream, TokenTree};
David Tolnaycc543712018-01-08 11:29:54 -080023
24#[cfg(feature = "parsing")]
David Tolnayab919512017-12-30 23:31:51 -050025pub fn delimited(input: Cursor) -> PResult<(MacroDelimiter, TokenStream)> {
Alex Crichton9a4dca22018-03-28 06:32:19 -070026 if let Some((TokenTree::Group(g), rest)) = input.token_tree() {
27 let span = g.span();
28 let delimiter = match g.delimiter() {
29 Delimiter::Parenthesis => MacroDelimiter::Paren(Paren(span)),
30 Delimiter::Brace => MacroDelimiter::Brace(Brace(span)),
31 Delimiter::Bracket => MacroDelimiter::Bracket(Bracket(span)),
32 Delimiter::None => return parse_error(),
33 };
34
David Tolnay94d2b792018-04-29 12:26:10 -070035 return Ok(((delimiter, g.stream().clone()), rest));
David Tolnaye0824032017-12-27 15:25:56 -050036 }
Alex Crichton9a4dca22018-03-28 06:32:19 -070037 parse_error()
David Tolnaye0824032017-12-27 15:25:56 -050038}
39
David Tolnaycc543712018-01-08 11:29:54 -080040#[cfg(all(feature = "full", feature = "parsing"))]
David Tolnayab919512017-12-30 23:31:51 -050041pub fn braced(input: Cursor) -> PResult<(Brace, TokenStream)> {
Alex Crichton9a4dca22018-03-28 06:32:19 -070042 if let Some((TokenTree::Group(g), rest)) = input.token_tree() {
43 if g.delimiter() == Delimiter::Brace {
David Tolnay94d2b792018-04-29 12:26:10 -070044 return Ok(((Brace(g.span()), g.stream().clone()), rest));
Alex Crichton9a4dca22018-03-28 06:32:19 -070045 }
David Tolnaye0824032017-12-27 15:25:56 -050046 }
Alex Crichton9a4dca22018-03-28 06:32:19 -070047 parse_error()
David Tolnaye0824032017-12-27 15:25:56 -050048}
49
David Tolnaycc543712018-01-08 11:29:54 -080050#[cfg(all(feature = "full", feature = "parsing"))]
David Tolnayab919512017-12-30 23:31:51 -050051pub fn parenthesized(input: Cursor) -> PResult<(Paren, TokenStream)> {
Alex Crichton9a4dca22018-03-28 06:32:19 -070052 if let Some((TokenTree::Group(g), rest)) = input.token_tree() {
53 if g.delimiter() == Delimiter::Parenthesis {
David Tolnay94d2b792018-04-29 12:26:10 -070054 return Ok(((Paren(g.span()), g.stream().clone()), rest));
Alex Crichton9a4dca22018-03-28 06:32:19 -070055 }
David Tolnaye0824032017-12-27 15:25:56 -050056 }
Alex Crichton9a4dca22018-03-28 06:32:19 -070057 parse_error()
David Tolnaye0824032017-12-27 15:25:56 -050058}
David Tolnayc43b44e2017-12-30 23:55:54 -050059
60#[cfg(feature = "extra-traits")]
61pub struct TokenTreeHelper<'a>(pub &'a TokenTree);
62
63#[cfg(feature = "extra-traits")]
64impl<'a> PartialEq for TokenTreeHelper<'a> {
65 fn eq(&self, other: &Self) -> bool {
66 use proc_macro2::Spacing;
67
Alex Crichton9a4dca22018-03-28 06:32:19 -070068 match (self.0, other.0) {
69 (&TokenTree::Group(ref g1), &TokenTree::Group(ref g2)) => {
70 match (g1.delimiter(), g2.delimiter()) {
David Tolnayc43b44e2017-12-30 23:55:54 -050071 (Delimiter::Parenthesis, Delimiter::Parenthesis)
72 | (Delimiter::Brace, Delimiter::Brace)
73 | (Delimiter::Bracket, Delimiter::Bracket)
74 | (Delimiter::None, Delimiter::None) => {}
75 _ => return false,
76 }
77
Alex Crichton9a4dca22018-03-28 06:32:19 -070078 let s1 = g1.stream().clone().into_iter();
79 let mut s2 = g2.stream().clone().into_iter();
David Tolnayc43b44e2017-12-30 23:55:54 -050080
81 for item1 in s1 {
82 let item2 = match s2.next() {
83 Some(item) => item,
84 None => return false,
85 };
86 if TokenTreeHelper(&item1) != TokenTreeHelper(&item2) {
87 return false;
88 }
89 }
90 s2.next().is_none()
91 }
Alex Crichtona74a1c82018-05-16 10:20:44 -070092 (&TokenTree::Punct(ref o1), &TokenTree::Punct(ref o2)) => {
93 o1.as_char() == o2.as_char() && match (o1.spacing(), o2.spacing()) {
David Tolnayc43b44e2017-12-30 23:55:54 -050094 (Spacing::Alone, Spacing::Alone) | (Spacing::Joint, Spacing::Joint) => true,
95 _ => false,
96 }
97 }
Alex Crichton9a4dca22018-03-28 06:32:19 -070098 (&TokenTree::Literal(ref l1), &TokenTree::Literal(ref l2)) => {
David Tolnayc43b44e2017-12-30 23:55:54 -050099 l1.to_string() == l2.to_string()
100 }
Alex Crichtona74a1c82018-05-16 10:20:44 -0700101 (&TokenTree::Ident(ref s1), &TokenTree::Ident(ref s2)) => s1 == s2,
David Tolnayc43b44e2017-12-30 23:55:54 -0500102 _ => false,
103 }
104 }
105}
106
107#[cfg(feature = "extra-traits")]
108impl<'a> Hash for TokenTreeHelper<'a> {
109 fn hash<H: Hasher>(&self, h: &mut H) {
110 use proc_macro2::Spacing;
111
Alex Crichton9a4dca22018-03-28 06:32:19 -0700112 match *self.0 {
113 TokenTree::Group(ref g) => {
David Tolnayc43b44e2017-12-30 23:55:54 -0500114 0u8.hash(h);
Alex Crichton9a4dca22018-03-28 06:32:19 -0700115 match g.delimiter() {
David Tolnayc43b44e2017-12-30 23:55:54 -0500116 Delimiter::Parenthesis => 0u8.hash(h),
117 Delimiter::Brace => 1u8.hash(h),
118 Delimiter::Bracket => 2u8.hash(h),
119 Delimiter::None => 3u8.hash(h),
120 }
121
Alex Crichton9a4dca22018-03-28 06:32:19 -0700122 for item in g.stream().clone() {
David Tolnayc43b44e2017-12-30 23:55:54 -0500123 TokenTreeHelper(&item).hash(h);
124 }
125 0xffu8.hash(h); // terminator w/ a variant we don't normally hash
126 }
Alex Crichtona74a1c82018-05-16 10:20:44 -0700127 TokenTree::Punct(ref op) => {
David Tolnayc43b44e2017-12-30 23:55:54 -0500128 1u8.hash(h);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700129 op.as_char().hash(h);
Alex Crichton9a4dca22018-03-28 06:32:19 -0700130 match op.spacing() {
David Tolnayc43b44e2017-12-30 23:55:54 -0500131 Spacing::Alone => 0u8.hash(h),
132 Spacing::Joint => 1u8.hash(h),
133 }
134 }
Alex Crichton9a4dca22018-03-28 06:32:19 -0700135 TokenTree::Literal(ref lit) => (2u8, lit.to_string()).hash(h),
Alex Crichtona74a1c82018-05-16 10:20:44 -0700136 TokenTree::Ident(ref word) => (3u8, word).hash(h),
David Tolnayc43b44e2017-12-30 23:55:54 -0500137 }
138 }
139}
140
141#[cfg(feature = "extra-traits")]
142pub struct TokenStreamHelper<'a>(pub &'a TokenStream);
143
144#[cfg(feature = "extra-traits")]
145impl<'a> PartialEq for TokenStreamHelper<'a> {
146 fn eq(&self, other: &Self) -> bool {
147 let left = self.0.clone().into_iter().collect::<Vec<_>>();
148 let right = other.0.clone().into_iter().collect::<Vec<_>>();
149 if left.len() != right.len() {
150 return false;
151 }
152 for (a, b) in left.into_iter().zip(right) {
153 if TokenTreeHelper(&a) != TokenTreeHelper(&b) {
154 return false;
155 }
156 }
157 true
158 }
159}
160
161#[cfg(feature = "extra-traits")]
162impl<'a> Hash for TokenStreamHelper<'a> {
163 fn hash<H: Hasher>(&self, state: &mut H) {
164 let tts = self.0.clone().into_iter().collect::<Vec<_>>();
165 tts.len().hash(state);
166 for tt in tts {
167 TokenTreeHelper(&tt).hash(state);
168 }
169 }
170}