Fill out build-fn generation for ast nodes.

This commit is contained in:
Jesse Brault 2025-09-14 15:40:39 -05:00
parent 968b950436
commit b75e51ee41
7 changed files with 164 additions and 49 deletions

View File

@ -1,9 +1,9 @@
use crate::spec::{ use crate::spec::{
BuildBooleanOn, ChildSpec, SingleBooleanChildToBuild, SingleChildToBuild, BooleanBuild, ChildSpec, SingleBooleanChildToBuild, SingleChildToBuild,
SingleTypeChildToBuild, StructBuildSpec, VecChild, VecChildToBuild, SingleLiteralChildToBuild, SingleTypeChildToBuild, StructBuildSpec, VecChild, VecChildToBuild,
}; };
use convert_case::{Case, Casing}; use convert_case::{Case, Casing};
use proc_macro2::TokenStream; use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote}; use quote::{format_ident, quote};
pub fn make_build_fn_name(s: &str) -> String { pub fn make_build_fn_name(s: &str) -> String {
@ -39,6 +39,16 @@ fn make_single_boolean_child_holder(
} }
} }
fn make_literal_child_holder(
literal_child: &SingleLiteralChildToBuild,
literal_type_ident: Ident,
) -> TokenStream {
let child_ident = format_ident!("{}", literal_child.var_name());
quote! {
let mut #child_ident: Option<#literal_type_ident> = None
}
}
fn make_child_holder(child_spec: &ChildSpec) -> Option<TokenStream> { fn make_child_holder(child_spec: &ChildSpec) -> Option<TokenStream> {
match child_spec { match child_spec {
ChildSpec::SkipChild(_) => None, ChildSpec::SkipChild(_) => None,
@ -50,10 +60,30 @@ fn make_child_holder(child_spec: &ChildSpec) -> Option<TokenStream> {
SingleChildToBuild::Boolean(boolean_child) => { SingleChildToBuild::Boolean(boolean_child) => {
Some(make_single_boolean_child_holder(boolean_child)) Some(make_single_boolean_child_holder(boolean_child))
} }
SingleChildToBuild::Int(literal_child) => Some(make_literal_child_holder(
literal_child,
format_ident!("i32"),
)),
SingleChildToBuild::Long(literal_child) => Some(make_literal_child_holder(
literal_child,
format_ident!("i64"),
)),
SingleChildToBuild::Double(literal_child) => Some(make_literal_child_holder(
literal_child,
format_ident!("f64"),
)),
SingleChildToBuild::String(literal_child) => Some(make_literal_child_holder(
literal_child,
format_ident!("String"),
)),
}, },
} }
} }
fn get_literal_child_ident(literal_child: &SingleLiteralChildToBuild) -> Ident {
format_ident!("{}", literal_child.var_name())
}
fn make_match_action(child_spec: &ChildSpec) -> TokenStream { fn make_match_action(child_spec: &ChildSpec) -> TokenStream {
match child_spec { match child_spec {
ChildSpec::SkipChild(_) => quote! {}, ChildSpec::SkipChild(_) => quote! {},
@ -78,10 +108,39 @@ fn make_match_action(child_spec: &ChildSpec) -> TokenStream {
} }
SingleChildToBuild::Boolean(single_boolean_child) => { SingleChildToBuild::Boolean(single_boolean_child) => {
let child_name_ident = format_ident!("{}", single_boolean_child.var_name()); let child_name_ident = format_ident!("{}", single_boolean_child.var_name());
match single_boolean_child.on() { match single_boolean_child.build() {
BuildBooleanOn::RulePresent => quote! { BooleanBuild::RulePresent => quote! {
#child_name_ident = true #child_name_ident = true
}, },
BooleanBuild::ParseWholePair => quote! {
#child_name_ident = Some(inner_pair.as_str().parse().unwrap())
},
}
}
SingleChildToBuild::Int(literal_child) => {
let child_ident = get_literal_child_ident(literal_child);
quote! {
#child_ident = Some(inner_pair.as_str().parse().unwrap())
}
}
SingleChildToBuild::Long(literal_child) => {
let child_ident = get_literal_child_ident(literal_child);
quote! {
let as_string = inner_pair.as_str();
let without_el = &as_string[0..(as_string.len() - 1)];
#child_ident = Some(without_el.parse().unwrap())
}
}
SingleChildToBuild::Double(literal_child) => {
let child_ident = get_literal_child_ident(literal_child);
quote! {
#child_ident = Some(inner_pair.as_str().parse().unwrap())
}
}
SingleChildToBuild::String(literal_child) => {
let child_ident = get_literal_child_ident(literal_child);
quote! {
#child_ident = Some(inner_pair.as_str().to_string())
} }
} }
}, },
@ -133,6 +192,22 @@ fn make_child_arg(child_spec: &ChildSpec) -> Option<TokenStream> {
let child_ident = format_ident!("{}", single_boolean_child.var_name()); let child_ident = format_ident!("{}", single_boolean_child.var_name());
Some(quote! { #child_ident }) Some(quote! { #child_ident })
} }
SingleChildToBuild::Int(literal_child) => {
let child_ident = get_literal_child_ident(literal_child);
Some(quote! { #child_ident.unwrap() })
}
SingleChildToBuild::Long(literal_child) => {
let child_ident = get_literal_child_ident(literal_child);
Some(quote! { #child_ident.unwrap() })
},
SingleChildToBuild::Double(literal_child) => {
let child_ident = get_literal_child_ident(literal_child);
Some(quote! { #child_ident.unwrap() })
}
SingleChildToBuild::String(literal_child) => {
let child_ident = get_literal_child_ident(literal_child);
Some(quote! { #child_ident.unwrap() })
}
}, },
} }
} }
@ -234,7 +309,7 @@ mod tests {
#[test] #[test]
fn single_boolean_child_holder() { fn single_boolean_child_holder() {
let single_boolean_child = let single_boolean_child =
SingleBooleanChildToBuild::new("test_child", BuildBooleanOn::RulePresent); SingleBooleanChildToBuild::new("test_child", BooleanBuild::RulePresent);
assert_eq!( assert_eq!(
make_single_boolean_child_holder(&single_boolean_child).to_string(), make_single_boolean_child_holder(&single_boolean_child).to_string(),
quote! { quote! {

View File

@ -1,5 +1,10 @@
use crate::build_fn_gen::make_build_fn_name; use crate::build_fn_gen::make_build_fn_name;
use crate::spec::{BuildBooleanOn, BuildSpec, ChildSpec, EnumBuildSpec, EnumRule, LeafEnumBuildSpec, LeafEnumRule, LeafEnumRuleBuild, SingleBooleanChildToBuild, SingleChild, SingleChildToBuild, SingleTypeChildToBuild, SkipChild, StructBuildSpec, VecChild, VecChildToBuild, VecTypeChildToBuild}; use crate::spec::{
BooleanBuild, BuildSpec, ChildSpec, EnumBuildSpec, EnumRule, LeafEnumBuildSpec, LeafEnumRule,
LeafEnumRuleBuild, SingleBooleanChildToBuild, SingleChild, SingleChildToBuild,
SingleLiteralChildToBuild, SingleTypeChildToBuild, SkipChild, StructBuildSpec, VecChild,
VecChildToBuild, VecTypeChildToBuild,
};
use convert_case::{Case, Casing}; use convert_case::{Case, Casing};
use yaml_rust2::{Yaml, YamlLoader}; use yaml_rust2::{Yaml, YamlLoader};
@ -51,14 +56,29 @@ fn get_single_child_to_build(
.as_str() .as_str()
.map(|s| s.to_string()) .map(|s| s.to_string())
.unwrap_or(name.to_string()); .unwrap_or(name.to_string());
let on = build["on"].as_str().unwrap(); match r#type {
if r#type.eq("boolean") && on.eq("rule_present") { "boolean" => {
if yaml_is_string(&build["on"], "rule_present") {
SingleChildToBuild::Boolean(SingleBooleanChildToBuild::new( SingleChildToBuild::Boolean(SingleBooleanChildToBuild::new(
&var_name, &var_name,
BuildBooleanOn::RulePresent, BooleanBuild::RulePresent,
))
} else if yaml_is_string(&build["from"], "parse_whole_pair") {
SingleChildToBuild::Boolean(SingleBooleanChildToBuild::new(
&var_name,
BooleanBuild::ParseWholePair,
)) ))
} else { } else {
todo!("currently on boolean types with on: rule_present are supported") panic!("invalid build on/from with type boolean");
}
}
"i32" => SingleChildToBuild::Int(SingleLiteralChildToBuild::new(&var_name)),
"i64" => SingleChildToBuild::Long(SingleLiteralChildToBuild::new(&var_name)),
"f64" => SingleChildToBuild::Double(SingleLiteralChildToBuild::new(&var_name)),
"string" => {
SingleChildToBuild::String(SingleLiteralChildToBuild::new(&var_name))
}
_ => panic!("unsupported build type: {}", r#type),
} }
} }
None => { None => {
@ -160,11 +180,13 @@ fn get_leaf_enum_rules(rules: &Yaml) -> Vec<LeafEnumRule> {
let (rule_as_string, rule_hash) = unwrap_single_member_hash(rule); let (rule_as_string, rule_hash) = unwrap_single_member_hash(rule);
if get_as_bool(&rule_hash["child"]) { if get_as_bool(&rule_hash["child"]) {
let build = LeafEnumRuleBuild::Child { let build = LeafEnumRuleBuild::Child {
with: make_build_fn_name(&rule_as_string) with: make_build_fn_name(&rule_as_string),
}; };
LeafEnumRule::new(&rule_as_string, build) LeafEnumRule::new(&rule_as_string, build)
} else { } else {
panic!("if a leaf_enum rule is a hash, its child prop must be present and true"); panic!(
"if a leaf_enum rule is a hash, its child prop must be present and true"
);
} }
} else { } else {
let rule_as_str = rule.as_str().unwrap(); let rule_as_str = rule.as_str().unwrap();
@ -221,7 +243,10 @@ fn deserialize_build_spec(build_spec_name: &Yaml, build_spec: &Yaml) -> BuildSpe
)) ))
} else if yaml_is_string(node_type, "leaf_enum") && rules.is_array() { } else if yaml_is_string(node_type, "leaf_enum") && rules.is_array() {
let leaf_enum_rules = get_leaf_enum_rules(rules); let leaf_enum_rules = get_leaf_enum_rules(rules);
BuildSpec::LeafEnum(LeafEnumBuildSpec::from_name(build_spec_name_pascal, leaf_enum_rules)) BuildSpec::LeafEnum(LeafEnumBuildSpec::from_name(
build_spec_name_pascal,
leaf_enum_rules,
))
} else if rules.is_array() { } else if rules.is_array() {
// enum node // enum node
let enum_rules = get_enum_rules(rules); let enum_rules = get_enum_rules(rules);

View File

@ -88,7 +88,7 @@ impl LeafEnumBuildSpec {
Self { Self {
name: name.to_string(), name: name.to_string(),
build: name.to_string(), build: name.to_string(),
rules rules,
} }
} }
@ -107,14 +107,14 @@ impl LeafEnumBuildSpec {
pub struct LeafEnumRule { pub struct LeafEnumRule {
rule: String, rule: String,
build: LeafEnumRuleBuild build: LeafEnumRuleBuild,
} }
impl LeafEnumRule { impl LeafEnumRule {
pub fn new(rule: &str, build: LeafEnumRuleBuild) -> Self { pub fn new(rule: &str, build: LeafEnumRuleBuild) -> Self {
Self { Self {
rule: rule.to_string(), rule: rule.to_string(),
build build,
} }
} }
@ -128,12 +128,8 @@ impl LeafEnumRule {
} }
pub enum LeafEnumRuleBuild { pub enum LeafEnumRuleBuild {
EnumRule { EnumRule { rule: String },
rule: String, Child { with: String },
},
Child {
with: String
}
} }
pub struct StructBuildSpec { pub struct StructBuildSpec {
@ -327,6 +323,10 @@ impl SingleChild {
pub enum SingleChildToBuild { pub enum SingleChildToBuild {
Type(SingleTypeChildToBuild), Type(SingleTypeChildToBuild),
Boolean(SingleBooleanChildToBuild), Boolean(SingleBooleanChildToBuild),
Int(SingleLiteralChildToBuild),
Long(SingleLiteralChildToBuild),
Double(SingleLiteralChildToBuild),
String(SingleLiteralChildToBuild),
} }
#[derive(Debug)] #[derive(Debug)]
@ -382,14 +382,14 @@ impl SingleTypeChildToBuild {
#[derive(Debug)] #[derive(Debug)]
pub struct SingleBooleanChildToBuild { pub struct SingleBooleanChildToBuild {
var_name: String, var_name: String,
on: BuildBooleanOn, build: BooleanBuild,
} }
impl SingleBooleanChildToBuild { impl SingleBooleanChildToBuild {
pub fn new(var_name: &str, on: BuildBooleanOn) -> Self { pub fn new(var_name: &str, build: BooleanBuild) -> Self {
Self { Self {
var_name: var_name.to_string(), var_name: var_name.to_string(),
on, build,
} }
} }
@ -397,12 +397,30 @@ impl SingleBooleanChildToBuild {
&self.var_name &self.var_name
} }
pub fn on(&self) -> &BuildBooleanOn { pub fn build(&self) -> &BooleanBuild {
&self.on &self.build
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub enum BuildBooleanOn { pub enum BooleanBuild {
RulePresent, RulePresent,
ParseWholePair,
}
#[derive(Debug)]
pub struct SingleLiteralChildToBuild {
var_name: String,
}
impl SingleLiteralChildToBuild {
pub fn new(var_name: &str) -> Self {
Self {
var_name: var_name.to_string(),
}
}
pub fn var_name(&self) -> &str {
&self.var_name
}
} }

View File

@ -136,6 +136,7 @@ fn make_struct_type(build_spec: &StructBuildSpec) -> TokenStream {
&mut accessors, &mut accessors,
); );
} }
_ => todo!()
}; };
} }
} }

View File

@ -1 +0,0 @@
include!(concat!(env!("OUT_DIR"), "/src/ast/build.rs"));

View File

@ -1,4 +1,10 @@
pub mod build; pub mod build {
//noinspection RsUnusedImport
use crate::parser::Rule;
include!(concat!(env!("OUT_DIR"), "/src/ast/build.rs"));
}
pub mod children; pub mod children;
pub mod node; pub mod node;
pub mod pretty_print; pub mod pretty_print;

View File

@ -625,20 +625,17 @@ IntLiteral:
- literal: - literal:
build: build:
type: i32 type: i32
from: parse_number_base
LongLiteral: LongLiteral:
children: children:
- number_base - number_base
- literal: - literal:
build: build:
type: i64 type: i64
from: parse_number_base
DoubleLiteral: DoubleLiteral:
children: children:
- literal: - literal:
build: build:
type: f64 type: f64
from: parse_whole_pair
NumberBase: NumberBase:
rules: rules:
- BinaryBase - BinaryBase
@ -649,7 +646,6 @@ DecimalBase:
- literal: - literal:
build: build:
type: string type: string
from: whole_pair
BinaryBase: BinaryBase:
children: children:
- binary_digits - binary_digits
@ -658,7 +654,6 @@ BinaryDigits:
- literal: - literal:
build: build:
type: string type: string
from: whole_pair
HexadecimalBase: HexadecimalBase:
children: children:
- hexadecimal_digits - hexadecimal_digits
@ -667,7 +662,6 @@ HexadecimalDigits:
- literal: - literal:
build: build:
type: string type: string
from: whole_pair
StringLiteral: StringLiteral:
rules: rules:
- SingleQuoteString - SingleQuoteString
@ -690,13 +684,11 @@ StringInner:
- literal: - literal:
build: build:
type: string type: string
from: whole_pair
DStringInner: DStringInner:
children: children:
- literal: - literal:
build: build:
type: string type: string
from: whole_pair
DStringExpression: DStringExpression:
children: children:
- expression - expression
@ -713,7 +705,6 @@ BacktickInner:
- literal: - literal:
build: build:
type: string type: string
from: whole_pair
BooleanLiteral: BooleanLiteral:
children: children:
- literal: - literal: