use crate::spec::leaf_enum_spec::LeafEnumBuildSpec; use crate::spec::leaf_struct_spec::{LeafStructBuildSpec, LeafStructMemberKind}; use crate::spec::polymorphic_enum_loop_spec::{ PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuildChild, }; 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_result() -> TokenStream { quote! { std::fmt::Result } } fn make_polymorphic_enum_loop_p2_impl(spec: &PolymorphicEnumLoopBuildSpec) -> TokenStream { let type_ident = format_ident!("{}", spec.name()); let type_string = spec.name(); let build = spec .rules() .find(|rule| match rule { PolymorphicEnumLoopRule::Build(_) => true, _ => false, }) .map(|rule| match rule { PolymorphicEnumLoopRule::Build(build) => build, _ => unreachable!(), }) .unwrap(); let child_print_statements = build .children() .map(|child| { let child_ident = match child { PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => { format_ident!("{}", use_current.name()) } PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => { format_ident!("{}", on_each.name()) } }; quote! { self.#child_ident().pretty_print(writer)?; } }) .collect::>(); let result = make_result(); quote! { impl PrettyPrint for #type_ident { fn pretty_print(&self, writer: &mut IndentWriter) -> #result { writer.writeln_indented(#type_string)?; writer.increase_indent(); #(#child_print_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::>(); let result = make_result(); quote! { impl PrettyPrint for #type_ident { fn pretty_print(&self, writer: &mut IndentWriter) -> #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::>(); let result = make_result(); quote! { impl PrettyPrint for #type_ident { fn pretty_print(&self, writer: &mut IndentWriter) -> #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::>(); let result = make_result(); quote! { impl PrettyPrint for #type_ident { fn pretty_print(&self, writer: &mut IndentWriter) -> #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("{}"), LeafStructMemberKind::FileId => None, LeafStructMemberKind::Range => None, }) .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| match member.kind() { LeafStructMemberKind::String => { let member_ident = format_ident!("{}", member.name()); Some(quote! { self.#member_ident() }) } _ => None, }) .filter(Option::is_some) .map(Option::unwrap) .collect::>(); let result = make_result(); quote! { impl PrettyPrint for #type_ident { fn pretty_print(&self, writer: &mut IndentWriter) -> #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()))?; }) } }, StructChild::Special(_) => None, }) .filter(Option::is_some) .map(Option::unwrap) .collect::>(); let type_ident = format_ident!("{}", struct_build_spec.build()); let type_string = struct_build_spec.build(); let result = make_result(); quote! { impl PrettyPrint for #type_ident { fn pretty_print(&self, writer: &mut IndentWriter) -> #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(polymorphic_enum_loop) => { Some(make_polymorphic_enum_loop_p2_impl(polymorphic_enum_loop)) } } }