I'm currently looking into Rust macros and I can not find any detailed documentation on repetitions. I would like to create macros with optional parameters. This would be my idea:
macro_rules! single_opt {
($mand_1, $mand_2, $($opt:expr)* ) =>{
match $opt {
Some(x) => println!("1. {} 2. {}, 3. {}", $mand_1, $mand_2, x);
None => single_opt!($mand_1, $mand_2, "Default");
}
}
}
fn main() {
single_opt!(4,4);
}
This example seems to be outdated, since I can not compile it. The Rust book mentions this topic just very briefly. How do I get this example to work?
NOTE: Since this answer was written, Rust has gained the ability to express optional elements in a pattern (stabilized in Rust 1.32.0) using the syntax $(tokens)?.
The first edition of the Rust book has a rather long chapter on macros, but the section on repetitions is a bit shy on examples...
There are several ways to handle optional arguments in macros. If you have an optional argument that can only occur once, then you shouldn't use repetitions: you should instead define multiple patterns in your macro, like this:
macro_rules! single_opt {
($mand_1:expr, $mand_2:expr) => {
single_opt!($mand_1, $mand_2, "Default")
};
($mand_1:expr, $mand_2:expr, $opt:expr) => {
println!("1. {} 2. {}, 3. {}", $mand_1, $mand_2, $opt)
};
}
fn main() {
single_opt!(4, 4);
}
If you want to allow an arbitrary number of arguments, then you need repetition. Your original macro doesn't work because you put the comma outside the repetition, so you'd have to invoke the macro as single_opt!(4,4,);. See How to allow optional trailing commas in macros? for a related case.
If you have a fixed number of arguments followed by a repetition, you can put the comma inside the repetition as the first token:
macro_rules! single_opt {
($mand_1:expr, $mand_2:expr $(, $opt:expr)*) => {
println!("1. {} 2. {}, 3. {}", $mand_1, $mand_2, $($opt),*)
};
}
However, it doesn't work in this specific case:
error: 3 positional arguments in format string, but there are 2 arguments
--> src/main.rs:3:22
|
3 | println!("1. {} 2. {}, 3. {}", $mand_1, $mand_2, $($opt),*)
| ^^ ^^ ^^
...
8 | single_opt!(4, 4);
| ------------------
| |
| in this macro invocation
| in this macro invocation
| in this macro invocation
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
So we'll have to go back to defining two patterns:
macro_rules! single_opt {
($mand_1:expr, $mand_2:expr) => {
single_opt!($mand_1, $mand_2, "Default")
};
($mand_1:expr, $mand_2:expr, $($opt:expr),*) => {
{
println!("1. {} 2. {}", $mand_1, $mand_2);
$(
println!("opt. {}", $opt);
)*
}
};
}
fn main() {
single_opt!(4, 4, 1, 2);
}
A repetition takes the form $( PATTERN ) SEPARATOR COUNT, where PATTERN is the pattern you want to repeat, SEPARATOR is an optional token that separates each repetition (here, it's ,) and COUNT is either * for "zero or more occurrences" or + for "one or more occurrences".
Then, in the macro expansion, we need a repetition block to be able to access $opt. The syntax is exactly the same, but note that the separator doesn't have to be the same (here, there's no separator in the expansion).
Related
I am trying to create some kind of a dut_error wrapper. Something that will take some arguments and construct them in a specific way to a dut_error.
I can't use a method to replace the calls to dut_error because to my understanding after check that ... then ... else can only come a dut_error (or dut_errorf). And indeed if I try to do something like:
my_dut_error(arg1: string, arg2: string) is {
dut_error("first argument is ", arg, " and second argument is ", arg2);
};
check that FALSE else my_dut_error("check1", "check2");
I get an error:
*** Error: Unrecognized exp
[Unrecognized expression 'FALSE else my_dut_error("check1", "check2")']
at line x in main.e
check that FALSE else my_dut_error("check1", "check2");
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
So I thought about defining a macro to simply do a textual replace from my wrapper to an actual dut_error:
define <my_dut_error'exp> "my_dut_error(<arg1'name>, <arg2'name>)" as {
dut_error("first argument is ", <arg1'name>, " and second argument is ", <arg2'name>)
};
But got the same error.
Then I read about the preprocessor directive #define so tried:
#define my_dut_error(arg1, arg2) dut_error("first argument is ", arg, " and second argument is ", arg2)
But that just gave a syntax error.
How can I define a pre-compiled textual replacement macro that takes arguments, similar to C?
The reason I want to do that is to achieve some sort of an "interface" to the dut_error so all errors have a consistent structure. This way, different people writing different errors will only pass the arguments necessary by that interface and internally an appropriate message will be created.
not sure i understood what you want to do in the wrapper, but perhaps you can achieve what you want by using the dut_error_struct.
it has set of api, which you can use as hooks (do something when the error is caught) and to query about the specific error.
for example:
extend dut_error_struct {
pre_error() is also {
if source_method_name() == "post_generate" and
source_struct() is a BLUE packet {
out("\nProblem in generation? ", source_location());
// do something for error during generation
};
write() is first {
if get_mesage() ~ "AHB Error..." {
ahb_monitor::increase_errors();
};
};
};
dut_error accepts one parameter, one string. but you can decide of a "separator", that will define two parts to the message.
e.g. - instruct people to write "XXX" in the message, before "first arg" and "second arg".
check that legal else dut_error("ONE thing", "XXX", "another thing");
check that x < 7 else dut_error("failure ", "XXX", "of x not 7 but is ", x);
extend dut_error_struct {
write() is first {
var message_parts := str_split(get_message(), "XXX");
if message_parts.size() == 2 {
out ("First part of message is ", message_parts[0],
"\nand second part of message is ", message_parts[1]
);
};
};
I could get pretty close to what I want using the dut_errorf method combined with a preprocessor directive defining the format string:
#define DUT_FORMAT "first argument is %s and second argument is %s"
check that FALSE else dut_errorf(DUT_FORMAT, "check1", "check2");
but I would still prefer a way that doesn't require this DUT_FORMAT directive and instead uses dut_error_struct or something similar.
I have following custom hook
function useConstant(fn) {
const ref = React.useRef()
if (!ref.current) {
ref.current = fn()
}
return ref.current
}
and it seems quite hard to port this to reasonml, I have to use type cast twice, what's the ideal way?
external toAny: 'a => 'b = "%identity";
external toBool: 'a => bool = "%identity";
let useConstant = (fn: unit => 'a) => {
let ref: React.Ref.t('a) = toAny(React.useRef());
if (!toBool(React.Ref.current(ref))) {
React.Ref.setCurrent(ref, fn());
};
React.Ref.current(ref);
};
If I understand the purpose of the hook correctly, it's really just a reimplementation of React.useMemo. But for the sake of learning, here's an implementation that should work.
let useLazy = (fn: unit => 'a): 'a => {
let ref = React.useRef(None);
switch (React.Ref.current(ref)) {
| Some(value) => value
| None =>
let value = fn();
React.Ref.setCurrent(ref, Some(value));
value;
};
};
It uses the option type, which is specifically designed for cases like this. If there's no value, we represent that using options None value, and if there is a value we use Some. Instead of using if with JavaScript's semantically unclear concept of truthiness, we pattern match on the option using switch to find that it's None and the value needs to be computed, or Some to get at the value.
The use of option and pattern matching is really common in Reason code, so it's one you should really try to understand using the links provided above for more details if needed.
Note that you could also have used Lazy for this. But that's far less commonly used and therefore also much less useful to learn.
I'm only beginning to study classes, so I don't understand the basics.
I want a method to construct regex using attributes of the object:
class TEST {
has Str $.str;
method reg {
return
rx/
<<
<[abc]> *
$!str
<!before foo>
/;
}
}
my $var = TEST.new(str => 'baz');
say $var.reg;
When trying to run this program, I get the following error message:
===SORRY!=== Error while compiling /home/evb/Desktop/p6/e.p6
Attribute $!str not available inside of a regex, since regexes are methods on Cursor.
Consider storing the attribute in a lexical, and using that in the regex.
at /home/evb/Desktop/p6/e.p6:11
------> <!before foo>⏏<EOL>
expecting any of:
infix stopper
So, what's the right way to do that?
Looks like this would work:
class TEST {
has Str $.str;
method reg {
my $str = $.str;
return
regex {
<<
<[abc]> *
$str
<!before foo>
}
}
}
my $var = TEST.new(str => 'baz');
say $var.reg;
say "foo" ~~ $var.reg;
say "<<abaz" ~~ $var.reg
You are returning an anonymous regex, which can be used as an actual regex, as it's done in the last two sentences.
Using EVAL solved my problem. So, I wonder, whether there are any drawbacks in this method.
class TEST {
has Str $.str;
method reg {
return
"rx/
<<
<[abc]> *
$!str
<!before foo>
/".EVAL;
}
}
my $var = TEST.new(str => 'baz');
say "abaz" ~~ $var.reg; # abaz
say "cbazfoo" ~~ $var.reg; # Nil
I'm trying to make a macro that I can call in the following manner:
mactest!(some::Path[1, 2, AnotherName[3, 4]])
Which would be equivalent to the following:
make_result(
"some::Path",
1.convert(),
2.convert(),
make_result(
"AnotherName",
3.convert(),
4.convert()
)
)
where convert is some trait that will be implemented for a bunch of types. (convert and make_result has the same result type).
This is as far as I've come:
// Note: u32 is used as an example result type.
// The real code attempts to create a more complicated object.
trait Foo {
fn convert(&self) -> u32;
}
fn make_result(name: &str, data: Vec<u32>) -> u32 {
// This example ignores name and makes a meaningless result
data.iter().fold(0,|a, &b| a + b)
}
#[macro_export]
macro_rules! mactest {
( [ $($inner:expr),* ] ) => {{
let mut result = Vec::new();
$(
// Process each element.
result.push(mactest!($inner));
)*
result
}};
($name:path [ $($inner:tt),* ] ) => {
make_result(stringify!($name), mactest!([$($inner),*]))
};
($name:ident [ $($inner:tt),* ] ) => {
make_result(stringify!($name), mactest!([$($inner),*]))
};
// Process single value. This is never matched?
($x:expr) => {
$x.convert()
};
}
The first matching branch of the macro is supposed to match each element of a list to either the path/ident[items] or the single item .convert branch at the end. But the final branch is never reached, with rust complaining error: expected ident, found '1' when single items enter the macro, i.e. mactest!(1).
My reasoning as a beginner rust user is that the macro has four patterns: [expr*], path[tt*], ident[tt*] and expr. When I pass something like 1 into the macro, I don't see why any of the above patterns should match/interfere.
Can someone explain why this doesn't work? Is there a workaround to get the intended result?
macro rules are tried by starting with the first one and going down from there. So if you want to prevent your other rules from triggering in special cases, you need to put the special case rule first.
Try it out in the playground
This works:
my $r = someSubroutine( map { ( 0 => $_ ) } #hosts)
This does not work, giving a syntax error:
my $r = someSubroutine( map { 0 => $_ } #hosts)
What I think I understand is that the { } after the map amounts to a closure or anonymous subroutine.
But if I put a "value, value" at the end of a normal subroutine, it will return a list of those values. If I use this brevity with the map, it is a syntax error.
First of all, this is a very strange statement. The list that map produces will look like
0, $hosts[0], 0, $hosts[1], 0, $hosts[2], ...
so it's useless for assignment to a hash as it would be the same as
my %hash = (0 => $hosts[-1])
map will accept either a BLOCK (which is what you're using) or a simple EXPRESSION for its first parameter. The problem here is that { 0 => $_ } looks very like an anonymous hash with a single element, which is an EXPRESSION, and that is what the parser guesses it is. An EXPRESSION requires a comma after it, before the second parameter, but when perl gets to the closing brace in map { 0 => $_ } #hosts it doesn't find one so it has to throw a syntax error as it is too far to backtrack to the opening brace and assume a block instead
The documentation puts it like this
{ starts both hash references and blocks, so map { ... could be either the start of map BLOCK LIST or map EXPR, LIST. Because Perl doesn't look ahead for the closing } it has to take a guess at which it's dealing with based on what it finds just after the {. Usually it gets it right, but if it doesn't it won't realize something is wrong until it gets to the } and encounters the missing (or unexpected) comma. The syntax error will be reported close to the }, but you'll need to change something near the { such as using a unary + or semicolon to give Perl some help
The solution is to disambiguate it as you discovered. Any of these will work
map +( 0 => $_ ), #hosts
map(( 0 => $_ ), #hosts)
map { +0 => $_ } #hosts
map { ( 0 => $_ ) } #hosts
map { ; 0 => $_ } #hosts
map has two syntax:
map BLOCK LIST e.g. map { f() } g()
map EXPR, LIST e.g. map f(), g()
When Perl encounters map, it needs to determine which syntax was used. Let's say the first token after map is {. That's the start of a BLOCK, right? Hold on! Expressions can start with { too!
my $hash_ref = { key => 'val' };
The grammar is ambiguous. Perl has to "guess" which syntax you are using. Perl looks ahead at the next token to help guess, but sometimes it guesses incorrectly nonetheless. This is one of those cases.
The following are the standard workarounds for this:
map {; ... } LIST # Force Perl to recognize the curly as the start of a BLOCK
map +{ ... }, LIST # Force Perl to recognize the curly as the start of a hash constructor
; can't be part of a hash constructor, so the { can only start a BLOCK.
+ necessarily starts an EXPR (and not a BLOCK). It's an operator that does nothing but help in situations like this.
For example,
map {; +{ $row->{id} => $row->{val} } } #rows
This is described in perldoc on map: http://perldoc.perl.org/functions/map.html
In short you should use little helper like parens or +-symbol so perl will be able to parse {...} construct correctly:
my $r = someSubroutine( map { + 0 => $_ } #hosts)