1use proc_macro::TokenStream;
4use proc_macro2::Span as ProcSpan;
5use quote::{format_ident, quote};
6use syn::{
7 Data, DeriveInput, Expr, Fields, FieldsNamed, Ident, Path, Token,
8 parse::{Parse, ParseStream},
9 parse_macro_input,
10 punctuated::Punctuated,
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 { type_path, fields })
63 }
64}
65
66#[proc_macro]
79pub fn cclosure(input: TokenStream) -> TokenStream {
80 let CmapInput { type_path, fields } = parse_macro_input!(input as CmapInput);
81
82 let mut pattern_elements = Vec::new();
84 for field in &fields {
85 match field {
86 CmapField::Assign { name, .. } => {
87 pattern_elements.push(quote! { #name });
88 }
89 CmapField::Shorthand { name } => {
90 pattern_elements.push(quote! { #name });
91 }
92 CmapField::Wildcard => {
93 pattern_elements.push(quote! { _ });
94 }
95 }
96 }
97
98 let pattern = if pattern_elements.len() == 1 {
100 let elem = &pattern_elements[0];
101 quote! { #elem }
102 } else {
103 quote! { (#(#pattern_elements),*) }
104 };
105
106 let field_assignments = fields.iter().filter_map(|field| match field {
108 CmapField::Assign { name, expr } => Some(quote! { #name: #expr }),
109 CmapField::Shorthand { name } => Some(quote! { #name: #name }),
110 CmapField::Wildcard => None,
111 });
112
113 let expanded = quote! {
115 move |#pattern, span| {
116 #type_path {
117 #(#field_assignments,)*
118 span
119 }
120 }
121 };
122
123 TokenStream::from(expanded)
124}
125
126#[proc_macro]
139pub fn c(input: TokenStream) -> TokenStream {
140 let input_parsed = parse_macro_input!(input with parse_c_input);
142
143 let (span_expr, type_path, fields) = input_parsed;
144
145 let field_assignments = fields.iter().filter_map(|field| match field {
147 CmapField::Assign { name, expr } => Some(quote! { #name: #expr }),
148 CmapField::Shorthand { name } => Some(quote! { #name: #name }),
149 CmapField::Wildcard => None,
150 });
151
152 let expanded = quote! {
154 #type_path {
155 #(#field_assignments,)*
156 span: #span_expr
157 }
158 };
159
160 TokenStream::from(expanded)
161}
162
163fn parse_c_input(
164 input: ParseStream,
165) -> syn::Result<(Expr, Path, Punctuated<CmapField, Token![,]>)> {
166 let fork = input.fork();
168
169 let (span_expr, type_path) = if let Ok(_expr) = fork.parse::<Expr>() {
171 if fork.peek(Token![=>]) {
172 let expr: Expr = input.parse()?;
174 let _arrow: Token![=>] = input.parse()?;
175 let type_path: Path = input.parse()?;
176 (expr, type_path)
177 } else {
178 let type_path: Path = input.parse()?;
180 (syn::parse_quote!(span), type_path)
181 }
182 } else {
183 let type_path: Path = input.parse()?;
185 (syn::parse_quote!(span), type_path)
186 };
187
188 let fields = if input.peek(syn::token::Brace) {
190 let fields_content;
191 syn::braced!(fields_content in input);
192 fields_content.parse_terminated(CmapField::parse, Token![,])?
193 } else {
194 Punctuated::new()
195 };
196
197 Ok((span_expr, type_path, fields))
198}
199
200#[proc_macro]
213pub fn ok(input: TokenStream) -> TokenStream {
214 let c_result = c(input);
217 let c_tokens: proc_macro2::TokenStream = c_result.into();
218
219 let expanded = quote! {
220 Ok(#c_tokens)
221 };
222
223 TokenStream::from(expanded)
224}
225
226#[proc_macro]
239pub fn err(input: TokenStream) -> TokenStream {
240 let input_parsed = parse_macro_input!(input with parse_err_input);
242
243 let (span_expr, error_kind) = input_parsed;
244
245 let expanded = quote! {
248 Err(crate::parser::PtxParseError {
249 kind: #error_kind,
250 span: #span_expr
251 })
252 };
253
254 TokenStream::from(expanded)
255}
256
257fn parse_err_input(input: ParseStream) -> syn::Result<(Expr, Expr)> {
258 let span_expr = if input.peek2(Token![=>]) {
260 let expr: Expr = input.parse()?;
261 let _arrow: Token![=>] = input.parse()?;
262 expr
263 } else {
264 syn::parse_quote!(span)
266 };
267
268 let error_kind: Expr = input.parse()?;
270
271 Ok((span_expr, error_kind))
272}
273
274#[proc_macro]
287pub fn okmap(input: TokenStream) -> TokenStream {
288 let CmapInput { type_path, fields } = parse_macro_input!(input as CmapInput);
289
290 let mut pattern_elements = Vec::new();
292 for field in &fields {
293 match field {
294 CmapField::Assign { name, .. } => {
295 pattern_elements.push(quote! { #name });
296 }
297 CmapField::Shorthand { name } => {
298 pattern_elements.push(quote! { #name });
299 }
300 CmapField::Wildcard => {
301 pattern_elements.push(quote! { _ });
302 }
303 }
304 }
305
306 let pattern = if pattern_elements.len() == 1 {
308 let elem = &pattern_elements[0];
309 quote! { #elem }
310 } else {
311 quote! { (#(#pattern_elements),*) }
312 };
313
314 let field_assignments = fields.iter().filter_map(|field| match field {
316 CmapField::Assign { name, expr } => Some(quote! { #name: #expr }),
317 CmapField::Shorthand { name } => Some(quote! { #name: #name }),
318 CmapField::Wildcard => None,
319 });
320
321 let expanded = quote! {
323 move |#pattern, span| {
324 Ok(#type_path {
325 #(#field_assignments,)*
326 span
327 })
328 }
329 };
330
331 TokenStream::from(expanded)
332}
333
334#[proc_macro]
347pub fn func(input: TokenStream) -> TokenStream {
348 use syn::{ExprClosure, Pat};
349
350 let closure = parse_macro_input!(input as ExprClosure);
351
352 let mut params = closure.inputs.clone();
354
355 let span_param: Pat = syn::parse_quote!(span);
357 params.push(span_param);
358
359 let body = closure.body;
361
362 let expanded = quote! {
364 |#params| #body
365 };
366
367 TokenStream::from(expanded)
368}
369
370#[proc_macro_derive(Spanned)]
375pub fn derive_spanned(input: TokenStream) -> TokenStream {
376 let input = parse_macro_input!(input as DeriveInput);
377 match impl_spanned(&input) {
378 Ok(tokens) => tokens.into(),
379 Err(error) => error.to_compile_error().into(),
380 }
381}
382
383fn impl_spanned(input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
384 let name = &input.ident;
385 let generics = &input.generics;
386 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
387
388 let (span_arms, set_arms) = match &input.data {
389 Data::Struct(data) => {
390 let (span_arm, set_arm) = build_match_arm(quote! { Self }, &data.fields)?;
391 (vec![span_arm], vec![set_arm])
392 }
393 Data::Enum(data) => {
394 let mut span_arms = Vec::new();
395 let mut set_arms = Vec::new();
396 for variant in &data.variants {
397 let ident = &variant.ident;
398 let path = quote! { Self::#ident };
399 let (span_arm, set_arm) = build_match_arm(path, &variant.fields)?;
400 span_arms.push(span_arm);
401 set_arms.push(set_arm);
402 }
403 (span_arms, set_arms)
404 }
405 Data::Union(_) => {
406 return Err(syn::Error::new_spanned(
407 &input.ident,
408 "Spanned cannot be derived for unions",
409 ));
410 }
411 };
412
413 let span_ty = quote! { crate::parser::Span };
414 let trait_path = quote! { crate::span::Spanned };
415 let span_match = quote! {
416 match self {
417 #(#span_arms)*
418 }
419 };
420 let set_match = quote! {
421 match self {
422 #(#set_arms)*
423 }
424 };
425
426 Ok(quote! {
427 impl #impl_generics #trait_path for #name #ty_generics #where_clause {
428 fn span(&self) -> #span_ty {
429 #span_match
430 }
431
432 fn set_span(&mut self, span: #span_ty) {
433 #set_match
434 }
435 }
436
437 impl #impl_generics #name #ty_generics #where_clause {
438 pub fn span(&self) -> #span_ty {
439 <Self as #trait_path>::span(self)
440 }
441
442 pub fn with_span(mut self, span: #span_ty) -> Self {
443 <Self as #trait_path>::set_span(&mut self, span);
444 self
445 }
446 }
447 })
448}
449
450fn build_match_arm(
451 path: proc_macro2::TokenStream,
452 fields: &Fields,
453) -> syn::Result<(proc_macro2::TokenStream, proc_macro2::TokenStream)> {
454 match fields {
455 Fields::Named(named) => build_named_arm(path, named),
456 _ => Err(syn::Error::new(
457 ProcSpan::call_site(),
458 "Spanned derive only supports structs/enums with named `span` fields",
459 )),
460 }
461}
462
463fn build_named_arm(
464 path: proc_macro2::TokenStream,
465 fields: &FieldsNamed,
466) -> syn::Result<(proc_macro2::TokenStream, proc_macro2::TokenStream)> {
467 let has_span = fields
468 .named
469 .iter()
470 .any(|field| field.ident.as_ref().is_some_and(|ident| ident == "span"));
471 if !has_span {
472 return Err(syn::Error::new(
473 ProcSpan::call_site(),
474 "Spanned derive requires a field named `span`",
475 ));
476 }
477
478 let binding = format_ident!("__span_field");
479 let span_arm = quote! {
480 #path { span: #binding, .. } => #binding.clone(),
481 };
482 let set_arm = quote! {
483 #path { span: #binding, .. } => {
484 *#binding = span;
485 }
486 };
487
488 Ok((span_arm, set_arm))
489}