1use macros_utils::{
2 call_site, MacroStream, Match, Parse, ParserInput, ParserOutput, Repr, Spacing, Token,
3};
4use proc_macro2::{Span, TokenStream};
5use proc_macro_error::{abort_call_site, proc_macro_error};
6use quote::quote;
7
8#[proc_macro_error]
26#[proc_macro]
27pub fn parser(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
28 match MacroStream::from_tokens(stream.into()) {
29 Err(err) => err.into_diagnostic().abort(),
30 Ok(stream) => parser_impl(stream).into(),
31 }
32}
33
34#[derive(Clone)]
35struct Empty {}
36
37impl ParserOutput for Empty {
38 fn set_match(&mut self, _: &str, _: Match) {}
39 fn name() -> &'static str {
40 "Empty"
41 }
42}
43
44fn parser_impl(mut stream: MacroStream) -> TokenStream {
45 let name = stream.pop();
46 match name {
47 Some(Token::Ident { name, .. }) => {
48 let next = stream.pop();
49 let next2 = stream.pop();
50 match (next, next2) {
51 (
52 Some(Token::Punctuation {
53 value: '=',
54 spacing: Spacing::Joint,
55 ..
56 }),
57 Some(Token::Punctuation {
58 value: '>',
59 spacing: Spacing::Alone,
60 ..
61 }),
62 ) => {
63 let input = match ParserInput::<Empty>::parse(&mut stream) {
64 Err(err) => err.into_diagnostic().abort(),
65 Ok(input) => input,
66 };
67 let patterns = &input
68 .patterns
69 .iter()
70 .map(|p| p.repr(&name))
71 .collect::<Vec<_>>();
72 let struct_name = Token::Ident {
73 name: name.clone(),
74 span: Span::call_site(),
75 };
76 let raw_params = input
77 .params()
78 .into_iter()
79 .map(|(name, optional)| {
80 let ident = Token::Ident {
81 name,
82 span: Span::call_site(),
83 };
84 (ident, optional)
85 })
86 .collect::<Vec<_>>();
87 let var_params = raw_params.iter().map(|(ident, optional)| {
88 if *optional {
89 quote! {
90 #ident: None,
91 }
92 } else {
93 quote! {
94 #ident: macros_utils::Match::None,
95 }
96 }
97 });
98 let struct_fields = raw_params.iter().map(|(ident, optional)| {
99 if *optional {
100 quote! {
101 pub #ident: Option<macros_utils::Match>,
102 }
103 } else {
104 quote! {
105 pub #ident: macros_utils::Match,
106 }
107 }
108 });
109 let patterns_const = Token::Ident {
110 name: format!("__{}_PATTERNS", name.to_ascii_uppercase()),
111 span: call_site(),
112 };
113 let set_params = raw_params.iter().map(|(ident, _)| {
114 let name = ident.ident().unwrap();
115 quote! {
116 #name => self.#ident = value,
117 }
118 });
119 quote! {
120 #[derive(Debug, Clone)]
121 pub struct #struct_name {
122 #(#struct_fields)*
123 }
124
125 macros_utils::lazy_static! {
126 static ref #patterns_const: Vec<macros_utils::Pattern<#struct_name>> = vec![
127 #(#patterns,)*
128 ];
129 }
130
131 #[allow(clippy::never_loop)]
132 impl macros_utils::Parse for #struct_name {
133 fn parse(stream: &mut macros_utils::MacroStream) -> Result<Self, macros_utils::MacrosError> {
134 let mut o = Self {
135 #(#var_params)*
136 };
137 let (res, o) = macros_utils::Pattern::<#struct_name>::match_patterns(std::borrow::Cow::Owned(o), &#patterns_const, stream);
138 match res {
139 Ok(_) => Ok(o.into_owned()),
140 Err(e) => Err(e),
141 }
142 }
143 }
144
145 impl macros_utils::ParserOutput for #struct_name {
146 fn set_match(&mut self, name: &str, value: macros_utils::Match) {
147 match name {
148 #(#set_params)*
149 _ => (),
150 }
151 }
152
153 fn name() -> &'static str {
154 #name
155 }
156 }
157 }
158 },
159 _ => abort_call_site!("expected => after the name of the parser"),
160 }
161 },
162 _ => abort_call_site!("expected the name of the parser first"),
163 }
164}