auto merge of #11228 : sfackler/rust/syntaxenv, r=pcwalton
I'd really like to be able to do something like ```rust 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.
This commit is contained in:
commit
b9c39c6a27
|
@ -155,57 +155,48 @@ pub enum SyntaxExtension {
|
||||||
// The SyntaxEnv is the environment that's threaded through the expansion
|
// The SyntaxEnv is the environment that's threaded through the expansion
|
||||||
// of macros. It contains bindings for macros, and also a special binding
|
// of macros. It contains bindings for macros, and also a special binding
|
||||||
// for " block" (not a legal identifier) that maps to a BlockInfo
|
// for " block" (not a legal identifier) that maps to a BlockInfo
|
||||||
pub type SyntaxEnv = @mut MapChain<Name, Transformer>;
|
pub type SyntaxEnv = MapChain<Name, SyntaxExtension>;
|
||||||
|
|
||||||
// Transformer : the codomain of SyntaxEnvs
|
|
||||||
|
|
||||||
pub enum Transformer {
|
|
||||||
// this identifier maps to a syntax extension or macro
|
|
||||||
SE(SyntaxExtension),
|
|
||||||
// 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 {
|
pub struct BlockInfo {
|
||||||
// should macros escape from this scope?
|
// should macros escape from this scope?
|
||||||
macros_escape : bool,
|
macros_escape : bool,
|
||||||
// what are the pending renames?
|
// what are the pending renames?
|
||||||
pending_renames : @mut RenameList
|
pending_renames : RenameList
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockInfo {
|
||||||
|
pub fn new() -> BlockInfo {
|
||||||
|
BlockInfo {
|
||||||
|
macros_escape: false,
|
||||||
|
pending_renames: ~[]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// a list of ident->name renamings
|
// a list of ident->name renamings
|
||||||
type RenameList = ~[(ast::Ident,Name)];
|
pub type RenameList = ~[(ast::Ident,Name)];
|
||||||
|
|
||||||
// The base map of methods for expanding syntax extension
|
// The base map of methods for expanding syntax extension
|
||||||
// AST nodes into full ASTs
|
// AST nodes into full ASTs
|
||||||
pub fn syntax_expander_table() -> SyntaxEnv {
|
pub fn syntax_expander_table() -> SyntaxEnv {
|
||||||
// utility function to simplify creating NormalTT syntax extensions
|
// utility function to simplify creating NormalTT syntax extensions
|
||||||
fn builtin_normal_tt_no_ctxt(f: SyntaxExpanderTTFunNoCtxt)
|
fn builtin_normal_tt_no_ctxt(f: SyntaxExpanderTTFunNoCtxt)
|
||||||
-> @Transformer {
|
-> SyntaxExtension {
|
||||||
@SE(NormalTT(@SyntaxExpanderTT{
|
NormalTT(@SyntaxExpanderTT{
|
||||||
expander: SyntaxExpanderTTExpanderWithoutContext(f),
|
expander: SyntaxExpanderTTExpanderWithoutContext(f),
|
||||||
span: None,
|
span: None,
|
||||||
} as @SyntaxExpanderTTTrait,
|
} as @SyntaxExpanderTTTrait,
|
||||||
None))
|
None)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut syntax_expanders = HashMap::new();
|
let mut syntax_expanders = MapChain::new();
|
||||||
// NB identifier starts with space, and can't conflict with legal idents
|
|
||||||
syntax_expanders.insert(intern(&" block"),
|
|
||||||
@BlockInfo(BlockInfo{
|
|
||||||
macros_escape : false,
|
|
||||||
pending_renames : @mut ~[]
|
|
||||||
}));
|
|
||||||
syntax_expanders.insert(intern(&"macro_rules"),
|
syntax_expanders.insert(intern(&"macro_rules"),
|
||||||
@SE(IdentTT(@SyntaxExpanderTTItem {
|
IdentTT(@SyntaxExpanderTTItem {
|
||||||
expander: SyntaxExpanderTTItemExpanderWithContext(
|
expander: SyntaxExpanderTTItemExpanderWithContext(
|
||||||
ext::tt::macro_rules::add_new_extension),
|
ext::tt::macro_rules::add_new_extension),
|
||||||
span: None,
|
span: None,
|
||||||
} as @SyntaxExpanderTTItemTrait,
|
} as @SyntaxExpanderTTItemTrait,
|
||||||
None)));
|
None));
|
||||||
syntax_expanders.insert(intern(&"fmt"),
|
syntax_expanders.insert(intern(&"fmt"),
|
||||||
builtin_normal_tt_no_ctxt(
|
builtin_normal_tt_no_ctxt(
|
||||||
ext::fmt::expand_syntax_ext));
|
ext::fmt::expand_syntax_ext));
|
||||||
|
@ -231,8 +222,7 @@ pub fn syntax_expander_table() -> SyntaxEnv {
|
||||||
builtin_normal_tt_no_ctxt(
|
builtin_normal_tt_no_ctxt(
|
||||||
ext::log_syntax::expand_syntax_ext));
|
ext::log_syntax::expand_syntax_ext));
|
||||||
syntax_expanders.insert(intern(&"deriving"),
|
syntax_expanders.insert(intern(&"deriving"),
|
||||||
@SE(ItemDecorator(
|
ItemDecorator(ext::deriving::expand_meta_deriving));
|
||||||
ext::deriving::expand_meta_deriving)));
|
|
||||||
|
|
||||||
// Quasi-quoting expanders
|
// Quasi-quoting expanders
|
||||||
syntax_expanders.insert(intern(&"quote_tokens"),
|
syntax_expanders.insert(intern(&"quote_tokens"),
|
||||||
|
@ -287,7 +277,7 @@ pub fn syntax_expander_table() -> SyntaxEnv {
|
||||||
syntax_expanders.insert(intern(&"trace_macros"),
|
syntax_expanders.insert(intern(&"trace_macros"),
|
||||||
builtin_normal_tt_no_ctxt(
|
builtin_normal_tt_no_ctxt(
|
||||||
ext::trace_macros::expand_trace_macros));
|
ext::trace_macros::expand_trace_macros));
|
||||||
MapChain::new(~syntax_expanders)
|
syntax_expanders
|
||||||
}
|
}
|
||||||
|
|
||||||
// One of these is made during expansion and incrementally updated as we go;
|
// One of these is made during expansion and incrementally updated as we go;
|
||||||
|
@ -298,11 +288,6 @@ pub struct ExtCtxt {
|
||||||
cfg: ast::CrateConfig,
|
cfg: ast::CrateConfig,
|
||||||
backtrace: Option<@ExpnInfo>,
|
backtrace: Option<@ExpnInfo>,
|
||||||
|
|
||||||
// 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
|
|
||||||
mod_path: ~[ast::Ident],
|
mod_path: ~[ast::Ident],
|
||||||
trace_mac: bool
|
trace_mac: bool
|
||||||
}
|
}
|
||||||
|
@ -324,7 +309,7 @@ impl ExtCtxt {
|
||||||
match e.node {
|
match e.node {
|
||||||
ast::ExprMac(..) => {
|
ast::ExprMac(..) => {
|
||||||
let mut expander = expand::MacroExpander {
|
let mut expander = expand::MacroExpander {
|
||||||
extsbox: @mut syntax_expander_table(),
|
extsbox: syntax_expander_table(),
|
||||||
cx: self,
|
cx: self,
|
||||||
};
|
};
|
||||||
e = expand::expand_expr(e, &mut expander);
|
e = expand::expand_expr(e, &mut expander);
|
||||||
|
@ -459,11 +444,7 @@ pub fn get_exprs_from_tts(cx: &ExtCtxt,
|
||||||
// we want to implement the notion of a transformation
|
// we want to implement the notion of a transformation
|
||||||
// environment.
|
// environment.
|
||||||
|
|
||||||
// This environment maps Names to Transformers.
|
// This environment maps Names to SyntaxExtensions.
|
||||||
// Initially, this includes macro definitions and
|
|
||||||
// block directives.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Actually, the following implementation is parameterized
|
// Actually, the following implementation is parameterized
|
||||||
// by both key and value types.
|
// by both key and value types.
|
||||||
|
@ -478,169 +459,98 @@ pub fn get_exprs_from_tts(cx: &ExtCtxt,
|
||||||
// able to refer to a macro that was added to an enclosing
|
// able to refer to a macro that was added to an enclosing
|
||||||
// scope lexically later than the deeper scope.
|
// scope lexically later than the deeper scope.
|
||||||
|
|
||||||
// Note on choice of representation: I've been pushed to
|
// Only generic to make it easy to test
|
||||||
// use a top-level managed pointer by some difficulties
|
struct MapChainFrame<K, V> {
|
||||||
// with pushing and popping functionally, and the ownership
|
info: BlockInfo,
|
||||||
// issues. As a result, the values returned by the table
|
map: HashMap<K, V>,
|
||||||
// also need to be managed; the &'a ... 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> {
|
|
||||||
BaseMapChain(~HashMap<K,@V>),
|
|
||||||
ConsMapChain(~HashMap<K,@V>,@mut MapChain<K,V>)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only generic to make it easy to test
|
||||||
// get the map from an env frame
|
pub struct MapChain<K, V> {
|
||||||
impl <K: Eq + Hash + IterBytes + 'static, V: 'static> MapChain<K,V>{
|
priv chain: ~[MapChainFrame<K, V>],
|
||||||
// Constructor. I don't think we need a zero-arg one.
|
|
||||||
pub fn new(init: ~HashMap<K,@V>) -> @mut MapChain<K,V> {
|
|
||||||
@mut BaseMapChain(init)
|
|
||||||
}
|
|
||||||
|
|
||||||
// add a new frame to the environment (functionally)
|
|
||||||
pub fn push_frame (@mut self) -> @mut MapChain<K,V> {
|
|
||||||
@mut ConsMapChain(~HashMap::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.
|
|
||||||
pub fn get_map<'a>(&'a self) -> &'a HashMap<K,@V> {
|
|
||||||
match *self {
|
|
||||||
BaseMapChain (~ref map) => map,
|
|
||||||
ConsMapChain (~ref map,_) => map
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// traits just don't work anywhere...?
|
|
||||||
//impl Map<Name,SyntaxExtension> for MapChain {
|
|
||||||
|
|
||||||
pub fn contains_key (&self, key: &K) -> bool {
|
|
||||||
match *self {
|
|
||||||
BaseMapChain (ref map) => map.contains_key(key),
|
|
||||||
ConsMapChain (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....
|
|
||||||
pub fn each_key (&self, _f: |&K| -> bool) {
|
|
||||||
fail!("unimplemented 2013-02-15T10:01");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn each_value (&self, _f: |&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).
|
|
||||||
pub fn find (&self, key: &K) -> Option<@V> {
|
|
||||||
match self.get_map().find (key) {
|
|
||||||
Some(ref v) => Some(**v),
|
|
||||||
None => match *self {
|
|
||||||
BaseMapChain (_) => None,
|
|
||||||
ConsMapChain (_,ref rest) => rest.find(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_in_topmost_frame(&self, key: &K) -> Option<@V> {
|
|
||||||
let map = match *self {
|
|
||||||
BaseMapChain(ref map) => map,
|
|
||||||
ConsMapChain(ref map,_) => map
|
|
||||||
};
|
|
||||||
// strip one layer of indirection off the pointer.
|
|
||||||
map.find(key).map(|r| {*r})
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert the binding into the top-level map
|
|
||||||
pub fn insert (&mut self, key: K, ext: @V) -> bool {
|
|
||||||
// can't abstract over get_map because of flow sensitivity...
|
|
||||||
match *self {
|
|
||||||
BaseMapChain (~ref mut map) => map.insert(key, ext),
|
|
||||||
ConsMapChain (~ref mut map,_) => map.insert(key,ext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 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".
|
|
||||||
pub fn insert_into_frame(&mut self,
|
|
||||||
key: K,
|
|
||||||
ext: @V,
|
|
||||||
n: K,
|
|
||||||
pred: |&@V| -> bool) {
|
|
||||||
match *self {
|
|
||||||
BaseMapChain (~ref mut map) => {
|
|
||||||
if satisfies_pred(map,&n,pred) {
|
|
||||||
map.insert(key,ext);
|
|
||||||
} else {
|
|
||||||
fail!("expected map chain containing satisfying frame")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ConsMapChain (~ref mut map, rest) => {
|
|
||||||
if satisfies_pred(map,&n,|v|pred(v)) {
|
|
||||||
map.insert(key,ext);
|
|
||||||
} else {
|
|
||||||
rest.insert_into_frame(key,ext,n,pred)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if the binding for 'n' satisfies 'pred' in 'map'
|
impl<K: Hash+Eq, V> MapChain<K, V> {
|
||||||
fn satisfies_pred<K:Eq + Hash + IterBytes,
|
pub fn new() -> MapChain<K, V> {
|
||||||
V>(
|
let mut map = MapChain { chain: ~[] };
|
||||||
map: &mut HashMap<K,V>,
|
map.push_frame();
|
||||||
n: &K,
|
map
|
||||||
pred: |&V| -> bool)
|
}
|
||||||
-> bool {
|
|
||||||
match map.find(n) {
|
pub fn push_frame(&mut self) {
|
||||||
Some(ref v) => (pred(*v)),
|
self.chain.push(MapChainFrame {
|
||||||
None => false
|
info: BlockInfo::new(),
|
||||||
|
map: HashMap::new(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_frame(&mut self) {
|
||||||
|
assert!(self.chain.len() > 1, "too many pops on MapChain!");
|
||||||
|
self.chain.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_escape_frame<'a>(&'a mut self) -> &'a mut MapChainFrame<K, V> {
|
||||||
|
for (i, frame) in self.chain.mut_iter().enumerate().invert() {
|
||||||
|
if !frame.info.macros_escape || i == 0 {
|
||||||
|
return frame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find<'a>(&'a self, k: &K) -> Option<&'a V> {
|
||||||
|
for frame in self.chain.iter().invert() {
|
||||||
|
match frame.map.find(k) {
|
||||||
|
Some(v) => return Some(v),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, k: K, v: V) {
|
||||||
|
self.find_escape_frame().map.insert(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn info<'a>(&'a mut self) -> &'a mut BlockInfo {
|
||||||
|
&mut self.chain[self.chain.len()-1].info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::MapChain;
|
use super::MapChain;
|
||||||
use std::hashmap::HashMap;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn testenv() {
|
fn testenv() {
|
||||||
let mut a = HashMap::new();
|
let mut m = MapChain::new();
|
||||||
a.insert (@"abc",@15);
|
let (a,b,c,d) = ("a", "b", "c", "d");
|
||||||
let m = MapChain::new(~a);
|
m.insert(1, a);
|
||||||
m.insert (@"def",@16);
|
assert_eq!(Some(&a), m.find(&1));
|
||||||
assert_eq!(m.find(&@"abc"),Some(@15));
|
|
||||||
assert_eq!(m.find(&@"def"),Some(@16));
|
m.push_frame();
|
||||||
assert_eq!(*(m.find(&@"abc").unwrap()),15);
|
m.info().macros_escape = true;
|
||||||
assert_eq!(*(m.find(&@"def").unwrap()),16);
|
m.insert(2, b);
|
||||||
let n = m.push_frame();
|
assert_eq!(Some(&a), m.find(&1));
|
||||||
// old bindings are still present:
|
assert_eq!(Some(&b), m.find(&2));
|
||||||
assert_eq!(*(n.find(&@"abc").unwrap()),15);
|
m.pop_frame();
|
||||||
assert_eq!(*(n.find(&@"def").unwrap()),16);
|
|
||||||
n.insert (@"def",@17);
|
assert_eq!(Some(&a), m.find(&1));
|
||||||
// n shows the new binding
|
assert_eq!(Some(&b), m.find(&2));
|
||||||
assert_eq!(*(n.find(&@"abc").unwrap()),15);
|
|
||||||
assert_eq!(*(n.find(&@"def").unwrap()),17);
|
m.push_frame();
|
||||||
// ... but m still has the old ones
|
m.push_frame();
|
||||||
assert_eq!(m.find(&@"abc"),Some(@15));
|
m.info().macros_escape = true;
|
||||||
assert_eq!(m.find(&@"def"),Some(@16));
|
m.insert(3, c);
|
||||||
assert_eq!(*(m.find(&@"abc").unwrap()),15);
|
assert_eq!(Some(&c), m.find(&3));
|
||||||
assert_eq!(*(m.find(&@"def").unwrap()),16);
|
m.pop_frame();
|
||||||
|
assert_eq!(Some(&c), m.find(&3));
|
||||||
|
m.pop_frame();
|
||||||
|
assert_eq!(None, m.find(&3));
|
||||||
|
|
||||||
|
m.push_frame();
|
||||||
|
m.insert(4, d);
|
||||||
|
m.pop_frame();
|
||||||
|
assert_eq!(None, m.find(&4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,13 +53,13 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
|
||||||
let extname = &pth.segments[0].identifier;
|
let extname = &pth.segments[0].identifier;
|
||||||
let extnamestr = ident_to_str(extname);
|
let extnamestr = ident_to_str(extname);
|
||||||
// leaving explicit deref here to highlight unbox op:
|
// leaving explicit deref here to highlight unbox op:
|
||||||
match (*fld.extsbox).find(&extname.name) {
|
match fld.extsbox.find(&extname.name) {
|
||||||
None => {
|
None => {
|
||||||
fld.cx.span_fatal(
|
fld.cx.span_fatal(
|
||||||
pth.span,
|
pth.span,
|
||||||
format!("macro undefined: '{}'", extnamestr))
|
format!("macro undefined: '{}'", extnamestr))
|
||||||
}
|
}
|
||||||
Some(@SE(NormalTT(expandfun, exp_span))) => {
|
Some(&NormalTT(expandfun, exp_span)) => {
|
||||||
fld.cx.bt_push(ExpnInfo {
|
fld.cx.bt_push(ExpnInfo {
|
||||||
call_site: e.span,
|
call_site: e.span,
|
||||||
callee: NameAndSpan {
|
callee: NameAndSpan {
|
||||||
|
@ -221,8 +221,8 @@ pub fn expand_mod_items(module_: &ast::_mod, fld: &mut MacroExpander) -> ast::_m
|
||||||
item.attrs.rev_iter().fold(~[*item], |items, attr| {
|
item.attrs.rev_iter().fold(~[*item], |items, attr| {
|
||||||
let mname = attr.name();
|
let mname = attr.name();
|
||||||
|
|
||||||
match (*fld.extsbox).find(&intern(mname)) {
|
match fld.extsbox.find(&intern(mname)) {
|
||||||
Some(@SE(ItemDecorator(dec_fn))) => {
|
Some(&ItemDecorator(dec_fn)) => {
|
||||||
fld.cx.bt_push(ExpnInfo {
|
fld.cx.bt_push(ExpnInfo {
|
||||||
call_site: attr.span,
|
call_site: attr.span,
|
||||||
callee: NameAndSpan {
|
callee: NameAndSpan {
|
||||||
|
@ -249,19 +249,14 @@ pub fn expand_mod_items(module_: &ast::_mod, fld: &mut MacroExpander) -> ast::_m
|
||||||
// eval $e with a new exts frame:
|
// eval $e with a new exts frame:
|
||||||
macro_rules! with_exts_frame (
|
macro_rules! with_exts_frame (
|
||||||
($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
|
($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
|
||||||
({let extsbox = $extsboxexpr;
|
({$extsboxexpr.push_frame();
|
||||||
let oldexts = *extsbox;
|
$extsboxexpr.info().macros_escape = $macros_escape;
|
||||||
*extsbox = oldexts.push_frame();
|
|
||||||
extsbox.insert(intern(special_block_name),
|
|
||||||
@BlockInfo(BlockInfo{macros_escape:$macros_escape,pending_renames:@mut ~[]}));
|
|
||||||
let result = $e;
|
let result = $e;
|
||||||
*extsbox = oldexts;
|
$extsboxexpr.pop_frame();
|
||||||
result
|
result
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
static special_block_name : &'static str = " block";
|
|
||||||
|
|
||||||
// When we enter a module, record it, for the sake of `module!`
|
// When we enter a module, record it, for the sake of `module!`
|
||||||
pub fn expand_item(it: @ast::item, fld: &mut MacroExpander)
|
pub fn expand_item(it: @ast::item, fld: &mut MacroExpander)
|
||||||
-> SmallVector<@ast::item> {
|
-> SmallVector<@ast::item> {
|
||||||
|
@ -302,11 +297,11 @@ pub fn expand_item_mac(it: @ast::item, fld: &mut MacroExpander)
|
||||||
let extname = &pth.segments[0].identifier;
|
let extname = &pth.segments[0].identifier;
|
||||||
let extnamestr = ident_to_str(extname);
|
let extnamestr = ident_to_str(extname);
|
||||||
let fm = fresh_mark();
|
let fm = fresh_mark();
|
||||||
let expanded = match (*fld.extsbox).find(&extname.name) {
|
let expanded = match fld.extsbox.find(&extname.name) {
|
||||||
None => fld.cx.span_fatal(pth.span,
|
None => fld.cx.span_fatal(pth.span,
|
||||||
format!("macro undefined: '{}!'", extnamestr)),
|
format!("macro undefined: '{}!'", extnamestr)),
|
||||||
|
|
||||||
Some(@SE(NormalTT(expander, span))) => {
|
Some(&NormalTT(expander, span)) => {
|
||||||
if it.ident.name != parse::token::special_idents::invalid.name {
|
if it.ident.name != parse::token::special_idents::invalid.name {
|
||||||
fld.cx.span_fatal(pth.span,
|
fld.cx.span_fatal(pth.span,
|
||||||
format!("macro {}! expects no ident argument, \
|
format!("macro {}! expects no ident argument, \
|
||||||
|
@ -326,7 +321,7 @@ pub fn expand_item_mac(it: @ast::item, fld: &mut MacroExpander)
|
||||||
let marked_ctxt = new_mark(fm,ctxt);
|
let marked_ctxt = new_mark(fm,ctxt);
|
||||||
expander.expand(fld.cx, it.span, marked_before, marked_ctxt)
|
expander.expand(fld.cx, it.span, marked_before, marked_ctxt)
|
||||||
}
|
}
|
||||||
Some(@SE(IdentTT(expander, span))) => {
|
Some(&IdentTT(expander, span)) => {
|
||||||
if it.ident.name == parse::token::special_idents::invalid.name {
|
if it.ident.name == parse::token::special_idents::invalid.name {
|
||||||
fld.cx.span_fatal(pth.span,
|
fld.cx.span_fatal(pth.span,
|
||||||
format!("macro {}! expects an ident argument",
|
format!("macro {}! expects an ident argument",
|
||||||
|
@ -369,7 +364,7 @@ pub fn expand_item_mac(it: @ast::item, fld: &mut MacroExpander)
|
||||||
MRDef(ref mdef) => {
|
MRDef(ref mdef) => {
|
||||||
// yikes... no idea how to apply the mark to this. I'm afraid
|
// yikes... no idea how to apply the mark to this. I'm afraid
|
||||||
// we're going to have to wait-and-see on this one.
|
// we're going to have to wait-and-see on this one.
|
||||||
insert_macro(*fld.extsbox,intern(mdef.name), @SE((*mdef).ext));
|
fld.extsbox.insert(intern(mdef.name), (*mdef).ext);
|
||||||
SmallVector::zero()
|
SmallVector::zero()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -377,23 +372,6 @@ pub fn expand_item_mac(it: @ast::item, fld: &mut MacroExpander)
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// insert a macro into the innermost frame that doesn't have the
|
|
||||||
// macro_escape tag.
|
|
||||||
fn insert_macro(exts: SyntaxEnv, name: ast::Name, transformer: @Transformer) {
|
|
||||||
let is_non_escaping_block =
|
|
||||||
|t : &@Transformer| -> bool{
|
|
||||||
match t {
|
|
||||||
&@BlockInfo(BlockInfo {macros_escape:false,..}) => true,
|
|
||||||
&@BlockInfo(BlockInfo {..}) => false,
|
|
||||||
_ => fail!("special identifier {:?} was bound to a non-BlockInfo",
|
|
||||||
special_block_name)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
exts.insert_into_frame(name,transformer,intern(special_block_name),
|
|
||||||
is_non_escaping_block)
|
|
||||||
}
|
|
||||||
|
|
||||||
// expand a stmt
|
// expand a stmt
|
||||||
pub fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<@Stmt> {
|
pub fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<@Stmt> {
|
||||||
// why the copying here and not in expand_expr?
|
// why the copying here and not in expand_expr?
|
||||||
|
@ -414,12 +392,12 @@ pub fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<@Stmt> {
|
||||||
}
|
}
|
||||||
let extname = &pth.segments[0].identifier;
|
let extname = &pth.segments[0].identifier;
|
||||||
let extnamestr = ident_to_str(extname);
|
let extnamestr = ident_to_str(extname);
|
||||||
let fully_expanded: SmallVector<@Stmt> = match (*fld.extsbox).find(&extname.name) {
|
let fully_expanded: SmallVector<@Stmt> = match fld.extsbox.find(&extname.name) {
|
||||||
None => {
|
None => {
|
||||||
fld.cx.span_fatal(pth.span, format!("macro undefined: '{}'", extnamestr))
|
fld.cx.span_fatal(pth.span, format!("macro undefined: '{}'", extnamestr))
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(@SE(NormalTT(expandfun, exp_span))) => {
|
Some(&NormalTT(expandfun, exp_span)) => {
|
||||||
fld.cx.bt_push(ExpnInfo {
|
fld.cx.bt_push(ExpnInfo {
|
||||||
call_site: s.span,
|
call_site: s.span,
|
||||||
callee: NameAndSpan {
|
callee: NameAndSpan {
|
||||||
|
@ -497,8 +475,6 @@ fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander)
|
||||||
span: stmt_span
|
span: stmt_span
|
||||||
},
|
},
|
||||||
node_id) => {
|
node_id) => {
|
||||||
let block_info = get_block_info(*fld.extsbox);
|
|
||||||
let pending_renames = block_info.pending_renames;
|
|
||||||
|
|
||||||
// take it apart:
|
// take it apart:
|
||||||
let @Local {
|
let @Local {
|
||||||
|
@ -522,12 +498,16 @@ fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander)
|
||||||
let new_name = fresh_name(ident);
|
let new_name = fresh_name(ident);
|
||||||
new_pending_renames.push((*ident,new_name));
|
new_pending_renames.push((*ident,new_name));
|
||||||
}
|
}
|
||||||
let mut rename_fld = renames_to_fold(new_pending_renames);
|
let rewritten_pat = {
|
||||||
// rewrite the pattern using the new names (the old ones
|
let mut rename_fld = renames_to_fold(new_pending_renames);
|
||||||
// have already been applied):
|
// rewrite the pattern using the new names (the old ones
|
||||||
let rewritten_pat = rename_fld.fold_pat(expanded_pat);
|
// have already been applied):
|
||||||
|
rename_fld.fold_pat(expanded_pat)
|
||||||
|
};
|
||||||
// add them to the existing pending renames:
|
// add them to the existing pending renames:
|
||||||
for pr in new_pending_renames.iter() {pending_renames.push(*pr)}
|
for pr in new_pending_renames.iter() {
|
||||||
|
fld.extsbox.info().pending_renames.push(*pr)
|
||||||
|
}
|
||||||
// also, don't forget to expand the init:
|
// also, don't forget to expand the init:
|
||||||
let new_init_opt = init.map(|e| fld.fold_expr(e));
|
let new_init_opt = init.map(|e| fld.fold_expr(e));
|
||||||
let rewritten_local =
|
let rewritten_local =
|
||||||
|
@ -618,16 +598,23 @@ pub fn expand_block(blk: &Block, fld: &mut MacroExpander) -> P<Block> {
|
||||||
|
|
||||||
// expand the elements of a block.
|
// expand the elements of a block.
|
||||||
pub fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P<Block> {
|
pub fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P<Block> {
|
||||||
let block_info = get_block_info(*fld.extsbox);
|
|
||||||
let pending_renames = block_info.pending_renames;
|
|
||||||
let mut rename_fld = renames_to_fold(pending_renames);
|
|
||||||
let new_view_items = b.view_items.map(|x| fld.fold_view_item(x));
|
let new_view_items = b.view_items.map(|x| fld.fold_view_item(x));
|
||||||
let new_stmts = b.stmts.iter()
|
let new_stmts = b.stmts.iter()
|
||||||
.map(|x| rename_fld.fold_stmt(*x)
|
.map(|x| {
|
||||||
.expect_one("rename_fold didn't return one value"))
|
let pending_renames = &mut fld.extsbox.info().pending_renames;
|
||||||
|
let mut rename_fld = renames_to_fold(pending_renames);
|
||||||
|
rename_fld.fold_stmt(*x).expect_one("rename_fold didn't return one value")
|
||||||
|
})
|
||||||
.flat_map(|x| fld.fold_stmt(x).move_iter())
|
.flat_map(|x| fld.fold_stmt(x).move_iter())
|
||||||
.collect();
|
.collect();
|
||||||
let new_expr = b.expr.map(|x| fld.fold_expr(rename_fld.fold_expr(x)));
|
let new_expr = b.expr.map(|x| {
|
||||||
|
let expr = {
|
||||||
|
let pending_renames = &mut fld.extsbox.info().pending_renames;
|
||||||
|
let mut rename_fld = renames_to_fold(pending_renames);
|
||||||
|
rename_fld.fold_expr(x)
|
||||||
|
};
|
||||||
|
fld.fold_expr(expr)
|
||||||
|
});
|
||||||
P(Block {
|
P(Block {
|
||||||
view_items: new_view_items,
|
view_items: new_view_items,
|
||||||
stmts: new_stmts,
|
stmts: new_stmts,
|
||||||
|
@ -638,20 +625,11 @@ pub fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P<Block> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the (innermost) BlockInfo from an exts stack
|
struct IdentRenamer<'a> {
|
||||||
fn get_block_info(exts : SyntaxEnv) -> BlockInfo {
|
renames: &'a mut RenameList,
|
||||||
match exts.find_in_topmost_frame(&intern(special_block_name)) {
|
|
||||||
Some(@BlockInfo(bi)) => bi,
|
|
||||||
_ => fail!("special identifier {:?} was bound to a non-BlockInfo",
|
|
||||||
@" block")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct IdentRenamer {
|
impl<'a> ast_fold for IdentRenamer<'a> {
|
||||||
renames: @mut ~[(ast::Ident,ast::Name)],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ast_fold for IdentRenamer {
|
|
||||||
fn fold_ident(&mut self, id: ast::Ident) -> ast::Ident {
|
fn fold_ident(&mut self, id: ast::Ident) -> ast::Ident {
|
||||||
let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
|
let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
|
||||||
new_rename(from, to, ctxt)
|
new_rename(from, to, ctxt)
|
||||||
|
@ -665,7 +643,7 @@ impl ast_fold for IdentRenamer {
|
||||||
|
|
||||||
// given a mutable list of renames, return a tree-folder that applies those
|
// given a mutable list of renames, return a tree-folder that applies those
|
||||||
// renames.
|
// renames.
|
||||||
pub fn renames_to_fold(renames: @mut ~[(ast::Ident,ast::Name)]) -> IdentRenamer {
|
pub fn renames_to_fold<'a>(renames: &'a mut RenameList) -> IdentRenamer<'a> {
|
||||||
IdentRenamer {
|
IdentRenamer {
|
||||||
renames: renames,
|
renames: renames,
|
||||||
}
|
}
|
||||||
|
@ -931,7 +909,7 @@ pub fn inject_std_macros(parse_sess: @mut parse::ParseSess,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MacroExpander<'a> {
|
pub struct MacroExpander<'a> {
|
||||||
extsbox: @mut SyntaxEnv,
|
extsbox: SyntaxEnv,
|
||||||
cx: &'a mut ExtCtxt,
|
cx: &'a mut ExtCtxt,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -964,15 +942,9 @@ impl<'a> ast_fold for MacroExpander<'a> {
|
||||||
pub fn expand_crate(parse_sess: @mut parse::ParseSess,
|
pub fn expand_crate(parse_sess: @mut parse::ParseSess,
|
||||||
cfg: ast::CrateConfig,
|
cfg: ast::CrateConfig,
|
||||||
c: Crate) -> Crate {
|
c: Crate) -> Crate {
|
||||||
// adding *another* layer of indirection here so that the block
|
|
||||||
// visitor can swap out one exts table for another for the duration
|
|
||||||
// of the block. The cleaner alternative would be to thread the
|
|
||||||
// exts table through the fold, but that would require updating
|
|
||||||
// every method/element of AstFoldFns in fold.rs.
|
|
||||||
let extsbox = syntax_expander_table();
|
|
||||||
let mut cx = ExtCtxt::new(parse_sess, cfg.clone());
|
let mut cx = ExtCtxt::new(parse_sess, cfg.clone());
|
||||||
let mut expander = MacroExpander {
|
let mut expander = MacroExpander {
|
||||||
extsbox: @mut extsbox,
|
extsbox: syntax_expander_table(),
|
||||||
cx: &mut cx,
|
cx: &mut cx,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue