base.rs 22.9 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 16 17
use diagnostic::span_handler;
use ext;
use parse;
A
Alex Crichton 已提交
18
use parse::token;
19
use parse::token::{ident_to_str, intern, str_to_ident};
20

21
use std::hashmap::HashMap;
22

23 24
// new-style macro! tt code:
//
25
//    MacResult, NormalTT, IdentTT
26
//
27 28 29
// also note that ast::mac used to have a bunch of extraneous cases and
// is now probably a redundant AST node, can be merged with
// ast::mac_invoc_tt.
30

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

36 37 38 39 40
pub type ItemDecorator = extern "Rust" fn(@ExtCtxt,
                                          Span,
                                          @ast::MetaItem,
                                          ~[@ast::item])
                                          -> ~[@ast::item];
41

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
pub struct SyntaxExpanderTT {
    expander: SyntaxExpanderTTExpander,
    span: Option<Span>
}

pub trait SyntaxExpanderTTTrait {
    fn expand(&self,
              ecx: @ExtCtxt,
              span: Span,
              token_tree: &[ast::token_tree],
              context: ast::SyntaxContext)
              -> MacResult;
}

pub type SyntaxExpanderTTFunNoCtxt =
    extern "Rust" fn(ecx: @ExtCtxt,
                     span: codemap::Span,
                     token_tree: &[ast::token_tree])
                     -> MacResult;

enum SyntaxExpanderTTExpander {
    SyntaxExpanderTTExpanderWithoutContext(SyntaxExpanderTTFunNoCtxt),
}

impl SyntaxExpanderTTTrait for SyntaxExpanderTT {
    fn expand(&self,
              ecx: @ExtCtxt,
              span: Span,
              token_tree: &[ast::token_tree],
              _: ast::SyntaxContext)
              -> MacResult {
        match self.expander {
            SyntaxExpanderTTExpanderWithoutContext(f) => {
                f(ecx, span, token_tree)
            }
        }
    }
}
J
John Clements 已提交
80

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
enum SyntaxExpanderTTItemExpander {
    SyntaxExpanderTTItemExpanderWithContext(SyntaxExpanderTTItemFun),
    SyntaxExpanderTTItemExpanderWithoutContext(SyntaxExpanderTTItemFunNoCtxt),
}

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

pub trait SyntaxExpanderTTItemTrait {
    fn expand(&self,
              cx: @ExtCtxt,
              sp: Span,
              ident: ast::Ident,
              token_tree: ~[ast::token_tree],
              context: ast::SyntaxContext)
              -> MacResult;
}

impl SyntaxExpanderTTItemTrait for SyntaxExpanderTTItem {
    fn expand(&self,
              cx: @ExtCtxt,
              sp: Span,
              ident: ast::Ident,
              token_tree: ~[ast::token_tree],
              context: ast::SyntaxContext)
              -> MacResult {
        match self.expander {
            SyntaxExpanderTTItemExpanderWithContext(fun) => {
                fun(cx, sp, ident, token_tree, context)
            }
            SyntaxExpanderTTItemExpanderWithoutContext(fun) => {
                fun(cx, sp, ident, token_tree)
            }
        }
    }
}

pub type SyntaxExpanderTTItemFun = extern "Rust" fn(@ExtCtxt,
                                                    Span,
                                                    ast::Ident,
                                                    ~[ast::token_tree],
                                                    ast::SyntaxContext)
                                                    -> MacResult;

pub type SyntaxExpanderTTItemFunNoCtxt =
    extern "Rust" fn(@ExtCtxt, Span, ast::Ident, ~[ast::token_tree])
                     -> MacResult;

pub trait AnyMacro {
    fn make_expr(&self) -> @ast::Expr;
    fn make_item(&self) -> Option<@ast::item>;
    fn make_stmt(&self) -> @ast::Stmt;
}
J
John Clements 已提交
136

137
pub enum MacResult {
138
    MRExpr(@ast::Expr),
139
    MRItem(@ast::item),
140 141
    MRAny(@AnyMacro),
    MRDef(MacroDef),
142
}
143

