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