如何匹配宏中的点并重建原始标记集?

Dan*_*ous 2 rust rust-macros

我正在尝试编写一个宏来扩展它:

let res = log_request_response!(client.my_call("friend".to_string(), 5));
Run Code Online (Sandbox Code Playgroud)

进入这个:

let res = {
    debug!("Request: {}", args_separated_by_commas);
    let res = client.my_call("friend".to_string(), 5);
    debug!("Response: {}", res);
    res
};
Run Code Online (Sandbox Code Playgroud)

到目前为止我的尝试是这样的:

#[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
    };
}
Run Code Online (Sandbox Code Playgroud)

但这无法编译:

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
Run Code Online (Sandbox Code Playgroud)

如果我删除和匹配.之间的内容,它会引发有关模糊匹配的不同错误(这是有道理的)。clientcall

所以我的第一个具体问题是如何匹配点?对我来说,这场比赛看起来是正确的,但显然不是。

除此之外,任何帮助制作一个可以实现我想要的功能的宏都会很棒。如果它是正则表达式我只想这样:

.*\((.*)\).*
Run Code Online (Sandbox Code Playgroud)

我只是捕获括号内的内容并将它们分开。然后我使用第 0 个捕获组来获取整个内容。

谢谢!

sk_*_*ant 5

错误消息并不是因为您以某种方式错误地匹配了点,而是因为您没有返回表达式。你想返回这个:

\n
{\n    debug!("Request: {}", args_separated_by_commas);\n    let res = client.my_call("friend".to_string(), 5);\n    debug!("Response: {}", res);\n    res\n};\n
Run Code Online (Sandbox Code Playgroud)\n

但是,您的宏当前返回的内容与此更类似:

\n
debug!("Request: {}", args_separated_by_commas);\nlet res = client.my_call("friend".to_string(), 5);\ndebug!("Response: {}", res);\nres\n
Run Code Online (Sandbox Code Playgroud)\n

请注意缺少的花括号。通过将完整的转录器部分括在大括号中,可以很容易地解决这个问题。

\n
\n

我不确定为什么client在你的匹配器中是可选的。我假设您希望有选择地允许宏的用户对某个变量调用函数或方法。那是对的吗?如果是,那么您的代码当前不允许 \xe2\x80\x93 与client.my_call(...)以及匹配.some_function(...),但不允许some_function(...)(请注意从开头删除的空格)。要执行您想要的操作,您可以匹配$variable:ident$(.$field:ident)?\xe2\x80\x93 请注意,这里的点也是可选的 \xe2\x80\x93 ,甚至更好地$variable:ident$(.$field:ident)*允许在 loval 变量的字段的字段上调用方法(所以,类似的东西variable.sub_struct.do_something()

\n
\n

生成的代码带有一些示例:

\n
macro_rules! log_request_response {\n    ($variable:ident$(.$field:ident)*($($arg:expr),*)) => {\n        {\n            let mut s = String::new();\n            $(\n                {\n                    s.push_str(&format!("{:?}, ", $arg));\n                }\n            )*\n            s.truncate(s.len() - 2);\n            // using println! here because I don\'t want to set up logging infrastructure\n            println!("Request: {}", s);\n            let res = $variable$(.$field)*($($arg),*);\n            println!("Response: {}", res);\n            res\n        }\n    };\n}\n\nfn test_func(_: String, i: i32) -> i32 {\n    i\n}\n\nstruct TestStruct;\n\nimpl TestStruct {\n    fn test_method(&self, _: String, i: i32) -> i32 {\n        i\n    }\n}\n\nfn main() {\n    let _ = log_request_response!(TestStruct.test_method("friend".to_string(), 5));\n    let _ = log_request_response!(test_func("friend".to_string(), 5));\n}\n
Run Code Online (Sandbox Code Playgroud)\n

操场

\n