use crate::spec::{ BooleanBuild, ChildSpec, SingleBooleanChildToBuild, SingleChildToBuild, SingleLiteralChildToBuild, SingleTypeChildToBuild, StructBuildSpec, VecChild, VecChildToBuild, }; use convert_case::{Case, Casing}; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use crate::util::make_build_pair; fn make_vec_child_holder(vec_child: &VecChild) -> TokenStream { let (child_ident, child_type_ident) = match vec_child.build() { VecChildToBuild::Type(vec_type_child) => ( format_ident!("{}", vec_type_child.var_name()), format_ident!("{}", vec_type_child.build()), ), }; quote! { let mut #child_ident: Vec> = vec![] } } fn make_single_type_child_holder(single_type_child: &SingleTypeChildToBuild) -> TokenStream { let child_ident = format_ident!("{}", single_type_child.var_name()); let child_type_ident = format_ident!("{}", single_type_child.build()); quote! { let mut #child_ident: Option> = None } } fn make_single_boolean_child_holder( single_boolean_child: &SingleBooleanChildToBuild, ) -> TokenStream { let child_ident = format_ident!("{}", single_boolean_child.var_name()); quote! { let mut #child_ident: bool = false } } fn make_literal_child_holder( literal_child: &SingleLiteralChildToBuild, literal_type_ident: Ident, ) -> TokenStream { let child_ident = format_ident!("{}", literal_child.var_name()); quote! { let mut #child_ident: Option<#literal_type_ident> = None } } fn make_child_holder(child_spec: &ChildSpec) -> Option { match child_spec { ChildSpec::SkipChild(_) => None, ChildSpec::VecChild(vec_child) => Some(make_vec_child_holder(vec_child)), ChildSpec::SingleChild(single_child) => match single_child.build() { SingleChildToBuild::Type(single_type_child) => { Some(make_single_type_child_holder(single_type_child)) } SingleChildToBuild::Boolean(boolean_child) => { Some(make_single_boolean_child_holder(boolean_child)) } SingleChildToBuild::Int(literal_child) => Some(make_literal_child_holder( literal_child, format_ident!("i32"), )), SingleChildToBuild::Long(literal_child) => Some(make_literal_child_holder( literal_child, format_ident!("i64"), )), SingleChildToBuild::Double(literal_child) => Some(make_literal_child_holder( literal_child, format_ident!("f64"), )), SingleChildToBuild::String(literal_child) => Some(make_literal_child_holder( literal_child, format_ident!("String"), )), }, } } fn get_literal_child_ident(literal_child: &SingleLiteralChildToBuild) -> Ident { format_ident!("{}", literal_child.var_name()) } fn make_match_action(child_spec: &ChildSpec) -> TokenStream { match child_spec { ChildSpec::SkipChild(_) => quote! {}, ChildSpec::VecChild(vec_child) => { let (child_name_ident, build_fn_ident) = match vec_child.build() { VecChildToBuild::Type(vec_type_child) => ( format_ident!("{}", vec_type_child.var_name()), format_ident!("{}", vec_type_child.with()), ), }; quote! { #child_name_ident.push(Box::new(#build_fn_ident(inner_pair))) } } ChildSpec::SingleChild(single_child) => match single_child.build() { SingleChildToBuild::Type(single_type_child) => { let child_name_ident = format_ident!("{}", single_type_child.var_name()); let build_fn_ident = format_ident!("{}", single_type_child.with()); quote! { #child_name_ident = Some(Box::new(#build_fn_ident(inner_pair))) } } SingleChildToBuild::Boolean(single_boolean_child) => { let child_name_ident = format_ident!("{}", single_boolean_child.var_name()); match single_boolean_child.build() { BooleanBuild::RulePresent => quote! { #child_name_ident = true }, BooleanBuild::ParseWholePair => quote! { #child_name_ident = Some(inner_pair.as_str().parse().unwrap()) }, } } SingleChildToBuild::Int(literal_child) => { let child_ident = get_literal_child_ident(literal_child); quote! { #child_ident = Some(inner_pair.as_str().parse().unwrap()) } } SingleChildToBuild::Long(literal_child) => { let child_ident = get_literal_child_ident(literal_child); quote! { let as_string = inner_pair.as_str(); let without_el = &as_string[0..(as_string.len() - 1)]; #child_ident = Some(without_el.parse().unwrap()) } } SingleChildToBuild::Double(literal_child) => { let child_ident = get_literal_child_ident(literal_child); quote! { #child_ident = Some(inner_pair.as_str().parse().unwrap()) } } SingleChildToBuild::String(literal_child) => { let child_ident = get_literal_child_ident(literal_child); quote! { #child_ident = Some(inner_pair.as_str().to_string()) } } }, } } fn make_rule_matcher(child_spec: &ChildSpec) -> TokenStream { let rule_ident = match child_spec { ChildSpec::SkipChild(skip_child) => format_ident!("{}", skip_child.rule()), ChildSpec::VecChild(vec_child) => format_ident!("{}", vec_child.rule()), ChildSpec::SingleChild(single_child) => format_ident!("{}", single_child.rule()), }; let action = make_match_action(child_spec); quote! { Rule::#rule_ident => { #action; } } } fn make_child_arg(child_spec: &ChildSpec) -> Option { match child_spec { ChildSpec::SkipChild(_) => None, ChildSpec::VecChild(vec_child) => { let child_ident = match vec_child.build() { VecChildToBuild::Type(vec_type_child) => { format_ident!("{}", vec_type_child.var_name()) } }; Some(quote! { #child_ident }) } ChildSpec::SingleChild(single_child) => match single_child.build() { SingleChildToBuild::Type(single_type_child) => { let child_ident = format_ident!("{}", single_type_child.var_name()); if single_type_child.optional() { Some(quote! { #child_ident }) } else if let Some(or_else) = single_type_child.or_else() { let child_type_ident = format_ident!("{}", single_type_child.build()); let or_else_ident = format_ident!("{}", or_else); Some(quote! { #child_ident.unwrap_or_else(|| Box::new(#child_type_ident::#or_else_ident())) }) } else { Some(quote! { #child_ident.unwrap() }) } } SingleChildToBuild::Boolean(single_boolean_child) => { let child_ident = format_ident!("{}", single_boolean_child.var_name()); Some(quote! { #child_ident }) } SingleChildToBuild::Int(literal_child) => { let child_ident = get_literal_child_ident(literal_child); Some(quote! { #child_ident.unwrap() }) } SingleChildToBuild::Long(literal_child) => { let child_ident = get_literal_child_ident(literal_child); Some(quote! { #child_ident.unwrap() }) } SingleChildToBuild::Double(literal_child) => { let child_ident = get_literal_child_ident(literal_child); Some(quote! { #child_ident.unwrap() }) } SingleChildToBuild::String(literal_child) => { let child_ident = get_literal_child_ident(literal_child); Some(quote! { #child_ident.unwrap() }) } }, } } fn make_return_value_stream(build_spec: &StructBuildSpec) -> TokenStream { let type_ident = format_ident!("{}", build_spec.build()); let child_args = build_spec .children() .iter() .map(|child| make_child_arg(child)) .filter(|child_arg| child_arg.is_some()) .map(|child_arg| child_arg.unwrap()) .collect::>(); quote! { #type_ident::new( #(#child_args,)* ) } } pub fn make_struct_build_fn(build_spec: &StructBuildSpec) -> TokenStream { let build_fn_ident = format_ident!("{}", build_spec.with()); // TODO: get rid of with let pair_ident = format_ident!("{}", make_build_pair(build_spec.var_name())); // TODO: get rid of var_name let return_type_ident = format_ident!("{}", build_spec.build()); let child_holders = build_spec .children() .iter() .map(|child_spec| make_child_holder(child_spec)) .filter(|child_holder| child_holder.is_some()) .map(|child_holder| child_holder.unwrap()) .collect::>(); let rule_matchers = build_spec .children() .iter() .map(|child_spec| make_rule_matcher(child_spec)) .collect::>(); let iter_stream = quote! { for inner_pair in #pair_ident.into_inner() { match inner_pair.as_rule() { #(#rule_matchers)* } } }; let new_stream = make_return_value_stream(build_spec); quote! { fn #build_fn_ident(#pair_ident: Pair) -> #return_type_ident { #(#child_holders;)* #iter_stream #new_stream } } } #[cfg(test)] mod tests { use super::*; use crate::spec::VecTypeChildToBuild; #[test] fn vec_child_holder() { let vec_child = VecChild::new( "test_child", "Test", VecChildToBuild::Type(VecTypeChildToBuild::new( "TestType", "test_child", "build_test_child", )), ); assert_eq!( make_vec_child_holder(&vec_child).to_string(), quote! { let mut test_child: Vec> = vec![] } .to_string() ); } #[test] fn single_type_child_holder() { let single_type_child = SingleTypeChildToBuild::from_build_or_rule("TestType", None, false); assert_eq!( make_single_type_child_holder(&single_type_child).to_string(), quote! { let mut test_type: Option> = None } .to_string() ); } #[test] fn single_boolean_child_holder() { let single_boolean_child = SingleBooleanChildToBuild::new("test_child", BooleanBuild::RulePresent); assert_eq!( make_single_boolean_child_holder(&single_boolean_child).to_string(), quote! { let mut test_child: bool = false } .to_string() ); } }