remove ast::*Kind enums

With the new owned trees, we don't need an indirection here
This commit is contained in:
Aleksey Kladov 2019-08-19 13:58:49 +03:00
parent ba2836245b
commit 39e444d701
3 changed files with 398 additions and 391 deletions

File diff suppressed because it is too large Load diff

View file

@ -186,8 +186,8 @@ fn api_walkthrough() {
// Let's fetch the `foo` function. // Let's fetch the `foo` function.
let mut func = None; let mut func = None;
for item in file.items() { for item in file.items() {
match item.kind() { match item {
ast::ModuleItemKind::FnDef(f) => func = Some(f), ast::ModuleItem::FnDef(f) => func = Some(f),
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -206,12 +206,12 @@ fn api_walkthrough() {
let block: ast::Block = func.body().unwrap(); let block: ast::Block = func.body().unwrap();
let expr: ast::Expr = block.expr().unwrap(); let expr: ast::Expr = block.expr().unwrap();
// "Enum"-like nodes are represented using the "kind" pattern. It allows us // Enums are used to group related ast nodes together, and can be used for
// to match exhaustively against all flavors of nodes, while maintaining // matching. However, because there are no public fields, it's possible to
// internal representation flexibility. The drawback is that one can't write // match only the top level enum: that is the price we pay for increased API
// nested matches as one pattern. // flexibility
let bin_expr: ast::BinExpr = match expr.kind() { let bin_expr: &ast::BinExpr = match &expr {
ast::ExprKind::BinExpr(e) => e, ast::Expr::BinExpr(e) => e,
_ => unreachable!(), _ => unreachable!(),
}; };

View file

@ -37,41 +37,72 @@ fn generate_ast(grammar: &Grammar) -> Result<String> {
ast_node.variants.iter().map(|var| format_ident!("{}", var)).collect::<Vec<_>>(); ast_node.variants.iter().map(|var| format_ident!("{}", var)).collect::<Vec<_>>();
let name = format_ident!("{}", name); let name = format_ident!("{}", name);
let kinds = if variants.is_empty() { vec![name.clone()] } else { variants.clone() } let adt = if variants.is_empty() {
.into_iter() let kind = format_ident!("{}", name.to_string().to_shouty_snake_case());
.map(|name| format_ident!("{}", name.to_string().to_shouty_snake_case())) quote! {
.collect::<Vec<_>>(); #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct #name {
pub(crate) syntax: SyntaxNode,
}
let variants = if variants.is_empty() { impl AstNode for #name {
None fn can_cast(kind: SyntaxKind) -> bool {
match kind {
#kind => true,
_ => false,
}
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
}
} else { } else {
let kind_enum = format_ident!("{}Kind", name); let kinds = variants
Some(quote!( .iter()
pub enum #kind_enum { .map(|name| format_ident!("{}", name.to_string().to_shouty_snake_case()))
.collect::<Vec<_>>();
quote! {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum #name {
#(#variants(#variants),)* #(#variants(#variants),)*
} }
#( #(
impl From<#variants> for #name { impl From<#variants> for #name {
fn from(node: #variants) -> #name { fn from(node: #variants) -> #name {
#name { syntax: node.syntax } #name::#variants(node)
} }
} }
)* )*
impl #name { impl AstNode for #name {
pub fn kind(&self) -> #kind_enum { fn can_cast(kind: SyntaxKind) -> bool {
let syntax = self.syntax.clone(); match kind {
match syntax.kind() { #(#kinds)|* => true,
_ => false,
}
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
let res = match syntax.kind() {
#( #(
#kinds => #kinds => #name::#variants(#variants { syntax }),
#kind_enum::#variants(#variants { syntax }), )*
_ => return None,
};
Some(res)
}
fn syntax(&self) -> &SyntaxNode {
match self {
#(
#name::#variants(it) => &it.syntax,
)* )*
_ => unreachable!(),
} }
} }
} }
)) }
}; };
let traits = ast_node.traits.iter().map(|trait_name| { let traits = ast_node.traits.iter().map(|trait_name| {
@ -105,25 +136,7 @@ fn generate_ast(grammar: &Grammar) -> Result<String> {
}); });
quote! { quote! {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #adt
pub struct #name {
pub(crate) syntax: SyntaxNode,
}
impl AstNode for #name {
fn can_cast(kind: SyntaxKind) -> bool {
match kind {
#(#kinds)|* => true,
_ => false,
}
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
#variants
#(#traits)* #(#traits)*