Skip to content

Commit 08b6057

Browse files
committed
Macros now leave scope
Macro scope is now delimited by function, block, and module boundaries, except for modules that are marked with #[macro_escape], which allows macros to escape.
1 parent 5e319fb commit 08b6057

File tree

7 files changed

+586
-111
lines changed

7 files changed

+586
-111
lines changed

src/libsyntax/ast.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,37 @@ macro_rules! interner_key (
2929
(-3 as uint, 0u)))
3030
)
3131

32+
// an identifier contains an index into the interner
33+
// table and a SyntaxContext to track renaming and
34+
// macro expansion per Flatt et al., "Macros
35+
// That Work Together"
3236
#[deriving_eq]
33-
pub struct ident { repr: uint }
37+
pub struct ident { repr: Name }
38+
39+
// a SyntaxContext represents a chain of macro-expandings
40+
// and renamings. Each macro expansion corresponds to
41+
// a fresh uint
42+
#[deriving_eq]
43+
pub enum SyntaxContext {
44+
MT,
45+
Mark (Mrk,~SyntaxContext),
46+
Rename (~ident,Name,~SyntaxContext)
47+
}
48+
49+
/*
50+
// ** this is going to have to apply to paths, not to idents.
51+
// Returns true if these two identifiers access the same
52+
// local binding or top-level binding... that's what it
53+
// should do. For now, it just compares the names.
54+
pub fn free_ident_eq (a : ident, b: ident) -> bool{
55+
a.repr == b.repr
56+
}
57+
*/
58+
// a name represents a string, interned
59+
type Name = uint;
60+
// a mark represents a unique id associated
61+
// with a macro expansion
62+
type Mrk = uint;
3463

