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"),
    }
}