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.
This commit is contained in:
John Clements
2013-02-26 10:15:29 -08:00
parent 5e319fb282
commit 08b6057538
7 changed files with 585 additions and 110 deletions

View File

@@ -21,12 +21,12 @@ use parse::{parser, token};
use core::io;
use core::vec;
use std::oldmap::HashMap;
use core::hashmap::linear::LinearMap;
// new-style macro! tt code:
//
// SyntaxExpanderTT, SyntaxExpanderTTItem, MacResult,
// NormalTT, ItemTT
// NormalTT, IdentTT
//
// 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
@@ -71,25 +71,55 @@ pub enum SyntaxExtension {
// Token-tree expanders
NormalTT(SyntaxExpanderTT),
// An IdentTT is a macro that has an
// identifier in between the name of the
// macro and the argument. Currently,
// the only examples of this are
// macro_rules! and proto!
// perhaps macro_rules! will lose its odd special identifier argument,
// and this can go away also
ItemTT(SyntaxExpanderTTItem),
IdentTT(SyntaxExpanderTTItem),
}
type SyntaxExtensions = HashMap<@~str, SyntaxExtension>;
type SyntaxEnv = @mut MapChain<Name, Transformer>;
// A temporary hard-coded map of methods for expanding syntax extension
// Name : the domain of SyntaxEnvs
// want to change these to uints....
// note that we use certain strings that are not legal as identifiers
// to indicate, for instance, how blocks are supposed to behave.
type Name = @~str;
// Transformer : the codomain of SyntaxEnvs
// NB: it may seem crazy to lump both of these into one environment;
// what would it mean to bind "foo" to BlockLimit(true)? The idea
// is that this follows the lead of MTWT, and accommodates growth
// toward a more uniform syntax syntax (sorry) where blocks are just
// another kind of transformer.
enum Transformer {
// this identifier maps to a syntax extension or macro
SE(SyntaxExtension),
// should blocks occurring here limit macro scopes?
ScopeMacros(bool)
}
// The base map of methods for expanding syntax extension
// AST nodes into full ASTs
pub fn syntax_expander_table() -> SyntaxExtensions {
pub fn syntax_expander_table() -> SyntaxEnv {
// utility function to simplify creating NormalTT syntax extensions
fn builtin_normal_tt(f: SyntaxExpanderTTFun) -> SyntaxExtension {
NormalTT(SyntaxExpanderTT{expander: f, span: None})
fn builtin_normal_tt(f: SyntaxExpanderTTFun) -> @Transformer {
@SE(NormalTT(SyntaxExpanderTT{expander: f, span: None}))
}
// utility function to simplify creating ItemTT syntax extensions
fn builtin_item_tt(f: SyntaxExpanderTTItemFun) -> SyntaxExtension {
ItemTT(SyntaxExpanderTTItem{expander: f, span: None})
// utility function to simplify creating IdentTT syntax extensions
fn builtin_item_tt(f: SyntaxExpanderTTItemFun) -> @Transformer {
@SE(IdentTT(SyntaxExpanderTTItem{expander: f, span: None}))
}
let syntax_expanders = HashMap();
let mut syntax_expanders = LinearMap::new();
// NB identifier starts with space, and can't conflict with legal idents
syntax_expanders.insert(@~" block",
@ScopeMacros(true));
syntax_expanders.insert(@~"macro_rules",
builtin_item_tt(
ext::tt::macro_rules::add_new_extension));
@@ -97,10 +127,10 @@ pub fn syntax_expander_table() -> SyntaxExtensions {
builtin_normal_tt(ext::fmt::expand_syntax_ext));
syntax_expanders.insert(
@~"auto_encode",
ItemDecorator(ext::auto_encode::expand_auto_encode));
@SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
syntax_expanders.insert(
@~"auto_decode",
ItemDecorator(ext::auto_encode::expand_auto_decode));
@SE(ItemDecorator(ext::auto_encode::expand_auto_decode)));
syntax_expanders.insert(@~"env",
builtin_normal_tt(ext::env::expand_syntax_ext));
syntax_expanders.insert(@~"concat_idents",
@@ -110,25 +140,25 @@ pub fn syntax_expander_table() -> SyntaxExtensions {
builtin_normal_tt(
ext::log_syntax::expand_syntax_ext));
syntax_expanders.insert(@~"deriving_eq",
ItemDecorator(
ext::deriving::expand_deriving_eq));
@SE(ItemDecorator(
ext::deriving::expand_deriving_eq)));
syntax_expanders.insert(@~"deriving_iter_bytes",
ItemDecorator(
ext::deriving::expand_deriving_iter_bytes));
@SE(ItemDecorator(
ext::deriving::expand_deriving_iter_bytes)));
// Quasi-quoting expanders
syntax_expanders.insert(@~"quote_tokens",
builtin_normal_tt(ext::quote::expand_quote_tokens));
syntax_expanders.insert(@~"quote_expr",
builtin_normal_tt(ext::quote::expand_quote_expr));
builtin_normal_tt(ext::quote::expand_quote_expr));
syntax_expanders.insert(@~"quote_ty",
builtin_normal_tt(ext::quote::expand_quote_ty));
builtin_normal_tt(ext::quote::expand_quote_ty));
syntax_expanders.insert(@~"quote_item",
builtin_normal_tt(ext::quote::expand_quote_item));
builtin_normal_tt(ext::quote::expand_quote_item));
syntax_expanders.insert(@~"quote_pat",
builtin_normal_tt(ext::quote::expand_quote_pat));
builtin_normal_tt(ext::quote::expand_quote_pat));
syntax_expanders.insert(@~"quote_stmt",
builtin_normal_tt(ext::quote::expand_quote_stmt));
builtin_normal_tt(ext::quote::expand_quote_stmt));
syntax_expanders.insert(@~"line",
builtin_normal_tt(
@@ -159,7 +189,7 @@ pub fn syntax_expander_table() -> SyntaxExtensions {
syntax_expanders.insert(
@~"trace_macros",
builtin_normal_tt(ext::trace_macros::expand_trace_macros));
return syntax_expanders;
MapChain::new(~syntax_expanders)
}
// 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])
es
}
// 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
// also need to be managed; the &self/... type that Maps
// 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> {
TEC_Base(~LinearMap<K,@V>),
TEC_Cons(~LinearMap<K,@V>,@mut MapChain<K,V>)
}
// get the map from an env frame
impl <K: Eq + Hash + IterBytes ,V: Copy> MapChain<K,V>{
// Constructor. I don't think we need a zero-arg one.
static fn new(+init: ~LinearMap<K,@V>) -> @mut MapChain<K,V> {
@mut TEC_Base(init)
}
// add a new frame to the environment (functionally)
fn push_frame (@mut self) -> @mut MapChain<K,V> {
@mut TEC_Cons(~LinearMap::new() ,self)
}
// 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.
fn get_map(&self) -> &self/LinearMap<K,@V> {
match *self {
TEC_Base (~ref map) => map,
TEC_Cons (~ref map,_) => map
}
}
// traits just don't work anywhere...?
//pub impl Map<Name,SyntaxExtension> for MapChain {
pure fn contains_key (&self, key: &K) -> bool {
match *self {
TEC_Base (ref map) => map.contains_key(key),
TEC_Cons (ref map,ref rest) =>
(map.contains_key(key)
|| rest.contains_key(key))
}
}
// should each_key and each_value operate on shadowed
// names? I think not.
// delaying implementing this....
pure fn each_key (&self, _f: &fn (&K)->bool) {
fail!(~"unimplemented 2013-02-15T10:01");
}
pure fn each_value (&self, _f: &fn (&V) -> bool) {
fail!(~"unimplemented 2013-02-15T10:02");
}
// Returns a copy of the value that the name maps to.
// Goes down the chain 'til it finds one (or bottom out).
fn find (&self, key: &K) -> Option<@V> {
match self.get_map().find (key) {
Some(ref v) => Some(**v),
None => match *self {
TEC_Base (_) => None,
TEC_Cons (_,ref rest) => rest.find(key)
}
}
}
// insert the binding into the top-level map
fn insert (&mut self, +key: K, +ext: @V) -> bool {
// can't abstract over get_map because of flow sensitivity...
match *self {
TEC_Base (~ref mut map) => map.insert(key, ext),
TEC_Cons (~ref mut map,_) => map.insert(key,ext)
}
}
}
#[cfg(test)]
mod test {
use super::*;
use super::MapChain;
use util::testing::check_equal;
#[test] fn testenv () {
let mut a = LinearMap::new();
a.insert (@~"abc",@15);
let m = MapChain::new(~a);
m.insert (@~"def",@16);
// FIXME: #4492 (ICE) check_equal(m.find(&@~"abc"),Some(@15));
// .... check_equal(m.find(&@~"def"),Some(@16));
check_equal(*(m.find(&@~"abc").get()),15);
check_equal(*(m.find(&@~"def").get()),16);
let n = m.push_frame();
// old bindings are still present:
check_equal(*(n.find(&@~"abc").get()),15);
check_equal(*(n.find(&@~"def").get()),16);
n.insert (@~"def",@17);
// n shows the new binding
check_equal(*(n.find(&@~"abc").get()),15);
check_equal(*(n.find(&@~"def").get()),17);
// ... but m still has the old ones
// FIXME: #4492: check_equal(m.find(&@~"abc"),Some(@15));
// FIXME: #4492: check_equal(m.find(&@~"def"),Some(@16));
check_equal(*(m.find(&@~"abc").get()),15);
check_equal(*(m.find(&@~"def").get()),16);
}
}
//
// Local Variables:
// mode: rust