1use proc_macro::TokenStream;
18use proc_macro2::{Literal, TokenStream as TokenStream2};
19use quote::{format_ident, quote, ToTokens};
20use syn::{spanned::Spanned, Attribute, DataEnum, DataStruct, DeriveInput, Error, Fields, Token};
21
22#[proc_macro_derive(FromTokens, attributes(pspp))]
23pub fn from_tokens_derive(input: TokenStream) -> TokenStream {
24 let ast: DeriveInput = syn::parse(input).unwrap();
27
28 match parse_derive_input(ast) {
29 Ok(output) => output.into(),
30 Err(error) => error.to_compile_error().into(),
31 }
32}
33
34fn parse_derive_input(ast: DeriveInput) -> Result<TokenStream2, Error> {
35 match &ast.data {
36 syn::Data::Enum(e) => derive_enum(&ast, e),
37 syn::Data::Struct(s) => derive_struct(&ast, s),
38 syn::Data::Union(_) => Err(Error::new(
39 ast.span(),
40 "Only struct and enums may currently be derived",
41 )),
42 }
43}
44
45fn derive_enum(ast: &DeriveInput, e: &DataEnum) -> Result<TokenStream2, Error> {
46 let struct_attrs = StructAttrs::parse(&ast.attrs)?;
47 let mut body = TokenStream2::new();
48 let name = &ast.ident;
49 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
50 for variant in &e.variants {
51 let ident = &variant.ident;
52 let field_attrs = FieldAttrs::parse(&variant.attrs)?;
53 let selector = field_attrs.selector.unwrap_or(struct_attrs.selector);
54 let construction =
55 construct_fields(&variant.fields, quote! { #name::#ident }, selector, None);
56 let fnname = format_ident!("construct_{ident}");
57 body.extend(quote! {
58 fn #fnname #impl_generics(input: &TokenSlice) -> ParseResult<#name #ty_generics> #where_clause { let input = input.clone(); #construction }
59 });
60 }
61
62 for variant in &e.variants {
63 let ident = &variant.ident;
64 let fnname = format_ident!("construct_{ident}");
65 let field_attrs = FieldAttrs::parse(&variant.attrs)?;
66 let selector = field_attrs.selector.unwrap_or(struct_attrs.selector);
67 if selector {
68 let ident_string = ident.to_string();
69 let select_expr = if let Some(syntax) = &field_attrs.syntax {
70 quote! { input.skip_syntax(#syntax) }
71 } else if ident_string.eq_ignore_ascii_case("all") {
72 quote! { input.skip(&Token::Punct(Punct::All))}
73 } else {
74 quote! { input.skip_keyword(#ident_string)}
75 };
76 body.extend(quote! { if let Some(input) = #select_expr { return #fnname(&input); } });
77 } else {
78 body.extend(quote! {
79 let result = #fnname(&input);
80 if let Ok(_) | Err(ParseError::Error(_)) = result {
81 return result;
82 }
83 });
84 }
85 }
86 body.extend(quote! { Err(ParseError::Mismatch(input.error("Syntax error.").into())) });
87
88 let output = quote! {
89 impl #impl_generics FromTokens for #name #ty_generics #where_clause {
90 fn from_tokens(input: &TokenSlice) -> ParseResult<Self> {
91 #body
92 }
93 }
94 };
95 Ok(output)
97}
98
99fn construct_fields(
100 fields: &Fields,
101 name: impl ToTokens,
102 mismatch_to_error: bool,
103 syntax: Option<&Literal>,
104) -> impl ToTokens {
105 let mut construction = TokenStream2::new();
106 if !fields.is_empty() {
107 construction
108 .extend(quote! { let mut diagnostics = crate::command::Diagnostics::default(); });
109 }
110 let convert = if mismatch_to_error {
111 quote! { .mismatch_to_error() }
112 } else {
113 quote! {}
114 };
115 for (index, _field) in fields.iter().enumerate() {
116 let varname = format_ident!("field{index}");
117 construction
118 .extend(quote! { let (#varname, input) = FromTokens::from_tokens(&input) #convert ?.take_diagnostics(&mut diagnostics); });
119 }
120 match fields {
121 Fields::Named(named) => {
122 let mut body = TokenStream2::new();
123 for (index, field) in named.named.iter().enumerate() {
124 let varname = format_ident!("field{index}");
125 let field_name = &field.ident;
126 body.extend(quote! { #field_name: #varname, });
127 }
128 quote! { #construction Ok(Parsed::new(#name { #body }, input, diagnostics)) }
129 }
130 Fields::Unnamed(unnamed) => {
131 let mut body = TokenStream2::new();
132 for (index, _field) in unnamed.unnamed.iter().enumerate() {
133 let varname = format_ident!("field{index}");
134 body.extend(quote! { #varname, });
135 }
136 quote! { #construction Ok(Parsed::new(#name ( #body ), input, diagnostics)) }
137 }
138 Fields::Unit => {
139 if let Some(syntax) = syntax {
140 quote! { crate::command::parse_syntax(input, #syntax).map(|p| p.map(|()| #name)) }
141 } else {
142 quote! { Ok(Parsed::ok(#name, input)) }
143 }
144 }
145 }
146}
147
148fn derive_struct(ast: &DeriveInput, s: &DataStruct) -> Result<TokenStream2, Error> {
149 let struct_attrs = StructAttrs::parse(&ast.attrs)?;
150 let name = &ast.ident;
151 let syntax = if let Some(syntax) = struct_attrs.syntax.as_ref() {
152 syntax.clone()
153 } else {
154 Literal::string(&name.to_string())
155 };
156 let construction = construct_fields(&s.fields, quote! {#name}, false, Some(&syntax));
157 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
158 let output = quote! {
159 impl #impl_generics FromTokens for #name #ty_generics #where_clause {
160 fn from_tokens(input: &TokenSlice) -> ParseResult<Self> {
161 #construction
162 }
163 }
164 };
165 Ok(output)
167}
168
169#[derive(Default)]
170struct FieldAttrs {
171 syntax: Option<Literal>,
172 selector: Option<bool>,
173}
174
175impl FieldAttrs {
176 fn parse(attributes: &[Attribute]) -> Result<Self, Error> {
177 let mut field_attrs = Self::default();
178 for attr in attributes {
179 if !attr.path().is_ident("pspp") {
180 continue;
181 }
182 attr.parse_nested_meta(|meta| {
183 if meta.path.is_ident("syntax") {
184 meta.input.parse::<Token![=]>()?;
185 let syntax = meta.input.parse::<Literal>()?;
186 field_attrs.syntax = Some(syntax);
187 } else if meta.path.is_ident("no_selector") {
188 field_attrs.selector = Some(false);
189 } else {
190 return Err(Error::new(meta.path.span(), "Unknown attribute"));
191 }
192 Ok(())
193 })?;
194 }
195 Ok(field_attrs)
196 }
197}
198
199struct StructAttrs {
200 syntax: Option<Literal>,
201 selector: bool,
202}
203
204impl Default for StructAttrs {
205 fn default() -> Self {
206 Self {
207 syntax: None,
208 selector: true,
209 }
210 }
211}
212
213impl StructAttrs {
214 fn parse(attributes: &[Attribute]) -> Result<Self, Error> {
215 let mut field_attrs = Self::default();
217 for attr in attributes {
218 if !attr.path().is_ident("pspp") {
219 continue;
220 }
221 attr.parse_nested_meta(|meta| {
222 if meta.path.is_ident("syntax") {
223 meta.input.parse::<Token![=]>()?;
224 let syntax = meta.input.parse::<Literal>()?;
225 field_attrs.syntax = Some(syntax);
226 } else if meta.path.is_ident("no_selector") {
227 field_attrs.selector = false;
228 } else {
229 return Err(Error::new(meta.path.span(), "Unknown attribute"));
230 }
231 Ok(())
232 })?;
233 }
234 Ok(field_attrs)
235 }
236}