blob: 445edffda9f3e824321043dbb6c8282400e142ea [file] [log] [blame]
// 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, FormatStringParser,
IntegerDisplayType, 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<TokenStream2>,
}
impl TestGenerator {
pub fn new() -> Self {
Self {
code_fragments: Vec::new(),
}
}
}
impl FormatMacroGenerator for TestGenerator {
fn finalize(self) -> Result<TokenStream2> {
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,
display_type: IntegerDisplayType,
type_width: u8, // in bits
expression: Arg,
) -> Result<()> {
let expression = format!("{}", expression.to_token_stream());
self.code_fragments.push(quote! {
ops.push(TestGeneratorOps::IntegerConversion{
display_type: #display_type,
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) -> 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<T: FormatStringParser>(tokens: TokenStream) -> TokenStream {
let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor<T>);
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::<PrintfFormatStringParser>(tokens)
}
#[proc_macro]
pub fn core_fmt_format_generator_test_macro(tokens: TokenStream) -> TokenStream {
generator_test_macro_impl::<CoreFmtFormatStringParser>(tokens)
}
// Generator for testing `generate_printf()`. Allows control over the return
// value of the conversion functions.
struct PrintfTestGenerator {
code_fragments: Vec<TokenStream2>,
integer_specifier_override: Option<String>,
string_specifier_override: Option<String>,
char_specifier_override: Option<String>,
}
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<TokenStream2> {
// 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::<Result<Vec<_>>>()?;
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<Option<String>> {
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<Option<String>> {
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<Option<String>> {
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<T: FormatStringParser>(tokens: TokenStream) -> TokenStream {
let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor<T>);
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::<PrintfFormatStringParser>(tokens)
}
#[proc_macro]
pub fn core_fmt_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream {
printf_generator_test_macro_impl::<CoreFmtFormatStringParser>(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<PrintfFormatStringParser>);
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<PrintfFormatStringParser>);
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<PrintfFormatStringParser>);
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<TokenStream2>,
integer_specifier_override: Option<String>,
string_specifier_override: Option<String>,
char_specifier_override: Option<String>,
}
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<TokenStream2> {
// 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<Option<String>> {
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<Option<String>> {
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<Option<String>> {
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<T: FormatStringParser>(tokens: TokenStream) -> TokenStream {
let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor<T>);
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::<PrintfFormatStringParser>(tokens)
}
#[proc_macro]
pub fn printf_core_fmt_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream {
core_fmt_generator_test_macro_impl::<CoreFmtFormatStringParser>(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<PrintfFormatStringParser>);
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<PrintfFormatStringParser>);
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<PrintfFormatStringParser>);
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(),
}
}