diff --git a/ast-generator/src/deserialize/leaf_enum_spec.rs b/ast-generator/src/deserialize/leaf_enum_spec.rs new file mode 100644 index 0000000..bb145ac --- /dev/null +++ b/ast-generator/src/deserialize/leaf_enum_spec.rs @@ -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) +} diff --git a/ast-generator/src/deserialize/leaf_struct_spec.rs b/ast-generator/src/deserialize/leaf_struct_spec.rs new file mode 100644 index 0000000..5243eed --- /dev/null +++ b/ast-generator/src/deserialize/leaf_struct_spec.rs @@ -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) +} diff --git a/ast-generator/src/deserialize/mod.rs b/ast-generator/src/deserialize/mod.rs index acfcf4e..1608173 100644 --- a/ast-generator/src/deserialize/mod.rs +++ b/ast-generator/src/deserialize/mod.rs @@ -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::>()[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::>(); - 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> { - 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 { - Box::new(LeafEnumRule::new(rule_yaml.as_str().unwrap())) -} - -fn deserialize_leaf_enum_rules(rules_yaml: &Yaml) -> Vec> { - 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> { - 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 { - Box::new(EnumRuleChild::new(Box::new(EnumRuleChildKind::Node( - EnumRuleNodeChild::new(rule), - )))) -} - -fn deserialize_enum_rule(rule_yaml: &Yaml) -> Box { - 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> { - 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 { - 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> { - 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 { - 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 { - Box::new(MemberChildToBuild::Boolean(BooleanChildToBuild::new(name))) -} - -fn deserialize_member_child_to_build( - name: &str, - rule: &str, - props: &Yaml, - optional: bool, -) -> Box { - 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 { - 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 { - 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 { - 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 { - 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 { - Box::new(StructChildSpec::SkipChild(SkipChild::new(name, rule))) -} - -fn deserialize_struct_hash_child(child: &Yaml) -> Box { - 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 { - 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> { - 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); } } diff --git a/ast-generator/src/deserialize/node_production_spec.rs b/ast-generator/src/deserialize/node_production_spec.rs new file mode 100644 index 0000000..b016331 --- /dev/null +++ b/ast-generator/src/deserialize/node_production_spec.rs @@ -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) +} diff --git a/ast-generator/src/deserialize/polymorphic_enum_loop_spec.rs b/ast-generator/src/deserialize/polymorphic_enum_loop_spec.rs new file mode 100644 index 0000000..a26c3c9 --- /dev/null +++ b/ast-generator/src/deserialize/polymorphic_enum_loop_spec.rs @@ -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) +} diff --git a/ast-generator/src/deserialize/polymorphic_type_spec.rs b/ast-generator/src/deserialize/polymorphic_type_spec.rs new file mode 100644 index 0000000..54d34c8 --- /dev/null +++ b/ast-generator/src/deserialize/polymorphic_type_spec.rs @@ -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) +} \ No newline at end of file diff --git a/ast-generator/src/deserialize/production_spec.rs b/ast-generator/src/deserialize/production_spec.rs new file mode 100644 index 0000000..34ea86c --- /dev/null +++ b/ast-generator/src/deserialize/production_spec.rs @@ -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) +} diff --git a/ast-generator/src/deserialize/struct_spec.rs b/ast-generator/src/deserialize/struct_spec.rs new file mode 100644 index 0000000..e03aee5 --- /dev/null +++ b/ast-generator/src/deserialize/struct_spec.rs @@ -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> { + 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) +} diff --git a/ast-generator/src/deserialize/tree_enum_spec.rs b/ast-generator/src/deserialize/tree_enum_spec.rs new file mode 100644 index 0000000..016fc2e --- /dev/null +++ b/ast-generator/src/deserialize/tree_enum_spec.rs @@ -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) +} diff --git a/ast-generator/src/deserialize/util.rs b/ast-generator/src/deserialize/util.rs new file mode 100644 index 0000000..345d8e8 --- /dev/null +++ b/ast-generator/src/deserialize/util.rs @@ -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::>()[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) +} diff --git a/ast-generator/src/spec/leaf_enum_spec.rs b/ast-generator/src/spec/leaf_enum_spec.rs new file mode 100644 index 0000000..5c687fc --- /dev/null +++ b/ast-generator/src/spec/leaf_enum_spec.rs @@ -0,0 +1,21 @@ +pub struct LeafEnumBuildSpec { + build: String, + rules: Vec, +} + +impl LeafEnumBuildSpec { + pub fn new(build: &str, rules: Vec) -> Self { + Self { + build: build.to_string(), + rules, + } + } + + pub fn build(&self) -> &str { + &self.build + } + + pub fn rules(&self) -> impl Iterator { + self.rules.iter().map(AsRef::as_ref) + } +} \ No newline at end of file diff --git a/ast-generator/src/spec/leaf_struct_spec.rs b/ast-generator/src/spec/leaf_struct_spec.rs new file mode 100644 index 0000000..0c12df4 --- /dev/null +++ b/ast-generator/src/spec/leaf_struct_spec.rs @@ -0,0 +1,47 @@ +pub struct LeafStructBuildSpec { + build: String, + members: Vec>, +} + +impl LeafStructBuildSpec { + pub fn new(build: &str, members: Vec>) -> Self { + Self { + build: build.to_string(), + members, + } + } + + pub fn build(&self) -> &str { + &self.build + } + + pub fn members(&self) -> impl Iterator { + 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, +} diff --git a/ast-generator/src/spec/mod.rs b/ast-generator/src/spec/mod.rs index 955e485..7a83e08 100644 --- a/ast-generator/src/spec/mod.rs +++ b/ast-generator/src/spec/mod.rs @@ -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>, -} - -impl EnumBuildSpec { - pub fn new(build: &str, rules: Vec>) -> 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 { - self.rules.iter().map(Box::as_ref) - } -} - -pub struct EnumRule { - rule: String, - child: Option>, -} - -impl EnumRule { - pub fn new(rule: &str, child: Option>) -> 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, -} - -impl EnumRuleChild { - pub fn new(kind: Box) -> 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>, -} - -impl LeafEnumBuildSpec { - pub fn new(build: &str, rules: Vec>) -> Self { - Self { - build: build.to_string(), - rules, - } - } - - pub fn build(&self) -> &str { - &self.build - } - - pub fn rules(&self) -> impl Iterator { - 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>, -} - -impl StructBuildSpec { - pub fn new(build: &str, children: Vec>) -> 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 { - 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, -} - -impl VecChild { - pub fn new(name: &str, rule: &str, build: Box) -> 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, -} - -impl MemberChild { - pub fn new(name: &str, rule: &str, build: Box) -> 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, - optional: bool, -} - -impl NodeChildToBuild { - pub fn new(kind: &str, with: &str, or_else: Option, 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>, -} - -impl LeafStructBuildSpec { - pub fn new(build: &str, members: Vec>) -> Self { - Self { - build: build.to_string(), - members, - } - } - - pub fn build(&self) -> &str { - &self.build - } - - pub fn members(&self) -> impl Iterator { - 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>, - build_kind: String, -} - -impl PolymorphicTypeBuildSpec { - pub fn new( - name: &str, - enum_members: Vec>, - 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 { - 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>, -} - -impl PolymorphicBuildBuildSpec { - pub fn new( - name: &str, - return_type: &str, - alternatives: Vec>, - ) -> 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 { - 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>, -} - -impl AlternativeBuild { - pub fn new(enum_variant: &str, children: Vec>) -> Self { - Self { - enum_variant: enum_variant.to_string(), - children, - } - } - - pub fn enum_variant(&self) -> &str { - &self.enum_variant - } - - pub fn children(&self) -> impl Iterator { - 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>, -} - -impl PolymorphicEnumBuildSpec { - pub fn new(name: &str, return_type: &str, rules: Vec>) -> 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 { - 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), } diff --git a/ast-generator/src/spec/node_production_spec.rs b/ast-generator/src/spec/node_production_spec.rs new file mode 100644 index 0000000..2f019a6 --- /dev/null +++ b/ast-generator/src/spec/node_production_spec.rs @@ -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 + } +} \ No newline at end of file diff --git a/ast-generator/src/spec/polymorphic_enum_loop_spec.rs b/ast-generator/src/spec/polymorphic_enum_loop_spec.rs new file mode 100644 index 0000000..25312dd --- /dev/null +++ b/ast-generator/src/spec/polymorphic_enum_loop_spec.rs @@ -0,0 +1,133 @@ +pub struct PolymorphicEnumLoopBuildSpec { + name: String, + kind: String, + rules: Vec>, +} + +impl PolymorphicEnumLoopBuildSpec { + pub fn new(name: &str, kind: &str, rules: Vec>) -> 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 { + 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>, +} + +impl PolymorphicEnumLoopRuleBuild { + pub fn new(name: &str, variant: &str, children: Vec>) -> Self { + Self { + name: name.to_string(), + variant: variant.to_string(), + children, + } + } + + pub fn variant(&self) -> &str { + &self.variant + } + + pub fn children(&self) -> impl Iterator { + 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 + } +} diff --git a/ast-generator/src/spec/polymorphic_type_spec.rs b/ast-generator/src/spec/polymorphic_type_spec.rs new file mode 100644 index 0000000..97eee0e --- /dev/null +++ b/ast-generator/src/spec/polymorphic_type_spec.rs @@ -0,0 +1,49 @@ +pub struct PolymorphicTypeBuildSpec { + name: String, + variants: Vec>, + kind: String, +} + +impl PolymorphicTypeBuildSpec { + pub fn new(name: &str, variants: Vec>, 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 { + 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 + } +} diff --git a/ast-generator/src/spec/production_spec.rs b/ast-generator/src/spec/production_spec.rs new file mode 100644 index 0000000..31714be --- /dev/null +++ b/ast-generator/src/spec/production_spec.rs @@ -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, +} diff --git a/ast-generator/src/spec/struct_spec.rs b/ast-generator/src/spec/struct_spec.rs new file mode 100644 index 0000000..56acd01 --- /dev/null +++ b/ast-generator/src/spec/struct_spec.rs @@ -0,0 +1,171 @@ +pub struct StructSpec { + build: String, + children: Vec>, +} + +impl StructSpec { + pub fn new(build: &str, children: Vec>) -> 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 { + 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, +} + +impl MemberChild { + pub fn new(name: &str, rule: &str, optional: bool, build: Box) -> 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, +} + +impl NodeMemberBuild { + pub fn new(kind: &str, with: &str, or_else: Option) -> 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 +} \ No newline at end of file diff --git a/ast-generator/src/spec/tree_enum_spec.rs b/ast-generator/src/spec/tree_enum_spec.rs new file mode 100644 index 0000000..56301c8 --- /dev/null +++ b/ast-generator/src/spec/tree_enum_spec.rs @@ -0,0 +1,73 @@ +pub struct TreeEnumBuildSpec { + build: String, + rules: Vec>, +} + +impl TreeEnumBuildSpec { + pub fn new(build: &str, rules: Vec>) -> 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 { + self.rules.iter().map(Box::as_ref) + } +} + +pub struct TreeEnumRule { + rule: String, + child: Option>, +} + +impl TreeEnumRule { + pub fn new(rule: &str, child: Option>) -> 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, +} + +impl EnumRuleChild { + pub fn new(kind: Box) -> Self { + Self { kind } + } + + pub fn kind(&self) -> &EnumRuleChildKind { + &self.kind + } +} + +pub enum EnumRuleChildKind { + Node, + Int, + Long, + Double, + String, + Boolean, +} diff --git a/ast-generator/src/util.rs b/ast-generator/src/util.rs deleted file mode 100644 index ae0220b..0000000 --- a/ast-generator/src/util.rs +++ /dev/null @@ -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)) -} diff --git a/src/parser/ast.schema.yaml b/src/parser/ast.schema.yaml index cf16fb5..30292db 100644 --- a/src/parser/ast.schema.yaml +++ b/src/parser/ast.schema.yaml @@ -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: diff --git a/src/parser/ast.yaml b/src/parser/ast.yaml index 58debfc..da89d98 100644 --- a/src/parser/ast.yaml +++ b/src/parser/ast.yaml @@ -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: