]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/expand.rs
Add initial support for a new formatting syntax
[rust.git] / src / libsyntax / ext / expand.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 ast::{Block, Crate, NodeId, expr_, expr_mac, ident, mac_invoc_tt};
12 use ast::{item_mac, stmt_, stmt_mac, stmt_expr, stmt_semi};
13 use ast::{illegal_ctxt};
14 use ast;
15 use ast_util::{new_rename, new_mark, resolve};
16 use attr;
17 use attr::AttrMetaMethods;
18 use codemap;
19 use codemap::{span, spanned, ExpnInfo, NameAndSpan};
20 use ext::base::*;
21 use fold::*;
22 use parse;
23 use parse::{parse_item_from_source_str};
24 use parse::token;
25 use parse::token::{ident_to_str, intern};
26 use visit;
27 use visit::Visitor;
28
29 use std::vec;
30
31 pub fn expand_expr(extsbox: @mut SyntaxEnv,
32                    cx: @ExtCtxt,
33                    e: &expr_,
34                    s: span,
35                    fld: @ast_fold,
36                    orig: @fn(&expr_, span, @ast_fold) -> (expr_, span))
37                 -> (expr_, span) {
38     match *e {
39         // expr_mac should really be expr_ext or something; it's the
40         // entry-point for all syntax extensions.
41         expr_mac(ref mac) => {
42             match (*mac).node {
43                 // Token-tree macros:
44                 mac_invoc_tt(ref pth, ref tts) => {
45                     if (pth.idents.len() > 1u) {
46                         cx.span_fatal(
47                             pth.span,
48                             fmt!("expected macro name without module \
49                                   separators"));
50                     }
51                     let extname = &pth.idents[0];
52                     let extnamestr = ident_to_str(extname);
53                     // leaving explicit deref here to highlight unbox op:
54                     match (*extsbox).find(&extname.name) {
55                         None => {
56                             cx.span_fatal(
57                                 pth.span,
58                                 fmt!("macro undefined: '%s'", extnamestr))
59                         }
60                         Some(@SE(NormalTT(SyntaxExpanderTT{
61                             expander: exp,
62                             span: exp_sp
63                         }))) => {
64                             cx.bt_push(ExpnInfo {
65                                 call_site: s,
66                                 callee: NameAndSpan {
67                                     name: extnamestr,
68                                     span: exp_sp,
69                                 },
70                             });
71
72                             let expanded = match exp(cx, mac.span, *tts) {
73                                 MRExpr(e) => e,
74                                 MRAny(expr_maker,_,_) => expr_maker(),
75                                 _ => {
76                                     cx.span_fatal(
77                                         pth.span,
78                                         fmt!(
79                                             "non-expr macro in expr pos: %s",
80                                             extnamestr
81                                         )
82                                     )
83                                 }
84                             };
85
86                             //keep going, outside-in
87                             let fully_expanded =
88                                 fld.fold_expr(expanded).node.clone();
89                             cx.bt_pop();
90
91                             (fully_expanded, s)
92                         }
93                         _ => {
94                             cx.span_fatal(
95                                 pth.span,
96                                 fmt!("'%s' is not a tt-style macro", extnamestr)
97                             )
98                         }
99                     }
100                 }
101             }
102         }
103
104         // Desugar expr_for_loop
105         // From: `for <src_pat> in <src_expr> <src_loop_block>`
106         ast::expr_for_loop(src_pat, src_expr, ref src_loop_block) => {
107             let src_pat = src_pat.clone();
108             let src_expr = src_expr.clone();
109
110             // Expand any interior macros etc.
111             // NB: we don't fold pats yet. Curious.
112             let src_expr = fld.fold_expr(src_expr).clone();
113             let src_loop_block = fld.fold_block(src_loop_block).clone();
114
115             let span = s;
116             let lo = s.lo;
117             let hi = s.hi;
118
119             pub fn mk_expr(cx: @ExtCtxt, span: span,
120                            node: expr_) -> @ast::expr {
121                 @ast::expr {
122                     id: cx.next_id(),
123                     node: node,
124                     span: span,
125                 }
126             }
127
128             fn mk_block(cx: @ExtCtxt,
129                         stmts: &[@ast::stmt],
130                         expr: Option<@ast::expr>,
131                         span: span) -> ast::Block {
132                 ast::Block {
133                     view_items: ~[],
134                     stmts: stmts.to_owned(),
135                     expr: expr,
136                     id: cx.next_id(),
137                     rules: ast::DefaultBlock,
138                     span: span,
139                 }
140             }
141
142             fn mk_simple_path(ident: ast::ident, span: span) -> ast::Path {
143                 ast::Path {
144                     span: span,
145                     global: false,
146                     idents: ~[ident],
147                     rp: None,
148                     types: ~[]
149                 }
150             }
151
152             // to:
153             //
154             // {
155             //   let _i = &mut <src_expr>;
156             //   loop {
157             //       match i.next() {
158             //           None => break,
159             //           Some(<src_pat>) => <src_loop_block>
160             //       }
161             //   }
162             // }
163
164             let local_ident = token::gensym_ident("i");
165             let some_ident = token::str_to_ident("Some");
166             let none_ident = token::str_to_ident("None");
167             let next_ident = token::str_to_ident("next");
168
169             let local_path_1 = mk_simple_path(local_ident, span);
170             let local_path_2 = mk_simple_path(local_ident, span);
171             let some_path = mk_simple_path(some_ident, span);
172             let none_path = mk_simple_path(none_ident, span);
173
174             // `let i = &mut <src_expr>`
175             let iter_decl_stmt = {
176                 let ty = ast::Ty {
177                     id: cx.next_id(),
178                     node: ast::ty_infer,
179                     span: span
180                 };
181                 let local = @ast::Local {
182                     is_mutbl: false,
183                     ty: ty,
184                     pat: @ast::pat {
185                         id: cx.next_id(),
186                         node: ast::pat_ident(ast::bind_infer, local_path_1, None),
187                         span: src_expr.span
188                     },
189                     init: Some(mk_expr(cx, src_expr.span,
190                                        ast::expr_addr_of(ast::m_mutbl, src_expr))),
191                     id: cx.next_id(),
192                     span: src_expr.span,
193                 };
194                 let e = @spanned(src_expr.span.lo,
195                                  src_expr.span.hi,
196                                  ast::decl_local(local));
197                 @spanned(lo, hi, ast::stmt_decl(e, cx.next_id()))
198             };
199
200             // `None => break;`
201             let none_arm = {
202                 let break_expr = mk_expr(cx, span, ast::expr_break(None));
203                 let break_stmt = @spanned(lo, hi, ast::stmt_expr(break_expr, cx.next_id()));
204                 let none_block = mk_block(cx, [break_stmt], None, span);
205                 let none_pat = @ast::pat {
206                     id: cx.next_id(),
207                     node: ast::pat_ident(ast::bind_infer, none_path, None),
208                     span: span
209                 };
210                 ast::arm {
211                     pats: ~[none_pat],
212                     guard: None,
213                     body: none_block
214                 }
215             };
216
217             // `Some(<src_pat>) => <src_loop_block>`
218             let some_arm = {
219                 let pat = @ast::pat {
220                     id: cx.next_id(),
221                     node: ast::pat_enum(some_path, Some(~[src_pat])),
222                     span: src_pat.span
223                 };
224                 ast::arm {
225                     pats: ~[pat],
226                     guard: None,
227                     body: src_loop_block
228                 }
229             };
230
231             // `match i.next() { ... }`
232             let match_stmt = {
233                 let local_expr = mk_expr(cx, span, ast::expr_path(local_path_2));
234                 let next_call_expr = mk_expr(cx, span,
235                                              ast::expr_method_call(cx.next_id(),
236                                                                    local_expr, next_ident,
237                                                                    ~[], ~[], ast::NoSugar));
238                 let match_expr = mk_expr(cx, span, ast::expr_match(next_call_expr,
239                                                                    ~[none_arm, some_arm]));
240                 @spanned(lo, hi, ast::stmt_expr(match_expr, cx.next_id()))
241             };
242
243             // `loop { ... }`
244             let loop_block = {
245                 let loop_body_block = mk_block(cx, [match_stmt], None, span);
246                 let loop_body_expr = mk_expr(cx, span, ast::expr_loop(loop_body_block, None));
247                 let loop_body_stmt = @spanned(lo, hi, ast::stmt_expr(loop_body_expr, cx.next_id()));
248                 mk_block(cx, [iter_decl_stmt,
249                               loop_body_stmt],
250                          None, span)
251             };
252
253             (ast::expr_block(loop_block), span)
254         }
255
256         _ => orig(e, s, fld)
257     }
258 }
259
260 // This is a secondary mechanism for invoking syntax extensions on items:
261 // "decorator" attributes, such as #[auto_encode]. These are invoked by an
262 // attribute prefixing an item, and are interpreted by feeding the item
263 // through the named attribute _as a syntax extension_ and splicing in the
264 // resulting item vec into place in favour of the decorator. Note that
265 // these do _not_ work for macro extensions, just ItemDecorator ones.
266 //
267 // NB: there is some redundancy between this and expand_item, below, and
268 // they might benefit from some amount of semantic and language-UI merger.
269 pub fn expand_mod_items(extsbox: @mut SyntaxEnv,
270                         cx: @ExtCtxt,
271                         module_: &ast::_mod,
272                         fld: @ast_fold,
273                         orig: @fn(&ast::_mod, @ast_fold) -> ast::_mod)
274                      -> ast::_mod {
275
276     // Fold the contents first:
277     let module_ = orig(module_, fld);
278
279     // For each item, look through the attributes.  If any of them are
280     // decorated with "item decorators", then use that function to transform
281     // the item into a new set of items.
282     let new_items = do vec::flat_map(module_.items) |item| {
283         do item.attrs.rev_iter().fold(~[*item]) |items, attr| {
284             let mname = attr.name();
285
286             match (*extsbox).find(&intern(mname)) {
287               Some(@SE(ItemDecorator(dec_fn))) => {
288                   cx.bt_push(ExpnInfo {
289                       call_site: attr.span,
290                       callee: NameAndSpan {
291                           name: mname,
292                           span: None
293                       }
294                   });
295                   let r = dec_fn(cx, attr.span, attr.node.value, items);
296                   cx.bt_pop();
297                   r
298               },
299               _ => items,
300             }
301         }
302     };
303
304     ast::_mod { items: new_items, ..module_ }
305 }
306
307
308 // eval $e with a new exts frame:
309 macro_rules! with_exts_frame (
310     ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
311     ({let extsbox = $extsboxexpr;
312       let oldexts = *extsbox;
313       *extsbox = oldexts.push_frame();
314       extsbox.insert(intern(special_block_name),
315                      @BlockInfo(BlockInfo{macros_escape:$macros_escape,pending_renames:@mut ~[]}));
316       let result = $e;
317       *extsbox = oldexts;
318       result
319      })
320 )
321
322 static special_block_name : &'static str = " block";
323
324 // When we enter a module, record it, for the sake of `module!`
325 pub fn expand_item(extsbox: @mut SyntaxEnv,
326                    cx: @ExtCtxt,
327                    it: @ast::item,
328                    fld: @ast_fold,
329                    orig: @fn(@ast::item, @ast_fold) -> Option<@ast::item>)
330                 -> Option<@ast::item> {
331     // need to do expansion first... it might turn out to be a module.
332     let maybe_it = match it.node {
333       ast::item_mac(*) => expand_item_mac(extsbox, cx, it, fld),
334       _ => Some(it)
335     };
336     match maybe_it {
337       Some(it) => {
338           match it.node {
339               ast::item_mod(_) | ast::item_foreign_mod(_) => {
340                   cx.mod_push(it.ident);
341                   let macro_escape = contains_macro_escape(it.attrs);
342                   let result = with_exts_frame!(extsbox,macro_escape,orig(it,fld));
343                   cx.mod_pop();
344                   result
345               }
346               _ => orig(it,fld)
347           }
348       }
349       None => None
350     }
351 }
352
353 // does this attribute list contain "macro_escape" ?
354 pub fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
355     attr::contains_name(attrs, "macro_escape")
356 }
357
358 // Support for item-position macro invocations, exactly the same
359 // logic as for expression-position macro invocations.
360 pub fn expand_item_mac(extsbox: @mut SyntaxEnv,
361                        cx: @ExtCtxt, it: @ast::item,
362                        fld: @ast_fold)
363                     -> Option<@ast::item> {
364     let (pth, tts) = match it.node {
365         item_mac(codemap::spanned { node: mac_invoc_tt(ref pth, ref tts), _}) => {
366             (pth, (*tts).clone())
367         }
368         _ => cx.span_bug(it.span, "invalid item macro invocation")
369     };
370
371     let extname = &pth.idents[0];
372     let extnamestr = ident_to_str(extname);
373     let expanded = match (*extsbox).find(&extname.name) {
374         None => cx.span_fatal(pth.span,
375                               fmt!("macro undefined: '%s!'", extnamestr)),
376
377         Some(@SE(NormalTT(ref expand))) => {
378             if it.ident != parse::token::special_idents::invalid {
379                 cx.span_fatal(pth.span,
380                               fmt!("macro %s! expects no ident argument, \
381                                     given '%s'", extnamestr,
382                                    ident_to_str(&it.ident)));
383             }
384             cx.bt_push(ExpnInfo {
385                 call_site: it.span,
386                 callee: NameAndSpan {
387                     name: extnamestr,
388                     span: expand.span
389                 }
390             });
391             ((*expand).expander)(cx, it.span, tts)
392         }
393         Some(@SE(IdentTT(ref expand))) => {
394             if it.ident == parse::token::special_idents::invalid {
395                 cx.span_fatal(pth.span,
396                               fmt!("macro %s! expects an ident argument",
397                                    extnamestr));
398             }
399             cx.bt_push(ExpnInfo {
400                 call_site: it.span,
401                 callee: NameAndSpan {
402                     name: extnamestr,
403                     span: expand.span
404                 }
405             });
406             ((*expand).expander)(cx, it.span, it.ident, tts)
407         }
408         _ => cx.span_fatal(
409             it.span, fmt!("%s! is not legal in item position", extnamestr))
410     };
411
412     let maybe_it = match expanded {
413         MRItem(it) => fld.fold_item(it),
414         MRExpr(_) => cx.span_fatal(pth.span,
415                                    fmt!("expr macro in item position: %s", extnamestr)),
416         MRAny(_, item_maker, _) => item_maker().chain(|i| {fld.fold_item(i)}),
417         MRDef(ref mdef) => {
418             insert_macro(*extsbox,intern(mdef.name), @SE((*mdef).ext));
419             None
420         }
421     };
422     cx.bt_pop();
423     return maybe_it;
424 }
425
426
427 // insert a macro into the innermost frame that doesn't have the
428 // macro_escape tag.
429 fn insert_macro(exts: SyntaxEnv, name: ast::Name, transformer: @Transformer) {
430     let is_non_escaping_block =
431         |t : &@Transformer| -> bool{
432         match t {
433             &@BlockInfo(BlockInfo {macros_escape:false,_}) => true,
434             &@BlockInfo(BlockInfo {_}) => false,
435             _ => fail!(fmt!("special identifier %? was bound to a non-BlockInfo",
436                             special_block_name))
437         }
438     };
439     exts.insert_into_frame(name,transformer,intern(special_block_name),
440                            is_non_escaping_block)
441 }
442
443 // expand a stmt
444 pub fn expand_stmt(extsbox: @mut SyntaxEnv,
445                    cx: @ExtCtxt,
446                    s: &stmt_,
447                    sp: span,
448                    fld: @ast_fold,
449                    orig: @fn(&stmt_, span, @ast_fold)
450                              -> (Option<stmt_>, span))
451                 -> (Option<stmt_>, span) {
452     let (mac, pth, tts, semi) = match *s {
453         stmt_mac(ref mac, semi) => {
454             match mac.node {
455                 mac_invoc_tt(ref pth, ref tts) => {
456                     ((*mac).clone(), pth, (*tts).clone(), semi)
457                 }
458             }
459         }
460         _ => return orig(s, sp, fld)
461     };
462     if (pth.idents.len() > 1u) {
463         cx.span_fatal(
464             pth.span,
465             fmt!("expected macro name without module \
466                   separators"));
467     }
468     let extname = &pth.idents[0];
469     let extnamestr = ident_to_str(extname);
470     let (fully_expanded, sp) = match (*extsbox).find(&extname.name) {
471         None =>
472             cx.span_fatal(pth.span, fmt!("macro undefined: '%s'", extnamestr)),
473
474         Some(@SE(NormalTT(
475             SyntaxExpanderTT{expander: exp, span: exp_sp}))) => {
476             cx.bt_push(ExpnInfo {
477                 call_site: sp,
478                 callee: NameAndSpan { name: extnamestr, span: exp_sp }
479             });
480             let expanded = match exp(cx, mac.span, tts) {
481                 MRExpr(e) =>
482                     @codemap::spanned { node: stmt_expr(e, cx.next_id()),
483                                     span: e.span},
484                 MRAny(_,_,stmt_mkr) => stmt_mkr(),
485                 _ => cx.span_fatal(
486                     pth.span,
487                     fmt!("non-stmt macro in stmt pos: %s", extnamestr))
488             };
489
490             //keep going, outside-in
491             let fully_expanded = match fld.fold_stmt(expanded) {
492                 Some(stmt) => {
493                     let fully_expanded = &stmt.node;
494                     cx.bt_pop();
495                     (*fully_expanded).clone()
496                 }
497                 None => {
498                     cx.span_fatal(pth.span,
499                                   "macro didn't expand to a statement")
500                 }
501             };
502
503             (fully_expanded, sp)
504         }
505
506         _ => {
507             cx.span_fatal(pth.span,
508                           fmt!("'%s' is not a tt-style macro", extnamestr))
509         }
510     };
511
512     (match fully_expanded {
513         stmt_expr(e, stmt_id) if semi => Some(stmt_semi(e, stmt_id)),
514         _ => { Some(fully_expanded) } /* might already have a semi */
515     }, sp)
516
517 }
518
519 #[deriving(Clone)]
520 struct NewNameFinderContext {
521     ident_accumulator: @mut ~[ast::ident],
522 }
523
524 impl Visitor<()> for NewNameFinderContext {
525     fn visit_pat(@mut self, pattern: @ast::pat, _: ()) {
526         match *pattern {
527             // we found a pat_ident!
528             ast::pat {
529                 id: _,
530                 node: ast::pat_ident(_, ref path, ref inner),
531                 span: _
532             } => {
533                 match path {
534                     // a path of length one:
535                     &ast::Path {
536                         global: false,
537                         idents: [id],
538                         span: _,
539                         rp: _,
540                         types: _
541                     } => self.ident_accumulator.push(id),
542                     // I believe these must be enums...
543                     _ => ()
544                 }
545                 // visit optional subpattern of pat_ident:
546                 for subpat in inner.iter() {
547                     self.visit_pat(*subpat, ())
548                 }
549             }
550             // use the default traversal for non-pat_idents
551             _ => visit::visit_pat(self as @Visitor<()>, pattern, ())
552         }
553     }
554
555     // XXX: Methods below can become default methods.
556
557     fn visit_mod(@mut self, module: &ast::_mod, _: span, _: NodeId, _: ()) {
558         visit::visit_mod(self as @Visitor<()>, module, ())
559     }
560
561     fn visit_view_item(@mut self, view_item: &ast::view_item, _: ()) {
562         visit::visit_view_item(self as @Visitor<()>, view_item, ())
563     }
564
565     fn visit_item(@mut self, item: @ast::item, _: ()) {
566         visit::visit_item(self as @Visitor<()>, item, ())
567     }
568
569     fn visit_foreign_item(@mut self,
570                           foreign_item: @ast::foreign_item,
571                           _: ()) {
572         visit::visit_foreign_item(self as @Visitor<()>, foreign_item, ())
573     }
574
575     fn visit_local(@mut self, local: @ast::Local, _: ()) {
576         visit::visit_local(self as @Visitor<()>, local, ())
577     }
578
579     fn visit_block(@mut self, block: &ast::Block, _: ()) {
580         visit::visit_block(self as @Visitor<()>, block, ())
581     }
582
583     fn visit_stmt(@mut self, stmt: @ast::stmt, _: ()) {
584         visit::visit_stmt(self as @Visitor<()>, stmt, ())
585     }
586
587     fn visit_arm(@mut self, arm: &ast::arm, _: ()) {
588         visit::visit_arm(self as @Visitor<()>, arm, ())
589     }
590
591     fn visit_decl(@mut self, decl: @ast::decl, _: ()) {
592         visit::visit_decl(self as @Visitor<()>, decl, ())
593     }
594
595     fn visit_expr(@mut self, expr: @ast::expr, _: ()) {
596         visit::visit_expr(self as @Visitor<()>, expr, ())
597     }
598
599     fn visit_expr_post(@mut self, _: @ast::expr, _: ()) {
600         // Empty!
601     }
602
603     fn visit_ty(@mut self, typ: &ast::Ty, _: ()) {
604         visit::visit_ty(self as @Visitor<()>, typ, ())
605     }
606
607     fn visit_generics(@mut self, generics: &ast::Generics, _: ()) {
608         visit::visit_generics(self as @Visitor<()>, generics, ())
609     }
610
611     fn visit_fn(@mut self,
612                 function_kind: &visit::fn_kind,
613                 function_declaration: &ast::fn_decl,
614                 block: &ast::Block,
615                 span: span,
616                 node_id: NodeId,
617                 _: ()) {
618         visit::visit_fn(self as @Visitor<()>,
619                         function_kind,
620                         function_declaration,
621                         block,
622                         span,
623                         node_id,
624                         ())
625     }
626
627     fn visit_ty_method(@mut self, ty_method: &ast::TypeMethod, _: ()) {
628         visit::visit_ty_method(self as @Visitor<()>, ty_method, ())
629     }
630
631     fn visit_trait_method(@mut self,
632                           trait_method: &ast::trait_method,
633                           _: ()) {
634         visit::visit_trait_method(self as @Visitor<()>, trait_method, ())
635     }
636
637     fn visit_struct_def(@mut self,
638                         struct_def: @ast::struct_def,
639                         ident: ident,
640                         generics: &ast::Generics,
641                         node_id: NodeId,
642                         _: ()) {
643         visit::visit_struct_def(self as @Visitor<()>,
644                                 struct_def,
645                                 ident,
646                                 generics,
647                                 node_id,
648                                 ())
649     }
650
651     fn visit_struct_field(@mut self,
652                           struct_field: @ast::struct_field,
653                           _: ()) {
654         visit::visit_struct_field(self as @Visitor<()>, struct_field, ())
655     }
656 }
657
658 // return a visitor that extracts the pat_ident paths
659 // from a given pattern and puts them in a mutable
660 // array (passed in to the traversal)
661 pub fn new_name_finder(idents: @mut ~[ast::ident]) -> @Visitor<()> {
662     let context = @mut NewNameFinderContext {
663         ident_accumulator: idents,
664     };
665     context as @Visitor<()>
666 }
667
668 pub fn expand_block(extsbox: @mut SyntaxEnv,
669                     _cx: @ExtCtxt,
670                     blk: &Block,
671                     fld: @ast_fold,
672                     orig: @fn(&Block, @ast_fold) -> Block)
673                  -> Block {
674     // see note below about treatment of exts table
675     with_exts_frame!(extsbox,false,orig(blk,fld))
676 }
677
678
679 // get the (innermost) BlockInfo from an exts stack
680 fn get_block_info(exts : SyntaxEnv) -> BlockInfo {
681     match exts.find_in_topmost_frame(&intern(special_block_name)) {
682         Some(@BlockInfo(bi)) => bi,
683         _ => fail!(fmt!("special identifier %? was bound to a non-BlockInfo",
684                        @" block"))
685     }
686 }
687
688
689 // given a mutable list of renames, return a tree-folder that applies those
690 // renames.
691 fn renames_to_fold(renames : @mut ~[(ast::ident,ast::Name)]) -> @ast_fold {
692     let afp = default_ast_fold();
693     let f_pre = @AstFoldFns {
694         fold_ident: |id,_| {
695             // the individual elements are memoized... it would
696             // also be possible to memoize on the whole list at once.
697             let new_ctxt = renames.iter().fold(id.ctxt,|ctxt,&(from,to)| {
698                 new_rename(from,to,ctxt)
699             });
700             ast::ident{name:id.name,ctxt:new_ctxt}
701         },
702         .. *afp
703     };
704     make_fold(f_pre)
705 }
706
707 // perform a bunch of renames
708 fn apply_pending_renames(folder : @ast_fold, stmt : ast::stmt) -> @ast::stmt {
709     match folder.fold_stmt(&stmt) {
710         Some(s) => s,
711         None => fail!(fmt!("renaming of stmt produced None"))
712     }
713 }
714
715
716
717 pub fn new_span(cx: @ExtCtxt, sp: span) -> span {
718     /* this discards information in the case of macro-defining macros */
719     return span {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()};
720 }
721
722 // FIXME (#2247): this is a moderately bad kludge to inject some macros into
723 // the default compilation environment. It would be much nicer to use
724 // a mechanism like syntax_quote to ensure hygiene.
725
726 pub fn std_macros() -> @str {
727     return
728 @"mod __std_macros {
729     #[macro_escape];
730     #[doc(hidden)];
731
732     macro_rules! ignore (($($x:tt)*) => (()))
733
734     macro_rules! error (
735         ($arg:expr) => (
736             __log(1u32, fmt!( \"%?\", $arg ))
737         );
738         ($( $arg:expr ),+) => (
739             __log(1u32, fmt!( $($arg),+ ))
740         )
741     )
742
743     macro_rules! warn (
744         ($arg:expr) => (
745             __log(2u32, fmt!( \"%?\", $arg ))
746         );
747         ($( $arg:expr ),+) => (
748             __log(2u32, fmt!( $($arg),+ ))
749         )
750     )
751
752     macro_rules! info (
753         ($arg:expr) => (
754             __log(3u32, fmt!( \"%?\", $arg ))
755         );
756         ($( $arg:expr ),+) => (
757             __log(3u32, fmt!( $($arg),+ ))
758         )
759     )
760
761     // conditionally define debug!, but keep it type checking even
762     // in non-debug builds.
763     macro_rules! __debug (
764         ($arg:expr) => (
765             __log(4u32, fmt!( \"%?\", $arg ))
766         );
767         ($( $arg:expr ),+) => (
768             __log(4u32, fmt!( $($arg),+ ))
769         )
770     )
771
772     #[cfg(debug)]
773     #[macro_escape]
774     mod debug_macro {
775         macro_rules! debug (($($arg:expr),*) => {
776             __debug!($($arg),*)
777         })
778     }
779
780     #[cfg(not(debug))]
781     #[macro_escape]
782     mod debug_macro {
783         macro_rules! debug (($($arg:expr),*) => {
784             if false { __debug!($($arg),*) }
785         })
786     }
787
788     macro_rules! fail(
789         () => (
790             fail!(\"explicit failure\")
791         );
792         ($msg:expr) => (
793             ::std::sys::FailWithCause::fail_with($msg, file!(), line!())
794         );
795         ($( $arg:expr ),+) => (
796             ::std::sys::FailWithCause::fail_with(fmt!( $($arg),+ ), file!(), line!())
797         )
798     )
799
800     macro_rules! assert(
801         ($cond:expr) => {
802             if !$cond {
803                 ::std::sys::FailWithCause::fail_with(
804                     \"assertion failed: \" + stringify!($cond), file!(), line!())
805             }
806         };
807         ($cond:expr, $msg:expr) => {
808             if !$cond {
809                 ::std::sys::FailWithCause::fail_with($msg, file!(), line!())
810             }
811         };
812         ($cond:expr, $( $arg:expr ),+) => {
813             if !$cond {
814                 ::std::sys::FailWithCause::fail_with(fmt!( $($arg),+ ), file!(), line!())
815             }
816         }
817     )
818
819     macro_rules! assert_eq (
820         ($given:expr , $expected:expr) => (
821             {
822                 let given_val = $given;
823                 let expected_val = $expected;
824                 // check both directions of equality....
825                 if !((given_val == expected_val) && (expected_val == given_val)) {
826                     fail!(\"assertion failed: `(left == right) && (right == \
827                     left)` (left: `%?`, right: `%?`)\", given_val, expected_val);
828                 }
829             }
830         )
831     )
832
833     macro_rules! assert_approx_eq (
834         ($given:expr , $expected:expr) => (
835             {
836                 use std::cmp::ApproxEq;
837
838                 let given_val = $given;
839                 let expected_val = $expected;
840                 // check both directions of equality....
841                 if !(
842                     given_val.approx_eq(&expected_val) &&
843                     expected_val.approx_eq(&given_val)
844                 ) {
845                     fail!(\"left: %? does not approximately equal right: %?\",
846                           given_val, expected_val);
847                 }
848             }
849         );
850         ($given:expr , $expected:expr , $epsilon:expr) => (
851             {
852                 use std::cmp::ApproxEq;
853
854                 let given_val = $given;
855                 let expected_val = $expected;
856                 let epsilon_val = $epsilon;
857                 // check both directions of equality....
858                 if !(
859                     given_val.approx_eq_eps(&expected_val, &epsilon_val) &&
860                     expected_val.approx_eq_eps(&given_val, &epsilon_val)
861                 ) {
862                     fail!(\"left: %? does not approximately equal right: %? with epsilon: %?\",
863                           given_val, expected_val, epsilon_val);
864                 }
865             }
866         )
867     )
868
869     macro_rules! condition (
870
871         { pub $c:ident: $input:ty -> $out:ty; } => {
872
873             pub mod $c {
874                 #[allow(non_uppercase_statics)];
875                 static key: ::std::local_data::Key<
876                     @::std::condition::Handler<$input, $out>> =
877                     &::std::local_data::Key;
878
879                 pub static cond :
880                     ::std::condition::Condition<$input,$out> =
881                     ::std::condition::Condition {
882                         name: stringify!($c),
883                         key: key
884                     };
885             }
886         };
887
888         { $c:ident: $input:ty -> $out:ty; } => {
889
890             // FIXME (#6009): remove mod's `pub` below once variant above lands.
891             pub mod $c {
892                 #[allow(non_uppercase_statics)];
893                 static key: ::std::local_data::Key<
894                     @::std::condition::Handler<$input, $out>> =
895                     &::std::local_data::Key;
896
897                 pub static cond :
898                     ::std::condition::Condition<$input,$out> =
899                     ::std::condition::Condition {
900                         name: stringify!($c),
901                         key: key
902                     };
903             }
904         }
905     )
906
907     //
908     // A scheme-style conditional that helps to improve code clarity in some instances when
909     // the `if`, `else if`, and `else` keywords obscure predicates undesirably.
910     //
911     // # Example
912     //
913     // ~~~
914     // let clamped =
915     //     if x > mx { mx }
916     //     else if x < mn { mn }
917     //     else { x };
918     // ~~~
919     //
920     // Using `cond!`, the above could be written as:
921     //
922     // ~~~
923     // let clamped = cond!(
924     //     (x > mx) { mx }
925     //     (x < mn) { mn }
926     //     _        { x  }
927     // );
928     // ~~~
929     //
930     // The optional default case is denoted by `_`.
931     //
932     macro_rules! cond (
933         ( $(($pred:expr) $body:block)+ _ $default:block ) => (
934             $(if $pred $body else)+
935             $default
936         );
937         // for if the default case was ommitted
938         ( $(($pred:expr) $body:block)+ ) => (
939             $(if $pred $body)else+
940         );
941     )
942
943     macro_rules! printf (
944         ($arg:expr) => (
945             print(fmt!(\"%?\", $arg))
946         );
947         ($( $arg:expr ),+) => (
948             print(fmt!($($arg),+))
949         )
950     )
951
952     macro_rules! printfln (
953         ($arg:expr) => (
954             println(fmt!(\"%?\", $arg))
955         );
956         ($( $arg:expr ),+) => (
957             println(fmt!($($arg),+))
958         )
959     )
960 }";
961 }
962
963 // add a bunch of macros as though they were placed at the head of the
964 // program (ick). This should run before cfg stripping.
965 pub fn inject_std_macros(parse_sess: @mut parse::ParseSess,
966                          cfg: ast::CrateConfig, c: &Crate) -> @Crate {
967     let sm = match parse_item_from_source_str(@"<std-macros>",
968                                               std_macros(),
969                                               cfg.clone(),
970                                               ~[],
971                                               parse_sess) {
972         Some(item) => item,
973         None => fail!("expected core macros to parse correctly")
974     };
975
976     let injecter = @AstFoldFns {
977         fold_mod: |modd, _| {
978             // just inject the std macros at the start of the first
979             // module in the crate (i.e the crate file itself.)
980             let items = vec::append(~[sm], modd.items);
981             ast::_mod {
982                 items: items,
983                 // FIXME #2543: Bad copy.
984                 .. (*modd).clone()
985             }
986         },
987         .. *default_ast_fold()
988     };
989     @make_fold(injecter).fold_crate(c)
990 }
991
992 pub fn expand_crate(parse_sess: @mut parse::ParseSess,
993                     cfg: ast::CrateConfig, c: &Crate) -> @Crate {
994     // adding *another* layer of indirection here so that the block
995     // visitor can swap out one exts table for another for the duration
996     // of the block.  The cleaner alternative would be to thread the
997     // exts table through the fold, but that would require updating
998     // every method/element of AstFoldFns in fold.rs.
999     let extsbox = @mut syntax_expander_table();
1000     let afp = default_ast_fold();
1001     let cx = ExtCtxt::new(parse_sess, cfg.clone());
1002     let f_pre = @AstFoldFns {
1003         fold_expr: |expr,span,recur|
1004             expand_expr(extsbox, cx, expr, span, recur, afp.fold_expr),
1005         fold_mod: |modd,recur|
1006             expand_mod_items(extsbox, cx, modd, recur, afp.fold_mod),
1007         fold_item: |item,recur|
1008             expand_item(extsbox, cx, item, recur, afp.fold_item),
1009         fold_stmt: |stmt,span,recur|
1010             expand_stmt(extsbox, cx, stmt, span, recur, afp.fold_stmt),
1011         fold_block: |blk,recur|
1012             expand_block(extsbox, cx, blk, recur, afp.fold_block),
1013         new_span: |a| new_span(cx, a),
1014         .. *afp};
1015     let f = make_fold(f_pre);
1016
1017     let ret = @f.fold_crate(c);
1018     parse_sess.span_diagnostic.handler().abort_if_errors();
1019     return ret;
1020 }
1021
1022 // given a function from idents to idents, produce
1023 // an ast_fold that applies that function:
1024 pub fn fun_to_ident_folder(f: @fn(ast::ident)->ast::ident) -> @ast_fold{
1025     let afp = default_ast_fold();
1026     let f_pre = @AstFoldFns{
1027         fold_ident : |id, _| f(id),
1028         .. *afp
1029     };
1030     make_fold(f_pre)
1031 }
1032
1033 // update the ctxts in a path to get a rename node
1034 pub fn new_ident_renamer(from: ast::ident,
1035                       to: ast::Name) ->
1036     @fn(ast::ident)->ast::ident {
1037     |id : ast::ident|
1038     ast::ident{
1039         name: id.name,
1040         ctxt: new_rename(from,to,id.ctxt)
1041     }
1042 }
1043
1044
1045 // update the ctxts in a path to get a mark node
1046 pub fn new_ident_marker(mark: uint) ->
1047     @fn(ast::ident)->ast::ident {
1048     |id : ast::ident|
1049     ast::ident{
1050         name: id.name,
1051         ctxt: new_mark(mark,id.ctxt)
1052     }
1053 }
1054
1055 // perform resolution (in the MTWT sense) on all of the
1056 // idents in the tree. This is the final step in expansion.
1057 pub fn new_ident_resolver() ->
1058     @fn(ast::ident)->ast::ident {
1059     |id : ast::ident|
1060     ast::ident {
1061         name : resolve(id),
1062         ctxt : illegal_ctxt
1063     }
1064 }
1065
1066
1067 #[cfg(test)]
1068 mod test {
1069     use super::*;
1070     use ast;
1071     use ast::{Attribute_, AttrOuter, MetaWord, empty_ctxt};
1072     use codemap;
1073     use codemap::spanned;
1074     use parse;
1075     use parse::token::{intern, get_ident_interner};
1076     use print::pprust;
1077     use util::parser_testing::{string_to_item, string_to_pat, strs_to_idents};
1078     use oldvisit::{mk_vt};
1079
1080     // make sure that fail! is present
1081     #[test] fn fail_exists_test () {
1082         let src = @"fn main() { fail!(\"something appropriately gloomy\");}";
1083         let sess = parse::new_parse_sess(None);
1084         let crate_ast = parse::parse_crate_from_source_str(
1085             @"<test>",
1086             src,
1087             ~[],sess);
1088         let crate_ast = inject_std_macros(sess, ~[], crate_ast);
1089         // don't bother with striping, doesn't affect fail!.
1090         expand_crate(sess,~[],crate_ast);
1091     }
1092
1093     // these following tests are quite fragile, in that they don't test what
1094     // *kind* of failure occurs.
1095
1096     // make sure that macros can leave scope
1097     #[should_fail]
1098     #[test] fn macros_cant_escape_fns_test () {
1099         let src = @"fn bogus() {macro_rules! z (() => (3+4))}\
1100                     fn inty() -> int { z!() }";
1101         let sess = parse::new_parse_sess(None);
1102         let crate_ast = parse::parse_crate_from_source_str(
1103             @"<test>",
1104             src,
1105             ~[],sess);
1106         // should fail:
1107         expand_crate(sess,~[],crate_ast);
1108     }
1109
1110     // make sure that macros can leave scope for modules
1111     #[should_fail]
1112     #[test] fn macros_cant_escape_mods_test () {
1113         let src = @"mod foo {macro_rules! z (() => (3+4))}\
1114                     fn inty() -> int { z!() }";
1115         let sess = parse::new_parse_sess(None);
1116         let crate_ast = parse::parse_crate_from_source_str(
1117             @"<test>",
1118             src,
1119             ~[],sess);
1120         // should fail:
1121         expand_crate(sess,~[],crate_ast);
1122     }
1123
1124     // macro_escape modules shouldn't cause macros to leave scope
1125     #[test] fn macros_can_escape_flattened_mods_test () {
1126         let src = @"#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
1127                     fn inty() -> int { z!() }";
1128         let sess = parse::new_parse_sess(None);
1129         let crate_ast = parse::parse_crate_from_source_str(
1130             @"<test>",
1131             src,
1132             ~[], sess);
1133         // should fail:
1134         expand_crate(sess,~[],crate_ast);
1135     }
1136
1137     #[test] fn std_macros_must_parse () {
1138         let src = super::std_macros();
1139         let sess = parse::new_parse_sess(None);
1140         let cfg = ~[];
1141         let item_ast = parse::parse_item_from_source_str(
1142             @"<test>",
1143             src,
1144             cfg,~[],sess);
1145         match item_ast {
1146             Some(_) => (), // success
1147             None => fail!("expected this to parse")
1148         }
1149     }
1150
1151     #[test] fn test_contains_flatten (){
1152         let attr1 = make_dummy_attr (@"foo");
1153         let attr2 = make_dummy_attr (@"bar");
1154         let escape_attr = make_dummy_attr (@"macro_escape");
1155         let attrs1 = ~[attr1, escape_attr, attr2];
1156         assert_eq!(contains_macro_escape (attrs1),true);
1157         let attrs2 = ~[attr1,attr2];
1158         assert_eq!(contains_macro_escape (attrs2),false);
1159     }
1160
1161     // make a MetaWord outer attribute with the given name
1162     fn make_dummy_attr(s: @str) -> ast::Attribute {
1163         spanned {
1164             span:codemap::dummy_sp(),
1165             node: Attribute_ {
1166                 style: AttrOuter,
1167                 value: @spanned {
1168                     node: MetaWord(s),
1169                     span: codemap::dummy_sp(),
1170                 },
1171                 is_sugared_doc: false,
1172             }
1173         }
1174     }
1175
1176     #[test]
1177     fn renaming () {
1178         let maybe_item_ast = string_to_item(@"fn a() -> int { let b = 13; b }");
1179         let item_ast = match maybe_item_ast {
1180             Some(x) => x,
1181             None => fail!("test case fail")
1182         };
1183         let a_name = intern("a");
1184         let a2_name = intern("a2");
1185         let renamer = new_ident_renamer(ast::ident{name:a_name,ctxt:empty_ctxt},
1186                                         a2_name);
1187         let renamed_ast = fun_to_ident_folder(renamer).fold_item(item_ast).unwrap();
1188         let resolver = new_ident_resolver();
1189         let resolved_ast = fun_to_ident_folder(resolver).fold_item(renamed_ast).unwrap();
1190         let resolved_as_str = pprust::item_to_str(resolved_ast,
1191                                                   get_ident_interner());
1192         assert_eq!(resolved_as_str,~"fn a2() -> int { let b = 13; b }");
1193
1194
1195     }
1196
1197     // sigh... it looks like I have two different renaming mechanisms, now...
1198
1199     #[test]
1200     fn pat_idents(){
1201         let pat = string_to_pat(@"(a,Foo{x:c @ (b,9),y:Bar(4,d)})");
1202         let idents = @mut ~[];
1203         let pat_idents = new_name_finder(idents);
1204         pat_idents.visit_pat(pat, ());
1205         assert_eq!(idents, @mut strs_to_idents(~["a","c","b","d"]));
1206     }
1207 }