diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-04-10 16:07:36 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-04-10 16:07:36 +0100 |
commit | fcd609862b8d5b0567bb74895ef75b02912a375d (patch) | |
tree | 4bc012bfc0c2428bc4dee692a1ea2388152a4269 /xtask/src/codegen/gen_syntax.rs | |
parent | 38e0d0f3348900bb4078deb09d48c5b6483d43d6 (diff) | |
parent | 5c5bde47fb759440d007c90fd83021de538120b8 (diff) |
Merge #3926
3926: Rename some tokens r=matklad a=matklad
bors r+
🤖
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'xtask/src/codegen/gen_syntax.rs')
-rw-r--r-- | xtask/src/codegen/gen_syntax.rs | 287 |
1 files changed, 105 insertions, 182 deletions
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs index cc98802f6..ec1f6ad8a 100644 --- a/xtask/src/codegen/gen_syntax.rs +++ b/xtask/src/codegen/gen_syntax.rs | |||
@@ -3,16 +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::{ | 6 | use std::collections::HashSet; |
7 | borrow::Cow, | ||
8 | collections::{BTreeSet, HashSet}, | ||
9 | }; | ||
10 | 7 | ||
11 | use proc_macro2::{Punct, Spacing}; | 8 | use proc_macro2::{Punct, Spacing}; |
12 | use quote::{format_ident, quote}; | 9 | use quote::{format_ident, quote}; |
13 | 10 | ||
14 | use crate::{ | 11 | use crate::{ |
15 | ast_src::{AstSrc, FieldSrc, KindsSrc, AST_SRC, KINDS_SRC}, | 12 | ast_src::{AstSrc, Field, FieldSrc, KindsSrc, AST_SRC, KINDS_SRC}, |
16 | codegen::{self, update, Mode}, | 13 | codegen::{self, update, Mode}, |
17 | project_root, Result, | 14 | project_root, Result, |
18 | }; | 15 | }; |
@@ -22,61 +19,31 @@ pub fn generate_syntax(mode: Mode) -> Result<()> { | |||
22 | let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?; | 19 | let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?; |
23 | update(syntax_kinds_file.as_path(), &syntax_kinds, mode)?; | 20 | update(syntax_kinds_file.as_path(), &syntax_kinds, mode)?; |
24 | 21 | ||
22 | let ast_tokens_file = project_root().join(codegen::AST_TOKENS); | ||
23 | let contents = generate_tokens(AST_SRC)?; | ||
24 | update(ast_tokens_file.as_path(), &contents, mode)?; | ||
25 | |||
25 | let ast_nodes_file = project_root().join(codegen::AST_NODES); | 26 | let ast_nodes_file = project_root().join(codegen::AST_NODES); |
26 | let contents = generate_nodes(KINDS_SRC, AST_SRC)?; | 27 | let contents = generate_nodes(KINDS_SRC, AST_SRC)?; |
27 | update(ast_nodes_file.as_path(), &contents, mode)?; | 28 | update(ast_nodes_file.as_path(), &contents, mode)?; |
28 | 29 | ||
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)?; | ||
32 | |||
33 | Ok(()) | 30 | Ok(()) |
34 | } | 31 | } |
35 | 32 | ||
36 | #[derive(Debug, Default, Clone)] | 33 | fn generate_tokens(grammar: AstSrc<'_>) -> Result<String> { |
37 | struct ElementKinds { | 34 | let tokens = grammar.tokens.iter().map(|token| { |
38 | kinds: BTreeSet<proc_macro2::Ident>, | 35 | let name = format_ident!("{}", token); |
39 | has_nodes: bool, | 36 | let kind = format_ident!("{}", to_upper_snake_case(token)); |
40 | has_tokens: bool, | 37 | quote! { |
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().filter_map(|kind_str| { | ||
62 | if kind_str.ends_with("_KW") { | ||
63 | return None; | ||
64 | } | ||
65 | let kind_str = &**kind_str; | ||
66 | let kind = format_ident!("{}", kind_str); | ||
67 | let name = format_ident!("{}", to_pascal_case(kind_str)); | ||
68 | let res = quote! { | ||
69 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 38 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
70 | pub struct #name { | 39 | pub struct #name { |
71 | pub(crate) syntax: SyntaxToken, | 40 | pub(crate) syntax: SyntaxToken, |
72 | } | 41 | } |
73 | |||
74 | impl std::fmt::Display for #name { | 42 | impl std::fmt::Display for #name { |
75 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | 43 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
76 | std::fmt::Display::fmt(&self.syntax, f) | 44 | std::fmt::Display::fmt(&self.syntax, f) |
77 | } | 45 | } |
78 | } | 46 | } |
79 | |||
80 | impl AstToken for #name { | 47 | impl AstToken for #name { |
81 | fn can_cast(kind: SyntaxKind) -> bool { kind == #kind } | 48 | fn can_cast(kind: SyntaxKind) -> bool { kind == #kind } |
82 | fn cast(syntax: SyntaxToken) -> Option<Self> { | 49 | fn cast(syntax: SyntaxToken) -> Option<Self> { |
@@ -84,103 +51,18 @@ fn generate_tokens(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
84 | } | 51 | } |
85 | fn syntax(&self) -> &SyntaxToken { &self.syntax } | 52 | fn syntax(&self) -> &SyntaxToken { &self.syntax } |
86 | } | 53 | } |
87 | }; | ||
88 | Some(res) | ||
89 | }); | ||
90 | |||
91 | let enums = grammar.token_enums.iter().map(|en| { | ||
92 | let variants = en.variants.iter().map(|var| format_ident!("{}", var)).collect::<Vec<_>>(); | ||
93 | let name = format_ident!("{}", en.name); | ||
94 | let kinds = variants | ||
95 | .iter() | ||
96 | .map(|name| format_ident!("{}", to_upper_snake_case(&name.to_string()))) | ||
97 | .collect::<Vec<_>>(); | ||
98 | assert!(en.traits.is_empty()); | ||
99 | |||
100 | quote! { | ||
101 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
102 | pub enum #name { | ||
103 | #(#variants(#variants),)* | ||
104 | } | ||
105 | |||
106 | #( | ||
107 | impl From<#variants> for #name { | ||
108 | fn from(node: #variants) -> #name { | ||
109 | #name::#variants(node) | ||
110 | } | ||
111 | } | ||
112 | )* | ||
113 | |||
114 | impl std::fmt::Display for #name { | ||
115 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
116 | std::fmt::Display::fmt(self.syntax(), f) | ||
117 | } | ||
118 | } | ||
119 | |||
120 | impl AstToken for #name { | ||
121 | fn can_cast(kind: SyntaxKind) -> bool { | ||
122 | match kind { | ||
123 | #(#kinds)|* => true, | ||
124 | _ => false, | ||
125 | } | ||
126 | } | ||
127 | fn cast(syntax: SyntaxToken) -> Option<Self> { | ||
128 | let res = match syntax.kind() { | ||
129 | #( | ||
130 | #kinds => #name::#variants(#variants { syntax }), | ||
131 | )* | ||
132 | _ => return None, | ||
133 | }; | ||
134 | Some(res) | ||
135 | } | ||
136 | fn syntax(&self) -> &SyntaxToken { | ||
137 | match self { | ||
138 | #( | ||
139 | #name::#variants(it) => &it.syntax, | ||
140 | )* | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | } | 54 | } |
145 | }); | 55 | }); |
146 | 56 | ||
147 | crate::reformat(quote! { | 57 | let pretty = crate::reformat(quote! { |
148 | use crate::{SyntaxToken, SyntaxKind::{self, *}, ast::AstToken}; | 58 | use crate::{SyntaxKind::{self, *}, SyntaxToken, ast::AstToken}; |
149 | |||
150 | #(#tokens)* | 59 | #(#tokens)* |
151 | #(#enums)* | 60 | })? |
152 | }) | 61 | .replace("#[derive", "\n#[derive"); |
62 | Ok(pretty) | ||
153 | } | 63 | } |
154 | 64 | ||
155 | fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | 65 | fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { |
156 | let all_token_kinds: Vec<_> = kinds | ||
157 | .punct | ||
158 | .into_iter() | ||
159 | .map(|(_, kind)| kind) | ||
160 | .copied() | ||
161 | .map(|x| x.into()) | ||
162 | .chain( | ||
163 | kinds | ||
164 | .keywords | ||
165 | .into_iter() | ||
166 | .chain(kinds.contextual_keywords.into_iter()) | ||
167 | .map(|name| Cow::Owned(format!("{}_KW", to_upper_snake_case(&name)))), | ||
168 | ) | ||
169 | .chain(kinds.literals.into_iter().copied().map(|x| x.into())) | ||
170 | .chain(kinds.tokens.into_iter().copied().map(|x| x.into())) | ||
171 | .collect(); | ||
172 | |||
173 | let mut token_kinds = HashSet::new(); | ||
174 | for kind in &all_token_kinds { | ||
175 | let kind = &**kind; | ||
176 | let name = to_pascal_case(kind); | ||
177 | token_kinds.insert(name); | ||
178 | } | ||
179 | |||
180 | for en in grammar.token_enums { | ||
181 | token_kinds.insert(en.name.to_string()); | ||
182 | } | ||
183 | |||
184 | let nodes = grammar.nodes.iter().map(|node| { | 66 | let nodes = grammar.nodes.iter().map(|node| { |
185 | let name = format_ident!("{}", node.name); | 67 | let name = format_ident!("{}", node.name); |
186 | let kind = format_ident!("{}", to_upper_snake_case(&name.to_string())); | 68 | let kind = format_ident!("{}", to_upper_snake_case(&name.to_string())); |
@@ -189,53 +71,27 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
189 | quote!(impl ast::#trait_name for #name {}) | 71 | quote!(impl ast::#trait_name for #name {}) |
190 | }); | 72 | }); |
191 | 73 | ||
192 | let methods = node.fields.iter().map(|(name, field)| { | 74 | let methods = node.fields.iter().map(|field| { |
193 | let is_kw = name.ends_with("Kw"); | 75 | let method_name = field.method_name(); |
194 | let method_name = match field { | 76 | let ty = field.ty(); |
195 | FieldSrc::Shorthand => { | ||
196 | let name = if is_kw { &name[..name.len() - 2] } else { &name }; | ||
197 | format_ident!("{}", to_lower_snake_case(name)) | ||
198 | } | ||
199 | _ => format_ident!("{}", name), | ||
200 | }; | ||
201 | let ty = match field { | ||
202 | FieldSrc::Optional(ty) | FieldSrc::Many(ty) => ty, | ||
203 | FieldSrc::Shorthand => name, | ||
204 | }; | ||
205 | 77 | ||
206 | let ty = format_ident!("{}", ty); | 78 | if field.is_many() { |
207 | 79 | quote! { | |
208 | match field { | 80 | pub fn #method_name(&self) -> AstChildren<#ty> { |
209 | FieldSrc::Many(_) => { | 81 | support::children(&self.syntax) |
210 | quote! { | ||
211 | pub fn #method_name(&self) -> AstChildren<#ty> { | ||
212 | support::children(&self.syntax) | ||
213 | } | ||
214 | } | 82 | } |
215 | } | 83 | } |
216 | FieldSrc::Optional(_) | FieldSrc::Shorthand => { | 84 | } else { |
217 | let is_token = token_kinds.contains(&ty.to_string()); | 85 | if let Some(token_kind) = field.token_kind() { |
218 | if is_token { | 86 | quote! { |
219 | let method_name = format_ident!("{}_token", method_name); | 87 | pub fn #method_name(&self) -> Option<#ty> { |
220 | if is_kw { | 88 | support::token(&self.syntax, #token_kind) |
221 | let token_kind = format_ident!("{}", to_upper_snake_case(name)); | ||
222 | quote! { | ||
223 | pub fn #method_name(&self) -> Option<SyntaxToken> { | ||
224 | support::token2(&self.syntax, #token_kind) | ||
225 | } | ||
226 | } | ||
227 | } else { | ||
228 | quote! { | ||
229 | pub fn #method_name(&self) -> Option<#ty> { | ||
230 | support::token(&self.syntax) | ||
231 | } | ||
232 | } | ||
233 | } | 89 | } |
234 | } else { | 90 | } |
235 | quote! { | 91 | } else { |
236 | pub fn #method_name(&self) -> Option<#ty> { | 92 | quote! { |
237 | support::child(&self.syntax) | 93 | pub fn #method_name(&self) -> Option<#ty> { |
238 | } | 94 | support::child(&self.syntax) |
239 | } | 95 | } |
240 | } | 96 | } |
241 | } | 97 | } |
@@ -351,16 +207,16 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
351 | use crate::{ | 207 | use crate::{ |
352 | SyntaxNode, SyntaxToken, SyntaxKind::{self, *}, | 208 | SyntaxNode, SyntaxToken, SyntaxKind::{self, *}, |
353 | ast::{self, AstNode, AstChildren, support}, | 209 | ast::{self, AstNode, AstChildren, support}, |
210 | T, | ||
354 | }; | 211 | }; |
355 | 212 | ||
356 | use super::tokens::*; | ||
357 | |||
358 | #(#nodes)* | 213 | #(#nodes)* |
359 | #(#enums)* | 214 | #(#enums)* |
360 | #(#displays)* | 215 | #(#displays)* |
361 | }; | 216 | }; |
362 | 217 | ||
363 | let pretty = crate::reformat(ast)?; | 218 | let ast = ast.to_string().replace("T ! [ ", "T![").replace(" ] )", "])"); |
219 | let pretty = crate::reformat(ast)?.replace("#[derive", "\n#[derive"); | ||
364 | Ok(pretty) | 220 | Ok(pretty) |
365 | } | 221 | } |
366 | 222 | ||
@@ -468,8 +324,10 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> Result<String> { | |||
468 | 324 | ||
469 | #[macro_export] | 325 | #[macro_export] |
470 | macro_rules! T { | 326 | macro_rules! T { |
471 | #((#punctuation_values) => { $crate::SyntaxKind::#punctuation };)* | 327 | #([#punctuation_values] => { $crate::SyntaxKind::#punctuation };)* |
472 | #((#all_keywords_idents) => { $crate::SyntaxKind::#all_keywords };)* | 328 | #([#all_keywords_idents] => { $crate::SyntaxKind::#all_keywords };)* |
329 | [lifetime] => { $crate::SyntaxKind::LIFETIME }; | ||
330 | [ident] => { $crate::SyntaxKind::IDENT }; | ||
473 | } | 331 | } |
474 | }; | 332 | }; |
475 | 333 | ||
@@ -519,3 +377,68 @@ fn to_pascal_case(s: &str) -> String { | |||
519 | } | 377 | } |
520 | buf | 378 | buf |
521 | } | 379 | } |
380 | |||
381 | impl Field<'_> { | ||
382 | fn is_many(&self) -> bool { | ||
383 | match self { | ||
384 | Field::Node { src: FieldSrc::Many(_), .. } => true, | ||
385 | _ => false, | ||
386 | } | ||
387 | } | ||
388 | fn token_kind(&self) -> Option<proc_macro2::TokenStream> { | ||
389 | let res = match self { | ||
390 | Field::Token(token) => { | ||
391 | let token: proc_macro2::TokenStream = token.parse().unwrap(); | ||
392 | quote! { T![#token] } | ||
393 | } | ||
394 | _ => return None, | ||
395 | }; | ||
396 | Some(res) | ||
397 | } | ||
398 | fn method_name(&self) -> proc_macro2::Ident { | ||
399 | match self { | ||
400 | Field::Token(name) => { | ||
401 | let name = match *name { | ||
402 | ";" => "semicolon", | ||
403 | "->" => "thin_arrow", | ||
404 | "'{'" => "l_curly", | ||
405 | "'}'" => "r_curly", | ||
406 | "'('" => "l_paren", | ||
407 | "')'" => "r_paren", | ||
408 | "'['" => "l_brack", | ||
409 | "']'" => "r_brack", | ||
410 | "<" => "l_angle", | ||
411 | ">" => "r_angle", | ||
412 | "=" => "eq", | ||
413 | "!" => "excl", | ||
414 | "*" => "star", | ||
415 | "&" => "amp", | ||
416 | "_" => "underscore", | ||
417 | "." => "dot", | ||
418 | ".." => "dotdot", | ||
419 | "..." => "dotdotdot", | ||
420 | "=>" => "fat_arrow", | ||
421 | "@" => "at", | ||
422 | ":" => "colon", | ||
423 | "::" => "coloncolon", | ||
424 | "#" => "pound", | ||
425 | _ => name, | ||
426 | }; | ||
427 | format_ident!("{}_token", name) | ||
428 | } | ||
429 | Field::Node { name, src } => match src { | ||
430 | FieldSrc::Shorthand => format_ident!("{}", to_lower_snake_case(name)), | ||
431 | _ => format_ident!("{}", name), | ||
432 | }, | ||
433 | } | ||
434 | } | ||
435 | fn ty(&self) -> proc_macro2::Ident { | ||
436 | match self { | ||
437 | Field::Token(_) => format_ident!("SyntaxToken"), | ||
438 | Field::Node { name, src } => match src { | ||
439 | FieldSrc::Optional(ty) | FieldSrc::Many(ty) => format_ident!("{}", ty), | ||
440 | FieldSrc::Shorthand => format_ident!("{}", name), | ||
441 | }, | ||
442 | } | ||
443 | } | ||
444 | } | ||