]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/tt/macro_rules.rs
librustc: Fix merge fallout.
[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};
12 use ast::{TTDelim};
13 use ast;
14 use codemap::{Span, Spanned, DUMMY_SP};
15 use ext::base::{AnyMacro, ExtCtxt, MacResult, MRAny, MRDef, MacroDef};
16 use ext::base::{NormalTT, MacroExpander};
17 use ext::base;
18 use ext::tt::macro_parser::{Success, Error, Failure};
19 use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
20 use ext::tt::macro_parser::{parse, parse_or_else};
21 use parse::lexer::{new_tt_reader, Reader};
22 use parse::parser::Parser;
23 use parse::attr::ParserAttr;
24 use parse::token::{get_ident_interner, special_idents, gensym_ident, ident_to_str};
25 use parse::token::{FAT_ARROW, SEMI, NtMatchers, NtTT, EOF};
26 use parse::token;
27 use print;
28 use std::cell::RefCell;
29 use util::small_vector::SmallVector;
30
31 struct ParserAnyMacro {
32     parser: RefCell<Parser>,
33 }
34
35 impl ParserAnyMacro {
36     /// Make sure we don't have any tokens left to parse, so we don't
37     /// silently drop anything. `allow_semi` is so that "optional"
38     /// semilons at the end of normal expressions aren't complained
39     /// about e.g. the semicolon in `macro_rules! kapow( () => {
40     /// fail!(); } )` doesn't get picked up by .parse_expr(), but it's
41     /// allowed to be there.
42     fn ensure_complete_parse(&self, allow_semi: bool) {
43         let mut parser = self.parser.borrow_mut();
44         if allow_semi && parser.get().token == SEMI {
45             parser.get().bump()
46         }
47         if parser.get().token != EOF {
48             let token_str = parser.get().this_token_to_str();
49             let msg = format!("macro expansion ignores token `{}` and any \
50                                following",
51                               token_str);
52             let span = parser.get().span;
53             parser.get().span_err(span, msg);
54         }
55     }
56 }
57
58 impl AnyMacro for ParserAnyMacro {
59     fn make_expr(&self) -> @ast::Expr {
60         let ret = {
61             let mut parser = self.parser.borrow_mut();
62             parser.get().parse_expr()
63         };
64         self.ensure_complete_parse(true);
65         ret
66     }
67     fn make_items(&self) -> SmallVector<@ast::Item> {
68         let mut ret = SmallVector::zero();
69         loop {
70             let mut parser = self.parser.borrow_mut();
71             let attrs = parser.get().parse_outer_attributes();
72             match parser.get().parse_item(attrs) {
73                 Some(item) => ret.push(item),
74                 None => break
75             }
76         }
77         self.ensure_complete_parse(false);
78         ret
79     }
80     fn make_stmt(&self) -> @ast::Stmt {
81         let ret = {
82             let mut parser = self.parser.borrow_mut();
83             let attrs = parser.get().parse_outer_attributes();
84             parser.get().parse_stmt(attrs)
85         };
86         self.ensure_complete_parse(true);
87         ret
88     }
89 }
90
91 struct MacroRulesMacroExpander {
92     name: Ident,
93     lhses: @~[@NamedMatch],
94     rhses: @~[@NamedMatch],
95 }
96
97 impl MacroExpander for MacroRulesMacroExpander {
98     fn expand(&self,
99               cx: &mut ExtCtxt,
100               sp: Span,
101               arg: &[ast::TokenTree])
102               -> MacResult {
103         generic_extension(cx, sp, self.name, arg, *self.lhses, *self.rhses)
104     }
105 }
106
107 // Given `lhses` and `rhses`, this is the new macro we create
108 fn generic_extension(cx: &ExtCtxt,
109                      sp: Span,
110                      name: Ident,
111                      arg: &[ast::TokenTree],
112                      lhses: &[@NamedMatch],
113                      rhses: &[@NamedMatch])
114                      -> MacResult {
115     if cx.trace_macros() {
116         let interned_name = token::get_ident(name.name);
117         println!("{}! \\{ {} \\}",
118                  interned_name.get(),
119                  print::pprust::tt_to_str(&TTDelim(@arg.to_owned()),
120                                           get_ident_interner()));
121     }
122
123     // Which arm's failure should we report? (the one furthest along)
124     let mut best_fail_spot = DUMMY_SP;
125     let mut best_fail_msg = ~"internal error: ran no matchers";
126
127     let s_d = cx.parse_sess().span_diagnostic;
128
129     for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers
130         match **lhs {
131           MatchedNonterminal(NtMatchers(ref mtcs)) => {
132             // `none` is because we're not interpolating
133             let arg_rdr = new_tt_reader(s_d, None, arg.to_owned()) as @Reader;
134             match parse(cx.parse_sess(), cx.cfg(), arg_rdr, *mtcs) {
135               Success(named_matches) => {
136                 let rhs = match *rhses[i] {
137                     // okay, what's your transcriber?
138                     MatchedNonterminal(NtTT(tt)) => {
139                         match *tt {
140                             // cut off delimiters; don't parse 'em
141                             TTDelim(ref tts) => {
142                                 (*tts).slice(1u,(*tts).len()-1u).to_owned()
143                             }
144                             _ => cx.span_fatal(
145                                 sp, "macro rhs must be delimited")
146                         }
147                     },
148                     _ => cx.span_bug(sp, "bad thing in rhs")
149                 };
150                 // rhs has holes ( `$id` and `$(...)` that need filled)
151                 let trncbr = new_tt_reader(s_d, Some(named_matches),
152                                            rhs);
153                 let p = Parser(cx.parse_sess(), cx.cfg(), trncbr as @Reader);
154                 // Let the context choose how to interpret the result.
155                 // Weird, but useful for X-macros.
156                 return MRAny(@ParserAnyMacro {
157                     parser: RefCell::new(p),
158                 } as @AnyMacro)
159               }
160               Failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo {
161                 best_fail_spot = sp;
162                 best_fail_msg = (*msg).clone();
163               },
164               Error(sp, ref msg) => cx.span_fatal(sp, (*msg))
165             }
166           }
167           _ => cx.bug("non-matcher found in parsed lhses")
168         }
169     }
170     cx.span_fatal(best_fail_spot, best_fail_msg);
171 }
172
173 // this procedure performs the expansion of the
174 // macro_rules! macro. It parses the RHS and adds
175 // an extension to the current context.
176 pub fn add_new_extension(cx: &mut ExtCtxt,
177                          sp: Span,
178                          name: Ident,
179                          arg: ~[ast::TokenTree])
180                          -> base::MacResult {
181     // these spans won't matter, anyways
182     fn ms(m: Matcher_) -> Matcher {
183         Spanned {
184             node: m.clone(),
185             span: DUMMY_SP
186         }
187     }
188
189     let lhs_nm =  gensym_ident("lhs");
190     let rhs_nm =  gensym_ident("rhs");
191
192     // The pattern that macro_rules matches.
193     // The grammar for macro_rules! is:
194     // $( $lhs:mtcs => $rhs:tt );+
195     // ...quasiquoting this would be nice.
196     let argument_gram = ~[
197         ms(MatchSeq(~[
198             ms(MatchNonterminal(lhs_nm, special_idents::matchers, 0u)),
199             ms(MatchTok(FAT_ARROW)),
200             ms(MatchNonterminal(rhs_nm, special_idents::tt, 1u)),
201         ], Some(SEMI), false, 0u, 2u)),
202         //to phase into semicolon-termination instead of
203         //semicolon-separation
204         ms(MatchSeq(~[ms(MatchTok(SEMI))], None, true, 2u, 2u))];
205
206
207     // Parse the macro_rules! invocation (`none` is for no interpolations):
208     let arg_reader = new_tt_reader(cx.parse_sess().span_diagnostic,
209                                    None,
210                                    arg.clone());
211     let argument_map = parse_or_else(cx.parse_sess(),
212                                      cx.cfg(),
213                                      arg_reader as @Reader,
214                                      argument_gram);
215
216     // Extract the arguments:
217     let lhses = match **argument_map.get(&lhs_nm) {
218         MatchedSeq(ref s, _) => /* FIXME (#2543) */ @(*s).clone(),
219         _ => cx.span_bug(sp, "wrong-structured lhs")
220     };
221
222     let rhses = match **argument_map.get(&rhs_nm) {
223         MatchedSeq(ref s, _) => /* FIXME (#2543) */ @(*s).clone(),
224         _ => cx.span_bug(sp, "wrong-structured rhs")
225     };
226
227     let exp = ~MacroRulesMacroExpander {
228         name: name,
229         lhses: lhses,
230         rhses: rhses,
231     };
232
233     return MRDef(MacroDef {
234         name: ident_to_str(&name),
235         ext: NormalTT(exp, Some(sp))
236     });
237 }