structmeta_derive/
parse.rs

1use crate::{syn_utils::*, to_tokens_attribute::*};
2use proc_macro2::{Span, TokenStream};
3use quote::{format_ident, quote, quote_spanned};
4use std::unreachable;
5use syn::{
6    parenthesized,
7    parse::{Parse, ParseStream},
8    parse2, parse_quote,
9    punctuated::Punctuated,
10    spanned::Spanned,
11    Data, DataEnum, DataStruct, DeriveInput, Fields, Ident, Result, Token,
12};
13
14pub fn derive_parse(input: DeriveInput) -> Result<TokenStream> {
15    let mut dump = false;
16    for attr in &input.attrs {
17        if attr.path.is_ident("parse") {
18            let attr: ParseAttribute = parse2(attr.tokens.clone())?;
19            dump = dump || attr.dump.is_some();
20        }
21    }
22
23    let ts = match &input.data {
24        Data::Struct(data) => code_from_struct(data)?,
25        Data::Enum(data) => code_from_enum(&input.ident, data)?,
26        Data::Union(_) => {
27            bail!(Span::call_site(), "Not supported for union.")
28        }
29    };
30    let ts = quote! {
31        fn parse(input: ::syn::parse::ParseStream<'_>) -> ::syn::Result<Self> {
32            #ts
33        }
34    };
35    let ts = impl_trait_result(&input, &parse_quote!(::syn::parse::Parse), &[], ts, dump)?;
36    Ok(ts)
37}
38
39fn code_from_struct(data: &DataStruct) -> Result<TokenStream> {
40    code_from_fields(quote!(Self), &data.fields, None)
41}
42fn code_from_enum(self_ident: &Ident, data: &DataEnum) -> Result<TokenStream> {
43    let mut ts = TokenStream::new();
44    ts.extend(quote!(
45        use syn::ext::*;
46    ));
47    let mut input_is_forked = false;
48    let mut input_is_moved = false;
49    for index in 0..data.variants.len() {
50        let variant = &data.variants[index];
51        let variant_ident = &variant.ident;
52        let is_last = index == data.variants.len() - 1;
53        let fn_ident = format_ident!("_parse_{}", &variant.ident);
54        let mut peeks = Vec::new();
55        let fn_expr = code_from_fields(
56            quote!(#self_ident::#variant_ident),
57            &variant.fields,
58            Some(&mut peeks),
59        )?;
60        let fn_def = quote! {
61            #[allow(non_snake_case)]
62            fn #fn_ident(input: ::syn::parse::ParseStream<'_>) -> ::syn::Result<#self_ident> {
63                #fn_expr
64            }
65        };
66        let code = if peeks.is_empty() {
67            if is_last && !input_is_forked {
68                input_is_moved = true;
69                quote! {
70                    #fn_ident(input)
71                }
72            } else {
73                input_is_forked = true;
74                quote! {
75                    let fork = input.fork();
76                    if let Ok(value) = #fn_ident(&fork) {
77                        ::syn::parse::discouraged::Speculative::advance_to(input, &fork);
78                        return Ok(value);
79                    }
80                }
81            }
82        } else {
83            let mut preds = Vec::new();
84            for (index, peek) in peeks.into_iter().enumerate() {
85                preds.push(to_predicate(index, &peek)?);
86            }
87            quote! {
88                if #(#preds )&&* {
89                    return #fn_ident(&input);
90                }
91            }
92        };
93        ts.extend(quote! {
94            #fn_def
95            #code
96        });
97    }
98    if !input_is_moved {
99        ts.extend(quote! {
100            Err(input.error("parse failed."))
101        });
102    }
103    Ok(ts)
104}
105fn to_predicate(index: usize, peek: &PeekItem) -> Result<TokenStream> {
106    let peek_ident: Ident = match index {
107        0 => parse_quote!(peek),
108        1 => parse_quote!(peek2),
109        2 => parse_quote!(peek3),
110        _ => bail!(peek.span, "more than three `#[parse(peek)]` was specified."),
111    };
112    let peek_arg = &peek.arg;
113    Ok(quote!(input.#peek_ident(#peek_arg)))
114}
115
116struct Scope {
117    input: Ident,
118    close: Option<char>,
119}
120struct PeekItem {
121    span: Span,
122    arg: TokenStream,
123}
124fn to_parse_bracket(c: char) -> Ident {
125    match c {
126        '(' => parse_quote!(parenthesized),
127        '[' => parse_quote!(bracketed),
128        '{' => parse_quote!(braced),
129        _ => unreachable!(),
130    }
131}
132fn code_from_fields(
133    self_path: TokenStream,
134    fields: &Fields,
135    mut peeks: Option<&mut Vec<PeekItem>>,
136) -> Result<TokenStream> {
137    let mut scopes = vec![Scope {
138        input: parse_quote!(input),
139        close: None,
140    }];
141    let mut ts = TokenStream::new();
142    let mut inits = Vec::new();
143    let mut non_peek_field = None;
144    for (index, field) in fields.iter().enumerate() {
145        let ty = &field.ty;
146        let var_ident = to_var_ident(index, &field.ident);
147        let mut use_parse = true;
148        let mut peek = None;
149        let mut is_any = false;
150        let mut is_terminated = false;
151        let mut is_root = scopes.len() == 1;
152        for attr in &field.attrs {
153            if attr.path.is_ident("to_tokens") {
154                let attr: ToTokensAttribute = parse2(attr.tokens.clone())?;
155                for token in attr.token {
156                    for c in token.value().chars() {
157                        match c {
158                            '(' | '[' | '{' => {
159                                use_parse = false;
160                                let parse_bracket = if is_macro_delimiter(&field.ty) {
161                                    quote!(::structmeta::helpers_parse_macro_delimiter)
162                                } else {
163                                    let parse_bracket = to_parse_bracket(c);
164                                    quote!(::syn::#parse_bracket)
165                                };
166                                let input_old = &scopes.last().unwrap().input;
167                                let input = format_ident!("input_{}", index);
168                                let ty = &field.ty;
169                                let code = quote_spanned!(field.span()=>
170                                    let #input;
171                                    let #var_ident = #parse_bracket!(#input in #input_old);
172                                    let #var_ident : #ty = #var_ident;
173                                    let #input = &#input;
174                                );
175                                // We need `let #var_ident : #ty = #var_ident;` to make error messages easier to understand.
176                                // Try remove this line and run compile fail tests for details.
177
178                                ts.extend(code);
179                                scopes.push(Scope {
180                                    close: Some(to_close(c)),
181                                    input,
182                                });
183                            }
184                            ')' | ']' | '}' => {
185                                if scopes.last().unwrap().close != Some(c) {
186                                    bail!(token.span(), "mismatched closing delimiter `{}`.", c);
187                                }
188                                scopes.pop();
189                                if scopes.len() == 1 {
190                                    is_root = true;
191                                }
192                            }
193                            _ => {
194                                bail!(
195                                    token.span(),
196                                    "expected '(', ')', '[', ']', '{{' or '}}', found `{}`.",
197                                    c
198                                );
199                            }
200                        }
201                    }
202                }
203            }
204            if attr.path.is_ident("parse") {
205                let attr: ParseAttribute = parse2(attr.tokens.clone())?;
206                peek = peek.or(attr.peek);
207                is_any = is_any || attr.any.is_some();
208                is_terminated = is_terminated || attr.terminated.is_some();
209            }
210        }
211        if let Some(peeks) = &mut peeks {
212            if let Some(peek) = peek {
213                let span = peek.span();
214                if !is_root {
215                    bail!(span, "`#[parse(peek)]` cannot be specified with a field enclosed by `[]`, `()` or `{}`.");
216                }
217                if let Some(non_peek_field) = &non_peek_field {
218                    bail!(
219                            span,
220                            "you need to peek all previous tokens. consider specifying `#[parse(peek)]` for field `{}`.",
221                            non_peek_field
222                        );
223                }
224                let arg = if is_any {
225                    quote!(#ty::peek_any)
226                } else {
227                    quote!(#ty)
228                };
229                peeks.push(PeekItem { span, arg });
230            }
231        }
232        if is_root && peek.is_none() && non_peek_field.is_none() {
233            non_peek_field = Some(to_display(index, &field.ident));
234        }
235        if use_parse {
236            let input = &scopes.last().unwrap().input;
237            let expr = match (is_terminated, is_any) {
238                (false, false) => quote!(::syn::parse::Parse::parse(#input)),
239                (false, true) => quote!(<#ty>::parse_any(#input)),
240                (true, false) => {
241                    quote!(<#ty>::parse_terminated(#input))
242                }
243                (true, true) => {
244                    quote!(<#ty>::parse_terminated_with(#input, ::syn::ext::IdentExt::parse_any))
245                }
246            };
247            let code = quote_spanned!(field.span()=>let #var_ident = #expr?;);
248            ts.extend(code);
249        }
250        if let Some(field_ident) = &field.ident {
251            inits.push(quote!(#field_ident : #var_ident));
252        } else {
253            inits.push(quote!(#var_ident));
254        }
255    }
256    let init = match &fields {
257        Fields::Named(_) => quote!({#(#inits,)*}),
258        Fields::Unnamed(_) => quote!((#(#inits,)*)),
259        Fields::Unit => quote!(),
260    };
261    Ok(quote! {
262        use syn::ext::*;
263        #ts
264        Ok(#self_path #init)
265    })
266}
267
268fn to_var_ident(index: usize, ident: &Option<Ident>) -> Ident {
269    if let Some(ident) = ident {
270        format_ident!("_{}", ident)
271    } else {
272        format_ident!("_{}", index)
273    }
274}
275fn to_display(index: usize, ident: &Option<Ident>) -> String {
276    if let Some(ident) = ident {
277        format!("{ident}")
278    } else {
279        format!("{index}")
280    }
281}
282
283struct ParseAttribute {
284    any: Option<kw::any>,
285    peek: Option<kw::peek>,
286    terminated: Option<kw::terminated>,
287    dump: Option<kw::dump>,
288}
289impl Parse for ParseAttribute {
290    fn parse(input: ParseStream) -> Result<Self> {
291        let content;
292        parenthesized!(content in input);
293        let mut any = None;
294        let mut peek = None;
295        let mut terminated = None;
296        let mut dump = None;
297        let args: Punctuated<ParseAttributeArg, Token![,]> =
298            content.parse_terminated(ParseAttributeArg::parse)?;
299        for arg in args.into_iter() {
300            match arg {
301                ParseAttributeArg::Any(kw_any) => any = any.or(Some(kw_any)),
302                ParseAttributeArg::Peek(kw_peek) => peek = peek.or(Some(kw_peek)),
303                ParseAttributeArg::Terminated(kw_terminated) => {
304                    terminated = terminated.or(Some(kw_terminated))
305                }
306                ParseAttributeArg::Dump(kw_dump) => dump = dump.or(Some(kw_dump)),
307            }
308        }
309        Ok(Self {
310            any,
311            peek,
312            terminated,
313            dump,
314        })
315    }
316}
317mod kw {
318    use syn::custom_keyword;
319    custom_keyword!(any);
320    custom_keyword!(peek);
321    custom_keyword!(terminated);
322    custom_keyword!(dump);
323}
324
325enum ParseAttributeArg {
326    Any(kw::any),
327    Peek(kw::peek),
328    Terminated(kw::terminated),
329    Dump(kw::dump),
330}
331impl Parse for ParseAttributeArg {
332    fn parse(input: ParseStream) -> Result<Self> {
333        if input.peek(kw::any) {
334            Ok(Self::Any(input.parse()?))
335        } else if input.peek(kw::peek) {
336            Ok(Self::Peek(input.parse()?))
337        } else if input.peek(kw::terminated) {
338            Ok(Self::Terminated(input.parse()?))
339        } else if input.peek(kw::dump) {
340            Ok(Self::Dump(input.parse()?))
341        } else {
342            Err(input.error("expected `any`, `peek`, `terminated` or `dump`."))
343        }
344    }
345}