]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/base.rs
6ed5ca3e402a8dc4ac5e69ada83f9255c30bc75f
[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 diagnostic::span_handler;
16 use ext;
17 use parse;
18 use parse::token;
19 use parse::token::{ident_to_str, intern, str_to_ident};
20
21 use std::hashmap::HashMap;
22
23 // new-style macro! tt code:
24 //
25 //    SyntaxExpanderTT, SyntaxExpanderTTItem, MacResult,
26 //    NormalTT, IdentTT
27 //
28 // also note that ast::mac used to have a bunch of extraneous cases and
29 // is now probably a redundant AST node, can be merged with
30 // ast::mac_invoc_tt.
31
32 pub struct MacroDef {
33     name: @str,
34     ext: SyntaxExtension
35 }
36
37 pub type ItemDecorator = @fn(@ExtCtxt,
38                              span,
39                              @ast::MetaItem,
40                              ~[@ast::item])
41                           -> ~[@ast::item];
42
43 pub struct SyntaxExpanderTT {
44     expander: SyntaxExpanderTTFun,
45     span: Option<span>
46 }
47
48 pub type SyntaxExpanderTTFun = @fn(@ExtCtxt,
49                                    span,
50                                    &[ast::token_tree])
51                                 -> MacResult;
52
53 pub struct SyntaxExpanderTTItem {
54     expander: SyntaxExpanderTTItemFun,
55     span: Option<span>
56 }
57
58 pub type SyntaxExpanderTTItemFun = @fn(@ExtCtxt,
59                                        span,
60                                        ast::ident,
61                                        ~[ast::token_tree])
62                                     -> MacResult;
63
64 pub enum MacResult {
65     MRExpr(@ast::expr),
66     MRItem(@ast::item),
67     MRAny(@fn() -> @ast::expr,
68           @fn() -> Option<@ast::item>,
69           @fn() -> @ast::stmt),
70     MRDef(MacroDef)
71 }
72
73 pub enum SyntaxExtension {
74
75     // #[auto_encode] and such
76     ItemDecorator(ItemDecorator),
77
78     // Token-tree expanders
79     NormalTT(SyntaxExpanderTT),
80
81     // An IdentTT is a macro that has an
82     // identifier in between the name of the
83     // macro and the argument. Currently,
84     // the only examples of this is
85     // macro_rules!
86
87     // perhaps macro_rules! will lose its odd special identifier argument,
88     // and this can go away also
89     IdentTT(SyntaxExpanderTTItem),
90 }
91
92 // The SyntaxEnv is the environment that's threaded through the expansion
93 // of macros. It contains bindings for macros, and also a special binding
94 // for " block" (not a legal identifier) that maps to a BlockInfo
95 pub type SyntaxEnv = @mut MapChain<Name, Transformer>;
96
97 // Transformer : the codomain of SyntaxEnvs
98
99 pub enum Transformer {
100     // this identifier maps to a syntax extension or macro
101     SE(SyntaxExtension),
102     // blockinfo : this is ... well, it's simpler than threading
103     // another whole data stack-structured data structure through
104     // expansion. Basically, there's an invariant that every
105     // map must contain a binding for " block".
106     BlockInfo(BlockInfo)
107 }
108
109 pub struct BlockInfo {
110     // should macros escape from this scope?
111     macros_escape : bool,
112     // what are the pending renames?
113     pending_renames : @mut RenameList
114 }
115
116 // a list of ident->name renamings
117 type RenameList = ~[(ast::ident,Name)];
118
119 // The base map of methods for expanding syntax extension
120 // AST nodes into full ASTs
121 pub fn syntax_expander_table() -> SyntaxEnv {
122     // utility function to simplify creating NormalTT syntax extensions
123     fn builtin_normal_tt(f: SyntaxExpanderTTFun) -> @Transformer {
124         @SE(NormalTT(SyntaxExpanderTT{expander: f, span: None}))
125     }
126     // utility function to simplify creating IdentTT syntax extensions
127     fn builtin_item_tt(f: SyntaxExpanderTTItemFun) -> @Transformer {
128         @SE(IdentTT(SyntaxExpanderTTItem{expander: f, span: None}))
129     }
130     let mut syntax_expanders = HashMap::new();
131     // NB identifier starts with space, and can't conflict with legal idents
132     syntax_expanders.insert(intern(&" block"),
133                             @BlockInfo(BlockInfo{
134                                 macros_escape : false,
135                                 pending_renames : @mut ~[]
136                             }));
137     syntax_expanders.insert(intern(&"macro_rules"),
138                             builtin_item_tt(
139                                 ext::tt::macro_rules::add_new_extension));
140     syntax_expanders.insert(intern(&"fmt"),
141                             builtin_normal_tt(ext::fmt::expand_syntax_ext));
142     syntax_expanders.insert(
143         intern(&"auto_encode"),
144         @SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
145     syntax_expanders.insert(
146         intern(&"auto_decode"),
147         @SE(ItemDecorator(ext::auto_encode::expand_auto_decode)));
148     syntax_expanders.insert(intern(&"env"),
149                             builtin_normal_tt(ext::env::expand_syntax_ext));
150     syntax_expanders.insert(intern("bytes"),
151                             builtin_normal_tt(ext::bytes::expand_syntax_ext));
152     syntax_expanders.insert(intern("concat_idents"),
153                             builtin_normal_tt(
154                                 ext::concat_idents::expand_syntax_ext));
155     syntax_expanders.insert(intern(&"log_syntax"),
156                             builtin_normal_tt(
157                                 ext::log_syntax::expand_syntax_ext));
158     syntax_expanders.insert(intern(&"deriving"),
159                             @SE(ItemDecorator(
160                                 ext::deriving::expand_meta_deriving)));
161
162     // Quasi-quoting expanders
163     syntax_expanders.insert(intern(&"quote_tokens"),
164                        builtin_normal_tt(ext::quote::expand_quote_tokens));
165     syntax_expanders.insert(intern(&"quote_expr"),
166                        builtin_normal_tt(ext::quote::expand_quote_expr));
167     syntax_expanders.insert(intern(&"quote_ty"),
168                        builtin_normal_tt(ext::quote::expand_quote_ty));
169     syntax_expanders.insert(intern(&"quote_item"),
170                        builtin_normal_tt(ext::quote::expand_quote_item));
171     syntax_expanders.insert(intern(&"quote_pat"),
172                        builtin_normal_tt(ext::quote::expand_quote_pat));
173     syntax_expanders.insert(intern(&"quote_stmt"),
174                        builtin_normal_tt(ext::quote::expand_quote_stmt));
175
176     syntax_expanders.insert(intern(&"line"),
177                             builtin_normal_tt(
178                                 ext::source_util::expand_line));
179     syntax_expanders.insert(intern(&"col"),
180                             builtin_normal_tt(
181                                 ext::source_util::expand_col));
182     syntax_expanders.insert(intern(&"file"),
183                             builtin_normal_tt(
184                                 ext::source_util::expand_file));
185     syntax_expanders.insert(intern(&"stringify"),
186                             builtin_normal_tt(
187                                 ext::source_util::expand_stringify));
188     syntax_expanders.insert(intern(&"include"),
189                             builtin_normal_tt(
190                                 ext::source_util::expand_include));
191     syntax_expanders.insert(intern(&"include_str"),
192                             builtin_normal_tt(
193                                 ext::source_util::expand_include_str));
194     syntax_expanders.insert(intern(&"include_bin"),
195                             builtin_normal_tt(
196                                 ext::source_util::expand_include_bin));
197     syntax_expanders.insert(intern(&"module_path"),
198                             builtin_normal_tt(
199                                 ext::source_util::expand_mod));
200     syntax_expanders.insert(intern(&"asm"),
201                             builtin_normal_tt(ext::asm::expand_asm));
202     syntax_expanders.insert(intern(&"cfg"),
203                             builtin_normal_tt(ext::cfg::expand_cfg));
204     syntax_expanders.insert(
205         intern(&"trace_macros"),
206         builtin_normal_tt(ext::trace_macros::expand_trace_macros));
207     MapChain::new(~syntax_expanders)
208 }
209
210 // One of these is made during expansion and incrementally updated as we go;
211 // when a macro expansion occurs, the resulting nodes have the backtrace()
212 // -> expn_info of their expansion context stored into their span.
213 pub struct ExtCtxt {
214     parse_sess: @mut parse::ParseSess,
215     cfg: ast::CrateConfig,
216     backtrace: @mut Option<@ExpnInfo>,
217
218     // These two @mut's should really not be here,
219     // but the self types for CtxtRepr are all wrong
220     // and there are bugs in the code for object
221     // types that make this hard to get right at the
222     // moment. - nmatsakis
223     mod_path: @mut ~[ast::ident],
224     trace_mac: @mut bool
225 }
226
227 impl ExtCtxt {
228     pub fn new(parse_sess: @mut parse::ParseSess, cfg: ast::CrateConfig)
229                -> @ExtCtxt {
230         @ExtCtxt {
231             parse_sess: parse_sess,
232             cfg: cfg,
233             backtrace: @mut None,
234             mod_path: @mut ~[],
235             trace_mac: @mut false
236         }
237     }
238
239     pub fn codemap(&self) -> @CodeMap { self.parse_sess.cm }
240     pub fn parse_sess(&self) -> @mut parse::ParseSess { self.parse_sess }
241     pub fn cfg(&self) -> ast::CrateConfig { self.cfg.clone() }
242     pub fn call_site(&self) -> span {
243         match *self.backtrace {
244             Some(@ExpnInfo {call_site: cs, _}) => cs,
245             None => self.bug("missing top span")
246         }
247     }
248     pub fn print_backtrace(&self) { }
249     pub fn backtrace(&self) -> Option<@ExpnInfo> { *self.backtrace }
250     pub fn mod_push(&self, i: ast::ident) { self.mod_path.push(i); }
251     pub fn mod_pop(&self) { self.mod_path.pop(); }
252     pub fn mod_path(&self) -> ~[ast::ident] { (*self.mod_path).clone() }
253     pub fn bt_push(&self, ei: codemap::ExpnInfo) {
254         match ei {
255             ExpnInfo {call_site: cs, callee: ref callee} => {
256                 *self.backtrace =
257                     Some(@ExpnInfo {
258                         call_site: span {lo: cs.lo, hi: cs.hi,
259                                          expn_info: *self.backtrace},
260                         callee: *callee});
261             }
262         }
263     }
264     pub fn bt_pop(&self) {
265         match *self.backtrace {
266             Some(@ExpnInfo {
267                 call_site: span {expn_info: prev, _}, _}) => {
268                 *self.backtrace = prev
269             }
270             _ => self.bug("tried to pop without a push")
271         }
272     }
273     pub fn span_fatal(&self, sp: span, msg: &str) -> ! {
274         self.print_backtrace();
275         self.parse_sess.span_diagnostic.span_fatal(sp, msg);
276     }
277     pub fn span_err(&self, sp: span, msg: &str) {
278         self.print_backtrace();
279         self.parse_sess.span_diagnostic.span_err(sp, msg);
280     }
281     pub fn span_warn(&self, sp: span, msg: &str) {
282         self.print_backtrace();
283         self.parse_sess.span_diagnostic.span_warn(sp, msg);
284     }
285     pub fn span_unimpl(&self, sp: span, msg: &str) -> ! {
286         self.print_backtrace();
287         self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
288     }
289     pub fn span_bug(&self, sp: span, msg: &str) -> ! {
290         self.print_backtrace();
291         self.parse_sess.span_diagnostic.span_bug(sp, msg);
292     }
293     pub fn bug(&self, msg: &str) -> ! {
294         self.print_backtrace();
295         self.parse_sess.span_diagnostic.handler().bug(msg);
296     }
297     pub fn next_id(&self) -> ast::NodeId {
298         parse::next_node_id(self.parse_sess)
299     }
300     pub fn trace_macros(&self) -> bool {
301         *self.trace_mac
302     }
303     pub fn set_trace_macros(&self, x: bool) {
304         *self.trace_mac = x
305     }
306     pub fn str_of(&self, id: ast::ident) -> @str {
307         ident_to_str(&id)
308     }
309     pub fn ident_of(&self, st: &str) -> ast::ident {
310         str_to_ident(st)
311     }
312 }
313
314 pub fn expr_to_str(cx: @ExtCtxt, expr: @ast::expr, err_msg: ~str) -> @str {
315     match expr.node {
316       ast::expr_lit(l) => match l.node {
317         ast::lit_str(s) => s,
318         _ => cx.span_fatal(l.span, err_msg)
319       },
320       _ => cx.span_fatal(expr.span, err_msg)
321     }
322 }
323
324 pub fn expr_to_ident(cx: @ExtCtxt,
325                      expr: @ast::expr,
326                      err_msg: &str) -> ast::ident {
327     match expr.node {
328       ast::expr_path(ref p) => {
329         if p.types.len() > 0u || p.idents.len() != 1u {
330             cx.span_fatal(expr.span, err_msg);
331         }
332         return p.idents[0];
333       }
334       _ => cx.span_fatal(expr.span, err_msg)
335     }
336 }
337
338 pub fn check_zero_tts(cx: @ExtCtxt, sp: span, tts: &[ast::token_tree],
339                       name: &str) {
340     if tts.len() != 0 {
341         cx.span_fatal(sp, fmt!("%s takes no arguments", name));
342     }
343 }
344
345 pub fn get_single_str_from_tts(cx: @ExtCtxt,
346                                sp: span,
347                                tts: &[ast::token_tree],
348                                name: &str) -> @str {
349     if tts.len() != 1 {
350         cx.span_fatal(sp, fmt!("%s takes 1 argument.", name));
351     }
352
353     match tts[0] {
354         ast::tt_tok(_, token::LIT_STR(ident)) => cx.str_of(ident),
355         _ =>
356         cx.span_fatal(sp, fmt!("%s requires a string.", name))
357     }
358 }
359
360 pub fn get_exprs_from_tts(cx: @ExtCtxt,
361                           sp: span,
362                           tts: &[ast::token_tree]) -> ~[@ast::expr] {
363     let p = parse::new_parser_from_tts(cx.parse_sess(),
364                                        cx.cfg(),
365                                        tts.to_owned());
366     let mut es = ~[];
367     while *p.token != token::EOF {
368         if es.len() != 0 && !p.eat(&token::COMMA) {
369             cx.span_fatal(sp, "expected token: `,`");
370         }
371         es.push(p.parse_expr());
372     }
373     es
374 }
375
376 // in order to have some notion of scoping for macros,
377 // we want to implement the notion of a transformation
378 // environment.
379
380 // This environment maps Names to Transformers.
381 // Initially, this includes macro definitions and
382 // block directives.
383
384
385
386 // Actually, the following implementation is parameterized
387 // by both key and value types.
388
389 //impl question: how to implement it? Initially, the
390 // env will contain only macros, so it might be painful
391 // to add an empty frame for every context. Let's just
392 // get it working, first....
393
394 // NB! the mutability of the underlying maps means that
395 // if expansion is out-of-order, a deeper scope may be
396 // able to refer to a macro that was added to an enclosing
397 // scope lexically later than the deeper scope.
398
399 // Note on choice of representation: I've been pushed to
400 // use a top-level managed pointer by some difficulties
401 // with pushing and popping functionally, and the ownership
402 // issues.  As a result, the values returned by the table
403 // also need to be managed; the &'self ... type that Maps
404 // return won't work for things that need to get outside
405 // of that managed pointer.  The easiest way to do this
406 // is just to insist that the values in the tables are
407 // managed to begin with.
408
409 // a transformer env is either a base map or a map on top
410 // of another chain.
411 pub enum MapChain<K,V> {
412     BaseMapChain(~HashMap<K,@V>),
413     ConsMapChain(~HashMap<K,@V>,@mut MapChain<K,V>)
414 }
415
416
417 // get the map from an env frame
418 impl <K: Eq + Hash + IterBytes + 'static, V: 'static> MapChain<K,V>{
419     // Constructor. I don't think we need a zero-arg one.
420     fn new(init: ~HashMap<K,@V>) -> @mut MapChain<K,V> {
421         @mut BaseMapChain(init)
422     }
423
424     // add a new frame to the environment (functionally)
425     fn push_frame (@mut self) -> @mut MapChain<K,V> {
426         @mut ConsMapChain(~HashMap::new() ,self)
427     }
428
429 // no need for pop, it'll just be functional.
430
431     // utility fn...
432
433     // ugh: can't get this to compile with mut because of the
434     // lack of flow sensitivity.
435     fn get_map<'a>(&'a self) -> &'a HashMap<K,@V> {
436         match *self {
437             BaseMapChain (~ref map) => map,
438             ConsMapChain (~ref map,_) => map
439         }
440     }
441
442 // traits just don't work anywhere...?
443 //impl Map<Name,SyntaxExtension> for MapChain {
444
445     fn contains_key (&self, key: &K) -> bool {
446         match *self {
447             BaseMapChain (ref map) => map.contains_key(key),
448             ConsMapChain (ref map,ref rest) =>
449             (map.contains_key(key)
450              || rest.contains_key(key))
451         }
452     }
453     // should each_key and each_value operate on shadowed
454     // names? I think not.
455     // delaying implementing this....
456     fn each_key (&self, _f: &fn (&K)->bool) {
457         fail!("unimplemented 2013-02-15T10:01");
458     }
459
460     fn each_value (&self, _f: &fn (&V) -> bool) {
461         fail!("unimplemented 2013-02-15T10:02");
462     }
463
464     // Returns a copy of the value that the name maps to.
465     // Goes down the chain 'til it finds one (or bottom out).
466     fn find (&self, key: &K) -> Option<@V> {
467         match self.get_map().find (key) {
468             Some(ref v) => Some(**v),
469             None => match *self {
470                 BaseMapChain (_) => None,
471                 ConsMapChain (_,ref rest) => rest.find(key)
472             }
473         }
474     }
475
476     fn find_in_topmost_frame(&self, key: &K) -> Option<@V> {
477         let map = match *self {
478             BaseMapChain(ref map) => map,
479             ConsMapChain(ref map,_) => map
480         };
481         // strip one layer of indirection off the pointer.
482         map.find(key).map(|r| {**r})
483     }
484
485     // insert the binding into the top-level map
486     fn insert (&mut self, key: K, ext: @V) -> bool {
487         // can't abstract over get_map because of flow sensitivity...
488         match *self {
489             BaseMapChain (~ref mut map) => map.insert(key, ext),
490             ConsMapChain (~ref mut map,_) => map.insert(key,ext)
491         }
492     }
493     // insert the binding into the topmost frame for which the binding
494     // associated with 'n' exists and satisfies pred
495     // ... there are definitely some opportunities for abstraction
496     // here that I'm ignoring. (e.g., manufacturing a predicate on
497     // the maps in the chain, and using an abstract "find".
498     fn insert_into_frame(&mut self, key: K, ext: @V, n: K, pred: &fn(&@V)->bool) {
499         match *self {
500             BaseMapChain (~ref mut map) => {
501                 if satisfies_pred(map,&n,pred) {
502                     map.insert(key,ext);
503                 } else {
504                     fail!(~"expected map chain containing satisfying frame")
505                 }
506             },
507             ConsMapChain (~ref mut map, rest) => {
508                 if satisfies_pred(map,&n,|v|pred(v)) {
509                     map.insert(key,ext);
510                 } else {
511                     rest.insert_into_frame(key,ext,n,pred)
512                 }
513             }
514         }
515     }
516 }
517
518 // returns true if the binding for 'n' satisfies 'pred' in 'map'
519 fn satisfies_pred<K : Eq + Hash + IterBytes,V>(map : &mut HashMap<K,V>,
520                                                n: &K,
521                                                pred: &fn(&V)->bool)
522     -> bool {
523     match map.find(n) {
524         Some(ref v) => (pred(*v)),
525         None => false
526     }
527 }
528
529 #[cfg(test)]
530 mod test {
531     use super::MapChain;
532     use std::hashmap::HashMap;
533
534     #[test] fn testenv () {
535         let mut a = HashMap::new();
536         a.insert (@"abc",@15);
537         let m = MapChain::new(~a);
538         m.insert (@"def",@16);
539         // FIXME: #4492 (ICE)  assert_eq!(m.find(&@"abc"),Some(@15));
540         //  ....               assert_eq!(m.find(&@"def"),Some(@16));
541         assert_eq!(*(m.find(&@"abc").unwrap()),15);
542         assert_eq!(*(m.find(&@"def").unwrap()),16);
543         let n = m.push_frame();
544         // old bindings are still present:
545         assert_eq!(*(n.find(&@"abc").unwrap()),15);
546         assert_eq!(*(n.find(&@"def").unwrap()),16);
547         n.insert (@"def",@17);
548         // n shows the new binding
549         assert_eq!(*(n.find(&@"abc").unwrap()),15);
550         assert_eq!(*(n.find(&@"def").unwrap()),17);
551         // ... but m still has the old ones
552         // FIXME: #4492: assert_eq!(m.find(&@"abc"),Some(@15));
553         // FIXME: #4492: assert_eq!(m.find(&@"def"),Some(@16));
554         assert_eq!(*(m.find(&@"abc").unwrap()),15);
555         assert_eq!(*(m.find(&@"def").unwrap()),16);
556     }
557 }