Refactor ast-generation.
This commit is contained in:
parent
fcca1b7aba
commit
8b310ad5d4
127
ast-generator/src/build_fn_gen.rs
Normal file
127
ast-generator/src/build_fn_gen.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
use crate::spec::{
|
||||||
|
BuildBooleanOn, ChildSpec, SingleChildToBuild, StructBuildSpec, VecChildToBuild,
|
||||||
|
};
|
||||||
|
use convert_case::{Case, Casing};
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
|
||||||
|
pub fn make_build_fn_name(s: &str) -> String {
|
||||||
|
format!("build_{}", s.to_case(Case::Snake))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_child_holder(child_spec: &ChildSpec) -> Option<TokenStream> {
|
||||||
|
match child_spec {
|
||||||
|
ChildSpec::SkipChild(_) => None,
|
||||||
|
ChildSpec::VecChild(vec_child) => {
|
||||||
|
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()),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
Some(quote! {
|
||||||
|
let mut #child_ident: Vec<#child_type_ident> = vec![]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ChildSpec::SingleChild(single_child) => match single_child.build() {
|
||||||
|
SingleChildToBuild::Type(single_type_child) => {
|
||||||
|
let child_ident = format_ident!("{}", single_type_child.var_name());
|
||||||
|
let child_type_ident = format_ident!("{}", single_type_child.build());
|
||||||
|
Some(quote! {
|
||||||
|
let mut #child_ident: Option<#child_type_ident> = None;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
SingleChildToBuild::Boolean(boolean_child) => {
|
||||||
|
let child_ident = format_ident!("{}", boolean_child.var_name());
|
||||||
|
Some(quote! {
|
||||||
|
let #child_ident: bool = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(#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(#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.on() {
|
||||||
|
BuildBooleanOn::RulePresent => quote! {
|
||||||
|
#child_name_ident = true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_struct_build_fn(build_spec: &StructBuildSpec) -> TokenStream {
|
||||||
|
let build_fn_ident = format_ident!("{}", build_spec.with());
|
||||||
|
let pair_ident = format_ident!("{}_pair", build_spec.build().to_case(Case::Snake));
|
||||||
|
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)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
fn #build_fn_ident(#pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||||
|
#(#child_holders;)*
|
||||||
|
|
||||||
|
#iter_stream
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
167
ast-generator/src/deserialize.rs
Normal file
167
ast-generator/src/deserialize.rs
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
use crate::build_fn_gen::make_build_fn_name;
|
||||||
|
use crate::spec::{
|
||||||
|
BuildBooleanOn, BuildSpec, ChildSpec, EnumBuildSpec, EnumRule, SingleBooleanChildToBuild,
|
||||||
|
SingleChild, SingleChildToBuild, SingleTypeChildToBuild, SkipChild, StructBuildSpec, VecChild,
|
||||||
|
VecChildToBuild, VecTypeChildToBuild,
|
||||||
|
};
|
||||||
|
use convert_case::{Case, Casing};
|
||||||
|
use yaml_rust2::{Yaml, YamlLoader};
|
||||||
|
|
||||||
|
fn get_skip(skip: &Yaml) -> bool {
|
||||||
|
skip.as_bool().unwrap_or_else(|| false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_vec(vec: &Yaml) -> bool {
|
||||||
|
vec.as_bool().unwrap_or_else(|| false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_vec_child_to_build(build: &Yaml, rule: &str) -> VecChildToBuild {
|
||||||
|
if build.is_hash() {
|
||||||
|
let build_type = build["build"].as_str().unwrap();
|
||||||
|
let var_name = build["var"]
|
||||||
|
.as_str()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.unwrap_or_else(|| build_type.to_case(Case::Snake));
|
||||||
|
let with = build["with"]
|
||||||
|
.as_str()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.unwrap_or_else(|| make_build_fn_name(build_type));
|
||||||
|
|
||||||
|
VecChildToBuild::Type(VecTypeChildToBuild::new(build_type, &var_name, &with))
|
||||||
|
} else {
|
||||||
|
let build_as_str = build.as_str().unwrap_or(rule);
|
||||||
|
VecChildToBuild::Type(VecTypeChildToBuild::new(
|
||||||
|
build_as_str,
|
||||||
|
build_as_str.to_case(Case::Snake).as_str(),
|
||||||
|
make_build_fn_name(build_as_str).as_str(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_vec_child(name: &str, rule: &str, build: &Yaml) -> ChildSpec {
|
||||||
|
ChildSpec::VecChild(VecChild::new(
|
||||||
|
name,
|
||||||
|
rule,
|
||||||
|
get_vec_child_to_build(build, rule),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_single_child_to_build(name: &str, rule: &str, build: &Yaml) -> SingleChildToBuild {
|
||||||
|
if build.is_hash() {
|
||||||
|
let r#type = build["type"].as_str().unwrap();
|
||||||
|
let var_name = build["var"]
|
||||||
|
.as_str()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.unwrap_or(name.to_string());
|
||||||
|
let on = build["on"].as_str().unwrap();
|
||||||
|
if r#type.eq("boolean") && on.eq("rule_present") {
|
||||||
|
SingleChildToBuild::Boolean(SingleBooleanChildToBuild::new(
|
||||||
|
&var_name,
|
||||||
|
BuildBooleanOn::RulePresent,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
todo!("currently on boolean types with on: rule_present are supported")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match build.as_str() {
|
||||||
|
Some(s) => SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(s)),
|
||||||
|
None => SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(rule)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_single_child(name: &str, rule: &str, build: &Yaml) -> ChildSpec {
|
||||||
|
ChildSpec::SingleChild(SingleChild::new(
|
||||||
|
name,
|
||||||
|
rule,
|
||||||
|
get_single_child_to_build(name, rule, build),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_child_specs(children: &Yaml) -> Vec<ChildSpec> {
|
||||||
|
children
|
||||||
|
.as_vec()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|child_spec| {
|
||||||
|
if child_spec.is_hash() {
|
||||||
|
let as_hash = child_spec.as_hash().unwrap();
|
||||||
|
let only_pair = as_hash.iter().next().unwrap();
|
||||||
|
|
||||||
|
let name = only_pair.0.as_str().unwrap();
|
||||||
|
let props = only_pair.1;
|
||||||
|
|
||||||
|
let rule = props["rule"].as_str().unwrap();
|
||||||
|
|
||||||
|
if get_skip(&props["skip"]) {
|
||||||
|
return ChildSpec::SkipChild(SkipChild::new(name, rule));
|
||||||
|
}
|
||||||
|
|
||||||
|
let build = &props["build"];
|
||||||
|
|
||||||
|
if get_vec(&props["vec"]) {
|
||||||
|
get_vec_child(name, rule, build)
|
||||||
|
} else {
|
||||||
|
get_single_child(name, rule, build)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ChildSpec::SingleChild(SingleChild::from_name_snake(child_spec.as_str().unwrap()))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_enum_rule_specs(rule_specs: &Yaml) -> Vec<EnumRule> {
|
||||||
|
rule_specs
|
||||||
|
.as_vec()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|rule_spec| {
|
||||||
|
if rule_spec.is_hash() {
|
||||||
|
let rule = rule_spec["rule"].as_str().unwrap();
|
||||||
|
let build = rule_spec["build"].as_str().unwrap();
|
||||||
|
let with = if !rule_spec["with"].is_badvalue() {
|
||||||
|
rule_spec.as_str().unwrap().to_string()
|
||||||
|
} else {
|
||||||
|
make_build_fn_name(build)
|
||||||
|
};
|
||||||
|
|
||||||
|
EnumRule::new(rule, build, &with)
|
||||||
|
} else {
|
||||||
|
EnumRule::from_rule(rule_spec.as_str().unwrap())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_build_spec(build_spec_name: &Yaml, build_spec: &Yaml) -> BuildSpec {
|
||||||
|
let build_spec_name_pascal = build_spec_name.as_str().unwrap();
|
||||||
|
let children = &build_spec["children"];
|
||||||
|
|
||||||
|
if children.is_array() {
|
||||||
|
let child_specs = get_child_specs(children);
|
||||||
|
BuildSpec::Struct(StructBuildSpec::from_name(
|
||||||
|
build_spec_name_pascal,
|
||||||
|
child_specs,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
let rule_specs = &build_spec["rules"];
|
||||||
|
if rule_specs.is_array() {
|
||||||
|
let enum_rules = get_enum_rule_specs(rule_specs);
|
||||||
|
BuildSpec::Enum(EnumBuildSpec::from_name(build_spec_name_pascal, enum_rules))
|
||||||
|
} else {
|
||||||
|
panic!("either children or rules must be present on the build spec");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_yaml_spec(yaml: &str) -> Vec<BuildSpec> {
|
||||||
|
let docs = YamlLoader::load_from_str(yaml).unwrap();
|
||||||
|
let doc = &docs[0];
|
||||||
|
|
||||||
|
doc.as_hash()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|(build_spec_name, build_spec)| deserialize_build_spec(build_spec_name, build_spec))
|
||||||
|
.collect()
|
||||||
|
}
|
@ -1,310 +1,47 @@
|
|||||||
use convert_case::{Case, Casing};
|
mod build_fn_gen;
|
||||||
use proc_macro2::{Ident, TokenStream};
|
mod deserialize;
|
||||||
use quote::{format_ident, quote};
|
mod spec;
|
||||||
use yaml_rust2::YamlLoader;
|
mod type_gen;
|
||||||
|
|
||||||
enum BuildSpec {
|
use crate::build_fn_gen::make_struct_build_fn;
|
||||||
Enum(EnumBuildSpec),
|
use crate::type_gen::make_type;
|
||||||
Struct(StructBuildSpec),
|
use proc_macro2::TokenStream;
|
||||||
}
|
use quote::quote;
|
||||||
|
use spec::BuildSpec;
|
||||||
|
use syn::File;
|
||||||
|
|
||||||
struct EnumBuildSpec {
|
fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) {
|
||||||
name: String,
|
println!("*** BuildSpec ***");
|
||||||
build: String,
|
match build_spec {
|
||||||
rules: Vec<EnumRule>,
|
BuildSpec::Enum(enum_build_spec) => {
|
||||||
|
println!("Spec name: {}", enum_build_spec.name());
|
||||||
}
|
}
|
||||||
|
BuildSpec::Struct(struct_build_spec) => {
|
||||||
struct EnumRule {
|
println!("Spec name: {}", struct_build_spec.name());
|
||||||
rule: String,
|
|
||||||
build: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EnumRule {
|
|
||||||
fn from_rule_name(rule_name: &str) -> Self {
|
|
||||||
Self {
|
|
||||||
rule: rule_name.to_string(),
|
|
||||||
build: rule_name.to_string(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
println!("{:#?}", token_stream);
|
||||||
fn new(rule: &str, build: &str) -> Self {
|
let parsed: File = syn::parse2(token_stream.clone()).unwrap();
|
||||||
Self {
|
println!("{}", prettyplease::unparse(&parsed));
|
||||||
rule: rule.to_string(),
|
|
||||||
build: build.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct StructBuildSpec {
|
|
||||||
name: String,
|
|
||||||
build: String,
|
|
||||||
children: Vec<ChildSpec>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct ChildSpec {
|
|
||||||
name: String,
|
|
||||||
rule: String,
|
|
||||||
vec: bool,
|
|
||||||
skip: bool,
|
|
||||||
build: ChildToBuild,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ChildSpec {
|
|
||||||
fn from_child_name(child_name: &str) -> Self {
|
|
||||||
Self {
|
|
||||||
name: child_name.to_string(),
|
|
||||||
rule: child_name.to_case(Case::Pascal),
|
|
||||||
skip: false,
|
|
||||||
vec: false,
|
|
||||||
build: ChildToBuild::TypeRef(child_name.to_case(Case::Pascal)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(name: &str, rule: &str, vec: bool, skip: bool, build: ChildToBuild) -> Self {
|
|
||||||
Self {
|
|
||||||
name: name.to_string(),
|
|
||||||
rule: rule.to_string(),
|
|
||||||
vec,
|
|
||||||
skip,
|
|
||||||
build,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum ChildToBuild {
|
|
||||||
TypeRef(String),
|
|
||||||
Boolean { on: BuildBooleanOn },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum BuildBooleanOn {
|
|
||||||
RulePresent,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_enum_type(build_spec: &EnumBuildSpec) -> TokenStream {
|
|
||||||
let children: Vec<TokenStream> = build_spec
|
|
||||||
.rules
|
|
||||||
.iter()
|
|
||||||
.map(|rule| {
|
|
||||||
let member_name_ident = format_ident!("{}", rule.rule);
|
|
||||||
let child_name_ident = format_ident!("{}", rule.build);
|
|
||||||
quote! {
|
|
||||||
#member_name_ident(#child_name_ident)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let type_name_ident = format_ident!("{}", build_spec.build);
|
|
||||||
quote! {
|
|
||||||
pub enum #type_name_ident {
|
|
||||||
#(#children),*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_child_type_ident(child_spec: &ChildSpec) -> Ident {
|
|
||||||
match &child_spec.build {
|
|
||||||
ChildToBuild::TypeRef(type_name) => format_ident!("{}", type_name),
|
|
||||||
ChildToBuild::Boolean { on: _ } => format_ident!("bool"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_child_ident(child_spec: &ChildSpec) -> Ident {
|
|
||||||
format_ident!("{}", child_spec.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_struct_type(build_spec: &StructBuildSpec) -> TokenStream {
|
|
||||||
let mut member_names: Vec<Ident> = vec![];
|
|
||||||
let mut annotated_members: Vec<TokenStream> = vec![];
|
|
||||||
let mut accessors: Vec<TokenStream> = vec![];
|
|
||||||
|
|
||||||
for child_spec in build_spec.children.iter() {
|
|
||||||
println!("{:?}", child_spec);
|
|
||||||
if !child_spec.skip {
|
|
||||||
let child_ident = make_child_ident(child_spec);
|
|
||||||
member_names.push(child_ident.clone());
|
|
||||||
|
|
||||||
let child_type_ident = make_child_type_ident(child_spec);
|
|
||||||
let type_annotation = if child_spec.vec {
|
|
||||||
quote! { Vec<#child_type_ident> }
|
|
||||||
} else {
|
|
||||||
quote! { #child_type_ident }
|
|
||||||
};
|
|
||||||
annotated_members.push(quote! {
|
|
||||||
#child_ident: #type_annotation
|
|
||||||
});
|
|
||||||
|
|
||||||
accessors.push(quote! {
|
|
||||||
pub fn #child_ident(&self) -> &#type_annotation {
|
|
||||||
&self.#child_ident
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let type_ident = format_ident!("{}", build_spec.build);
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
pub struct #type_ident {
|
|
||||||
#(#annotated_members),*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl #type_ident {
|
|
||||||
pub fn new(#(#annotated_members),*) -> Self {
|
|
||||||
Self {
|
|
||||||
#(#member_names),*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#(#accessors)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_child_holder(child_spec: &ChildSpec) -> TokenStream {
|
|
||||||
if child_spec.skip {
|
|
||||||
return quote! {};
|
|
||||||
}
|
|
||||||
let child_ident = make_child_ident(child_spec);
|
|
||||||
let child_type_ident = make_child_type_ident(child_spec);
|
|
||||||
|
|
||||||
if child_spec.vec {
|
|
||||||
quote! { let mut #child_ident: Vec<#child_type_ident> = vec![] }
|
|
||||||
} else {
|
|
||||||
match &child_spec.build {
|
|
||||||
ChildToBuild::TypeRef(_) => quote! {
|
|
||||||
let mut #child_ident: Option<#child_type_ident> = None
|
|
||||||
},
|
|
||||||
ChildToBuild::Boolean { on } => match on {
|
|
||||||
BuildBooleanOn::RulePresent => {
|
|
||||||
quote! { let mut #child_ident: bool = false }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_struct_build_fn(build_spec: &StructBuildSpec) -> TokenStream {
|
|
||||||
let child_holders = build_spec
|
|
||||||
.children
|
|
||||||
.iter()
|
|
||||||
.map(|child_spec| make_child_holder(child_spec))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let build_fn_identifier = format_ident!("build_{}", build_spec.name);
|
|
||||||
let return_type_ident = format_ident!("{}", build_spec.build);
|
|
||||||
quote! {
|
|
||||||
fn #build_fn_identifier() -> #return_type_ident {
|
|
||||||
#(#child_holders);*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_yaml_file() -> Vec<BuildSpec> {
|
|
||||||
let docs = YamlLoader::load_from_str(include_str!("../../src/parser/ast.yaml")).unwrap();
|
|
||||||
let doc = &docs[0];
|
|
||||||
let mut build_specs: Vec<BuildSpec> = vec![];
|
|
||||||
|
|
||||||
for (build_spec_name, build_spec) in doc.as_hash().unwrap() {
|
|
||||||
let build_spec_name_pascal = build_spec_name.as_str().unwrap();
|
|
||||||
let children = &build_spec["children"];
|
|
||||||
|
|
||||||
if children.is_array() {
|
|
||||||
let mut child_specs: Vec<ChildSpec> = vec![];
|
|
||||||
for child_spec in children.as_vec().unwrap() {
|
|
||||||
if child_spec.is_hash() {
|
|
||||||
let as_hash = child_spec.as_hash().unwrap();
|
|
||||||
let only_pair = as_hash.iter().next().unwrap();
|
|
||||||
|
|
||||||
let name = only_pair.0.as_str().unwrap();
|
|
||||||
let props = only_pair.1;
|
|
||||||
|
|
||||||
let rule = props["rule"].as_str().unwrap();
|
|
||||||
|
|
||||||
let skip = if !props["skip"].is_badvalue() {
|
|
||||||
props["skip"].as_bool().unwrap()
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
let vec = if !props["vec"].is_badvalue() {
|
|
||||||
props["vec"].as_bool().unwrap()
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
let build = &props["build"];
|
|
||||||
let child_to_build = if build.is_hash() {
|
|
||||||
let r#type = build["type"].as_str().unwrap();
|
|
||||||
let on = build["on"].as_str().unwrap();
|
|
||||||
if r#type.eq("boolean") && on.eq("rule_present") {
|
|
||||||
ChildToBuild::Boolean {
|
|
||||||
on: BuildBooleanOn::RulePresent,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
todo!("currently on boolean types are supported")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match build.as_str() {
|
|
||||||
Some(s) => ChildToBuild::TypeRef(s.to_string()),
|
|
||||||
None => ChildToBuild::TypeRef(rule.to_string()),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
child_specs.push(ChildSpec::new(name, rule, vec, skip, child_to_build))
|
|
||||||
} else {
|
|
||||||
child_specs.push(ChildSpec::from_child_name(child_spec.as_str().unwrap()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
build_specs.push(BuildSpec::Struct(StructBuildSpec {
|
|
||||||
name: build_spec_name_pascal.to_string(),
|
|
||||||
build: build_spec_name_pascal.to_string(),
|
|
||||||
children: child_specs,
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
let rule_specs = &build_spec["rules"];
|
|
||||||
if rule_specs.is_array() {
|
|
||||||
let mut enum_rules: Vec<EnumRule> = vec![];
|
|
||||||
for rule_spec in rule_specs.as_vec().unwrap() {
|
|
||||||
if rule_spec.is_hash() {
|
|
||||||
let rule = rule_spec["rule"].as_str().unwrap();
|
|
||||||
let build = rule_spec["build"].as_str().unwrap();
|
|
||||||
enum_rules.push(EnumRule::new(rule, build));
|
|
||||||
} else {
|
|
||||||
enum_rules.push(EnumRule::from_rule_name(rule_spec.as_str().unwrap()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panic!("either children or rules must be present on the build spec");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
build_specs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_dump() -> String {
|
pub fn test_dump() -> String {
|
||||||
let build_specs = deserialize_yaml_file();
|
let build_specs = deserialize::deserialize_yaml_spec(include_str!("../../src/parser/ast.yaml"));
|
||||||
let mut streams: Vec<TokenStream> = vec![];
|
let mut streams: Vec<TokenStream> = vec![];
|
||||||
|
|
||||||
for build_spec in &build_specs {
|
for build_spec in &build_specs {
|
||||||
match build_spec {
|
let type_stream = make_type(build_spec);
|
||||||
BuildSpec::Enum(enum_spec) => {
|
debug_built_spec(build_spec, &type_stream);
|
||||||
streams.push(make_enum_type(enum_spec));
|
streams.push(type_stream);
|
||||||
}
|
|
||||||
BuildSpec::Struct(struct_spec) => {
|
|
||||||
streams.push(make_struct_type(struct_spec));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for build_spec in &build_specs {
|
for build_spec in &build_specs {
|
||||||
match build_spec {
|
match build_spec {
|
||||||
BuildSpec::Enum(_) => {}
|
BuildSpec::Enum(_) => {}
|
||||||
BuildSpec::Struct(struct_spec) => {
|
BuildSpec::Struct(struct_spec) => {
|
||||||
streams.push(make_struct_type(struct_spec));
|
let struct_build_fn_stream = make_struct_build_fn(struct_spec);
|
||||||
|
debug_built_spec(build_spec, &struct_build_fn_stream);
|
||||||
|
streams.push(struct_build_fn_stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -312,7 +49,6 @@ pub fn test_dump() -> String {
|
|||||||
let combined = quote! {
|
let combined = quote! {
|
||||||
#(#streams)*
|
#(#streams)*
|
||||||
};
|
};
|
||||||
|
let file: File = syn::parse2(combined).unwrap();
|
||||||
let syntax_tree = syn::parse2(combined).unwrap();
|
prettyplease::unparse(&file)
|
||||||
prettyplease::unparse(&syntax_tree)
|
|
||||||
}
|
}
|
||||||
|
328
ast-generator/src/spec.rs
Normal file
328
ast-generator/src/spec.rs
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
use crate::build_fn_gen::make_build_fn_name;
|
||||||
|
use convert_case::{Case, Casing};
|
||||||
|
|
||||||
|
pub enum BuildSpec {
|
||||||
|
Enum(EnumBuildSpec),
|
||||||
|
Struct(StructBuildSpec),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EnumBuildSpec {
|
||||||
|
name: String,
|
||||||
|
build: String,
|
||||||
|
rules: Vec<EnumRule>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnumBuildSpec {
|
||||||
|
pub fn from_name(name: &str, rules: Vec<EnumRule>) -> Self {
|
||||||
|
EnumBuildSpec {
|
||||||
|
name: name.to_string(),
|
||||||
|
build: name.to_string(),
|
||||||
|
rules,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The top-level key for the build spec in the yaml file.
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The enum type to be built, in Pascal case.
|
||||||
|
pub fn build(&self) -> &str {
|
||||||
|
&self.build
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The individual rule specs.
|
||||||
|
pub fn rules(&self) -> &[EnumRule] {
|
||||||
|
&self.rules
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EnumRule {
|
||||||
|
rule: String,
|
||||||
|
build: String,
|
||||||
|
with: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnumRule {
|
||||||
|
pub fn from_rule(rule: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
rule: rule.to_string(),
|
||||||
|
build: rule.to_string(),
|
||||||
|
with: make_build_fn_name(rule),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(rule: &str, build: &str, with: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
rule: rule.to_string(),
|
||||||
|
build: build.to_string(),
|
||||||
|
with: with.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The enum rule to match, in Pascal case.
|
||||||
|
pub fn rule(&self) -> &str {
|
||||||
|
&self.rule
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type to build, in Pascal case.
|
||||||
|
pub fn build(&self) -> &str {
|
||||||
|
&self.build
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The build-fn name, in snake case.
|
||||||
|
pub fn with(&self) -> &str {
|
||||||
|
&self.with
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StructBuildSpec {
|
||||||
|
name: String,
|
||||||
|
build: String,
|
||||||
|
var_name: String,
|
||||||
|
with: String,
|
||||||
|
children: Vec<ChildSpec>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructBuildSpec {
|
||||||
|
pub fn from_name(name: &str, child_specs: Vec<ChildSpec>) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
build: name.to_string(),
|
||||||
|
var_name: name.to_case(Case::Snake),
|
||||||
|
with: make_build_fn_name(name),
|
||||||
|
children: child_specs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The top-level name of this build spec.
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type to be built, in Pascal case.
|
||||||
|
pub fn build(&self) -> &str {
|
||||||
|
&self.build
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The name of the variable to be built, in snake case.
|
||||||
|
pub fn var_name(&self) -> &str {
|
||||||
|
&self.var_name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The build-fn name, in snake case.
|
||||||
|
pub fn with(&self) -> &str {
|
||||||
|
&self.with
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The children for this build spec.
|
||||||
|
pub fn children(&self) -> &[ChildSpec] {
|
||||||
|
&self.children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ChildSpec {
|
||||||
|
SkipChild(SkipChild),
|
||||||
|
VecChild(VecChild),
|
||||||
|
SingleChild(SingleChild),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SkipChild {
|
||||||
|
name: String,
|
||||||
|
rule: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SkipChild {
|
||||||
|
pub fn new(name: &str, rule: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
rule: rule.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The name of this child spec.
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The grammar rule to match.
|
||||||
|
pub fn rule(&self) -> &str {
|
||||||
|
&self.rule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VecChild {
|
||||||
|
name: String,
|
||||||
|
rule: String,
|
||||||
|
build: VecChildToBuild,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VecChild {
|
||||||
|
pub fn new(name: &str, rule: &str, build: VecChildToBuild) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
rule: rule.to_string(),
|
||||||
|
build,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The name of this child.
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The rule to match to build this child.
|
||||||
|
pub fn rule(&self) -> &str {
|
||||||
|
&self.rule
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The build info for this child.
|
||||||
|
pub fn build(&self) -> &VecChildToBuild {
|
||||||
|
&self.build
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum VecChildToBuild {
|
||||||
|
Type(VecTypeChildToBuild),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VecTypeChildToBuild {
|
||||||
|
build: String,
|
||||||
|
var_name: String,
|
||||||
|
with: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VecTypeChildToBuild {
|
||||||
|
pub fn new(build: &str, var_name: &str, with: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
build: build.to_string(),
|
||||||
|
var_name: var_name.to_string(),
|
||||||
|
with: with.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type to build, in Pascal case.
|
||||||
|
pub fn build(&self) -> &str {
|
||||||
|
&self.build
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The name of the variable to build, in snake case.
|
||||||
|
pub fn var_name(&self) -> &str {
|
||||||
|
&self.var_name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The build-fn name.
|
||||||
|
pub fn with(&self) -> &str {
|
||||||
|
&self.with
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SingleChild {
|
||||||
|
name: String,
|
||||||
|
rule: String,
|
||||||
|
build: SingleChildToBuild,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SingleChild {
|
||||||
|
pub fn from_name_snake(name: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
rule: name.to_case(Case::Pascal),
|
||||||
|
build: SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(
|
||||||
|
&name.to_case(Case::Pascal),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(name: &str, rule: &str, build: SingleChildToBuild) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
rule: rule.to_string(),
|
||||||
|
build,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The name of this child in the yaml file, in snake case.
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The grammar rule to match to build this child.
|
||||||
|
pub fn rule(&self) -> &str {
|
||||||
|
&self.rule
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The specification for what to actually build.
|
||||||
|
pub fn build(&self) -> &SingleChildToBuild {
|
||||||
|
&self.build
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SingleChildToBuild {
|
||||||
|
Type(SingleTypeChildToBuild),
|
||||||
|
Boolean(SingleBooleanChildToBuild),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SingleTypeChildToBuild {
|
||||||
|
build: String,
|
||||||
|
var_name: String,
|
||||||
|
with: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SingleTypeChildToBuild {
|
||||||
|
pub fn from_build_or_rule(build_or_rule: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
build: build_or_rule.to_string(),
|
||||||
|
var_name: build_or_rule.to_case(Case::Snake),
|
||||||
|
with: make_build_fn_name(build_or_rule),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type to build, in Pascal case.
|
||||||
|
pub fn build(&self) -> &str {
|
||||||
|
&self.build
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The variable name to build, in snake case.
|
||||||
|
pub fn var_name(&self) -> &str {
|
||||||
|
&self.var_name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The build-fn name.
|
||||||
|
pub fn with(&self) -> &str {
|
||||||
|
&self.with
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SingleBooleanChildToBuild {
|
||||||
|
var_name: String,
|
||||||
|
on: BuildBooleanOn,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SingleBooleanChildToBuild {
|
||||||
|
pub fn new(var_name: &str, on: BuildBooleanOn) -> Self {
|
||||||
|
Self {
|
||||||
|
var_name: var_name.to_string(),
|
||||||
|
on,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn var_name(&self) -> &str {
|
||||||
|
&self.var_name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on(&self) -> &BuildBooleanOn {
|
||||||
|
&self.on
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum BuildBooleanOn {
|
||||||
|
RulePresent,
|
||||||
|
}
|
152
ast-generator/src/type_gen.rs
Normal file
152
ast-generator/src/type_gen.rs
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
use crate::spec::{
|
||||||
|
BuildSpec, ChildSpec, EnumBuildSpec, SingleBooleanChildToBuild, SingleChildToBuild,
|
||||||
|
SingleTypeChildToBuild, StructBuildSpec, VecChild, VecChildToBuild,
|
||||||
|
};
|
||||||
|
use proc_macro2::{Ident, TokenStream};
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
|
||||||
|
fn make_enum_type(build_spec: &EnumBuildSpec) -> TokenStream {
|
||||||
|
let children: Vec<TokenStream> = build_spec
|
||||||
|
.rules()
|
||||||
|
.iter()
|
||||||
|
.map(|rule| {
|
||||||
|
let member_name_ident = format_ident!("{}", rule.rule());
|
||||||
|
let child_name_ident = format_ident!("{}", rule.build());
|
||||||
|
quote! {
|
||||||
|
#member_name_ident(#child_name_ident)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let type_name_ident = format_ident!("{}", build_spec.build());
|
||||||
|
quote! {
|
||||||
|
pub enum #type_name_ident {
|
||||||
|
#(#children),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_vec_child(
|
||||||
|
vec_child: &VecChild,
|
||||||
|
member_names: &mut Vec<Ident>,
|
||||||
|
annotated_members: &mut Vec<TokenStream>,
|
||||||
|
accessors: &mut Vec<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()),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
member_names.push(child_ident.clone());
|
||||||
|
annotated_members.push(quote! {
|
||||||
|
#child_ident: Vec<#child_type_ident>
|
||||||
|
});
|
||||||
|
accessors.push(quote! {
|
||||||
|
pub fn #child_ident(&self) -> &[#child_type_ident] {
|
||||||
|
&self.#child_ident
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_single_type_child(
|
||||||
|
single_type_child: &SingleTypeChildToBuild,
|
||||||
|
member_names: &mut Vec<Ident>,
|
||||||
|
annotated_members: &mut Vec<TokenStream>,
|
||||||
|
accessors: &mut Vec<TokenStream>,
|
||||||
|
) {
|
||||||
|
let child_ident = format_ident!("{}", single_type_child.var_name());
|
||||||
|
let child_type_ident = format_ident!("{}", single_type_child.build());
|
||||||
|
member_names.push(child_ident.clone());
|
||||||
|
annotated_members.push(quote! {
|
||||||
|
#child_ident: #child_type_ident
|
||||||
|
});
|
||||||
|
accessors.push(quote! {
|
||||||
|
pub fn #child_ident(&self) -> &#child_type_ident {
|
||||||
|
&self.#child_ident
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_single_boolean_child(
|
||||||
|
single_boolean_child: &SingleBooleanChildToBuild,
|
||||||
|
member_names: &mut Vec<Ident>,
|
||||||
|
annotated_members: &mut Vec<TokenStream>,
|
||||||
|
accessors: &mut Vec<TokenStream>,
|
||||||
|
) {
|
||||||
|
let child_ident = format_ident!("{}", single_boolean_child.var_name());
|
||||||
|
member_names.push(child_ident.clone());
|
||||||
|
annotated_members.push(quote! {
|
||||||
|
#child_ident: bool
|
||||||
|
});
|
||||||
|
accessors.push(quote! {
|
||||||
|
pub fn #child_ident(&self) -> bool {
|
||||||
|
self.#child_ident
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_struct_type(build_spec: &StructBuildSpec) -> TokenStream {
|
||||||
|
let mut member_names: Vec<Ident> = vec![];
|
||||||
|
let mut annotated_members: Vec<TokenStream> = vec![];
|
||||||
|
let mut accessors: Vec<TokenStream> = vec![];
|
||||||
|
|
||||||
|
for child_spec in build_spec.children().iter() {
|
||||||
|
match child_spec {
|
||||||
|
ChildSpec::SkipChild(_) => {}
|
||||||
|
ChildSpec::VecChild(vec_child) => {
|
||||||
|
handle_vec_child(
|
||||||
|
vec_child,
|
||||||
|
&mut member_names,
|
||||||
|
&mut annotated_members,
|
||||||
|
&mut accessors,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ChildSpec::SingleChild(single_child) => {
|
||||||
|
match single_child.build() {
|
||||||
|
SingleChildToBuild::Type(single_type_child) => {
|
||||||
|
handle_single_type_child(
|
||||||
|
single_type_child,
|
||||||
|
&mut member_names,
|
||||||
|
&mut annotated_members,
|
||||||
|
&mut accessors,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
SingleChildToBuild::Boolean(single_boolean_child) => {
|
||||||
|
handle_single_boolean_child(
|
||||||
|
single_boolean_child,
|
||||||
|
&mut member_names,
|
||||||
|
&mut annotated_members,
|
||||||
|
&mut accessors,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let type_ident = format_ident!("{}", build_spec.build());
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
pub struct #type_ident {
|
||||||
|
#(#annotated_members),*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl #type_ident {
|
||||||
|
pub fn new(#(#annotated_members),*) -> Self {
|
||||||
|
Self {
|
||||||
|
#(#member_names),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#(#accessors)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_type(build_spec: &BuildSpec) -> TokenStream {
|
||||||
|
match build_spec {
|
||||||
|
BuildSpec::Enum(enum_build_spec) => make_enum_type(enum_build_spec),
|
||||||
|
BuildSpec::Struct(struct_build_spec) => make_struct_type(struct_build_spec),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user