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.
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.
11 use ast::{Ident, Matcher_, Matcher, MatchTok, MatchNonterminal, MatchSeq};
14 use codemap::{Span, Spanned, DUMMY_SP};
15 use ext::base::{AnyMacro, ExtCtxt, MacResult, MRAny, MRDef, MacroDef};
16 use ext::base::{NormalTT, MacroExpander};
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};
28 use std::cell::RefCell;
29 use util::small_vector::SmallVector;
31 struct ParserAnyMacro {
32 parser: RefCell<Parser>,
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 {
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 \
52 let span = parser.get().span;
53 parser.get().span_err(span, msg);
58 impl AnyMacro for ParserAnyMacro {
59 fn make_expr(&self) -> @ast::Expr {
61 let mut parser = self.parser.borrow_mut();
62 parser.get().parse_expr()
64 self.ensure_complete_parse(true);
67 fn make_items(&self) -> SmallVector<@ast::Item> {
68 let mut ret = SmallVector::zero();
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),
77 self.ensure_complete_parse(false);
80 fn make_stmt(&self) -> @ast::Stmt {
82 let mut parser = self.parser.borrow_mut();
83 let attrs = parser.get().parse_outer_attributes();
84 parser.get().parse_stmt(attrs)
86 self.ensure_complete_parse(true);
91 struct MacroRulesMacroExpander {
93 lhses: @~[@NamedMatch],
94 rhses: @~[@NamedMatch],
97 impl MacroExpander for MacroRulesMacroExpander {
101 arg: &[ast::TokenTree])
103 generic_extension(cx, sp, self.name, arg, *self.lhses, *self.rhses)
107 // Given `lhses` and `rhses`, this is the new macro we create
108 fn generic_extension(cx: &ExtCtxt,
111 arg: &[ast::TokenTree],
112 lhses: &[@NamedMatch],
113 rhses: &[@NamedMatch])
115 if cx.trace_macros() {
116 let interned_name = token::get_ident(name.name);
117 println!("{}! \\{ {} \\}",
119 print::pprust::tt_to_str(&TTDelim(@arg.to_owned()),
120 get_ident_interner()));
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";
127 let s_d = cx.parse_sess().span_diagnostic;
129 for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers
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)) => {
140 // cut off delimiters; don't parse 'em
141 TTDelim(ref tts) => {
142 (*tts).slice(1u,(*tts).len()-1u).to_owned()
145 sp, "macro rhs must be delimited")
148 _ => cx.span_bug(sp, "bad thing in rhs")
150 // rhs has holes ( `$id` and `$(...)` that need filled)
151 let trncbr = new_tt_reader(s_d, Some(named_matches),
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),
160 Failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo {
162 best_fail_msg = (*msg).clone();
164 Error(sp, ref msg) => cx.span_fatal(sp, (*msg))
167 _ => cx.bug("non-matcher found in parsed lhses")
170 cx.span_fatal(best_fail_spot, best_fail_msg);
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,
179 arg: ~[ast::TokenTree])
181 // these spans won't matter, anyways
182 fn ms(m: Matcher_) -> Matcher {
189 let lhs_nm = gensym_ident("lhs");
190 let rhs_nm = gensym_ident("rhs");
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 = ~[
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))];
207 // Parse the macro_rules! invocation (`none` is for no interpolations):
208 let arg_reader = new_tt_reader(cx.parse_sess().span_diagnostic,
211 let argument_map = parse_or_else(cx.parse_sess(),
213 arg_reader as @Reader,
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")
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")
227 let exp = ~MacroRulesMacroExpander {
233 return MRDef(MacroDef {
234 name: ident_to_str(&name),
235 ext: NormalTT(exp, Some(sp))