Owned AST IR

This commit is contained in:
Aleksey Kladov 2020-07-29 11:48:32 +02:00
parent 04d2b7b256
commit 627eddbc7e
2 changed files with 1990 additions and 1981 deletions

View file

@ -223,35 +223,35 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
], ],
}; };
pub(crate) struct AstSrc<'a> { pub(crate) struct AstSrc {
pub(crate) tokens: &'a [&'a str], pub(crate) tokens: Vec<String>,
pub(crate) nodes: &'a [AstNodeSrc<'a>], pub(crate) nodes: Vec<AstNodeSrc>,
pub(crate) enums: &'a [AstEnumSrc<'a>], pub(crate) enums: Vec<AstEnumSrc>,
} }
pub(crate) struct AstNodeSrc<'a> { pub(crate) struct AstNodeSrc {
pub(crate) doc: &'a [&'a str], pub(crate) doc: Vec<String>,
pub(crate) name: &'a str, pub(crate) name: String,
pub(crate) traits: &'a [&'a str], pub(crate) traits: Vec<String>,
pub(crate) fields: &'a [Field<'a>], pub(crate) fields: Vec<Field>,
} }
pub(crate) enum Field<'a> { pub(crate) enum Field {
Token(&'a str), Token(String),
Node { name: &'a str, src: FieldSrc<'a> }, Node { name: String, src: FieldSrc },
} }
pub(crate) enum FieldSrc<'a> { pub(crate) enum FieldSrc {
Shorthand, Shorthand,
Optional(&'a str), Optional(String),
Many(&'a str), Many(String),
} }
pub(crate) struct AstEnumSrc<'a> { pub(crate) struct AstEnumSrc {
pub(crate) doc: &'a [&'a str], pub(crate) doc: Vec<String>,
pub(crate) name: &'a str, pub(crate) name: String,
pub(crate) traits: &'a [&'a str], pub(crate) traits: Vec<String>,
pub(crate) variants: &'a [&'a str], pub(crate) variants: Vec<String>,
} }
macro_rules! ast_nodes { macro_rules! ast_nodes {
@ -261,12 +261,12 @@ macro_rules! ast_nodes {
$($field_name:ident $(![$token:tt])? $(: $ty:tt)?),*$(,)? $($field_name:ident $(![$token:tt])? $(: $ty:tt)?),*$(,)?
} }
)*) => { )*) => {
[$( vec![$(
AstNodeSrc { AstNodeSrc {
doc: &[$($doc),*], doc: vec![$($doc.to_string()),*],
name: stringify!($name), name: stringify!($name).to_string(),
traits: &[$($(stringify!($trait)),*)?], traits: vec![$($(stringify!($trait).to_string()),*)?],
fields: &[ fields: vec![
$(field!($(T![$token])? $field_name $($ty)?)),* $(field!($(T![$token])? $field_name $($ty)?)),*
], ],
@ -277,16 +277,22 @@ macro_rules! ast_nodes {
macro_rules! field { macro_rules! field {
(T![$token:tt] T) => { (T![$token:tt] T) => {
Field::Token(stringify!($token)) Field::Token(stringify!($token).to_string())
}; };
($field_name:ident) => { ($field_name:ident) => {
Field::Node { name: stringify!($field_name), src: FieldSrc::Shorthand } Field::Node { name: stringify!($field_name).to_string(), src: FieldSrc::Shorthand }
}; };
($field_name:ident [$ty:ident]) => { ($field_name:ident [$ty:ident]) => {
Field::Node { name: stringify!($field_name), src: FieldSrc::Many(stringify!($ty)) } Field::Node {
name: stringify!($field_name).to_string(),
src: FieldSrc::Many(stringify!($ty).to_string()),
}
}; };
($field_name:ident $ty:ident) => { ($field_name:ident $ty:ident) => {
Field::Node { name: stringify!($field_name), src: FieldSrc::Optional(stringify!($ty)) } Field::Node {
name: stringify!($field_name).to_string(),
src: FieldSrc::Optional(stringify!($ty).to_string()),
}
}; };
} }
@ -297,20 +303,21 @@ macro_rules! ast_enums {
$($variant:ident),*$(,)? $($variant:ident),*$(,)?
} }
)*) => { )*) => {
[$( vec![$(
AstEnumSrc { AstEnumSrc {
doc: &[$($doc),*], doc: vec![$($doc.to_string()),*],
name: stringify!($name), name: stringify!($name).to_string(),
traits: &[$($(stringify!($trait)),*)?], traits: vec![$($(stringify!($trait).to_string()),*)?],
variants: &[$(stringify!($variant)),*], variants: vec![$(stringify!($variant).to_string()),*],
} }
),*] ),*]
}; };
} }
pub(crate) const AST_SRC: AstSrc = AstSrc { pub(crate) fn rust_ast() -> AstSrc {
tokens: &["Whitespace", "Comment", "String", "RawString"], AstSrc {
nodes: &ast_nodes! { tokens: vec!["Whitespace".into(), "Comment".into(), "String".into(), "RawString".into()],
nodes: ast_nodes! {
/// The entire Rust source file. Includes all top-level inner attributes and module items. /// The entire Rust source file. Includes all top-level inner attributes and module items.
/// ///
/// [Reference](https://doc.rust-lang.org/reference/crates-and-source-files.html) /// [Reference](https://doc.rust-lang.org/reference/crates-and-source-files.html)
@ -2089,7 +2096,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
Name, TokenTree Name, TokenTree
} }
}, },
enums: &ast_enums! { enums: ast_enums! {
/// Any kind of nominal type definition. /// Any kind of nominal type definition.
enum NominalDef: NameOwner, TypeParamsOwner, AttrsOwner { enum NominalDef: NameOwner, TypeParamsOwner, AttrsOwner {
StructDef, EnumDef, UnionDef, StructDef, EnumDef, UnionDef,
@ -2240,4 +2247,5 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
TupleFieldDefList, TupleFieldDefList,
} }
}, },
}; }
}

