diff --git a/ast-generator/src/deserialize.rs b/ast-generator/src/deserialize.rs index 07ee4a9..6bcfae2 100644 --- a/ast-generator/src/deserialize.rs +++ b/ast-generator/src/deserialize.rs @@ -1,4 +1,4 @@ -use crate::spec::{BooleanChildToBuild, BuildSpec, EnumBuildSpec, EnumRule, EnumRuleChild, EnumRuleChildKind, EnumRuleNodeChild, LeafEnumBuildSpec, LeafEnumRule, LeafStructBuildSpec, LeafStructMember, LeafStructMemberKind, MemberChild, MemberChildToBuild, NodeChildToBuild, PolymorphicBuildSpec, PolymorphicEnumMember, ProductionBuildSpec, ProductionKind, ProductionStringFrom, SkipChild, StructBuildSpec, StructChildSpec, VecChild, VecChildToBuild, VecNodeChildToBuild}; +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}; @@ -18,7 +18,87 @@ fn unwrap_single_member_hash(hash: &Yaml) -> (String, &Yaml) { (key_as_string, member_value) } -fn deserialize_polymorphic_enum_members(enum_members_yaml: &Yaml) -> Vec> { +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() @@ -32,27 +112,38 @@ fn deserialize_polymorphic_enum_members(enum_members_yaml: &Yaml) -> Vec PolymorphicBuildSpec { +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(); - PolymorphicBuildSpec::new(name, enum_members, build_kind) + PolymorphicTypeBuildSpec::new(name, enum_members, build_kind) } fn deserialize_production_spec(rule: &str, production_yaml: &Yaml) -> ProductionBuildSpec { - let kind = 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()), + 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) } @@ -66,9 +157,7 @@ fn deserialize_leaf_enum_rules(rules_yaml: &Yaml) -> Vec> { .as_vec() .unwrap() .iter() - .map(|rule_yaml| { - deserialize_leaf_enum_rule(rule_yaml) - }) + .map(|rule_yaml| deserialize_leaf_enum_rule(rule_yaml)) .collect() } @@ -83,16 +172,19 @@ fn deserialize_enum_rule_custom_child(rule_props: &Yaml) -> Option EnumRuleChildKind::USize, "string" => EnumRuleChildKind::String, "boolean" => EnumRuleChildKind::Boolean, - _ => panic!("unsupported enum rule kind: {}", rule_props["kind"].as_str().unwrap()), + _ => 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)), - ))) + Box::new(EnumRuleChild::new(Box::new(EnumRuleChildKind::Node( + EnumRuleNodeChild::new(rule), + )))) } fn deserialize_enum_rule(rule_yaml: &Yaml) -> Box { @@ -100,7 +192,7 @@ fn deserialize_enum_rule(rule_yaml: &Yaml) -> Box { let (rule, rule_props) = unwrap_single_member_hash(rule_yaml); Box::new(EnumRule::new( &rule, - deserialize_enum_rule_custom_child(rule_props) + deserialize_enum_rule_custom_child(rule_props), )) } else { let rule_as_str = rule_yaml.as_str().unwrap(); @@ -184,7 +276,9 @@ fn deserialize_member_child_to_build( } } else { let optional = get_as_bool(&props["optional"]); - Box::new(MemberChildToBuild::Node(NodeChildToBuild::new(rule, None, optional))) + Box::new(MemberChildToBuild::Node(NodeChildToBuild::new( + rule, None, optional, + ))) } } @@ -297,11 +391,29 @@ fn deserialize_build_spec(build_spec_name: &str, build_spec: &Yaml) -> BuildSpec 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"])) + 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"])) + 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, or a production type."); + panic!( + "Expected a node spec for either a struct, leaf_struct, enum, leaf_enum node type, production, polymorphic type, or polymorphic build type." + ); } } diff --git a/ast-generator/src/lib.rs b/ast-generator/src/lib.rs index b721cc7..85c534c 100644 --- a/ast-generator/src/lib.rs +++ b/ast-generator/src/lib.rs @@ -2,7 +2,9 @@ pub mod deserialize; mod enum_build_fn; mod leaf_enum_build_fn; mod leaf_struct_build_fn; +mod polymorphic_build_build_fn; mod polymorphic_build_fn; +mod polymorphic_enum_build_fn; mod production_build_fn; mod spec; mod struct_build_fn; @@ -12,6 +14,9 @@ mod util; use crate::enum_build_fn::make_enum_build_fn; use crate::leaf_enum_build_fn::make_leaf_enum_build_fn; use crate::leaf_struct_build_fn::make_leaf_struct_build_fn; +use crate::polymorphic_build_build_fn::make_polymorphic_build_build_fn; +use crate::polymorphic_build_fn::make_polymorphic_build_fn; +use crate::polymorphic_enum_build_fn::make_polymorphic_enum_build_fn; use crate::production_build_fn::make_production_build_fn; use crate::struct_build_fn::make_struct_build_fn; use crate::type_gen::make_type; @@ -19,7 +24,6 @@ use proc_macro2::TokenStream; use quote::quote; use spec::BuildSpec; use syn::File; -use crate::polymorphic_build_fn::make_polymorphic_build_fn; fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) { println!("*** BuildSpec ***"); @@ -43,7 +47,22 @@ fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) { println!("Production Spec - rule: {}", production_build_spec.rule()); } BuildSpec::Polymorphic(polymorphic_build_spec) => { - println!("Polymorphic Spec - name: {}", polymorphic_build_spec.name()); + println!( + "Polymorphic Type Spec - name: {}", + polymorphic_build_spec.name() + ); + } + BuildSpec::PolymorphicBuild(polymorphic_build_build_spec) => { + println!( + "Polymorphic Build Spec - name: {}", + polymorphic_build_build_spec.name() + ); + } + BuildSpec::PolymorphicEnum(polymorphic_enum_build_spec) => { + println!( + "Polymorphic Enum Spec - name: {}", + polymorphic_enum_build_spec.name() + ); } } println!("{:#?}", token_stream); @@ -95,6 +114,16 @@ fn generate_build_file(build_specs: &[BuildSpec]) -> AstGeneratedFile { debug_built_spec(build_spec, &stream); stream } + BuildSpec::PolymorphicBuild(polymorphic_build_build_spec) => { + let stream = make_polymorphic_build_build_fn(polymorphic_build_build_spec); + debug_built_spec(build_spec, &stream); + stream + } + BuildSpec::PolymorphicEnum(polymorphic_enum_build_spec) => { + let stream = make_polymorphic_enum_build_fn(polymorphic_enum_build_spec); + debug_built_spec(build_spec, &stream); + stream + } }) .collect::>(); let combined = quote! { diff --git a/ast-generator/src/polymorphic_build_build_fn.rs b/ast-generator/src/polymorphic_build_build_fn.rs new file mode 100644 index 0000000..d2a9c27 --- /dev/null +++ b/ast-generator/src/polymorphic_build_build_fn.rs @@ -0,0 +1,134 @@ +use convert_case::{Case, Casing}; +use crate::spec::{AlternativeAction, AlternativeBuild, AlternativeBuildChild, AlternativeChild, AlternativeTest, PolymorphicBuildBuildSpec}; +use crate::util::{make_build_fn_name, make_build_pair}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +fn make_build_child(build_child: &AlternativeBuildChild) -> TokenStream { + let rule_ident = format_ident!("{}", build_child.rule()); + let child_ident = format_ident!("{}", build_child.name()); + let child_build_fn_ident = format_ident!("{}", make_build_fn_name(build_child.rule())); + + quote! { + Rule::#rule_ident => { + #child_ident = Some(#child_build_fn_ident(inner_pair)); + } + } +} + +fn make_build_action( + spec: &PolymorphicBuildBuildSpec, + alternative_build: &AlternativeBuild, +) -> TokenStream { + let enum_type_ident = format_ident!("{}", spec.return_type()); + let enum_variant_ident = format_ident!("{}", alternative_build.enum_variant()); + + let child_holders = alternative_build + .children() + .map(|child| { + match child { + AlternativeChild::Skip => None, + AlternativeChild::Build(build_child) => { + let child_ident = format_ident!("{}", build_child.name()); + let child_type_ident = format_ident!("{}", build_child.kind()); + Some(quote! { + let mut #child_ident: Option<#child_type_ident> = None + }) + } + } + }) + .filter(Option::is_some) + .map(Option::unwrap) + .collect::>(); + + let pair_ident = format_ident!("{}", make_build_pair(spec.name())); + + let rule_matchers = alternative_build + .children() + .map(|child| { + match child { + AlternativeChild::Skip => None, + AlternativeChild::Build(build_child) => { + Some(make_build_child(build_child)) + } + } + }) + .filter(Option::is_some) + .map(Option::unwrap) + .collect::>(); + + let built_ident = format_ident!("{}", spec.name().to_case(Case::Snake)); + let inner_type_ident = format_ident!("{}", spec.name()); + let child_args = alternative_build.children() + .map(|child| { + match child { + AlternativeChild::Skip => None, + AlternativeChild::Build(child_build) => { + let child_ident = format_ident!("{}", child_build.name()); + Some(quote! { + Box::new(#child_ident.unwrap()) + }) + } + } + }) + .filter(Option::is_some) + .map(Option::unwrap) + .collect::>(); + + quote! { + #(#child_holders;)* + + for inner_pair in #pair_ident.into_inner() { + match inner_pair.as_rule() { + #(#rule_matchers),* + _ => unreachable!() + } + } + + let #built_ident = #inner_type_ident::new(#(#child_args),*); + + #enum_type_ident::#enum_variant_ident(#built_ident) + } +} + +pub fn make_polymorphic_build_build_fn(spec: &PolymorphicBuildBuildSpec) -> TokenStream { + let build_fn_ident = format_ident!("{}", make_build_fn_name(spec.name())); + let pair_ident = format_ident!("{}", make_build_pair(spec.name())); + let return_type_ident = format_ident!("{}", spec.return_type()); + + let alternatives = spec + .alternatives() + .map(|alternative| { + let count_to_match: usize = match alternative.test() { + AlternativeTest::NumberOfPairs(count) => count.clone().try_into().unwrap(), + }; + let action = match alternative.action() { + AlternativeAction::ReturnBuild(kind) => { + let inner_build_fn_ident = format_ident!("{}", make_build_fn_name(kind)); + quote! { + let inner_pair = #pair_ident.into_inner().next().unwrap(); + #inner_build_fn_ident(inner_pair) + } + } + AlternativeAction::Build(alternative_build) => { + make_build_action(spec, alternative_build) + } + }; + quote! { + #count_to_match => { + #action + } + } + }) + .collect::>(); + + quote! { + fn #build_fn_ident(#pair_ident: Pair) -> #return_type_ident { + let count = #pair_ident.clone().into_inner().count(); + match count { + #(#alternatives,)* + _ => unreachable!() + } + } + } +} diff --git a/ast-generator/src/polymorphic_build_fn.rs b/ast-generator/src/polymorphic_build_fn.rs index 52d3922..ebe326d 100644 --- a/ast-generator/src/polymorphic_build_fn.rs +++ b/ast-generator/src/polymorphic_build_fn.rs @@ -1,19 +1,18 @@ +use crate::spec::PolymorphicTypeBuildSpec; +use crate::util::{make_build_fn_name, make_build_pair}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use crate::spec::PolymorphicBuildSpec; -use crate::token_stream_to_string; -use crate::util::{make_build_fn_name, make_build_pair}; -pub fn make_polymorphic_build_fn(build_spec: &PolymorphicBuildSpec) -> TokenStream { +pub fn make_polymorphic_build_fn(build_spec: &PolymorphicTypeBuildSpec) -> TokenStream { let build_fn_ident = format_ident!("{}", make_build_fn_name(build_spec.name())); let pair_ident = format_ident!("{}", make_build_pair(&build_spec.name())); let return_type_ident = format_ident!("{}", build_spec.name()); let inner_build_fn_ident = format_ident!("{}", make_build_fn_name(build_spec.build_kind())); - + quote! { fn #build_fn_ident(#pair_ident: Pair) -> #return_type_ident { let inner_pair = #pair_ident.into_inner().next().unwrap(); - #inner_build_fn_ident(inner_pair) + #inner_build_fn_ident(inner_pair) } } -} \ No newline at end of file +} diff --git a/ast-generator/src/polymorphic_enum_build_fn.rs b/ast-generator/src/polymorphic_enum_build_fn.rs new file mode 100644 index 0000000..24502a8 --- /dev/null +++ b/ast-generator/src/polymorphic_enum_build_fn.rs @@ -0,0 +1,42 @@ +use crate::spec::{PolymorphicEnumBuildSpec, PolymorphicEnumRule}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use crate::util::{make_build_fn_name, make_build_pair}; + +pub fn make_polymorphic_enum_build_fn(spec: &PolymorphicEnumBuildSpec) -> TokenStream { + let build_fn_ident = format_ident!("{}", make_build_fn_name(spec.name())); + let pair_ident = format_ident!("{}", make_build_pair(spec.name())); + let return_type_ident = format_ident!("{}", spec.return_type()); + + let match_arms = spec.rules() + .map(|rule| { + match rule { + PolymorphicEnumRule::Wrap(name_and_kind) => { + let rule_ident = format_ident!("{}", name_and_kind.name()); + let enum_variant_ident = format_ident!("{}", name_and_kind.kind()); + let rule_build_fn = format_ident!("{}", make_build_fn_name(name_and_kind.name())); + quote! { + Rule::#rule_ident => #return_type_ident::#enum_variant_ident(#rule_build_fn(inner_pair)) + } + }, + PolymorphicEnumRule::ReturnBuild(name_and_kind) => { + let rule_ident = format_ident!("{}", name_and_kind.name()); + let rule_build_fn = format_ident!("{}", make_build_fn_name(name_and_kind.kind())); + quote! { + Rule::#rule_ident => #rule_build_fn(inner_pair) + } + } + } + }) + .collect::>(); + + quote! { + fn #build_fn_ident(#pair_ident: Pair) -> #return_type_ident { + let inner_pair = #pair_ident.into_inner().next().unwrap(); + match inner_pair.as_rule() { + #(#match_arms,)* + _ => unreachable!() + } + } + } +} diff --git a/ast-generator/src/production_build_fn.rs b/ast-generator/src/production_build_fn.rs index 57ad1b1..1f28146 100644 --- a/ast-generator/src/production_build_fn.rs +++ b/ast-generator/src/production_build_fn.rs @@ -11,6 +11,7 @@ pub fn make_production_build_fn(production_build_spec: &ProductionBuildSpec) -> ProductionKind::Double => format_ident!("f64"), ProductionKind::String(_) => format_ident!("String"), ProductionKind::Boolean => format_ident!("bool"), + ProductionKind::Node(node_type) => format_ident!("{}", node_type), }; let pair_ident = format_ident!("{}", make_build_pair(production_build_spec.rule())); @@ -77,6 +78,13 @@ pub fn make_production_build_fn(production_build_spec: &ProductionBuildSpec) -> ProductionKind::Boolean => quote! { #pair_ident.as_str().parse::().unwrap() }, + ProductionKind::Node(node_type) => { + let build_fn_ident = format_ident!("{}", make_build_fn_name(node_type)); + quote! { + let inner_pair = #pair_ident.into_inner().next().unwrap(); + #build_fn_ident(inner_pair) + } + } }; quote! { diff --git a/ast-generator/src/spec.rs b/ast-generator/src/spec.rs index fd12980..c5f2bb6 100644 --- a/ast-generator/src/spec.rs +++ b/ast-generator/src/spec.rs @@ -4,7 +4,9 @@ pub enum BuildSpec { Struct(StructBuildSpec), LeafStruct(LeafStructBuildSpec), Production(ProductionBuildSpec), - Polymorphic(PolymorphicBuildSpec), + Polymorphic(PolymorphicTypeBuildSpec), + PolymorphicBuild(PolymorphicBuildBuildSpec), + PolymorphicEnum(PolymorphicEnumBuildSpec), } // Enum build spec @@ -35,14 +37,14 @@ impl EnumBuildSpec { pub struct EnumRule { rule: String, - child: Option> + child: Option>, } impl EnumRule { pub fn new(rule: &str, child: Option>) -> Self { Self { rule: rule.to_string(), - child + child, } } @@ -50,7 +52,7 @@ impl EnumRule { pub fn rule(&self) -> &str { &self.rule } - + pub fn child(&self) -> Option<&EnumRuleChild> { if let Some(child) = &self.child { Some(child.as_ref()) @@ -61,14 +63,14 @@ impl EnumRule { } pub struct EnumRuleChild { - kind: Box + kind: Box, } impl EnumRuleChild { pub fn new(kind: Box) -> Self { Self { kind } } - + pub fn kind(&self) -> &EnumRuleChildKind { &self.kind } @@ -81,11 +83,11 @@ pub enum EnumRuleChildKind { Double, USize, String, - Boolean + Boolean, } pub struct EnumRuleNodeChild { - build: String + build: String, } impl EnumRuleNodeChild { @@ -94,7 +96,7 @@ impl EnumRuleNodeChild { build: build.to_string(), } } - + pub fn build(&self) -> &str { &self.build } @@ -111,7 +113,7 @@ impl LeafEnumBuildSpec { pub fn new(build: &str, rules: Vec>) -> Self { Self { build: build.to_string(), - rules + rules, } } @@ -151,7 +153,7 @@ impl StructBuildSpec { pub fn new(build: &str, children: Vec>) -> Self { Self { build: build.to_string(), - children + children, } } @@ -159,7 +161,7 @@ impl StructBuildSpec { 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) @@ -230,7 +232,7 @@ impl VecChild { #[derive(Debug)] pub enum VecChildToBuild { Node(VecNodeChildToBuild), - String + String, } #[derive(Debug)] @@ -240,7 +242,9 @@ pub struct VecNodeChildToBuild { impl VecNodeChildToBuild { pub fn new(build: &str) -> Self { - Self { build: build.to_string() } + Self { + build: build.to_string(), + } } /// The type to build, in Pascal case. @@ -295,11 +299,7 @@ pub struct NodeChildToBuild { } impl NodeChildToBuild { - pub fn new( - build: &str, - or_else: Option, - optional: bool, - ) -> Self { + pub fn new(build: &str, or_else: Option, optional: bool) -> Self { Self { build: build.to_string(), or_else, @@ -325,13 +325,13 @@ impl NodeChildToBuild { #[derive(Debug)] pub struct BooleanChildToBuild { - name: String + name: String, } impl BooleanChildToBuild { pub fn new(name: &str) -> Self { Self { - name: name.to_string() + name: name.to_string(), } } @@ -402,11 +402,11 @@ impl ProductionBuildSpec { kind, } } - + pub fn rule(&self) -> &str { &self.rule } - + pub fn kind(&self) -> &ProductionKind { &self.kind } @@ -417,39 +417,44 @@ pub enum ProductionKind { Long, Double, String(ProductionStringFrom), - Boolean + Boolean, + Node(String), } pub enum ProductionStringFrom { StringInner, - WholePair + WholePair, } // Polymorphic build spec -pub struct PolymorphicBuildSpec { +pub struct PolymorphicTypeBuildSpec { name: String, enum_members: Vec>, - build_kind: String + build_kind: String, } -impl PolymorphicBuildSpec { - pub fn new(name: &str, enum_members: Vec>, build_kind: &str) -> Self { +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() + 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() } @@ -457,22 +462,192 @@ impl PolymorphicBuildSpec { pub struct PolymorphicEnumMember { name: String, - inner_kind: 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() + inner_kind: inner_kind.to_string(), } } - + pub fn name(&self) -> &str { &self.name } - + pub fn inner_kind(&self) -> &str { &self.inner_kind } -} \ No newline at end of file +} + +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 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 + } +} diff --git a/ast-generator/src/type_gen.rs b/ast-generator/src/type_gen.rs index e2169b2..8feedfc 100644 --- a/ast-generator/src/type_gen.rs +++ b/ast-generator/src/type_gen.rs @@ -1,12 +1,75 @@ use crate::spec::{ - BooleanChildToBuild, BuildSpec, EnumBuildSpec, EnumRuleChildKind, LeafEnumBuildSpec, - LeafStructBuildSpec, LeafStructMemberKind, MemberChildToBuild, NodeChildToBuild, - PolymorphicBuildSpec, StructBuildSpec, StructChildSpec, VecChild, VecChildToBuild, + AlternativeAction, AlternativeChild, BooleanChildToBuild, BuildSpec, EnumBuildSpec, + EnumRuleChildKind, LeafEnumBuildSpec, LeafStructBuildSpec, LeafStructMemberKind, + MemberChildToBuild, NodeChildToBuild, PolymorphicBuildBuildSpec, PolymorphicTypeBuildSpec, + StructBuildSpec, StructChildSpec, VecChild, VecChildToBuild, }; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; -fn make_polymorphic_type(build_spec: &PolymorphicBuildSpec) -> TokenStream { +fn make_polymorphic_build_type(build_spec: &PolymorphicBuildBuildSpec) -> TokenStream { + let alternative_action = build_spec + .alternatives() + .find(|alternative| { + if let AlternativeAction::Build(_) = alternative.action() { + true + } else { + false + } + }) + .unwrap(); + + let alternative_build = + if let AlternativeAction::Build(alternative_build) = alternative_action.action() { + alternative_build + } else { + unreachable!() + }; + + let annotated_members = alternative_build + .children() + .map(|child| match child { + AlternativeChild::Skip => None, + AlternativeChild::Build(build_child) => { + let child_name_ident = format_ident!("{}", build_child.name()); + let type_ident = format_ident!("{}", build_child.kind()); + Some(quote! { + #child_name_ident: Box<#type_ident> + }) + } + }) + .filter(Option::is_some) + .map(Option::unwrap) + .collect::>(); + + let initializers = alternative_build + .children() + .map(|build_child| match build_child { + AlternativeChild::Skip => None, + AlternativeChild::Build(build_child) => Some(format_ident!("{}", build_child.name())), + }) + .filter(Option::is_some) + .map(Option::unwrap) + .collect::>(); + + let type_ident = format_ident!("{}", build_spec.name()); + + quote! { + pub struct #type_ident { + #(#annotated_members),* + } + + impl #type_ident { + pub fn new(#(#annotated_members),*) -> Self { + Self { + #(#initializers),* + } + } + } + } +} + +fn make_polymorphic_type(build_spec: &PolymorphicTypeBuildSpec) -> TokenStream { let members = build_spec .enum_members() .map(|enum_member| { @@ -370,5 +433,9 @@ pub fn make_type(build_spec: &BuildSpec) -> Option { BuildSpec::Polymorphic(polymorphic_build_spec) => { Some(make_polymorphic_type(polymorphic_build_spec)) } + BuildSpec::PolymorphicBuild(polymorphic_build_build_spec) => { + Some(make_polymorphic_build_type(polymorphic_build_build_spec)) + } + BuildSpec::PolymorphicEnum(_) => None, } } diff --git a/src/parser/ast.yaml b/src/parser/ast.yaml index de56e6b..b46483f 100644 --- a/src/parser/ast.yaml +++ b/src/parser/ast.yaml @@ -605,50 +605,109 @@ Expression: build: kind: TernaryExpression TernaryExpression: - children: - - or_expression - - ternary_alternatives: - optional: true -TernaryAlternatives: - children: - - ternary_true_alternative - - ternary_false_alternative + polymorphic_build: + return_type: Expression + alternatives: + - test: + number_of_pairs: 1 + action: + return_build: + kind: OrExpression + - test: + number_of_pairs: 3 + action: + build: + enum_variant: Ternary + children: + - test: + kind: Expression + rule: OrExpression + - on_true: + kind: Expression + rule: TernaryTrueAlternative + - on_false: + kind: Expression + rule: TernaryFalseAlternative TernaryTrueAlternative: - children: - - expression + produce: + kind: + node: Expression TernaryFalseAlternative: - children: - - expression + produce: + kind: + node: Expression OrExpression: - children: - - left: - rule: AndExpression - - or_sym: - rule: Or - skip: true - - right: - rule: Expression - optional: true + polymorphic_build: + return_type: Expression + alternatives: + - test: + number_of_pairs: 1 + action: + return_build: + kind: AndExpression + - test: + number_of_pairs: 3 + action: + build: + enum_variant: Or + children: + - left: + kind: Expression + rule: AndExpression + - or_sym: + rule: Or + skip: true + - right: + kind: Expression + rule: Expression AndExpression: - children: - - left: - rule: ComparisonExpression - - and_sym: - rule: And - skip: true - - right: - rule: Expression - optional: true + polymorphic_build: + return_type: Expression + alternatives: + - test: + number_of_pairs: 1 + action: + return_build: + kind: ComparisonExpression + - test: + number_of_pairs: 3 + action: + build: + enum_variant: And + children: + - left: + kind: Expression + rule: ComparisonExpression + - and_sym: + rule: And + skip: true + - right: + kind: Expression + rule: Expression ComparisonExpression: - children: - - left: - rule: ShiftExpression - - operator: - rule: ComparisonOperator - optional: true - - right: - rule: Expression - optional: true + polymorphic_build: + return_type: Expression + alternatives: + - test: + number_of_pairs: 1 + action: + return_build: + kind: ShiftExpression + - test: + number_of_pairs: 3 + action: + build: + enum_variant: Comparison + children: + - left: + kind: Expression + rule: ShiftExpression + - operator: + kind: ComparisonOperator + rule: ComparisonOperator + - right: + kind: Expression + rule: Expression ComparisonOperator: leaf_rules: - Greater @@ -658,64 +717,144 @@ ComparisonOperator: - EqualTo - NotEqualTo ShiftExpression: - children: - - left: - rule: AdditiveExpression - - operator: - rule: ShiftOperator - optional: true - - right: - rule: Expression - optional: true + polymorphic_build: + return_type: Expression + alternatives: + - test: + number_of_pairs: 1 + action: + return_build: + kind: AdditiveExpression + - test: + number_of_pairs: 3 + action: + build: + enum_variant: Shift + children: + - left: + kind: Expression + rule: AdditiveExpression + - operator: + kind: ShiftOperator + rule: ShiftOperator + - right: + kind: Expression + rule: Expression ShiftOperator: leaf_rules: - LeftShift - RightShift AdditiveExpression: - children: - - left: - rule: MultiplicativeExpression - - operator: - rule: AdditiveOperator - optional: true - - right: - rule: Expression - optional: true + polymorphic_build: + return_type: Expression + alternatives: + - test: + number_of_pairs: 1 + action: + return_build: + kind: MultiplicativeExpression + - test: + number_of_pairs: 3 + action: + build: + enum_variant: Additive + children: + - left: + kind: Expression + rule: MultiplicativeExpression + - operator: + kind: AdditiveOperator + rule: AdditiveOperator + - right: + kind: Expression + rule: Expression AdditiveOperator: leaf_rules: - Add - Subtract MultiplicativeExpression: - children: - - left: - rule: PrefixExpression - - operator: - rule: MultiplicativeOperator - optional: true - - right: - rule: Expression - optional: true + polymorphic_build: + return_type: Expression + alternatives: + - test: + number_of_pairs: 1 + action: + return_build: + kind: PrefixExpression + - test: + number_of_pairs: 3 + action: + build: + enum_variant: Multiplicative + children: + - left: + kind: Expression + rule: PrefixExpression + - operator: + kind: MultiplicativeOperator + rule: MultiplicativeOperator + - right: + kind: Expression + rule: Expression MultiplicativeOperator: leaf_rules: - Multiply - Divide - Modulo PrefixExpression: + polymorphic_build: + return_type: Expression + alternatives: + - test: + number_of_pairs: 1 + action: + return_build: + kind: SuffixExpression + - test: + number_of_pairs: 2 + action: + build: + enum_variant: Prefix + children: + - prefix_operators: + kind: PrefixOperators + rule: PrefixOperators + - right: + kind: Expression + rule: SuffixExpression +PrefixOperators: children: - operators: rule: PrefixOperator vec: true - - right: - rule: SuffixExpression PrefixOperator: leaf_rules: - Spread - Not - Negative SuffixExpression: + polymorphic_build: + return_type: Expression + alternatives: + - test: + number_of_pairs: 1 + action: + return_build: + kind: PrimaryExpression + - test: + number_of_pairs: 2 + action: + build: + enum_variant: Suffix + children: + - left: + kind: Expression + rule: PrimaryExpression + - suffix_operators: + kind: SuffixOperators + rule: SuffixOperators +SuffixOperators: children: - - left: - rule: PrimaryExpression - operators: rule: SuffixOperator vec: true @@ -735,11 +874,21 @@ ObjectIndex: children: - expression PrimaryExpression: - rules: - - Literal - - FullyQualifiedName - - Closure - - ParenthesizedExpression + polymorphic_enum: + return_type: Expression + rules: + - Literal: + wrap: + enum_variant: Literal + - FullyQualifiedName: + wrap: + enum_variant: Fqn + - Closure: + wrap: + enum_variant: Closure + - ParenthesizedExpression: + return_build: + kind: Expression ParenthesizedExpression: children: - expression diff --git a/src/parser/deimos.pest b/src/parser/deimos.pest index 5256085..741cda5 100644 --- a/src/parser/deimos.pest +++ b/src/parser/deimos.pest @@ -596,12 +596,10 @@ Expression = { TernaryExpression = { OrExpression - ~ ( TernaryAlternatives )? -} - -TernaryAlternatives = { - TernaryTrueAlternative - ~ TernaryFalseAlternative + ~ ( + TernaryTrueAlternative + ~ TernaryFalseAlternative + )? } TernaryTrueAlternative = { @@ -682,10 +680,14 @@ MultiplicativeOperator = { } PrefixExpression = { - PrefixOperator* + PrefixOperators? ~ SuffixExpression } +PrefixOperators = { + PrefixOperator+ +} + PrefixOperator = { Spread | Not @@ -694,7 +696,11 @@ PrefixOperator = { SuffixExpression = { PrimaryExpression - ~ SuffixOperator* + ~ SuffixOperators? +} + +SuffixOperators = { + SuffixOperator+ } SuffixOperator = {