1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 use proc_macro::TokenStream;
16 use proc_macro2::Ident;
17 use quote::{quote, ToTokens};
18 use syn::{
19 parse::{Parse, ParseStream},
20 parse_macro_input, Expr, Token,
21 };
22
23 use pw_format::macros::{
24 generate_printf, Arg, CoreFmtFormatStringParser, FormatAndArgsFlavor, FormatStringParser,
25 PrintfFormatMacroGenerator, PrintfFormatStringFragment, PrintfFormatStringParser, Result,
26 };
27
28 type TokenStream2 = proc_macro2::TokenStream;
29
30 // Arguments to `pw_log[f]_backend`. A log level followed by a [`pw_format`]
31 // format string.
32 #[derive(Debug)]
33 struct PwLogArgs<T: FormatStringParser> {
34 log_level: Expr,
35 format_and_args: FormatAndArgsFlavor<T>,
36 }
37
38 impl<T: FormatStringParser> Parse for PwLogArgs<T> {
parse(input: ParseStream) -> syn::parse::Result<Self>39 fn parse(input: ParseStream) -> syn::parse::Result<Self> {
40 let log_level: Expr = input.parse()?;
41 input.parse::<Token![,]>()?;
42 let format_and_args: FormatAndArgsFlavor<_> = input.parse()?;
43
44 Ok(PwLogArgs {
45 log_level,
46 format_and_args,
47 })
48 }
49 }
50
51 // Generator that implements [`pw_format::PrintfFormatMacroGenerator`] to take
52 // a log line and turn it into `printf` calls;
53 struct LogfGenerator<'a> {
54 log_level: &'a Expr,
55 args: Vec<TokenStream2>,
56 }
57
58 impl<'a> LogfGenerator<'a> {
new(log_level: &'a Expr) -> Self59 fn new(log_level: &'a Expr) -> Self {
60 Self {
61 log_level,
62 args: Vec::new(),
63 }
64 }
65 }
66
67 // Use a [`pw_format::PrintfFormatMacroGenerator`] to prepare arguments to call
68 // `printf`.
69 impl PrintfFormatMacroGenerator for LogfGenerator<'_> {
finalize( self, format_string_fragments: &[PrintfFormatStringFragment], ) -> Result<TokenStream2>70 fn finalize(
71 self,
72 format_string_fragments: &[PrintfFormatStringFragment],
73 ) -> Result<TokenStream2> {
74 let log_level = self.log_level;
75 let args = &self.args;
76 let format_string_pieces: Vec<_> = format_string_fragments
77 .iter()
78 .map(|fragment| fragment.as_token_stream("__pw_log_backend_crate"))
79 .collect::<Result<Vec<_>>>()?;
80 Ok(quote! {
81 {
82 use core::ffi::{c_int, c_uchar};
83 use __pw_log_backend_crate::{Arguments, VarArgs};
84 // Prepend log level tag and append newline and null terminator for
85 // C string validity.
86 let format_string = __pw_log_backend_crate::concat_static_strs!(
87 "[%s] ", #(#format_string_pieces),*, "\n\0"
88 );
89 unsafe {
90 // Build up the argument type/value.
91 let args = ();
92 #(#args)*
93
94 // Call printf through the argument type/value.
95 args.call_printf(format_string.as_ptr(),
96 __pw_log_backend_crate::log_level_tag(#log_level).as_ptr());
97 }
98 }
99 })
100 }
101
string_fragment(&mut self, _string: &str) -> Result<()>102 fn string_fragment(&mut self, _string: &str) -> Result<()> {
103 // String fragments are encoded directly into the format string.
104 Ok(())
105 }
106
integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>>107 fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>> {
108 self.args.push(quote! {
109 let args = <#ty as Arguments<#ty>>::push_arg(args, &((#expression) as #ty));
110 });
111 Ok(None)
112 }
113
string_conversion(&mut self, expression: Arg) -> Result<Option<String>>114 fn string_conversion(&mut self, expression: Arg) -> Result<Option<String>> {
115 // In order to not convert Rust Strings to CStrings at runtime, we use
116 // the "%.*s" specifier to explicitly bound the length of the
117 // non-null-terminated Rust String.
118 self.args.push(quote! {
119 let args = <&str as Arguments<&str>>::push_arg(args, &((#expression) as &str));
120 });
121 Ok(Some("%.*s".into()))
122 }
123
char_conversion(&mut self, expression: Arg) -> Result<Option<String>>124 fn char_conversion(&mut self, expression: Arg) -> Result<Option<String>> {
125 self.args.push(quote! {
126 let args = <char as Arguments<char>>::push_arg(args, &((#expression) as char));
127 });
128 Ok(None)
129 }
130
untyped_conversion(&mut self, expression: Arg) -> Result<()>131 fn untyped_conversion(&mut self, expression: Arg) -> Result<()> {
132 match &expression {
133 Arg::ExprCast(cast) => {
134 let ty = &cast.ty;
135 self.args.push(quote! {
136 let args = <#ty as Arguments<#ty>>::push_arg(args, &(#expression));
137 });
138 }
139 Arg::Expr(_) => {
140 return Err(pw_format::macros::Error::new(&format!(
141 "Expected argument to untyped format (%v) to be a cast expression (e.g. x as i32), but found {}.",
142 expression.to_token_stream()
143 )));
144 }
145 }
146 Ok(())
147 }
148 }
149
150 #[proc_macro]
_pw_log_backend(tokens: TokenStream) -> TokenStream151 pub fn _pw_log_backend(tokens: TokenStream) -> TokenStream {
152 let input = parse_macro_input!(tokens as PwLogArgs<CoreFmtFormatStringParser>);
153 let generator = LogfGenerator::new(&input.log_level);
154
155 match generate_printf(generator, input.format_and_args.into()) {
156 Ok(token_stream) => token_stream.into(),
157 Err(e) => e.to_compile_error().into(),
158 }
159 }
160
161 #[proc_macro]
_pw_logf_backend(tokens: TokenStream) -> TokenStream162 pub fn _pw_logf_backend(tokens: TokenStream) -> TokenStream {
163 let input = parse_macro_input!(tokens as PwLogArgs<PrintfFormatStringParser>);
164 let generator = LogfGenerator::new(&input.log_level);
165
166 match generate_printf(generator, input.format_and_args.into()) {
167 Ok(token_stream) => token_stream.into(),
168 Err(e) => e.to_compile_error().into(),
169 }
170 }
171