How to pass a blank / empty argument to a macro in Rust? - macros

In some cases you may want to pass in an argument to a macro which is either some text, or nothing (blank space, as if nothing was written).
Given this starting point:
macro_rules! testme {
($var:ident, $code:block) => {
for i in 0..10 {
let $var = i;
{ $code }
if $var > 5 {
println!("over 5");
}
}
}
}
fn main() {
testme!(myvar, {
println!("{}", myvar);
});
}
We may want var to optionally be mutable, assuming the macro body is larger then in the example above, its best not to duplicate the entire macro.
macro_rules! testme {
(private $var:ident, $code:block, $var_qual:tt) => {
for i in 0..10 {
// imagine this is a lot more code :)
let $var_qual $var = i;
{ $code }
if $var > 5 {
println!("over 5");
}
}
};
(mut $var:ident, $code:block) => {
testme!(private $var, $code, mut)
};
/*
($var:ident, $code:block) => {
testme!(private $var, $code, )
// ^ how to pass in a blank argument?
};
*/
}
fn main() {
testme!(mut myvar_mut, {
myvar_mut += 10;
println!("{}", myvar_mut);
});
/*
testme!(myvar_immutable, {
println!("{}", myvar_immutable);
});
*/
}
As far as I can tell there is no way to pass in a an empty argument, uncomment the /**/ comments to see the error.
Is it possible to pass in an empty argument to a macro to make an example like this work?

As far as I know its not possible to pass in blank / empty arguments.
It is possible however to pass in a locally defined macro which optionally adds a prefix.
Working example:
macro_rules! testme {
(private $var:ident, $code:block, $var_qual_macro:ident) => {
for i in 0..10 {
// imagine this is a lot more code :)
let $var_qual_macro!($var) = i;
{ $code }
if $var > 5 {
println!("over 5");
}
}
};
(mut $var:ident, $code:block) => {
macro_rules! var_qualifier { ($v:ident) => { mut $v } }
testme!(private $var, $code, var_qualifier)
};
($var:ident, $code:block) => {
macro_rules! var_qualifier { ($v:ident) => { $v } }
testme!(private $var, $code, var_qualifier)
};
}
fn main() {
testme!(mut myvar_mut, {
myvar_mut += 10;
println!("{}", myvar_mut);
});
testme!(myvar_immutable, {
println!("{}", myvar_immutable);
});
}
Take care when nesting macros like this that the name of the macro (var_qualifier in this case) is isn't the same name used inside a different macro since the name will be silently shadowed.

Related

VSCode Selection to snippet

It's a beginner question so don't be to hard to me.
Example:
A,B,C,D, ..
I need to convert this string to the following output in VSCode
enum id name
{
value(0; A) { Caption = 'A'; }
value(1; B) { Caption = 'B'; }
value(2; C) { Caption = 'C'; }
value(3; D) { Caption = 'D'; }
}
I can read the selection and split it into separate tokens.
But I'm stuck when it comes to writing it back to my line.
My Code:
'use strict';
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
//import * as vscode from 'vscode';
import { window, commands, Disposable, ExtensionContext, StatusBarAlignment, StatusBarItem, TextDocument, TextEditor, ViewColumn, workspace, TextLine, TextEdit, Uri, Position } from 'vscode';
import { stringify } from 'querystring';
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
export function activate(context: ExtensionContext) {
console.log('"Cg Helper" is now active!');
let cgHelper = new CgALHelper;
let disp = commands.registerCommand('extension.convertSelection2Enum', () =>{
cgHelper.convertSelection2Enum(window.activeTextEditor);
})
context.subscriptions.push(disp);
}
// this method is called when your extension is deactivated
export function deactivate() {
}
class CgALHelper
{
public convertSelection2Enum(editor: TextEditor)
{
this.convertTextLine2Enum(editor);
}
public convertInputText2Enum()
{
}
private convertTextLine2Enum(editor: TextEditor)
{
let line = editor.document.lineAt(editor.selection.active.line);
if (line != null && line.text != null)
{
window.showInformationMessage(line.text);
let tokens = line.text.split(',');
if (tokens[0] != null && tokens[0] != '' && tokens.length != 0 )
{
tokens.forEach(tok => {
// I'm stuck here !
});
} else
window.showErrorMessage('nothing to convert');
} else
window.showErrorMessage('Nothing to convert');
}
}
You want to construct a SnippetString. Snippet strings can be constructed incrementally:
const snippet = new vscode.SnippetString();
snippet.appendText('enum id name {');
tokens.forEach(tok => {
snippet.appendText(` value(0; ${tok}) { Caption = '${tok}'; }`)
});
snippet.appendText('}');
Then apply the snippet to the editor using TextEditor.insertSnippet:
editor.insertSnippet(snippet);

