base.rs 18.3 KB
Newer Older
1
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 3 4 5 6 7 8 9 10
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

11
use ast;
12
use ast::Name;
13
use codemap;
14
use codemap::{CodeMap, Span, ExpnInfo};
15
use ext;
16
use ext::expand;
17
use parse;
A
Alex Crichton 已提交
18
use parse::token;
19
use parse::token::{ident_to_str, intern, str_to_ident};
S
Steven Fackler 已提交
20
use util::small_vector::SmallVector;
21

22
use std::hashmap::HashMap;
23

24 25
// new-style macro! tt code:
//
26
//    MacResult, 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::MacInvocTT.
31

32
pub struct MacroDef {
33
    name: @str,
34
    ext: SyntaxExtension
35
}
36

37
pub type ItemDecorator =
38
    fn(&ExtCtxt, Span, @ast::MetaItem, ~[@ast::Item]) -> ~[@ast::Item];
39

40 41 42 43 44 45 46
pub struct SyntaxExpanderTT {
    expander: SyntaxExpanderTTExpander,
    span: Option<Span>
}

pub trait SyntaxExpanderTTTrait {
    fn expand(&self,
S
Steven Fackler 已提交
47
              ecx: &mut ExtCtxt,
48
              span: Span,
49
              token_tree: &[ast::TokenTree],
50 51 52 53 54
              context: ast::SyntaxContext)
              -> MacResult;
}

pub type SyntaxExpanderTTFunNoCtxt =
55
    fn(ecx: &mut ExtCtxt, span: codemap::Span, token_tree: &[ast::TokenTree])
56
       -> MacResult;
57 58 59 60 61 62 63

enum SyntaxExpanderTTExpander {
    SyntaxExpanderTTExpanderWithoutContext(SyntaxExpanderTTFunNoCtxt),
}

impl SyntaxExpanderTTTrait for SyntaxExpanderTT {
    fn expand(&self,
S
Steven Fackler 已提交
64
              ecx: &mut ExtCtxt,
65
              span: Span,
66
              token_tree: &[ast::TokenTree],
67 68 69 70 71 72 73 74 75
              _: ast::SyntaxContext)
              -> MacResult {
        match self.expander {
            SyntaxExpanderTTExpanderWithoutContext(f) => {
                f(ecx, span, token_tree)
            }
        }
    }
}
J
John Clements 已提交
76

77 78 79 80 81 82 83 84 85 86 87 88
enum SyntaxExpanderTTItemExpander {
    SyntaxExpanderTTItemExpanderWithContext(SyntaxExpanderTTItemFun),
    SyntaxExpanderTTItemExpanderWithoutContext(SyntaxExpanderTTItemFunNoCtxt),
}

pub struct SyntaxExpanderTTItem {
    expander: SyntaxExpanderTTItemExpander,
    span: Option<Span>
}

pub trait SyntaxExpanderTTItemTrait {
    fn expand(&self,
S
Steven Fackler 已提交
89
              cx: &mut ExtCtxt,
90 91
              sp: Span,
              ident: ast::Ident,
92
              token_tree: ~[ast::TokenTree],
93 94 95 96 97 98
              context: ast::SyntaxContext)
              -> MacResult;
}

impl SyntaxExpanderTTItemTrait for SyntaxExpanderTTItem {
    fn expand(&self,
S
Steven Fackler 已提交
99
              cx: &mut ExtCtxt,
100 101
              sp: Span,
              ident: ast::Ident,
102
              token_tree: ~[ast::TokenTree],
103 104 105 106 107 108 109 110 111 112 113 114 115
              context: ast::SyntaxContext)
              -> MacResult {
        match self.expander {
            SyntaxExpanderTTItemExpanderWithContext(fun) => {
                fun(cx, sp, ident, token_tree, context)
            }
            SyntaxExpanderTTItemExpanderWithoutContext(fun) => {
                fun(cx, sp, ident, token_tree)
            }
        }
    }
}

