The following code is a simple test of macro_rule!'s limit. It correctly prints [1, 2] as the target.
macro_rules! test {
($target:tt) => {
println!("{:?}", $target);
};
($target:tt, $($rest:tt),*) => {
test!($target)
};
([$target:tt, $($inner_rest:tt),*],$($rest:tt),*) => {
test!($target)
}
}
fn main() {
test![[1, 2], [[5, 6], 3], 4];
}
However, if I change [[1,2] to [[1,2],3], the compiler complains:
<anon>:16:15: 16:16 error: mismatched types:
expected `[_; 2]`,
found `_`
(expected array of 2 elements,
found integral variable) [E0308]
<anon>:16 test![[[1,2],3],[[5,6],3],4];
^
<std macros>:2:25: 2:56 note: in this expansion of format_args!
<std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>)
<anon>:4:5: 4:30 note: in this expansion of println! (defined in <std macros>)
<anon>:7:6: 7:20 note: in this expansion of test! (defined in <anon>)
<anon>:16:2: 16:31 note: in this expansion of test! (defined in <anon>)
<anon>:16:15: 16:16 help: see the detailed explanation for E0308
error: aborting due to previous error
playpen: application terminated with error code 101
Is there a way to let the macro recursively deal with brackets and print the first item? I have studied the example given by the Rust documentation, but still have no idea about how to implement it in my case.
You asked it to parse an expression, so that's exactly what it tried to do.
The first thing it sees is [[1, 2], 3] which is invalid because it's an array literal where the first element is of type [i32; 2] and the second element is of type i32. So, it fails.
If you want to macro to deal with recursive syntax, you're going to have to write it to handle it. I have no idea what you're trying to do ("deal with bracket" how?), so I can't offer a concrete example. Try adding an extra rule before the existing ones that explicitly deals with [[$($stuff:tt)*] .... Rules are processed top-to-bottom, so you need to put the most specific rule first. The following prints out [1, 2]:
macro_rules! test {
([$target:tt, $($inner_rest:tt),*], $($rest:tt),*) => {
test!($target)
};
($target:tt) => {
println!("{:?}", $target);
};
($target:tt, $($rest:tt),*) => {
test!($target)
};
}
fn main() {
test![[[1,2],3],[[5,6],3],4];
}
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.
This is a bit of unexpected behavior that's likely to bite beginners. First, is this intended? Second, what other things does Raku use to guess which object to create? Does it start off thinking it's Block or Hash and change later, or does it decide on the end?
You can construct a Hash with braces and the fat arrow:
my $color-name-to-rgb = {
'red' => 'FF0000',
};
put $color-name-to-rgb.^name; # Hash
Using the other Pair notation creates a Hash too.
my $color-name-to-rgb = {
:red('FF0000'),
};
But, absent the fat arrow, I get a Block instead:
my $color-name-to-rgb = {
'red', 'FF0000',
};
put $color-name-to-rgb.^name; # Block
The Hash docs only mention that using $_ inside the braces creates a Block.
There are other ways to define a hash, but I'm asking about this particular bit of syntax and not looking for the workarounds I already know about.
$ perl6 -v
This is Rakudo version 2017.04.3 built on MoarVM version 2017.04-53-g66c6dda
implementing Perl 6.c.
When it's a Hash
Your question1 and this answer only apply to braced blocks in term position2.
Braced code that precisely follows the rule explained below constructs a Hash:
say WHAT { } # (Hash)
say WHAT { %foo } # (Hash)
say WHAT { %foo, ... } # (Hash)
say WHAT { foo => 42, ... } # (Hash)
say WHAT { :foo, ... } # (Hash)
say WHAT { key => $foo, ... } # (Hash)
The rule
If the block is empty, or contains just a list whose first element is a % sigil'd variable (eg %foo) or a literal pair (eg :bar), and it does not have a signature or include top level statements, it's a Hash. Otherwise it's a Block.
To force Block or Hash interpretation
To force a {...} term to construct a Block instead of a Hash, write a ; at the start i.e. { ; ... }.
To write an empty Block term, write {;}.
To write an empty Hash term, write {}.
To force a {...} term to construct a Hash instead of a Block, follow the rule (explained in detail in the rest of this answer), or write %(...) instead.
An explicit signature means it's a Block
Some braced code has an explicit signature, i.e. it has explicit parameters such as $foo below. It always constructs a Block no matter what's inside the braces:
say WHAT { key => $foo, 'a', 'b' } # (Hash)
say WHAT -> $foo { key => $foo, 'a', 'b' } # (Block)
An implicit signature also means it's a Block
Some braced code has an implicit signature that is generated due to some explicit choice of coding within the block:
Use of a "pronoun" inside {...} means it's a Block with a signature (an implicit signature if it doesn't already have an explicit one). The pronouns are $_, #_, and %_.
This includes implied use of $_ inside {...} due to a .method call with no left hand side argument. In other words, even { .foo } has a signature ((;; $_? is raw)) due to .foo's lack of a left hand side argument.
Use of a "placeholder" variable (e.g. $^foo).
As with an explicit signature, if braced code has an implicit signature then it always constructs a Block no matter what's inside the braces:
say WHAT { key => $_ } # (Block)
say WHAT { key => 'value', .foo, .bar } # (Block)
Top level statements mean it's a Block
say WHAT { :foo; (do 'a'), (do 'b') } # (Block)
say WHAT { :foo, (do 'a'), (do 'b') } # (Hash)
The second line contains multiple statements but they're producing values within individual elements of a list that's the single top level expression.
A top level declaration of an identifier mean it's a Block
A declaration is a statement, but I've included this section just in case someone doesn't realize that.
say WHAT { :foo, $baz, {my $bar} } # (Hash)
say WHAT { :foo, $baz, (my $bar) } # (Block)
The first line contains a Block as a key that contains a declaration (my $bar). But that declaration belongs to the inner {my $bar} Block, not the outer {...}. So the inner Block is just a value as far as the outer {...} is concerned, and thus that outer braced code is still interpreted as a Hash.
In contrast the second line declares a variable directly within the outer {...}. So it's a Block.
Still Blocks, not Hashs
Recall that, to be a Hash, the content of braced code must be a list that begins with either a % sigil'd variable or a literal pair. So these all produce Blocks:
my $bar = key => 'value';
say WHAT { $bar, %baz } # (Block)
say WHAT { |%baz } # (Block)
say WHAT { %#quux } # (Block)
say WHAT { 'a', 'b', key => $foo } # (Block)
say WHAT { Pair.new: 'key', $foo } # (Block)
Footnotes
1 This "Hash or Block?" question is an example of DWIM design. In Raku culture, good DWIM design is considered a good thing. But every DWIM comes with corresponding WATs3. The key to good DWIM design is ensuring that, in general, WATs' barks are worse than their bites4; and that the barks are useful5; and that the net benefits of the DWIM are considered to far outweigh all the barking and biting.6
2 A term is Raku's analog of a noun or noun phrase in English. It's a value.
Examples of braced blocks that are terms:
.say given { ... } # closure? hash?
say 42, { ... } # closure? hash?
Examples of braced blocks that are not terms:
if True { ... } # always a closure
class foo { ... } # always a package
put bar{ ... } # always a hash index
This answer only discusses braced blocks that are terms. For more details about terms, or more specifically "term position" (places in the grammar where a braced block will be interpreted as a term), see the comments below this answer.
3 WAT refers to a dev's incredulous surprise when something seems crazy to them. It's known that, even for well designed DWIMs, for each one that works for most folk, most of the time, there are inevitably one or more related WATs that surprise some folk, some of the time, including some of the same folk who at other times benefit from the DWIM.
4 The bite of the WATs related to this DWIM varies. It's typically a bark (error message) that makes the problem obvious. But it can also be much more obscure:
say { a => 42 }() ; # No such method 'CALL-ME' for invocant of type 'Hash' WAT? Oh.
say { a => $_ }<a> ; # Type Block does not support associative indexing. WAT? Oh.
say { a => $_, b => 42, c => 99 } .elems # 1 WAT?????
5 A "bark" is an error message or warning in documentation. These can often be improved. cf Lock.protect({}) fails, but with surprising message.
6 Community member opinions differ on whether DWIM design in general, or any given DWIM in particular, is worth it. cf my perspective vs Sam's answer to this question.
The preferred Perl6 way is to use %( ) to create hashes.
my $color-name-to-rgb = %(
'red', 'FF0000',
);
I would not recommend people use braces to create hashes, ever. If they want to make a hash then %( ) is the proper way to do it.
If you are coming from the Perl 5 world it's best to just get in the habit of using %( ) instead of { } when creating a Hash.
I'm using a library for string interning (string-cache), that uses macros to efficient create elements (atom!). However for simplification here is a similar macro that demonstrates the problem
macro_rules! string_intern {
("d") => ("Found D");
}
say I need to call this macro from another macro and give it a string version of an identifier.
macro_rules! print_ident {
($id:ident) => (
string_intern!(stringify!($id));
);
}
However calling this macro
fn main() {
print_ident!(d);
}
Fails with error:
error: no rules expected the token `stringify`
--> <anon>:7:24
|
7 | string_intern!(stringify!($id));
| ^^^^^^^^^
Playground link
I know stringify! correctly converts to identifier d to string "d", because giving it to println! works as expected. Is there a way to pass the identifier I want turned into string to string_intern?
println! lets you do this because it uses format_args! under the covers, which is a compiler-provided "intrinsic" that forcibly evaluates its first argument before using it. You cannot do this from a user-defined macro; you'd have to write a compiler plugin (which requires a nightly compiler and no guarantee of stability).
So, yeah; you can't. Sorry. The only thing you can do is redefine the macro in such a way that you don't need an actual string literal, or change how you invoke it.
Your definition of string_intern! is expecting a literal "d" and nothing else, but you are passing in these tokens: stringify, !, ... which why it fails. The definition of string_intern! that you want is probably:
macro_rules! string_intern {
($e:expr) => {
match $e {
"d" => "Found D",
_ => "Not found",
}
}
}
which can accept any expression that evaluates to a string type.
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.
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),+)
})
}
}