deimos-lang/ast-generator/src/deserialize.rs
2025-09-17 17:21:37 -05:00

433 lines
15 KiB
Rust

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 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["kind"].is_hash() {
let node = production_yaml["kind"]["node"].as_str().unwrap();
ProductionKind::Node(node.to_string())
} 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);
Box::new(MemberChildToBuild::Node(NodeChildToBuild::new(
rule, 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"]);
Box::new(MemberChildToBuild::Node(NodeChildToBuild::new(
rule, 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,
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(
build_spec_name,
&build_spec["produce"],
))
} else if build_spec["polymorphic_type"].is_hash() {
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 {
panic!(
"Expected a node spec for either a struct, leaf_struct, enum, leaf_enum node type, production, polymorphic type, or polymorphic build type."
);
}
}
pub fn deserialize_yaml_spec(yaml: &str) -> Vec<BuildSpec> {
let docs = YamlLoader::load_from_str(yaml).unwrap();
let doc = &docs[0];
doc.as_hash()
.unwrap()
.iter()
.map(|(build_spec_name, build_spec)| {
let name_as_str = build_spec_name.as_str().unwrap();
deserialize_build_spec(name_as_str, build_spec)
})
.collect()
}