WIP refactor of deserialization and ast code generation.

This commit is contained in:
Jesse Brault 2025-09-15 18:20:09 -05:00
parent 2aee2cdd4e
commit b5cdb8dd29
9 changed files with 587 additions and 632 deletions

View File

@ -1,11 +1,4 @@
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 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 convert_case::{Case, Casing};
use yaml_rust2::{Yaml, YamlLoader};
@ -13,150 +6,6 @@ fn get_as_bool(yaml: &Yaml) -> bool {
yaml.as_bool().unwrap_or_else(|| false)
}
fn get_vec_child_to_build(build: &Yaml, name: &str, rule: &str) -> VecChildToBuild {
if build.is_hash() {
let build_type = build["build"].as_str().unwrap();
let var_name = build["var"]
.as_str()
.map(|s| s.to_string())
.unwrap_or_else(|| build_type.to_case(Case::Snake));
let with = build["with"]
.as_str()
.map(|s| s.to_string())
.unwrap_or_else(|| make_build_fn_name(build_type));
VecChildToBuild::Type(VecTypeChildToBuild::new(build_type, &var_name, &with))
} else {
let build_as_str = build.as_str().unwrap_or(rule);
VecChildToBuild::Type(VecTypeChildToBuild::new(
build_as_str,
name,
make_build_fn_name(build_as_str).as_str(),
))
}
}
fn get_vec_child(name: &str, rule: &str, build: &Yaml) -> ChildSpec {
ChildSpec::VecChild(VecChild::new(
name,
rule,
get_vec_child_to_build(build, name, rule),
))
}
fn get_single_child_to_build(
name: &str,
rule: &str,
optional: bool,
build: &Yaml,
) -> SingleChildToBuild {
if build.is_hash() {
match build["type"].as_str() {
Some(r#type) => {
let var_name = build["var"]
.as_str()
.map(|s| s.to_string())
.unwrap_or(name.to_string());
match r#type {
"boolean" => {
if yaml_is_string(&build["on"], "rule_present") {
SingleChildToBuild::Boolean(SingleBooleanChildToBuild::new(
&var_name,
BooleanBuild::RulePresent,
))
} else if yaml_is_string(&build["from"], "parse_whole_pair") {
SingleChildToBuild::Boolean(SingleBooleanChildToBuild::new(
&var_name,
BooleanBuild::ParseWholePair,
))
} else {
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 => {
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 {
match build.as_str() {
Some(s) => SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(
s, None, optional,
)),
None => SingleChildToBuild::Type(SingleTypeChildToBuild::new(
&rule,
&name,
&make_build_fn_name(&rule),
None,
optional,
)),
}
}
}
fn get_single_child(name: &str, rule: &str, optional: bool, build: &Yaml) -> ChildSpec {
ChildSpec::SingleChild(SingleChild::new(
name,
rule,
get_single_child_to_build(name, rule, optional, build),
))
}
fn get_child_specs(children: &Yaml) -> Vec<ChildSpec> {
children
.as_vec()
.unwrap()
.iter()
.map(|child_spec| {
if child_spec.is_hash() {
let (name, props) = unwrap_single_member_hash(child_spec);
let rule = props["rule"]
.as_str()
.map(|s| s.to_string())
.unwrap_or(name.to_case(Case::Pascal));
if get_as_bool(&props["skip"]) {
return ChildSpec::SkipChild(SkipChild::new(&name, &rule));
}
let build = &props["build"];
if get_as_bool(&props["vec"]) {
get_vec_child(&name, &rule, build)
} else {
let optional = get_as_bool(&props["optional"]);
get_single_child(&name, &rule, optional, build)
}
} else {
ChildSpec::SingleChild(SingleChild::from_name_snake(child_spec.as_str().unwrap()))
}
})
.collect()
}
fn unwrap_single_member_hash(hash: &Yaml) -> (String, &Yaml) {
let as_hash = hash.as_hash().unwrap();
if as_hash.is_empty() {
@ -169,110 +18,267 @@ fn unwrap_single_member_hash(hash: &Yaml) -> (String, &Yaml) {
(key_as_string, member_value)
}
fn get_leaf_enum_rules(rules: &Yaml) -> Vec<LeafEnumRule> {
rules
.as_vec()
.unwrap()
.iter()
.map(|rule| {
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::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!(
"if a leaf_enum rule is a hash, its child prop must be present and true"
);
}
} else {
let rule_as_str = rule.as_str().unwrap();
let build = LeafEnumRuleBuild::new(rule_as_str, None);
LeafEnumRule::new(rule_as_str, build)
}
})
.collect()
}
fn get_enum_rules(rule_specs: &Yaml) -> Vec<EnumRule> {
rule_specs
.as_vec()
.unwrap()
.iter()
.map(|rule_spec| {
if rule_spec.is_hash() {
let rule = rule_spec["rule"].as_str().unwrap();
let build = rule_spec["build"].as_str().unwrap();
let with = if !rule_spec["with"].is_badvalue() {
rule_spec.as_str().unwrap().to_string()
} else {
make_build_fn_name(build)
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()),
};
EnumRule::new(rule, build, &with)
} else {
EnumRule::from_rule(rule_spec.as_str().unwrap())
ProductionKind::String(from)
},
_ => panic!("invalid kind: {}", production_yaml["kind"].as_str().unwrap()),
};
ProductionBuildSpec::new(rule, kind)
}
fn deserialize_leaf_enum_rule(rule_yaml: &Yaml) -> Box<LeafEnumRule> {
Box::new(LeafEnumRule::new(rule_yaml.as_str().unwrap()))
}
fn deserialize_leaf_enum_rules(rules_yaml: &Yaml) -> Vec<Box<LeafEnumRule>> {
rules_yaml
.as_vec()
.unwrap()
.iter()
.map(|rule_yaml| {
deserialize_leaf_enum_rule(rule_yaml)
})
.collect()
}
fn get_leaf_struct_child_specs(children: &Yaml) -> Vec<LeafStructChild> {
fn deserialize_enum_rule_custom_child(rule_props: &Yaml) -> Option<Box<EnumRuleChild>> {
if !rule_props["child"].as_bool().unwrap() {
None
} else {
let kind = match rule_props["kind"].as_str().unwrap() {
"int" => EnumRuleChildKind::Int,
"long" => EnumRuleChildKind::Long,
"double" => EnumRuleChildKind::Double,
"usize" => EnumRuleChildKind::USize,
"string" => EnumRuleChildKind::String,
"boolean" => EnumRuleChildKind::Boolean,
_ => 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<EnumRuleChild> {
Box::new(EnumRuleChild::new(Box::new(
EnumRuleChildKind::Node(EnumRuleNodeChild::new(rule)),
)))
}
fn deserialize_enum_rule(rule_yaml: &Yaml) -> Box<EnumRule> {
if rule_yaml.is_hash() {
let (rule, rule_props) = unwrap_single_member_hash(rule_yaml);
Box::new(EnumRule::new(
&rule,
deserialize_enum_rule_custom_child(rule_props)
))
} else {
let rule_as_str = rule_yaml.as_str().unwrap();
Box::new(EnumRule::new(
rule_as_str,
Some(deserialize_enum_rule_node_child(rule_as_str)),
))
}
}
fn deserialize_enum_rules(rules_yaml: &Yaml) -> Vec<Box<EnumRule>> {
rules_yaml
.as_vec()
.unwrap()
.iter()
.map(|rule_yaml| deserialize_enum_rule(rule_yaml))
.collect()
}
fn deserialize_leaf_struct_member(name: &str, props: &Yaml) -> Box<LeafStructMember> {
let kind = props["kind"].as_str().unwrap();
if kind == "string" {
Box::new(LeafStructMember::new(name, LeafStructMemberKind::String))
} else {
panic!("invalid member kind: {}", kind);
}
}
fn deserialize_leaf_struct_members(members_yaml: &Yaml) -> Vec<Box<LeafStructMember>> {
members_yaml
.as_vec()
.unwrap()
.iter()
.map(|member| {
let (member_name, member_props) = unwrap_single_member_hash(member);
deserialize_leaf_struct_member(&member_name, member_props)
})
.collect()
}
fn deserialize_member_node_to_build(
rule: &str,
build_props: &Yaml,
optional: bool,
) -> Box<MemberChildToBuild> {
let or_else = build_props["or_else"]
.as_str()
.or_else(|| {
if get_as_bool(&build_props["or_else_default"]) {
Some("default")
} else {
None
}
})
.map(ToString::to_string);
Box::new(MemberChildToBuild::Node(NodeChildToBuild::new(
rule, or_else, optional,
)))
}
fn deserialize_member_boolean_to_build(name: &str) -> Box<MemberChildToBuild> {
Box::new(MemberChildToBuild::Boolean(BooleanChildToBuild::new(name)))
}
fn deserialize_member_child_to_build(
name: &str,
rule: &str,
props: &Yaml,
optional: bool,
) -> Box<MemberChildToBuild> {
if props["build"].is_hash() {
let build_props = &props["build"];
let kind = build_props["kind"].as_str().or(Some("node")).unwrap();
if kind == "node" {
deserialize_member_node_to_build(rule, build_props, optional)
} else if kind == "boolean" {
deserialize_member_boolean_to_build(name)
} else {
panic!("unsupported kind: {}", kind)
}
} else {
panic!("build is required for a member child")
}
}
fn deserialize_member_child(
name: &str,
rule: &str,
optional: bool,
props: &Yaml,
) -> Box<StructChildSpec> {
Box::new(StructChildSpec::MemberChild(MemberChild::new(
name,
rule,
deserialize_member_child_to_build(name, rule, props, optional),
)))
}
fn deserialize_vec_node_child(name: &str, props: &Yaml) -> Box<StructChildSpec> {
let rule = props["rule"].as_str().unwrap();
Box::new(StructChildSpec::VecChild(VecChild::new(
name,
rule,
Box::new(VecChildToBuild::Node(VecNodeChildToBuild::new(rule))),
)))
}
fn deserialize_vec_string_child(name: &str, props: &Yaml) -> Box<StructChildSpec> {
let rule = props["rule"].as_str().unwrap();
Box::new(StructChildSpec::VecChild(VecChild::new(
name,
rule,
Box::new(VecChildToBuild::String),
)))
}
fn deserialize_vec_child(name: &str, props: &Yaml) -> Box<StructChildSpec> {
let kind = props["kind"].as_str().or_else(|| Some("node")).unwrap();
if kind == "node" {
deserialize_vec_node_child(name, props)
} else if kind == "string" {
deserialize_vec_string_child(name, props)
} else {
panic!("invalid kind: {}", kind);
}
}
fn deserialize_skip_child(name: &str, rule: &str) -> Box<StructChildSpec> {
Box::new(StructChildSpec::SkipChild(SkipChild::new(name, rule)))
}
fn deserialize_struct_hash_child(child: &Yaml) -> Box<StructChildSpec> {
let (name, props) = unwrap_single_member_hash(child);
let rule = props["rule"]
.as_str()
.map(|s| s.to_string())
.unwrap_or(name.to_case(Case::Pascal));
if get_as_bool(&props["skip"]) {
deserialize_skip_child(&name, &rule)
} else {
if get_as_bool(&props["vec"]) {
deserialize_vec_child(&name, props)
} else {
let optional = get_as_bool(&props["optional"]);
deserialize_member_child(&name, &rule, optional, props)
}
}
}
fn deserialize_struct_string_child(child: &Yaml) -> Box<StructChildSpec> {
let child_as_str = child.as_str().unwrap();
let child_name_pascal = child_as_str.to_case(Case::Pascal);
Box::new(StructChildSpec::MemberChild(MemberChild::new(
child_as_str,
&child_name_pascal,
Box::new(MemberChildToBuild::Node(NodeChildToBuild::new(
&child_name_pascal,
None,
false,
))),
)))
}
fn deserialize_struct_children(children: &Yaml) -> Vec<Box<StructChildSpec>> {
children
.as_vec()
.unwrap()
.iter()
.map(|child_spec| {
let (name, hash) = unwrap_single_member_hash(child_spec);
LeafStructChild::new(&name, LeafStructChildType::String)
})
.collect::<Vec<_>>()
}
fn yaml_is_string(yaml: &Yaml, test: &str) -> bool {
match yaml.as_str() {
Some(s) => s == test,
None => false,
}
}
fn deserialize_build_spec(build_spec_name: &Yaml, build_spec: &Yaml) -> BuildSpec {
let build_spec_name_pascal = build_spec_name.as_str().unwrap();
let node_type = &build_spec["type"];
let children = &build_spec["children"];
let rules = &build_spec["rules"];
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,
child_specs,
))
} else if yaml_is_string(node_type, "leaf_enum") && rules.is_array() {
let leaf_enum_rules = get_leaf_enum_rules(rules);
BuildSpec::LeafEnum(LeafEnumBuildSpec::from_name(
build_spec_name_pascal,
leaf_enum_rules,
))
} else if rules.is_array() {
// enum node
let enum_rules = get_enum_rules(rules);
BuildSpec::Enum(EnumBuildSpec::from_name(build_spec_name_pascal, enum_rules))
if child_spec.is_hash() {
deserialize_struct_hash_child(child_spec)
} else {
panic!("Expected a node spec for either a struct, leaf_struct, enum, leaf_enum node type.");
deserialize_struct_string_child(child_spec)
}
})
.collect()
}
fn deserialize_build_spec(build_spec_name: &str, build_spec: &Yaml) -> BuildSpec {
if build_spec["children"].is_array() {
let children = deserialize_struct_children(&build_spec["children"]);
BuildSpec::Struct(StructBuildSpec::new(build_spec_name, children))
} else if build_spec["members"].is_array() {
let members = deserialize_leaf_struct_members(&build_spec["members"]);
BuildSpec::LeafStruct(LeafStructBuildSpec::new(build_spec_name, members))
} else if build_spec["rules"].is_array() {
let rules = deserialize_enum_rules(&build_spec["rules"]);
BuildSpec::Enum(EnumBuildSpec::new(build_spec_name, rules))
} else if build_spec["leaf_rules"].is_array() {
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"]))
} else {
panic!("Expected a node spec for either a struct, leaf_struct, enum, leaf_enum node type, or a production type.");
}
}
@ -283,6 +289,9 @@ pub fn deserialize_yaml_spec(yaml: &str) -> Vec<BuildSpec> {
doc.as_hash()
.unwrap()
.iter()
.map(|(build_spec_name, build_spec)| deserialize_build_spec(build_spec_name, build_spec))
.map(|(build_spec_name, build_spec)| {
let name_as_str = build_spec_name.as_str().unwrap();
deserialize_build_spec(name_as_str, build_spec)
})
.collect()
}

View File

@ -1,4 +1,4 @@
use crate::spec::{LeafStructBuildSpec, LeafStructChildType};
use crate::spec::{LeafStructBuildSpec, LeafStructMemberKind};
use crate::util::{make_build_fn_name, make_build_pair};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
@ -9,12 +9,12 @@ pub fn make_leaf_struct_build_fn(build_spec: &LeafStructBuildSpec) -> TokenStrea
let return_type_ident = format_ident!("{}", build_spec.build());
let child_builders = build_spec
.children()
.members()
.iter()
.map(|leaf_struct_child| {
let child_ident = format_ident!("{}", leaf_struct_child.name());
match leaf_struct_child.r#type() {
LeafStructChildType::String => {
LeafStructMemberKind::String => {
quote! {
let #child_ident = #pair_ident.as_str()
}
@ -24,7 +24,7 @@ pub fn make_leaf_struct_build_fn(build_spec: &LeafStructBuildSpec) -> TokenStrea
.collect::<Vec<_>>();
let child_args = build_spec
.children()
.members()
.iter()
.map(|leaf_struct_child| format_ident!("{}", leaf_struct_child.name()))
.collect::<Vec<_>>();

View File

@ -2,6 +2,7 @@ pub mod deserialize;
mod enum_build_fn;
mod leaf_enum_build_fn;
mod leaf_struct_build_fn;
mod production_build_fn;
mod spec;
mod struct_build_fn;
mod type_gen;
@ -10,6 +11,7 @@ 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::production_build_fn::make_production_build_fn;
use crate::struct_build_fn::make_struct_build_fn;
use crate::type_gen::make_type;
use proc_macro2::TokenStream;
@ -21,16 +23,22 @@ fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) {
println!("*** BuildSpec ***");
match build_spec {
BuildSpec::Enum(enum_build_spec) => {
println!("Spec name: {}", enum_build_spec.name());
println!("Enum Spec - build: {}", enum_build_spec.build());
}
BuildSpec::LeafEnum(leaf_enum_build_spec) => {
println!("Spec name: {}", leaf_enum_build_spec.name());
println!("Leaf Enum Spec - build: {}", leaf_enum_build_spec.build());
}
BuildSpec::Struct(struct_build_spec) => {
println!("Spec name: {}", struct_build_spec.name());
println!("Struct Spec - build: {}", struct_build_spec.build());
}
BuildSpec::LeafStruct(leaf_struct_build_spec) => {
println!("Spec name: {}", leaf_struct_build_spec.name());
println!(
"Leaf Struct Spec - build: {}",
leaf_struct_build_spec.build()
);
}
BuildSpec::Production(production_build_spec) => {
println!("Production Spec - rule: {}", production_build_spec.rule())
}
}
println!("{:#?}", token_stream);
@ -72,6 +80,11 @@ fn generate_build_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
debug_built_spec(build_spec, &stream);
stream
}
BuildSpec::Production(production_build_spec) => {
let stream = make_production_build_fn(production_build_spec);
debug_built_spec(build_spec, &stream);
stream
}
})
.collect::<Vec<_>>();
let combined = quote! {
@ -103,33 +116,3 @@ pub fn generate_files(build_specs: &[BuildSpec]) -> Vec<AstGeneratedFile> {
generate_node_file(build_specs),
]
}
pub fn test_dump() -> String {
let build_specs = deserialize::deserialize_yaml_spec(include_str!("../../src/parser/ast.yaml"));
let mut streams: Vec<TokenStream> = vec![];
for build_spec in &build_specs {
let type_stream = make_type(build_spec);
debug_built_spec(build_spec, &type_stream);
streams.push(type_stream);
}
for build_spec in &build_specs {
match build_spec {
BuildSpec::Enum(_) => {}
BuildSpec::LeafEnum(_) => {}
BuildSpec::Struct(struct_spec) => {
let struct_build_fn_stream = make_struct_build_fn(struct_spec);
debug_built_spec(build_spec, &struct_build_fn_stream);
streams.push(struct_build_fn_stream);
}
BuildSpec::LeafStruct(_) => {}
}
}
let combined = quote! {
#(#streams)*
};
let file: File = syn::parse2(combined).unwrap();
prettyplease::unparse(&file)
}

View File

@ -0,0 +1,90 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use crate::spec::{ProductionBuildSpec, ProductionKind, ProductionStringFrom};
use crate::util::{make_build_fn_name, make_build_pair};
pub fn make_production_build_fn(production_build_spec: &ProductionBuildSpec) -> TokenStream {
let build_fn_ident = format_ident!("{}", make_build_fn_name(production_build_spec.rule()));
let return_type_ident = match production_build_spec.kind() {
ProductionKind::Int => format_ident!("i32"),
ProductionKind::Long => format_ident!("i64"),
ProductionKind::Double => format_ident!("f64"),
ProductionKind::String(_) => format_ident!("String"),
ProductionKind::Boolean => format_ident!("bool"),
};
let pair_ident = format_ident!("{}", make_build_pair(production_build_spec.rule()));
let pair_mapper = match production_build_spec.kind() {
ProductionKind::Int => quote! {
let number_base_pair = #pair_ident.to_inner()
.next()
.unwrap();
let inner_number_base_pair = number_base_pair.to_inner()
.next()
.unwrap();
match inner_number_base_pair.as_rule() {
Rule::BinaryBase => {
todo!()
}
Rule::HexadecimalBase => {
todo!()
}
Rule::DecimalBase => {
inner_number_base_pair.as_str().parse::<i32>().unwrap()
}
_ => panic!()
}
},
ProductionKind::Long => quote! {
let number_base_pair = #pair_ident.to_inner()
.next()
.unwrap();
let inner_number_base_pair = number_base_pair.to_inner()
.next()
.unwrap();
match inner_number_base_pair.as_rule() {
Rule::BinaryBase => {
todo!()
}
Rule::HexadecimalBase => {
todo!()
}
Rule::DecimalBase => {
inner_number_base_pair.as_str().parse::<i64>().unwrap()
}
_ => panic!()
}
},
ProductionKind::Double => quote! {
#pair_ident.as_str().parse::<f64>().unwrap()
},
ProductionKind::String(from) => {
match from {
ProductionStringFrom::StringInner => {
quote! {
#pair_ident.to_inner()
.iter()
.next()
.unwrap()
.as_str()
.to_string()
}
}
ProductionStringFrom::WholePair => {
quote! {
#pair_ident.as_string().to_string()
}
}
}
},
ProductionKind::Boolean => quote! {
#pair_ident.as_str().parse::<bool>().unwrap()
}
};
quote! {
fn #build_fn_ident(#pair_ident: Pair<Rule>) -> #return_type_ident {
#pair_mapper
}
}
}

View File

@ -6,59 +6,45 @@ pub enum BuildSpec {
LeafEnum(LeafEnumBuildSpec),
Struct(StructBuildSpec),
LeafStruct(LeafStructBuildSpec),
Production(ProductionBuildSpec),
}
// Enum build spec
pub struct EnumBuildSpec {
name: String,
build: String,
rules: Vec<EnumRule>,
rules: Vec<Box<EnumRule>>,
}
impl EnumBuildSpec {
pub fn from_name(name: &str, rules: Vec<EnumRule>) -> Self {
pub fn new(build: &str, rules: Vec<Box<EnumRule>>) -> Self {
EnumBuildSpec {
name: name.to_string(),
build: name.to_string(),
build: build.to_string(),
rules,
}
}
/// The top-level key for the build spec in the yaml file.
pub fn name(&self) -> &str {
&self.name
}
/// The enum type to be built, in Pascal case.
pub fn build(&self) -> &str {
&self.build
}
/// The individual rule specs.
pub fn rules(&self) -> &[EnumRule] {
&self.rules
pub fn rules(&self) -> impl Iterator<Item = &EnumRule> {
self.rules.iter().map(Box::as_ref)
}
}
pub struct EnumRule {
rule: String,
build: String,
with: String,
child: Option<Box<EnumRuleChild>>
}
impl EnumRule {
pub fn from_rule(rule: &str) -> Self {
pub fn new(rule: &str, child: Option<Box<EnumRuleChild>>) -> Self {
Self {
rule: rule.to_string(),
build: rule.to_string(),
with: make_build_fn_name(rule),
}
}
pub fn new(rule: &str, build: &str, with: &str) -> Self {
Self {
rule: rule.to_string(),
build: build.to_string(),
with: with.to_string(),
child
}
}
@ -67,160 +53,125 @@ impl EnumRule {
&self.rule
}
/// The type to build, in Pascal case.
pub fn child(&self) -> Option<&EnumRuleChild> {
if let Some(child) = &self.child {
Some(child.as_ref())
} else {
None
}
}
}
pub struct EnumRuleChild {
kind: Box<EnumRuleChildKind>
}
impl EnumRuleChild {
pub fn new(kind: Box<EnumRuleChildKind>) -> Self {
Self { kind }
}
pub fn kind(&self) -> &EnumRuleChildKind {
&self.kind
}
}
pub enum EnumRuleChildKind {
Node(EnumRuleNodeChild),
Int,
Long,
Double,
USize,
String,
Boolean
}
pub struct EnumRuleNodeChild {
build: String
}
impl EnumRuleNodeChild {
pub fn new(build: &str) -> Self {
Self {
build: build.to_string(),
}
}
pub fn build(&self) -> &str {
&self.build
}
}
/// The build-fn name, in snake case.
pub fn with(&self) -> &str {
&self.with
}
}
// Leaf enum build spec
pub struct LeafEnumBuildSpec {
name: String,
build: String,
rules: Vec<LeafEnumRule>,
rules: Vec<Box<LeafEnumRule>>,
}
impl LeafEnumBuildSpec {
pub fn from_name(name: &str, rules: Vec<LeafEnumRule>) -> Self {
pub fn new(build: &str, rules: Vec<Box<LeafEnumRule>>) -> Self {
Self {
name: name.to_string(),
build: name.to_string(),
rules,
build: build.to_string(),
rules
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn build(&self) -> &str {
&self.build
}
pub fn rules(&self) -> &[LeafEnumRule] {
&self.rules
pub fn rules(&self) -> impl Iterator<Item = &LeafEnumRule> {
self.rules.iter().map(Box::as_ref)
}
}
pub struct LeafEnumRule {
rule: String,
build: LeafEnumRuleBuild,
}
impl LeafEnumRule {
pub fn new(rule: &str, build: LeafEnumRuleBuild) -> Self {
pub fn new(rule: &str) -> Self {
Self {
rule: rule.to_string(),
build,
}
}
pub fn rule(&self) -> &str {
&self.rule
}
pub fn build(&self) -> &LeafEnumRuleBuild {
&self.build
}
}
pub struct LeafEnumRuleBuild {
rule: String,
child: Option<LeafEnumRuleBuildChild>,
}
impl LeafEnumRuleBuild {
pub fn new(rule: &str, child: Option<LeafEnumRuleBuildChild>) -> 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
}
}
// Struct build spec
pub struct StructBuildSpec {
name: String,
build: String,
var_name: String,
with: String,
children: Vec<ChildSpec>,
children: Vec<Box<StructChildSpec>>,
}
impl StructBuildSpec {
pub fn from_name(name: &str, child_specs: Vec<ChildSpec>) -> Self {
pub fn new(build: &str, children: Vec<Box<StructChildSpec>>) -> Self {
Self {
name: name.to_string(),
build: name.to_string(),
var_name: name.to_case(Case::Snake),
with: make_build_fn_name(name),
children: child_specs,
build: build.to_string(),
children
}
}
/// The top-level name of this build spec.
pub fn name(&self) -> &str {
&self.name
}
/// The type to be built, in Pascal case.
pub fn build(&self) -> &str {
&self.build
}
/// The name of the variable to be built, in snake case.
pub fn var_name(&self) -> &str {
&self.var_name
}
/// The build-fn name, in snake case.
pub fn with(&self) -> &str {
&self.with
}
/// The children for this build spec.
pub fn children(&self) -> &[ChildSpec] {
&self.children
pub fn children(&self) -> impl Iterator<Item = &StructChildSpec> {
self.children.iter().map(Box::as_ref)
}
}
pub enum ChildSpec {
pub enum StructChildSpec {
SkipChild(SkipChild),
VecChild(VecChild),
SingleChild(SingleChild),
MemberChild(MemberChild),
}
pub struct SkipChild {
@ -250,11 +201,11 @@ impl SkipChild {
pub struct VecChild {
name: String,
rule: String,
build: VecChildToBuild,
build: Box<VecChildToBuild>,
}
impl VecChild {
pub fn new(name: &str, rule: &str, build: VecChildToBuild) -> Self {
pub fn new(name: &str, rule: &str, build: Box<VecChildToBuild>) -> Self {
Self {
name: name.to_string(),
rule: rule.to_string(),
@ -280,62 +231,35 @@ impl VecChild {
#[derive(Debug)]
pub enum VecChildToBuild {
Type(VecTypeChildToBuild),
Node(VecNodeChildToBuild),
String
}
#[derive(Debug)]
pub struct VecTypeChildToBuild {
pub struct VecNodeChildToBuild {
build: String,
var_name: String,
with: String,
}
impl VecTypeChildToBuild {
pub fn new(build: &str, var_name: &str, with: &str) -> Self {
Self {
build: build.to_string(),
var_name: var_name.to_string(),
with: with.to_string(),
}
impl VecNodeChildToBuild {
pub fn new(build: &str) -> Self {
Self { build: build.to_string() }
}
/// The type to build, in Pascal case.
pub fn build(&self) -> &str {
&self.build
}
/// The name of the variable to build, in snake case.
pub fn var_name(&self) -> &str {
&self.var_name
}
/// The build-fn name.
pub fn with(&self) -> &str {
&self.with
}
}
#[derive(Debug)]
pub struct SingleChild {
pub struct MemberChild {
name: String,
rule: String,
build: SingleChildToBuild,
build: Box<MemberChildToBuild>,
}
impl SingleChild {
pub fn from_name_snake(name: &str) -> Self {
Self {
name: name.to_string(),
rule: name.to_case(Case::Pascal),
build: SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(
&name.to_case(Case::Pascal),
None,
false,
)),
}
}
pub fn new(name: &str, rule: &str, build: SingleChildToBuild) -> Self {
impl MemberChild {
pub fn new(name: &str, rule: &str, build: Box<MemberChildToBuild>) -> Self {
Self {
name: name.to_string(),
rule: rule.to_string(),
@ -354,56 +278,32 @@ impl SingleChild {
}
/// The specification for what to actually build.
pub fn build(&self) -> &SingleChildToBuild {
pub fn build(&self) -> &MemberChildToBuild {
&self.build
}
}
#[derive(Debug)]
pub enum SingleChildToBuild {
Type(SingleTypeChildToBuild),
Boolean(SingleBooleanChildToBuild),
Int(SingleLiteralChildToBuild),
Long(SingleLiteralChildToBuild),
Double(SingleLiteralChildToBuild),
String(SingleLiteralChildToBuild),
pub enum MemberChildToBuild {
Node(NodeChildToBuild),
Boolean(BooleanChildToBuild),
}
#[derive(Debug)]
pub struct SingleTypeChildToBuild {
pub struct NodeChildToBuild {
build: String,
var_name: String,
with: String,
or_else: Option<String>,
optional: bool,
}
impl SingleTypeChildToBuild {
pub fn from_build_or_rule(
build_or_rule: &str,
or_else: Option<String>,
optional: bool,
) -> Self {
Self {
build: build_or_rule.to_string(),
var_name: build_or_rule.to_case(Case::Snake),
with: make_build_fn_name(build_or_rule),
or_else,
optional,
}
}
impl NodeChildToBuild {
pub fn new(
build: &str,
var_name: &str,
with: &str,
or_else: Option<String>,
optional: bool,
) -> Self {
Self {
build: build.to_string(),
var_name: var_name.to_string(),
with: with.to_string(),
or_else,
optional,
}
@ -414,16 +314,6 @@ impl SingleTypeChildToBuild {
&self.build
}
/// The variable name to build, in snake case.
pub fn var_name(&self) -> &str {
&self.var_name
}
/// The build-fn name.
pub fn with(&self) -> &str {
&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()
@ -436,89 +326,56 @@ impl SingleTypeChildToBuild {
}
#[derive(Debug)]
pub struct SingleBooleanChildToBuild {
var_name: String,
build: BooleanBuild,
pub struct BooleanChildToBuild {
name: String
}
impl SingleBooleanChildToBuild {
pub fn new(var_name: &str, build: BooleanBuild) -> Self {
impl BooleanChildToBuild {
pub fn new(name: &str) -> Self {
Self {
var_name: var_name.to_string(),
build,
}
}
pub fn var_name(&self) -> &str {
&self.var_name
}
pub fn build(&self) -> &BooleanBuild {
&self.build
}
}
#[derive(Debug)]
pub enum BooleanBuild {
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
}
}
pub struct LeafStructBuildSpec {
name: String,
build: String,
children: Vec<LeafStructChild>,
}
impl LeafStructBuildSpec {
pub fn new(name: &str, build: &str, children: Vec<LeafStructChild>) -> Self {
Self {
name: name.to_string(),
build: build.to_string(),
children,
name: name.to_string()
}
}
pub fn name(&self) -> &str {
&self.name
}
}
// Leaf Struct build spec
pub struct LeafStructBuildSpec {
build: String,
members: Vec<Box<LeafStructMember>>,
}
impl LeafStructBuildSpec {
pub fn new(build: &str, members: Vec<Box<LeafStructMember>>) -> Self {
Self {
build: build.to_string(),
members,
}
}
pub fn build(&self) -> &str {
&self.build
}
pub fn children(&self) -> &[LeafStructChild] {
&self.children
pub fn members(&self) -> impl Iterator<Item = &LeafStructMember> {
self.members.iter().map(Box::as_ref)
}
}
pub struct LeafStructChild {
pub struct LeafStructMember {
name: String,
r#type: LeafStructChildType,
kind: LeafStructMemberKind,
}
impl LeafStructChild {
pub fn new(name: &str, r#type: LeafStructChildType) -> Self {
impl LeafStructMember {
pub fn new(name: &str, kind: LeafStructMemberKind) -> Self {
Self {
name: name.to_string(),
r#type,
kind,
}
}
@ -526,11 +383,46 @@ impl LeafStructChild {
&self.name
}
pub fn r#type(&self) -> &LeafStructChildType {
&self.r#type
pub fn kind(&self) -> &LeafStructMemberKind {
&self.kind
}
}
pub enum LeafStructChildType {
pub enum LeafStructMemberKind {
String,
}
pub struct ProductionBuildSpec {
rule: String,
kind: ProductionKind,
}
impl ProductionBuildSpec {
pub fn new(rule: &str, kind: ProductionKind) -> Self {
Self {
rule: rule.to_string(),
kind,
}
}
pub fn rule(&self) -> &str {
&self.rule
}
pub fn kind(&self) -> &ProductionKind {
&self.kind
}
}
pub enum ProductionKind {
Int,
Long,
Double,
String(ProductionStringFrom),
Boolean
}
pub enum ProductionStringFrom {
StringInner,
WholePair
}

View File

@ -1,6 +1,6 @@
use crate::spec::{
BooleanBuild, ChildSpec, SingleBooleanChildToBuild, SingleChildToBuild,
SingleLiteralChildToBuild, SingleTypeChildToBuild, StructBuildSpec, VecChild, VecChildToBuild,
BooleanChildToBuild, MemberChildToBuild, NodeChildToBuild,
StructBuildSpec, StructChildSpec, VecChild, VecChildToBuild,
};
use crate::util::make_build_pair;
use proc_macro2::{Ident, TokenStream};
@ -8,7 +8,7 @@ use quote::{format_ident, quote};
fn make_vec_child_holder(vec_child: &VecChild) -> TokenStream {
let (child_ident, child_type_ident) = match vec_child.build() {
VecChildToBuild::Type(vec_type_child) => (
VecChildToBuild::Node(vec_type_child) => (
format_ident!("{}", vec_type_child.var_name()),
format_ident!("{}", vec_type_child.build()),
),
@ -18,7 +18,7 @@ fn make_vec_child_holder(vec_child: &VecChild) -> TokenStream {
}
}
fn make_single_type_child_holder(single_type_child: &SingleTypeChildToBuild) -> TokenStream {
fn make_single_type_child_holder(single_type_child: &NodeChildToBuild) -> TokenStream {
let child_ident = format_ident!("{}", single_type_child.var_name());
let child_type_ident = format_ident!("{}", single_type_child.build());
quote! {
@ -26,10 +26,8 @@ fn make_single_type_child_holder(single_type_child: &SingleTypeChildToBuild) ->
}
}
fn make_single_boolean_child_holder(
single_boolean_child: &SingleBooleanChildToBuild,
) -> TokenStream {
let child_ident = format_ident!("{}", single_boolean_child.var_name());
fn make_single_boolean_child_holder(single_boolean_child: &BooleanChildToBuild) -> TokenStream {
let child_ident = format_ident!("{}", single_boolean_child.name());
quote! {
let mut #child_ident: bool = false
}
@ -45,30 +43,30 @@ fn make_literal_child_holder(
}
}
fn make_child_holder(child_spec: &ChildSpec) -> Option<TokenStream> {
fn make_child_holder(child_spec: &StructChildSpec) -> Option<TokenStream> {
match child_spec {
ChildSpec::SkipChild(_) => None,
ChildSpec::VecChild(vec_child) => Some(make_vec_child_holder(vec_child)),
ChildSpec::SingleChild(single_child) => match single_child.build() {
SingleChildToBuild::Type(single_type_child) => {
StructChildSpec::SkipChild(_) => None,
StructChildSpec::VecChild(vec_child) => Some(make_vec_child_holder(vec_child)),
StructChildSpec::MemberChild(single_child) => match single_child.build() {
MemberChildToBuild::Node(single_type_child) => {
Some(make_single_type_child_holder(single_type_child))
}
SingleChildToBuild::Boolean(boolean_child) => {
MemberChildToBuild::Boolean(boolean_child) => {
Some(make_single_boolean_child_holder(boolean_child))
}
SingleChildToBuild::Int(literal_child) => Some(make_literal_child_holder(
MemberChildToBuild::Int(literal_child) => Some(make_literal_child_holder(
literal_child,
format_ident!("i32"),
)),
SingleChildToBuild::Long(literal_child) => Some(make_literal_child_holder(
MemberChildToBuild::Long(literal_child) => Some(make_literal_child_holder(
literal_child,
format_ident!("i64"),
)),
SingleChildToBuild::Double(literal_child) => Some(make_literal_child_holder(
MemberChildToBuild::Double(literal_child) => Some(make_literal_child_holder(
literal_child,
format_ident!("f64"),
)),
SingleChildToBuild::String(literal_child) => Some(make_literal_child_holder(
MemberChildToBuild::String(literal_child) => Some(make_literal_child_holder(
literal_child,
format_ident!("String"),
)),
@ -80,12 +78,12 @@ 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: &StructChildSpec) -> TokenStream {
match child_spec {
ChildSpec::SkipChild(_) => quote! {},
ChildSpec::VecChild(vec_child) => {
StructChildSpec::SkipChild(_) => quote! {},
StructChildSpec::VecChild(vec_child) => {
let (child_name_ident, build_fn_ident) = match vec_child.build() {
VecChildToBuild::Type(vec_type_child) => (
VecChildToBuild::Node(vec_type_child) => (
format_ident!("{}", vec_type_child.var_name()),
format_ident!("{}", vec_type_child.with()),
),
@ -94,16 +92,16 @@ fn make_match_action(child_spec: &ChildSpec) -> TokenStream {
#child_name_ident.push(Box::new(#build_fn_ident(inner_pair)))
}
}
ChildSpec::SingleChild(single_child) => match single_child.build() {
SingleChildToBuild::Type(single_type_child) => {
StructChildSpec::MemberChild(single_child) => match single_child.build() {
MemberChildToBuild::Node(single_type_child) => {
let child_name_ident = format_ident!("{}", single_type_child.var_name());
let build_fn_ident = format_ident!("{}", single_type_child.with());
quote! {
#child_name_ident = Some(Box::new(#build_fn_ident(inner_pair)))
}
}
SingleChildToBuild::Boolean(single_boolean_child) => {
let child_name_ident = format_ident!("{}", single_boolean_child.var_name());
MemberChildToBuild::Boolean(single_boolean_child) => {
let child_name_ident = format_ident!("{}", single_boolean_child.name());
match single_boolean_child.build() {
BooleanBuild::RulePresent => quote! {
#child_name_ident = true
@ -113,13 +111,13 @@ fn make_match_action(child_spec: &ChildSpec) -> TokenStream {
},
}
}
SingleChildToBuild::Int(literal_child) => {
MemberChildToBuild::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) => {
MemberChildToBuild::Long(literal_child) => {
let child_ident = get_literal_child_ident(literal_child);
quote! {
let as_string = inner_pair.as_str();
@ -127,13 +125,13 @@ fn make_match_action(child_spec: &ChildSpec) -> TokenStream {
#child_ident = Some(without_el.parse().unwrap())
}
}
SingleChildToBuild::Double(literal_child) => {
MemberChildToBuild::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) => {
MemberChildToBuild::String(literal_child) => {
let child_ident = get_literal_child_ident(literal_child);
quote! {
#child_ident = Some(inner_pair.as_str().to_string())
@ -143,11 +141,11 @@ fn make_match_action(child_spec: &ChildSpec) -> TokenStream {
}
}
fn make_rule_matcher(child_spec: &ChildSpec) -> TokenStream {
fn make_rule_matcher(child_spec: &StructChildSpec) -> TokenStream {
let rule_ident = match child_spec {
ChildSpec::SkipChild(skip_child) => format_ident!("{}", skip_child.rule()),
ChildSpec::VecChild(vec_child) => format_ident!("{}", vec_child.rule()),
ChildSpec::SingleChild(single_child) => format_ident!("{}", single_child.rule()),
StructChildSpec::SkipChild(skip_child) => format_ident!("{}", skip_child.rule()),
StructChildSpec::VecChild(vec_child) => format_ident!("{}", vec_child.rule()),
StructChildSpec::MemberChild(single_child) => format_ident!("{}", single_child.rule()),
};
let action = make_match_action(child_spec);
@ -158,19 +156,19 @@ fn make_rule_matcher(child_spec: &ChildSpec) -> TokenStream {
}
}
fn make_child_arg(child_spec: &ChildSpec) -> Option<TokenStream> {
fn make_child_arg(child_spec: &StructChildSpec) -> Option<TokenStream> {
match child_spec {
ChildSpec::SkipChild(_) => None,
ChildSpec::VecChild(vec_child) => {
StructChildSpec::SkipChild(_) => None,
StructChildSpec::VecChild(vec_child) => {
let child_ident = match vec_child.build() {
VecChildToBuild::Type(vec_type_child) => {
VecChildToBuild::Node(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) => {
StructChildSpec::MemberChild(single_child) => match single_child.build() {
MemberChildToBuild::Node(single_type_child) => {
let child_ident = format_ident!("{}", single_type_child.var_name());
if single_type_child.optional() {
Some(quote! { #child_ident })
@ -184,23 +182,23 @@ fn make_child_arg(child_spec: &ChildSpec) -> Option<TokenStream> {
Some(quote! { #child_ident.unwrap() })
}
}
SingleChildToBuild::Boolean(single_boolean_child) => {
let child_ident = format_ident!("{}", single_boolean_child.var_name());
MemberChildToBuild::Boolean(single_boolean_child) => {
let child_ident = format_ident!("{}", single_boolean_child.name());
Some(quote! { #child_ident })
}
SingleChildToBuild::Int(literal_child) => {
MemberChildToBuild::Int(literal_child) => {
let child_ident = get_literal_child_ident(literal_child);
Some(quote! { #child_ident.unwrap() })
}
SingleChildToBuild::Long(literal_child) => {
MemberChildToBuild::Long(literal_child) => {
let child_ident = get_literal_child_ident(literal_child);
Some(quote! { #child_ident.unwrap() })
}
SingleChildToBuild::Double(literal_child) => {
MemberChildToBuild::Double(literal_child) => {
let child_ident = get_literal_child_ident(literal_child);
Some(quote! { #child_ident.unwrap() })
}
SingleChildToBuild::String(literal_child) => {
MemberChildToBuild::String(literal_child) => {
let child_ident = get_literal_child_ident(literal_child);
Some(quote! { #child_ident.unwrap() })
}
@ -269,14 +267,14 @@ pub fn make_struct_build_fn(build_spec: &StructBuildSpec) -> TokenStream {
#[cfg(test)]
mod tests {
use super::*;
use crate::spec::VecTypeChildToBuild;
use crate::spec::VecNodeChildToBuild;
#[test]
fn vec_child_holder() {
let vec_child = VecChild::new(
"test_child",
"Test",
VecChildToBuild::Type(VecTypeChildToBuild::new(
VecChildToBuild::Node(VecNodeChildToBuild::new(
"TestType",
"test_child",
"build_test_child",
@ -293,7 +291,7 @@ mod tests {
#[test]
fn single_type_child_holder() {
let single_type_child = SingleTypeChildToBuild::from_build_or_rule("TestType", None, false);
let single_type_child = NodeChildToBuild::from_build_or_rule("TestType", None, false);
assert_eq!(
make_single_type_child_holder(&single_type_child).to_string(),
quote! {
@ -306,7 +304,7 @@ mod tests {
#[test]
fn single_boolean_child_holder() {
let single_boolean_child =
SingleBooleanChildToBuild::new("test_child", BooleanBuild::RulePresent);
BooleanChildToBuild::new("test_child", BooleanBuild::RulePresent);
assert_eq!(
make_single_boolean_child_holder(&single_boolean_child).to_string(),
quote! {

View File

@ -1,7 +1,7 @@
use crate::spec::{
BuildSpec, ChildSpec, EnumBuildSpec, LeafEnumBuildSpec, LeafStructBuildSpec,
LeafStructChildType, SingleBooleanChildToBuild, SingleChildToBuild, SingleTypeChildToBuild,
StructBuildSpec, VecChild, VecChildToBuild,
BooleanChildToBuild, BuildSpec, EnumBuildSpec, LeafEnumBuildSpec, LeafStructBuildSpec,
LeafStructMemberKind, MemberChildToBuild, NodeChildToBuild, StructBuildSpec, StructChildSpec,
VecChild, VecChildToBuild,
};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
@ -61,7 +61,7 @@ fn handle_vec_child(
accessors: &mut Vec<TokenStream>,
) {
let (child_ident, child_ident_mut, child_type_ident) = match vec_child.build() {
VecChildToBuild::Type(vec_type_child) => (
VecChildToBuild::Node(vec_type_child) => (
format_ident!("{}", vec_type_child.var_name()),
format_ident!("{}_mut", vec_type_child.var_name()),
format_ident!("{}", vec_type_child.build()),
@ -84,7 +84,7 @@ fn handle_vec_child(
}
fn handle_single_type_child(
single_type_child: &SingleTypeChildToBuild,
single_type_child: &NodeChildToBuild,
member_names: &mut Vec<Ident>,
annotated_members: &mut Vec<TokenStream>,
accessors: &mut Vec<TokenStream>,
@ -134,12 +134,12 @@ fn handle_single_type_child(
}
fn handle_single_boolean_child(
single_boolean_child: &SingleBooleanChildToBuild,
single_boolean_child: &BooleanChildToBuild,
member_names: &mut Vec<Ident>,
annotated_members: &mut Vec<TokenStream>,
accessors: &mut Vec<TokenStream>,
) {
let child_ident = format_ident!("{}", single_boolean_child.var_name());
let child_ident = format_ident!("{}", single_boolean_child.name());
member_names.push(child_ident.clone());
annotated_members.push(quote! {
#child_ident: bool
@ -158,8 +158,8 @@ fn make_struct_type(build_spec: &StructBuildSpec) -> TokenStream {
for child_spec in build_spec.children().iter() {
match child_spec {
ChildSpec::SkipChild(_) => {}
ChildSpec::VecChild(vec_child) => {
StructChildSpec::SkipChild(_) => {}
StructChildSpec::VecChild(vec_child) => {
handle_vec_child(
vec_child,
&mut member_names,
@ -167,9 +167,9 @@ fn make_struct_type(build_spec: &StructBuildSpec) -> TokenStream {
&mut accessors,
);
}
ChildSpec::SingleChild(single_child) => {
StructChildSpec::MemberChild(single_child) => {
match single_child.build() {
SingleChildToBuild::Type(single_type_child) => {
MemberChildToBuild::Node(single_type_child) => {
handle_single_type_child(
single_type_child,
&mut member_names,
@ -177,7 +177,7 @@ fn make_struct_type(build_spec: &StructBuildSpec) -> TokenStream {
&mut accessors,
);
}
SingleChildToBuild::Boolean(single_boolean_child) => {
MemberChildToBuild::Boolean(single_boolean_child) => {
handle_single_boolean_child(
single_boolean_child,
&mut member_names,
@ -214,12 +214,12 @@ fn make_leaf_struct_type(build_spec: &LeafStructBuildSpec) -> TokenStream {
let type_ident = format_ident!("{}", build_spec.build());
let annotated_members = build_spec
.children()
.members()
.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"),
LeafStructMemberKind::String => format_ident!("{}", "String"),
};
quote! {
#name_ident: #type_ident
@ -228,18 +228,18 @@ fn make_leaf_struct_type(build_spec: &LeafStructBuildSpec) -> TokenStream {
.collect::<Vec<_>>();
let member_names = build_spec
.children()
.members()
.iter()
.map(|leaf_struct_child| format_ident!("{}", leaf_struct_child.name()))
.collect::<Vec<_>>();
let accessors = build_spec
.children()
.members()
.iter()
.map(|leaf_struct_child| {
let name_ident = format_ident!("{}", leaf_struct_child.name());
match leaf_struct_child.r#type() {
LeafStructChildType::String => {
LeafStructMemberKind::String => {
quote! {
pub fn #name_ident(&self) -> &str {
&self.#name_ident

View File

@ -132,6 +132,7 @@ $defs:
kind:
type: string
enum:
- node # default
- string
vec:
type: boolean
@ -142,9 +143,6 @@ $defs:
StructChildMemberDefinition:
type: object
additionalProperties: false
description: |
A definition for a child rule that builds one member. If a bare string, it is assumed to be the name/build-type
for a node. An object allows different types (i.e., things additional to nodes) to be built.
properties:
rule:
type: string
@ -195,15 +193,15 @@ $defs:
oneOf:
- type: string
description: Shorthand where child name, var, build, and with are inferred from the given Pascal-case rule name.
- $ref: "#/$defs/LongEnumChildDefinition"
LongEnumChildDefinition:
- $ref: "#/$defs/LongEnumChildWrapper"
LongEnumChildWrapper:
type: object
minProperties: 1
maxProperties: 1
additionalProperties: false
description: A format for an advanced enum child.
properties:
child:
type: boolean
kind:
patternProperties:
"^([A-Z][a-z]*)*$":
type: string
enum:
- int
- long
@ -211,12 +209,6 @@ $defs:
- usize
- string
- boolean
from:
enum:
- translate
required:
- kind
- from
# Production definition
ProductionDefinition:
@ -234,7 +226,6 @@ $defs:
- boolean
from:
enum:
- translate_and_parse
- string_inner
- whole_pair
- parse_whole_pair

View File

@ -753,35 +753,27 @@ Literal:
rules:
- IntLiteral:
kind: int
from: translate
- LongLiteral:
kind: long
from: translate
- DoubleLiteral:
kind: double
from: translate
- SingleQuoteString:
kind: string
from: translate
- DString
- BacktickString
- BooleanLiteral:
kind: boolean
from: translate
# Numbers
IntLiteral:
produce:
kind: int
from: translate_and_parse
LongLiteral:
produce:
kind: long
from: translate_and_parse
DoubleLiteral:
produce:
kind: double
from: translate_and_parse
# Strings
SingleQuoteString: