diff options
Diffstat (limited to 'xtask/src/codegen')
-rw-r--r-- | xtask/src/codegen/gen_syntax.rs | 257 |
1 files changed, 226 insertions, 31 deletions
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs index 32afd47bc..b5594e3a9 100644 --- a/xtask/src/codegen/gen_syntax.rs +++ b/xtask/src/codegen/gen_syntax.rs | |||
@@ -3,6 +3,11 @@ | |||
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 | 13 | ||
@@ -17,14 +22,161 @@ pub fn generate_syntax(mode: Mode) -> Result<()> { | |||
17 | let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?; | 22 | let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?; |
18 | update(syntax_kinds_file.as_path(), &syntax_kinds, mode)?; | 23 | update(syntax_kinds_file.as_path(), &syntax_kinds, mode)?; |
19 | 24 | ||
20 | let ast_file = project_root().join(codegen::AST); | 25 | let ast_nodes_file = project_root().join(codegen::AST_NODES); |
21 | let ast = generate_ast(AST_SRC)?; | 26 | let contents = generate_nodes(KINDS_SRC, AST_SRC)?; |
22 | 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)?; | ||
23 | 32 | ||
24 | Ok(()) | 33 | Ok(()) |
25 | } | 34 | } |
26 | 35 | ||
27 | fn generate_ast(grammar: AstSrc<'_>) -> Result<String> { | 36 | #[derive(Debug, Default, Clone)] |
37 | struct ElementKinds { | ||
38 | kinds: BTreeSet<proc_macro2::Ident>, | ||
39 | has_nodes: bool, | ||
40 | has_tokens: bool, | ||
41 | } | ||
42 | |||
43 | fn generate_tokens(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | ||
44 | let all_token_kinds: Vec<_> = kinds | ||
45 | .punct | ||
46 | .into_iter() | ||
47 | .map(|(_, kind)| kind) | ||
48 | .copied() | ||
49 | .map(|x| x.into()) | ||
50 | .chain( | ||
51 | kinds | ||
52 | .keywords | ||
53 | .into_iter() | ||
54 | .chain(kinds.contextual_keywords.into_iter()) | ||
55 | .map(|name| Cow::Owned(format!("{}_KW", to_upper_snake_case(&name)))), | ||
56 | ) | ||
57 | .chain(kinds.literals.into_iter().copied().map(|x| x.into())) | ||
58 | .chain(kinds.tokens.into_iter().copied().map(|x| x.into())) | ||
59 | .collect(); | ||
60 | |||
61 | let tokens = all_token_kinds.iter().map(|kind_str| { | ||
62 | let kind_str = &**kind_str; | ||
63 | let kind = format_ident!("{}", kind_str); | ||
64 | let name = format_ident!("{}", to_pascal_case(kind_str)); | ||
65 | quote! { | ||
66 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
67 | pub struct #name { | ||
68 | pub(crate) syntax: SyntaxToken, | ||
69 | } | ||
70 | |||
71 | impl std::fmt::Display for #name { | ||
72 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
73 | std::fmt::Display::fmt(&self.syntax, f) | ||
74 | } | ||
75 | } | ||
76 | |||
77 | impl AstToken for #name { | ||
78 | fn can_cast(kind: SyntaxKind) -> bool { kind == #kind } | ||
79 | fn cast(syntax: SyntaxToken) -> Option<Self> { | ||
80 | if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } | ||
81 | } | ||
82 | fn syntax(&self) -> &SyntaxToken { &self.syntax } | ||
83 | } | ||
84 | } | ||
85 | }); | ||
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 | |||
28 | let nodes = grammar.nodes.iter().map(|node| { | 180 | let nodes = grammar.nodes.iter().map(|node| { |
29 | let name = format_ident!("{}", node.name); | 181 | let name = format_ident!("{}", node.name); |
30 | let kind = format_ident!("{}", to_upper_snake_case(&name.to_string())); | 182 | let kind = format_ident!("{}", to_upper_snake_case(&name.to_string())); |
@@ -42,20 +194,31 @@ fn generate_ast(grammar: AstSrc<'_>) -> Result<String> { | |||
42 | FieldSrc::Optional(ty) | FieldSrc::Many(ty) => ty, | 194 | FieldSrc::Optional(ty) | FieldSrc::Many(ty) => ty, |
43 | FieldSrc::Shorthand => name, | 195 | FieldSrc::Shorthand => name, |
44 | }; | 196 | }; |
197 | |||
45 | let ty = format_ident!("{}", ty); | 198 | let ty = format_ident!("{}", ty); |
46 | 199 | ||
47 | match field { | 200 | match field { |
48 | FieldSrc::Many(_) => { | 201 | FieldSrc::Many(_) => { |
49 | quote! { | 202 | quote! { |
50 | pub fn #method_name(&self) -> AstChildren<#ty> { | 203 | pub fn #method_name(&self) -> AstChildren<#ty> { |
51 | AstChildren::new(&self.syntax) | 204 | support::children(&self.syntax) |
52 | } | 205 | } |
53 | } | 206 | } |
54 | } | 207 | } |
55 | FieldSrc::Optional(_) | FieldSrc::Shorthand => { | 208 | FieldSrc::Optional(_) | FieldSrc::Shorthand => { |
56 | quote! { | 209 | let is_token = token_kinds.contains(&ty.to_string()); |
57 | pub fn #method_name(&self) -> Option<#ty> { | 210 | if is_token { |
58 | AstChildren::new(&self.syntax).next() | 211 | let method_name = format_ident!("{}_token", method_name); |
212 | quote! { | ||
213 | pub fn #method_name(&self) -> Option<#ty> { | ||
214 | support::token(&self.syntax) | ||
215 | } | ||
216 | } | ||
217 | } else { | ||
218 | quote! { | ||
219 | pub fn #method_name(&self) -> Option<#ty> { | ||
220 | support::child(&self.syntax) | ||
221 | } | ||
59 | } | 222 | } |
60 | } | 223 | } |
61 | } | 224 | } |
@@ -68,24 +231,16 @@ fn generate_ast(grammar: AstSrc<'_>) -> Result<String> { | |||
68 | pub(crate) syntax: SyntaxNode, | 231 | pub(crate) syntax: SyntaxNode, |
69 | } | 232 | } |
70 | 233 | ||
71 | impl std::fmt::Display for #name { | ||
72 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
73 | std::fmt::Display::fmt(self.syntax(), f) | ||
74 | } | ||
75 | } | ||
76 | |||
77 | impl AstNode for #name { | 234 | impl AstNode for #name { |
78 | fn can_cast(kind: SyntaxKind) -> bool { | 235 | fn can_cast(kind: SyntaxKind) -> bool { |
79 | match kind { | 236 | kind == #kind |
80 | #kind => true, | ||
81 | _ => false, | ||
82 | } | ||
83 | } | 237 | } |
84 | fn cast(syntax: SyntaxNode) -> Option<Self> { | 238 | fn cast(syntax: SyntaxNode) -> Option<Self> { |
85 | if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } | 239 | if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } |
86 | } | 240 | } |
87 | fn syntax(&self) -> &SyntaxNode { &self.syntax } | 241 | fn syntax(&self) -> &SyntaxNode { &self.syntax } |
88 | } | 242 | } |
243 | |||
89 | #(#traits)* | 244 | #(#traits)* |
90 | 245 | ||
91 | impl #name { | 246 | impl #name { |
@@ -120,12 +275,6 @@ fn generate_ast(grammar: AstSrc<'_>) -> Result<String> { | |||
120 | } | 275 | } |
121 | )* | 276 | )* |
122 | 277 | ||
123 | impl std::fmt::Display for #name { | ||
124 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
125 | std::fmt::Display::fmt(self.syntax(), f) | ||
126 | } | ||
127 | } | ||
128 | |||
129 | impl AstNode for #name { | 278 | impl AstNode for #name { |
130 | fn can_cast(kind: SyntaxKind) -> bool { | 279 | fn can_cast(kind: SyntaxKind) -> bool { |
131 | match kind { | 280 | match kind { |
@@ -150,18 +299,48 @@ fn generate_ast(grammar: AstSrc<'_>) -> Result<String> { | |||
150 | } | 299 | } |
151 | } | 300 | } |
152 | } | 301 | } |
302 | |||
153 | #(#traits)* | 303 | #(#traits)* |
154 | } | 304 | } |
155 | }); | 305 | }); |
156 | 306 | ||
307 | let displays = grammar | ||
308 | .enums | ||
309 | .iter() | ||
310 | .map(|it| format_ident!("{}", it.name)) | ||
311 | .chain(grammar.nodes.iter().map(|it| format_ident!("{}", it.name))) | ||
312 | .map(|name| { | ||
313 | quote! { | ||
314 | impl std::fmt::Display for #name { | ||
315 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
316 | std::fmt::Display::fmt(self.syntax(), f) | ||
317 | } | ||
318 | } | ||
319 | } | ||
320 | }); | ||
321 | |||
322 | let defined_nodes: HashSet<_> = grammar.nodes.iter().map(|node| node.name).collect(); | ||
323 | |||
324 | for node in kinds | ||
325 | .nodes | ||
326 | .iter() | ||
327 | .map(|kind| to_pascal_case(*kind)) | ||
328 | .filter(|name| !defined_nodes.contains(&**name)) | ||
329 | { | ||
330 | eprintln!("Warning: node {} not defined in ast source", node); | ||
331 | } | ||
332 | |||
157 | let ast = quote! { | 333 | let ast = quote! { |
158 | use crate::{ | 334 | use crate::{ |
159 | SyntaxNode, SyntaxKind::{self, *}, | 335 | SyntaxNode, SyntaxKind::{self, *}, |
160 | ast::{self, AstNode, AstChildren}, | 336 | ast::{self, AstNode, AstChildren, support}, |
161 | }; | 337 | }; |
162 | 338 | ||
339 | use super::tokens::*; | ||
340 | |||
163 | #(#nodes)* | 341 | #(#nodes)* |
164 | #(#enums)* | 342 | #(#enums)* |
343 | #(#displays)* | ||
165 | }; | 344 | }; |
166 | 345 | ||
167 | let pretty = crate::reformat(ast)?; | 346 | let pretty = crate::reformat(ast)?; |
@@ -282,12 +461,12 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> Result<String> { | |||
282 | 461 | ||
283 | fn to_upper_snake_case(s: &str) -> String { | 462 | fn to_upper_snake_case(s: &str) -> String { |
284 | let mut buf = String::with_capacity(s.len()); | 463 | let mut buf = String::with_capacity(s.len()); |
285 | let mut prev_is_upper = None; | 464 | let mut prev = false; |
286 | for c in s.chars() { | 465 | for c in s.chars() { |
287 | if c.is_ascii_uppercase() && prev_is_upper == Some(false) { | 466 | if c.is_ascii_uppercase() && prev { |
288 | buf.push('_') | 467 | buf.push('_') |
289 | } | 468 | } |
290 | prev_is_upper = Some(c.is_ascii_uppercase()); | 469 | prev = true; |
291 | 470 | ||
292 | buf.push(c.to_ascii_uppercase()); | 471 | buf.push(c.to_ascii_uppercase()); |
293 | } | 472 | } |
@@ -296,14 +475,30 @@ fn to_upper_snake_case(s: &str) -> String { | |||
296 | 475 | ||
297 | fn to_lower_snake_case(s: &str) -> String { | 476 | fn to_lower_snake_case(s: &str) -> String { |
298 | let mut buf = String::with_capacity(s.len()); | 477 | let mut buf = String::with_capacity(s.len()); |
299 | let mut prev_is_upper = None; | 478 | let mut prev = false; |
300 | for c in s.chars() { | 479 | for c in s.chars() { |
301 | if c.is_ascii_uppercase() && prev_is_upper == Some(false) { | 480 | if c.is_ascii_uppercase() && prev { |
302 | buf.push('_') | 481 | buf.push('_') |
303 | } | 482 | } |
304 | prev_is_upper = Some(c.is_ascii_uppercase()); | 483 | prev = true; |
305 | 484 | ||
306 | buf.push(c.to_ascii_lowercase()); | 485 | buf.push(c.to_ascii_lowercase()); |
307 | } | 486 | } |
308 | buf | 487 | buf |
309 | } | 488 | } |
489 | |||
490 | fn to_pascal_case(s: &str) -> String { | ||
491 | let mut buf = String::with_capacity(s.len()); | ||
492 | let mut prev_is_underscore = true; | ||
493 | for c in s.chars() { | ||
494 | if c == '_' { | ||
495 | prev_is_underscore = true; | ||
496 | } else if prev_is_underscore { | ||
497 | buf.push(c.to_ascii_uppercase()); | ||
498 | prev_is_underscore = false; | ||
499 | } else { | ||
500 | buf.push(c.to_ascii_lowercase()); | ||
501 | } | ||
502 | } | ||
503 | buf | ||
504 | } | ||