base.rs 18.2 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 {
327
            Some(expn_info) => expn_info.call_site,
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(expn_info) => self.backtrace = expn_info.call_site.expn_info,
350
            _ => self.bug("tried to pop without a push")
351 352
        }
    }
353
    pub fn span_fatal(&self, sp: Span, msg: &str) -> ! {
354 355 356
        self.print_backtrace();
        self.parse_sess.span_diagnostic.span_fatal(sp, msg);
    }
357
    pub fn span_err(&self, sp: Span, msg: &str) {
358 359 360
        self.print_backtrace();
        self.parse_sess.span_diagnostic.span_err(sp, msg);
    }
361
    pub fn span_warn(&self, sp: Span, msg: &str) {
362 363 364
        self.print_backtrace();
        self.parse_sess.span_diagnostic.span_warn(sp, msg);
    }
365
    pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! {
366 367 368
        self.print_backtrace();
        self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
    }
369
    pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
370 371 372
        self.print_backtrace();
        self.parse_sess.span_diagnostic.span_bug(sp, msg);
    }
373
    pub fn bug(&self, msg: &str) -> ! {
374 375 376
        self.print_backtrace();
        self.parse_sess.span_diagnostic.handler().bug(msg);
    }
377
    pub fn trace_macros(&self) -> bool {
S
Steven Fackler 已提交
378
        self.trace_mac
379
    }
S
Steven Fackler 已提交
380 381
    pub fn set_trace_macros(&mut self, x: bool) {
        self.trace_mac = x
382
    }
383
    pub fn str_of(&self, id: ast::Ident) -> @str {
384
        ident_to_str(&id)
385
    }
386
    pub fn ident_of(&self, st: &str) -> ast::Ident {
387
        str_to_ident(st)
388 389 390
    }
}

S
Steven Fackler 已提交
391
pub fn expr_to_str(cx: &ExtCtxt, expr: @ast::Expr, err_msg: &str) -> (@str, ast::StrStyle) {
392
    match expr.node {
393 394 395 396 397
        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)
398 399 400
    }
}

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

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

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

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

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

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

// 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 已提交
459 460 461 462
// Only generic to make it easy to test
struct MapChainFrame<K, V> {
    info: BlockInfo,
    map: HashMap<K, V>,
J
John Clements 已提交
463 464
}

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

S
Steven Fackler 已提交
470 471 472 473 474
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 已提交
475 476
    }

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

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

S
Steven Fackler 已提交
489 490 491 492 493
    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 已提交
494
        }
S
Steven Fackler 已提交
495
        unreachable!()
J
John Clements 已提交
496 497
    }

S
Steven Fackler 已提交
498 499 500 501 502
    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 已提交
503 504
            }
        }
S
Steven Fackler 已提交
505
        None
J
John Clements 已提交
506 507
    }

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

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

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

521 522
    #[test]
    fn testenv() {
S
Steven Fackler 已提交
523 524 525 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
        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 已提交
552 553
    }
}