]>
Commit | Line | Data |
---|---|---|
3b2f2976 XL |
1 | //! Quasi-quoting without a Syntex dependency, intended for use with [Macros |
2 | //! 1.1](https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md). | |
3 | //! | |
4 | //! ```toml | |
5 | //! [dependencies] | |
6 | //! quote = "0.3" | |
7 | //! ``` | |
8 | //! | |
9 | //! ```rust,ignore | |
10 | //! #[macro_use] | |
11 | //! extern crate quote; | |
12 | //! ``` | |
13 | //! | |
14 | //! Interpolation is done with `#var`: | |
15 | //! | |
16 | //! ```text | |
17 | //! let tokens = quote! { | |
18 | //! struct SerializeWith #generics #where_clause { | |
19 | //! value: &'a #field_ty, | |
20 | //! phantom: ::std::marker::PhantomData<#item_ty>, | |
21 | //! } | |
22 | //! | |
23 | //! impl #generics serde::Serialize for SerializeWith #generics #where_clause { | |
24 | //! fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error> | |
25 | //! where S: serde::Serializer | |
26 | //! { | |
27 | //! #path(self.value, s) | |
28 | //! } | |
29 | //! } | |
30 | //! | |
31 | //! SerializeWith { | |
32 | //! value: #value, | |
33 | //! phantom: ::std::marker::PhantomData::<#item_ty>, | |
34 | //! } | |
35 | //! }; | |
36 | //! ``` | |
37 | //! | |
38 | //! Repetition is done using `#(...)*` or `#(...),*` very similar to `macro_rules!`: | |
39 | //! | |
40 | //! - `#(#var)*` - no separators | |
41 | //! - `#(#var),*` - the character before the asterisk is used as a separator | |
42 | //! - `#( struct #var; )*` - the repetition can contain other things | |
43 | //! - `#( #k => println!("{}", #v), )*` - even multiple interpolations | |
44 | //! | |
45 | //! The return type of `quote!` is `quote::Tokens`. Tokens can be interpolated into | |
46 | //! other quotes: | |
47 | //! | |
48 | //! ```text | |
49 | //! let t = quote! { /* ... */ }; | |
50 | //! return quote! { /* ... */ #t /* ... */ }; | |
51 | //! ``` | |
52 | //! | |
53 | //! Call `to_string()` or `as_str()` on a Tokens to get a `String` or `&str` of Rust | |
54 | //! code. | |
55 | //! | |
56 | //! The `quote!` macro relies on deep recursion so some large invocations may fail | |
57 | //! with "recursion limit reached" when you compile. If it fails, bump up the | |
58 | //! recursion limit by adding `#![recursion_limit = "128"]` to your crate. An even | |
59 | //! higher limit may be necessary for especially large invocations. | |
60 | ||
61 | mod tokens; | |
62 | pub use tokens::Tokens; | |
63 | ||
64 | mod to_tokens; | |
65 | pub use to_tokens::{ToTokens, ByteStr, Hex}; | |
66 | ||
67 | mod ident; | |
68 | pub use ident::Ident; | |
69 | ||
70 | /// The whole point. | |
71 | #[macro_export] | |
72 | macro_rules! quote { | |
73 | () => { | |
74 | $crate::Tokens::new() | |
75 | }; | |
76 | ||
77 | ($($tt:tt)+) => { | |
78 | { | |
79 | let mut _s = $crate::Tokens::new(); | |
80 | quote_each_token!(_s $($tt)*); | |
81 | _s | |
82 | } | |
83 | }; | |
84 | } | |
85 | ||
86 | // Extract the names of all #metavariables and pass them to the $finish macro. | |
87 | // | |
88 | // in: pounded_var_names!(then () a #b c #( #d )* #e) | |
89 | // out: then!(() b d e) | |
90 | #[macro_export] | |
91 | #[doc(hidden)] | |
92 | macro_rules! pounded_var_names { | |
93 | ($finish:ident ($($found:ident)*) # ( $($inner:tt)* ) $($rest:tt)*) => { | |
94 | pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*) | |
95 | }; | |
96 | ||
97 | ($finish:ident ($($found:ident)*) # [ $($inner:tt)* ] $($rest:tt)*) => { | |
98 | pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*) | |
99 | }; | |
100 | ||
101 | ($finish:ident ($($found:ident)*) # { $($inner:tt)* } $($rest:tt)*) => { | |
102 | pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*) | |
103 | }; | |
104 | ||
105 | ($finish:ident ($($found:ident)*) # $first:ident $($rest:tt)*) => { | |
106 | pounded_var_names!($finish ($($found)* $first) $($rest)*) | |
107 | }; | |
108 | ||
109 | ($finish:ident ($($found:ident)*) ( $($inner:tt)* ) $($rest:tt)*) => { | |
110 | pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*) | |
111 | }; | |
112 | ||
113 | ($finish:ident ($($found:ident)*) [ $($inner:tt)* ] $($rest:tt)*) => { | |
114 | pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*) | |
115 | }; | |
116 | ||
117 | ($finish:ident ($($found:ident)*) { $($inner:tt)* } $($rest:tt)*) => { | |
118 | pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*) | |
119 | }; | |
120 | ||
121 | ($finish:ident ($($found:ident)*) $ignore:tt $($rest:tt)*) => { | |
122 | pounded_var_names!($finish ($($found)*) $($rest)*) | |
123 | }; | |
124 | ||
125 | ($finish:ident ($($found:ident)*)) => { | |
126 | $finish!(() $($found)*) | |
127 | }; | |
128 | } | |
129 | ||
130 | // in: nested_tuples_pat!(() a b c d e) | |
131 | // out: ((((a b) c) d) e) | |
132 | // | |
133 | // in: nested_tuples_pat!(() a) | |
134 | // out: a | |
135 | #[macro_export] | |
136 | #[doc(hidden)] | |
137 | macro_rules! nested_tuples_pat { | |
138 | (()) => { | |
139 | &() | |
140 | }; | |
141 | ||
142 | (() $first:ident $($rest:ident)*) => { | |
143 | nested_tuples_pat!(($first) $($rest)*) | |
144 | }; | |
145 | ||
146 | (($pat:pat) $first:ident $($rest:ident)*) => { | |
147 | nested_tuples_pat!((($pat, $first)) $($rest)*) | |
148 | }; | |
149 | ||
150 | (($done:pat)) => { | |
151 | $done | |
152 | }; | |
153 | } | |
154 | ||
155 | // in: multi_zip_expr!(() a b c d e) | |
156 | // out: a.into_iter().zip(b).zip(c).zip(d).zip(e) | |
157 | // | |
158 | // in: multi_zip_iter!(() a) | |
159 | // out: a | |
160 | #[macro_export] | |
161 | #[doc(hidden)] | |
162 | macro_rules! multi_zip_expr { | |
163 | (()) => { | |
164 | &[] | |
165 | }; | |
166 | ||
167 | (() $single:ident) => { | |
168 | $single | |
169 | }; | |
170 | ||
171 | (() $first:ident $($rest:ident)*) => { | |
172 | multi_zip_expr!(($first.into_iter()) $($rest)*) | |
173 | }; | |
174 | ||
175 | (($zips:expr) $first:ident $($rest:ident)*) => { | |
176 | multi_zip_expr!(($zips.zip($first)) $($rest)*) | |
177 | }; | |
178 | ||
179 | (($done:expr)) => { | |
180 | $done | |
181 | }; | |
182 | } | |
183 | ||
184 | #[macro_export] | |
185 | #[doc(hidden)] | |
186 | macro_rules! quote_each_token { | |
187 | ($tokens:ident) => {}; | |
188 | ||
189 | ($tokens:ident # ! $($rest:tt)*) => { | |
190 | $tokens.append("#"); | |
191 | $tokens.append("!"); | |
192 | quote_each_token!($tokens $($rest)*); | |
193 | }; | |
194 | ||
195 | ($tokens:ident # ( $($inner:tt)* ) * $($rest:tt)*) => { | |
196 | for pounded_var_names!(nested_tuples_pat () $($inner)*) | |
197 | in pounded_var_names!(multi_zip_expr () $($inner)*) { | |
198 | quote_each_token!($tokens $($inner)*); | |
199 | } | |
200 | quote_each_token!($tokens $($rest)*); | |
201 | }; | |
202 | ||
203 | ($tokens:ident # ( $($inner:tt)* ) $sep:tt * $($rest:tt)*) => { | |
204 | for (_i, pounded_var_names!(nested_tuples_pat () $($inner)*)) | |
205 | in pounded_var_names!(multi_zip_expr () $($inner)*).into_iter().enumerate() { | |
206 | if _i > 0 { | |
207 | $tokens.append(stringify!($sep)); | |
208 | } | |
209 | quote_each_token!($tokens $($inner)*); | |
210 | } | |
211 | quote_each_token!($tokens $($rest)*); | |
212 | }; | |
213 | ||
214 | ($tokens:ident # [ $($inner:tt)* ] $($rest:tt)*) => { | |
215 | $tokens.append("#"); | |
216 | $tokens.append("["); | |
217 | quote_each_token!($tokens $($inner)*); | |
218 | $tokens.append("]"); | |
219 | quote_each_token!($tokens $($rest)*); | |
220 | }; | |
221 | ||
222 | ($tokens:ident # $first:ident $($rest:tt)*) => { | |
223 | $crate::ToTokens::to_tokens(&$first, &mut $tokens); | |
224 | quote_each_token!($tokens $($rest)*); | |
225 | }; | |
226 | ||
227 | ($tokens:ident ( $($first:tt)* ) $($rest:tt)*) => { | |
228 | $tokens.append("("); | |
229 | quote_each_token!($tokens $($first)*); | |
230 | $tokens.append(")"); | |
231 | quote_each_token!($tokens $($rest)*); | |
232 | }; | |
233 | ||
234 | ($tokens:ident [ $($first:tt)* ] $($rest:tt)*) => { | |
235 | $tokens.append("["); | |
236 | quote_each_token!($tokens $($first)*); | |
237 | $tokens.append("]"); | |
238 | quote_each_token!($tokens $($rest)*); | |
239 | }; | |
240 | ||
241 | ($tokens:ident { $($first:tt)* } $($rest:tt)*) => { | |
242 | $tokens.append("{"); | |
243 | quote_each_token!($tokens $($first)*); | |
244 | $tokens.append("}"); | |
245 | quote_each_token!($tokens $($rest)*); | |
246 | }; | |
247 | ||
248 | ($tokens:ident $first:tt $($rest:tt)*) => { | |
249 | $tokens.append(stringify!($first)); | |
250 | quote_each_token!($tokens $($rest)*); | |
251 | }; | |
252 | } |