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#[proc_macro_derive(Grammar, attributes(grammar))]
7pub fn derive_grammar(input: TokenStream) -> TokenStream {
8 let mut input = syn::parse_macro_input!(input as DeriveInput);
9 for param in input.generics.type_params_mut() {
10 param.bounds.push(parse_quote!(::tygr::Grammar));
11 }
12 match impl_grammar(&input) {
13 Ok(tokens) => tokens.into(),
14 Err(err) => err.to_compile_error().into(),
15 }
16}
17
18#[proc_macro_derive(GrammarFromStr)]
20pub fn derive_grammar_from_str(input: TokenStream) -> TokenStream {
21 derive_convert(input, Convert::FromStr)
22}
23
24#[proc_macro_derive(GrammarFromOther)]
26pub fn derive_grammar_from_source(input: TokenStream) -> TokenStream {
27 derive_convert(input, Convert::From)
28}
29
30#[proc_macro_derive(GrammarTryFromOther)]
32pub fn derive_grammar_try_from_source(input: TokenStream) -> TokenStream {
33 derive_convert(input, Convert::TryFrom)
34}
35
36#[derive(Clone, Copy)]
37enum Convert {
38 FromStr,
39 From,
40 TryFrom,
41}
42
43fn derive_convert(input: TokenStream, convert: Convert) -> TokenStream {
44 let mut input = syn::parse_macro_input!(input as DeriveInput);
45 for param in input.generics.type_params_mut() {
46 param.bounds.push(parse_quote!(::tygr::Grammar));
47 }
48 match impl_convert(&input, convert) {
49 Ok(tokens) => tokens.into(),
50 Err(err) => err.to_compile_error().into(),
51 }
52}
53
54#[proc_macro]
63#[allow(non_snake_case)]
64pub fn StringEq(input: TokenStream) -> TokenStream {
65 let lit = syn::parse_macro_input!(input as syn::LitStr);
66 let value = lit.value();
67 let chars: Vec<char> = value.chars().collect();
68
69 if chars.is_empty() {
70 return syn::Error::new(
71 lit.span(),
72 "StringEq!() requires a non-empty string literal",
73 )
74 .to_compile_error()
75 .into();
76 }
77
78 let chain = build_nested_chain(&chars, |ch, rest| quote! { ::tygr::CharThen<#ch, #rest> });
79 quote! { ::tygr::StringEq<#chain> }.into()
80}
81
82fn build_nested_chain<F>(items: &[char], mapper: F) -> TokenStream2
84where
85 F: Fn(&char, TokenStream2) -> TokenStream2,
86{
87 items
88 .iter()
89 .rev()
90 .fold(quote! { () }, |rest, ch| mapper(ch, rest))
91}
92
93#[proc_macro]
94#[allow(non_snake_case)]
95pub fn StringEqCI(input: TokenStream) -> TokenStream {
96 let lit = syn::parse_macro_input!(input as syn::LitStr);
97 let value = lit.value();
98 let chars: Vec<char> = value.chars().collect();
99
100 if chars.is_empty() {
101 return syn::Error::new(
102 lit.span(),
103 "StringEqCI!() requires a non-empty string literal",
104 )
105 .to_compile_error()
106 .into();
107 }
108
109 let chain = build_nested_chain(&chars, |ch, rest| quote! { ::tygr::CharCIThen<#ch, #rest> });
110 quote! { ::tygr::StringEqCI<#chain> }.into()
111}
112
113#[derive(Clone)]
114struct Tag {
115 name: TokenStream2,
116 case: Option<TokenStream2>,
117}
118
119impl Tag {
120 fn new(name: TokenStream2, case: Option<TokenStream2>) -> Self {
121 Self { name, case }
122 }
123
124 fn as_constructor(&self) -> TokenStream2 {
125 let name = &self.name;
126 let case = if let Some(case) = &self.case {
127 quote! { :: #case }
128 } else {
129 quote! {}
130 };
131 quote! { #name #case }
132 }
133}
134
135fn with_node(transparent: bool, body: TokenStream2) -> TokenStream2 {
136 if transparent {
137 body
138 } else {
139 quote! {
140 let mut state = state.node(Self::NAME, pos);
141 #body
142 }
143 }
144}
145
146fn impl_grammar(input: &DeriveInput) -> syn::Result<TokenStream2> {
147 let ident = &input.ident;
148 let generics = &input.generics;
149 let GrammarAttr {
150 name,
151 hidden,
152 transparent,
153 filtered,
154 } = grammar_attr(input);
155 let name = name.unwrap_or_else(|| {
156 let ident = ident.to_string();
157 #[cfg(feature = "lower_bnf_name")]
158 let ident = ident.to_ascii_lowercase();
159 #[cfg(feature = "upper_bnf_name")]
160 let ident = ident.to_ascii_uppercase();
161 ident
162 });
163 match &input.data {
164 Data::Struct(data) => {
165 impl_struct(ident, generics, name, hidden, transparent, filtered, data)
166 }
167 Data::Enum(data) => impl_enum(ident, generics, name, hidden, transparent, filtered, data),
168 Data::Union(_) => Err(syn::Error::new_spanned(
169 ident,
170 "Grammar cannot be derived for unions",
171 )),
172 }
173}
174
175fn grammar_attr(input: &DeriveInput) -> GrammarAttr {
176 input
177 .attrs
178 .iter()
179 .filter_map(|attr| {
180 if attr.path().is_ident("grammar") {
181 attr.parse_args::<GrammarAttr>().ok()
182 } else {
183 None
184 }
185 })
186 .next()
187 .unwrap_or_default()
188}
189
190#[derive(Default)]
191struct GrammarAttr {
192 name: Option<String>,
193 hidden: bool,
194 transparent: bool,
195 filtered: bool,
196}
197
198impl syn::parse::Parse for GrammarAttr {
199 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
200 let mut attr = GrammarAttr::default();
201
202 while !input.is_empty() {
203 let ident: syn::Ident = input.parse()?;
204 if ident == "name" {
205 let _: syn::Token![=] = input.parse()?;
206 let lit: syn::LitStr = input.parse()?;
207 attr.name = Some(lit.value());
208 } else if ident == "hidden" {
209 attr.hidden = true;
210 attr.transparent = true;
213 } else if ident == "transparent" {
214 attr.transparent = true;
215 } else if ident == "filtered" {
216 attr.filtered = true;
217 } else {
218 return Err(syn::Error::new(
219 ident.span(),
220 "expected `name`, `hidden`, `transparent`, or `filtered`",
221 ));
222 }
223 if input.is_empty() {
224 break;
225 }
226 let _: syn::Token![,] = input.parse()?;
227 }
228
229 Ok(attr)
230 }
231}
232
233type ProcessedFields<'a> = Vec<(String, Ident, &'a syn::Type)>;
234
235fn components_and_xts(tag: TokenStream2, fields: &Fields) -> (TokenStream2, ProcessedFields<'_>) {
236 match fields {
237 Fields::Named(fields) => {
238 let fields: Vec<_> = fields
239 .named
240 .iter()
241 .map(|field| {
242 let x = field.ident.as_ref().unwrap().clone();
243 let t = &field.ty;
244 (x.to_string(), x, t)
245 })
246 .collect();
247 let xs = fields.iter().map(|(_, x, _)| x);
248 (quote! { #tag { #(#xs),* } }, fields)
249 }
250 Fields::Unnamed(fields) => {
251 let fields: Vec<_> = fields
252 .unnamed
253 .iter()
254 .enumerate()
255 .map(|(i, field)| {
256 let x = Ident::new(&format!("_f{i}"), Span::call_site());
257 let t = &field.ty;
258 (i.to_string(), x, t)
259 })
260 .collect();
261 let xs = fields.iter().map(|(_, x, _)| x);
262 (quote! { #tag ( #(#xs),* ) }, fields)
263 }
264 Fields::Unit => (quote! { #tag }, vec![]),
265 }
266}
267
268fn parse_at(filtered: bool, fields: &ProcessedFields, constructor: &TokenStream2) -> TokenStream2 {
269 let steps: Vec<_> = fields
270 .iter()
271 .map(|(_, x, t)| {
272 quote! {
273 let (#x, pos) = <#t as ::tygr::Grammar>::parse_at(input, pos, state.reborrow())?;
274 }
275 })
276 .collect();
277 let filter = if filtered {
278 quote! {
279 if let Some(be_valid) = ::tygr::FilterResult::be_valid(value.filter()) {
280 return None
281 }
282 }
283 } else {
284 quote! {}
285 };
286 quote! {
287 #(#steps)*
288 let value = #constructor;
289 #filter
290 Some((value, pos))
291 }
292}
293
294fn scan_at(filtered: bool, fields: &ProcessedFields, constructor: &TokenStream2) -> TokenStream2 {
295 if filtered {
296 let parse_at = parse_at(true, fields, constructor);
297 quote! { ({#parse_at}).map(|(_, pos)| pos) }
298 } else {
299 let steps: Vec<_> = fields
300 .iter()
301 .map(|(_, _, t)| {
302 quote! {
303 let pos = <#t as ::tygr::Grammar>::scan_at(input, pos, state.reborrow())?;
304 }
305 })
306 .collect();
307 quote! {
308 #(#steps)*
309 Some(pos)
310 }
311 }
312}
313
314fn print_steps(fields: &ProcessedFields) -> Vec<TokenStream2> {
315 fields
316 .iter()
317 .map(|(_, x, _)| {
318 quote! {
319 ::tygr::Grammar::print_to(#x, buf);
320 }
321 })
322 .collect()
323}
324
325fn to_bnf(fields: &ProcessedFields) -> TokenStream2 {
326 let ts = fields.iter().map(|(_, _, t)| quote! { #t });
327 quote! {
328 ::tygr::bnf::Expr::sequence(vec![
329 #(<#ts as ::tygr::Grammar>::to_bnf()),*
330 ])
331 }
332}
333
334fn bnf_ref(
335 grammar_name: &str,
336 hidden: bool,
337 to_bnf: TokenStream2,
338 transparent: bool,
339) -> TokenStream2 {
340 if hidden {
341 quote! { ::tygr::bnf::Expr::Empty }
342 } else if transparent {
343 to_bnf
344 } else {
345 quote! { ::tygr::bnf::Expr::RuleRef(#grammar_name.to_string()) }
346 }
347}
348
349struct FieldsInfo {
350 constructor: TokenStream2,
351 parse_at: TokenStream2,
352 scan_at: TokenStream2,
353 print_to: TokenStream2,
354 to_bnf: TokenStream2,
355 first: TokenStream2,
356}
357
358impl FieldsInfo {
359 fn from(filtered: bool, tag: Tag, fields: &Fields) -> Self {
360 let (constructor, fields) = components_and_xts(tag.as_constructor(), fields);
361 let print_steps = print_steps(&fields);
362 let parse_at = parse_at(filtered, &fields, &constructor);
363 let scan_at = scan_at(filtered, &fields, &constructor);
364 let mut first = quote! { ::tygr::EmptyFirst };
365 for (_, _, t) in &fields {
366 first = quote! {
367 <#first as ::tygr::First>::Concat<#t>
368 }
369 }
370 Self {
371 parse_at: quote! { #parse_at },
372 scan_at: quote! { #scan_at },
373 print_to: quote! {#(#print_steps)*},
374 to_bnf: to_bnf(&fields),
375 constructor,
376 first,
377 }
378 }
379}
380
381fn impl_convert(input: &DeriveInput, convert: Convert) -> syn::Result<TokenStream2> {
382 let ident = &input.ident;
383 let generics = &input.generics;
384 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
385 let self_ty = quote! { #ident #ty_generics };
386 let source = quote! { <#self_ty as ::tygr::GrammarFrom>::Source };
387 let trace = if cfg!(feature = "trace_attempts") {
389 quote! {
390 state.expect(
391 pos,
392 ::tygr::Expectation::Conversion(::std::string::ToString::to_string(&err)),
393 );
394 }
395 } else if cfg!(feature = "trace_pos") {
396 quote! {
397 state.expect(pos);
398 }
399 } else {
400 quote! {}
401 };
402 let parse_at = match convert {
403 Convert::FromStr => quote! {
404 let end = <#source as ::tygr::Grammar>::scan_at(input, pos, state.reborrow())?;
405 match <#self_ty as ::core::str::FromStr>::from_str(&input[pos..end]) {
406 Ok(value) => Some((value, end)),
407 Err(err) => {
408 #trace
409 let _ = &err;
410 None
411 }
412 }
413 },
414 Convert::From => quote! {
415 let (source, end) = <#source as ::tygr::Grammar>::parse_at(input, pos, state.reborrow())?;
416 Some((<#self_ty as ::core::convert::From<#source>>::from(source), end))
417 },
418 Convert::TryFrom => quote! {
419 let (source, end) = <#source as ::tygr::Grammar>::parse_at(input, pos, state.reborrow())?;
420 match <#self_ty as ::core::convert::TryFrom<#source>>::try_from(source) {
421 Ok(value) => Some((value, end)),
422 Err(err) => {
423 #trace
424 let _ = &err;
425 None
426 }
427 }
428 },
429 };
430 let scan_at = match convert {
432 Convert::From => quote! {
433 <#source as ::tygr::Grammar>::scan_at(input, pos, state)
434 },
435 Convert::FromStr | Convert::TryFrom => quote! {
436 Self::parse_at(input, pos, state).map(|(_, end)| end)
437 },
438 };
439 Ok(quote! {
440 impl #impl_generics ::tygr::Grammar for #self_ty #where_clause {
441 type First = <#source as ::tygr::Grammar>::First;
442
443 #[inline]
444 fn parse_at(input: &str, pos: usize, #[allow(unused_mut)] mut state: ::tygr::State) -> Option<(Self, usize)> {
445 #parse_at
446 }
447
448 #[inline]
449 fn scan_at(input: &str, pos: usize, #[allow(unused_mut)] mut state: ::tygr::State) -> Option<usize> {
450 #scan_at
451 }
452
453 fn print_to(&self, buf: &mut ::std::string::String) {
454 <#self_ty as ::tygr::GrammarFrom>::print_to(self, buf);
455 }
456
457 fn to_bnf() -> ::tygr::bnf::Expr {
458 <#source as ::tygr::Grammar>::to_bnf()
459 }
460 }
461 })
462}
463
464fn impl_struct(
465 ident: &Ident,
466 generics: &Generics,
467 name: String,
468 hidden: bool,
469 transparent: bool,
470 filtered: bool,
471 data: &DataStruct,
472) -> syn::Result<TokenStream2> {
473 let tag = Tag::new(quote! { #ident }, None);
474 let fields = &data.fields;
475 let FieldsInfo {
476 constructor,
477 parse_at,
478 scan_at,
479 print_to,
480 to_bnf,
481 first,
482 } = FieldsInfo::from(filtered, tag, fields);
483 let parse_at = with_node(transparent, parse_at);
484 let scan_at = with_node(transparent, scan_at);
485 let bnf_ref = bnf_ref(&name, hidden, to_bnf.clone(), transparent);
486 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
487 Ok(quote! {
488 impl #impl_generics ::tygr::Grammar for #ident #ty_generics #where_clause {
489 type First = #first;
490
491 #[inline]
492 fn parse_at(input: &str, pos: usize, #[allow(unused_mut)] mut state: ::tygr::State) -> Option<(Self, usize)> {
493 #parse_at
494 }
495
496 #[inline]
497 fn scan_at(input: &str, pos: usize, #[allow(unused_mut)] mut state: ::tygr::State) -> Option<usize> {
498 #scan_at
499 }
500
501 fn print_to(&self, buf: &mut ::std::string::String) {
502 let #constructor = &self;
503 #print_to
504 }
505
506 fn to_bnf() -> ::tygr::bnf::Expr {
507 #bnf_ref
508 }
509 }
510
511 impl #impl_generics ::tygr::GrammarRule for #ident #ty_generics #where_clause {
512 const NAME: &'static str = #name;
513
514 fn to_bnf_def() -> ::tygr::bnf::Expr {
515 #to_bnf
516 }
517 }
518 })
519}
520
521fn impl_enum(
524 ident: &Ident,
525 generics: &Generics,
526 name: String,
527 hidden: bool,
528 transparent: bool,
529 filtered: bool,
530 data: &DataEnum,
531) -> syn::Result<TokenStream2> {
532 let mut each_constructor = vec![];
533 let mut each_parse_at = vec![];
534 let mut each_scan_at = vec![];
535 let mut each_print_to = vec![];
536 let mut each_to_bnf = vec![];
537 let mut each_first = vec![];
538 for variant in &data.variants {
539 let variant_ident = &variant.ident;
540 let tag = Tag::new(quote! { #ident }, Some(quote! {#variant_ident}));
541 let FieldsInfo {
542 constructor,
543 parse_at,
544 scan_at,
545 print_to,
546 to_bnf,
547 first,
548 } = FieldsInfo::from(filtered, tag, &variant.fields);
549 each_constructor.push(constructor);
550 each_parse_at.push(parse_at);
551 each_scan_at.push(scan_at);
552 each_print_to.push(print_to);
553 each_to_bnf.push(to_bnf);
554 each_first.push(first);
555 }
556 let to_bnf = quote! { ::tygr::bnf::Expr::alternation(vec![ #(#each_to_bnf),* ]) };
557 let bnf_ref = bnf_ref(&name, hidden, to_bnf.clone(), transparent);
558 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
559 let n = each_first.len();
560 let self_ty = quote! { #ident #ty_generics };
561 let parse_fn_ty = quote! {
562 fn(&str, usize, ::tygr::State<'_>) -> Option<(#self_ty, usize)>
563 };
564 let scan_fn_ty = quote! {
565 fn(&str, usize, ::tygr::State<'_>) -> Option<usize>
566 };
567 let case_fns: Vec<Ident> = (0..n)
568 .map(|k| Ident::new(&format!("parse_case_{k}"), Span::call_site()))
569 .collect();
570 let scan_fns: Vec<Ident> = (0..n)
571 .map(|k| Ident::new(&format!("scan_case_{k}"), Span::call_site()))
572 .collect();
573 let case_defs = case_fns.iter().zip(&each_parse_at).enumerate().map(|(k, (name, arm))| {
574 let next = if k + 1 < n {
575 let nx = &case_fns[k + 1];
576 quote! { Self::#nx(input, pos, state) }
577 } else {
578 quote! { None }
579 };
580 quote! {
581 #[inline]
582 fn #name(input: &str, pos: usize, #[allow(unused_variables, unused_mut)] mut state: ::tygr::State) -> Option<(Self, usize)> {
583 if let Some(result) = (|| { #arm })() { return Some(result); }
584 #next
585 }
586 }
587 });
588 let scan_case_defs = scan_fns
589 .iter()
590 .zip(&each_scan_at)
591 .enumerate()
592 .map(|(k, (name, arm))| {
593 let next = if k + 1 < n {
594 let nx = &scan_fns[k + 1];
595 quote! { Self::#nx(input, pos, state) }
596 } else {
597 quote! { None }
598 };
599 quote! {
600 #[inline]
601 fn #name(input: &str, pos: usize, #[allow(unused_variables, unused_mut)] mut state: ::tygr::State) -> Option<usize> {
602 if let Some(result) = (|| { #arm })() { return Some(result); }
603 #next
604 }
605 }
606 });
607 let build_table = |fn_ty: &TokenStream2, fns: &[Ident]| {
610 quote! {{
611 let parsers: [#fn_ty; #n] = [ #(<#self_ty>::#fns),* ];
612 let mut table: [#fn_ty; 257] = [parsers[#n - 1]; 257];
613 let mut first = 0usize;
614 while first < 257 {
615 let mut case = 0usize;
616 while case < #n {
617 if CONTAINS_NIL[case] || (first <= 255 && CONTAINS_BYTE[case][first]) {
618 table[first] = parsers[case];
619 break;
620 }
621 case += 1;
622 }
623 first += 1;
624 }
625 table
626 }}
627 };
628 let parse_table = build_table(&parse_fn_ty, &case_fns);
629 let scan_table = build_table(&scan_fn_ty, &scan_fns);
630 const DISPATCH_THRESHOLD: usize = 4;
631 let (dispatch_body, scan_dispatch_body, case_impls) = if n >= DISPATCH_THRESHOLD {
632 (
633 quote! {
634 const CONTAINS_NIL: [bool; #n] = [ #(<#each_first as ::tygr::First>::CONTAINS_NIL),* ];
635 const CONTAINS_BYTE: [[bool; 256]; #n] = [ #(<#each_first as ::tygr::First>::CONTAINS_BYTE),* ];
636 const DISPATCH: [#parse_fn_ty; 257] = #parse_table;
637 let first = input.as_bytes().get(pos).map(|&first| first as usize).unwrap_or(256);
638 DISPATCH[first](input, pos, state)
639 },
640 quote! {
641 const CONTAINS_NIL: [bool; #n] = [ #(<#each_first as ::tygr::First>::CONTAINS_NIL),* ];
642 const CONTAINS_BYTE: [[bool; 256]; #n] = [ #(<#each_first as ::tygr::First>::CONTAINS_BYTE),* ];
643 const DISPATCH: [#scan_fn_ty; 257] = #scan_table;
644 let first = input.as_bytes().get(pos).map(|&first| first as usize).unwrap_or(256);
645 DISPATCH[first](input, pos, state)
646 },
647 quote! {
648 #(#case_defs)*
649 #(#scan_case_defs)*
650 },
651 )
652 } else {
653 (
654 quote! {
655 #( if let Some(result) = (|| { #each_parse_at })() { return Some(result); } )*
656 None
657 },
658 quote! {
659 #( if let Some(result) = (|| { #each_scan_at })() { return Some(result); } )*
660 None
661 },
662 quote! {},
663 )
664 };
665 let parse_body = with_node(transparent, dispatch_body);
666 let scan_body = with_node(transparent, scan_dispatch_body);
667 let first = {
668 let mut the_first = quote! { ::tygr::EmptyByteSet };
669 for first in each_first {
670 the_first = quote! { <#the_first as ::tygr::First>::Union<#first> };
671 }
672 the_first
673 };
674 Ok(quote! {
675 impl #impl_generics #ident #ty_generics #where_clause {
676 #case_impls
677
678 #[inline]
679 fn parse_case_none(_input: &str, _pos: usize, _state: ::tygr::State) -> Option<(Self, usize)> {
680 None
681 }
682 }
683
684 impl #impl_generics ::tygr::Grammar for #ident #ty_generics #where_clause {
685 type First = #first;
686
687 #[inline]
688 fn parse_at(input: &str, pos: usize, #[allow(unused_variables, unused_mut)] mut state: ::tygr::State) -> Option<(Self, usize)> {
689 #parse_body
690 }
691
692 #[inline]
693 fn scan_at(input: &str, pos: usize, #[allow(unused_variables, unused_mut)] mut state: ::tygr::State) -> Option<usize> {
694 #scan_body
695 }
696
697 fn print_to(&self, buf: &mut ::std::string::String) {
698 match self {
699 #(#each_constructor => { #each_print_to }),*
700 }
701 }
702
703 fn to_bnf() -> ::tygr::bnf::Expr {
704 #bnf_ref
705 }
706 }
707
708 impl #impl_generics ::tygr::GrammarRule for #ident #ty_generics #where_clause {
709 const NAME: &'static str = #name;
710
711 fn to_bnf_def() -> ::tygr::bnf::Expr {
712 #to_bnf
713 }
714 }
715 })
716}