Skip to content

Commit b74613b

Browse files
committed
Rewrite SyntaxEnv
I'd really like to be able to do something like struct MapChain<'next, K, V> { info: BlockInfo, map: HashMap<K, V>, next: Option<&'next mut MapChain<'next, K, V> } but I can't get the lifetimes to work out.
1 parent 1502b11 commit b74613b

File tree

2 files changed

+129
-247
lines changed

2 files changed

+129
-247
lines changed

src/libsyntax/ext/base.rs

+88-178
Original file line numberDiff line numberDiff line change
@@ -156,57 +156,48 @@ pub enum SyntaxExtension {
156156
// The SyntaxEnv is the environment that's threaded through the expansion
157157
// of macros. It contains bindings for macros, and also a special binding
158158
// for " block" (not a legal identifier) that maps to a BlockInfo
159-
pub type SyntaxEnv = @mut MapChain<Name, Transformer>;
160-
161-
// Transformer : the codomain of SyntaxEnvs
162-
163-
pub enum Transformer {
164-
// this identifier maps to a syntax extension or macro
165-
SE(SyntaxExtension),
166-
// blockinfo : this is ... well, it's simpler than threading
167-
// another whole data stack-structured data structure through
168-
// expansion. Basically, there's an invariant that every
169-
// map must contain a binding for " block".
170-
BlockInfo(BlockInfo)
171-
}
159+
pub type SyntaxEnv = MapChain<Name, SyntaxExtension>;
172160

173161
pub struct BlockInfo {
174162
// should macros escape from this scope?
175163
macros_escape : bool,
176164
// what are the pending renames?
177-
pending_renames : @mut RenameList
165+
pending_renames : RenameList
166+
}
167+
168+
impl BlockInfo {
169+
pub fn new() -> BlockInfo {
170+
BlockInfo {
171+
macros_escape: false,
172+
pending_renames: ~[]
173+
}
174+
}
178175
}
179176

180177
// a list of ident->name renamings
181-
type RenameList = ~[(ast::Ident,Name)];
178+
pub type RenameList = ~[(ast::Ident,Name)];
182179

183180
// The base map of methods for expanding syntax extension
184181
// AST nodes into full ASTs
185182
pub fn syntax_expander_table() -> SyntaxEnv {
186183
// utility function to simplify creating NormalTT syntax extensions
187184
fn builtin_normal_tt_no_ctxt(f: SyntaxExpanderTTFunNoCtxt)
188-
-> @Transformer {
189-
@SE(NormalTT(@SyntaxExpanderTT{
185+
-> SyntaxExtension {
186+
NormalTT(@SyntaxExpanderTT{
190187
expander: SyntaxExpanderTTExpanderWithoutContext(f),
191188
span: None,
192189
} as @SyntaxExpanderTTTrait,
193-
None))
190+
None)
194191
}
195192

196-
let mut syntax_expanders = HashMap::new();
197-
// NB identifier starts with space, and can't conflict with legal idents
198-
syntax_expanders.insert(intern(&" block"),
199-
@BlockInfo(BlockInfo{
200-
macros_escape : false,
201-
pending_renames : @mut ~[]
202-
}));
193+
let mut syntax_expanders = MapChain::new();
203194
syntax_expanders.insert(intern(&"macro_rules"),
204-
@SE(IdentTT(@SyntaxExpanderTTItem {
195+
IdentTT(@SyntaxExpanderTTItem {
205196
expander: SyntaxExpanderTTItemExpanderWithContext(
206197
ext::tt::macro_rules::add_new_extension),
207198
span: None,
208199
} as @SyntaxExpanderTTItemTrait,
209-
None)));
200+
None));
210201
syntax_expanders.insert(intern(&"fmt"),
211202
builtin_normal_tt_no_ctxt(
212203
ext::fmt::expand_syntax_ext));
@@ -232,8 +223,7 @@ pub fn syntax_expander_table() -> SyntaxEnv {
232223
builtin_normal_tt_no_ctxt(
233224
ext::log_syntax::expand_syntax_ext));
234225
syntax_expanders.insert(intern(&"deriving"),
235-
@SE(ItemDecorator(
236-
ext::deriving::expand_meta_deriving)));
226+
ItemDecorator(ext::deriving::expand_meta_deriving));
237227

238228
// Quasi-quoting expanders
239229
syntax_expanders.insert(intern(&"quote_tokens"),
@@ -288,7 +278,7 @@ pub fn syntax_expander_table() -> SyntaxEnv {
288278
syntax_expanders.insert(intern(&"trace_macros"),
289279
builtin_normal_tt_no_ctxt(
290280
ext::trace_macros::expand_trace_macros));
291-
MapChain::new(~syntax_expanders)
281+
syntax_expanders
292282
}
293283

294284
// One of these is made during expansion and incrementally updated as we go;
@@ -299,11 +289,6 @@ pub struct ExtCtxt {
299289
cfg: ast::CrateConfig,
300290
backtrace: Option<@ExpnInfo>,
301291

302-
// These two @mut's should really not be here,
303-
// but the self types for CtxtRepr are all wrong
304-
// and there are bugs in the code for object
305-
// types that make this hard to get right at the
306-
// moment. - nmatsakis
307292
mod_path: ~[ast::Ident],
308293
trace_mac: bool
309294
}
@@ -325,7 +310,7 @@ impl ExtCtxt {
325310
match e.node {
326311
ast::ExprMac(..) => {
327312
let mut expander = expand::MacroExpander {
328-
extsbox: @mut syntax_expander_table(),
313+
extsbox: syntax_expander_table(),
329314
cx: self,
330315
};
331316
e = expand::expand_expr(e, &mut expander);
@@ -460,11 +445,7 @@ pub fn get_exprs_from_tts(cx: &ExtCtxt,
460445
// we want to implement the notion of a transformation
461446
// environment.
462447

463-
// This environment maps Names to Transformers.
464-
// Initially, this includes macro definitions and
465-
// block directives.
466-
467-
448+
// This environment maps Names to SyntaxExtensions.
468449

469450
// Actually, the following implementation is parameterized
470451
// by both key and value types.
@@ -479,169 +460,98 @@ pub fn get_exprs_from_tts(cx: &ExtCtxt,
479460
// able to refer to a macro that was added to an enclosing
480461
// scope lexically later than the deeper scope.
481462

482-
// Note on choice of representation: I've been pushed to
483-
// use a top-level managed pointer by some difficulties
484-
// with pushing and popping functionally, and the ownership
485-
// issues. As a result, the values returned by the table
486-
// also need to be managed; the &'a ... type that Maps
487-
// return won't work for things that need to get outside
488-
// of that managed pointer. The easiest way to do this
489-
// is just to insist that the values in the tables are
490-
// managed to begin with.
491-
492-
// a transformer env is either a base map or a map on top
493-
// of another chain.
494-
pub enum MapChain<K,V> {
495-
BaseMapChain(~HashMap<K,@V>),
496-
ConsMapChain(~HashMap<K,@V>,@mut MapChain<K,V>)
463+
// Only generic to make it easy to test
464+
struct MapChainFrame<K, V> {
465+
info: BlockInfo,
466+
map: HashMap<K, V>,
497467
}
498468

469+
// Only generic to make it easy to test
470+
pub struct MapChain<K, V> {
471+
priv chain: ~[MapChainFrame<K, V>],
472+
}
499473

500-
// get the map from an env frame
501-
impl <K: Eq + Hash + IterBytes + 'static, V: 'static> MapChain<K,V>{
502-
// Constructor. I don't think we need a zero-arg one.
503-
pub fn new(init: ~HashMap<K,@V>) -> @mut MapChain<K,V> {
504-
@mut BaseMapChain(init)
474+
impl<K: Hash+Eq, V> MapChain<K, V> {
475+
pub fn new() -> MapChain<K, V> {
476+
let mut map = MapChain { chain: ~[] };
477+
map.push_frame();
478+
map
505479
}
506480

507-
// add a new frame to the environment (functionally)
508-
pub fn push_frame (@mut self) -> @mut MapChain<K,V> {
509-
@mut ConsMapChain(~HashMap::new() ,self)
481+
pub fn push_frame(&mut self) {
482+
self.chain.push(MapChainFrame {
483+
info: BlockInfo::new(),
484+
map: HashMap::new(),
485+
});
510486
}
511487

512-
// no need for pop, it'll just be functional.
513-
514-
// utility fn...
515-
516-
// ugh: can't get this to compile with mut because of the
517-
// lack of flow sensitivity.
518-
pub fn get_map<'a>(&'a self) -> &'a HashMap<K,@V> {
519-
match *self {
520-
BaseMapChain (~ref map) => map,
521-
ConsMapChain (~ref map,_) => map
522-
}
488+
pub fn pop_frame(&mut self) {
489+
assert!(self.chain.len() > 1, "too many pops on MapChain!");
490+
self.chain.pop();
523491
}
524492

525-
// traits just don't work anywhere...?
526-
//impl Map<Name,SyntaxExtension> for MapChain {
527-
528-
pub fn contains_key (&self, key: &K) -> bool {
529-
match *self {
530-
BaseMapChain (ref map) => map.contains_key(key),
531-
ConsMapChain (ref map,ref rest) =>
532-
(map.contains_key(key)
533-
|| rest.contains_key(key))
493+
fn find_escape_frame<'a>(&'a mut self) -> &'a mut MapChainFrame<K, V> {
494+
for (i, frame) in self.chain.mut_iter().enumerate().invert() {
495+
if !frame.info.macros_escape || i == 0 {
496+
return frame
497+
}
534498
}
535-
}
536-
// should each_key and each_value operate on shadowed
537-
// names? I think not.
538-
// delaying implementing this....
539-
pub fn each_key (&self, _f: |&K| -> bool) {
540-
fail!("unimplemented 2013-02-15T10:01");
541-
}
542-
543-
pub fn each_value (&self, _f: |&V| -> bool) {
544-
fail!("unimplemented 2013-02-15T10:02");
499+
unreachable!()
545500
}
546501

547-
// Returns a copy of the value that the name maps to.
548-
// Goes down the chain 'til it finds one (or bottom out).
549-
pub fn find (&self, key: &K) -> Option<@V> {
550-
match self.get_map().find (key) {
551-
Some(ref v) => Some(**v),
552-
None => match *self {
553-
BaseMapChain (_) => None,
554-
ConsMapChain (_,ref rest) => rest.find(key)
502+
pub fn find<'a>(&'a self, k: &K) -> Option<&'a V> {
503+
for frame in self.chain.iter().invert() {
504+
match frame.map.find(k) {
505+
Some(v) => return Some(v),
506+
None => {}
555507
}
556508
}
509+
None
557510
}
558511

559-
pub fn find_in_topmost_frame(&self, key: &K) -> Option<@V> {
560-
let map = match *self {
561-
BaseMapChain(ref map) => map,
562-
ConsMapChain(ref map,_) => map
563-
};
564-
// strip one layer of indirection off the pointer.
565-
map.find(key).map(|r| {*r})
566-
}
567-
568-
// insert the binding into the top-level map
569-
pub fn insert (&mut self, key: K, ext: @V) -> bool {
570-
// can't abstract over get_map because of flow sensitivity...
571-
match *self {
572-
BaseMapChain (~ref mut map) => map.insert(key, ext),
573-
ConsMapChain (~ref mut map,_) => map.insert(key,ext)
574-
}
512+
pub fn insert(&mut self, k: K, v: V) {
513+
self.find_escape_frame().map.insert(k, v);
575514
}
576-
// insert the binding into the topmost frame for which the binding
577-
// associated with 'n' exists and satisfies pred
578-
// ... there are definitely some opportunities for abstraction
579-
// here that I'm ignoring. (e.g., manufacturing a predicate on
580-
// the maps in the chain, and using an abstract "find".
581-
pub fn insert_into_frame(&mut self,
582-
key: K,
583-
ext: @V,
584-
n: K,
585-
pred: |&@V| -> bool) {
586-
match *self {
587-
BaseMapChain (~ref mut map) => {
588-
if satisfies_pred(map,&n,pred) {
589-
map.insert(key,ext);
590-
} else {
591-
fail!("expected map chain containing satisfying frame")
592-
}
593-
},
594-
ConsMapChain (~ref mut map, rest) => {
595-
if satisfies_pred(map,&n,|v|pred(v)) {
596-
map.insert(key,ext);
597-
} else {
598-
rest.insert_into_frame(key,ext,n,pred)
599-
}
600-
}
601-
}
602-
}
603-
}
604515

605-
// returns true if the binding for 'n' satisfies 'pred' in 'map'
606-
fn satisfies_pred<K:Eq + Hash + IterBytes,
607-
V>(
608-
map: &mut HashMap<K,V>,
609-
n: &K,
610-
pred: |&V| -> bool)
611-
-> bool {
612-
match map.find(n) {
613-
Some(ref v) => (pred(*v)),
614-
None => false
516+
pub fn info<'a>(&'a mut self) -> &'a mut BlockInfo {
517+
&mut self.chain[self.chain.len()-1].info
615518
}
616519
}
617520

618521
#[cfg(test)]
619522
mod test {
620523
use super::MapChain;
621-
use std::hashmap::HashMap;
622524

623525
#[test]
624526
fn testenv() {
625-
let mut a = HashMap::new();
626-
a.insert (@"abc",@15);
627-
let m = MapChain::new(~a);
628-
m.insert (@"def",@16);
629-
assert_eq!(m.find(&@"abc"),Some(@15));
630-
assert_eq!(m.find(&@"def"),Some(@16));
631-
assert_eq!(*(m.find(&@"abc").unwrap()),15);
632-
assert_eq!(*(m.find(&@"def").unwrap()),16);
633-
let n = m.push_frame();
634-
// old bindings are still present:
635-
assert_eq!(*(n.find(&@"abc").unwrap()),15);
636-
assert_eq!(*(n.find(&@"def").unwrap()),16);
637-
n.insert (@"def",@17);
638-
// n shows the new binding
639-
assert_eq!(*(n.find(&@"abc").unwrap()),15);
640-
assert_eq!(*(n.find(&@"def").unwrap()),17);
641-
// ... but m still has the old ones
642-
assert_eq!(m.find(&@"abc"),Some(@15));
643-
assert_eq!(m.find(&@"def"),Some(@16));
644-
assert_eq!(*(m.find(&@"abc").unwrap()),15);
645-
assert_eq!(*(m.find(&@"def").unwrap()),16);
527+
let mut m = MapChain::new();
528+
let (a,b,c,d) = ("a", "b", "c", "d");
529+
m.insert(1, a);
530+
assert_eq!(Some(&a), m.find(&1));
531+
532+
m.push_frame();
533+
m.info().macros_escape = true;
534+
m.insert(2, b);
535+
assert_eq!(Some(&a), m.find(&1));
536+
assert_eq!(Some(&b), m.find(&2));
537+
m.pop_frame();
538+
539+
assert_eq!(Some(&a), m.find(&1));
540+
assert_eq!(Some(&b), m.find(&2));
541+
542+
m.push_frame();
543+
m.push_frame();
544+
m.info().macros_escape = true;
545+
m.insert(3, c);
546+
assert_eq!(Some(&c), m.find(&3));
547+
m.pop_frame();
548+
assert_eq!(Some(&c), m.find(&3));
549+
m.pop_frame();
550+
assert_eq!(None, m.find(&3));
551+
552+
m.push_frame();
553+
m.insert(4, d);
554+
m.pop_frame();
555+
assert_eq!(None, m.find(&4));
646556
}
647557
}

0 commit comments

Comments
 (0)