]>
Commit | Line | Data |
---|---|---|
223e47cc LB |
1 | // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
223e47cc | 11 | use ast; |
970d7e83 | 12 | use ast::Name; |
223e47cc LB |
13 | use codemap; |
14 | use codemap::{CodeMap, span, ExpnInfo, ExpandedFrom}; | |
15 | use codemap::CallInfo; | |
16 | use diagnostic::span_handler; | |
17 | use ext; | |
18 | use parse; | |
19 | use parse::token; | |
970d7e83 | 20 | use parse::token::{ident_to_str, intern, str_to_ident}; |
223e47cc | 21 | |
970d7e83 LB |
22 | use std::vec; |
23 | use std::hashmap::HashMap; | |
223e47cc LB |
24 | |
25 | // new-style macro! tt code: | |
26 | // | |
27 | // SyntaxExpanderTT, SyntaxExpanderTTItem, MacResult, | |
28 | // NormalTT, IdentTT | |
29 | // | |
30 | // also note that ast::mac used to have a bunch of extraneous cases and | |
31 | // is now probably a redundant AST node, can be merged with | |
32 | // ast::mac_invoc_tt. | |
33 | ||
34 | pub struct MacroDef { | |
970d7e83 | 35 | name: @str, |
223e47cc LB |
36 | ext: SyntaxExtension |
37 | } | |
38 | ||
970d7e83 | 39 | pub type ItemDecorator = @fn(@ExtCtxt, |
223e47cc LB |
40 | span, |
41 | @ast::meta_item, | |
42 | ~[@ast::item]) | |
43 | -> ~[@ast::item]; | |
44 | ||
45 | pub struct SyntaxExpanderTT { | |
46 | expander: SyntaxExpanderTTFun, | |
47 | span: Option<span> | |
48 | } | |
49 | ||
970d7e83 | 50 | pub type SyntaxExpanderTTFun = @fn(@ExtCtxt, |
223e47cc LB |
51 | span, |
52 | &[ast::token_tree]) | |
53 | -> MacResult; | |
54 | ||
55 | pub struct SyntaxExpanderTTItem { | |
56 | expander: SyntaxExpanderTTItemFun, | |
57 | span: Option<span> | |
58 | } | |
59 | ||
970d7e83 | 60 | pub type SyntaxExpanderTTItemFun = @fn(@ExtCtxt, |
223e47cc LB |
61 | span, |
62 | ast::ident, | |
63 | ~[ast::token_tree]) | |
64 | -> MacResult; | |
65 | ||
66 | pub enum MacResult { | |
67 | MRExpr(@ast::expr), | |
68 | MRItem(@ast::item), | |
69 | MRAny(@fn() -> @ast::expr, | |
70 | @fn() -> Option<@ast::item>, | |
71 | @fn() -> @ast::stmt), | |
72 | MRDef(MacroDef) | |
73 | } | |
74 | ||
75 | pub enum SyntaxExtension { | |
76 | ||
77 | // #[auto_encode] and such | |
78 | ItemDecorator(ItemDecorator), | |
79 | ||
80 | // Token-tree expanders | |
81 | NormalTT(SyntaxExpanderTT), | |
82 | ||
83 | // An IdentTT is a macro that has an | |
84 | // identifier in between the name of the | |
85 | // macro and the argument. Currently, | |
86 | // the only examples of this are | |
87 | // macro_rules! and proto! | |
88 | ||
89 | // perhaps macro_rules! will lose its odd special identifier argument, | |
90 | // and this can go away also | |
91 | IdentTT(SyntaxExpanderTTItem), | |
92 | } | |
93 | ||
970d7e83 LB |
94 | // The SyntaxEnv is the environment that's threaded through the expansion |
95 | // of macros. It contains bindings for macros, and also a special binding | |
96 | // for " block" (not a legal identifier) that maps to a BlockInfo | |
223e47cc LB |
97 | pub type SyntaxEnv = @mut MapChain<Name, Transformer>; |
98 | ||
223e47cc LB |
99 | // Transformer : the codomain of SyntaxEnvs |
100 | ||
223e47cc LB |
101 | pub enum Transformer { |
102 | // this identifier maps to a syntax extension or macro | |
103 | SE(SyntaxExtension), | |
970d7e83 LB |
104 | // blockinfo : this is ... well, it's simpler than threading |
105 | // another whole data stack-structured data structure through | |
106 | // expansion. Basically, there's an invariant that every | |
107 | // map must contain a binding for " block". | |
108 | BlockInfo(BlockInfo) | |
223e47cc LB |
109 | } |
110 | ||
970d7e83 LB |
111 | pub struct BlockInfo { |
112 | // should macros escape from this scope? | |
113 | macros_escape : bool, | |
114 | // what are the pending renames? | |
115 | pending_renames : @mut RenameList | |
116 | } | |
117 | ||
118 | // a list of ident->name renamings | |
119 | type RenameList = ~[(ast::ident,Name)]; | |
120 | ||
223e47cc LB |
121 | // The base map of methods for expanding syntax extension |
122 | // AST nodes into full ASTs | |
123 | pub fn syntax_expander_table() -> SyntaxEnv { | |
124 | // utility function to simplify creating NormalTT syntax extensions | |
125 | fn builtin_normal_tt(f: SyntaxExpanderTTFun) -> @Transformer { | |
126 | @SE(NormalTT(SyntaxExpanderTT{expander: f, span: None})) | |
127 | } | |
128 | // utility function to simplify creating IdentTT syntax extensions | |
129 | fn builtin_item_tt(f: SyntaxExpanderTTItemFun) -> @Transformer { | |
130 | @SE(IdentTT(SyntaxExpanderTTItem{expander: f, span: None})) | |
131 | } | |
970d7e83 | 132 | let mut syntax_expanders = HashMap::new(); |
223e47cc | 133 | // NB identifier starts with space, and can't conflict with legal idents |
970d7e83 LB |
134 | syntax_expanders.insert(intern(&" block"), |
135 | @BlockInfo(BlockInfo{ | |
136 | macros_escape : false, | |
137 | pending_renames : @mut ~[] | |
138 | })); | |
139 | syntax_expanders.insert(intern(&"macro_rules"), | |
223e47cc LB |
140 | builtin_item_tt( |
141 | ext::tt::macro_rules::add_new_extension)); | |
970d7e83 | 142 | syntax_expanders.insert(intern(&"fmt"), |
223e47cc LB |
143 | builtin_normal_tt(ext::fmt::expand_syntax_ext)); |
144 | syntax_expanders.insert( | |
970d7e83 | 145 | intern(&"auto_encode"), |
223e47cc LB |
146 | @SE(ItemDecorator(ext::auto_encode::expand_auto_encode))); |
147 | syntax_expanders.insert( | |
970d7e83 | 148 | intern(&"auto_decode"), |
223e47cc | 149 | @SE(ItemDecorator(ext::auto_encode::expand_auto_decode))); |
970d7e83 | 150 | syntax_expanders.insert(intern(&"env"), |
223e47cc | 151 | builtin_normal_tt(ext::env::expand_syntax_ext)); |
970d7e83 LB |
152 | syntax_expanders.insert(intern("bytes"), |
153 | builtin_normal_tt(ext::bytes::expand_syntax_ext)); | |
154 | syntax_expanders.insert(intern("concat_idents"), | |
223e47cc LB |
155 | builtin_normal_tt( |
156 | ext::concat_idents::expand_syntax_ext)); | |
970d7e83 | 157 | syntax_expanders.insert(intern(&"log_syntax"), |
223e47cc LB |
158 | builtin_normal_tt( |
159 | ext::log_syntax::expand_syntax_ext)); | |
970d7e83 | 160 | syntax_expanders.insert(intern(&"deriving"), |
223e47cc LB |
161 | @SE(ItemDecorator( |
162 | ext::deriving::expand_meta_deriving))); | |
223e47cc LB |
163 | |
164 | // Quasi-quoting expanders | |
970d7e83 | 165 | syntax_expanders.insert(intern(&"quote_tokens"), |
223e47cc | 166 | builtin_normal_tt(ext::quote::expand_quote_tokens)); |
970d7e83 | 167 | syntax_expanders.insert(intern(&"quote_expr"), |
223e47cc | 168 | builtin_normal_tt(ext::quote::expand_quote_expr)); |
970d7e83 | 169 | syntax_expanders.insert(intern(&"quote_ty"), |
223e47cc | 170 | builtin_normal_tt(ext::quote::expand_quote_ty)); |
970d7e83 | 171 | syntax_expanders.insert(intern(&"quote_item"), |
223e47cc | 172 | builtin_normal_tt(ext::quote::expand_quote_item)); |
970d7e83 | 173 | syntax_expanders.insert(intern(&"quote_pat"), |
223e47cc | 174 | builtin_normal_tt(ext::quote::expand_quote_pat)); |
970d7e83 | 175 | syntax_expanders.insert(intern(&"quote_stmt"), |
223e47cc LB |
176 | builtin_normal_tt(ext::quote::expand_quote_stmt)); |
177 | ||
970d7e83 | 178 | syntax_expanders.insert(intern(&"line"), |
223e47cc LB |
179 | builtin_normal_tt( |
180 | ext::source_util::expand_line)); | |
970d7e83 | 181 | syntax_expanders.insert(intern(&"col"), |
223e47cc LB |
182 | builtin_normal_tt( |
183 | ext::source_util::expand_col)); | |
970d7e83 | 184 | syntax_expanders.insert(intern(&"file"), |
223e47cc LB |
185 | builtin_normal_tt( |
186 | ext::source_util::expand_file)); | |
970d7e83 | 187 | syntax_expanders.insert(intern(&"stringify"), |
223e47cc LB |
188 | builtin_normal_tt( |
189 | ext::source_util::expand_stringify)); | |
970d7e83 | 190 | syntax_expanders.insert(intern(&"include"), |
223e47cc LB |
191 | builtin_normal_tt( |
192 | ext::source_util::expand_include)); | |
970d7e83 | 193 | syntax_expanders.insert(intern(&"include_str"), |
223e47cc LB |
194 | builtin_normal_tt( |
195 | ext::source_util::expand_include_str)); | |
970d7e83 | 196 | syntax_expanders.insert(intern(&"include_bin"), |
223e47cc LB |
197 | builtin_normal_tt( |
198 | ext::source_util::expand_include_bin)); | |
970d7e83 | 199 | syntax_expanders.insert(intern(&"module_path"), |
223e47cc LB |
200 | builtin_normal_tt( |
201 | ext::source_util::expand_mod)); | |
970d7e83 | 202 | syntax_expanders.insert(intern(&"proto"), |
223e47cc | 203 | builtin_item_tt(ext::pipes::expand_proto)); |
970d7e83 | 204 | syntax_expanders.insert(intern(&"asm"), |
223e47cc LB |
205 | builtin_normal_tt(ext::asm::expand_asm)); |
206 | syntax_expanders.insert( | |
970d7e83 | 207 | intern(&"trace_macros"), |
223e47cc LB |
208 | builtin_normal_tt(ext::trace_macros::expand_trace_macros)); |
209 | MapChain::new(~syntax_expanders) | |
210 | } | |
211 | ||
212 | // One of these is made during expansion and incrementally updated as we go; | |
213 | // when a macro expansion occurs, the resulting nodes have the backtrace() | |
214 | // -> expn_info of their expansion context stored into their span. | |
970d7e83 LB |
215 | pub struct ExtCtxt { |
216 | parse_sess: @mut parse::ParseSess, | |
217 | cfg: ast::crate_cfg, | |
218 | backtrace: @mut Option<@ExpnInfo>, | |
219 | ||
220 | // These two @mut's should really not be here, | |
221 | // but the self types for CtxtRepr are all wrong | |
222 | // and there are bugs in the code for object | |
223 | // types that make this hard to get right at the | |
224 | // moment. - nmatsakis | |
225 | mod_path: @mut ~[ast::ident], | |
226 | trace_mac: @mut bool | |
223e47cc LB |
227 | } |
228 | ||
970d7e83 LB |
229 | impl ExtCtxt { |
230 | pub fn new(parse_sess: @mut parse::ParseSess, cfg: ast::crate_cfg) | |
231 | -> @ExtCtxt { | |
232 | @ExtCtxt { | |
233 | parse_sess: parse_sess, | |
234 | cfg: cfg, | |
235 | backtrace: @mut None, | |
236 | mod_path: @mut ~[], | |
237 | trace_mac: @mut false | |
238 | } | |
239 | } | |
240 | ||
241 | pub fn codemap(&self) -> @CodeMap { self.parse_sess.cm } | |
242 | pub fn parse_sess(&self) -> @mut parse::ParseSess { self.parse_sess } | |
243 | pub fn cfg(&self) -> ast::crate_cfg { copy self.cfg } | |
244 | pub fn call_site(&self) -> span { | |
245 | match *self.backtrace { | |
246 | Some(@ExpandedFrom(CallInfo {call_site: cs, _})) => cs, | |
247 | None => self.bug("missing top span") | |
223e47cc | 248 | } |
970d7e83 LB |
249 | } |
250 | pub fn print_backtrace(&self) { } | |
251 | pub fn backtrace(&self) -> Option<@ExpnInfo> { *self.backtrace } | |
252 | pub fn mod_push(&self, i: ast::ident) { self.mod_path.push(i); } | |
253 | pub fn mod_pop(&self) { self.mod_path.pop(); } | |
254 | pub fn mod_path(&self) -> ~[ast::ident] { copy *self.mod_path } | |
255 | pub fn bt_push(&self, ei: codemap::ExpnInfo) { | |
256 | match ei { | |
257 | ExpandedFrom(CallInfo {call_site: cs, callee: ref callee}) => { | |
223e47cc LB |
258 | *self.backtrace = |
259 | Some(@ExpandedFrom(CallInfo { | |
260 | call_site: span {lo: cs.lo, hi: cs.hi, | |
261 | expn_info: *self.backtrace}, | |
262 | callee: copy *callee})); | |
223e47cc LB |
263 | } |
264 | } | |
970d7e83 LB |
265 | } |
266 | pub fn bt_pop(&self) { | |
267 | match *self.backtrace { | |
268 | Some(@ExpandedFrom( | |
269 | CallInfo { | |
270 | call_site: span {expn_info: prev, _}, _ | |
271 | })) => { | |
223e47cc | 272 | *self.backtrace = prev |
223e47cc | 273 | } |
970d7e83 | 274 | _ => self.bug("tried to pop without a push") |
223e47cc | 275 | } |
223e47cc | 276 | } |
970d7e83 LB |
277 | pub fn span_fatal(&self, sp: span, msg: &str) -> ! { |
278 | self.print_backtrace(); | |
279 | self.parse_sess.span_diagnostic.span_fatal(sp, msg); | |
280 | } | |
281 | pub fn span_err(&self, sp: span, msg: &str) { | |
282 | self.print_backtrace(); | |
283 | self.parse_sess.span_diagnostic.span_err(sp, msg); | |
284 | } | |
285 | pub fn span_warn(&self, sp: span, msg: &str) { | |
286 | self.print_backtrace(); | |
287 | self.parse_sess.span_diagnostic.span_warn(sp, msg); | |
288 | } | |
289 | pub fn span_unimpl(&self, sp: span, msg: &str) -> ! { | |
290 | self.print_backtrace(); | |
291 | self.parse_sess.span_diagnostic.span_unimpl(sp, msg); | |
292 | } | |
293 | pub fn span_bug(&self, sp: span, msg: &str) -> ! { | |
294 | self.print_backtrace(); | |
295 | self.parse_sess.span_diagnostic.span_bug(sp, msg); | |
296 | } | |
297 | pub fn bug(&self, msg: &str) -> ! { | |
298 | self.print_backtrace(); | |
299 | self.parse_sess.span_diagnostic.handler().bug(msg); | |
300 | } | |
301 | pub fn next_id(&self) -> ast::node_id { | |
302 | parse::next_node_id(self.parse_sess) | |
303 | } | |
304 | pub fn trace_macros(&self) -> bool { | |
305 | *self.trace_mac | |
306 | } | |
307 | pub fn set_trace_macros(&self, x: bool) { | |
308 | *self.trace_mac = x | |
309 | } | |
310 | pub fn str_of(&self, id: ast::ident) -> @str { | |
311 | ident_to_str(&id) | |
312 | } | |
313 | pub fn ident_of(&self, st: &str) -> ast::ident { | |
314 | str_to_ident(st) | |
315 | } | |
223e47cc LB |
316 | } |
317 | ||
970d7e83 | 318 | pub fn expr_to_str(cx: @ExtCtxt, expr: @ast::expr, err_msg: ~str) -> @str { |
223e47cc LB |
319 | match expr.node { |
320 | ast::expr_lit(l) => match l.node { | |
970d7e83 | 321 | ast::lit_str(s) => s, |
223e47cc LB |
322 | _ => cx.span_fatal(l.span, err_msg) |
323 | }, | |
324 | _ => cx.span_fatal(expr.span, err_msg) | |
325 | } | |
326 | } | |
327 | ||
970d7e83 | 328 | pub fn expr_to_ident(cx: @ExtCtxt, |
223e47cc | 329 | expr: @ast::expr, |
970d7e83 | 330 | err_msg: &str) -> ast::ident { |
223e47cc LB |
331 | match expr.node { |
332 | ast::expr_path(p) => { | |
970d7e83 | 333 | if p.types.len() > 0u || p.idents.len() != 1u { |
223e47cc LB |
334 | cx.span_fatal(expr.span, err_msg); |
335 | } | |
336 | return p.idents[0]; | |
337 | } | |
338 | _ => cx.span_fatal(expr.span, err_msg) | |
339 | } | |
340 | } | |
341 | ||
970d7e83 | 342 | pub fn check_zero_tts(cx: @ExtCtxt, sp: span, tts: &[ast::token_tree], |
223e47cc LB |
343 | name: &str) { |
344 | if tts.len() != 0 { | |
345 | cx.span_fatal(sp, fmt!("%s takes no arguments", name)); | |
346 | } | |
347 | } | |
348 | ||
970d7e83 | 349 | pub fn get_single_str_from_tts(cx: @ExtCtxt, |
223e47cc LB |
350 | sp: span, |
351 | tts: &[ast::token_tree], | |
970d7e83 | 352 | name: &str) -> @str { |
223e47cc LB |
353 | if tts.len() != 1 { |
354 | cx.span_fatal(sp, fmt!("%s takes 1 argument.", name)); | |
355 | } | |
356 | ||
357 | match tts[0] { | |
358 | ast::tt_tok(_, token::LIT_STR(ident)) => cx.str_of(ident), | |
359 | _ => | |
360 | cx.span_fatal(sp, fmt!("%s requires a string.", name)) | |
361 | } | |
362 | } | |
363 | ||
970d7e83 | 364 | pub fn get_exprs_from_tts(cx: @ExtCtxt, tts: &[ast::token_tree]) |
223e47cc LB |
365 | -> ~[@ast::expr] { |
366 | let p = parse::new_parser_from_tts(cx.parse_sess(), | |
367 | cx.cfg(), | |
970d7e83 | 368 | vec::to_owned(tts)); |
223e47cc LB |
369 | let mut es = ~[]; |
370 | while *p.token != token::EOF { | |
371 | if es.len() != 0 { | |
372 | p.eat(&token::COMMA); | |
373 | } | |
374 | es.push(p.parse_expr()); | |
375 | } | |
376 | es | |
377 | } | |
378 | ||
379 | // in order to have some notion of scoping for macros, | |
380 | // we want to implement the notion of a transformation | |
381 | // environment. | |
382 | ||
383 | // This environment maps Names to Transformers. | |
384 | // Initially, this includes macro definitions and | |
385 | // block directives. | |
386 | ||
387 | ||
388 | ||
389 | // Actually, the following implementation is parameterized | |
390 | // by both key and value types. | |
391 | ||
392 | //impl question: how to implement it? Initially, the | |
393 | // env will contain only macros, so it might be painful | |
394 | // to add an empty frame for every context. Let's just | |
395 | // get it working, first.... | |
396 | ||
397 | // NB! the mutability of the underlying maps means that | |
398 | // if expansion is out-of-order, a deeper scope may be | |
399 | // able to refer to a macro that was added to an enclosing | |
400 | // scope lexically later than the deeper scope. | |
401 | ||
402 | // Note on choice of representation: I've been pushed to | |
403 | // use a top-level managed pointer by some difficulties | |
404 | // with pushing and popping functionally, and the ownership | |
405 | // issues. As a result, the values returned by the table | |
406 | // also need to be managed; the &'self ... type that Maps | |
407 | // return won't work for things that need to get outside | |
408 | // of that managed pointer. The easiest way to do this | |
409 | // is just to insist that the values in the tables are | |
410 | // managed to begin with. | |
411 | ||
412 | // a transformer env is either a base map or a map on top | |
413 | // of another chain. | |
414 | pub enum MapChain<K,V> { | |
970d7e83 LB |
415 | BaseMapChain(~HashMap<K,@V>), |
416 | ConsMapChain(~HashMap<K,@V>,@mut MapChain<K,V>) | |
223e47cc LB |
417 | } |
418 | ||
419 | ||
420 | // get the map from an env frame | |
421 | impl <K: Eq + Hash + IterBytes ,V: Copy> MapChain<K,V>{ | |
422 | ||
423 | // Constructor. I don't think we need a zero-arg one. | |
970d7e83 | 424 | fn new(init: ~HashMap<K,@V>) -> @mut MapChain<K,V> { |
223e47cc LB |
425 | @mut BaseMapChain(init) |
426 | } | |
427 | ||
428 | // add a new frame to the environment (functionally) | |
429 | fn push_frame (@mut self) -> @mut MapChain<K,V> { | |
970d7e83 | 430 | @mut ConsMapChain(~HashMap::new() ,self) |
223e47cc LB |
431 | } |
432 | ||
433 | // no need for pop, it'll just be functional. | |
434 | ||
435 | // utility fn... | |
436 | ||
437 | // ugh: can't get this to compile with mut because of the | |
438 | // lack of flow sensitivity. | |
970d7e83 | 439 | fn get_map<'a>(&'a self) -> &'a HashMap<K,@V> { |
223e47cc LB |
440 | match *self { |
441 | BaseMapChain (~ref map) => map, | |
442 | ConsMapChain (~ref map,_) => map | |
443 | } | |
444 | } | |
445 | ||
446 | // traits just don't work anywhere...? | |
970d7e83 | 447 | //impl Map<Name,SyntaxExtension> for MapChain { |
223e47cc LB |
448 | |
449 | fn contains_key (&self, key: &K) -> bool { | |
450 | match *self { | |
451 | BaseMapChain (ref map) => map.contains_key(key), | |
452 | ConsMapChain (ref map,ref rest) => | |
453 | (map.contains_key(key) | |
454 | || rest.contains_key(key)) | |
455 | } | |
456 | } | |
457 | // should each_key and each_value operate on shadowed | |
458 | // names? I think not. | |
459 | // delaying implementing this.... | |
460 | fn each_key (&self, _f: &fn (&K)->bool) { | |
970d7e83 | 461 | fail!("unimplemented 2013-02-15T10:01"); |
223e47cc LB |
462 | } |
463 | ||
464 | fn each_value (&self, _f: &fn (&V) -> bool) { | |
970d7e83 | 465 | fail!("unimplemented 2013-02-15T10:02"); |
223e47cc LB |
466 | } |
467 | ||
468 | // Returns a copy of the value that the name maps to. | |
469 | // Goes down the chain 'til it finds one (or bottom out). | |
470 | fn find (&self, key: &K) -> Option<@V> { | |
471 | match self.get_map().find (key) { | |
472 | Some(ref v) => Some(**v), | |
473 | None => match *self { | |
474 | BaseMapChain (_) => None, | |
475 | ConsMapChain (_,ref rest) => rest.find(key) | |
476 | } | |
477 | } | |
478 | } | |
479 | ||
970d7e83 LB |
480 | fn find_in_topmost_frame(&self, key: &K) -> Option<@V> { |
481 | let map = match *self { | |
482 | BaseMapChain(ref map) => map, | |
483 | ConsMapChain(ref map,_) => map | |
484 | }; | |
485 | // strip one layer of indirection off the pointer. | |
486 | map.find(key).map(|r| {**r}) | |
487 | } | |
488 | ||
223e47cc | 489 | // insert the binding into the top-level map |
970d7e83 | 490 | fn insert (&mut self, key: K, ext: @V) -> bool { |
223e47cc LB |
491 | // can't abstract over get_map because of flow sensitivity... |
492 | match *self { | |
493 | BaseMapChain (~ref mut map) => map.insert(key, ext), | |
494 | ConsMapChain (~ref mut map,_) => map.insert(key,ext) | |
495 | } | |
496 | } | |
970d7e83 LB |
497 | // insert the binding into the topmost frame for which the binding |
498 | // associated with 'n' exists and satisfies pred | |
499 | // ... there are definitely some opportunities for abstraction | |
500 | // here that I'm ignoring. (e.g., manufacturing a predicate on | |
501 | // the maps in the chain, and using an abstract "find". | |
502 | fn insert_into_frame(&mut self, key: K, ext: @V, n: K, pred: &fn(&@V)->bool) { | |
503 | match *self { | |
504 | BaseMapChain (~ref mut map) => { | |
505 | if satisfies_pred(map,&n,pred) { | |
506 | map.insert(key,ext); | |
507 | } else { | |
508 | fail!(~"expected map chain containing satisfying frame") | |
509 | } | |
510 | }, | |
511 | ConsMapChain (~ref mut map, rest) => { | |
512 | if satisfies_pred(map,&n,|v|pred(v)) { | |
513 | map.insert(key,ext); | |
514 | } else { | |
515 | rest.insert_into_frame(key,ext,n,pred) | |
516 | } | |
517 | } | |
518 | } | |
519 | } | |
520 | } | |
223e47cc | 521 | |
970d7e83 LB |
522 | // returns true if the binding for 'n' satisfies 'pred' in 'map' |
523 | fn satisfies_pred<K : Eq + Hash + IterBytes,V>(map : &mut HashMap<K,V>, | |
524 | n: &K, | |
525 | pred: &fn(&V)->bool) | |
526 | -> bool { | |
527 | match map.find(n) { | |
528 | Some(ref v) => (pred(*v)), | |
529 | None => false | |
530 | } | |
223e47cc LB |
531 | } |
532 | ||
533 | #[cfg(test)] | |
534 | mod test { | |
535 | use super::MapChain; | |
970d7e83 | 536 | use std::hashmap::HashMap; |
223e47cc LB |
537 | |
538 | #[test] fn testenv () { | |
970d7e83 LB |
539 | let mut a = HashMap::new(); |
540 | a.insert (@"abc",@15); | |
223e47cc | 541 | let m = MapChain::new(~a); |
970d7e83 LB |
542 | m.insert (@"def",@16); |
543 | // FIXME: #4492 (ICE) assert_eq!(m.find(&@"abc"),Some(@15)); | |
544 | // .... assert_eq!(m.find(&@"def"),Some(@16)); | |
545 | assert_eq!(*(m.find(&@"abc").get()),15); | |
546 | assert_eq!(*(m.find(&@"def").get()),16); | |
223e47cc LB |
547 | let n = m.push_frame(); |
548 | // old bindings are still present: | |
970d7e83 LB |
549 | assert_eq!(*(n.find(&@"abc").get()),15); |
550 | assert_eq!(*(n.find(&@"def").get()),16); | |
551 | n.insert (@"def",@17); | |
223e47cc | 552 | // n shows the new binding |
970d7e83 LB |
553 | assert_eq!(*(n.find(&@"abc").get()),15); |
554 | assert_eq!(*(n.find(&@"def").get()),17); | |
223e47cc | 555 | // ... but m still has the old ones |
970d7e83 LB |
556 | // FIXME: #4492: assert_eq!(m.find(&@"abc"),Some(@15)); |
557 | // FIXME: #4492: assert_eq!(m.find(&@"def"),Some(@16)); | |
558 | assert_eq!(*(m.find(&@"abc").get()),15); | |
559 | assert_eq!(*(m.find(&@"def").get()),16); | |
223e47cc LB |
560 | } |
561 | } |