Skip to main content

galvan_ast_macro/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{parse_macro_input, Data, DeriveInput, Fields};
6
7#[proc_macro_derive(AstNode)]
8pub fn ast_node_derive(input: TokenStream) -> TokenStream {
9    let input = parse_macro_input!(input as DeriveInput);
10
11    let struct_name = input.ident;
12    match &input.data {
13        Data::Struct(data) => {
14            let fields = match &data.fields {
15                Fields::Named(fields) => &fields.named,
16                _ => unimplemented!(),
17            };
18            let field_names: Vec<_> = fields
19                .iter()
20                .filter_map(|f| {
21                    if f.ident.as_ref().unwrap() != "span" {
22                        Some(f.ident.as_ref().unwrap())
23                    } else {
24                        None
25                    }
26                })
27                .collect();
28
29            let gen = quote! {
30                impl AstNode for #struct_name {
31                    fn span(&self) -> Span {
32                        self.span
33                    }
34
35                    fn print(&self, indent: usize) -> String {
36                        let indent_str = " ".repeat(indent);
37                        let mut result = format!("{}{}\n", indent_str, stringify!(#struct_name));
38                        #(
39                            let field_name = stringify!(#field_names);
40                            let field_value = self.#field_names.print_ast(indent + 2);
41                            result.push_str(&format!("{}  {}{}\n", indent_str, field_name, field_value));
42                        )*
43                        result
44                    }
45                }
46            };
47
48            gen.into()
49        }
50        Data::Enum(e) => {
51            let cases: Vec<_> = e.variants.iter().map(|v| v.ident.clone()).collect();
52            let gen = quote! {
53                impl AstNode for #struct_name {
54                    fn print(&self, indent: usize) -> String {
55                        use #struct_name::*;
56                        match self {
57                            #(
58                                #cases(c) => c.print(indent),
59                            )*
60                        }
61                    }
62
63                    fn span(&self) -> Span {
64                        use #struct_name::*;
65                        match self {
66                            #(
67                                #cases(c) => c.span(),
68                            )*
69                        }
70                    }
71                }
72            };
73
74            gen.into()
75        }
76        Data::Union(_) => panic!("Not allowed on unions!"),
77    }
78}
79
80#[proc_macro_derive(PrintAst)]
81pub fn print_ast_derive(input: TokenStream) -> TokenStream {
82    let input = parse_macro_input!(input as DeriveInput);
83
84    let struct_name = input.ident;
85    match &input.data {
86        Data::Struct(data) => {
87            let fields = match &data.fields {
88                Fields::Named(fields) => &fields.named,
89                _ => unimplemented!(),
90            };
91            let field_names: Vec<_> = fields
92                .iter()
93                .filter_map(|f| {
94                    if f.ident.as_ref().unwrap() != "span" {
95                        Some(f.ident.as_ref().unwrap())
96                    } else {
97                        None
98                    }
99                })
100                .collect();
101
102            let gen = quote! {
103                impl PrintAst for #struct_name {
104                    fn print_ast(&self, indent: usize) -> String {
105                        let indent_str = " ".repeat(indent);
106                        let mut result = format!("{}{}\n", indent_str, stringify!(#struct_name));
107                        #(
108                            let field_name = stringify!(#field_names);
109                            let field_value = self.#field_names.print_ast(indent + 2);
110                            result.push_str(&format!("{}  {}{}\n", indent_str, field_name, field_value));
111                        )*
112                        result
113                    }
114                }
115            };
116
117            gen.into()
118        }
119        Data::Enum(e) => {
120            let cases: Vec<_> = e.variants.iter().map(|v| v.ident.clone()).collect();
121
122            let gen = quote! {
123                impl PrintAst for #struct_name {
124                    fn print_ast(&self, indent: usize) -> String {
125                        use #struct_name::*;
126                        match self {
127                            #(
128                                #cases(c) => c.print_ast(indent),
129                            )*
130                        }
131                    }
132                }
133            };
134
135            gen.into()
136        }
137        Data::Union(_) => panic!("Not allowed on unions!"),
138    }
139}