diff --git a/ast-generator/src/build_fn/mod.rs b/ast-generator/src/build_fn/mod.rs index 13912e7..af47179 100644 --- a/ast-generator/src/build_fn/mod.rs +++ b/ast-generator/src/build_fn/mod.rs @@ -8,6 +8,7 @@ use crate::build_fn::struct_build_fn::make_struct_build_fn; use crate::build_fn::tree_enum_build_fn::make_enum_build_fn; use crate::spec::BuildSpec; use proc_macro2::TokenStream; +use crate::build_fn::polymorphic_pass_through_build_fn::make_polymorphic_pass_through_build_fn; mod leaf_enum_build_fn; mod leaf_struct_build_fn; @@ -15,6 +16,7 @@ mod node_production_build_fn; mod polymorphic_build_build_fn; mod polymorphic_enum_build_fn; mod polymorphic_enum_loop_build_fn; +mod polymorphic_pass_through_build_fn; mod polymorphic_type_build_fn; mod production_build_fn; mod struct_build_fn; @@ -36,5 +38,8 @@ pub fn make_build_fn(build_spec: &BuildSpec) -> TokenStream { BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop_spec) => { make_polymorphic_enum_loop_build_fn(polymorphic_enum_loop_spec) } + BuildSpec::PolymorphicPassThrough(polymorphic_pass_through_spec) => { + make_polymorphic_pass_through_build_fn(polymorphic_pass_through_spec) + } } } diff --git a/ast-generator/src/build_fn/polymorphic_pass_through_build_fn.rs b/ast-generator/src/build_fn/polymorphic_pass_through_build_fn.rs new file mode 100644 index 0000000..6a61699 --- /dev/null +++ b/ast-generator/src/build_fn/polymorphic_pass_through_build_fn.rs @@ -0,0 +1,50 @@ +use crate::deserialize::util::{make_build_fn_name, make_build_pair}; +use crate::spec::polymorphic_pass_through_spec::{PolymorphicPassThroughBuildSpec, PolymorphicPassThroughVariant}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +pub fn make_polymorphic_pass_through_build_fn( + spec: &PolymorphicPassThroughBuildSpec, +) -> TokenStream { + let build_fn_ident = format_ident!("{}", make_build_fn_name(spec.name())); + let pair_ident = format_ident!("{}", make_build_pair(spec.name())); + let return_type_ident = format_ident!("{}", spec.build_kind()); + + let match_arms = spec.variants() + .map(|variant| { + match variant { + PolymorphicPassThroughVariant::Inner { name, kind} => { + let rule_ident = format_ident!("{}", kind); + let variant_ident = format_ident!("{}", name); + let inner_build_fn_ident = format_ident!("{}", make_build_fn_name(kind)); + + quote! { + Rule::#rule_ident => { + #return_type_ident::#variant_ident( + #inner_build_fn_ident(inner_pair) + ) + } + } + } + PolymorphicPassThroughVariant::PassThrough { name, kind: _kind } => { + let rule_ident = format_ident!("{}", name); + let inner_build_fn_ident = format_ident!("{}", make_build_fn_name(name)); + + quote! { + Rule::#rule_ident => #inner_build_fn_ident(inner_pair) + } + } + } + }) + .collect::>(); + + quote! { + fn #build_fn_ident(#pair_ident: Pair) -> #return_type_ident { + let inner_pair = #pair_ident.into_inner().next().unwrap(); + match inner_pair.as_rule() { + #(#match_arms,)* + _ => unreachable!(), + } + } + } +} diff --git a/ast-generator/src/deserialize/mod.rs b/ast-generator/src/deserialize/mod.rs index 1608173..f47b1c7 100644 --- a/ast-generator/src/deserialize/mod.rs +++ b/ast-generator/src/deserialize/mod.rs @@ -2,6 +2,7 @@ mod leaf_enum_spec; mod leaf_struct_spec; mod node_production_spec; mod polymorphic_enum_loop_spec; +mod polymorphic_pass_through_spec; mod polymorphic_type_spec; mod production_spec; mod struct_spec; @@ -18,6 +19,7 @@ use crate::deserialize::struct_spec::deserialize_struct_spec; use crate::deserialize::tree_enum_spec::deserialize_tree_enum; use crate::spec::BuildSpec; use yaml_rust2::{Yaml, YamlLoader}; +use crate::deserialize::polymorphic_pass_through_spec::deserialize_polymorphic_pass_through; fn deserialize_build_spec(build_spec_name: &str, build_spec: &Yaml) -> BuildSpec { if build_spec["struct"].is_hash() { @@ -60,6 +62,11 @@ fn deserialize_build_spec(build_spec_name: &str, build_spec: &Yaml) -> BuildSpec build_spec_name, &build_spec["polymorphic_enum_loop_build"], )) + } else if build_spec["polymorphic_pass_through"].is_hash() { + BuildSpec::PolymorphicPassThrough(deserialize_polymorphic_pass_through( + build_spec_name, + &build_spec["polymorphic_pass_through"], + )) } else { panic!("Missing or incorrect build type for {}", build_spec_name); } diff --git a/ast-generator/src/deserialize/polymorphic_pass_through_spec.rs b/ast-generator/src/deserialize/polymorphic_pass_through_spec.rs new file mode 100644 index 0000000..dd3ac17 --- /dev/null +++ b/ast-generator/src/deserialize/polymorphic_pass_through_spec.rs @@ -0,0 +1,30 @@ +use crate::deserialize::util::unwrap_single_member_hash; +use crate::spec::polymorphic_pass_through_spec::{PolymorphicPassThroughBuildSpec, PolymorphicPassThroughVariant}; +use yaml_rust2::Yaml; + +pub fn deserialize_polymorphic_pass_through(name: &str, props: &Yaml) -> PolymorphicPassThroughBuildSpec { + let build_kind = props["build"]["kind"].as_str().unwrap(); + let variants = props["variants"].as_vec().unwrap() + .iter() + .map(|variant_yaml| { + let (variant_name, variant_props) = unwrap_single_member_hash(variant_yaml); + if variant_props["inner"].is_hash() { + let inner_kind = variant_props["inner"]["kind"].as_str().unwrap(); + PolymorphicPassThroughVariant::Inner { + name: variant_name, + kind: inner_kind.to_string(), + } + } else if variant_props["pass_through"].is_hash() { + let pass_through_kind = variant_props["pass_through"]["kind"].is_hash(); + PolymorphicPassThroughVariant::PassThrough { + name: variant_name, + kind: pass_through_kind.to_string(), + } + } else { + panic!() + } + }) + .map(Box::new) + .collect(); + PolymorphicPassThroughBuildSpec::new(name, build_kind, variants) +} diff --git a/ast-generator/src/spec/mod.rs b/ast-generator/src/spec/mod.rs index 7a83e08..ce6929c 100644 --- a/ast-generator/src/spec/mod.rs +++ b/ast-generator/src/spec/mod.rs @@ -1,16 +1,18 @@ use crate::spec::node_production_spec::NodeProductionBuildSpec; +use crate::spec::polymorphic_enum_loop_spec::PolymorphicEnumLoopBuildSpec; use leaf_enum_spec::LeafEnumBuildSpec; use leaf_struct_spec::LeafStructBuildSpec; use polymorphic_type_spec::PolymorphicTypeBuildSpec; use production_spec::ProductionBuildSpec; use struct_spec::StructSpec; use tree_enum_spec::TreeEnumBuildSpec; -use crate::spec::polymorphic_enum_loop_spec::PolymorphicEnumLoopBuildSpec; +use crate::spec::polymorphic_pass_through_spec::PolymorphicPassThroughBuildSpec; pub(crate) mod leaf_enum_spec; pub(crate) mod leaf_struct_spec; pub(crate) mod node_production_spec; pub(crate) mod polymorphic_enum_loop_spec; +pub(crate) mod polymorphic_pass_through_spec; pub(crate) mod polymorphic_type_spec; pub(crate) mod production_spec; pub(crate) mod struct_spec; @@ -25,4 +27,5 @@ pub enum BuildSpec { NodeProduction(NodeProductionBuildSpec), PolymorphicType(PolymorphicTypeBuildSpec), PolymorphicEnumLoop(PolymorphicEnumLoopBuildSpec), + PolymorphicPassThrough(PolymorphicPassThroughBuildSpec) } diff --git a/ast-generator/src/spec/polymorphic_pass_through_spec.rs b/ast-generator/src/spec/polymorphic_pass_through_spec.rs new file mode 100644 index 0000000..29a35f1 --- /dev/null +++ b/ast-generator/src/spec/polymorphic_pass_through_spec.rs @@ -0,0 +1,36 @@ +pub struct PolymorphicPassThroughBuildSpec { + name: String, + build_kind: String, + variants: Vec>, +} + +impl PolymorphicPassThroughBuildSpec { + pub fn new( + name: &str, + build_kind: &str, + variants: Vec>, + ) -> Self { + Self { + name: name.to_string(), + build_kind: build_kind.to_string(), + variants, + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn build_kind(&self) -> &str { + &self.build_kind + } + + pub fn variants(&self) -> impl Iterator { + self.variants.iter().map(Box::as_ref) + } +} + +pub enum PolymorphicPassThroughVariant { + Inner { name: String, kind: String }, + PassThrough { name: String, kind: String }, +}