Add polymorphic build types gen.

This commit is contained in:
Jesse Brault 2025-09-17 17:21:37 -05:00
parent a7eabae3e3
commit 7399a8748c
10 changed files with 885 additions and 164 deletions

View File

@ -1,4 +1,4 @@
use crate::spec::{BooleanChildToBuild, BuildSpec, EnumBuildSpec, EnumRule, EnumRuleChild, EnumRuleChildKind, EnumRuleNodeChild, LeafEnumBuildSpec, LeafEnumRule, LeafStructBuildSpec, LeafStructMember, LeafStructMemberKind, MemberChild, MemberChildToBuild, NodeChildToBuild, PolymorphicBuildSpec, PolymorphicEnumMember, ProductionBuildSpec, ProductionKind, ProductionStringFrom, SkipChild, StructBuildSpec, StructChildSpec, VecChild, VecChildToBuild, VecNodeChildToBuild}; use crate::spec::{AlternativeAction, AlternativeBuild, AlternativeBuildChild, AlternativeChild, AlternativeTest, BooleanChildToBuild, BuildSpec, EnumBuildSpec, EnumRule, EnumRuleChild, EnumRuleChildKind, EnumRuleNodeChild, LeafEnumBuildSpec, LeafEnumRule, LeafStructBuildSpec, LeafStructMember, LeafStructMemberKind, MemberChild, MemberChildToBuild, NameAndKind, NodeChildToBuild, PolymorphicBuildAlternative, PolymorphicBuildBuildSpec, PolymorphicEnumBuildSpec, PolymorphicEnumMember, PolymorphicEnumRule, PolymorphicTypeBuildSpec, ProductionBuildSpec, ProductionKind, ProductionStringFrom, SkipChild, StructBuildSpec, StructChildSpec, VecChild, VecChildToBuild, VecNodeChildToBuild};
use convert_case::{Case, Casing}; use convert_case::{Case, Casing};
use yaml_rust2::{Yaml, YamlLoader}; use yaml_rust2::{Yaml, YamlLoader};
@ -18,7 +18,87 @@ fn unwrap_single_member_hash(hash: &Yaml) -> (String, &Yaml) {
(key_as_string, member_value) (key_as_string, member_value)
} }
fn deserialize_polymorphic_enum_members(enum_members_yaml: &Yaml) -> Vec<Box<PolymorphicEnumMember>> { fn deserialize_polymorphic_enum_build_spec(name: &str, build_spec: &Yaml) -> PolymorphicEnumBuildSpec {
let return_type = build_spec["return_type"].as_str().unwrap();
let rules = build_spec["rules"]
.as_vec()
.unwrap()
.iter()
.map(|rule_yaml| {
let (rule_name, props) = unwrap_single_member_hash(rule_yaml);
if props["wrap"].is_hash() {
PolymorphicEnumRule::Wrap(NameAndKind::new(
&rule_name,
props["wrap"]["enum_variant"].as_str().unwrap()
))
} else if props["return_build"].is_hash() {
PolymorphicEnumRule::ReturnBuild(NameAndKind::new(
&rule_name,
props["return_build"]["kind"].as_str().unwrap()
))
} else {
panic!()
}
})
.map(Box::new)
.collect::<Vec<_>>();
PolymorphicEnumBuildSpec::new(name, return_type, rules)
}
fn deserialize_polymorphic_action(action_yaml: &Yaml) -> AlternativeAction {
if action_yaml["return_build"].is_hash() {
let kind = action_yaml["return_build"]["kind"].as_str().unwrap();
AlternativeAction::ReturnBuild(kind.to_string())
} else if action_yaml["build"].is_hash() {
let build_children = action_yaml["build"]["children"]
.as_vec()
.unwrap()
.iter()
.map(|child_yaml| {
let (child_name, child_props) = unwrap_single_member_hash(child_yaml);
if get_as_bool(&child_props["skip"]) {
AlternativeChild::Skip
} else {
let kind = child_props["kind"].as_str().unwrap();
let rule = child_props["rule"].as_str().unwrap();
AlternativeChild::Build(AlternativeBuildChild::new(&child_name, kind, rule))
}
})
.map(Box::new)
.collect();
let enum_variant = action_yaml["build"]["enum_variant"].as_str().unwrap();
let build = AlternativeBuild::new(enum_variant, build_children);
AlternativeAction::Build(build)
} else {
panic!("return_build or build is required for an alternative")
}
}
fn deserialize_polymorphic_build_build_spec(
name: &str,
polymorphic_build_yaml: &Yaml
) -> PolymorphicBuildBuildSpec {
let return_type = polymorphic_build_yaml["return_type"].as_str().unwrap();
let alternatives = polymorphic_build_yaml["alternatives"]
.as_vec()
.unwrap()
.iter()
.map(|alternative_yaml| {
let number_of_pairs = alternative_yaml["test"]["number_of_pairs"].as_i64().unwrap();
let action = deserialize_polymorphic_action(&alternative_yaml["action"]);
PolymorphicBuildAlternative::new(
AlternativeTest::NumberOfPairs(number_of_pairs),
action
)
})
.map(Box::new)
.collect();
PolymorphicBuildBuildSpec::new(name, return_type, alternatives)
}
fn deserialize_polymorphic_enum_members(
enum_members_yaml: &Yaml,
) -> Vec<Box<PolymorphicEnumMember>> {
enum_members_yaml enum_members_yaml
.as_vec() .as_vec()
.unwrap() .unwrap()
@ -32,27 +112,38 @@ fn deserialize_polymorphic_enum_members(enum_members_yaml: &Yaml) -> Vec<Box<Pol
.collect() .collect()
} }
fn deserialize_polymorphic_spec(name: &str, spec_props: &Yaml) -> PolymorphicBuildSpec { fn deserialize_polymorphic_spec(name: &str, spec_props: &Yaml) -> PolymorphicTypeBuildSpec {
let enum_members = deserialize_polymorphic_enum_members(&spec_props["enum_members"]); let enum_members = deserialize_polymorphic_enum_members(&spec_props["enum_members"]);
let build_kind = spec_props["build"]["kind"].as_str().unwrap(); let build_kind = spec_props["build"]["kind"].as_str().unwrap();
PolymorphicBuildSpec::new(name, enum_members, build_kind) PolymorphicTypeBuildSpec::new(name, enum_members, build_kind)
} }
fn deserialize_production_spec(rule: &str, production_yaml: &Yaml) -> ProductionBuildSpec { fn deserialize_production_spec(rule: &str, production_yaml: &Yaml) -> ProductionBuildSpec {
let kind = match production_yaml["kind"].as_str().unwrap() { let kind = if production_yaml["kind"].is_hash() {
"int" => ProductionKind::Int, let node = production_yaml["kind"]["node"].as_str().unwrap();
"long" => ProductionKind::Long, ProductionKind::Node(node.to_string())
"double" => ProductionKind::Double, } else {
"boolean" => ProductionKind::Boolean, match production_yaml["kind"].as_str().unwrap() {
"string" => { "int" => ProductionKind::Int,
let from = match production_yaml["from"].as_str().unwrap() { "long" => ProductionKind::Long,
"string_inner" => ProductionStringFrom::StringInner, "double" => ProductionKind::Double,
"whole_pair" => ProductionStringFrom::WholePair, "boolean" => ProductionKind::Boolean,
_ => panic!("invalid from: {}", production_yaml["from"].as_str().unwrap()), "string" => {
}; let from = match production_yaml["from"].as_str().unwrap() {
ProductionKind::String(from) "string_inner" => ProductionStringFrom::StringInner,
}, "whole_pair" => ProductionStringFrom::WholePair,
_ => panic!("invalid kind: {}", production_yaml["kind"].as_str().unwrap()), _ => panic!(
"invalid from: {}",
production_yaml["from"].as_str().unwrap()
),
};
ProductionKind::String(from)
}
_ => panic!(
"invalid kind: {}",
production_yaml["kind"].as_str().unwrap()
),
}
}; };
ProductionBuildSpec::new(rule, kind) ProductionBuildSpec::new(rule, kind)
} }
@ -66,9 +157,7 @@ fn deserialize_leaf_enum_rules(rules_yaml: &Yaml) -> Vec<Box<LeafEnumRule>> {
.as_vec() .as_vec()
.unwrap() .unwrap()
.iter() .iter()
.map(|rule_yaml| { .map(|rule_yaml| deserialize_leaf_enum_rule(rule_yaml))
deserialize_leaf_enum_rule(rule_yaml)
})
.collect() .collect()
} }
@ -83,16 +172,19 @@ fn deserialize_enum_rule_custom_child(rule_props: &Yaml) -> Option<Box<EnumRuleC
"usize" => EnumRuleChildKind::USize, "usize" => EnumRuleChildKind::USize,
"string" => EnumRuleChildKind::String, "string" => EnumRuleChildKind::String,
"boolean" => EnumRuleChildKind::Boolean, "boolean" => EnumRuleChildKind::Boolean,
_ => panic!("unsupported enum rule kind: {}", rule_props["kind"].as_str().unwrap()), _ => panic!(
"unsupported enum rule kind: {}",
rule_props["kind"].as_str().unwrap()
),
}; };
Some(Box::new(EnumRuleChild::new(Box::new(kind)))) Some(Box::new(EnumRuleChild::new(Box::new(kind))))
} }
} }
fn deserialize_enum_rule_node_child(rule: &str) -> Box<EnumRuleChild> { fn deserialize_enum_rule_node_child(rule: &str) -> Box<EnumRuleChild> {
Box::new(EnumRuleChild::new(Box::new( Box::new(EnumRuleChild::new(Box::new(EnumRuleChildKind::Node(
EnumRuleChildKind::Node(EnumRuleNodeChild::new(rule)), EnumRuleNodeChild::new(rule),
))) ))))
} }
fn deserialize_enum_rule(rule_yaml: &Yaml) -> Box<EnumRule> { fn deserialize_enum_rule(rule_yaml: &Yaml) -> Box<EnumRule> {
@ -100,7 +192,7 @@ fn deserialize_enum_rule(rule_yaml: &Yaml) -> Box<EnumRule> {
let (rule, rule_props) = unwrap_single_member_hash(rule_yaml); let (rule, rule_props) = unwrap_single_member_hash(rule_yaml);
Box::new(EnumRule::new( Box::new(EnumRule::new(
&rule, &rule,
deserialize_enum_rule_custom_child(rule_props) deserialize_enum_rule_custom_child(rule_props),
)) ))
} else { } else {
let rule_as_str = rule_yaml.as_str().unwrap(); let rule_as_str = rule_yaml.as_str().unwrap();
@ -184,7 +276,9 @@ fn deserialize_member_child_to_build(
} }
} else { } else {
let optional = get_as_bool(&props["optional"]); let optional = get_as_bool(&props["optional"]);
Box::new(MemberChildToBuild::Node(NodeChildToBuild::new(rule, None, optional))) Box::new(MemberChildToBuild::Node(NodeChildToBuild::new(
rule, None, optional,
)))
} }
} }
@ -297,11 +391,29 @@ fn deserialize_build_spec(build_spec_name: &str, build_spec: &Yaml) -> BuildSpec
let leaf_rules = deserialize_leaf_enum_rules(&build_spec["leaf_rules"]); let leaf_rules = deserialize_leaf_enum_rules(&build_spec["leaf_rules"]);
BuildSpec::LeafEnum(LeafEnumBuildSpec::new(build_spec_name, leaf_rules)) BuildSpec::LeafEnum(LeafEnumBuildSpec::new(build_spec_name, leaf_rules))
} else if build_spec["produce"].is_hash() { } else if build_spec["produce"].is_hash() {
BuildSpec::Production(deserialize_production_spec(build_spec_name, &build_spec["produce"])) BuildSpec::Production(deserialize_production_spec(
build_spec_name,
&build_spec["produce"],
))
} else if build_spec["polymorphic_type"].is_hash() { } else if build_spec["polymorphic_type"].is_hash() {
BuildSpec::Polymorphic(deserialize_polymorphic_spec(build_spec_name, &build_spec["polymorphic_type"])) BuildSpec::Polymorphic(deserialize_polymorphic_spec(
build_spec_name,
&build_spec["polymorphic_type"],
))
} else if build_spec["polymorphic_build"].is_hash() {
BuildSpec::PolymorphicBuild(deserialize_polymorphic_build_build_spec(
build_spec_name,
&build_spec["polymorphic_build"],
))
} else if build_spec["polymorphic_enum"].is_hash() {
BuildSpec::PolymorphicEnum(deserialize_polymorphic_enum_build_spec(
build_spec_name,
&build_spec["polymorphic_enum"],
))
} else { } else {
panic!("Expected a node spec for either a struct, leaf_struct, enum, leaf_enum node type, or a production type."); panic!(
"Expected a node spec for either a struct, leaf_struct, enum, leaf_enum node type, production, polymorphic type, or polymorphic build type."
);
} }
} }

