base.rs 23.1 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
use diagnostic::span_handler;
use ext;
17
use ext::expand;
18
use parse;
A
Alex Crichton 已提交
19
use parse::token;
20
use parse::token::{ident_to_str, intern, str_to_ident};
S
Steven Fackler 已提交
21
use util::small_vector::SmallVector;
22

23
use std::hashmap::HashMap;
24

25 26
// new-style macro! tt code:
//
27
//    MacResult, NormalTT, IdentTT
28
//
29 30 31
// 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.
32

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

38 39 40 41 42
pub type ItemDecorator = extern "Rust" fn(@ExtCtxt,
                                          Span,
                                          @ast::MetaItem,
                                          ~[@ast::item])
                                          -> ~[@ast::item];
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 80 81
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 已提交
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
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;
S
Steven Fackler 已提交
135
    fn make_items(&self) -> SmallVector<@ast::item>;
136 137
    fn make_stmt(&self) -> @ast::Stmt;
}
J
John Clements 已提交
138

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

146
pub enum SyntaxExtension {
K
klutzy 已提交
147
    // #[deriving] and such
148
    ItemDecorator(ItemDecorator),
149

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

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

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

J
John Clements 已提交
164

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

// Transformer : the codomain of SyntaxEnvs

172
pub enum Transformer {
J
John Clements 已提交
173 174
    // this identifier maps to a syntax extension or macro
    SE(SyntaxExtension),
175 176 177 178 179 180 181 182 183 184 185 186
    // 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 已提交
187
}
188

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

J
John Clements 已提交
192
// The base map of methods for expanding syntax extension
193
// AST nodes into full ASTs
J
John Clements 已提交
194
pub fn syntax_expander_table() -> SyntaxEnv {
J
John Clements 已提交
195
    // utility function to simplify creating NormalTT syntax extensions
196 197 198 199 200 201 202
    fn builtin_normal_tt_no_ctxt(f: SyntaxExpanderTTFunNoCtxt)
                                 -> @Transformer {
        @SE(NormalTT(@SyntaxExpanderTT{
            expander: SyntaxExpanderTTExpanderWithoutContext(f),
            span: None,
        } as @SyntaxExpanderTTTrait,
        None))
203
    }
K
Kiet Tran 已提交
204

205
    let mut syntax_expanders = HashMap::new();
J
John Clements 已提交
206
    // NB identifier starts with space, and can't conflict with legal idents
207 208 209 210 211 212
    syntax_expanders.insert(intern(&" block"),
                            @BlockInfo(BlockInfo{
                                macros_escape : false,
                                pending_renames : @mut ~[]
                            }));
    syntax_expanders.insert(intern(&"macro_rules"),
213
                            @SE(IdentTT(@SyntaxExpanderTTItem {
P
Patrick Walton 已提交
214 215
                                expander: SyntaxExpanderTTItemExpanderWithContext(
                                    ext::tt::macro_rules::add_new_extension),
216 217 218
                                span: None,
                            } as @SyntaxExpanderTTItemTrait,
                            None)));
A
Alex Crichton 已提交
219
    syntax_expanders.insert(intern(&"fmt"),
220 221
                            builtin_normal_tt_no_ctxt(
                                ext::fmt::expand_syntax_ext));
A
Alex Crichton 已提交
222
    syntax_expanders.insert(intern(&"format_args"),
223 224
                            builtin_normal_tt_no_ctxt(
                                ext::format::expand_args));
225
    syntax_expanders.insert(intern(&"env"),
226 227
                            builtin_normal_tt_no_ctxt(
                                    ext::env::expand_env));
S
Steven Fackler 已提交
228
    syntax_expanders.insert(intern(&"option_env"),
229 230
                            builtin_normal_tt_no_ctxt(
                                    ext::env::expand_option_env));
231
    syntax_expanders.insert(intern("bytes"),
232 233
                            builtin_normal_tt_no_ctxt(
                                    ext::bytes::expand_syntax_ext));
234
    syntax_expanders.insert(intern("concat_idents"),
J
John Clements 已提交
235
                            builtin_normal_tt_no_ctxt(
236
                                    ext::concat_idents::expand_syntax_ext));
237 238 239
    syntax_expanders.insert(intern("concat"),
                            builtin_normal_tt_no_ctxt(
                                    ext::concat::expand_syntax_ext));
240
    syntax_expanders.insert(intern(&"log_syntax"),
J
John Clements 已提交
241
                            builtin_normal_tt_no_ctxt(
242
                                    ext::log_syntax::expand_syntax_ext));
243
    syntax_expanders.insert(intern(&"deriving"),
244 245
                            @SE(ItemDecorator(
                                ext::deriving::expand_meta_deriving)));
246 247

    // Quasi-quoting expanders
248
    syntax_expanders.insert(intern(&"quote_tokens"),
249 250
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_tokens));
251
    syntax_expanders.insert(intern(&"quote_expr"),
252 253
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_expr));
254
    syntax_expanders.insert(intern(&"quote_ty"),
255 256
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_ty));
257
    syntax_expanders.insert(intern(&"quote_item"),
258 259
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_item));
260
    syntax_expanders.insert(intern(&"quote_pat"),
261 262
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_pat));
263
    syntax_expanders.insert(intern(&"quote_stmt"),
264 265
                       builtin_normal_tt_no_ctxt(
                            ext::quote::expand_quote_stmt));
