Skip to main content

mini_paste_proc_macro/
mod.rs

1extern 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/** Not part of the public API **/ #[doc(hidden)]
147#[proc_macro_derive(__expr_hack__)] pub
148fn __expr_hack__ (input: TokenStream)
149  -> TokenStream
150{
151    // enum
152    // EnumName
153    // {
154    //     VariantName
155    //     =
156    //     (
157    //         stringify
158    //         !
159    //         (
160    //             <input>
161    //         )
162    // , 0).1,}
163
164    let mut tokens = input.into_iter();
165    // `enum EnumName`
166    let _ = tokens.by_ref().take(2).for_each(drop);
167    // `{ <tokens> }`
168    let mut tokens = if let Some(TT::Group(it)) = tokens.next() { it } else {
169        panic!()
170    }.stream().into_iter();
171    // `VariantName =`
172    let _ = tokens.by_ref().take(2).for_each(drop);
173    // `( <tokens> )`
174    let mut tokens = if let Some(TT::Group(it)) = tokens.next() { it } else {
175        panic!()
176    }.stream().into_iter();
177    // `stringify !`
178    let _ = tokens.by_ref().take(2).for_each(drop);
179    // `( <input> )`
180    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}