274 lines
9.1 KiB
Rust
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
|
|
}
|
|
}
|
|
}
|