144
pub enum SyntaxExtension {
145
    // #[auto_encode] and such
146
    ItemDecorator(ItemDecorator),
147

148
    // Token-tree expanders
149
    NormalTT(@SyntaxExpanderTTTrait, Option<Span>),
150

J
John Clements 已提交
151 152 153
    // An IdentTT is a macro that has an
    // identifier in between the name of the
    // macro and the argument. Currently,
B
Brian Anderson 已提交
154 155
    // the only examples of this is
    // macro_rules!
J
John Clements 已提交
156

157 158
    // perhaps macro_rules! will lose its odd special identifier argument,
    // and this can go away also
159
    IdentTT(@SyntaxExpanderTTItemTrait, Option<Span>),
160
}
161

J
John Clements 已提交
162

163 164 165
// 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
166
pub type SyntaxEnv = @mut MapChain<Name, Transformer>;
J
John Clements 已提交
167 168 169

// Transformer : the codomain of SyntaxEnvs

170
pub enum Transformer {
J
John Clements 已提交
171 172
    // this identifier maps to a syntax extension or macro
    SE(SyntaxExtension),
173 174 175 176 177 178 179 180 181 182 183 184
    // blockinfo : this is ... well, it's simpler than threading
    // another whole data stack-structured data structure through
    // expansion. Basically, there's an invariant that every
    // map must contain a binding for " block".
    BlockInfo(BlockInfo)
}

pub struct BlockInfo {
    // should macros escape from this scope?
    macros_escape : bool,
    // what are the pending renames?
    pending_renames : @mut RenameList
J
John Clements 已提交
185
}
186

187
// a list of ident->name renamings
188
type RenameList = ~[(ast::Ident,Name)];
189

J
John Clements 已提交
190
// The base map of methods for expanding syntax extension
191
// AST nodes into full ASTs
J
John Clements 已提交
192
pub fn syntax_expander_table() -> SyntaxEnv {
J
John Clements 已提交
193
    // utility function to simplify creating NormalTT syntax extensions
194 195 196 197 198 199 200
    fn builtin_normal_tt_no_ctxt(f: SyntaxExpanderTTFunNoCtxt)
                                 -> @Transformer {
        @SE(NormalTT(@SyntaxExpanderTT{
            expander: SyntaxExpanderTTExpanderWithoutContext(f),
            span: None,
        } as @SyntaxExpanderTTTrait,
        None))
201
    }
J
John Clements 已提交
202
    // utility function to simplify creating IdentTT syntax extensions
J
John Clements 已提交
203 204
    // that ignore their contexts
    fn builtin_item_tt_no_ctxt(f: SyntaxExpanderTTItemFunNoCtxt) -> @Transformer {
205 206 207 208 209
        @SE(IdentTT(@SyntaxExpanderTTItem {
            expander: SyntaxExpanderTTItemExpanderWithoutContext(f),
            span: None,
        } as @SyntaxExpanderTTItemTrait,
        None))
210
    }
211
    let mut syntax_expanders = HashMap::new();
J
John Clements 已提交
212
    // NB identifier starts with space, and can't conflict with legal idents
213 214 215 216 217 218
    syntax_expanders.insert(intern(&" block"),
                            @BlockInfo(BlockInfo{
                                macros_escape : false,
                                pending_renames : @mut ~[]
                            }));
    syntax_expanders.insert(intern(&"macro_rules"),
219
                            @SE(IdentTT(@SyntaxExpanderTTItem {
P
Patrick Walton 已提交
220 221
                                expander: SyntaxExpanderTTItemExpanderWithContext(
                                    ext::tt::macro_rules::add_new_extension),
222 223 224
                                span: None,
                            } as @SyntaxExpanderTTItemTrait,
                            None)));
A
Alex Crichton 已提交
225
    syntax_expanders.insert(intern(&"fmt"),
226 227
                            builtin_normal_tt_no_ctxt(
                                ext::fmt::expand_syntax_ext));
A
Alex Crichton 已提交
228
    syntax_expanders.insert(intern(&"format_args"),
229 230
                            builtin_normal_tt_no_ctxt(
                                ext::format::expand_args));
231
    syntax_expanders.insert(
232
        intern(&"auto_encode"),
J
John Clements 已提交
233
        @SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
234
    syntax_expanders.insert(
235
        intern(&"auto_decode"),
J
John Clements 已提交
236
        @SE(ItemDecorator(ext::auto_encode::expand_auto_decode)));
237
    syntax_expanders.insert(intern(&"env"),
238 239
                            builtin_normal_tt_no_ctxt(
                                    ext::env::expand_env));
S
Steven Fackler 已提交
240
    syntax_expanders.insert(intern(&"option_env"),
241 242
                            builtin_normal_tt_no_ctxt(
                                    ext::env::expand_option_env));
243
    syntax_expanders.insert(intern("bytes"),
244 245
                            builtin_normal_tt_no_ctxt(
                                    ext::bytes::expand_syntax_ext));
246
    syntax_expanders.insert(intern("concat_idents"),
J
John Clements 已提交
247
                            builtin_normal_tt_no_ctxt(
248
                                    ext::concat_idents::expand_syntax_ext));
249
    syntax_expanders.insert(intern(&"log_syntax"),
J
John Clements 已提交
250
                            builtin_normal_tt_no_ctxt(
251
                                    ext::log_syntax::expand_syntax_ext));
252
    syntax_expanders.insert(intern(&"deriving"),
253 254
                            @SE(ItemDecorator(
                                ext::deriving::expand_meta_deriving)));
255 256

    // Quasi-quoting expanders
257
    syntax_expanders.insert(intern(&"quote_tokens"),
258 259
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_tokens));
260
    syntax_expanders.insert(intern(&"quote_expr"),
261 262
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_expr));
263
    syntax_expanders.insert(intern(&"quote_ty"),
264 265
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_ty));
266
    syntax_expanders.insert(intern(&"quote_item"),
267 268
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_item));
269
    syntax_expanders.insert(intern(&"quote_pat"),
270 271
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_pat));
272
    syntax_expanders.insert(intern(&"quote_stmt"),
273 274
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_stmt));
275

