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::rc::Rc;
21 use collections::HashMap;
22
23 ///an unzipping of `TokenTree`s
24 #[deriving(Clone)]
25 struct TtFrame {
26 forest: Rc<Vec<ast::TokenTree>>,
27 idx: uint,
28 dotdotdoted: bool,
29 sep: Option<Token>,
30 }
31
32 #[deriving(Clone)]
33 pub struct TtReader<'a> {
34 pub sp_diag: &'a SpanHandler,
35 // the unzipped tree:
36 stack: Vec<TtFrame>,
37 /* for MBE-style macro transcription */
38 interpolations: HashMap<Ident, Rc<NamedMatch>>,
39 repeat_idx: Vec<uint>,
40 repeat_len: Vec<uint>,
41 /* cached: */
42 pub cur_tok: Token,
43 pub cur_span: 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<'a>(sp_diag: &'a SpanHandler,
50 interp: Option<HashMap<Ident, Rc<NamedMatch>>>,
51 src: Vec<ast::TokenTree> )
52 -> TtReader<'a> {
53 let mut r = TtReader {
54 sp_diag: sp_diag,
55 stack: vec!(TtFrame {
56 forest: Rc::new(src),
57 idx: 0,
58 dotdotdoted: false,
59 sep: None,
60 }),
61 interpolations: match interp { /* just a convienience */
62 None => HashMap::new(),
63 Some(x) => x,
64 },
65 repeat_idx: Vec::new(),
66 repeat_len: Vec::new(),
67 /* dummy values, never read: */
68 cur_tok: EOF,
69 cur_span: DUMMY_SP,
70 };
71 tt_next_token(&mut r); /* get cur_tok and cur_span set up */
72 r
73 }
74
75 fn lookup_cur_matched_by_matched(r: &TtReader, start: Rc<NamedMatch>) -> Rc<NamedMatch> {
76 r.repeat_idx.iter().fold(start, |ad, idx| {
77 match *ad {
78 MatchedNonterminal(_) => {
79 // end of the line; duplicate henceforth
80 ad.clone()
81 }
82 MatchedSeq(ref ads, _) => ads.get(*idx).clone()
83 }
84 })
85 }
86
87 fn lookup_cur_matched(r: &TtReader, name: Ident) -> Rc<NamedMatch> {
88 let matched_opt = r.interpolations.find_copy(&name);
89 match matched_opt {
90 Some(s) => lookup_cur_matched_by_matched(r, s),
91 None => {
92 r.sp_diag.span_fatal(r.cur_span,
93 format!("unknown macro variable `{}`",
94 token::get_ident(name)));
95 }
96 }
97 }
98
99 #[deriving(Clone)]
100 enum LockstepIterSize {
101 LisUnconstrained,
102 LisConstraint(uint, Ident),
103 LisContradiction(StrBuf),
104 }
105
106 fn lis_merge(lhs: LockstepIterSize, rhs: LockstepIterSize) -> LockstepIterSize {
107 match lhs {
108 LisUnconstrained => rhs.clone(),
109 LisContradiction(_) => lhs.clone(),
110 LisConstraint(l_len, l_id) => match rhs {
111 LisUnconstrained => lhs.clone(),
112 LisContradiction(_) => rhs.clone(),
113 LisConstraint(r_len, _) if l_len == r_len => lhs.clone(),
114 LisConstraint(r_len, r_id) => {
115 let l_n = token::get_ident(l_id);
116 let r_n = token::get_ident(r_id);
117 LisContradiction(format!("inconsistent lockstep iteration: \
118 '{}' has {} items, but '{}' has {}",
119 l_n, l_len, r_n, r_len).to_strbuf())
120 }
121 }
122 }
123 }
124
125 fn lockstep_iter_size(t: &TokenTree, r: &TtReader) -> LockstepIterSize {
126 match *t {
127 TTDelim(ref tts) | TTSeq(_, ref tts, _, _) => {
128 tts.iter().fold(LisUnconstrained, |lis, tt| {
129 lis_merge(lis, lockstep_iter_size(tt, r))
130 })
131 }
132 TTTok(..) => LisUnconstrained,
133 TTNonterminal(_, name) => match *lookup_cur_matched(r, name) {
134 MatchedNonterminal(_) => LisUnconstrained,
135 MatchedSeq(ref ads, _) => LisConstraint(ads.len(), name)
136 }
137 }
138 }
139
140 // return the next token from the TtReader.
141 // EFFECT: advances the reader's token field
142 pub fn tt_next_token(r: &mut TtReader) -> TokenAndSpan {
143 // FIXME(pcwalton): Bad copy?
144 let ret_val = TokenAndSpan {
145 tok: r.cur_tok.clone(),
146 sp: r.cur_span.clone(),
147 };
148 loop {
149 let should_pop = match r.stack.last() {
150 None => {
151 assert_eq!(ret_val.tok, EOF);
152 return ret_val;
153 }
154 Some(frame) => {
155 if frame.idx < frame.forest.len() {
156 break;
157 }
158 !frame.dotdotdoted ||
159 *r.repeat_idx.last().unwrap() == *r.repeat_len.last().unwrap() - 1
160 }
161 };
162
163 /* done with this set; pop or repeat? */
164 if should_pop {
165 let prev = r.stack.pop().unwrap();
166 match r.stack.mut_last() {
167 None => {
168 r.cur_tok = EOF;
169 return ret_val;
170 }
171 Some(frame) => {
172 frame.idx += 1;
173 }
174 }
175 if prev.dotdotdoted {
176 r.repeat_idx.pop();
177 r.repeat_len.pop();
178 }
179 } else { /* repeat */
180 *r.repeat_idx.mut_last().unwrap() += 1u;
181 r.stack.mut_last().unwrap().idx = 0;
182 match r.stack.last().unwrap().sep.clone() {
183 Some(tk) => {
184 r.cur_tok = tk; /* repeat same span, I guess */
185 return ret_val;
186 }
187 None => {}
188 }
189 }
190 }
191 loop { /* because it's easiest, this handles `TTDelim` not starting
192 with a `TTTok`, even though it won't happen */
193 let t = {
194 let frame = r.stack.last().unwrap();
195 // FIXME(pcwalton): Bad copy.
196 (*frame.forest.get(frame.idx)).clone()
197 };
198 match t {
199 TTDelim(tts) => {
200 r.stack.push(TtFrame {
201 forest: tts,
202 idx: 0,
203 dotdotdoted: false,
204 sep: None
205 });
206 // if this could be 0-length, we'd need to potentially recur here
207 }
208 TTTok(sp, tok) => {
209 r.cur_span = sp;
210 r.cur_tok = tok;
211 r.stack.mut_last().unwrap().idx += 1;
212 return ret_val;
213 }
214 TTSeq(sp, tts, sep, zerok) => {
215 // FIXME(pcwalton): Bad copy.
216 match lockstep_iter_size(&TTSeq(sp, tts.clone(), sep.clone(), zerok), 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.as_slice());
227 }
228 LisConstraint(len, _) => {
229 if len == 0 {
230 if !zerok {
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.mut_last().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 forest: tts,
243 idx: 0,
244 dotdotdoted: true,
245 sep: sep.clone()
246 });
247 }
248 }
249 }
250 // FIXME #2887: think about span stuff here
251 TTNonterminal(sp, ident) => {
252 r.stack.mut_last().unwrap().idx += 1;
253 match *lookup_cur_matched(r, ident) {
254 /* sidestep the interpolation tricks for ident because
255 (a) idents can be in lots of places, so it'd be a pain
256 (b) we actually can, since it's a token. */
257 MatchedNonterminal(NtIdent(box sn, b)) => {
258 r.cur_span = sp;
259 r.cur_tok = IDENT(sn,b);
260 return ret_val;
261 }
262 MatchedNonterminal(ref other_whole_nt) => {
263 // FIXME(pcwalton): Bad copy.
264 r.cur_span = sp;
265 r.cur_tok = INTERPOLATED((*other_whole_nt).clone());
266 return ret_val;
267 }
268 MatchedSeq(..) => {
269 r.sp_diag.span_fatal(
270 r.cur_span, /* blame the macro writer */
271 format!("variable '{}' is still repeating at this depth",
272 token::get_ident(ident)));
273 }
274 }
275 }
276 }
277 }
278 }
libsyntax/ext/tt/transcribe.rs:124:1-124:1 -fn- definition:
fn lockstep_iter_size(t: &TokenTree, r: &TtReader) -> LockstepIterSize {
match *t {
TTDelim(ref tts) | TTSeq(_, ref tts, _, _) => {
references:- 2128: tts.iter().fold(LisUnconstrained, |lis, tt| {
129: lis_merge(lis, lockstep_iter_size(tt, r))
130: })
--
215: // FIXME(pcwalton): Bad copy.
216: match lockstep_iter_size(&TTSeq(sp, tts.clone(), sep.clone(), zerok), r) {
217: LisUnconstrained => {
libsyntax/ext/tt/transcribe.rs:32:19-32:19 -struct- definition:
pub struct TtReader<'a> {
pub sp_diag: &'a SpanHandler,
// the unzipped tree:
references:- 1352: -> TtReader<'a> {
53: let mut r = TtReader {
54: sp_diag: sp_diag,
--
87: fn lookup_cur_matched(r: &TtReader, name: Ident) -> Rc<NamedMatch> {
88: let matched_opt = r.interpolations.find_copy(&name);
--
141: // EFFECT: advances the reader's token field
142: pub fn tt_next_token(r: &mut TtReader) -> TokenAndSpan {
143: // FIXME(pcwalton): Bad copy?
libsyntax/ext/tt/macro_parser.rs:
209: cfg: ast::CrateConfig,
210: rdr: TtReader,
211: ms: Vec<Matcher> )
--
235: cfg: ast::CrateConfig,
236: mut rdr: TtReader,
237: ms: &[Matcher])
libsyntax/parse/lexer.rs:
117: impl<'a> Reader for TtReader<'a> {
118: fn is_eof(&self) -> bool {
libsyntax/ext/tt/transcribe.rs:
125: fn lockstep_iter_size(t: &TokenTree, r: &TtReader) -> LockstepIterSize {
126: match *t {
libsyntax/ext/tt/transcribe.rs:24:19-24:19 -struct- definition:
struct TtFrame {
forest: Rc<Vec<ast::TokenTree>>,
idx: uint,
references:- 854: sp_diag: sp_diag,
55: stack: vec!(TtFrame {
56: forest: Rc::new(src),
--
240: r.repeat_idx.push(0);
241: r.stack.push(TtFrame {
242: forest: tts,
libsyntax/ext/tt/transcribe.rs:86:1-86:1 -fn- definition:
fn lookup_cur_matched(r: &TtReader, name: Ident) -> Rc<NamedMatch> {
let matched_opt = r.interpolations.find_copy(&name);
match matched_opt {
references:- 2132: TTTok(..) => LisUnconstrained,
133: TTNonterminal(_, name) => match *lookup_cur_matched(r, name) {
134: MatchedNonterminal(_) => LisUnconstrained,
--
252: r.stack.mut_last().unwrap().idx += 1;
253: match *lookup_cur_matched(r, ident) {
254: /* sidestep the interpolation tricks for ident because
libsyntax/ext/tt/transcribe.rs:48:24-48:24 -fn- definition:
* should) be none. */
pub fn new_tt_reader<'a>(sp_diag: &'a SpanHandler,
interp: Option<HashMap<Ident, Rc<NamedMatch>>>,
references:- 4libsyntax/ext/tt/macro_rules.rs:
140: // `None` is because we're not interpolating
141: let arg_rdr = new_tt_reader(&cx.parse_sess().span_diagnostic,
142: None,
--
165: // rhs has holes ( `$id` and `$(...)` that need filled)
166: let trncbr = new_tt_reader(&cx.parse_sess().span_diagnostic,
167: Some(named_matches),
libsyntax/parse/mod.rs:
257: cfg: ast::CrateConfig) -> Parser<'a> {
258: let trdr = lexer::new_tt_reader(&sess.span_diagnostic, None, tts);
259: Parser(sess, cfg, box trdr)
libsyntax/ext/tt/macro_rules.rs:
222: // Parse the macro_rules! invocation (`none` is for no interpolations):
223: let arg_reader = new_tt_reader(&cx.parse_sess().span_diagnostic,
224: None,
libsyntax/ext/tt/transcribe.rs:141:45-141:45 -fn- definition:
// EFFECT: advances the reader's token field
pub fn tt_next_token(r: &mut TtReader) -> TokenAndSpan {
// FIXME(pcwalton): Bad copy?
references:- 370: };
71: tt_next_token(&mut r); /* get cur_tok and cur_span set up */
72: r
libsyntax/parse/lexer.rs:
121: fn next_token(&mut self) -> TokenAndSpan {
122: let r = tt_next_token(self);
123: debug!("TtReader: r={:?}", r);
libsyntax/ext/tt/transcribe.rs:
236: r.stack.mut_last().unwrap().idx += 1;
237: return tt_next_token(r);
238: }
libsyntax/ext/tt/transcribe.rs:99:19-99:19 -enum- definition:
enum LockstepIterSize {
LisUnconstrained,
LisConstraint(uint, Ident),
references:- 6106: fn lis_merge(lhs: LockstepIterSize, rhs: LockstepIterSize) -> LockstepIterSize {
107: match lhs {
--
125: fn lockstep_iter_size(t: &TokenTree, r: &TtReader) -> LockstepIterSize {
126: match *t {