1extern crate proc_macro;
2extern crate proc_macro2;
3
4use proc_macro::TokenStream;
5use proc_macro2::Span;
6use quote::quote;
7use syn::parse::{Parse, ParseStream, Result};
8use syn::parse_macro_input;
9use syn::punctuated::Punctuated;
10use syn::{Expr, ExprLit, ExprPath, Lit, LitStr, Token};
11
12mod parsers;
13
14#[proc_macro]
17pub fn eve_format(input: TokenStream) -> TokenStream {
18 let call = parse_macro_input!(input as EVEFormat);
19 let fmt = call.fmt;
20 let args = call.args;
21
22 let fmt_src = &fmt.value().into_bytes()[..];
23 let mut format_chars: Vec<u8> = Vec::with_capacity(fmt_src.len());
24 let mut arg_elems: Punctuated<Expr, syn::Token![,]> = Punctuated::new();
25
26 let int_variant_path: ExprPath =
27 syn::parse_str("::evegfx::commands::strfmt::Argument::Int").unwrap();
28 let uint_variant_path: ExprPath =
29 syn::parse_str("::evegfx::commands::strfmt::Argument::UInt").unwrap();
30 let char_variant_path: ExprPath =
31 syn::parse_str("::evegfx::commands::strfmt::Argument::Char").unwrap();
32 let string_variant_path: ExprPath =
33 syn::parse_str("::evegfx::commands::strfmt::Argument::String").unwrap();
34
35 let mut remain = fmt_src.clone();
36 let mut next_arg = 0;
37 let mut needs_fmt = false;
38 while remain.len() > 0 {
39 use parsers::Token::*;
40 let (token, next) = parsers::next_token(remain);
41 remain = next;
42 match token {
43 Literal(bytes) => {
44 format_chars.extend(bytes);
45 }
46 Verb(bytes) => {
47 needs_fmt = true;
48 if next_arg >= args.len() {
49 let err = syn::Error::new(
50 fmt.span(),
51 format!("not enough arguments to populate {} verbs", next_arg + 1),
52 );
53 return err.into_compile_error().into();
54 }
55 let given_expr = args[next_arg].clone();
56 next_arg += 1;
57
58 format_chars.extend(bytes);
59 let mode = *bytes.last().unwrap();
66 match mode {
67 b'd' | b'i' => {
68 let arg_expr = enum_variant_expr(int_variant_path.clone(), given_expr);
69 arg_elems.push(arg_expr);
70 }
71 b'u' | b'o' | b'x' | b'X' => {
72 let arg_expr = enum_variant_expr(uint_variant_path.clone(), given_expr);
73 arg_elems.push(arg_expr);
74 }
75 b'c' => {
76 let arg_expr = enum_variant_expr(char_variant_path.clone(), given_expr);
77 arg_elems.push(arg_expr);
78 }
79 b's' => {
80 let arg_expr = enum_variant_expr(string_variant_path.clone(), given_expr);
81 arg_elems.push(arg_expr);
82 }
83 _ => {
85 use std::convert::TryInto;
88 let letter: char = mode.try_into().unwrap();
89
90 let err = syn::Error::new(
91 fmt.span(),
92 format!("unsupported format verb '%{}'", letter),
93 );
94 return err.into_compile_error().into();
95 }
96 }
97 }
98 Percent(bytes) => {
99 needs_fmt = true;
100 format_chars.extend(bytes);
101 }
102 Null(_) => {
103 needs_fmt = true;
108 format_chars.extend(b"%c");
109 let arg_expr = enum_variant_expr(
110 uint_variant_path.clone(),
111 Expr::Lit(ExprLit {
112 attrs: Vec::new(),
113 lit: Lit::Int(syn::LitInt::new("0", fmt.span())),
114 }),
115 );
116 arg_elems.push(arg_expr);
117 }
118 Unterminated(_) => {
119 let err = syn::Error::new(fmt.span(), "unterminated format sequence");
120 return err.into_compile_error().into();
121 }
122 Invalid(_) => {
123 let err = syn::Error::new(fmt.span(), "invalid format sequence");
124 return err.into_compile_error().into();
125 }
126 };
127 }
128
129 if next_arg < args.len() {
130 use syn::spanned::Spanned;
131 let error_span = args[next_arg].span();
132 let err = syn::Error::new(error_span, "too many arguments for format string");
133 return err.into_compile_error().into();
134 }
135
136 format_chars.push(0);
138
139 let mut args: Punctuated<Expr, syn::Token![,]> = Punctuated::new();
140 args.push(byte_string_expr(&format_chars, fmt.span()));
141 if needs_fmt {
142 args.push(array_slice_expr(arg_elems));
143 }
144
145 if needs_fmt {
146 quote!(
147 ::evegfx::commands::strfmt::Message::new(#args)
148 )
149 } else {
150 quote!(
151 ::evegfx::commands::strfmt::Message::new_literal(#args)
152 )
153 }
154 .into()
155}
156
157fn enum_variant_expr(path: ExprPath, val: Expr) -> syn::Expr {
158 let mut args: Punctuated<Expr, syn::Token![,]> = Punctuated::new();
159 args.push(val);
160 syn::Expr::Call(syn::ExprCall {
161 attrs: Vec::new(),
162 func: Box::new(Expr::Path(path)),
163 args: args,
164 paren_token: syn::token::Paren {
165 span: Span::call_site(),
166 },
167 })
168}
169
170fn byte_string_expr(bytes: &[u8], span: proc_macro2::Span) -> syn::Expr {
171 syn::Expr::Lit(syn::ExprLit {
172 attrs: Vec::new(),
173 lit: syn::LitByteStr::new(bytes, span).into(),
174 })
175}
176
177fn array_slice_expr(elems: Punctuated<syn::Expr, syn::Token![,]>) -> syn::Expr {
178 syn::Expr::Reference(syn::ExprReference {
179 attrs: Vec::new(),
180 and_token: syn::token::And {
181 spans: [Span::call_site()],
182 },
183 mutability: None,
184 expr: Box::new(syn::Expr::Array(syn::ExprArray {
185 attrs: Vec::new(),
186 bracket_token: syn::token::Bracket {
187 span: Span::call_site(),
188 },
189 elems: elems,
190 })),
191 raw: std::default::Default::default(),
192 })
193}
194
195struct EVEFormat {
196 fmt: LitStr,
197 args: Punctuated<Expr, syn::Token![,]>,
198}
199
200impl Parse for EVEFormat {
201 fn parse(input: ParseStream) -> Result<Self> {
202 let fmt: LitStr = input.parse()?;
203 let args: Punctuated<Expr, syn::Token![,]> = if let Ok(_) = input.parse::<Token![,]>() {
204 Punctuated::parse_terminated(input)?
205 } else {
206 Punctuated::new()
207 };
208
209 Ok(EVEFormat {
210 fmt: fmt,
211 args: args,
212 })
213 }
214}