ptx_90_parser_construct/
lib.rs1use proc_macro::TokenStream;
5use quote::quote;
6use syn::{
7 parse::{Parse, ParseStream},
8 parse_macro_input,
9 punctuated::Punctuated,
10 Expr, Ident, Path, Token,
11};
12
13enum CmapField {
15 Assign { name: Ident, expr: Expr },
17 Shorthand { name: Ident },
19 Wildcard,
21}
22
23impl Parse for CmapField {
24 fn parse(input: ParseStream) -> syn::Result<Self> {
25 if input.peek(Token![_]) {
27 let _: Token![_] = input.parse()?;
28 return Ok(CmapField::Wildcard);
29 }
30
31 let name: Ident = input.parse()?;
32
33 if input.peek(Token![=]) {
35 let _eq: Token![=] = input.parse()?;
36 let expr: Expr = input.parse()?;
37 Ok(CmapField::Assign { name, expr })
38 } else {
39 Ok(CmapField::Shorthand { name })
40 }
41 }
42}
43
44struct CmapInput {
48 type_path: Path,
49 fields: Punctuated<CmapField, Token![,]>,
50}
51
52impl Parse for CmapInput {
53 fn parse(input: ParseStream) -> syn::Result<Self> {
54 let type_path: Path = input.parse()?;
56
57 let fields_content;
59 syn::braced!(fields_content in input);
60 let fields = fields_content.parse_terminated(CmapField::parse, Token![,])?;
61
62 Ok(CmapInput {
63 type_path,
64 fields,
65 })
66 }
67}
68
69#[proc_macro]
82pub fn cclosure(input: TokenStream) -> TokenStream {
83 let CmapInput {
84 type_path,
85 fields,
86 } = parse_macro_input!(input as CmapInput);
87
88 let mut pattern_elements = Vec::new();
90 for field in &fields {
91 match field {
92 CmapField::Assign { name, .. } => {
93 pattern_elements.push(quote! { #name });
94 }
95 CmapField::Shorthand { name } => {
96 pattern_elements.push(quote! { #name });
97 }
98 CmapField::Wildcard => {
99 pattern_elements.push(quote! { _ });
100 }
101 }
102 }
103
104 let pattern = if pattern_elements.len() == 1 {
106 let elem = &pattern_elements[0];
107 quote! { #elem }
108 } else {
109 quote! { (#(#pattern_elements),*) }
110 };
111
112 let field_assignments = fields.iter().filter_map(|field| match field {
114 CmapField::Assign { name, expr } => {
115 Some(quote! { #name: #expr })
116 }
117 CmapField::Shorthand { name } => {
118 Some(quote! { #name: #name })
119 }
120 CmapField::Wildcard => None,
121 });
122
123 let expanded = quote! {
125 move |#pattern, span| {
126 #type_path {
127 #(#field_assignments,)*
128 span
129 }
130 }
131 };
132
133 TokenStream::from(expanded)
134}
135
136#[proc_macro]
149pub fn c(input: TokenStream) -> TokenStream {
150 let input_parsed = parse_macro_input!(input with parse_c_input);
152
153 let (span_expr, type_path, fields) = input_parsed;
154
155 let field_assignments = fields.iter().filter_map(|field| match field {
157 CmapField::Assign { name, expr } => {
158 Some(quote! { #name: #expr })
159 }
160 CmapField::Shorthand { name } => {
161 Some(quote! { #name: #name })
162 }
163 CmapField::Wildcard => None,
164 });
165
166 let expanded = quote! {
168 #type_path {
169 #(#field_assignments,)*
170 span: #span_expr
171 }
172 };
173
174 TokenStream::from(expanded)
175}
176
177fn parse_c_input(input: ParseStream) -> syn::Result<(Expr, Path, Punctuated<CmapField, Token![,]>)> {
178 let fork = input.fork();
180
181 let (span_expr, type_path) = if let Ok(_expr) = fork.parse::<Expr>() {
183 if fork.peek(Token![=>]) {
184 let expr: Expr = input.parse()?;
186 let _arrow: Token![=>] = input.parse()?;
187 let type_path: Path = input.parse()?;
188 (expr, type_path)
189 } else {
190 let type_path: Path = input.parse()?;
192 (syn::parse_quote!(span), type_path)
193 }
194 } else {
195 let type_path: Path = input.parse()?;
197 (syn::parse_quote!(span), type_path)
198 };
199
200 let fields = if input.peek(syn::token::Brace) {
202 let fields_content;
203 syn::braced!(fields_content in input);
204 fields_content.parse_terminated(CmapField::parse, Token![,])?
205 } else {
206 Punctuated::new()
207 };
208
209 Ok((span_expr, type_path, fields))
210}
211
212#[proc_macro]
225pub fn ok(input: TokenStream) -> TokenStream {
226 let c_result = c(input);
229 let c_tokens: proc_macro2::TokenStream = c_result.into();
230
231 let expanded = quote! {
232 Ok(#c_tokens)
233 };
234
235 TokenStream::from(expanded)
236}
237
238#[proc_macro]
251pub fn err(input: TokenStream) -> TokenStream {
252 let input_parsed = parse_macro_input!(input with parse_err_input);
254
255 let (span_expr, error_kind) = input_parsed;
256
257 let expanded = quote! {
260 Err(crate::parser::PtxParseError {
261 kind: #error_kind,
262 span: #span_expr
263 })
264 };
265
266 TokenStream::from(expanded)
267}
268
269fn parse_err_input(input: ParseStream) -> syn::Result<(Expr, Expr)> {
270 let span_expr = if input.peek2(Token![=>]) {
272 let expr: Expr = input.parse()?;
273 let _arrow: Token![=>] = input.parse()?;
274 expr
275 } else {
276 syn::parse_quote!(span)
278 };
279
280 let error_kind: Expr = input.parse()?;
282
283 Ok((span_expr, error_kind))
284}
285
286#[proc_macro]
299pub fn okmap(input: TokenStream) -> TokenStream {
300 let CmapInput {
301 type_path,
302 fields,
303 } = parse_macro_input!(input as CmapInput);
304
305 let mut pattern_elements = Vec::new();
307 for field in &fields {
308 match field {
309 CmapField::Assign { name, .. } => {
310 pattern_elements.push(quote! { #name });
311 }
312 CmapField::Shorthand { name } => {
313 pattern_elements.push(quote! { #name });
314 }
315 CmapField::Wildcard => {
316 pattern_elements.push(quote! { _ });
317 }
318 }
319 }
320
321 let pattern = if pattern_elements.len() == 1 {
323 let elem = &pattern_elements[0];
324 quote! { #elem }
325 } else {
326 quote! { (#(#pattern_elements),*) }
327 };
328
329 let field_assignments = fields.iter().filter_map(|field| match field {
331 CmapField::Assign { name, expr } => {
332 Some(quote! { #name: #expr })
333 }
334 CmapField::Shorthand { name } => {
335 Some(quote! { #name: #name })
336 }
337 CmapField::Wildcard => None,
338 });
339
340 let expanded = quote! {
342 move |#pattern, span| {
343 Ok(#type_path {
344 #(#field_assignments,)*
345 span
346 })
347 }
348 };
349
350 TokenStream::from(expanded)
351}
352
353#[proc_macro]
366pub fn func(input: TokenStream) -> TokenStream {
367 use syn::{ExprClosure, Pat};
368
369 let closure = parse_macro_input!(input as ExprClosure);
370
371 let mut params = closure.inputs.clone();
373
374 let span_param: Pat = syn::parse_quote!(span);
376 params.push(span_param);
377
378 let body = closure.body;
380
381 let expanded = quote! {
383 |#params| #body
384 };
385
386 TokenStream::from(expanded)
387}