From eb83d1202a4bdb2dbe76106b188b730c501cb430 Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Tue, 23 Sep 2025 10:56:11 -0500 Subject: [PATCH] Make polymorphic enum loop build fn. --- ast-generator/src/build_fn/mod.rs | 3 +- .../polymorphic_enum_loop_build_fn.rs | 100 ++++++++++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 ast-generator/src/build_fn/polymorphic_enum_loop_build_fn.rs diff --git a/ast-generator/src/build_fn/mod.rs b/ast-generator/src/build_fn/mod.rs index afbff1f..38f5dc9 100644 --- a/ast-generator/src/build_fn/mod.rs +++ b/ast-generator/src/build_fn/mod.rs @@ -1,8 +1,9 @@ pub mod leaf_enum_build_fn; pub mod leaf_struct_build_fn; -mod node_production_build_fn; +pub mod node_production_build_fn; pub mod polymorphic_build_build_fn; pub mod polymorphic_enum_build_fn; +pub mod polymorphic_enum_loop_build_fn; pub mod polymorphic_type_build_fn; pub mod production_build_fn; pub mod struct_build_fn; diff --git a/ast-generator/src/build_fn/polymorphic_enum_loop_build_fn.rs b/ast-generator/src/build_fn/polymorphic_enum_loop_build_fn.rs new file mode 100644 index 0000000..33c781f --- /dev/null +++ b/ast-generator/src/build_fn/polymorphic_enum_loop_build_fn.rs @@ -0,0 +1,100 @@ +use crate::deserialize::util::{make_build_fn_name, make_build_pair}; +use crate::spec::polymorphic_enum_loop_spec::{ + PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, + PolymorphicEnumLoopRuleBuild, PolymorphicEnumLoopRuleBuildChild, + PolymorphicEnumLoopRuleChildOnEach, PolymorphicEnumLoopRulePassThrough, +}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +fn make_pass_through(pass_through: &PolymorphicEnumLoopRulePassThrough) -> TokenStream { + let rule_ident = format_ident!("{}", pass_through.name()); + let inner_build_fn_ident = format_ident!("{}", pass_through.with()); + quote! { + Rule::#rule_ident => { + result = Some(#inner_build_fn_ident(inner_pair)) + } + } +} + +fn make_on_each_child_build(child: &PolymorphicEnumLoopRuleChildOnEach) -> TokenStream { + let child_build_fn_ident = format_ident!("{}", make_build_fn_name(child.rule())); + quote! { + #child_build_fn_ident(inner_pair) + } +} + +fn make_build( + spec: &PolymorphicEnumLoopBuildSpec, + build: &PolymorphicEnumLoopRuleBuild, +) -> TokenStream { + let rule_ident = format_ident!("{}", build.name()); + let variant_type_ident = format_ident!("{}", build.name()); + let return_type_ident = format_ident!("{}", spec.kind()); + let variant_ident = format_ident!("{}", build.variant()); + + let on_each_child = build + .children() + .find(|child| match child { + PolymorphicEnumLoopRuleBuildChild::OnEach(_) => true, + _ => false, + }) + .map(|child| { + match child { + PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => on_each, + _ => unreachable!(), + } + }) + .unwrap(); + + let build_on_each_child = make_on_each_child_build(on_each_child); + + let child_args = build + .children() + .map(|child| match child { + PolymorphicEnumLoopRuleBuildChild::UseCurrent(_) => { + quote! { result.unwrap() } + } + PolymorphicEnumLoopRuleBuildChild::OnEach(_) => quote! { on_each_child }, + }) + .collect::>(); + + quote! { + Rule::#rule_ident => { + let on_each_child = #build_on_each_child; + let built = #variant_type_ident::new(#(#child_args),*); + result = Some(#return_type_ident::#variant_ident(built)) + } + } +} + +fn make_match_arm(spec: &PolymorphicEnumLoopBuildSpec, rule: &PolymorphicEnumLoopRule) -> TokenStream { + match rule { + PolymorphicEnumLoopRule::PassThrough(pass_through) => make_pass_through(pass_through), + PolymorphicEnumLoopRule::Build(build) => make_build(spec, build), + } +} + +pub fn make_polymorphic_enum_loop_build_fn(spec: &PolymorphicEnumLoopBuildSpec) -> 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.kind()); + + let match_arms = spec + .rules() + .map(|rule| make_match_arm(spec, rule)) + .collect::>(); + + quote! { + fn #build_fn_ident(#pair_ident: Pair) -> #return_type_ident { + let mut result: Option<#return_type_ident> = None; + for inner_pair in #pair_ident.into_inner() { + match inner_pair.as_rule() { + #(#match_arms,)*, + _ => unreachable!() + } + } + result.unwrap() + } + } +}