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}