peginator_codegen/
choice.rs

1// Copyright (C) 2022, Alex Badics
2// This file is part of peginator
3// Licensed under the MIT license. See LICENSE file in the project root for details.
4
5use anyhow::Result;
6use proc_macro2::TokenStream;
7use quote::{format_ident, quote};
8
9use super::common::{
10    generate_inner_parse_function, safe_ident, Arity, CloneState, Codegen, CodegenSettings,
11    FieldDescriptor,
12};
13use crate::{
14    common::combine_field_types,
15    grammar::{Choice, Grammar},
16};
17
18impl Codegen for Choice {
19    fn generate_code_spec(
20        &self,
21        rule_fields: &[FieldDescriptor],
22        grammar: &Grammar,
23        settings: &CodegenSettings,
24    ) -> Result<TokenStream> {
25        if self.choices.len() < 2 {
26            return self.choices[0].generate_code_spec(rule_fields, grammar, settings);
27        }
28        let choice_bodies = self
29            .choices
30            .iter()
31            .enumerate()
32            .filter(|(_, choice)| {
33                choice
34                    .generate_inline_body(rule_fields, grammar, settings, CloneState::Yes)
35                    .ok()
36                    .flatten()
37                    .is_none()
38            })
39            .map(|(num, choice)| -> Result<TokenStream> {
40                let choice_mod = format_ident!("choice_{num}");
41                let sequence_body = choice.generate_code(rule_fields, grammar, settings)?;
42                Ok(quote!(
43                    mod #choice_mod{
44                    use super::*;
45                        #sequence_body
46                    }
47                ))
48            })
49            .collect::<Result<TokenStream>>()?;
50        let parse_body =
51            self.generate_parse_body(rule_fields, grammar, settings, CloneState::No)?;
52        let parse_function = generate_inner_parse_function(parse_body, settings);
53        Ok(quote!(
54            #choice_bodies
55            #parse_function
56        ))
57    }
58
59    fn generate_inline_body(
60        &self,
61        rule_fields: &[FieldDescriptor],
62        grammar: &Grammar,
63        settings: &CodegenSettings,
64        clone_state: CloneState,
65    ) -> Result<Option<TokenStream>> {
66        if self.choices.len() < 2 {
67            self.choices[0].generate_inline_body(rule_fields, grammar, settings, clone_state)
68        } else if self.choices.iter().all(|c| {
69            c.generate_inline_body(rule_fields, grammar, settings, CloneState::No)
70                .ok()
71                .flatten()
72                .is_some()
73        }) && self.get_filtered_rule_fields(rule_fields, grammar)?.len() <= 1
74        {
75            Ok(Some(self.generate_parse_body(
76                rule_fields,
77                grammar,
78                settings,
79                clone_state,
80            )?))
81        } else {
82            Ok(None)
83        }
84    }
85
86    fn get_fields<'a>(&'a self, grammar: &'a Grammar) -> Result<Vec<FieldDescriptor<'a>>> {
87        let mut all_fields = Vec::<FieldDescriptor>::new();
88        let mut first_iteration = true;
89        for choice in &self.choices {
90            let new_fields = choice.get_fields(grammar)?;
91
92            if !first_iteration {
93                for field in &mut all_fields {
94                    if field.arity == Arity::One && !new_fields.iter().any(|f| f.name == field.name)
95                    {
96                        field.arity = Arity::Optional;
97                    }
98                }
99            }
100
101            for new_field in new_fields {
102                if let Some(original) = all_fields.iter_mut().find(|f| f.name == new_field.name) {
103                    original.arity = combine_arities_for_choice(&original.arity, &new_field.arity);
104                    combine_field_types(&mut original.types, &new_field.types);
105                } else if first_iteration || new_field.arity != Arity::One {
106                    all_fields.push(new_field);
107                } else {
108                    all_fields.push(FieldDescriptor {
109                        arity: Arity::Optional,
110                        ..new_field
111                    });
112                }
113            }
114
115            first_iteration = false;
116        }
117        Ok(all_fields)
118    }
119}
120
121impl Choice {
122    fn generate_parse_body(
123        &self,
124        rule_fields: &[FieldDescriptor],
125        grammar: &Grammar,
126        settings: &CodegenSettings,
127        clone_state: CloneState,
128    ) -> Result<TokenStream> {
129        let fields = self.get_filtered_rule_fields(rule_fields, grammar)?;
130        let calls = self
131            .choices
132            .iter()
133            .enumerate()
134            .map(|(num, choice)| {
135                let parse_call = if let Some(inline_body) = choice
136                    .generate_inline_body(rule_fields, grammar, settings, CloneState::No)
137                    .unwrap()
138                {
139                    inline_body
140                } else {
141                    let choice_mod = format_ident!("choice_{num}");
142                    quote!(#choice_mod::parse(state, global))
143                };
144                let inner_fields = choice.get_fields(grammar).unwrap();
145                let postprocess = Self::generate_result_converter(&fields, &inner_fields);
146                quote!(
147                    .choice(|state| #parse_call #postprocess)
148                )
149            })
150            .collect::<TokenStream>();
151        let state = match clone_state {
152            CloneState::No => quote!(state),
153            CloneState::Yes => quote!(state.clone()),
154        };
155        Ok(quote!(
156            ChoiceHelper::new(#state)
157                #calls
158                .end()
159        ))
160    }
161
162    fn generate_result_converter(
163        fields: &[FieldDescriptor],
164        inner_fields: &[FieldDescriptor],
165    ) -> TokenStream {
166        if fields.is_empty() {
167            TokenStream::new()
168        } else if fields.len() == 1 {
169            if inner_fields.is_empty() {
170                let default = Self::generate_default_field(&fields[0]);
171                quote!(.map_inner(|_| #default))
172            } else {
173                TokenStream::new()
174            }
175        } else {
176            let field_assignments: TokenStream = fields
177                .iter()
178                .map(|field| {
179                    let name = safe_ident(field.name);
180                    let inner_exists = inner_fields
181                        .iter()
182                        .any(|inner_field| inner_field.name == field.name);
183                    let value = if inner_exists {
184                        if inner_fields.len() == 1 {
185                            quote!(r)
186                        } else {
187                            quote!(r.#name)
188                        }
189                    } else {
190                        Self::generate_default_field(field)
191                    };
192                    quote!(#name: #value,)
193                })
194                .collect();
195            quote!(.map_inner(|r| Parsed{ #field_assignments }))
196        }
197    }
198
199    fn generate_default_field(field: &FieldDescriptor) -> TokenStream {
200        match field.arity {
201            Arity::One => {
202                panic!("Outer field ({field:?}) cannot be One if inner does not exist",)
203            }
204            Arity::Optional => quote!(None),
205            Arity::Multiple => quote!(Vec::new()),
206        }
207    }
208}
209
210fn combine_arities_for_choice(left: &Arity, right: &Arity) -> Arity {
211    match (left, right) {
212        (Arity::One, Arity::One) => Arity::One,
213        (Arity::One, Arity::Optional) => Arity::Optional,
214        (Arity::One, Arity::Multiple) => Arity::Multiple,
215        (Arity::Optional, Arity::One) => Arity::Optional,
216        (Arity::Optional, Arity::Optional) => Arity::Optional,
217        (Arity::Optional, Arity::Multiple) => Arity::Multiple,
218        (Arity::Multiple, Arity::One) => Arity::Multiple,
219        (Arity::Multiple, Arity::Optional) => Arity::Multiple,
220        (Arity::Multiple, Arity::Multiple) => Arity::Multiple,
221    }
222}