(index<- )        ./libsyntax/ext/tt/macro_rules.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   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::{ExtCtxt, MacResult, 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;
  22  use parse::parser::Parser;
  23  use parse::attr::ParserAttr;
  24  use parse::token::{special_idents, gensym_ident};
  25  use parse::token::{FAT_ARROW, SEMI, NtMatchers, NtTT, EOF};
  26  use parse::token;
  27  use print;
  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      /// semilons 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_semibool) {
  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_str();
  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) -> Option<@ast::Expr> {
  62          let ret = self.parser.borrow_mut().parse_expr();
  63          self.ensure_complete_parse(true);
  64          Some(ret)
  65      }
  66      fn make_items(&self) -> Option<SmallVector<@ast::Item>> {
  67          let mut ret = SmallVector::zero();
  68          loop {
  69              let mut parser = self.parser.borrow_mut();
  70              let attrs = parser.parse_outer_attributes();
  71              match parser.parse_item(attrs) {
  72                  Some(item) => ret.push(item),
  73                  None => break
  74              }
  75          }
  76          self.ensure_complete_parse(false);
  77          Some(ret)
  78      }
  79      fn make_stmt(&self) -> Option<@ast::Stmt> {
  80          let attrs = self.parser.borrow_mut().parse_outer_attributes();
  81          let ret = self.parser.borrow_mut().parse_stmt(attrs);
  82          self.ensure_complete_parse(true);
  83          Some(ret)
  84      }
  85  }
  86  
  87  struct MacroRulesMacroExpander {
  88      name: Ident,
  89      lhses: Vec<Rc<NamedMatch>>,
  90      rhses: Vec<Rc<NamedMatch>>,
  91  }
  92  
  93  impl MacroExpander for MacroRulesMacroExpander {
  94      fn expand(&self,
  95                cx&mut ExtCtxt,
  96                spSpan,
  97                arg&[ast::TokenTree])
  98                -> Box<MacResult> {
  99          generic_extension(cx,
 100                            sp,
 101                            self.name,
 102                            arg,
 103                            self.lhses.as_slice(),
 104                            self.rhses.as_slice())
 105      }
 106  }
 107  
 108  struct MacroRulesDefiner {
 109      def: RefCell<Option<MacroDef>>
 110  }
 111  impl MacResult for MacroRulesDefiner {
 112      fn make_def(&self) -> Option<MacroDef> {
 113          Some(self.def.borrow_mut().take().expect("MacroRulesDefiner expanded twice"))
 114      }
 115  }
 116  
 117  // Given `lhses` and `rhses`, this is the new macro we create
 118  fn generic_extension(cx: &ExtCtxt,
 119                       spSpan,
 120                       nameIdent,
 121                       arg: &[ast::TokenTree],
 122                       lhses: &[Rc<NamedMatch>],
 123                       rhses: &[Rc<NamedMatch>])
 124                       -> Box<MacResult> {
 125      if cx.trace_macros() {
 126          println!("{}! \\{ {} \\}",
 127                   token::get_ident(name),
 128                   print::pprust::tt_to_str(&TTDelim(Rc::new(arg.iter()
 129                                                                .map(|x| (*x).clone())
 130                                                                .collect()))));
 131      }
 132  
 133      // Which arm's failure should we report? (the one furthest along)
 134      let mut best_fail_spot = DUMMY_SP;
 135      let mut best_fail_msg = "internal error: ran no matchers".to_strbuf();
 136  
 137      for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers
 138          match **lhs {
 139            MatchedNonterminal(NtMatchers(ref mtcs)) => {
 140              // `None` is because we're not interpolating
 141              let arg_rdr = new_tt_reader(&cx.parse_sess().span_diagnostic,
 142                                          None,
 143                                          arg.iter()
 144                                             .map(|x| (*x).clone())
 145                                             .collect());
 146              match parse(cx.parse_sess(), cx.cfg(), arg_rdr, mtcs.as_slice()) {
 147                Success(named_matches) => {
 148                  let rhs = match *rhses[i] {
 149                      // okay, what's your transcriber?
 150                      MatchedNonterminal(NtTT(tt)) => {
 151                          match *tt {
 152                              // cut off delimiters; don't parse 'em
 153                              TTDelim(ref tts) => {
 154                                  (*tts).slice(1u,(*tts).len()-1u)
 155                                        .iter()
 156                                        .map(|x| (*x).clone())
 157                                        .collect()
 158                              }
 159                              _ => cx.span_fatal(
 160                                  sp, "macro rhs must be delimited")
 161                          }
 162                      },
 163                      _ => cx.span_bug(sp, "bad thing in rhs")
 164                  };
 165                  // rhs has holes ( `$id` and `$(...)` that need filled)
 166                  let trncbr = new_tt_reader(&cx.parse_sess().span_diagnostic,
 167                                             Some(named_matches),
 168                                             rhs);
 169                  let p = Parser(cx.parse_sess(), cx.cfg(), box trncbr);
 170                  // Let the context choose how to interpret the result.
 171                  // Weird, but useful for X-macros.
 172                  return box ParserAnyMacro {
 173                      parser: RefCell::new(p),
 174                  } as Box<MacResult>
 175                }
 176                Failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo {
 177                  best_fail_spot = sp;
 178                  best_fail_msg = (*msg).clone();
 179                },
 180                Error(sp, ref msg) => cx.span_fatal(sp, msg.as_slice())
 181              }
 182            }
 183            _ => cx.bug("non-matcher found in parsed lhses")
 184          }
 185      }
 186      cx.span_fatal(best_fail_spot, best_fail_msg.as_slice());
 187  }
 188  
 189  // this procedure performs the expansion of the
 190  // macro_rules! macro. It parses the RHS and adds
 191  // an extension to the current context.
 192  pub fn add_new_extension(cx: &mut ExtCtxt,
 193                           spSpan,
 194                           nameIdent,
 195                           argVec<ast::TokenTree> )
 196                           -> Box<base::MacResult> {
 197      // these spans won't matter, anyways
 198      fn ms(mMatcher_) -> Matcher {
 199          Spanned {
 200              node: m.clone(),
 201              span: DUMMY_SP
 202          }
 203      }
 204  
 205      let lhs_nm =  gensym_ident("lhs");
 206      let rhs_nm =  gensym_ident("rhs");
 207  
 208      // The pattern that macro_rules matches.
 209      // The grammar for macro_rules! is:
 210      // $( $lhs:mtcs => $rhs:tt );+
 211      // ...quasiquoting this would be nice.
 212      let argument_gram = vec!(
 213          ms(MatchSeq(vec!(
 214              ms(MatchNonterminal(lhs_nm, special_idents::matchers, 0u)),
 215              ms(MatchTok(FAT_ARROW)),
 216              ms(MatchNonterminal(rhs_nm, special_idents::tt, 1u))), Some(SEMI), false, 0u, 2u)),
 217          //to phase into semicolon-termination instead of
 218          //semicolon-separation
 219          ms(MatchSeq(vec!(ms(MatchTok(SEMI))), None, true, 2u, 2u)));
 220  
 221  
 222      // Parse the macro_rules! invocation (`none` is for no interpolations):
 223      let arg_reader = new_tt_reader(&cx.parse_sess().span_diagnostic,
 224                                     None,
 225                                     arg.clone());
 226      let argument_map = parse_or_else(cx.parse_sess(),
 227                                       cx.cfg(),
 228                                       arg_reader,
 229                                       argument_gram);
 230  
 231      // Extract the arguments:
 232      let lhses = match **argument_map.get(&lhs_nm) {
 233          MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
 234          _ => cx.span_bug(sp, "wrong-structured lhs")
 235      };
 236  
 237      let rhses = match **argument_map.get(&rhs_nm) {
 238          MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
 239          _ => cx.span_bug(sp, "wrong-structured rhs")
 240      };
 241  
 242      let exp = box MacroRulesMacroExpander {
 243          name: name,
 244          lhses: lhses,
 245          rhses: rhses,
 246      };
 247  
 248      box MacroRulesDefiner {
 249          def: RefCell::new(Some(MacroDef {
 250              name: token::get_ident(name).to_str().to_strbuf(),
 251              ext: NormalTT(exp, Some(sp))
 252          }))
 253      } as Box<MacResult>
 254  }