lwb_parser/codegen/
generate_from_pairs.rs

1use crate::codegen::check_recursive::{BreadthFirstAstIterator, RecursionChecker};
2use crate::codegen::error::CodegenError;
3use crate::codegen::sanitize_identifier;
4use crate::parser::peg::parser_sugar_ast::Annotation::SingleString;
5use crate::parser::peg::parser_sugar_ast::{Annotation, Expression, Sort, SyntaxFileAst};
6use itertools::Itertools;
7use proc_macro2::TokenStream;
8use quote::{format_ident, quote};
9use std::collections::HashMap;
10
11fn generate_unpack_expression(
12    expression: &Expression,
13    sort: &str,
14    src: TokenStream,
15    ckr: &RecursionChecker,
16    non_exhaustive: TokenStream,
17    sort_list: &HashMap<&str, &Sort>,
18) -> Option<TokenStream> {
19    let unreachable_exp = quote!(unreachable!("expected different parse pair expression in pair to ast conversion of {}", #sort););
20
21    Some(match expression {
22        Expression::Sort(name) => {
23            if sort_list
24                .get(name.as_str())
25                .map(|i| i.annotations.contains(&Annotation::Hidden))
26                .unwrap_or_default()
27            {
28                return None;
29            }
30
31            let iname = format_ident!("{}", sanitize_identifier(name));
32
33            let inner = ckr.maybe_box(
34                name,
35                quote!(
36                    #iname::from_pairs(s, generator)
37                ),
38            );
39
40            quote!(
41                if let ParsePairExpression::Sort(_, ref s) = #src {
42                    #inner
43                } else { #unreachable_exp }
44            )
45        }
46        Expression::CharacterClass(_) => {
47            quote!(
48                if let ParsePairExpression::Empty(ref span) = #src {
49                    span.as_str().to_string()
50                } else { #unreachable_exp }
51            )
52        }
53        Expression::Repeat { min, max, e } | Expression::Delimited { min, max, e, .. } => {
54            if let Some(ue) =
55                generate_unpack_expression(e, sort, quote!(x), ckr, non_exhaustive, sort_list)
56            {
57                match (min, max) {
58                    (0, Some(1)) => quote!(
59                        if let ParsePairExpression::List(_, ref l) = #src {
60                            l.first().map(|x| #ue)
61                        } else { #unreachable_exp }
62                    ),
63                    _ => quote!(
64                        if let ParsePairExpression::List(_, ref l) = #src {
65                            l.iter().map(|x| #ue).collect()
66                        } else { #unreachable_exp }
67                    ),
68                }
69            } else {
70                match (min, max) {
71                    (0, Some(1)) => quote!(
72                        if let ParsePairExpression::List(_, ref l) = #src {
73                            l.first().is_some()
74                        } else { #unreachable_exp }
75                    ),
76                    _ => quote!(
77                        if let ParsePairExpression::List(_, ref l) = #src {
78                            l.iter().len()
79                        } else { #unreachable_exp }
80                    ),
81                }
82            }
83        }
84        Expression::Literal(_) => return None,
85        Expression::Sequence(c) => {
86            let mut expressions = Vec::new();
87            for (index, i) in c.iter().enumerate() {
88                match i {
89                    Expression::Sequence(_) => unreachable!(),
90                    Expression::Choice(_) => todo!(),
91                    Expression::Literal(_) => continue,
92                    Expression::Negative(_) => continue,
93                    Expression::Positive(_) => continue,
94                    _ => {}
95                }
96
97                if let Some(line) = generate_unpack_expression(
98                    i,
99                    sort,
100                    quote!(p[#index]),
101                    ckr,
102                    non_exhaustive.clone(),
103                    sort_list,
104                ) {
105                    expressions.push(line)
106                }
107            }
108
109            if expressions.is_empty() {
110                return None;
111            } else if let [ref expression] = expressions.as_slice() {
112                quote!(
113                    if let ParsePairExpression::List(_, ref l) = #src {
114                        #expression
115                    } else { #unreachable_exp }
116                )
117            } else {
118                quote!(
119                    if let ParsePairExpression::List(_, ref l) = #src {
120                        (#(#expressions),*)
121                    } else { #unreachable_exp }
122                )
123            }
124        }
125        a => unreachable!(
126            "this expression should never be given to generate_unpack_expression: {:?}",
127            a
128        ),
129    })
130}
131
132#[allow(clippy::too_many_arguments)]
133fn generate_unpack(
134    sort: &str,
135    constructor: TokenStream,
136    expression: &Expression,
137    no_layout: bool,
138    ckr: &RecursionChecker,
139    non_exhaustive: TokenStream,
140    sort_list: &HashMap<&str, &Sort>,
141    dont_put_in_ast: bool,
142) -> TokenStream {
143    if no_layout {
144        return quote!(
145            return #constructor(info, pair.constructor_value.span().as_str().to_string());
146        );
147    }
148
149    let unreachable_exp = quote!(unreachable!("expected different parse pair expression in pair to ast conversion of {}", #sort););
150
151    match expression {
152        a @ Expression::Sort(_) => {
153            let nested = generate_unpack_expression(
154                a,
155                sort,
156                quote!(pair.constructor_value),
157                ckr,
158                non_exhaustive.clone(),
159                sort_list,
160            );
161
162            if dont_put_in_ast {
163                quote!(
164                    *#nested
165                )
166            } else {
167                quote!(
168                    #constructor(info, #nested #non_exhaustive)
169                )
170            }
171        }
172        Expression::Sequence(c) => {
173            let mut expressions = Vec::new();
174            for (index, i) in c.iter().enumerate() {
175                match i {
176                    Expression::Sequence(_) => unreachable!(),
177                    Expression::Choice(_) => todo!(),
178                    Expression::Literal(_) | Expression::Negative(_) | Expression::Positive(_) => {
179                        continue;
180                    }
181                    _ => {}
182                }
183
184                if let Some(line) = generate_unpack_expression(
185                    i,
186                    sort,
187                    quote!(l[#index]),
188                    ckr,
189                    non_exhaustive.clone(),
190                    sort_list,
191                ) {
192                    expressions.push(line)
193                }
194            }
195
196            if expressions.is_empty() {
197                quote!(
198                    #constructor(info #non_exhaustive)
199                )
200            } else {
201                quote!(
202                    if let ParsePairExpression::List(_, ref l) = pair.constructor_value {
203                        #constructor(info, #(#expressions),* #non_exhaustive)
204                    } else { #unreachable_exp }
205                )
206            }
207        }
208        a @ Expression::Repeat { .. }
209        | a @ Expression::Delimited { .. }
210        | a @ Expression::CharacterClass(_) => {
211            if let Some(expression) = generate_unpack_expression(
212                a,
213                sort,
214                quote!(pair.constructor_value),
215                ckr,
216                non_exhaustive.clone(),
217                sort_list,
218            ) {
219                quote!(#constructor(info, #expression #non_exhaustive))
220            } else {
221                quote!(#constructor(info #non_exhaustive))
222            }
223        }
224        Expression::Choice(_) => todo!(),
225        Expression::Literal(_) => {
226            quote!(#constructor(info #non_exhaustive))
227        }
228        Expression::Negative(_) => todo!(),
229        Expression::Positive(_) => todo!(),
230    }
231}
232
233pub fn flatten_sequences(syntax: Expression) -> Expression {
234    match syntax {
235        Expression::Sequence(s) => Expression::Sequence(
236            s.into_iter()
237                .flat_map(|i| match flatten_sequences(i) {
238                    Expression::Sequence(s) => s,
239                    a => vec![a],
240                })
241                .collect(),
242        ),
243        a => a,
244    }
245}
246
247pub fn generate_from_pairs(
248    syntax: &SyntaxFileAst,
249    non_exhaustive: bool,
250) -> Result<TokenStream, CodegenError> {
251    let mut impls = Vec::new();
252
253    let non_exhaustive = if non_exhaustive {
254        quote!(, NonExhaustive)
255    } else {
256        TokenStream::new()
257    };
258    let sort_list = syntax
259        .sorts
260        .iter()
261        .map(|(k, v)| (k.as_str(), v))
262        .collect::<HashMap<&str, &Sort>>();
263
264    let arena = Default::default();
265    let sorts_iterator = BreadthFirstAstIterator::new(syntax, &arena);
266
267    for (sort, ckr) in sorts_iterator {
268        if sort.annotations.contains(&Annotation::Hidden) {
269            continue;
270        }
271
272        let sortnames_str = syntax.names(&sort.name);
273        let sortname = format_ident!("{}", sanitize_identifier(&sort.name));
274
275        let unpack_body = if sort.constructors.len() == 1 {
276            let constr = &sort.constructors[0];
277
278            generate_unpack(
279                &sort.name,
280                quote!(Self),
281                &flatten_sequences(constr.expression.clone()),
282                constr.annotations.contains(&SingleString),
283                ckr,
284                non_exhaustive.clone(),
285                &sort_list,
286                constr.dont_put_in_ast,
287            )
288        } else {
289            let constructor_names_str = sort
290                .constructors
291                .iter()
292                .filter(|i| {
293                    !i.annotations
294                        .iter()
295                        .any(|i| matches!(i, Annotation::Error(_)))
296                })
297                .map(|i| i.name.as_str())
298                .collect_vec();
299
300            let unpacks = sort
301                .constructors
302                .iter()
303                .filter(|i| {
304                    !i.annotations
305                        .iter()
306                        .any(|i| matches!(i, Annotation::Error(_)))
307                })
308                .map(|constr| {
309                    let name = format_ident!("{}", sanitize_identifier(&constr.name));
310
311                    generate_unpack(
312                        &sort.name,
313                        quote!(
314                            Self::#name
315                        ),
316                        &flatten_sequences(constr.expression.clone()),
317                        constr.annotations.contains(&SingleString),
318                        ckr,
319                        non_exhaustive.clone(),
320                        &sort_list,
321                        constr.dont_put_in_ast,
322                    )
323                })
324                .collect_vec();
325
326            quote!(
327                match pair.constructor_name {
328                    #(
329                        #constructor_names_str => #unpacks
330                    ),*,
331                    a => unreachable!("{}", a),
332                }
333            )
334        };
335
336        impls.push(quote!(
337            impl<M: AstInfo> FromPairs<M> for #sortname<M> {
338                fn from_pairs<G: GenerateAstInfo<Result = M>>(pair: &ParsePairSort, generator: &mut G) -> Self {
339                    assert!(vec![#(#sortnames_str),*].contains(&pair.sort), "{} not in {:?}", pair.sort, vec![#(#sortnames_str),*]);
340                    let info = generator.generate(&pair);
341
342                    #unpack_body
343                }
344            }
345        ));
346    }
347
348    Ok(quote!(
349        use super::prelude::*;
350
351        #(#impls)*
352    ))
353}
354
355#[cfg(test)]
356mod tests {
357    use crate::codegen::generate_from_pairs::flatten_sequences;
358    use crate::parser::peg::parser_sugar_ast::Expression::{Literal, Sequence};
359
360    #[test]
361    fn test_flatten_sequences() {
362        assert_eq!(
363            flatten_sequences(Sequence(vec![Literal("a".to_string())])),
364            Sequence(vec![Literal("a".to_string())])
365        );
366        assert_eq!(
367            flatten_sequences(Sequence(vec![
368                Literal("a".to_string()),
369                Literal("b".to_string()),
370            ])),
371            Sequence(vec![Literal("a".to_string()), Literal("b".to_string()),])
372        );
373        assert_eq!(
374            flatten_sequences(Sequence(vec![
375                Sequence(vec![Literal("a".to_string()), Literal("b".to_string()),]),
376                Sequence(vec![
377                    Literal("c".to_string()),
378                    Sequence(vec![Literal("d".to_string()), Literal("e".to_string()),])
379                ])
380            ])),
381            Sequence(vec![
382                Literal("a".to_string()),
383                Literal("b".to_string()),
384                Literal("c".to_string()),
385                Literal("d".to_string()),
386                Literal("e".to_string()),
387            ])
388        );
389    }
390}