1extern 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