Skip to main content

tygr_derive/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::{Span, TokenStream as TokenStream2};
3use quote::quote;
4use syn::{parse_quote, Data, DataEnum, DataStruct, DeriveInput, Fields, Generics, Ident};
5
6/// Derive `Grammar` (and `GrammarRule`) for a `struct` or `enum`.
7///
8/// `struct`s become a concatenation of their fields; `enum`s become an
9/// alternation of their variants' own fields — see `tygr`'s crate-level
10/// `Design` section for the full mapping from Rust constructs to EBNF.
11///
12/// `#[grammar(...)]` on the derived type is optional:
13/// - `name = "..."` — override the BNF rule name (defaults to the type name).
14/// - `hidden` — omit this type from BNF output; parsing/printing unaffected.
15/// - `inline` — splice this type's own definition wherever it's
16///   referenced, instead of a rule reference.
17/// - `validated` — after a successful parse, run `Validate::validate` on the
18///   value; a rejection backtracks as if the grammar hadn't matched.
19#[proc_macro_derive(Grammar, attributes(grammar))]
20pub fn derive_grammar(input: TokenStream) -> TokenStream {
21    let mut input = syn::parse_macro_input!(input as DeriveInput);
22    for param in input.generics.type_params_mut() {
23        param.bounds.push(parse_quote!(::tygr::Grammar));
24    }
25    match impl_grammar(&input) {
26        Ok(tokens) => tokens.into(),
27        Err(err) => err.to_compile_error().into(),
28    }
29}
30
31/// `Grammar` via `GrammarFrom` + [`FromStr`](std::str::FromStr) on the matched text.
32///
33/// Parses `GrammarFrom::Source`, then calls `Self::from_str` on the exact
34/// substring it matched. `Err` backtracks the parse as if nothing had
35/// matched, and is traced under the `trace`/`trace_pos` features.
36#[proc_macro_derive(GrammarFromStr)]
37pub fn derive_grammar_from_str(input: TokenStream) -> TokenStream {
38    derive_convert(input, Convert::FromStr)
39}
40
41/// `Grammar` via `GrammarFrom` + [`From<Source>`](From).
42///
43/// Parses `GrammarFrom::Source`, then builds `Self` with `From::from` — this
44/// conversion can't fail, so nothing is traced.
45#[proc_macro_derive(GrammarFromOther)]
46pub fn derive_grammar_from_source(input: TokenStream) -> TokenStream {
47    derive_convert(input, Convert::From)
48}
49
50/// `Grammar` via `GrammarFrom` + [`TryFrom<Source>`](TryFrom).
51///
52/// Parses `GrammarFrom::Source`, then calls `Self::try_from`. `Err`
53/// backtracks the parse as if nothing had matched, and is traced under the
54/// `trace`/`trace_pos` features.
55#[proc_macro_derive(GrammarTryFromOther)]
56pub fn derive_grammar_try_from_source(input: TokenStream) -> TokenStream {
57    derive_convert(input, Convert::TryFrom)
58}
59
60#[derive(Clone, Copy)]
61enum Convert {
62    FromStr,
63    From,
64    TryFrom,
65}
66
67fn derive_convert(input: TokenStream, convert: Convert) -> TokenStream {
68    let mut input = syn::parse_macro_input!(input as DeriveInput);
69    for param in input.generics.type_params_mut() {
70        param.bounds.push(parse_quote!(::tygr::Grammar));
71    }
72    match impl_convert(&input, convert) {
73        Ok(tokens) => tokens.into(),
74        Err(err) => err.to_compile_error().into(),
75    }
76}
77
78/// Expands a string literal into a type-level literal token.
79///
80/// This is the *primary way* to create literal tokens — the underlying
81/// `StringEq<CharThen<CH, T>>` type is an implementation detail and should not
82/// be named directly.
83///
84/// - Single character: `StringEq!(",")` → `StringEq<CharThen<',', ()>>`
85/// - Multiple characters: `StringEq!("->")` → `StringEq<CharThen<'-', CharThen<'>', ()>>>`
86#[proc_macro]
87#[allow(non_snake_case)]
88pub fn StringEq(input: TokenStream) -> TokenStream {
89    let lit = syn::parse_macro_input!(input as syn::LitStr);
90    let value = lit.value();
91    let chars: Vec<char> = value.chars().collect();
92
93    if chars.is_empty() {
94        return syn::Error::new(
95            lit.span(),
96            "StringEq!() requires a non-empty string literal",
97        )
98        .to_compile_error()
99        .into();
100    }
101
102    let chain = build_nested_chain(&chars, |ch, rest| quote! { ::tygr::CharThen<#ch, #rest> });
103    quote! { ::tygr::StringEq<#chain> }.into()
104}
105
106/// Build a right-nested chain of literal-token types terminated by `()`.
107fn build_nested_chain<F>(items: &[char], mapper: F) -> TokenStream2
108where
109    F: Fn(&char, TokenStream2) -> TokenStream2,
110{
111    items
112        .iter()
113        .rev()
114        .fold(quote! { () }, |rest, ch| mapper(ch, rest))
115}
116
117#[proc_macro]
118#[allow(non_snake_case)]
119pub fn StringEqCI(input: TokenStream) -> TokenStream {
120    let lit = syn::parse_macro_input!(input as syn::LitStr);
121    let value = lit.value();
122    let chars: Vec<char> = value.chars().collect();
123
124    if chars.is_empty() {
125        return syn::Error::new(
126            lit.span(),
127            "StringEqCI!() requires a non-empty string literal",
128        )
129        .to_compile_error()
130        .into();
131    }
132
133    let chain = build_nested_chain(&chars, |ch, rest| quote! { ::tygr::CharCIThen<#ch, #rest> });
134    quote! { ::tygr::StringEqCI<#chain> }.into()
135}
136
137#[derive(Clone)]
138struct Tag {
139    name: TokenStream2,
140    case: Option<TokenStream2>,
141}
142
143impl Tag {
144    fn new(name: TokenStream2, case: Option<TokenStream2>) -> Self {
145        Self { name, case }
146    }
147
148    fn as_constructor(&self) -> TokenStream2 {
149        let name = &self.name;
150        let case = if let Some(case) = &self.case {
151            quote! { :: #case }
152        } else {
153            quote! {}
154        };
155        quote! { #name #case }
156    }
157}
158
159fn with_node(inline: bool, body: TokenStream2) -> TokenStream2 {
160    if inline {
161        body
162    } else {
163        quote! {
164            let mut state = state.node(Self::NAME, pos);
165            #body
166        }
167    }
168}
169
170fn impl_grammar(input: &DeriveInput) -> syn::Result<TokenStream2> {
171    let ident = &input.ident;
172    let generics = &input.generics;
173    let GrammarAttr {
174        name,
175        hidden,
176        inline,
177        validated,
178    } = grammar_attr(input)?;
179    let name = name.unwrap_or_else(|| {
180        let ident = ident.to_string();
181        #[cfg(all(feature = "lower_bnf_name", not(feature = "upper_bnf_name")))]
182        let ident = ident.to_ascii_lowercase();
183        #[cfg(all(feature = "upper_bnf_name", not(feature = "lower_bnf_name")))]
184        let ident = ident.to_ascii_uppercase();
185        ident
186    });
187    match &input.data {
188        Data::Struct(data) => impl_struct(ident, generics, name, hidden, inline, validated, data),
189        Data::Enum(data) => impl_enum(ident, generics, name, hidden, inline, validated, data),
190        Data::Union(_) => Err(syn::Error::new_spanned(
191            ident,
192            "Grammar cannot be derived for unions",
193        )),
194    }
195}
196
197fn grammar_attr(input: &DeriveInput) -> syn::Result<GrammarAttr> {
198    for attr in &input.attrs {
199        if attr.path().is_ident("grammar") {
200            return attr.parse_args::<GrammarAttr>();
201        }
202    }
203    Ok(GrammarAttr::default())
204}
205
206#[derive(Default)]
207struct GrammarAttr {
208    name: Option<String>,
209    hidden: bool,
210    inline: bool,
211    validated: bool,
212}
213
214impl syn::parse::Parse for GrammarAttr {
215    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
216        let mut attr = GrammarAttr::default();
217
218        while !input.is_empty() {
219            let ident: syn::Ident = input.parse()?;
220            if ident == "name" {
221                let _: syn::Token![=] = input.parse()?;
222                let lit: syn::LitStr = input.parse()?;
223                attr.name = Some(lit.value());
224            } else if ident == "hidden" {
225                attr.hidden = true;
226                // Hidden nodes are not presented in BNF so they must be inline
227                // as non-inline node names are presented in traces
228                attr.inline = true;
229            } else if ident == "inline" {
230                attr.inline = true;
231            } else if ident == "validated" {
232                attr.validated = true;
233            } else {
234                return Err(syn::Error::new(
235                    ident.span(),
236                    "expected `name`, `hidden`, `inline`, or `validated`",
237                ));
238            }
239            if input.is_empty() {
240                break;
241            }
242            let _: syn::Token![,] = input.parse()?;
243        }
244
245        Ok(attr)
246    }
247}
248
249type ProcessedFields<'a> = Vec<(String, Ident, &'a syn::Type)>;
250
251fn components_and_xts(tag: TokenStream2, fields: &Fields) -> (TokenStream2, ProcessedFields<'_>) {
252    match fields {
253        Fields::Named(fields) => {
254            let fields: Vec<_> = fields
255                .named
256                .iter()
257                .map(|field| {
258                    let x = field.ident.as_ref().unwrap().clone();
259                    let t = &field.ty;
260                    (x.to_string(), x, t)
261                })
262                .collect();
263            let xs = fields.iter().map(|(_, x, _)| x);
264            (quote! { #tag { #(#xs),* } }, fields)
265        }
266        Fields::Unnamed(fields) => {
267            let fields: Vec<_> = fields
268                .unnamed
269                .iter()
270                .enumerate()
271                .map(|(i, field)| {
272                    let x = Ident::new(&format!("_f{i}"), Span::call_site());
273                    let t = &field.ty;
274                    (i.to_string(), x, t)
275                })
276                .collect();
277            let xs = fields.iter().map(|(_, x, _)| x);
278            (quote! { #tag ( #(#xs),* ) }, fields)
279        }
280        Fields::Unit => (quote! { #tag }, vec![]),
281    }
282}
283
284fn parse_at(validated: bool, fields: &ProcessedFields, constructor: &TokenStream2) -> TokenStream2 {
285    let steps: Vec<_> = fields
286        .iter()
287        .map(|(_, x, t)| {
288            quote! {
289                let (#x, pos) = <#t as ::tygr::Grammar>::parse_at(input, pos, state.reborrow())?;
290            }
291        })
292        .collect();
293    let (start_pos, validate) = if validated {
294        let trace = if cfg!(feature = "trace") {
295            quote! {
296                state.expect(pos, ::tygr::Expectation::Valid { pos: start_pos, be_valid });
297            }
298        } else if cfg!(feature = "trace_pos") {
299            quote! {
300                state.expect(pos);
301            }
302        } else {
303            quote! {}
304        };
305        (
306            quote! { let start_pos = pos; },
307            quote! {
308                if let Some(be_valid) = ::tygr::Validation::be_valid(::tygr::Validate::validate(&value)) {
309                    #trace
310                    return None
311                }
312            },
313        )
314    } else {
315        (quote! {}, quote! {})
316    };
317    quote! {
318        #start_pos
319        #(#steps)*
320        let value = #constructor;
321        #validate
322        Some((value, pos))
323    }
324}
325
326fn scan_at(validated: bool, fields: &ProcessedFields, constructor: &TokenStream2) -> TokenStream2 {
327    if validated {
328        let parse_at = parse_at(true, fields, constructor);
329        quote! { ({#parse_at}).map(|(_, pos)| pos) }
330    } else {
331        let steps: Vec<_> = fields
332            .iter()
333            .map(|(_, _, t)| {
334                quote! {
335                    let pos = <#t as ::tygr::Grammar>::scan_at(input, pos, state.reborrow())?;
336                }
337            })
338            .collect();
339        quote! {
340            #(#steps)*
341            Some(pos)
342        }
343    }
344}
345
346fn print_steps(fields: &ProcessedFields) -> Vec<TokenStream2> {
347    fields
348        .iter()
349        .map(|(_, x, _)| {
350            quote! {
351                ::tygr::Grammar::print_to(#x, buf);
352            }
353        })
354        .collect()
355}
356
357fn to_bnf(fields: &ProcessedFields) -> TokenStream2 {
358    let ts = fields.iter().map(|(_, _, t)| quote! { #t });
359    quote! {
360        ::tygr::bnf::Expr::sequence(vec![
361            #(<#ts as ::tygr::Grammar>::to_bnf()),*
362        ])
363    }
364}
365
366/// `A::fail_at(..) || B::fail_at(..) || .. || false`: each field reports itself
367/// unconditionally; a required (non-nullable) field short-circuits the rest,
368/// since real parsing would never reach them either.
369fn fail_at(fields: &ProcessedFields) -> TokenStream2 {
370    let calls = fields.iter().map(|(_, _, t)| {
371        quote! { <#t as ::tygr::Grammar>::fail_at(pos, state.reborrow()) }
372    });
373    quote! {
374        #(#calls ||)* false
375    }
376}
377
378fn bnf_ref(grammar_name: &str, hidden: bool, to_bnf: TokenStream2, inline: bool) -> TokenStream2 {
379    if hidden {
380        quote! { ::tygr::bnf::Expr::empty() }
381    } else if inline {
382        to_bnf
383    } else {
384        quote! { ::tygr::bnf::Expr::RuleRef(#grammar_name.to_string()) }
385    }
386}
387
388struct FieldsInfo {
389    constructor: TokenStream2,
390    parse_at: TokenStream2,
391    scan_at: TokenStream2,
392    print_to: TokenStream2,
393    to_bnf: TokenStream2,
394    fail_at: TokenStream2,
395    first: TokenStream2,
396}
397
398impl FieldsInfo {
399    fn from(validated: bool, tag: Tag, fields: &Fields) -> Self {
400        let (constructor, fields) = components_and_xts(tag.as_constructor(), fields);
401        let print_steps = print_steps(&fields);
402        let parse_at = parse_at(validated, &fields, &constructor);
403        let scan_at = scan_at(validated, &fields, &constructor);
404        let mut first = quote! { ::tygr::OptionalFirst<::tygr::EmptyByteSet> };
405        for (_, _, t) in &fields {
406            first = quote! {
407                <#first as ::tygr::First>::Concat<#t>
408            }
409        }
410        Self {
411            parse_at: quote! { #parse_at },
412            scan_at: quote! { #scan_at },
413            print_to: quote! {#(#print_steps)*},
414            to_bnf: to_bnf(&fields),
415            fail_at: fail_at(&fields),
416            constructor,
417            first,
418        }
419    }
420}
421
422fn impl_convert(input: &DeriveInput, convert: Convert) -> syn::Result<TokenStream2> {
423    let ident = &input.ident;
424    let generics = &input.generics;
425    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
426    let self_ty = quote! { #ident #ty_generics };
427    let source = quote! { <#self_ty as ::tygr::GrammarFrom>::Source };
428    // parse_at: parse Source, then build Self via the forward conversion.
429    let trace = if cfg!(feature = "trace") {
430        quote! {
431            state.expect(
432                end,
433                ::tygr::Expectation::GrammarFrom(::std::string::ToString::to_string(&err)),
434            );
435        }
436    } else if cfg!(feature = "trace_pos") {
437        quote! {
438            state.expect(end);
439        }
440    } else {
441        quote! {}
442    };
443    let parse_at = match convert {
444        Convert::FromStr => quote! {
445            let end = <#source as ::tygr::Grammar>::scan_at(input, pos, state.reborrow())?;
446            match <#self_ty as ::core::str::FromStr>::from_str(&input[pos..end]) {
447                Ok(value) => Some((value, end)),
448                Err(err) => {
449                    #trace
450                    let _ = &err;
451                    None
452                }
453            }
454        },
455        Convert::From => quote! {
456            let (source, end) = <#source as ::tygr::Grammar>::parse_at(input, pos, state.reborrow())?;
457            Some((<#self_ty as ::core::convert::From<#source>>::from(source), end))
458        },
459        Convert::TryFrom => quote! {
460            let (source, end) = <#source as ::tygr::Grammar>::parse_at(input, pos, state.reborrow())?;
461            match <#self_ty as ::core::convert::TryFrom<#source>>::try_from(source) {
462                Ok(value) => Some((value, end)),
463                Err(err) => {
464                    #trace
465                    let _ = &err;
466                    None
467                }
468            }
469        },
470    };
471    // scan_at: `From` can't fail beyond Source; `FromStr`/`TryFrom` run the check.
472    let scan_at = match convert {
473        Convert::From => quote! {
474            <#source as ::tygr::Grammar>::scan_at(input, pos, state)
475        },
476        Convert::FromStr | Convert::TryFrom => quote! {
477            Self::parse_at(input, pos, state).map(|(_, end)| end)
478        },
479    };
480    Ok(quote! {
481        impl #impl_generics ::tygr::Grammar for #self_ty #where_clause {
482            type First = <#source as ::tygr::Grammar>::First;
483
484            #[inline]
485            fn parse_at(input: &str, pos: usize, #[allow(unused_mut)] mut state: ::tygr::State) -> Option<(Self, usize)> {
486                #parse_at
487            }
488
489            #[inline]
490            fn scan_at(input: &str, pos: usize, #[allow(unused_mut)] mut state: ::tygr::State) -> Option<usize> {
491                #scan_at
492            }
493
494            fn print_to(&self, buf: &mut ::std::string::String) {
495                <#self_ty as ::tygr::GrammarFrom>::print_to(self, buf);
496            }
497
498            fn to_bnf() -> ::tygr::bnf::Expr {
499                <#source as ::tygr::Grammar>::to_bnf()
500            }
501
502            fn fail_at(pos: usize, state: ::tygr::State) -> bool {
503                <#source as ::tygr::Grammar>::fail_at(pos, state)
504            }
505        }
506    })
507}
508
509fn impl_struct(
510    ident: &Ident,
511    generics: &Generics,
512    name: String,
513    hidden: bool,
514    inline: bool,
515    validated: bool,
516    data: &DataStruct,
517) -> syn::Result<TokenStream2> {
518    let tag = Tag::new(quote! { #ident }, None);
519    let fields = &data.fields;
520    let FieldsInfo {
521        constructor,
522        parse_at,
523        scan_at,
524        print_to,
525        to_bnf,
526        fail_at,
527        first,
528    } = FieldsInfo::from(validated, tag, fields);
529    let parse_at = with_node(inline, parse_at);
530    let scan_at = with_node(inline, scan_at);
531    let fail_at = with_node(inline, fail_at);
532    let bnf_ref = bnf_ref(&name, hidden, to_bnf.clone(), inline);
533    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
534    Ok(quote! {
535        impl #impl_generics ::tygr::Grammar for #ident #ty_generics #where_clause {
536            type First = #first;
537
538            #[inline]
539            fn parse_at(input: &str, pos: usize, #[allow(unused_mut)] mut state: ::tygr::State) -> Option<(Self, usize)> {
540                #parse_at
541            }
542
543            #[inline]
544            fn scan_at(input: &str, pos: usize, #[allow(unused_mut)] mut state: ::tygr::State) -> Option<usize> {
545                #scan_at
546            }
547
548            fn print_to(&self, buf: &mut ::std::string::String) {
549                let #constructor = &self;
550                #print_to
551            }
552
553            fn to_bnf() -> ::tygr::bnf::Expr {
554                #bnf_ref
555            }
556
557            fn fail_at(#[allow(unused_variables)] pos: usize, #[allow(unused_variables, unused_mut)] mut state: ::tygr::State) -> bool {
558                #fail_at
559            }
560        }
561
562        impl #impl_generics ::tygr::GrammarRule for #ident #ty_generics #where_clause {
563            const NAME: &'static str = #name;
564
565            fn to_bnf_def() -> ::tygr::bnf::Expr {
566                #to_bnf
567            }
568        }
569    })
570}
571
572// ── Enum → alternation ──────────────────────────────────────────────────────
573
574fn impl_enum(
575    ident: &Ident,
576    generics: &Generics,
577    name: String,
578    hidden: bool,
579    inline: bool,
580    validated: bool,
581    data: &DataEnum,
582) -> syn::Result<TokenStream2> {
583    let mut each_constructor = vec![];
584    let mut each_parse_at = vec![];
585    let mut each_scan_at = vec![];
586    let mut each_print_to = vec![];
587    let mut each_to_bnf = vec![];
588    let mut each_fail_at = vec![];
589    let mut each_first = vec![];
590    for variant in &data.variants {
591        let variant_ident = &variant.ident;
592        let tag = Tag::new(quote! { #ident }, Some(quote! {#variant_ident}));
593        let FieldsInfo {
594            constructor,
595            parse_at,
596            scan_at,
597            print_to,
598            to_bnf,
599            fail_at,
600            first,
601        } = FieldsInfo::from(validated, tag, &variant.fields);
602        each_constructor.push(constructor);
603        each_parse_at.push(parse_at);
604        each_scan_at.push(scan_at);
605        each_print_to.push(print_to);
606        each_to_bnf.push(to_bnf);
607        each_fail_at.push(fail_at);
608        each_first.push(first);
609    }
610    // Alternatives aren't sequential: every variant reports itself unconditionally
611    // (no short-circuiting between them). The enum as a whole is required only if
612    // every variant is (`&`, not `&&`, so all still get called for their side effects).
613    let fail_at_variants_body = {
614        let mut variants = each_fail_at.iter();
615        let first = variants.next().cloned().unwrap_or_else(|| quote! { false });
616        variants.fold(first, |acc, next| quote! { (#acc) & (#next) })
617    };
618    let to_bnf = quote! { ::tygr::bnf::Expr::alternation(vec![ #(#each_to_bnf),* ]) };
619    let bnf_ref = bnf_ref(&name, hidden, to_bnf.clone(), inline);
620    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
621    let n = each_first.len();
622    let self_ty = quote! { #ident #ty_generics };
623    let parse_fn_ty = quote! {
624        fn(&str, usize, ::tygr::State<'_>) -> Option<(#self_ty, usize)>
625    };
626    let scan_fn_ty = quote! {
627        fn(&str, usize, ::tygr::State<'_>) -> Option<usize>
628    };
629    let case_fns: Vec<Ident> = (0..n)
630        .map(|k| Ident::new(&format!("parse_case_{k}"), Span::call_site()))
631        .collect();
632    let scan_fns: Vec<Ident> = (0..n)
633        .map(|k| Ident::new(&format!("scan_case_{k}"), Span::call_site()))
634        .collect();
635    let case_defs = case_fns.iter().zip(&each_parse_at).enumerate().map(|(k, (name, arm))| {
636        let next = if k + 1 < n {
637            let nx = &case_fns[k + 1];
638            quote! { Self::#nx(input, pos, state) }
639        } else {
640            quote! { None }
641        };
642        quote! {
643            #[inline]
644            fn #name(input: &str, pos: usize, #[allow(unused_variables, unused_mut)] mut state: ::tygr::State) -> Option<(Self, usize)> {
645                if let Some(result) = (|| { #arm })() { return Some(result); }
646                #next
647            }
648        }
649    });
650    let scan_case_defs = scan_fns
651        .iter()
652        .zip(&each_scan_at)
653        .enumerate()
654        .map(|(k, (name, arm))| {
655            let next = if k + 1 < n {
656                let nx = &scan_fns[k + 1];
657                quote! { Self::#nx(input, pos, state) }
658            } else {
659                quote! { None }
660            };
661            quote! {
662                #[inline]
663                fn #name(input: &str, pos: usize, #[allow(unused_variables, unused_mut)] mut state: ::tygr::State) -> Option<usize> {
664                    if let Some(result) = (|| { #arm })() { return Some(result); }
665                    #next
666                }
667            }
668        });
669    // slots 0-255: u8 / byte / ascii char
670    // slots 256  : EOF
671    let build_table = |fn_ty: &TokenStream2, fns: &[Ident], miss: &TokenStream2| {
672        quote! {{
673            let parsers: [#fn_ty; #n] = [ #(<#self_ty>::#fns),* ];
674            // No case's FIRST set contains this byte, so none can match: dispatch
675            // straight to `miss`, which reports every variant's expectation in
676            // O(1) without attempting any of their (guaranteed-failing) real parses.
677            let mut table: [#fn_ty; 257] = [#miss; 257];
678            let mut first = 0usize;
679            while first < 257 {
680                let mut case = 0usize;
681                while case < #n {
682                    if CONTAINS_NIL[case] || (first <= 255 && CONTAINS_BYTE[case][first]) {
683                        table[first] = parsers[case];
684                        break;
685                    }
686                    case += 1;
687                }
688                first += 1;
689            }
690            table
691        }}
692    };
693    let parse_table = build_table(
694        &parse_fn_ty,
695        &case_fns,
696        &quote! { <#self_ty>::parse_case_miss },
697    );
698    let scan_table = build_table(
699        &scan_fn_ty,
700        &scan_fns,
701        &quote! { <#self_ty>::scan_case_miss },
702    );
703    const DISPATCH_THRESHOLD: usize = 4;
704    // `cfg!`, not `#[cfg(...)]`: this call is spliced into whatever crate
705    // derives `Grammar`, so an embedded `#[cfg(feature = "trace_pos")]`
706    // would check that crate's own (nonexistent) feature, not tygr's.
707    let fail_at_on_miss = if cfg!(feature = "trace_pos") {
708        quote! { Self::fail_at_variants(pos, state); }
709    } else {
710        quote! {}
711    };
712    let (dispatch_body, scan_dispatch_body, case_impls) = if n >= DISPATCH_THRESHOLD {
713        (
714            quote! {
715                const CONTAINS_NIL: [bool; #n] = [ #(<#each_first as ::tygr::First>::CONTAINS_NIL),* ];
716                const CONTAINS_BYTE: [[bool; 256]; #n] = [ #(<#each_first as ::tygr::First>::CONTAINS_BYTE),* ];
717                const DISPATCH: [#parse_fn_ty; 257] = #parse_table;
718                let first = input.as_bytes().get(pos).map(|&first| first as usize).unwrap_or(256);
719                DISPATCH[first](input, pos, state)
720            },
721            quote! {
722                const CONTAINS_NIL: [bool; #n] = [ #(<#each_first as ::tygr::First>::CONTAINS_NIL),* ];
723                const CONTAINS_BYTE: [[bool; 256]; #n] = [ #(<#each_first as ::tygr::First>::CONTAINS_BYTE),* ];
724                const DISPATCH: [#scan_fn_ty; 257] = #scan_table;
725                let first = input.as_bytes().get(pos).map(|&first| first as usize).unwrap_or(256);
726                DISPATCH[first](input, pos, state)
727            },
728            quote! {
729                #(#case_defs)*
730                #(#scan_case_defs)*
731
732                #[inline]
733                fn parse_case_miss(_input: &str, #[allow(unused_variables)] pos: usize, #[allow(unused_variables)] state: ::tygr::State) -> Option<(Self, usize)> {
734                    #fail_at_on_miss
735                    None
736                }
737
738                #[inline]
739                fn scan_case_miss(_input: &str, #[allow(unused_variables)] pos: usize, #[allow(unused_variables)] state: ::tygr::State) -> Option<usize> {
740                    #fail_at_on_miss
741                    None
742                }
743            },
744        )
745    } else {
746        (
747            quote! {
748                #( if let Some(result) = (|| { #each_parse_at })() { return Some(result); } )*
749                None
750            },
751            quote! {
752                #( if let Some(result) = (|| { #each_scan_at })() { return Some(result); } )*
753                None
754            },
755            quote! {},
756        )
757    };
758    let parse_body = with_node(inline, dispatch_body);
759    let scan_body = with_node(inline, scan_dispatch_body);
760    let fail_at = with_node(inline, quote! { Self::fail_at_variants(pos, state) });
761    let first = {
762        let mut the_first = quote! { ::tygr::EmptyByteSet };
763        for first in each_first {
764            the_first = quote! { <#the_first as ::tygr::First>::Union<#first> };
765        }
766        the_first
767    };
768    Ok(quote! {
769        impl #impl_generics #ident #ty_generics #where_clause {
770            #case_impls
771
772            #[inline]
773            fn parse_case_none(_input: &str, _pos: usize, _state: ::tygr::State) -> Option<(Self, usize)> {
774                None
775            }
776
777            #[inline]
778            fn fail_at_variants(#[allow(unused_variables)] pos: usize, #[allow(unused_variables, unused_mut)] mut state: ::tygr::State) -> bool {
779                #fail_at_variants_body
780            }
781        }
782
783        impl #impl_generics ::tygr::Grammar for #ident #ty_generics #where_clause {
784            type First = #first;
785
786            #[inline]
787            fn parse_at(input: &str, pos: usize, #[allow(unused_variables, unused_mut)] mut state: ::tygr::State) -> Option<(Self, usize)> {
788                #parse_body
789            }
790
791            #[inline]
792            fn scan_at(input: &str, pos: usize, #[allow(unused_variables, unused_mut)] mut state: ::tygr::State) -> Option<usize> {
793                #scan_body
794            }
795
796            fn print_to(&self, buf: &mut ::std::string::String) {
797                match self {
798                    #(#each_constructor => { #each_print_to }),*
799                }
800            }
801
802            fn to_bnf() -> ::tygr::bnf::Expr {
803                #bnf_ref
804            }
805
806            fn fail_at(pos: usize, #[allow(unused_variables, unused_mut)] mut state: ::tygr::State) -> bool {
807                #fail_at
808            }
809        }
810
811        impl #impl_generics ::tygr::GrammarRule for #ident #ty_generics #where_clause {
812            const NAME: &'static str = #name;
813
814            fn to_bnf_def() -> ::tygr::bnf::Expr {
815                #to_bnf
816            }
817        }
818    })
819}