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.
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::*;
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};
20 use parse::lexer::TokenAndSpan;
24 use std::collections::HashMap;
26 ///an unzipping of `TokenTree`s
36 pub struct TtReader<'a> {
37 pub sp_diag: &'a SpanHandler,
38 /// the unzipped tree:
40 /* for MBE-style macro transcription */
41 interpolations: HashMap<Ident, Rc<NamedMatch>>,
42 repeat_idx: Vec<uint>,
43 repeat_len: Vec<uint>,
47 /// Transform doc comments. Only useful in macro invocations
48 pub desugar_doc_comments: bool,
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
54 pub fn new_tt_reader<'a>(sp_diag: &'a SpanHandler,
55 interp: Option<HashMap<Ident, Rc<NamedMatch>>>,
56 src: Vec<ast::TokenTree> )
58 let mut r = TtReader {
61 forest: TtSequence(DUMMY_SP, Rc::new(ast::SequenceRepetition {
63 // doesn't matter. This merely holds the root unzipping.
64 separator: None, op: ast::ZeroOrMore, num_captures: 0
70 interpolations: match interp { /* just a convenience */
71 None => HashMap::new(),
74 repeat_idx: Vec::new(),
75 repeat_len: Vec::new(),
76 desugar_doc_comments: false,
77 /* dummy values, never read: */
81 tt_next_token(&mut r); /* get cur_tok and cur_span set up */
85 fn lookup_cur_matched_by_matched(r: &TtReader, start: Rc<NamedMatch>) -> Rc<NamedMatch> {
86 r.repeat_idx.iter().fold(start, |ad, idx| {
88 MatchedNonterminal(_) => {
89 // end of the line; duplicate henceforth
92 MatchedSeq(ref ads, _) => ads[*idx].clone()
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))
103 enum LockstepIterSize {
105 LisConstraint(uint, Ident),
106 LisContradiction(String),
109 impl Add<LockstepIterSize, LockstepIterSize> for LockstepIterSize {
110 fn add(self, other: LockstepIterSize) -> LockstepIterSize {
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())
130 fn lockstep_iter_size(t: &TokenTree, r: &TtReader) -> LockstepIterSize {
132 TtDelimited(_, ref delimed) => {
133 delimed.tts.iter().fold(LisUnconstrained, |size, tt| {
134 size + lockstep_iter_size(tt, r)
137 TtSequence(_, ref seq) => {
138 seq.tts.iter().fold(LisUnconstrained, |size, tt| {
139 size + lockstep_iter_size(tt, r)
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),
148 _ => LisUnconstrained
150 TtToken(..) => LisUnconstrained,
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(),
163 let should_pop = match r.stack.last() {
165 assert_eq!(ret_val.tok, token::Eof);
169 if frame.idx < frame.forest.len() {
172 !frame.dotdotdoted ||
173 *r.repeat_idx.last().unwrap() == *r.repeat_len.last().unwrap() - 1
177 /* done with this set; pop or repeat? */
179 let prev = r.stack.pop().unwrap();
180 match r.stack.last_mut() {
182 r.cur_tok = token::Eof;
189 if prev.dotdotdoted {
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() {
198 r.cur_tok = tk; /* repeat same span, I guess */
205 loop { /* because it's easiest, this handles `TtDelimited` not starting
206 with a `TtToken`, even though it won't happen */
208 let frame = r.stack.last().unwrap();
209 // FIXME(pcwalton): Bad copy.
210 frame.forest.get_tt(frame.idx)
213 TtSequence(sp, seq) => {
214 // FIXME(pcwalton): Bad copy.
215 match lockstep_iter_size(&TtSequence(sp, seq.clone()),
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");
224 LisContradiction(ref msg) => {
225 // FIXME #2887 blame macro invoker instead
226 r.sp_diag.span_fatal(sp.clone(), msg[]);
228 LisConstraint(len, _) => {
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");
236 r.stack.last_mut().unwrap().idx += 1;
237 return tt_next_token(r);
239 r.repeat_len.push(len);
240 r.repeat_idx.push(0);
241 r.stack.push(TtFrame {
244 sep: seq.separator.clone(),
245 forest: TtSequence(sp, seq),
250 // FIXME #2887: think about span stuff here
251 TtToken(sp, SubstNt(ident, namep)) => {
252 match lookup_cur_matched(r, ident) {
254 r.stack.push(TtFrame {
255 forest: TtToken(sp, SubstNt(ident, namep)),
260 // this can't be 0 length, just like TtDelimited
262 Some(cur_matched) => {
263 r.stack.last_mut().unwrap().idx += 1;
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)) => {
270 r.cur_tok = token::Ident(sn, b);
273 MatchedNonterminal(ref other_whole_nt) => {
274 // FIXME(pcwalton): Bad copy.
276 r.cur_tok = token::Interpolated((*other_whole_nt).clone());
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))[]);
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 {
298 // if this could be 0-length, we'd need to potentially recur here
300 TtToken(sp, DocComment(name)) if r.desugar_doc_comments => {
301 r.stack.push(TtFrame {
302 forest: TtToken(sp, DocComment(name)),
308 TtToken(sp, tok) => {
311 r.stack.last_mut().unwrap().idx += 1;