]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/base.rs
librustc: De-`@str` `NameAndSpan`
[rust.git] / src / libsyntax / ext / base.rs
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 ast::Name;
13 use codemap;
14 use codemap::{CodeMap, Span, ExpnInfo};
15 use ext;
16 use ext::expand;
17 use parse;
18 use parse::token;
19 use parse::token::{InternedString, intern, str_to_ident};
20 use util::small_vector::SmallVector;
21
22 use std::hashmap::HashMap;
23 use std::unstable::dynamic_lib::DynamicLibrary;
24
25 // new-style macro! tt code:
26 //
27 //    MacResult, NormalTT, IdentTT
28 //
29 // also note that ast::Mac used to have a bunch of extraneous cases and
30 // is now probably a redundant AST node, can be merged with
31 // ast::MacInvocTT.
32
33 pub struct MacroDef {
34     name: @str,
35     ext: SyntaxExtension
36 }
37
38 pub type ItemDecorator =
39     fn(&ExtCtxt, Span, @ast::MetaItem, ~[@ast::Item]) -> ~[@ast::Item];
40
41 pub struct BasicMacroExpander {
42     expander: MacroExpanderFn,
43     span: Option<Span>
44 }
45
46 pub trait MacroExpander {
47     fn expand(&self,
48               ecx: &mut ExtCtxt,
49               span: Span,
50               token_tree: &[ast::TokenTree])
51               -> MacResult;
52 }
53
54 pub type MacroExpanderFn =
55     fn(ecx: &mut ExtCtxt, span: codemap::Span, token_tree: &[ast::TokenTree])
56        -> MacResult;
57
58 impl MacroExpander for BasicMacroExpander {
59     fn expand(&self,
60               ecx: &mut ExtCtxt,
61               span: Span,
62               token_tree: &[ast::TokenTree])
63               -> MacResult {
64         (self.expander)(ecx, span, token_tree)
65     }
66 }
67
68 pub struct BasicIdentMacroExpander {
69     expander: IdentMacroExpanderFn,
70     span: Option<Span>
71 }
72
73 pub trait IdentMacroExpander {
74     fn expand(&self,
75               cx: &mut ExtCtxt,
76               sp: Span,
77               ident: ast::Ident,
78               token_tree: ~[ast::TokenTree])
79               -> MacResult;
80 }
81
82 impl IdentMacroExpander for BasicIdentMacroExpander {
83     fn expand(&self,
84               cx: &mut ExtCtxt,
85               sp: Span,
86               ident: ast::Ident,
87               token_tree: ~[ast::TokenTree])
88               -> MacResult {
89         (self.expander)(cx, sp, ident, token_tree)
90     }
91 }
92
93 pub type IdentMacroExpanderFn =
94     fn(&mut ExtCtxt, Span, ast::Ident, ~[ast::TokenTree]) -> MacResult;
95
96 pub type MacroCrateRegistrationFun =
97     fn(|ast::Name, SyntaxExtension|);
98
99 pub trait AnyMacro {
100     fn make_expr(&self) -> @ast::Expr;
101     fn make_items(&self) -> SmallVector<@ast::Item>;
102     fn make_stmt(&self) -> @ast::Stmt;
103 }
104
105 pub enum MacResult {
106     MRExpr(@ast::Expr),
107     MRItem(@ast::Item),
108     MRAny(@AnyMacro),
109     MRDef(MacroDef),
110 }
111 impl MacResult {
112     /// Create an empty expression MacResult; useful for satisfying
113     /// type signatures after emitting a non-fatal error (which stop
114     /// compilation well before the validity (or otherwise)) of the
115     /// expression are checked.
116     pub fn dummy_expr() -> MacResult {
117         MRExpr(@ast::Expr {
118                 id: ast::DUMMY_NODE_ID, node: ast::ExprLogLevel, span: codemap::DUMMY_SP
119             })
120     }
121 }
122
123 pub enum SyntaxExtension {
124     // #[deriving] and such
125     ItemDecorator(ItemDecorator),
126
127     // Token-tree expanders
128     NormalTT(~MacroExpander:'static, Option<Span>),
129
130     // An IdentTT is a macro that has an
131     // identifier in between the name of the
132     // macro and the argument. Currently,
133     // the only examples of this is
134     // macro_rules!
135
136     // perhaps macro_rules! will lose its odd special identifier argument,
137     // and this can go away also
138     IdentTT(~IdentMacroExpander:'static, Option<Span>),
139 }
140
141 pub struct BlockInfo {
142     // should macros escape from this scope?
143     macros_escape : bool,
144     // what are the pending renames?
145     pending_renames : RenameList,
146     // references for crates loaded in this scope
147     macro_crates: ~[DynamicLibrary],
148 }
149
150 impl BlockInfo {
151     pub fn new() -> BlockInfo {
152         BlockInfo {
153             macros_escape: false,
154             pending_renames: ~[],
155             macro_crates: ~[],
156         }
157     }
158 }
159
160 // a list of ident->name renamings
161 pub type RenameList = ~[(ast::Ident,Name)];
162
163 // The base map of methods for expanding syntax extension
164 // AST nodes into full ASTs
165 pub fn syntax_expander_table() -> SyntaxEnv {
166     // utility function to simplify creating NormalTT syntax extensions
167     fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension {
168         NormalTT(~BasicMacroExpander {
169                 expander: f,
170                 span: None,
171             },
172             None)
173     }
174
175     let mut syntax_expanders = SyntaxEnv::new();
176     syntax_expanders.insert(intern(&"macro_rules"),
177                             IdentTT(~BasicIdentMacroExpander {
178                                 expander: ext::tt::macro_rules::add_new_extension,
179                                 span: None,
180                             },
181                             None));
182     syntax_expanders.insert(intern(&"fmt"),
183                             builtin_normal_expander(
184                                 ext::fmt::expand_syntax_ext));
185     syntax_expanders.insert(intern(&"format_args"),
186                             builtin_normal_expander(
187                                 ext::format::expand_args));
188     syntax_expanders.insert(intern(&"env"),
189                             builtin_normal_expander(
190                                     ext::env::expand_env));
191     syntax_expanders.insert(intern(&"option_env"),
192                             builtin_normal_expander(
193                                     ext::env::expand_option_env));
194     syntax_expanders.insert(intern("bytes"),
195                             builtin_normal_expander(
196                                     ext::bytes::expand_syntax_ext));
197     syntax_expanders.insert(intern("concat_idents"),
198                             builtin_normal_expander(
199                                     ext::concat_idents::expand_syntax_ext));
200     syntax_expanders.insert(intern("concat"),
201                             builtin_normal_expander(
202                                     ext::concat::expand_syntax_ext));
203     syntax_expanders.insert(intern(&"log_syntax"),
204                             builtin_normal_expander(
205                                     ext::log_syntax::expand_syntax_ext));
206     syntax_expanders.insert(intern(&"deriving"),
207                             ItemDecorator(ext::deriving::expand_meta_deriving));
208
209     // Quasi-quoting expanders
210     syntax_expanders.insert(intern(&"quote_tokens"),
211                        builtin_normal_expander(
212                             ext::quote::expand_quote_tokens));
213     syntax_expanders.insert(intern(&"quote_expr"),
214                        builtin_normal_expander(
215                             ext::quote::expand_quote_expr));
216     syntax_expanders.insert(intern(&"quote_ty"),
217                        builtin_normal_expander(
218                             ext::quote::expand_quote_ty));
219     syntax_expanders.insert(intern(&"quote_item"),
220                        builtin_normal_expander(
221                             ext::quote::expand_quote_item));
222     syntax_expanders.insert(intern(&"quote_pat"),
223                        builtin_normal_expander(
224                             ext::quote::expand_quote_pat));
225     syntax_expanders.insert(intern(&"quote_stmt"),
226                        builtin_normal_expander(
227                             ext::quote::expand_quote_stmt));
228
229     syntax_expanders.insert(intern(&"line"),
230                             builtin_normal_expander(
231                                     ext::source_util::expand_line));
232     syntax_expanders.insert(intern(&"col"),
233                             builtin_normal_expander(
234                                     ext::source_util::expand_col));
235     syntax_expanders.insert(intern(&"file"),
236                             builtin_normal_expander(
237                                     ext::source_util::expand_file));
238     syntax_expanders.insert(intern(&"stringify"),
239                             builtin_normal_expander(
240                                     ext::source_util::expand_stringify));
241     syntax_expanders.insert(intern(&"include"),
242                             builtin_normal_expander(
243                                     ext::source_util::expand_include));
244     syntax_expanders.insert(intern(&"include_str"),
245                             builtin_normal_expander(
246                                     ext::source_util::expand_include_str));
247     syntax_expanders.insert(intern(&"include_bin"),
248                             builtin_normal_expander(
249                                     ext::source_util::expand_include_bin));
250     syntax_expanders.insert(intern(&"module_path"),
251                             builtin_normal_expander(
252                                     ext::source_util::expand_mod));
253     syntax_expanders.insert(intern(&"asm"),
254                             builtin_normal_expander(
255                                     ext::asm::expand_asm));
256     syntax_expanders.insert(intern(&"cfg"),
257                             builtin_normal_expander(
258                                     ext::cfg::expand_cfg));
259     syntax_expanders.insert(intern(&"trace_macros"),
260                             builtin_normal_expander(
261                                     ext::trace_macros::expand_trace_macros));
262     syntax_expanders
263 }
264
265 pub struct MacroCrate {
266     lib: Option<Path>,
267     cnum: ast::CrateNum,
268 }
269
270 pub trait CrateLoader {
271     fn load_crate(&mut self, crate: &ast::ViewItem) -> MacroCrate;
272     fn get_exported_macros(&mut self, crate_num: ast::CrateNum) -> ~[~str];
273     fn get_registrar_symbol(&mut self, crate_num: ast::CrateNum) -> Option<~str>;
274 }
275
276 // One of these is made during expansion and incrementally updated as we go;
277 // when a macro expansion occurs, the resulting nodes have the backtrace()
278 // -> expn_info of their expansion context stored into their span.
279 pub struct ExtCtxt<'a> {
280     parse_sess: @parse::ParseSess,
281     cfg: ast::CrateConfig,
282     backtrace: Option<@ExpnInfo>,
283     loader: &'a mut CrateLoader,
284
285     mod_path: ~[ast::Ident],
286     trace_mac: bool
287 }
288
289 impl<'a> ExtCtxt<'a> {
290     pub fn new<'a>(parse_sess: @parse::ParseSess, cfg: ast::CrateConfig,
291                loader: &'a mut CrateLoader) -> ExtCtxt<'a> {
292         ExtCtxt {
293             parse_sess: parse_sess,
294             cfg: cfg,
295             backtrace: None,
296             loader: loader,
297             mod_path: ~[],
298             trace_mac: false
299         }
300     }
301
302     pub fn expand_expr(&mut self, mut e: @ast::Expr) -> @ast::Expr {
303         loop {
304             match e.node {
305                 ast::ExprMac(..) => {
306                     let mut expander = expand::MacroExpander {
307                         extsbox: syntax_expander_table(),
308                         cx: self,
309                     };
310                     e = expand::expand_expr(e, &mut expander);
311                 }
312                 _ => return e
313             }
314         }
315     }
316
317     pub fn codemap(&self) -> @CodeMap { self.parse_sess.cm }
318     pub fn parse_sess(&self) -> @parse::ParseSess { self.parse_sess }
319     pub fn cfg(&self) -> ast::CrateConfig { self.cfg.clone() }
320     pub fn call_site(&self) -> Span {
321         match self.backtrace {
322             Some(expn_info) => expn_info.call_site,
323             None => self.bug("missing top span")
324         }
325     }
326     pub fn print_backtrace(&self) { }
327     pub fn backtrace(&self) -> Option<@ExpnInfo> { self.backtrace }
328     pub fn mod_push(&mut self, i: ast::Ident) { self.mod_path.push(i); }
329     pub fn mod_pop(&mut self) { self.mod_path.pop().unwrap(); }
330     pub fn mod_path(&self) -> ~[ast::Ident] { self.mod_path.clone() }
331     pub fn bt_push(&mut self, ei: codemap::ExpnInfo) {
332         match ei {
333             ExpnInfo {call_site: cs, callee: ref callee} => {
334                 self.backtrace =
335                     Some(@ExpnInfo {
336                         call_site: Span {lo: cs.lo, hi: cs.hi,
337                                          expn_info: self.backtrace},
338                         callee: (*callee).clone()
339                     });
340             }
341         }
342     }
343     pub fn bt_pop(&mut self) {
344         match self.backtrace {
345             Some(expn_info) => self.backtrace = expn_info.call_site.expn_info,
346             _ => self.bug("tried to pop without a push")
347         }
348     }
349     /// Emit `msg` attached to `sp`, and stop compilation immediately.
350     ///
351     /// `span_err` should be strongly prefered where-ever possible:
352     /// this should *only* be used when
353     /// - continuing has a high risk of flow-on errors (e.g. errors in
354     ///   declaring a macro would cause all uses of that macro to
355     ///   complain about "undefined macro"), or
356     /// - there is literally nothing else that can be done (however,
357     ///   in most cases one can construct a dummy expression/item to
358     ///   substitute; we never hit resolve/type-checking so the dummy
359     ///   value doesn't have to match anything)
360     pub fn span_fatal(&self, sp: Span, msg: &str) -> ! {
361         self.print_backtrace();
362         self.parse_sess.span_diagnostic.span_fatal(sp, msg);
363     }
364
365     /// Emit `msg` attached to `sp`, without immediately stopping
366     /// compilation.
367     ///
368     /// Compilation will be stopped in the near future (at the end of
369     /// the macro expansion phase).
370     pub fn span_err(&self, sp: Span, msg: &str) {
371         self.print_backtrace();
372         self.parse_sess.span_diagnostic.span_err(sp, msg);
373     }
374     pub fn span_warn(&self, sp: Span, msg: &str) {
375         self.print_backtrace();
376         self.parse_sess.span_diagnostic.span_warn(sp, msg);
377     }
378     pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! {
379         self.print_backtrace();
380         self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
381     }
382     pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
383         self.print_backtrace();
384         self.parse_sess.span_diagnostic.span_bug(sp, msg);
385     }
386     pub fn span_note(&self, sp: Span, msg: &str) {
387         self.print_backtrace();
388         self.parse_sess.span_diagnostic.span_note(sp, msg);
389     }
390     pub fn bug(&self, msg: &str) -> ! {
391         self.print_backtrace();
392         self.parse_sess.span_diagnostic.handler().bug(msg);
393     }
394     pub fn trace_macros(&self) -> bool {
395         self.trace_mac
396     }
397     pub fn set_trace_macros(&mut self, x: bool) {
398         self.trace_mac = x
399     }
400     pub fn ident_of(&self, st: &str) -> ast::Ident {
401         str_to_ident(st)
402     }
403 }
404
405 /// Extract a string literal from `expr`, emitting `err_msg` if `expr`
406 /// is not a string literal. This does not stop compilation on error,
407 /// merely emits a non-fatal error and returns None.
408 pub fn expr_to_str(cx: &ExtCtxt, expr: @ast::Expr, err_msg: &str)
409                    -> Option<(InternedString, ast::StrStyle)> {
410     match expr.node {
411         ast::ExprLit(l) => match l.node {
412             ast::LitStr(ref s, style) => return Some(((*s).clone(), style)),
413             _ => cx.span_err(l.span, err_msg)
414         },
415         _ => cx.span_err(expr.span, err_msg)
416     }
417     None
418 }
419
420 /// Non-fatally assert that `tts` is empty. Note that this function
421 /// returns even when `tts` is non-empty, macros that *need* to stop
422 /// compilation should call
423 /// `cx.parse_sess.span_diagnostic.abort_if_errors()` (this should be
424 /// done as rarely as possible).
425 pub fn check_zero_tts(cx: &ExtCtxt,
426                       sp: Span,
427                       tts: &[ast::TokenTree],
428                       name: &str) {
429     if tts.len() != 0 {
430         cx.span_err(sp, format!("{} takes no arguments", name));
431     }
432 }
433
434 /// Extract the string literal from the first token of `tts`. If this
435 /// is not a string literal, emit an error and return None.
436 pub fn get_single_str_from_tts(cx: &ExtCtxt,
437                                sp: Span,
438                                tts: &[ast::TokenTree],
439                                name: &str)
440                                -> Option<~str> {
441     if tts.len() != 1 {
442         cx.span_err(sp, format!("{} takes 1 argument.", name));
443     } else {
444         match tts[0] {
445             ast::TTTok(_, token::LIT_STR(ident))
446             | ast::TTTok(_, token::LIT_STR_RAW(ident, _)) => {
447                 let interned_str = token::get_ident(ident.name);
448                 return Some(interned_str.get().to_str())
449             }
450             _ => cx.span_err(sp, format!("{} requires a string.", name)),
451         }
452     }
453     None
454 }
455
456 /// Extract comma-separated expressions from `tts`. If there is a
457 /// parsing error, emit a non-fatal error and return None.
458 pub fn get_exprs_from_tts(cx: &ExtCtxt,
459                           sp: Span,
460                           tts: &[ast::TokenTree]) -> Option<~[@ast::Expr]> {
461     let mut p = parse::new_parser_from_tts(cx.parse_sess(),
462                                            cx.cfg(),
463                                            tts.to_owned());
464     let mut es = ~[];
465     while p.token != token::EOF {
466         if es.len() != 0 && !p.eat(&token::COMMA) {
467             cx.span_err(sp, "expected token: `,`");
468             return None;
469         }
470         es.push(p.parse_expr());
471     }
472     Some(es)
473 }
474
475 // in order to have some notion of scoping for macros,
476 // we want to implement the notion of a transformation
477 // environment.
478
479 // This environment maps Names to SyntaxExtensions.
480
481 // Actually, the following implementation is parameterized
482 // by both key and value types.
483
484 //impl question: how to implement it? Initially, the
485 // env will contain only macros, so it might be painful
486 // to add an empty frame for every context. Let's just
487 // get it working, first....
488
489 // NB! the mutability of the underlying maps means that
490 // if expansion is out-of-order, a deeper scope may be
491 // able to refer to a macro that was added to an enclosing
492 // scope lexically later than the deeper scope.
493
494 struct MapChainFrame {
495     info: BlockInfo,
496     map: HashMap<Name, SyntaxExtension>,
497 }
498
499 #[unsafe_destructor]
500 impl Drop for MapChainFrame {
501     fn drop(&mut self) {
502         // make sure that syntax extension dtors run before we drop the libs
503         self.map.clear();
504     }
505 }
506
507 // Only generic to make it easy to test
508 pub struct SyntaxEnv {
509     priv chain: ~[MapChainFrame],
510 }
511
512 impl SyntaxEnv {
513     pub fn new() -> SyntaxEnv {
514         let mut map = SyntaxEnv { chain: ~[] };
515         map.push_frame();
516         map
517     }
518
519     pub fn push_frame(&mut self) {
520         self.chain.push(MapChainFrame {
521             info: BlockInfo::new(),
522             map: HashMap::new(),
523         });
524     }
525
526     pub fn pop_frame(&mut self) {
527         assert!(self.chain.len() > 1, "too many pops on MapChain!");
528         self.chain.pop();
529     }
530
531     fn find_escape_frame<'a>(&'a mut self) -> &'a mut MapChainFrame {
532         for (i, frame) in self.chain.mut_iter().enumerate().rev() {
533             if !frame.info.macros_escape || i == 0 {
534                 return frame
535             }
536         }
537         unreachable!()
538     }
539
540     pub fn find<'a>(&'a self, k: &Name) -> Option<&'a SyntaxExtension> {
541         for frame in self.chain.iter().rev() {
542             match frame.map.find(k) {
543                 Some(v) => return Some(v),
544                 None => {}
545             }
546         }
547         None
548     }
549
550     pub fn insert(&mut self, k: Name, v: SyntaxExtension) {
551         self.find_escape_frame().map.insert(k, v);
552     }
553
554     pub fn insert_macro_crate(&mut self, lib: DynamicLibrary) {
555         self.find_escape_frame().info.macro_crates.push(lib);
556     }
557
558     pub fn info<'a>(&'a mut self) -> &'a mut BlockInfo {
559         &mut self.chain[self.chain.len()-1].info
560     }
561 }