276
    syntax_expanders.insert(intern(&"line"),
J
John Clements 已提交
277
                            builtin_normal_tt_no_ctxt(
278
                                    ext::source_util::expand_line));
279
    syntax_expanders.insert(intern(&"col"),
J
John Clements 已提交
280
                            builtin_normal_tt_no_ctxt(
281
                                    ext::source_util::expand_col));
282
    syntax_expanders.insert(intern(&"file"),
J
John Clements 已提交
283
                            builtin_normal_tt_no_ctxt(
284
                                    ext::source_util::expand_file));
285
    syntax_expanders.insert(intern(&"stringify"),
J
John Clements 已提交
286
                            builtin_normal_tt_no_ctxt(
287
                                    ext::source_util::expand_stringify));
288
    syntax_expanders.insert(intern(&"include"),
J
John Clements 已提交
289
                            builtin_normal_tt_no_ctxt(
290
                                    ext::source_util::expand_include));
291
    syntax_expanders.insert(intern(&"include_str"),
J
John Clements 已提交
292
                            builtin_normal_tt_no_ctxt(
293
                                    ext::source_util::expand_include_str));
294
    syntax_expanders.insert(intern(&"include_bin"),
J
John Clements 已提交
295
                            builtin_normal_tt_no_ctxt(
296
                                    ext::source_util::expand_include_bin));
297
    syntax_expanders.insert(intern(&"module_path"),
J
John Clements 已提交
298
                            builtin_normal_tt_no_ctxt(
299
                                    ext::source_util::expand_mod));
300
    syntax_expanders.insert(intern(&"asm"),
301 302
                            builtin_normal_tt_no_ctxt(
                                    ext::asm::expand_asm));
303
    syntax_expanders.insert(intern(&"cfg"),
304 305 306 307 308
                            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));
J
John Clements 已提交
309
    MapChain::new(~syntax_expanders)
310
}
311 312 313 314

// 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.
315
pub struct ExtCtxt {
316
    parse_sess: @mut parse::ParseSess,
317
    cfg: ast::CrateConfig,
318
    backtrace: @mut Option<@ExpnInfo>,
319

320 321 322 323 324
    // These two @mut's should really not be here,
    // but the self types for CtxtRepr are all wrong
    // and there are bugs in the code for object
    // types that make this hard to get right at the
    // moment. - nmatsakis
325
    mod_path: @mut ~[ast::Ident],
326 327
    trace_mac: @mut bool
}
N
Niko Matsakis 已提交
328

