with_builtin_macros_proc_macros/
lib.rs1#![forbid(unsafe_code)]
2#![allow(nonstandard_style, unused_imports)]
3
4extern crate proc_macro;
6
7use ::proc_macro::{
8 TokenStream,
9};
10use ::proc_macro2::{
11 Group,
12 Span,
13 TokenStream as TokenStream2,
14};
15use ::quote::{
16 quote,
17 quote_spanned,
18 ToTokens,
19};
20use ::syn::{*,
21 parse::{
22 Parse,
23 Parser,
24 ParseStream,
25 },
26 punctuated::Punctuated,
27 spanned::Spanned,
28 Result,
29};
30
31use self::builtin_macros as builtin;
32
33use compile_time_string::CompileTimeString;
34mod compile_time_string;
35
36supported_builtin_macros![
37 concat!(...),
38 concat_idents!(...),
39 env!(...),
40 option_env!(...),
41 include_from_root!(...),
42 include_bytes_from_root!(...),
43 include_str_from_root!(...),
44 stringify!(...),
45];
46
47#[proc_macro] pub
119fn with_builtin (input: TokenStream)
120 -> TokenStream
121{
122 let Input {
123 metavar,
124 macro_invocation: MacroInvocation { builtin_macro, macro_input },
125 template,
126 } = parse_macro_input!(input);
127 let input_span = macro_input.span();
128 let expansion = match builtin_macro.call_on(macro_input) {
129 | Ok(it) => it,
130 | Err(mut err) => {
131 if format!("{:?}", err.span()) == format!("{:?}", Span::call_site()) {
132 err = Error::new(input_span, &err.to_string());
133 }
134 return err.to_compile_error().into();
135 },
136 };
137 map_replace(&metavar, &expansion, template)
138 .into()
139}
140
141struct Input {
142 metavar: Ident,
143 macro_invocation: MacroInvocation,
144 template: TokenStream2,
145}
146
147impl Parse for Input {
148 fn parse (input: ParseStream<'_>)
149 -> Result<Self>
150 {
151 let _: Token![ let ] = input.parse()?;
152 let _: Token![ $ ] = input.parse()?;
153 let metavar: Ident = input.parse()?;
154 let _: Token![ = ] = input.parse()?;
155 let macro_invocation: MacroInvocation = input.parse()?;
156 let _: Token![ in ] = input.parse()?;
157 let template; braced!(template in input);
158 Ok(Input {
159 metavar,
160 macro_invocation,
161 template: template.parse()?,
162 })
163 }
164}
165
166struct MacroInvocation {
167 builtin_macro: BuiltinMacro,
168 macro_input: TokenStream2,
169}
170
171impl Parse for MacroInvocation {
172 fn parse(input: ParseStream<'_>)
173 -> Result<MacroInvocation>
174 {
175 let builtin_macro: BuiltinMacro = input.parse()?;
176 let _: Token![ ! ] = input.parse()?;
177 let macro_input = { let g: Group = input.parse()?; g.stream() };
178 Ok(Self {
179 builtin_macro,
180 macro_input,
181 })
182 }
183}
184
185macro_rules! supported_builtin_macros {(
186 $(
187 $( #[cfg $cfg:tt] )?
188 $macro_name:ident !(...)
189 ),+ $(,)?
190) => (
191 mod kw {
192 $(
193 $( #[cfg $cfg] )?
194 ::syn::custom_keyword!($macro_name);
195 )+
196 }
197
198 mod builtin_macros {
199 use super::*;
200
201 $(
202 $( #[cfg $cfg] )?
203 pub
204 mod $macro_name;
205 )+
206 }
207
208 enum BuiltinMacro {
209 $(
210 $( #[cfg $cfg] )?
211 $macro_name(kw::$macro_name),
212 )*
213 }
214
215 impl Parse for BuiltinMacro {
216 fn parse (input: ParseStream<'_>)
217 -> Result<Self>
218 {
219 let lookahead = input.lookahead1();
220 match () {
221 $(
222 $( #[cfg $cfg] )?
223 _case if lookahead.peek(kw::$macro_name) => {
224 input
225 .parse::<kw::$macro_name>()
226 .map(Self::$macro_name)
227 },
228 )*
229 _default => Err(lookahead.error()),
230 }
231 }
232 }
233
234 impl Spanned for BuiltinMacro {
235 fn span (self: &'_ Self)
236 -> Span
237 {
238 match *self {
239 $(
240 $( #[cfg $cfg] )?
241 | Self::$macro_name(ref it) => it.span(),
242 )+
243 }
244 }
245 }
246
247 impl BuiltinMacro {
248 fn call_on (self: &'_ Self, args: TokenStream2)
249 -> Result<TokenStream2>
250 {
251 match *self {
252 $(
253 $( #[cfg $cfg] )?
254 | Self::$macro_name(_) => {
255 builtin::$macro_name::call_on(args)
256 .map(|it| it.into_token_stream())
257 },
258 )+
259 }
260 }
261 }
262)}
263use supported_builtin_macros;
264
265trait MockToTokens {
266 fn into_token_stream (self: Self)
267 -> TokenStream2
268 ;
269
270 const _impl: () = {
271 impl MockToTokens for Vec<u8> {
272 fn into_token_stream (self: Vec<u8>)
273 -> TokenStream2
274 {
275 let each_byte = &self;
276 match self.len() {
277 | 0 => quote!(
278 [0_u8; 0]
279 ),
280 | _ => quote!(
281 [
282 #(#each_byte),*
283 ]
284 ),
285 }
286 }
287 }
288 };
289}
290
291fn map_replace (
292 metavar: &'_ Ident,
293 tokenized: &'_ TokenStream2,
294 template: TokenStream2,
295) -> TokenStream2
296{
297 use ::proc_macro2::{*, TokenTree as TT};
298 let mut tokens = template.into_iter().peekable();
299 let mut ret = TokenStream2::new();
300 loop {
301 match (tokens.next(), tokens.peek()) {
302 | (
303 Some(TT::Punct(dollar)),
304 Some(&TT::Ident(ref ident)),
305 )
306 if dollar.as_char() == '$'
307 && ident == metavar
308 => {
309 drop(tokens.next());
310 ret.extend(tokenized.clone());
311 },
312
313 | (Some(TT::Group(group)), _) => {
314 ret.extend(Some(TT::Group(Group::new(
315 group.delimiter(),
316 map_replace(metavar, tokenized, group.stream()),
317 ))));
318 },
319
320 | (None, _) => break,
321
322 | (tt, _) => ret.extend(tt),
323 }
324 }
325 ret
326}
327
328#[proc_macro] pub
329fn with_eager_expansions(input: TokenStream)
330 -> TokenStream
331{
332 map_replace_eager_expansions(input.into()).into()
333}
334
335
336fn map_replace_eager_expansions (
337 template: TokenStream2,
338) -> TokenStream2
339{
340 use ::proc_macro2::{*, TokenTree as TT};
341 let mut tokens = template.into_iter().peekable();
342 let mut ret = TokenStream2::new();
343 loop {
344 let tt = tokens.next();
345 match (&tt, tokens.peek()) {
346 | (
348 &Some(TT::Punct(ref dollar)),
349 Some(&TT::Group(ref g)),
350 ) if dollar.as_char() == '#'
351 && g.delimiter() == Delimiter::Brace
352 => {
353 if let Ok(MacroInvocation { builtin_macro, macro_input }) =
354 parse2(g.stream())
355 {
356 drop(tokens.next());
357 let input_span = macro_input.span();
358 let expansion = match builtin_macro.call_on(macro_input) {
359 | Ok(it) => it,
360 | Err(mut err) => {
361 if format!("{:?}", err.span())
362 == format!("{:?}", Span::call_site())
363 {
364 err = Error::new(input_span, &err.to_string());
365 }
366 return err.to_compile_error().into();
367 },
368 };
369 ret.extend(expansion);
370 } else {
371 ret.extend(tt);
372 }
373 },
374
375 | (&Some(TT::Group(ref group)), _) => {
376 ret.extend(Some(TT::Group(Group::new(
377 group.delimiter(),
378 map_replace_eager_expansions(group.stream()),
379 ))));
380 },
381
382 | (None, _) => break,
383
384 | (_, _) => ret.extend(tt),
385 }
386 }
387 ret
388}