3564
pub impl<S:Encoder> Encodable<S> for ident {
3665
fn encode(&self, s: &S) {
@@ -1230,6 +1259,7 @@ pub enum item_ {
12301259
Option<@trait_ref>, // (optional) trait this impl implements
12311260
@Ty, // self
12321261
~[@method]),
1262+
// a macro invocation (which includes macro definition)
12331263
item_mac(mac),
12341264
}
12351265

src/libsyntax/ext/base.rs

+197-24
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ use parse::{parser, token};
2121

2222
use core::io;
2323
use core::vec;
24-
use std::oldmap::HashMap;
24+
use core::hashmap::linear::LinearMap;
2525

2626
// new-style macro! tt code:
2727
//
2828
// SyntaxExpanderTT, SyntaxExpanderTTItem, MacResult,
29-
// NormalTT, ItemTT
29+
// NormalTT, IdentTT
3030
//
3131
// also note that ast::mac used to have a bunch of extraneous cases and
3232
// is now probably a redundant AST node, can be merged with
@@ -71,36 +71,66 @@ pub enum SyntaxExtension {
7171
// Token-tree expanders
7272
NormalTT(SyntaxExpanderTT),
7373

74+
// An IdentTT is a macro that has an
75+
// identifier in between the name of the
76+
// macro and the argument. Currently,
77+
// the only examples of this are
78+
// macro_rules! and proto!
79+
7480
// perhaps macro_rules! will lose its odd special identifier argument,
7581
// and this can go away also
76-
ItemTT(SyntaxExpanderTTItem),
82+
IdentTT(SyntaxExpanderTTItem),
7783
}
7884

79-
type SyntaxExtensions = HashMap<@~str, SyntaxExtension>;
85+
type SyntaxEnv = @mut MapChain<Name, Transformer>;
86+
87+
// Name : the domain of SyntaxEnvs
88+
// want to change these to uints....
89+
// note that we use certain strings that are not legal as identifiers
90+
// to indicate, for instance, how blocks are supposed to behave.
91+
type Name = @~str;
92+
93+
// Transformer : the codomain of SyntaxEnvs
94+
95+
// NB: it may seem crazy to lump both of these into one environment;
96+
// what would it mean to bind "foo" to BlockLimit(true)? The idea
97+
// is that this follows the lead of MTWT, and accommodates growth
98+
// toward a more uniform syntax syntax (sorry) where blocks are just
99+
// another kind of transformer.
100+
101+
enum Transformer {
102+
// this identifier maps to a syntax extension or macro
103+
SE(SyntaxExtension),
104+
// should blocks occurring here limit macro scopes?
105+
ScopeMacros(bool)
106+
}
80107

81-
// A temporary hard-coded map of methods for expanding syntax extension
108+
// The base map of methods for expanding syntax extension
82109
// AST nodes into full ASTs
83-
pub fn syntax_expander_table() -> SyntaxExtensions {
110+
pub fn syntax_expander_table() -> SyntaxEnv {
84111
// utility function to simplify creating NormalTT syntax extensions
85-
fn builtin_normal_tt(f: SyntaxExpanderTTFun) -> SyntaxExtension {
86-
NormalTT(SyntaxExpanderTT{expander: f, span: None})
112+
fn builtin_normal_tt(f: SyntaxExpanderTTFun) -> @Transformer {
113+
@SE(NormalTT(SyntaxExpanderTT{expander: f, span: None}))
87114
}
88-
// utility function to simplify creating ItemTT syntax extensions
89-
fn builtin_item_tt(f: SyntaxExpanderTTItemFun) -> SyntaxExtension {
90-
ItemTT(SyntaxExpanderTTItem{expander: f, span: None})
115+
// utility function to simplify creating IdentTT syntax extensions
116+
fn builtin_item_tt(f: SyntaxExpanderTTItemFun) -> @Transformer {
117+
@SE(IdentTT(SyntaxExpanderTTItem{expander: f, span: None}))
91118
}
92-
let syntax_expanders = HashMap();
119+
let mut syntax_expanders = LinearMap::new();
120+
// NB identifier starts with space, and can't conflict with legal idents
121+
syntax_expanders.insert(@~" block",
122+
@ScopeMacros(true));
93123
syntax_expanders.insert(@~"macro_rules",
94124
builtin_item_tt(
95125
ext::tt::macro_rules::add_new_extension));
96126
syntax_expanders.insert(@~"fmt",
97127
builtin_normal_tt(ext::fmt::expand_syntax_ext));
98128
syntax_expanders.insert(
99129
@~"auto_encode",
100-
ItemDecorator(ext::auto_encode::expand_auto_encode));
130+
@SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
101131
syntax_expanders.insert(
102132
@~"auto_decode",
103-
ItemDecorator(ext::auto_encode::expand_auto_decode));
133+
@SE(ItemDecorator(ext::auto_encode::expand_auto_decode)));
104134
syntax_expanders.insert(@~"env",
105135
builtin_normal_tt(ext::env::expand_syntax_ext));
106136
syntax_expanders.insert(@~"concat_idents",
@@ -110,25 +140,25 @@ pub fn syntax_expander_table() -> SyntaxExtensions {
110140
builtin_normal_tt(
111141
ext::log_syntax::expand_syntax_ext));
112142
syntax_expanders.insert(@~"deriving_eq",
113-
ItemDecorator(
114-
ext::deriving::expand_deriving_eq));
143+
@SE(ItemDecorator(
144+
ext::deriving::expand_deriving_eq)));
115145
syntax_expanders.insert(@~"deriving_iter_bytes",
116-
ItemDecorator(
117-
ext::deriving::expand_deriving_iter_bytes));
146+
@SE(ItemDecorator(
147+
ext::deriving::expand_deriving_iter_bytes)));
118148

119149
// Quasi-quoting expanders
120150
syntax_expanders.insert(@~"quote_tokens",
121151
builtin_normal_tt(ext::quote::expand_quote_tokens));
122152
syntax_expanders.insert(@~"quote_expr",
123-
builtin_normal_tt(ext::quote::expand_quote_expr));
153+
builtin_normal_tt(ext::quote::expand_quote_expr));
124154
syntax_expanders.insert(@~"quote_ty",
125-
builtin_normal_tt(ext::quote::expand_quote_ty));
155+
builtin_normal_tt(ext::quote::expand_quote_ty));
126156
syntax_expanders.insert(@~"quote_item",
127-
builtin_normal_tt(ext::quote::expand_quote_item));
157+
builtin_normal_tt(ext::quote::expand_quote_item));
128158
syntax_expanders.insert(@~"quote_pat",
129-
builtin_normal_tt(ext::quote::expand_quote_pat));
159+
builtin_normal_tt(ext::quote::expand_quote_pat));
130160
syntax_expanders.insert(@~"quote_stmt",
131-
builtin_normal_tt(ext::quote::expand_quote_stmt));
161+
builtin_normal_tt(ext::quote::expand_quote_stmt));
132162

