New deserialization code.

This commit is contained in:
Jesse Brault 2025-09-22 20:19:30 -05:00
parent 0f64fee5ef
commit 63643d86ba
22 changed files with 1028 additions and 1146 deletions

View File

@ -0,0 +1,13 @@
use yaml_rust2::Yaml;
use crate::spec::leaf_enum_spec::LeafEnumBuildSpec;
pub fn deserialize_leaf_enum(name: &str, props: &Yaml) -> LeafEnumBuildSpec {
let rules = props["rules"].as_vec()
.unwrap()
.iter()
.map(|rule_yaml| {
rule_yaml.as_str().unwrap().to_string()
})
.collect();
LeafEnumBuildSpec::new(name, rules)
}

View File

@ -0,0 +1,21 @@
use crate::deserialize::util::unwrap_single_member_hash;
use yaml_rust2::Yaml;
use crate::spec::leaf_struct_spec::{LeafStructBuildSpec, LeafStructMember, LeafStructMemberKind};
fn deserialize_member(member_name: &str) -> LeafStructMember {
LeafStructMember::new(member_name, LeafStructMemberKind::String)
}
pub fn deserialize_leaf_struct(name: &str, props: &Yaml) -> LeafStructBuildSpec {
let members = props["members"]
.as_vec()
.unwrap()
.iter()
.map(|member_hash| {
let (member_name, _props) = unwrap_single_member_hash(member_hash);
deserialize_member(&member_name)
})
.map(Box::new)
.collect();
LeafStructBuildSpec::new(name, members)
}

View File

