pigeon_macs/
lib.rs

1mod fmtparser;
2mod builder;
3
4use fmtparser::*;
5use builder::*;
6use syn::*;
7use quote::{quote, ToTokens};
8use proc_macro2::*;
9
10// A simple macro that eject error variants into compile error. 
11macro_rules! bail {
12    ($x: expr) => {match $x {
13        Ok(out)  => out,
14        Err(err) => return err.into_compile_error().into()
15    }};
16}
17
18pub(crate) const CRATE: &str = "pigeon";
19
20/// Generate Parse<GROUP> trait(s) from rule attributes
21#[proc_macro_derive(ParseImpl, attributes(rule))]
22pub fn parse_impl_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
23    let input = bail!(parse::<DeriveInput>(input));
24    let builder = bail!(Builder::new(input));
25    let mut output = TokenStream::new();
26    output.extend(bail!(builder.parse_impl_build()));
27    output.extend(bail!(builder.rules_impl_build()));
28    output.into()
29}
30
31/// Generate AstImpl trait from rule attributes
32#[proc_macro_derive(EnumAstImpl, attributes(rule, with))]
33pub fn ast_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
34    let input = bail!(parse::<DeriveInput>(input));
35    let builder = bail!(Builder::new(input));
36    let mut output = TokenStream::new();
37    output.extend(bail!(builder.ast_impl_build()));
38    output.into()
39}
40
41/// Generate AstImpl trait from Prepend trait
42#[proc_macro_derive(PrependAstImpl)]
43pub fn prepend_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
44    let input = bail!(parse::<DeriveInput>(input));
45    let _crate = parse_str::<Ident>(CRATE).unwrap();
46    let generics = input.generics.params;
47    let ident = input.ident;
48    quote! {
49        impl<#generics, Extra: Copy> AstImpl<Extra> for #ident<#generics> where
50            Self: Prepend<Extra>,
51            <Self as Prepend<Extra>>::Item: AstImpl<Extra>
52        {
53            fn ast<'lifetime>(
54                input: &'lifetime str, 
55                stack: &'lifetime [#_crate::Tag], 
56                with: Extra
57            ) -> (&'lifetime [#_crate::Tag], Self) {
58                let tag = &stack[stack.len()-1];
59                let mut stack = &stack[..stack.len()-1];
60                let mut this = <Self as Prepend<Extra>>::empty(with);
61                for i in 0..tag.rule {
62                    let (stack_, value) = <<Self as Prepend<Extra>>::Item as AstImpl<Extra>>::ast(input, stack, with);
63                    this.prepend(value, with);
64                    stack = stack_;
65                }
66                (stack, this)
67            }
68        }
69    }.into()
70}
71
72/// Generate Num trait from rule attributes
73#[proc_macro_derive(Num, attributes(rule))]
74pub fn num_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
75    let input = bail!(parse::<DeriveInput>(input));
76    let builder = bail!(Builder::new(input));
77    bail!(builder.num_build()).into()
78}
79
80/// Generate Space trait the handles '\n\t '
81#[proc_macro_derive(Space)]
82pub fn space_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
83    let input = bail!(syn::parse::<DeriveInput>(input));
84    let ident = input.ident;
85    let generics = input.generics.params;
86    let comma = generics.to_token_stream().into_iter().last().map(|x| x.to_string() == ",").unwrap_or(false);
87    let generics = 
88        if !comma && !generics.is_empty() { quote! { #generics, } }
89        else                              { quote! { #generics  } };
90    quote! {
91        impl<#generics> Space for #ident<#generics> {}
92    }.into()
93}