]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/tt/macro_rules.rs
Change 'print(fmt!(...))' to printf!/printfln! in src/lib*
[rust.git] / src / libsyntax / ext / tt / macro_rules.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::{ident, matcher_, matcher, match_tok, match_nonterminal, match_seq};
12 use ast::{tt_delim};
13 use ast;
14 use codemap::{span, spanned, dummy_sp};
15 use ext::base::{ExtCtxt, MacResult, MRAny, MRDef, MacroDef, NormalTT};
16 use ext::base;
17 use ext::tt::macro_parser::{error};
18 use ext::tt::macro_parser::{named_match, matched_seq, matched_nonterminal};
19 use ext::tt::macro_parser::{parse, parse_or_else, success, failure};
20 use parse::lexer::{new_tt_reader, reader};
21 use parse::parser::Parser;
22 use parse::token::{get_ident_interner, special_idents, gensym_ident, ident_to_str};
23 use parse::token::{FAT_ARROW, SEMI, nt_matchers, nt_tt};
24 use print;
25
26 pub fn add_new_extension(cx: @ExtCtxt,
27                          sp: span,
28                          name: ident,
29                          arg: ~[ast::token_tree])
30                       -> base::MacResult {
31     // these spans won't matter, anyways
32     fn ms(m: matcher_) -> matcher {
33         spanned {
34             node: m.clone(),
35             span: dummy_sp()
36         }
37     }
38
39     let lhs_nm =  gensym_ident("lhs");
40     let rhs_nm =  gensym_ident("rhs");
41
42     // The grammar for macro_rules! is:
43     // $( $lhs:mtcs => $rhs:tt );+
44     // ...quasiquoting this would be nice.
45     let argument_gram = ~[
46         ms(match_seq(~[
47             ms(match_nonterminal(lhs_nm, special_idents::matchers, 0u)),
48             ms(match_tok(FAT_ARROW)),
49             ms(match_nonterminal(rhs_nm, special_idents::tt, 1u)),
50         ], Some(SEMI), false, 0u, 2u)),
51         //to phase into semicolon-termination instead of
52         //semicolon-separation
53         ms(match_seq(~[ms(match_tok(SEMI))], None, true, 2u, 2u))];
54
55
56     // Parse the macro_rules! invocation (`none` is for no interpolations):
57     let arg_reader = new_tt_reader(cx.parse_sess().span_diagnostic,
58                                    None,
59                                    arg.clone());
60     let argument_map = parse_or_else(cx.parse_sess(),
61                                      cx.cfg(),
62                                      arg_reader as @reader,
63                                      argument_gram);
64
65     // Extract the arguments:
66     let lhses = match *argument_map.get(&lhs_nm) {
67         @matched_seq(ref s, _) => /* FIXME (#2543) */ @(*s).clone(),
68         _ => cx.span_bug(sp, "wrong-structured lhs")
69     };
70
71     let rhses = match *argument_map.get(&rhs_nm) {
72       @matched_seq(ref s, _) => /* FIXME (#2543) */ @(*s).clone(),
73       _ => cx.span_bug(sp, "wrong-structured rhs")
74     };
75
76     // Given `lhses` and `rhses`, this is the new macro we create
77     fn generic_extension(cx: @ExtCtxt, sp: span, name: ident,
78                          arg: &[ast::token_tree],
79                          lhses: &[@named_match], rhses: &[@named_match])
80     -> MacResult {
81
82         if cx.trace_macros() {
83             printfln!("%s! { %s }",
84                       cx.str_of(name),
85                       print::pprust::tt_to_str(
86                           &ast::tt_delim(@mut arg.to_owned()),
87                           get_ident_interner()));
88         }
89
90         // Which arm's failure should we report? (the one furthest along)
91         let mut best_fail_spot = dummy_sp();
92         let mut best_fail_msg = ~"internal error: ran no matchers";
93
94         let s_d = cx.parse_sess().span_diagnostic;
95
96         for lhses.iter().enumerate().advance |(i, lhs)| { // try each arm's matchers
97             match *lhs {
98               @matched_nonterminal(nt_matchers(ref mtcs)) => {
99                 // `none` is because we're not interpolating
100                 let arg_rdr = new_tt_reader(
101                     s_d,
102                     None,
103                     arg.to_owned()
104                 ) as @reader;
105                 match parse(cx.parse_sess(), cx.cfg(), arg_rdr, *mtcs) {
106                   success(named_matches) => {
107                     let rhs = match rhses[i] {
108                         // okay, what's your transcriber?
109                         @matched_nonterminal(nt_tt(@ref tt)) => {
110                             match (*tt) {
111                                 // cut off delimiters; don't parse 'em
112                                 tt_delim(ref tts) => {
113                                     (*tts).slice(1u,(*tts).len()-1u).to_owned()
114                                 }
115                                 _ => cx.span_fatal(
116                                     sp, "macro rhs must be delimited")
117                             }
118                         },
119                         _ => cx.span_bug(sp, "bad thing in rhs")
120                     };
121                     // rhs has holes ( `$id` and `$(...)` that need filled)
122                     let trncbr = new_tt_reader(s_d, Some(named_matches),
123                                                rhs);
124                     let p = @Parser(cx.parse_sess(),
125                                     cx.cfg(),
126                                     trncbr as @reader);
127
128                     // Let the context choose how to interpret the result.
129                     // Weird, but useful for X-macros.
130                     return MRAny(|| p.parse_expr(),
131                                   || p.parse_item(~[/* no attrs*/]),
132                                   || p.parse_stmt(~[/* no attrs*/]));
133                   }
134                   failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo {
135                     best_fail_spot = sp;
136                     best_fail_msg = (*msg).clone();
137                   },
138                   error(sp, ref msg) => cx.span_fatal(sp, (*msg))
139                 }
140               }
141               _ => cx.bug("non-matcher found in parsed lhses")
142             }
143         }
144         cx.span_fatal(best_fail_spot, best_fail_msg);
145     }
146
147     let exp: @fn(@ExtCtxt, span, &[ast::token_tree]) -> MacResult =
148         |cx, sp, arg| generic_extension(cx, sp, name, arg, *lhses, *rhses);
149
150     return MRDef(MacroDef{
151         name: ident_to_str(&name),
152         ext: NormalTT(base::SyntaxExpanderTT{expander: exp, span: Some(sp)})
153     });
154 }