266

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

// 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.
306
pub struct ExtCtxt {
307
    parse_sess: @mut parse::ParseSess,
308
    cfg: ast::CrateConfig,
309
    backtrace: @mut Option<@ExpnInfo>,
310

311 312 313 314 315
    // 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
316
    mod_path: @mut ~[ast::Ident],
317 318
    trace_mac: @mut bool
}
N
Niko Matsakis 已提交
319

320
impl ExtCtxt {
321
    pub fn new(parse_sess: @mut parse::ParseSess, cfg: ast::CrateConfig)
322
               -> @ExtCtxt {
323 324 325 326 327 328 329 330 331
        @ExtCtxt {
            parse_sess: parse_sess,
            cfg: cfg,
            backtrace: @mut None,
            mod_path: @mut ~[],
            trace_mac: @mut false
        }
    }

332 333 334
    pub fn expand_expr(@self, mut e: @ast::Expr) -> @ast::Expr {
        loop {
            match e.node {
A
Alex Crichton 已提交
335
                ast::ExprMac(..) => {
336 337 338 339 340 341 342 343 344 345 346 347
                    let extsbox = @mut syntax_expander_table();
                    let expander = expand::MacroExpander {
                        extsbox: extsbox,
                        cx: self,
                    };
                    e = expand::expand_expr(extsbox, self, e, &expander);
                }
                _ => return e
            }
        }
    }

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

420
pub fn expr_to_str(cx: @ExtCtxt, expr: @ast::Expr, err_msg: &str) -> (@str, ast::StrStyle) {
421
    match expr.node {
422
      ast::ExprLit(l) => match l.node {
423
        ast::lit_str(s, style) => (s, style),
424
        _ => cx.span_fatal(l.span, err_msg)
425
      },
426
      _ => cx.span_fatal(expr.span, err_msg)
427 428 429
    }
}

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

437
pub fn get_single_str_from_tts(cx: @ExtCtxt,
438
                               sp: Span,
439
                               tts: &[ast::token_tree],
440 441
                               name: &str)
                               -> @str {
442
    if tts.len() != 1 {
A
Alex Crichton 已提交
443
        cx.span_fatal(sp, format!("{} takes 1 argument.", name));
444 445
    }

446
    match tts[0] {
447 448
        ast::tt_tok(_, token::LIT_STR(ident))
        | ast::tt_tok(_, token::LIT_STR_RAW(ident, _)) => cx.str_of(ident),
A
Alex Crichton 已提交
449
        _ => cx.span_fatal(sp, format!("{} requires a string.", name)),
450 451
    }
}
452

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

J
John Clements 已提交
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
// 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
496
// also need to be managed; the &'self ... type that Maps
J
John Clements 已提交
497 498 499 500 501 502 503 504
// 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> {
505 506
    BaseMapChain(~HashMap<K,@V>),
    ConsMapChain(~HashMap<K,@V>,@mut MapChain<K,V>)
J
John Clements 已提交
507 508 509 510
}


// get the map from an env frame
511
impl <K: Eq + Hash + IterBytes + 'static, V: 'static> MapChain<K,V>{
J
John Clements 已提交
512
    // Constructor. I don't think we need a zero-arg one.
513
    pub fn new(init: ~HashMap<K,@V>) -> @mut MapChain<K,V> {
J
John Clements 已提交
514
        @mut BaseMapChain(init)
J
John Clements 已提交
515 516 517
    }

    // add a new frame to the environment (functionally)
518
    pub fn push_frame (@mut self) -> @mut MapChain<K,V> {
519
        @mut ConsMapChain(~HashMap::new() ,self)
J
John Clements 已提交
520 521 522 523 524 525 526 527
    }

// 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.
528
    pub fn get_map<'a>(&'a self) -> &'a HashMap<K,@V> {
529 530 531 532 533 534
        match *self {
            BaseMapChain (~ref map) => map,
            ConsMapChain (~ref map,_) => map
        }
    }

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