How to match Rust's `if` expressions in a macro?

I'm trying to write a macro that will rewrite certain Rust control flow, but I'm having difficulty matching an if expression. The problem is that the predicate is an expression, but an expr is not permitted to be followed by a block or {.
The best I've got is to use tt:
macro_rules! branch {
(
if $pred:tt
$r1:block
else
$r2:block
) => {
if $pred {
$r1
} else {
$r2
}
};
}
Which works fine with single-token or grouped predicates:
branch! {
if (foo == bar) {
1
} else {
2
}
}
But fails if the predicate was not grouped:
branch! {
if foo == bar {
1
} else {
2
}
}
error: no rules expected the token `==`
I also tried to use a repeating pattern of tt in the predicate:
macro_rules! branch {
(
if $($pred:tt)+
$r1:block
else
$r2:block
) => {
if $($pred)+ {
$r1
} else {
$r2
}
};
}
But this produces an error because it's now ambiguous whether subsequent block should match the tt too:
error: local ambiguity: multiple parsing options: built-in NTs tt ('pred') or block ('r1').
Is there a way to do this, or am I stuck with inventing special syntax to use in the macro?
You could use a TT muncher to parse the predicate:
macro_rules! branch {
{
if $($rest:tt)*
} => {
branch_parser! {
predicate = ()
rest = ($($rest)*)
}
};
}
macro_rules! branch_parser {
{
predicate = ($($predicate:tt)*)
rest = ({ $($then:tt)* } else { $($else:tt)* })
} => {
println!("predicate: {}", stringify!($($predicate)*));
println!("then: {}", stringify!($($then)*));
println!("else: {}", stringify!($($else)*));
};
{
predicate = ($($predicate:tt)*)
rest = ($next:tt $($rest:tt)*)
} => {
branch_parser! {
predicate = ($($predicate)* $next)
rest = ($($rest)*)
}
};
}
fn main() {
branch! {
if foo == bar {
1
} else {
2
}
}
}
Output:
predicate: foo == bar
then: 1
else: 2

How to embed a Rust macro variable into documentation?

I'd like to use a macro variable in the macro-generated documentation:
macro_rules! impl_foo {
($name:ident) => {
/// Returns a new `$name`.
fn myfoo() -> $name {
}
};
}
However, the variable won't be substituted. I also tried using the #[doc] attribute:
macro_rules! impl_foo {
($name:ident) => {
#[doc = concat!("Returns a new `", $name, "`.")]
fn myfoo() -> $name {
}
};
}
This one even fails to parse: unexpected token: 'concat'
This can be done using a recursive macro:
macro_rules! impl_foo {
($name:ident, $sname:expr) => {
#[doc = "Returns a new `"]
#[doc = $sname]
#[doc = "`."]
pub fn myfoo() -> $name {
42
}
};
($name:tt) => {
impl_foo!($name, stringify!($name));
};
}
impl_foo!(u32);
fn main() {
println!("Hello, world!");
}
Which renders as:
While the answer #mcarton gave does work perfectly fine for simple examples, it breaks a bit for more complicated ones. Rustdoc seems to insert spaces between the different doc attributes. The markdown processor strips them out most of the time, but sometimes, it transform them to spaces instead. Consider this example:
macro_rules! impl_foo {
($name:ident, $sname:expr) => {
#[doc = "You can call this as `myfoo("]
#[doc = $sname]
#[doc = ")`."]
pub fn myfoo(_: $name) -> $name {
42
}
};
($name:tt) => {
impl_foo!($name, stringify!($name));
};
}
impl_foo!(i32);
fn main() {
println!("Hello, world!");
}
This should generate the documentation "You can call this as myfoo(i32).", but in reality, it results in "You can call this as myfoo( i32 )." (note the additional spaces):
I'm not too sure whether my solution would have worked with the 2017 rustc back when the question was asked, but in modern Rust, this can be done by combining stringify! with concat!:
macro_rules! impl_foo {
($name:tt) => {
#[doc = concat!("You can call this as `myfoo(", stringify!($name), ")`.")]
pub fn myfoo(_: $name) -> $name {
42
}
};
}
impl_foo!(i32);
fn main() {
println!("Hello, world!");
}
This results in the documentation you want (so, without superfluous spaces):

How to choose between macros at compile time?

I have different versions of the same macro and I want to be able to choose one of them at compile time.
Here is the code I have:
macro_rules! macro_a {
($identifier:ident) => {
println!("A: {}", stringify!($identifier));
}
}
macro_rules! macro_b {
($identifier:ident) => {
println!("B: {}", stringify!($identifier));
}
}
macro_rules! macro_c {
($identifier:ident) => {
println!("C: {}", stringify!($identifier));
}
}
macro_rules! choose_macro {
(a) => {
const CHOSEN_MACRO: u32 = 1;
};
(b) => {
const CHOSEN_MACRO: u32 = 2;
};
(c) => {
const CHOSEN_MACRO: u32 = 3;
};
}
choose_macro!(c);
macro_rules! use_macro {
($identifier:ident) => {
match CHOSEN_MACRO {
1 => macro_a!($identifier),
2 => macro_b!($identifier),
3 => macro_c!($identifier),
_ => unreachable!(),
}
}
}
fn main() {
use_macro!(test);
}
This will print, as expected:
C: test
I wonder if there is a better way to doing this (with macro or attribute or anything else).
It is not clear if the macro is chosen at compile time here. Will Rust remove the match because it is on a constant?
Update: I prefer to choose the macro in the code, not using compiler flags. Also, I do not want to hide the macros that are not chosen: I want to be able to use them using their real name.
I would recommend using conditional compilation flags for something like this. See https://doc.rust-lang.org/book/conditional-compilation.html
In your case, it might look something like this:
#[cfg(feature = "feature_a")]
macro_rules! use_macro {
($identifier:ident) => {
println!("A: {}", stringify!($identifier));
}
}
#[cfg(feature = "feature_b")]
macro_rules! use_macro {
($identifier:ident) => {
println!("B: {}", stringify!($identifier));
}
}
#[cfg(feature = "feature_c")]
macro_rules! use_macro {
($identifier:ident) => {
println!("C: {}", stringify!($identifier));
}
}
fn main() {
use_macro!(test);
}
Then add the following to your Cargo.toml file:
[features]
feature_a = []
feature_b = []
feature_c = []
If you want it to print out "C: test" for example, then run the following:
cargo run --features feature_c

Getting nth/last value inside a hash of hashes

Im trying to make a hashes of hashes to uniquely identify the number that only comes under one set of levels. the hash structure looks something like this :
my %gh = {
'Test1' => {
'level1a' => {
'level2b' => {
'level3a' => {
'level4a' => {
'level5' => '63'
}
}
}
}
}
};
Can some please tell me what is the simplest way to traverse the hash so i can get the value 63.
I have been using
my $x = '';
foreach my $l0 (%gh){
foreach my $l1 (%{$l0}){
foreach my $l2 (%$l1){
foreach my $l3 (%{$l2}){
foreach my $l4 (%$l3){
foreach my $l5 (%{$l4}){
$x = $l5;
}
}
}
}
}
}
This process seems to be working fine . But i was just looking for something simpler and shorter;
Thanks in advance
This will work in your case (only hashes, and plain scalar value at the end)
sub hval {
my ($h) = #_;
return map { ref() ? hval($_) : $_ } values %$h;
}
my $gh = {
'Test1' => {
'level1a' => {
'level2b' => {
'level3a' => {
'level4a' => {
'level5' => '63'
}
}
}
}
}
};
my ($x) = hval($gh);
If you use a reference to a hash instead, here is one way:
use warnings;
use strict;
my $gh = {
'Test1' => {
'level1a' => {
'level2b' => {
'level3a' => {
'level4a' => {
'level5' => '63'
}
}
}
}
}
};
print $gh->{Test1}{level1a}{level2b}{level3a}{level4a}{level5}, "\n";
See also: perldoc perldsc and Data::Diver