From 968b9504364f712e72f8e5c2cbc4d7f797f95db0 Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Sun, 14 Sep 2025 09:36:38 -0500 Subject: [PATCH] Handle deserialization of leaf_enum nodes. --- ast-generator/src/deserialize.rs | 63 +++++++++++++++++++++++--------- ast-generator/src/lib.rs | 8 ++-- ast-generator/src/spec.rs | 61 ++++++++++++++++++++++++++++++- ast-generator/src/type_gen.rs | 2 +- 4 files changed, 112 insertions(+), 22 deletions(-) diff --git a/ast-generator/src/deserialize.rs b/ast-generator/src/deserialize.rs index 647a3ea..93bf24e 100644 --- a/ast-generator/src/deserialize.rs +++ b/ast-generator/src/deserialize.rs @@ -1,18 +1,10 @@ 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 crate::spec::{BuildBooleanOn, BuildSpec, ChildSpec, EnumBuildSpec, EnumRule, LeafEnumBuildSpec, LeafEnumRule, LeafEnumRuleBuild, 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_as_bool(yaml: &Yaml) -> bool { + yaml.as_bool().unwrap_or_else(|| false) } fn get_vec_child_to_build(build: &Yaml, name: &str, rule: &str) -> VecChildToBuild { @@ -126,13 +118,13 @@ fn get_child_specs(children: &Yaml) -> Vec { .map(|s| s.to_string()) .unwrap_or(name.to_case(Case::Pascal)); - if get_skip(&props["skip"]) { + if get_as_bool(&props["skip"]) { return ChildSpec::SkipChild(SkipChild::new(name, &rule)); } let build = &props["build"]; - if get_vec(&props["vec"]) { + if get_as_bool(&props["vec"]) { get_vec_child(name, &rule, build) } else { let optional = props["optional"].as_bool().unwrap_or_else(|| false); @@ -146,6 +138,45 @@ fn get_child_specs(children: &Yaml) -> Vec { .collect() } +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::>()[0]; + let key_as_string = member_key.as_str().unwrap().to_string(); + (key_as_string, member_value) +} + +fn get_leaf_enum_rules(rules: &Yaml) -> Vec { + rules + .as_vec() + .unwrap() + .iter() + .map(|rule| { + if rule.is_hash() { + let (rule_as_string, rule_hash) = unwrap_single_member_hash(rule); + if get_as_bool(&rule_hash["child"]) { + let build = LeafEnumRuleBuild::Child { + with: make_build_fn_name(&rule_as_string) + }; + LeafEnumRule::new(&rule_as_string, build) + } else { + panic!("if a leaf_enum rule is a hash, its child prop must be present and true"); + } + } else { + let rule_as_str = rule.as_str().unwrap(); + let build = LeafEnumRuleBuild::EnumRule { + rule: rule_as_str.to_string(), + }; + LeafEnumRule::new(rule_as_str, build) + } + }) + .collect() +} + fn get_enum_rules(rule_specs: &Yaml) -> Vec { rule_specs .as_vec() @@ -189,10 +220,8 @@ fn deserialize_build_spec(build_spec_name: &Yaml, build_spec: &Yaml) -> BuildSpe child_specs, )) } else if yaml_is_string(node_type, "leaf_enum") && rules.is_array() { - todo!( - "Not yet ready for leaf_enum node: {}", - build_spec_name_pascal - ) + let leaf_enum_rules = get_leaf_enum_rules(rules); + BuildSpec::LeafEnum(LeafEnumBuildSpec::from_name(build_spec_name_pascal, leaf_enum_rules)) } else if rules.is_array() { // enum node let enum_rules = get_enum_rules(rules); diff --git a/ast-generator/src/lib.rs b/ast-generator/src/lib.rs index 9ee1fa0..e993ea4 100644 --- a/ast-generator/src/lib.rs +++ b/ast-generator/src/lib.rs @@ -16,7 +16,9 @@ fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) { BuildSpec::Enum(enum_build_spec) => { println!("Spec name: {}", enum_build_spec.name()); } - BuildSpec::LeafEnum() => todo!(), + BuildSpec::LeafEnum(leaf_enum_build_spec) => { + println!("Spec name: {}", leaf_enum_build_spec.name()); + }, BuildSpec::Struct(struct_build_spec) => { println!("Spec name: {}", struct_build_spec.name()); } @@ -41,7 +43,7 @@ fn generate_build_file(build_specs: &[BuildSpec]) -> AstGeneratedFile { .map(|build_spec| { match build_spec { BuildSpec::Enum(enum_build_spec) => { quote! {} } - BuildSpec::LeafEnum() => { quote! {} } + BuildSpec::LeafEnum(leaf_enum_build_spec) => { quote! {} } BuildSpec::Struct(struct_build_spec) => { let struct_build_fn_stream = make_struct_build_fn(struct_build_spec); debug_built_spec(build_spec, &struct_build_fn_stream); @@ -76,7 +78,7 @@ pub fn test_dump() -> String { for build_spec in &build_specs { match build_spec { BuildSpec::Enum(_) => {} - BuildSpec::LeafEnum() => {} + BuildSpec::LeafEnum(_) => {} BuildSpec::Struct(struct_spec) => { let struct_build_fn_stream = make_struct_build_fn(struct_spec); debug_built_spec(build_spec, &struct_build_fn_stream); diff --git a/ast-generator/src/spec.rs b/ast-generator/src/spec.rs index a96a08f..0972882 100644 --- a/ast-generator/src/spec.rs +++ b/ast-generator/src/spec.rs @@ -3,7 +3,7 @@ use convert_case::{Case, Casing}; pub enum BuildSpec { Enum(EnumBuildSpec), - LeafEnum(), + LeafEnum(LeafEnumBuildSpec), Struct(StructBuildSpec), } @@ -77,6 +77,65 @@ impl EnumRule { } } +pub struct LeafEnumBuildSpec { + name: String, + build: String, + rules: Vec, +} + +impl LeafEnumBuildSpec { + pub fn from_name(name: &str, rules: Vec) -> Self { + Self { + name: name.to_string(), + build: name.to_string(), + rules + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn build(&self) -> &str { + &self.build + } + + pub fn rules(&self) -> &[LeafEnumRule] { + &self.rules + } +} + +pub struct LeafEnumRule { + rule: String, + build: LeafEnumRuleBuild +} + +impl LeafEnumRule { + pub fn new(rule: &str, build: LeafEnumRuleBuild) -> Self { + Self { + rule: rule.to_string(), + build + } + } + + pub fn rule(&self) -> &str { + &self.rule + } + + pub fn build(&self) -> &LeafEnumRuleBuild { + &self.build + } +} + +pub enum LeafEnumRuleBuild { + EnumRule { + rule: String, + }, + Child { + with: String + } +} + pub struct StructBuildSpec { name: String, build: String, diff --git a/ast-generator/src/type_gen.rs b/ast-generator/src/type_gen.rs index 02248ed..b244077 100644 --- a/ast-generator/src/type_gen.rs +++ b/ast-generator/src/type_gen.rs @@ -163,7 +163,7 @@ fn make_struct_type(build_spec: &StructBuildSpec) -> TokenStream { pub fn make_type(build_spec: &BuildSpec) -> TokenStream { match build_spec { BuildSpec::Enum(enum_build_spec) => make_enum_type(enum_build_spec), - BuildSpec::LeafEnum() => todo!(), + BuildSpec::LeafEnum(leaf_enum_build_spec) => todo!(), BuildSpec::Struct(struct_build_spec) => make_struct_type(struct_build_spec), } }