]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/tt/macro_rules.rs
rollup merge of #20608: nikomatsakis/assoc-types-method-dispatch
[rust.git] / src / libsyntax / ext / tt / macro_rules.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::{Ident, TtDelimited, TtSequence, TtToken};
12 use ast;
13 use codemap::{Span, DUMMY_SP};
14 use ext::base::{ExtCtxt, MacResult, MacroDef};
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;
20 use parse::parser::Parser;
21 use parse::attr::ParserAttr;
22 use parse::token::{special_idents, gensym_ident};
23 use parse::token::{MatchNt, NtTT};
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: Ident,
113     lhses: Vec<Rc<NamedMatch>>,
114     rhses: Vec<Rc<NamedMatch>>,
115 }
116
117 impl TTMacroExpander for MacroRulesMacroExpander {
118     fn expand<'cx>(&self,
119                    cx: &'cx mut ExtCtxt,
120                    sp: Span,
121                    arg: &[ast::TokenTree])
122                    -> Box<MacResult+'cx> {
123         generic_extension(cx,
124                           sp,
125                           self.name,
126                           arg,
127                           self.lhses[],
128                           self.rhses[])
129     }
130 }
131
132 struct MacroRulesDefiner {
133     def: Option<MacroDef>
134 }
135 impl MacResult for MacroRulesDefiner {
136     fn make_def(&mut self) -> Option<MacroDef> {
137         Some(self.def.take().expect("empty MacroRulesDefiner"))
138     }
139 }
140
141 /// Given `lhses` and `rhses`, this is the new macro we create
142 fn generic_extension<'cx>(cx: &'cx ExtCtxt,
143                           sp: Span,
144                           name: Ident,
145                           arg: &[ast::TokenTree],
146                           lhses: &[Rc<NamedMatch>],
147                           rhses: &[Rc<NamedMatch>])
148                           -> Box<MacResult+'cx> {
149     if cx.trace_macros() {
150         println!("{}! {{ {} }}",
151                  token::get_ident(name),
152                  print::pprust::tts_to_string(arg));
153     }
154
155     // Which arm's failure should we report? (the one furthest along)
156     let mut best_fail_spot = DUMMY_SP;
157     let mut best_fail_msg = "internal error: ran no matchers".to_string();
158
159     for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers
160         match **lhs {
161           MatchedNonterminal(NtTT(ref lhs_tt)) => {
162             let lhs_tt = match **lhs_tt {
163                 TtDelimited(_, ref delim) => delim.tts[],
164                 _ => cx.span_fatal(sp, "malformed macro lhs")
165             };
166             // `None` is because we're not interpolating
167             let mut arg_rdr = new_tt_reader(&cx.parse_sess().span_diagnostic,
168                                             None,
169                                             arg.iter()
170                                                .map(|x| (*x).clone())
171                                                .collect());
172             arg_rdr.desugar_doc_comments = true;
173             match parse(cx.parse_sess(), cx.cfg(), arg_rdr, lhs_tt) {
174               Success(named_matches) => {
175                 let rhs = match *rhses[i] {
176                     // okay, what's your transcriber?
177                     MatchedNonterminal(NtTT(ref tt)) => {
178                         match **tt {
179                             // ignore delimiters
180                             TtDelimited(_, ref delimed) => delimed.tts.clone(),
181                             _ => cx.span_fatal(sp, "macro rhs must be delimited"),
182                         }
183                     },
184                     _ => cx.span_bug(sp, "bad thing in rhs")
185                 };
186                 // rhs has holes ( `$id` and `$(...)` that need filled)
187                 let trncbr = new_tt_reader(&cx.parse_sess().span_diagnostic,
188                                            Some(named_matches),
189                                            rhs);
190                 let p = Parser::new(cx.parse_sess(), cx.cfg(), box trncbr);
191                 // Let the context choose how to interpret the result.
192                 // Weird, but useful for X-macros.
193                 return box ParserAnyMacro {
194                     parser: RefCell::new(p),
195                 } as Box<MacResult+'cx>
196               }
197               Failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo {
198                 best_fail_spot = sp;
199                 best_fail_msg = (*msg).clone();
200               },
201               Error(sp, ref msg) => cx.span_fatal(sp, msg[])
202             }
203           }
204           _ => cx.bug("non-matcher found in parsed lhses")
205         }
206     }
207     cx.span_fatal(best_fail_spot, best_fail_msg[]);
208 }
209
210 // Note that macro-by-example's input is also matched against a token tree:
211 //                   $( $lhs:tt => $rhs:tt );+
212 //
213 // Holy self-referential!
214
215 /// This procedure performs the expansion of the
216 /// macro_rules! macro. It parses the RHS and adds
217 /// an extension to the current context.
218 pub fn add_new_extension<'cx>(cx: &'cx mut ExtCtxt,
219                               sp: Span,
220                               name: Ident,
221                               arg: Vec<ast::TokenTree> )
222                               -> Box<MacResult+'cx> {
223
224     let lhs_nm =  gensym_ident("lhs");
225     let rhs_nm =  gensym_ident("rhs");
226
227     // The pattern that macro_rules matches.
228     // The grammar for macro_rules! is:
229     // $( $lhs:tt => $rhs:tt );+
230     // ...quasiquoting this would be nice.
231     // These spans won't matter, anyways
232     let match_lhs_tok = MatchNt(lhs_nm, special_idents::tt, token::Plain, token::Plain);
233     let match_rhs_tok = MatchNt(rhs_nm, special_idents::tt, token::Plain, token::Plain);
234     let argument_gram = vec!(
235         TtSequence(DUMMY_SP,
236                    Rc::new(ast::SequenceRepetition {
237                        tts: vec![
238                            TtToken(DUMMY_SP, match_lhs_tok),
239                            TtToken(DUMMY_SP, token::FatArrow),
240                            TtToken(DUMMY_SP, match_rhs_tok)],
241                        separator: Some(token::Semi),
242                        op: ast::OneOrMore,
243                        num_captures: 2
244                    })),
245         //to phase into semicolon-termination instead of
246         //semicolon-separation
247         TtSequence(DUMMY_SP,
248                    Rc::new(ast::SequenceRepetition {
249                        tts: vec![TtToken(DUMMY_SP, token::Semi)],
250                        separator: None,
251                        op: ast::ZeroOrMore,
252                        num_captures: 0
253                    })));
254
255
256     // Parse the macro_rules! invocation (`none` is for no interpolations):
257     let arg_reader = new_tt_reader(&cx.parse_sess().span_diagnostic,
258                                    None,
259                                    arg.clone());
260     let argument_map = parse_or_else(cx.parse_sess(),
261                                      cx.cfg(),
262                                      arg_reader,
263                                      argument_gram);
264
265     // Extract the arguments:
266     let lhses = match *argument_map[lhs_nm] {
267         MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
268         _ => cx.span_bug(sp, "wrong-structured lhs")
269     };
270
271     let rhses = match *argument_map[rhs_nm] {
272         MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
273         _ => cx.span_bug(sp, "wrong-structured rhs")
274     };
275
276     let exp = box MacroRulesMacroExpander {
277         name: name,
278         lhses: lhses,
279         rhses: rhses,
280     };
281
282     box MacroRulesDefiner {
283         def: Some(MacroDef {
284             name: token::get_ident(name).to_string(),
285             ext: NormalTT(exp, Some(sp))
286         })
287     } as Box<MacResult+'cx>
288 }