deimos-lang/ast-generator/src/lib.rs
2025-11-22 17:59:08 -06:00

280 lines
8.2 KiB
Rust

mod ast_node;
mod build_fn;
mod deserialize;
mod pretty_print;
mod spec;
mod type_gen;
mod walk;
use crate::ast_node::{
make_ast_enum_member, make_ast_enum_mut_member, make_ast_node_impl,
make_ast_node_ref_mut_unwrapper, make_ast_node_ref_unwrapper,
};
use crate::build_fn::make_build_fn;
use crate::deserialize::deserialize_yaml_spec;
use crate::pretty_print::make_pretty_print_impl;
use crate::type_gen::make_type;
use crate::walk::make_walk_fn;
use proc_macro2::TokenStream;
use quote::quote;
use spec::BuildSpec;
use syn::File;
fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) {
println!("*** BuildSpec ***");
match build_spec {
BuildSpec::Enum(enum_build_spec) => {
println!("Enum Spec - build: {}", enum_build_spec.build());
}
BuildSpec::LeafEnum(leaf_enum_build_spec) => {
println!("Leaf Enum Spec - build: {}", leaf_enum_build_spec.build());
}
BuildSpec::Struct(struct_build_spec) => {
println!("Struct Spec - build: {}", struct_build_spec.build());
}
BuildSpec::LeafStruct(leaf_struct_build_spec) => {
println!(
"Leaf Struct Spec - build: {}",
leaf_struct_build_spec.build()
);
}
BuildSpec::Production(production_build_spec) => {
println!("Production Spec - name: {}", production_build_spec.name());
}
BuildSpec::NodeProduction(node_production_build_spec) => {
println!(
"Node Production Spec - name: {}",
node_production_build_spec.name()
);
}
BuildSpec::PolymorphicType(polymorphic_build_spec) => {
println!(
"Polymorphic Type Spec - name: {}",
polymorphic_build_spec.name()
);
}
BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop_build_spec) => {
println!(
"Polymorphic Enum Loop Spec - name: {}",
polymorphic_enum_loop_build_spec.name()
);
}
BuildSpec::PolymorphicPassThrough(pass_through_build_spec) => {
println!(
"Polymorphic Pass Through Spec - name: {}",
pass_through_build_spec.name()
);
}
BuildSpec::PolymorphicEnumInnerBuild(polymorphic_enum_inner_build_spec) => {
println!(
"Polymorphic Enum Inner Build Spec - name: {}",
polymorphic_enum_inner_build_spec.name()
);
}
BuildSpec::PolymorphicLeafEnum(polymorphic_leaf_enum) => {
println!(
"Polymorphic Leaf Enum Build Spec - name: {}",
polymorphic_leaf_enum.name()
);
}
BuildSpec::PolymorphicTreeEnum(polymorphic_tree_enum) => {
println!(
"Polymorphic Tree Enum Build Spec - name: {}",
polymorphic_tree_enum.name()
);
}
}
println!("{:#?}", token_stream);
let parsed: File = syn::parse2(token_stream.clone()).unwrap();
println!("{}", prettyplease::unparse(&parsed));
}
pub struct AstGeneratedFile {
pub name: String,
pub contents: String,
}
fn token_stream_to_string(token_stream: TokenStream) -> String {
let file: File = syn::parse2(token_stream).unwrap();
prettyplease::unparse(&file)
}
fn generate_build_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
let build_fns = build_specs
.iter()
.map(|build_spec| {
let build_fn = make_build_fn(build_spec);
debug_built_spec(build_spec, &build_fn);
build_fn
})
.collect::<Vec<_>>();
let combined = quote! {
use crate::parser::Rule;
use pest::iterators::Pair;
use crate::ast::node::*;
use std::range::Range;
#(#build_fns)*
};
AstGeneratedFile {
name: String::from("build.rs"),
contents: token_stream_to_string(combined),
}
}
fn generate_node_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
let types = build_specs
.iter()
.map(|build_spec| make_type(build_spec))
.filter(Option::is_some)
.collect::<Vec<_>>();
let combined = quote! {
use std::range::Range;
use std::rc::Rc;
use std::cell::RefCell;
use crate::name_analysis::symbol::*;
use crate::type_analysis::kinds::*;
#(#types)*
};
AstGeneratedFile {
name: String::from("node.rs"),
contents: token_stream_to_string(combined),
}
}
fn generate_pretty_print_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
let impls = build_specs
.iter()
.map(|build_spec| {
let maybe_stream = make_pretty_print_impl(build_spec);
if let Some(stream) = &maybe_stream {
debug_built_spec(build_spec, &stream);
}
maybe_stream
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let combined = quote! {
use crate::ast::node::*;
#(#impls)*
};
AstGeneratedFile {
name: String::from("pretty_print.rs"),
contents: token_stream_to_string(combined),
}
}
fn generate_ast_node_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
let impls = build_specs
.iter()
.map(|build_spec| {
let maybe_stream = make_ast_node_impl(build_spec);
if let Some(stream) = &maybe_stream {
debug_built_spec(build_spec, &stream);
}
maybe_stream
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let ast_enum_members = build_specs
.iter()
.map(|build_spec| make_ast_enum_member(build_spec))
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let ast_enum_mut_members = build_specs
.iter()
.map(|build_spec| make_ast_enum_mut_member(build_spec))
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let inner_unwrappers = build_specs
.iter()
.map(|build_spec| make_ast_node_ref_unwrapper(build_spec))
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let inner_mut_unwrappers = build_specs
.iter()
.map(|build_spec| make_ast_node_ref_mut_unwrapper(build_spec))
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let combined = quote! {
use crate::ast::node::*;
pub enum AstNodeRef<'a> {
#(#ast_enum_members,)*
}
impl<'a> AstNodeRef<'a> {
pub fn inner(&self) -> &dyn AstNode<'a> {
match self {
#(#inner_unwrappers,)*
}
}
}
pub enum AstNodeRefMut<'a> {
#(#ast_enum_mut_members,)*
}
impl<'a> AstNodeRefMut<'a> {
pub fn inner(&mut self) -> &mut dyn AstNode<'a> {
match self {
#(#inner_mut_unwrappers,)*
}
}
}
pub trait AstNode<'a> {
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>>;
fn for_each_child_mut(&'a mut self, f: &mut dyn FnMut(&'a mut dyn AstNode<'a>));
fn as_node_ref(&'a self) -> AstNodeRef<'a>;
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a>;
}
#(#impls)*
};
AstGeneratedFile {
name: String::from("ast_node.rs"),
contents: token_stream_to_string(combined),
}
}
fn generate_walk_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
let stream = make_walk_fn(build_specs);
AstGeneratedFile {
name: String::from("walk.rs"),
contents: token_stream_to_string(stream),
}
}
pub fn get_build_specs(yaml: &str) -> Vec<BuildSpec> {
deserialize_yaml_spec(yaml)
}
pub fn generate_files(build_specs: &[BuildSpec]) -> Vec<AstGeneratedFile> {
vec![
generate_build_file(build_specs),
generate_node_file(build_specs),
generate_pretty_print_file(build_specs),
generate_ast_node_file(build_specs),
generate_walk_file(build_specs),
]
}