derive_syn_parse/
fields.rs

1//! Handling for generating a `Parse` implementation using fields
2
3use proc_macro2::{Span, TokenStream};
4use quote::{format_ident, quote, quote_spanned, ToTokens};
5use std::convert::{TryFrom, TryInto};
6use syn::parse::{Parse, ParseStream};
7use syn::spanned::Spanned;
8use syn::{AttrStyle, Attribute, Expr, Fields, Ident, Path, Result, Token, Type};
9
10pub(crate) fn generate_fn_body(
11    base_tyname: &impl ToTokens,
12    fields: Fields,
13    with_return: bool,
14) -> Result<TokenStream> {
15    let initialize_self = initialize_type_or_variant(base_tyname, &fields);
16    let parse_fields = fields
17        .into_iter()
18        .enumerate()
19        .map(parse_field)
20        .collect::<Result<Vec<_>>>()?;
21
22    let maybe_return = match with_return {
23        true => Token![return](Span::call_site()).into_token_stream(),
24        false => TokenStream::new(),
25    };
26
27    Ok(quote! {
28        #( #parse_fields )*
29
30        #maybe_return Ok(#initialize_self)
31    })
32}
33
34enum FieldAttr {
35    Inside(Ident),
36    Tree(TreeKind),
37    Call(Expr),
38    ParseTerminated(Path),
39    Peek(PeekAttr),
40    Prefix(NeighborAttr),
41    Postfix(NeighborAttr),
42}
43
44enum TreeKind {
45    Paren,
46    Bracket,
47    Brace,
48}
49
50enum ParseMethod {
51    Tree(TreeKind, Span),
52    Call(Expr),
53    ParseTerminated(Path),
54    Default,
55}
56
57enum PeekAttr {
58    Peek(Expr),
59    PeekWith(Expr),
60    ParseIf(Expr),
61}
62
63// An attribute that's either a #[prefix] or #[postfix] directive. These can either be
64// free-standing (i.e. discarded immediately after parsing) or named - saved for later parsing.
65//
66// They can additionally be specified as inside a particular token stream, so the full syntax is:
67//
68//   "#[prefix(" <Type> [ "as" <Ident> ] [ "in" <Ident> ] ")]"
69struct NeighborAttr {
70    parse_ty: Type,
71    maybe_named: Option<Ident>,
72    maybe_inside: Option<Ident>,
73}
74
75impl Parse for NeighborAttr {
76    fn parse(input: ParseStream) -> syn::Result<Self> {
77        let parse_ty = input.parse()?;
78        let as_token: Option<Token![as]> = input.parse()?;
79        let maybe_named = match as_token.is_some() {
80            true => Some(input.parse()?),
81            false => None,
82        };
83
84        let in_token: Option<Token![in]> = input.parse()?;
85        let maybe_inside = match in_token.is_some() {
86            true => Some(input.parse()?),
87            false => None,
88        };
89
90        Ok(NeighborAttr {
91            parse_ty,
92            maybe_named,
93            maybe_inside,
94        })
95    }
96}
97
98impl ParseMethod {
99    fn is_default(&self) -> bool {
100        matches!(self, Self::Default)
101    }
102}
103
104struct FieldAttrs {
105    prefix: Vec<NeighborAttr>,
106    postfix: Vec<NeighborAttr>,
107    inside: Option<Ident>,
108    parse_method: ParseMethod,
109    peek: Option<PeekAttr>,
110}
111
112struct ParseField {
113    required_var_defs: Option<Ident>,
114    parse_expr: TokenStream,
115    pre_parse: TokenStream,
116    post_parse: TokenStream,
117}
118
119// This needs to return tokenstreams because tuple structs use integer indices as field names
120//
121// For example: We'd end up writing the following
122//   Ok(Self {
123//       0: _field_0,
124//       1: _field_1,
125//       ...
126//   })
127//
128// Otherwise, we would totally just return a list of identifiers
129fn initialize_type_or_variant(name: &impl ToTokens, fields: &syn::Fields) -> TokenStream {
130    use syn::Fields::{Named, Unit, Unnamed};
131
132    match fields {
133        Unit => name.to_token_stream(),
134        Named(fields) => {
135            let iter = fields.named.iter().map(|f| {
136                f.ident
137                    .as_ref()
138                    .expect("named field was unnamed! the impossible is now possible!")
139            });
140            quote! {
141                #name { #( #iter, )* }
142            }
143        }
144        Unnamed(fields) => {
145            let iter = fields
146                .unnamed
147                .iter()
148                .enumerate()
149                .map(|(i, f)| field_name_for_idx(i, f.span()));
150            quote! {
151                #name( #( #iter, )* )
152            }
153        }
154    }
155}
156
157fn field_name_for_idx(idx: usize, span: Span) -> Ident {
158    format_ident!("_field_{}", idx, span = span)
159}
160
161fn parse_field((idx, field): (usize, syn::Field)) -> Result<TokenStream> {
162    let span = field.span();
163
164    let assigned_name = field.ident.unwrap_or_else(|| field_name_for_idx(idx, span));
165
166    let attrs = (field.attrs)
167        .into_iter()
168        .filter_map(try_as_field_attr)
169        .collect::<Result<Vec<_>>>()?
170        .try_into()?;
171
172    let ParseField {
173        required_var_defs,
174        parse_expr,
175        pre_parse,
176        post_parse,
177    } = handle_field_attrs(&assigned_name, field.ty.span(), attrs);
178
179    // convert the Option to an iterator, so we can declare variables conditionally:
180    let required_var_defs = required_var_defs.into_iter();
181    let field_ty = field.ty;
182    Ok(quote_spanned! {
183        span=>
184        #( let #required_var_defs; )*
185        #pre_parse
186        let #assigned_name: #field_ty = #parse_expr;
187        #post_parse
188    })
189}
190
191fn try_as_field_attr(attr: Attribute) -> Option<Result<(FieldAttr, Span)>> {
192    let name = attr.path().get_ident()?.to_string();
193    let span = attr.span();
194
195    macro_rules! expect_outer_attr {
196        ($name:literal) => {{
197            if let AttrStyle::Inner(_) = attr.style {
198                return Some(Err(syn::Error::new(
199                    span,
200                    concat!(
201                        "the `#[",
202                        $name,
203                        "]` parsing attribute can only be used as an outer attribute"
204                    ),
205                )));
206            }
207        }};
208    }
209
210    #[rustfmt::skip]
211    macro_rules! expect_no_attr_args {
212        ($name:expr) => {{
213            use syn::Meta::*;
214            match attr.meta {
215                List(args)      =>  return Some(Err(syn::Error::new(
216                    span,
217                    format!("the `#[{}]` parsing attribute does not expect arguments {:?}", $name, args.tokens.to_string()),
218                ))),
219                NameValue(val)  =>  return Some(Err(syn::Error::new(
220                    span,
221                    format!("the `#[{}]` parsing attribute does not expect value {:?}", $name, val.value.to_token_stream().to_string()),
222                ))),
223                _               =>  {}
224            }
225        }};
226    }
227
228    macro_rules! expect_parenthesis {
229        ($attr_lit:literal) => {{
230            use syn::{MacroDelimiter::*, Meta};
231            match attr.meta {
232                Meta::List(ref ml) => match ml.delimiter {
233                    Paren(_) => {}
234                    Brace(_) => {
235                        return Some(Err(syn::Error::new(
236                            span,
237                            concat!(
238                                "expected parenthesis in `#[",
239                                $attr_lit,
240                                "(...)]`, found braces"
241                            ),
242                        )))
243                    }
244                    Bracket(_) => {
245                        return Some(Err(syn::Error::new(
246                            span,
247                            concat!(
248                                "expected parenthesis in `#[",
249                                $attr_lit,
250                                "(...)]`, found brackets"
251                            ),
252                        )))
253                    }
254                },
255                _ => {
256                    return Some(Err(syn::Error::new(
257                        span,
258                        concat!("expected parenthesis in `#[", $attr_lit, "(...)]`"),
259                    )))
260                }
261            }
262        }};
263    }
264    match name.as_str() {
265        "inside" => {
266            expect_outer_attr!("inside(...)");
267            expect_parenthesis!("inside");
268            Some(
269                attr.parse_args()
270                    .map(move |id| (FieldAttr::Inside(id), span)),
271            )
272        }
273        "call" => {
274            expect_outer_attr!("call(...)");
275            expect_parenthesis!("call");
276            Some(attr.parse_args().map(move |id| (FieldAttr::Call(id), span)))
277        }
278        "parse_terminated" => {
279            expect_outer_attr!("parse_terminated(...)");
280            expect_parenthesis!("parse_terminated");
281            Some(
282                attr.parse_args()
283                    .map(move |id| (FieldAttr::ParseTerminated(id), span)),
284            )
285        }
286        "paren" => {
287            expect_outer_attr!("paren");
288            expect_no_attr_args!("paren");
289            Some(Ok((FieldAttr::Tree(TreeKind::Paren), span)))
290        }
291        "bracket" => {
292            expect_outer_attr!("bracket");
293            expect_no_attr_args!("bracket");
294            Some(Ok((FieldAttr::Tree(TreeKind::Bracket), span)))
295        }
296        "brace" => {
297            expect_outer_attr!("brace");
298            expect_no_attr_args!("brace");
299            Some(Ok((FieldAttr::Tree(TreeKind::Brace), span)))
300        }
301        "peek" => {
302            expect_outer_attr!("peek(...)");
303            expect_parenthesis!("peek");
304            Some(
305                attr.parse_args()
306                    .map(move |id| (FieldAttr::Peek(PeekAttr::Peek(id)), span)),
307            )
308        }
309        "peek_with" => {
310            expect_outer_attr!("peek_with(...)");
311            expect_parenthesis!("peek_with");
312            Some(
313                attr.parse_args()
314                    .map(move |id| (FieldAttr::Peek(PeekAttr::PeekWith(id)), span)),
315            )
316        }
317        "parse_if" => {
318            expect_outer_attr!("parse_if(...)");
319            expect_parenthesis!("parse_if");
320            Some(
321                attr.parse_args()
322                    .map(move |id| (FieldAttr::Peek(PeekAttr::ParseIf(id)), span)),
323            )
324        }
325        "prefix" => {
326            expect_outer_attr!("prefix(...)");
327            expect_parenthesis!("prefix");
328            Some(
329                attr.parse_args()
330                    .map(move |id| (FieldAttr::Prefix(id), span)),
331            )
332        }
333        "postfix" => {
334            expect_outer_attr!("postifx(...)");
335            expect_parenthesis!("postfix");
336            Some(
337                attr.parse_args()
338                    .map(move |id| (FieldAttr::Postfix(id), span)),
339            )
340        }
341        _ => None,
342    }
343}
344
345impl TryFrom<Vec<(FieldAttr, Span)>> for FieldAttrs {
346    type Error = syn::Error;
347
348    fn try_from(vec: Vec<(FieldAttr, Span)>) -> Result<Self> {
349        use FieldAttr::{Call, Inside, ParseTerminated, Peek, Postfix, Prefix, Tree};
350
351        let mut inside = None;
352        let mut peek = None;
353        let mut parse_method = ParseMethod::Default;
354        let mut prefix = Vec::new();
355        let mut postfix = Vec::new();
356
357        for (attr, span) in vec {
358            match attr {
359                Tree(_) | Call(_) | ParseTerminated(_) if !parse_method.is_default() => {
360                    return Err(syn::Error::new(span, "parsing method specified twice"));
361                }
362                Inside(_) if inside.is_some() => {
363                    return Err(syn::Error::new(
364                        span,
365                        "containing parse stream is specified twice",
366                    ));
367                }
368                Peek(_) if peek.is_some() => {
369                    return Err(syn::Error::new(span, "peeking can only be specified once"));
370                }
371
372                Call(expr) => parse_method = ParseMethod::Call(expr),
373                ParseTerminated(path) => parse_method = ParseMethod::ParseTerminated(path),
374                Tree(kind) => parse_method = ParseMethod::Tree(kind, span),
375                Inside(name) => inside = Some(name),
376                Peek(p) => peek = Some(p),
377
378                Prefix(_) if inside.is_some() => {
379                    return Err(syn::Error::new(
380                        span,
381                        "`#[prefix]` cannot be used after `#[inside]`. Perhaps try `#[prefix(<Type> in <token>)]`?",
382                    ));
383                }
384
385                Postfix(_) if inside.is_some() => {
386                    return Err(syn::Error::new(
387                        span,
388                        "`#[postfix]` cannot be used after `#[inside]`. Perhaps try `#[prefix(<Type> in <token>)]`?",
389                    ));
390                }
391
392                Prefix(attr) => prefix.push(attr),
393                Postfix(attr) => postfix.push(attr),
394            }
395        }
396
397        Ok(FieldAttrs {
398            prefix,
399            postfix,
400            inside,
401            parse_method,
402            peek,
403        })
404    }
405}
406
407fn handle_field_attrs(field_name: &Ident, ty_span: Span, attrs: FieldAttrs) -> ParseField {
408    use ParseMethod::{Call, Default, ParseTerminated, Tree};
409
410    let input_source = attrs
411        .inside
412        .as_ref()
413        .map(tree_name)
414        .unwrap_or_else(crate::parse_input);
415
416    let required_var_defs;
417    let mut parse_expr;
418
419    match attrs.parse_method {
420        Default => {
421            required_var_defs = None;
422            parse_expr = quote_spanned! { ty_span=> #input_source.parse()? };
423        }
424        Call(expr) => {
425            required_var_defs = None;
426            parse_expr = quote_spanned!(expr.span()=> {
427                let result: ::syn::Result<_> = (#expr)(&#input_source);
428                result?
429            });
430        }
431        ParseTerminated(path) => {
432            required_var_defs = None;
433            parse_expr = quote_spanned! { path.span()=> #input_source.parse_terminated(#path)? };
434        }
435        Tree(tree_kind, span) => {
436            required_var_defs = Some(tree_name(field_name));
437
438            let macro_name = tree_kind.macro_name();
439            let tree_name = tree_name(field_name);
440            parse_expr = quote_spanned! { span=> ::syn::#macro_name!(#tree_name in #input_source) };
441        }
442    }
443
444    if let Some(p) = attrs.peek {
445        parse_expr = match p {
446            PeekAttr::Peek(expr) => quote_spanned!(expr.span()=> match #input_source.peek(#expr) {
447                true => Some(#parse_expr),
448                false => None,
449            }),
450            PeekAttr::PeekWith(expr) => quote_spanned!(expr.span()=> match (#expr)(#input_source) {
451                true => Some(#parse_expr),
452                false => None,
453            }),
454            PeekAttr::ParseIf(expr) => quote_spanned!(expr.span()=> match #expr {
455                true => Some(#parse_expr),
456                false => None,
457            }),
458        };
459    }
460
461    let neighbor_map = |na: NeighborAttr| -> TokenStream {
462        let assigned_name = na
463            .maybe_named
464            .unwrap_or_else(|| Ident::new("_", Span::call_site()));
465        let source = na
466            .maybe_inside
467            .as_ref()
468            .map(tree_name)
469            .unwrap_or_else(crate::parse_input);
470        let parse_ty = na.parse_ty;
471        quote! {
472            let #assigned_name: #parse_ty = #source.parse()?;
473        }
474    };
475
476    let pre_parse = attrs.prefix.into_iter().map(neighbor_map).collect();
477    let post_parse = attrs.postfix.into_iter().map(neighbor_map).collect();
478
479    ParseField {
480        required_var_defs,
481        parse_expr,
482        pre_parse,
483        post_parse,
484    }
485}
486
487fn tree_name(field_name: &Ident) -> Ident {
488    format_ident!("__{}_backing_token_stream", field_name)
489}
490
491impl TreeKind {
492    // Gives the name of the syn macro that corresponds to attempting parse the next token as a
493    // certain group
494    fn macro_name(&self) -> Ident {
495        let span = Span::call_site();
496        match self {
497            TreeKind::Paren => Ident::new("parenthesized", span),
498            TreeKind::Bracket => Ident::new("bracketed", span),
499            TreeKind::Brace => Ident::new("braced", span),
500        }
501    }
502}