deimos-lang/ast-generator/src/struct_build_fn.rs

319 lines
12 KiB
Rust

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<Box<#child_type_ident>> = 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<Box<#child_type_ident>> = 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<TokenStream> {
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<TokenStream> {
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::<Vec<_>>();
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::<Vec<_>>();
let rule_matchers = build_spec
.children()
.iter()
.map(|child_spec| make_rule_matcher(child_spec))
.collect::<Vec<_>>();
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<Rule>) -> #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<Box<TestType>> = 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<Box<TestType>> = 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()
);
}
}