]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
223e47cc LB |
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; | |
3157f602 | 12 | use syntax_pos::{self, Pos, Span}; |
223e47cc LB |
13 | use ext::base::*; |
14 | use ext::base; | |
970d7e83 | 15 | use ext::build::AstBuilder; |
1a4d82fc | 16 | use parse::token; |
223e47cc LB |
17 | use parse; |
18 | use print::pprust; | |
1a4d82fc | 19 | use ptr::P; |
3157f602 | 20 | use tokenstream; |
1a4d82fc | 21 | use util::small_vector::SmallVector; |
223e47cc | 22 | |
c34b1796 AL |
23 | use std::fs::File; |
24 | use std::io::prelude::*; | |
25 | use std::path::{Path, PathBuf}; | |
1a4d82fc | 26 | use std::rc::Rc; |
223e47cc LB |
27 | |
28 | // These macros all relate to the file system; they either return | |
29 | // the column/row/filename of the expression, or they include | |
30 | // a given file into the current one. | |
31 | ||
1a4d82fc | 32 | /// line!(): expands to the current line number |
3157f602 | 33 | pub fn expand_line(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree]) |
1a4d82fc | 34 | -> Box<base::MacResult+'static> { |
223e47cc LB |
35 | base::check_zero_tts(cx, sp, tts, "line!"); |
36 | ||
d9579d0f | 37 | let topmost = cx.expansion_cause(); |
1a4d82fc | 38 | let loc = cx.codemap().lookup_char_pos(topmost.lo); |
223e47cc | 39 | |
c34b1796 | 40 | base::MacEager::expr(cx.expr_u32(topmost, loc.line as u32)) |
223e47cc LB |
41 | } |
42 | ||
1a4d82fc | 43 | /* column!(): expands to the current column number */ |
3157f602 | 44 | pub fn expand_column(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree]) |
1a4d82fc JJ |
45 | -> Box<base::MacResult+'static> { |
46 | base::check_zero_tts(cx, sp, tts, "column!"); | |
223e47cc | 47 | |
d9579d0f | 48 | let topmost = cx.expansion_cause(); |
1a4d82fc | 49 | let loc = cx.codemap().lookup_char_pos(topmost.lo); |
c34b1796 AL |
50 | |
51 | base::MacEager::expr(cx.expr_u32(topmost, loc.col.to_usize() as u32)) | |
223e47cc LB |
52 | } |
53 | ||
1a4d82fc JJ |
54 | /// file!(): expands to the current filename */ |
55 | /// The filemap (`loc.file`) contains a bunch more information we could spit | |
56 | /// out if we wanted. | |
3157f602 | 57 | pub fn expand_file(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree]) |
1a4d82fc | 58 | -> Box<base::MacResult+'static> { |
223e47cc LB |
59 | base::check_zero_tts(cx, sp, tts, "file!"); |
60 | ||
d9579d0f | 61 | let topmost = cx.expansion_cause(); |
1a4d82fc | 62 | let loc = cx.codemap().lookup_char_pos(topmost.lo); |
c34b1796 AL |
63 | let filename = token::intern_and_get_ident(&loc.file.name); |
64 | base::MacEager::expr(cx.expr_str(topmost, filename)) | |
223e47cc LB |
65 | } |
66 | ||
3157f602 | 67 | pub fn expand_stringify(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree]) |
1a4d82fc JJ |
68 | -> Box<base::MacResult+'static> { |
69 | let s = pprust::tts_to_string(tts); | |
c34b1796 | 70 | base::MacEager::expr(cx.expr_str(sp, |
85aaf69f | 71 | token::intern_and_get_ident(&s[..]))) |
223e47cc LB |
72 | } |
73 | ||
3157f602 | 74 | pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree]) |
1a4d82fc | 75 | -> Box<base::MacResult+'static> { |
223e47cc | 76 | base::check_zero_tts(cx, sp, tts, "module_path!"); |
9e0c209e SL |
77 | let mod_path = &cx.current_expansion.module.mod_path; |
78 | let string = mod_path.iter().map(|x| x.to_string()).collect::<Vec<String>>().join("::"); | |
79 | ||
c34b1796 | 80 | base::MacEager::expr(cx.expr_str( |
1a4d82fc | 81 | sp, |
85aaf69f | 82 | token::intern_and_get_ident(&string[..]))) |
223e47cc LB |
83 | } |
84 | ||
1a4d82fc JJ |
85 | /// include! : parse the given file as an expr |
86 | /// This is generally a bad idea because it's going to behave | |
87 | /// unhygienically. | |
3157f602 | 88 | pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree]) |
1a4d82fc JJ |
89 | -> Box<base::MacResult+'cx> { |
90 | let file = match get_single_str_from_tts(cx, sp, tts, "include!") { | |
91 | Some(f) => f, | |
92 | None => return DummyResult::expr(sp), | |
93 | }; | |
94 | // The file will be added to the code map by the parser | |
c30ab7b3 SL |
95 | let path = res_rel_file(cx, sp, Path::new(&file)); |
96 | let p = parse::new_sub_parser_from_file(cx.parse_sess(), &path, true, None, sp); | |
1a4d82fc JJ |
97 | |
98 | struct ExpandResult<'a> { | |
99 | p: parse::parser::Parser<'a>, | |
100 | } | |
101 | impl<'a> base::MacResult for ExpandResult<'a> { | |
102 | fn make_expr(mut self: Box<ExpandResult<'a>>) -> Option<P<ast::Expr>> { | |
92a42be0 | 103 | Some(panictry!(self.p.parse_expr())) |
1a4d82fc JJ |
104 | } |
105 | fn make_items(mut self: Box<ExpandResult<'a>>) | |
106 | -> Option<SmallVector<P<ast::Item>>> { | |
107 | let mut ret = SmallVector::zero(); | |
85aaf69f | 108 | while self.p.token != token::Eof { |
92a42be0 | 109 | match panictry!(self.p.parse_item()) { |
1a4d82fc | 110 | Some(item) => ret.push(item), |
9cc50fc6 SL |
111 | None => panic!(self.p.diagnostic().span_fatal(self.p.span, |
112 | &format!("expected item, found `{}`", | |
113 | self.p.this_token_to_string()))) | |
1a4d82fc JJ |
114 | } |
115 | } | |
116 | Some(ret) | |
117 | } | |
223e47cc | 118 | } |
1a4d82fc | 119 | |
d9579d0f | 120 | Box::new(ExpandResult { p: p }) |
223e47cc LB |
121 | } |
122 | ||
1a4d82fc | 123 | // include_str! : read the given file, insert it as a literal string expr |
3157f602 | 124 | pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree]) |
1a4d82fc JJ |
125 | -> Box<base::MacResult+'static> { |
126 | let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") { | |
127 | Some(f) => f, | |
128 | None => return DummyResult::expr(sp) | |
129 | }; | |
c34b1796 AL |
130 | let file = res_rel_file(cx, sp, Path::new(&file)); |
131 | let mut bytes = Vec::new(); | |
132 | match File::open(&file).and_then(|mut f| f.read_to_end(&mut bytes)) { | |
133 | Ok(..) => {} | |
1a4d82fc JJ |
134 | Err(e) => { |
135 | cx.span_err(sp, | |
85aaf69f | 136 | &format!("couldn't read {}: {}", |
1a4d82fc | 137 | file.display(), |
85aaf69f | 138 | e)); |
1a4d82fc JJ |
139 | return DummyResult::expr(sp); |
140 | } | |
1a4d82fc JJ |
141 | }; |
142 | match String::from_utf8(bytes) { | |
143 | Ok(src) => { | |
144 | // Add this input file to the code map to make it available as | |
145 | // dependency information | |
85aaf69f SL |
146 | let filename = format!("{}", file.display()); |
147 | let interned = token::intern_and_get_ident(&src[..]); | |
3157f602 | 148 | cx.codemap().new_filemap_and_lines(&filename, None, &src); |
1a4d82fc | 149 | |
c34b1796 | 150 | base::MacEager::expr(cx.expr_str(sp, interned)) |
1a4d82fc JJ |
151 | } |
152 | Err(_) => { | |
153 | cx.span_err(sp, | |
85aaf69f SL |
154 | &format!("{} wasn't a utf-8 file", |
155 | file.display())); | |
1a4d82fc JJ |
156 | return DummyResult::expr(sp); |
157 | } | |
223e47cc LB |
158 | } |
159 | } | |
160 | ||
3157f602 | 161 | pub fn expand_include_bytes(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree]) |
1a4d82fc JJ |
162 | -> Box<base::MacResult+'static> { |
163 | let file = match get_single_str_from_tts(cx, sp, tts, "include_bytes!") { | |
164 | Some(f) => f, | |
165 | None => return DummyResult::expr(sp) | |
166 | }; | |
c34b1796 AL |
167 | let file = res_rel_file(cx, sp, Path::new(&file)); |
168 | let mut bytes = Vec::new(); | |
169 | match File::open(&file).and_then(|mut f| f.read_to_end(&mut bytes)) { | |
1a4d82fc JJ |
170 | Err(e) => { |
171 | cx.span_err(sp, | |
85aaf69f | 172 | &format!("couldn't read {}: {}", file.display(), e)); |
1a4d82fc JJ |
173 | return DummyResult::expr(sp); |
174 | } | |
c34b1796 | 175 | Ok(..) => { |
9346a6ac AL |
176 | // Add this input file to the code map to make it available as |
177 | // dependency information, but don't enter it's contents | |
178 | let filename = format!("{}", file.display()); | |
3157f602 | 179 | cx.codemap().new_filemap_and_lines(&filename, None, ""); |
9346a6ac | 180 | |
7453a54e | 181 | base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Rc::new(bytes)))) |
223e47cc LB |
182 | } |
183 | } | |
184 | } | |
185 | ||
186 | // resolve a file-system path to an absolute file-system path (if it | |
187 | // isn't already) | |
3157f602 | 188 | fn res_rel_file(cx: &mut ExtCtxt, sp: syntax_pos::Span, arg: &Path) -> PathBuf { |
223e47cc | 189 | // NB: relative paths are resolved relative to the compilation unit |
1a4d82fc | 190 | if !arg.is_absolute() { |
3157f602 XL |
191 | let callsite = cx.codemap().source_callsite(sp); |
192 | let mut cu = PathBuf::from(&cx.codemap().span_to_filename(callsite)); | |
1a4d82fc JJ |
193 | cu.pop(); |
194 | cu.push(arg); | |
195 | cu | |
223e47cc | 196 | } else { |
c34b1796 | 197 | arg.to_path_buf() |
223e47cc LB |
198 | } |
199 | } |