116
pub type SyntaxExpanderTTItemFun =
117
    fn(&mut ExtCtxt, Span, ast::Ident, ~[ast::TokenTree], ast::SyntaxContext)
118
       -> MacResult;
119 120

pub type SyntaxExpanderTTItemFunNoCtxt =
121
    fn(&mut ExtCtxt, Span, ast::Ident, ~[ast::TokenTree]) -> MacResult;
122 123 124

pub trait AnyMacro {
    fn make_expr(&self) -> @ast::Expr;
125
    fn make_items(&self) -> SmallVector<@ast::Item>;
126 127
    fn make_stmt(&self) -> @ast::Stmt;
}
J
John Clements 已提交
128

129
pub enum MacResult {
130
    MRExpr(@ast::Expr),
131
    MRItem(@ast::Item),
132 133
    MRAny(@AnyMacro),
    MRDef(MacroDef),
134
}
135

136
pub enum SyntaxExtension {
K
klutzy 已提交
137
    // #[deriving] and such
138
    ItemDecorator(ItemDecorator),
139

140
    // Token-tree expanders
141
    NormalTT(~SyntaxExpanderTTTrait:'static, Option<Span>),
142

J
John Clements 已提交
143 144 145
    // An IdentTT is a macro that has an
    // identifier in between the name of the
    // macro and the argument. Currently,
B
Brian Anderson 已提交
146 147
    // the only examples of this is
    // macro_rules!
J
John Clements 已提交
148

149 150
    // perhaps macro_rules! will lose its odd special identifier argument,
    // and this can go away also
151
    IdentTT(~SyntaxExpanderTTItemTrait:'static, Option<Span>),
152
}
153

J
John Clements 已提交
154

155 156 157
// The SyntaxEnv is the environment that's threaded through the expansion
// of macros. It contains bindings for macros, and also a special binding
// for " block" (not a legal identifier) that maps to a BlockInfo
S
Steven Fackler 已提交
158
pub type SyntaxEnv = MapChain<Name, SyntaxExtension>;
159 160 161 162 163

pub struct BlockInfo {
    // should macros escape from this scope?
    macros_escape : bool,
    // what are the pending renames?
S
Steven Fackler 已提交
164 165 166 167 168 169 170 171 172 173
    pending_renames : RenameList
}

impl BlockInfo {
    pub fn new() -> BlockInfo {
        BlockInfo {
            macros_escape: false,
            pending_renames: ~[]
        }
    }
J
John Clements 已提交
174
}
175

176
// a list of ident->name renamings
S
Steven Fackler 已提交
177
pub type RenameList = ~[(ast::Ident,Name)];
178

J
John Clements 已提交
179
// The base map of methods for expanding syntax extension
180
// AST nodes into full ASTs
J
John Clements 已提交
181
pub fn syntax_expander_table() -> SyntaxEnv {
J
John Clements 已提交
182
    // utility function to simplify creating NormalTT syntax extensions
183
    fn builtin_normal_tt_no_ctxt(f: SyntaxExpanderTTFunNoCtxt)
S
Steven Fackler 已提交
184
                                 -> SyntaxExtension {
185
        NormalTT(~SyntaxExpanderTT{
186 187
            expander: SyntaxExpanderTTExpanderWithoutContext(f),
            span: None,
188
        },
S
Steven Fackler 已提交
189
        None)
190
    }
K
Kiet Tran 已提交
191

S
Steven Fackler 已提交
192
    let mut syntax_expanders = MapChain::new();
193
    syntax_expanders.insert(intern(&"macro_rules"),
194
                            IdentTT(~SyntaxExpanderTTItem {
P
Patrick Walton 已提交
195 196
                                expander: SyntaxExpanderTTItemExpanderWithContext(
                                    ext::tt::macro_rules::add_new_extension),
197
                                span: None,
198
                            },
S
Steven Fackler 已提交
199
                            None));
A
Alex Crichton 已提交
200
    syntax_expanders.insert(intern(&"fmt"),
201 202
                            builtin_normal_tt_no_ctxt(
                                ext::fmt::expand_syntax_ext));
A
Alex Crichton 已提交
203
    syntax_expanders.insert(intern(&"format_args"),
204 205
                            builtin_normal_tt_no_ctxt(
                                ext::format::expand_args));
206
    syntax_expanders.insert(intern(&"env"),
207 208
                            builtin_normal_tt_no_ctxt(
                                    ext::env::expand_env));
S
Steven Fackler 已提交
209
    syntax_expanders.insert(intern(&"option_env"),
210 211
                            builtin_normal_tt_no_ctxt(
                                    ext::env::expand_option_env));
212
    syntax_expanders.insert(intern("bytes"),
213 214
                            builtin_normal_tt_no_ctxt(
                                    ext::bytes::expand_syntax_ext));
215
    syntax_expanders.insert(intern("concat_idents"),
J
John Clements 已提交
216
                            builtin_normal_tt_no_ctxt(
217
                                    ext::concat_idents::expand_syntax_ext));
218 219 220
    syntax_expanders.insert(intern("concat"),
                            builtin_normal_tt_no_ctxt(
                                    ext::concat::expand_syntax_ext));
221
    syntax_expanders.insert(intern(&"log_syntax"),
J
John Clements 已提交
222
                            builtin_normal_tt_no_ctxt(
223
                                    ext::log_syntax::expand_syntax_ext));
224
    syntax_expanders.insert(intern(&"deriving"),
S
Steven Fackler 已提交
225
                            ItemDecorator(ext::deriving::expand_meta_deriving));
226 227

    // Quasi-quoting expanders
228
    syntax_expanders.insert(intern(&"quote_tokens"),
229 230
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_tokens));
231
    syntax_expanders.insert(intern(&"quote_expr"),
232 233
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_expr));
234
    syntax_expanders.insert(intern(&"quote_ty"),
235 236
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_ty));
237
    syntax_expanders.insert(intern(&"quote_item"),
238 239
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_item));
240
    syntax_expanders.insert(intern(&"quote_pat"),
241 242
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_pat));
243
    syntax_expanders.insert(intern(&"quote_stmt"),
244 245
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_stmt));
246

