aboutsummaryrefslogtreecommitdiff
path: root/xtask
diff options
context:
space:
mode:
Diffstat (limited to 'xtask')
-rw-r--r--xtask/src/codegen.rs12
-rw-r--r--xtask/src/codegen/gen_syntax.rs250
2 files changed, 136 insertions, 126 deletions
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index 678b40133..0e4dcb95a 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -9,9 +9,9 @@ mod gen_syntax;
9mod gen_parser_tests; 9mod gen_parser_tests;
10mod gen_assists_docs; 10mod gen_assists_docs;
11 11
12use std::{fs, mem, path::Path}; 12use std::{mem, path::Path};
13 13
14use crate::Result; 14use crate::{not_bash::fs2, Result};
15 15
16pub use self::{ 16pub use self::{
17 gen_assists_docs::generate_assists_docs, gen_parser_tests::generate_parser_tests, 17 gen_assists_docs::generate_assists_docs, gen_parser_tests::generate_parser_tests,
@@ -39,7 +39,7 @@ pub enum Mode {
39/// A helper to update file on disk if it has changed. 39/// A helper to update file on disk if it has changed.
40/// With verify = false, 40/// With verify = false,
41fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { 41fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
42 match fs::read_to_string(path) { 42 match fs2::read_to_string(path) {
43 Ok(ref old_contents) if normalize(old_contents) == normalize(contents) => { 43 Ok(ref old_contents) if normalize(old_contents) == normalize(contents) => {
44 return Ok(()); 44 return Ok(());
45 } 45 }
@@ -49,7 +49,7 @@ fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
49 anyhow::bail!("`{}` is not up-to-date", path.display()); 49 anyhow::bail!("`{}` is not up-to-date", path.display());
50 } 50 }
51 eprintln!("updating {}", path.display()); 51 eprintln!("updating {}", path.display());
52 fs::write(path, contents)?; 52 fs2::write(path, contents)?;
53 return Ok(()); 53 return Ok(());
54 54
55 fn normalize(s: &str) -> String { 55 fn normalize(s: &str) -> String {
@@ -65,7 +65,7 @@ fn extract_comment_blocks_with_empty_lines(text: &str) -> Vec<Vec<String>> {
65 do_extract_comment_blocks(text, true) 65 do_extract_comment_blocks(text, true)
66} 66}
67 67
68fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lins: bool) -> Vec<Vec<String>> { 68fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lines: bool) -> Vec<Vec<String>> {
69 let mut res = Vec::new(); 69 let mut res = Vec::new();
70 70
71 let prefix = "// "; 71 let prefix = "// ";
@@ -73,7 +73,7 @@ fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lins: bool) ->
73 73
74 let mut block = vec![]; 74 let mut block = vec![];
75 for line in lines { 75 for line in lines {
76 if line == "//" && allow_blocks_with_empty_lins { 76 if line == "//" && allow_blocks_with_empty_lines {
77 block.push(String::new()); 77 block.push(String::new());
78 continue; 78 continue;
79 } 79 }
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
65fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { 65fn 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
381impl Field<'_> { 395impl 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 {