syn,quote
quote
This crate provides the quote! macro for turning Rust syntax tree data structures into tokens of source code. The idea of quasi-quoting is that we write code that we treat as data.
基本类型
#![allow(unused)] fn main() { let u = 12usize; let i = -38i32; let f = 3.1415926f64; let c = '\r'; let s = "\r\nhello\tworld\r\n"; assert_eq!( "12usize - 38i32 3.1415926f64 '\\r' \"\\r\\nhello\\tworld\\r\\n\"", quote! { u #i #f #c #s } .to_string() ); macro_rules! m { ($literal:literal) => { quote!($literal) }; } let expected = "- false"; assert_eq!(expected, m!(-false).to_string()); }
重复、循环
#![allow(unused)] fn main() { let f0 = format_ident!("World"); let f1 = format_ident!("Hello{x}", x = f0); assert_eq!("HelloWorld",f1.to_string()); // ident not impl f64 let f2 = format_ident!("Hello{x}", x = 4050usize); assert_eq!("Hello4050",f2.to_string()); let num: u32 = 10; let octal = format_ident!("Id_{:o}", num); assert_eq!(octal, "Id_12"); let binary = format_ident!("Id_{:b}", num); assert_eq!(binary, "Id_1010"); let lower_hex = format_ident!("Id_{:x}", num); assert_eq!(lower_hex, "Id_a"); let upper_hex = format_ident!("Id_{:X}", num); assert_eq!(upper_hex, "Id_A"); }
注释
#![allow(unused)] fn main() { let token1 = quote!{ /* comment */ }; let token2 = quote!{ // comment }; assert_eq!(token1.to_string(),token2.to_string()) }
syn
Syn is a parsing library for parsing a stream of Rust tokens into a syntax tree of Rust source code.
syn::File
#![allow(unused)] fn main() { let mut file = File::open(&filename).expect("Unable to open file"); let mut src = String::new(); file.read_to_string(&mut src).expect("Unable to read file"); let ast = syn::parse_file(&src).unwrap(); if let Some(shebang) = ast.shebang { println!("{}", shebang); } println!("{} items", ast.items.len()); }
DeriveInput for proc_macro_derive
#![allow(unused)] fn main() { #[proc_macro_derive(HeapSize)] pub fn derive_heap_size(input: proc_macro::TokenStream) -> proc_macro::TokenStream { // Parse the input tokens into a syntax tree. let input = parse_macro_input!(input as DeriveInput); // Used in the quasi-quotation below as `#name`. let name = input.ident; // Add a bound `T: HeapSize` to every type parameter T. let generics = add_trait_bounds(input.generics); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); // Generate an expression to sum up the heap size of each field. let sum = heap_size_sum(&input.data); let expanded = quote! { // The generated impl. ... }; // Hand the output tokens back to the compiler. proc_macro::TokenStream::from(expanded) } }
fold 可以在语法转换时hook
#![allow(unused)] fn main() { impl Fold for Args { // 处理expr类型时 fn fold_expr(&mut self, e: Expr) -> Expr { match e { Expr::Assign(e) => { if self.should_print_expr(&e.left) { self.assign_and_print(*e.left, &e.eq_token, *e.right) } else { Expr::Assign(fold::fold_expr_assign(self, e)) } } Expr::Binary(e) if is_assign_op(e.op) => { if self.should_print_expr(&e.left) { self.assign_and_print(*e.left, &e.op, *e.right) } else { Expr::Binary(fold::fold_expr_binary(self, e)) } } _ => fold::fold_expr(self, e), } } // 处理stmt类型 fn fold_stmt(&mut self, s: Stmt) -> Stmt { match s { Stmt::Local(s) => { if s.init.is_some() && self.should_print_pat(&s.pat) { self.let_and_print(s) } else { Stmt::Local(fold::fold_local(self, s)) } } _ => fold::fold_stmt(self, s), } } } }