@ -1,451 +1,67 @@
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, ProductionNodeKind, ProductionStringFrom, SkipChild, StructBuildSpec,
StructChildSpec, VecChild, VecChildToBuild, VecNodeChildToBuild,
};
use crate::util::make_build_fn_name;
use convert_case::{Case, Casing};
mod leaf_enum_spec;
mod leaf_struct_spec;
mod node_production_spec;
mod polymorphic_enum_loop_spec;
mod polymorphic_type_spec;
mod production_spec;
mod struct_spec;
mod tree_enum_spec;
pub(crate) mod util;
use crate::deserialize::leaf_enum_spec::deserialize_leaf_enum;
use crate::deserialize::leaf_struct_spec::deserialize_leaf_struct;
use crate::deserialize::node_production_spec::deserialize_node_production;
use crate::deserialize::polymorphic_enum_loop_spec::deserialize_polymorphic_enum_loop;
use crate::deserialize::polymorphic_type_spec::deserialize_polymorphic_type;
use crate::deserialize::production_spec::deserialize_production;
use crate::deserialize::struct_spec::deserialize_struct_spec;
use crate::deserialize::tree_enum_spec::deserialize_tree_enum;
use crate::spec::BuildSpec;
use yaml_rust2::{Yaml, YamlLoader};
fn get_as_bool(yaml: &Yaml) -> bool {
yaml.as_bool().unwrap_or_else(|| false)
}
fn unwrap_single_member_hash(hash: &Yaml) -> (String, &Yaml) {
let as_hash = hash.as_hash().unwrap();
if as_hash.is_empty() {
panic!("empty hash");
} else if as_hash.len() > 1 {
panic!("hash contains more than one key");
}
let (member_key, member_value) = as_hash.iter().collect::<Vec<(&Yaml, &Yaml)>>()[0];
let key_as_string = member_key.as_str().unwrap().to_string();
(key_as_string, member_value)
}
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
.as_vec()
.unwrap()
.iter()
.map(|enum_member_yaml| {
let (member_name, member_hash) = unwrap_single_member_hash(enum_member_yaml);
let inner_kind = member_hash["inner"]["kind"].as_str().unwrap();
PolymorphicEnumMember::new(&member_name, inner_kind)
})
.map(Box::new)
.collect()
}
fn deserialize_polymorphic_spec(name: &str, spec_props: &Yaml) -> PolymorphicTypeBuildSpec {
let enum_members = deserialize_polymorphic_enum_members(&spec_props["enum_members"]);
let build_kind = spec_props["build"]["kind"].as_str().unwrap();
PolymorphicTypeBuildSpec::new(name, enum_members, build_kind)
}
fn deserialize_production_spec(rule: &str, production_yaml: &Yaml) -> ProductionBuildSpec {
let kind = if production_yaml["node"].is_hash() {
let kind = production_yaml["node"]["kind"].as_str().unwrap();
let with = production_yaml["node"]["with"].as_str().unwrap();
ProductionKind::Node(ProductionNodeKind::new(kind, with))
} else {
match production_yaml["kind"].as_str().unwrap() {
"int" => ProductionKind::Int,
"long" => ProductionKind::Long,
"double" => ProductionKind::Double,
"boolean" => ProductionKind::Boolean,
"string" => {
let from = match production_yaml["from"].as_str().unwrap() {
"string_inner" => ProductionStringFrom::StringInner,
"whole_pair" => ProductionStringFrom::WholePair,
_ => 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)
}
fn deserialize_leaf_enum_rule(rule_yaml: &Yaml) -> Box<LeafEnumRule> {
Box::new(LeafEnumRule::new(rule_yaml.as_str().unwrap()))
}
fn deserialize_leaf_enum_rules(rules_yaml: &Yaml) -> Vec<Box<LeafEnumRule>> {
rules_yaml
.as_vec()
.unwrap()
.iter()
.map(|rule_yaml| deserialize_leaf_enum_rule(rule_yaml))
.collect()
}
fn deserialize_enum_rule_custom_child(rule_props: &Yaml) -> Option<Box<EnumRuleChild>> {
if !rule_props["child"].as_bool().unwrap_or(true) {
None
} else {
let kind = match rule_props["kind"].as_str().unwrap() {
"int" => EnumRuleChildKind::Int,
"long" => EnumRuleChildKind::Long,
"double" => EnumRuleChildKind::Double,
"usize" => EnumRuleChildKind::USize,
"string" => EnumRuleChildKind::String,
"boolean" => EnumRuleChildKind::Boolean,
_ => panic!(
"unsupported enum rule kind: {}",
rule_props["kind"].as_str().unwrap()
),
};
Some(Box::new(EnumRuleChild::new(Box::new(kind))))
}
}
fn deserialize_enum_rule_node_child(rule: &str) -> Box<EnumRuleChild> {
Box::new(EnumRuleChild::new(Box::new(EnumRuleChildKind::Node(
EnumRuleNodeChild::new(rule),
))))
}
fn deserialize_enum_rule(rule_yaml: &Yaml) -> Box<EnumRule> {
if rule_yaml.is_hash() {
let (rule, rule_props) = unwrap_single_member_hash(rule_yaml);
Box::new(EnumRule::new(
&rule,
deserialize_enum_rule_custom_child(rule_props),
))
} else {
let rule_as_str = rule_yaml.as_str().unwrap();
Box::new(EnumRule::new(
rule_as_str,
Some(deserialize_enum_rule_node_child(rule_as_str)),
))
}
}
fn deserialize_enum_rules(rules_yaml: &Yaml) -> Vec<Box<EnumRule>> {
rules_yaml
.as_vec()
.unwrap()
.iter()
.map(|rule_yaml| deserialize_enum_rule(rule_yaml))
.collect()
}
fn deserialize_leaf_struct_member(name: &str, props: &Yaml) -> Box<LeafStructMember> {
let kind = props["kind"].as_str().unwrap();
if kind == "string" {
Box::new(LeafStructMember::new(name, LeafStructMemberKind::String))
} else {
panic!("invalid member kind: {}", kind);
}
}
fn deserialize_leaf_struct_members(members_yaml: &Yaml) -> Vec<Box<LeafStructMember>> {
members_yaml
.as_vec()
.unwrap()
.iter()
.map(|member| {
let (member_name, member_props) = unwrap_single_member_hash(member);
deserialize_leaf_struct_member(&member_name, member_props)
})
.collect()
}
fn deserialize_member_node_to_build(
rule: &str,
build_props: &Yaml,
optional: bool,
) -> Box<MemberChildToBuild> {
let or_else = build_props["or_else"]
.as_str()
.or_else(|| {
if get_as_bool(&build_props["or_else_default"]) {
Some("default")
} else {
None
}
})
.map(ToString::to_string);
let kind = build_props["kind"]
.as_str()
.map(ToString::to_string)
.unwrap_or_else(|| make_build_fn_name(rule));
let with = build_props["with"]
.as_str()
.map(ToString::to_string)
.unwrap_or_else(|| make_build_fn_name(rule));
Box::new(MemberChildToBuild::Node(NodeChildToBuild::new(
&kind, &with, or_else, optional,
)))
}
fn deserialize_member_boolean_to_build(name: &str) -> Box<MemberChildToBuild> {
Box::new(MemberChildToBuild::Boolean(BooleanChildToBuild::new(name)))
}
fn deserialize_member_child_to_build(
name: &str,
rule: &str,
props: &Yaml,
optional: bool,
) -> Box<MemberChildToBuild> {
if props["build"].is_hash() {
let build_props = &props["build"];
let kind = build_props["kind"].as_str().or(Some("node")).unwrap();
if kind == "node" {
deserialize_member_node_to_build(rule, build_props, optional)
} else if kind == "boolean" {
deserialize_member_boolean_to_build(name)
} else {
panic!("unsupported kind: {}", kind)
}
} else {
let optional = get_as_bool(&props["optional"]);
let with = props["with"]
.as_str()
.map(ToString::to_string)
.unwrap_or_else(|| make_build_fn_name(rule));
Box::new(MemberChildToBuild::Node(NodeChildToBuild::new(
rule, &with, None, optional,
)))
}
}
fn deserialize_member_child(
name: &str,
rule: &str,
optional: bool,
props: &Yaml,
) -> Box<StructChildSpec> {
Box::new(StructChildSpec::MemberChild(MemberChild::new(
name,
rule,
deserialize_member_child_to_build(name, rule, props, optional),
)))
}
fn deserialize_vec_node_child(name: &str, props: &Yaml) -> Box<StructChildSpec> {
let rule = props["rule"].as_str().unwrap();
Box::new(StructChildSpec::VecChild(VecChild::new(
name,
rule,
Box::new(VecChildToBuild::Node(VecNodeChildToBuild::new(rule))),
)))
}
fn deserialize_vec_string_child(name: &str, props: &Yaml) -> Box<StructChildSpec> {
let rule = props["rule"].as_str().unwrap();
Box::new(StructChildSpec::VecChild(VecChild::new(
name,
rule,
Box::new(VecChildToBuild::String),
)))
}
fn deserialize_vec_child(name: &str, props: &Yaml) -> Box<StructChildSpec> {
let kind = props["kind"].as_str().or_else(|| Some("node")).unwrap();
if kind == "node" {
deserialize_vec_node_child(name, props)
} else if kind == "string" {
deserialize_vec_string_child(name, props)
} else {
panic!("invalid kind: {}", kind);
}
}
fn deserialize_skip_child(name: &str, rule: &str) -> Box<StructChildSpec> {
Box::new(StructChildSpec::SkipChild(SkipChild::new(name, rule)))
}
fn deserialize_struct_hash_child(child: &Yaml) -> Box<StructChildSpec> {
let (name, props) = unwrap_single_member_hash(child);
let rule = props["rule"]
.as_str()
.map(|s| s.to_string())
.unwrap_or(name.to_case(Case::Pascal));
if get_as_bool(&props["skip"]) {
deserialize_skip_child(&name, &rule)
} else {
if get_as_bool(&props["vec"]) {
deserialize_vec_child(&name, props)
} else {
let optional = get_as_bool(&props["optional"]);
deserialize_member_child(&name, &rule, optional, props)
}
}
}
fn deserialize_struct_string_child(child: &Yaml) -> Box<StructChildSpec> {
let child_as_str = child.as_str().unwrap();
let child_name_pascal = child_as_str.to_case(Case::Pascal);
Box::new(StructChildSpec::MemberChild(MemberChild::new(
child_as_str,
&child_name_pascal,
Box::new(MemberChildToBuild::Node(NodeChildToBuild::new(
&child_name_pascal,
&make_build_fn_name(&child_name_pascal),
None,
false,
))),
)))
}
fn deserialize_struct_children(children: &Yaml) -> Vec<Box<StructChildSpec>> {
children
.as_vec()
.unwrap()
.iter()
.map(|child_spec| {
if child_spec.is_hash() {
deserialize_struct_hash_child(child_spec)
} else {
deserialize_struct_string_child(child_spec)
}
})
.collect()
}
fn deserialize_build_spec(build_spec_name: &str, build_spec: &Yaml) -> BuildSpec {
if build_spec["children"].is_array() {
let children = deserialize_struct_children(&build_spec["children"]);
BuildSpec::Struct(StructBuildSpec::new(build_spec_name, children))
} else if build_spec["members"].is_array() {
let members = deserialize_leaf_struct_members(&build_spec["members"]);
BuildSpec::LeafStruct(LeafStructBuildSpec::new(build_spec_name, members))
} else if build_spec["rules"].is_array() {
let rules = deserialize_enum_rules(&build_spec["rules"]);
BuildSpec::Enum(EnumBuildSpec::new(build_spec_name, rules))
} else if build_spec["leaf_rules"].is_array() {
let leaf_rules = deserialize_leaf_enum_rules(&build_spec["leaf_rules"]);
BuildSpec::LeafEnum(LeafEnumBuildSpec::new(build_spec_name, leaf_rules))
} else if build_spec["produce"].is_hash() {
BuildSpec::Production(deserialize_production_spec(
if build_spec["struct"].is_hash() {
BuildSpec::Struct(deserialize_struct_spec(
build_spec_name,
&build_spec["produce"],
&build_spec["struct"],
))
} else if build_spec["leaf_struct"].is_hash() {
BuildSpec::LeafStruct(deserialize_leaf_struct(
build_spec_name,
&build_spec["leaf_struct"],
))
} else if build_spec["tree_enum"].is_hash() {
BuildSpec::Enum(deserialize_tree_enum(
build_spec_name,
&build_spec["tree_enum"],
))
} else if build_spec["leaf_enum"].is_hash() {
BuildSpec::LeafEnum(deserialize_leaf_enum(
build_spec_name,
&build_spec["leaf_enum"],
))
} else if build_spec["production"].is_hash() {
BuildSpec::Production(deserialize_production(
build_spec_name,
&build_spec["production"],
))
} else if build_spec["node_production"].is_hash() {
BuildSpec::NodeProduction(deserialize_node_production(
build_spec_name,
&build_spec["node_production"],
))
} else if build_spec["polymorphic_type"].is_hash() {
BuildSpec::Polymorphic(deserialize_polymorphic_spec(
BuildSpec::PolymorphicType(deserialize_polymorphic_type(
build_spec_name,
&build_spec["polymorphic_type"],
))
} else if build_spec["polymorphic_build"].is_hash() {
BuildSpec::PolymorphicBuild(deserialize_polymorphic_build_build_spec(
} else if build_spec["polymorphic_enum_loop_build"].is_hash() {
BuildSpec::PolymorphicEnumLoop(deserialize_polymorphic_enum_loop(
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"],
&build_spec["polymorphic_enum_loop_build"],
))
} else {
panic!(
"Expected a node spec for either a struct, leaf_struct, enum, leaf_enum node type, production, polymorphic type, or polymorphic build type."
);
panic!("Missing or incorrect build type for {}", build_spec_name);
}
}

View File

@ -0,0 +1,9 @@
use crate::deserialize::util::make_build_fn_name;
use crate::spec::node_production_spec::NodeProductionBuildSpec;
use yaml_rust2::Yaml;
pub fn deserialize_node_production(name: &str, props: &Yaml) -> NodeProductionBuildSpec {
let kind = props["kind"].as_str().unwrap();
let with = make_build_fn_name(props["with"].as_str().unwrap());
NodeProductionBuildSpec::new(name, kind, &with)
}

View File

@ -0,0 +1,74 @@
use crate::deserialize::util::{make_build_fn_name, unwrap_single_member_hash};
use crate::spec::polymorphic_enum_loop_spec::{
PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopChildUseCurrent, PolymorphicEnumLoopRule,
PolymorphicEnumLoopRuleBuild, PolymorphicEnumLoopRuleBuildChild,
PolymorphicEnumLoopRuleChildOnEach, PolymorphicEnumLoopRulePassThrough,
};
use yaml_rust2::Yaml;
fn deserialize_build_child(child_name: &str, props: &Yaml) -> PolymorphicEnumLoopRuleBuildChild {
if props["use_current"].is_hash() {
let kind = props["use_current"]["kind"].as_str().unwrap();
PolymorphicEnumLoopRuleBuildChild::UseCurrent(PolymorphicEnumLoopChildUseCurrent::new(
child_name, kind,
))
} else if props["on_each"].is_hash() {
let rule = props["on_each"]["rule"].as_str().unwrap();
PolymorphicEnumLoopRuleBuildChild::OnEach(PolymorphicEnumLoopRuleChildOnEach::new(
child_name, rule,
))
} else {
panic!("Expected 'use_current' or 'on_each' hash for polymorphic enum loop build child");
}
}
fn deserialize_build(name: &str, props: &Yaml) -> PolymorphicEnumLoopRuleBuild {
let variant = props["variant"].as_str().unwrap();
let children = props["children"]
.as_vec()
.unwrap()
.iter()
.map(|child_yaml| {
let (child_name, child_props) = unwrap_single_member_hash(child_yaml);
deserialize_build_child(&child_name, child_props)
})
.map(Box::new)
.collect();
PolymorphicEnumLoopRuleBuild::new(name, variant, children)
}
fn deserialize_pass_through(name: &str, props: &Yaml) -> PolymorphicEnumLoopRulePassThrough {
let kind = props["kind"].as_str().unwrap();
let with = make_build_fn_name(props["with"].as_str().unwrap());
PolymorphicEnumLoopRulePassThrough::new(name, kind, &with)
}
fn deserialize_rule(rule_name: &str, props: &Yaml) -> PolymorphicEnumLoopRule {
if props["pass_through"].is_hash() {
PolymorphicEnumLoopRule::PassThrough(deserialize_pass_through(
rule_name,
&props["pass_through"],
))
} else if props["build"].is_hash() {
PolymorphicEnumLoopRule::Build(deserialize_build(rule_name, &props["build"]))
} else {
panic!("Polymorphic enum loop rule must have 'pass_through' or 'build' hash.");
}
}
pub fn deserialize_polymorphic_enum_loop(name: &str, props: &Yaml) -> PolymorphicEnumLoopBuildSpec {
let kind = props["kind"].as_str().unwrap();
let rules = props["rules"]
.as_vec()
.unwrap()
.iter()
.map(|rule_yaml| {
let (rule_name, rule_props) = unwrap_single_member_hash(rule_yaml);
deserialize_rule(&rule_name, rule_props)
})
.map(Box::new)
.collect();
PolymorphicEnumLoopBuildSpec::new(name, kind, rules)
}

View File

@ -0,0 +1,23 @@
use crate::deserialize::util::unwrap_single_member_hash;
use crate::spec::polymorphic_type_spec::{PolymorphicTypeBuildSpec, PolymorphicTypeVariant};
use yaml_rust2::Yaml;
fn deserialize_variant(variant_name: &str, props: &Yaml) -> PolymorphicTypeVariant {
let inner_kind = props["inner"]["kind"].as_str().unwrap();
PolymorphicTypeVariant::new(variant_name, inner_kind)
}
pub fn deserialize_polymorphic_type(name: &str, props: &Yaml) -> PolymorphicTypeBuildSpec {
let variants = props["variants"]
.as_vec()
.unwrap()
.iter()
.map(|variant_yaml| {
let (variant_name, variant_props) = unwrap_single_member_hash(variant_yaml);
deserialize_variant(&variant_name, variant_props)
})
.map(Box::new)
.collect();
let kind = props["build"]["kind"].as_str().unwrap();
PolymorphicTypeBuildSpec::new(name, variants, kind)
}

View File

@ -0,0 +1,21 @@
use crate::spec::production_spec::{ProductionBooleanFrom, ProductionBuildSpec, ProductionKind, ProductionStringFrom};
use yaml_rust2::Yaml;
pub fn deserialize_production(name: &str, props: &Yaml) -> ProductionBuildSpec {
let kind = match props["kind"].as_str().unwrap() {
"int" => ProductionKind::Int,
"long" => ProductionKind::Long,
"double" => ProductionKind::Double,
"string" => {
let from = match props["from"].as_str().unwrap() {
"string_inner" => ProductionStringFrom::StringInner,
"whole_pair" => ProductionStringFrom::WholePair,
_ => panic!("Unknown string production from: {}", props["from"].as_str().unwrap())
};
ProductionKind::String(from)
},
"boolean" => ProductionKind::Boolean(ProductionBooleanFrom::ParseWholePair),
_ => panic!("Unknown production kind: {}", props["kind"].as_str().unwrap()),
};
ProductionBuildSpec::new(name, kind)
}

View File

@ -0,0 +1,140 @@
use crate::deserialize_error;
use convert_case::{Case, Casing};
use crate::deserialize::util::{get_as_bool, make_build_fn_name, unwrap_single_member_hash};
use crate::spec::struct_spec::*;
use yaml_rust2::Yaml;
fn deserialize_skip_child(props: &Yaml) -> StructChild {
let rule = props["rule"].as_str().unwrap();
StructChild::SkipChild(SkipChild::new(rule))
}
fn deserialize_vec_child(child_name: &str, props: &Yaml) -> StructChild {
let rule = props["rule"].as_str().unwrap();
let kind = props["kind"].as_str()
.unwrap_or(rule);
StructChild::VecChild(VecChild::new(child_name, rule, kind))
}
fn deserialize_member_build(child_name: &str, rule: &str, props: &Yaml) -> MemberChildBuild {
if props["node"].is_hash() {
let node_props = &props["node"];
let kind = node_props["kind"].as_str().unwrap_or(rule);
let with = node_props["with"]
.as_str()
.map(ToString::to_string)
.unwrap_or_else(|| make_build_fn_name(kind));
let or_else = if get_as_bool(&node_props["or_else_default"]) {
Some(String::from("default"))
} else if let Some(or_else) = node_props["or_else"].as_str() {
Some(or_else.to_string())
} else {
None
};
MemberChildBuild::Node(NodeMemberBuild::new(kind, &with, or_else))
} else if props["boolean"].is_hash() {
let boolean_props = &props["boolean"];
if let Some(on) = boolean_props["on"].as_str() {
if on == "rule_present" {
MemberChildBuild::Boolean(BooleanMemberBuild::new(
BooleanMemberBuildOn::RulePresent,
))
} else {
panic!(
"Expected 'on' in 'boolean' in 'build' in {} to be 'rule_present'",
child_name
);
}
} else {
panic!("Expected 'on' in 'boolean' in 'build' in {}", child_name);
}
} else {
panic!(
"Expected either of 'node' or 'boolean' in 'build' in {}",
child_name
);
}
}
fn deserialize_member_child(child_name: &str, props: &Yaml) -> StructChild {
let rule = props["rule"]
.as_str()
.map(ToString::to_string)
.unwrap_or_else(|| child_name.to_case(Case::Pascal));
let optional = get_as_bool(&props["optional"]);
if props["build"].is_hash() {
let build = deserialize_member_build(child_name, &rule, &props["build"]);
StructChild::MemberChild(MemberChild::new(
child_name,
&rule,
optional,
Box::new(build),
))
} else {
StructChild::MemberChild(MemberChild::new(
child_name,
&rule,
optional,
Box::new(MemberChildBuild::Node(NodeMemberBuild::new(
&rule,
&make_build_fn_name(&rule),
None,
))),
))
}
}
fn deserialize_hash_child(name: &str, props: &Yaml) -> StructChild {
if props["skip"].is_hash() {
deserialize_skip_child(&props["skip"])
} else if props["vec"].is_hash() {
deserialize_vec_child(name, &props["vec"])
} else if props["member"].is_hash() {
deserialize_member_child(name, &props["member"])
} else {
panic!("Expected 'skip' or 'vec' in 'member' in {}", name);
}
}
fn deserialize_string_child(name: &str) -> StructChild {
let name_pascal = name.to_case(Case::Pascal);
StructChild::MemberChild(MemberChild::new(
name,
&name_pascal,
false,
Box::new(MemberChildBuild::Node(NodeMemberBuild::new(
&name_pascal,
&make_build_fn_name(name),
None,
))),
))
}
fn deserialize_children(children_yaml: &Yaml) -> Vec<Box<StructChild>> {
children_yaml
.as_vec()
.unwrap()
.iter()
.map(|child_yaml| {
if let Some(name) = child_yaml.as_str() {
deserialize_string_child(name)
} else {
let (child_name, child_props) = unwrap_single_member_hash(child_yaml);
deserialize_hash_child(&child_name, child_props)
}
})
.map(Box::new)
.collect()
}
pub fn deserialize_struct_spec(name: &str, struct_yaml: &Yaml) -> StructSpec {
let children = if struct_yaml["children"].is_array() {
deserialize_children(&struct_yaml["children"])
} else {
deserialize_error!("array", "children", name);
};
StructSpec::new(name, children)
}

View File

@ -0,0 +1,50 @@
use crate::deserialize::util::{get_as_bool_or, unwrap_single_member_hash};
use crate::spec::tree_enum_spec::{
EnumRuleChild, EnumRuleChildKind, TreeEnumBuildSpec, TreeEnumRule,
};
use yaml_rust2::Yaml;
fn deserialize_hash_enum_rule(rule: &str, props: &Yaml) -> TreeEnumRule {
if get_as_bool_or(&props["child"], true) {
let kind = match props["kind"].as_str().unwrap() {
"int" => EnumRuleChildKind::Int,
"long" => EnumRuleChildKind::Long,
"double" => EnumRuleChildKind::Double,
"string" => EnumRuleChildKind::String,
"boolean" => EnumRuleChildKind::Boolean,
_ => panic!("Invalid kind: {}", props["kind"].as_str().unwrap()),
};
TreeEnumRule::new(rule, Some(Box::new(EnumRuleChild::new(Box::new(kind)))))
} else {
// no child
TreeEnumRule::new(rule, None)
}
}
fn deserialize_string_enum_rule(rule: &str) -> TreeEnumRule {
TreeEnumRule::new(
rule,
Some(Box::new(EnumRuleChild::new(Box::new(
EnumRuleChildKind::Node,
)))),
)
}
pub fn deserialize_tree_enum(name: &str, props: &Yaml) -> TreeEnumBuildSpec {
let rules = props["rules"]
.as_vec()
.unwrap()
.iter()
.map(|rule_yaml| {
if let Some(rule) = rule_yaml.as_str() {
deserialize_string_enum_rule(rule)
} else {
let (rule, props) = unwrap_single_member_hash(rule_yaml);
deserialize_hash_enum_rule(&rule, props)
}
})
.map(Box::new)
.collect();
TreeEnumBuildSpec::new(name, rules)
}

View File

@ -0,0 +1,37 @@
use convert_case::{Case, Casing};
use yaml_rust2::Yaml;
#[macro_export]
macro_rules! deserialize_error {
($kind:expr, $key:expr, $name:expr) => {
panic!("Expected {} '{}' in {}.", $kind, $key, $name);
};
}
pub fn make_build_fn_name(s: &str) -> String {
format!("build_{}", s.to_case(Case::Snake))
}
pub fn make_build_pair(s: &str) -> String {
format!("{}_pair", s.to_case(Case::Snake))
}
pub fn unwrap_single_member_hash(hash: &Yaml) -> (String, &Yaml) {
let as_hash = hash.as_hash().unwrap();
if as_hash.is_empty() {
panic!("empty hash");
} else if as_hash.len() > 1 {
panic!("hash contains more than one key");
}
let (member_key, member_value) = as_hash.iter().collect::<Vec<(&Yaml, &Yaml)>>()[0];
let key_as_string = member_key.as_str().unwrap().to_string();
(key_as_string, member_value)
}
pub fn get_as_bool(yaml: &Yaml) -> bool {
yaml.as_bool().unwrap_or(false)
}
pub fn get_as_bool_or(yaml: &Yaml, or: bool) -> bool {
yaml.as_bool().unwrap_or(or)
}

View File

@ -0,0 +1,21 @@
pub struct LeafEnumBuildSpec {
build: String,
rules: Vec<String>,
}
impl LeafEnumBuildSpec {
pub fn new(build: &str, rules: Vec<String>) -> Self {
Self {
build: build.to_string(),
rules,
}
}
pub fn build(&self) -> &str {
&self.build
}
pub fn rules(&self) -> impl Iterator<Item = &str> {
self.rules.iter().map(AsRef::as_ref)
}
}

View File

@ -0,0 +1,47 @@
pub struct LeafStructBuildSpec {
build: String,
members: Vec<Box<LeafStructMember>>,
}
impl LeafStructBuildSpec {
pub fn new(build: &str, members: Vec<Box<LeafStructMember>>) -> Self {
Self {
build: build.to_string(),
members,
}
}
pub fn build(&self) -> &str {
&self.build
}
pub fn members(&self) -> impl Iterator<Item = &LeafStructMember> {
self.members.iter().map(Box::as_ref)
}
}
pub struct LeafStructMember {
name: String,
kind: LeafStructMemberKind,
}
impl LeafStructMember {
pub fn new(name: &str, kind: LeafStructMemberKind) -> Self {
Self {
name: name.to_string(),
kind,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &LeafStructMemberKind {
&self.kind
}
}
pub enum LeafStructMemberKind {
String,
}

View File

@ -1,700 +1,28 @@
use crate::spec::node_production_spec::NodeProductionBuildSpec;
use leaf_enum_spec::LeafEnumBuildSpec;
use leaf_struct_spec::LeafStructBuildSpec;
use polymorphic_type_spec::PolymorphicTypeBuildSpec;
use production_spec::ProductionBuildSpec;
use struct_spec::StructSpec;
use tree_enum_spec::TreeEnumBuildSpec;
use crate::spec::polymorphic_enum_loop_spec::PolymorphicEnumLoopBuildSpec;
pub(crate) mod leaf_enum_spec;
pub(crate) mod leaf_struct_spec;
pub(crate) mod node_production_spec;
pub(crate) mod polymorphic_enum_loop_spec;
pub(crate) mod polymorphic_type_spec;
pub(crate) mod production_spec;
pub(crate) mod struct_spec;
pub(crate) mod tree_enum_spec;
pub enum BuildSpec {
Enum(EnumBuildSpec),
Enum(TreeEnumBuildSpec),
LeafEnum(LeafEnumBuildSpec),
Struct(StructBuildSpec),
Struct(StructSpec),
LeafStruct(LeafStructBuildSpec),
Production(ProductionBuildSpec),
Polymorphic(PolymorphicTypeBuildSpec),
PolymorphicBuild(PolymorphicBuildBuildSpec),
PolymorphicEnum(PolymorphicEnumBuildSpec),
}
// Enum build spec
pub struct EnumBuildSpec {
build: String,
rules: Vec<Box<EnumRule>>,
}
impl EnumBuildSpec {
pub fn new(build: &str, rules: Vec<Box<EnumRule>>) -> Self {
EnumBuildSpec {
build: build.to_string(),
rules,
}
}
/// The enum type to be built, in Pascal case.
pub fn build(&self) -> &str {
&self.build
}
/// The individual rule specs.
pub fn rules(&self) -> impl Iterator<Item = &EnumRule> {
self.rules.iter().map(Box::as_ref)
}
}
pub struct EnumRule {
rule: String,
child: Option<Box<EnumRuleChild>>,
}
impl EnumRule {
pub fn new(rule: &str, child: Option<Box<EnumRuleChild>>) -> Self {
Self {
rule: rule.to_string(),
child,
}
}
/// The enum rule to match, in Pascal case.
pub fn rule(&self) -> &str {
&self.rule
}
pub fn child(&self) -> Option<&EnumRuleChild> {
if let Some(child) = &self.child {
Some(child.as_ref())
} else {
None
}
}
}
pub struct EnumRuleChild {
kind: Box<EnumRuleChildKind>,
}
impl EnumRuleChild {
pub fn new(kind: Box<EnumRuleChildKind>) -> Self {
Self { kind }
}
pub fn kind(&self) -> &EnumRuleChildKind {
&self.kind
}
}
pub enum EnumRuleChildKind {
Node(EnumRuleNodeChild),
Int,
Long,
Double,
USize,
String,
Boolean,
}
pub struct EnumRuleNodeChild {
build: String,
}
impl EnumRuleNodeChild {
pub fn new(build: &str) -> Self {
Self {
build: build.to_string(),
}
}
pub fn build(&self) -> &str {
&self.build
}
}
// Leaf enum build spec
pub struct LeafEnumBuildSpec {
build: String,
rules: Vec<Box<LeafEnumRule>>,
}
impl LeafEnumBuildSpec {
pub fn new(build: &str, rules: Vec<Box<LeafEnumRule>>) -> Self {
Self {
build: build.to_string(),
rules,
}
}
pub fn build(&self) -> &str {
&self.build
}
pub fn rules(&self) -> impl Iterator<Item = &LeafEnumRule> {
self.rules.iter().map(Box::as_ref)
}
}
pub struct LeafEnumRule {
rule: String,
}
impl LeafEnumRule {
pub fn new(rule: &str) -> Self {
Self {
rule: rule.to_string(),
}
}
pub fn rule(&self) -> &str {
&self.rule
}
}
// Struct build spec
pub struct StructBuildSpec {
build: String,
children: Vec<Box<StructChildSpec>>,
}
impl StructBuildSpec {
pub fn new(build: &str, children: Vec<Box<StructChildSpec>>) -> Self {
Self {
build: build.to_string(),
children,
}
}
/// The type to be built, in Pascal case.
pub fn build(&self) -> &str {
&self.build
}
/// The children for this build spec.
pub fn children(&self) -> impl Iterator<Item = &StructChildSpec> {
self.children.iter().map(Box::as_ref)
}
}
pub enum StructChildSpec {
SkipChild(SkipChild),
VecChild(VecChild),
MemberChild(MemberChild),
}
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: Box<VecChildToBuild>,
}
impl VecChild {
pub fn new(name: &str, rule: &str, build: Box<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 {
Node(VecNodeChildToBuild),
String,
}
#[derive(Debug)]
pub struct VecNodeChildToBuild {
build: String,
}
impl VecNodeChildToBuild {
pub fn new(build: &str) -> Self {
Self {
build: build.to_string(),
}
}
/// The type to build, in Pascal case.
pub fn build(&self) -> &str {
&self.build
}
}
#[derive(Debug)]
pub struct MemberChild {
name: String,
rule: String,
build: Box<MemberChildToBuild>,
}
impl MemberChild {
pub fn new(name: &str, rule: &str, build: Box<MemberChildToBuild>) -> 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) -> &MemberChildToBuild {
&self.build
}
}
#[derive(Debug)]
pub enum MemberChildToBuild {
Node(NodeChildToBuild),
Boolean(BooleanChildToBuild),
}
#[derive(Debug)]
pub struct NodeChildToBuild {
kind: String,
with: String,
or_else: Option<String>,
optional: bool,
}
impl NodeChildToBuild {
pub fn new(kind: &str, with: &str, or_else: Option<String>, optional: bool) -> Self {
Self {
kind: kind.to_string(),
with: with.to_string(),
or_else,
optional,
}
}
/// The type to build, in Pascal case.
pub fn kind(&self) -> &str {
&self.kind
}
pub fn with(&self) -> &str {
&self.with
}
/// The default fn to call when unwrapping the child (before passing as arg to new).
pub fn or_else(&self) -> Option<&str> {
self.or_else.as_deref()
}
/// If the type should be wrapped in an Option.
pub fn optional(&self) -> bool {
self.optional
}
}
#[derive(Debug)]
pub struct BooleanChildToBuild {
name: String,
}
impl BooleanChildToBuild {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
}
}
pub fn name(&self) -> &str {
&self.name
}
}
// Leaf Struct build spec
pub struct LeafStructBuildSpec {
build: String,
members: Vec<Box<LeafStructMember>>,
}
impl LeafStructBuildSpec {
pub fn new(build: &str, members: Vec<Box<LeafStructMember>>) -> Self {
Self {
build: build.to_string(),
members,
}
}
pub fn build(&self) -> &str {
&self.build
}
pub fn members(&self) -> impl Iterator<Item = &LeafStructMember> {
self.members.iter().map(Box::as_ref)
}
}
pub struct LeafStructMember {
name: String,
kind: LeafStructMemberKind,
}
impl LeafStructMember {
pub fn new(name: &str, kind: LeafStructMemberKind) -> Self {
Self {
name: name.to_string(),
kind,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &LeafStructMemberKind {
&self.kind
}
}
pub enum LeafStructMemberKind {
String,
}
pub struct ProductionBuildSpec {
rule: String,
kind: ProductionKind,
}
impl ProductionBuildSpec {
pub fn new(rule: &str, kind: ProductionKind) -> Self {
Self {
rule: rule.to_string(),
kind,
}
}
pub fn rule(&self) -> &str {
&self.rule
}
pub fn kind(&self) -> &ProductionKind {
&self.kind
}
}
pub enum ProductionKind {
Int,
Long,
Double,
String(ProductionStringFrom),
Boolean,
Node(ProductionNodeKind),
}
pub struct ProductionNodeKind {
kind: String,
with: String,
}
impl ProductionNodeKind {
pub fn new(kind: &str, with: &str) -> Self {
Self {
kind: kind.to_string(),
with: with.to_string(),
}
}
pub fn kind(&self) -> &str {
&self.kind
}
pub fn with(&self) -> &str {
&self.with
}
}
pub enum ProductionStringFrom {
StringInner,
WholePair,
}
// Polymorphic build spec
pub struct PolymorphicTypeBuildSpec {
name: String,
enum_members: Vec<Box<PolymorphicEnumMember>>,
build_kind: String,
}
impl PolymorphicTypeBuildSpec {
pub fn new(
name: &str,
enum_members: Vec<Box<PolymorphicEnumMember>>,
build_kind: &str,
) -> Self {
Self {
name: name.to_string(),
enum_members,
build_kind: build_kind.to_string(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn enum_members(&self) -> impl Iterator<Item = &PolymorphicEnumMember> {
self.enum_members.iter().map(Box::as_ref)
}
pub fn build_kind(&self) -> &str {
self.build_kind.as_str()
}
}
pub struct PolymorphicEnumMember {
name: String,
inner_kind: String,
}
impl PolymorphicEnumMember {
pub fn new(name: &str, inner_kind: &str) -> Self {
Self {
name: name.to_string(),
inner_kind: inner_kind.to_string(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn inner_kind(&self) -> &str {
&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 fn primary_alternative(&self) -> (&AlternativeTest, &AlternativeBuild) {
let primary_build_alternative = self
.alternatives
.iter()
.find(|alternative| match alternative.action {
AlternativeAction::Build(_) => true,
_ => false,
})
.map(Box::as_ref)
.unwrap();
let alternative_build =
if let AlternativeAction::Build(build) = &primary_build_alternative.action {
build
} else {
unreachable!();
};
(primary_build_alternative.test(), alternative_build)
}
}
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
}
NodeProduction(NodeProductionBuildSpec),
PolymorphicType(PolymorphicTypeBuildSpec),
PolymorphicEnumLoop(PolymorphicEnumLoopBuildSpec),
}

View File

@ -0,0 +1,27 @@
pub struct NodeProductionBuildSpec {
name: String,
kind: String,
with: String
}
impl NodeProductionBuildSpec {
pub fn new(name: &str, kind: &str, with: &str) -> Self {
Self {
name: name.to_string(),
kind: kind.to_string(),
with: with.to_string()
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &str {
&self.kind
}
pub fn with(&self) -> &str {
&self.with
}
}

View File

@ -0,0 +1,133 @@
pub struct PolymorphicEnumLoopBuildSpec {
name: String,
kind: String,
rules: Vec<Box<PolymorphicEnumLoopRule>>,
}
impl PolymorphicEnumLoopBuildSpec {
pub fn new(name: &str, kind: &str, rules: Vec<Box<PolymorphicEnumLoopRule>>) -> Self {
Self {
name: name.to_string(),
kind: kind.to_string(),
rules,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &str {
&self.kind
}
pub fn rules(&self) -> impl Iterator<Item = &PolymorphicEnumLoopRule> {
self.rules.iter().map(Box::as_ref)
}
}
pub enum PolymorphicEnumLoopRule {
PassThrough(PolymorphicEnumLoopRulePassThrough),
Build(PolymorphicEnumLoopRuleBuild),
}
pub struct PolymorphicEnumLoopRulePassThrough {
name: String,
kind: String,
with: String,
}
impl PolymorphicEnumLoopRulePassThrough {
pub fn new(name: &str, kind: &str, with: &str) -> Self {
Self {
name: name.to_string(),
kind: kind.to_string(),
with: with.to_string(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &str {
&self.kind
}
pub fn with(&self) -> &str {
&self.with
}
}
pub struct PolymorphicEnumLoopRuleBuild {
name: String,
variant: String,
children: Vec<Box<PolymorphicEnumLoopRuleBuildChild>>,
}
impl PolymorphicEnumLoopRuleBuild {
pub fn new(name: &str, variant: &str, children: Vec<Box<PolymorphicEnumLoopRuleBuildChild>>) -> Self {
Self {
name: name.to_string(),
variant: variant.to_string(),
children,
}
}
pub fn variant(&self) -> &str {
&self.variant
}
pub fn children(&self) -> impl Iterator<Item = &PolymorphicEnumLoopRuleBuildChild> {
self.children.iter().map(Box::as_ref)
}
}
pub enum PolymorphicEnumLoopRuleBuildChild {
UseCurrent(PolymorphicEnumLoopChildUseCurrent),
OnEach(PolymorphicEnumLoopRuleChildOnEach),
}
pub struct PolymorphicEnumLoopChildUseCurrent {
name: String,
kind: String,
}
impl PolymorphicEnumLoopChildUseCurrent {
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
}
}
pub struct PolymorphicEnumLoopRuleChildOnEach {
name: String,
rule: String,
}
impl PolymorphicEnumLoopRuleChildOnEach {
pub fn new(name: &str, rule: &str) -> Self {
Self {
name: name.to_string(),
rule: rule.to_string(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn rule(&self) -> &str {
&self.rule
}
}

View File

@ -0,0 +1,49 @@
pub struct PolymorphicTypeBuildSpec {
name: String,
variants: Vec<Box<PolymorphicTypeVariant>>,
kind: String,
}
impl PolymorphicTypeBuildSpec {
pub fn new(name: &str, variants: Vec<Box<PolymorphicTypeVariant>>, kind: &str) -> Self {
Self {
name: name.to_string(),
variants,
kind: kind.to_string(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn variants(&self) -> impl Iterator<Item = &PolymorphicTypeVariant> {
self.variants.iter().map(Box::as_ref)
}
pub fn kind(&self) -> &str {
self.kind.as_str()
}
}
pub struct PolymorphicTypeVariant {
name: String,
inner_kind: String,
}
impl PolymorphicTypeVariant {
pub fn new(name: &str, inner_kind: &str) -> Self {
Self {
name: name.to_string(),
inner_kind: inner_kind.to_string(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn inner_kind(&self) -> &str {
&self.inner_kind
}
}

View File

@ -0,0 +1,38 @@
pub struct ProductionBuildSpec {
name: String,
kind: ProductionKind,
}
impl ProductionBuildSpec {
pub fn new(name: &str, kind: ProductionKind) -> Self {
Self {
name: name.to_string(),
kind,
}
}
pub fn rule(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &ProductionKind {
&self.kind
}
}
pub enum ProductionKind {
Int,
Long,
Double,
String(ProductionStringFrom),
Boolean(ProductionBooleanFrom),
}
pub enum ProductionStringFrom {
StringInner,
WholePair,
}
pub enum ProductionBooleanFrom {
ParseWholePair,
}

View File

@ -0,0 +1,171 @@
pub struct StructSpec {
build: String,
children: Vec<Box<StructChild>>,
}
impl StructSpec {
pub fn new(build: &str, children: Vec<Box<StructChild>>) -> Self {
Self {
build: build.to_string(),
children,
}
}
/// The type to be built, in Pascal case.
pub fn build(&self) -> &str {
&self.build
}
/// The children for this build spec.
pub fn children(&self) -> impl Iterator<Item = &StructChild> {
self.children.iter().map(Box::as_ref)
}
}
pub enum StructChild {
SkipChild(SkipChild),
VecChild(VecChild),
MemberChild(MemberChild),
}
pub struct SkipChild {
rule: String,
}
impl SkipChild {
pub fn new(rule: &str) -> Self {
Self {
rule: rule.to_string(),
}
}
/// The grammar rule to match.
pub fn rule(&self) -> &str {
&self.rule
}
}
pub struct VecChild {
name: String,
rule: String,
kind: String,
}
impl VecChild {
pub fn new(name: &str, rule: &str, kind: &str) -> Self {
Self {
name: name.to_string(),
rule: rule.to_string(),
kind: kind.to_string(),
}
}
/// 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
}
pub fn kind(&self) -> &str {
&self.kind
}
}
#[derive(Debug)]
pub struct MemberChild {
name: String,
rule: String,
optional: bool,
build: Box<MemberChildBuild>,
}
impl MemberChild {
pub fn new(name: &str, rule: &str, optional: bool, build: Box<MemberChildBuild>) -> Self {
Self {
name: name.to_string(),
rule: rule.to_string(),
optional,
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
}
pub fn optional(&self) -> bool {
self.optional
}
/// The specification for what to actually build.
pub fn build(&self) -> &MemberChildBuild {
&self.build
}
}
#[derive(Debug)]
pub enum MemberChildBuild {
Node(NodeMemberBuild),
Boolean(BooleanMemberBuild),
}
#[derive(Debug)]
pub struct NodeMemberBuild {
kind: String,
with: String,
or_else: Option<String>,
}
impl NodeMemberBuild {
pub fn new(kind: &str, with: &str, or_else: Option<String>) -> Self {
Self {
kind: kind.to_string(),
with: with.to_string(),
or_else,
}
}
/// The type to build, in Pascal case.
pub fn kind(&self) -> &str {
&self.kind
}
pub fn with(&self) -> &str {
&self.with
}
/// The default fn to call when unwrapping the child (before passing as arg to new).
pub fn or_else(&self) -> Option<&str> {
self.or_else.as_deref()
}
}
#[derive(Debug)]
pub struct BooleanMemberBuild {
on: BooleanMemberBuildOn,
}
impl BooleanMemberBuild {
pub fn new(on: BooleanMemberBuildOn) -> Self {
Self { on }
}
pub fn on(&self) -> &BooleanMemberBuildOn {
&self.on
}
}
#[derive(Debug)]
pub enum BooleanMemberBuildOn {
RulePresent
}

View File

@ -0,0 +1,73 @@
pub struct TreeEnumBuildSpec {
build: String,
rules: Vec<Box<TreeEnumRule>>,
}
impl TreeEnumBuildSpec {
pub fn new(build: &str, rules: Vec<Box<TreeEnumRule>>) -> Self {
TreeEnumBuildSpec {
build: build.to_string(),
rules,
}
}
/// The enum type to be built, in Pascal case.
pub fn build(&self) -> &str {
&self.build
}
/// The individual rule specs.
pub fn rules(&self) -> impl Iterator<Item = &TreeEnumRule> {
self.rules.iter().map(Box::as_ref)
}
}
pub struct TreeEnumRule {
rule: String,
child: Option<Box<EnumRuleChild>>,
}
impl TreeEnumRule {
pub fn new(rule: &str, child: Option<Box<EnumRuleChild>>) -> Self {
Self {
rule: rule.to_string(),
child,
}
}
/// The enum rule to match, in Pascal case.
pub fn rule(&self) -> &str {
&self.rule
}
pub fn child(&self) -> Option<&EnumRuleChild> {
if let Some(child) = &self.child {
Some(child.as_ref())
} else {
None
}
}
}
pub struct EnumRuleChild {
kind: Box<EnumRuleChildKind>,
}
impl EnumRuleChild {
pub fn new(kind: Box<EnumRuleChildKind>) -> Self {
Self { kind }
}
pub fn kind(&self) -> &EnumRuleChildKind {
&self.kind
}
}
pub enum EnumRuleChildKind {
Node,
Int,
Long,
Double,
String,
Boolean,
}

View File

@ -1,9 +0,0 @@
use convert_case::{Case, Casing};
pub fn make_build_fn_name(s: &str) -> String {
format!("build_{}", s.to_case(Case::Snake))
}
pub fn make_build_pair(s: &str) -> String {
format!("{}_pair", s.to_case(Case::Snake))
}

View File

@ -171,7 +171,9 @@ $defs:
additionalProperties: false
properties:
boolean:
$ref: "#/$defs/StructChildMemberBuildBooleanProps"
$ref: "#/$defs/StructChildMemberBuildBooleanProps"'
required:
- boolean
StructChildMemberBuildBooleanProps:
type: object
additionalProperties: false
@ -181,6 +183,8 @@ $defs:
type: string
enum:
- rule_present
required:
- on
# Leaf Struct Node
LeafStructNodeDefinition:
@ -219,12 +223,8 @@ $defs:
kind:
enum:
- string
from:
enum:
- whole_pair
required:
- kind
- from
# Enum node
EnumNodeDefinition:

View File

@ -33,7 +33,6 @@ Identifier:
members:
- name:
kind: string
from: whole_pair
FullyQualifiedName:
struct:
children:
@ -801,6 +800,7 @@ OrRhs:
build:
node:
kind: Expression
with: AndExpression
# And
AndExpression: