[英]rust procedural macros, how to find an identifier?
I am breaking my head against the wall trying to figure this one out.我正把头撞在墙上,试图弄清楚这一点。
What I want:我想要的是:
A procedural macro that replaces all tagged instances of an identifier with a pluralized version of the same word.一个程序宏,它用同一个单词的复数形式替换标识符的所有标记实例。
I have the macro itself set up:我已经设置了宏本身:
extern crate proc_macro;
use syn::{parse_macro_input, DataEnum, DataUnion, DeriveInput, FieldsNamed, FieldsUnnamed};
use regex::Regex;
extern crate quote;
use proc_macro::TokenStream;
#[proc_macro]
pub fn pluralize(input: TokenStream) -> TokenStream
{
//let DeriveInput { ident, data, .. } = parse_macro_input!(input);
return input;
}
I have done tests and it is getting called properly.我已经完成了测试,并且可以正确调用它。 I also have a function that uses regular expressions to find the tagged portions where replacement needs to happen.我还有一个 function 使用正则表达式来查找需要替换的标记部分。
let input_str = input.to_string();
let re_token = Regex::new(r"\[<.*s\s*>\]").unwrap();
let re_name = Regex::new(r"\w+").unwrap();
for m in re_token.find_iter(&input_str)
{
let name = re_name.find(m.as_str()).unwrap();
println!("{}", m.as_str());
}
All I need to make this work is to get the identifier from the TokenStream, and I cannot for the life of me figure out how.完成这项工作所需要做的就是从 TokenStream 中获取标识符,而我终其一生都无法弄清楚如何去做。
You can use syn
's visit_mut
.您可以使用syn
的visit_mut
。 Implement the VisitMut
trait and override visit_ident_mut()
.实现VisitMut
特征并覆盖visit_ident_mut()
。
However, this has two serious disadvantages:但是,这有两个严重的缺点:
[<... >]
which are not valid Rust constructs in this way.很难以这种方式识别无效的 Rust 构造的构造,例如[<... >]
。 And also, it pulls the heavy syn
as a dependency, and even requires the full
feature (and visit-mut
).而且,它把沉重的syn
作为依赖,甚至需要full
的功能(和visit-mut
)。
A better way is to use TokenStream
directly.更好的方法是直接使用TokenStream
。 Look for Group
s delimited with []
, and if they contain <
and >
as first and last tokens pluralize all identifiers.查找以[]
分隔的Group
,如果它们包含<
和>
作为第一个和最后一个标记,则将所有标识符复数。 Recurse for groups.递归组。 This can be something like:这可能是这样的:
fn process_token_stream(tokens: TokenStream, pluralize: bool) -> TokenStream {
tokens
.into_iter()
.map(|token_tree| match token_tree {
TokenTree::Ident(ident) if pluralize => {
TokenTree::Ident(Ident::new(&(ident.to_string() + "s"), ident.span()))
}
TokenTree::Group(group) => {
let mut pluralize = pluralize;
let mut stream = VecDeque::from_iter(group.stream());
let mut delimiter = group.delimiter();
if group.delimiter() == Delimiter::Bracket {
if let (Some(TokenTree::Punct(first)), Some(TokenTree::Punct(last))) =
(stream.front(), stream.back())
{
if first.as_char() == '<' && last.as_char() == '>' {
pluralize = true;
stream.pop_front();
stream.pop_back();
delimiter = Delimiter::None;
}
}
}
let mut new_group = Group::new(
delimiter,
process_token_stream(TokenStream::from_iter(stream), pluralize),
);
new_group.set_span(group.span());
TokenTree::Group(new_group)
}
other => other,
})
.collect()
}
Playground . 游乐场。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.