538
    pub fn contains_key (&self, key: &K) -> bool {
J
John Clements 已提交
539
        match *self {
J
John Clements 已提交
540 541
            BaseMapChain (ref map) => map.contains_key(key),
            ConsMapChain (ref map,ref rest) =>
J
John Clements 已提交
542 543 544 545 546 547 548
            (map.contains_key(key)
             || rest.contains_key(key))
        }
    }
    // should each_key and each_value operate on shadowed
    // names? I think not.
    // delaying implementing this....
549
    pub fn each_key (&self, _f: |&K| -> bool) {
550
        fail!("unimplemented 2013-02-15T10:01");
J
John Clements 已提交
551 552
    }

553
    pub fn each_value (&self, _f: |&V| -> bool) {
554
        fail!("unimplemented 2013-02-15T10:02");
J
John Clements 已提交
555 556 557 558
    }

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

569
    pub fn find_in_topmost_frame(&self, key: &K) -> Option<@V> {
J
John Clements 已提交
570 571 572 573 574
        let map = match *self {
            BaseMapChain(ref map) => map,
            ConsMapChain(ref map,_) => map
        };
        // strip one layer of indirection off the pointer.
575
        map.find(key).map(|r| {*r})
J
John Clements 已提交
576 577
    }

J
John Clements 已提交
578
    // insert the binding into the top-level map
579
    pub fn insert (&mut self, key: K, ext: @V) -> bool {
J
John Clements 已提交
580 581
        // can't abstract over get_map because of flow sensitivity...
        match *self {
J
John Clements 已提交
582 583
            BaseMapChain (~ref mut map) => map.insert(key, ext),
            ConsMapChain (~ref mut map,_) => map.insert(key,ext)
J
John Clements 已提交
584 585
        }
    }
586 587 588 589 590
    // 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".
591 592 593 594 595
    pub fn insert_into_frame(&mut self,
                             key: K,
                             ext: @V,
                             n: K,
                             pred: |&@V| -> bool) {
596 597 598 599 600
        match *self {
            BaseMapChain (~ref mut map) => {
                if satisfies_pred(map,&n,pred) {
                    map.insert(key,ext);
                } else {
601
                    fail!("expected map chain containing satisfying frame")
602 603 604
                }
            },
            ConsMapChain (~ref mut map, rest) => {
605
                if satisfies_pred(map,&n,|v|pred(v)) {
606 607 608 609 610 611 612 613
                    map.insert(key,ext);
                } else {
                    rest.insert_into_frame(key,ext,n,pred)
                }
            }
        }
    }
}
J
John Clements 已提交
614

J
John Clements 已提交
615
// returns true if the binding for 'n' satisfies 'pred' in 'map'
616 617 618 619 620 621
fn satisfies_pred<K:Eq + Hash + IterBytes,
                  V>(
                  map: &mut HashMap<K,V>,
                  n: &K,
                  pred: |&V| -> bool)
                  -> bool {
622 623 624 625
    match map.find(n) {
        Some(ref v) => (pred(*v)),
        None => false
    }
J
John Clements 已提交
626 627 628 629 630
}

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

633 634
    #[test]
    fn testenv() {
635
        let mut a = HashMap::new();
636
        a.insert (@"abc",@15);
J
John Clements 已提交
637
        let m = MapChain::new(~a);
638
        m.insert (@"def",@16);
S
Steven Fackler 已提交
639 640
        assert_eq!(m.find(&@"abc"),Some(@15));
        assert_eq!(m.find(&@"def"),Some(@16));
641 642
        assert_eq!(*(m.find(&@"abc").unwrap()),15);
        assert_eq!(*(m.find(&@"def").unwrap()),16);
J
John Clements 已提交
643 644
        let n = m.push_frame();
        // old bindings are still present:
645 646
        assert_eq!(*(n.find(&@"abc").unwrap()),15);
        assert_eq!(*(n.find(&@"def").unwrap()),16);
647
        n.insert (@"def",@17);
J
John Clements 已提交
648
        // n shows the new binding
649 650
        assert_eq!(*(n.find(&@"abc").unwrap()),15);
        assert_eq!(*(n.find(&@"def").unwrap()),17);
J
John Clements 已提交
651
        // ... but m still has the old ones
S
Steven Fackler 已提交
652 653
        assert_eq!(m.find(&@"abc"),Some(@15));
        assert_eq!(m.find(&@"def"),Some(@16));
654 655
        assert_eq!(*(m.find(&@"abc").unwrap()),15);
        assert_eq!(*(m.find(&@"def").unwrap()),16);
J
John Clements 已提交
656 657
    }
}