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)*);
}
};
}
Related
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.
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.
In C and C++ you can get the name of the currently executing function through the __func__ macro with C99 & C++11 and ___FUNCTION___ for MSVC.
Is there an equivalent of this in Rust?
Example of __func__ in C:
#include "stdio.h"
void funny_hello() {
printf ("Hello from %s\n", __func__);
}
int main() {
funny_hello();
}
Outputs Hello from funny_hello.
You can hack one together with std::any::type_name.
macro_rules! function {
() => {{
fn f() {}
fn type_name_of<T>(_: T) -> &'static str {
std::any::type_name::<T>()
}
let name = type_name_of(f);
&name[..name.len() - 3]
}}
}
Note that this gives a full pathname, so my::path::my_func instead of just my_func. A demo is available.
There was an RFC about this, but it was never agreed upon or implemented.
The rationale for its absence:
"In general I don't think any of us have given an inordinate amount of
thought to these "debugging related" macros in terms of long term
stability. Most of them seem fairly harmless, but committing to
provide all of them for all Rust programs forever is a strong
commitment to make. We may want to briefly consider the story of these
macros in conjunction with considering adding this new macro."
Maybe Rust will have something comparable in the future,
but for now you will need to rely on your own tagging.
side note: __FUNCTION__ is non standard, __func__ exists in C99 / C++11.
Adding to Veedrac's answer, you can get the function's name without its full path by adding this:
macro_rules! function {
() => {{
fn f() {}
fn type_name_of<T>(_: T) -> &'static str {
std::any::type_name::<T>()
}
let name = type_name_of(f);
// Find and cut the rest of the path
match &name[..name.len() - 3].rfind(':') {
Some(pos) => &name[pos + 1..name.len() - 3],
None => &name[..name.len() - 3],
}
}};
}
You will get my_func instead of my::path::my_func for example.
It appears that function_name crate will do this.
https://docs.rs/function_name/latest/function_name/
The example from the docs is
use ::function_name::named;
#[named]
fn my_super_duper_function ()
{
assert_eq!(
function_name!(),
"my_super_duper_function",
);
}
I am not involved with the project and have not actually tried it yet.
While there's a lack of official support, there's the stdext crate (also mentioned in the RFC issue) that makes it easy to use.
use stdext::function_name;
fn foo() {
println!("{}", function_name!());
}
This includes module/trait names, like ::module::Trait::function if any.
If you only care about the name and not its entire path, you could do something like this for trait methods (keep in mind that there is runtime overhead for this, you may want to limit it with, e.g. OnceCell):
let fn_name = function_name!()
.rsplit_once(':')
.expect("could not parse function name")
.1;
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),+)
})
}
}