Skip to content

Commit eb4cbd5

Browse files
committed
Add an ItemModifier syntax extension type
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.
1 parent 294d3dd commit eb4cbd5

File tree

5 files changed

+101
-14
lines changed

5 files changed

+101
-14
lines changed

src/libstd/vec_ng.rs

+15
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,21 @@ impl<T: Clone> Vec<T> {
108108
}
109109
*self.get_mut(index) = val;
110110
}
111+
112+
pub fn partitioned(&self, f: |&T| -> bool) -> (Vec<T>, Vec<T>) {
113+
let mut lefts = Vec::new();
114+
let mut rights = Vec::new();
115+
116+
for elt in self.iter() {
117+
if f(elt) {
118+
lefts.push(elt.clone());
119+
} else {
120+
rights.push(elt.clone());
121+
}
122+
}
123+
124+
(lefts, rights)
125+
}
111126
}
112127

113128
impl<T:Clone> Clone for Vec<T> {

src/libsyntax/ext/base.rs

+19-10
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ pub struct MacroDef {
3838
pub type ItemDecorator =
3939
fn(&mut ExtCtxt, Span, @ast::MetaItem, @ast::Item, |@ast::Item|);
4040

41+
pub type ItemModifier =
42+
fn(&mut ExtCtxt, Span, @ast::MetaItem, @ast::Item) -> @ast::Item;
43+
4144
pub struct BasicMacroExpander {
4245
expander: MacroExpanderFn,
4346
span: Option<Span>
@@ -126,21 +129,27 @@ impl MacResult {
126129
}
127130
}
128131

132+
/// An enum representing the different kinds of syntax extensions.
129133
pub enum SyntaxExtension {
130-
// #[deriving] and such
134+
/// A syntax extension that is attached to an item and creates new items
135+
/// based upon it.
136+
///
137+
/// `#[deriving(...)]` is an `ItemDecorator`.
131138
ItemDecorator(ItemDecorator),
132139

133-
// Token-tree expanders
134-
NormalTT(~MacroExpander:'static, Option<Span>),
140+
/// A syntax extension that is attached to an item and modifies it
141+
/// in-place.
142+
ItemModifier(ItemModifier),
135143

136-
// An IdentTT is a macro that has an
137-
// identifier in between the name of the
138-
// macro and the argument. Currently,
139-
// the only examples of this is
140-
// macro_rules!
144+
/// A normal, function-like syntax extension.
145+
///
146+
/// `bytes!` is a `NormalTT`.
147+
NormalTT(~MacroExpander:'static, Option<Span>),
141148

142-
// perhaps macro_rules! will lose its odd special identifier argument,
143-
// and this can go away also
149+
/// A function-like syntax extension that has an extra ident before
150+
/// the block.
151+
///
152+
/// `macro_rules!` is an `IdentTT`.
144153
IdentTT(~IdentMacroExpander:'static, Option<Span>),
145154
}
146155

src/libsyntax/ext/expand.rs

+46-1
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,9 @@ macro_rules! with_exts_frame (
260260
// When we enter a module, record it, for the sake of `module!`
261261
pub fn expand_item(it: @ast::Item, fld: &mut MacroExpander)
262262
-> SmallVector<@ast::Item> {
263-
let mut decorator_items: SmallVector<@ast::Item> = SmallVector::zero();
263+
let it = expand_item_modifiers(it, fld);
264+
265+
let mut decorator_items = SmallVector::zero();
264266
for attr in it.attrs.rev_iter() {
265267
let mname = attr.name();
266268

@@ -307,6 +309,48 @@ pub fn expand_item(it: @ast::Item, fld: &mut MacroExpander)
307309
new_items
308310
}
309311

312+
fn expand_item_modifiers(mut it: @ast::Item, fld: &mut MacroExpander)
313+
-> @ast::Item {
314+
let (modifiers, attrs) = it.attrs.partitioned(|attr| {
315+
match fld.extsbox.find(&intern(attr.name().get())) {
316+
Some(&ItemModifier(_)) => true,
317+
_ => false
318+
}
319+
});
320+
321+
it = @ast::Item {
322+
attrs: attrs,
323+
..(*it).clone()
324+
};
325+
326+
if modifiers.is_empty() {
327+
return it;
328+
}
329+
330+
for attr in modifiers.iter() {
331+
let mname = attr.name();
332+
333+
match fld.extsbox.find(&intern(mname.get())) {
334+
Some(&ItemModifier(dec_fn)) => {
335+
fld.cx.bt_push(ExpnInfo {
336+
call_site: attr.span,
337+
callee: NameAndSpan {
338+
name: mname.get().to_str(),
339+
format: MacroAttribute,
340+
span: None,
341+
}
342+
});
343+
it = dec_fn(fld.cx, attr.span, attr.node.value, it);
344+
fld.cx.bt_pop();
345+
}
346+
_ => unreachable!()
347+
}
348+
}
349+
350+
// expansion may have added new ItemModifiers
351+
expand_item_modifiers(it, fld)
352+
}
353+
310354
// does this attribute list contain "macro_escape" ?
311355
pub fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
312356
attr::contains_name(attrs, "macro_escape")
@@ -492,6 +536,7 @@ fn load_extern_macros(krate: &ast::ViewItem, fld: &mut MacroExpander) {
492536
NormalTT(ext, _) => NormalTT(ext, Some(krate.span)),
493537
IdentTT(ext, _) => IdentTT(ext, Some(krate.span)),
494538
ItemDecorator(ext) => ItemDecorator(ext),
539+
ItemModifier(ext) => ItemModifier(ext),
495540
};
496541
fld.extsbox.insert(name, extension);
497542
});

src/test/auxiliary/macro_crate_test.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010

1111
// force-host
1212

13-
#[feature(globs, macro_registrar, macro_rules, quote)];
13+
#[feature(globs, macro_registrar, macro_rules, quote, managed_boxes)];
1414

1515
extern crate syntax;
1616

17-
use syntax::ast::{Name, TokenTree};
17+
use syntax::ast::{Name, TokenTree, Item, MetaItem};
1818
use syntax::codemap::Span;
1919
use syntax::ext::base::*;
2020
use syntax::parse::token;
@@ -32,13 +32,22 @@ pub fn macro_registrar(register: |Name, SyntaxExtension|) {
3232
span: None,
3333
},
3434
None));
35+
register(token::intern("into_foo"), ItemModifier(expand_into_foo));
3536
}
3637

37-
pub fn expand_make_a_1(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult {
38+
fn expand_make_a_1(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult {
3839
if !tts.is_empty() {
3940
cx.span_fatal(sp, "make_a_1 takes no arguments");
4041
}
4142
MRExpr(quote_expr!(cx, 1i))
4243
}
4344

45+
fn expand_into_foo(cx: &mut ExtCtxt, sp: Span, attr: @MetaItem, it: @Item)
46+
-> @Item {
47+
@Item {
48+
attrs: it.attrs.clone(),
49+
..(*quote_item!(cx, enum Foo { Bar, Baz }).unwrap()).clone()
50+
}
51+
}
52+
4453
pub fn foo() {}

src/test/run-pass-fulldeps/macro-crate.rs

+9
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,16 @@
1919
#[phase(syntax)]
2020
extern crate macro_crate_test;
2121

22+
#[into_foo]
23+
#[deriving(Eq, Clone, Show)]
24+
fn foo() -> AFakeTypeThatHadBetterGoAway {}
25+
2226
pub fn main() {
2327
assert_eq!(1, make_a_1!());
2428
assert_eq!(2, exported_macro!());
29+
30+
assert_eq!(Bar, Bar);
31+
test(None::<Foo>);
2532
}
33+
34+
fn test<T: Eq+Clone>(_: Option<T>) {}

0 commit comments

Comments
 (0)