]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/tt/macro_rules.rs
rollup merge of #17306 : scialex/fix-zsh
[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, TTDelim};
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::{FAT_ARROW, SEMI, NtMatchers, NtTT, EOF};
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     /// fail!(); } )` 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 == SEMI {
47             parser.bump()
48         }
49         if parser.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                 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                  "{",
153                  print::pprust::tt_to_string(&TTDelim(Rc::new(arg.iter()
154                                                               .map(|x| (*x).clone())
155                                                               .collect()))),
156                  "}");
157     }
158
159     // Which arm's failure should we report? (the one furthest along)
160     let mut best_fail_spot = DUMMY_SP;
161     let mut best_fail_msg = "internal error: ran no matchers".to_string();
162
163     for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers
164         match **lhs {
165           MatchedNonterminal(NtMatchers(ref mtcs)) => {
166             // `None` is because we're not interpolating
167             let arg_rdr = new_tt_reader(&cx.parse_sess().span_diagnostic,
168                                         None,
169                                         arg.iter()
170                                            .map(|x| (*x).clone())
171                                            .collect());
172             match parse(cx.parse_sess(), cx.cfg(), arg_rdr, mtcs.as_slice()) {
173               Success(named_matches) => {
174                 let rhs = match *rhses[i] {
175                     // okay, what's your transcriber?
176                     MatchedNonterminal(NtTT(ref tt)) => {
177                         match **tt {
178                             // cut off delimiters; don't parse 'em
179                             TTDelim(ref tts) => {
180                                 (*tts).slice(1u,(*tts).len()-1u)
181                                       .iter()
182                                       .map(|x| (*x).clone())
183                                       .collect()
184                             }
185                             _ => cx.span_fatal(
186                                 sp, "macro rhs must be delimited")
187                         }
188                     },
189                     _ => cx.span_bug(sp, "bad thing in rhs")
190                 };
191                 // rhs has holes ( `$id` and `$(...)` that need filled)
192                 let trncbr = new_tt_reader(&cx.parse_sess().span_diagnostic,
193                                            Some(named_matches),
194                                            rhs);
195                 let p = Parser::new(cx.parse_sess(), cx.cfg(), box trncbr);
196                 // Let the context choose how to interpret the result.
197                 // Weird, but useful for X-macros.
198                 return box ParserAnyMacro {
199                     parser: RefCell::new(p),
200                 } as Box<MacResult+'cx>
201               }
202               Failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo {
203                 best_fail_spot = sp;
204                 best_fail_msg = (*msg).clone();
205               },
206               Error(sp, ref msg) => cx.span_fatal(sp, msg.as_slice())
207             }
208           }
209           _ => cx.bug("non-matcher found in parsed lhses")
210         }
211     }
212     cx.span_fatal(best_fail_spot, best_fail_msg.as_slice());
213 }
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     // these spans won't matter, anyways
224     fn ms(m: Matcher_) -> Matcher {
225         Spanned {
226             node: m.clone(),
227             span: DUMMY_SP
228         }
229     }
230
231     let lhs_nm =  gensym_ident("lhs");
232     let rhs_nm =  gensym_ident("rhs");
233
234     // The pattern that macro_rules matches.
235     // The grammar for macro_rules! is:
236     // $( $lhs:mtcs => $rhs:tt );+
237     // ...quasiquoting this would be nice.
238     let argument_gram = vec!(
239         ms(MatchSeq(vec!(
240             ms(MatchNonterminal(lhs_nm, special_idents::matchers, 0u)),
241             ms(MatchTok(FAT_ARROW)),
242             ms(MatchNonterminal(rhs_nm, special_idents::tt, 1u))), Some(SEMI), false, 0u, 2u)),
243         //to phase into semicolon-termination instead of
244         //semicolon-separation
245         ms(MatchSeq(vec!(ms(MatchTok(SEMI))), None, true, 2u, 2u)));
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                                    arg.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.get(&lhs_nm) {
259         MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
260         _ => cx.span_bug(sp, "wrong-structured lhs")
261     };
262
263     let rhses = match **argument_map.get(&rhs_nm) {
264         MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
265         _ => cx.span_bug(sp, "wrong-structured rhs")
266     };
267
268     let exp = box MacroRulesMacroExpander {
269         name: name,
270         lhses: lhses,
271         rhses: rhses,
272     };
273
274     box MacroRulesDefiner {
275         def: Some(MacroDef {
276             name: token::get_ident(name).to_string(),
277             ext: NormalTT(exp, Some(sp))
278         })
279     } as Box<MacResult+'cx>
280 }