329
impl ExtCtxt {
330
    pub fn new(parse_sess: @mut parse::ParseSess, cfg: ast::CrateConfig)
331
               -> @ExtCtxt {
332 333 334 335 336 337 338 339 340
        @ExtCtxt {
            parse_sess: parse_sess,
            cfg: cfg,
            backtrace: @mut None,
            mod_path: @mut ~[],
            trace_mac: @mut false
        }
    }

341 342
    pub fn codemap(&self) -> @CodeMap { self.parse_sess.cm }
    pub fn parse_sess(&self) -> @mut parse::ParseSess { self.parse_sess }
343
    pub fn cfg(&self) -> ast::CrateConfig { self.cfg.clone() }
344
    pub fn call_site(&self) -> Span {
345
        match *self.backtrace {
346
            Some(@ExpnInfo {call_site: cs, _}) => cs,
347
            None => self.bug("missing top span")
348
        }
349
    }
350 351
    pub fn print_backtrace(&self) { }
    pub fn backtrace(&self) -> Option<@ExpnInfo> { *self.backtrace }
352
    pub fn mod_push(&self, i: ast::Ident) { self.mod_path.push(i); }
353
    pub fn mod_pop(&self) { self.mod_path.pop(); }
354
    pub fn mod_path(&self) -> ~[ast::Ident] { (*self.mod_path).clone() }
355
    pub fn bt_push(&self, ei: codemap::ExpnInfo) {
356
        match ei {
357
            ExpnInfo {call_site: cs, callee: ref callee} => {
P
Patrick Walton 已提交
358
                *self.backtrace =
359
                    Some(@ExpnInfo {
360
                        call_site: Span {lo: cs.lo, hi: cs.hi,
P
Patrick Walton 已提交
361
                                         expn_info: *self.backtrace},
362
                        callee: *callee});
K
Kevin Atkinson 已提交
363
            }
364
        }
365
    }
366
    pub fn bt_pop(&self) {
367
        match *self.backtrace {
368
            Some(@ExpnInfo {
369
                call_site: Span {expn_info: prev, _}, _}) => {
P
Patrick Walton 已提交
370
                *self.backtrace = prev
371
            }
372
            _ => self.bug("tried to pop without a push")
373 374
        }
    }
375
    pub fn span_fatal(&self, sp: Span, msg: &str) -> ! {
376 377 378
        self.print_backtrace();
        self.parse_sess.span_diagnostic.span_fatal(sp, msg);
    }
379
    pub fn span_err(&self, sp: Span, msg: &str) {
380 381 382
        self.print_backtrace();
        self.parse_sess.span_diagnostic.span_err(sp, msg);
    }
383
    pub fn span_warn(&self, sp: Span, msg: &str) {
384 385 386
        self.print_backtrace();
        self.parse_sess.span_diagnostic.span_warn(sp, msg);
    }
387
    pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! {
388 389 390
        self.print_backtrace();
        self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
    }
391
    pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
392 393 394
        self.print_backtrace();
        self.parse_sess.span_diagnostic.span_bug(sp, msg);
    }
395
    pub fn bug(&self, msg: &str) -> ! {
396 397 398
        self.print_backtrace();
        self.parse_sess.span_diagnostic.handler().bug(msg);
    }
399
    pub fn trace_macros(&self) -> bool {
400 401
        *self.trace_mac
    }
402
    pub fn set_trace_macros(&self, x: bool) {
403 404
        *self.trace_mac = x
    }
405
    pub fn str_of(&self, id: ast::Ident) -> @str {
406
        ident_to_str(&id)
407
    }
408
    pub fn ident_of(&self, st: &str) -> ast::Ident {
409
        str_to_ident(st)
410 411 412
    }
}

413
pub fn expr_to_str(cx: @ExtCtxt, expr: @ast::Expr, err_msg: &str) -> (@str, ast::StrStyle) {
414
    match expr.node {
415
      ast::ExprLit(l) => match l.node {
416
        ast::lit_str(s, style) => (s, style),
417
        _ => cx.span_fatal(l.span, err_msg)
418
      },
419
      _ => cx.span_fatal(expr.span, err_msg)
420 421 422
    }
}

423
pub fn check_zero_tts(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree],
424
                      name: &str) {
425
    if tts.len() != 0 {
A
Alex Crichton 已提交
426
        cx.span_fatal(sp, format!("{} takes no arguments", name));
427
    }
428 429
}

430
pub fn get_single_str_from_tts(cx: @ExtCtxt,
431
                               sp: Span,
432
                               tts: &[ast::token_tree],
433 434
                               name: &str)
                               -> @str {
435
    if tts.len() != 1 {
A
Alex Crichton 已提交
436
        cx.span_fatal(sp, format!("{} takes 1 argument.", name));
437 438
    }

439
    match tts[0] {
440 441
        ast::tt_tok(_, token::LIT_STR(ident))
        | ast::tt_tok(_, token::LIT_STR_RAW(ident, _)) => cx.str_of(ident),
A
Alex Crichton 已提交
442
        _ => cx.span_fatal(sp, format!("{} requires a string.", name)),
443 444
    }
}
445

