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