From 972d3b2ba30ec40bebe85452117e669536faa167 Mon Sep 17 00:00:00 2001 From: veetaha Date: Sat, 18 Apr 2020 23:51:13 +0300 Subject: Group generated ast boilerplate apart from the interesting part --- xtask/src/codegen/gen_syntax.rs | 250 +++++++++++++++++++++------------------- 1 file changed, 130 insertions(+), 120 deletions(-) (limited to 'xtask/src/codegen') 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 { } fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result { - let nodes = grammar.nodes.iter().map(|node| { - let name = format_ident!("{}", node.name); - let kind = format_ident!("{}", to_upper_snake_case(&name.to_string())); - let traits = node.traits.iter().map(|trait_name| { - let trait_name = format_ident!("{}", trait_name); - quote!(impl ast::#trait_name for #name {}) - }); - - let methods = node.fields.iter().map(|field| { - let method_name = field.method_name(); - let ty = field.ty(); - - if field.is_many() { - quote! { - pub fn #method_name(&self) -> AstChildren<#ty> { - support::children(&self.syntax) - } - } - } else { - if let Some(token_kind) = field.token_kind() { + let (node_defs, node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar + .nodes + .iter() + .map(|node| { + let name = format_ident!("{}", node.name); + let kind = format_ident!("{}", to_upper_snake_case(&name.to_string())); + let traits = node.traits.iter().map(|trait_name| { + let trait_name = format_ident!("{}", trait_name); + quote!(impl ast::#trait_name for #name {}) + }); + + let methods = node.fields.iter().map(|field| { + let method_name = field.method_name(); + let ty = field.ty(); + + if field.is_many() { quote! { - pub fn #method_name(&self) -> Option<#ty> { - support::token(&self.syntax, #token_kind) + pub fn #method_name(&self) -> AstChildren<#ty> { + support::children(&self.syntax) } } } else { - quote! { - pub fn #method_name(&self) -> Option<#ty> { - support::child(&self.syntax) + if let Some(token_kind) = field.token_kind() { + quote! { + pub fn #method_name(&self) -> Option<#ty> { + support::token(&self.syntax, #token_kind) + } + } + } else { + quote! { + pub fn #method_name(&self) -> Option<#ty> { + support::child(&self.syntax) + } } } } - } - }); - - quote! { - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct #name { - pub(crate) syntax: SyntaxNode, - } - - impl AstNode for #name { - fn can_cast(kind: SyntaxKind) -> bool { - kind == #kind - } - fn cast(syntax: SyntaxNode) -> Option { - if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } - } - fn syntax(&self) -> &SyntaxNode { &self.syntax } - } - - #(#traits)* - - impl #name { - #(#methods)* - } - } - }); + }); + ( + quote! { + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct #name { + pub(crate) syntax: SyntaxNode, + } - let enums = grammar.enums.iter().map(|en| { - let variants = en.variants.iter().map(|var| format_ident!("{}", var)).collect::>(); - let name = format_ident!("{}", en.name); - let kinds = variants - .iter() - .map(|name| format_ident!("{}", to_upper_snake_case(&name.to_string()))) - .collect::>(); - let traits = en.traits.iter().map(|trait_name| { - let trait_name = format_ident!("{}", trait_name); - quote!(impl ast::#trait_name for #name {}) - }); + #(#traits)* - quote! { - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub enum #name { - #(#variants(#variants),)* - } + impl #name { + #(#methods)* + } + }, + quote! { + impl AstNode for #name { + fn can_cast(kind: SyntaxKind) -> bool { + kind == #kind + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } + } + }, + ) + }) + .unzip(); - #( - impl From<#variants> for #name { - fn from(node: #variants) -> #name { - #name::#variants(node) - } - } - )* + let (enum_defs, enum_boilerplate_impls): (Vec<_>, Vec<_>) = grammar + .enums + .iter() + .map(|en| { + let variants: Vec<_> = en.variants.iter().map(|var| format_ident!("{}", var)).collect(); + let name = format_ident!("{}", en.name); + let kinds: Vec<_> = variants + .iter() + .map(|name| format_ident!("{}", to_upper_snake_case(&name.to_string()))) + .collect(); + let traits = en.traits.iter().map(|trait_name| { + let trait_name = format_ident!("{}", trait_name); + quote!(impl ast::#trait_name for #name {}) + }); + + ( + quote! { + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub enum #name { + #(#variants(#variants),)* + } - impl AstNode for #name { - fn can_cast(kind: SyntaxKind) -> bool { - match kind { - #(#kinds)|* => true, - _ => false, + #(#traits)* + }, + quote! { + #( + impl From<#variants> for #name { + fn from(node: #variants) -> #name { + #name::#variants(node) + } } - } - fn cast(syntax: SyntaxNode) -> Option { - let res = match syntax.kind() { - #( - #kinds => #name::#variants(#variants { syntax }), - )* - _ => return None, - }; - Some(res) - } - fn syntax(&self) -> &SyntaxNode { - match self { - #( - #name::#variants(it) => &it.syntax, - )* + )* + + impl AstNode for #name { + fn can_cast(kind: SyntaxKind) -> bool { + match kind { + #(#kinds)|* => true, + _ => false, + } + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + #( + #kinds => #name::#variants(#variants { syntax }), + )* + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + #( + #name::#variants(it) => &it.syntax, + )* + } + } } - } - } + }, + ) + }) + .unzip(); - #(#traits)* - } - }); + let enum_names = grammar.enums.iter().map(|it| it.name); + let node_names = grammar.nodes.iter().map(|it| it.name); - let displays = grammar - .enums - .iter() - .map(|it| format_ident!("{}", it.name)) - .chain(grammar.nodes.iter().map(|it| format_ident!("{}", it.name))) - .map(|name| { + let display_impls = + enum_names.chain(node_names.clone()).map(|it| format_ident!("{}", it)).map(|name| { quote! { impl std::fmt::Display for #name { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -192,13 +204,13 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result { } }); - let defined_nodes: HashSet<_> = grammar.nodes.iter().map(|node| node.name).collect(); + let defined_nodes: HashSet<_> = node_names.collect(); for node in kinds .nodes .iter() - .map(|kind| to_pascal_case(*kind)) - .filter(|name| !defined_nodes.contains(&**name)) + .map(|kind| to_pascal_case(kind)) + .filter(|name| !defined_nodes.contains(name.as_str())) { eprintln!("Warning: node {} not defined in ast source", node); } @@ -210,9 +222,11 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result { T, }; - #(#nodes)* - #(#enums)* - #(#displays)* + #(#node_defs)* + #(#enum_defs)* + #(#node_boilerplate_impls)* + #(#enum_boilerplate_impls)* + #(#display_impls)* }; let ast = ast.to_string().replace("T ! [ ", "T![").replace(" ] )", "])"); @@ -380,20 +394,16 @@ fn to_pascal_case(s: &str) -> String { impl Field<'_> { fn is_many(&self) -> bool { - match self { - Field::Node { src: FieldSrc::Many(_), .. } => true, - _ => false, - } + matches!(self, Field::Node { src: FieldSrc::Many(_), .. }) } fn token_kind(&self) -> Option { - let res = match self { + match self { Field::Token(token) => { let token: proc_macro2::TokenStream = token.parse().unwrap(); - quote! { T![#token] } + Some(quote! { T![#token] }) } - _ => return None, - }; - Some(res) + _ => None, + } } fn method_name(&self) -> proc_macro2::Ident { match self { -- cgit v1.2.3