446
pub fn get_exprs_from_tts(cx: @ExtCtxt,
447
                          sp: Span,
448
                          tts: &[ast::token_tree]) -> ~[@ast::Expr] {
449 450
    let p = parse::new_parser_from_tts(cx.parse_sess(),
                                       cx.cfg(),
451
                                       tts.to_owned());
452
    let mut es = ~[];
453
    while *p.token != token::EOF {
454 455
        if es.len() != 0 && !p.eat(&token::COMMA) {
            cx.span_fatal(sp, "expected token: `,`");
456 457
        }
        es.push(p.parse_expr());
458
    }
459
    es
460 461
}

J
John Clements 已提交
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
// in order to have some notion of scoping for macros,
// we want to implement the notion of a transformation
// environment.

// This environment maps Names to Transformers.
// Initially, this includes macro definitions and
// block directives.



// 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.

// Note on choice of representation: I've been pushed to
// use a top-level managed pointer by some difficulties
// with pushing and popping functionally, and the ownership
// issues.  As a result, the values returned by the table
489
// also need to be managed; the &'self ... type that Maps
J
John Clements 已提交
490 491 492 493 494 495 496 497
// return won't work for things that need to get outside
// of that managed pointer.  The easiest way to do this
// is just to insist that the values in the tables are
// managed to begin with.

// a transformer env is either a base map or a map on top
// of another chain.
pub enum MapChain<K,V> {
498 499
    BaseMapChain(~HashMap<K,@V>),
    ConsMapChain(~HashMap<K,@V>,@mut MapChain<K,V>)
J
John Clements 已提交
500 501 502 503
}


// get the map from an env frame
504
impl <K: Eq + Hash + IterBytes + 'static, V: 'static> MapChain<K,V>{
J
John Clements 已提交
505
    // Constructor. I don't think we need a zero-arg one.
506
    pub fn new(init: ~HashMap<K,@V>) -> @mut MapChain<K,V> {
J
John Clements 已提交
507
        @mut BaseMapChain(init)
J
John Clements 已提交
508 509 510
    }

    // add a new frame to the environment (functionally)
511
    pub fn push_frame (@mut self) -> @mut MapChain<K,V> {
512
        @mut ConsMapChain(~HashMap::new() ,self)
J
John Clements 已提交
513 514 515 516 517 518 519 520
    }

// no need for pop, it'll just be functional.

    // utility fn...

    // ugh: can't get this to compile with mut because of the
    // lack of flow sensitivity.
521
    pub fn get_map<'a>(&'a self) -> &'a HashMap<K,@V> {
522 523 524 525 526 527
        match *self {
            BaseMapChain (~ref map) => map,
            ConsMapChain (~ref map,_) => map
        }
    }

J
John Clements 已提交
528
// traits just don't work anywhere...?
529
//impl Map<Name,SyntaxExtension> for MapChain {
J
John Clements 已提交
530

531
    pub fn contains_key (&self, key: &K) -> bool {
J
John Clements 已提交
532
        match *self {
J
John Clements 已提交
533 534
            BaseMapChain (ref map) => map.contains_key(key),
            ConsMapChain (ref map,ref rest) =>
J
John Clements 已提交
535 536 537 538 539 540 541
            (map.contains_key(key)
             || rest.contains_key(key))
        }
    }
    // should each_key and each_value operate on shadowed
    // names? I think not.
    // delaying implementing this....
542
    pub fn each_key (&self, _f: &fn (&K)->bool) {
A
Alex Crichton 已提交
543
        fail2!("unimplemented 2013-02-15T10:01");
J
John Clements 已提交
544 545
    }

546
    pub fn each_value (&self, _f: &fn (&V) -> bool) {
A
Alex Crichton 已提交
547
        fail2!("unimplemented 2013-02-15T10:02");
J
John Clements 已提交
548 549 550 551
    }

    // Returns a copy of the value that the name maps to.
    // Goes down the chain 'til it finds one (or bottom out).
