auto merge of #12617 : sfackler/rust/item-modifier, r=alexcrichton
Where ItemDecorator creates new items given a single item, ItemModifier alters the tagged item in place. The expansion rules for this are a bit weird, but I think are the most reasonable option available. When an item is expanded, all ItemModifier attributes are stripped from it and the item is folded through all ItemModifiers. At that point, the process repeats until there are no ItemModifiers in the new item. cc @huonw
This commit is contained in:
commit
b389615560
@ -108,6 +108,21 @@ pub fn grow_set(&mut self, index: uint, initval: &T, val: T) {
|
||||
}
|
||||
*self.get_mut(index) = val;
|
||||
}
|
||||
|
||||
pub fn partitioned(&self, f: |&T| -> bool) -> (Vec<T>, Vec<T>) {
|
||||
let mut lefts = Vec::new();
|
||||
let mut rights = Vec::new();
|
||||
|
||||
for elt in self.iter() {
|
||||
if f(elt) {
|
||||
lefts.push(elt.clone());
|
||||
} else {
|
||||
rights.push(elt.clone());
|
||||
}
|
||||
}
|
||||
|
||||
(lefts, rights)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Clone> Clone for Vec<T> {
|
||||
|
@ -38,6 +38,9 @@ pub struct MacroDef {
|
||||
pub type ItemDecorator =
|
||||
fn(&mut ExtCtxt, Span, @ast::MetaItem, @ast::Item, |@ast::Item|);
|
||||
|
||||
pub type ItemModifier =
|
||||
fn(&mut ExtCtxt, Span, @ast::MetaItem, @ast::Item) -> @ast::Item;
|
||||
|
||||
pub struct BasicMacroExpander {
|
||||
expander: MacroExpanderFn,
|
||||
span: Option<Span>
|
||||
@ -126,21 +129,27 @@ pub fn dummy_expr(sp: codemap::Span) -> MacResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// An enum representing the different kinds of syntax extensions.
|
||||
pub enum SyntaxExtension {
|
||||
// #[deriving] and such
|
||||
/// A syntax extension that is attached to an item and creates new items
|
||||
/// based upon it.
|
||||
///
|
||||
/// `#[deriving(...)]` is an `ItemDecorator`.
|
||||
ItemDecorator(ItemDecorator),
|
||||
|
||||
// Token-tree expanders
|
||||
/// A syntax extension that is attached to an item and modifies it
|
||||
/// in-place.
|
||||
ItemModifier(ItemModifier),
|
||||
|
||||
/// A normal, function-like syntax extension.
|
||||
///
|
||||
/// `bytes!` is a `NormalTT`.
|
||||
NormalTT(~MacroExpander:'static, Option<Span>),
|
||||
|
||||
// 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 is
|
||||
// macro_rules!
|
||||
|
||||
// perhaps macro_rules! will lose its odd special identifier argument,
|
||||
// and this can go away also
|
||||
/// A function-like syntax extension that has an extra ident before
|
||||
/// the block.
|
||||
///
|
||||
/// `macro_rules!` is an `IdentTT`.
|
||||
IdentTT(~IdentMacroExpander:'static, Option<Span>),
|
||||
}
|
||||
|
||||
|
@ -260,7 +260,9 @@ macro_rules! with_exts_frame (
|
||||
// When we enter a module, record it, for the sake of `module!`
|
||||
pub fn expand_item(it: @ast::Item, fld: &mut MacroExpander)
|
||||
-> SmallVector<@ast::Item> {
|
||||
let mut decorator_items: SmallVector<@ast::Item> = SmallVector::zero();
|
||||
let it = expand_item_modifiers(it, fld);
|
||||
|
||||
let mut decorator_items = SmallVector::zero();
|
||||
for attr in it.attrs.rev_iter() {
|
||||
let mname = attr.name();
|
||||
|
||||
@ -307,6 +309,48 @@ pub fn expand_item(it: @ast::Item, fld: &mut MacroExpander)
|
||||
new_items
|
||||
}
|
||||
|
||||
fn expand_item_modifiers(mut it: @ast::Item, fld: &mut MacroExpander)
|
||||
-> @ast::Item {
|
||||
let (modifiers, attrs) = it.attrs.partitioned(|attr| {
|
||||
match fld.extsbox.find(&intern(attr.name().get())) {
|
||||
Some(&ItemModifier(_)) => true,
|
||||
_ => false
|
||||
}
|
||||
});
|
||||
|
||||
it = @ast::Item {
|
||||
attrs: attrs,
|
||||
..(*it).clone()
|
||||
};
|
||||
|
||||
if modifiers.is_empty() {
|
||||
return it;
|
||||
}
|
||||
|
||||
for attr in modifiers.iter() {
|
||||
let mname = attr.name();
|
||||
|
||||
match fld.extsbox.find(&intern(mname.get())) {
|
||||
Some(&ItemModifier(dec_fn)) => {
|
||||
fld.cx.bt_push(ExpnInfo {
|
||||
call_site: attr.span,
|
||||
callee: NameAndSpan {
|
||||
name: mname.get().to_str(),
|
||||
format: MacroAttribute,
|
||||
span: None,
|
||||
}
|
||||
});
|
||||
it = dec_fn(fld.cx, attr.span, attr.node.value, it);
|
||||
fld.cx.bt_pop();
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
// expansion may have added new ItemModifiers
|
||||
expand_item_modifiers(it, fld)
|
||||
}
|
||||
|
||||
// does this attribute list contain "macro_escape" ?
|
||||
pub fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
|
||||
attr::contains_name(attrs, "macro_escape")
|
||||
@ -492,6 +536,7 @@ fn load_extern_macros(krate: &ast::ViewItem, fld: &mut MacroExpander) {
|
||||
NormalTT(ext, _) => NormalTT(ext, Some(krate.span)),
|
||||
IdentTT(ext, _) => IdentTT(ext, Some(krate.span)),
|
||||
ItemDecorator(ext) => ItemDecorator(ext),
|
||||
ItemModifier(ext) => ItemModifier(ext),
|
||||
};
|
||||
fld.extsbox.insert(name, extension);
|
||||
});
|
||||
|
@ -10,11 +10,11 @@
|
||||
|
||||
// force-host
|
||||
|
||||
#[feature(globs, macro_registrar, macro_rules, quote)];
|
||||
#[feature(globs, macro_registrar, macro_rules, quote, managed_boxes)];
|
||||
|
||||
extern crate syntax;
|
||||
|
||||
use syntax::ast::{Name, TokenTree};
|
||||
use syntax::ast::{Name, TokenTree, Item, MetaItem};
|
||||
use syntax::codemap::Span;
|
||||
use syntax::ext::base::*;
|
||||
use syntax::parse::token;
|
||||
@ -32,13 +32,22 @@ pub fn macro_registrar(register: |Name, SyntaxExtension|) {
|
||||
span: None,
|
||||
},
|
||||
None));
|
||||
register(token::intern("into_foo"), ItemModifier(expand_into_foo));
|
||||
}
|
||||
|
||||
pub fn expand_make_a_1(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult {
|
||||
fn expand_make_a_1(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult {
|
||||
if !tts.is_empty() {
|
||||
cx.span_fatal(sp, "make_a_1 takes no arguments");
|
||||
}
|
||||
MRExpr(quote_expr!(cx, 1i))
|
||||
}
|
||||
|
||||
fn expand_into_foo(cx: &mut ExtCtxt, sp: Span, attr: @MetaItem, it: @Item)
|
||||
-> @Item {
|
||||
@Item {
|
||||
attrs: it.attrs.clone(),
|
||||
..(*quote_item!(cx, enum Foo { Bar, Baz }).unwrap()).clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn foo() {}
|
||||
|
@ -19,7 +19,16 @@
|
||||
#[phase(syntax)]
|
||||
extern crate macro_crate_test;
|
||||
|
||||
#[into_foo]
|
||||
#[deriving(Eq, Clone, Show)]
|
||||
fn foo() -> AFakeTypeThatHadBetterGoAway {}
|
||||
|
||||
pub fn main() {
|
||||
assert_eq!(1, make_a_1!());
|
||||
assert_eq!(2, exported_macro!());
|
||||
|
||||
assert_eq!(Bar, Bar);
|
||||
test(None::<Foo>);
|
||||
}
|
||||
|
||||
fn test<T: Eq+Clone>(_: Option<T>) {}
|
||||
|
Loading…
Reference in New Issue
Block a user