// Copyright 2023 The Pigweed Authors // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. use proc_macro::TokenStream; use pw_format::macros::{ generate, Arg, FormatAndArgsFlavor, FormatMacroGenerator, FormatParams, PrintfFormatStringParser, Result, }; use quote::quote; use syn::{ parse::{Parse, ParseStream}, parse_macro_input, Expr, Token, }; type TokenStream2 = proc_macro2::TokenStream; // Declare a struct to hold our proc macro arguments. #[derive(Debug)] struct MacroArgs { prefix: Expr, // Select printf style format strings. format_and_args: FormatAndArgsFlavor, } // Implement `Parse` for our argument struct. impl Parse for MacroArgs { fn parse(input: ParseStream) -> syn::parse::Result { // Our prefix argument comes first argument and ends with a `,`. let prefix: Expr = input.parse()?; input.parse::()?; // Prase the remaining arguments as a format string and arguments. let format_and_args: FormatAndArgsFlavor<_> = input.parse()?; Ok(MacroArgs { prefix, format_and_args, }) } } // Our generator struct needs to track the prefix as well as the code // fragments we've generated. struct Generator { prefix: Expr, code_fragments: Vec, } impl Generator { pub fn new(prefix: Expr) -> Self { Self { prefix, code_fragments: Vec::new(), } } } // This toy example implements the generator by calling `format!()` at runtime. impl FormatMacroGenerator for Generator { // Wrap all our fragments in boilerplate and return the code. fn finalize(self) -> Result { // Create locally scoped alias so we can refer to them in `quote!()`. let prefix = self.prefix; let code_fragments = self.code_fragments; Ok(quote! { { // Initialize the result string with our prefix. let mut result = String::new(); result.push_str(#prefix); // Enumerate all our code fragments. #(#code_fragments);*; // Return the resulting string result } }) } // Handles a string embedded in the format string. This is different from // `string_conversion` which is used to handle a string referenced by the // format string (i.e. "%s'"). fn string_fragment(&mut self, string: &str) -> Result<()> { self.code_fragments.push(quote! { result.push_str(#string); }); Ok(()) } // This example ignores display type and width. fn integer_conversion( &mut self, _params: &FormatParams, _signed: bool, _type_width: u8, // in bits expression: Arg, ) -> Result<()> { self.code_fragments.push(quote! { result.push_str(&format!("{}", #expression)); }); Ok(()) } fn string_conversion(&mut self, expression: Arg) -> Result<()> { self.code_fragments.push(quote! { result.push_str(&format!("{}", #expression)); }); Ok(()) } fn char_conversion(&mut self, expression: Arg) -> Result<()> { self.code_fragments.push(quote! { result.push_str(&format!("{}", #expression)); }); Ok(()) } fn untyped_conversion(&mut self, expression: Arg, _params: &FormatParams) -> Result<()> { self.code_fragments.push(quote! { result.push_str(&format!("{}", #expression)); }); Ok(()) } } // Lastly we declare our proc macro. #[proc_macro] pub fn example_macro(tokens: TokenStream) -> TokenStream { // Parse our proc macro arguments. let input = parse_macro_input!(tokens as MacroArgs); // Create our generator. let generator = Generator::new(input.prefix); // Call into `generate()` to handle the code generation. match generate(generator, input.format_and_args.into()) { Ok(token_stream) => token_stream.into(), Err(e) => e.to_compile_error().into(), } }