552
    pub fn find (&self, key: &K) -> Option<@V> {
J
John Clements 已提交
553 554 555
        match self.get_map().find (key) {
            Some(ref v) => Some(**v),
            None => match *self {
J
John Clements 已提交
556 557
                BaseMapChain (_) => None,
                ConsMapChain (_,ref rest) => rest.find(key)
J
John Clements 已提交
558 559 560 561
            }
        }
    }

562
    pub fn find_in_topmost_frame(&self, key: &K) -> Option<@V> {
J
John Clements 已提交
563 564 565 566 567
        let map = match *self {
            BaseMapChain(ref map) => map,
            ConsMapChain(ref map,_) => map
        };
        // strip one layer of indirection off the pointer.
568
        map.find(key).map(|r| {*r})
J
John Clements 已提交
569 570
    }

J
John Clements 已提交
571
    // insert the binding into the top-level map
572
    pub fn insert (&mut self, key: K, ext: @V) -> bool {
J
John Clements 已提交
573 574
        // can't abstract over get_map because of flow sensitivity...
        match *self {
J
John Clements 已提交
575 576
            BaseMapChain (~ref mut map) => map.insert(key, ext),
            ConsMapChain (~ref mut map,_) => map.insert(key,ext)
J
John Clements 已提交
577 578
        }
    }
579 580 581 582 583
    // insert the binding into the topmost frame for which the binding
    // associated with 'n' exists and satisfies pred
    // ... there are definitely some opportunities for abstraction
    // here that I'm ignoring. (e.g., manufacturing a predicate on
    // the maps in the chain, and using an abstract "find".
584
    pub fn insert_into_frame(&mut self, key: K, ext: @V, n: K, pred: &fn(&@V)->bool) {
585 586 587 588 589
        match *self {
            BaseMapChain (~ref mut map) => {
                if satisfies_pred(map,&n,pred) {
                    map.insert(key,ext);
                } else {
A
Alex Crichton 已提交
590
                    fail2!("expected map chain containing satisfying frame")
591 592 593
                }
            },
            ConsMapChain (~ref mut map, rest) => {
594
                if satisfies_pred(map,&n,|v|pred(v)) {
595 596 597 598 599 600 601 602
                    map.insert(key,ext);
                } else {
                    rest.insert_into_frame(key,ext,n,pred)
                }
            }
        }
    }
}
J
John Clements 已提交
603

J
John Clements 已提交
604
// returns true if the binding for 'n' satisfies 'pred' in 'map'
605 606 607 608 609 610 611 612
fn satisfies_pred<K : Eq + Hash + IterBytes,V>(map : &mut HashMap<K,V>,
                                               n: &K,
                                               pred: &fn(&V)->bool)
    -> bool {
    match map.find(n) {
        Some(ref v) => (pred(*v)),
        None => false
    }
J
John Clements 已提交
613 614 615 616 617
}

#[cfg(test)]
mod test {
    use super::MapChain;
618
    use std::hashmap::HashMap;
J
John Clements 已提交
619 620

    #[test] fn testenv () {
621
        let mut a = HashMap::new();
622
        a.insert (@"abc",@15);
J
John Clements 已提交
623
        let m = MapChain::new(~a);
624
        m.insert (@"def",@16);
S
Steven Fackler 已提交
625 626
        assert_eq!(m.find(&@"abc"),Some(@15));
        assert_eq!(m.find(&@"def"),Some(@16));
627 628
        assert_eq!(*(m.find(&@"abc").unwrap()),15);
        assert_eq!(*(m.find(&@"def").unwrap()),16);
J
John Clements 已提交
629 630
        let n = m.push_frame();
        // old bindings are still present:
631 632
        assert_eq!(*(n.find(&@"abc").unwrap()),15);
        assert_eq!(*(n.find(&@"def").unwrap()),16);
633
        n.insert (@"def",@17);
J
John Clements 已提交
634
        // n shows the new binding
635 636
        assert_eq!(*(n.find(&@"abc").unwrap()),15);
        assert_eq!(*(n.find(&@"def").unwrap()),17);
J
John Clements 已提交
637
        // ... but m still has the old ones
S
Steven Fackler 已提交
638 639
        assert_eq!(m.find(&@"abc"),Some(@15));
        assert_eq!(m.find(&@"def"),Some(@16));
640 641
        assert_eq!(*(m.find(&@"abc").unwrap()),15);
        assert_eq!(*(m.find(&@"def").unwrap()),16);
J
John Clements 已提交
642 643
    }
}