diff options
author | Benjamin Coenen <[email protected]> | 2020-04-09 17:37:34 +0100 |
---|---|---|
committer | Benjamin Coenen <[email protected]> | 2020-04-09 18:12:50 +0100 |
commit | c1317d692321ba5ba8f138067ebefbb9559d098d (patch) | |
tree | e29a44577e4d2cf55b6f53e3428abea43bbd33d7 /xtask/src/codegen | |
parent | fc70cf9458c5234decafdd52b9aced790ac43d7a (diff) | |
parent | 30f0ad159a0f260f54356385de63c171722adb72 (diff) |
feat: add support for feature attributes in struct literal
Signed-off-by: Benjamin Coenen <[email protected]>
Diffstat (limited to 'xtask/src/codegen')
-rw-r--r-- | xtask/src/codegen/gen_syntax.rs | 218 |
1 files changed, 142 insertions, 76 deletions
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs index 2dfb68371..6657c9fc5 100644 --- a/xtask/src/codegen/gen_syntax.rs +++ b/xtask/src/codegen/gen_syntax.rs | |||
@@ -3,10 +3,13 @@ | |||
3 | //! Specifically, it generates the `SyntaxKind` enum and a number of newtype | 3 | //! Specifically, it generates the `SyntaxKind` enum and a number of newtype |
4 | //! wrappers around `SyntaxNode` which implement `ra_syntax::AstNode`. | 4 | //! wrappers around `SyntaxNode` which implement `ra_syntax::AstNode`. |
5 | 5 | ||
6 | use std::{ | ||
7 | borrow::Cow, | ||
8 | collections::{BTreeSet, HashSet}, | ||
9 | }; | ||
10 | |||
6 | use proc_macro2::{Punct, Spacing}; | 11 | use proc_macro2::{Punct, Spacing}; |
7 | use quote::{format_ident, quote}; | 12 | use quote::{format_ident, quote}; |
8 | use std::borrow::Cow; | ||
9 | use std::collections::{BTreeSet, HashMap, HashSet}; | ||
10 | 13 | ||
11 | use crate::{ | 14 | use crate::{ |
12 | ast_src::{AstSrc, FieldSrc, KindsSrc, AST_SRC, KINDS_SRC}, | 15 | ast_src::{AstSrc, FieldSrc, KindsSrc, AST_SRC, KINDS_SRC}, |
@@ -19,9 +22,13 @@ pub fn generate_syntax(mode: Mode) -> Result<()> { | |||
19 | let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?; | 22 | let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?; |
20 | update(syntax_kinds_file.as_path(), &syntax_kinds, mode)?; | 23 | update(syntax_kinds_file.as_path(), &syntax_kinds, mode)?; |
21 | 24 | ||
22 | let ast_file = project_root().join(codegen::AST); | 25 | let ast_nodes_file = project_root().join(codegen::AST_NODES); |
23 | let ast = generate_ast(KINDS_SRC, AST_SRC)?; | 26 | let contents = generate_nodes(KINDS_SRC, AST_SRC)?; |
24 | update(ast_file.as_path(), &ast, mode)?; | 27 | update(ast_nodes_file.as_path(), &contents, mode)?; |
28 | |||
29 | let ast_tokens_file = project_root().join(codegen::AST_TOKENS); | ||
30 | let contents = generate_tokens(KINDS_SRC, AST_SRC)?; | ||
31 | update(ast_tokens_file.as_path(), &contents, mode)?; | ||
25 | 32 | ||
26 | Ok(()) | 33 | Ok(()) |
27 | } | 34 | } |
@@ -33,7 +40,7 @@ struct ElementKinds { | |||
33 | has_tokens: bool, | 40 | has_tokens: bool, |
34 | } | 41 | } |
35 | 42 | ||
36 | fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | 43 | fn generate_tokens(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { |
37 | let all_token_kinds: Vec<_> = kinds | 44 | let all_token_kinds: Vec<_> = kinds |
38 | .punct | 45 | .punct |
39 | .into_iter() | 46 | .into_iter() |
@@ -51,46 +58,6 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
51 | .chain(kinds.tokens.into_iter().copied().map(|x| x.into())) | 58 | .chain(kinds.tokens.into_iter().copied().map(|x| x.into())) |
52 | .collect(); | 59 | .collect(); |
53 | 60 | ||
54 | let mut element_kinds_map = HashMap::new(); | ||
55 | for kind in &all_token_kinds { | ||
56 | let kind = &**kind; | ||
57 | let name = to_pascal_case(kind); | ||
58 | element_kinds_map.insert( | ||
59 | name, | ||
60 | ElementKinds { | ||
61 | kinds: Some(format_ident!("{}", kind)).into_iter().collect(), | ||
62 | has_nodes: false, | ||
63 | has_tokens: true, | ||
64 | }, | ||
65 | ); | ||
66 | } | ||
67 | |||
68 | for kind in kinds.nodes { | ||
69 | let name = to_pascal_case(kind); | ||
70 | element_kinds_map.insert( | ||
71 | name, | ||
72 | ElementKinds { | ||
73 | kinds: Some(format_ident!("{}", *kind)).into_iter().collect(), | ||
74 | has_nodes: true, | ||
75 | has_tokens: false, | ||
76 | }, | ||
77 | ); | ||
78 | } | ||
79 | |||
80 | for en in grammar.enums { | ||
81 | let mut element_kinds: ElementKinds = Default::default(); | ||
82 | for variant in en.variants { | ||
83 | if let Some(variant_element_kinds) = element_kinds_map.get(*variant) { | ||
84 | element_kinds.kinds.extend(variant_element_kinds.kinds.iter().cloned()); | ||
85 | element_kinds.has_tokens |= variant_element_kinds.has_tokens; | ||
86 | element_kinds.has_nodes |= variant_element_kinds.has_nodes; | ||
87 | } else { | ||
88 | panic!("Enum variant has type that does not exist or was not declared before the enum: {}", *variant); | ||
89 | } | ||
90 | } | ||
91 | element_kinds_map.insert(en.name.to_string(), element_kinds); | ||
92 | } | ||
93 | |||
94 | let tokens = all_token_kinds.iter().map(|kind_str| { | 61 | let tokens = all_token_kinds.iter().map(|kind_str| { |
95 | let kind_str = &**kind_str; | 62 | let kind_str = &**kind_str; |
96 | let kind = format_ident!("{}", kind_str); | 63 | let kind = format_ident!("{}", kind_str); |
@@ -108,12 +75,7 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
108 | } | 75 | } |
109 | 76 | ||
110 | impl AstToken for #name { | 77 | impl AstToken for #name { |
111 | fn can_cast(kind: SyntaxKind) -> bool { | 78 | fn can_cast(kind: SyntaxKind) -> bool { kind == #kind } |
112 | match kind { | ||
113 | #kind => true, | ||
114 | _ => false, | ||
115 | } | ||
116 | } | ||
117 | fn cast(syntax: SyntaxToken) -> Option<Self> { | 79 | fn cast(syntax: SyntaxToken) -> Option<Self> { |
118 | if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } | 80 | if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } |
119 | } | 81 | } |
@@ -122,6 +84,99 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
122 | } | 84 | } |
123 | }); | 85 | }); |
124 | 86 | ||
87 | let enums = grammar.token_enums.iter().map(|en| { | ||
88 | let variants = en.variants.iter().map(|var| format_ident!("{}", var)).collect::<Vec<_>>(); | ||
89 | let name = format_ident!("{}", en.name); | ||
90 | let kinds = variants | ||
91 | .iter() | ||
92 | .map(|name| format_ident!("{}", to_upper_snake_case(&name.to_string()))) | ||
93 | .collect::<Vec<_>>(); | ||
94 | assert!(en.traits.is_empty()); | ||
95 | |||
96 | quote! { | ||
97 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
98 | pub enum #name { | ||
99 | #(#variants(#variants),)* | ||
100 | } | ||
101 | |||
102 | #( | ||
103 | impl From<#variants> for #name { | ||
104 | fn from(node: #variants) -> #name { | ||
105 | #name::#variants(node) | ||
106 | } | ||
107 | } | ||
108 | )* | ||
109 | |||
110 | impl std::fmt::Display for #name { | ||
111 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
112 | std::fmt::Display::fmt(self.syntax(), f) | ||
113 | } | ||
114 | } | ||
115 | |||
116 | impl AstToken for #name { | ||
117 | fn can_cast(kind: SyntaxKind) -> bool { | ||
118 | match kind { | ||
119 | #(#kinds)|* => true, | ||
120 | _ => false, | ||
121 | } | ||
122 | } | ||
123 | fn cast(syntax: SyntaxToken) -> Option<Self> { | ||
124 | let res = match syntax.kind() { | ||
125 | #( | ||
126 | #kinds => #name::#variants(#variants { syntax }), | ||
127 | )* | ||
128 | _ => return None, | ||
129 | }; | ||
130 | Some(res) | ||
131 | } | ||
132 | fn syntax(&self) -> &SyntaxToken { | ||
133 | match self { | ||
134 | #( | ||
135 | #name::#variants(it) => &it.syntax, | ||
136 | )* | ||
137 | } | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | }); | ||
142 | |||
143 | crate::reformat(quote! { | ||
144 | use crate::{SyntaxToken, SyntaxKind::{self, *}, ast::AstToken}; | ||
145 | |||
146 | #(#tokens)* | ||
147 | #(#enums)* | ||
148 | }) | ||
149 | } | ||
150 | |||
151 | fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | ||
152 | let all_token_kinds: Vec<_> = kinds | ||
153 | .punct | ||
154 | .into_iter() | ||
155 | .map(|(_, kind)| kind) | ||
156 | .copied() | ||
157 | .map(|x| x.into()) | ||
158 | .chain( | ||
159 | kinds | ||
160 | .keywords | ||
161 | .into_iter() | ||
162 | .chain(kinds.contextual_keywords.into_iter()) | ||
163 | .map(|name| Cow::Owned(format!("{}_KW", to_upper_snake_case(&name)))), | ||
164 | ) | ||
165 | .chain(kinds.literals.into_iter().copied().map(|x| x.into())) | ||
166 | .chain(kinds.tokens.into_iter().copied().map(|x| x.into())) | ||
167 | .collect(); | ||
168 | |||
169 | let mut token_kinds = HashSet::new(); | ||
170 | for kind in &all_token_kinds { | ||
171 | let kind = &**kind; | ||
172 | let name = to_pascal_case(kind); | ||
173 | token_kinds.insert(name); | ||
174 | } | ||
175 | |||
176 | for en in grammar.token_enums { | ||
177 | token_kinds.insert(en.name.to_string()); | ||
178 | } | ||
179 | |||
125 | let nodes = grammar.nodes.iter().map(|node| { | 180 | let nodes = grammar.nodes.iter().map(|node| { |
126 | let name = format_ident!("{}", node.name); | 181 | let name = format_ident!("{}", node.name); |
127 | let kind = format_ident!("{}", to_upper_snake_case(&name.to_string())); | 182 | let kind = format_ident!("{}", to_upper_snake_case(&name.to_string())); |
@@ -146,14 +201,23 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
146 | FieldSrc::Many(_) => { | 201 | FieldSrc::Many(_) => { |
147 | quote! { | 202 | quote! { |
148 | pub fn #method_name(&self) -> AstChildren<#ty> { | 203 | pub fn #method_name(&self) -> AstChildren<#ty> { |
149 | AstChildren::new(&self.syntax) | 204 | support::children(&self.syntax) |
150 | } | 205 | } |
151 | } | 206 | } |
152 | } | 207 | } |
153 | FieldSrc::Optional(_) | FieldSrc::Shorthand => { | 208 | FieldSrc::Optional(_) | FieldSrc::Shorthand => { |
154 | quote! { | 209 | let is_token = token_kinds.contains(&ty.to_string()); |
155 | pub fn #method_name(&self) -> Option<#ty> { | 210 | if is_token { |
156 | AstChildren::new(&self.syntax).next() | 211 | quote! { |
212 | pub fn #method_name(&self) -> Option<#ty> { | ||
213 | support::token(&self.syntax) | ||
214 | } | ||
215 | } | ||
216 | } else { | ||
217 | quote! { | ||
218 | pub fn #method_name(&self) -> Option<#ty> { | ||
219 | support::child(&self.syntax) | ||
220 | } | ||
157 | } | 221 | } |
158 | } | 222 | } |
159 | } | 223 | } |
@@ -166,18 +230,9 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
166 | pub(crate) syntax: SyntaxNode, | 230 | pub(crate) syntax: SyntaxNode, |
167 | } | 231 | } |
168 | 232 | ||
169 | impl std::fmt::Display for #name { | ||
170 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
171 | std::fmt::Display::fmt(self.syntax(), f) | ||
172 | } | ||
173 | } | ||
174 | |||
175 | impl AstNode for #name { | 233 | impl AstNode for #name { |
176 | fn can_cast(kind: SyntaxKind) -> bool { | 234 | fn can_cast(kind: SyntaxKind) -> bool { |
177 | match kind { | 235 | kind == #kind |
178 | #kind => true, | ||
179 | _ => false, | ||
180 | } | ||
181 | } | 236 | } |
182 | fn cast(syntax: SyntaxNode) -> Option<Self> { | 237 | fn cast(syntax: SyntaxNode) -> Option<Self> { |
183 | if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } | 238 | if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } |
@@ -219,12 +274,6 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
219 | } | 274 | } |
220 | )* | 275 | )* |
221 | 276 | ||
222 | impl std::fmt::Display for #name { | ||
223 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
224 | std::fmt::Display::fmt(self.syntax(), f) | ||
225 | } | ||
226 | } | ||
227 | |||
228 | impl AstNode for #name { | 277 | impl AstNode for #name { |
229 | fn can_cast(kind: SyntaxKind) -> bool { | 278 | fn can_cast(kind: SyntaxKind) -> bool { |
230 | match kind { | 279 | match kind { |
@@ -249,10 +298,26 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
249 | } | 298 | } |
250 | } | 299 | } |
251 | } | 300 | } |
301 | |||
252 | #(#traits)* | 302 | #(#traits)* |
253 | } | 303 | } |
254 | }); | 304 | }); |
255 | 305 | ||
306 | let displays = grammar | ||
307 | .enums | ||
308 | .iter() | ||
309 | .map(|it| format_ident!("{}", it.name)) | ||
310 | .chain(grammar.nodes.iter().map(|it| format_ident!("{}", it.name))) | ||
311 | .map(|name| { | ||
312 | quote! { | ||
313 | impl std::fmt::Display for #name { | ||
314 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
315 | std::fmt::Display::fmt(self.syntax(), f) | ||
316 | } | ||
317 | } | ||
318 | } | ||
319 | }); | ||
320 | |||
256 | let defined_nodes: HashSet<_> = grammar.nodes.iter().map(|node| node.name).collect(); | 321 | let defined_nodes: HashSet<_> = grammar.nodes.iter().map(|node| node.name).collect(); |
257 | 322 | ||
258 | for node in kinds | 323 | for node in kinds |
@@ -265,15 +330,16 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
265 | } | 330 | } |
266 | 331 | ||
267 | let ast = quote! { | 332 | let ast = quote! { |
268 | #[allow(unused_imports)] | ||
269 | use crate::{ | 333 | use crate::{ |
270 | SyntaxNode, SyntaxToken, SyntaxElement, NodeOrToken, SyntaxKind::{self, *}, | 334 | SyntaxNode, SyntaxKind::{self, *}, |
271 | ast::{self, AstNode, AstToken, AstChildren}, | 335 | ast::{self, AstNode, AstChildren, support}, |
272 | }; | 336 | }; |
273 | 337 | ||
274 | #(#tokens)* | 338 | use super::tokens::*; |
339 | |||
275 | #(#nodes)* | 340 | #(#nodes)* |
276 | #(#enums)* | 341 | #(#enums)* |
342 | #(#displays)* | ||
277 | }; | 343 | }; |
278 | 344 | ||
279 | let pretty = crate::reformat(ast)?; | 345 | let pretty = crate::reformat(ast)?; |