View file

@ -9,28 +9,29 @@ use proc_macro2::{Punct, Spacing};
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use crate::{ use crate::{
ast_src::{AstSrc, Field, FieldSrc, KindsSrc, AST_SRC, KINDS_SRC}, ast_src::{rust_ast, AstSrc, Field, FieldSrc, KindsSrc, KINDS_SRC},
codegen::{self, update, Mode}, codegen::{self, update, Mode},
project_root, Result, project_root, Result,
}; };
pub fn generate_syntax(mode: Mode) -> Result<()> { pub fn generate_syntax(mode: Mode) -> Result<()> {
let ast = rust_ast();
let syntax_kinds_file = project_root().join(codegen::SYNTAX_KINDS); let syntax_kinds_file = project_root().join(codegen::SYNTAX_KINDS);
let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?; let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?;
update(syntax_kinds_file.as_path(), &syntax_kinds, mode)?; update(syntax_kinds_file.as_path(), &syntax_kinds, mode)?;
let ast_tokens_file = project_root().join(codegen::AST_TOKENS); let ast_tokens_file = project_root().join(codegen::AST_TOKENS);
let contents = generate_tokens(AST_SRC)?; let contents = generate_tokens(&ast)?;
update(ast_tokens_file.as_path(), &contents, mode)?; update(ast_tokens_file.as_path(), &contents, mode)?;
let ast_nodes_file = project_root().join(codegen::AST_NODES); let ast_nodes_file = project_root().join(codegen::AST_NODES);
let contents = generate_nodes(KINDS_SRC, AST_SRC)?; let contents = generate_nodes(KINDS_SRC, &ast)?;
update(ast_nodes_file.as_path(), &contents, mode)?; update(ast_nodes_file.as_path(), &contents, mode)?;
Ok(()) Ok(())
} }
fn generate_tokens(grammar: AstSrc<'_>) -> Result<String> { fn generate_tokens(grammar: &AstSrc) -> Result<String> {
let tokens = grammar.tokens.iter().map(|token| { let tokens = grammar.tokens.iter().map(|token| {
let name = format_ident!("{}", token); let name = format_ident!("{}", token);
let kind = format_ident!("{}", to_upper_snake_case(token)); let kind = format_ident!("{}", to_upper_snake_case(token));
@ -62,13 +63,13 @@ fn generate_tokens(grammar: AstSrc<'_>) -> Result<String> {
Ok(pretty) Ok(pretty)
} }
fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> Result<String> {
let (node_defs, node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar let (node_defs, node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar
.nodes .nodes
.iter() .iter()
.map(|node| { .map(|node| {
let name = format_ident!("{}", node.name); let name = format_ident!("{}", node.name);
let kind = format_ident!("{}", to_upper_snake_case(node.name)); let kind = format_ident!("{}", to_upper_snake_case(&node.name));
let traits = node.traits.iter().map(|trait_name| { let traits = node.traits.iter().map(|trait_name| {
let trait_name = format_ident!("{}", trait_name); let trait_name = format_ident!("{}", trait_name);
quote!(impl ast::#trait_name for #name {}) quote!(impl ast::#trait_name for #name {})
@ -192,8 +193,8 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
}) })
.unzip(); .unzip();
let enum_names = grammar.enums.iter().map(|it| it.name); let enum_names = grammar.enums.iter().map(|it| &it.name);
let node_names = grammar.nodes.iter().map(|it| it.name); let node_names = grammar.nodes.iter().map(|it| &it.name);
let display_impls = let display_impls =
enum_names.chain(node_names.clone()).map(|it| format_ident!("{}", it)).map(|name| { enum_names.chain(node_names.clone()).map(|it| format_ident!("{}", it)).map(|name| {
@ -212,7 +213,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
.nodes .nodes
.iter() .iter()
.map(|kind| to_pascal_case(kind)) .map(|kind| to_pascal_case(kind))
.filter(|name| !defined_nodes.contains(name.as_str())) .filter(|name| !defined_nodes.iter().any(|&it| it == name))
{ {
eprintln!("Warning: node {} not defined in ast source", node); eprintln!("Warning: node {} not defined in ast source", node);
} }
@ -236,12 +237,12 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
let mut res = String::with_capacity(ast.len() * 2); let mut res = String::with_capacity(ast.len() * 2);
let mut docs = let mut docs =
grammar.nodes.iter().map(|it| it.doc).chain(grammar.enums.iter().map(|it| it.doc)); grammar.nodes.iter().map(|it| &it.doc).chain(grammar.enums.iter().map(|it| &it.doc));
for chunk in ast.split("# [ pretty_doc_comment_placeholder_workaround ]") { for chunk in ast.split("# [ pretty_doc_comment_placeholder_workaround ]") {
res.push_str(chunk); res.push_str(chunk);
if let Some(doc) = docs.next() { if let Some(doc) = docs.next() {
write_doc_comment(doc, &mut res); write_doc_comment(&doc, &mut res);
} }
} }
@ -249,7 +250,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
Ok(pretty) Ok(pretty)
} }
fn write_doc_comment(contents: &[&str], dest: &mut String) { fn write_doc_comment(contents: &[String], dest: &mut String) {
for line in contents { for line in contents {
writeln!(dest, "///{}", line).unwrap(); writeln!(dest, "///{}", line).unwrap();
} }
@ -413,7 +414,7 @@ fn to_pascal_case(s: &str) -> String {
buf buf
} }
impl Field<'_> { impl Field {
fn is_many(&self) -> bool { fn is_many(&self) -> bool {
matches!(self, Field::Node { src: FieldSrc::Many(_), .. }) matches!(self, Field::Node { src: FieldSrc::Many(_), .. })
} }
@ -429,7 +430,7 @@ impl Field<'_> {
fn method_name(&self) -> proc_macro2::Ident { fn method_name(&self) -> proc_macro2::Ident {
match self { match self {
Field::Token(name) => { Field::Token(name) => {
let name = match *name { let name = match name.as_str() {
";" => "semicolon", ";" => "semicolon",
"->" => "thin_arrow", "->" => "thin_arrow",
"'{'" => "l_curly", "'{'" => "l_curly",