From a7eabae3e30e2dd05363861bd8b32ad0072ab1d3 Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Tue, 16 Sep 2025 10:59:38 -0500 Subject: [PATCH] WIP polymorphic building. --- ast-generator/src/deserialize.rs | 24 +++++++++- ast-generator/src/lib.rs | 12 ++++- ast-generator/src/polymorphic_build_fn.rs | 19 ++++++++ ast-generator/src/spec.rs | 53 +++++++++++++++++++++++ ast-generator/src/type_gen.rs | 24 +++++++++- src/ast/mod.rs | 27 ------------ src/parser/ast.yaml | 42 +++++++++++++++++- 7 files changed, 169 insertions(+), 32 deletions(-) create mode 100644 ast-generator/src/polymorphic_build_fn.rs diff --git a/ast-generator/src/deserialize.rs b/ast-generator/src/deserialize.rs index a13cddd..07ee4a9 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, ProductionBuildSpec, ProductionKind, ProductionStringFrom, SkipChild, StructBuildSpec, StructChildSpec, VecChild, VecChildToBuild, VecNodeChildToBuild}; +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 convert_case::{Case, Casing}; use yaml_rust2::{Yaml, YamlLoader}; @@ -18,6 +18,26 @@ fn unwrap_single_member_hash(hash: &Yaml) -> (String, &Yaml) { (key_as_string, member_value) } +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) -> PolymorphicBuildSpec { + 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) +} + fn deserialize_production_spec(rule: &str, production_yaml: &Yaml) -> ProductionBuildSpec { let kind = match production_yaml["kind"].as_str().unwrap() { "int" => ProductionKind::Int, @@ -278,6 +298,8 @@ fn deserialize_build_spec(build_spec_name: &str, build_spec: &Yaml) -> BuildSpec BuildSpec::LeafEnum(LeafEnumBuildSpec::new(build_spec_name, leaf_rules)) } else if build_spec["produce"].is_hash() { BuildSpec::Production(deserialize_production_spec(build_spec_name, &build_spec["produce"])) + } else if build_spec["polymorphic_type"].is_hash() { + BuildSpec::Polymorphic(deserialize_polymorphic_spec(build_spec_name, &build_spec["polymorphic_type"])) } else { panic!("Expected a node spec for either a struct, leaf_struct, enum, leaf_enum node type, or a production type."); } diff --git a/ast-generator/src/lib.rs b/ast-generator/src/lib.rs index 50fb8e1..b721cc7 100644 --- a/ast-generator/src/lib.rs +++ b/ast-generator/src/lib.rs @@ -2,6 +2,7 @@ pub mod deserialize; mod enum_build_fn; mod leaf_enum_build_fn; mod leaf_struct_build_fn; +mod polymorphic_build_fn; mod production_build_fn; mod spec; mod struct_build_fn; @@ -18,6 +19,7 @@ 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 ***"); @@ -38,7 +40,10 @@ fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) { ); } BuildSpec::Production(production_build_spec) => { - println!("Production Spec - rule: {}", production_build_spec.rule()) + println!("Production Spec - rule: {}", production_build_spec.rule()); + } + BuildSpec::Polymorphic(polymorphic_build_spec) => { + println!("Polymorphic Spec - name: {}", polymorphic_build_spec.name()); } } println!("{:#?}", token_stream); @@ -85,6 +90,11 @@ fn generate_build_file(build_specs: &[BuildSpec]) -> AstGeneratedFile { debug_built_spec(build_spec, &stream); stream } + BuildSpec::Polymorphic(polymorphic_build_spec) => { + let stream = make_polymorphic_build_fn(polymorphic_build_spec); + debug_built_spec(build_spec, &stream); + stream + } }) .collect::>(); let combined = quote! { diff --git a/ast-generator/src/polymorphic_build_fn.rs b/ast-generator/src/polymorphic_build_fn.rs new file mode 100644 index 0000000..52d3922 --- /dev/null +++ b/ast-generator/src/polymorphic_build_fn.rs @@ -0,0 +1,19 @@ +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 { + 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) + } + } +} \ No newline at end of file diff --git a/ast-generator/src/spec.rs b/ast-generator/src/spec.rs index 22da2c3..fd12980 100644 --- a/ast-generator/src/spec.rs +++ b/ast-generator/src/spec.rs @@ -4,6 +4,7 @@ pub enum BuildSpec { Struct(StructBuildSpec), LeafStruct(LeafStructBuildSpec), Production(ProductionBuildSpec), + Polymorphic(PolymorphicBuildSpec), } // Enum build spec @@ -423,3 +424,55 @@ pub enum ProductionStringFrom { StringInner, WholePair } + +// Polymorphic build spec + +pub struct PolymorphicBuildSpec { + name: String, + enum_members: Vec>, + build_kind: String +} + +impl PolymorphicBuildSpec { + 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 + } +} \ No newline at end of file diff --git a/ast-generator/src/type_gen.rs b/ast-generator/src/type_gen.rs index 4ec62db..e2169b2 100644 --- a/ast-generator/src/type_gen.rs +++ b/ast-generator/src/type_gen.rs @@ -1,11 +1,30 @@ use crate::spec::{ BooleanChildToBuild, BuildSpec, EnumBuildSpec, EnumRuleChildKind, LeafEnumBuildSpec, LeafStructBuildSpec, LeafStructMemberKind, MemberChildToBuild, NodeChildToBuild, - StructBuildSpec, StructChildSpec, VecChild, VecChildToBuild, + PolymorphicBuildSpec, StructBuildSpec, StructChildSpec, VecChild, VecChildToBuild, }; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; +fn make_polymorphic_type(build_spec: &PolymorphicBuildSpec) -> TokenStream { + let members = build_spec + .enum_members() + .map(|enum_member| { + let member_ident = format_ident!("{}", enum_member.name()); + let inner_type_ident = format_ident!("{}", enum_member.inner_kind()); + quote! { + #member_ident(#inner_type_ident) + } + }) + .collect::>(); + let type_name_ident = format_ident!("{}", build_spec.name()); + quote! { + pub enum #type_name_ident { + #(#members),* + } + } +} + fn make_enum_type(build_spec: &EnumBuildSpec) -> TokenStream { let children: Vec = build_spec .rules() @@ -348,5 +367,8 @@ pub fn make_type(build_spec: &BuildSpec) -> Option { Some(make_leaf_struct_type(leaf_struct_build_spec)) } BuildSpec::Production(_) => None, + BuildSpec::Polymorphic(polymorphic_build_spec) => { + Some(make_polymorphic_type(polymorphic_build_spec)) + } } } diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 0f65dc7..8e3f555 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -42,31 +42,6 @@ pub mod node { Self::new(vec![]) } } - - impl Expression { - pub fn unwrap_identifier(&self) -> &Identifier { - let primary = self.ternary_expression() - .or_expression() - .left() - .left() - .left() - .left() - .left() - .left() - .right() - .left(); - match primary { - PrimaryExpression::FullyQualifiedName(fqn) => { - if fqn.identifiers().count() == 1 { - fqn.identifiers().next().unwrap() - } else { - panic!() - } - } - _ => panic!(), - } - } - } } pub mod build { @@ -133,8 +108,6 @@ pub mod build { fn d_string_expression_simple() { let pair = parse(Rule::DStringExpression, "${thing}"); let d_string_expression = build_d_string_expression(pair); - let identifier = d_string_expression.expression().unwrap_identifier(); - assert_eq!("thing", identifier.name()); } } } diff --git a/src/parser/ast.yaml b/src/parser/ast.yaml index b8ef053..de56e6b 100644 --- a/src/parser/ast.yaml +++ b/src/parser/ast.yaml @@ -564,8 +564,46 @@ ForStatement: # Expressions Expression: - children: - - ternary_expression + polymorphic_type: + enum_members: + - Ternary: + inner: + kind: TernaryExpression + - Or: + inner: + kind: OrExpression + - And: + inner: + kind: AndExpression + - Comparison: + inner: + kind: ComparisonExpression + - Shift: + inner: + kind: ShiftExpression + - Additive: + inner: + kind: AdditiveExpression + - Multiplicative: + inner: + kind: MultiplicativeExpression + - Prefix: + inner: + kind: PrefixExpression + - Suffix: + inner: + kind: SuffixExpression + - Literal: + inner: + kind: Literal + - Fqn: + inner: + kind: FullyQualifiedName + - Closure: + inner: + kind: Closure + build: + kind: TernaryExpression TernaryExpression: children: - or_expression