]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/base.rs
Add initial support for a new formatting syntax
[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(intern(&"ifmt"),
143                             builtin_normal_tt(ext::ifmt::expand_syntax_ext));
144     syntax_expanders.insert(
145         intern(&"auto_encode"),
146         @SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
147     syntax_expanders.insert(
148         intern(&"auto_decode"),
149         @SE(ItemDecorator(ext::auto_encode::expand_auto_decode)));
150     syntax_expanders.insert(intern(&"env"),
151                             builtin_normal_tt(ext::env::expand_syntax_ext));
152     syntax_expanders.insert(intern("bytes"),
153                             builtin_normal_tt(ext::bytes::expand_syntax_ext));
154     syntax_expanders.insert(intern("concat_idents"),
155                             builtin_normal_tt(
156                                 ext::concat_idents::expand_syntax_ext));
157     syntax_expanders.insert(intern(&"log_syntax"),
158                             builtin_normal_tt(
159                                 ext::log_syntax::expand_syntax_ext));
160     syntax_expanders.insert(intern(&"deriving"),
161                             @SE(ItemDecorator(
162                                 ext::deriving::expand_meta_deriving)));
163
164     // Quasi-quoting expanders
165     syntax_expanders.insert(intern(&"quote_tokens"),
166                        builtin_normal_tt(ext::quote::expand_quote_tokens));
167     syntax_expanders.insert(intern(&"quote_expr"),
168                        builtin_normal_tt(ext::quote::expand_quote_expr));
169     syntax_expanders.insert(intern(&"quote_ty"),
170                        builtin_normal_tt(ext::quote::expand_quote_ty));
171     syntax_expanders.insert(intern(&"quote_item"),
172                        builtin_normal_tt(ext::quote::expand_quote_item));
173     syntax_expanders.insert(intern(&"quote_pat"),
174                        builtin_normal_tt(ext::quote::expand_quote_pat));
175     syntax_expanders.insert(intern(&"quote_stmt"),
176                        builtin_normal_tt(ext::quote::expand_quote_stmt));
177
178     syntax_expanders.insert(intern(&"line"),
179                             builtin_normal_tt(
180                                 ext::source_util::expand_line));
181     syntax_expanders.insert(intern(&"col"),
182                             builtin_normal_tt(
183                                 ext::source_util::expand_col));
184     syntax_expanders.insert(intern(&"file"),
185                             builtin_normal_tt(
186                                 ext::source_util::expand_file));
187     syntax_expanders.insert(intern(&"stringify"),
188                             builtin_normal_tt(
189                                 ext::source_util::expand_stringify));
190     syntax_expanders.insert(intern(&"include"),
191                             builtin_normal_tt(
192                                 ext::source_util::expand_include));
193     syntax_expanders.insert(intern(&"include_str"),
194                             builtin_normal_tt(
195                                 ext::source_util::expand_include_str));
196     syntax_expanders.insert(intern(&"include_bin"),
197                             builtin_normal_tt(
198                                 ext::source_util::expand_include_bin));
199     syntax_expanders.insert(intern(&"module_path"),
200                             builtin_normal_tt(
201                                 ext::source_util::expand_mod));
202     syntax_expanders.insert(intern(&"asm"),
203                             builtin_normal_tt(ext::asm::expand_asm));
204     syntax_expanders.insert(intern(&"cfg"),
205                             builtin_normal_tt(ext::cfg::expand_cfg));
206     syntax_expanders.insert(
207         intern(&"trace_macros"),
208         builtin_normal_tt(ext::trace_macros::expand_trace_macros));
209     MapChain::new(~syntax_expanders)
210 }
211
212 // One of these is made during expansion and incrementally updated as we go;
213 // when a macro expansion occurs, the resulting nodes have the backtrace()
214 // -> expn_info of their expansion context stored into their span.
215 pub struct ExtCtxt {
216     parse_sess: @mut parse::ParseSess,
217     cfg: ast::CrateConfig,
218     backtrace: @mut Option<@ExpnInfo>,
219
220     // These two @mut's should really not be here,
221     // but the self types for CtxtRepr are all wrong
222     // and there are bugs in the code for object
223     // types that make this hard to get right at the
224     // moment. - nmatsakis
225     mod_path: @mut ~[ast::ident],
226     trace_mac: @mut bool
227 }
228
229 impl ExtCtxt {
230     pub fn new(parse_sess: @mut parse::ParseSess, cfg: ast::CrateConfig)
231                -> @ExtCtxt {
232         @ExtCtxt {
233             parse_sess: parse_sess,
234             cfg: cfg,
235             backtrace: @mut None,
236             mod_path: @mut ~[],
237             trace_mac: @mut false
238         }
239     }
240
241     pub fn codemap(&self) -> @CodeMap { self.parse_sess.cm }
242     pub fn parse_sess(&self) -> @mut parse::ParseSess { self.parse_sess }
243     pub fn cfg(&self) -> ast::CrateConfig { self.cfg.clone() }
244     pub fn call_site(&self) -> span {
245         match *self.backtrace {
246             Some(@ExpnInfo {call_site: cs, _}) => cs,
247             None => self.bug("missing top span")
248         }
249     }
250     pub fn print_backtrace(&self) { }
251     pub fn backtrace(&self) -> Option<@ExpnInfo> { *self.backtrace }
252     pub fn mod_push(&self, i: ast::ident) { self.mod_path.push(i); }
253     pub fn mod_pop(&self) { self.mod_path.pop(); }
254     pub fn mod_path(&self) -> ~[ast::ident] { (*self.mod_path).clone() }
255     pub fn bt_push(&self, ei: codemap::ExpnInfo) {
256         match ei {
257             ExpnInfo {call_site: cs, callee: ref callee} => {
258                 *self.backtrace =
259                     Some(@ExpnInfo {
260                         call_site: span {lo: cs.lo, hi: cs.hi,
261                                          expn_info: *self.backtrace},
262                         callee: *callee});
263             }
264         }
265     }
266     pub fn bt_pop(&self) {
267         match *self.backtrace {
268             Some(@ExpnInfo {
269                 call_site: span {expn_info: prev, _}, _}) => {
270                 *self.backtrace = prev
271             }
272             _ => self.bug("tried to pop without a push")
273         }
274     }
275     pub fn span_fatal(&self, sp: span, msg: &str) -> ! {
276         self.print_backtrace();
277         self.parse_sess.span_diagnostic.span_fatal(sp, msg);
278     }
279     pub fn span_err(&self, sp: span, msg: &str) {
280         self.print_backtrace();
281         self.parse_sess.span_diagnostic.span_err(sp, msg);
282     }
283     pub fn span_warn(&self, sp: span, msg: &str) {
284         self.print_backtrace();
285         self.parse_sess.span_diagnostic.span_warn(sp, msg);
286     }
287     pub fn span_unimpl(&self, sp: span, msg: &str) -> ! {
288         self.print_backtrace();
289         self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
290     }
291     pub fn span_bug(&self, sp: span, msg: &str) -> ! {
292         self.print_backtrace();
293         self.parse_sess.span_diagnostic.span_bug(sp, msg);
294     }
295     pub fn bug(&self, msg: &str) -> ! {
296         self.print_backtrace();
297         self.parse_sess.span_diagnostic.handler().bug(msg);
298     }
299     pub fn next_id(&self) -> ast::NodeId {
300         parse::next_node_id(self.parse_sess)
301     }
302     pub fn trace_macros(&self) -> bool {
303         *self.trace_mac
304     }
305     pub fn set_trace_macros(&self, x: bool) {
306         *self.trace_mac = x
307     }
308     pub fn str_of(&self, id: ast::ident) -> @str {
309         ident_to_str(&id)
310     }
311     pub fn ident_of(&self, st: &str) -> ast::ident {
312         str_to_ident(st)
313     }
314 }
315
316 pub fn expr_to_str(cx: @ExtCtxt, expr: @ast::expr, err_msg: ~str) -> @str {
317     match expr.node {
318       ast::expr_lit(l) => match l.node {
319         ast::lit_str(s) => s,
320         _ => cx.span_fatal(l.span, err_msg)
321       },
322       _ => cx.span_fatal(expr.span, err_msg)
323     }
324 }
325
326 pub fn expr_to_ident(cx: @ExtCtxt,
327                      expr: @ast::expr,
328                      err_msg: &str) -> ast::ident {
329     match expr.node {
330       ast::expr_path(ref p) => {
331         if p.types.len() > 0u || p.idents.len() != 1u {
332             cx.span_fatal(expr.span, err_msg);
333         }
334         return p.idents[0];
335       }
336       _ => cx.span_fatal(expr.span, err_msg)
337     }
338 }
339
340 pub fn check_zero_tts(cx: @ExtCtxt, sp: span, tts: &[ast::token_tree],
341                       name: &str) {
342     if tts.len() != 0 {
343         cx.span_fatal(sp, fmt!("%s takes no arguments", name));
344     }
345 }
346
347 pub fn get_single_str_from_tts(cx: @ExtCtxt,
348                                sp: span,
349                                tts: &[ast::token_tree],
350                                name: &str) -> @str {
351     if tts.len() != 1 {
352         cx.span_fatal(sp, fmt!("%s takes 1 argument.", name));
353     }
354
355     match tts[0] {
356         ast::tt_tok(_, token::LIT_STR(ident)) => cx.str_of(ident),
357         _ =>
358         cx.span_fatal(sp, fmt!("%s requires a string.", name))
359     }
360 }
361
362 pub fn get_exprs_from_tts(cx: @ExtCtxt,
363                           sp: span,
364                           tts: &[ast::token_tree]) -> ~[@ast::expr] {
365     let p = parse::new_parser_from_tts(cx.parse_sess(),
366                                        cx.cfg(),
367                                        tts.to_owned());
368     let mut es = ~[];
369     while *p.token != token::EOF {
370         if es.len() != 0 && !p.eat(&token::COMMA) {
371             cx.span_fatal(sp, "expected token: `,`");
372         }
373         es.push(p.parse_expr());
374     }
375     es
376 }
377
378 // in order to have some notion of scoping for macros,
379 // we want to implement the notion of a transformation
380 // environment.
381
382 // This environment maps Names to Transformers.
383 // Initially, this includes macro definitions and
384 // block directives.
385
386
387
388 // Actually, the following implementation is parameterized
389 // by both key and value types.
390
391 //impl question: how to implement it? Initially, the
392 // env will contain only macros, so it might be painful
393 // to add an empty frame for every context. Let's just
394 // get it working, first....
395
396 // NB! the mutability of the underlying maps means that
397 // if expansion is out-of-order, a deeper scope may be
398 // able to refer to a macro that was added to an enclosing
399 // scope lexically later than the deeper scope.
400
401 // Note on choice of representation: I've been pushed to
402 // use a top-level managed pointer by some difficulties
403 // with pushing and popping functionally, and the ownership
404 // issues.  As a result, the values returned by the table
405 // also need to be managed; the &'self ... type that Maps
406 // return won't work for things that need to get outside
407 // of that managed pointer.  The easiest way to do this
408 // is just to insist that the values in the tables are
409 // managed to begin with.
410
411 // a transformer env is either a base map or a map on top
412 // of another chain.
413 pub enum MapChain<K,V> {
414     BaseMapChain(~HashMap<K,@V>),
415     ConsMapChain(~HashMap<K,@V>,@mut MapChain<K,V>)
416 }
417
418
419 // get the map from an env frame
420 impl <K: Eq + Hash + IterBytes + 'static, V: 'static> MapChain<K,V>{
421     // Constructor. I don't think we need a zero-arg one.
422     fn new(init: ~HashMap<K,@V>) -> @mut MapChain<K,V> {
423         @mut BaseMapChain(init)
424     }
425
426     // add a new frame to the environment (functionally)
427     fn push_frame (@mut self) -> @mut MapChain<K,V> {
428         @mut ConsMapChain(~HashMap::new() ,self)
429     }
430
431 // no need for pop, it'll just be functional.
432
433     // utility fn...
434
435     // ugh: can't get this to compile with mut because of the
436     // lack of flow sensitivity.
437     fn get_map<'a>(&'a self) -> &'a HashMap<K,@V> {
438         match *self {
439             BaseMapChain (~ref map) => map,
440             ConsMapChain (~ref map,_) => map
441         }
442     }
443
444 // traits just don't work anywhere...?
445 //impl Map<Name,SyntaxExtension> for MapChain {
446
447     fn contains_key (&self, key: &K) -> bool {
448         match *self {
449             BaseMapChain (ref map) => map.contains_key(key),
450             ConsMapChain (ref map,ref rest) =>
451             (map.contains_key(key)
452              || rest.contains_key(key))
453         }
454     }
455     // should each_key and each_value operate on shadowed
456     // names? I think not.
457     // delaying implementing this....
458     fn each_key (&self, _f: &fn (&K)->bool) {
459         fail!("unimplemented 2013-02-15T10:01");
460     }
461
462     fn each_value (&self, _f: &fn (&V) -> bool) {
463         fail!("unimplemented 2013-02-15T10:02");
464     }
465
466     // Returns a copy of the value that the name maps to.
467     // Goes down the chain 'til it finds one (or bottom out).
468     fn find (&self, key: &K) -> Option<@V> {
469         match self.get_map().find (key) {
470             Some(ref v) => Some(**v),
471             None => match *self {
472                 BaseMapChain (_) => None,
473                 ConsMapChain (_,ref rest) => rest.find(key)
474             }
475         }
476     }
477
478     fn find_in_topmost_frame(&self, key: &K) -> Option<@V> {
479         let map = match *self {
480             BaseMapChain(ref map) => map,
481             ConsMapChain(ref map,_) => map
482         };
483         // strip one layer of indirection off the pointer.
484         map.find(key).map(|r| {**r})
485     }
486
487     // insert the binding into the top-level map
488     fn insert (&mut self, key: K, ext: @V) -> bool {
489         // can't abstract over get_map because of flow sensitivity...
490         match *self {
491             BaseMapChain (~ref mut map) => map.insert(key, ext),
492             ConsMapChain (~ref mut map,_) => map.insert(key,ext)
493         }
494     }
495     // insert the binding into the topmost frame for which the binding
496     // associated with 'n' exists and satisfies pred
497     // ... there are definitely some opportunities for abstraction
498     // here that I'm ignoring. (e.g., manufacturing a predicate on
499     // the maps in the chain, and using an abstract "find".
500     fn insert_into_frame(&mut self, key: K, ext: @V, n: K, pred: &fn(&@V)->bool) {
501         match *self {
502             BaseMapChain (~ref mut map) => {
503                 if satisfies_pred(map,&n,pred) {
504                     map.insert(key,ext);
505                 } else {
506                     fail!(~"expected map chain containing satisfying frame")
507                 }
508             },
509             ConsMapChain (~ref mut map, rest) => {
510                 if satisfies_pred(map,&n,|v|pred(v)) {
511                     map.insert(key,ext);
512                 } else {
513                     rest.insert_into_frame(key,ext,n,pred)
514                 }
515             }
516         }
517     }
518 }
519
520 // returns true if the binding for 'n' satisfies 'pred' in 'map'
521 fn satisfies_pred<K : Eq + Hash + IterBytes,V>(map : &mut HashMap<K,V>,
522                                                n: &K,
523                                                pred: &fn(&V)->bool)
524     -> bool {
525     match map.find(n) {
526         Some(ref v) => (pred(*v)),
527         None => false
528     }
529 }
530
531 #[cfg(test)]
532 mod test {
533     use super::MapChain;
534     use std::hashmap::HashMap;
535
536     #[test] fn testenv () {
537         let mut a = HashMap::new();
538         a.insert (@"abc",@15);
539         let m = MapChain::new(~a);
540         m.insert (@"def",@16);
541         // FIXME: #4492 (ICE)  assert_eq!(m.find(&@"abc"),Some(@15));
542         //  ....               assert_eq!(m.find(&@"def"),Some(@16));
543         assert_eq!(*(m.find(&@"abc").unwrap()),15);
544         assert_eq!(*(m.find(&@"def").unwrap()),16);
545         let n = m.push_frame();
546         // old bindings are still present:
547         assert_eq!(*(n.find(&@"abc").unwrap()),15);
548         assert_eq!(*(n.find(&@"def").unwrap()),16);
549         n.insert (@"def",@17);
550         // n shows the new binding
551         assert_eq!(*(n.find(&@"abc").unwrap()),15);
552         assert_eq!(*(n.find(&@"def").unwrap()),17);
553         // ... but m still has the old ones
554         // FIXME: #4492: assert_eq!(m.find(&@"abc"),Some(@15));
555         // FIXME: #4492: assert_eq!(m.find(&@"def"),Some(@16));
556         assert_eq!(*(m.find(&@"abc").unwrap()),15);
557         assert_eq!(*(m.find(&@"def").unwrap()),16);
558     }
559 }