// 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 proc_macro2::Ident; use pw_format::macros::{ generate, generate_core_fmt, generate_printf, Arg, CoreFmtFormatMacroGenerator, CoreFmtFormatStringParser, FormatAndArgsFlavor, FormatMacroGenerator, FormatParams, FormatStringParser, PrintfFormatMacroGenerator, PrintfFormatStringFragment, PrintfFormatStringParser, Result, }; use quote::{quote, ToTokens}; use syn::parse_macro_input; type TokenStream2 = proc_macro2::TokenStream; // Generator for testing `generate()`. struct TestGenerator { code_fragments: Vec, } impl TestGenerator { pub fn new() -> Self { Self { code_fragments: Vec::new(), } } } impl FormatMacroGenerator for TestGenerator { fn finalize(self) -> Result { let code_fragments = self.code_fragments; Ok(quote! { { use pw_format_test_macros_test::TestGeneratorOps; let mut ops = Vec::new(); #(#code_fragments);*; ops.push(TestGeneratorOps::Finalize); ops } }) } fn string_fragment(&mut self, string: &str) -> Result<()> { self.code_fragments.push(quote! { ops.push(TestGeneratorOps::StringFragment(#string.to_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<()> { let expression = format!("{}", expression.to_token_stream()); self.code_fragments.push(quote! { ops.push(TestGeneratorOps::IntegerConversion{ params: #params, signed: #signed, type_width: #type_width, arg: #expression.to_string(), }); }); Ok(()) } fn string_conversion(&mut self, expression: Arg) -> Result<()> { let expression = format!("{}", expression.to_token_stream()); self.code_fragments.push(quote! { ops.push(TestGeneratorOps::StringConversion(#expression.to_string())); }); Ok(()) } fn char_conversion(&mut self, expression: Arg) -> Result<()> { let expression = format!("{}", expression.to_token_stream()); self.code_fragments.push(quote! { ops.push(TestGeneratorOps::CharConversion(#expression.to_string())); }); Ok(()) } fn untyped_conversion(&mut self, expression: Arg, _params: &FormatParams) -> Result<()> { let expression = format!("{}", expression.to_token_stream()); self.code_fragments.push(quote! { ops.push(TestGeneratorOps::UntypedConversion(#expression.to_string())); }); Ok(()) } } fn generator_test_macro_impl(tokens: TokenStream) -> TokenStream { let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor); let generator = TestGenerator::new(); match generate(generator, format_and_args.into()) { Ok(token_stream) => token_stream.into(), Err(e) => e.to_compile_error().into(), } } #[proc_macro] pub fn printf_format_generator_test_macro(tokens: TokenStream) -> TokenStream { generator_test_macro_impl::(tokens) } #[proc_macro] pub fn core_fmt_format_generator_test_macro(tokens: TokenStream) -> TokenStream { generator_test_macro_impl::(tokens) } // Generator for testing `generate_printf()`. Allows control over the return // value of the conversion functions. struct PrintfTestGenerator { code_fragments: Vec, integer_specifier_override: Option, string_specifier_override: Option, char_specifier_override: Option, } impl PrintfTestGenerator { pub fn new() -> Self { Self { code_fragments: Vec::new(), integer_specifier_override: None, string_specifier_override: None, char_specifier_override: None, } } pub fn with_integer_specifier(mut self, specifier: &str) -> Self { self.integer_specifier_override = Some(specifier.to_string()); self } pub fn with_string_specifier(mut self, specifier: &str) -> Self { self.string_specifier_override = Some(specifier.to_string()); self } pub fn with_char_specifier(mut self, specifier: &str) -> Self { self.char_specifier_override = Some(specifier.to_string()); self } } impl PrintfFormatMacroGenerator for PrintfTestGenerator { fn finalize( self, format_string_fragments: &[PrintfFormatStringFragment], ) -> Result { // Create locally scoped alias so we can refer to them in `quote!()`. let code_fragments = self.code_fragments; let format_string_pieces: Vec<_> = format_string_fragments .iter() .map(|fragment| fragment.as_token_stream("pw_format_core")) .collect::>>()?; Ok(quote! { { use pw_format_test_macros_test::PrintfTestGeneratorOps; let mut ops = Vec::new(); let format_string = pw_bytes::concat_static_strs!(#(#format_string_pieces),*); #(#code_fragments);*; ops.push(PrintfTestGeneratorOps::Finalize); (format_string, ops) } }) } fn string_fragment(&mut self, string: &str) -> Result<()> { self.code_fragments.push(quote! { ops.push(PrintfTestGeneratorOps::StringFragment(#string.to_string())); }); Ok(()) } fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result> { let ty_str = format!("{ty}"); let expression = format!("{}", expression.to_token_stream()); self.code_fragments.push(quote! { ops.push(PrintfTestGeneratorOps::IntegerConversion{ ty: #ty_str.to_string(), arg: #expression.to_string(), }); }); Ok(self.integer_specifier_override.clone()) } fn string_conversion(&mut self, expression: Arg) -> Result> { let expression = format!("{}", expression.to_token_stream()); self.code_fragments.push(quote! { ops.push(PrintfTestGeneratorOps::StringConversion(#expression.to_string())); }); Ok(self.string_specifier_override.clone()) } fn char_conversion(&mut self, expression: Arg) -> Result> { let expression = format!("{}", expression.to_token_stream()); self.code_fragments.push(quote! { ops.push(PrintfTestGeneratorOps::CharConversion(#expression.to_string())); }); Ok(self.char_specifier_override.clone()) } fn untyped_conversion(&mut self, expression: Arg) -> Result<()> { let expression = format!("{}", expression.to_token_stream()); self.code_fragments.push(quote! { ops.push(PrintfTestGeneratorOps::UntypedConversion(#expression.to_string())); }); Ok(()) } } fn printf_generator_test_macro_impl(tokens: TokenStream) -> TokenStream { let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor); let generator = PrintfTestGenerator::new(); match generate_printf(generator, format_and_args.into()) { Ok(token_stream) => token_stream.into(), Err(e) => e.to_compile_error().into(), } } #[proc_macro] pub fn printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream { printf_generator_test_macro_impl::(tokens) } #[proc_macro] pub fn core_fmt_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream { printf_generator_test_macro_impl::(tokens) } // Causes the generator to substitute %d with %K. #[proc_macro] pub fn integer_sub_printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream { let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor); let generator = PrintfTestGenerator::new().with_integer_specifier("%K"); match generate_printf(generator, format_and_args.into()) { Ok(token_stream) => token_stream.into(), Err(e) => e.to_compile_error().into(), } } // Causes the generator to substitute %s with %K. #[proc_macro] pub fn string_sub_printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream { let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor); let generator = PrintfTestGenerator::new().with_string_specifier("%K"); match generate_printf(generator, format_and_args.into()) { Ok(token_stream) => token_stream.into(), Err(e) => e.to_compile_error().into(), } } // Causes the generator to substitute %c with %K. #[proc_macro] pub fn char_sub_printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream { let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor); let generator = PrintfTestGenerator::new().with_char_specifier("%K"); match generate_printf(generator, format_and_args.into()) { Ok(token_stream) => token_stream.into(), Err(e) => e.to_compile_error().into(), } } // Generator for testing `generate_core_fmt()`. Allows control over the return // value of the conversion functions. struct CoreFmtTestGenerator { code_fragments: Vec, integer_specifier_override: Option, string_specifier_override: Option, char_specifier_override: Option, } impl CoreFmtTestGenerator { pub fn new() -> Self { Self { code_fragments: Vec::new(), integer_specifier_override: None, string_specifier_override: None, char_specifier_override: None, } } pub fn with_integer_specifier(mut self, specifier: &str) -> Self { self.integer_specifier_override = Some(specifier.to_string()); self } pub fn with_string_specifier(mut self, specifier: &str) -> Self { self.string_specifier_override = Some(specifier.to_string()); self } pub fn with_char_specifier(mut self, specifier: &str) -> Self { self.char_specifier_override = Some(specifier.to_string()); self } } // Until they diverge, we reuse `PrintfTestGeneratorOps` here. impl CoreFmtFormatMacroGenerator for CoreFmtTestGenerator { fn finalize(self, format_string: String) -> Result { // Create locally scoped alias so we can refer to them in `quote!()`. let code_fragments = self.code_fragments; Ok(quote! { { use pw_format_test_macros_test::PrintfTestGeneratorOps; let mut ops = Vec::new(); #(#code_fragments);*; ops.push(PrintfTestGeneratorOps::Finalize); (#format_string, ops) } }) } fn string_fragment(&mut self, string: &str) -> Result<()> { self.code_fragments.push(quote! { ops.push(PrintfTestGeneratorOps::StringFragment(#string.to_string())); }); Ok(()) } fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result> { let ty_str = format!("{ty}"); let expression = format!("{}", expression.to_token_stream()); self.code_fragments.push(quote! { ops.push(PrintfTestGeneratorOps::IntegerConversion{ ty: #ty_str.to_string(), arg: #expression.to_string() }); }); Ok(self.integer_specifier_override.clone()) } fn string_conversion(&mut self, expression: Arg) -> Result> { let expression = format!("{}", expression.to_token_stream()); self.code_fragments.push(quote! { ops.push(PrintfTestGeneratorOps::StringConversion(#expression.to_string())); }); Ok(self.string_specifier_override.clone()) } fn char_conversion(&mut self, expression: Arg) -> Result> { let expression = format!("{}", expression.to_token_stream()); self.code_fragments.push(quote! { ops.push(PrintfTestGeneratorOps::CharConversion(#expression.to_string())); }); Ok(self.char_specifier_override.clone()) } fn untyped_conversion(&mut self, expression: Arg) -> Result<()> { let expression = format!("{}", expression.to_token_stream()); self.code_fragments.push(quote! { ops.push(PrintfTestGeneratorOps::UntypedConversion(#expression.to_string())); }); Ok(()) } } fn core_fmt_generator_test_macro_impl(tokens: TokenStream) -> TokenStream { let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor); let generator = CoreFmtTestGenerator::new(); match generate_core_fmt(generator, format_and_args.into()) { Ok(token_stream) => token_stream.into(), Err(e) => e.to_compile_error().into(), } } #[proc_macro] pub fn printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream { core_fmt_generator_test_macro_impl::(tokens) } #[proc_macro] pub fn core_fmt_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream { core_fmt_generator_test_macro_impl::(tokens) } // Causes the generator to substitute {} with {:?}. #[proc_macro] pub fn integer_sub_printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream { let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor); let generator = CoreFmtTestGenerator::new().with_integer_specifier("{:?}"); match generate_core_fmt(generator, format_and_args.into()) { Ok(token_stream) => token_stream.into(), Err(e) => e.to_compile_error().into(), } } // Causes the generator to substitute {} with {:?}. #[proc_macro] pub fn string_sub_printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream { let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor); let generator = CoreFmtTestGenerator::new().with_string_specifier("{:?}"); match generate_core_fmt(generator, format_and_args.into()) { Ok(token_stream) => token_stream.into(), Err(e) => e.to_compile_error().into(), } } // Causes the generator to substitute {} with {:?}. #[proc_macro] pub fn char_sub_printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream { let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor); let generator = CoreFmtTestGenerator::new().with_char_specifier("{:?}"); match generate_core_fmt(generator, format_and_args.into()) { Ok(token_stream) => token_stream.into(), Err(e) => e.to_compile_error().into(), } }