]>
Commit | Line | Data |
---|---|---|
223e47cc LB |
1 | // Copyright 2012 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
1a4d82fc | 10 | use self::LockstepIterSize::*; |
223e47cc | 11 | |
5bcae85e | 12 | use ast::Ident; |
3157f602 | 13 | use syntax_pos::{Span, DUMMY_SP}; |
a7813a04 | 14 | use errors::{Handler, DiagnosticBuilder}; |
1a4d82fc | 15 | use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal}; |
9cc50fc6 | 16 | use parse::token::{DocComment, MatchNt, SubstNt}; |
5bcae85e | 17 | use parse::token::{Token, Interpolated, NtIdent, NtTT, SpecialMacroVar}; |
1a4d82fc | 18 | use parse::token; |
223e47cc | 19 | use parse::lexer::TokenAndSpan; |
3157f602 | 20 | use tokenstream::{self, TokenTree}; |
223e47cc | 21 | |
1a4d82fc JJ |
22 | use std::rc::Rc; |
23 | use std::ops::Add; | |
24 | use std::collections::HashMap; | |
223e47cc | 25 | |
1a4d82fc JJ |
26 | ///an unzipping of `TokenTree`s |
27 | #[derive(Clone)] | |
223e47cc | 28 | struct TtFrame { |
1a4d82fc | 29 | forest: TokenTree, |
85aaf69f | 30 | idx: usize, |
223e47cc LB |
31 | dotdotdoted: bool, |
32 | sep: Option<Token>, | |
223e47cc LB |
33 | } |
34 | ||
1a4d82fc JJ |
35 | #[derive(Clone)] |
36 | pub struct TtReader<'a> { | |
9cc50fc6 | 37 | pub sp_diag: &'a Handler, |
1a4d82fc JJ |
38 | /// the unzipped tree: |
39 | stack: Vec<TtFrame>, | |
223e47cc | 40 | /* for MBE-style macro transcription */ |
5bcae85e | 41 | interpolations: HashMap<Ident, Rc<NamedMatch>>, |
1a4d82fc JJ |
42 | imported_from: Option<Ident>, |
43 | ||
44 | // Some => return imported_from as the next token | |
45 | crate_name_next: Option<Span>, | |
85aaf69f SL |
46 | repeat_idx: Vec<usize>, |
47 | repeat_len: Vec<usize>, | |
223e47cc | 48 | /* cached: */ |
1a4d82fc JJ |
49 | pub cur_tok: Token, |
50 | pub cur_span: Span, | |
51 | /// Transform doc comments. Only useful in macro invocations | |
52 | pub desugar_doc_comments: bool, | |
a7813a04 | 53 | pub fatal_errs: Vec<DiagnosticBuilder<'a>>, |
1a4d82fc JJ |
54 | } |
55 | ||
56 | /// This can do Macro-By-Example transcription. On the other hand, if | |
92a42be0 | 57 | /// `src` contains no `TokenTree::Sequence`s, `MatchNt`s or `SubstNt`s, `interp` can |
1a4d82fc | 58 | /// (and should) be None. |
7453a54e | 59 | pub fn new_tt_reader(sp_diag: &Handler, |
5bcae85e | 60 | interp: Option<HashMap<Ident, Rc<NamedMatch>>>, |
7453a54e | 61 | imported_from: Option<Ident>, |
3157f602 | 62 | src: Vec<tokenstream::TokenTree>) |
7453a54e | 63 | -> TtReader { |
1a4d82fc | 64 | new_tt_reader_with_doc_flag(sp_diag, interp, imported_from, src, false) |
223e47cc LB |
65 | } |
66 | ||
1a4d82fc JJ |
67 | /// The extra `desugar_doc_comments` flag enables reading doc comments |
68 | /// like any other attribute which consists of `meta` and surrounding #[ ] tokens. | |
69 | /// | |
70 | /// This can do Macro-By-Example transcription. On the other hand, if | |
92a42be0 | 71 | /// `src` contains no `TokenTree::Sequence`s, `MatchNt`s or `SubstNt`s, `interp` can |
1a4d82fc | 72 | /// (and should) be None. |
7453a54e | 73 | pub fn new_tt_reader_with_doc_flag(sp_diag: &Handler, |
5bcae85e | 74 | interp: Option<HashMap<Ident, Rc<NamedMatch>>>, |
7453a54e | 75 | imported_from: Option<Ident>, |
3157f602 | 76 | src: Vec<tokenstream::TokenTree>, |
7453a54e SL |
77 | desugar_doc_comments: bool) |
78 | -> TtReader { | |
1a4d82fc | 79 | let mut r = TtReader { |
223e47cc | 80 | sp_diag: sp_diag, |
1a4d82fc | 81 | stack: vec!(TtFrame { |
5bcae85e | 82 | forest: TokenTree::Sequence(DUMMY_SP, Rc::new(tokenstream::SequenceRepetition { |
1a4d82fc JJ |
83 | tts: src, |
84 | // doesn't matter. This merely holds the root unzipping. | |
3157f602 | 85 | separator: None, op: tokenstream::KleeneOp::ZeroOrMore, num_captures: 0 |
5bcae85e | 86 | })), |
1a4d82fc | 87 | idx: 0, |
223e47cc LB |
88 | dotdotdoted: false, |
89 | sep: None, | |
1a4d82fc JJ |
90 | }), |
91 | interpolations: match interp { /* just a convenience */ | |
970d7e83 | 92 | None => HashMap::new(), |
1a4d82fc | 93 | Some(x) => x, |
223e47cc | 94 | }, |
1a4d82fc JJ |
95 | imported_from: imported_from, |
96 | crate_name_next: None, | |
97 | repeat_idx: Vec::new(), | |
98 | repeat_len: Vec::new(), | |
99 | desugar_doc_comments: desugar_doc_comments, | |
223e47cc | 100 | /* dummy values, never read: */ |
1a4d82fc JJ |
101 | cur_tok: token::Eof, |
102 | cur_span: DUMMY_SP, | |
a7813a04 | 103 | fatal_errs: Vec::new(), |
223e47cc | 104 | }; |
1a4d82fc JJ |
105 | tt_next_token(&mut r); /* get cur_tok and cur_span set up */ |
106 | r | |
223e47cc LB |
107 | } |
108 | ||
1a4d82fc JJ |
109 | fn lookup_cur_matched_by_matched(r: &TtReader, start: Rc<NamedMatch>) -> Rc<NamedMatch> { |
110 | r.repeat_idx.iter().fold(start, |ad, idx| { | |
111 | match *ad { | |
112 | MatchedNonterminal(_) => { | |
113 | // end of the line; duplicate henceforth | |
114 | ad.clone() | |
115 | } | |
116 | MatchedSeq(ref ads, _) => ads[*idx].clone() | |
223e47cc | 117 | } |
1a4d82fc | 118 | }) |
223e47cc LB |
119 | } |
120 | ||
1a4d82fc | 121 | fn lookup_cur_matched(r: &TtReader, name: Ident) -> Option<Rc<NamedMatch>> { |
5bcae85e | 122 | let matched_opt = r.interpolations.get(&name).cloned(); |
1a4d82fc | 123 | matched_opt.map(|s| lookup_cur_matched_by_matched(r, s)) |
223e47cc LB |
124 | } |
125 | ||
1a4d82fc JJ |
126 | #[derive(Clone)] |
127 | enum LockstepIterSize { | |
128 | LisUnconstrained, | |
85aaf69f | 129 | LisConstraint(usize, Ident), |
1a4d82fc | 130 | LisContradiction(String), |
223e47cc LB |
131 | } |
132 | ||
1a4d82fc JJ |
133 | impl Add for LockstepIterSize { |
134 | type Output = LockstepIterSize; | |
135 | ||
136 | fn add(self, other: LockstepIterSize) -> LockstepIterSize { | |
137 | match self { | |
138 | LisUnconstrained => other, | |
139 | LisContradiction(_) => self, | |
140 | LisConstraint(l_len, ref l_id) => match other { | |
141 | LisUnconstrained => self.clone(), | |
142 | LisContradiction(_) => other, | |
143 | LisConstraint(r_len, _) if l_len == r_len => self.clone(), | |
144 | LisConstraint(r_len, r_id) => { | |
1a4d82fc | 145 | LisContradiction(format!("inconsistent lockstep iteration: \ |
c1a9b12d SL |
146 | '{}' has {} items, but '{}' has {}", |
147 | l_id, l_len, r_id, r_len)) | |
1a4d82fc JJ |
148 | } |
149 | }, | |
970d7e83 LB |
150 | } |
151 | } | |
223e47cc | 152 | } |
223e47cc | 153 | |
1a4d82fc | 154 | fn lockstep_iter_size(t: &TokenTree, r: &TtReader) -> LockstepIterSize { |
970d7e83 | 155 | match *t { |
92a42be0 | 156 | TokenTree::Delimited(_, ref delimed) => { |
1a4d82fc JJ |
157 | delimed.tts.iter().fold(LisUnconstrained, |size, tt| { |
158 | size + lockstep_iter_size(tt, r) | |
159 | }) | |
160 | }, | |
92a42be0 | 161 | TokenTree::Sequence(_, ref seq) => { |
1a4d82fc JJ |
162 | seq.tts.iter().fold(LisUnconstrained, |size, tt| { |
163 | size + lockstep_iter_size(tt, r) | |
164 | }) | |
165 | }, | |
a7813a04 | 166 | TokenTree::Token(_, SubstNt(name)) | TokenTree::Token(_, MatchNt(name, _)) => |
1a4d82fc JJ |
167 | match lookup_cur_matched(r, name) { |
168 | Some(matched) => match *matched { | |
169 | MatchedNonterminal(_) => LisUnconstrained, | |
170 | MatchedSeq(ref ads, _) => LisConstraint(ads.len(), name), | |
171 | }, | |
172 | _ => LisUnconstrained | |
173 | }, | |
92a42be0 | 174 | TokenTree::Token(..) => LisUnconstrained, |
223e47cc LB |
175 | } |
176 | } | |
177 | ||
1a4d82fc JJ |
178 | /// Return the next token from the TtReader. |
179 | /// EFFECT: advances the reader's token field | |
223e47cc | 180 | pub fn tt_next_token(r: &mut TtReader) -> TokenAndSpan { |
1a4d82fc | 181 | // FIXME(pcwalton): Bad copy? |
223e47cc | 182 | let ret_val = TokenAndSpan { |
1a4d82fc JJ |
183 | tok: r.cur_tok.clone(), |
184 | sp: r.cur_span.clone(), | |
223e47cc LB |
185 | }; |
186 | loop { | |
1a4d82fc JJ |
187 | match r.crate_name_next.take() { |
188 | None => (), | |
189 | Some(sp) => { | |
190 | r.cur_span = sp; | |
a7813a04 | 191 | r.cur_tok = token::Ident(r.imported_from.unwrap()); |
1a4d82fc JJ |
192 | return ret_val; |
193 | }, | |
223e47cc | 194 | } |
1a4d82fc JJ |
195 | let should_pop = match r.stack.last() { |
196 | None => { | |
197 | assert_eq!(ret_val.tok, token::Eof); | |
223e47cc | 198 | return ret_val; |
1a4d82fc JJ |
199 | } |
200 | Some(frame) => { | |
201 | if frame.idx < frame.forest.len() { | |
202 | break; | |
223e47cc | 203 | } |
1a4d82fc JJ |
204 | !frame.dotdotdoted || |
205 | *r.repeat_idx.last().unwrap() == *r.repeat_len.last().unwrap() - 1 | |
223e47cc | 206 | } |
1a4d82fc | 207 | }; |
223e47cc | 208 | |
1a4d82fc JJ |
209 | /* done with this set; pop or repeat? */ |
210 | if should_pop { | |
211 | let prev = r.stack.pop().unwrap(); | |
212 | match r.stack.last_mut() { | |
213 | None => { | |
214 | r.cur_tok = token::Eof; | |
215 | return ret_val; | |
216 | } | |
217 | Some(frame) => { | |
218 | frame.idx += 1; | |
219 | } | |
220 | } | |
221 | if prev.dotdotdoted { | |
222 | r.repeat_idx.pop(); | |
223 | r.repeat_len.pop(); | |
224 | } | |
223e47cc | 225 | } else { /* repeat */ |
85aaf69f | 226 | *r.repeat_idx.last_mut().unwrap() += 1; |
1a4d82fc | 227 | r.stack.last_mut().unwrap().idx = 0; |
3157f602 XL |
228 | if let Some(tk) = r.stack.last().unwrap().sep.clone() { |
229 | r.cur_tok = tk; // repeat same span, I guess | |
230 | return ret_val; | |
223e47cc LB |
231 | } |
232 | } | |
233 | } | |
92a42be0 SL |
234 | loop { /* because it's easiest, this handles `TokenTree::Delimited` not starting |
235 | with a `TokenTree::Token`, even though it won't happen */ | |
1a4d82fc JJ |
236 | let t = { |
237 | let frame = r.stack.last().unwrap(); | |
238 | // FIXME(pcwalton): Bad copy. | |
239 | frame.forest.get_tt(frame.idx) | |
240 | }; | |
241 | match t { | |
92a42be0 | 242 | TokenTree::Sequence(sp, seq) => { |
1a4d82fc | 243 | // FIXME(pcwalton): Bad copy. |
92a42be0 | 244 | match lockstep_iter_size(&TokenTree::Sequence(sp, seq.clone()), |
1a4d82fc JJ |
245 | r) { |
246 | LisUnconstrained => { | |
9346a6ac | 247 | panic!(r.sp_diag.span_fatal( |
1a4d82fc JJ |
248 | sp.clone(), /* blame macro writer */ |
249 | "attempted to repeat an expression \ | |
250 | containing no syntax \ | |
9346a6ac | 251 | variables matched as repeating at this depth")); |
1a4d82fc JJ |
252 | } |
253 | LisContradiction(ref msg) => { | |
254 | // FIXME #2887 blame macro invoker instead | |
9346a6ac | 255 | panic!(r.sp_diag.span_fatal(sp.clone(), &msg[..])); |
1a4d82fc JJ |
256 | } |
257 | LisConstraint(len, _) => { | |
258 | if len == 0 { | |
3157f602 | 259 | if seq.op == tokenstream::KleeneOp::OneOrMore { |
1a4d82fc | 260 | // FIXME #2887 blame invoker |
9346a6ac AL |
261 | panic!(r.sp_diag.span_fatal(sp.clone(), |
262 | "this must repeat at least once")); | |
1a4d82fc | 263 | } |
223e47cc | 264 | |
1a4d82fc JJ |
265 | r.stack.last_mut().unwrap().idx += 1; |
266 | return tt_next_token(r); | |
267 | } | |
268 | r.repeat_len.push(len); | |
269 | r.repeat_idx.push(0); | |
270 | r.stack.push(TtFrame { | |
271 | idx: 0, | |
272 | dotdotdoted: true, | |
273 | sep: seq.separator.clone(), | |
92a42be0 | 274 | forest: TokenTree::Sequence(sp, seq), |
1a4d82fc JJ |
275 | }); |
276 | } | |
223e47cc | 277 | } |
223e47cc | 278 | } |
1a4d82fc | 279 | // FIXME #2887: think about span stuff here |
a7813a04 | 280 | TokenTree::Token(sp, SubstNt(ident)) => { |
1a4d82fc JJ |
281 | match lookup_cur_matched(r, ident) { |
282 | None => { | |
5bcae85e | 283 | r.stack.last_mut().unwrap().idx += 1; |
1a4d82fc | 284 | r.cur_span = sp; |
a7813a04 | 285 | r.cur_tok = SubstNt(ident); |
1a4d82fc | 286 | return ret_val; |
92a42be0 | 287 | // this can't be 0 length, just like TokenTree::Delimited |
1a4d82fc JJ |
288 | } |
289 | Some(cur_matched) => { | |
290 | match *cur_matched { | |
291 | // sidestep the interpolation tricks for ident because | |
292 | // (a) idents can be in lots of places, so it'd be a pain | |
293 | // (b) we actually can, since it's a token. | |
a7813a04 | 294 | MatchedNonterminal(NtIdent(ref sn)) => { |
5bcae85e | 295 | r.stack.last_mut().unwrap().idx += 1; |
9cc50fc6 | 296 | r.cur_span = sn.span; |
a7813a04 | 297 | r.cur_tok = token::Ident(sn.node); |
1a4d82fc JJ |
298 | return ret_val; |
299 | } | |
5bcae85e SL |
300 | MatchedNonterminal(NtTT(ref tt)) => { |
301 | r.stack.push(TtFrame { | |
302 | forest: TokenTree::Token(sp, Interpolated(NtTT(tt.clone()))), | |
303 | idx: 0, | |
304 | dotdotdoted: false, | |
305 | sep: None, | |
306 | }); | |
307 | } | |
1a4d82fc | 308 | MatchedNonterminal(ref other_whole_nt) => { |
5bcae85e | 309 | r.stack.last_mut().unwrap().idx += 1; |
1a4d82fc JJ |
310 | // FIXME(pcwalton): Bad copy. |
311 | r.cur_span = sp; | |
5bcae85e | 312 | r.cur_tok = Interpolated((*other_whole_nt).clone()); |
1a4d82fc JJ |
313 | return ret_val; |
314 | } | |
315 | MatchedSeq(..) => { | |
9346a6ac | 316 | panic!(r.sp_diag.span_fatal( |
e9174d1e | 317 | sp, /* blame the macro writer */ |
c1a9b12d SL |
318 | &format!("variable '{}' is still repeating at this depth", |
319 | ident))); | |
1a4d82fc JJ |
320 | } |
321 | } | |
322 | } | |
323 | } | |
324 | } | |
92a42be0 SL |
325 | // TokenTree::Delimited or any token that can be unzipped |
326 | seq @ TokenTree::Delimited(..) | seq @ TokenTree::Token(_, MatchNt(..)) => { | |
1a4d82fc JJ |
327 | // do not advance the idx yet |
328 | r.stack.push(TtFrame { | |
329 | forest: seq, | |
330 | idx: 0, | |
331 | dotdotdoted: false, | |
332 | sep: None | |
333 | }); | |
334 | // if this could be 0-length, we'd need to potentially recur here | |
335 | } | |
92a42be0 | 336 | TokenTree::Token(sp, DocComment(name)) if r.desugar_doc_comments => { |
1a4d82fc | 337 | r.stack.push(TtFrame { |
92a42be0 | 338 | forest: TokenTree::Token(sp, DocComment(name)), |
1a4d82fc JJ |
339 | idx: 0, |
340 | dotdotdoted: false, | |
341 | sep: None | |
342 | }); | |
343 | } | |
92a42be0 | 344 | TokenTree::Token(sp, token::SpecialVarNt(SpecialMacroVar::CrateMacroVar)) => { |
1a4d82fc JJ |
345 | r.stack.last_mut().unwrap().idx += 1; |
346 | ||
347 | if r.imported_from.is_some() { | |
348 | r.cur_span = sp; | |
349 | r.cur_tok = token::ModSep; | |
350 | r.crate_name_next = Some(sp); | |
351 | return ret_val; | |
352 | } | |
353 | ||
354 | // otherwise emit nothing and proceed to the next token | |
355 | } | |
92a42be0 | 356 | TokenTree::Token(sp, tok) => { |
223e47cc | 357 | r.cur_span = sp; |
1a4d82fc JJ |
358 | r.cur_tok = tok; |
359 | r.stack.last_mut().unwrap().idx += 1; | |
223e47cc | 360 | return ret_val; |
223e47cc | 361 | } |
223e47cc LB |
362 | } |
363 | } | |
223e47cc | 364 | } |