Add return value to build fn.

This commit is contained in:
Jesse Brault 2025-09-04 11:08:23 -05:00
parent 0adb4bbe0e
commit 0a97cc01b9
6 changed files with 140 additions and 52 deletions

View File

@ -90,6 +90,57 @@ fn make_rule_matcher(child_spec: &ChildSpec) -> TokenStream {
} }
} }
fn make_child_arg(child_spec: &ChildSpec) -> Option<TokenStream> {
match child_spec {
ChildSpec::SkipChild(_) => None,
ChildSpec::VecChild(vec_child) => {
let child_ident = match vec_child.build() {
VecChildToBuild::Type(vec_type_child) => format_ident!("{}", vec_type_child.var_name())
};
Some(quote! { #child_ident })
},
ChildSpec::SingleChild(single_child) => {
match single_child.build() {
SingleChildToBuild::Type(single_type_child) => {
let child_ident = format_ident!("{}", single_type_child.var_name());
if single_type_child.optional() {
Some(quote! { #child_ident })
} else if let Some(or_else) = single_type_child.or_else() {
let child_type_ident = format_ident!("{}", single_type_child.build());
let or_else_ident = format_ident!("{}", or_else);
Some(quote! {
#child_ident.unwrap_or_else(|| Box::new(#child_type_ident::#or_else_ident()))
})
} else {
Some(quote! { #child_ident.unwrap() })
}
},
SingleChildToBuild::Boolean(single_boolean_child) => {
let child_ident = format_ident!("{}", single_boolean_child.var_name());
Some(quote! { #child_ident })
}
}
}
}
}
fn make_return_value_stream(build_spec: &StructBuildSpec) -> TokenStream {
let type_ident = format_ident!("{}", build_spec.build());
let child_args = build_spec
.children()
.iter()
.map(|child| make_child_arg(child))
.filter(|child_arg| child_arg.is_some())
.map(|child_arg| child_arg.unwrap())
.collect::<Vec<_>>();
quote! {
#type_ident::new(
#(#child_args,)*
)
}
}
pub fn make_struct_build_fn(build_spec: &StructBuildSpec) -> TokenStream { pub fn make_struct_build_fn(build_spec: &StructBuildSpec) -> TokenStream {
let build_fn_ident = format_ident!("{}", build_spec.with()); let build_fn_ident = format_ident!("{}", build_spec.with());
let pair_ident = format_ident!("{}_pair", build_spec.build().to_case(Case::Snake)); let pair_ident = format_ident!("{}_pair", build_spec.build().to_case(Case::Snake));
@ -117,11 +168,15 @@ pub fn make_struct_build_fn(build_spec: &StructBuildSpec) -> TokenStream {
} }
}; };
let new_stream = make_return_value_stream(build_spec);
quote! { quote! {
fn #build_fn_ident(#pair_ident: Pair<Rule>) -> #return_type_ident { fn #build_fn_ident(#pair_ident: Pair<Rule>) -> #return_type_ident {
#(#child_holders;)* #(#child_holders;)*
#iter_stream #iter_stream
#new_stream
} }
} }
} }

View File

@ -48,34 +48,56 @@ fn get_vec_child(name: &str, rule: &str, build: &Yaml) -> ChildSpec {
fn get_single_child_to_build(name: &str, rule: &str, optional: bool, build: &Yaml) -> SingleChildToBuild { fn get_single_child_to_build(name: &str, rule: &str, optional: bool, build: &Yaml) -> SingleChildToBuild {
if build.is_hash() { if build.is_hash() {
let r#type = build["type"].as_str().unwrap(); match build["type"].as_str() {
let var_name = build["var"] Some(r#type) => {
.as_str() let var_name = build["var"]
.map(|s| s.to_string()) .as_str()
.unwrap_or(name.to_string()); .map(|s| s.to_string())
let on = build["on"].as_str().unwrap(); .unwrap_or(name.to_string());
if r#type.eq("boolean") && on.eq("rule_present") { let on = build["on"].as_str().unwrap();
SingleChildToBuild::Boolean(SingleBooleanChildToBuild::new( if r#type.eq("boolean") && on.eq("rule_present") {
&var_name, SingleChildToBuild::Boolean(SingleBooleanChildToBuild::new(
BuildBooleanOn::RulePresent, &var_name,
)) BuildBooleanOn::RulePresent,
} else { ))
todo!("currently on boolean types with on: rule_present are supported") } else {
todo!("currently on boolean types with on: rule_present are supported")
}
},
None => {
let or_else = build["or_else"]
.as_str()
.map(|s| s.to_string())
.or_else(|| {
let or_else_default = build["or_else_default"]
.as_bool()
.unwrap_or_else(|| false);
if or_else_default {
Some(String::from("default"))
} else {
None
}
});
SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(
rule,
or_else,
optional,
))
}
} }
} else { } else {
match build.as_str() { match build.as_str() {
Some(s) => SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(s, optional)), Some(s) => SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(s, None, optional)),
None => SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(rule, optional)), None => SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(rule, None, optional)),
} }
} }
} }
fn get_single_child(name: &str, rule: &str, optional: bool, or_else: Option<String>, build: &Yaml) -> ChildSpec { fn get_single_child(name: &str, rule: &str, optional: bool, build: &Yaml) -> ChildSpec {
ChildSpec::SingleChild(SingleChild::new( ChildSpec::SingleChild(SingleChild::new(
name, name,
rule, rule,
get_single_child_to_build(name, rule, optional, build), get_single_child_to_build(name, rule, optional, build),
or_else
)) ))
} }
@ -110,21 +132,8 @@ fn get_child_specs(children: &Yaml) -> Vec<ChildSpec> {
let optional = props["optional"] let optional = props["optional"]
.as_bool() .as_bool()
.unwrap_or_else(|| false); .unwrap_or_else(|| false);
let or_else = props["or_else"]
.as_str()
.map(|s| s.to_string())
.or_else(|| {
let or_else_default = props["or_else_default"]
.as_bool()
.unwrap_or_else(|| false);
if or_else_default {
Some(String::from("default"))
} else {
None
}
});
get_single_child(name, &rule, optional, or_else, build) get_single_child(name, &rule, optional, build)
} }
} else { } else {
ChildSpec::SingleChild(SingleChild::from_name_snake(child_spec.as_str().unwrap())) ChildSpec::SingleChild(SingleChild::from_name_snake(child_spec.as_str().unwrap()))

View File

@ -9,6 +9,7 @@ use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
use spec::BuildSpec; use spec::BuildSpec;
use syn::File; use syn::File;
use syn::spanned::Spanned;
fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) { fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) {
println!("*** BuildSpec ***"); println!("*** BuildSpec ***");

View File

@ -224,7 +224,6 @@ pub struct SingleChild {
name: String, name: String,
rule: String, rule: String,
build: SingleChildToBuild, build: SingleChildToBuild,
or_else: Option<String>
} }
impl SingleChild { impl SingleChild {
@ -234,18 +233,17 @@ impl SingleChild {
rule: name.to_case(Case::Pascal), rule: name.to_case(Case::Pascal),
build: SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule( build: SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(
&name.to_case(Case::Pascal), &name.to_case(Case::Pascal),
None,
false, false,
)), )),
or_else: None,
} }
} }
pub fn new(name: &str, rule: &str, build: SingleChildToBuild, or_else: Option<String>) -> Self { pub fn new(name: &str, rule: &str, build: SingleChildToBuild) -> Self {
Self { Self {
name: name.to_string(), name: name.to_string(),
rule: rule.to_string(), rule: rule.to_string(),
build, build,
or_else,
} }
} }
@ -276,15 +274,21 @@ pub struct SingleTypeChildToBuild {
build: String, build: String,
var_name: String, var_name: String,
with: String, with: String,
or_else: Option<String>,
optional: bool, optional: bool,
} }
impl SingleTypeChildToBuild { impl SingleTypeChildToBuild {
pub fn from_build_or_rule(build_or_rule: &str, optional: bool) -> Self { pub fn from_build_or_rule(
build_or_rule: &str,
or_else: Option<String>,
optional: bool,
) -> Self {
Self { Self {
build: build_or_rule.to_string(), build: build_or_rule.to_string(),
var_name: build_or_rule.to_case(Case::Snake), var_name: build_or_rule.to_case(Case::Snake),
with: make_build_fn_name(build_or_rule), with: make_build_fn_name(build_or_rule),
or_else,
optional, optional,
} }
} }
@ -304,6 +308,11 @@ impl SingleTypeChildToBuild {
&self.with &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. /// If the type should be wrapped in an Option.
pub fn optional(&self) -> bool { pub fn optional(&self) -> bool {
self.optional self.optional

View File

@ -82,12 +82,6 @@ $defs:
optional: optional:
type: boolean type: boolean
description: If true, this child will be stored as an Option. description: If true, this child will be stored as an Option.
or_else:
type: string
description: The method name to call upon the built-type if the rule is not found. Takes precedence over "or_else_default".
or_else_default:
type: boolean
description: Whether to call the default method on the built-type if the rule is not found.
build: build:
oneOf: oneOf:
- type: string - type: string
@ -97,7 +91,19 @@ $defs:
additionalProperties: false additionalProperties: false
description: A definition of what exactly to build for a given child rule. description: A definition of what exactly to build for a given child rule.
oneOf: oneOf:
- $ref: "#/$defs/BuildSingleTypeChild"
- $ref: "#/$defs/BuildBooleanChild" - $ref: "#/$defs/BuildBooleanChild"
BuildSingleTypeChild:
type: object
additionalProperties: false
description: A definition of a single-type child to build.
properties:
or_else:
type: string
description: The method name to call upon the built-type if the rule is not found. Takes precedence over "or_else_default".
or_else_default:
type: boolean
description: Whether to call the default method on the built-type if the rule is not found.
BuildBooleanChild: BuildBooleanChild:
type: object type: object
additionalProperties: false additionalProperties: false

View File

@ -112,9 +112,11 @@ Interface:
skip: true skip: true
- identifier - identifier
- generic_parameters: - generic_parameters:
or_else_default: true build:
or_else_default: true
- implements_list: - implements_list:
or_else_default: true build:
or_else_default: true
- declarations: - declarations:
rule: InterfaceLevelDeclaration rule: InterfaceLevelDeclaration
vec: true vec: true
@ -133,11 +135,14 @@ Class:
skip: true skip: true
- identifier - identifier
- generic_parameters: - generic_parameters:
or_else_default: true build:
or_else_default: true
- class_constructor: - class_constructor:
or_else_default: true build:
or_else_default: true
- implements_list: - implements_list:
or_else_default: true build:
or_else_default: true
- class_level_declarations: - class_level_declarations:
rule: ClassLevelDeclaration rule: ClassLevelDeclaration
vec: true vec: true
@ -155,11 +160,11 @@ Function:
skip: true skip: true
- generics: - generics:
rule: GenericParameters rule: GenericParameters
or_else_default: true
- identifier - identifier
- parameters - parameters
- return_type: - return_type:
or_else: void build:
or_else: void
- function_body - function_body
OperatorFunction: OperatorFunction:
children: children:
@ -173,11 +178,13 @@ OperatorFunction:
skip: true skip: true
- generics: - generics:
rule: GenericParameters rule: GenericParameters
or_else_default: true build:
or_else_default: true
- operator - operator
- parameters - parameters
- return_type: - return_type:
or_else: void build:
or_else: void
- function_body - function_body
PlatformFunction: PlatformFunction:
children: children:
@ -194,7 +201,8 @@ PlatformFunction:
skip: true skip: true
- generics: - generics:
rule: GenericParameters rule: GenericParameters
or_else_default: true build:
or_else_default: true
- identifier - identifier
- parameters - parameters
- return_type - return_type