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::>(); 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::>(); 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::>(); 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::>(); let ast_enum_members = build_specs .iter() .map(|build_spec| make_ast_enum_member(build_spec)) .filter(Option::is_some) .map(Option::unwrap) .collect::>(); 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::>(); let inner_unwrappers = build_specs .iter() .map(|build_spec| make_ast_node_ref_unwrapper(build_spec)) .filter(Option::is_some) .map(Option::unwrap) .collect::>(); 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::>(); 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 { deserialize_yaml_spec(yaml) } pub fn generate_files(build_specs: &[BuildSpec]) -> Vec { 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), ] }