]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/tt/macro_rules.rs
rollup merge of #20482: kmcallister/macro-reform
[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, 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;
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     imported_from: Option<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: Ident,
138                           imported_from: Option<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 mut arg_rdr = new_tt_reader(&cx.parse_sess().span_diagnostic,
162                                             None,
163                                             None,
164                                             arg.iter()
165                                                .map(|x| (*x).clone())
166                                                .collect());
167             arg_rdr.desugar_doc_comments = 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 p = Parser::new(cx.parse_sess(), cx.cfg(), box trncbr);
187                 // Let the context choose how to interpret the result.
188                 // Weird, but useful for X-macros.
189                 return box ParserAnyMacro {
190                     parser: RefCell::new(p),
191                 } as Box<MacResult+'cx>
192               }
193               Failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo {
194                 best_fail_spot = sp;
195                 best_fail_msg = (*msg).clone();
196               },
197               Error(sp, ref msg) => cx.span_fatal(sp, msg[])
198             }
199           }
200           _ => cx.bug("non-matcher found in parsed lhses")
201         }
202     }
203     cx.span_fatal(best_fail_spot, best_fail_msg[]);
204 }
205
206 // Note that macro-by-example's input is also matched against a token tree:
207 //                   $( $lhs:tt => $rhs:tt );+
208 //
209 // Holy self-referential!
210
211 /// Converts a `macro_rules!` invocation into a syntax extension.
212 pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
213                     def: &ast::MacroDef) -> SyntaxExtension {
214
215     let lhs_nm =  gensym_ident("lhs");
216     let rhs_nm =  gensym_ident("rhs");
217
218     // The pattern that macro_rules matches.
219     // The grammar for macro_rules! is:
220     // $( $lhs:tt => $rhs:tt );+
221     // ...quasiquoting this would be nice.
222     // These spans won't matter, anyways
223     let match_lhs_tok = MatchNt(lhs_nm, special_idents::tt, token::Plain, token::Plain);
224     let match_rhs_tok = MatchNt(rhs_nm, special_idents::tt, token::Plain, token::Plain);
225     let argument_gram = vec!(
226         TtSequence(DUMMY_SP,
227                    Rc::new(ast::SequenceRepetition {
228                        tts: vec![
229                            TtToken(DUMMY_SP, match_lhs_tok),
230                            TtToken(DUMMY_SP, token::FatArrow),
231                            TtToken(DUMMY_SP, match_rhs_tok)],
232                        separator: Some(token::Semi),
233                        op: ast::OneOrMore,
234                        num_captures: 2
235                    })),
236         //to phase into semicolon-termination instead of
237         //semicolon-separation
238         TtSequence(DUMMY_SP,
239                    Rc::new(ast::SequenceRepetition {
240                        tts: vec![TtToken(DUMMY_SP, token::Semi)],
241                        separator: None,
242                        op: ast::ZeroOrMore,
243                        num_captures: 0
244                    })));
245
246
247     // Parse the macro_rules! invocation (`none` is for no interpolations):
248     let arg_reader = new_tt_reader(&cx.parse_sess().span_diagnostic,
249                                    None,
250                                    None,
251                                    def.body.clone());
252     let argument_map = parse_or_else(cx.parse_sess(),
253                                      cx.cfg(),
254                                      arg_reader,
255                                      argument_gram);
256
257     // Extract the arguments:
258     let lhses = match *argument_map[lhs_nm] {
259         MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
260         _ => cx.span_bug(def.span, "wrong-structured lhs")
261     };
262
263     let rhses = match *argument_map[rhs_nm] {
264         MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
265         _ => cx.span_bug(def.span, "wrong-structured rhs")
266     };
267
268     let exp = box MacroRulesMacroExpander {
269         name: def.ident,
270         imported_from: def.imported_from,
271         lhses: lhses,
272         rhses: rhses,
273     };
274
275     NormalTT(exp, Some(def.span))
276 }