过程宏API
1extern crate proc_macro;
2use proc_macro::TokenStream;
3
4#[proc_macro]
5pub fn foo(body: TokenStream) -> TokenStream {
6 return body
7}
1use my_proc_macro::*;
2 foo! {
3 fn hello() {
4 println!("Hello, world!");
5 }
6 }
7
8fn main() {
9 hello();
10}
1extern crate proc_macro;
2use proc_macro::TokenStream;
3
4#[proc_macro_attribute]
5pub fn baz(
6 attr: TokenStream,
7 item: TokenStream
8) -> TokenStream {
9 return item
10}
11…
12use my_proc_macro::*;
13#[baz]
14fn hello() {
15 println!("Hello, world!");
16}
17fn main() {
18 hello();
19}
Token是一个特定类型的字符串,在宏主体解析期间给它赋值。有三种类型的Token:标识符、标点符号和文字。
1struct TokenStream(Vec<TokenTree>);
2enum TokenTree {
3 Ident(Ident),
4 Punct(Punct),
5 Literal(Literal),
6 ...
7}
1#[proc_macro]
2pub fn foo(body: TokenStream) -> TokenStream {
3 for tt in body.into_iter() {
4 match tt {
5 TokenTree::Ident(_) => eprintln!("Ident"),
6 TokenTree::Punct(_) => eprintln!("Punct"),
7 TokenTree::Literal(_) => eprintln!("Literal"),
8 _ => {}
9 }
10 }
11 return TokenStream::new();
12}
1enum TokenTree {
2 Ident(Ident),
3 Punct(Punct),
4 Literal(Literal),
5 Group(Group),
6}
当解析器遇到括号时,就会出现Group。组成Group的括号可以是圆的、方的或大括号。
foo!( foo { 2 + 2 } bar );
1#[proc_macro]
2pub fn foo(body: TokenStream) -> TokenStream {
3 return [
4 TokenTree::Ident(Ident::new("foo", Span::mixed_site())),
5 TokenTree::Group(Group::new(Delimiter::Parenthesis, body))
6 ].into_iter().collect();
7}
fn main() {
foo!(1, 2);
}
1#[proc_macro]
2pub fn foo(body: String) -> String {// this doesn't work!
3 format!("foo({})", body)
4}
Span包含关于Token在原始代码中的位置信息,这对于编译器正确地显示错误是必要的。
error[E0308]: mismatched types
--> src/main.rs:26:13
|
26 | foo!(1, "");
| ^^ expected `i32`, found `&str`
这是可能的,因为我们将整个TokenStream传递到扩展中,每个Token包含一个Span。Span通知编译器,这个特定的代码片段应该映射到宏展开的那个代码片段。通过这种方式,编译器可以显示在编译扩展代码期间发生的错误。