pegcel_macros/
lib.rs

1extern crate proc_macro;
2
3#[allow(warnings)]
4mod grammar;
5mod manual_grammar;
6
7use {
8    crate::{
9        grammar::{Item, *},
10        manual_grammar::*,
11    },
12    indexmap::{IndexMap, IndexSet},
13    itertools::{izip, zip, Itertools},
14    matches::{assert_matches, matches},
15    proc_macro2::{Span, TokenStream},
16    quote::{quote, TokenStreamExt},
17    syn::{parse_quote, spanned::Spanned, Ident, Result},
18    uuid::Uuid,
19};
20
21// FIXME: We should have deterministic output
22fn new_uuidv4_ident() -> Ident {
23    Ident::new(
24        &format!("_{}", Uuid::new_v4().to_simple()),
25        Span::call_site(),
26    )
27}
28
29#[proc_macro]
30// procedural macros cannot expand to macro definitions (see issue #54727) so shim through a derive
31// https://www.reddit.com/r/rust/comments/be9oqk/_/el4i6r3/
32pub fn pegcel_syn(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
33    let ident = new_uuidv4_ident();
34    let input: proc_macro2::TokenStream = input.into();
35    (quote! {
36        #[derive(::pegcel_macros::__pegcel_syn_dummy_derive)]
37        #[real_input(#input)]
38        struct #ident;
39    })
40    .into()
41}
42
43#[doc(hidden)]
44#[proc_macro_derive(__pegcel_syn_dummy_derive, attributes(real_input))]
45pub fn pegcel_syn_real(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
46    let input: Parenthesized<proc_macro2::TokenStream> = syn::parse2(
47        syn::parse_macro_input!(tokens as syn::DeriveInput)
48            .attrs
49            .into_iter()
50            .find(|attr| attr.path.is_ident("real_input"))
51            .unwrap()
52            .tts,
53    )
54    .unwrap();
55    let input: proc_macro::TokenStream = input.0.into();
56
57    // syn::Error::new(Span::call_site(),
58    make_syn(syn::parse_macro_input!(input))
59        .unwrap_or_else(|err| err.to_compile_error())
60        // ).to_compile_error()
61        .into()
62}
63
64struct Parenthesized<T>(T);
65impl<T: syn::parse::Parse> syn::parse::Parse for Parenthesized<T> {
66    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
67        let content;
68        syn::parenthesized!(content in input);
69        Ok(Parenthesized(content.parse()?))
70    }
71}
72
73impl Item {
74    fn unnamed(&self) -> &UnnamedItem {
75        match self {
76            Item::Unnamed(item) => item,
77            Item::Named(named) => &named.item,
78        }
79    }
80}
81
82impl Predicate {
83    fn positive(&self) -> bool {
84        match self {
85            Predicate::Positive(_) => true,
86            Predicate::Negative(_) => false,
87        }
88    }
89}
90
91fn make_syn(input: Grammar) -> Result<proc_macro2::TokenStream> {
92    // this method only handles targeting syn
93    assert_matches!(input.kind, GrammarUse::Syn(..));
94
95    let mut output = TokenStream::new();
96
97    // make sure the required crate dependencies are present
98    output.append_all(quote! {
99        // FIXME: these should probably be `pegcel::runtime::external::*` instead
100        extern crate proc_macro2;
101        extern crate quote;
102        extern crate syn;
103    });
104
105    // include manual `use`s
106    for Use { anchor, tree, .. } in &input.uses {
107        output.append_all(quote! {
108            use #anchor #tree ;
109        })
110    }
111
112    // `mod kw`, `mod punct`, `macro Token`
113    output.append_all(custom_tokens(&input));
114
115    for item in input.items {
116        output.append_all(make_production_def(item)?);
117    }
118
119    Ok(output)
120}
121
122fn custom_tokens(input: &Grammar) -> TokenStream {
123    #[derive(Debug, Default)]
124    struct Visitor {
125        custom_keywords: IndexSet<Ident>,
126        custom_punctuation: IndexMap<String, Ident>,
127    };
128
129    impl Visitor {
130        fn to_defs(&self) -> TokenStream {
131            let mut tokens = TokenStream::new();
132
133            let keywords = &self.custom_keywords;
134            let kws = &self.custom_keywords;
135            let punct_name: Vec<_> = self.custom_punctuation.values().collect();
136            let punct_name = &*punct_name;
137            let punct_val: Vec<TokenStream> = self
138                .custom_punctuation
139                .keys()
140                .map(|it| syn::parse_str(it).unwrap())
141                .collect();
142            let punct_val = &*punct_val;
143
144            let token_macro = new_uuidv4_ident();
145
146            tokens.append_all(quote! {
147                pub mod kw {
148                    #(::syn::custom_keyword!(#keywords);)*
149                }
150                pub mod punct {
151                    #(::syn::custom_punctuation!(#punct_name, #punct_val);)*
152                }
153                #[doc(hidden)]
154                macro_rules! #token_macro {
155                    #((#keywords) => {self::kw::#kws};)*
156                    #((#punct_val) => {self::punct::#punct_name};)*
157                    ($($tt:tt)*) => {::syn::Token![$($tt)*]};
158                }
159                // FIXME: Provide some way to export this conditionally
160                // error: cannot export macro_rules! macros from a `proc-macro` crate type currently
161                // Also, we probably want to offer fallback to non-syn Token! to allow stacking
162                pub(crate) use #token_macro as Token;
163            });
164
165            tokens
166        }
167
168        fn visit_grammar(&mut self, grammar: &Grammar) -> &mut Self {
169            for named in &grammar.items {
170                self.visit_unnamed_item(&named.item);
171            }
172            self
173        }
174
175        fn visit_unnamed_item(&mut self, item: &UnnamedItem) -> &mut Self {
176            match item {
177                UnnamedItem::Predicated(_, item) => self.visit_unnamed_item(item),
178                UnnamedItem::Repeated(item, rep) => {
179                    self.visit_unnamed_item(item);
180                    match rep {
181                        Repetition::OnePlus(_, Some(inter))
182                        | Repetition::ZeroPlus(_, Some(inter)) => match inter {
183                            Interspersion::Terminated(_, sym)
184                            | Interspersion::Separated(_, sym) => self.visit_symbol(sym),
185                        },
186                        _ => self,
187                    }
188                }
189                UnnamedItem::Block(items) => {
190                    match &items.value {
191                        Items::Sequence(items) => {
192                            for item in items {
193                                match &item {
194                                    Item::Named(named) => self.visit_unnamed_item(&named.item),
195                                    Item::Unnamed(item) => self.visit_unnamed_item(item),
196                                };
197                            }
198                        }
199                        Items::OrderedChoice(_, items) => {
200                            for named in items {
201                                self.visit_unnamed_item(&named.item);
202                            }
203                        }
204                    }
205                    self
206                }
207                UnnamedItem::Symbol(symb) => self.visit_symbol(symb),
208            }
209        }
210
211        fn visit_symbol(&mut self, symbol: &Symbol) -> &mut Self {
212            match symbol {
213                Symbol::Path(_) => (),
214                Symbol::Literal(lit) => match &*lit.value() {
215                    "abstract" | "as" | "async" | "auto" | "become" | "box" | "break" | "const"
216                    | "continue" | "crate" | "default" | "do" | "dyn" | "else" | "enum"
217                    | "existential" | "extern" | "final" | "fn" | "for" | "if" | "impl" | "in"
218                    | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut" | "override"
219                    | "priv" | "pub" | "ref" | "return" | "Self" | "self" | "static" | "struct"
220                    | "super" | "trait" | "try" | "type" | "typeof" | "union" | "unsafe"
221                    | "unsized" | "use" | "virtual" | "where" | "while" | "yield" | "+" | "+="
222                    | "&" | "&&" | "&=" | "@" | "!" | "^" | "^=" | ":" | "::" | "," | "/"
223                    | "/=" | "." | ".." | "..." | "..=" | "=" | "==" | ">=" | ">" | "<=" | "<"
224                    | "*=" | "!=" | "|" | "|=" | "||" | "#" | "?" | "->" | "<-" | "%" | "%="
225                    | "=>" | ";" | "<<" | "<<=" | ">>" | ">>=" | "*" | "-" | "-=" | "~" | "_" => (),
226                    s => {
227                        if s.chars()
228                            .all(|c| 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z')
229                        {
230                            self.custom_keywords
231                                .insert(Ident::new(s, Span::call_site()));
232                        } else {
233                            self.custom_punctuation
234                                .insert(lit.value(), new_uuidv4_ident());
235                        }
236                    }
237                },
238            }
239            self
240        }
241    }
242
243    Visitor::default().visit_grammar(input).to_defs()
244}
245
246fn make_production_def(input: NamedItem) -> Result<TokenStream> {
247    let items = if let UnnamedItem::Block(Braced { value, .. }) = input.item {
248        value
249    } else {
250        Err(syn::Error::new(
251            input.item.span(),
252            "Root production must be braced",
253        ))?
254    };
255
256    match items {
257        Items::OrderedChoice(_, items) => {
258            make_enum_production_def(input.name, items.into_iter().collect())
259        }
260        Items::Sequence(items) => make_struct_production_def(input.name, items),
261    }
262}
263
264fn make_enum_production_def(name: Ident, variants: Vec<NamedItem>) -> Result<TokenStream> {
265    let variant_names = variants.iter().map(|item| item.name.clone()).collect_vec();
266    let variant_names = &*variant_names;
267    let name_ = variant_names.iter().map(|_| &name).collect_vec();
268    let variant_fields: Vec<syn::Fields> = variants
269        .iter()
270        .map(|item| match &item.item {
271            UnnamedItem::Block(Braced { value: block, .. }) => {
272                let items = match block {
273                    // FIXME: This shouldn't panic, rather Err with a useful span
274                    Items::OrderedChoice(..) => Err(syn::Error::new(
275                        item.item.span(),
276                        "ordered choice not allowed here",
277                    ))?,
278                    Items::Sequence(items) => items,
279                };
280                Ok(make_fields(items, syn::Visibility::Inherited))
281            }
282            UnnamedItem::Symbol(_) | UnnamedItem::Repeated(..) => {
283                // FIXME: This shouldn't panic, rather Err with a useful span
284                let ty =
285                    make_type(&item.item).unwrap_or_else(|| panic!("Non-advancing repetition"));;
286                Ok(syn::Fields::Unnamed(syn::FieldsUnnamed {
287                    paren_token: Default::default(),
288                    unnamed: {
289                        let mut punctuated = syn::punctuated::Punctuated::new();
290                        punctuated.push(syn::Field {
291                            attrs: vec![],
292                            vis: syn::Visibility::Inherited,
293                            ident: None,
294                            colon_token: None,
295                            ty,
296                        });
297                        punctuated
298                    },
299                }))
300            }
301            UnnamedItem::Predicated(..) => Err(syn::Error::new(
302                item.item.span(),
303                "predicate not allowed here",
304            )),
305        })
306        .collect::<Result<_>>()?;
307
308    let param = new_uuidv4_ident();
309
310    // impl ToTokens
311
312    let field_names: Vec<Vec<syn::Ident>> = variant_fields
313        .iter()
314        .map(|fields| match fields {
315            syn::Fields::Named(_) => fields
316                .iter()
317                .map(|field| field.ident.as_ref().unwrap().clone())
318                .collect_vec(),
319            syn::Fields::Unnamed(_) => fields.iter().map(|_| new_uuidv4_ident()).collect_vec(),
320            syn::Fields::Unit => unreachable!("Unexpected Unit type {:?}", name),
321        })
322        .collect_vec();
323    let field_pats: Vec<syn::Pat> = izip!(&field_names, &variant_fields)
324        .map(|(names, fields)| match fields {
325            syn::Fields::Named(_) => parse_quote!({ #(#names),* }),
326            syn::Fields::Unnamed(_) => parse_quote!(( #(#names),* )),
327            syn::Fields::Unit => unreachable!("Unexpected Unit type {:?}", name),
328        })
329        .collect_vec();
330
331    let mut to_tokens = TokenStream::new();
332
333    for (fields, field_pat, variant_name, variant) in
334        izip!(&field_names, &field_pats, variant_names, &variants)
335    {
336        let variant = &variant.item;
337        let dt = match variant {
338            UnnamedItem::Predicated(..) => unreachable!("ordered choice cannot be predicate"),
339            UnnamedItem::Repeated(..) | UnnamedItem::Symbol(..) => {
340                // in this case `field_pat` is `(#ident)` because it's a newtype variant
341                tokenize(parse_quote!(#field_pat), variant, &param)
342            }
343            UnnamedItem::Block(Braced { value: block, .. }) => {
344                let items = match block {
345                    // FIXME: This shouldn't panic, rather Err with a useful span
346                    Items::OrderedChoice(..) => panic!("choice not allowed here"),
347                    Items::Sequence(items) => items,
348                };
349                let items = items
350                    .iter()
351                    .filter(|item| make_type(item.unnamed()).is_some())
352                    .collect_vec();
353
354                let mut dt = TokenStream::new();
355                assert_eq!(fields.len(), items.len());
356                for (field, item) in zip(fields, items) {
357                    dt.extend(tokenize(parse_quote!(#field), item.unnamed(), &param));
358                }
359                dt
360            }
361        };
362        to_tokens.extend(quote! {
363            #name :: #variant_name #field_pat => { #dt },
364        })
365    }
366
367    // TODO: impl Parse
368    let is_variant = (&variants[..variants.len() - 1])
369        .iter()
370        .map(|item| match &item.item {
371            UnnamedItem::Predicated(..) => unreachable!("checked before"),
372            UnnamedItem::Repeated(..) => {
373                unreachable!("cannot start non-last choice with repetition")
374            }
375            UnnamedItem::Block(Braced { value: block, .. }) => {
376                let items = match block {
377                    // FIXME: This shouldn't panic, rather Err with a useful span
378                    Items::OrderedChoice(..) => panic!("choice not allowed here"),
379                    Items::Sequence(items) => items,
380                };
381                block_la(&items, &param)
382            }
383            UnnamedItem::Symbol(symbol) => {
384                let ty = make_symbol_type(symbol);
385                quote!(#param.peek(#ty))
386            }
387        })
388        .chain(Some(quote!(true)))
389        .collect_vec();
390    let parse: Vec<TokenStream> = zip(&*variants, &*field_names)
391        .map(|(variant, fields)| {
392            match &variant.item {
393                UnnamedItem::Predicated(..) => TokenStream::new(), // skip
394                item @ UnnamedItem::Symbol(_) | item @ UnnamedItem::Repeated(..) => {
395                    assert_eq!(fields.len(), 1);
396                    let field = &fields[0];
397                    detokenize(parse_quote!(#field), item, &param)
398                }
399                UnnamedItem::Block(Braced { value: block, .. }) => {
400                    let items = match block {
401                        // FIXME: This shouldn't panic, rather Err with a useful span
402                        Items::OrderedChoice(..) => panic!("choice not allowed here"),
403                        Items::Sequence(items) => items,
404                    };
405                    let items = items
406                        .iter()
407                        .filter(|item| !matches!(item.unnamed(), UnnamedItem::Predicated(..)))
408                        .collect_vec();
409                    assert_eq!(items.len(), fields.len());
410                    let mut dt = TokenStream::new();
411                    for (name, item) in zip(fields, items) {
412                        dt.extend(detokenize(parse_quote!(#name), item.unnamed(), &param));
413                    }
414                    dt
415                }
416            }
417        })
418        .collect_vec();
419
420    Ok(quote! {
421        // deriving all is fairly safe since we depend on syn:extra-traits
422        // so long as the runtime syn is 0.15 we'll have syn:extra-traits on
423        // if not, something else is likely to break anyway
424        // FIXME: Use pegcel::runtime::external::* to use known version
425        #[derive(Clone, Debug, Eq, PartialEq, Hash)]
426        pub enum #name {
427            #(#variant_names #variant_fields,)*
428        }
429
430        impl ::syn::parse::Parse for #name {
431            fn parse(#param: ::syn::parse::ParseStream) -> ::syn::parse::Result<Self> {
432                #(if #is_variant {
433                    #(let mut #field_names;)*
434                    #parse
435                    Ok(#name_ :: #variant_names #field_pats)
436                } else)* {
437                    // FIXME: Allow versions that don't assume the last is unpeekable
438                    // i.e. do something like syn::parse::Lookahead1 for better errors when possible
439                    unreachable!("pegcel ordered choice fallthrough case reached")
440                }
441            }
442        }
443
444        impl ::quote::ToTokens for #name {
445            fn to_tokens(&self, #param: &mut ::proc_macro2::TokenStream) {
446                use ::quote::{ToTokens, TokenStreamExt};
447                match self {
448                    #to_tokens
449                }
450            }
451        }
452    })
453}
454
455fn make_struct_production_def(name: Ident, members: Vec<Item>) -> Result<TokenStream> {
456    let fields = make_fields(&members, parse_quote!(pub));
457    let param = new_uuidv4_ident();
458
459    // impl ToTokens
460
461    let member_names: Vec<syn::Member> = match fields {
462        syn::Fields::Named(_) => fields
463            .iter()
464            .map(|field| syn::Member::Named(field.ident.as_ref().unwrap().clone()))
465            .collect(),
466        syn::Fields::Unnamed(_) => fields
467            .iter()
468            .enumerate()
469            .map(|(i, field)| {
470                syn::Member::Unnamed(syn::Index {
471                    index: i as u32,
472                    span: field.span(),
473                })
474            })
475            .collect(),
476        syn::Fields::Unit => unreachable!("Unexpected Unit type {:?}", name),
477    };
478    let member_names = &*member_names;
479
480    let mut to_tokens = quote! {
481        use ::quote::{ToTokens, TokenStreamExt};
482    };
483
484    for (name, item) in zip(member_names, &members) {
485        to_tokens.extend(tokenize(
486            parse_quote!((&self.#name)),
487            item.unnamed(),
488            &param,
489        ));
490    }
491
492    // impl Parse
493
494    let field_names: Vec<Ident> = member_names
495        .iter()
496        .map(|member| match member {
497            syn::Member::Named(name) => name.clone(),
498            syn::Member::Unnamed(_) => new_uuidv4_ident(),
499        })
500        .collect();
501    let field_names = &*field_names;
502
503    let mut parse = TokenStream::new();
504
505    for (name, item) in zip(field_names, &members) {
506        parse.extend(detokenize(parse_quote!(#name), item.unnamed(), &param));
507    }
508
509    let make_struct = match fields {
510        syn::Fields::Named(_) => quote!(#name { #(#field_names),* }),
511        syn::Fields::Unnamed(_) => quote!(#name( #(#field_names),* )),
512        syn::Fields::Unit => unreachable!("Unexpected Unit type {:?}", name),
513    };
514
515    // add semi only for tuple struct
516    let fields = match fields {
517        syn::Fields::Unnamed(fields) => quote!(#fields;),
518        syn::Fields::Named(fields) => quote!(#fields),
519        syn::Fields::Unit => unreachable!("Unexpected Unit type {:?}", name),
520    };
521
522    Ok(quote! {
523        // deriving all is fairly safe since we depend on syn:extra-traits
524        // so long as the runtime syn is 0.15 we'll have syn:extra-traits on
525        // if not, something else is likely to break anyway
526        // FIXME: Use pegcel::runtime::external::* to use known version
527        #[derive(Clone, Debug, Eq, PartialEq, Hash)]
528        pub struct #name #fields
529
530        impl ::syn::parse::Parse for #name {
531            fn parse(#param: ::syn::parse::ParseStream) -> ::syn::parse::Result<Self> {
532                #(let mut #field_names;)*
533                #parse
534                Ok(#make_struct)
535            }
536        }
537
538        impl ::quote::ToTokens for #name {
539            fn to_tokens(&self, #param: &mut ::proc_macro2::TokenStream) {
540                #to_tokens
541            }
542        }
543    })
544}
545
546fn tokenize(place: syn::Expr, item: &UnnamedItem, param: &Ident) -> TokenStream {
547    match item {
548        UnnamedItem::Predicated(..) => TokenStream::new(), // skip
549        UnnamedItem::Symbol(_) => quote!(#place.to_tokens(#param);),
550        UnnamedItem::Block(Braced { value: block, .. }) => {
551            let items = match block {
552                Items::OrderedChoice(..) => unreachable!("errored earlier"),
553                Items::Sequence(items) => items
554                    .iter()
555                    .map(|item| match item {
556                        Item::Named(_) => unreachable!("errored earlier"),
557                        Item::Unnamed(item) => item,
558                    })
559                    .filter(|item| !matches!(item, UnnamedItem::Predicated(..)))
560                    .collect_vec(),
561            };
562            let names = items.iter().map(|_| new_uuidv4_ident()).collect_vec();
563            let tokenization = zip(&names, &items)
564                .map(|(name, item)| tokenize(parse_quote!(#name), item, param))
565                .collect_vec();
566
567            quote! {
568                let (#(#names),*) = #place;
569                #(#tokenization)*
570            }
571        }
572        UnnamedItem::Repeated(item, repetition) => match repetition {
573            Repetition::ZeroOne(_) => quote!(#place.to_tokens(#param);),
574            Repetition::ZeroPlus(_, None) | Repetition::OnePlus(_, None) => {
575                let ident = new_uuidv4_ident();
576                let sub_tokenize = tokenize(parse_quote!(#ident), &item, param);
577                quote! {
578                    for #ident in #place {
579                        #sub_tokenize
580                    }
581                }
582            }
583            Repetition::ZeroPlus(_, Some(_)) | Repetition::OnePlus(_, Some(_)) => {
584                let pair = new_uuidv4_ident();
585                let value = new_uuidv4_ident();
586                let punct = new_uuidv4_ident();
587                let sub_tokenize = tokenize(parse_quote!(#value), &item, param);
588                quote! {
589                    for #pair in #place.pairs() {
590                        let #value = #pair.value();
591                        #sub_tokenize
592
593                        if let Some(#punct) = #pair.punct() {
594                            #punct.to_tokens(#param);
595                        }
596                    }
597                }
598            }
599        },
600    }
601}
602
603#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
604#[allow(clippy::enum_variant_names)] // rust-lang/rust-clippy#739
605enum Peek {
606    Peek1,
607    Peek2,
608    Peek3,
609}
610
611impl Peek {
612    fn succ(self) -> Option<Peek> {
613        match self {
614            Peek::Peek1 => Some(Peek::Peek2),
615            Peek::Peek2 => Some(Peek::Peek3),
616            Peek::Peek3 => None,
617        }
618    }
619    fn succn(self, n: usize) -> Option<Peek> {
620        if n > 0 {
621            self.succ().and_then(|p| p.succn(n - 1))
622        } else {
623            Some(self)
624        }
625    }
626}
627
628fn la(parity: bool, peek: Peek, item: &UnnamedItem, param: &Ident) -> TokenStream {
629    let not = if !parity { quote!(!) } else { quote!() };
630    let f = match peek {
631        Peek::Peek1 => quote!(#not #param.peek),
632        Peek::Peek2 => quote!(#not #param.peek2),
633        Peek::Peek3 => quote!(#not #param.peek3),
634    };
635    match item {
636        UnnamedItem::Predicated(pred, item) => {
637            // +/+ => +
638            // +/- & -/+ => -
639            // -/- => +
640            la(parity == pred.positive(), peek, item, param)
641        }
642        UnnamedItem::Repeated(..) => unreachable!("predicate cannot contain repetition"),
643        UnnamedItem::Symbol(symbol) => {
644            let ty = make_symbol_type(symbol);
645            quote!(#f(#ty))
646        }
647        UnnamedItem::Block(Braced { value: block, .. }) => match block {
648            Items::OrderedChoice(_, items) => {
649                let individual = items
650                    .iter()
651                    .map(|item| {
652                        // FIXME: These should not require names
653                        la(parity, peek, &item.item, param)
654                    })
655                    .collect_vec();
656                quote!((#(#individual)||*))
657            }
658            Items::Sequence(items) => {
659                let mut i = 0;
660                let i = &mut i;
661                let individual = items
662                    .iter()
663                    .map(|item| {
664                        // FIXME: This shouldn't panic, rather Err with a useful span
665                        let expr = la(
666                            parity,
667                            peek.succn(*i).expect("peek distance"),
668                            item.unnamed(),
669                            param,
670                        );
671                        if !matches!(item.unnamed(), UnnamedItem::Predicated(..)) {
672                            *i += 1;
673                        }
674                        expr
675                    })
676                    .collect_vec();
677                quote!((#(#individual &&)* true))
678            }
679        },
680    }
681}
682
683fn block_la(items: &[Item], param: &Ident) -> TokenStream {
684    assert!(!items.is_empty()); // `{}?` already caught as non-advancing
685    match items[0].unnamed() {
686        UnnamedItem::Block(..) | UnnamedItem::Repeated(..) => {
687            // FIXME: This shouldn't panic, rather Err with a useful span
688            // FIXME: Used for ordered choice lookahead as well, needs appropriate error
689            panic!("cannot start repetition with block or repetition")
690        }
691        UnnamedItem::Predicated(pred, item) => la(pred.positive(), Peek::Peek1, item, param),
692        UnnamedItem::Symbol(symbol) => {
693            let ty = make_symbol_type(symbol);
694            quote!(#param.peek(#ty))
695        }
696    }
697}
698
699fn detokenize(place: syn::Expr, item: &UnnamedItem, param: &Ident) -> TokenStream {
700    match item {
701        UnnamedItem::Predicated(pred, item) => {
702            let la = la(pred.positive(), Peek::Peek1, item, param);
703            quote!(assert!(#la);)
704        }
705        UnnamedItem::Symbol(_) => quote!(#place = #param.parse()?;),
706        UnnamedItem::Repeated(item, rep) => match rep {
707            Repetition::ZeroOne(_) => match &**item {
708                UnnamedItem::Predicated(..) | UnnamedItem::Repeated(..) => {
709                    unreachable!("repetition cannot contain predicate or repetition")
710                }
711                UnnamedItem::Symbol(_) => quote!(#place = #param.parse()?;),
712                UnnamedItem::Block(Braced { value: block, .. }) => {
713                    let items = match block {
714                        // FIXME: This shouldn't panic, rather Err with a useful span
715                        Items::OrderedChoice(..) => panic!("choice not allowed here"),
716                        Items::Sequence(items) => items,
717                    };
718                    let cond = block_la(items, param);
719                    let val = new_uuidv4_ident();
720                    let dt = detokenize(parse_quote!(#val), item, param);
721                    quote! {
722                        if #cond {
723                            let #val;
724                            #dt
725                            #place = Some(#val);
726                        } else {
727                            #place = None;
728                        }
729                    }
730                }
731            },
732            Repetition::ZeroPlus(_, None) | Repetition::OnePlus(_, None) => {
733                let cond = match &**item {
734                    UnnamedItem::Predicated(..) | UnnamedItem::Repeated(..) => {
735                        unreachable!("repetition cannot contain predicate or repetition")
736                    }
737                    UnnamedItem::Symbol(symbol) => match symbol {
738                        // FIXME: This doesn't allow stopping with a peekable symbol
739                        // WORKAROUND: {&syn::Ident syn::Ident}*
740                        Symbol::Path(_) => quote!(!#param.is_empty()),
741                        Symbol::Literal(_) => {
742                            let ty = make_symbol_type(symbol);
743                            quote!(#param.peek(#ty))
744                        }
745                    },
746                    UnnamedItem::Block(Braced { value: block, .. }) => {
747                        let items = match block {
748                            // FIXME: This shouldn't panic, rather Err with a useful span
749                            Items::OrderedChoice(..) => panic!("choice not allowed here"),
750                            Items::Sequence(items) => items,
751                        };
752                        // FIXME: This doesn't allow non-peekable blocks
753                        block_la(items, param)
754                    }
755                };
756                let val = new_uuidv4_ident();
757                let dt = detokenize(parse_quote!(#val), item, param);
758                let init = match rep {
759                    Repetition::ZeroPlus(..) => quote!(),
760                    Repetition::OnePlus(..) => quote!({ let #val; #dt #val }),
761                    _ => unreachable!("matched above"),
762                };
763                quote! {
764                    #place = vec![#init];
765                    while #cond {
766                        let #val;
767                        #dt
768                        #place.push(#val);
769                    }
770                }
771            }
772            Repetition::ZeroPlus(_, Some(Interspersion::Separated(..))) => quote! {
773                // FIXME: Do manual parsing to allow peeking to move on
774                #place = if #param.is_empty {
775                    ::syn::punctuated::Punctuated::new();
776                } else {
777                    #param.call(::syn::punctuated::Punctuated::parse_separated_nonempty)?;
778                }
779            },
780            Repetition::OnePlus(_, Some(Interspersion::Separated(..))) => quote! {
781                // FIXME: Do manual parsing to open possibility of non-symbol separators
782                #place = #param.call(::syn::punctuated::Punctuated::parse_separated_nonempty)?;
783            },
784            Repetition::ZeroPlus(_, Some(Interspersion::Terminated(..))) => quote! {
785                // FIXME: Do manual parsing to make this not require ending stream
786                #place = #param.call(::syn::punctuated::Punctuated::parse_terminated)?;
787            },
788            Repetition::OnePlus(_, Some(Interspersion::Terminated(..))) => {
789                // FIXME: Do manual parsing to avoid double-parsing the first item
790                let fork = new_uuidv4_ident();
791                let ty = make_type(item).unwrap();
792                quote! {
793                    let #fork = #param.fork();
794                    let _: #ty = #fork.parse()?;
795                    #place = #param.call(::syn::punctuated::Punctuated::parse_terminated)?;
796                }
797            }
798        },
799        UnnamedItem::Block(Braced { value: block, .. }) => {
800            let items = match block {
801                // FIXME: This shouldn't panic, rather Err with a useful span
802                Items::OrderedChoice(..) => panic!("choice not allowed in non-root"),
803                Items::Sequence(items) => items,
804            };
805            let mut make = TokenStream::new();
806            let mut ret = vec![];
807            for item in items {
808                let i = new_uuidv4_ident();
809                let p = detokenize(parse_quote!(#i), item.unnamed(), param);
810                if matches!(item.unnamed(), UnnamedItem::Predicated(..)) {
811                    make.extend(p);
812                } else {
813                    make.extend(quote!(let #i; #p));
814                    ret.push(i);
815                }
816            }
817            quote!(#place = { #make (#(#ret),*) };)
818        }
819    }
820}
821
822fn make_fields(items: &[Item], vis: syn::Visibility) -> syn::Fields {
823    if items.iter().any(|item| matches!(item, Item::Named(_))) {
824        syn::Fields::Named(make_named_fields(items.iter(), vis))
825    } else {
826        syn::Fields::Unnamed(make_unnamed_fields(
827            items.iter().map(|item| match item {
828                Item::Unnamed(item) => item,
829                Item::Named(_) => unreachable!(),
830            }),
831            vis,
832        ))
833    }
834}
835
836fn make_named_fields<'a>(
837    items: impl Iterator<Item = &'a Item>,
838    vis: syn::Visibility,
839) -> syn::FieldsNamed {
840    syn::FieldsNamed {
841        brace_token: Default::default(),
842        named: items
843            .map(|item| match item {
844                Item::Named(item) => Some(syn::Field {
845                    attrs: vec![],
846                    vis: vis.clone(),
847                    ident: Some(item.name.clone()),
848                    colon_token: Some(Default::default()),
849                    ty: make_type(&item.item)?,
850                }),
851                Item::Unnamed(item) => Some(syn::Field {
852                    attrs: vec![],
853                    vis: syn::Visibility::Inherited,
854                    ident: Some(new_uuidv4_ident()),
855                    colon_token: Some(Default::default()),
856                    ty: make_type(item)?,
857                }),
858            })
859            .flatten()
860            .collect(),
861    }
862}
863
864fn make_unnamed_fields<'a>(
865    items: impl Iterator<Item = &'a UnnamedItem>,
866    vis: syn::Visibility,
867) -> syn::FieldsUnnamed {
868    syn::FieldsUnnamed {
869        paren_token: Default::default(),
870        unnamed: items
871            .map(|item| {
872                Some(syn::Field {
873                    attrs: vec![],
874                    vis: vis.clone(),
875                    ident: None,
876                    colon_token: None,
877                    ty: make_type(item)?,
878                })
879            })
880            .flatten()
881            .collect(),
882    }
883}
884
885fn make_symbol_type(symbol: &Symbol) -> syn::Type {
886    match symbol {
887        Symbol::Path(path) => syn::Type::Path(syn::TypePath {
888            qself: None,
889            path: (*path).clone(),
890        }),
891        Symbol::Literal(lit) => {
892            // FIXME: This shouldn't panic, rather Err with a useful span
893            let tok: TokenStream = lit
894                .parse()
895                .unwrap_or_else(|_| panic!("Failed to parse token literal"));
896            parse_quote!(Token![#tok])
897        }
898    }
899}
900
901fn make_type(item: &UnnamedItem) -> Option<syn::Type> {
902    match item {
903        UnnamedItem::Predicated(_, _) => None,
904        UnnamedItem::Repeated(item, repetition) => Some({
905            // FIXME: This shouldn't panic, rather Err with a useful span
906            let inner_ty = make_type(item).unwrap_or_else(|| panic!("Non-advancing repetition"));
907            match repetition {
908                Repetition::ZeroOne(_) => parse_quote!(Option<#inner_ty>),
909                Repetition::ZeroPlus(_, None) | Repetition::OnePlus(_, None) => {
910                    parse_quote!(Vec<#inner_ty>)
911                }
912                Repetition::ZeroPlus(_, Some(interspersion))
913                | Repetition::OnePlus(_, Some(interspersion)) => match interspersion {
914                    Interspersion::Terminated(_, symbol) | Interspersion::Separated(_, symbol) => {
915                        let symbol_ty = make_symbol_type(symbol);
916                        parse_quote!(::syn::punctuated::Punctuated<#inner_ty, #symbol_ty>)
917                    }
918                },
919            }
920        }),
921        UnnamedItem::Block(Braced { value: items, .. }) => Some({
922            match items {
923                Items::OrderedChoice(..) => {
924                    // FIXME: This shouldn't panic, rather Err with a useful span
925                    panic!("Cannot have ordered choice blocks not at root level")
926                }
927                Items::Sequence(items) => {
928                    syn::Type::Tuple(syn::TypeTuple {
929                        paren_token: Default::default(),
930                        elems: items
931                            .iter()
932                            .map(|item| match item {
933                                Item::Named(_) => {
934                                    // FIXME: This shouldn't panic, rather Err with a useful span
935                                    panic!("Cannot have named blocks not at root level")
936                                }
937                                Item::Unnamed(item) => make_type(item),
938                            })
939                            .flatten()
940                            .collect(),
941                    })
942                }
943            }
944        }),
945        UnnamedItem::Symbol(symbol) => Some(make_symbol_type(symbol)),
946    }
947}