Compare commits
26 Commits
cde6d18e5c
...
2dd3bf5a06
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2dd3bf5a06 | ||
|
|
fc2912edd2 | ||
|
|
de8e2ba397 | ||
|
|
ac9ff6ecec | ||
|
|
608d89645e | ||
|
|
f3c3e40eb2 | ||
|
|
5d640ca585 | ||
|
|
b5cdb8dd29 | ||
|
|
2aee2cdd4e | ||
|
|
4c2ee8f929 | ||
|
|
e9ccb0a5bd | ||
|
|
799d8762cd | ||
|
|
44f6ab10af | ||
|
|
434df5642a | ||
|
|
42cc6720d1 | ||
|
|
0842690e6f | ||
|
|
c2c885d85b | ||
|
|
300e65a8d3 | ||
|
|
b75e51ee41 | ||
|
|
968b950436 | ||
|
|
0704e7d504 | ||
|
|
152f5a6150 | ||
|
|
e802fc70d8 | ||
|
|
c453557deb | ||
|
|
9c43e28f32 | ||
|
|
024baf2064 |
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -169,6 +169,17 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cst-test-generator"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"convert_case",
|
||||||
|
"prettyplease",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deimos"
|
name = "deimos"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -176,6 +187,7 @@ dependencies = [
|
|||||||
"ast-generator",
|
"ast-generator",
|
||||||
"clap",
|
"clap",
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
|
"cst-test-generator",
|
||||||
"indoc",
|
"indoc",
|
||||||
"log",
|
"log",
|
||||||
"pest",
|
"pest",
|
||||||
|
|||||||
@ -21,7 +21,8 @@ indoc = "2.0.6"
|
|||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
ast-generator = { path = "ast-generator" }
|
ast-generator = { path = "ast-generator" }
|
||||||
|
cst-test-generator = { path = "cst-test-generator" }
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["ast-generator"]
|
members = ["ast-generator", "cst-test-generator"]
|
||||||
|
|||||||
@ -1,246 +0,0 @@
|
|||||||
use crate::spec::{
|
|
||||||
BuildBooleanOn, ChildSpec, SingleBooleanChildToBuild, SingleChildToBuild,
|
|
||||||
SingleTypeChildToBuild, StructBuildSpec, VecChild, VecChildToBuild,
|
|
||||||
};
|
|
||||||
use convert_case::{Case, Casing};
|
|
||||||
use proc_macro2::TokenStream;
|
|
||||||
use quote::{format_ident, quote};
|
|
||||||
|
|
||||||
pub fn make_build_fn_name(s: &str) -> String {
|
|
||||||
format!("build_{}", s.to_case(Case::Snake))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_vec_child_holder(vec_child: &VecChild) -> TokenStream {
|
|
||||||
let (child_ident, child_type_ident) = match vec_child.build() {
|
|
||||||
VecChildToBuild::Type(vec_type_child) => (
|
|
||||||
format_ident!("{}", vec_type_child.var_name()),
|
|
||||||
format_ident!("{}", vec_type_child.build()),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
quote! {
|
|
||||||
let mut #child_ident: Vec<Box<#child_type_ident>> = vec![]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_single_type_child_holder(single_type_child: &SingleTypeChildToBuild) -> TokenStream {
|
|
||||||
let child_ident = format_ident!("{}", single_type_child.var_name());
|
|
||||||
let child_type_ident = format_ident!("{}", single_type_child.build());
|
|
||||||
quote! {
|
|
||||||
let mut #child_ident: Option<Box<#child_type_ident>> = None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_single_boolean_child_holder(
|
|
||||||
single_boolean_child: &SingleBooleanChildToBuild,
|
|
||||||
) -> TokenStream {
|
|
||||||
let child_ident = format_ident!("{}", single_boolean_child.var_name());
|
|
||||||
quote! {
|
|
||||||
let mut #child_ident: bool = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_child_holder(child_spec: &ChildSpec) -> 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) => {
|
|
||||||
Some(make_single_type_child_holder(single_type_child))
|
|
||||||
}
|
|
||||||
SingleChildToBuild::Boolean(boolean_child) => {
|
|
||||||
Some(make_single_boolean_child_holder(boolean_child))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_match_action(child_spec: &ChildSpec) -> TokenStream {
|
|
||||||
match child_spec {
|
|
||||||
ChildSpec::SkipChild(_) => quote! {},
|
|
||||||
ChildSpec::VecChild(vec_child) => {
|
|
||||||
let (child_name_ident, build_fn_ident) = match vec_child.build() {
|
|
||||||
VecChildToBuild::Type(vec_type_child) => (
|
|
||||||
format_ident!("{}", vec_type_child.var_name()),
|
|
||||||
format_ident!("{}", vec_type_child.with()),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
quote! {
|
|
||||||
#child_name_ident.push(Box::new(#build_fn_ident(inner_pair)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ChildSpec::SingleChild(single_child) => match single_child.build() {
|
|
||||||
SingleChildToBuild::Type(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());
|
|
||||||
match single_boolean_child.on() {
|
|
||||||
BuildBooleanOn::RulePresent => quote! {
|
|
||||||
#child_name_ident = true
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_rule_matcher(child_spec: &ChildSpec) -> 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()),
|
|
||||||
};
|
|
||||||
let action = make_match_action(child_spec);
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
Rule::#rule_ident => {
|
|
||||||
#action;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
let build_fn_ident = format_ident!("{}", build_spec.with());
|
|
||||||
let pair_ident = format_ident!("{}_pair", build_spec.build().to_case(Case::Snake));
|
|
||||||
let return_type_ident = format_ident!("{}", build_spec.build());
|
|
||||||
|
|
||||||
let child_holders = build_spec
|
|
||||||
.children()
|
|
||||||
.iter()
|
|
||||||
.map(|child_spec| make_child_holder(child_spec))
|
|
||||||
.filter(|child_holder| child_holder.is_some())
|
|
||||||
.map(|child_holder| child_holder.unwrap())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let rule_matchers = build_spec
|
|
||||||
.children()
|
|
||||||
.iter()
|
|
||||||
.map(|child_spec| make_rule_matcher(child_spec))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let iter_stream = quote! {
|
|
||||||
for inner_pair in #pair_ident.into_inner() {
|
|
||||||
match inner_pair.as_rule() {
|
|
||||||
#(#rule_matchers)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_stream = make_return_value_stream(build_spec);
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
fn #build_fn_ident(#pair_ident: Pair<Rule>) -> #return_type_ident {
|
|
||||||
#(#child_holders;)*
|
|
||||||
|
|
||||||
#iter_stream
|
|
||||||
|
|
||||||
#new_stream
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::spec::VecTypeChildToBuild;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn vec_child_holder() {
|
|
||||||
let vec_child = VecChild::new(
|
|
||||||
"test_child",
|
|
||||||
"Test",
|
|
||||||
VecChildToBuild::Type(VecTypeChildToBuild::new(
|
|
||||||
"TestType",
|
|
||||||
"test_child",
|
|
||||||
"build_test_child",
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
make_vec_child_holder(&vec_child).to_string(),
|
|
||||||
quote! {
|
|
||||||
let mut test_child: Vec<Box<TestType>> = vec![]
|
|
||||||
}
|
|
||||||
.to_string()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn single_type_child_holder() {
|
|
||||||
let single_type_child = SingleTypeChildToBuild::from_build_or_rule("TestType", None, false);
|
|
||||||
assert_eq!(
|
|
||||||
make_single_type_child_holder(&single_type_child).to_string(),
|
|
||||||
quote! {
|
|
||||||
let mut test_type: Option<Box<TestType>> = None
|
|
||||||
}
|
|
||||||
.to_string()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn single_boolean_child_holder() {
|
|
||||||
let single_boolean_child =
|
|
||||||
SingleBooleanChildToBuild::new("test_child", BuildBooleanOn::RulePresent);
|
|
||||||
assert_eq!(
|
|
||||||
make_single_boolean_child_holder(&single_boolean_child).to_string(),
|
|
||||||
quote! {
|
|
||||||
let mut test_child: bool = false
|
|
||||||
}
|
|
||||||
.to_string()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,188 +1,285 @@
|
|||||||
use crate::build_fn_gen::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 crate::spec::{
|
|
||||||
BuildBooleanOn, BuildSpec, ChildSpec, EnumBuildSpec, EnumRule, SingleBooleanChildToBuild,
|
|
||||||
SingleChild, SingleChildToBuild, 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};
|
||||||
|
|
||||||
fn get_skip(skip: &Yaml) -> bool {
|
fn get_as_bool(yaml: &Yaml) -> bool {
|
||||||
skip.as_bool().unwrap_or_else(|| false)
|
yaml.as_bool().unwrap_or_else(|| false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_vec(vec: &Yaml) -> bool {
|
fn unwrap_single_member_hash(hash: &Yaml) -> (String, &Yaml) {
|
||||||
vec.as_bool().unwrap_or_else(|| false)
|
let as_hash = hash.as_hash().unwrap();
|
||||||
|
if as_hash.is_empty() {
|
||||||
|
panic!("empty hash");
|
||||||
|
} else if as_hash.len() > 1 {
|
||||||
|
panic!("hash contains more than one key");
|
||||||
|
}
|
||||||
|
let (member_key, member_value) = as_hash.iter().collect::<Vec<(&Yaml, &Yaml)>>()[0];
|
||||||
|
let key_as_string = member_key.as_str().unwrap().to_string();
|
||||||
|
(key_as_string, member_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_vec_child_to_build(build: &Yaml, name: &str, rule: &str) -> VecChildToBuild {
|
fn deserialize_production_spec(rule: &str, production_yaml: &Yaml) -> ProductionBuildSpec {
|
||||||
if build.is_hash() {
|
let kind = match production_yaml["kind"].as_str().unwrap() {
|
||||||
let build_type = build["build"].as_str().unwrap();
|
"int" => ProductionKind::Int,
|
||||||
let var_name = build["var"]
|
"long" => ProductionKind::Long,
|
||||||
.as_str()
|
"double" => ProductionKind::Double,
|
||||||
.map(|s| s.to_string())
|
"boolean" => ProductionKind::Boolean,
|
||||||
.unwrap_or_else(|| build_type.to_case(Case::Snake));
|
"string" => {
|
||||||
let with = build["with"]
|
let from = match production_yaml["from"].as_str().unwrap() {
|
||||||
.as_str()
|
"string_inner" => ProductionStringFrom::StringInner,
|
||||||
.map(|s| s.to_string())
|
"whole_pair" => ProductionStringFrom::WholePair,
|
||||||
.unwrap_or_else(|| make_build_fn_name(build_type));
|
_ => panic!("invalid from: {}", production_yaml["from"].as_str().unwrap()),
|
||||||
|
};
|
||||||
|
ProductionKind::String(from)
|
||||||
|
},
|
||||||
|
_ => panic!("invalid kind: {}", production_yaml["kind"].as_str().unwrap()),
|
||||||
|
};
|
||||||
|
ProductionBuildSpec::new(rule, kind)
|
||||||
|
}
|
||||||
|
|
||||||
VecChildToBuild::Type(VecTypeChildToBuild::new(build_type, &var_name, &with))
|
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 deserialize_enum_rule_custom_child(rule_props: &Yaml) -> Option<Box<EnumRuleChild>> {
|
||||||
|
if !rule_props["child"].as_bool().unwrap_or(true) {
|
||||||
|
None
|
||||||
} else {
|
} else {
|
||||||
let build_as_str = build.as_str().unwrap_or(rule);
|
let kind = match rule_props["kind"].as_str().unwrap() {
|
||||||
VecChildToBuild::Type(VecTypeChildToBuild::new(
|
"int" => EnumRuleChildKind::Int,
|
||||||
build_as_str,
|
"long" => EnumRuleChildKind::Long,
|
||||||
name,
|
"double" => EnumRuleChildKind::Double,
|
||||||
make_build_fn_name(build_as_str).as_str(),
|
"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 get_vec_child(name: &str, rule: &str, build: &Yaml) -> ChildSpec {
|
fn deserialize_enum_rules(rules_yaml: &Yaml) -> Vec<Box<EnumRule>> {
|
||||||
ChildSpec::VecChild(VecChild::new(
|
rules_yaml
|
||||||
name,
|
.as_vec()
|
||||||
rule,
|
.unwrap()
|
||||||
get_vec_child_to_build(build, name, rule),
|
.iter()
|
||||||
))
|
.map(|rule_yaml| deserialize_enum_rule(rule_yaml))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_single_child_to_build(name: &str, rule: &str, optional: bool, build: &Yaml) -> SingleChildToBuild {
|
fn deserialize_leaf_struct_member(name: &str, props: &Yaml) -> Box<LeafStructMember> {
|
||||||
if build.is_hash() {
|
let kind = props["kind"].as_str().unwrap();
|
||||||
match build["type"].as_str() {
|
if kind == "string" {
|
||||||
Some(r#type) => {
|
Box::new(LeafStructMember::new(name, LeafStructMemberKind::String))
|
||||||
let var_name = build["var"]
|
} else {
|
||||||
.as_str()
|
panic!("invalid member kind: {}", kind);
|
||||||
.map(|s| s.to_string())
|
}
|
||||||
.unwrap_or(name.to_string());
|
}
|
||||||
let on = build["on"].as_str().unwrap();
|
|
||||||
if r#type.eq("boolean") && on.eq("rule_present") {
|
fn deserialize_leaf_struct_members(members_yaml: &Yaml) -> Vec<Box<LeafStructMember>> {
|
||||||
SingleChildToBuild::Boolean(SingleBooleanChildToBuild::new(
|
members_yaml
|
||||||
&var_name,
|
.as_vec()
|
||||||
BuildBooleanOn::RulePresent,
|
.unwrap()
|
||||||
))
|
.iter()
|
||||||
} else {
|
.map(|member| {
|
||||||
todo!("currently on boolean types with on: rule_present are supported")
|
let (member_name, member_props) = unwrap_single_member_hash(member);
|
||||||
}
|
deserialize_leaf_struct_member(&member_name, member_props)
|
||||||
},
|
})
|
||||||
None => {
|
.collect()
|
||||||
let or_else = build["or_else"]
|
}
|
||||||
.as_str()
|
|
||||||
.map(|s| s.to_string())
|
fn deserialize_member_node_to_build(
|
||||||
.or_else(|| {
|
rule: &str,
|
||||||
let or_else_default = build["or_else_default"]
|
build_props: &Yaml,
|
||||||
.as_bool()
|
optional: bool,
|
||||||
.unwrap_or_else(|| false);
|
) -> Box<MemberChildToBuild> {
|
||||||
if or_else_default {
|
let or_else = build_props["or_else"]
|
||||||
Some(String::from("default"))
|
.as_str()
|
||||||
} else {
|
.or_else(|| {
|
||||||
None
|
if get_as_bool(&build_props["or_else_default"]) {
|
||||||
}
|
Some("default")
|
||||||
});
|
} else {
|
||||||
SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(
|
None
|
||||||
rule,
|
|
||||||
or_else,
|
|
||||||
optional,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.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 {
|
} else {
|
||||||
match build.as_str() {
|
let optional = get_as_bool(&props["optional"]);
|
||||||
Some(s) => SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(s, None, optional)),
|
Box::new(MemberChildToBuild::Node(NodeChildToBuild::new(rule, None, optional)))
|
||||||
None => SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(rule, None, optional)),
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 get_single_child(name: &str, rule: &str, optional: bool, build: &Yaml) -> ChildSpec {
|
fn deserialize_struct_string_child(child: &Yaml) -> Box<StructChildSpec> {
|
||||||
ChildSpec::SingleChild(SingleChild::new(
|
let child_as_str = child.as_str().unwrap();
|
||||||
name,
|
let child_name_pascal = child_as_str.to_case(Case::Pascal);
|
||||||
rule,
|
Box::new(StructChildSpec::MemberChild(MemberChild::new(
|
||||||
get_single_child_to_build(name, rule, optional, build),
|
child_as_str,
|
||||||
))
|
&child_name_pascal,
|
||||||
|
Box::new(MemberChildToBuild::Node(NodeChildToBuild::new(
|
||||||
|
&child_name_pascal,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
))),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_child_specs(children: &Yaml) -> Vec<ChildSpec> {
|
fn deserialize_struct_children(children: &Yaml) -> Vec<Box<StructChildSpec>> {
|
||||||
children
|
children
|
||||||
.as_vec()
|
.as_vec()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|child_spec| {
|
.map(|child_spec| {
|
||||||
if child_spec.is_hash() {
|
if child_spec.is_hash() {
|
||||||
let as_hash = child_spec.as_hash().unwrap();
|
deserialize_struct_hash_child(child_spec)
|
||||||
let (name, props) = as_hash
|
|
||||||
.iter()
|
|
||||||
.next()
|
|
||||||
.map(|(name, props)| (name.as_str().unwrap(), props))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let rule = props["rule"]
|
|
||||||
.as_str()
|
|
||||||
.map(|s| s.to_string())
|
|
||||||
.unwrap_or(name.to_case(Case::Pascal));
|
|
||||||
|
|
||||||
if get_skip(&props["skip"]) {
|
|
||||||
return ChildSpec::SkipChild(SkipChild::new(name, &rule));
|
|
||||||
}
|
|
||||||
|
|
||||||
let build = &props["build"];
|
|
||||||
|
|
||||||
if get_vec(&props["vec"]) {
|
|
||||||
get_vec_child(name, &rule, build)
|
|
||||||
} else {
|
|
||||||
let optional = props["optional"]
|
|
||||||
.as_bool()
|
|
||||||
.unwrap_or_else(|| false);
|
|
||||||
|
|
||||||
get_single_child(name, &rule, optional, build)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
ChildSpec::SingleChild(SingleChild::from_name_snake(child_spec.as_str().unwrap()))
|
deserialize_struct_string_child(child_spec)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_enum_rule_specs(rule_specs: &Yaml) -> Vec<EnumRule> {
|
fn deserialize_build_spec(build_spec_name: &str, build_spec: &Yaml) -> BuildSpec {
|
||||||
rule_specs
|
if build_spec["children"].is_array() {
|
||||||
.as_vec()
|
let children = deserialize_struct_children(&build_spec["children"]);
|
||||||
.unwrap()
|
BuildSpec::Struct(StructBuildSpec::new(build_spec_name, children))
|
||||||
.iter()
|
} else if build_spec["members"].is_array() {
|
||||||
.map(|rule_spec| {
|
let members = deserialize_leaf_struct_members(&build_spec["members"]);
|
||||||
if rule_spec.is_hash() {
|
BuildSpec::LeafStruct(LeafStructBuildSpec::new(build_spec_name, members))
|
||||||
let rule = rule_spec["rule"].as_str().unwrap();
|
} else if build_spec["rules"].is_array() {
|
||||||
let build = rule_spec["build"].as_str().unwrap();
|
let rules = deserialize_enum_rules(&build_spec["rules"]);
|
||||||
let with = if !rule_spec["with"].is_badvalue() {
|
BuildSpec::Enum(EnumBuildSpec::new(build_spec_name, rules))
|
||||||
rule_spec.as_str().unwrap().to_string()
|
} else if build_spec["leaf_rules"].is_array() {
|
||||||
} else {
|
let leaf_rules = deserialize_leaf_enum_rules(&build_spec["leaf_rules"]);
|
||||||
make_build_fn_name(build)
|
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"]))
|
||||||
EnumRule::new(rule, build, &with)
|
|
||||||
} else {
|
|
||||||
EnumRule::from_rule(rule_spec.as_str().unwrap())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_build_spec(build_spec_name: &Yaml, build_spec: &Yaml) -> BuildSpec {
|
|
||||||
let build_spec_name_pascal = build_spec_name.as_str().unwrap();
|
|
||||||
let children = &build_spec["children"];
|
|
||||||
|
|
||||||
if children.is_array() {
|
|
||||||
let child_specs = get_child_specs(children);
|
|
||||||
BuildSpec::Struct(StructBuildSpec::from_name(
|
|
||||||
build_spec_name_pascal,
|
|
||||||
child_specs,
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
let rule_specs = &build_spec["rules"];
|
panic!("Expected a node spec for either a struct, leaf_struct, enum, leaf_enum node type, or a production type.");
|
||||||
if rule_specs.is_array() {
|
|
||||||
let enum_rules = get_enum_rule_specs(rule_specs);
|
|
||||||
BuildSpec::Enum(EnumBuildSpec::from_name(build_spec_name_pascal, enum_rules))
|
|
||||||
} else {
|
|
||||||
panic!("either children or rules must be present on the build spec");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,6 +290,9 @@ pub fn deserialize_yaml_spec(yaml: &str) -> Vec<BuildSpec> {
|
|||||||
doc.as_hash()
|
doc.as_hash()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.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()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|||||||
48
ast-generator/src/enum_build_fn.rs
Normal file
48
ast-generator/src/enum_build_fn.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use crate::spec::{EnumBuildSpec, EnumRuleChildKind};
|
||||||
|
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 {
|
||||||
|
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()
|
||||||
|
.map(|enum_rule| {
|
||||||
|
let rule_ident = format_ident!("{}", enum_rule.rule());
|
||||||
|
if let Some(child) = enum_rule.child() {
|
||||||
|
let inner_builder = match child.kind() {
|
||||||
|
EnumRuleChildKind::Node(node_child) => {
|
||||||
|
let inner_build_fn_ident =
|
||||||
|
format_ident!("{}", make_build_fn_name(node_child.build()));
|
||||||
|
quote! { #inner_build_fn_ident(inner_pair) }
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let inner_build_fn_ident =
|
||||||
|
format_ident!("{}", make_build_fn_name(enum_rule.rule()));
|
||||||
|
quote! { #inner_build_fn_ident(inner_pair) }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
quote! {
|
||||||
|
Rule::#rule_ident => #return_type_ident::#rule_ident(#inner_builder)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
Rule::#rule_ident => #return_type_ident::#rule_ident
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
fn #build_fn_ident(#pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||||
|
let inner_pair = #pair_ident.into_inner().next().unwrap();
|
||||||
|
match inner_pair.as_rule() {
|
||||||
|
#(#rule_branches,)*
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
ast-generator/src/leaf_enum_build_fn.rs
Normal file
29
ast-generator/src/leaf_enum_build_fn.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
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()
|
||||||
|
.map(|leaf_enum_rule| {
|
||||||
|
let rule_ident = format_ident!("{}", leaf_enum_rule.rule());
|
||||||
|
quote! {
|
||||||
|
Rule::#rule_ident => #return_type_ident::#rule_ident
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
fn #build_fn_ident(#pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||||
|
let inner_pair = #pair_ident.into_inner().next().unwrap();
|
||||||
|
match inner_pair.as_rule() {
|
||||||
|
#(#rule_branches,)*
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
ast-generator/src/leaf_struct_build_fn.rs
Normal file
38
ast-generator/src/leaf_struct_build_fn.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use crate::spec::{LeafStructBuildSpec, LeafStructMemberKind};
|
||||||
|
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
|
||||||
|
.members()
|
||||||
|
.map(|member| {
|
||||||
|
let child_ident = format_ident!("{}", member.name());
|
||||||
|
match member.kind() {
|
||||||
|
LeafStructMemberKind::String => {
|
||||||
|
quote! {
|
||||||
|
let #child_ident = #pair_ident.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let child_args = build_spec
|
||||||
|
.members()
|
||||||
|
.map(|member| format_ident!("{}", member.name()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
fn #build_fn_ident(#pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||||
|
#(#child_builders;)*
|
||||||
|
#return_type_ident::new(
|
||||||
|
#(#child_args,)*
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,24 +1,44 @@
|
|||||||
mod build_fn_gen;
|
pub mod deserialize;
|
||||||
mod deserialize;
|
mod enum_build_fn;
|
||||||
|
mod leaf_enum_build_fn;
|
||||||
|
mod leaf_struct_build_fn;
|
||||||
|
mod production_build_fn;
|
||||||
mod spec;
|
mod spec;
|
||||||
|
mod struct_build_fn;
|
||||||
mod type_gen;
|
mod type_gen;
|
||||||
|
mod util;
|
||||||
|
|
||||||
use crate::build_fn_gen::make_struct_build_fn;
|
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 crate::type_gen::make_type;
|
||||||
use proc_macro2::TokenStream;
|
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 ***");
|
||||||
match build_spec {
|
match build_spec {
|
||||||
BuildSpec::Enum(enum_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!("Leaf Enum Spec - build: {}", leaf_enum_build_spec.build());
|
||||||
}
|
}
|
||||||
BuildSpec::Struct(struct_build_spec) => {
|
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!(
|
||||||
|
"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);
|
println!("{:#?}", token_stream);
|
||||||
@ -26,30 +46,74 @@ fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) {
|
|||||||
println!("{}", prettyplease::unparse(&parsed));
|
println!("{}", prettyplease::unparse(&parsed));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_dump() -> String {
|
pub struct AstGeneratedFile {
|
||||||
let build_specs = deserialize::deserialize_yaml_spec(include_str!("../../src/parser/ast.yaml"));
|
pub name: String,
|
||||||
let mut streams: Vec<TokenStream> = vec![];
|
pub contents: String,
|
||||||
|
}
|
||||||
|
|
||||||
for build_spec in &build_specs {
|
fn token_stream_to_string(token_stream: TokenStream) -> String {
|
||||||
let type_stream = make_type(build_spec);
|
let file: File = syn::parse2(token_stream).unwrap();
|
||||||
debug_built_spec(build_spec, &type_stream);
|
|
||||||
streams.push(type_stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
for build_spec in &build_specs {
|
|
||||||
match build_spec {
|
|
||||||
BuildSpec::Enum(_) => {}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let combined = quote! {
|
|
||||||
#(#streams)*
|
|
||||||
};
|
|
||||||
let file: File = syn::parse2(combined).unwrap();
|
|
||||||
prettyplease::unparse(&file)
|
prettyplease::unparse(&file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_build_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
|
||||||
|
let build_fns = build_specs
|
||||||
|
.iter()
|
||||||
|
.map(|build_spec| match build_spec {
|
||||||
|
BuildSpec::Enum(enum_build_spec) => {
|
||||||
|
let stream = make_enum_build_fn(enum_build_spec);
|
||||||
|
debug_built_spec(build_spec, &stream);
|
||||||
|
stream
|
||||||
|
}
|
||||||
|
BuildSpec::LeafEnum(leaf_enum_build_spec) => {
|
||||||
|
let stream = make_leaf_enum_build_fn(leaf_enum_build_spec);
|
||||||
|
debug_built_spec(build_spec, &stream);
|
||||||
|
stream
|
||||||
|
}
|
||||||
|
BuildSpec::Struct(struct_build_spec) => {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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! {
|
||||||
|
#(#build_fns)*
|
||||||
|
};
|
||||||
|
AstGeneratedFile {
|
||||||
|
name: String::from("build.rs"),
|
||||||
|
contents: token_stream_to_string(combined),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_node_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
|
||||||
|
let types = build_specs
|
||||||
|
.iter()
|
||||||
|
.map(|build_spec| make_type(build_spec))
|
||||||
|
.filter(Option::is_some)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let combined = quote! {
|
||||||
|
#(#types)*
|
||||||
|
};
|
||||||
|
AstGeneratedFile {
|
||||||
|
name: String::from("node.rs"),
|
||||||
|
contents: token_stream_to_string(combined),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_files(build_specs: &[BuildSpec]) -> Vec<AstGeneratedFile> {
|
||||||
|
vec![
|
||||||
|
generate_build_file(build_specs),
|
||||||
|
generate_node_file(build_specs),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +0,0 @@
|
|||||||
use ast_generator::test_dump;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let s = test_dump();
|
|
||||||
println!("{}", s);
|
|
||||||
}
|
|
||||||
87
ast-generator/src/production_build_fn.rs
Normal file
87
ast-generator/src/production_build_fn.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
use crate::spec::{ProductionBuildSpec, ProductionKind, ProductionStringFrom};
|
||||||
|
use crate::util::{make_build_fn_name, make_build_pair};
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
|
||||||
|
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.into_inner()
|
||||||
|
.next()
|
||||||
|
.unwrap();
|
||||||
|
let inner_number_base_pair = number_base_pair.into_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.into_inner()
|
||||||
|
.next()
|
||||||
|
.unwrap();
|
||||||
|
let inner_number_base_pair = number_base_pair.into_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.into_inner()
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ProductionStringFrom::WholePair => {
|
||||||
|
quote! {
|
||||||
|
#pair_ident.as_str().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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,62 +1,47 @@
|
|||||||
use crate::build_fn_gen::make_build_fn_name;
|
|
||||||
use convert_case::{Case, Casing};
|
|
||||||
|
|
||||||
pub enum BuildSpec {
|
pub enum BuildSpec {
|
||||||
Enum(EnumBuildSpec),
|
Enum(EnumBuildSpec),
|
||||||
|
LeafEnum(LeafEnumBuildSpec),
|
||||||
Struct(StructBuildSpec),
|
Struct(StructBuildSpec),
|
||||||
|
LeafStruct(LeafStructBuildSpec),
|
||||||
|
Production(ProductionBuildSpec),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enum build spec
|
||||||
|
|
||||||
pub struct EnumBuildSpec {
|
pub struct EnumBuildSpec {
|
||||||
name: String,
|
|
||||||
build: String,
|
build: String,
|
||||||
rules: Vec<EnumRule>,
|
rules: Vec<Box<EnumRule>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnumBuildSpec {
|
impl EnumBuildSpec {
|
||||||
pub fn from_name(name: &str, rules: Vec<EnumRule>) -> Self {
|
pub fn new(build: &str, rules: Vec<Box<EnumRule>>) -> Self {
|
||||||
EnumBuildSpec {
|
EnumBuildSpec {
|
||||||
name: name.to_string(),
|
build: build.to_string(),
|
||||||
build: name.to_string(),
|
|
||||||
rules,
|
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.
|
/// The enum type to be built, in Pascal case.
|
||||||
pub fn build(&self) -> &str {
|
pub fn build(&self) -> &str {
|
||||||
&self.build
|
&self.build
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The individual rule specs.
|
/// The individual rule specs.
|
||||||
pub fn rules(&self) -> &[EnumRule] {
|
pub fn rules(&self) -> impl Iterator<Item = &EnumRule> {
|
||||||
&self.rules
|
self.rules.iter().map(Box::as_ref)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EnumRule {
|
pub struct EnumRule {
|
||||||
rule: String,
|
rule: String,
|
||||||
build: String,
|
child: Option<Box<EnumRuleChild>>
|
||||||
with: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnumRule {
|
impl EnumRule {
|
||||||
pub fn from_rule(rule: &str) -> Self {
|
pub fn new(rule: &str, child: Option<Box<EnumRuleChild>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
rule: rule.to_string(),
|
rule: rule.to_string(),
|
||||||
build: rule.to_string(),
|
child
|
||||||
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(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,67 +49,126 @@ impl EnumRule {
|
|||||||
pub fn rule(&self) -> &str {
|
pub fn rule(&self) -> &str {
|
||||||
&self.rule
|
&self.rule
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leaf enum build spec
|
||||||
|
|
||||||
|
pub struct LeafEnumBuildSpec {
|
||||||
|
build: String,
|
||||||
|
rules: Vec<Box<LeafEnumRule>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LeafEnumBuildSpec {
|
||||||
|
pub fn new(build: &str, rules: Vec<Box<LeafEnumRule>>) -> Self {
|
||||||
|
Self {
|
||||||
|
build: build.to_string(),
|
||||||
|
rules
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The type to build, in Pascal case.
|
|
||||||
pub fn build(&self) -> &str {
|
pub fn build(&self) -> &str {
|
||||||
&self.build
|
&self.build
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The build-fn name, in snake case.
|
pub fn rules(&self) -> impl Iterator<Item = &LeafEnumRule> {
|
||||||
pub fn with(&self) -> &str {
|
self.rules.iter().map(Box::as_ref)
|
||||||
&self.with
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StructBuildSpec {
|
pub struct LeafEnumRule {
|
||||||
name: String,
|
rule: String,
|
||||||
build: String,
|
|
||||||
var_name: String,
|
|
||||||
with: String,
|
|
||||||
children: Vec<ChildSpec>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructBuildSpec {
|
impl LeafEnumRule {
|
||||||
pub fn from_name(name: &str, child_specs: Vec<ChildSpec>) -> Self {
|
pub fn new(rule: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: name.to_string(),
|
rule: rule.to_string(),
|
||||||
build: name.to_string(),
|
|
||||||
var_name: name.to_case(Case::Snake),
|
|
||||||
with: make_build_fn_name(name),
|
|
||||||
children: child_specs,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The top-level name of this build spec.
|
pub fn rule(&self) -> &str {
|
||||||
pub fn name(&self) -> &str {
|
&self.rule
|
||||||
&self.name
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Struct build spec
|
||||||
|
|
||||||
|
pub struct StructBuildSpec {
|
||||||
|
build: String,
|
||||||
|
children: Vec<Box<StructChildSpec>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructBuildSpec {
|
||||||
|
pub fn new(build: &str, children: Vec<Box<StructChildSpec>>) -> Self {
|
||||||
|
Self {
|
||||||
|
build: build.to_string(),
|
||||||
|
children
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The type to be built, in Pascal case.
|
/// The type to be built, in Pascal case.
|
||||||
pub fn build(&self) -> &str {
|
pub fn build(&self) -> &str {
|
||||||
&self.build
|
&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.
|
/// The children for this build spec.
|
||||||
pub fn children(&self) -> &[ChildSpec] {
|
pub fn children(&self) -> impl Iterator<Item = &StructChildSpec> {
|
||||||
&self.children
|
self.children.iter().map(Box::as_ref)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ChildSpec {
|
pub enum StructChildSpec {
|
||||||
SkipChild(SkipChild),
|
SkipChild(SkipChild),
|
||||||
VecChild(VecChild),
|
VecChild(VecChild),
|
||||||
SingleChild(SingleChild),
|
MemberChild(MemberChild),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SkipChild {
|
pub struct SkipChild {
|
||||||
@ -154,11 +198,11 @@ impl SkipChild {
|
|||||||
pub struct VecChild {
|
pub struct VecChild {
|
||||||
name: String,
|
name: String,
|
||||||
rule: String,
|
rule: String,
|
||||||
build: VecChildToBuild,
|
build: Box<VecChildToBuild>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VecChild {
|
impl VecChild {
|
||||||
pub fn new(name: &str, rule: &str, build: VecChildToBuild) -> Self {
|
pub fn new(name: &str, rule: &str, build: Box<VecChildToBuild>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
rule: rule.to_string(),
|
rule: rule.to_string(),
|
||||||
@ -184,62 +228,35 @@ impl VecChild {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum VecChildToBuild {
|
pub enum VecChildToBuild {
|
||||||
Type(VecTypeChildToBuild),
|
Node(VecNodeChildToBuild),
|
||||||
|
String
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VecTypeChildToBuild {
|
pub struct VecNodeChildToBuild {
|
||||||
build: String,
|
build: String,
|
||||||
var_name: String,
|
|
||||||
with: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VecTypeChildToBuild {
|
impl VecNodeChildToBuild {
|
||||||
pub fn new(build: &str, var_name: &str, with: &str) -> Self {
|
pub fn new(build: &str) -> Self {
|
||||||
Self {
|
Self { build: build.to_string() }
|
||||||
build: build.to_string(),
|
|
||||||
var_name: var_name.to_string(),
|
|
||||||
with: with.to_string(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The type to build, in Pascal case.
|
/// The type to build, in Pascal case.
|
||||||
pub fn build(&self) -> &str {
|
pub fn build(&self) -> &str {
|
||||||
&self.build
|
&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)]
|
#[derive(Debug)]
|
||||||
pub struct SingleChild {
|
pub struct MemberChild {
|
||||||
name: String,
|
name: String,
|
||||||
rule: String,
|
rule: String,
|
||||||
build: SingleChildToBuild,
|
build: Box<MemberChildToBuild>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SingleChild {
|
impl MemberChild {
|
||||||
pub fn from_name_snake(name: &str) -> Self {
|
pub fn new(name: &str, rule: &str, build: Box<MemberChildToBuild>) -> 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 {
|
|
||||||
Self {
|
Self {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
rule: rule.to_string(),
|
rule: rule.to_string(),
|
||||||
@ -258,36 +275,32 @@ impl SingleChild {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The specification for what to actually build.
|
/// The specification for what to actually build.
|
||||||
pub fn build(&self) -> &SingleChildToBuild {
|
pub fn build(&self) -> &MemberChildToBuild {
|
||||||
&self.build
|
&self.build
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SingleChildToBuild {
|
pub enum MemberChildToBuild {
|
||||||
Type(SingleTypeChildToBuild),
|
Node(NodeChildToBuild),
|
||||||
Boolean(SingleBooleanChildToBuild),
|
Boolean(BooleanChildToBuild),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SingleTypeChildToBuild {
|
pub struct NodeChildToBuild {
|
||||||
build: String,
|
build: String,
|
||||||
var_name: String,
|
|
||||||
with: String,
|
|
||||||
or_else: Option<String>,
|
or_else: Option<String>,
|
||||||
optional: bool,
|
optional: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SingleTypeChildToBuild {
|
impl NodeChildToBuild {
|
||||||
pub fn from_build_or_rule(
|
pub fn new(
|
||||||
build_or_rule: &str,
|
build: &str,
|
||||||
or_else: Option<String>,
|
or_else: Option<String>,
|
||||||
optional: bool,
|
optional: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
build: build_or_rule.to_string(),
|
build: build.to_string(),
|
||||||
var_name: build_or_rule.to_case(Case::Snake),
|
|
||||||
with: make_build_fn_name(build_or_rule),
|
|
||||||
or_else,
|
or_else,
|
||||||
optional,
|
optional,
|
||||||
}
|
}
|
||||||
@ -298,16 +311,6 @@ impl SingleTypeChildToBuild {
|
|||||||
&self.build
|
&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).
|
/// The default fn to call when unwrapping the child (before passing as arg to new).
|
||||||
pub fn or_else(&self) -> Option<&str> {
|
pub fn or_else(&self) -> Option<&str> {
|
||||||
self.or_else.as_deref()
|
self.or_else.as_deref()
|
||||||
@ -320,29 +323,103 @@ impl SingleTypeChildToBuild {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SingleBooleanChildToBuild {
|
pub struct BooleanChildToBuild {
|
||||||
var_name: String,
|
name: String
|
||||||
on: BuildBooleanOn,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SingleBooleanChildToBuild {
|
impl BooleanChildToBuild {
|
||||||
pub fn new(var_name: &str, on: BuildBooleanOn) -> Self {
|
pub fn new(name: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
var_name: var_name.to_string(),
|
name: name.to_string()
|
||||||
on,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn var_name(&self) -> &str {
|
pub fn name(&self) -> &str {
|
||||||
&self.var_name
|
&self.name
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on(&self) -> &BuildBooleanOn {
|
|
||||||
&self.on
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
// Leaf Struct build spec
|
||||||
pub enum BuildBooleanOn {
|
|
||||||
RulePresent,
|
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 members(&self) -> impl Iterator<Item = &LeafStructMember> {
|
||||||
|
self.members.iter().map(Box::as_ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LeafStructMember {
|
||||||
|
name: String,
|
||||||
|
kind: LeafStructMemberKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LeafStructMember {
|
||||||
|
pub fn new(name: &str, kind: LeafStructMemberKind) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
kind,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn kind(&self) -> &LeafStructMemberKind {
|
||||||
|
&self.kind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
216
ast-generator/src/struct_build_fn.rs
Normal file
216
ast-generator/src/struct_build_fn.rs
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
use crate::spec::{BooleanChildToBuild, MemberChildToBuild, NodeChildToBuild, StructBuildSpec, StructChildSpec, VecChild, VecChildToBuild};
|
||||||
|
use crate::util::{make_build_fn_name, make_build_pair};
|
||||||
|
use convert_case::{Case, Casing};
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
|
||||||
|
fn make_var_name(s: &str) -> String {
|
||||||
|
s.to_case(Case::Snake)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_vec_child_holder(vec_child: &VecChild) -> TokenStream {
|
||||||
|
let child_ident = format_ident!("{}", vec_child.name());
|
||||||
|
match vec_child.build() {
|
||||||
|
VecChildToBuild::Node(node_child) => {
|
||||||
|
let child_type_ident = format_ident!("{}", node_child.build());
|
||||||
|
quote! {
|
||||||
|
let mut #child_ident: Vec<Box<#child_type_ident >> = vec![]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
VecChildToBuild::String => quote! {
|
||||||
|
let mut #child_ident: Vec<String> = vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_node_child_holder(name: &str, node_child: &NodeChildToBuild) -> TokenStream {
|
||||||
|
let child_ident = format_ident!("{}", name);
|
||||||
|
let child_type_ident = format_ident!("{}", node_child.build());
|
||||||
|
quote! {
|
||||||
|
let mut #child_ident: Option<Box<#child_type_ident>> = None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_boolean_child_holder(boolean_child: &BooleanChildToBuild) -> TokenStream {
|
||||||
|
let child_ident = format_ident!("{}", boolean_child.name());
|
||||||
|
quote! {
|
||||||
|
let mut #child_ident: bool = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_child_holder(child_spec: &StructChildSpec) -> Option<TokenStream> {
|
||||||
|
match child_spec {
|
||||||
|
StructChildSpec::SkipChild(_) => None,
|
||||||
|
StructChildSpec::VecChild(vec_child) => Some(make_vec_child_holder(vec_child)),
|
||||||
|
StructChildSpec::MemberChild(member_child) => match member_child.build() {
|
||||||
|
MemberChildToBuild::Node(node_child) => {
|
||||||
|
Some(make_node_child_holder(member_child.name(), node_child))
|
||||||
|
}
|
||||||
|
MemberChildToBuild::Boolean(boolean_child) => {
|
||||||
|
Some(make_boolean_child_holder(boolean_child))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_vec_child_match_action(vec_child: &VecChild) -> TokenStream {
|
||||||
|
let child_name_ident = format_ident!("{}", vec_child.name());
|
||||||
|
match vec_child.build() {
|
||||||
|
VecChildToBuild::Node(vec_node_child) => {
|
||||||
|
let build_fn_ident = format_ident!("{}", make_build_fn_name(vec_node_child.build()));
|
||||||
|
quote! {
|
||||||
|
#child_name_ident.push(Box::new(#build_fn_ident(inner_pair)))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
VecChildToBuild::String => {
|
||||||
|
let build_fn_ident = format_ident!("{}", make_build_fn_name(vec_child.rule()));
|
||||||
|
quote! {
|
||||||
|
#child_name_ident.push(#build_fn_ident(inner_pair))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_node_member_child_match_action(name: &str, node_child: &NodeChildToBuild) -> TokenStream {
|
||||||
|
let child_name_ident = format_ident!("{}", name);
|
||||||
|
let build_fn_ident = format_ident!("{}", make_build_fn_name(node_child.build()));
|
||||||
|
quote! {
|
||||||
|
#child_name_ident = Some(Box::new(#build_fn_ident(inner_pair)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_boolean_member_child_match_action(boolean_child: &BooleanChildToBuild) -> TokenStream {
|
||||||
|
let child_name_ident = format_ident!("{}", make_var_name(boolean_child.name()));
|
||||||
|
quote! {
|
||||||
|
#child_name_ident = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_match_action(child_spec: &StructChildSpec) -> TokenStream {
|
||||||
|
match child_spec {
|
||||||
|
StructChildSpec::SkipChild(_) => quote! {},
|
||||||
|
StructChildSpec::VecChild(vec_child) => {
|
||||||
|
make_vec_child_match_action(vec_child)
|
||||||
|
}
|
||||||
|
StructChildSpec::MemberChild(member_child) => match member_child.build() {
|
||||||
|
MemberChildToBuild::Node(node_child) => {
|
||||||
|
make_node_member_child_match_action(member_child.name(), node_child)
|
||||||
|
}
|
||||||
|
MemberChildToBuild::Boolean(boolean_child) => {
|
||||||
|
make_boolean_member_child_match_action(boolean_child)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_rule_matcher(child_spec: &StructChildSpec) -> TokenStream {
|
||||||
|
let rule_ident = match child_spec {
|
||||||
|
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);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
Rule::#rule_ident => {
|
||||||
|
#action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_vec_child_arg(vec_child: &VecChild) -> TokenStream {
|
||||||
|
let child_ident = format_ident!("{}", vec_child.name());
|
||||||
|
quote! { #child_ident }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_node_member_child_arg(name: &str, node_child: &NodeChildToBuild) -> TokenStream {
|
||||||
|
let child_ident = format_ident!("{}", name);
|
||||||
|
if node_child.optional() {
|
||||||
|
quote! { #child_ident }
|
||||||
|
} else if let Some(or_else) = node_child.or_else() {
|
||||||
|
let child_type_ident = format_ident!("{}", node_child.build());
|
||||||
|
let or_else_ident = format_ident!("{}", or_else);
|
||||||
|
quote! {
|
||||||
|
#child_ident.unwrap_or_else(|| Box::new(#child_type_ident::#or_else_ident()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! { #child_ident.unwrap() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_boolean_member_child_arg(boolean_child: &BooleanChildToBuild) -> TokenStream {
|
||||||
|
let child_ident = format_ident!("{}", boolean_child.name());
|
||||||
|
quote! { #child_ident }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_child_arg(child_spec: &StructChildSpec) -> Option<TokenStream> {
|
||||||
|
match child_spec {
|
||||||
|
StructChildSpec::SkipChild(_) => None,
|
||||||
|
StructChildSpec::VecChild(vec_child) => {
|
||||||
|
Some(make_vec_child_arg(vec_child))
|
||||||
|
}
|
||||||
|
StructChildSpec::MemberChild(member_child) => match member_child.build() {
|
||||||
|
MemberChildToBuild::Node(single_type_child) => {
|
||||||
|
Some(make_node_member_child_arg(member_child.name(), single_type_child))
|
||||||
|
}
|
||||||
|
MemberChildToBuild::Boolean(boolean_child) => {
|
||||||
|
Some(make_boolean_member_child_arg(boolean_child))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_return_value_stream(build_spec: &StructBuildSpec) -> TokenStream {
|
||||||
|
let type_ident = format_ident!("{}", build_spec.build());
|
||||||
|
let child_args = build_spec
|
||||||
|
.children()
|
||||||
|
.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 {
|
||||||
|
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_holders = build_spec
|
||||||
|
.children()
|
||||||
|
.map(|child_spec| make_child_holder(child_spec))
|
||||||
|
.filter(|child_holder| child_holder.is_some())
|
||||||
|
.map(|child_holder| child_holder.unwrap())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let rule_matchers = build_spec
|
||||||
|
.children()
|
||||||
|
.map(|child_spec| make_rule_matcher(child_spec))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let iter_stream = quote! {
|
||||||
|
for inner_pair in #pair_ident.into_inner() {
|
||||||
|
match inner_pair.as_rule() {
|
||||||
|
#(#rule_matchers)*
|
||||||
|
_ => panic!("Unexpected rule: {:?}", inner_pair.as_rule())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_stream = make_return_value_stream(build_spec);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
fn #build_fn_ident(#pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||||
|
#(#child_holders;)*
|
||||||
|
|
||||||
|
#iter_stream
|
||||||
|
|
||||||
|
#new_stream
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
use crate::spec::{
|
use crate::spec::{
|
||||||
BuildSpec, ChildSpec, EnumBuildSpec, SingleBooleanChildToBuild, SingleChildToBuild,
|
BooleanChildToBuild, BuildSpec, EnumBuildSpec, EnumRuleChildKind, LeafEnumBuildSpec,
|
||||||
SingleTypeChildToBuild, StructBuildSpec, VecChild, VecChildToBuild,
|
LeafStructBuildSpec, LeafStructMemberKind, MemberChildToBuild, NodeChildToBuild,
|
||||||
|
StructBuildSpec, StructChildSpec, VecChild, VecChildToBuild,
|
||||||
};
|
};
|
||||||
use proc_macro2::{Ident, TokenStream};
|
use proc_macro2::{Ident, TokenStream};
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
@ -8,12 +9,27 @@ use quote::{format_ident, quote};
|
|||||||
fn make_enum_type(build_spec: &EnumBuildSpec) -> TokenStream {
|
fn make_enum_type(build_spec: &EnumBuildSpec) -> TokenStream {
|
||||||
let children: Vec<TokenStream> = build_spec
|
let children: Vec<TokenStream> = build_spec
|
||||||
.rules()
|
.rules()
|
||||||
.iter()
|
.map(|enum_rule| {
|
||||||
.map(|rule| {
|
let member_name_ident = format_ident!("{}", enum_rule.rule());
|
||||||
let member_name_ident = format_ident!("{}", rule.rule());
|
if let Some(enum_rule_child) = enum_rule.child() {
|
||||||
let child_name_ident = format_ident!("{}", rule.build());
|
let child_type_ident = match enum_rule_child.kind() {
|
||||||
quote! {
|
EnumRuleChildKind::Node(node_child) => {
|
||||||
#member_name_ident(#child_name_ident)
|
format_ident!("{}", node_child.build())
|
||||||
|
}
|
||||||
|
EnumRuleChildKind::Int => format_ident!("i32"),
|
||||||
|
EnumRuleChildKind::Long => format_ident!("i64"),
|
||||||
|
EnumRuleChildKind::Double => format_ident!("f64"),
|
||||||
|
EnumRuleChildKind::USize => format_ident!("usize"),
|
||||||
|
EnumRuleChildKind::String => format_ident!("String"),
|
||||||
|
EnumRuleChildKind::Boolean => format_ident!("bool"),
|
||||||
|
};
|
||||||
|
quote! {
|
||||||
|
#member_name_ident(#child_type_ident)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
#member_name_ident
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@ -25,114 +41,200 @@ 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()
|
||||||
|
.map(|leaf_enum_rule| {
|
||||||
|
let rule_name_ident = format_ident!("{}", leaf_enum_rule.rule());
|
||||||
|
quote! {
|
||||||
|
#rule_name_ident
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
pub enum #type_name_ident {
|
||||||
|
#(#children),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_vec_child(
|
fn handle_vec_child(
|
||||||
vec_child: &VecChild,
|
vec_child: &VecChild,
|
||||||
member_names: &mut Vec<Ident>,
|
member_names: &mut Vec<Ident>,
|
||||||
annotated_members: &mut Vec<TokenStream>,
|
annotated_members: &mut Vec<TokenStream>,
|
||||||
|
member_args: &mut Vec<TokenStream>,
|
||||||
accessors: &mut Vec<TokenStream>,
|
accessors: &mut Vec<TokenStream>,
|
||||||
) {
|
) {
|
||||||
let (child_ident, child_ident_mut, child_type_ident) = match vec_child.build() {
|
let (child_ident, child_ident_mut) = (
|
||||||
VecChildToBuild::Type(vec_type_child) => (
|
format_ident!("{}", vec_child.name()),
|
||||||
format_ident!("{}", vec_type_child.var_name()),
|
format_ident!("{}_mut", vec_child.name()),
|
||||||
format_ident!("{}_mut", vec_type_child.var_name()),
|
);
|
||||||
format_ident!("{}", vec_type_child.build()),
|
let child_type_ident = match vec_child.build() {
|
||||||
),
|
VecChildToBuild::Node(vec_node_child) => format_ident!("{}", vec_node_child.build()),
|
||||||
|
VecChildToBuild::String => format_ident!("{}", "String"),
|
||||||
};
|
};
|
||||||
|
|
||||||
member_names.push(child_ident.clone());
|
member_names.push(child_ident.clone());
|
||||||
annotated_members.push(quote! {
|
|
||||||
#child_ident: Vec<Box<#child_type_ident>>
|
|
||||||
});
|
|
||||||
accessors.push(quote! {
|
|
||||||
pub fn #child_ident(&self) -> impl Iterator<Item = &#child_type_ident> {
|
|
||||||
self.#child_ident.iter().map(Box::as_ref)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn #child_ident_mut(&mut self) -> impl Iterator<Item = &mut #child_type_ident> {
|
match vec_child.build() {
|
||||||
self.#child_ident.iter_mut().map(Box::as_mut)
|
VecChildToBuild::Node(_) => {
|
||||||
|
annotated_members.push(quote! {
|
||||||
|
#child_ident: Vec<Box<#child_type_ident>>
|
||||||
|
});
|
||||||
|
member_args.push(quote! {
|
||||||
|
#child_ident: Vec<Box<#child_type_ident>>
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
VecChildToBuild::String => {
|
||||||
|
annotated_members.push(quote! {
|
||||||
|
#child_ident: Vec<String>
|
||||||
|
});
|
||||||
|
member_args.push(quote! {
|
||||||
|
#child_ident: Vec<String>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match vec_child.build() {
|
||||||
|
VecChildToBuild::Node(_) => {
|
||||||
|
accessors.push(quote! {
|
||||||
|
pub fn #child_ident(&self) -> impl Iterator<Item = &#child_type_ident> {
|
||||||
|
self.#child_ident.iter().map(Box::as_ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn #child_ident_mut(&mut self) -> impl Iterator<Item = &mut #child_type_ident> {
|
||||||
|
self.#child_ident.iter_mut().map(Box::as_mut)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
VecChildToBuild::String => accessors.push(quote! {
|
||||||
|
pub fn #child_ident(&self) -> impl Iterator<Item = &str> {
|
||||||
|
self.#child_ident.iter().map(String::as_str)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_single_type_child(
|
fn handle_node_child(
|
||||||
single_type_child: &SingleTypeChildToBuild,
|
name: &str,
|
||||||
|
node_child: &NodeChildToBuild,
|
||||||
member_names: &mut Vec<Ident>,
|
member_names: &mut Vec<Ident>,
|
||||||
annotated_members: &mut Vec<TokenStream>,
|
annotated_members: &mut Vec<TokenStream>,
|
||||||
|
member_args: &mut Vec<TokenStream>,
|
||||||
accessors: &mut Vec<TokenStream>,
|
accessors: &mut Vec<TokenStream>,
|
||||||
) {
|
) {
|
||||||
let child_ident = format_ident!("{}", single_type_child.var_name());
|
let child_ident = format_ident!("{}", name);
|
||||||
let child_ident_mut = format_ident!("{}_mut", single_type_child.var_name());
|
let child_ident_mut = format_ident!("{}_mut", name);
|
||||||
let child_type_ident = format_ident!("{}", single_type_child.build());
|
let child_type_ident = format_ident!("{}", node_child.build());
|
||||||
|
|
||||||
member_names.push(child_ident.clone());
|
member_names.push(child_ident.clone());
|
||||||
if single_type_child.optional() {
|
|
||||||
|
if node_child.optional() {
|
||||||
annotated_members.push(quote! {
|
annotated_members.push(quote! {
|
||||||
#child_ident: Option<Box<#child_type_ident>>
|
#child_ident: Option<Box<#child_type_ident>>
|
||||||
});
|
});
|
||||||
|
member_args.push(quote! {
|
||||||
|
#child_ident: Option<Box<#child_type_ident>>
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
annotated_members.push(quote! {
|
annotated_members.push(quote! {
|
||||||
#child_ident: Box<#child_type_ident>
|
#child_ident: Box<#child_type_ident>
|
||||||
|
});
|
||||||
|
member_args.push(quote! {
|
||||||
|
#child_ident: Box<#child_type_ident>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
accessors.push(quote! {
|
if node_child.optional() {
|
||||||
pub fn #child_ident(&self) -> &#child_type_ident {
|
accessors.push(quote! {
|
||||||
self.#child_ident.as_ref()
|
pub fn #child_ident(&self) -> Option<&#child_type_ident> {
|
||||||
}
|
if let Some(#child_ident) = &self.#child_ident {
|
||||||
|
Some(#child_ident.as_ref())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn #child_ident_mut(&mut self) -> &mut #child_type_ident {
|
pub fn #child_ident_mut(&mut self) -> Option<&mut #child_type_ident> {
|
||||||
self.#child_ident.as_mut()
|
if let Some(#child_ident) = &mut self.#child_ident {
|
||||||
}
|
Some(#child_ident.as_mut())
|
||||||
});
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
accessors.push(quote! {
|
||||||
|
pub fn #child_ident(&self) -> &#child_type_ident {
|
||||||
|
self.#child_ident.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn #child_ident_mut(&mut self) -> &mut #child_type_ident {
|
||||||
|
self.#child_ident.as_mut()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_single_boolean_child(
|
fn handle_boolean_child(
|
||||||
single_boolean_child: &SingleBooleanChildToBuild,
|
single_boolean_child: &BooleanChildToBuild,
|
||||||
member_names: &mut Vec<Ident>,
|
member_names: &mut Vec<Ident>,
|
||||||
annotated_members: &mut Vec<TokenStream>,
|
annotated_members: &mut Vec<TokenStream>,
|
||||||
|
member_args: &mut Vec<TokenStream>,
|
||||||
accessors: &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());
|
member_names.push(child_ident.clone());
|
||||||
annotated_members.push(quote! {
|
annotated_members.push(quote! {
|
||||||
#child_ident: bool
|
#child_ident: bool
|
||||||
});
|
});
|
||||||
|
member_args.push(quote! {
|
||||||
|
#child_ident: bool
|
||||||
|
});
|
||||||
accessors.push(quote! {
|
accessors.push(quote! {
|
||||||
pub fn #child_ident(&self) -> bool {
|
pub fn #child_ident(&self) -> bool {
|
||||||
self.#child_ident
|
self.#child_ident
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_struct_type(build_spec: &StructBuildSpec) -> TokenStream {
|
fn make_struct_type(build_spec: &StructBuildSpec) -> TokenStream {
|
||||||
let mut member_names: Vec<Ident> = vec![];
|
let mut member_names: Vec<Ident> = vec![];
|
||||||
let mut annotated_members: Vec<TokenStream> = vec![];
|
let mut annotated_members: Vec<TokenStream> = vec![];
|
||||||
|
let mut member_args: Vec<TokenStream> = vec![];
|
||||||
let mut accessors: Vec<TokenStream> = vec![];
|
let mut accessors: Vec<TokenStream> = vec![];
|
||||||
|
|
||||||
for child_spec in build_spec.children().iter() {
|
for child_spec in build_spec.children() {
|
||||||
match child_spec {
|
match child_spec {
|
||||||
ChildSpec::SkipChild(_) => {}
|
StructChildSpec::SkipChild(_) => {}
|
||||||
ChildSpec::VecChild(vec_child) => {
|
StructChildSpec::VecChild(vec_child) => {
|
||||||
handle_vec_child(
|
handle_vec_child(
|
||||||
vec_child,
|
vec_child,
|
||||||
&mut member_names,
|
&mut member_names,
|
||||||
&mut annotated_members,
|
&mut annotated_members,
|
||||||
|
&mut member_args,
|
||||||
&mut accessors,
|
&mut accessors,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ChildSpec::SingleChild(single_child) => {
|
StructChildSpec::MemberChild(member_child) => {
|
||||||
match single_child.build() {
|
match member_child.build() {
|
||||||
SingleChildToBuild::Type(single_type_child) => {
|
MemberChildToBuild::Node(node_child) => {
|
||||||
handle_single_type_child(
|
handle_node_child(
|
||||||
single_type_child,
|
member_child.name(),
|
||||||
|
node_child,
|
||||||
&mut member_names,
|
&mut member_names,
|
||||||
&mut annotated_members,
|
&mut annotated_members,
|
||||||
|
&mut member_args,
|
||||||
&mut accessors,
|
&mut accessors,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
SingleChildToBuild::Boolean(single_boolean_child) => {
|
MemberChildToBuild::Boolean(boolean_child) => {
|
||||||
handle_single_boolean_child(
|
handle_boolean_child(
|
||||||
single_boolean_child,
|
boolean_child,
|
||||||
&mut member_names,
|
&mut member_names,
|
||||||
&mut annotated_members,
|
&mut annotated_members,
|
||||||
|
&mut member_args,
|
||||||
&mut accessors,
|
&mut accessors,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -160,9 +262,91 @@ fn make_struct_type(build_spec: &StructBuildSpec) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_type(build_spec: &BuildSpec) -> TokenStream {
|
fn make_leaf_struct_type(build_spec: &LeafStructBuildSpec) -> TokenStream {
|
||||||
match build_spec {
|
let type_ident = format_ident!("{}", build_spec.build());
|
||||||
BuildSpec::Enum(enum_build_spec) => make_enum_type(enum_build_spec),
|
|
||||||
BuildSpec::Struct(struct_build_spec) => make_struct_type(struct_build_spec),
|
let annotated_members = build_spec
|
||||||
|
.members()
|
||||||
|
.map(|member| {
|
||||||
|
let name_ident = format_ident!("{}", member.name());
|
||||||
|
let type_ident = match member.kind() {
|
||||||
|
LeafStructMemberKind::String => format_ident!("{}", "String"),
|
||||||
|
};
|
||||||
|
quote! {
|
||||||
|
#name_ident: #type_ident
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let member_args = build_spec.members().map(|member| {
|
||||||
|
let name_ident = format_ident!("{}", member.name());
|
||||||
|
let type_stream = match member.kind() {
|
||||||
|
LeafStructMemberKind::String => {
|
||||||
|
quote! { &str }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
quote! {
|
||||||
|
#name_ident: #type_stream
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let initializers = build_spec
|
||||||
|
.members()
|
||||||
|
.map(|leaf_struct_member| {
|
||||||
|
let member_ident = format_ident!("{}", leaf_struct_member.name());
|
||||||
|
match leaf_struct_member.kind() {
|
||||||
|
LeafStructMemberKind::String => {
|
||||||
|
quote! {
|
||||||
|
#member_ident: #member_ident.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let accessors = build_spec
|
||||||
|
.members()
|
||||||
|
.map(|member| {
|
||||||
|
let name_ident = format_ident!("{}", member.name());
|
||||||
|
match member.kind() {
|
||||||
|
LeafStructMemberKind::String => {
|
||||||
|
quote! {
|
||||||
|
pub fn #name_ident(&self) -> &str {
|
||||||
|
&self.#name_ident
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
pub struct #type_ident {
|
||||||
|
#(#annotated_members),*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl #type_ident {
|
||||||
|
pub fn new(#(#member_args),*) -> Self {
|
||||||
|
Self {
|
||||||
|
#(#initializers),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#(#accessors)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_type(build_spec: &BuildSpec) -> Option<TokenStream> {
|
||||||
|
match build_spec {
|
||||||
|
BuildSpec::Enum(enum_build_spec) => Some(make_enum_type(enum_build_spec)),
|
||||||
|
BuildSpec::LeafEnum(leaf_enum_build_spec) => {
|
||||||
|
Some(make_leaf_enum_type(leaf_enum_build_spec))
|
||||||
|
}
|
||||||
|
BuildSpec::Struct(struct_build_spec) => Some(make_struct_type(struct_build_spec)),
|
||||||
|
BuildSpec::LeafStruct(leaf_struct_build_spec) => {
|
||||||
|
Some(make_leaf_struct_type(leaf_struct_build_spec))
|
||||||
|
}
|
||||||
|
BuildSpec::Production(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
ast-generator/src/util.rs
Normal file
9
ast-generator/src/util.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
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))
|
||||||
|
}
|
||||||
45
build.rs
45
build.rs
@ -1,9 +1,40 @@
|
|||||||
fn main() -> std::io::Result<()> {
|
use ast_generator::{deserialize, generate_files};
|
||||||
println!("cargo:rerun-if-changed=src/parser/deimos.pest");
|
use cst_test_generator::generate_test_files;
|
||||||
// let out_dir = env::var("OUT_DIR").unwrap();
|
use std::env;
|
||||||
// let out_dir_path = Path::new(&out_dir);
|
use std::fs;
|
||||||
// let testing_txt_path = out_dir_path.join("testing.rs");
|
use std::io;
|
||||||
// let output = test_dump();
|
use std::path::Path;
|
||||||
// write(&testing_txt_path, output)?;
|
|
||||||
|
fn generate_parser_tests(out_dir: &Path) -> io::Result<()> {
|
||||||
|
let parser_tests_dir = out_dir.join("src").join("parser").join("tests");
|
||||||
|
fs::create_dir_all(&parser_tests_dir)?;
|
||||||
|
let test_suites_file = generate_test_files(Path::new("src/parser/tests"))?;
|
||||||
|
let file_path = parser_tests_dir.join(&test_suites_file.file_name);
|
||||||
|
fs::write(file_path, &test_suites_file.contents)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_ast_files(out_dir: &Path) -> io::Result<()> {
|
||||||
|
let gen_ast_dir = out_dir.join("src").join("ast");
|
||||||
|
fs::create_dir_all(&gen_ast_dir)?;
|
||||||
|
|
||||||
|
let ast_yaml = include_str!("src/parser/ast.yaml");
|
||||||
|
let build_specs = deserialize::deserialize_yaml_spec(ast_yaml);
|
||||||
|
let generated_files = generate_files(&build_specs);
|
||||||
|
for generated_file in &generated_files {
|
||||||
|
let path = gen_ast_dir.join(&generated_file.name);
|
||||||
|
fs::write(path, &generated_file.contents)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> io::Result<()> {
|
||||||
|
println!("cargo:rerun-if-changed=src/parser/tests");
|
||||||
|
println!("cargo:rerun-if-changed=src/parser/ast.yaml");
|
||||||
|
println!("cargo:rerun-if-changed=src/parser/deimos.pest");
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
let out_dir_path = Path::new(&out_dir);
|
||||||
|
generate_parser_tests(out_dir_path)?;
|
||||||
|
generate_ast_files(out_dir_path)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
11
cst-test-generator/Cargo.toml
Normal file
11
cst-test-generator/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "cst-test-generator"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
convert_case = "0.8.0"
|
||||||
|
prettyplease = "0.2.37"
|
||||||
|
proc-macro2 = "1.0.101"
|
||||||
|
quote = "1.0.40"
|
||||||
|
syn = "2.0.106"
|
||||||
72
cst-test-generator/src/lib.rs
Normal file
72
cst-test-generator/src/lib.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
use convert_case::{Case, Casing};
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::{fs, io};
|
||||||
|
use syn::File;
|
||||||
|
|
||||||
|
pub struct ParserTestSuitesFile {
|
||||||
|
pub file_name: String,
|
||||||
|
pub contents: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_test_files(tests_dir: &Path) -> io::Result<ParserTestSuitesFile> {
|
||||||
|
let mut test_suites: Vec<TokenStream> = vec![];
|
||||||
|
|
||||||
|
// generate test file for each sub dir
|
||||||
|
for sub_dir in fs::read_dir(tests_dir)? {
|
||||||
|
let sub_dir = sub_dir?;
|
||||||
|
let sub_dir_path = sub_dir.path();
|
||||||
|
if sub_dir_path.is_dir() {
|
||||||
|
let sub_dir_file_name = sub_dir.file_name();
|
||||||
|
let sub_dir_string = sub_dir_file_name.to_string_lossy();
|
||||||
|
|
||||||
|
let sub_dir_pascal = sub_dir_string.to_case(Case::Pascal);
|
||||||
|
let rule_ident = format_ident!("{}", sub_dir_pascal);
|
||||||
|
|
||||||
|
let mut tests: Vec<TokenStream> = vec![];
|
||||||
|
for test_file in fs::read_dir(sub_dir_path)? {
|
||||||
|
let test_file = test_file?;
|
||||||
|
let test_file_name = test_file.file_name();
|
||||||
|
let test_ident = format_ident!("{}", test_file_name.to_string_lossy());
|
||||||
|
|
||||||
|
let src_input = fs::read_to_string(test_file.path())?;
|
||||||
|
|
||||||
|
let test = quote! {
|
||||||
|
#[test]
|
||||||
|
fn #test_ident() {
|
||||||
|
parses_to(Rule::#rule_ident, #src_input)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tests.push(test);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tests_mod_ident = format_ident!("{}_tests", sub_dir.file_name().to_string_lossy());
|
||||||
|
let test_suite = quote! {
|
||||||
|
mod #tests_mod_ident {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#(#tests)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
test_suites.push(test_suite);
|
||||||
|
} else {
|
||||||
|
println!("Warning: not a directory: {:?}", sub_dir_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate main test_suites file
|
||||||
|
let test_suites = quote! {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#(#test_suites)*
|
||||||
|
};
|
||||||
|
let test_suites_file: File = syn::parse2(test_suites).unwrap();
|
||||||
|
let test_suites_file_contents = prettyplease::unparse(&test_suites_file);
|
||||||
|
|
||||||
|
Ok(ParserTestSuitesFile {
|
||||||
|
file_name: String::from("test_suites.rs"),
|
||||||
|
contents: test_suites_file_contents,
|
||||||
|
})
|
||||||
|
}
|
||||||
1
src/ast/build_tests/backtick_inner/greeting
Normal file
1
src/ast/build_tests/backtick_inner/greeting
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello, World!
|
||||||
1
src/ast/build_tests/backtick_string/mixed
Normal file
1
src/ast/build_tests/backtick_string/mixed
Normal file
@ -0,0 +1 @@
|
|||||||
|
`Hello, ${world}!`
|
||||||
1
src/ast/build_tests/boolean_literal/false
Normal file
1
src/ast/build_tests/boolean_literal/false
Normal file
@ -0,0 +1 @@
|
|||||||
|
false
|
||||||
1
src/ast/build_tests/boolean_literal/true
Normal file
1
src/ast/build_tests/boolean_literal/true
Normal file
@ -0,0 +1 @@
|
|||||||
|
true
|
||||||
112
src/ast/mod.rs
112
src/ast/mod.rs
@ -1,6 +1,106 @@
|
|||||||
pub mod build;
|
pub mod node {
|
||||||
pub mod children;
|
include!(concat!(env!("OUT_DIR"), "/src/ast/node.rs"));
|
||||||
pub mod node;
|
|
||||||
pub mod pretty_print;
|
impl Default for Parameters {
|
||||||
pub mod unparse;
|
fn default() -> Self {
|
||||||
pub mod walk;
|
Self::new(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GenericParameters {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(Box::new(IdentifierList::new(vec![])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GenericArguments {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(Box::new(TypeUseList::new(vec![])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ImplementsList {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReturnType {
|
||||||
|
pub fn void() -> Self {
|
||||||
|
Self::new(Box::new(TypeUse::PrimitiveType(PrimitiveType::Void)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ClassConstructor {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ClosureParameters {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod build {
|
||||||
|
//noinspection RsUnusedImport
|
||||||
|
use crate::parser::Rule;
|
||||||
|
//noinspection RsUnusedImport
|
||||||
|
use pest::iterators::Pair;
|
||||||
|
//noinspection RsUnusedImport
|
||||||
|
use crate::ast::node::*;
|
||||||
|
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/src/ast/build.rs"));
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod build_tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::parser::DeimosParser;
|
||||||
|
use pest::Parser;
|
||||||
|
|
||||||
|
fn parse(rule: Rule, input: &str) -> Pair<Rule> {
|
||||||
|
let parse_result = DeimosParser::parse(rule, input);
|
||||||
|
parse_result.expect("parsing failed").next().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn boolean_literal_true() {
|
||||||
|
let pair = parse(
|
||||||
|
Rule::BooleanLiteral,
|
||||||
|
include_str!("build_tests/boolean_literal/true"),
|
||||||
|
);
|
||||||
|
assert_eq!(true, build_boolean_literal(pair));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn boolean_literal_false() {
|
||||||
|
let pair = parse(
|
||||||
|
Rule::BooleanLiteral,
|
||||||
|
include_str!("build_tests/boolean_literal/false"),
|
||||||
|
);
|
||||||
|
assert_eq!(false, build_boolean_literal(pair));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn backtick_inner_greeting() {
|
||||||
|
let pair = parse(
|
||||||
|
Rule::BacktickInner,
|
||||||
|
include_str!("build_tests/backtick_inner/greeting"),
|
||||||
|
);
|
||||||
|
assert_eq!("Hello, World!", build_backtick_inner(pair));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn backtick_string_mixed() {
|
||||||
|
let pair = parse(
|
||||||
|
Rule::BacktickString,
|
||||||
|
include_str!("build_tests/backtick_string/mixed"),
|
||||||
|
);
|
||||||
|
let backtick_string = build_backtick_string(pair);
|
||||||
|
assert_eq!(backtick_string.inners().count(), 2);
|
||||||
|
assert_eq!(backtick_string.expressions().count(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -101,45 +101,45 @@ where
|
|||||||
f(node.as_node_ref());
|
f(node.as_node_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
// #[cfg(test)]
|
||||||
mod tests {
|
// mod tests {
|
||||||
use crate::ast::build::build_ast;
|
// use crate::ast::build::build_ast;
|
||||||
use crate::ast::children::NodeRef;
|
// use crate::ast::children::NodeRef;
|
||||||
use crate::ast::walk::walk_depth_first;
|
// use crate::ast::walk::walk_depth_first;
|
||||||
use crate::parser::{DeimosParser, Rule};
|
// use crate::parser::{DeimosParser, Rule};
|
||||||
use indoc::indoc;
|
// use indoc::indoc;
|
||||||
use pest::Parser;
|
// use pest::Parser;
|
||||||
|
//
|
||||||
#[test]
|
// #[test]
|
||||||
fn collect_identifiers() {
|
// fn collect_identifiers() {
|
||||||
let parse_result = DeimosParser::parse(
|
// let parse_result = DeimosParser::parse(
|
||||||
Rule::CompilationUnit,
|
// Rule::CompilationUnit,
|
||||||
indoc! {"
|
// indoc! {"
|
||||||
ns greeter;
|
// ns greeter;
|
||||||
|
//
|
||||||
class Greeter {}
|
// class Greeter {}
|
||||||
|
//
|
||||||
fn main() {
|
// fn main() {
|
||||||
let greeter = Greeter();
|
// let greeter = Greeter();
|
||||||
}
|
// }
|
||||||
"},
|
// "},
|
||||||
);
|
// );
|
||||||
match parse_result {
|
// match parse_result {
|
||||||
Ok(cu_pairs) => {
|
// Ok(cu_pairs) => {
|
||||||
let cu = build_ast("greeter.dm", 0, cu_pairs.into_iter().next().unwrap());
|
// let cu = build_ast("greeter.dm", 0, cu_pairs.into_iter().next().unwrap());
|
||||||
let mut identifier_count = 0;
|
// let mut identifier_count = 0;
|
||||||
walk_depth_first(&cu, &mut |node_ref| match node_ref {
|
// walk_depth_first(&cu, &mut |node_ref| match node_ref {
|
||||||
NodeRef::Identifier(identifier) => {
|
// NodeRef::Identifier(identifier) => {
|
||||||
dbg!(identifier);
|
// dbg!(identifier);
|
||||||
identifier_count += 1;
|
// identifier_count += 1;
|
||||||
}
|
// }
|
||||||
_ => {}
|
// _ => {}
|
||||||
});
|
// });
|
||||||
assert_eq!(identifier_count, 5);
|
// assert_eq!(identifier_count, 5);
|
||||||
}
|
// }
|
||||||
Err(err) => {
|
// Err(err) => {
|
||||||
panic!("{}", err);
|
// panic!("{}", err);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
@ -1,54 +1,58 @@
|
|||||||
mod name_analysis;
|
// mod name_analysis;
|
||||||
mod p3;
|
// mod p3;
|
||||||
mod unparse;
|
// mod unparse;
|
||||||
|
//
|
||||||
use std::path::PathBuf;
|
// use std::path::PathBuf;
|
||||||
|
//
|
||||||
use crate::name_analysis::name_analysis;
|
// use crate::name_analysis::name_analysis;
|
||||||
use crate::p3::pretty_print_parse;
|
// use crate::p3::pretty_print_parse;
|
||||||
use crate::unparse::unparse;
|
// use crate::unparse::unparse;
|
||||||
use clap::{Parser, Subcommand};
|
// use clap::{Parser, Subcommand};
|
||||||
|
//
|
||||||
#[derive(Debug, Parser)]
|
// #[derive(Debug, Parser)]
|
||||||
#[command(name = "dmc")]
|
// #[command(name = "dmc")]
|
||||||
#[command(about = "Deimos Compiler", long_about = None)]
|
// #[command(about = "Deimos Compiler", long_about = None)]
|
||||||
struct Cli {
|
// struct Cli {
|
||||||
#[command(subcommand)]
|
// #[command(subcommand)]
|
||||||
command: Commands,
|
// command: Commands,
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
#[derive(Debug, Subcommand)]
|
// #[derive(Debug, Subcommand)]
|
||||||
enum Commands {
|
// enum Commands {
|
||||||
#[command(arg_required_else_help = true)]
|
// #[command(arg_required_else_help = true)]
|
||||||
Unparse {
|
// Unparse {
|
||||||
paths: Vec<PathBuf>,
|
// paths: Vec<PathBuf>,
|
||||||
},
|
// },
|
||||||
P3 {
|
// P3 {
|
||||||
paths: Vec<PathBuf>,
|
// paths: Vec<PathBuf>,
|
||||||
},
|
// },
|
||||||
NameAnalysis {
|
// NameAnalysis {
|
||||||
paths: Vec<PathBuf>,
|
// paths: Vec<PathBuf>,
|
||||||
},
|
// },
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
// fn main() {
|
||||||
|
// let args = Cli::parse();
|
||||||
|
// match args.command {
|
||||||
|
// Commands::Unparse { paths } => {
|
||||||
|
// for path in paths {
|
||||||
|
// unparse(&path);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// Commands::P3 { paths } => {
|
||||||
|
// for path in paths {
|
||||||
|
// pretty_print_parse(&path)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// Commands::NameAnalysis { paths } => {
|
||||||
|
// let result = name_analysis(&paths);
|
||||||
|
// if let Err(e) = result {
|
||||||
|
// eprintln!("{}", e)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = Cli::parse();
|
panic!("TODO")
|
||||||
match args.command {
|
|
||||||
Commands::Unparse { paths } => {
|
|
||||||
for path in paths {
|
|
||||||
unparse(&path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Commands::P3 { paths } => {
|
|
||||||
for path in paths {
|
|
||||||
pretty_print_parse(&path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Commands::NameAnalysis { paths } => {
|
|
||||||
let result = name_analysis(&paths);
|
|
||||||
if let Err(e) = result {
|
|
||||||
eprintln!("{}", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ extern crate core;
|
|||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod diagnostic;
|
pub mod diagnostic;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
pub mod name_analysis;
|
// pub mod name_analysis;
|
||||||
pub mod object_file;
|
pub mod object_file;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod std_core;
|
pub mod std_core;
|
||||||
|
|||||||
@ -4,14 +4,12 @@ pub struct DmModule {
|
|||||||
|
|
||||||
pub enum NamespaceVisibility {
|
pub enum NamespaceVisibility {
|
||||||
Public,
|
Public,
|
||||||
Partial {
|
Partial { visible_to_fqns: Vec<String> },
|
||||||
visible_to_fqns: Vec<String>
|
Private,
|
||||||
},
|
|
||||||
Private
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DmNamespace {
|
pub struct DmNamespace {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub dependencies: Vec<String>,
|
pub dependencies: Vec<String>,
|
||||||
pub visibility: NamespaceVisibility
|
pub visibility: NamespaceVisibility,
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
1320
src/name_analysis/gather.rs.bak
Normal file
1320
src/name_analysis/gather.rs.bak
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,268 +0,0 @@
|
|||||||
/*!
|
|
||||||
# Name Analysis
|
|
||||||
|
|
||||||
There are two phases in name analysis.
|
|
||||||
|
|
||||||
## 1. Gather
|
|
||||||
|
|
||||||
The gather phase has three responsibilities:
|
|
||||||
|
|
||||||
1. Add all declared symbols to the symbol table.
|
|
||||||
2. Set the `scope_id` property of all identifiers and fully-qualified-names.
|
|
||||||
3. For the main identifiers of `UseStatement`s (i.e., the last identifier in the `UseStatement`):
|
|
||||||
set a 'linking' symbol on the identifier, which will later be filled in with the linked-to
|
|
||||||
symbol (probably in another file, or from the standard library).
|
|
||||||
|
|
||||||
## 2. Resolve
|
|
||||||
|
|
||||||
The resolve phase has one main responsibility: resolve all references based on the identifier's
|
|
||||||
`scope_id` property.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use crate::ast::node::compilation_unit::CompilationUnit;
|
|
||||||
use crate::ast::node::named::Named;
|
|
||||||
use crate::diagnostic::DmDiagnostic;
|
|
||||||
use crate::name_analysis::gather::gather_compilation_unit;
|
|
||||||
use crate::name_analysis::resolve::resolve_compilation_unit;
|
|
||||||
use crate::name_analysis::symbol_table::SymbolTable;
|
|
||||||
|
|
||||||
mod fqn_context;
|
|
||||||
mod gather;
|
|
||||||
mod resolve;
|
|
||||||
pub mod symbol;
|
|
||||||
pub mod symbol_table;
|
|
||||||
|
|
||||||
pub fn analyze_names(
|
|
||||||
compilation_units: &mut Vec<CompilationUnit>,
|
|
||||||
symbol_table: &mut SymbolTable,
|
|
||||||
) -> Vec<DmDiagnostic> {
|
|
||||||
let mut diagnostics = vec![];
|
|
||||||
|
|
||||||
// gather symbols
|
|
||||||
for compilation_unit in compilation_units.iter_mut() {
|
|
||||||
gather_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolve symbols
|
|
||||||
for compilation_unit in compilation_units.iter_mut() {
|
|
||||||
resolve_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
|
|
||||||
}
|
|
||||||
|
|
||||||
diagnostics.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::ast::build::build_ast;
|
|
||||||
use crate::ast::children::NodeRef;
|
|
||||||
use crate::ast::walk::walk_depth_first;
|
|
||||||
use crate::parser::{DeimosParser, Rule};
|
|
||||||
use crate::std_core::add_std_core_symbols;
|
|
||||||
use codespan_reporting::files::SimpleFiles;
|
|
||||||
use codespan_reporting::term;
|
|
||||||
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
|
||||||
use indoc::indoc;
|
|
||||||
use pest::Parser;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
fn assert_number_of_diagnostics(
|
|
||||||
sources: HashMap<&str, &str>,
|
|
||||||
symbol_table: &mut SymbolTable,
|
|
||||||
n_diagnostics: usize,
|
|
||||||
) -> Vec<CompilationUnit> {
|
|
||||||
let mut files = SimpleFiles::new();
|
|
||||||
let mut compilation_units = vec![];
|
|
||||||
|
|
||||||
for (file_name, source) in sources {
|
|
||||||
let file_id = files.add(file_name, source);
|
|
||||||
let parse_result = DeimosParser::parse(Rule::CompilationUnit, source);
|
|
||||||
if let Err(err) = &parse_result {
|
|
||||||
panic!("{}", err);
|
|
||||||
}
|
|
||||||
let mut pairs = parse_result.unwrap();
|
|
||||||
if pairs.as_str().trim() != source.trim() {
|
|
||||||
panic!("Parsing did not consume entire input.");
|
|
||||||
}
|
|
||||||
|
|
||||||
compilation_units.push(build_ast(file_name, file_id, pairs.next().unwrap()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let diagnostics = analyze_names(&mut compilation_units, symbol_table);
|
|
||||||
|
|
||||||
if diagnostics.len() != n_diagnostics {
|
|
||||||
let writer = StandardStream::stderr(ColorChoice::Always);
|
|
||||||
let config = term::Config::default();
|
|
||||||
|
|
||||||
for diagnostic in &diagnostics {
|
|
||||||
term::emit(&mut writer.lock(), &config, &files, &diagnostic).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
eprintln!("{}", symbol_table);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(n_diagnostics, diagnostics.len());
|
|
||||||
|
|
||||||
compilation_units
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_no_diagnostics(
|
|
||||||
sources: HashMap<&str, &str>,
|
|
||||||
symbol_table: &mut SymbolTable,
|
|
||||||
) -> Vec<CompilationUnit> {
|
|
||||||
assert_number_of_diagnostics(sources, symbol_table, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_saved_symbols(compilation_unit: &CompilationUnit) {
|
|
||||||
walk_depth_first(compilation_unit, &mut |node_ref| match node_ref {
|
|
||||||
NodeRef::Identifier(identifier) => {
|
|
||||||
if identifier.saved_symbol().is_none() {
|
|
||||||
panic!("{:?} does not have a saved symbol.", identifier)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NodeRef::FullyQualifiedName(fqn) => {
|
|
||||||
if fqn.saved_symbol().is_none() {
|
|
||||||
panic!("{:?} does not have a saved symbol.", fqn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NodeRef::UseStatement(use_statement) => match use_statement {
|
|
||||||
_ => todo!(),
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_resolved_symbols(compilation_unit: &CompilationUnit) {
|
|
||||||
walk_depth_first(compilation_unit, &mut |node_ref| match node_ref {
|
|
||||||
NodeRef::UseStatement(use_statement) => match use_statement {
|
|
||||||
_ => todo!(),
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn params_seen() {
|
|
||||||
let sources: HashMap<&str, &str> = HashMap::from([(
|
|
||||||
"main.dm",
|
|
||||||
indoc! {"
|
|
||||||
fn main(args: Array<String>) {
|
|
||||||
let x = args;
|
|
||||||
}"},
|
|
||||||
)]);
|
|
||||||
|
|
||||||
let cus = assert_no_diagnostics(sources, &mut SymbolTable::new());
|
|
||||||
for ref cu in cus {
|
|
||||||
assert_saved_symbols(cu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn two_files() {
|
|
||||||
let sources: HashMap<&str, &str> = HashMap::from([
|
|
||||||
(
|
|
||||||
"main.dm",
|
|
||||||
indoc! {"
|
|
||||||
use test::Greeter;
|
|
||||||
"},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"deps.dm",
|
|
||||||
indoc! {"
|
|
||||||
ns test;
|
|
||||||
|
|
||||||
pub class Greeter {}
|
|
||||||
"},
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
|
|
||||||
let cus = assert_no_diagnostics(sources, &mut SymbolTable::new());
|
|
||||||
for ref cu in cus {
|
|
||||||
assert_saved_symbols(cu);
|
|
||||||
assert_resolved_symbols(cu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sees_std_core_println() {
|
|
||||||
let sources: HashMap<&str, &str> = HashMap::from([(
|
|
||||||
"main.dm",
|
|
||||||
indoc! {"
|
|
||||||
fn main(args: Array<String>) {
|
|
||||||
println(args)
|
|
||||||
}
|
|
||||||
"},
|
|
||||||
)]);
|
|
||||||
|
|
||||||
let mut symbol_table = SymbolTable::new();
|
|
||||||
add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols.");
|
|
||||||
let cus = assert_no_diagnostics(sources, &mut symbol_table);
|
|
||||||
for ref cu in cus {
|
|
||||||
assert_saved_symbols(cu);
|
|
||||||
assert_resolved_symbols(cu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sees_duplicate_fn() {
|
|
||||||
let sources: HashMap<&str, &str> = HashMap::from([(
|
|
||||||
"main.dm",
|
|
||||||
indoc! {"
|
|
||||||
fn main(args: Array<String>) {}
|
|
||||||
fn main(args: Array<String>) {}
|
|
||||||
"},
|
|
||||||
)]);
|
|
||||||
assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn use_class_from_other_file() {
|
|
||||||
let sources: HashMap<&str, &str> = HashMap::from([
|
|
||||||
(
|
|
||||||
"main.dm",
|
|
||||||
indoc! {"
|
|
||||||
use greeter::Greeter;
|
|
||||||
|
|
||||||
fn test(greeter: Greeter) {}
|
|
||||||
"},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"greeter.dm",
|
|
||||||
indoc! {"
|
|
||||||
ns greeter;
|
|
||||||
|
|
||||||
class Greeter {}
|
|
||||||
"},
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
let mut symbol_table = SymbolTable::new();
|
|
||||||
let cus = assert_no_diagnostics(sources, &mut symbol_table);
|
|
||||||
for ref cu in cus {
|
|
||||||
assert_saved_symbols(cu);
|
|
||||||
assert_resolved_symbols(cu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn shadow_import() {
|
|
||||||
let sources: HashMap<&str, &str> = HashMap::from([
|
|
||||||
(
|
|
||||||
"main.dm",
|
|
||||||
indoc! {"
|
|
||||||
use greeter::Greeter;
|
|
||||||
|
|
||||||
class Greeter {}
|
|
||||||
"},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"greeter.dm",
|
|
||||||
indoc! {"
|
|
||||||
ns greeter;
|
|
||||||
|
|
||||||
class Greeter {}
|
|
||||||
"},
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
267
src/name_analysis/mod.rs.bak
Normal file
267
src/name_analysis/mod.rs.bak
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
/*!
|
||||||
|
# Name Analysis
|
||||||
|
|
||||||
|
There are two phases in name analysis.
|
||||||
|
|
||||||
|
## 1. Gather
|
||||||
|
|
||||||
|
The gather phase has three responsibilities:
|
||||||
|
|
||||||
|
1. Add all declared symbols to the symbol table.
|
||||||
|
2. Set the `scope_id` property of all identifiers and fully-qualified-names.
|
||||||
|
3. For the main identifiers of `UseStatement`s (i.e., the last identifier in the `UseStatement`):
|
||||||
|
set a 'linking' symbol on the identifier, which will later be filled in with the linked-to
|
||||||
|
symbol (probably in another file, or from the standard library).
|
||||||
|
|
||||||
|
## 2. Resolve
|
||||||
|
|
||||||
|
The resolve phase has one main responsibility: resolve all references based on the identifier's
|
||||||
|
`scope_id` property.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::ast::node::CompilationUnit;
|
||||||
|
use crate::diagnostic::DmDiagnostic;
|
||||||
|
use crate::name_analysis::gather::gather_compilation_unit;
|
||||||
|
use crate::name_analysis::resolve::resolve_compilation_unit;
|
||||||
|
use crate::name_analysis::symbol_table::SymbolTable;
|
||||||
|
|
||||||
|
mod fqn_context;
|
||||||
|
mod gather;
|
||||||
|
mod resolve;
|
||||||
|
pub mod symbol;
|
||||||
|
pub mod symbol_table;
|
||||||
|
|
||||||
|
pub fn analyze_names(
|
||||||
|
compilation_units: &mut Vec<CompilationUnit>,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
) -> Vec<DmDiagnostic> {
|
||||||
|
let mut diagnostics = vec![];
|
||||||
|
|
||||||
|
// gather symbols
|
||||||
|
for compilation_unit in compilation_units.iter_mut() {
|
||||||
|
gather_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve symbols
|
||||||
|
for compilation_unit in compilation_units.iter_mut() {
|
||||||
|
resolve_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostics.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[cfg(test)]
|
||||||
|
// mod tests {
|
||||||
|
// use super::*;
|
||||||
|
// use crate::ast::build::build_ast;
|
||||||
|
// use crate::ast::children::NodeRef;
|
||||||
|
// use crate::ast::walk::walk_depth_first;
|
||||||
|
// use crate::parser::{DeimosParser, Rule};
|
||||||
|
// use crate::std_core::add_std_core_symbols;
|
||||||
|
// use codespan_reporting::files::SimpleFiles;
|
||||||
|
// use codespan_reporting::term;
|
||||||
|
// use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
||||||
|
// use indoc::indoc;
|
||||||
|
// use pest::Parser;
|
||||||
|
// use std::collections::HashMap;
|
||||||
|
//
|
||||||
|
// fn assert_number_of_diagnostics(
|
||||||
|
// sources: HashMap<&str, &str>,
|
||||||
|
// symbol_table: &mut SymbolTable,
|
||||||
|
// n_diagnostics: usize,
|
||||||
|
// ) -> Vec<CompilationUnit> {
|
||||||
|
// let mut files = SimpleFiles::new();
|
||||||
|
// let mut compilation_units = vec![];
|
||||||
|
//
|
||||||
|
// for (file_name, source) in sources {
|
||||||
|
// let file_id = files.add(file_name, source);
|
||||||
|
// let parse_result = DeimosParser::parse(Rule::CompilationUnit, source);
|
||||||
|
// if let Err(err) = &parse_result {
|
||||||
|
// panic!("{}", err);
|
||||||
|
// }
|
||||||
|
// let mut pairs = parse_result.unwrap();
|
||||||
|
// if pairs.as_str().trim() != source.trim() {
|
||||||
|
// panic!("Parsing did not consume entire input.");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// compilation_units.push(build_ast(file_name, file_id, pairs.next().unwrap()));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// let diagnostics = analyze_names(&mut compilation_units, symbol_table);
|
||||||
|
//
|
||||||
|
// if diagnostics.len() != n_diagnostics {
|
||||||
|
// let writer = StandardStream::stderr(ColorChoice::Always);
|
||||||
|
// let config = term::Config::default();
|
||||||
|
//
|
||||||
|
// for diagnostic in &diagnostics {
|
||||||
|
// term::emit(&mut writer.lock(), &config, &files, &diagnostic).unwrap();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// eprintln!("{}", symbol_table);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// assert_eq!(n_diagnostics, diagnostics.len());
|
||||||
|
//
|
||||||
|
// compilation_units
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fn assert_no_diagnostics(
|
||||||
|
// sources: HashMap<&str, &str>,
|
||||||
|
// symbol_table: &mut SymbolTable,
|
||||||
|
// ) -> Vec<CompilationUnit> {
|
||||||
|
// assert_number_of_diagnostics(sources, symbol_table, 0)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fn assert_saved_symbols(compilation_unit: &CompilationUnit) {
|
||||||
|
// walk_depth_first(compilation_unit, &mut |node_ref| match node_ref {
|
||||||
|
// NodeRef::Identifier(identifier) => {
|
||||||
|
// if identifier.saved_symbol().is_none() {
|
||||||
|
// panic!("{:?} does not have a saved symbol.", identifier)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// NodeRef::FullyQualifiedName(fqn) => {
|
||||||
|
// if fqn.saved_symbol().is_none() {
|
||||||
|
// panic!("{:?} does not have a saved symbol.", fqn)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// NodeRef::UseStatement(use_statement) => match use_statement {
|
||||||
|
// _ => todo!(),
|
||||||
|
// },
|
||||||
|
// _ => {}
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fn assert_resolved_symbols(compilation_unit: &CompilationUnit) {
|
||||||
|
// walk_depth_first(compilation_unit, &mut |node_ref| match node_ref {
|
||||||
|
// NodeRef::UseStatement(use_statement) => match use_statement {
|
||||||
|
// _ => todo!(),
|
||||||
|
// },
|
||||||
|
// _ => {}
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn params_seen() {
|
||||||
|
// let sources: HashMap<&str, &str> = HashMap::from([(
|
||||||
|
// "main.dm",
|
||||||
|
// indoc! {"
|
||||||
|
// fn main(args: Array<String>) {
|
||||||
|
// let x = args;
|
||||||
|
// }"},
|
||||||
|
// )]);
|
||||||
|
//
|
||||||
|
// let cus = assert_no_diagnostics(sources, &mut SymbolTable::new());
|
||||||
|
// for ref cu in cus {
|
||||||
|
// assert_saved_symbols(cu);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn two_files() {
|
||||||
|
// let sources: HashMap<&str, &str> = HashMap::from([
|
||||||
|
// (
|
||||||
|
// "main.dm",
|
||||||
|
// indoc! {"
|
||||||
|
// use test::Greeter;
|
||||||
|
// "},
|
||||||
|
// ),
|
||||||
|
// (
|
||||||
|
// "deps.dm",
|
||||||
|
// indoc! {"
|
||||||
|
// ns test;
|
||||||
|
//
|
||||||
|
// pub class Greeter {}
|
||||||
|
// "},
|
||||||
|
// ),
|
||||||
|
// ]);
|
||||||
|
//
|
||||||
|
// let cus = assert_no_diagnostics(sources, &mut SymbolTable::new());
|
||||||
|
// for ref cu in cus {
|
||||||
|
// assert_saved_symbols(cu);
|
||||||
|
// assert_resolved_symbols(cu);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn sees_std_core_println() {
|
||||||
|
// let sources: HashMap<&str, &str> = HashMap::from([(
|
||||||
|
// "main.dm",
|
||||||
|
// indoc! {"
|
||||||
|
// fn main(args: Array<String>) {
|
||||||
|
// println(args)
|
||||||
|
// }
|
||||||
|
// "},
|
||||||
|
// )]);
|
||||||
|
//
|
||||||
|
// let mut symbol_table = SymbolTable::new();
|
||||||
|
// add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols.");
|
||||||
|
// let cus = assert_no_diagnostics(sources, &mut symbol_table);
|
||||||
|
// for ref cu in cus {
|
||||||
|
// assert_saved_symbols(cu);
|
||||||
|
// assert_resolved_symbols(cu);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn sees_duplicate_fn() {
|
||||||
|
// let sources: HashMap<&str, &str> = HashMap::from([(
|
||||||
|
// "main.dm",
|
||||||
|
// indoc! {"
|
||||||
|
// fn main(args: Array<String>) {}
|
||||||
|
// fn main(args: Array<String>) {}
|
||||||
|
// "},
|
||||||
|
// )]);
|
||||||
|
// assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn use_class_from_other_file() {
|
||||||
|
// let sources: HashMap<&str, &str> = HashMap::from([
|
||||||
|
// (
|
||||||
|
// "main.dm",
|
||||||
|
// indoc! {"
|
||||||
|
// use greeter::Greeter;
|
||||||
|
//
|
||||||
|
// fn test(greeter: Greeter) {}
|
||||||
|
// "},
|
||||||
|
// ),
|
||||||
|
// (
|
||||||
|
// "greeter.dm",
|
||||||
|
// indoc! {"
|
||||||
|
// ns greeter;
|
||||||
|
//
|
||||||
|
// class Greeter {}
|
||||||
|
// "},
|
||||||
|
// ),
|
||||||
|
// ]);
|
||||||
|
// let mut symbol_table = SymbolTable::new();
|
||||||
|
// let cus = assert_no_diagnostics(sources, &mut symbol_table);
|
||||||
|
// for ref cu in cus {
|
||||||
|
// assert_saved_symbols(cu);
|
||||||
|
// assert_resolved_symbols(cu);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn shadow_import() {
|
||||||
|
// let sources: HashMap<&str, &str> = HashMap::from([
|
||||||
|
// (
|
||||||
|
// "main.dm",
|
||||||
|
// indoc! {"
|
||||||
|
// use greeter::Greeter;
|
||||||
|
//
|
||||||
|
// class Greeter {}
|
||||||
|
// "},
|
||||||
|
// ),
|
||||||
|
// (
|
||||||
|
// "greeter.dm",
|
||||||
|
// indoc! {"
|
||||||
|
// ns greeter;
|
||||||
|
//
|
||||||
|
// class Greeter {}
|
||||||
|
// "},
|
||||||
|
// ),
|
||||||
|
// ]);
|
||||||
|
// assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@ -1,29 +1,10 @@
|
|||||||
use crate::ast::node::call_expression::*;
|
use crate::ast::node::named::Named;
|
||||||
use crate::ast::node::class::*;
|
use crate::ast::node::*;
|
||||||
use crate::ast::node::closure::*;
|
|
||||||
use crate::ast::node::compilation_unit::*;
|
|
||||||
use crate::ast::node::d_string::*;
|
|
||||||
use crate::ast::node::expression::*;
|
|
||||||
use crate::ast::node::function::*;
|
|
||||||
use crate::ast::node::generics::*;
|
|
||||||
use crate::ast::node::implements_list::*;
|
|
||||||
use crate::ast::node::interface::*;
|
|
||||||
use crate::ast::node::level::*;
|
|
||||||
use crate::ast::node::literal::*;
|
|
||||||
use crate::ast::node::module::*;
|
|
||||||
use crate::ast::node::names::*;
|
|
||||||
use crate::ast::node::object_access::*;
|
|
||||||
use crate::ast::node::statement::*;
|
|
||||||
use crate::ast::node::tuple_arguments::*;
|
|
||||||
use crate::ast::node::type_use::*;
|
|
||||||
use crate::ast::node::use_statement::*;
|
|
||||||
use crate::diagnostic::DmDiagnostic;
|
use crate::diagnostic::DmDiagnostic;
|
||||||
use crate::name_analysis::symbol::Symbol;
|
use crate::name_analysis::symbol::Symbol;
|
||||||
use crate::name_analysis::symbol_table::{SymbolLookupError, SymbolTable};
|
use crate::name_analysis::symbol_table::{SymbolLookupError, SymbolTable};
|
||||||
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
||||||
use std::ops::DerefMut;
|
|
||||||
use std::range::Range;
|
use std::range::Range;
|
||||||
use crate::ast::node::named::Named;
|
|
||||||
/* Type Use */
|
/* Type Use */
|
||||||
|
|
||||||
fn resolve_type_use(
|
fn resolve_type_use(
|
||||||
@ -1,6 +1,3 @@
|
|||||||
use crate::ast::node::named::Named;
|
|
||||||
use crate::ast::node::names::Identifier;
|
|
||||||
use crate::ast::node::use_statement::UseStatement;
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
@ -28,7 +25,7 @@ impl SourceDefinition {
|
|||||||
range: borrowed.range(),
|
range: borrowed.range(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_id(&self) -> usize {
|
pub fn file_id(&self) -> usize {
|
||||||
self.file_id
|
self.file_id
|
||||||
}
|
}
|
||||||
@ -5,34 +5,65 @@ description: Top level is a map of node names in Pascal case (e.g., CompilationU
|
|||||||
additionalProperties:
|
additionalProperties:
|
||||||
$ref: "#/$defs/NodeDefinition"
|
$ref: "#/$defs/NodeDefinition"
|
||||||
$defs:
|
$defs:
|
||||||
|
# Top level
|
||||||
NodeDefinition:
|
NodeDefinition:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
description: A definition of a node type.
|
description: |
|
||||||
|
A definition of a node type. The main key in this hash determines the type of node: `children` for Struct node,
|
||||||
|
`members` for Leaf-Struct node, `rules` for Enum node, `leaf_rules` for Leaf-Enum node, and `produce` for a
|
||||||
|
translate rule.
|
||||||
|
|
||||||
|
A Struct node has child nodes and other properties, and is built by looping through all the inner pairs of the
|
||||||
|
given Parser Pair.
|
||||||
|
|
||||||
|
A Leaf-Struct node does not have child nodes, but does have members. The members are built by some kind of parsing
|
||||||
|
of the string value of the the given Parser Pair (i.e., no looping through inner pairs).
|
||||||
|
|
||||||
|
An Enum node maps Parser Rules to an enum of node types. Each enum member may have a child, or not. By default, a
|
||||||
|
Rule name maps to the node type-name of a single child.
|
||||||
|
|
||||||
|
A Leaf-Enum node is like a regular enum node, but no children are allowed. Rather, the Parser Rule maps to a bare
|
||||||
|
enum member.
|
||||||
|
|
||||||
|
A translate rule simply translates the Parser rule into some kind of arbitrary type, such as a string, int, etc.
|
||||||
oneOf:
|
oneOf:
|
||||||
- $ref: "#/$defs/StructNodeDefinition"
|
- $ref: "#/$defs/StructNodeDefinition"
|
||||||
|
- $ref: "#/$defs/LeafStructNodeDefinition"
|
||||||
- $ref: "#/$defs/EnumNodeDefinition"
|
- $ref: "#/$defs/EnumNodeDefinition"
|
||||||
- $ref: "#/$defs/LeafEnumNodeDefinition"
|
- $ref: "#/$defs/LeafEnumNodeDefinition"
|
||||||
|
- $ref: "#/$defs/ProductionDefinition"
|
||||||
|
|
||||||
|
# Four main types of nodes
|
||||||
StructNodeDefinition:
|
StructNodeDefinition:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
description: A description of a Struct node to be built.
|
description: A description of a Struct node to be built.
|
||||||
properties:
|
properties:
|
||||||
type:
|
|
||||||
const: struct
|
|
||||||
children:
|
children:
|
||||||
|
type: array
|
||||||
description: Ordered child fields for this node.
|
description: Ordered child fields for this node.
|
||||||
items:
|
items:
|
||||||
$ref: "#/$defs/StructChildDefinition"
|
$ref: "#/$defs/StructChildDefinition"
|
||||||
required:
|
required:
|
||||||
- children
|
- children
|
||||||
|
LeafStructNodeDefinition:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
description: A description of a Leaf-Struct node to be built.
|
||||||
|
properties:
|
||||||
|
members:
|
||||||
|
type: array
|
||||||
|
description: Ordered members for this node.
|
||||||
|
items:
|
||||||
|
$ref: "#/$defs/LeafStructMemberDefinition"
|
||||||
|
required:
|
||||||
|
- members
|
||||||
EnumNodeDefinition:
|
EnumNodeDefinition:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
description: A description of an Enum node to be built.
|
description: A description of an Enum node to be built.
|
||||||
properties:
|
properties:
|
||||||
type:
|
|
||||||
const: enum
|
|
||||||
rules:
|
rules:
|
||||||
type: array
|
type: array
|
||||||
description: Alternative parse rules that build this node.
|
description: Alternative parse rules that build this node.
|
||||||
@ -45,39 +76,40 @@ $defs:
|
|||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
description: A description of a leaf-enum node to be built.
|
description: A description of a leaf-enum node to be built.
|
||||||
properties:
|
properties:
|
||||||
type:
|
leaf_rules:
|
||||||
const: leaf_enum
|
|
||||||
rules:
|
|
||||||
type: array
|
type: array
|
||||||
description: Alternative parse rules that build this node.
|
description: Alternative parse rules that build this node.
|
||||||
items:
|
items:
|
||||||
$ref: "#/$defs/LeafEnumChildDefinition"
|
type: string
|
||||||
required:
|
required:
|
||||||
- type
|
- leaf_rules
|
||||||
- rules
|
|
||||||
|
# Struct node children
|
||||||
StructChildDefinition:
|
StructChildDefinition:
|
||||||
description: A definition of a node's child. Either a bare child name (string) in snake case, or an object.
|
description: |
|
||||||
|
A definition of a Struct node's child. Either a bare child name (string) in snake case, or an object. The former
|
||||||
|
is a shorthand where the child name and built type are the same; casing is automatically done. The latter allows
|
||||||
|
further customization of the built child.
|
||||||
oneOf:
|
oneOf:
|
||||||
- type: string
|
- type: string
|
||||||
description: Shorthand where child name, var, build, and with are inferred from the given snake-case child name.
|
- $ref: "#/$defs/StructChildDefinitionWrapper"
|
||||||
- $ref: "#/$defs/ChildDefinitionWrapper"
|
StructChildDefinitionWrapper:
|
||||||
ChildDefinitionWrapper:
|
|
||||||
type: object
|
type: object
|
||||||
description: Single-key object mapping the child-name to its spec.
|
description: Single-key object mapping the child-name to its advanced definition.
|
||||||
minProperties: 1
|
minProperties: 1
|
||||||
maxProperties: 1
|
maxProperties: 1
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
patternProperties:
|
patternProperties:
|
||||||
"^[a-z][a-z0-9_]*$":
|
"^[a-z][a-z0-9_]*$":
|
||||||
$ref: "#/$defs/ChildDefinition"
|
$ref: "#/$defs/StructChildAdvancedDefinition"
|
||||||
ChildDefinition:
|
StructChildAdvancedDefinition:
|
||||||
type: object
|
type: object
|
||||||
description: One of skip/vec/single child specs.
|
description: One of skip/vec/single child specs.
|
||||||
oneOf:
|
oneOf:
|
||||||
- $ref: "#/$defs/SkipChildDefinition"
|
- $ref: "#/$defs/StructChildSkipChildDefinition"
|
||||||
- $ref: "#/$defs/VecChildDefinition"
|
- $ref: "#/$defs/StructChildVecChildDefinition"
|
||||||
- $ref: "#/$defs/SingleChildDefinition"
|
- $ref: "#/$defs/StructChildMemberDefinition"
|
||||||
SkipChildDefinition:
|
StructChildSkipChildDefinition:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
description: A definition for a child rule that does nothing, i.e., is skipped.
|
description: A definition for a child rule that does nothing, i.e., is skipped.
|
||||||
@ -85,49 +117,121 @@ $defs:
|
|||||||
rule:
|
rule:
|
||||||
type: string
|
type: string
|
||||||
skip:
|
skip:
|
||||||
type: boolean # note: must be true
|
type: boolean
|
||||||
|
const: true
|
||||||
required:
|
required:
|
||||||
- rule
|
- rule
|
||||||
- skip
|
- skip
|
||||||
VecChildDefinition:
|
StructChildVecChildDefinition:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
description: A definition for a child rule that can be matched multiple times.
|
description: A definition for a child rule that can be matched multiple times.
|
||||||
properties:
|
properties:
|
||||||
rule:
|
rule:
|
||||||
type: string
|
type: string
|
||||||
|
kind:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- node # default
|
||||||
|
- string
|
||||||
vec:
|
vec:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
const: true
|
||||||
required:
|
required:
|
||||||
- rule
|
- rule
|
||||||
- vec
|
- vec
|
||||||
SingleChildDefinition:
|
StructChildMemberDefinition:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
description: A definition for a child rule that builds one item.
|
|
||||||
properties:
|
properties:
|
||||||
rule:
|
rule:
|
||||||
type: string
|
type: string
|
||||||
description: The type to build, in Pascal case.
|
description: The rule to match.
|
||||||
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.
|
||||||
build:
|
build:
|
||||||
oneOf:
|
oneOf:
|
||||||
- type: string
|
- type: string
|
||||||
- $ref: "#/$defs/SingleChildBuildDefinition"
|
- $ref: "#/$defs/StructChildMemberBuildDefinition"
|
||||||
SingleChildBuildDefinition:
|
StructChildMemberBuildDefinition:
|
||||||
type: object
|
type: object
|
||||||
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/BuildNode"
|
||||||
- $ref: "#/$defs/BuildBooleanChild"
|
- $ref: "#/$defs/BuildBoolean"
|
||||||
- $ref: "#/$defs/BuildStringChild"
|
|
||||||
- $ref: "#/$defs/BuildDoubleChild"
|
# Leaf Struct children
|
||||||
- $ref: "#/$defs/BuildIntChild"
|
LeafStructMemberDefinition:
|
||||||
- $ref: "#/$defs/BuildLongChild"
|
type: object
|
||||||
BuildSingleTypeChild:
|
description: Single-key object mapping the member-name to what is to be built by parsing the Parser Pair.
|
||||||
|
minProperties: 1
|
||||||
|
maxProperties: 1
|
||||||
|
additionalProperties: false
|
||||||
|
patternProperties:
|
||||||
|
"^[a-z][a-z0-9_]*$":
|
||||||
|
oneOf:
|
||||||
|
- $ref: "#/$defs/BuildMember"
|
||||||
|
BuildMember:
|
||||||
|
type: object
|
||||||
|
description: A specification for a member to build.
|
||||||
|
additionalProperties: false
|
||||||
|
properties:
|
||||||
|
kind:
|
||||||
|
enum:
|
||||||
|
- string
|
||||||
|
from:
|
||||||
|
enum:
|
||||||
|
- whole_pair
|
||||||
|
required:
|
||||||
|
- kind
|
||||||
|
- from
|
||||||
|
|
||||||
|
# Enum children
|
||||||
|
EnumChildDefinition:
|
||||||
|
oneOf:
|
||||||
|
- type: string
|
||||||
|
description: Shorthand where child name, var, build, and with are inferred from the given Pascal-case rule name.
|
||||||
|
- $ref: "#/$defs/LongEnumChildWrapper"
|
||||||
|
LongEnumChildWrapper:
|
||||||
|
type: object
|
||||||
|
minProperties: 1
|
||||||
|
maxProperties: 1
|
||||||
|
additionalProperties: false
|
||||||
|
patternProperties:
|
||||||
|
"^([A-Z][a-z]*)*$":
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- int
|
||||||
|
- long
|
||||||
|
- double
|
||||||
|
- usize
|
||||||
|
- string
|
||||||
|
- boolean
|
||||||
|
|
||||||
|
# Production definition
|
||||||
|
ProductionDefinition:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
produce:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
kind:
|
||||||
|
enum:
|
||||||
|
- int
|
||||||
|
- long
|
||||||
|
- double
|
||||||
|
- string
|
||||||
|
- boolean
|
||||||
|
from:
|
||||||
|
enum:
|
||||||
|
- string_inner
|
||||||
|
- whole_pair
|
||||||
|
- parse_whole_pair
|
||||||
|
|
||||||
|
# Common things to build
|
||||||
|
BuildNode:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
description: A definition of a single-type child to build.
|
description: A definition of a single-type child to build.
|
||||||
@ -138,107 +242,15 @@ $defs:
|
|||||||
or_else_default:
|
or_else_default:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Whether to call the default method on the built-type if the rule is not found.
|
description: Whether to call the default method on the built-type if the rule is not found.
|
||||||
BuildBooleanChild:
|
BuildBoolean:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
description: A definition for building a boolean child.
|
description: A boolean member to be built.
|
||||||
properties:
|
properties:
|
||||||
type:
|
kind:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
const: boolean
|
||||||
- boolean
|
|
||||||
on:
|
on:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
- rule_present
|
- rule_present
|
||||||
from:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- parse_whole_pair
|
|
||||||
BuildStringChild:
|
|
||||||
type: object
|
|
||||||
additionalProperties: false
|
|
||||||
description: A definition for building a string child.
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
const: string
|
|
||||||
from:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- whole_pair
|
|
||||||
BuildDoubleChild:
|
|
||||||
type: object
|
|
||||||
additionalProperties: false
|
|
||||||
description: A definition for building a Double child.
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
const: f64
|
|
||||||
from:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- parse_whole_pair
|
|
||||||
BuildIntChild:
|
|
||||||
type: object
|
|
||||||
additionalProperties: false
|
|
||||||
description: A definition for building an Int child.
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
const: i32
|
|
||||||
from:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- parse_number_base
|
|
||||||
BuildLongChild:
|
|
||||||
type: object
|
|
||||||
additionalProperties: false
|
|
||||||
description: A definition for building a Long child.
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
const: i64
|
|
||||||
from:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- parse_number_base
|
|
||||||
EnumChildDefinition:
|
|
||||||
description: A definition of an enum node's child. Either a bare rule (string) in Pascal case, or an object.
|
|
||||||
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:
|
|
||||||
type: object
|
|
||||||
additionalProperties: false
|
|
||||||
description: A format for specifying more specific information for an enum child.
|
|
||||||
properties:
|
|
||||||
rule:
|
|
||||||
type: string
|
|
||||||
build:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- rule
|
|
||||||
- build
|
|
||||||
LeafEnumChildDefinition:
|
|
||||||
description: A definition of a leaf-enum node's child. Either a bare rule-string in Pascal case, or an object.
|
|
||||||
oneOf:
|
|
||||||
- type: string
|
|
||||||
description: Shorthand where the rule name maps onto an empty enum rule.
|
|
||||||
- $ref: "#/$defs/LongLeafEnumChildDefinitionWrapper"
|
|
||||||
LongLeafEnumChildDefinitionWrapper:
|
|
||||||
type: object
|
|
||||||
description: Single-key object mapping the child-name to its spec.
|
|
||||||
minProperties: 1
|
|
||||||
maxProperties: 1
|
|
||||||
additionalProperties: false
|
|
||||||
patternProperties:
|
|
||||||
"^([A-Z][a-z0-9]*)*$":
|
|
||||||
$ref: "#/$defs/LongLeafEnumChildDefinition"
|
|
||||||
LongLeafEnumChildDefinition:
|
|
||||||
type: object
|
|
||||||
additionalProperties: false
|
|
||||||
description: A format for specifying more specific information about a leaf-enum child.
|
|
||||||
properties:
|
|
||||||
child:
|
|
||||||
type: boolean
|
|
||||||
description: If true, a node of the same name is built as the lone member of the enum child.
|
|
||||||
required:
|
|
||||||
- child
|
|
||||||
@ -1,28 +1,159 @@
|
|||||||
# $schema: ./ast.schema.yaml
|
# $schema: ./ast.schema.yaml
|
||||||
|
# Operators
|
||||||
|
Operator:
|
||||||
|
leaf_rules:
|
||||||
|
- Or
|
||||||
|
- And
|
||||||
|
- EqualTo
|
||||||
|
- NotEqualTo
|
||||||
|
- Greater
|
||||||
|
- Less
|
||||||
|
- GreaterEqual
|
||||||
|
- LessEqual
|
||||||
|
- Add
|
||||||
|
- Subtract
|
||||||
|
- Multiply
|
||||||
|
- Divide
|
||||||
|
- Modulo
|
||||||
|
- LeftShift
|
||||||
|
- RightShift
|
||||||
|
- Spread
|
||||||
|
- Star
|
||||||
|
- Not
|
||||||
|
- Negative
|
||||||
|
- PlusPlus
|
||||||
|
- MinusMinus
|
||||||
|
- CallOp
|
||||||
|
- Index
|
||||||
|
|
||||||
# Names
|
# Names
|
||||||
Identifier:
|
Identifier:
|
||||||
children:
|
members:
|
||||||
- literal:
|
- name:
|
||||||
build:
|
kind: string
|
||||||
type: string
|
from: whole_pair
|
||||||
from: parse_whole_pair
|
|
||||||
FullyQualifiedName:
|
FullyQualifiedName:
|
||||||
children:
|
children:
|
||||||
- identifiers:
|
- identifiers:
|
||||||
rule: Identifier
|
rule: Identifier
|
||||||
vec: true
|
vec: true
|
||||||
|
|
||||||
|
# Lists
|
||||||
|
TypeUseList:
|
||||||
|
children:
|
||||||
|
- type_uses:
|
||||||
|
rule: TypeUse
|
||||||
|
vec: true
|
||||||
|
IdentifierList:
|
||||||
|
children:
|
||||||
|
- identifiers:
|
||||||
|
rule: Identifier
|
||||||
|
vec: true
|
||||||
|
ParenthesesOptionalTypeUseList:
|
||||||
|
children:
|
||||||
|
- type_use_list:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
# Type Use
|
||||||
|
TypeUse:
|
||||||
|
rules:
|
||||||
|
- PrimitiveType
|
||||||
|
- InterfaceOrClassTypeUse
|
||||||
|
- TupleTypeUse
|
||||||
|
- FunctionTypeUse
|
||||||
|
PrimitiveType:
|
||||||
|
leaf_rules:
|
||||||
|
- Byte
|
||||||
|
- Short
|
||||||
|
- Char
|
||||||
|
- Int
|
||||||
|
- Long
|
||||||
|
- Double
|
||||||
|
- Bool
|
||||||
|
- String
|
||||||
|
- TypedArray
|
||||||
|
- Any
|
||||||
|
- Void
|
||||||
|
TypedArray:
|
||||||
|
children:
|
||||||
|
- array_kw:
|
||||||
|
rule: Array
|
||||||
|
skip: true
|
||||||
|
- generic_arguments
|
||||||
|
InterfaceOrClassTypeUse:
|
||||||
|
children:
|
||||||
|
- fully_qualified_name
|
||||||
|
- generic_arguments:
|
||||||
|
build:
|
||||||
|
or_else_default: true
|
||||||
|
TupleTypeUse:
|
||||||
|
children:
|
||||||
|
- tuple_arguments
|
||||||
|
FunctionTypeUse:
|
||||||
|
children:
|
||||||
|
- fn_kw:
|
||||||
|
rule: Fn
|
||||||
|
skip: true
|
||||||
|
- generic_parameters:
|
||||||
|
build:
|
||||||
|
or_else_default: true
|
||||||
|
- parameters:
|
||||||
|
build:
|
||||||
|
or_else_default: true
|
||||||
|
- return_type
|
||||||
|
|
||||||
|
# Generic Arguments
|
||||||
|
GenericArguments:
|
||||||
|
children:
|
||||||
|
- type_use_list
|
||||||
|
|
||||||
|
# Generic Parameters
|
||||||
|
GenericParameters:
|
||||||
|
children:
|
||||||
|
- identifier_list
|
||||||
|
|
||||||
|
# Tuple Arguments
|
||||||
|
TupleArguments:
|
||||||
|
children:
|
||||||
|
- parentheses_optional_type_use_list
|
||||||
|
|
||||||
|
# Implements List
|
||||||
|
ImplementsList:
|
||||||
|
children:
|
||||||
|
- type_uses:
|
||||||
|
rule: TypeUse
|
||||||
|
vec: true
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
Parameters:
|
||||||
|
children:
|
||||||
|
- parameters:
|
||||||
|
rule: Parameter
|
||||||
|
vec: true
|
||||||
|
Parameter:
|
||||||
|
children:
|
||||||
|
- identifier
|
||||||
|
- type_use
|
||||||
|
|
||||||
|
# Return Type
|
||||||
|
ReturnType:
|
||||||
|
children:
|
||||||
|
- type_use
|
||||||
|
|
||||||
# Top-level constructs
|
# Top-level constructs
|
||||||
CompilationUnit:
|
CompilationUnit:
|
||||||
children:
|
children:
|
||||||
- parent_mod
|
- parent_mod:
|
||||||
|
optional: true
|
||||||
- use_statements:
|
- use_statements:
|
||||||
rule: UseStatement
|
rule: UseStatement
|
||||||
vec: true
|
vec: true
|
||||||
- module_level_declarations:
|
- module_level_declarations:
|
||||||
rule: ModuleLevelDeclaration
|
rule: ModuleLevelDeclaration
|
||||||
vec: true
|
vec: true
|
||||||
|
- eoi:
|
||||||
|
rule: EOI
|
||||||
|
skip: true
|
||||||
ParentMod:
|
ParentMod:
|
||||||
children:
|
children:
|
||||||
- mod_kw:
|
- mod_kw:
|
||||||
@ -46,8 +177,8 @@ UseStatementPrefix:
|
|||||||
UseStatementSuffix:
|
UseStatementSuffix:
|
||||||
rules:
|
rules:
|
||||||
- Identifier
|
- Identifier
|
||||||
- rule: Star
|
- Star:
|
||||||
build: UseStatementStarSuffix
|
child: false
|
||||||
- UseList
|
- UseList
|
||||||
UseList:
|
UseList:
|
||||||
children:
|
children:
|
||||||
@ -87,7 +218,7 @@ Module:
|
|||||||
- is_public:
|
- is_public:
|
||||||
rule: Pub
|
rule: Pub
|
||||||
build:
|
build:
|
||||||
type: boolean
|
kind: boolean
|
||||||
on: rule_present
|
on: rule_present
|
||||||
- mod_kw:
|
- mod_kw:
|
||||||
rule: Mod
|
rule: Mod
|
||||||
@ -119,7 +250,7 @@ Interface:
|
|||||||
- is_public:
|
- is_public:
|
||||||
rule: Pub
|
rule: Pub
|
||||||
build:
|
build:
|
||||||
type: boolean
|
kind: boolean
|
||||||
on: rule_present
|
on: rule_present
|
||||||
- int_kw:
|
- int_kw:
|
||||||
rule: IntKw
|
rule: IntKw
|
||||||
@ -142,7 +273,7 @@ Class:
|
|||||||
- is_public:
|
- is_public:
|
||||||
rule: Pub
|
rule: Pub
|
||||||
build:
|
build:
|
||||||
type: boolean
|
kind: boolean
|
||||||
on: rule_present
|
on: rule_present
|
||||||
- class_kw:
|
- class_kw:
|
||||||
rule: ClassKw
|
rule: ClassKw
|
||||||
@ -167,7 +298,7 @@ Function:
|
|||||||
- is_public:
|
- is_public:
|
||||||
rule: Pub
|
rule: Pub
|
||||||
build:
|
build:
|
||||||
type: boolean
|
kind: boolean
|
||||||
on: rule_present
|
on: rule_present
|
||||||
- fn_kw:
|
- fn_kw:
|
||||||
rule: Fn
|
rule: Fn
|
||||||
@ -185,7 +316,7 @@ OperatorFunction:
|
|||||||
- is_public:
|
- is_public:
|
||||||
rule: Pub
|
rule: Pub
|
||||||
build:
|
build:
|
||||||
type: boolean
|
kind: boolean
|
||||||
on: rule_present
|
on: rule_present
|
||||||
- op_kw:
|
- op_kw:
|
||||||
rule: Op
|
rule: Op
|
||||||
@ -205,7 +336,7 @@ PlatformFunction:
|
|||||||
- is_public:
|
- is_public:
|
||||||
rule: Pub
|
rule: Pub
|
||||||
build:
|
build:
|
||||||
type: boolean
|
kind: boolean
|
||||||
on: rule_present
|
on: rule_present
|
||||||
- platform_kw:
|
- platform_kw:
|
||||||
rule: Platform
|
rule: Platform
|
||||||
@ -316,12 +447,12 @@ Member:
|
|||||||
- is_public:
|
- is_public:
|
||||||
rule: Pub
|
rule: Pub
|
||||||
build:
|
build:
|
||||||
type: boolean
|
kind: boolean
|
||||||
on: rule_present
|
on: rule_present
|
||||||
- is_mut:
|
- is_mut:
|
||||||
rule: Mut
|
rule: Mut
|
||||||
build:
|
build:
|
||||||
type: boolean
|
kind: boolean
|
||||||
on: rule_present
|
on: rule_present
|
||||||
- identifier
|
- identifier
|
||||||
- type_use
|
- type_use
|
||||||
@ -333,8 +464,7 @@ Statement:
|
|||||||
- AssignmentStatement
|
- AssignmentStatement
|
||||||
- ExpressionStatement
|
- ExpressionStatement
|
||||||
- UseStatement
|
- UseStatement
|
||||||
- IfElseStatement
|
- IfStatement
|
||||||
- IfStatementStatement
|
|
||||||
- WhileStatement
|
- WhileStatement
|
||||||
- ForStatement
|
- ForStatement
|
||||||
VariableDeclaration:
|
VariableDeclaration:
|
||||||
@ -345,7 +475,7 @@ VariableDeclaration:
|
|||||||
- is_mut:
|
- is_mut:
|
||||||
rule: Mut
|
rule: Mut
|
||||||
build:
|
build:
|
||||||
type: boolean
|
kind: boolean
|
||||||
on: rule_present
|
on: rule_present
|
||||||
- identifier
|
- identifier
|
||||||
- type_use:
|
- type_use:
|
||||||
@ -430,7 +560,7 @@ ForStatement:
|
|||||||
vec: true
|
vec: true
|
||||||
- end_kw:
|
- end_kw:
|
||||||
rule: End
|
rule: End
|
||||||
vec: true
|
skip: true
|
||||||
|
|
||||||
# Expressions
|
# Expressions
|
||||||
Expression:
|
Expression:
|
||||||
@ -482,7 +612,7 @@ ComparisonExpression:
|
|||||||
rule: Expression
|
rule: Expression
|
||||||
optional: true
|
optional: true
|
||||||
ComparisonOperator:
|
ComparisonOperator:
|
||||||
rules:
|
leaf_rules:
|
||||||
- Greater
|
- Greater
|
||||||
- Less
|
- Less
|
||||||
- GreaterEqual
|
- GreaterEqual
|
||||||
@ -498,8 +628,9 @@ ShiftExpression:
|
|||||||
optional: true
|
optional: true
|
||||||
- right:
|
- right:
|
||||||
rule: Expression
|
rule: Expression
|
||||||
|
optional: true
|
||||||
ShiftOperator:
|
ShiftOperator:
|
||||||
rules:
|
leaf_rules:
|
||||||
- LeftShift
|
- LeftShift
|
||||||
- RightShift
|
- RightShift
|
||||||
AdditiveExpression:
|
AdditiveExpression:
|
||||||
@ -512,6 +643,10 @@ AdditiveExpression:
|
|||||||
- right:
|
- right:
|
||||||
rule: Expression
|
rule: Expression
|
||||||
optional: true
|
optional: true
|
||||||
|
AdditiveOperator:
|
||||||
|
leaf_rules:
|
||||||
|
- Add
|
||||||
|
- Subtract
|
||||||
MultiplicativeExpression:
|
MultiplicativeExpression:
|
||||||
children:
|
children:
|
||||||
- left:
|
- left:
|
||||||
@ -521,9 +656,9 @@ MultiplicativeExpression:
|
|||||||
optional: true
|
optional: true
|
||||||
- right:
|
- right:
|
||||||
rule: Expression
|
rule: Expression
|
||||||
|
optional: true
|
||||||
MultiplicativeOperator:
|
MultiplicativeOperator:
|
||||||
type: leaf_enum
|
leaf_rules:
|
||||||
rules:
|
|
||||||
- Multiply
|
- Multiply
|
||||||
- Divide
|
- Divide
|
||||||
- Modulo
|
- Modulo
|
||||||
@ -532,9 +667,10 @@ PrefixExpression:
|
|||||||
- operators:
|
- operators:
|
||||||
rule: PrefixOperator
|
rule: PrefixOperator
|
||||||
vec: true
|
vec: true
|
||||||
|
- right:
|
||||||
|
rule: SuffixExpression
|
||||||
PrefixOperator:
|
PrefixOperator:
|
||||||
type: leaf_enum
|
leaf_rules:
|
||||||
rules:
|
|
||||||
- Spread
|
- Spread
|
||||||
- Not
|
- Not
|
||||||
- Negative
|
- Negative
|
||||||
@ -546,16 +682,14 @@ SuffixExpression:
|
|||||||
rule: SuffixOperator
|
rule: SuffixOperator
|
||||||
vec: true
|
vec: true
|
||||||
SuffixOperator:
|
SuffixOperator:
|
||||||
type: leaf_enum
|
|
||||||
rules:
|
rules:
|
||||||
- PlusPlus
|
- PlusPlus:
|
||||||
- MinusMinus
|
child: false
|
||||||
- ObjectProperty:
|
- MinusMinus:
|
||||||
child: true
|
child: false
|
||||||
- ObjectIndex:
|
- ObjectProperty
|
||||||
child: true
|
- ObjectIndex
|
||||||
- Call:
|
- Call
|
||||||
child: true
|
|
||||||
ObjectProperty:
|
ObjectProperty:
|
||||||
children:
|
children:
|
||||||
- identifier
|
- identifier
|
||||||
@ -625,92 +759,48 @@ ClosureParameter:
|
|||||||
# Literals
|
# Literals
|
||||||
Literal:
|
Literal:
|
||||||
rules:
|
rules:
|
||||||
- NumberLiteral
|
- IntLiteral:
|
||||||
- StringLiteral
|
kind: int
|
||||||
- BooleanLiteral
|
- LongLiteral:
|
||||||
NumberLiteral:
|
kind: long
|
||||||
rules:
|
- DoubleLiteral:
|
||||||
- DoubleLiteral
|
kind: double
|
||||||
- LongLiteral
|
- SingleQuoteString:
|
||||||
- IntLiteral
|
kind: string
|
||||||
IntLiteral:
|
- DString
|
||||||
children:
|
|
||||||
- number_base
|
|
||||||
- literal:
|
|
||||||
build:
|
|
||||||
type: i32
|
|
||||||
from: parse_number_base
|
|
||||||
LongLiteral:
|
|
||||||
children:
|
|
||||||
- number_base
|
|
||||||
- literal:
|
|
||||||
build:
|
|
||||||
type: i64
|
|
||||||
from: parse_number_base
|
|
||||||
DoubleLiteral:
|
|
||||||
children:
|
|
||||||
- literal:
|
|
||||||
build:
|
|
||||||
type: f64
|
|
||||||
from: parse_whole_pair
|
|
||||||
NumberBase:
|
|
||||||
rules:
|
|
||||||
- BinaryBase
|
|
||||||
- HexadecimalBase
|
|
||||||
- DecimalBase
|
|
||||||
DecimalBase:
|
|
||||||
children:
|
|
||||||
- literal:
|
|
||||||
build:
|
|
||||||
type: string
|
|
||||||
from: whole_pair
|
|
||||||
BinaryBase:
|
|
||||||
children:
|
|
||||||
- binary_digits
|
|
||||||
BinaryDigits:
|
|
||||||
children:
|
|
||||||
- literal:
|
|
||||||
build:
|
|
||||||
type: string
|
|
||||||
from: whole_pair
|
|
||||||
HexadecimalBase:
|
|
||||||
children:
|
|
||||||
- hexadecimal_digits
|
|
||||||
HexadecimalDigits:
|
|
||||||
children:
|
|
||||||
- literal:
|
|
||||||
build:
|
|
||||||
type: string
|
|
||||||
from: whole_pair
|
|
||||||
StringLiteral:
|
|
||||||
rules:
|
|
||||||
- SingleQuoteString
|
|
||||||
- DoubleQuoteString
|
|
||||||
- BacktickString
|
- BacktickString
|
||||||
|
- BooleanLiteral:
|
||||||
|
kind: boolean
|
||||||
|
|
||||||
|
# Numbers
|
||||||
|
IntLiteral:
|
||||||
|
produce:
|
||||||
|
kind: int
|
||||||
|
LongLiteral:
|
||||||
|
produce:
|
||||||
|
kind: long
|
||||||
|
DoubleLiteral:
|
||||||
|
produce:
|
||||||
|
kind: double
|
||||||
|
|
||||||
|
# Strings
|
||||||
SingleQuoteString:
|
SingleQuoteString:
|
||||||
children:
|
produce:
|
||||||
- string_inner:
|
kind: string
|
||||||
optional: true
|
from: string_inner
|
||||||
DoubleQuoteString:
|
DString:
|
||||||
children:
|
children:
|
||||||
- inners:
|
- inners:
|
||||||
rule: DStringInner
|
rule: DStringInner
|
||||||
|
kind: string
|
||||||
vec: true
|
vec: true
|
||||||
- expressions:
|
- expressions:
|
||||||
rule: DStringExpression
|
rule: DStringExpression
|
||||||
vec: true
|
vec: true
|
||||||
StringInner:
|
|
||||||
children:
|
|
||||||
- literal:
|
|
||||||
build:
|
|
||||||
type: string
|
|
||||||
from: whole_pair
|
|
||||||
DStringInner:
|
DStringInner:
|
||||||
children:
|
produce:
|
||||||
- literal:
|
kind: string
|
||||||
build:
|
from: whole_pair
|
||||||
type: string
|
|
||||||
from: whole_pair
|
|
||||||
DStringExpression:
|
DStringExpression:
|
||||||
children:
|
children:
|
||||||
- expression
|
- expression
|
||||||
@ -718,19 +808,16 @@ BacktickString:
|
|||||||
children:
|
children:
|
||||||
- inners:
|
- inners:
|
||||||
rule: BacktickInner
|
rule: BacktickInner
|
||||||
|
kind: string
|
||||||
vec: true
|
vec: true
|
||||||
- expressions:
|
- expressions:
|
||||||
rule: DStringExpression
|
rule: DStringExpression
|
||||||
vec: true
|
vec: true
|
||||||
BacktickInner:
|
BacktickInner:
|
||||||
children:
|
produce:
|
||||||
- literal:
|
kind: string
|
||||||
build:
|
from: whole_pair
|
||||||
type: string
|
|
||||||
from: whole_pair
|
|
||||||
BooleanLiteral:
|
BooleanLiteral:
|
||||||
children:
|
produce:
|
||||||
- literal:
|
kind: boolean
|
||||||
build:
|
from: parse_whole_pair
|
||||||
type: boolean
|
|
||||||
from: parse_whole_pair
|
|
||||||
@ -148,8 +148,6 @@ Operator = {
|
|||||||
| RightShift
|
| RightShift
|
||||||
// unary prefix
|
// unary prefix
|
||||||
| Spread
|
| Spread
|
||||||
| BorrowMut
|
|
||||||
| Borrow
|
|
||||||
| Star
|
| Star
|
||||||
| Not
|
| Not
|
||||||
| Negative
|
| Negative
|
||||||
@ -197,18 +195,14 @@ IdentifierList = {
|
|||||||
~ ( "," ~ Identifier )*
|
~ ( "," ~ Identifier )*
|
||||||
}
|
}
|
||||||
|
|
||||||
ParenthesesTypeUseList = {
|
|
||||||
"("
|
|
||||||
~ TypeUseList
|
|
||||||
~ ")"
|
|
||||||
}
|
|
||||||
|
|
||||||
ParenthesesOptionalTypeUseList = {
|
ParenthesesOptionalTypeUseList = {
|
||||||
"("
|
"("
|
||||||
~ TypeUseList?
|
~ TypeUseList?
|
||||||
~ ")"
|
~ ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type Use
|
||||||
|
|
||||||
// In general:
|
// In general:
|
||||||
// Arguments = usage
|
// Arguments = usage
|
||||||
// Parameters = declaration
|
// Parameters = declaration
|
||||||
@ -229,28 +223,27 @@ PrimitiveType = {
|
|||||||
| Double
|
| Double
|
||||||
| Bool
|
| Bool
|
||||||
| String
|
| String
|
||||||
| Array ~ GenericArguments?
|
| TypedArray
|
||||||
| Any
|
| Any
|
||||||
| Void
|
| Void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypedArray = {
|
||||||
|
Array
|
||||||
|
~ GenericArguments?
|
||||||
|
}
|
||||||
|
|
||||||
InterfaceOrClassTypeUse = {
|
InterfaceOrClassTypeUse = {
|
||||||
Borrow*
|
FullyQualifiedName
|
||||||
~ Mut?
|
|
||||||
~ FullyQualifiedName
|
|
||||||
~ GenericArguments?
|
~ GenericArguments?
|
||||||
}
|
}
|
||||||
|
|
||||||
TupleTypeUse = {
|
TupleTypeUse = {
|
||||||
Borrow*
|
TupleArguments
|
||||||
~ Mut?
|
|
||||||
~ TupleArguments
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionTypeUse = {
|
FunctionTypeUse = {
|
||||||
Borrow*
|
Fn
|
||||||
~ FunctionTypeModifier?
|
|
||||||
~ Fn
|
|
||||||
~ GenericParameters?
|
~ GenericParameters?
|
||||||
~ Parameters
|
~ Parameters
|
||||||
~ ReturnType
|
~ ReturnType
|
||||||
@ -286,15 +279,6 @@ ImplementsList = {
|
|||||||
~ ( "+" ~ TypeUse )*
|
~ ( "+" ~ TypeUse )*
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function type modifier
|
|
||||||
|
|
||||||
FunctionTypeModifier = {
|
|
||||||
Cons
|
|
||||||
| Mut ~ Ref
|
|
||||||
| Mut
|
|
||||||
| Ref
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parameters
|
// Parameters
|
||||||
|
|
||||||
Parameters = {
|
Parameters = {
|
||||||
@ -317,13 +301,6 @@ Parameter = {
|
|||||||
ReturnType = {
|
ReturnType = {
|
||||||
"->"
|
"->"
|
||||||
~ TypeUse
|
~ TypeUse
|
||||||
~ RefList?
|
|
||||||
}
|
|
||||||
|
|
||||||
RefList = {
|
|
||||||
Ref
|
|
||||||
~ Identifier
|
|
||||||
~ ( "," ~ Identifier )
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Top-level constructs
|
// Top-level constructs
|
||||||
@ -808,22 +785,20 @@ ClosureParameter = {
|
|||||||
// Literals
|
// Literals
|
||||||
|
|
||||||
Literal = {
|
Literal = {
|
||||||
NumberLiteral
|
|
||||||
| StringLiteral
|
|
||||||
| BooleanLiteral
|
|
||||||
}
|
|
||||||
|
|
||||||
NumberLiteral = {
|
|
||||||
DoubleLiteral
|
DoubleLiteral
|
||||||
| LongLiteral
|
| LongLiteral
|
||||||
| IntLiteral
|
| IntLiteral
|
||||||
|
| SingleQuoteString
|
||||||
|
| DString
|
||||||
|
| BacktickString
|
||||||
|
| BooleanLiteral
|
||||||
}
|
}
|
||||||
|
|
||||||
IntLiteral = { NumberBase }
|
IntLiteral = { NumberBase }
|
||||||
|
|
||||||
LongLiteral = ${ NumberBase ~ "L" }
|
LongLiteral = ${ NumberBase ~ "L" }
|
||||||
|
|
||||||
DoubleLiteral = @{ DecimalBase ~ "." ~ DecimalBase}
|
DoubleLiteral = @{ DecimalBase ~ "." ~ DecimalBase }
|
||||||
|
|
||||||
NumberBase = {
|
NumberBase = {
|
||||||
BinaryBase
|
BinaryBase
|
||||||
@ -845,15 +820,9 @@ HexadecimalDigits = @{ HexadecimalDigit+ }
|
|||||||
|
|
||||||
HexadecimalDigit = { '0'..'9' | 'a'..'f' }
|
HexadecimalDigit = { '0'..'9' | 'a'..'f' }
|
||||||
|
|
||||||
StringLiteral = {
|
|
||||||
SingleQuoteString
|
|
||||||
| DoubleQuoteString
|
|
||||||
| BacktickString
|
|
||||||
}
|
|
||||||
|
|
||||||
SingleQuoteString = { "'" ~ StringInner? ~ "'" }
|
SingleQuoteString = { "'" ~ StringInner? ~ "'" }
|
||||||
|
|
||||||
DoubleQuoteString = {
|
DString = {
|
||||||
"\""
|
"\""
|
||||||
~ ( DStringInner? ~ DStringExpression )*
|
~ ( DStringInner? ~ DStringExpression )*
|
||||||
~ DStringInner?
|
~ DStringInner?
|
||||||
|
|||||||
@ -7,7 +7,6 @@ pub struct DeimosParser;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod deimos_parser_tests {
|
mod deimos_parser_tests {
|
||||||
use crate::parser::{DeimosParser, Rule};
|
use crate::parser::{DeimosParser, Rule};
|
||||||
use indoc::indoc;
|
|
||||||
use pest::Parser;
|
use pest::Parser;
|
||||||
|
|
||||||
macro_rules! fail_rule {
|
macro_rules! fail_rule {
|
||||||
@ -45,160 +44,7 @@ mod deimos_parser_tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
mod generated_tests {
|
||||||
fn hex_int() {
|
include!(concat!(env!("OUT_DIR"), "/src/parser/tests/test_suites.rs"));
|
||||||
parses_to(Rule::IntLiteral, "0x1234abcd");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_long() {
|
|
||||||
parses_to(Rule::LongLiteral, "0x123456789abcdefL");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn suffix_expression_call_single_identifier() {
|
|
||||||
parses_to(Rule::SuffixExpression, "foo()");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simple_interface() {
|
|
||||||
parses_to(Rule::CompilationUnit, "pub int Simple { fn foo() -> Void }");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn interface_with_op() {
|
|
||||||
parses_to(
|
|
||||||
Rule::CompilationUnit,
|
|
||||||
"pub int Callable { op () () -> Void }",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn interface_with_alias() {
|
|
||||||
parses_to(
|
|
||||||
Rule::CompilationUnit,
|
|
||||||
indoc! {"
|
|
||||||
pub int Callable {
|
|
||||||
fn call() -> Void
|
|
||||||
def op () () -> Void alias call
|
|
||||||
}
|
|
||||||
"},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn index_identifier() {
|
|
||||||
parses_to(Rule::SuffixExpression, "foo[0]");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn chained_index_call_on_identifier() {
|
|
||||||
parses_to(Rule::SuffixExpression, "foo[0]()");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_statement() {
|
|
||||||
parses_to(
|
|
||||||
Rule::IfStatement,
|
|
||||||
indoc! {"
|
|
||||||
if (foo == 42) {
|
|
||||||
bar()
|
|
||||||
}"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_else_statement() {
|
|
||||||
parses_to(
|
|
||||||
Rule::IfElseStatement,
|
|
||||||
indoc! {"
|
|
||||||
if (foo == 42) {
|
|
||||||
bar()
|
|
||||||
} else {
|
|
||||||
baz()
|
|
||||||
}"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_else_if_statement() {
|
|
||||||
parses_to(
|
|
||||||
Rule::IfElseStatement,
|
|
||||||
indoc! {"
|
|
||||||
if (foo == 42) {
|
|
||||||
bar()
|
|
||||||
} else if (foo == 16) {
|
|
||||||
baz()
|
|
||||||
}"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_else_if_else_statement() {
|
|
||||||
parses_to(
|
|
||||||
Rule::IfElseStatement,
|
|
||||||
indoc! {"
|
|
||||||
if (foo == 42) {
|
|
||||||
foo()
|
|
||||||
} else if (foo == 16) {
|
|
||||||
baz()
|
|
||||||
} else {
|
|
||||||
fizz()
|
|
||||||
}"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn while_statement() {
|
|
||||||
parses_to(Rule::WhileStatement, "while (foo) { bar() }");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn for_statement() {
|
|
||||||
parses_to(Rule::ForStatement, "for (foo in bar) { baz(foo); }");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_statement_with_call_condition() {
|
|
||||||
parses_to(
|
|
||||||
Rule::IfStatement,
|
|
||||||
indoc! {"
|
|
||||||
if (foo()) {
|
|
||||||
bar()
|
|
||||||
}
|
|
||||||
"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn while_statement_with_call_condition() {
|
|
||||||
parses_to(Rule::WhileStatement, "while (foo()) { bar(); }")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn for_statement_with_call_iterator() {
|
|
||||||
parses_to(
|
|
||||||
Rule::ForStatement,
|
|
||||||
indoc! {"
|
|
||||||
for (foo in bar()) {
|
|
||||||
baz(foo);
|
|
||||||
}
|
|
||||||
"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn use_statement() {
|
|
||||||
parses_to(Rule::UseStatement, "use std::core::println");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn use_star() {
|
|
||||||
parses_to(Rule::UseStatement, "use std::core::*")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn use_list() {
|
|
||||||
parses_to(Rule::UseStatement, "use std::core::{print, println}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/parser/tests/backtick_string/empty
Normal file
1
src/parser/tests/backtick_string/empty
Normal file
@ -0,0 +1 @@
|
|||||||
|
``
|
||||||
1
src/parser/tests/backtick_string/expressions
Normal file
1
src/parser/tests/backtick_string/expressions
Normal file
@ -0,0 +1 @@
|
|||||||
|
`${greeting}, ${world}!`
|
||||||
1
src/parser/tests/backtick_string/no_expressions
Normal file
1
src/parser/tests/backtick_string/no_expressions
Normal file
@ -0,0 +1 @@
|
|||||||
|
`Hello, World!`
|
||||||
1
src/parser/tests/int_literal/hex_int
Normal file
1
src/parser/tests/int_literal/hex_int
Normal file
@ -0,0 +1 @@
|
|||||||
|
0x1234abcd
|
||||||
3
src/parser/tests/interface/simple
Normal file
3
src/parser/tests/interface/simple
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub int Simple
|
||||||
|
fn foo() -> Void
|
||||||
|
end
|
||||||
4
src/parser/tests/interface/with_alias
Normal file
4
src/parser/tests/interface/with_alias
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub int Callable
|
||||||
|
fn call() -> Void
|
||||||
|
def op () () -> Void alias call
|
||||||
|
end
|
||||||
3
src/parser/tests/interface/with_op
Normal file
3
src/parser/tests/interface/with_op
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub int Callable
|
||||||
|
op () () -> Void
|
||||||
|
end
|
||||||
1
src/parser/tests/long_literal/hex_long
Normal file
1
src/parser/tests/long_literal/hex_long
Normal file
@ -0,0 +1 @@
|
|||||||
|
0x123456789abcdefL
|
||||||
@ -1,14 +1,14 @@
|
|||||||
use crate::name_analysis::symbol::{FunctionSymbol, ParameterSymbol};
|
// use crate::name_analysis::symbol::{FunctionSymbol, ParameterSymbol};
|
||||||
use crate::name_analysis::symbol_table::{SymbolInsertError, SymbolTable};
|
// use crate::name_analysis::symbol_table::{SymbolInsertError, SymbolTable};
|
||||||
|
//
|
||||||
pub fn add_std_core_symbols(symbol_table: &mut SymbolTable) -> Result<(), SymbolInsertError> {
|
// pub fn add_std_core_symbols(symbol_table: &mut SymbolTable) -> Result<(), SymbolInsertError> {
|
||||||
symbol_table.insert_function_symbol(
|
// symbol_table.insert_function_symbol(
|
||||||
FunctionSymbol::new("std::core::println", "println", true, true, None)
|
// FunctionSymbol::new("std::core::println", "println", true, true, None)
|
||||||
.with_parameters(vec![ParameterSymbol::new("msg", None)]),
|
// .with_parameters(vec![ParameterSymbol::new("msg", None)]),
|
||||||
)?;
|
// )?;
|
||||||
symbol_table.insert_function_symbol(
|
// symbol_table.insert_function_symbol(
|
||||||
FunctionSymbol::new("std::core::print", "print", true, true, None)
|
// FunctionSymbol::new("std::core::print", "print", true, true, None)
|
||||||
.with_parameters(vec![ParameterSymbol::new("msg", None)]),
|
// .with_parameters(vec![ParameterSymbol::new("msg", None)]),
|
||||||
)?;
|
// )?;
|
||||||
Ok(())
|
// Ok(())
|
||||||
}
|
// }
|
||||||
|
|||||||
@ -5,9 +5,13 @@ pub struct IndentWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IndentWriter {
|
impl IndentWriter {
|
||||||
pub fn new(start_level: usize, indent_string: &str, out: Box<dyn std::io::Write>) -> IndentWriter {
|
pub fn new(
|
||||||
|
start_level: usize,
|
||||||
|
indent_string: &str,
|
||||||
|
out: Box<dyn std::io::Write>,
|
||||||
|
) -> IndentWriter {
|
||||||
IndentWriter {
|
IndentWriter {
|
||||||
indent_level: start_level,
|
indent_level: start_level,
|
||||||
indent_string: indent_string.to_string(),
|
indent_string: indent_string.to_string(),
|
||||||
out,
|
out,
|
||||||
}
|
}
|
||||||
@ -24,7 +28,7 @@ impl IndentWriter {
|
|||||||
pub fn write(&mut self, s: &str) -> std::io::Result<()> {
|
pub fn write(&mut self, s: &str) -> std::io::Result<()> {
|
||||||
write!(self.out, "{}", s)
|
write!(self.out, "{}", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn writeln(&mut self, s: &str) -> std::io::Result<()> {
|
pub fn writeln(&mut self, s: &str) -> std::io::Result<()> {
|
||||||
self.write(&format!("{}\n", s))
|
self.write(&format!("{}\n", s))
|
||||||
}
|
}
|
||||||
@ -35,7 +39,7 @@ impl IndentWriter {
|
|||||||
}
|
}
|
||||||
write!(self.out, "{}", s)
|
write!(self.out, "{}", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn writeln_indented(&mut self, s: &str) -> std::io::Result<()> {
|
pub fn writeln_indented(&mut self, s: &str) -> std::io::Result<()> {
|
||||||
self.write_indented(&format!("{}\n", s))
|
self.write_indented(&format!("{}\n", s))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum DvmConstant {
|
pub enum DvmConstant {
|
||||||
String(String),
|
String(String),
|
||||||
Array(DvmConstantArray)
|
Array(DvmConstantArray),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -13,5 +13,5 @@ pub enum DvmConstantArray {
|
|||||||
USizes(Vec<usize>),
|
USizes(Vec<usize>),
|
||||||
Booleans(Vec<bool>),
|
Booleans(Vec<bool>),
|
||||||
Strings(Vec<String>),
|
Strings(Vec<String>),
|
||||||
Arrays(Vec<DvmConstantArray>)
|
Arrays(Vec<DvmConstantArray>),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,7 @@ impl DvmFunction {
|
|||||||
pub fn instructions(&self) -> &Vec<Instruction> {
|
pub fn instructions(&self) -> &Vec<Instruction> {
|
||||||
&self.instructions
|
&self.instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn source_code_location(&self) -> &SourceCodeLocation {
|
pub fn source_code_location(&self) -> &SourceCodeLocation {
|
||||||
&self.source_code_location
|
&self.source_code_location
|
||||||
}
|
}
|
||||||
|
|||||||
@ -111,8 +111,8 @@ pub enum Instruction {
|
|||||||
},
|
},
|
||||||
Return,
|
Return,
|
||||||
DumpState {
|
DumpState {
|
||||||
message: String
|
message: String,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|||||||
@ -21,7 +21,7 @@ impl DvmMethod {
|
|||||||
implements,
|
implements,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fqn(&self) -> &str {
|
pub fn fqn(&self) -> &str {
|
||||||
self.function.fqn()
|
self.function.fqn()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ impl SourceCodeLocation {
|
|||||||
SourceCodeLocation {
|
SourceCodeLocation {
|
||||||
source_file_name: source_file_name.to_string(),
|
source_file_name: source_file_name.to_string(),
|
||||||
line,
|
line,
|
||||||
col
|
col,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,9 +7,9 @@ pub enum DvmType {
|
|||||||
Double,
|
Double,
|
||||||
Boolean,
|
Boolean,
|
||||||
USize,
|
USize,
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
Object,
|
Object,
|
||||||
Array,
|
Array,
|
||||||
ConstantPointer
|
ConstantPointer,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,7 +70,7 @@ impl DvmValue {
|
|||||||
panic!("Expected DvmValue::Int, but found DvmValue::{:?}", self);
|
panic!("Expected DvmValue::Int, but found DvmValue::{:?}", self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expect_int_or_else(&self, f: impl FnOnce(&Self) -> i32) -> i32 {
|
pub fn expect_int_or_else(&self, f: impl FnOnce(&Self) -> i32) -> i32 {
|
||||||
if let DvmValue::Int(i) = self {
|
if let DvmValue::Int(i) = self {
|
||||||
*i
|
*i
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user