1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
mod pattern;
use macros_utils::{MacroStream, Parse, Token};
use pattern::{pattern_statement, ParserInput};
use proc_macro2::{Span, TokenStream};
use proc_macro_error::{abort_call_site, proc_macro_error};
use quote::quote;
#[proc_macro_error]
#[proc_macro]
pub fn parser(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
match MacroStream::from_tokens(stream.into()) {
Err(err) => err.into_diagnostic().abort(),
Ok(stream) => parser_impl(stream).into(),
}
}
fn parser_impl(mut stream: MacroStream) -> TokenStream {
let name = stream.pop();
match name {
Some(Token::Ident { name, .. }) => {
let next = stream.pop();
match next {
Some(Token::Punctuation { value, .. }) if value == "=>" => {
let input = match ParserInput::parse(&mut stream) {
Err(err) => err.into_diagnostic().abort(),
Ok(input) => input,
};
let struct_name = Token::Ident {
name,
span: Span::call_site(),
};
let raw_params = input
.params()
.into_iter()
.map(|(name, optional)| {
let ident = Token::Ident {
name,
span: Span::call_site(),
};
(ident, optional)
})
.collect::<Vec<_>>();
let var_params = raw_params.iter().map(|(ident, optional)| {
if *optional {
quote! {
let mut #ident = None;
}
} else {
quote! {
let mut #ident = macros_utils::Match::None;
}
}
});
let return_params = raw_params.iter().map(|(ident, _)| {
quote! {
#ident,
}
});
let struct_fields = raw_params.iter().map(|(ident, optional)| {
if *optional {
quote! {
pub #ident: Option<macros_utils::Match>,
}
} else {
quote! {
pub #ident: macros_utils::Match,
}
}
});
let patterns = input.patterns.into_iter().map(|pattern| {
let statement = pattern_statement(pattern, &raw_params);
quote! {
#statement?;
}
});
quote! {
#[derive(Debug, Clone)]
pub struct #struct_name {
#(#struct_fields)*
}
#[allow(clippy::never_loop)]
impl macros_utils::Parse for #struct_name {
fn parse(stream: &mut macros_utils::MacroStream) -> Result<Self, macros_utils::MacrosError> {
#(#var_params)*
// declare variables for each parameter at the top, if variable is optional it will default to None, otherwise be uninitialized
// variables can either be Option<MacroStream> or MacroStream
// each pattern needs to be parsed off the stream, and also able to assign to variables
// have a match statement for each pattern, if it matches then assign and keep going, if not then error/abort
// the match statement (can make it a generic variable above using quote) will return a Result, if it's an error then it will be unwrapped and returned from the function
#(#patterns)*
Ok(Self {
#(#return_params)*
})
}
}
}
},
_ => abort_call_site!("expected => after the name of the parser"),
}
},
_ => abort_call_site!("expected the name of the parser first"),
}
}