diff --git a/ast-generator/src/lib.rs b/ast-generator/src/lib.rs index 863322e..ff1281a 100644 --- a/ast-generator/src/lib.rs +++ b/ast-generator/src/lib.rs @@ -6,7 +6,7 @@ mod type_gen; use crate::build_fn::make_build_fn; use crate::deserialize::deserialize_yaml_spec; -// use crate::pretty_print::make_pretty_print_impl; +use crate::pretty_print::make_pretty_print_impl; use crate::type_gen::make_type; use proc_macro2::TokenStream; use quote::quote; @@ -117,24 +117,27 @@ fn generate_node_file(build_specs: &[BuildSpec]) -> AstGeneratedFile { } fn generate_pretty_print_file(build_specs: &[BuildSpec]) -> AstGeneratedFile { - // let impls = build_specs - // .iter() - // .map(|build_spec| { - // let stream = make_pretty_print_impl(build_spec); - // debug_built_spec(build_spec, &stream); - // stream - // }) - // .collect::>(); - // - // let combined = quote! { - // use crate::ast::node::*; - // #(#impls)* - // }; - // AstGeneratedFile { - // name: String::from("pretty_print.rs"), - // contents: token_stream_to_string(combined), - // } - todo!() + 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), + } } pub fn get_build_specs(yaml: &str) -> Vec { @@ -145,6 +148,6 @@ 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_pretty_print_file(build_specs), ] } diff --git a/ast-generator/src/pretty_print.rs b/ast-generator/src/pretty_print.rs index 8429283..dbe6304 100644 --- a/ast-generator/src/pretty_print.rs +++ b/ast-generator/src/pretty_print.rs @@ -1,260 +1,214 @@ -// use crate::spec::leaf_enum_spec::LeafEnumBuildSpec; -// use crate::spec::leaf_struct_spec::{LeafStructBuildSpec, LeafStructMemberKind}; -// use crate::spec::polymorphic_type_spec::PolymorphicTypeBuildSpec; -// use crate::spec::production_spec::ProductionBuildSpec; -// use crate::spec::struct_spec::StructSpec; -// use crate::spec::tree_enum_spec::{EnumRuleChildKind, TreeEnumBuildSpec}; -// use crate::spec::{ -// AlternativeChild, BuildSpec, MemberChildToBuild, PolymorphicBuildBuildSpec, -// PolymorphicEnumBuildSpec, StructChildSpec, VecChildToBuild, -// }; -// use convert_case::{Case, Casing}; -// use proc_macro2::TokenStream; -// use quote::{format_ident, quote}; -// -// fn make_production_p2_impl(_spec: &ProductionBuildSpec) -> TokenStream { -// quote! {} -// } -// -// fn make_polymorphic_enum_p2_impl(_spec: &PolymorphicEnumBuildSpec) -> TokenStream { -// quote! {} -// } -// -// fn make_polymorphic_build_p2_impl(spec: &PolymorphicBuildBuildSpec) -> TokenStream { -// let (_, build) = spec.primary_alternative(); -// let type_ident = format_ident!("{}", spec.name()); -// let name_str = spec.name(); -// -// let child_statements = build -// .children() -// .map(|child| match child { -// AlternativeChild::Skip => None, -// AlternativeChild::Build(build) => { -// let child_ident = format_ident!("{}", build.name()); -// Some(quote! { -// self.#child_ident().pretty_print(writer)? -// }) -// } -// }) -// .filter(Option::is_some) -// .map(Option::unwrap) -// .collect::>(); -// -// quote! { -// impl PrettyPrint for #type_ident { -// fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { -// writer.writeln_indented(#name_str); -// writer.increase_indent(); -// #(#child_statements;)* -// writer.decrease_indent(); -// Ok(()) -// } -// } -// } -// } -// -// fn make_polymorphic_type_p2_impl(spec: &PolymorphicTypeBuildSpec) -> TokenStream { -// let type_ident = format_ident!("{}", spec.name()); -// let child_matchers = spec -// .variants() -// .map(|member| { -// let enum_member_ident = format_ident!("{}", member.name()); -// let inner_name = format_ident!("{}", member.inner_kind().to_case(Case::Snake)); -// quote! { -// #type_ident::#enum_member_ident(#inner_name) => #inner_name.pretty_print(writer) -// } -// }) -// .collect::>(); -// -// quote! { -// impl PrettyPrint for #type_ident { -// fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { -// match self { -// #(#child_matchers,)* -// } -// } -// } -// } -// } -// -// fn make_leaf_enum_p2_impl(spec: &LeafEnumBuildSpec) -> TokenStream { -// let type_ident = format_ident!("{}", spec.build()); -// let child_matchers = spec -// .rules() -// .map(|rule| { -// let enum_variant_ident = format_ident!("{}", rule.rule()); -// let name_str = rule.rule(); -// quote! { -// #type_ident::#enum_variant_ident => writer.writeln_indented(#name_str) -// } -// }) -// .collect::>(); -// -// quote! { -// impl PrettyPrint for #type_ident { -// fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { -// match self { -// #(#child_matchers,)* -// } -// } -// } -// } -// } -// -// fn make_enum_p2_impl(spec: &TreeEnumBuildSpec) -> TokenStream { -// let type_ident = format_ident!("{}", spec.build()); -// let type_str = spec.build(); -// -// let child_matchers = spec -// .rules() -// .map(|rule| { -// let enum_variant_ident = format_ident!("{}", rule.rule()); -// if let Some(child) = rule.child() { -// match child.kind() { -// EnumRuleChildKind::Node(node_child) => { -// let child_name_ident = -// format_ident!("{}", node_child.build().to_case(Case::Snake)); -// Some(quote! { -// #type_ident::#enum_variant_ident(#child_name_ident) => { -// #child_name_ident.pretty_print(writer)?; -// } -// }) -// } -// _ => None, -// } -// } else { -// let variant_str = rule.rule(); -// Some(quote! { -// #type_ident::#enum_variant_ident => writer.writeln_indented(#variant_str)? -// }) -// } -// }) -// .filter(Option::is_some) -// .map(Option::unwrap) -// .collect::>(); -// -// quote! { -// impl PrettyPrint for #type_ident { -// fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { -// writer.writeln_indented(#type_str)?; -// writer.increase_indent(); -// match self { -// #(#child_matchers,)* -// _ => {} -// } -// writer.decrease_indent(); -// Ok(()) -// } -// } -// } -// } -// -// fn make_leaf_struct_p2_impl(leaf_struct_build_spec: &LeafStructBuildSpec) -> TokenStream { -// let type_ident = format_ident!("{}", leaf_struct_build_spec.build()); -// let member_formatters = leaf_struct_build_spec -// .members() -// .map(|member| match member.kind() { -// LeafStructMemberKind::String => Some("{}"), -// }) -// .filter(Option::is_some) -// .map(Option::unwrap) -// .collect::>() -// .join(", "); -// -// let format_string = format!("{}({})", leaf_struct_build_spec.build(), member_formatters); -// -// let members = leaf_struct_build_spec -// .members() -// .map(|member| { -// let member_ident = format_ident!("{}", member.name()); -// quote! { -// self.#member_ident() -// } -// }) -// .collect::>(); -// -// quote! { -// impl PrettyPrint for #type_ident { -// fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { -// writer.writeln_indented(&format!(#format_string, #(#members),*)) -// } -// } -// } -// } -// -// fn make_struct_p2_impl(struct_build_spec: &StructSpec) -> TokenStream { -// let child_print_statements = struct_build_spec -// .children() -// .map(|child| match child { -// StructChildSpec::SkipChild(_) => None, -// StructChildSpec::VecChild(vec_child) => match vec_child.build() { -// VecChildToBuild::Node(_) => { -// let child_ident = format_ident!("{}", vec_child.name()); -// Some(quote! { -// for child in self.#child_ident() { -// child.pretty_print(writer)?; -// } -// }) -// } -// VecChildToBuild::String => None, -// }, -// StructChildSpec::MemberChild(member_child) => match member_child.build() { -// MemberChildToBuild::Node(node_member_child) => { -// let child_ident = format_ident!("{}", member_child.name()); -// if node_member_child.optional() { -// Some(quote! { -// if let Some(child) = self.#child_ident() { -// child.pretty_print(writer)?; -// } -// }) -// } else { -// Some(quote! { -// self.#child_ident().pretty_print(writer)?; -// }) -// } -// } -// MemberChildToBuild::Boolean(boolean_member_child) => { -// let format_string = format!("{}({})", boolean_member_child.name(), "{}"); -// let child_ident = format_ident!("{}", boolean_member_child.name()); -// Some(quote! { -// writer.writeln_indented(&format!(#format_string, self.#child_ident()))?; -// }) -// } -// }, -// }) -// .filter(Option::is_some) -// .map(Option::unwrap) -// .collect::>(); -// -// let type_ident = format_ident!("{}", struct_build_spec.build()); -// let type_string = struct_build_spec.build(); -// -// quote! { -// impl PrettyPrint for #type_ident { -// fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { -// writer.writeln_indented(#type_string)?; -// writer.increase_indent(); -// #(#child_print_statements)* -// writer.decrease_indent(); -// Ok(()) -// } -// } -// } -// } -// -// pub fn make_pretty_print_impl(build_spec: &BuildSpec) -> TokenStream { -// match build_spec { -// BuildSpec::Struct(struct_spec) => make_struct_p2_impl(struct_spec), -// BuildSpec::LeafStruct(leaf_struct) => make_leaf_struct_p2_impl(leaf_struct), -// BuildSpec::Enum(enum_spec) => make_enum_p2_impl(enum_spec), -// BuildSpec::LeafEnum(leaf_enum) => make_leaf_enum_p2_impl(leaf_enum), -// BuildSpec::PolymorphicType(polymorphic) => make_polymorphic_type_p2_impl(polymorphic), -// BuildSpec::PolymorphicBuild(polymorphic_build) => { -// make_polymorphic_build_p2_impl(polymorphic_build) -// } -// BuildSpec::PolymorphicEnum(polymorphic_enum) => { -// make_polymorphic_enum_p2_impl(polymorphic_enum) -// } -// BuildSpec::Production(production) => make_production_p2_impl(production), -// } -// } +use crate::spec::leaf_enum_spec::LeafEnumBuildSpec; +use crate::spec::leaf_struct_spec::{LeafStructBuildSpec, LeafStructMemberKind}; +use crate::spec::polymorphic_type_spec::PolymorphicTypeBuildSpec; +use crate::spec::struct_spec::{MemberChildBuild, StructChild, StructSpec, VecChildBuild}; +use crate::spec::tree_enum_spec::{EnumRuleChildKind, TreeEnumBuildSpec}; +use crate::spec::BuildSpec; +use convert_case::{Case, Casing}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +fn make_polymorphic_type_p2_impl(spec: &PolymorphicTypeBuildSpec) -> TokenStream { + let type_ident = format_ident!("{}", spec.name()); + let child_matchers = spec + .variants() + .map(|member| { + let enum_member_ident = format_ident!("{}", member.name()); + let inner_name = format_ident!("{}", member.inner_kind().to_case(Case::Snake)); + quote! { + #type_ident::#enum_member_ident(#inner_name) => #inner_name.pretty_print(writer) + } + }) + .collect::>(); + + quote! { + impl PrettyPrint for #type_ident { + fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + match self { + #(#child_matchers,)* + } + } + } + } +} + +fn make_leaf_enum_p2_impl(spec: &LeafEnumBuildSpec) -> TokenStream { + let type_ident = format_ident!("{}", spec.build()); + let child_matchers = spec + .rules() + .map(|rule| { + let enum_variant_ident = format_ident!("{}", rule); + let name_str = rule; + quote! { + #type_ident::#enum_variant_ident => writer.writeln_indented(#name_str) + } + }) + .collect::>(); + + quote! { + impl PrettyPrint for #type_ident { + fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + match self { + #(#child_matchers,)* + } + } + } + } +} + +fn make_tree_enum_p2_impl(spec: &TreeEnumBuildSpec) -> TokenStream { + let type_ident = format_ident!("{}", spec.build()); + let type_str = spec.build(); + + let child_matchers = spec + .rules() + .map(|rule| { + let enum_variant_ident = format_ident!("{}", rule.rule()); + if let Some(child) = rule.child() { + match child.kind() { + EnumRuleChildKind::Node(node_child) => { + let child_name_ident = + format_ident!("{}", node_child.node_kind().to_case(Case::Snake)); + Some(quote! { + #type_ident::#enum_variant_ident(#child_name_ident) => { + #child_name_ident.pretty_print(writer)?; + } + }) + } + _ => None, + } + } else { + let variant_str = rule.rule(); + Some(quote! { + #type_ident::#enum_variant_ident => writer.writeln_indented(#variant_str)? + }) + } + }) + .filter(Option::is_some) + .map(Option::unwrap) + .collect::>(); + + quote! { + impl PrettyPrint for #type_ident { + fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + writer.writeln_indented(#type_str)?; + writer.increase_indent(); + match self { + #(#child_matchers,)* + _ => {} + } + writer.decrease_indent(); + Ok(()) + } + } + } +} + +fn make_leaf_struct_p2_impl(leaf_struct_build_spec: &LeafStructBuildSpec) -> TokenStream { + let type_ident = format_ident!("{}", leaf_struct_build_spec.build()); + let member_formatters = leaf_struct_build_spec + .members() + .map(|member| match member.kind() { + LeafStructMemberKind::String => Some("{}"), + }) + .filter(Option::is_some) + .map(Option::unwrap) + .collect::>() + .join(", "); + + let format_string = format!("{}({})", leaf_struct_build_spec.build(), member_formatters); + + let members = leaf_struct_build_spec + .members() + .map(|member| { + let member_ident = format_ident!("{}", member.name()); + quote! { + self.#member_ident() + } + }) + .collect::>(); + + quote! { + impl PrettyPrint for #type_ident { + fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + writer.writeln_indented(&format!(#format_string, #(#members),*)) + } + } + } +} + +fn make_struct_p2_impl(struct_build_spec: &StructSpec) -> TokenStream { + let child_print_statements = struct_build_spec + .children() + .map(|child| match child { + StructChild::SkipChild(_) => None, + StructChild::VecChild(vec_child) => match vec_child.build() { + VecChildBuild::Node(_) => { + let child_ident = format_ident!("{}", vec_child.name()); + Some(quote! { + for child in self.#child_ident() { + child.pretty_print(writer)?; + } + }) + } + VecChildBuild::String(_) => None, + }, + StructChild::MemberChild(member_child) => match member_child.build() { + MemberChildBuild::Node(_) => { + let child_ident = format_ident!("{}", member_child.name()); + if member_child.optional() { + Some(quote! { + if let Some(child) = self.#child_ident() { + child.pretty_print(writer)?; + } + }) + } else { + Some(quote! { + self.#child_ident().pretty_print(writer)?; + }) + } + } + MemberChildBuild::Boolean(_) => { + let format_string = format!("{}({})", member_child.name(), "{}"); + let child_ident = format_ident!("{}", member_child.name()); + Some(quote! { + writer.writeln_indented(&format!(#format_string, self.#child_ident()))?; + }) + } + }, + }) + .filter(Option::is_some) + .map(Option::unwrap) + .collect::>(); + + let type_ident = format_ident!("{}", struct_build_spec.build()); + let type_string = struct_build_spec.build(); + + quote! { + impl PrettyPrint for #type_ident { + fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + writer.writeln_indented(#type_string)?; + writer.increase_indent(); + #(#child_print_statements)* + writer.decrease_indent(); + Ok(()) + } + } + } +} + +pub fn make_pretty_print_impl(build_spec: &BuildSpec) -> Option { + match build_spec { + BuildSpec::Struct(struct_spec) => Some(make_struct_p2_impl(struct_spec)), + BuildSpec::LeafStruct(leaf_struct) => Some(make_leaf_struct_p2_impl(leaf_struct)), + BuildSpec::Enum(enum_spec) => Some(make_tree_enum_p2_impl(enum_spec)), + BuildSpec::LeafEnum(leaf_enum) => Some(make_leaf_enum_p2_impl(leaf_enum)), + BuildSpec::Production(_) => None, + BuildSpec::NodeProduction(_) => None, + BuildSpec::PolymorphicType(polymorphic_type) => { + Some(make_polymorphic_type_p2_impl(polymorphic_type)) + } + BuildSpec::PolymorphicPassThrough(_) => None, + BuildSpec::PolymorphicEnumLoop(_) => None, + } +}