-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Macro syntax to count sequence repetitions #88
Conversation
Off Topic: You can see the rendered view by going to the files changed tab and clicking |
@sinistersnare You can go to the Files changed tab and click |
Under "Alternatives" you say that a #![feature(macro_rules)]
macro_rules! vec_new(
($($e:expr),*) => ({
// leading _ to allow empty construction without a warning.
let mut _temp = ::std::vec::Vec::with_capacity(arity!($($e),*));
$(_temp.push($e);)*
_temp
});
($($e:expr),+,) => (vec_new!($($e),+))
)
// Trust me, you *really* want the helper macros...
// If you attempt this without them, may God have mercy on your soul.
macro_rules! arity(
($($thing:expr),+) => (arity_expr!($($thing),*));
($($thing:pat),+) => (arity_pat!($($thing),*));
() => (0);
)
macro_rules! arity_expr(
($head:expr, $($tail:expr),*) => (1 + arity_expr!($($tail),*));
($last:expr) => (1);
)
macro_rules! arity_pat(
($head:pat, $($tail:pat),*) => (1 + arity_pat!($($tail),*));
($last:pat) => (1);
)
fn test_arity() {
assert!(arity!() == 0);
// expr
assert!(arity!(9) == 1);
assert!(arity!(9,9,9) == 3);
// ident - matched by expr
assert!(arity!(Nine) == 1);
assert!(arity!(Nine,Nine,Nine) == 3);
// ty - matched by expr
assert!(arity!(Option::<int>) == 1);
assert!(arity!(Option::<int>,Option::<int>,Option::<int>) == 3);
// block - matched by expr
assert!(arity!({9;9}) == 1);
assert!(arity!({9;9},{9;9},{9;9}) == 3);
// pat
assert!(arity!(foo @ _) == 1);
assert!(arity!(foo @ _,foo @ _,foo @ _) == 3);
println!("All tests pass");
}
fn main() {
test_arity();
let v = vec_new![7,8,9];
println!("{}", v);
} I can't think of any cases where this wouldn't suffice. True, |
Eh, I don't know about that: #![feature(macro_rules)]
macro_rules! iota(
($($thing:expr),+) => (iota_expr!(1, $($thing),*));
($($thing:pat),+) => (iota_pat!(1, $($thing),*));
() => (());
)
macro_rules! iota_expr(
($id:expr, $head:expr, $($tail:expr),*) => ({
println!("expr {}", $id); iota_expr!($id+1, $($tail),*);
});
($id:expr, $last:expr) => ({ println!("expr {}", $id); });
)
macro_rules! iota_pat(
($id:expr, $head:pat, $($tail:pat),*) => ({
println!("pat {}", $id); iota_pat!($id+1, $($tail),*);
});
($id:expr, $last:pat) => ({ println!("pat {}", $id); });
)
fn test_iota() {
println!("empty test");
iota!();
// expr
println!("expr test");
iota!(9);
iota!(9,9,9);
// ident - matched by expr
println!("ident test");
iota!(Nine);
iota!(Nine,Nine,Nine);
// ty - matched by expr
println!("ty test");
iota!(Option::<int>);
iota!(Option::<int>,Option::<int>,Option::<int>);
// block - matched by expr
println!("block test");
iota!({9;9});
iota!({9;9},{9;9},{9;9});
// pat
println!("pat test");
iota!(foo @ _);
iota!(foo @ _,foo @ _,foo @ _);
}
fn main() {
test_iota();
} Though this pattern does get mightily inconvenient, mighty fast. The idea of iinitializing statics that utilize the current iteration number is a tantalizing problem, but I'm not sure if it's possible using this approach. |
@bstrie It didn't occur to me that you could have the same macro match multiple different types of nonterminals, although I would not be surprised if you can trigger parse errors that way (e.g. the first arg could match as an expression, so it does, but then the second arg is a real pattern and can't match as an expr). As for |
Yeah, your |
Would something like |
@huonw That seems extremely hacky to be exporting from libstd. |
It seems far more controlled (and less hacky) than an count macro that just takes arbitrary arguments without a "type" (e.g. However, I think I still prefer a proper repeat-count for |
I think we could implement If we were willing to squirrel information away into the |
@paulstansifer Do you feel that a procedural macro would be the better approach? I feel like that's trying to paper over a deficiency in the macro syntax. Better to just fix macros to be able to count the sequences directly, then to try and recreate that ability externally. |
Servo could use this. For now, we have a Python template that generates Rust code equivalent to a struct with a bunch of The plan is to replace the template with procedural macros, but this RFC would allow that particular bit to be a much simpler |
@kballard At least for |
@SimonSapin Unfortunately, I doubt that you'll be able to write I have an idea for a change we could make to at least allow |
I don’t know how the parser works exactly, but this compiles fine: static A: uint = 42;
static B: [u8, ..A] = [0, ..A]; |
@SimonSapin Oh, sure enough! It's syntactically an expression (handled by |
Using a hack to try and count the number of repetitions seems distinctly sub-par to just letting the macro parser count them itself, since the macro parser should already know the answer. It also seems more fragile (e.g. that multi-typed |
@kballard The solution that I proposed shouldn't be fragile at all. I admit that it's a little funny to essentially say "Paste this in, separated by commas, and then count the commas", but I don't think it's too bad. And name pollution is exactly my concern; instead of claiming a punctuation sequence for the concept, we can just use a descriptive name, freeing That said, I am sympathetic to the idea of just acquiring the information directly, since it'd be shorter to write. But I really like the idea of implementing a feature without having to change the language. |
@kballard Is there an example of a case where E.g. the discussion above has led me to think that If so, I recommend you revise the RFC so that this feature is provided via a macro like |
@pnkfelix match count {
Some(arity!($($arg),*)) => {
println!("the count matches the args");
}
_ => {
println!("wrong arg count");
}
} However |
Those limitations are just for the |
@rkjnsn You're right, I was only thinking of the Still, I think that the idea of using It also just plain seems weird. For it to work on all nonterminal types, it needs to work on the raw token stream, and literally just count the number of commas and add 1. That's the only way it would work with e.g. the Basically, the |
|
To clarify: I don’t care about what it looks like (custom syntax or built-in macro) as long as both the total count and the current iteration number can be obtained somehow. |
I can appreciate the argument that its bad to reserve the global name cc @jbclements who might be able to weigh in with some perspective on where the macro system is going. I will have to review the procedural macro system a bit more before I can comment further on @kballard's other point. (And I will also have to play around a little with the current iteration number desiderata. That does indeed seem important.) @kballard : In the meantime, it would be good to add an motivating example for |
In my mind, it looks like adding |
@kballard It'll never make sense to reify the INTERPOLATED tokens: the performance cost would be high, and it would buy nothing. I agree that "comma-separate, then count commas" is kinda weird, but I argue that reserving a name is strictly better than reserving a punctuation character. After all, there are tons of possible names, but not a lot of punctuation. What's more, |
That's certainly plausible, but I don't like making a macro that relies upon the current implementation details of nonterminal tokens.
The
I disagree. It's actually a bad name. It's not actually finding the arity of anything at all, it's just a hack that tries to count nonterminals by counting commas. Offhand, I'd expect something like |
@kballard do you think there are more points that need to be discussed here? Assuming not, would you like to attempt to update the RFC in the manner I outlined earlier? Or should we perhaps plan to postpone this work for post 1.0, since |
@pnkfelix I don't think there's anything more to discuss. I've actually had this page open on my main computer for a few weeks, to remind me to get around to updating the RFC, I just haven't had the time lately. I don't think I'll have time tonight either, but I'll try to get to it sometime this week. My biggest concern with postponing this to after 1.0 is that |
I think we're free to mess with |
Looks like rust-lang/rust@02d976a fixed the performance of |
NB: There is currently no way to get the repetition number (without writing a compiler plugin.) |
@kballard given SimonSapin's observation from yesterday, I am inclined to postpone this RFC to be addressed post 1.0. Do you think that's reasonable? |
@pnkfelix There's still plenty of use for this RFC. For example, there's a macro in the compiler (at least one, but possibly more than one) that could really benefit from this RFC (IIRC it wants the repetition number feature). And I seem to recall a couple of months ago having code in a personal project that could have used the repetition number feature as well. That said, if it's not clear that we want to take this yet, postponing to after 1.0 is acceptable. The |
(unassigning self; I think we should close as postponed.) |
@kballard The team agress that there is definitely utility in this feature. But we don't think there is a pressing need to put this extension in before 1.0. So we are closing as postponed. |
(p.s. @kballard thanks for all the effort you put into this. I really do think we will get something good out of this process once we can come back around to addressing this feature.) |
@kballard for completeness, that's easily fixed. The following seems to work for everything but item and meta (it allows mixed lists but there shouldn't be any ambiguity): macro_rules! arity {
($e:tt, $($toks:tt)+) => { 1 + arity!($($toks)+) };
($e:tt) => { 1 };
($e:pat, $($toks:tt)+) => { 1 + arity!($($toks)+) };
($e:pat) => { 1 };
($e:expr, $($toks:tt)+) => { 1 + arity!($($toks)+) };
($e:expr) => { 1 };
() => { 0 };
} |
@kballard are you still pursuing this? I am in need of the repetition index in some macros I am writing and there is currently no way of getting this in the current state of macros. Is the discussion done here folded into some other rfc that would provide such functionality in some other way? Or do you have plans of pursuing this rfc further? |
@alkis it's possible but you need to use recursion (you can't do it with the |
@Stebalien if you use recursion the repetition index will not be a literal but an expression. The index of the N'th item will expand to something like 0 + 1 + 1 + ... + 1. If you want the index to be used for accessing an element of a tuple unfortunately an expression won't work. AFAIK only a literal works. |
@alkis I see. No, this doesn't work with tuples etc. |
So what needs to be done to advance this RFC? |
is this rfc abandoned? superseded? |
I think procedural macros rust-lang/rust#38356 will make this largely unnecessary. |
@SimonSapin Procedural macros are generally a heavy-weight solution and counting is something I've needed quite often in declarative macros (although building a |
At least building this as a procedural macro outside of the standard library would make it easier to make a case for inclusion in |
Oh, I see. Good point! |
Rendered view