1extern crate proc_macro;
2extern crate regex;
3#[macro_use]
4extern crate lazy_static;
5#[macro_use]
6extern crate quote;
7use proc_macro2::Span;
8use proc_macro_hack::proc_macro_hack;
9use regex::Regex;
10use syn::{Token, Error};
11
12fn consume_expr(s: &str) -> (&str, String) {
13 lazy_static! {
14 static ref LITERAL: Regex = Regex::new(
17 concat!(r#"^('(?:\\[\s\S]+?|\\'|[^\\'])'"#,
18 r##"|"(?:\\[\s\S]|[^\\])+?")"##)).unwrap();
19 static ref RAW_STRING_START: Regex = Regex::new(
21 r##"^r(#*)""##).unwrap();
22 }
23
24 let mut s = s;
25 let mut expr = String::new();
26 let mut brace_count = 1;
27 while let Some(c) = s.chars().next() {
28 match c {
29 '{' => {
30 brace_count += 1;
31 s = &s[1..];
32 expr.push('{');
33 }
34 '}' => {
35 brace_count -= 1;
36 if brace_count == 0 {
37 return (s, expr);
38 } else {
39 expr.push('}');
40 s = &s[1..];
41 }
42 }
43 _ => {
44 let lit_match = LITERAL.find(s);
45 match lit_match {
46 Some(m) => {
47 s = &s[m.end()..];
48 expr.push_str(m.as_str());
49 continue;
52 }
53 None => (),
54 }
55 let raw_caps = RAW_STRING_START.captures(s);
56 match raw_caps {
57 Some(c) => {
60 let m = c.get(0).unwrap();
61 s = &s[m.end()..];
62 expr.push_str(m.as_str());
63
64 let cap = c.get(1).unwrap();
65 let hash_count = cap.end() - cap.start();
66 let end_r =
67 Regex::new(&format!(r##"^[\s\S]+?"#{{{}}}"##, hash_count)).unwrap();
68 let em = end_r.find(s).expect("unclosed internal raw string");
69
70 expr.push_str(em.as_str());
71 s = &s[em.end()..];
72 }
73 None => {
74 expr.push(c);
75 s = &s[c.len_utf8()..];
76 }
77 }
78 }
79 }
80 }
81
82 panic!("unbalanced {");
83}
84
85macro_rules! def_ifmt_macro {
86 ($name:ident, $to_wrap:ident) => {
87 #[proc_macro_hack]
88 pub fn $name(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
89 use syn::parse_macro_input;
90 let FormatContents{fmt, args} = parse_macro_input!(tokens as FormatContents);
91 (quote! {
92 ::std::$to_wrap!(#fmt, #(#args),*)
93 }).into()
94 }
95 }
96}
97
98def_ifmt_macro!(iformat, format);
99
100def_ifmt_macro!(iprint, print);
101
102def_ifmt_macro!(iprintln, println);
103
104def_ifmt_macro!(ieprint, eprint);
105
106def_ifmt_macro!(ieprintln, eprintln);
107
108def_ifmt_macro!(iformat_args, format_args);
109
110def_ifmt_macro!(ipanic, panic);
111
112struct FormatSpec {
113 spec: String,
114}
115
116fn parse_format_type(id: &str, span: Span, input: syn::parse::ParseStream) -> syn::parse::Result<String> {
117 let mut out = String::new();
118 if id == "x" || id == "X" {
119 out.push_str(&id);
120 if input.peek(Token![?]) {
121 input.parse::<Token![?]>()?;
122 out.push('?');
123 }
124 } else if id == "o" || id == "p" || id == "b" || id == "e" || id == "E" {
125 out.push_str(&id);
126 } else if id == "s" {
127 out.push('e');
128 } else if id == "S" {
129 out.push('E');
130 } else {
131 return Err(Error::new(span, format!("{} is not a valid format type", id)));
132 }
133 Ok(out)
134}
135
136fn parse_precision_type(input: syn::parse::ParseStream) -> syn::parse::Result<String> {
137 let mut spec = String::new();
140
141 let span = input.cursor().span();
142
143 if input.peek(Token![.]) {
145 input.parse::<Token![.]>()?;
146 spec.push('.');
147 let lit = input.parse::<syn::LitInt>()?;
149 let lit_str = lit.to_string();
150 if lit.suffix() != "" {
151 spec.push_str(&lit_str[..lit_str.len() - lit.suffix().len()]);
152 spec.push_str(&parse_format_type(&lit.suffix(), span, input)?);
154 } else {
155 spec.push_str(&lit_str);
156 }
157 }
158
159 if input.peek(Token![?]) {
161 input.parse::<Token![?]>()?;
162 spec.push('?');
163 } else if input.peek(syn::Ident) {
165 let id = input.parse::<syn::Ident>()?.to_string();
166 spec.push_str(&parse_format_type(&id, span, input)?);
167 }
168
169 Ok(spec)
170}
171
172
173impl syn::parse::Parse for FormatSpec {
174 fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
175 input.parse::<Token![;]>()?;
187 let mut spec = String::new();
188 if input.peek(syn::LitChar) {
190 let pad = input.parse::<syn::LitChar>().unwrap();
191 spec.push(pad.value());
192 let lookahead = input.lookahead1();
193 if lookahead.peek(Token![<]) {
194 input.parse::<Token![<]>()?;
195 spec.push('<');
196 } else if lookahead.peek(Token![>]) {
197 input.parse::<Token![>]>()?;
198 spec.push('>');
199 } else if lookahead.peek(Token![^]) {
200 input.parse::<Token![^]>()?;
201 spec.push('^');
202 } else {
203 return Err(lookahead.error());
204 }
205 } else if input.peek(Token![<]) {
206 input.parse::<Token![<]>()?;
207 spec.push('<');
208 } else if input.peek(Token![>]) {
209 input.parse::<Token![>]>()?;
210 spec.push('>');
211 } else if input.peek(Token![^]) {
212 input.parse::<Token![^]>()?;
213 spec.push('^');
214 }
215
216 if input.peek(Token![+]) {
218 input.parse::<Token![+]>()?;
219 spec.push('+');
220 } else if input.peek(Token![-]) {
221 input.parse::<Token![-]>()?;
222 spec.push('-');
223 }
224
225 if input.peek(Token![#]) {
227 input.parse::<Token![#]>()?;
228 spec.push('#');
229 }
230
231 if input.peek(syn::LitFloat) {
237 let span = input.cursor().span();
239 let lit = input.parse::<syn::LitFloat>()?;
240 let lit_str = lit.to_string();
241 if lit.suffix() != "" {
242 spec.push_str(&lit_str[..lit_str.len()-lit.suffix().len()]);
244 spec.push_str(&parse_format_type(&lit.suffix(), span, input)?);
246 } else {
247 spec.push_str(&lit_str);
249 if input.peek(syn::Ident) {
251 let id = input.parse::<syn::Ident>()?.to_string();
252 spec.push_str(&parse_format_type(&id, span, input)?);
253 }
254 }
255 } else if input.peek(syn::LitInt) {
256 let span = input.cursor().span();
258 let lit = input.parse::<syn::LitInt>()?;
259 let lit_str = lit.to_string();
260 if lit.suffix() != "" {
261 spec.push_str(&lit_str[..lit_str.len()-lit.suffix().len()]);
262 spec.push_str(&parse_format_type(&lit.suffix(), span, input)?);
264 } else {
265 spec.push_str(&lit_str);
267 spec.push_str(&parse_precision_type(input)?);
268 }
269 } else {
270 spec.push_str(&parse_precision_type(input)?);
271 }
272
273 Ok(FormatSpec { spec: spec })
274 }
275}
276
277struct FormatContents {
278 fmt: String,
279 args: Vec<syn::Expr>,
280}
281
282impl FormatContents {
283 fn parse_inlit(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
284 lazy_static! {
285 static ref SPEC: Regex =
286 Regex::new(r":([\s\S]?[<^>])?([\+\-])?(#)?(0)?(\d+|\*)?(\.\d+)?(\?|\w+)?$").unwrap();
287 }
288 let format_str = input.parse::<syn::LitStr>()?.value();
289
290 let mut format_lit = String::from("");
291 let mut exprs = vec![];
292 let mut s: &str = &format_str;
293 while let Some(c) = s.chars().next() {
294 match c {
295 '{' => {
296 s = &s[1..];
297 match s.chars().next() {
298 Some('{') => format_lit.push_str("{{"),
299 _ => {
300 let (new_s, mut expr) = consume_expr(s);
301 s = new_s;
302 let spec_match = SPEC.find(&expr);
303 match spec_match {
304 Some(m) => {
305 format_lit.push('{');
306 format_lit.push_str(m.as_str());
307 format_lit.push('}');
308 let si = m.start();
309
310 expr.truncate(si);
311 }
312 None => {
313 format_lit.push_str("{}");
314 }
315 }
316
317 exprs.push(syn::parse_str::<syn::Expr>(&expr)?);
318 }
319 }
320 }
321 '}' => {
322 s = &s[1..];
323 if s.chars().next() == Some('}') {
324 format_lit.push_str("}}");
325 } else {
326 panic!("unmatched }");
327 }
328 }
329 _ => format_lit.push(c),
330 }
331 s = &s[c.len_utf8()..];
332 }
333
334 Ok(FormatContents{fmt: format_lit, args: exprs})
335 }
336
337 fn parse_outlit(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
338 let mut expect_expr = true; let mut fmt = String::new();
340 let mut args = vec![];
341
342 while !input.is_empty() {
343 let lookahead = input.lookahead1();
344 if lookahead.peek(syn::LitStr) {
345 let ls = input.parse::<syn::LitStr>().unwrap();
346 fmt += &ls.value().replace("{", "{{").replace("}", "}}");
347 expect_expr = true;
348 } else if expect_expr {
349 let expr = input.parse::<syn::Expr>()?;
356 args.push(expr);
357 fmt.push_str("{");
358 if input.peek(Token![;]) {
359 let spec = &input.parse::<FormatSpec>()?.spec;
360 if !spec.is_empty() {
361 fmt.push_str(":");
362 fmt.push_str(spec);
363 }
364 }
365 fmt.push('}');
366 expect_expr = false;
367 } else {
368 return Err(lookahead.error());
369 }
370 }
371 Ok(FormatContents{fmt: fmt, args: args})
372 }
373}
374
375impl syn::parse::Parse for FormatContents {
376 fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
377 let forked = input.fork();
379 if forked.peek(syn::LitStr) && {forked.parse::<syn::LitStr>()?; forked.is_empty()} {
380 FormatContents::parse_inlit(input)
381 } else {
382 FormatContents::parse_outlit(input)
383 }
384 }
385}
386
387struct WriteFormat {
388 buf: syn::Expr,
389 fmt_contents: FormatContents
390}
391
392impl syn::parse::Parse for WriteFormat {
393 fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
394 Ok(WriteFormat {
395 buf: input.parse::<syn::Expr>()?,
396 fmt_contents: if !input.is_empty() {
397 input.parse::<Token![,]>()?;
398 input.parse::<FormatContents>()?
399 } else {
400 FormatContents{fmt: "".to_string(), args: vec![]}
401 }
402 })
403 }
404}
405
406#[proc_macro_hack]
407pub fn iwrite(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
408 use syn::parse_macro_input;
409 let WriteFormat{buf, fmt_contents: FormatContents{fmt, args}} = parse_macro_input!(tokens as WriteFormat);
410 (quote! {
411 ::std::write!(#buf, #fmt, #(#args),*)
412 }).into()
413}
414
415#[proc_macro_hack]
416pub fn iwriteln(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
417 use syn::parse_macro_input;
418 let WriteFormat{buf, fmt_contents: FormatContents{fmt, args}} = parse_macro_input!(tokens as WriteFormat);
419 (quote! {
420 ::std::writeln!(#buf, #fmt, #(#args),*)
421 }).into()
422}