247
    syntax_expanders.insert(intern(&"line"),
J
John Clements 已提交
248
                            builtin_normal_tt_no_ctxt(
249
                                    ext::source_util::expand_line));
250
    syntax_expanders.insert(intern(&"col"),
J
John Clements 已提交
251
                            builtin_normal_tt_no_ctxt(
252
                                    ext::source_util::expand_col));
253
    syntax_expanders.insert(intern(&"file"),
J
John Clements 已提交
254
                            builtin_normal_tt_no_ctxt(
255
                                    ext::source_util::expand_file));
256
    syntax_expanders.insert(intern(&"stringify"),
J
John Clements 已提交
257
                            builtin_normal_tt_no_ctxt(
258
                                    ext::source_util::expand_stringify));
259
    syntax_expanders.insert(intern(&"include"),
J
John Clements 已提交
260
                            builtin_normal_tt_no_ctxt(
261
                                    ext::source_util::expand_include));
262
    syntax_expanders.insert(intern(&"include_str"),
J
John Clements 已提交
263
                            builtin_normal_tt_no_ctxt(
264
                                    ext::source_util::expand_include_str));
265
    syntax_expanders.insert(intern(&"include_bin"),
J
John Clements 已提交
266
                            builtin_normal_tt_no_ctxt(
267
                                    ext::source_util::expand_include_bin));
268
    syntax_expanders.insert(intern(&"module_path"),
J
John Clements 已提交
269
                            builtin_normal_tt_no_ctxt(
270
                                    ext::source_util::expand_mod));
271
    syntax_expanders.insert(intern(&"asm"),
272 273
                            builtin_normal_tt_no_ctxt(
                                    ext::asm::expand_asm));
274
    syntax_expanders.insert(intern(&"cfg"),
275 276 277 278 279
                            builtin_normal_tt_no_ctxt(
                                    ext::cfg::expand_cfg));
    syntax_expanders.insert(intern(&"trace_macros"),
                            builtin_normal_tt_no_ctxt(
                                    ext::trace_macros::expand_trace_macros));
