cfmt_macros/
lib.rs

1//!
2//! cfmt-macros
3//! To parse format strings, `parse`/`unescape` from `ufmt` are reused here.
4//!
5//! 实现过程参考了UFMT的实现,重用了其部分代码,parse/unescape,涉及到格式化字符串的解析
6//!
7
8extern crate proc_macro;
9use core::mem;
10use std::time::{ SystemTime };
11use std::borrow::Cow;
12use proc_macro2::{ Span };
13use proc_macro::TokenStream;
14use quote::quote;
15use syn::{
16    parse_macro_input,
17    Expr, LitStr, Token,
18    parse::{self, Parse, ParseStream },
19    punctuated::Punctuated,
20    spanned::{ Spanned },
21};
22
23#[proc_macro]
24pub fn print(input: TokenStream) -> TokenStream {
25    cprintf(input, false, 1)
26}
27
28#[proc_macro]
29pub fn cprint(input: TokenStream) -> TokenStream {
30    cprintf(input, false, 1)
31}
32
33#[proc_macro]
34pub fn println(input: TokenStream) -> TokenStream {
35    cprintf(input, true, 1)
36}
37
38#[proc_macro]
39pub fn cprintln(input: TokenStream) -> TokenStream {
40    cprintf(input, true, 1)
41}
42
43#[proc_macro]
44pub fn eprint(input: TokenStream) -> TokenStream {
45    cprintf(input, false, 2)
46}
47
48#[proc_macro]
49pub fn ceprint(input: TokenStream) -> TokenStream {
50    cprintf(input, false, 2)
51}
52
53#[proc_macro]
54pub fn eprintln(input: TokenStream) -> TokenStream {
55    cprintf(input, true, 2)
56}
57
58#[proc_macro]
59pub fn ceprintln(input: TokenStream) -> TokenStream {
60    cprintf(input, true, 2)
61}
62
63#[proc_macro]
64pub fn csprint(input: TokenStream) -> TokenStream {
65    csnprintf(input, true)
66}
67
68#[proc_macro]
69pub fn sprint(input: TokenStream) -> TokenStream {
70    csnprintf(input, true)
71}
72
73#[proc_macro]
74pub fn cbprint(input: TokenStream) -> TokenStream {
75    csnprintf(input, false)
76}
77
78#[proc_macro]
79pub fn bprint(input: TokenStream) -> TokenStream {
80    csnprintf(input, false)
81}
82
83fn csnprintf(input: TokenStream, is_str: bool) -> TokenStream {
84    let input = parse_macro_input!(input as BufInput);
85    let mut buf_format = input.input.format.value();
86    buf_format.push('\0');
87
88    let buf = &input.buf;
89    let ident = cfmt_ident(9999, buf.span());
90    let mut buf_vars = vec![];
91    let mut buf_args = vec![];
92    if is_str {
93        buf_vars.push(quote!(let #ident: &mut str = #buf;));
94        buf_args.push(quote!(#ident.as_bytes_mut().as_mut_ptr()));
95    } else {
96        buf_vars.push(quote!(let #ident: &mut [u8] = #buf;));
97        buf_args.push(quote!(#ident.as_mut_ptr()));
98    }
99    buf_args.push(quote!(#ident.len() as usize));
100    cformat(&buf_format, &input.input, |vars, args, format| {
101        let tokens = quote!{ unsafe { #(#buf_vars)* #(#vars)* snprintf( #(#buf_args),*, #format.as_bytes().as_ptr(), #(#args),*); } };
102        tokens.into()
103    })
104}
105
106fn cprintf(input: TokenStream, ln: bool, fd: i32) -> TokenStream {
107    let input = parse_macro_input!(input as Input);
108    let mut format = input.format.value();
109    
110    if ln {
111        format.push_str("\n\0");
112    } else {
113        format.push('\0');
114    }
115    cformat(&format, &input, |vars, args, format| {
116        let tokens = quote!{ unsafe { #(#vars)* dprintf( #fd, #format.as_bytes().as_ptr(), #(#args),*); } };
117        tokens.into()
118    })
119}
120
121fn cfmt_ident(idx: usize, span: Span) -> syn::Ident {
122    let name = format!("_cfmt_{}_{}", idx, SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs());
123    syn::Ident::new(&name, span)
124}
125
126fn cformat<F>(format: &str, input: &Input, f: F) -> TokenStream 
127    where F: Fn(&Vec<proc_macro2::TokenStream>, &Vec<proc_macro2::TokenStream>, &str) -> TokenStream
128{
129    let pieces = match parse(format, input.format.span()) {
130        Err(e) => return e.to_compile_error().into(),
131        Ok(pieces) => pieces,
132    };
133
134    let argc: usize = input.args.len();
135    let required_argc: usize = pieces.iter().filter(|piece| !piece.is_literal()).count();
136
137    if argc != required_argc {
138        return parse::Error::new(input.format.span(),
139            &format!("format string required {} arguments but {} were supplied",
140                required_argc, argc)).to_compile_error().into();
141    }
142
143    let literal = gen_literal(&pieces);
144    let mut args = vec![];
145    let mut vars = vec![];
146
147    let mut i: usize = 0;
148    for piece in pieces {
149        if matches!(piece, Piece::Literal(_)) {
150            continue;
151        }
152        let arg = &input.args[i];
153        match piece {
154            Piece::Literal(_) => {
155            },
156            Piece::Str | Piece::Bytes => {
157                let ident = cfmt_ident(i, arg.span());
158                args.push(quote!(#ident.len() as i32));
159                if matches!(piece, Piece::Str) {
160                    vars.push(quote!(let #ident: &str = #arg;));
161                    args.push(quote!(#ident.as_bytes().as_ptr()));
162                } else {
163                    vars.push(quote!(let #ident: &[u8] = #arg;));
164                    args.push(quote!(#ident.as_ptr()));
165                }
166            },
167            Piece::Char => {
168                let ident = cfmt_ident(i, arg.span());
169                vars.push(quote!(
170                    let mut #ident = [0_u8; 5];
171                    let #ident = orion_cfmt::encode_utf8(#arg, &mut #ident);
172                ));
173                args.push(quote!(#ident));
174            },
175            Piece::CChar => {
176                args.push(quote!((#arg) as i32));
177            },
178            Piece::CStr | Piece::Pointer => {
179                args.push(quote!((#arg) as *const _ as *const u8));
180            },
181            Piece::Double => {
182                args.push(quote!((#arg) as f64));
183            },
184            _ => {
185                args.push(quote!((#arg) as i64));
186            }
187        }
188        i += 1;
189    }
190
191    f(&vars, &args, &literal)
192}
193
194fn gen_literal(pieces: &Vec<Piece>) -> String {
195    let mut buf = String::new();
196    pieces.iter().all(|piece| {
197        match piece {
198            Piece::Literal(s) => buf.push_str(&s),
199            Piece::CStr => buf.push_str("%s"),
200            Piece::Pointer => buf.push_str("%p"),
201            Piece::Str => buf.push_str("%.*s"),
202            Piece::Bytes => buf.push_str("%.*s"),
203            Piece::Signed => buf.push_str("%lld"),
204            Piece::Unsigned => buf.push_str("%llu"),
205            Piece::Hex => buf.push_str("%llx"),
206            Piece::Char => buf.push_str("%s"),
207            Piece::CChar => buf.push_str("%c"),
208            Piece::Double => buf.push_str("%e"),
209        }
210        true
211    });
212    buf
213}
214
215struct Input {
216    format: LitStr,
217    _comma: Option<Token![,]>,
218    args:   Punctuated<Expr, Token![,]>, 
219}
220
221impl Parse for Input {
222    fn parse(input: ParseStream) -> parse::Result<Self> {
223        let format = input.parse()?;
224        if input.is_empty() {
225            Ok(Input {
226                format: format,
227                _comma: None,
228                args:   Punctuated::new(),
229            })
230        } else {
231            Ok(Input {
232                format: format,
233                _comma: input.parse()?,
234                args:   Punctuated::parse_terminated(input)?,
235            })
236        }
237    }
238}
239
240struct BufInput {
241    buf: Expr,
242    input: Input,
243}
244
245impl Parse for BufInput {
246    fn parse(input: ParseStream) -> parse::Result<Self> {
247        let buf = input.parse()?;
248        let _: Option<Token![,]> = input.parse()?;
249        let input = Input::parse(input)?;
250        Ok(BufInput {
251            buf: buf,
252            input: input,
253        })
254    }
255}
256
257enum Piece<'a> {
258    Literal(Cow<'a, str>),
259    CStr,
260    Pointer,
261    CChar,
262    Char,
263    Str,
264    Bytes,
265    Hex,
266    Unsigned,
267    Signed,
268    Double,
269}
270
271impl Piece<'_> {
272    fn is_literal(&self) -> bool {
273        matches!(self, Piece::Literal(_))
274    }
275}
276
277fn parse(mut format: &str, span: Span) -> parse::Result<Vec<Piece>> {
278    let mut pieces = vec![];
279    let mut buf = String::new();
280    loop {
281        let mut parts = format.splitn(2, '{');
282        match (parts.next(), parts.next()) {
283            (None, None) => break,
284            (Some(s), None) => {
285                if buf.is_empty() {
286                    if !s.is_empty() {
287                        pieces.push(Piece::Literal(unescape(s, span)?));
288                    }
289                } else {
290                    buf.push_str(&unescape(s, span)?);
291                    pieces.push(Piece::Literal(Cow::Owned(buf)));
292                }
293                break;
294            },
295            (head, Some(tail)) => {
296                const CSTR: &str = ":cs}";
297                const POINTER: &str = ":p}";
298                const STR: &str = ":rs}";
299                const BYTES: &str = ":rb}";
300                const HEX: &str = ":x}";
301                const SIGNED: &str = ":d}";
302                const UNSIGNED: &str = ":u}";
303                const DOUBLE: &str = ":e}";
304                const CCHAR: &str = ":cc}";
305                const CHAR: &str = ":rc}";
306                const ESCAPE_BRACE: &str = "{";
307
308                let head = head.unwrap_or("");
309                if tail.starts_with(CSTR)
310                    || tail.starts_with(POINTER)
311                    || tail.starts_with(STR)
312                    || tail.starts_with(BYTES)
313                    || tail.starts_with(HEX)
314                    || tail.starts_with(SIGNED)
315                    || tail.starts_with(UNSIGNED)
316                    || tail.starts_with(DOUBLE)
317                    || tail.starts_with(CCHAR)
318                    || tail.starts_with(CHAR)
319                {
320                    if buf.is_empty() {
321                        if !head.is_empty() {
322                            pieces.push(Piece::Literal(unescape(head, span)?));
323                        }
324                    } else {
325                        buf.push_str(&unescape(head, span)?);
326                        pieces.push(Piece::Literal(Cow::Owned(mem::take(&mut buf))));
327                    }
328                    
329                    if let Some(tail_tail) = tail.strip_prefix(CSTR) {
330                        pieces.push(Piece::CStr);
331                        format = tail_tail;
332                    } else if let Some(tail_tail) = tail.strip_prefix(POINTER) {
333                        pieces.push(Piece::Pointer);
334                        format = tail_tail;
335                    } else if let Some(tail_tail) = tail.strip_prefix(STR) {
336                        pieces.push(Piece::Str);
337                        format = tail_tail;
338                    } else if let Some(tail_tail) = tail.strip_prefix(BYTES) {
339                        pieces.push(Piece::Bytes);
340                        format = tail_tail;
341                    } else if let Some(tail_tail) = tail.strip_prefix(HEX) {
342                        pieces.push(Piece::Hex);
343                        format = tail_tail;
344                    } else if let Some(tail_tail) = tail.strip_prefix(SIGNED) {
345                        pieces.push(Piece::Signed);
346                        format = tail_tail;
347                    } else if let Some(tail_tail) = tail.strip_prefix(UNSIGNED) {
348                        pieces.push(Piece::Unsigned);
349                        format = tail_tail;
350                    } else if let Some(tail_tail) = tail.strip_prefix(CCHAR) {
351                        pieces.push(Piece::CChar);
352                        format = tail_tail;
353                    } else if let Some(tail_tail) = tail.strip_prefix(CHAR) {
354                        pieces.push(Piece::Char);
355                        format = tail_tail;
356                    } else if let Some(tail_tail) = tail.strip_prefix(DOUBLE) {
357                        pieces.push(Piece::Double);
358                        format = tail_tail;
359                    }
360                    
361                } else if let Some(tail_tail) = tail.strip_prefix(ESCAPE_BRACE) {
362                    buf.push_str(&unescape(head, span)?);
363                    buf.push('{');
364                    format = tail_tail;
365                } else {
366                    return Err(parse::Error::new(span,
367                        "invalid format string: expected {:d}, {:u}, {:x}, {:e}, {:p}, {:cs}, {:rs}, {:rb} {:cc} {:rc} {{"));
368                }
369            }
370        }
371    }
372
373    Ok(pieces)
374}
375
376fn unescape(mut format: &str, span: Span) -> parse::Result<Cow<str>> {
377    if format.contains('}') {
378        let mut buf = String::new();
379        while format.contains('}') {
380            const ERR: &str = "invalid format string: unmatched right brace";
381            let mut parts = format.splitn(2, '}');
382            match (parts.next(), parts.next()) {
383                (Some(head), Some(tail)) => {
384                    const ESCAPE_BRACE: &str = "}";
385                    if let Some(tail_tail) = tail.strip_prefix(ESCAPE_BRACE) {
386                        buf.push_str(head);
387                        buf.push('}');
388                        format = tail_tail;
389                    } else {
390                        return Err(parse::Error::new(span, ERR));
391                    }
392                },
393                _ => unreachable!(),
394            }
395        }
396        buf.push_str(format);
397        Ok(buf.into())
398    } else {
399        Ok(Cow::Borrowed(format))
400    }
401}
402