I would like to pass mutability to a macro so that I can do
mymacro![mut foo];
mymacro![bar];
and the macro will see them as different matches. which specifier to use?
There isn't one. You'll need two rules: one which matches a literal mut, and one that doesn't.
macro_rules! do_something {
(mut $name:ident) => { ... };
($name:ident) => { ... };
}
And yes, they do have to be in that order, because macro arms are matched top-to-bottom.
Related
Can I repeat match in a Rust macro? I want to be able to do something like:
my_dsl! {
foo <other tokens>;
bar <other tokens>;
foo <other tokens>;
...
}
Basically, an arbitrary number of semicolon-delimited statement, and each statement is handled by different rules.
I know I can have several foo!(), bar!() macros - each per statement, but ideally I'd like to avoid that.
I was thinking if I can capture something like $($t:tt)*, but excluding semicolon, and then delegate to other macros?
You should read The Little Book of Rust Macros and specifically for your question section 4.2: Incremental TT munchers.
For example:
macro_rules! my_dsl {
() => {};
(foo $name:ident; $($tail:tt)*) => {
{
println!(concat!("foo ", stringify!($name));
my_dsl!($($tail)*);
}
};
(bar $name:ident; $($tail:tt)*) => {
{
println!(concat!("bar ", stringify!($name));
my_dsl!($($tail)*);
}
};
}
I would like to write a macro_rules! macro whose arguments are one ident and a list of string literals. The expansion of the macro needs to contain both the string literals, and the corresponding byte literals. The macro is for tests, and will only ever be used with strings containing only characters in the U+0000 ... U+007F range. It is OK if supplying anything other than string literals to the macro causes a compile-time error.
If this isn't currently possible without resorting to procedural macros, just tell me so, don't bother actually writing the procedural macro ;-)
The desired invocation and expansion is like this:
all_s! isalpha [ "abcdefghijklmnopqrstuvwxyz" /* , ... */ ];
=>
assert!(isalpha("abcdefghijklmnopqrstuvwxyz"));
assert!("abcdefghijklmnopqrstuvwxyz".chars().all(|b| isalpha(b));
assert!(isalpha(b"abcdefghijklmnopqrstuvwxyz"));
assert!(b"abcdefghijklmnopqrstuvwxyz".iter().all(|b| isalpha(b)));
/* ... */
This is as far as I've gotten:
macro_rules! all_s {
($what: ident, $( $str: tt ),* ) => {{
$(
assert!($what($str));
assert!($str.chars().all(|b| $what(b));
assert!($what(BYTE_LITERAL!($str)));
assert!(BYTE_LITERAL!($str).iter().all(|b| $what(b)));
)*
}}
}
but I don't know what to put where it says BYTE_LITERAL!, and also I'm getting error messages that suggest that I haven't written the match pattern correctly, e.g. "macro all_s! expects no ident argument, given 'isalpha'" when '$what:ident' is right there.
You cannot convert a literal to another type of literal; it's just not how macros work.
You can convert a &str to a &[u8] via str::as_bytes:
fn is_alpha<T>(_: T) -> bool { true }
macro_rules! all_s {
($what: ident, $str: tt) => {{
assert!($what($str));
assert!($str.chars().all(|b| $what(b)));
assert!($what($str.as_bytes()));
assert!($str.as_bytes().iter().all(|b| $what(b)));
}};
}
fn main() {
all_s!(is_alpha, "abcdefghijklmnopqrstuvwxyz");
}
I also don't know how to reject tts that aren't string literals.
If you make use of the fact that the arguments are strings, then the typechecker will take care of producing errors.
macro_rules! all_s {
($what: ident, $( $str: tt ),* ) => {{
$(
assert!($what($str));
assert!(str::chars($str).all(|c| char::is_alphabetic(c)));
assert!(str::chars($str).all(|c| char::is_alphabetic(c)));
)*
}}
}
In your question code, you are trying to use the same isalpha with values of both char and u8. You'll have to convert the chars to u8s first:
assert!(str::chars($str)
.map(|c| c as u8) // this is safe given the assumption you stated
.all(|b| b < 0x7F));
Is it possible to write a macro that expands an expression into multiple indexed arguments, which can be passed to a function or another macro?
See this simple self contained example.The aim is to have unpack3 expand v into v[0], v[1], v[2].
macro_rules! elem {
($val:expr, $($var:expr), *) => {
$($val == $var) || *
}
}
// attempt to expand an array.
macro_rules! unpack3 {
($v:expr) => {
$v[0], $v[1], $v[2]
}
}
fn main() {
let a = 2;
let vars = [0, 1, 3];
// works!
if elem!(a, vars[0], vars[1], vars[2]) {
println!("Found!");
}
// fails!
if elem!(a, unpack3!(vars)) {
println!("Found!");
}
}
The second example fails, is it possible to make this work?
Possible solutions could include:
Changing use of macro grammar.
Using tuples, then expanding into arguments after.
Re-arranging the expressions to workaround macro constraints.
Note, this may be related to Escaping commas in macro output but don't think its a duplicate.
This is impossible in two different ways.
First, to quote the answer to the question you yourself linked: "No; the result of a macro must be a complete grammar construct like an expression or an item. You absolutely cannot have random bits of syntax like a comma or a closing brace." Just because it isn't exactly a comma doesn't change matters: a collection of function arguments are not a complete grammar construct.
Secondly, macros cannot parse the output of other macros. This requires eager expansion, which Rust doesn't have. You can only do this using recursion.
Is it possible to build an enum inside a Rust macro using fields that are defined as macro parameters? I've tried this:
macro_rules! build {
($($case:ty),*) => { enum Test { $($case),* } };
}
fn main() {
build!{ Foo(i32), Bar(i32, i32) };
}
But it fails with error: expected ident, found 'Foo(i32)'
Note that if the fields are defined inside the enum, there is no problem:
macro_rules! build {
($($case:ty),*) => { enum Test { Foo(i32), Bar(i32, i32) } };
}
fn main() {
build!{ Foo(i32), Bar(i32, i32) };
}
It also works if my macro only accepts simple fields:
macro_rules! build {
($($case:ident),*) => { enum Test { $($case),* } };
}
fn main() {
build!{ Foo, Bar };
}
But I've been unable to get it to work in the general case.
It's absolutely possible, but you're conflating totally unrelated concepts.
Something like $case:ty does not mean $case is something which looks like a type, it means $case is literally a type. Enums are not made up of a sequence of types; they're made up of a sequence of variants which are an identifier followed (optionally) by a tuple structure body, a record structure body, or a tag value.
The parser doesn't care if the type you give it happens to coincidentally look like a valid variant, it's simply not expecting a type, and will refuse to parse one in that position.
What you need is to use something like $case:variant. Unfortunately for you, no such matcher exists. The only way to do something like this is to manually parse it using a recursive incremental parser and that is so out of scope of an SO question it's not funny. If you want to learn more, try the chapter on incremental TT munchers in the Little Book of Rust Macros as a starting point.
However, you don't appear to actually do anything with the cases. You're just blindly substituting them. In that case, you can just cheat and not bother with trying to match anything coherent:
macro_rules! build {
($($body:tt)*) => {
as_item! {
enum Test { $($body)* }
}
};
}
macro_rules! as_item {
($i:item) => { $i };
}
fn main() {
build!{ Foo, Bar };
}
(Incidentally, that as_item! thing is explained in the section on AST coercion (a.k.a. "the reparse trick").)
This just grabs everything provided as input to build!, and shoves it into the body of an enum without caring what it looks like.
If you were trying to do something meaningful with the variants, well, you're going to have to be more specific about what you're actually trying to accomplish, as the best advice of how to proceed varies wildly depending on the answer.
I am trying to make a wrapper for a macro. The trouble is that I don't want to repeat the same rules in both macro. Is there a way to do that?
Here is what I tried:
macro_rules! inner {
($test:ident) => { stringify!($test) };
($test:ident.run()) => { format!("{}.run()", stringify!($test)) };
}
macro_rules! outer {
($expression:expr) => {
println!("{}", inner!($expression));
}
}
fn main() {
println!("{}", inner!(test));
println!("{}", inner!(test.run()));
outer!(test);
outer!(test.run());
}
but I get the following error:
src/main.rs:8:31: 8:42 error: expected ident, found test
src/main.rs:8 println!("{}", inner!($expression));
^~~~~~~~~~~
If I change the outer macro for this, the code compile:
macro_rules! outer {
($expression:expr) => {
println!("{}", stringify!($expression));
}
}
What am I doing wrong?
macro_rules! is both cleverer and dumber than you might realise.
Initially, all input to a macro begins life as undifferentiated token soup. An Ident here, StrLit there, etc. However, when you match and capture a bit of the input, generally the input will be parsed in an Abstract Syntax Tree node; this is the case with expr.
The "clever" bit is that when you substitute this capture (for example, $expression), you don't just substitute the tokens that were originally matched: you substitute the entire AST node as a single token. So there's now this weird not-really-a-token in the output that's an entire syntax element.
The "dumb" bit is that this process is basically irreversible and mostly totally invisible. So let's take your example:
outer!(test);
We run this through one level of expansion, and it becomes this:
println!("{}", inner!(test));
Except, that's not what it looks like. To make things clearer, I'm going to invent some non-standard syntax:
println!("{}", inner!( $(test):expr ));
Pretend that $(test):expr is a single token: it's an expression which can be represented by the token sequence test. It is not simply the token sequence test. This is important, because when the macro interpreter goes to expand that inner! macro, it checks the first rule:
($test:ident) => { stringify!($test) };
The problem is that $(test):expr is an expression, not an identifier. Yes, it contains an identifier, but the macro interpreter doesn't look that deep. It sees an expression and just gives up.
It fails to match the second rule for the same reason.
So what do you do? ... Well, that depends. If outer! doesn't do any sort of processing on its input, you can use a tt matcher instead:
macro_rules! outer {
($($tts:tt)*) => {
println!("{}", inner!($($tts)*));
}
}
tt will match any token tree (see the Macros chapter of the Rust Book). $($tts:tt)* will match any sequence of tokens, without changing them. This of this as a way to safely forward a bunch of tokens to another macro.
If you need to do processing on the input and forward it on to the inner! macro... you're probably going to have to repeat the rules.
I had some success with the $($stuff: expr),+ syntax.
macro_rules! println {
( $($stuff: expr),+) => {
avr_device::interrupt::free(|cs| {
uwriteln!(unsafe { &SERIAL_STATIC}.borrow(cs).borrow_mut().as_mut().unwrap(),
$($stuff),+)
})
}
}