S
Steven Fackler 已提交
280
    syntax_expanders
281
}
282 283 284 285

// One of these is made during expansion and incrementally updated as we go;
// when a macro expansion occurs, the resulting nodes have the backtrace()
// -> expn_info of their expansion context stored into their span.
286
pub struct ExtCtxt {
287
    parse_sess: @parse::ParseSess,
288
    cfg: ast::CrateConfig,
S
Steven Fackler 已提交
289
    backtrace: Option<@ExpnInfo>,
290

S
Steven Fackler 已提交
291 292
    mod_path: ~[ast::Ident],
    trace_mac: bool
293
}
N
Niko Matsakis 已提交
294

295
impl ExtCtxt {
296
    pub fn new(parse_sess: @parse::ParseSess, cfg: ast::CrateConfig)
S
Steven Fackler 已提交
297 298
               -> ExtCtxt {
        ExtCtxt {
299 300
            parse_sess: parse_sess,
            cfg: cfg,
S
Steven Fackler 已提交
301 302 303
            backtrace: None,
            mod_path: ~[],
            trace_mac: false
304 305 306
        }
    }

S
Steven Fackler 已提交
307
    pub fn expand_expr(&mut self, mut e: @ast::Expr) -> @ast::Expr {
308 309
        loop {
            match e.node {
A
Alex Crichton 已提交
310
                ast::ExprMac(..) => {
S
Steven Fackler 已提交
311
                    let mut expander = expand::MacroExpander {
S
Steven Fackler 已提交
312
                        extsbox: syntax_expander_table(),
313 314
                        cx: self,
                    };
S
Steven Fackler 已提交
315
                    e = expand::expand_expr(e, &mut expander);
316 317 318 319 320 321
                }
                _ => return e
            }
        }
    }

322
    pub fn codemap(&self) -> @CodeMap { self.parse_sess.cm }
323
    pub fn parse_sess(&self) -> @parse::ParseSess { self.parse_sess }
324
    pub fn cfg(&self) -> ast::CrateConfig { self.cfg.clone() }
325
    pub fn call_site(&self) -> Span {
S
Steven Fackler 已提交
326
        match self.backtrace {
A
Alex Crichton 已提交
327
            Some(@ExpnInfo {call_site: cs, ..}) => cs,
328
            None => self.bug("missing top span")
329
        }
330
    }
331
    pub fn print_backtrace(&self) { }
S
Steven Fackler 已提交
332 333 334 335 336
    pub fn backtrace(&self) -> Option<@ExpnInfo> { self.backtrace }
    pub fn mod_push(&mut self, i: ast::Ident) { self.mod_path.push(i); }
    pub fn mod_pop(&mut self) { self.mod_path.pop(); }
    pub fn mod_path(&self) -> ~[ast::Ident] { self.mod_path.clone() }
    pub fn bt_push(&mut self, ei: codemap::ExpnInfo) {
337
        match ei {
338
            ExpnInfo {call_site: cs, callee: ref callee} => {
S
Steven Fackler 已提交
339
                self.backtrace =
340
                    Some(@ExpnInfo {
341
                        call_site: Span {lo: cs.lo, hi: cs.hi,
S
Steven Fackler 已提交
342
                                         expn_info: self.backtrace},
343
                        callee: *callee});
K
Kevin Atkinson 已提交
344
            }
345
        }
346
    }
S
Steven Fackler 已提交
347 348
    pub fn bt_pop(&mut self) {
        match self.backtrace {
349
            Some(@ExpnInfo {
A
Alex Crichton 已提交
350
                call_site: Span {expn_info: prev, ..}, ..}) => {
S
Steven Fackler 已提交
351
                self.backtrace = prev
352
            }
353
            _ => self.bug("tried to pop without a push")
354 355
        }
    }
356
    pub fn span_fatal(&self, sp: Span, msg: &str) -> ! {
357 358 359
        self.print_backtrace();
        self.parse_sess.span_diagnostic.span_fatal(sp, msg);
    }
360
    pub fn span_err(&self, sp: Span, msg: &str) {
361 362 363
        self.print_backtrace();
        self.parse_sess.span_diagnostic.span_err(sp, msg);
    }
364
    pub fn span_warn(&self, sp: Span, msg: &str) {
365 366 367
        self.print_backtrace();
        self.parse_sess.span_diagnostic.span_warn(sp, msg);
    }
368
    pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! {
369 370 371
        self.print_backtrace();
        self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
    }
372
    pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
373 374 375
        self.print_backtrace();
        self.parse_sess.span_diagnostic.span_bug(sp, msg);
    }
