deimos-lang/ast-generator/src/deserialize/struct_spec.rs
2025-10-21 18:36:03 -05:00

199 lines
6.4 KiB
Rust

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_field(field_yaml: &Yaml) -> StructField {
let (name, props) = unwrap_single_member_hash(field_yaml);
let kind = props["kind"].as_str().unwrap();
let wrap = if let Some(wrap) = props["wrap"].as_str() {
match wrap {
"rc_ref_cell" => Some(StructFieldWrap::RcRefCell),
_ => panic!(),
}
} else {
None
};
let vec = get_as_bool(&props["vec"]);
StructField::new(&name, kind, wrap, vec)
}
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);
if kind == "string" {
let build = VecChildBuild::String(VecChildStringBuild::new(&make_build_fn_name(rule)));
StructChild::VecChild(VecChild::new(child_name, rule, Box::new(build)))
} else {
let build = VecChildBuild::Node(VecChildNodeBuild::new(rule, &make_build_fn_name(rule)));
StructChild::VecChild(VecChild::new(child_name, rule, Box::new(build)))
}
}
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(|with| make_build_fn_name(with))
.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 one of 'node', 'boolean', or 'special' 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_special_child(name: &str, props: &Yaml) -> StructChild {
match props["kind"].as_str().unwrap() {
"file_id" => StructChild::Special(SpecialChild::new(name, SpecialChildKind::FileId)),
"range" => StructChild::Special(SpecialChild::new(name, SpecialChildKind::Range)),
_ => panic!(
"Invalid special child kind {} in {}",
props["kind"].as_str().unwrap(),
name
),
}
}
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 if props["special"].is_hash() {
deserialize_special_child(name, &props["special"])
} 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);
};
let fields = if struct_yaml["fields"].is_array() {
struct_yaml["fields"]
.as_vec()
.unwrap()
.iter()
.map(|field| deserialize_field(field))
.map(Box::new)
.collect()
} else {
vec![]
};
let derive = if struct_yaml["derive"].is_array() {
struct_yaml["derive"]
.as_vec()
.unwrap()
.iter()
.map(|derive| derive.as_str().unwrap().to_string())
.collect()
} else {
vec![]
};
StructSpec::new(name, children, fields, derive)
}