diff --git a/src/libstd/vec_ng.rs b/src/libstd/vec_ng.rs index a195ce8deaebc..76fd68a526513 100644 --- a/src/libstd/vec_ng.rs +++ b/src/libstd/vec_ng.rs @@ -108,6 +108,21 @@ impl Vec { } *self.get_mut(index) = val; } + + pub fn partitioned(&self, f: |&T| -> bool) -> (Vec, Vec) { + 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 Clone for Vec { diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 459c0d1d0e3c5..ae8c13a5f9870 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -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 @@ -126,21 +129,27 @@ impl 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 - NormalTT(~MacroExpander:'static, Option), + /// A syntax extension that is attached to an item and modifies it + /// in-place. + ItemModifier(ItemModifier), - // 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! + /// A normal, function-like syntax extension. + /// + /// `bytes!` is a `NormalTT`. + NormalTT(~MacroExpander:'static, Option), - // 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), } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 30b04b7f37782..dc79ceb4daae2 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -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); }); diff --git a/src/test/auxiliary/macro_crate_test.rs b/src/test/auxiliary/macro_crate_test.rs index b8baffcfea5fc..b473ca3264e51 100644 --- a/src/test/auxiliary/macro_crate_test.rs +++ b/src/test/auxiliary/macro_crate_test.rs @@ -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() {} diff --git a/src/test/run-pass-fulldeps/macro-crate.rs b/src/test/run-pass-fulldeps/macro-crate.rs index 69c6e7b0585a4..6f412f01bb991 100644 --- a/src/test/run-pass-fulldeps/macro-crate.rs +++ b/src/test/run-pass-fulldeps/macro-crate.rs @@ -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::); } + +fn test(_: Option) {}