376
    pub fn bug(&self, msg: &str) -> ! {
377 378 379
        self.print_backtrace();
        self.parse_sess.span_diagnostic.handler().bug(msg);
    }
380
    pub fn trace_macros(&self) -> bool {
S
Steven Fackler 已提交
381
        self.trace_mac
382
    }
S
Steven Fackler 已提交
383 384
    pub fn set_trace_macros(&mut self, x: bool) {
        self.trace_mac = x
385
    }
386
    pub fn str_of(&self, id: ast::Ident) -> @str {
387
        ident_to_str(&id)
388
    }
389
    pub fn ident_of(&self, st: &str) -> ast::Ident {
390
        str_to_ident(st)
391 392 393
    }
}

S
Steven Fackler 已提交
394
pub fn expr_to_str(cx: &ExtCtxt, expr: @ast::Expr, err_msg: &str) -> (@str, ast::StrStyle) {
395
    match expr.node {
396 397 398 399 400
        ast::ExprLit(l) => match l.node {
            ast::LitStr(s, style) => (s, style),
            _ => cx.span_fatal(l.span, err_msg)
        },
        _ => cx.span_fatal(expr.span, err_msg)
401 402 403
    }
}

404
pub fn check_zero_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree],
405
                      name: &str) {
406
    if tts.len() != 0 {
A
Alex Crichton 已提交
407
        cx.span_fatal(sp, format!("{} takes no arguments", name));
408
    }
409 410
}

S
Steven Fackler 已提交
411
pub fn get_single_str_from_tts(cx: &ExtCtxt,
412
                               sp: Span,
413
                               tts: &[ast::TokenTree],
414 415
                               name: &str)
                               -> @str {
416
    if tts.len() != 1 {
A
Alex Crichton 已提交
417
        cx.span_fatal(sp, format!("{} takes 1 argument.", name));
418 419
    }

420
    match tts[0] {
421 422
        ast::TTTok(_, token::LIT_STR(ident))
        | ast::TTTok(_, token::LIT_STR_RAW(ident, _)) => cx.str_of(ident),
A
Alex Crichton 已提交
423
        _ => cx.span_fatal(sp, format!("{} requires a string.", name)),
424 425
    }
}
426

S
Steven Fackler 已提交
427
pub fn get_exprs_from_tts(cx: &ExtCtxt,
428
                          sp: Span,
429
                          tts: &[ast::TokenTree]) -> ~[@ast::Expr] {
430 431 432
    let mut p = parse::new_parser_from_tts(cx.parse_sess(),
                                           cx.cfg(),
                                           tts.to_owned());
433
    let mut es = ~[];
434
    while p.token != token::EOF {
435 436
        if es.len() != 0 && !p.eat(&token::COMMA) {
            cx.span_fatal(sp, "expected token: `,`");
437 438
        }
        es.push(p.parse_expr());
439
    }
440
    es
441 442
}

J
John Clements 已提交
443 444 445 446
// in order to have some notion of scoping for macros,
// we want to implement the notion of a transformation
// environment.

S
Steven Fackler 已提交
447
// This environment maps Names to SyntaxExtensions.
J
John Clements 已提交
448 449 450 451 452 453 454 455 456 457 458 459 460 461

// Actually, the following implementation is parameterized
// by both key and value types.