133163
syntax_expanders.insert(@~"line",
134164
builtin_normal_tt(
@@ -159,7 +189,7 @@ pub fn syntax_expander_table() -> SyntaxExtensions {
159189
syntax_expanders.insert(
160190
@~"trace_macros",
161191
builtin_normal_tt(ext::trace_macros::expand_trace_macros));
162-
return syntax_expanders;
192+
MapChain::new(~syntax_expanders)
163193
}
164194

165195
// One of these is made during expansion and incrementally updated as we go;
@@ -348,6 +378,149 @@ pub fn get_exprs_from_tts(cx: ext_ctxt, tts: ~[ast::token_tree])
348378
es
349379
}
350380

381+
// in order to have some notion of scoping for macros,
382+
// we want to implement the notion of a transformation
383+
// environment.
384+
385+
// This environment maps Names to Transformers.
386+
// Initially, this includes macro definitions and
387+
// block directives.
388+
389+
390+
391+
// Actually, the following implementation is parameterized
392+
// by both key and value types.
393+
394+
//impl question: how to implement it? Initially, the
395+
// env will contain only macros, so it might be painful
396+
// to add an empty frame for every context. Let's just
397+
// get it working, first....
398+
399+
// NB! the mutability of the underlying maps means that
400+
// if expansion is out-of-order, a deeper scope may be
401+
// able to refer to a macro that was added to an enclosing
402+
// scope lexically later than the deeper scope.
403+
404+
// Note on choice of representation: I've been pushed to
405+
// use a top-level managed pointer by some difficulties
406+
// with pushing and popping functionally, and the ownership
407+
// issues. As a result, the values returned by the table
408+
// also need to be managed; the &self/... type that Maps
409+
// return won't work for things that need to get outside
410+
// of that managed pointer. The easiest way to do this
411+
// is just to insist that the values in the tables are
412+
// managed to begin with.
413+
414+
// a transformer env is either a base map or a map on top
415+
// of another chain.
416+
pub enum MapChain<K,V> {
417+
TEC_Base(~LinearMap<K,@V>),
418+
TEC_Cons(~LinearMap<K,@V>,@mut MapChain<K,V>)
419+
}
420+
421+
422+
// get the map from an env frame
423+
impl <K: Eq + Hash + IterBytes ,V: Copy> MapChain<K,V>{
424+
425+
// Constructor. I don't think we need a zero-arg one.
426+
static fn new(+init: ~LinearMap<K,@V>) -> @mut MapChain<K,V> {
427+
@mut TEC_Base(init)
428+
}
429+
430+
// add a new frame to the environment (functionally)
431+
fn push_frame (@mut self) -> @mut MapChain<K,V> {
432+
@mut TEC_Cons(~LinearMap::new() ,self)
433+
}
434+
435+
// no need for pop, it'll just be functional.
436+
437+
// utility fn...
438+
439+
// ugh: can't get this to compile with mut because of the
440+
// lack of flow sensitivity.
441+
fn get_map(&self) -> &self/LinearMap<K,@V> {
442+
match *self {
443+
TEC_Base (~ref map) => map,
444+
TEC_Cons (~ref map,_) => map
445+
}
446+
}
447+
448+
// traits just don't work anywhere...?
449+
//pub impl Map<Name,SyntaxExtension> for MapChain {
450+
451+
pure fn contains_key (&self, key: &K) -> bool {
452+
match *self {
453+
TEC_Base (ref map) => map.contains_key(key),
454+
TEC_Cons (ref map,ref rest) =>
455+
(map.contains_key(key)
456+
|| rest.contains_key(key))
457+
}
458+
}
459+
// should each_key and each_value operate on shadowed
460+
// names? I think not.
461+
// delaying implementing this....
462+
pure fn each_key (&self, _f: &fn (&K)->bool) {
463+
fail!(~"unimplemented 2013-02-15T10:01");
464+
}
465+
466+
pure fn each_value (&self, _f: &fn (&V) -> bool) {
467+
fail!(~"unimplemented 2013-02-15T10:02");
468+
}
469+
470+
// Returns a copy of the value that the name maps to.
471+
// Goes down the chain 'til it finds one (or bottom out).
472+
fn find (&self, key: &K) -> Option<@V> {
473+
match self.get_map().find (key) {
474+
Some(ref v) => Some(**v),
475+
None => match *self {
476+
TEC_Base (_) => None,
477+
TEC_Cons (_,ref rest) => rest.find(key)
478+
}
479+
}
480+
}
481+
482+
// insert the binding into the top-level map
483+
fn insert (&mut self, +key: K, +ext: @V) -> bool {
484+
// can't abstract over get_map because of flow sensitivity...
485+
match *self {
486+
TEC_Base (~ref mut map) => map.insert(key, ext),
487+
TEC_Cons (~ref mut map,_) => map.insert(key,ext)
488+
}
489+
}
490+
491+
}
492+
493+
#[cfg(test)]
494+
mod test {
495+
use super::*;
496+
use super::MapChain;
497+
use util::testing::check_equal;
498+
499+
#[test] fn testenv () {
500+
let mut a = LinearMap::new();
501+
a.insert (@~"abc",@15);
502+
let m = MapChain::new(~a);
503+
m.insert (@~"def",@16);
504+
// FIXME: #4492 (ICE) check_equal(m.find(&@~"abc"),Some(@15));
505+
// .... check_equal(m.find(&@~"def"),Some(@16));
506+
check_equal(*(m.find(&@~"abc").get()),15);
507+
check_equal(*(m.find(&@~"def").get()),16);
508+
let n = m.push_frame();
509+
// old bindings are still present:
510+
check_equal(*(n.find(&@~"abc").get()),15);
511+
check_equal(*(n.find(&@~"def").get()),16);
512+
n.insert (@~"def",@17);
513+
// n shows the new binding
514+
check_equal(*(n.find(&@~"abc").get()),15);
515+
check_equal(*(n.find(&@~"def").get()),17);
516+
// ... but m still has the old ones
517+
// FIXME: #4492: check_equal(m.find(&@~"abc"),Some(@15));
518+
// FIXME: #4492: check_equal(m.find(&@~"def"),Some(@16));
519+
check_equal(*(m.find(&@~"abc").get()),15);
520+
check_equal(*(m.find(&@~"def").get()),16);
521+
}
522+
}
523+
351524
//
352525
// Local Variables:
353526
// mode: rust

0 commit comments

Comments
 (0)