]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/tt/transcribe.rs
libsyntax: Fix errors arising from the automated `~[T]` conversion
[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
11 use ast;
12 use ast::{TokenTree, TTDelim, TTTok, TTSeq, TTNonterminal, Ident};
13 use codemap::{Span, DUMMY_SP};
14 use diagnostic::SpanHandler;
15 use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
16 use parse::token::{EOF, INTERPOLATED, IDENT, Token, NtIdent};
17 use parse::token;
18 use parse::lexer::TokenAndSpan;
19
20 use std::cell::{Cell, RefCell};
21 use std::vec_ng::Vec;
22 use collections::HashMap;
23
24 ///an unzipping of `TokenTree`s
25 struct TtFrame {
26     forest: @Vec<ast::TokenTree> ,
27     idx: Cell<uint>,
28     dotdotdoted: bool,
29     sep: Option<Token>,
30     up: Option<@TtFrame>,
31 }
32
33 pub struct TtReader {
34     sp_diag: @SpanHandler,
35     // the unzipped tree:
36     priv stack: RefCell<@TtFrame>,
37     /* for MBE-style macro transcription */
38     priv interpolations: RefCell<HashMap<Ident, @NamedMatch>>,
39     priv repeat_idx: RefCell<Vec<uint> >,
40     priv repeat_len: RefCell<Vec<uint> >,
41     /* cached: */
42     cur_tok: RefCell<Token>,
43     cur_span: RefCell<Span>,
44 }
45
46 /** This can do Macro-By-Example transcription. On the other hand, if
47  *  `src` contains no `TTSeq`s and `TTNonterminal`s, `interp` can (and
48  *  should) be none. */
49 pub fn new_tt_reader(sp_diag: @SpanHandler,
50                      interp: Option<HashMap<Ident, @NamedMatch>>,
51                      src: Vec<ast::TokenTree> )
52                      -> TtReader {
53     let r = TtReader {
54         sp_diag: sp_diag,
55         stack: RefCell::new(@TtFrame {
56             forest: @src,
57             idx: Cell::new(0u),
58             dotdotdoted: false,
59             sep: None,
60             up: None
61         }),
62         interpolations: match interp { /* just a convienience */
63             None => RefCell::new(HashMap::new()),
64             Some(x) => RefCell::new(x),
65         },
66         repeat_idx: RefCell::new(Vec::new()),
67         repeat_len: RefCell::new(Vec::new()),
68         /* dummy values, never read: */
69         cur_tok: RefCell::new(EOF),
70         cur_span: RefCell::new(DUMMY_SP),
71     };
72     tt_next_token(&r); /* get cur_tok and cur_span set up */
73     return r;
74 }
75
76 fn dup_tt_frame(f: @TtFrame) -> @TtFrame {
77     @TtFrame {
78         forest: @(*f.forest).clone(),
79         idx: f.idx.clone(),
80         dotdotdoted: f.dotdotdoted,
81         sep: f.sep.clone(),
82         up: match f.up {
83             Some(up_frame) => Some(dup_tt_frame(up_frame)),
84             None => None
85         }
86     }
87 }
88
89 pub fn dup_tt_reader(r: &TtReader) -> TtReader {
90     TtReader {
91         sp_diag: r.sp_diag,
92         stack: RefCell::new(dup_tt_frame(r.stack.get())),
93         repeat_idx: r.repeat_idx.clone(),
94         repeat_len: r.repeat_len.clone(),
95         cur_tok: r.cur_tok.clone(),
96         cur_span: r.cur_span.clone(),
97         interpolations: r.interpolations.clone(),
98     }
99 }
100
101
102 fn lookup_cur_matched_by_matched(r: &TtReader, start: @NamedMatch)
103                                  -> @NamedMatch {
104     fn red(ad: @NamedMatch, idx: &uint) -> @NamedMatch {
105         match *ad {
106             MatchedNonterminal(_) => {
107                 // end of the line; duplicate henceforth
108                 ad
109             }
110             MatchedSeq(ref ads, _) => *ads.get(*idx)
111         }
112     }
113     let repeat_idx = r.repeat_idx.borrow();
114     repeat_idx.get().iter().fold(start, red)
115 }
116
117 fn lookup_cur_matched(r: &TtReader, name: Ident) -> @NamedMatch {
118     let matched_opt = {
119         let interpolations = r.interpolations.borrow();
120         interpolations.get().find_copy(&name)
121     };
122     match matched_opt {
123         Some(s) => lookup_cur_matched_by_matched(r, s),
124         None => {
125             r.sp_diag.span_fatal(r.cur_span.get(),
126                                  format!("unknown macro variable `{}`",
127                                          token::get_ident(name)));
128         }
129     }
130 }
131
132 #[deriving(Clone)]
133 enum LockstepIterSize {
134     LisUnconstrained,
135     LisConstraint(uint, Ident),
136     LisContradiction(~str),
137 }
138
139 fn lis_merge(lhs: LockstepIterSize, rhs: LockstepIterSize) -> LockstepIterSize {
140     match lhs {
141         LisUnconstrained => rhs.clone(),
142         LisContradiction(_) => lhs.clone(),
143         LisConstraint(l_len, l_id) => match rhs {
144             LisUnconstrained => lhs.clone(),
145             LisContradiction(_) => rhs.clone(),
146             LisConstraint(r_len, _) if l_len == r_len => lhs.clone(),
147             LisConstraint(r_len, r_id) => {
148                 let l_n = token::get_ident(l_id);
149                 let r_n = token::get_ident(r_id);
150                 LisContradiction(format!("inconsistent lockstep iteration: \
151                                           '{}' has {} items, but '{}' has {}",
152                                           l_n, l_len, r_n, r_len))
153             }
154         }
155     }
156 }
157
158 fn lockstep_iter_size(t: &TokenTree, r: &TtReader) -> LockstepIterSize {
159     match *t {
160         TTDelim(ref tts) | TTSeq(_, ref tts, _, _) => {
161             tts.iter().fold(LisUnconstrained, |lis, tt| {
162                 lis_merge(lis, lockstep_iter_size(tt, r))
163             })
164         }
165         TTTok(..) => LisUnconstrained,
166         TTNonterminal(_, name) => match *lookup_cur_matched(r, name) {
167             MatchedNonterminal(_) => LisUnconstrained,
168             MatchedSeq(ref ads, _) => LisConstraint(ads.len(), name)
169         }
170     }
171 }
172
173 // return the next token from the TtReader.
174 // EFFECT: advances the reader's token field
175 pub fn tt_next_token(r: &TtReader) -> TokenAndSpan {
176     // FIXME(pcwalton): Bad copy?
177     let ret_val = TokenAndSpan {
178         tok: r.cur_tok.get(),
179         sp: r.cur_span.get(),
180     };
181     loop {
182         {
183             let mut stack = r.stack.borrow_mut();
184             if stack.get().idx.get() < stack.get().forest.len() {
185                 break;
186             }
187         }
188
189         /* done with this set; pop or repeat? */
190         if !r.stack.get().dotdotdoted || {
191                 let repeat_idx = r.repeat_idx.borrow();
192                 let repeat_len = r.repeat_len.borrow();
193                 *repeat_idx.get().last().unwrap() ==
194                 *repeat_len.get().last().unwrap() - 1
195             } {
196
197             match r.stack.get().up {
198               None => {
199                 r.cur_tok.set(EOF);
200                 return ret_val;
201               }
202               Some(tt_f) => {
203                 if r.stack.get().dotdotdoted {
204                     {
205                         let mut repeat_idx = r.repeat_idx.borrow_mut();
206                         let mut repeat_len = r.repeat_len.borrow_mut();
207                         repeat_idx.get().pop().unwrap();
208                         repeat_len.get().pop().unwrap();
209                     }
210                 }
211
212                 r.stack.set(tt_f);
213                 r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
214               }
215             }
216
217         } else { /* repeat */
218             r.stack.get().idx.set(0u);
219             {
220                 let mut repeat_idx = r.repeat_idx.borrow_mut();
221                 let last_repeat_idx = repeat_idx.get().len() - 1u;
222                 *repeat_idx.get().get_mut(last_repeat_idx) += 1u;
223             }
224             match r.stack.get().sep.clone() {
225               Some(tk) => {
226                 r.cur_tok.set(tk); /* repeat same span, I guess */
227                 return ret_val;
228               }
229               None => ()
230             }
231         }
232     }
233     loop { /* because it's easiest, this handles `TTDelim` not starting
234     with a `TTTok`, even though it won't happen */
235         // FIXME(pcwalton): Bad copy.
236         match (*r.stack.get().forest.get(r.stack.get().idx.get())).clone() {
237           TTDelim(tts) => {
238             r.stack.set(@TtFrame {
239                 forest: tts,
240                 idx: Cell::new(0u),
241                 dotdotdoted: false,
242                 sep: None,
243                 up: Some(r.stack.get())
244             });
245             // if this could be 0-length, we'd need to potentially recur here
246           }
247           TTTok(sp, tok) => {
248             r.cur_span.set(sp);
249             r.cur_tok.set(tok);
250             r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
251             return ret_val;
252           }
253           TTSeq(sp, tts, sep, zerok) => {
254             // FIXME(pcwalton): Bad copy.
255             let t = TTSeq(sp, tts, sep.clone(), zerok);
256             match lockstep_iter_size(&t, r) {
257               LisUnconstrained => {
258                 r.sp_diag.span_fatal(
259                     sp, /* blame macro writer */
260                       "attempted to repeat an expression \
261                        containing no syntax \
262                        variables matched as repeating at this depth");
263                   }
264                   LisContradiction(ref msg) => {
265                       /* FIXME #2887 blame macro invoker instead*/
266                       r.sp_diag.span_fatal(sp, (*msg));
267                   }
268                   LisConstraint(len, _) => {
269                     if len == 0 {
270                       if !zerok {
271                         r.sp_diag.span_fatal(sp, /* FIXME #2887 blame invoker
272                         */
273                                              "this must repeat at least \
274                                               once");
275                           }
276
277                     r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
278                     return tt_next_token(r);
279                 } else {
280                     {
281                         let mut repeat_idx = r.repeat_idx.borrow_mut();
282                         let mut repeat_len = r.repeat_len.borrow_mut();
283                         repeat_len.get().push(len);
284                         repeat_idx.get().push(0u);
285                         r.stack.set(@TtFrame {
286                             forest: tts,
287                             idx: Cell::new(0u),
288                             dotdotdoted: true,
289                             sep: sep,
290                             up: Some(r.stack.get())
291                         });
292                     }
293                 }
294               }
295             }
296           }
297           // FIXME #2887: think about span stuff here
298           TTNonterminal(sp, ident) => {
299             match *lookup_cur_matched(r, ident) {
300               /* sidestep the interpolation tricks for ident because
301               (a) idents can be in lots of places, so it'd be a pain
302               (b) we actually can, since it's a token. */
303               MatchedNonterminal(NtIdent(~sn,b)) => {
304                 r.cur_span.set(sp);
305                 r.cur_tok.set(IDENT(sn,b));
306                 r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
307                 return ret_val;
308               }
309               MatchedNonterminal(ref other_whole_nt) => {
310                 // FIXME(pcwalton): Bad copy.
311                 r.cur_span.set(sp);
312                 r.cur_tok.set(INTERPOLATED((*other_whole_nt).clone()));
313                 r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
314                 return ret_val;
315               }
316               MatchedSeq(..) => {
317                 r.sp_diag.span_fatal(
318                     r.cur_span.get(), /* blame the macro writer */
319                     format!("variable '{}' is still repeating at this depth",
320                             token::get_ident(ident)));
321               }
322             }
323           }
324         }
325     }
326
327 }