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