1extern crate proc_macro;
2
3mod helpers;
4
5use helpers::IdentifyFirstLast;
6use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
7use proc_macro_hack::proc_macro_hack;
8use std::iter::FromIterator;
9
10#[proc_macro_hack]
11pub fn expr(input: TokenStream) -> TokenStream {
12 let input = input.into_iter();
13
14 let trees = extdot(input);
15
16 let output = TokenStream::from_iter(trees);
17
18 TokenStream::from(TokenTree::Group(Group::new(Delimiter::Brace, output)))
19}
20
21#[proc_macro]
23pub fn item(input: TokenStream) -> TokenStream {
24 let input = input.into_iter();
25
26 let trees = extdot(input);
27
28 TokenStream::from_iter(trees)
29}
30
31
32fn extdot(trees: impl Iterator<Item = TokenTree>) -> impl Iterator<Item = TokenTree> {
33 let mut last_expression = vec![];
34 let mut was_dot = false;
35
36 trees.identify_first_last().flat_map(move |(_first, last, token)| {
37 let mut rv: Vec<Box<Iterator<Item = TokenTree>>> = vec![];
38
39 let token = match (was_dot, &token) {
40 (true, TokenTree::Group(ref grp)) => {
41 was_dot = false;
42
43 last_expression.pop();
45
46 transliterate(&mut last_expression, grp)
48 }
49 (false, TokenTree::Group(ref grp)) => {
50 let block = extdot(grp.stream().into_iter());
51 let block = TokenStream::from_iter(block);
52
53 TokenTree::Group(Group::new(grp.delimiter(), block))
54 }
55 (_, TokenTree::Punct(ref p)) if p.as_char() == '.' => {
56 was_dot = true;
57 token
58 }
59 _ => {
60 was_dot = false;
61 token
62 }
63 };
64
65 let last_token = last_expression.last().cloned();
66 last_expression.push(token.clone());
67
68 if last || !is_expressionable(&last_token, &token) {
69 rv.push(Box::new(last_expression.clone().into_iter()));
70 last_expression.clear();
71 }
72
73 rv.into_iter().flatten()
74 })
75}
76
77fn transliterate(expr: &mut Vec<TokenTree>, grp: &Group) -> TokenTree {
78 use std::str::FromStr;
79
80 if grp.stream().is_empty() {
81 #[cfg(nightly)]
82 grp.span().warning("empty extended dot, consider removing it");
83
84 return TokenTree::Group(Group::new(Delimiter::Brace, grp.stream()));
85 }
86
87 let mut output: Vec<TokenTree> = vec![];
88
89 output.extend(TokenStream::from_str("let mut it = ").unwrap());
90 output.append(expr);
91 output.push(TokenTree::Punct(Punct::new(';', Spacing::Alone)));
92
93 let mut gstream = extdot(grp.stream().into_iter()).into_iter().collect::<Vec<_>>();
96
97 let split_subexprs = |tok: &TokenTree| match tok {
98 TokenTree::Punct(ref p) if p.as_char() == ',' => true,
99 _ => false,
100 };
101
102 for mut subexpr in gstream.split_mut(split_subexprs) {
103 if subexpr.is_empty() {
104 output.extend(TokenStream::from_str("it").unwrap());
105 } else if is_ident(&subexpr) {
106 replace_it(subexpr);
107 output.extend_from_slice(subexpr);
108 output.extend(TokenStream::from_str("(it)").unwrap());
109 } else if is_fn_call(&subexpr) && has_no_it(&subexpr) {
110 output.extend(implicit_method_call(subexpr));
111 } else {
112 replace_it(&mut subexpr);
113 output.extend(subexpr.iter().cloned());
114 }
115
116 output.push(TokenTree::Punct(Punct::new(';', Spacing::Alone)));
117 }
118
119 output.pop();
120
121 TokenTree::Group(Group::new(
122 Delimiter::Brace,
123 TokenStream::from_iter(output.into_iter()),
124 ))
125}
126
127fn replace_it(block: &mut [TokenTree]) {
128 for token in block {
129 match token {
130 TokenTree::Ident(ref idnt) if idnt.to_string() == "it" => {
131 *token = TokenTree::Ident(Ident::new("it", Span::call_site()));
133 }
134 TokenTree::Group(ref grp) => {
135 let mut nested = grp.stream().into_iter().collect::<Vec<_>>();
136
137 replace_it(&mut nested);
138
139 *token = TokenTree::Group(Group::new(
140 grp.delimiter(),
141 TokenStream::from_iter(nested.into_iter()),
142 ))
143 }
144 _ => (),
145 }
146 }
147}
148
149fn is_expressionable(last_token: &Option<TokenTree>, token: &TokenTree) -> bool {
150 match token {
151 TokenTree::Group(ref grp) if grp.delimiter() == Delimiter::Parenthesis => true,
152 TokenTree::Group(ref grp) if grp.delimiter() == Delimiter::Brace => true,
153 TokenTree::Group(ref grp) if grp.delimiter() == Delimiter::Bracket => true,
154 TokenTree::Ident(_) => true,
155 TokenTree::Literal(_) => true,
156 TokenTree::Punct(ref punct) if punct.as_char() == '.' => true,
157 TokenTree::Punct(ref punct) if punct.as_char() == ':' && punct.spacing() == Spacing::Joint => true,
158 TokenTree::Punct(ref punct) if punct.as_char() == ':' && punct.spacing() == Spacing::Alone => {
159 match last_token {
160 Some(TokenTree::Punct(ref p)) if p.as_char() == ':' && p.spacing() == Spacing::Joint => true,
161 _ => false,
162 }
163 }
164 TokenTree::Punct(ref punct) if punct.as_char() == '<' => true,
165 TokenTree::Punct(ref punct) if punct.as_char() == '>' => true,
166 TokenTree::Punct(ref punct) if punct.as_char() == '?' => true,
167 _ => false,
168 }
169}
170
171fn is_ident(trees: &[TokenTree]) -> bool {
172 for token in trees {
173 match token {
174 TokenTree::Ident(_) => (),
175 TokenTree::Punct(ref punct) if punct.as_char() == ':' => (),
176 TokenTree::Literal(_) => return false,
177 TokenTree::Group(_) => return false,
178 TokenTree::Punct(_) => return false,
179 }
180 }
181
182 true
183}
184
185fn implicit_method_call(trees: &[TokenTree]) -> Vec<TokenTree> {
186 let mut output = vec![];
187
188 let mut trees = trees.iter();
189
190 if let Some(mut last_token) = trees.next() {
191 for token in trees {
192 match (last_token, token) {
193 (TokenTree::Ident(_), TokenTree::Group(ref grp))
194 if grp.delimiter() == Delimiter::Parenthesis => {
195 output.push(TokenTree::Ident(Ident::new("it", Span::call_site())));
196 output.push(TokenTree::Punct(Punct::new('.', Spacing::Alone)));
197 output.push(last_token.clone());
198 }
199 _ => output.push(last_token.clone()),
200 }
201
202 last_token = token;
203 }
204
205 output.push(last_token.clone());
206 }
207
208 output
209}
210
211fn has_no_it(trees: &[TokenTree]) -> bool {
212 for token in trees {
213 match token {
214 TokenTree::Ident(ref idnt) if idnt.to_string() == "it" => return false,
215 _ => ()
216 }
217 }
218
219 true
220}
221
222fn is_fn_call(trees: &[TokenTree]) -> bool {
223 let mut last_token = None;
224
225 for token in trees {
226 match token {
227 TokenTree::Group(ref grp) if grp.delimiter() == Delimiter::Parenthesis => {
228 if let Some(TokenTree::Ident(_)) = last_token {
229 return true
230 }
231 }
232 _ => ()
233 }
234
235 last_token = Some(token.clone());
236 }
237
238 false
239}