]> git.lizzy.rs Git - rust.git/commitdiff
Add an ItemModifier syntax extension type
authorSteven Fackler <sfackler@gmail.com>
Fri, 28 Feb 2014 07:49:25 +0000 (23:49 -0800)
committerSteven Fackler <sfackler@gmail.com>
Tue, 11 Mar 2014 07:28:25 +0000 (00:28 -0700)
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.

src/libstd/vec_ng.rs
src/libsyntax/ext/base.rs
src/libsyntax/ext/expand.rs
src/test/auxiliary/macro_crate_test.rs
src/test/run-pass-fulldeps/macro-crate.rs

index a195ce8deaebc977addfd1f1a0863991c575320c..76fd68a52651356158fecb0bcd7120dadbe4f71c 100644 (file)
@@ -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> {
index 459c0d1d0e3c5135a045cde6fadfc8619acc0fd6..ae8c13a5f98705a3f27354b81c6f0b9f484dea70 100644 (file)
@@ -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
-    NormalTT(~MacroExpander:'static, Option<Span>),
+    /// 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<Span>),
 
-    // 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>),
 }
 
index 30b04b7f37782e75e67b6a2f7986fef29fe4693a..dc79ceb4daae230a089394cd1264eb8e3d26ea85 100644 (file)
@@ -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);
         });
index b8baffcfea5fcbc4c3feb562a7f91c045b3e9870..b473ca3264e51b0d9f7dd3f21160a8e36e32a03b 100644 (file)
 
 // 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() {}
index 69c6e7b0585a432c55677ac35ca337ca0b1861ca..6f412f01bb991876fa603e3ef8811924c31a8529 100644 (file)
 #[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>) {}