Scale token generation back

This commit is contained in:
Aleksey Kladov 2020-04-10 11:49:13 +02:00
parent 779f06ed77
commit 8d71a6bf0c
9 changed files with 136 additions and 547 deletions

View file

@ -3,10 +3,7 @@
//! Specifically, it generates the `SyntaxKind` enum and a number of newtype
//! wrappers around `SyntaxNode` which implement `ra_syntax::AstNode`.
use std::{
borrow::Cow,
collections::{BTreeSet, HashSet},
};
use std::collections::{BTreeSet, HashSet};
use proc_macro2::{Punct, Spacing};
use quote::{format_ident, quote};
@ -26,10 +23,6 @@ pub fn generate_syntax(mode: Mode) -> Result<()> {
let contents = generate_nodes(KINDS_SRC, AST_SRC)?;
update(ast_nodes_file.as_path(), &contents, mode)?;
let ast_tokens_file = project_root().join(codegen::AST_TOKENS);
let contents = generate_tokens(KINDS_SRC, AST_SRC)?;
update(ast_tokens_file.as_path(), &contents, mode)?;
Ok(())
}
@ -40,147 +33,7 @@ struct ElementKinds {
has_tokens: bool,
}
fn generate_tokens(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
let all_token_kinds: Vec<_> = kinds
.punct
.into_iter()
.map(|(_, kind)| kind)
.copied()
.map(|x| x.into())
.chain(
kinds
.keywords
.into_iter()
.chain(kinds.contextual_keywords.into_iter())
.map(|name| Cow::Owned(format!("{}_KW", to_upper_snake_case(&name)))),
)
.chain(kinds.literals.into_iter().copied().map(|x| x.into()))
.chain(kinds.tokens.into_iter().copied().map(|x| x.into()))
.collect();
let tokens = all_token_kinds.iter().filter_map(|kind_str| {
if kind_str.ends_with("_KW") {
return None;
}
let kind_str = &**kind_str;
let kind = format_ident!("{}", kind_str);
let name = format_ident!("{}", to_pascal_case(kind_str));
let res = quote! {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct #name {
pub(crate) syntax: SyntaxToken,
}
impl std::fmt::Display for #name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&self.syntax, f)
}
}
impl AstToken for #name {
fn can_cast(kind: SyntaxKind) -> bool { kind == #kind }
fn cast(syntax: SyntaxToken) -> Option<Self> {
if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
}
fn syntax(&self) -> &SyntaxToken { &self.syntax }
}
};
Some(res)
});
let enums = grammar.token_enums.iter().map(|en| {
let variants = en.variants.iter().map(|var| format_ident!("{}", var)).collect::<Vec<_>>();
let name = format_ident!("{}", en.name);
let kinds = variants
.iter()
.map(|name| format_ident!("{}", to_upper_snake_case(&name.to_string())))
.collect::<Vec<_>>();
assert!(en.traits.is_empty());
quote! {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum #name {
#(#variants(#variants),)*
}
#(
impl From<#variants> for #name {
fn from(node: #variants) -> #name {
#name::#variants(node)
}
}
)*
impl std::fmt::Display for #name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl AstToken for #name {
fn can_cast(kind: SyntaxKind) -> bool {
match kind {
#(#kinds)|* => true,
_ => false,
}
}
fn cast(syntax: SyntaxToken) -> Option<Self> {
let res = match syntax.kind() {
#(
#kinds => #name::#variants(#variants { syntax }),
)*
_ => return None,
};
Some(res)
}
fn syntax(&self) -> &SyntaxToken {
match self {
#(
#name::#variants(it) => &it.syntax,
)*
}
}
}
}
});
crate::reformat(quote! {
use crate::{SyntaxToken, SyntaxKind::{self, *}, ast::AstToken};
#(#tokens)*
#(#enums)*
})
}
fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
let all_token_kinds: Vec<_> = kinds
.punct
.into_iter()
.map(|(_, kind)| kind)
.copied()
.map(|x| x.into())
.chain(
kinds
.keywords
.into_iter()
.chain(kinds.contextual_keywords.into_iter())
.map(|name| Cow::Owned(format!("{}_KW", to_upper_snake_case(&name)))),
)
.chain(kinds.literals.into_iter().copied().map(|x| x.into()))
.chain(kinds.tokens.into_iter().copied().map(|x| x.into()))
.collect();
let mut token_kinds = HashSet::new();
for kind in &all_token_kinds {
let kind = &**kind;
let name = to_pascal_case(kind);
token_kinds.insert(name);
}
for en in grammar.token_enums {
token_kinds.insert(en.name.to_string());
}
let nodes = grammar.nodes.iter().map(|node| {
let name = format_ident!("{}", node.name);
let kind = format_ident!("{}", to_upper_snake_case(&name.to_string()));
@ -207,19 +60,9 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
}
}
} else {
let is_token = token_kinds.contains(&ty.to_string());
if is_token {
let method_name = format_ident!("{}_token", method_name);
quote! {
pub fn #method_name(&self) -> Option<#ty> {
support::token(&self.syntax)
}
}
} else {
quote! {
pub fn #method_name(&self) -> Option<#ty> {
support::child(&self.syntax)
}
quote! {
pub fn #method_name(&self) -> Option<#ty> {
support::child(&self.syntax)
}
}
}
@ -338,8 +181,6 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
T,
};
use super::tokens::*;
#(#nodes)*
#(#enums)*
#(#displays)*
@ -456,6 +297,8 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> Result<String> {
macro_rules! T {
#((#punctuation_values) => { $crate::SyntaxKind::#punctuation };)*
#((#all_keywords_idents) => { $crate::SyntaxKind::#all_keywords };)*
(lifetime) => { $crate::SyntaxKind::LIFETIME };
(ident) => { $crate::SyntaxKind::IDENT };
}
};
@ -535,8 +378,21 @@ impl Field<'_> {
"')'" => "r_paren",
"'['" => "l_brack",
"']'" => "r_brack",
"<" => "l_angle",
">" => "r_angle",
"=" => "eq",
"!" => "excl",
"*" => "star",
"&" => "amp",
"_" => "underscore",
"." => "dot",
".." => "dotdot",
"..." => "dotdotdot",
"=>" => "fat_arrow",
"@" => "at",
":" => "colon",
"::" => "coloncolon",
"#" => "pound",
_ => name,
};
format_ident!("{}_token", name)