aboutsummaryrefslogtreecommitdiff
path: root/xtask/src/codegen/gen_syntax.rs
diff options
context:
space:
mode:
Diffstat (limited to 'xtask/src/codegen/gen_syntax.rs')
-rw-r--r--xtask/src/codegen/gen_syntax.rs287
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
6use std::{ 6use std::collections::HashSet;
7 borrow::Cow,
8 collections::{BTreeSet, HashSet},
9};
10 7
11use proc_macro2::{Punct, Spacing}; 8use proc_macro2::{Punct, Spacing};
12use quote::{format_ident, quote}; 9use quote::{format_ident, quote};
13 10
14use crate::{ 11use 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)] 33fn generate_tokens(grammar: AstSrc<'_>) -> Result<String> {
37struct 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
43fn 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
155fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { 65fn 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
381impl 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}