(index<- )        ./libsyntax/ext/source_util.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-2013 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;
  12  use codemap;
  13  use codemap::{Pos, Span};
  14  use codemap::{ExpnInfo, NameAndSpan};
  15  use ext::base::*;
  16  use ext::base;
  17  use ext::build::AstBuilder;
  18  use parse;
  19  use parse::token;
  20  use print::pprust;
  21  
  22  use std::io::File;
  23  use std::rc::Rc;
  24  use std::str;
  25  
  26  // These macros all relate to the file system; they either return
  27  // the column/row/filename of the expression, or they include
  28  // a given file into the current one.
  29  
  30  /* line!(): expands to the current line number */
  31  pub fn expand_line(cx: &mut ExtCtxt, spSpan, tts: &[ast::TokenTree])
  32                     -> Box<base::MacResult> {
  33      base::check_zero_tts(cx, sp, tts, "line!");
  34  
  35      let topmost = topmost_expn_info(cx.backtrace().unwrap());
  36      let loc = cx.codemap().lookup_char_pos(topmost.call_site.lo);
  37  
  38      base::MacExpr::new(cx.expr_uint(topmost.call_site, loc.line))
  39  }
  40  
  41  /* col!(): expands to the current column number */
  42  pub fn expand_col(cx: &mut ExtCtxt, spSpan, tts: &[ast::TokenTree])
  43                    -> Box<base::MacResult> {
  44      base::check_zero_tts(cx, sp, tts, "col!");
  45  
  46      let topmost = topmost_expn_info(cx.backtrace().unwrap());
  47      let loc = cx.codemap().lookup_char_pos(topmost.call_site.lo);
  48      base::MacExpr::new(cx.expr_uint(topmost.call_site, loc.col.to_uint()))
  49  }
  50  
  51  /* file!(): expands to the current filename */
  52  /* The filemap (`loc.file`) contains a bunch more information we could spit
  53   * out if we wanted. */
  54  pub fn expand_file(cx: &mut ExtCtxt, spSpan, tts: &[ast::TokenTree])
  55                     -> Box<base::MacResult> {
  56      base::check_zero_tts(cx, sp, tts, "file!");
  57  
  58      let topmost = topmost_expn_info(cx.backtrace().unwrap());
  59      let loc = cx.codemap().lookup_char_pos(topmost.call_site.lo);
  60      let filename = token::intern_and_get_ident(loc.file.name.as_slice());
  61      base::MacExpr::new(cx.expr_str(topmost.call_site, filename))
  62  }
  63  
  64  pub fn expand_stringify(cx: &mut ExtCtxt, spSpan, tts: &[ast::TokenTree])
  65                          -> Box<base::MacResult> {
  66      let s = pprust::tts_to_str(tts);
  67      base::MacExpr::new(cx.expr_str(sp,
  68                                     token::intern_and_get_ident(s.as_slice())))
  69  }
  70  
  71  pub fn expand_mod(cx: &mut ExtCtxt, spSpan, tts: &[ast::TokenTree])
  72                    -> Box<base::MacResult> {
  73      base::check_zero_tts(cx, sp, tts, "module_path!");
  74      let string = cx.mod_path()
  75                     .iter()
  76                     .map(|x| token::get_ident(*x).get().to_strbuf())
  77                     .collect::<Vec<StrBuf>>()
  78                     .connect("::");
  79      base::MacExpr::new(cx.expr_str(sp, token::intern_and_get_ident(string)))
  80  }
  81  
  82  // include! : parse the given file as an expr
  83  // This is generally a bad idea because it's going to behave
  84  // unhygienically.
  85  pub fn expand_include(cx: &mut ExtCtxt, spSpan, tts: &[ast::TokenTree])
  86                        -> Box<base::MacResult> {
  87      let file = match get_single_str_from_tts(cx, sp, tts, "include!") {
  88          Some(f) => f,
  89          None => return DummyResult::expr(sp),
  90      };
  91      // The file will be added to the code map by the parser
  92      let mut p =
  93          parse::new_sub_parser_from_file(cx.parse_sess(),
  94                                          cx.cfg(),
  95                                          &res_rel_file(cx,
  96                                                        sp,
  97                                                        &Path::new(file)),
  98                                          sp);
  99      base::MacExpr::new(p.parse_expr())
 100  }
 101  
 102  // include_str! : read the given file, insert it as a literal string expr
 103  pub fn expand_include_str(cx: &mut ExtCtxt, spSpan, tts: &[ast::TokenTree])
 104                            -> Box<base::MacResult> {
 105      let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") {
 106          Some(f) => f,
 107          None => return DummyResult::expr(sp)
 108      };
 109      let file = res_rel_file(cx, sp, &Path::new(file));
 110      let bytes = match File::open(&file).read_to_end() {
 111          Err(e) => {
 112              cx.span_err(sp, format!("couldn't read {}{}", file.display(), e));
 113              return DummyResult::expr(sp);
 114          }
 115          Ok(bytes) => bytes,
 116      };
 117      match str::from_utf8(bytes.as_slice()) {
 118          Some(src) => {
 119              // Add this input file to the code map to make it available as
 120              // dependency information
 121              let filename = file.display().to_str().to_strbuf();
 122              let interned = token::intern_and_get_ident(src);
 123              cx.codemap().new_filemap(filename, src.to_strbuf());
 124  
 125              base::MacExpr::new(cx.expr_str(sp, interned))
 126          }
 127          None => {
 128              cx.span_err(sp, format!("{} wasn't a utf-8 file", file.display()));
 129              return DummyResult::expr(sp);
 130          }
 131      }
 132  }
 133  
 134  pub fn expand_include_bin(cx: &mut ExtCtxt, spSpan, tts: &[ast::TokenTree])
 135                            -> Box<base::MacResult> {
 136      let file = match get_single_str_from_tts(cx, sp, tts, "include_bin!") {
 137          Some(f) => f,
 138          None => return DummyResult::expr(sp)
 139      };
 140      let file = res_rel_file(cx, sp, &Path::new(file));
 141      match File::open(&file).read_to_end() {
 142          Err(e) => {
 143              cx.span_err(sp, format!("couldn't read {}{}", file.display(), e));
 144              return DummyResult::expr(sp);
 145          }
 146          Ok(bytes) => {
 147              let bytes = bytes.iter().map(|x| *x).collect();
 148              base::MacExpr::new(cx.expr_lit(sp, ast::LitBinary(Rc::new(bytes))))
 149          }
 150      }
 151  }
 152  
 153  // recur along an ExpnInfo chain to find the original expression
 154  fn topmost_expn_info(expn_info: @codemap::ExpnInfo) -> @codemap::ExpnInfo {
 155      match *expn_info {
 156          ExpnInfo { call_site: ref call_site, .. } => {
 157              match call_site.expn_info {
 158                  Some(next_expn_info) => {
 159                      match *next_expn_info {
 160                          ExpnInfo {
 161                              callee: NameAndSpan { name: ref name, .. },
 162                              ..
 163                          } => {
 164                              // Don't recurse into file using "include!"
 165                              if "include" == name.as_slice() {
 166                                  expn_info
 167                              } else {
 168                                  topmost_expn_info(next_expn_info)
 169                              }
 170                          }
 171                      }
 172                  },
 173                  None => expn_info
 174              }
 175          }
 176      }
 177  }
 178  
 179  // resolve a file-system path to an absolute file-system path (if it
 180  // isn't already)
 181  fn res_rel_file(cx: &mut ExtCtxt, spcodemap::Span, arg: &Path) -> Path {
 182      // NB: relative paths are resolved relative to the compilation unit
 183      if !arg.is_absolute() {
 184          let mut cu = Path::new(cx.codemap().span_to_filename(sp));
 185          cu.pop();
 186          cu.push(arg);
 187          cu
 188      } else {
 189          arg.clone()
 190      }
 191  }