//impl question: how to implement it? Initially, the
// env will contain only macros, so it might be painful
// to add an empty frame for every context. Let's just
// get it working, first....

// NB! the mutability of the underlying maps means that
// if expansion is out-of-order, a deeper scope may be
// able to refer to a macro that was added to an enclosing
// scope lexically later than the deeper scope.

S
Steven Fackler 已提交
462 463 464 465
// Only generic to make it easy to test
struct MapChainFrame<K, V> {
    info: BlockInfo,
    map: HashMap<K, V>,
J
John Clements 已提交
466 467
}

S
Steven Fackler 已提交
468 469 470 471
// Only generic to make it easy to test
pub struct MapChain<K, V> {
    priv chain: ~[MapChainFrame<K, V>],
}
J
John Clements 已提交
472

S
Steven Fackler 已提交
473 474 475 476 477
impl<K: Hash+Eq, V> MapChain<K, V> {
    pub fn new() -> MapChain<K, V> {
        let mut map = MapChain { chain: ~[] };
        map.push_frame();
        map
J
John Clements 已提交
478 479
    }

S
Steven Fackler 已提交
480 481 482 483 484
    pub fn push_frame(&mut self) {
        self.chain.push(MapChainFrame {
            info: BlockInfo::new(),
            map: HashMap::new(),
        });
J
John Clements 已提交
485 486
    }

S
Steven Fackler 已提交
487 488 489
    pub fn pop_frame(&mut self) {
        assert!(self.chain.len() > 1, "too many pops on MapChain!");
        self.chain.pop();
490 491
    }

S
Steven Fackler 已提交
492 493 494 495 496
    fn find_escape_frame<'a>(&'a mut self) -> &'a mut MapChainFrame<K, V> {
        for (i, frame) in self.chain.mut_iter().enumerate().invert() {
            if !frame.info.macros_escape || i == 0 {
                return frame
            }
J
John Clements 已提交
497
        }
S
Steven Fackler 已提交
498
        unreachable!()
J
John Clements 已提交
499 500
    }

S
Steven Fackler 已提交
501 502 503 504 505
    pub fn find<'a>(&'a self, k: &K) -> Option<&'a V> {
        for frame in self.chain.iter().invert() {
            match frame.map.find(k) {
                Some(v) => return Some(v),
                None => {}
J
John Clements 已提交
506 507
            }
        }
S
Steven Fackler 已提交
508
        None
J
John Clements 已提交
509 510
    }

S
Steven Fackler 已提交
511 512
    pub fn insert(&mut self, k: K, v: V) {
        self.find_escape_frame().map.insert(k, v);
J
John Clements 已提交
513 514
    }

S
Steven Fackler 已提交
515 516
    pub fn info<'a>(&'a mut self) -> &'a mut BlockInfo {
        &mut self.chain[self.chain.len()-1].info
517
    }
J
John Clements 已提交
518 519 520 521 522 523
}

#[cfg(test)]
mod test {
    use super::MapChain;

524 525
    #[test]
    fn testenv() {
S
Steven Fackler 已提交
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
        let mut m = MapChain::new();
        let (a,b,c,d) = ("a", "b", "c", "d");
        m.insert(1, a);
        assert_eq!(Some(&a), m.find(&1));

        m.push_frame();
        m.info().macros_escape = true;
        m.insert(2, b);
        assert_eq!(Some(&a), m.find(&1));
        assert_eq!(Some(&b), m.find(&2));
        m.pop_frame();

        assert_eq!(Some(&a), m.find(&1));
        assert_eq!(Some(&b), m.find(&2));

        m.push_frame();
        m.push_frame();
        m.info().macros_escape = true;
        m.insert(3, c);
        assert_eq!(Some(&c), m.find(&3));
        m.pop_frame();
        assert_eq!(Some(&c), m.find(&3));
        m.pop_frame();
        assert_eq!(None, m.find(&3));

        m.push_frame();
        m.insert(4, d);
        m.pop_frame();
        assert_eq!(None, m.find(&4));
J
John Clements 已提交
555 556
    }
}