From 434df5642ac3d6204234d6bb415f981a9976752d Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Sun, 14 Sep 2025 19:34:38 -0500 Subject: [PATCH] Much work on ast gen, leaf enums and leaf structs. --- ast-generator/src/deserialize.rs | 46 +++++++--- ast-generator/src/enum_build_fn.rs | 31 ++++++- ast-generator/src/leaf_enum_build_fn.rs | 36 ++++++++ ast-generator/src/leaf_struct_build_fn.rs | 40 +++++++++ ast-generator/src/lib.rs | 25 ++++-- ast-generator/src/spec.rs | 100 +++++++++++++++++++++- ast-generator/src/struct_build_fn.rs | 5 +- ast-generator/src/type_gen.rs | 95 +++++++++++++++++++- ast-generator/src/util.rs | 4 + src/parser/ast.yaml | 26 ++++-- 10 files changed, 369 insertions(+), 39 deletions(-) create mode 100644 ast-generator/src/leaf_enum_build_fn.rs create mode 100644 ast-generator/src/leaf_struct_build_fn.rs diff --git a/ast-generator/src/deserialize.rs b/ast-generator/src/deserialize.rs index 111eadd..8491925 100644 --- a/ast-generator/src/deserialize.rs +++ b/ast-generator/src/deserialize.rs @@ -1,9 +1,4 @@ -use crate::spec::{ - BooleanBuild, BuildSpec, ChildSpec, EnumBuildSpec, EnumRule, LeafEnumBuildSpec, LeafEnumRule, - LeafEnumRuleBuild, SingleBooleanChildToBuild, SingleChild, SingleChildToBuild, - SingleLiteralChildToBuild, SingleTypeChildToBuild, SkipChild, StructBuildSpec, VecChild, - VecChildToBuild, VecTypeChildToBuild, -}; +use crate::spec::{BooleanBuild, BuildSpec, ChildSpec, EnumBuildSpec, EnumRule, LeafEnumBuildSpec, LeafEnumRule, LeafEnumRuleBuild, LeafEnumRuleBuildChild, LeafStructBuildSpec, LeafStructChild, LeafStructChildType, SingleBooleanChildToBuild, SingleChild, SingleChildToBuild, SingleLiteralChildToBuild, SingleTypeChildToBuild, SkipChild, StructBuildSpec, VecChild, VecChildToBuild, VecTypeChildToBuild}; use crate::util::make_build_fn_name; use convert_case::{Case, Casing}; use yaml_rust2::{Yaml, YamlLoader}; @@ -177,9 +172,13 @@ fn get_leaf_enum_rules(rules: &Yaml) -> Vec { if rule.is_hash() { let (rule_as_string, rule_hash) = unwrap_single_member_hash(rule); if get_as_bool(&rule_hash["child"]) { - let build = LeafEnumRuleBuild::Child { - with: make_build_fn_name(&rule_as_string), - }; + let build = LeafEnumRuleBuild::new( + &rule_as_string, + Some(LeafEnumRuleBuildChild::new( + &rule_as_string, + &make_build_fn_name(&rule_as_string), + )), + ); LeafEnumRule::new(&rule_as_string, build) } else { panic!( @@ -188,9 +187,7 @@ fn get_leaf_enum_rules(rules: &Yaml) -> Vec { } } else { let rule_as_str = rule.as_str().unwrap(); - let build = LeafEnumRuleBuild::EnumRule { - rule: rule_as_str.to_string(), - }; + let build = LeafEnumRuleBuild::new(rule_as_str, None); LeafEnumRule::new(rule_as_str, build) } }) @@ -220,6 +217,20 @@ fn get_enum_rules(rule_specs: &Yaml) -> Vec { .collect() } +fn get_leaf_struct_child_specs(children: &Yaml) -> Vec { + children.as_vec() + .unwrap() + .iter() + .map(|child_spec| { + let (name, hash) = unwrap_single_member_hash(child_spec); + LeafStructChild::new( + &name, + LeafStructChildType::String + ) + }) + .collect::>() +} + fn yaml_is_string(yaml: &Yaml, test: &str) -> bool { match yaml.as_str() { Some(s) => s == test, @@ -233,7 +244,14 @@ fn deserialize_build_spec(build_spec_name: &Yaml, build_spec: &Yaml) -> BuildSpe let children = &build_spec["children"]; let rules = &build_spec["rules"]; - if yaml_is_string(node_type, "struct") || children.is_array() { + if yaml_is_string(node_type, "leaf_struct") && children.is_array() { + let child_specs = get_leaf_struct_child_specs(children); + BuildSpec::LeafStruct(LeafStructBuildSpec::new( + build_spec_name_pascal, + build_spec_name_pascal, + child_specs, + )) + } else if children.is_array() { let child_specs = get_child_specs(children); BuildSpec::Struct(StructBuildSpec::from_name( build_spec_name_pascal, @@ -250,7 +268,7 @@ fn deserialize_build_spec(build_spec_name: &Yaml, build_spec: &Yaml) -> BuildSpe let enum_rules = get_enum_rules(rules); BuildSpec::Enum(EnumBuildSpec::from_name(build_spec_name_pascal, enum_rules)) } else { - panic!("Expected a node spec for either a struct, enum, or leaf_enum node type."); + panic!("Expected a node spec for either a struct, leaf_struct, enum, leaf_enum node type."); } } diff --git a/ast-generator/src/enum_build_fn.rs b/ast-generator/src/enum_build_fn.rs index 0974f1b..906dd4d 100644 --- a/ast-generator/src/enum_build_fn.rs +++ b/ast-generator/src/enum_build_fn.rs @@ -1,6 +1,31 @@ -use proc_macro2::TokenStream; use crate::spec::EnumBuildSpec; +use crate::util::{make_build_fn_name, make_build_pair}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; pub fn make_enum_build_fn(enum_build_spec: &EnumBuildSpec) -> TokenStream { - todo!() -} \ No newline at end of file + let build_fn_ident = format_ident!("{}", make_build_fn_name(enum_build_spec.build())); + let pair_ident = format_ident!("{}", make_build_pair(enum_build_spec.build())); + let return_type_ident = format_ident!("{}", enum_build_spec.build()); + + let rule_branches = enum_build_spec + .rules() + .iter() + .map(|enum_rule| { + let rule_ident = format_ident!("{}", enum_rule.rule()); + let build_rule_ident = format_ident!("{}", enum_rule.with()); + quote! { + Rule::#rule_ident => #build_rule_ident(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() { + #(#rule_branches),* + } + } + } +} diff --git a/ast-generator/src/leaf_enum_build_fn.rs b/ast-generator/src/leaf_enum_build_fn.rs new file mode 100644 index 0000000..c40d3b9 --- /dev/null +++ b/ast-generator/src/leaf_enum_build_fn.rs @@ -0,0 +1,36 @@ +use crate::spec::LeafEnumBuildSpec; +use crate::util::{make_build_fn_name, make_build_pair}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +pub fn make_leaf_enum_build_fn(leaf_enum_build_spec: &LeafEnumBuildSpec) -> TokenStream { + let build_fn_ident = format_ident!("{}", make_build_fn_name(leaf_enum_build_spec.build())); + let pair_ident = format_ident!("{}", make_build_pair(leaf_enum_build_spec.build())); + let return_type_ident = format_ident!("{}", leaf_enum_build_spec.build()); + + let rule_branches = leaf_enum_build_spec.rules() + .iter() + .map(|leaf_enum_rule| { + let rule_ident = format_ident!("{}", leaf_enum_rule.rule()); + if leaf_enum_rule.build().child().is_some() { + let child_build_fn_ident = format_ident!("{}", leaf_enum_rule.build().child().unwrap().with()); + quote! { + Rule::#rule_ident => #return_type_ident::#rule_ident(#child_build_fn_ident(inner_pair)) + } + } else { + quote! { + Rule::#rule_ident => #return_type_ident::#rule_ident + } + } + }) + .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() { + #(#rule_branches),* + } + } + } +} diff --git a/ast-generator/src/leaf_struct_build_fn.rs b/ast-generator/src/leaf_struct_build_fn.rs new file mode 100644 index 0000000..d23aa17 --- /dev/null +++ b/ast-generator/src/leaf_struct_build_fn.rs @@ -0,0 +1,40 @@ +use crate::spec::{LeafStructBuildSpec, LeafStructChildType}; +use crate::util::{make_build_fn_name, make_build_pair}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +pub fn make_leaf_struct_build_fn(build_spec: &LeafStructBuildSpec) -> TokenStream { + let build_fn_ident = format_ident!("{}", make_build_fn_name(build_spec.build())); + let pair_ident = format_ident!("{}", make_build_pair(build_spec.build())); + let return_type_ident = format_ident!("{}", build_spec.build()); + + let child_builders = build_spec + .children() + .iter() + .map(|leaf_struct_child| { + let child_ident = format_ident!("{}", leaf_struct_child.name()); + match leaf_struct_child.r#type() { + LeafStructChildType::String => { + quote! { + let #child_ident = #pair_ident.as_str() + } + } + } + }) + .collect::>(); + + let child_args = build_spec + .children() + .iter() + .map(|leaf_struct_child| format_ident!("{}", leaf_struct_child.name())) + .collect::>(); + + quote! { + fn #build_fn_ident(#pair_ident: Pair) -> #return_type_ident { + #(#child_builders;)* + #return_type_ident::new( + #(#child_args,)* + ) + } + } +} diff --git a/ast-generator/src/lib.rs b/ast-generator/src/lib.rs index 87d2cc3..85b49c2 100644 --- a/ast-generator/src/lib.rs +++ b/ast-generator/src/lib.rs @@ -1,17 +1,21 @@ pub mod deserialize; mod enum_build_fn; +mod leaf_enum_build_fn; +mod leaf_struct_build_fn; mod spec; mod struct_build_fn; mod type_gen; mod util; +use crate::enum_build_fn::make_enum_build_fn; +use crate::leaf_enum_build_fn::make_leaf_enum_build_fn; use crate::struct_build_fn::make_struct_build_fn; use crate::type_gen::make_type; use proc_macro2::TokenStream; use quote::quote; use spec::BuildSpec; use syn::File; -use crate::enum_build_fn::make_enum_build_fn; +use crate::leaf_struct_build_fn::make_leaf_struct_build_fn; fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) { println!("*** BuildSpec ***"); @@ -25,6 +29,9 @@ fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) { BuildSpec::Struct(struct_build_spec) => { println!("Spec name: {}", struct_build_spec.name()); } + BuildSpec::LeafStruct(leaf_struct_build_spec) => { + println!("Spec name: {}", leaf_struct_build_spec.name()); + } } println!("{:#?}", token_stream); let parsed: File = syn::parse2(token_stream.clone()).unwrap(); @@ -51,12 +58,19 @@ fn generate_build_file(build_specs: &[BuildSpec]) -> AstGeneratedFile { stream } BuildSpec::LeafEnum(leaf_enum_build_spec) => { - todo!() + let stream = make_leaf_enum_build_fn(leaf_enum_build_spec); + debug_built_spec(build_spec, &stream); + stream } BuildSpec::Struct(struct_build_spec) => { - let struct_build_fn_stream = make_struct_build_fn(struct_build_spec); - debug_built_spec(build_spec, &struct_build_fn_stream); - struct_build_fn_stream + let stream = make_struct_build_fn(struct_build_spec); + debug_built_spec(build_spec, &stream); + stream + } + BuildSpec::LeafStruct(leaf_struct_build_spec) => { + let stream = make_leaf_struct_build_fn(leaf_struct_build_spec); + debug_built_spec(build_spec, &stream); + stream } }) .collect::>(); @@ -109,6 +123,7 @@ pub fn test_dump() -> String { debug_built_spec(build_spec, &struct_build_fn_stream); streams.push(struct_build_fn_stream); } + BuildSpec::LeafStruct(_) => {} } } diff --git a/ast-generator/src/spec.rs b/ast-generator/src/spec.rs index 036f76c..84ef085 100644 --- a/ast-generator/src/spec.rs +++ b/ast-generator/src/spec.rs @@ -5,6 +5,7 @@ pub enum BuildSpec { Enum(EnumBuildSpec), LeafEnum(LeafEnumBuildSpec), Struct(StructBuildSpec), + LeafStruct(LeafStructBuildSpec), } pub struct EnumBuildSpec { @@ -127,9 +128,48 @@ impl LeafEnumRule { } } -pub enum LeafEnumRuleBuild { - EnumRule { rule: String }, - Child { with: String }, +pub struct LeafEnumRuleBuild { + rule: String, + child: Option, +} + +impl LeafEnumRuleBuild { + pub fn new(rule: &str, child: Option) -> Self { + Self { + rule: rule.to_string(), + child, + } + } + + pub fn rule(&self) -> &str { + &self.rule + } + + pub fn child(&self) -> Option<&LeafEnumRuleBuildChild> { + self.child.as_ref() + } +} + +pub struct LeafEnumRuleBuildChild { + build: String, + with: String, +} + +impl LeafEnumRuleBuildChild { + pub fn new(build: &str, with: &str) -> Self { + Self { + build: build.to_string(), + with: with.to_string(), + } + } + + pub fn build(&self) -> &str { + &self.build + } + + pub fn with(&self) -> &str { + &self.with + } } pub struct StructBuildSpec { @@ -440,3 +480,57 @@ impl SingleLiteralChildToBuild { &self.var_name } } + +pub struct LeafStructBuildSpec { + name: String, + build: String, + children: Vec, +} + +impl LeafStructBuildSpec { + pub fn new(name: &str, build: &str, children: Vec) -> Self { + Self { + name: name.to_string(), + build: build.to_string(), + children, + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn build(&self) -> &str { + &self.build + } + + pub fn children(&self) -> &[LeafStructChild] { + &self.children + } +} + +pub struct LeafStructChild { + name: String, + r#type: LeafStructChildType, +} + +impl LeafStructChild { + pub fn new(name: &str, r#type: LeafStructChildType) -> Self { + Self { + name: name.to_string(), + r#type + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn r#type(&self) -> &LeafStructChildType { + &self.r#type + } +} + +pub enum LeafStructChildType { + String, +} diff --git a/ast-generator/src/struct_build_fn.rs b/ast-generator/src/struct_build_fn.rs index 5872c58..2bc781b 100644 --- a/ast-generator/src/struct_build_fn.rs +++ b/ast-generator/src/struct_build_fn.rs @@ -5,6 +5,7 @@ use crate::spec::{ use convert_case::{Case, Casing}; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; +use crate::util::make_build_pair; fn make_vec_child_holder(vec_child: &VecChild) -> TokenStream { let (child_ident, child_type_ident) = match vec_child.build() { @@ -226,8 +227,8 @@ fn make_return_value_stream(build_spec: &StructBuildSpec) -> TokenStream { } pub fn make_struct_build_fn(build_spec: &StructBuildSpec) -> TokenStream { - let build_fn_ident = format_ident!("{}", build_spec.with()); - let pair_ident = format_ident!("{}_pair", build_spec.build().to_case(Case::Snake)); + let build_fn_ident = format_ident!("{}", build_spec.with()); // TODO: get rid of with + let pair_ident = format_ident!("{}", make_build_pair(build_spec.var_name())); // TODO: get rid of var_name let return_type_ident = format_ident!("{}", build_spec.build()); let child_holders = build_spec diff --git a/ast-generator/src/type_gen.rs b/ast-generator/src/type_gen.rs index b73cb48..805d12a 100644 --- a/ast-generator/src/type_gen.rs +++ b/ast-generator/src/type_gen.rs @@ -1,6 +1,7 @@ use crate::spec::{ - BuildSpec, ChildSpec, EnumBuildSpec, SingleBooleanChildToBuild, SingleChildToBuild, - SingleTypeChildToBuild, StructBuildSpec, VecChild, VecChildToBuild, + BuildSpec, ChildSpec, EnumBuildSpec, LeafEnumBuildSpec, LeafStructBuildSpec, + LeafStructChildType, SingleBooleanChildToBuild, SingleChildToBuild, SingleTypeChildToBuild, + StructBuildSpec, VecChild, VecChildToBuild, }; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; @@ -25,6 +26,34 @@ fn make_enum_type(build_spec: &EnumBuildSpec) -> TokenStream { } } +fn make_leaf_enum_type(build_spec: &LeafEnumBuildSpec) -> TokenStream { + let type_name_ident = format_ident!("{}", build_spec.build()); + let children = build_spec + .rules() + .iter() + .map(|leaf_enum_rule| { + let rule_name_ident = format_ident!("{}", leaf_enum_rule.rule()); + if leaf_enum_rule.build().child().is_some() { + let child_type_ident = + format_ident!("{}", leaf_enum_rule.build().child().unwrap().build()); + quote! { + #rule_name_ident(#child_type_ident) + } + } else { + quote! { + #rule_name_ident + } + } + }) + .collect::>(); + + quote! { + pub enum #type_name_ident { + #(#children),* + } + } +} + fn handle_vec_child( vec_child: &VecChild, member_names: &mut Vec, @@ -161,10 +190,70 @@ fn make_struct_type(build_spec: &StructBuildSpec) -> TokenStream { } } +fn make_leaf_struct_type(build_spec: &LeafStructBuildSpec) -> TokenStream { + let type_ident = format_ident!("{}", build_spec.build()); + + let annotated_members = build_spec + .children() + .iter() + .map(|leaf_struct_child| { + let name_ident = format_ident!("{}", leaf_struct_child.name()); + let type_ident = match leaf_struct_child.r#type() { + LeafStructChildType::String => format_ident!("{}", "String"), + }; + quote! { + #name_ident: #type_ident + } + }) + .collect::>(); + + let member_names = build_spec + .children() + .iter() + .map(|leaf_struct_child| format_ident!("{}", leaf_struct_child.name())) + .collect::>(); + + let accessors = build_spec + .children() + .iter() + .map(|leaf_struct_child| { + let name_ident = format_ident!("{}", leaf_struct_child.name()); + match leaf_struct_child.r#type() { + LeafStructChildType::String => { + quote! { + pub fn #name_ident(&self) -> &str { + &self.#name_ident + } + } + } + } + }) + .collect::>(); + + quote! { + pub struct #type_ident { + #(#annotated_members),* + } + + impl #type_ident { + pub fn new(#(#annotated_members),*) -> Self { + Self { + #(#member_names),* + } + } + + #(#accessors)* + } + } +} + pub fn make_type(build_spec: &BuildSpec) -> TokenStream { match build_spec { BuildSpec::Enum(enum_build_spec) => make_enum_type(enum_build_spec), - BuildSpec::LeafEnum(leaf_enum_build_spec) => todo!(), + BuildSpec::LeafEnum(leaf_enum_build_spec) => make_leaf_enum_type(leaf_enum_build_spec), BuildSpec::Struct(struct_build_spec) => make_struct_type(struct_build_spec), + BuildSpec::LeafStruct(leaf_struct_build_spec) => { + make_leaf_struct_type(leaf_struct_build_spec) + } } } diff --git a/ast-generator/src/util.rs b/ast-generator/src/util.rs index c91ad4a..8d94e3a 100644 --- a/ast-generator/src/util.rs +++ b/ast-generator/src/util.rs @@ -3,3 +3,7 @@ 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)) +} \ No newline at end of file diff --git a/src/parser/ast.yaml b/src/parser/ast.yaml index aa7f2f2..5a81d35 100644 --- a/src/parser/ast.yaml +++ b/src/parser/ast.yaml @@ -15,7 +15,7 @@ Operator: - Subtract - Multiply - Divide - - Moduolo + - Modulo - LeftShift - RightShift - Spread @@ -29,11 +29,11 @@ Operator: # Names Identifier: + type: leaf_struct children: - name: build: type: string - from: parse_whole_pair FullyQualifiedName: children: - identifiers: @@ -174,11 +174,13 @@ UseStatementPrefix: children: - identifier UseStatementSuffix: + type: leaf_enum rules: - - Identifier - - rule: Star - build: UseStatementStarSuffix - - UseList + - Identifier: + child: true + - Star + - UseList: + child: true UseList: children: - identifiers: @@ -463,8 +465,7 @@ Statement: - AssignmentStatement - ExpressionStatement - UseStatement - - IfElseStatement - - IfStatementStatement + - IfStatement - WhileStatement - ForStatement VariableDeclaration: @@ -560,7 +561,7 @@ ForStatement: vec: true - end_kw: rule: End - vec: true + skip: true # Expressions Expression: @@ -612,6 +613,7 @@ ComparisonExpression: rule: Expression optional: true ComparisonOperator: + type: leaf_enum rules: - Greater - Less @@ -629,6 +631,7 @@ ShiftExpression: - right: rule: Expression ShiftOperator: + type: leaf_enum rules: - LeftShift - RightShift @@ -642,6 +645,11 @@ AdditiveExpression: - right: rule: Expression optional: true +AdditiveOperator: + type: leaf_enum + rules: + - Add + - Subtract MultiplicativeExpression: children: - left: