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

274 lines
9.1 KiB
Rust

use crate::deserialize::util::{make_build_fn_name, make_build_pair};
use crate::spec::struct_spec::{
MemberChildBuild, NodeMemberBuild, StructChild, StructSpec, VecChild, VecChildBuild,
};
use crate::spec::{SpecialChild, SpecialChildKind};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
fn make_preamble(spec: &StructSpec, pair_ident: &Ident) -> TokenStream {
if spec.children().any(StructChild::is_special) {
quote! {
let as_span = #pair_ident.as_span();
}
} else {
quote! {}
}
}
fn make_special_children(spec: &StructSpec) -> Vec<TokenStream> {
spec.children()
.map(StructChild::unwrap_special)
.filter(Option::is_some)
.map(Option::unwrap)
.map(|special_child| {
let child_ident = format_ident!("{}", special_child.name());
match special_child.kind() {
SpecialChildKind::FileId => {
quote! {
let #child_ident = file_id
}
}
SpecialChildKind::Range => {
quote! {
let #child_ident = Range {
start: as_span.start(),
end: as_span.end()
}
}
}
}
})
.collect::<Vec<_>>()
}
fn make_vec_child_holder(vec_child: &VecChild) -> TokenStream {
let child_ident = format_ident!("{}", vec_child.name());
match vec_child.build() {
VecChildBuild::String(_) => {
quote! {
let mut #child_ident: Vec<String> = vec![]
}
}
VecChildBuild::Node(node_build) => {
let child_type_ident = format_ident!("{}", node_build.kind());
quote! {
let mut #child_ident: Vec<Box<#child_type_ident>> = vec![]
}
}
}
}
fn make_node_child_holder(name: &str, node_child: &NodeMemberBuild) -> TokenStream {
let child_ident = format_ident!("{}", name);
let child_type_ident = format_ident!("{}", node_child.kind());
quote! {
let mut #child_ident: Option<Box<#child_type_ident>> = None
}
}
fn make_boolean_child_holder(name: &str) -> TokenStream {
let child_ident = format_ident!("{}", name);
quote! {
let mut #child_ident: bool = false
}
}
fn make_child_holder(child_spec: &StructChild) -> Option<TokenStream> {
match child_spec {
StructChild::SkipChild(_) => None,
StructChild::VecChild(vec_child) => Some(make_vec_child_holder(vec_child)),
StructChild::MemberChild(member_child) => match member_child.build() {
MemberChildBuild::Node(node_child) => {
Some(make_node_child_holder(member_child.name(), node_child))
}
MemberChildBuild::Boolean(_) => Some(make_boolean_child_holder(member_child.name())),
},
StructChild::Special(_) => None,
}
}
fn make_vec_child_match_action(vec_child: &VecChild) -> TokenStream {
let child_name_ident = format_ident!("{}", vec_child.name());
match vec_child.build() {
VecChildBuild::String(string_build) => {
let build_fn_ident = format_ident!("{}", string_build.with());
quote! {
#child_name_ident.push(#build_fn_ident(file_id, inner_pair))
}
}
VecChildBuild::Node(node_build) => {
let build_fn_ident = format_ident!("{}", node_build.with());
quote! {
#child_name_ident.push(Box::new(#build_fn_ident(file_id, inner_pair)))
}
}
}
}
fn make_node_member_child_match_action(name: &str, node_child: &NodeMemberBuild) -> TokenStream {
let child_name_ident = format_ident!("{}", name);
let build_fn_ident = format_ident!("{}", node_child.with());
quote! {
#child_name_ident = Some(Box::new(#build_fn_ident(file_id, inner_pair)))
}
}
fn make_boolean_member_child_match_action(name: &str) -> TokenStream {
let child_name_ident = format_ident!("{}", name);
quote! {
#child_name_ident = true
}
}
fn make_rule_matcher(child_spec: &StructChild) -> Option<TokenStream> {
match child_spec {
StructChild::SkipChild(skip_child) => {
let rule_ident = format_ident!("{}", skip_child.rule());
Some(quote! {
Rule::#rule_ident => {}
})
}
StructChild::VecChild(vec_child) => {
let rule_ident = format_ident!("{}", vec_child.rule());
let action = make_vec_child_match_action(vec_child);
Some(quote! {
Rule::#rule_ident => { #action }
})
}
StructChild::MemberChild(member_child) => match member_child.build() {
MemberChildBuild::Node(node_member_build) => {
let rule_ident = format_ident!("{}", member_child.rule());
let action =
make_node_member_child_match_action(member_child.name(), node_member_build);
Some(quote! {
Rule::#rule_ident => { #action }
})
}
MemberChildBuild::Boolean(_) => {
let rule_ident = format_ident!("{}", member_child.rule());
let action = make_boolean_member_child_match_action(member_child.name());
Some(quote! {
Rule::#rule_ident => { #action }
})
}
},
StructChild::Special(_) => None,
}
}
fn make_vec_child_arg(vec_child: &VecChild) -> TokenStream {
let child_ident = format_ident!("{}", vec_child.name());
quote! { #child_ident }
}
fn make_node_member_child_arg(
name: &str,
optional: bool,
node_child: &NodeMemberBuild,
) -> TokenStream {
let child_ident = format_ident!("{}", name);
if optional {
quote! { #child_ident }
} else if let Some(or_else) = node_child.or_else() {
let child_type_ident = format_ident!("{}", node_child.kind());
let or_else_ident = format_ident!("{}", or_else);
quote! {
#child_ident.unwrap_or_else(|| Box::new(#child_type_ident::#or_else_ident()))
}
} else {
quote! { #child_ident.unwrap() }
}
}
fn make_boolean_member_child_arg(name: &str) -> TokenStream {
let child_ident = format_ident!("{}", name);
quote! { #child_ident }
}
fn make_special_child_arg(special_child: &SpecialChild) -> TokenStream {
let child_ident = format_ident!("{}", special_child.name());
quote! { #child_ident }
}
fn make_child_arg(child_spec: &StructChild, pair_ident: &Ident) -> Option<TokenStream> {
match child_spec {
StructChild::SkipChild(_) => None,
StructChild::VecChild(vec_child) => Some(make_vec_child_arg(vec_child)),
StructChild::MemberChild(member_child) => match member_child.build() {
MemberChildBuild::Node(node_member_build) => Some(make_node_member_child_arg(
member_child.name(),
member_child.optional(),
node_member_build,
)),
MemberChildBuild::Boolean(_) => {
Some(make_boolean_member_child_arg(member_child.name()))
}
},
StructChild::Special(special_child) => Some(make_special_child_arg(special_child)),
}
}
fn make_return_value_stream(build_spec: &StructSpec, pair_ident: &Ident) -> TokenStream {
let type_ident = format_ident!("{}", build_spec.build());
let child_args = build_spec
.children()
.map(|child| make_child_arg(child, pair_ident))
.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: &StructSpec) -> TokenStream {
let build_fn_ident = format_ident!("{}", make_build_fn_name(build_spec.build()));
let pair_ident = format_ident!("{}", make_build_pair(build_spec.build()));
let return_type_ident = format_ident!("{}", build_spec.build());
let preamble = make_preamble(build_spec, &pair_ident);
let special_children = make_special_children(build_spec);
let child_holders = build_spec
.children()
.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()
.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)*
_ => panic!("Unexpected rule: {:?}", inner_pair.as_rule())
}
}
};
let new_stream = make_return_value_stream(build_spec, &pair_ident);
quote! {
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
#preamble
#(#special_children;)*
#(#child_holders;)*
#iter_stream
#new_stream
}
}
}