1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use proc_macro2::{Group, Ident, Span, TokenTree};
5use proc_macro_hack::proc_macro_hack;
6use quote::quote;
7use std::iter::FromIterator;
8use syn::parse::Parser;
9use syn::punctuated::Punctuated;
10use syn::{parse_macro_input, parse_quote, parse_str, Expr, LitStr, Token};
11
12fn rewrite_site(e: proc_macro2::TokenStream, span: Span) -> proc_macro2::TokenStream {
13 proc_macro2::TokenStream::from_iter(e.into_iter().map(|tt| match tt {
14 TokenTree::Ident(ident) => TokenTree::Ident(Ident::new(&ident.to_string(), span)),
15 TokenTree::Group(group) => TokenTree::Group(Group::new(
16 group.delimiter(),
17 rewrite_site(group.stream(), span),
18 )),
19 tt => tt,
20 }))
21}
22
23fn gen(macro_call: impl Fn(String, Vec<Expr>) -> TokenStream, s: LitStr) -> TokenStream {
24 let call_site = s.span();
25 let s = s.value();
26
27 let mut fmt_string = String::new();
28
29 let mut args: Vec<Expr> = vec![];
30
31 let s: Vec<char> = s.chars().collect();
32 let mut s = &s[0..];
33
34 while !s.is_empty() {
35 if s[0] == '}' {
36 if s.len() >= 2 && s[1] == '}' {
37 fmt_string.push_str("}}");
38 s = &s[2..];
39 continue;
40 } else {
41 panic!("incorrect occurence of `}`");
42 }
43 }
44
45 if s[0] != '{' {
46 fmt_string.push(s[0]);
47 s = &s[1..];
48 continue;
49 }
50
51 if s.len() >= 2 && s[1] == '{' {
53 fmt_string.push_str("{{");
54 s = &s[2..];
55 continue;
56 }
57
58 s = &s[1..];
60
61 let mut expr = vec![];
62 let mut level = 1;
63
64 while !s.is_empty() {
66 let c = s[0];
67 s = &s[1..];
68
69 if c == '}' {
70 level -= 1;
71 if level == 0 {
72 break;
73 }
74 } else if c == '{' {
75 level += 1;
76 }
77
78 expr.push(c);
79 }
80
81 let mut manip_ix = None;
82
83 for i in 0..expr.len() {
85 if !(i >= 1 && expr[i - 1] == ':')
86 && expr[i] == ':'
87 && !(i + 1 < expr.len() && expr[i + 1] == ':')
88 {
89 manip_ix = Some(i);
90 }
91 }
92
93 let (expr, manip) = if let Some(manip_ix) = manip_ix {
94 (&expr[..manip_ix], &expr[manip_ix..])
95 } else {
96 (&expr[..], &[] as &[char])
97 };
98
99 let expr: String = expr.iter().collect();
100 let manip: String = manip.iter().collect();
101
102 fmt_string.push_str(&format!("{{{}}}", manip));
103
104 let expr: Expr = parse_str(&expr).expect(&format!("Failed to parse: `{}`", &expr));
105
106 let expr = rewrite_site(quote! { #expr }, call_site);
107 let expr: Expr = parse_quote! { #expr };
108
109 args.push(expr);
110 }
111
112 macro_call(fmt_string, args)
113}
114
115#[proc_macro_hack]
116pub fn format(input: TokenStream) -> TokenStream {
117 gen(
118 |fmt_string, args| quote!(std::format!(#fmt_string #(, #args )*)).into(),
119 parse_macro_input!(input as LitStr),
120 )
121}
122
123#[proc_macro_hack]
124pub fn print(input: TokenStream) -> TokenStream {
125 gen(
126 |fmt_string, args| quote!(std::print!(#fmt_string #(, #args )*)).into(),
127 parse_macro_input!(input as LitStr),
128 )
129}
130
131#[proc_macro_hack]
132pub fn println(input: TokenStream) -> TokenStream {
133 gen(
134 |fmt_string, args| quote!(std::println!(#fmt_string #(, #args )*)).into(),
135 parse_macro_input!(input as LitStr),
136 )
137}
138
139#[proc_macro_hack]
140pub fn eprint(input: TokenStream) -> TokenStream {
141 gen(
142 |fmt_string, args| quote!(std::eprint!(#fmt_string #(, #args )*)).into(),
143 parse_macro_input!(input as LitStr),
144 )
145}
146
147#[proc_macro_hack]
148pub fn eprintln(input: TokenStream) -> TokenStream {
149 gen(
150 |fmt_string, args| quote!(std::eprintln!(#fmt_string #(, #args )*)).into(),
151 parse_macro_input!(input as LitStr),
152 )
153}
154
155fn parse_write_arg(input: TokenStream) -> (Expr, LitStr) {
156 let parser = Punctuated::<Expr, Token![,]>::parse_terminated;
157 let args = parser.parse(input).unwrap();
158 if args.len() != 2 {
159 panic!("too many arguments");
160 }
161 let mut it = args.iter();
162 let dst = it.next().unwrap();
163 let fmt = it.next().unwrap();
164 (dst.clone(), parse_quote! { #fmt })
165}
166
167#[proc_macro_hack]
168pub fn write(input: TokenStream) -> TokenStream {
169 let (dst, fmt) = parse_write_arg(input);
170 gen(
171 |fmt_string, args| quote!(std::write!(#dst, #fmt_string #(, #args )*)).into(),
172 fmt,
173 )
174}
175
176#[proc_macro_hack]
177pub fn writeln(input: TokenStream) -> TokenStream {
178 let (dst, fmt) = parse_write_arg(input);
179 gen(
180 |fmt_string, args| quote!(std::writeln!(#dst, #fmt_string #(, #args )*)).into(),
181 fmt,
182 )
183}