View File

@ -2,7 +2,9 @@ pub mod deserialize;
mod enum_build_fn; mod enum_build_fn;
mod leaf_enum_build_fn; mod leaf_enum_build_fn;
mod leaf_struct_build_fn; mod leaf_struct_build_fn;
mod polymorphic_build_build_fn;
mod polymorphic_build_fn; mod polymorphic_build_fn;
mod polymorphic_enum_build_fn;
mod production_build_fn; mod production_build_fn;
mod spec; mod spec;
mod struct_build_fn; mod struct_build_fn;
@ -12,6 +14,9 @@ mod util;
use crate::enum_build_fn::make_enum_build_fn; use crate::enum_build_fn::make_enum_build_fn;
use crate::leaf_enum_build_fn::make_leaf_enum_build_fn; use crate::leaf_enum_build_fn::make_leaf_enum_build_fn;
use crate::leaf_struct_build_fn::make_leaf_struct_build_fn; use crate::leaf_struct_build_fn::make_leaf_struct_build_fn;
use crate::polymorphic_build_build_fn::make_polymorphic_build_build_fn;
use crate::polymorphic_build_fn::make_polymorphic_build_fn;
use crate::polymorphic_enum_build_fn::make_polymorphic_enum_build_fn;
use crate::production_build_fn::make_production_build_fn; use crate::production_build_fn::make_production_build_fn;
use crate::struct_build_fn::make_struct_build_fn; use crate::struct_build_fn::make_struct_build_fn;
use crate::type_gen::make_type; use crate::type_gen::make_type;
@ -19,7 +24,6 @@ use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
use spec::BuildSpec; use spec::BuildSpec;
use syn::File; use syn::File;
use crate::polymorphic_build_fn::make_polymorphic_build_fn;
fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) { fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) {
println!("*** BuildSpec ***"); println!("*** BuildSpec ***");
@ -43,7 +47,22 @@ fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) {
println!("Production Spec - rule: {}", production_build_spec.rule()); println!("Production Spec - rule: {}", production_build_spec.rule());
} }
BuildSpec::Polymorphic(polymorphic_build_spec) => { BuildSpec::Polymorphic(polymorphic_build_spec) => {
println!("Polymorphic Spec - name: {}", polymorphic_build_spec.name()); println!(
"Polymorphic Type Spec - name: {}",
polymorphic_build_spec.name()
);
}
BuildSpec::PolymorphicBuild(polymorphic_build_build_spec) => {
println!(
"Polymorphic Build Spec - name: {}",
polymorphic_build_build_spec.name()
);
}
BuildSpec::PolymorphicEnum(polymorphic_enum_build_spec) => {
println!(
"Polymorphic Enum Spec - name: {}",
polymorphic_enum_build_spec.name()
);
} }
} }
println!("{:#?}", token_stream); println!("{:#?}", token_stream);
@ -95,6 +114,16 @@ fn generate_build_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
debug_built_spec(build_spec, &stream); debug_built_spec(build_spec, &stream);
stream stream
} }
BuildSpec::PolymorphicBuild(polymorphic_build_build_spec) => {
let stream = make_polymorphic_build_build_fn(polymorphic_build_build_spec);
debug_built_spec(build_spec, &stream);
stream
}
BuildSpec::PolymorphicEnum(polymorphic_enum_build_spec) => {
let stream = make_polymorphic_enum_build_fn(polymorphic_enum_build_spec);
debug_built_spec(build_spec, &stream);
stream
}
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let combined = quote! { let combined = quote! {

View File

@ -0,0 +1,134 @@
use convert_case::{Case, Casing};
use crate::spec::{AlternativeAction, AlternativeBuild, AlternativeBuildChild, AlternativeChild, AlternativeTest, PolymorphicBuildBuildSpec};
use crate::util::{make_build_fn_name, make_build_pair};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
fn make_build_child(build_child: &AlternativeBuildChild) -> TokenStream {
let rule_ident = format_ident!("{}", build_child.rule());
let child_ident = format_ident!("{}", build_child.name());
let child_build_fn_ident = format_ident!("{}", make_build_fn_name(build_child.rule()));
quote! {
Rule::#rule_ident => {
#child_ident = Some(#child_build_fn_ident(inner_pair));
}
}
}
fn make_build_action(
spec: &PolymorphicBuildBuildSpec,
alternative_build: &AlternativeBuild,
) -> TokenStream {
let enum_type_ident = format_ident!("{}", spec.return_type());
let enum_variant_ident = format_ident!("{}", alternative_build.enum_variant());
let child_holders = alternative_build
.children()
.map(|child| {
match child {
AlternativeChild::Skip => None,
AlternativeChild::Build(build_child) => {
let child_ident = format_ident!("{}", build_child.name());
let child_type_ident = format_ident!("{}", build_child.kind());
Some(quote! {
let mut #child_ident: Option<#child_type_ident> = None
})
}
}
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let pair_ident = format_ident!("{}", make_build_pair(spec.name()));
let rule_matchers = alternative_build
.children()
.map(|child| {
match child {
AlternativeChild::Skip => None,
AlternativeChild::Build(build_child) => {
Some(make_build_child(build_child))
}
}
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let built_ident = format_ident!("{}", spec.name().to_case(Case::Snake));
let inner_type_ident = format_ident!("{}", spec.name());
let child_args = alternative_build.children()
.map(|child| {
match child {
AlternativeChild::Skip => None,
AlternativeChild::Build(child_build) => {
let child_ident = format_ident!("{}", child_build.name());
Some(quote! {
Box::new(#child_ident.unwrap())
})
}
}
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
quote! {
#(#child_holders;)*
for inner_pair in #pair_ident.into_inner() {
match inner_pair.as_rule() {
#(#rule_matchers),*
_ => unreachable!()
}
}
let #built_ident = #inner_type_ident::new(#(#child_args),*);
#enum_type_ident::#enum_variant_ident(#built_ident)
}
}
pub fn make_polymorphic_build_build_fn(spec: &PolymorphicBuildBuildSpec) -> TokenStream {
let build_fn_ident = format_ident!("{}", make_build_fn_name(spec.name()));
let pair_ident = format_ident!("{}", make_build_pair(spec.name()));
let return_type_ident = format_ident!("{}", spec.return_type());
let alternatives = spec
.alternatives()
.map(|alternative| {
let count_to_match: usize = match alternative.test() {
AlternativeTest::NumberOfPairs(count) => count.clone().try_into().unwrap(),
};
let action = match alternative.action() {
AlternativeAction::ReturnBuild(kind) => {
let inner_build_fn_ident = format_ident!("{}", make_build_fn_name(kind));
quote! {
let inner_pair = #pair_ident.into_inner().next().unwrap();
#inner_build_fn_ident(inner_pair)
}
}
AlternativeAction::Build(alternative_build) => {
make_build_action(spec, alternative_build)
}
};
quote! {
#count_to_match => {
#action
}
}
})
.collect::<Vec<_>>();
quote! {
fn #build_fn_ident(#pair_ident: Pair<Rule>) -> #return_type_ident {
let count = #pair_ident.clone().into_inner().count();
match count {
#(#alternatives,)*
_ => unreachable!()
}
}
}
}

View File

@ -1,19 +1,18 @@
use crate::spec::PolymorphicTypeBuildSpec;
use crate::util::{make_build_fn_name, make_build_pair};
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use crate::spec::PolymorphicBuildSpec;
use crate::token_stream_to_string;
use crate::util::{make_build_fn_name, make_build_pair};
pub fn make_polymorphic_build_fn(build_spec: &PolymorphicBuildSpec) -> TokenStream { pub fn make_polymorphic_build_fn(build_spec: &PolymorphicTypeBuildSpec) -> TokenStream {
let build_fn_ident = format_ident!("{}", make_build_fn_name(build_spec.name())); let build_fn_ident = format_ident!("{}", make_build_fn_name(build_spec.name()));
let pair_ident = format_ident!("{}", make_build_pair(&build_spec.name())); let pair_ident = format_ident!("{}", make_build_pair(&build_spec.name()));
let return_type_ident = format_ident!("{}", build_spec.name()); let return_type_ident = format_ident!("{}", build_spec.name());
let inner_build_fn_ident = format_ident!("{}", make_build_fn_name(build_spec.build_kind())); let inner_build_fn_ident = format_ident!("{}", make_build_fn_name(build_spec.build_kind()));
quote! { quote! {
fn #build_fn_ident(#pair_ident: Pair<Rule>) -> #return_type_ident { fn #build_fn_ident(#pair_ident: Pair<Rule>) -> #return_type_ident {
let inner_pair = #pair_ident.into_inner().next().unwrap(); let inner_pair = #pair_ident.into_inner().next().unwrap();
#inner_build_fn_ident(inner_pair) #inner_build_fn_ident(inner_pair)
} }
} }
} }

View File

@ -0,0 +1,42 @@
use crate::spec::{PolymorphicEnumBuildSpec, PolymorphicEnumRule};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use crate::util::{make_build_fn_name, make_build_pair};
pub fn make_polymorphic_enum_build_fn(spec: &PolymorphicEnumBuildSpec) -> TokenStream {
let build_fn_ident = format_ident!("{}", make_build_fn_name(spec.name()));
let pair_ident = format_ident!("{}", make_build_pair(spec.name()));
let return_type_ident = format_ident!("{}", spec.return_type());
let match_arms = spec.rules()
.map(|rule| {
match rule {
PolymorphicEnumRule::Wrap(name_and_kind) => {
let rule_ident = format_ident!("{}", name_and_kind.name());
let enum_variant_ident = format_ident!("{}", name_and_kind.kind());
let rule_build_fn = format_ident!("{}", make_build_fn_name(name_and_kind.name()));
quote! {
Rule::#rule_ident => #return_type_ident::#enum_variant_ident(#rule_build_fn(inner_pair))
}
},
PolymorphicEnumRule::ReturnBuild(name_and_kind) => {
let rule_ident = format_ident!("{}", name_and_kind.name());
let rule_build_fn = format_ident!("{}", make_build_fn_name(name_and_kind.kind()));
quote! {
Rule::#rule_ident => #rule_build_fn(inner_pair)
}
}
}
})
.collect::<Vec<_>>();
quote! {
fn #build_fn_ident(#pair_ident: Pair<Rule>) -> #return_type_ident {
let inner_pair = #pair_ident.into_inner().next().unwrap();
match inner_pair.as_rule() {
#(#match_arms,)*
_ => unreachable!()
}
}
}
}

View File

@ -11,6 +11,7 @@ pub fn make_production_build_fn(production_build_spec: &ProductionBuildSpec) ->
ProductionKind::Double => format_ident!("f64"), ProductionKind::Double => format_ident!("f64"),
ProductionKind::String(_) => format_ident!("String"), ProductionKind::String(_) => format_ident!("String"),
ProductionKind::Boolean => format_ident!("bool"), ProductionKind::Boolean => format_ident!("bool"),
ProductionKind::Node(node_type) => format_ident!("{}", node_type),
}; };
let pair_ident = format_ident!("{}", make_build_pair(production_build_spec.rule())); let pair_ident = format_ident!("{}", make_build_pair(production_build_spec.rule()));
@ -77,6 +78,13 @@ pub fn make_production_build_fn(production_build_spec: &ProductionBuildSpec) ->
ProductionKind::Boolean => quote! { ProductionKind::Boolean => quote! {
#pair_ident.as_str().parse::<bool>().unwrap() #pair_ident.as_str().parse::<bool>().unwrap()
}, },
ProductionKind::Node(node_type) => {
let build_fn_ident = format_ident!("{}", make_build_fn_name(node_type));
quote! {
let inner_pair = #pair_ident.into_inner().next().unwrap();
#build_fn_ident(inner_pair)
}
}
}; };
quote! { quote! {

View File

@ -4,7 +4,9 @@ pub enum BuildSpec {
Struct(StructBuildSpec), Struct(StructBuildSpec),
LeafStruct(LeafStructBuildSpec), LeafStruct(LeafStructBuildSpec),
Production(ProductionBuildSpec), Production(ProductionBuildSpec),
Polymorphic(PolymorphicBuildSpec), Polymorphic(PolymorphicTypeBuildSpec),
PolymorphicBuild(PolymorphicBuildBuildSpec),
PolymorphicEnum(PolymorphicEnumBuildSpec),
} }
// Enum build spec // Enum build spec
@ -35,14 +37,14 @@ impl EnumBuildSpec {
pub struct EnumRule { pub struct EnumRule {
rule: String, rule: String,
child: Option<Box<EnumRuleChild>> child: Option<Box<EnumRuleChild>>,
} }
impl EnumRule { impl EnumRule {
pub fn new(rule: &str, child: Option<Box<EnumRuleChild>>) -> Self { pub fn new(rule: &str, child: Option<Box<EnumRuleChild>>) -> Self {
Self { Self {
rule: rule.to_string(), rule: rule.to_string(),
child child,
} }
} }
@ -50,7 +52,7 @@ impl EnumRule {
pub fn rule(&self) -> &str { pub fn rule(&self) -> &str {
&self.rule &self.rule
} }
pub fn child(&self) -> Option<&EnumRuleChild> { pub fn child(&self) -> Option<&EnumRuleChild> {
if let Some(child) = &self.child { if let Some(child) = &self.child {
Some(child.as_ref()) Some(child.as_ref())
@ -61,14 +63,14 @@ impl EnumRule {
} }
pub struct EnumRuleChild { pub struct EnumRuleChild {
kind: Box<EnumRuleChildKind> kind: Box<EnumRuleChildKind>,
} }
impl EnumRuleChild { impl EnumRuleChild {
pub fn new(kind: Box<EnumRuleChildKind>) -> Self { pub fn new(kind: Box<EnumRuleChildKind>) -> Self {
Self { kind } Self { kind }
} }
pub fn kind(&self) -> &EnumRuleChildKind { pub fn kind(&self) -> &EnumRuleChildKind {
&self.kind &self.kind
} }
@ -81,11 +83,11 @@ pub enum EnumRuleChildKind {
Double, Double,
USize, USize,
String, String,
Boolean Boolean,
} }
pub struct EnumRuleNodeChild { pub struct EnumRuleNodeChild {
build: String build: String,
} }
impl EnumRuleNodeChild { impl EnumRuleNodeChild {
@ -94,7 +96,7 @@ impl EnumRuleNodeChild {
build: build.to_string(), build: build.to_string(),
} }
} }
pub fn build(&self) -> &str { pub fn build(&self) -> &str {
&self.build &self.build
} }
@ -111,7 +113,7 @@ impl LeafEnumBuildSpec {
pub fn new(build: &str, rules: Vec<Box<LeafEnumRule>>) -> Self { pub fn new(build: &str, rules: Vec<Box<LeafEnumRule>>) -> Self {
Self { Self {
build: build.to_string(), build: build.to_string(),
rules rules,
} }
} }
@ -151,7 +153,7 @@ impl StructBuildSpec {
pub fn new(build: &str, children: Vec<Box<StructChildSpec>>) -> Self { pub fn new(build: &str, children: Vec<Box<StructChildSpec>>) -> Self {
Self { Self {
build: build.to_string(), build: build.to_string(),
children children,
} }
} }
@ -159,7 +161,7 @@ impl StructBuildSpec {
pub fn build(&self) -> &str { pub fn build(&self) -> &str {
&self.build &self.build
} }
/// The children for this build spec. /// The children for this build spec.
pub fn children(&self) -> impl Iterator<Item = &StructChildSpec> { pub fn children(&self) -> impl Iterator<Item = &StructChildSpec> {
self.children.iter().map(Box::as_ref) self.children.iter().map(Box::as_ref)
@ -230,7 +232,7 @@ impl VecChild {
#[derive(Debug)] #[derive(Debug)]
pub enum VecChildToBuild { pub enum VecChildToBuild {
Node(VecNodeChildToBuild), Node(VecNodeChildToBuild),
String String,
} }
#[derive(Debug)] #[derive(Debug)]
@ -240,7 +242,9 @@ pub struct VecNodeChildToBuild {
impl VecNodeChildToBuild { impl VecNodeChildToBuild {
pub fn new(build: &str) -> Self { pub fn new(build: &str) -> Self {
Self { build: build.to_string() } Self {
build: build.to_string(),
}
} }
/// The type to build, in Pascal case. /// The type to build, in Pascal case.
@ -295,11 +299,7 @@ pub struct NodeChildToBuild {
} }
impl NodeChildToBuild { impl NodeChildToBuild {
pub fn new( pub fn new(build: &str, or_else: Option<String>, optional: bool) -> Self {
build: &str,
or_else: Option<String>,
optional: bool,
) -> Self {
Self { Self {
build: build.to_string(), build: build.to_string(),
or_else, or_else,
@ -325,13 +325,13 @@ impl NodeChildToBuild {
#[derive(Debug)] #[derive(Debug)]
pub struct BooleanChildToBuild { pub struct BooleanChildToBuild {
name: String name: String,
} }
impl BooleanChildToBuild { impl BooleanChildToBuild {
pub fn new(name: &str) -> Self { pub fn new(name: &str) -> Self {
Self { Self {
name: name.to_string() name: name.to_string(),
} }
} }
@ -402,11 +402,11 @@ impl ProductionBuildSpec {
kind, kind,
} }
} }
pub fn rule(&self) -> &str { pub fn rule(&self) -> &str {
&self.rule &self.rule
} }
pub fn kind(&self) -> &ProductionKind { pub fn kind(&self) -> &ProductionKind {
&self.kind &self.kind
} }
@ -417,39 +417,44 @@ pub enum ProductionKind {
Long, Long,
Double, Double,
String(ProductionStringFrom), String(ProductionStringFrom),
Boolean Boolean,
Node(String),
} }
pub enum ProductionStringFrom { pub enum ProductionStringFrom {
StringInner, StringInner,
WholePair WholePair,
} }
// Polymorphic build spec // Polymorphic build spec
pub struct PolymorphicBuildSpec { pub struct PolymorphicTypeBuildSpec {
name: String, name: String,
enum_members: Vec<Box<PolymorphicEnumMember>>, enum_members: Vec<Box<PolymorphicEnumMember>>,
build_kind: String build_kind: String,
} }
impl PolymorphicBuildSpec { impl PolymorphicTypeBuildSpec {
pub fn new(name: &str, enum_members: Vec<Box<PolymorphicEnumMember>>, build_kind: &str) -> Self { pub fn new(
name: &str,
enum_members: Vec<Box<PolymorphicEnumMember>>,
build_kind: &str,
) -> Self {
Self { Self {
name: name.to_string(), name: name.to_string(),
enum_members, enum_members,
build_kind: build_kind.to_string() build_kind: build_kind.to_string(),
} }
} }
pub fn name(&self) -> &str { pub fn name(&self) -> &str {
&self.name &self.name
} }
pub fn enum_members(&self) -> impl Iterator<Item = &PolymorphicEnumMember> { pub fn enum_members(&self) -> impl Iterator<Item = &PolymorphicEnumMember> {
self.enum_members.iter().map(Box::as_ref) self.enum_members.iter().map(Box::as_ref)
} }
pub fn build_kind(&self) -> &str { pub fn build_kind(&self) -> &str {
self.build_kind.as_str() self.build_kind.as_str()
} }
@ -457,22 +462,192 @@ impl PolymorphicBuildSpec {
pub struct PolymorphicEnumMember { pub struct PolymorphicEnumMember {
name: String, name: String,
inner_kind: String inner_kind: String,
} }
impl PolymorphicEnumMember { impl PolymorphicEnumMember {
pub fn new(name: &str, inner_kind: &str) -> Self { pub fn new(name: &str, inner_kind: &str) -> Self {
Self { Self {
name: name.to_string(), name: name.to_string(),
inner_kind: inner_kind.to_string() inner_kind: inner_kind.to_string(),
} }
} }
pub fn name(&self) -> &str { pub fn name(&self) -> &str {
&self.name &self.name
} }
pub fn inner_kind(&self) -> &str { pub fn inner_kind(&self) -> &str {
&self.inner_kind &self.inner_kind
} }
} }
pub struct PolymorphicBuildBuildSpec {
name: String,
return_type: String,
alternatives: Vec<Box<PolymorphicBuildAlternative>>,
}
impl PolymorphicBuildBuildSpec {
pub fn new(
name: &str,
return_type: &str,
alternatives: Vec<Box<PolymorphicBuildAlternative>>,
) -> Self {
Self {
name: name.to_string(),
return_type: return_type.to_string(),
alternatives,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn return_type(&self) -> &str {
&self.return_type
}
pub fn alternatives(&self) -> impl Iterator<Item = &PolymorphicBuildAlternative> {
self.alternatives.iter().map(Box::as_ref)
}
}
pub struct PolymorphicBuildAlternative {
test: AlternativeTest,
action: AlternativeAction,
}
impl PolymorphicBuildAlternative {
pub fn new(test: AlternativeTest, action: AlternativeAction) -> Self {
Self { test, action }
}
pub fn test(&self) -> &AlternativeTest {
&self.test
}
pub fn action(&self) -> &AlternativeAction {
&self.action
}
}
pub enum AlternativeTest {
NumberOfPairs(i64),
}
pub enum AlternativeAction {
ReturnBuild(String),
Build(AlternativeBuild),
}
pub struct AlternativeBuild {
enum_variant: String,
children: Vec<Box<AlternativeChild>>,
}
impl AlternativeBuild {
pub fn new(enum_variant: &str, children: Vec<Box<AlternativeChild>>) -> Self {
Self {
enum_variant: enum_variant.to_string(),
children,
}
}
pub fn enum_variant(&self) -> &str {
&self.enum_variant
}
pub fn children(&self) -> impl Iterator<Item = &AlternativeChild> {
self.children.iter().map(Box::as_ref)
}
}
pub enum AlternativeChild {
Skip,
Build(AlternativeBuildChild),
}
pub struct AlternativeBuildChild {
name: String,
kind: String,
rule: String,
}
impl AlternativeBuildChild {
pub fn new(name: &str, kind: &str, rule: &str) -> Self {
Self {
name: name.to_string(),
kind: kind.to_string(),
rule: rule.to_string(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &str {
&self.kind
}
pub fn rule(&self) -> &str {
&self.rule
}
}
pub struct PolymorphicEnumBuildSpec {
name: String,
return_type: String,
rules: Vec<Box<PolymorphicEnumRule>>,
}
impl PolymorphicEnumBuildSpec {
pub fn new(name: &str, return_type: &str, rules: Vec<Box<PolymorphicEnumRule>>) -> Self {
Self {
name: name.to_string(),
return_type: return_type.to_string(),
rules,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn return_type(&self) -> &str {
&self.return_type
}
pub fn rules(&self) -> impl Iterator<Item = &PolymorphicEnumRule> {
self.rules.iter().map(Box::as_ref)
}
}
pub enum PolymorphicEnumRule {
Wrap(NameAndKind),
ReturnBuild(NameAndKind),
}
pub struct NameAndKind {
name: String,
kind: String,
}
impl NameAndKind {
pub fn new(name: &str, kind: &str) -> Self {
Self {
name: name.to_string(),
kind: kind.to_string(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &str {
&self.kind
}
}

View File

@ -1,12 +1,75 @@
use crate::spec::{ use crate::spec::{
BooleanChildToBuild, BuildSpec, EnumBuildSpec, EnumRuleChildKind, LeafEnumBuildSpec, AlternativeAction, AlternativeChild, BooleanChildToBuild, BuildSpec, EnumBuildSpec,
LeafStructBuildSpec, LeafStructMemberKind, MemberChildToBuild, NodeChildToBuild, EnumRuleChildKind, LeafEnumBuildSpec, LeafStructBuildSpec, LeafStructMemberKind,
PolymorphicBuildSpec, StructBuildSpec, StructChildSpec, VecChild, VecChildToBuild, MemberChildToBuild, NodeChildToBuild, PolymorphicBuildBuildSpec, PolymorphicTypeBuildSpec,
StructBuildSpec, StructChildSpec, VecChild, VecChildToBuild,
}; };
use proc_macro2::{Ident, TokenStream}; use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote}; use quote::{format_ident, quote};
fn make_polymorphic_type(build_spec: &PolymorphicBuildSpec) -> TokenStream { fn make_polymorphic_build_type(build_spec: &PolymorphicBuildBuildSpec) -> TokenStream {
let alternative_action = build_spec
.alternatives()
.find(|alternative| {
if let AlternativeAction::Build(_) = alternative.action() {
true
} else {
false
}
})
.unwrap();
let alternative_build =
if let AlternativeAction::Build(alternative_build) = alternative_action.action() {
alternative_build
} else {
unreachable!()
};
let annotated_members = alternative_build
.children()
.map(|child| match child {
AlternativeChild::Skip => None,
AlternativeChild::Build(build_child) => {
let child_name_ident = format_ident!("{}", build_child.name());
let type_ident = format_ident!("{}", build_child.kind());
Some(quote! {
#child_name_ident: Box<#type_ident>
})
}
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let initializers = alternative_build
.children()
.map(|build_child| match build_child {
AlternativeChild::Skip => None,
AlternativeChild::Build(build_child) => Some(format_ident!("{}", build_child.name())),
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let type_ident = format_ident!("{}", build_spec.name());
quote! {
pub struct #type_ident {
#(#annotated_members),*
}
impl #type_ident {
pub fn new(#(#annotated_members),*) -> Self {
Self {
#(#initializers),*
}
}
}
}
}
fn make_polymorphic_type(build_spec: &PolymorphicTypeBuildSpec) -> TokenStream {
let members = build_spec let members = build_spec
.enum_members() .enum_members()
.map(|enum_member| { .map(|enum_member| {
@ -370,5 +433,9 @@ pub fn make_type(build_spec: &BuildSpec) -> Option<TokenStream> {
BuildSpec::Polymorphic(polymorphic_build_spec) => { BuildSpec::Polymorphic(polymorphic_build_spec) => {
Some(make_polymorphic_type(polymorphic_build_spec)) Some(make_polymorphic_type(polymorphic_build_spec))
} }
BuildSpec::PolymorphicBuild(polymorphic_build_build_spec) => {
Some(make_polymorphic_build_type(polymorphic_build_build_spec))
}
BuildSpec::PolymorphicEnum(_) => None,
} }
} }

View File

@ -605,50 +605,109 @@ Expression:
build: build:
kind: TernaryExpression kind: TernaryExpression
TernaryExpression: TernaryExpression:
children: polymorphic_build:
- or_expression return_type: Expression
- ternary_alternatives: alternatives:
optional: true - test:
TernaryAlternatives: number_of_pairs: 1
children: action:
- ternary_true_alternative return_build:
- ternary_false_alternative kind: OrExpression
- test:
number_of_pairs: 3
action:
build:
enum_variant: Ternary
children:
- test:
kind: Expression
rule: OrExpression
- on_true:
kind: Expression
rule: TernaryTrueAlternative
- on_false:
kind: Expression
rule: TernaryFalseAlternative
TernaryTrueAlternative: TernaryTrueAlternative:
children: produce:
- expression kind:
node: Expression
TernaryFalseAlternative: TernaryFalseAlternative:
children: produce:
- expression kind:
node: Expression
OrExpression: OrExpression:
children: polymorphic_build:
- left: return_type: Expression
rule: AndExpression alternatives:
- or_sym: - test:
rule: Or number_of_pairs: 1
skip: true action:
- right: return_build:
rule: Expression kind: AndExpression
optional: true - test:
number_of_pairs: 3
action:
build:
enum_variant: Or
children:
- left:
kind: Expression
rule: AndExpression
- or_sym:
rule: Or
skip: true
- right:
kind: Expression
rule: Expression
AndExpression: AndExpression:
children: polymorphic_build:
- left: return_type: Expression
rule: ComparisonExpression alternatives:
- and_sym: - test:
rule: And number_of_pairs: 1
skip: true action:
- right: return_build:
rule: Expression kind: ComparisonExpression
optional: true - test:
number_of_pairs: 3
action:
build:
enum_variant: And
children:
- left:
kind: Expression
rule: ComparisonExpression
- and_sym:
rule: And
skip: true
- right:
kind: Expression
rule: Expression
ComparisonExpression: ComparisonExpression:
children: polymorphic_build:
- left: return_type: Expression
rule: ShiftExpression alternatives:
- operator: - test:
rule: ComparisonOperator number_of_pairs: 1
optional: true action:
- right: return_build:
rule: Expression kind: ShiftExpression
optional: true - test:
number_of_pairs: 3
action:
build:
enum_variant: Comparison
children:
- left:
kind: Expression
rule: ShiftExpression
- operator:
kind: ComparisonOperator
rule: ComparisonOperator
- right:
kind: Expression
rule: Expression
ComparisonOperator: ComparisonOperator:
leaf_rules: leaf_rules:
- Greater - Greater
@ -658,64 +717,144 @@ ComparisonOperator:
- EqualTo - EqualTo
- NotEqualTo - NotEqualTo
ShiftExpression: ShiftExpression:
children: polymorphic_build:
- left: return_type: Expression
rule: AdditiveExpression alternatives:
- operator: - test:
rule: ShiftOperator number_of_pairs: 1
optional: true action:
- right: return_build:
rule: Expression kind: AdditiveExpression
optional: true - test:
number_of_pairs: 3
action:
build:
enum_variant: Shift
children:
- left:
kind: Expression
rule: AdditiveExpression
- operator:
kind: ShiftOperator
rule: ShiftOperator
- right:
kind: Expression
rule: Expression
ShiftOperator: ShiftOperator:
leaf_rules: leaf_rules:
- LeftShift - LeftShift
- RightShift - RightShift
AdditiveExpression: AdditiveExpression:
children: polymorphic_build:
- left: return_type: Expression
rule: MultiplicativeExpression alternatives:
- operator: - test:
rule: AdditiveOperator number_of_pairs: 1
optional: true action:
- right: return_build:
rule: Expression kind: MultiplicativeExpression
optional: true - test:
number_of_pairs: 3
action:
build:
enum_variant: Additive
children:
- left:
kind: Expression
rule: MultiplicativeExpression
- operator:
kind: AdditiveOperator
rule: AdditiveOperator
- right:
kind: Expression
rule: Expression
AdditiveOperator: AdditiveOperator:
leaf_rules: leaf_rules:
- Add - Add
- Subtract - Subtract
MultiplicativeExpression: MultiplicativeExpression:
children: polymorphic_build:
- left: return_type: Expression
rule: PrefixExpression alternatives:
- operator: - test:
rule: MultiplicativeOperator number_of_pairs: 1
optional: true action:
- right: return_build:
rule: Expression kind: PrefixExpression
optional: true - test:
number_of_pairs: 3
action:
build:
enum_variant: Multiplicative
children:
- left:
kind: Expression
rule: PrefixExpression
- operator:
kind: MultiplicativeOperator
rule: MultiplicativeOperator
- right:
kind: Expression
rule: Expression
MultiplicativeOperator: MultiplicativeOperator:
leaf_rules: leaf_rules:
- Multiply - Multiply
- Divide - Divide
- Modulo - Modulo
PrefixExpression: PrefixExpression:
polymorphic_build:
return_type: Expression
alternatives:
- test:
number_of_pairs: 1
action:
return_build:
kind: SuffixExpression
- test:
number_of_pairs: 2
action:
build:
enum_variant: Prefix
children:
- prefix_operators:
kind: PrefixOperators
rule: PrefixOperators
- right:
kind: Expression
rule: SuffixExpression
PrefixOperators:
children: children:
- operators: - operators:
rule: PrefixOperator rule: PrefixOperator
vec: true vec: true
- right:
rule: SuffixExpression
PrefixOperator: PrefixOperator:
leaf_rules: leaf_rules:
- Spread - Spread
- Not - Not
- Negative - Negative
SuffixExpression: SuffixExpression:
polymorphic_build:
return_type: Expression
alternatives:
- test:
number_of_pairs: 1
action:
return_build:
kind: PrimaryExpression
- test:
number_of_pairs: 2
action:
build:
enum_variant: Suffix
children:
- left:
kind: Expression
rule: PrimaryExpression
- suffix_operators:
kind: SuffixOperators
rule: SuffixOperators
SuffixOperators:
children: children:
- left:
rule: PrimaryExpression
- operators: - operators:
rule: SuffixOperator rule: SuffixOperator
vec: true vec: true
@ -735,11 +874,21 @@ ObjectIndex:
children: children:
- expression - expression
PrimaryExpression: PrimaryExpression:
rules: polymorphic_enum:
- Literal return_type: Expression
- FullyQualifiedName rules:
- Closure - Literal:
- ParenthesizedExpression wrap:
enum_variant: Literal
- FullyQualifiedName:
wrap:
enum_variant: Fqn
- Closure:
wrap:
enum_variant: Closure
- ParenthesizedExpression:
return_build:
kind: Expression
ParenthesizedExpression: ParenthesizedExpression:
children: children:
- expression - expression

View File

@ -596,12 +596,10 @@ Expression = {
TernaryExpression = { TernaryExpression = {
OrExpression OrExpression
~ ( TernaryAlternatives )? ~ (
} TernaryTrueAlternative
~ TernaryFalseAlternative
TernaryAlternatives = { )?
TernaryTrueAlternative
~ TernaryFalseAlternative
} }
TernaryTrueAlternative = { TernaryTrueAlternative = {
@ -682,10 +680,14 @@ MultiplicativeOperator = {
} }
PrefixExpression = { PrefixExpression = {
PrefixOperator* PrefixOperators?
~ SuffixExpression ~ SuffixExpression
} }
PrefixOperators = {
PrefixOperator+
}
PrefixOperator = { PrefixOperator = {
Spread Spread
| Not | Not
@ -694,7 +696,11 @@ PrefixOperator = {
SuffixExpression = { SuffixExpression = {
PrimaryExpression PrimaryExpression
~ SuffixOperator* ~ SuffixOperators?
}
SuffixOperators = {
SuffixOperator+
} }
SuffixOperator = { SuffixOperator = {