diff --git a/ast-generator/src/lib.rs b/ast-generator/src/lib.rs index 2703842..2e11e1c 100644 --- a/ast-generator/src/lib.rs +++ b/ast-generator/src/lib.rs @@ -4,6 +4,7 @@ mod deserialize; mod pretty_print; mod spec; mod type_gen; +mod walk; use crate::ast_node::{make_ast_enum_member, make_ast_node_impl}; use crate::build_fn::make_build_fn; @@ -14,6 +15,7 @@ use proc_macro2::TokenStream; use quote::quote; use spec::BuildSpec; use syn::File; +use crate::walk::make_walk_fn; fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) { println!("*** BuildSpec ***"); @@ -184,6 +186,14 @@ fn generate_ast_node_file(build_specs: &[BuildSpec]) -> AstGeneratedFile { } } +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) } @@ -194,5 +204,6 @@ pub fn generate_files(build_specs: &[BuildSpec]) -> Vec { generate_node_file(build_specs), generate_pretty_print_file(build_specs), generate_ast_node_file(build_specs), + generate_walk_file(build_specs), ] } diff --git a/ast-generator/src/walk.rs b/ast-generator/src/walk.rs new file mode 100644 index 0000000..496cebc --- /dev/null +++ b/ast-generator/src/walk.rs @@ -0,0 +1,60 @@ +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,)* + } + } + } + } +} diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 5329df4..8e79ecd 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -161,3 +161,7 @@ pub mod ast_node { } } } + +pub mod walk { + include!(concat!(env!("OUT_DIR"), "/src/ast/walk.rs")); +}