]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/tt/macro_rules.rs
rollup merge of #18407 : thestinger/arena
[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, Matcher_, Matcher, MatchTok, MatchNonterminal, MatchSeq, TtDelimited};
12 use ast;
13 use codemap::{Span, Spanned, 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::{NtMatchers, 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.as_slice());
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.as_slice(),
128                           self.rhses.as_slice())
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(NtMatchers(ref mtcs)) => {
162             // `None` is because we're not interpolating
163             let arg_rdr = new_tt_reader(&cx.parse_sess().span_diagnostic,
164                                         None,
165                                         arg.iter()
166                                            .map(|x| (*x).clone())
167                                            .collect());
168             match parse(cx.parse_sess(), cx.cfg(), arg_rdr, mtcs.as_slice()) {
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) => {
176                                 let (_, ref tts, _) = **delimed;
177                                 tts.clone()
178                             },
179                             _ => cx.span_fatal(sp, "macro rhs must be delimited"),
180                         }
181                     },
182                     _ => cx.span_bug(sp, "bad thing in rhs")
183                 };
184                 // rhs has holes ( `$id` and `$(...)` that need filled)
185                 let trncbr = new_tt_reader(&cx.parse_sess().span_diagnostic,
186                                            Some(named_matches),
187                                            rhs);
188                 let p = Parser::new(cx.parse_sess(), cx.cfg(), box trncbr);
189                 // Let the context choose how to interpret the result.
190                 // Weird, but useful for X-macros.
191                 return box ParserAnyMacro {
192                     parser: RefCell::new(p),
193                 } as Box<MacResult+'cx>
194               }
195               Failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo {
196                 best_fail_spot = sp;
197                 best_fail_msg = (*msg).clone();
198               },
199               Error(sp, ref msg) => cx.span_fatal(sp, msg.as_slice())
200             }
201           }
202           _ => cx.bug("non-matcher found in parsed lhses")
203         }
204     }
205     cx.span_fatal(best_fail_spot, best_fail_msg.as_slice());
206 }
207
208 /// This procedure performs the expansion of the
209 /// macro_rules! macro. It parses the RHS and adds
210 /// an extension to the current context.
211 pub fn add_new_extension<'cx>(cx: &'cx mut ExtCtxt,
212                               sp: Span,
213                               name: Ident,
214                               arg: Vec<ast::TokenTree> )
215                               -> Box<MacResult+'cx> {
216     // these spans won't matter, anyways
217     fn ms(m: Matcher_) -> Matcher {
218         Spanned {
219             node: m.clone(),
220             span: DUMMY_SP
221         }
222     }
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:mtcs => $rhs:tt );+
230     // ...quasiquoting this would be nice.
231     let argument_gram = vec!(
232         ms(MatchSeq(vec!(
233             ms(MatchNonterminal(lhs_nm, special_idents::matchers, 0u)),
234             ms(MatchTok(token::FatArrow)),
235             ms(MatchNonterminal(rhs_nm, special_idents::tt, 1u))),
236                                 Some(token::Semi), ast::OneOrMore, 0u, 2u)),
237         //to phase into semicolon-termination instead of
238         //semicolon-separation
239         ms(MatchSeq(vec!(ms(MatchTok(token::Semi))), None,
240                             ast::ZeroOrMore, 2u, 2u)));
241
242
243     // Parse the macro_rules! invocation (`none` is for no interpolations):
244     let arg_reader = new_tt_reader(&cx.parse_sess().span_diagnostic,
245                                    None,
246                                    arg.clone());
247     let argument_map = parse_or_else(cx.parse_sess(),
248                                      cx.cfg(),
249                                      arg_reader,
250                                      argument_gram);
251
252     // Extract the arguments:
253     let lhses = match *argument_map[lhs_nm] {
254         MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
255         _ => cx.span_bug(sp, "wrong-structured lhs")
256     };
257
258     let rhses = match *argument_map[rhs_nm] {
259         MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
260         _ => cx.span_bug(sp, "wrong-structured rhs")
261     };
262
263     let exp = box MacroRulesMacroExpander {
264         name: name,
265         lhses: lhses,
266         rhses: rhses,
267     };
268
269     box MacroRulesDefiner {
270         def: Some(MacroDef {
271             name: token::get_ident(name).to_string(),
272             ext: NormalTT(exp, Some(sp))
273         })
274     } as Box<MacResult+'cx>
275 }