use crate::spec::BuildSpec; use convert_case::{Case, Casing}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; pub fn make_walk_fn(specs: &[BuildSpec]) -> TokenStream { let child_match_arms = specs .iter() .map(|spec| match spec { BuildSpec::Enum(enum_spec) => Some(( format_ident!("{}", enum_spec.build()), format_ident!("{}", enum_spec.build().to_case(Case::Snake)), )), BuildSpec::LeafEnum(leaf_enum) => Some(( format_ident!("{}", leaf_enum.build()), format_ident!("{}", leaf_enum.build().to_case(Case::Snake)), )), BuildSpec::Struct(struct_spec) => Some(( format_ident!("{}", struct_spec.build()), format_ident!("{}", struct_spec.build().to_case(Case::Snake)), )), BuildSpec::LeafStruct(leaf_struct) => Some(( format_ident!("{}", leaf_struct.build()), format_ident!("{}", leaf_struct.build().to_case(Case::Snake)), )), BuildSpec::Production(_) => None, BuildSpec::NodeProduction(_) => None, BuildSpec::PolymorphicType(polymorphic_type) => Some(( format_ident!("{}", polymorphic_type.name()), format_ident!("{}", polymorphic_type.name().to_case(Case::Snake)), )), BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop) => Some(( format_ident!("{}", polymorphic_enum_loop.name()), format_ident!("{}", polymorphic_enum_loop.name().to_case(Case::Snake)), )), BuildSpec::PolymorphicPassThrough(_) => None, }) .filter(Option::is_some) .map(Option::unwrap) .map(|(type_ident, inner_ident)| { quote! { #type_ident(#inner_ident) => walk_depth_first(#inner_ident, f) } }) .collect::>(); quote! { use crate::ast::node::*; use crate::ast::ast_node::*; pub fn walk_depth_first(node: &impl AstNode, f: &mut impl FnMut(AstNodeRef)) { use AstNodeRef::*; for child in node.children() { match child { #(#child_match_arms,)* } } f(node.as_node_ref()); } } }