mini_paste_proc_macro/
mod.rs1extern crate proc_macro;
2use ::proc_macro::{*, TokenTree as TT};
3use ::core::iter;
4
5macro_rules! matches {(
6 $expr:expr,
7 $(|)? $($pat:pat)|+
8 $(if $guard:expr)?
9 $(,)?
10) => (match $expr {
11 $(| $pat)+
12 $(if $guard)?
13 => true,
14 | _
15 => false,
16})}
17
18#[proc_macro] pub
19fn item (input: TokenStream)
20 -> TokenStream
21{
22 match try_map(input) {
23 | Ok(it) => it,
24 | Err((ref err_msg, span)) => compile_error(err_msg, span),
25 }
26}
27
28fn compile_error(err_msg: &'_ str, span: Span)
29 -> TokenStream
30{
31 macro_rules! spanned {($expr:expr) => ({
32 let mut it = $expr;
33 it.set_span(span);
34 it
35 })}
36 Vec::<TokenTree>::into_iter(vec![
37 TT::Ident(Ident::new("compile_error", span)),
38 TT::Punct(spanned!(Punct::new('!', Spacing::Alone))),
39 TT::Group(spanned!(Group::new(
40 Delimiter::Brace,
41 iter::once(TT::Literal(
42 spanned!(Literal::string(err_msg))
43 )).collect(),
44 ))),
45 ])
46 .collect()
47}
48
49fn try_map (input: TokenStream)
50 -> Result<TokenStream, (&'static str, Span)>
51{
52 input.into_iter().map(|mut tt| Ok({
53 if let TT::Group(ref group) = tt {
54 let span = tt.span();
55 tt = TT::Group(Group::new(
56 group.delimiter(),
57 try_map(group.stream())?
58 ));
59 tt.set_span(span);
60 }
61 match tt {
62 | TT::Group(ref group)
63 if true
64 && group.delimiter() == Delimiter::Bracket
65 && matches!(
66 group.stream().into_iter().next(),
67 Some(TT::Punct(p))
68 if p.as_char() == '<'
69 ,
70 )
71 => {
72 let mut tokens: Box<dyn Iterator<Item = TokenTree>> =
73 Box::new(group.stream().into_iter())
74 ;
75 let _ = tokens.next();
76 let ref mut ident = String::new();
77 loop { match tokens.next() {
78 | None
79 => {
80 return Err((
81 "Missing `>` before the closing `]`",
82 tt.span(),
83 ));
84 },
85 | Some(TT::Punct(p))
86 if p.as_char() == '>'
87 => if let Some(unexpected_tt) = tokens.next() {
88 return Err((
89 "Unexpected trailing token after the terminating `>`",
90 unexpected_tt.span(),
91 ));
92 } else {
93 break;
94 },
95 | Some(TT::Literal(lit))
96 => {
97 let ref s = lit.to_string();
98 if s.chars().all(|c| matches!(c,
99 'a' ..= 'z' |
100 'A' ..= 'Z' |
101 '0' ..= '9' |
102 '_'
103 ))
104 {
105 ident.push_str(s);
106 } else {
107 return Err((
108 "Cannot be converted into an identifier",
109 lit.span(),
110 ));
111 }
112 },
113 | Some(TT::Ident(it))
114 => {
115 ident.push_str(&it.to_string());
116 },
117 | Some(TT::Group(it))
118 if it.delimiter() == Delimiter::None
119 => {
120 tokens = Box::new(it.stream().into_iter().chain(tokens));
121 continue;
122 },
123 | Some(it @ TT::Group(_))
124 => {
125 return Err((
126 "Unexpected group",
127 it.span(),
128 ));
129 },
130 | Some(it @ TT::Punct(_))
131 => {
132 return Err((
133 "Unexpected punct",
134 it.span(),
135 ));
136 },
137 }}
138 return Ok(TT::Ident(Ident::new(ident, tt.span())));
139 },
140 | _ => {},
141 }
142 tt
143 })).collect()
144}
145
146#[doc(hidden)]
147#[proc_macro_derive(__expr_hack__)] pub
148fn __expr_hack__ (input: TokenStream)
149 -> TokenStream
150{
151 let mut tokens = input.into_iter();
165 let _ = tokens.by_ref().take(2).for_each(drop);
167 let mut tokens = if let Some(TT::Group(it)) = tokens.next() { it } else {
169 panic!()
170 }.stream().into_iter();
171 let _ = tokens.by_ref().take(2).for_each(drop);
173 let mut tokens = if let Some(TT::Group(it)) = tokens.next() { it } else {
175 panic!()
176 }.stream().into_iter();
177 let _ = tokens.by_ref().take(2).for_each(drop);
179 let input = if let Some(TT::Group(it)) = tokens.next() { it } else {
181 panic!()
182 }.stream();
183 let ret = match try_map(input) {
184 | Ok(it) => it,
185 | Err((ref err_msg, span)) => return compile_error(err_msg, span),
186 };
187 let span = Span::call_site();
188 Vec::<TokenTree>::into_iter(vec![
189 TT::Ident(Ident::new("macro_rules", span)),
190 TT::Punct(Punct::new('!', Spacing::Alone)),
191 TT::Ident(Ident::new("__mini_paste__Hack__", span)),
192 TT::Group(Group::new(
193 Delimiter::Brace,
194 Vec::<TokenTree>::into_iter(vec![
195 TT::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
196 TT::Punct(Punct::new('=', Spacing::Joint)),
197 TT::Punct(Punct::new('>', Spacing::Alone)),
198 TT::Group(Group::new(
199 Delimiter::Parenthesis,
200 ret,
201 )),
202 ]).collect(),
203 )),
204 ])
205 .collect()
206}