1extern crate proc_macro;
2
3use core::mem;
4use proc_macro::TokenStream;
5use std::cmp::Ordering;
6use std::borrow::Cow;
7
8use proc_macro2::Span;
9use quote::quote;
10use syn::{
11 parse::{self, Parse, ParseStream},
12 parse_macro_input,
13 punctuated::Punctuated,
14 spanned::Spanned,
15 Expr, ExprLit, LitStr, Token, Ident,
16};
17
18#[proc_macro]
19pub fn print(input: TokenStream) -> TokenStream {
20 write(input, false)
21}
22
23#[proc_macro]
24pub fn println(input: TokenStream) -> TokenStream {
25 write(input, true)
26}
27
28fn write(input: TokenStream, newline: bool) -> TokenStream {
29 let input = parse_macro_input!(input as Input);
30 let literal = input.literal;
31
32 let mut format = literal.value();
33 if newline {
34 format.push('\n');
35 }
36 let pieces = match parse(&format, literal.span()) {
37 Err(e) => return e.to_compile_error().into(),
38 Ok(pieces) => pieces,
39 };
40
41 let required_args = pieces.iter().filter(|piece| !piece.is_str()).count();
42 let supplied_args = input.args.len();
43 match supplied_args.cmp(&required_args) {
44 Ordering::Less => {
45 return parse::Error::new(
46 literal.span(),
47 &format!(
48 "format string requires {} arguments but {} {} supplied",
49 required_args,
50 supplied_args,
51 if supplied_args == 1 { "was" } else { "were" }
52 ),
53 )
54 .to_compile_error()
55 .into();
56 }
57 Ordering::Greater => {
58 return parse::Error::new(
59 input.args[required_args].span(),
60 &format!(
62 "format string requires {} arguments but {} {} supplied",
63 required_args,
64 supplied_args,
65 if supplied_args == 1 { "was" } else { "were" }
66 ),
67 )
68 .to_compile_error()
69 .into();
70 }
71 Ordering::Equal => {}
72 }
73
74 let mut args = vec![];
75 let mut exprs = vec![];
76 let mut dbgs = vec![];
77 let mut i = 0;
78 let mut format = String::new();
79 for piece in pieces {
80 if let Piece::Str(s) = piece {
81 format.push_str(&s.to_string());
82 } else {
83 let arg = &input.args[i];
84 i += 1;
85 args.push(quote!(&(#arg)));
86
87 match piece {
88 Piece::Percent(pct) => {
89 match pct {
90 PCT::Int => {
91 exprs.push(quote!( #arg ));
92 }
93 PCT::Ch => {
94 exprs.push(quote!( #arg ));
95 }
96 PCT::Str => {
97 exprs.push(quote!( (*#arg).as_ptr() as *const _ as InvokeParam, (*#arg).len() ));
98 }
99 PCT::Array => {
100 exprs.push(quote!( (*#arg).as_ptr() as *const _ as InvokeParam, (*#arg).len() ));
101 }
102 PCT::Hex => {
103 exprs.push(quote!( #arg ));
104 }
105 }
106 }
107 Piece::Display => {
108 let mut ok = false;
109 let mut err_msg = "".to_string();
110 let mut lable = "";
111 match arg {
112 Expr::Array(_) => {
113 lable = "%y";
114 exprs.push(
115 quote!( #arg.as_ptr() as *const _ as InvokeParam, (#arg.len() * core::mem::size_of_val(&#arg[0])) ),
116 );
117 ok = true;
118 }
119 Expr::Reference(ref_exp) => {
120 let ref_expr = &*ref_exp.expr;
121 match ref_expr {
122 Expr::Lit(lit) => match fn_match_lit(lit, arg) {
123 Ok((l, e)) => {
124 lable = l;
125 exprs.push(e);
126 ok = true;
127 }
128 Err(msg) => err_msg = msg.to_string(),
129 },
130 Expr::Array(_) => {
131 lable = "%y";
132 exprs.push(quote!( #arg.as_ptr() as *const _ as InvokeParam, (#arg.len() * core::mem::size_of_val(&#arg[0])) ));
133 ok = true;
134 }
135 Expr::Path(path_expr) => {
136 let first = path_expr.path.segments.first();
137 if let Some(segment) = first {
138 lable = "%y";
139 exprs.push(quote!( #segment.as_ptr() as *const _ as InvokeParam, (#segment.len() * core::mem::size_of_val(&#segment[0])) ));
140 ok = true;
141 } else {
142 err_msg = "Path has no segments".to_string();
143 }
144 }
145 _ => {
146 err_msg = map_expr_error(ref_expr);
147 err_msg.push_str(" -> in match Expr::Reference");
148 }
149 }
150 }
151 Expr::Path(path_expr) => {
152 let first = path_expr.path.segments.first();
153 if let Some(segment) = first {
154 lable = "%d";
155 exprs.push(quote!( #segment ));
156 ok = true;
157 } else {
158 err_msg = "Path has no segments".to_string();
159 }
160 }
161 Expr::Lit(lit) => match fn_match_lit(lit, arg) {
162 Ok((l, e)) => {
163 lable = l;
164 exprs.push(e);
165 ok = true;
166 }
167 Err(msg) => err_msg = msg.to_string(),
168 },
169 Expr::Call(_) => {
170 lable = "%d";
171 exprs.push(quote!( #arg ));
172 ok = true;
173 }
174 Expr::MethodCall(_) => {
175 lable = "%d";
176 exprs.push(quote!( #arg ));
177 ok = true;
178 }
179 _ => {
180 err_msg = map_expr_error(arg);
181 err_msg.push_str(" -> in match arg");
182 }
183 }
184 if !ok {
185 return parse::Error::new(arg.span(), err_msg.to_string())
186 .to_compile_error()
187 .into();
188 }
189
190 format.push_str(lable);
191 }
192 Piece::Debug => {
193 let buf_x = mk_name_with_idx("buf", i);
194 let str_x = mk_name_with_idx("str", i);
195
196 dbgs.push(quote!(
197 let mut #buf_x = [0u8; 256];
198 let #str_x = format::fmt_to_buf(
199 &mut #buf_x,
200 format_args!("{:?}", #arg),
201 ).unwrap_or("format error");
202 ));
203 format.push_str("%S");
204 exprs.push(quote!( #str_x.as_ptr() as *const _ as InvokeParam, #str_x.len() ));
205 }
206 Piece::Hex {
207 upper_case,
208 } => {
212 format.push_str(if upper_case { "%X" } else { "%x" });
213 exprs.push(quote!( #arg ));
214 }
215 Piece::Str(_) => unreachable!(),
216 }
217 }
218 }
219
220 quote!(
221 unsafe {
222 use embedded_c_sdk_bind_hal::*;
223
224 #(#dbgs )*
225 ll_invoke( INVOKE_ID_LOG_PRINT, concat!(#format, "\0").as_ptr() as *const _ as InvokeParam, #(#exprs as InvokeParam, )* )
226 }
227 )
228 .into()
229}
230
231fn fn_match_lit<'a>(
232 lit: &ExprLit,
233 arg: &Expr,
234) -> Result<(&'a str, proc_macro2::TokenStream), &'a str> {
235 let mut ok = false;
236 let mut err_msg = "";
237 let mut lable = "";
238 let mut expr = proc_macro2::TokenStream::new();
239 match &lit.lit {
240 syn::Lit::Str(_) | syn::Lit::ByteStr(_) => {
241 lable = "%S";
242 expr = quote!( #arg.as_ptr() as *const _ as InvokeParam, #arg.len() );
243 ok = true;
244 }
245 syn::Lit::CStr(_) => {
246 lable = "%s";
247 expr = quote!( #arg.as_ptr() as *const _ );
248 ok = true;
249 }
250 syn::Lit::Byte(_) => {
251 lable = "%d";
252 expr = quote!( #arg );
253 ok = true;
254 }
255 syn::Lit::Char(_) => {
256 lable = "%c";
257 expr = quote!( #arg );
258 ok = true;
259 }
260 syn::Lit::Int(_) => {
261 lable = "%d";
262 expr = quote!( #arg );
263 ok = true;
264 }
265 syn::Lit::Float(_) => {
266 err_msg = "Float types not supported yet";
267 }
268 syn::Lit::Bool(lit_bool) => {
269 lable = "%s";
270 if lit_bool.value() {
271 expr = quote!("true\0".as_ptr() as *const _).into();
272 } else {
273 expr = quote!("false\0".as_ptr() as *const _).into();
274 }
275 ok = true;
276 }
277 _ => {
278 err_msg = "Unsupported Lit parameter types";
279 }
280 }
281 if ok {
282 Ok((lable, expr))
283 } else {
284 Err(err_msg)
285 }
286}
287
288struct Input {
289 literal: LitStr,
290 _comma: Option<Token![,]>,
291 args: Punctuated<Expr, Token![,]>,
292}
293
294impl Parse for Input {
295 fn parse(input: ParseStream) -> parse::Result<Self> {
296 let literal = input.parse()?;
297
298 if input.is_empty() {
299 Ok(Input {
300 literal,
301 _comma: None,
302 args: Punctuated::new(),
303 })
304 } else {
305 Ok(Input {
306 literal,
307 _comma: input.parse()?,
308 args: Punctuated::parse_terminated(input)?,
309 })
310 }
311 }
312}
313
314#[derive(Debug, PartialEq)]
315enum PCT {
316 Int,
317 Ch,
318 Str,
319 Hex,
320 Array,
321}
322
323#[derive(Debug, PartialEq)]
324enum Piece<'a> {
325 Debug,
326 Display,
327 Str(Cow<'a, str>),
328 Percent(PCT),
329 Hex {
330 upper_case: bool,
331 },
335}
336
337impl Piece<'_> {
338 fn is_str(&self) -> bool {
339 matches!(self, Piece::Str(_))
340 }
341}
342
343fn mk_name_with_idx(prefix: &str, i: usize) -> Ident {
344 Ident::new(&format!("{}_{}", prefix, i), Span::call_site())
345}
346
347fn unescape(mut literal: &str, span: Span) -> parse::Result<Cow<str>> {
349 if literal.contains('}') {
350 let mut buf = String::new();
351
352 while literal.contains('}') {
353 const ERR: &str = "format string contains an unmatched right brace";
354 let mut parts = literal.splitn(2, '}');
355
356 match (parts.next(), parts.next()) {
357 (Some(left), Some(right)) => {
358 const ESCAPED_BRACE: &str = "}";
359
360 if let Some(tail) = right.strip_prefix(ESCAPED_BRACE) {
361 buf.push_str(left);
362 buf.push('}');
363
364 literal = tail;
365 } else {
366 return Err(parse::Error::new(span, ERR));
367 }
368 }
369
370 _ => unreachable!(),
371 }
372 }
373
374 buf.push_str(literal);
375
376 Ok(buf.into())
377 } else {
378 Ok(Cow::Borrowed(literal))
379 }
380}
381
382fn parse_c_format(literal: &str, span: Span) -> parse::Result<Vec<Piece>> {
383 let mut pieces = vec![];
384
385 let chars = &mut literal.chars();
386 while let Some(ch) = chars.next() {
387 if ch != '%' {
388 continue;
389 }
390 if let Some(lb) = chars.next() {
391 match lb {
392 'd' => {
393 pieces.push(Piece::Percent(PCT::Int));
394 }
395 'c' => {
396 pieces.push(Piece::Percent(PCT::Ch));
397 }
398 's' | 'S' => {
399 pieces.push(Piece::Percent(PCT::Str));
400 }
401 'x' | 'X' => {
402 pieces.push(Piece::Percent(PCT::Hex));
403 }
404 'y' | 'Y' => {
405 pieces.push(Piece::Percent(PCT::Array));
406 }
407 '%' => {}
408 _ => {
409 return Err(parse::Error::new(
410 span,
411 "invalid format string: expected `%d`, `%c`, `%s/S`, `%x/X` or `%y`",
412 ));
413 }
414 }
415 }
416 }
417 if literal.contains("%s") {
418 let literal = literal.replace("%s", "%S");
419 pieces.push(Piece::Str(literal.into()));
420 } else {
421 pieces.push(Piece::Str(literal.into()));
422 }
423
424 Ok(pieces)
425}
426fn parse(mut literal: &str, span: Span) -> parse::Result<Vec<Piece>> {
427 let mut pieces = vec![];
428
429 let mut buf = String::new();
430 loop {
431 let mut parts = literal.splitn(2, '{');
432 match (parts.next(), parts.next()) {
433 (None, None) => break,
435
436 (Some(s), None) => {
438 if pieces.is_empty() {
439 return parse_c_format(literal, span);
440 } else if buf.is_empty() {
441 if !s.is_empty() {
442 pieces.push(Piece::Str(unescape(s, span)?));
443 }
444 } else {
445 buf.push_str(&unescape(s, span)?);
446
447 pieces.push(Piece::Str(Cow::Owned(buf)));
448 }
449
450 break;
451 }
452
453 (head, Some(tail)) => {
454 const DEBUG: &str = ":?}";
455 const DEBUG_PRETTY: &str = ":#?}";
456 const DISPLAY: &str = "}";
457 const ESCAPED_BRACE: &str = "{";
458
459 let head = head.unwrap_or("");
460 if tail.starts_with(DEBUG)
461 || tail.starts_with(DEBUG_PRETTY)
462 || tail.starts_with(DISPLAY)
463 || tail.starts_with(':')
464 {
465 if buf.is_empty() {
466 if !head.is_empty() {
467 pieces.push(Piece::Str(unescape(head, span)?));
468 }
469 } else {
470 buf.push_str(&unescape(head, span)?);
471
472 pieces.push(Piece::Str(Cow::Owned(mem::take(&mut buf))));
473 }
474
475 if let Some(tail_tail) = tail.strip_prefix(DEBUG) {
476 pieces.push(Piece::Debug);
477
478 literal = tail_tail;
479 } else if let Some(tail_tail) = tail.strip_prefix(DEBUG_PRETTY) {
480 pieces.push(Piece::Debug);
481
482 literal = tail_tail;
483 } else if let Some(tail2) = tail.strip_prefix(':') {
484 let (piece, remainder) = parse_colon(tail2, span)?;
485 pieces.push(piece);
486 literal = remainder;
487 } else {
488 pieces.push(Piece::Display);
489
490 literal = &tail[DISPLAY.len()..];
491 }
492 } else if let Some(tail_tail) = tail.strip_prefix(ESCAPED_BRACE) {
493 buf.push_str(&unescape(head, span)?);
494 buf.push('{');
495
496 literal = tail_tail;
497 } else {
498 return Err(parse::Error::new(
499 span,
500 "invalid format string: expected `{{`, `{}`, `{:?}` or `{:#?}`",
501 ));
502 }
503 }
504 }
505 }
506
507 Ok(pieces)
508}
509
510fn split_number(src: &str) -> (&str, usize) {
512 let mut rval = 0;
513 let mut cursor = 0;
514
515 let chars = src.chars();
516 for (i, ch) in chars.enumerate() {
517 match ch.to_digit(10) {
518 Some(val) => {
519 rval = rval * 10 + val as usize;
520 cursor = i + 1;
521 }
522 None => break,
523 }
524 }
525
526 (&src[cursor..], rval)
527}
528
529fn parse_colon(format: &str, span: Span) -> parse::Result<(Piece, &str)> {
531 let (format, _prefix) = if let Some(tail) = format.strip_prefix('#') {
532 (tail, true)
533 } else {
534 (format, false)
535 };
536 let (format, _pad_char) = if let Some(tail) = format.strip_prefix('0') {
537 (tail, b'0')
538 } else {
539 (format, b' ')
540 };
541 let (format, _pad_length) = if !format.is_empty()
542 && if let Some(ch) = format.chars().next() {
543 ch.is_ascii_digit()
544 } else {
545 false
546 } {
547 split_number(format)
548 } else {
549 (format, 0)
550 };
551 if let Some(tail) = format.strip_prefix("x}") {
552 Ok((
553 Piece::Hex {
554 upper_case: false,
555 },
559 tail,
560 ))
561 } else if let Some(tail) = format.strip_prefix("X}") {
562 Ok((
563 Piece::Hex {
564 upper_case: true,
565 },
569 tail,
570 ))
571 } else {
572 Err(parse::Error::new(
573 span,
574 "invalid format string: expected `{{`, `{}`, `{:?}`, `{:#?}` or '{:x}'",
575 ))
576 }
577}
578
579fn map_expr_error(expr: &Expr) -> String {
580 let expr_type = match expr {
581 Expr::Assign(_) => "Assign",
582 Expr::Async(_) => "Async",
583 Expr::Await(_) => "Await",
584 Expr::Binary(_) => "Binary",
585 Expr::Block(_) => "Block",
586 Expr::Break(_) => "Break",
587 Expr::Call(_) => "Call",
588 Expr::Cast(_) => "Cast",
589 Expr::Closure(_) => "Closure",
590 Expr::Const(_) => "Const",
591 Expr::Continue(_) => "Continue",
592 Expr::Field(_) => "Field",
593 Expr::ForLoop(_) => "ForLoop",
594 Expr::Group(_) => "Group",
595 Expr::If(_) => "If",
596 Expr::Index(_) => "Index",
597 Expr::Infer(_) => "Infer",
598 Expr::Let(_) => "Let",
599 Expr::Loop(_) => "Loop",
600 Expr::Macro(_) => "Macro",
601 Expr::Match(_) => "Match",
602 Expr::MethodCall(_) => "MethodCall",
603 Expr::Paren(_) => "Paren",
604 Expr::Range(_) => "Range",
605 Expr::Repeat(_) => "Repeat",
606 Expr::Return(_) => "Return",
607 Expr::Struct(_) => "Struct",
608 Expr::Try(_) => "Try",
609 Expr::TryBlock(_) => "TryBlock",
610 Expr::Tuple(_) => "Tuple",
611 Expr::Unary(_) => "Unary",
612 Expr::Unsafe(_) => "Unsafe",
613 Expr::Verbatim(_) => "Verbatim",
614 Expr::While(_) => "While",
615 Expr::Yield(_) => "Yield",
616 Expr::Array(_) => "Array",
617 Expr::Lit(_) => "Lit",
618 Expr::Path(_) => "Path",
619 Expr::Reference(_) => "Reference",
620 _ => "Unknow",
621 };
622
623 let mut msg = "Expr not implement: ".to_string();
624 msg.push_str(expr_type);
625
626 return msg;
627}