]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/tt/macro_rules.rs
Kill RacyCell in favor of marking SyncSender explicitly Send.
[rust.git] / src / libsyntax / ext / tt / macro_rules.rs
1 // Copyright 2015 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::{TokenTree, TtDelimited, TtSequence, TtToken};
12 use ast;
13 use codemap::{Span, DUMMY_SP};
14 use ext::base::{ExtCtxt, MacResult, SyntaxExtension};
15 use ext::base::{NormalTT, TTMacroExpander};
16 use ext::tt::macro_parser::{Success, Error, Failure};
17 use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
18 use ext::tt::macro_parser::{parse, parse_or_else};
19 use parse::lexer::{new_tt_reader, new_tt_reader_with_doc_flag};
20 use parse::parser::Parser;
21 use parse::attr::ParserAttr;
22 use parse::token::{special_idents, gensym_ident, NtTT, Token};
23 use parse::token::Token::*;
24 use parse::token;
25 use print;
26 use ptr::P;
27
28 use util::small_vector::SmallVector;
29
30 use std::cell::RefCell;
31 use std::rc::Rc;
32
33 struct ParserAnyMacro<'a> {
34     parser: RefCell<Parser<'a>>,
35 }
36
37 impl<'a> ParserAnyMacro<'a> {
38     /// Make sure we don't have any tokens left to parse, so we don't
39     /// silently drop anything. `allow_semi` is so that "optional"
40     /// semicolons at the end of normal expressions aren't complained
41     /// about e.g. the semicolon in `macro_rules! kapow { () => {
42     /// panic!(); } }` doesn't get picked up by .parse_expr(), but it's
43     /// allowed to be there.
44     fn ensure_complete_parse(&self, allow_semi: bool) {
45         let mut parser = self.parser.borrow_mut();
46         if allow_semi && parser.token == token::Semi {
47             parser.bump()
48         }
49         if parser.token != token::Eof {
50             let token_str = parser.this_token_to_string();
51             let msg = format!("macro expansion ignores token `{}` and any \
52                                following",
53                               token_str);
54             let span = parser.span;
55             parser.span_err(span, &msg[]);
56         }
57     }
58 }
59
60 impl<'a> MacResult for ParserAnyMacro<'a> {
61     fn make_expr(self: Box<ParserAnyMacro<'a>>) -> Option<P<ast::Expr>> {
62         let ret = self.parser.borrow_mut().parse_expr();
63         self.ensure_complete_parse(true);
64         Some(ret)
65     }
66     fn make_pat(self: Box<ParserAnyMacro<'a>>) -> Option<P<ast::Pat>> {
67         let ret = self.parser.borrow_mut().parse_pat();
68         self.ensure_complete_parse(false);
69         Some(ret)
70     }
71     fn make_items(self: Box<ParserAnyMacro<'a>>) -> Option<SmallVector<P<ast::Item>>> {
72         let mut ret = SmallVector::zero();
73         loop {
74             let mut parser = self.parser.borrow_mut();
75             // so... do outer attributes attached to the macro invocation
76             // just disappear? This question applies to make_methods, as
77             // well.
78             match parser.parse_item_with_outer_attributes() {
79                 Some(item) => ret.push(item),
80                 None => break
81             }
82         }
83         self.ensure_complete_parse(false);
84         Some(ret)
85     }
86
87     fn make_methods(self: Box<ParserAnyMacro<'a>>) -> Option<SmallVector<P<ast::Method>>> {
88         let mut ret = SmallVector::zero();
89         loop {
90             let mut parser = self.parser.borrow_mut();
91             match parser.token {
92                 token::Eof => break,
93                 _ => {
94                     let attrs = parser.parse_outer_attributes();
95                     ret.push(parser.parse_method(attrs, ast::Inherited))
96                 }
97             }
98         }
99         self.ensure_complete_parse(false);
100         Some(ret)
101     }
102
103     fn make_stmt(self: Box<ParserAnyMacro<'a>>) -> Option<P<ast::Stmt>> {
104         let attrs = self.parser.borrow_mut().parse_outer_attributes();
105         let ret = self.parser.borrow_mut().parse_stmt(attrs);
106         self.ensure_complete_parse(true);
107         Some(ret)
108     }
109 }
110
111 struct MacroRulesMacroExpander {
112     name: ast::Ident,
113     imported_from: Option<ast::Ident>,
114     lhses: Vec<Rc<NamedMatch>>,
115     rhses: Vec<Rc<NamedMatch>>,
116 }
117
118 impl TTMacroExpander for MacroRulesMacroExpander {
119     fn expand<'cx>(&self,
120                    cx: &'cx mut ExtCtxt,
121                    sp: Span,
122                    arg: &[ast::TokenTree])
123                    -> Box<MacResult+'cx> {
124         generic_extension(cx,
125                           sp,
126                           self.name,
127                           self.imported_from,
128                           arg,
129                           &self.lhses[],
130                           &self.rhses[])
131     }
132 }
133
134 /// Given `lhses` and `rhses`, this is the new macro we create
135 fn generic_extension<'cx>(cx: &'cx ExtCtxt,
136                           sp: Span,
137                           name: ast::Ident,
138                           imported_from: Option<ast::Ident>,
139                           arg: &[ast::TokenTree],
140                           lhses: &[Rc<NamedMatch>],
141                           rhses: &[Rc<NamedMatch>])
142                           -> Box<MacResult+'cx> {
143     if cx.trace_macros() {
144         println!("{}! {{ {} }}",
145                  token::get_ident(name),
146                  print::pprust::tts_to_string(arg));
147     }
148
149     // Which arm's failure should we report? (the one furthest along)
150     let mut best_fail_spot = DUMMY_SP;
151     let mut best_fail_msg = "internal error: ran no matchers".to_string();
152
153     for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers
154         match **lhs {
155           MatchedNonterminal(NtTT(ref lhs_tt)) => {
156             let lhs_tt = match **lhs_tt {
157                 TtDelimited(_, ref delim) => &delim.tts[],
158                 _ => cx.span_fatal(sp, "malformed macro lhs")
159             };
160             // `None` is because we're not interpolating
161             let arg_rdr = new_tt_reader_with_doc_flag(&cx.parse_sess().span_diagnostic,
162                                                       None,
163                                                       None,
164                                                       arg.iter()
165                                                          .map(|x| (*x).clone())
166                                                          .collect(),
167                                                       true);
168             match parse(cx.parse_sess(), cx.cfg(), arg_rdr, lhs_tt) {
169               Success(named_matches) => {
170                 let rhs = match *rhses[i] {
171                     // okay, what's your transcriber?
172                     MatchedNonterminal(NtTT(ref tt)) => {
173                         match **tt {
174                             // ignore delimiters
175                             TtDelimited(_, ref delimed) => delimed.tts.clone(),
176                             _ => cx.span_fatal(sp, "macro rhs must be delimited"),
177                         }
178                     },
179                     _ => cx.span_bug(sp, "bad thing in rhs")
180                 };
181                 // rhs has holes ( `$id` and `$(...)` that need filled)
182                 let trncbr = new_tt_reader(&cx.parse_sess().span_diagnostic,
183                                            Some(named_matches),
184                                            imported_from,
185                                            rhs);
186                 let mut p = Parser::new(cx.parse_sess(), cx.cfg(), box trncbr);
187                 p.check_unknown_macro_variable();
188                 // Let the context choose how to interpret the result.
189                 // Weird, but useful for X-macros.
190                 return box ParserAnyMacro {
191                     parser: RefCell::new(p),
192                 } as Box<MacResult+'cx>
193               }
194               Failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo {
195                 best_fail_spot = sp;
196                 best_fail_msg = (*msg).clone();
197               },
198               Error(sp, ref msg) => cx.span_fatal(sp, &msg[])
199             }
200           }
201           _ => cx.bug("non-matcher found in parsed lhses")
202         }
203     }
204     cx.span_fatal(best_fail_spot, &best_fail_msg[]);
205 }
206
207 // Note that macro-by-example's input is also matched against a token tree:
208 //                   $( $lhs:tt => $rhs:tt );+
209 //
210 // Holy self-referential!
211
212 /// Converts a `macro_rules!` invocation into a syntax extension.
213 pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
214                     def: &ast::MacroDef) -> SyntaxExtension {
215
216     let lhs_nm =  gensym_ident("lhs");
217     let rhs_nm =  gensym_ident("rhs");
218
219     // The pattern that macro_rules matches.
220     // The grammar for macro_rules! is:
221     // $( $lhs:tt => $rhs:tt );+
222     // ...quasiquoting this would be nice.
223     // These spans won't matter, anyways
224     let match_lhs_tok = MatchNt(lhs_nm, special_idents::tt, token::Plain, token::Plain);
225     let match_rhs_tok = MatchNt(rhs_nm, special_idents::tt, token::Plain, token::Plain);
226     let argument_gram = vec!(
227         TtSequence(DUMMY_SP,
228                    Rc::new(ast::SequenceRepetition {
229                        tts: vec![
230                            TtToken(DUMMY_SP, match_lhs_tok),
231                            TtToken(DUMMY_SP, token::FatArrow),
232                            TtToken(DUMMY_SP, match_rhs_tok)],
233                        separator: Some(token::Semi),
234                        op: ast::OneOrMore,
235                        num_captures: 2
236                    })),
237         //to phase into semicolon-termination instead of
238         //semicolon-separation
239         TtSequence(DUMMY_SP,
240                    Rc::new(ast::SequenceRepetition {
241                        tts: vec![TtToken(DUMMY_SP, token::Semi)],
242                        separator: None,
243                        op: ast::ZeroOrMore,
244                        num_captures: 0
245                    })));
246
247
248     // Parse the macro_rules! invocation (`none` is for no interpolations):
249     let arg_reader = new_tt_reader(&cx.parse_sess().span_diagnostic,
250                                    None,
251                                    None,
252                                    def.body.clone());
253     let argument_map = parse_or_else(cx.parse_sess(),
254                                      cx.cfg(),
255                                      arg_reader,
256                                      argument_gram);
257
258     // Extract the arguments:
259     let lhses = match *argument_map[lhs_nm] {
260         MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
261         _ => cx.span_bug(def.span, "wrong-structured lhs")
262     };
263
264     for lhs in lhses.iter() {
265         check_lhs_nt_follows(cx, &**lhs, def.span);
266     }
267
268     let rhses = match *argument_map[rhs_nm] {
269         MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
270         _ => cx.span_bug(def.span, "wrong-structured rhs")
271     };
272
273     let exp = box MacroRulesMacroExpander {
274         name: def.ident,
275         imported_from: def.imported_from,
276         lhses: lhses,
277         rhses: rhses,
278     };
279
280     NormalTT(exp, Some(def.span))
281 }
282
283 fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &NamedMatch, sp: Span) {
284     // lhs is going to be like MatchedNonterminal(NtTT(TtDelimited(...))), where the entire lhs is
285     // those tts. Or, it can be a "bare sequence", not wrapped in parens.
286     match lhs {
287         &MatchedNonterminal(NtTT(ref inner)) => match &**inner {
288             &TtDelimited(_, ref tts) => {
289                 check_matcher(cx, tts.tts.iter(), &Eof);
290             },
291             tt @ &TtSequence(..) => {
292                 check_matcher(cx, Some(tt).into_iter(), &Eof);
293             },
294             _ => cx.span_bug(sp, "wrong-structured lhs for follow check (didn't find \
295             a TtDelimited or TtSequence)")
296         },
297         _ => cx.span_bug(sp, "wrong-structured lhs for follow check (didn't find a \
298            MatchedNonterminal)")
299     };
300     // we don't abort on errors on rejection, the driver will do that for us
301     // after parsing/expansion. we can report every error in every macro this way.
302 }
303
304 // returns the last token that was checked, for TtSequence. this gets used later on.
305 fn check_matcher<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token)
306 -> Option<(Span, Token)> where I: Iterator<Item=&'a TokenTree> {
307     use print::pprust::token_to_string;
308
309     let mut last = None;
310
311     // 2. For each token T in M:
312     let mut tokens = matcher.peekable();
313     while let Some(token) = tokens.next() {
314         last = match *token {
315             TtToken(sp, MatchNt(ref name, ref frag_spec, _, _)) => {
316                 // ii. If T is a simple NT, look ahead to the next token T' in
317                 // M.
318                 let next_token = match tokens.peek() {
319                     // If T' closes a complex NT, replace T' with F
320                     Some(&&TtToken(_, CloseDelim(_))) => follow.clone(),
321                     Some(&&TtToken(_, ref tok)) => tok.clone(),
322                     Some(&&TtSequence(sp, _)) => {
323                         cx.span_err(sp,
324                                     format!("`${0}:{1}` is followed by a \
325                                              sequence repetition, which is not \
326                                              allowed for `{1}` fragments",
327                                             name.as_str(), frag_spec.as_str())
328                                         .as_slice());
329                         Eof
330                     },
331                     // die next iteration
332                     Some(&&TtDelimited(_, ref delim)) => delim.close_token(),
333                     // else, we're at the end of the macro or sequence
334                     None => follow.clone()
335                 };
336
337                 let tok = if let TtToken(_, ref tok) = *token { tok } else { unreachable!() };
338                 // If T' is in the set FOLLOW(NT), continue. Else, reject.
339                 match &next_token {
340                     &Eof => return Some((sp, tok.clone())),
341                     _ if is_in_follow(cx, &next_token, frag_spec.as_str()) => continue,
342                     next => {
343                         cx.span_err(sp, format!("`${0}:{1}` is followed by `{2}`, which \
344                                                  is not allowed for `{1}` fragments",
345                                                  name.as_str(), frag_spec.as_str(),
346                                                  token_to_string(next)).as_slice());
347                         continue
348                     },
349                 }
350             },
351             TtSequence(sp, ref seq) => {
352                 // iii. Else, T is a complex NT.
353                 match seq.separator {
354                     // If T has the form $(...)U+ or $(...)U* for some token U,
355                     // run the algorithm on the contents with F set to U. If it
356                     // accepts, continue, else, reject.
357                     Some(ref u) => {
358                         let last = check_matcher(cx, seq.tts.iter(), u);
359                         match last {
360                             // Since the delimiter isn't required after the last
361                             // repetition, make sure that the *next* token is
362                             // sane. This doesn't actually compute the FIRST of
363                             // the rest of the matcher yet, it only considers
364                             // single tokens and simple NTs. This is imprecise,
365                             // but conservatively correct.
366                             Some((span, tok)) => {
367                                 let fol = match tokens.peek() {
368                                     Some(&&TtToken(_, ref tok)) => tok.clone(),
369                                     Some(&&TtDelimited(_, ref delim)) => delim.close_token(),
370                                     Some(_) => {
371                                         cx.span_err(sp, "sequence repetition followed by \
372                                                 another sequence repetition, which is not allowed");
373                                         Eof
374                                     },
375                                     None => Eof
376                                 };
377                                 check_matcher(cx, Some(&TtToken(span, tok.clone())).into_iter(),
378                                               &fol)
379                             },
380                             None => last,
381                         }
382                     },
383                     // If T has the form $(...)+ or $(...)*, run the algorithm
384                     // on the contents with F set to the token following the
385                     // sequence. If it accepts, continue, else, reject.
386                     None => {
387                         let fol = match tokens.peek() {
388                             Some(&&TtToken(_, ref tok)) => tok.clone(),
389                             Some(&&TtDelimited(_, ref delim)) => delim.close_token(),
390                             Some(_) => {
391                                 cx.span_err(sp, "sequence repetition followed by another \
392                                              sequence repetition, which is not allowed");
393                                 Eof
394                             },
395                             None => Eof
396                         };
397                         check_matcher(cx, seq.tts.iter(), &fol)
398                     }
399                 }
400             },
401             TtToken(..) => {
402                 // i. If T is not an NT, continue.
403                 continue
404             },
405             TtDelimited(_, ref tts) => {
406                 // if we don't pass in that close delimiter, we'll incorrectly consider the matcher
407                 // `{ $foo:ty }` as having a follow that isn't `RBrace`
408                 check_matcher(cx, tts.tts.iter(), &tts.close_token())
409             }
410         }
411     }
412     last
413 }
414
415 fn is_in_follow(cx: &ExtCtxt, tok: &Token, frag: &str) -> bool {
416     if let &CloseDelim(_) = tok {
417         return true;
418     }
419
420     match frag {
421         "item" => {
422             // since items *must* be followed by either a `;` or a `}`, we can
423             // accept anything after them
424             true
425         },
426         "block" => {
427             // anything can follow block, the braces provide a easy boundary to
428             // maintain
429             true
430         },
431         "stmt" | "expr"  => {
432             match *tok {
433                 FatArrow | Comma | Semi => true,
434                 _ => false
435             }
436         },
437         "pat" => {
438             match *tok {
439                 FatArrow | Comma | Eq => true,
440                 _ => false
441             }
442         },
443         "path" | "ty" => {
444             match *tok {
445                 Comma | FatArrow | Colon | Eq | Gt => true,
446                 Ident(i, _) if i.as_str() == "as" => true,
447                 _ => false
448             }
449         },
450         "ident" => {
451             // being a single token, idents are harmless
452             true
453         },
454         "meta" | "tt" => {
455             // being either a single token or a delimited sequence, tt is
456             // harmless
457             true
458         },
459         _ => cx.bug(format!("unrecognized builtin nonterminal {}",
460                             frag).as_slice()),
461     }
462 }