如何匹配宏中的点并重建原始标记集?
How to match dot in macro and reconstruct original set of tokens?
我正在尝试编写一个宏来扩展它:
let res = log_request_response!(client.my_call("friend".to_string(), 5));
进入这个:
let res = {
debug!("Request: {}", args_separated_by_commas);
let res = client.my_call("friend".to_string(), 5);
debug!("Response: {}", res);
res
};
到目前为止我的尝试是这样的:
#[macro_export]
macro_rules! log_request_response_to_scuba {
($($client:ident)?.$call:ident($($arg:expr),*);) => {
let mut s = String::new();
$(
{
s.push_str(&format!("{:?}, ", $arg));
}
)*
s.truncate(s.len() - 2);
debug!("Request: {}", s);
// Somehow reconstruct the entire thing with res = at the start.
debug!("Response: {}", res);
res
};
}
但是编译失败:
error: macro expansion ignores token `{` and any following
--> src/main.rs:10:13
|
10 | {
| ^
...
39 | let res = log_request_response_to_scuba!(client.my_call(hey, 5));
| ------------------------------------------------------ caused by the macro expansion here
|
= note: the usage of `log_request_response_to_scuba!` is likely invalid in expression context
如果我删除 client
和 call
匹配之间的 .
,它会抛出一个关于不明确匹配的不同错误(这是有道理的)。
所以我的第一个具体问题是如何匹配点?对我来说,这个匹配看起来是正确的,但显然不是。
除此之外,如果能帮助我制作一个能满足我要求的宏,那就太好了。如果它是一个正则表达式,我只想要这个:
.*\((.*)\).*
我只是捕获括号内的内容并将它们拆分。然后我使用第0个捕获组来获取整个东西。
谢谢!
错误消息不是因为您匹配的点不知何故错误,而是因为您没有 returning 表达式。你想要 return 这个:
{
debug!("Request: {}", args_separated_by_commas);
let res = client.my_call("friend".to_string(), 5);
debug!("Response: {}", res);
res
};
但是,您的宏目前 return 更类似于此:
debug!("Request: {}", args_separated_by_commas);
let res = client.my_call("friend".to_string(), 5);
debug!("Response: {}", res);
res
注意缺少的大括号。通过将完整的转录器部分括在大括号中,可以很容易地解决这个问题。
我不确定为什么 client
在您的匹配器中是可选的。我假设您希望有选择地允许宏的用户对某个变量调用函数或方法。那是对的吗?如果是,那么您的代码目前不允许这样做——它匹配 client.my_call(...)
以及 .some_function(...)
,但不匹配 some_function(...)
(请注意从开头删除的 space)。为了做你想做的事,你可以在 $variable:ident$(.$field:ident)?
上匹配——注意这里的点也是可选的——或者更好的是 $variable:ident$(.$field:ident)*
允许在一个 loval 变量的字段的字段上调用方法(所以,像 variable.sub_struct.do_something()
.
带有一些示例的结果代码:
macro_rules! log_request_response {
($variable:ident$(.$field:ident)*($($arg:expr),*)) => {
{
let mut s = String::new();
$(
{
s.push_str(&format!("{:?}, ", $arg));
}
)*
s.truncate(s.len() - 2);
// using println! here because I don't want to set up logging infrastructure
println!("Request: {}", s);
let res = $variable$(.$field)*($($arg),*);
println!("Response: {}", res);
res
}
};
}
fn test_func(_: String, i: i32) -> i32 {
i
}
struct TestStruct;
impl TestStruct {
fn test_method(&self, _: String, i: i32) -> i32 {
i
}
}
fn main() {
let _ = log_request_response!(TestStruct.test_method("friend".to_string(), 5));
let _ = log_request_response!(test_func("friend".to_string(), 5));
}
我正在尝试编写一个宏来扩展它:
let res = log_request_response!(client.my_call("friend".to_string(), 5));
进入这个:
let res = {
debug!("Request: {}", args_separated_by_commas);
let res = client.my_call("friend".to_string(), 5);
debug!("Response: {}", res);
res
};
到目前为止我的尝试是这样的:
#[macro_export]
macro_rules! log_request_response_to_scuba {
($($client:ident)?.$call:ident($($arg:expr),*);) => {
let mut s = String::new();
$(
{
s.push_str(&format!("{:?}, ", $arg));
}
)*
s.truncate(s.len() - 2);
debug!("Request: {}", s);
// Somehow reconstruct the entire thing with res = at the start.
debug!("Response: {}", res);
res
};
}
但是编译失败:
error: macro expansion ignores token `{` and any following
--> src/main.rs:10:13
|
10 | {
| ^
...
39 | let res = log_request_response_to_scuba!(client.my_call(hey, 5));
| ------------------------------------------------------ caused by the macro expansion here
|
= note: the usage of `log_request_response_to_scuba!` is likely invalid in expression context
如果我删除 client
和 call
匹配之间的 .
,它会抛出一个关于不明确匹配的不同错误(这是有道理的)。
所以我的第一个具体问题是如何匹配点?对我来说,这个匹配看起来是正确的,但显然不是。
除此之外,如果能帮助我制作一个能满足我要求的宏,那就太好了。如果它是一个正则表达式,我只想要这个:
.*\((.*)\).*
我只是捕获括号内的内容并将它们拆分。然后我使用第0个捕获组来获取整个东西。
谢谢!
错误消息不是因为您匹配的点不知何故错误,而是因为您没有 returning 表达式。你想要 return 这个:
{
debug!("Request: {}", args_separated_by_commas);
let res = client.my_call("friend".to_string(), 5);
debug!("Response: {}", res);
res
};
但是,您的宏目前 return 更类似于此:
debug!("Request: {}", args_separated_by_commas);
let res = client.my_call("friend".to_string(), 5);
debug!("Response: {}", res);
res
注意缺少的大括号。通过将完整的转录器部分括在大括号中,可以很容易地解决这个问题。
我不确定为什么 client
在您的匹配器中是可选的。我假设您希望有选择地允许宏的用户对某个变量调用函数或方法。那是对的吗?如果是,那么您的代码目前不允许这样做——它匹配 client.my_call(...)
以及 .some_function(...)
,但不匹配 some_function(...)
(请注意从开头删除的 space)。为了做你想做的事,你可以在 $variable:ident$(.$field:ident)?
上匹配——注意这里的点也是可选的——或者更好的是 $variable:ident$(.$field:ident)*
允许在一个 loval 变量的字段的字段上调用方法(所以,像 variable.sub_struct.do_something()
.
带有一些示例的结果代码:
macro_rules! log_request_response {
($variable:ident$(.$field:ident)*($($arg:expr),*)) => {
{
let mut s = String::new();
$(
{
s.push_str(&format!("{:?}, ", $arg));
}
)*
s.truncate(s.len() - 2);
// using println! here because I don't want to set up logging infrastructure
println!("Request: {}", s);
let res = $variable$(.$field)*($($arg),*);
println!("Response: {}", res);
res
}
};
}
fn test_func(_: String, i: i32) -> i32 {
i
}
struct TestStruct;
impl TestStruct {
fn test_method(&self, _: String, i: i32) -> i32 {
i
}
}
fn main() {
let _ = log_request_response!(TestStruct.test_method("friend".to_string(), 5));
let _ = log_request_response!(test_func("friend".to_string(), 5));
}