diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-04-21 13:58:27 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-04-21 13:58:27 +0100 |
commit | 4a250021b1a1def483f7faf2b534ec4dd7defd02 (patch) | |
tree | 81998d7371e05c44c0f6dd82f3ec4216db760f4b /xtask/src/codegen | |
parent | a88887df0726cc3d390db4bfbbc1274195d87f91 (diff) | |
parent | 972d3b2ba30ec40bebe85452117e669536faa167 (diff) |
Merge #4038
4038: Group generated ast boilerplate apart from the interesting part r=matklad a=Veetaha
Boilerplate `AstNode` and `From` impls are moved to the end further from the interesting part in `generated.rs`
Co-authored-by: veetaha <[email protected]>
Diffstat (limited to 'xtask/src/codegen')
-rw-r--r-- | xtask/src/codegen/gen_syntax.rs | 250 |
1 files changed, 130 insertions, 120 deletions
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs index ec1f6ad8a..e9dc09552 100644 --- a/xtask/src/codegen/gen_syntax.rs +++ b/xtask/src/codegen/gen_syntax.rs | |||
@@ -63,126 +63,138 @@ fn generate_tokens(grammar: AstSrc<'_>) -> Result<String> { | |||
63 | } | 63 | } |
64 | 64 | ||
65 | fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | 65 | fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { |
66 | let nodes = grammar.nodes.iter().map(|node| { | 66 | let (node_defs, node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar |
67 | let name = format_ident!("{}", node.name); | 67 | .nodes |
68 | let kind = format_ident!("{}", to_upper_snake_case(&name.to_string())); | 68 | .iter() |
69 | let traits = node.traits.iter().map(|trait_name| { | 69 | .map(|node| { |
70 | let trait_name = format_ident!("{}", trait_name); | 70 | let name = format_ident!("{}", node.name); |
71 | quote!(impl ast::#trait_name for #name {}) | 71 | let kind = format_ident!("{}", to_upper_snake_case(&name.to_string())); |
72 | }); | 72 | let traits = node.traits.iter().map(|trait_name| { |
73 | 73 | let trait_name = format_ident!("{}", trait_name); | |
74 | let methods = node.fields.iter().map(|field| { | 74 | quote!(impl ast::#trait_name for #name {}) |
75 | let method_name = field.method_name(); | 75 | }); |
76 | let ty = field.ty(); | 76 | |
77 | 77 | let methods = node.fields.iter().map(|field| { | |
78 | if field.is_many() { | 78 | let method_name = field.method_name(); |
79 | quote! { | 79 | let ty = field.ty(); |
80 | pub fn #method_name(&self) -> AstChildren<#ty> { | 80 | |
81 | support::children(&self.syntax) | 81 | if field.is_many() { |
82 | } | ||
83 | } | ||
84 | } else { | ||
85 | if let Some(token_kind) = field.token_kind() { | ||
86 | quote! { | 82 | quote! { |
87 | pub fn #method_name(&self) -> Option<#ty> { | 83 | pub fn #method_name(&self) -> AstChildren<#ty> { |
88 | support::token(&self.syntax, #token_kind) | 84 | support::children(&self.syntax) |
89 | } | 85 | } |
90 | } | 86 | } |
91 | } else { | 87 | } else { |
92 | quote! { | 88 | if let Some(token_kind) = field.token_kind() { |
93 | pub fn #method_name(&self) -> Option<#ty> { | 89 | quote! { |
94 | support::child(&self.syntax) | 90 | pub fn #method_name(&self) -> Option<#ty> { |
91 | support::token(&self.syntax, #token_kind) | ||
92 | } | ||
93 | } | ||
94 | } else { | ||
95 | quote! { | ||
96 | pub fn #method_name(&self) -> Option<#ty> { | ||
97 | support::child(&self.syntax) | ||
98 | } | ||
95 | } | 99 | } |
96 | } | 100 | } |
97 | } | 101 | } |
98 | } | 102 | }); |
99 | }); | 103 | ( |
100 | 104 | quote! { | |
101 | quote! { | 105 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
102 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 106 | pub struct #name { |
103 | pub struct #name { | 107 | pub(crate) syntax: SyntaxNode, |
104 | pub(crate) syntax: SyntaxNode, | 108 | } |
105 | } | ||
106 | |||
107 | impl AstNode for #name { | ||
108 | fn can_cast(kind: SyntaxKind) -> bool { | ||
109 | kind == #kind | ||
110 | } | ||
111 | fn cast(syntax: SyntaxNode) -> Option<Self> { | ||
112 | if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } | ||
113 | } | ||
114 | fn syntax(&self) -> &SyntaxNode { &self.syntax } | ||
115 | } | ||
116 | |||
117 | #(#traits)* | ||
118 | |||
119 | impl #name { | ||
120 | #(#methods)* | ||
121 | } | ||
122 | } | ||
123 | }); | ||
124 | 109 | ||
125 | let enums = grammar.enums.iter().map(|en| { | 110 | #(#traits)* |
126 | let variants = en.variants.iter().map(|var| format_ident!("{}", var)).collect::<Vec<_>>(); | ||
127 | let name = format_ident!("{}", en.name); | ||
128 | let kinds = variants | ||
129 | .iter() | ||
130 | .map(|name| format_ident!("{}", to_upper_snake_case(&name.to_string()))) | ||
131 | .collect::<Vec<_>>(); | ||
132 | let traits = en.traits.iter().map(|trait_name| { | ||
133 | let trait_name = format_ident!("{}", trait_name); | ||
134 | quote!(impl ast::#trait_name for #name {}) | ||
135 | }); | ||
136 | 111 | ||
137 | quote! { | 112 | impl #name { |
138 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 113 | #(#methods)* |
139 | pub enum #name { | 114 | } |
140 | #(#variants(#variants),)* | 115 | }, |
141 | } | 116 | quote! { |
117 | impl AstNode for #name { | ||
118 | fn can_cast(kind: SyntaxKind) -> bool { | ||
119 | kind == #kind | ||
120 | } | ||
121 | fn cast(syntax: SyntaxNode) -> Option<Self> { | ||
122 | if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } | ||
123 | } | ||
124 | fn syntax(&self) -> &SyntaxNode { &self.syntax } | ||
125 | } | ||
126 | }, | ||
127 | ) | ||
128 | }) | ||
129 | .unzip(); | ||
142 | 130 | ||
143 | #( | 131 | let (enum_defs, enum_boilerplate_impls): (Vec<_>, Vec<_>) = grammar |
144 | impl From<#variants> for #name { | 132 | .enums |
145 | fn from(node: #variants) -> #name { | 133 | .iter() |
146 | #name::#variants(node) | 134 | .map(|en| { |
147 | } | 135 | let variants: Vec<_> = en.variants.iter().map(|var| format_ident!("{}", var)).collect(); |
148 | } | 136 | let name = format_ident!("{}", en.name); |
149 | )* | 137 | let kinds: Vec<_> = variants |
138 | .iter() | ||
139 | .map(|name| format_ident!("{}", to_upper_snake_case(&name.to_string()))) | ||
140 | .collect(); | ||
141 | let traits = en.traits.iter().map(|trait_name| { | ||
142 | let trait_name = format_ident!("{}", trait_name); | ||
143 | quote!(impl ast::#trait_name for #name {}) | ||
144 | }); | ||
145 | |||
146 | ( | ||
147 | quote! { | ||
148 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
149 | pub enum #name { | ||
150 | #(#variants(#variants),)* | ||
151 | } | ||
150 | 152 | ||
151 | impl AstNode for #name { | 153 | #(#traits)* |
152 | fn can_cast(kind: SyntaxKind) -> bool { | 154 | }, |
153 | match kind { | 155 | quote! { |
154 | #(#kinds)|* => true, | 156 | #( |
155 | _ => false, | 157 | impl From<#variants> for #name { |
158 | fn from(node: #variants) -> #name { | ||
159 | #name::#variants(node) | ||
160 | } | ||
156 | } | 161 | } |
157 | } | 162 | )* |
158 | fn cast(syntax: SyntaxNode) -> Option<Self> { | 163 | |
159 | let res = match syntax.kind() { | 164 | impl AstNode for #name { |
160 | #( | 165 | fn can_cast(kind: SyntaxKind) -> bool { |
161 | #kinds => #name::#variants(#variants { syntax }), | 166 | match kind { |
162 | )* | 167 | #(#kinds)|* => true, |
163 | _ => return None, | 168 | _ => false, |
164 | }; | 169 | } |
165 | Some(res) | 170 | } |
166 | } | 171 | fn cast(syntax: SyntaxNode) -> Option<Self> { |
167 | fn syntax(&self) -> &SyntaxNode { | 172 | let res = match syntax.kind() { |
168 | match self { | 173 | #( |
169 | #( | 174 | #kinds => #name::#variants(#variants { syntax }), |
170 | #name::#variants(it) => &it.syntax, | 175 | )* |
171 | )* | 176 | _ => return None, |
177 | }; | ||
178 | Some(res) | ||
179 | } | ||
180 | fn syntax(&self) -> &SyntaxNode { | ||
181 | match self { | ||
182 | #( | ||
183 | #name::#variants(it) => &it.syntax, | ||
184 | )* | ||
185 | } | ||
186 | } | ||
172 | } | 187 | } |
173 | } | 188 | }, |
174 | } | 189 | ) |
190 | }) | ||
191 | .unzip(); | ||
175 | 192 | ||
176 | #(#traits)* | 193 | let enum_names = grammar.enums.iter().map(|it| it.name); |
177 | } | 194 | let node_names = grammar.nodes.iter().map(|it| it.name); |
178 | }); | ||
179 | 195 | ||
180 | let displays = grammar | 196 | let display_impls = |
181 | .enums | 197 | enum_names.chain(node_names.clone()).map(|it| format_ident!("{}", it)).map(|name| { |
182 | .iter() | ||
183 | .map(|it| format_ident!("{}", it.name)) | ||
184 | .chain(grammar.nodes.iter().map(|it| format_ident!("{}", it.name))) | ||
185 | .map(|name| { | ||
186 | quote! { | 198 | quote! { |
187 | impl std::fmt::Display for #name { | 199 | impl std::fmt::Display for #name { |
188 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | 200 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
@@ -192,13 +204,13 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
192 | } | 204 | } |
193 | }); | 205 | }); |
194 | 206 | ||
195 | let defined_nodes: HashSet<_> = grammar.nodes.iter().map(|node| node.name).collect(); | 207 | let defined_nodes: HashSet<_> = node_names.collect(); |
196 | 208 | ||
197 | for node in kinds | 209 | for node in kinds |
198 | .nodes | 210 | .nodes |
199 | .iter() | 211 | .iter() |
200 | .map(|kind| to_pascal_case(*kind)) | 212 | .map(|kind| to_pascal_case(kind)) |
201 | .filter(|name| !defined_nodes.contains(&**name)) | 213 | .filter(|name| !defined_nodes.contains(name.as_str())) |
202 | { | 214 | { |
203 | eprintln!("Warning: node {} not defined in ast source", node); | 215 | eprintln!("Warning: node {} not defined in ast source", node); |
204 | } | 216 | } |
@@ -210,9 +222,11 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
210 | T, | 222 | T, |
211 | }; | 223 | }; |
212 | 224 | ||
213 | #(#nodes)* | 225 | #(#node_defs)* |
214 | #(#enums)* | 226 | #(#enum_defs)* |
215 | #(#displays)* | 227 | #(#node_boilerplate_impls)* |
228 | #(#enum_boilerplate_impls)* | ||
229 | #(#display_impls)* | ||
216 | }; | 230 | }; |
217 | 231 | ||
218 | let ast = ast.to_string().replace("T ! [ ", "T![").replace(" ] )", "])"); | 232 | let ast = ast.to_string().replace("T ! [ ", "T![").replace(" ] )", "])"); |
@@ -380,20 +394,16 @@ fn to_pascal_case(s: &str) -> String { | |||
380 | 394 | ||
381 | impl Field<'_> { | 395 | impl Field<'_> { |
382 | fn is_many(&self) -> bool { | 396 | fn is_many(&self) -> bool { |
383 | match self { | 397 | matches!(self, Field::Node { src: FieldSrc::Many(_), .. }) |
384 | Field::Node { src: FieldSrc::Many(_), .. } => true, | ||
385 | _ => false, | ||
386 | } | ||
387 | } | 398 | } |
388 | fn token_kind(&self) -> Option<proc_macro2::TokenStream> { | 399 | fn token_kind(&self) -> Option<proc_macro2::TokenStream> { |
389 | let res = match self { | 400 | match self { |
390 | Field::Token(token) => { | 401 | Field::Token(token) => { |
391 | let token: proc_macro2::TokenStream = token.parse().unwrap(); | 402 | let token: proc_macro2::TokenStream = token.parse().unwrap(); |
392 | quote! { T![#token] } | 403 | Some(quote! { T![#token] }) |
393 | } | 404 | } |
394 | _ => return None, | 405 | _ => None, |
395 | }; | 406 | } |
396 | Some(res) | ||
397 | } | 407 | } |
398 | fn method_name(&self) -> proc_macro2::Ident { | 408 | fn method_name(&self) -> proc_macro2::Ident { |
399 | match self { | 409 | match self { |