Compare commits
157 Commits
main
...
grammar-ov
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ba79be920 | ||
|
|
8eb13791ee | ||
|
|
7ffa516c03 | ||
|
|
734a00ea92 | ||
|
|
8d73a8ea73 | ||
|
|
70aea0ba6f | ||
|
|
f656d0d4d4 | ||
|
|
026bc68fa7 | ||
|
|
9dfdcd716b | ||
|
|
f21128fd68 | ||
|
|
803b95c5dc | ||
|
|
053d08849e | ||
|
|
7439ca554c | ||
|
|
51c39f5f34 | ||
|
|
1d11a45c68 | ||
|
|
da05bb101b | ||
|
|
6bcef184eb | ||
|
|
0550df534e | ||
|
|
13330300c1 | ||
|
|
12a3a61156 | ||
|
|
9b83a531ca | ||
|
|
e5c5be6d95 | ||
|
|
d653d26e14 | ||
|
|
5721bd1e83 | ||
|
|
8b374e1066 | ||
|
|
93c6a71185 | ||
|
|
664aebfd61 | ||
|
|
bdbc2543b8 | ||
|
|
b5c0e44eeb | ||
|
|
273d197841 | ||
|
|
af8f0b5dac | ||
|
|
d32580a1d4 | ||
|
|
65136c3a1c | ||
|
|
b52df2b452 | ||
|
|
434a113d97 | ||
|
|
d09d945323 | ||
|
|
34fae6ccca | ||
|
|
dd249dd5bd | ||
|
|
6b206605c1 | ||
|
|
b47dea9136 | ||
|
|
2b5be6ca49 | ||
|
|
f0772fbf11 | ||
|
|
5d41a22899 | ||
|
|
6d37545b35 | ||
|
|
3ab59961dd | ||
|
|
d5ac6dfc2d | ||
|
|
36e28ae4a9 | ||
|
|
9731bb38fe | ||
|
|
8969186467 | ||
|
|
e879ad2d90 | ||
|
|
54882b187c | ||
|
|
1f5d17ef79 | ||
|
|
e578250ee6 | ||
|
|
583136711a | ||
|
|
eaebf8c926 | ||
|
|
058b33ece5 | ||
|
|
c32ae72beb | ||
|
|
d6faa37515 | ||
|
|
e8a4268949 | ||
|
|
5b772443f8 | ||
|
|
dd0bee1c91 | ||
|
|
df8e2279dc | ||
|
|
6e37e3a5dd | ||
|
|
cfe24aa107 | ||
|
|
309149c7dd | ||
|
|
5b5386c7e3 | ||
|
|
41673a68f8 | ||
|
|
86331ee9b0 | ||
|
|
4eb48cc1a2 | ||
|
|
3159f119bc | ||
|
|
5a3403cc28 | ||
|
|
39e9c2ddd5 | ||
|
|
12c565d0e1 | ||
|
|
8a6e4277a7 | ||
|
|
0d2db659ca | ||
|
|
5f1233a393 | ||
|
|
2a2936ef02 | ||
|
|
c73bb50d6f | ||
|
|
7a7eda97e3 | ||
|
|
2986bbe37e | ||
|
|
1565abace5 | ||
|
|
a347e69f9d | ||
|
|
13e2ae6b0c | ||
|
|
4b59abc989 | ||
|
|
e21b428e26 | ||
|
|
eb83d1202a | ||
|
|
d7b01377d7 | ||
|
|
8aa4248e07 | ||
|
|
de021789c1 | ||
|
|
f3ebcd77bd | ||
|
|
5842304f0b | ||
|
|
2d8843b80d | ||
|
|
1b23fbf683 | ||
|
|
8143894257 | ||
|
|
4d2e76338a | ||
|
|
63643d86ba | ||
|
|
0f64fee5ef | ||
|
|
b5e6f1c502 | ||
|
|
aff2fe2a2b | ||
|
|
2176d0eb8d | ||
|
|
dad25dcbf2 | ||
|
|
fe2fff5882 | ||
|
|
e795664a09 | ||
|
|
49a96eba85 | ||
|
|
9e3d71d73b | ||
|
|
5ff14f9dea | ||
|
|
522869371e | ||
|
|
11f97a2174 | ||
|
|
26cb28307c | ||
|
|
cce927d964 | ||
|
|
7399a8748c | ||
|
|
a7eabae3e3 | ||
|
|
c94a698a52 | ||
|
|
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 | ||
|
|
cde6d18e5c | ||
|
|
41693788fc | ||
|
|
29a2d77b6f | ||
|
|
5c01589ca3 | ||
|
|
6652b9fc63 | ||
|
|
bae90b8b80 | ||
|
|
4bc89d5ca3 | ||
|
|
3f3df59761 | ||
|
|
3f534bf7fd | ||
|
|
17f5d2d62d | ||
|
|
59165f6235 | ||
|
|
4d70765d17 | ||
|
|
0a97cc01b9 | ||
|
|
0adb4bbe0e | ||
|
|
9f3f3e0f0d | ||
|
|
3b07cef209 | ||
|
|
a53388155a | ||
|
|
4dcb5ee783 |
56
Cargo.lock
generated
56
Cargo.lock
generated
@ -169,6 +169,17 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cst-test-generator"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deimos"
|
||||
version = "0.1.0"
|
||||
@ -176,6 +187,7 @@ dependencies = [
|
||||
"ast-generator",
|
||||
"clap",
|
||||
"codespan-reporting",
|
||||
"cst-test-generator",
|
||||
"indoc",
|
||||
"log",
|
||||
"pest",
|
||||
@ -271,28 +283,21 @@ version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.8.0"
|
||||
version = "2.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6"
|
||||
checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror",
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.8.0"
|
||||
version = "2.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5"
|
||||
checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
@ -300,9 +305,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.8.0"
|
||||
version = "2.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841"
|
||||
checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
@ -313,11 +318,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.8.0"
|
||||
version = "2.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
|
||||
checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
"sha2",
|
||||
]
|
||||
@ -407,26 +411,6 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
|
||||
@ -12,16 +12,17 @@ name = "dmc"
|
||||
path = "src/bin/dmc/main.rs"
|
||||
|
||||
[dependencies]
|
||||
pest = { version = "2.8.0" }
|
||||
pest = { version = "2.8.4" }
|
||||
clap = { version = "4.5.23", features = ["derive"] }
|
||||
pest_derive = { version = "2.8.0", features = ["grammar-extras"] }
|
||||
pest_derive = { version = "2.8.4", features = ["grammar-extras"] }
|
||||
codespan-reporting = "0.12.0"
|
||||
log = "0.4.27"
|
||||
indoc = "2.0.6"
|
||||
|
||||
[build-dependencies]
|
||||
ast-generator = { path = "ast-generator" }
|
||||
cst-test-generator = { path = "cst-test-generator" }
|
||||
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["ast-generator"]
|
||||
members = ["ast-generator", "cst-test-generator"]
|
||||
|
||||
87
ast-generator/src/ast_node/enum_ast_node.rs
Normal file
87
ast-generator/src/ast_node/enum_ast_node.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use crate::spec::tree_enum_spec::{EnumRuleChildKind, TreeEnumBuildSpec};
|
||||
use convert_case::{Case, Casing};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_enum_ast_node_impl(enum_spec: &TreeEnumBuildSpec) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", enum_spec.build());
|
||||
let match_arms = enum_spec
|
||||
.rules()
|
||||
.map(|rule| {
|
||||
let rule_ident = format_ident!("{}", rule.rule());
|
||||
match rule.child() {
|
||||
Some(child) => match child.kind() {
|
||||
EnumRuleChildKind::Node(node_child) => {
|
||||
let child_ident =
|
||||
format_ident!("{}", node_child.node_kind().to_case(Case::Snake));
|
||||
quote! {
|
||||
#type_ident::#rule_ident(#child_ident) => vec![
|
||||
#child_ident
|
||||
]
|
||||
}
|
||||
}
|
||||
_ => quote! {
|
||||
#type_ident::#rule_ident(_) => vec![]
|
||||
},
|
||||
},
|
||||
None => {
|
||||
quote! {
|
||||
#type_ident::#rule_ident => vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut_match_arms = enum_spec
|
||||
.rules()
|
||||
.map(|rule| {
|
||||
let rule_ident = format_ident!("{}", rule.rule());
|
||||
match rule.child() {
|
||||
Some(child) => match child.kind() {
|
||||
EnumRuleChildKind::Node(node_child) => {
|
||||
let child_ident =
|
||||
format_ident!("{}", node_child.node_kind().to_case(Case::Snake));
|
||||
quote! {
|
||||
#type_ident::#rule_ident(#child_ident) => {
|
||||
f(#child_ident as &'a mut dyn AstNode<'a>)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => quote! {
|
||||
#type_ident::#rule_ident(_) => {}
|
||||
},
|
||||
},
|
||||
None => {
|
||||
quote! {
|
||||
#type_ident::#rule_ident => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl<'a> AstNode<'a> for #type_ident {
|
||||
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>> {
|
||||
match self {
|
||||
#(#match_arms,)*
|
||||
}
|
||||
}
|
||||
|
||||
fn for_each_child_mut(&'a mut self, mut f: &mut dyn FnMut(&'a mut dyn AstNode<'a>)) {
|
||||
match self {
|
||||
#(#mut_match_arms,)*
|
||||
}
|
||||
}
|
||||
|
||||
fn as_node_ref(&'a self) -> AstNodeRef<'a> {
|
||||
AstNodeRef::#type_ident(&self)
|
||||
}
|
||||
|
||||
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a> {
|
||||
AstNodeRefMut::#type_ident(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
ast-generator/src/ast_node/leaf_enum_ast_node.rs
Normal file
24
ast-generator/src/ast_node/leaf_enum_ast_node.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use crate::spec::leaf_enum_spec::LeafEnumBuildSpec;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_leaf_enum_ast_node_impl(spec: &LeafEnumBuildSpec) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", spec.build());
|
||||
quote! {
|
||||
impl<'a> AstNode<'a> for #type_ident {
|
||||
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn for_each_child_mut(&'a mut self, mut f: &mut dyn FnMut(&'a mut dyn AstNode<'a>)) {}
|
||||
|
||||
fn as_node_ref(&'a self) -> AstNodeRef<'a> {
|
||||
AstNodeRef::#type_ident(&self)
|
||||
}
|
||||
|
||||
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a> {
|
||||
AstNodeRefMut::#type_ident(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
ast-generator/src/ast_node/leaf_struct_ast_node.rs
Normal file
24
ast-generator/src/ast_node/leaf_struct_ast_node.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use crate::spec::leaf_struct_spec::LeafStructBuildSpec;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_leaf_struct_ast_node_impl(spec: &LeafStructBuildSpec) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", spec.build());
|
||||
quote! {
|
||||
impl<'a> AstNode<'a> for #type_ident {
|
||||
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn for_each_child_mut(&'a mut self, mut f: &mut dyn FnMut(&'a mut dyn AstNode<'a>)) {}
|
||||
|
||||
fn as_node_ref(&'a self) -> AstNodeRef<'a> {
|
||||
AstNodeRef::#type_ident(&self)
|
||||
}
|
||||
|
||||
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a> {
|
||||
AstNodeRefMut::#type_ident(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
96
ast-generator/src/ast_node/mod.rs
Normal file
96
ast-generator/src/ast_node/mod.rs
Normal file
@ -0,0 +1,96 @@
|
||||
mod enum_ast_node;
|
||||
mod leaf_enum_ast_node;
|
||||
mod leaf_struct_ast_node;
|
||||
mod polymorphic_enum_inner_build_ast_node;
|
||||
mod polymorphic_enum_loop_ast_node;
|
||||
mod polymorphic_type_ast_node;
|
||||
mod struct_ast_node;
|
||||
|
||||
use crate::ast_node::enum_ast_node::make_enum_ast_node_impl;
|
||||
use crate::ast_node::leaf_enum_ast_node::make_leaf_enum_ast_node_impl;
|
||||
use crate::ast_node::leaf_struct_ast_node::make_leaf_struct_ast_node_impl;
|
||||
use crate::ast_node::polymorphic_enum_inner_build_ast_node::make_polymorphic_enum_inner_build_ast_node_impl;
|
||||
use crate::ast_node::polymorphic_enum_loop_ast_node::make_polymorphic_enum_loop_ast_node_impl;
|
||||
use crate::ast_node::polymorphic_type_ast_node::make_polymorphic_type_ast_node_impl;
|
||||
use crate::ast_node::struct_ast_node::make_struct_ast_node_impl;
|
||||
use crate::spec::BuildSpec;
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_ast_node_impl(build_spec: &BuildSpec) -> Option<TokenStream> {
|
||||
match build_spec {
|
||||
BuildSpec::Enum(enum_spec) => Some(make_enum_ast_node_impl(enum_spec)),
|
||||
BuildSpec::LeafEnum(leaf_enum) => Some(make_leaf_enum_ast_node_impl(leaf_enum)),
|
||||
BuildSpec::Struct(struct_spec) => Some(make_struct_ast_node_impl(struct_spec)),
|
||||
BuildSpec::LeafStruct(leaf_struct) => Some(make_leaf_struct_ast_node_impl(leaf_struct)),
|
||||
BuildSpec::Production(_) => None,
|
||||
BuildSpec::NodeProduction(_) => None,
|
||||
BuildSpec::PolymorphicType(polymorphic_type) => {
|
||||
Some(make_polymorphic_type_ast_node_impl(polymorphic_type))
|
||||
}
|
||||
BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop) => Some(
|
||||
make_polymorphic_enum_loop_ast_node_impl(polymorphic_enum_loop),
|
||||
),
|
||||
BuildSpec::PolymorphicPassThrough(_) => None,
|
||||
BuildSpec::PolymorphicEnumInnerBuild(polymorphic_enum_inner_build) => Some(
|
||||
make_polymorphic_enum_inner_build_ast_node_impl(polymorphic_enum_inner_build),
|
||||
),
|
||||
BuildSpec::PolymorphicLeafEnum(_) => None,
|
||||
BuildSpec::PolymorphicTreeEnum(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_type_ident(build_spec: &BuildSpec) -> Option<Ident> {
|
||||
match build_spec {
|
||||
BuildSpec::Struct(struct_spec) => Some(format_ident!("{}", struct_spec.build())),
|
||||
BuildSpec::LeafStruct(leaf_struct) => Some(format_ident!("{}", leaf_struct.build())),
|
||||
BuildSpec::Enum(enum_spec) => Some(format_ident!("{}", enum_spec.build())),
|
||||
BuildSpec::LeafEnum(leaf_enum) => Some(format_ident!("{}", leaf_enum.build())),
|
||||
BuildSpec::PolymorphicType(polymorphic_type) => {
|
||||
Some(format_ident!("{}", polymorphic_type.name()))
|
||||
}
|
||||
BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop) => {
|
||||
Some(format_ident!("{}", polymorphic_enum_loop.name()))
|
||||
}
|
||||
BuildSpec::PolymorphicPassThrough(_) => None,
|
||||
BuildSpec::Production(_) => None,
|
||||
BuildSpec::NodeProduction(_) => None,
|
||||
BuildSpec::PolymorphicEnumInnerBuild(polymorphic_enum_inner_build) => {
|
||||
Some(format_ident!("{}", polymorphic_enum_inner_build.name()))
|
||||
}
|
||||
BuildSpec::PolymorphicLeafEnum(_) => None,
|
||||
BuildSpec::PolymorphicTreeEnum(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_ast_enum_member(build_spec: &BuildSpec) -> Option<TokenStream> {
|
||||
make_type_ident(build_spec).map(|type_ident| {
|
||||
quote! {
|
||||
#type_ident(&'a #type_ident)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn make_ast_enum_mut_member(build_spec: &BuildSpec) -> Option<TokenStream> {
|
||||
make_type_ident(build_spec).map(|type_ident| {
|
||||
quote! {
|
||||
#type_ident(&'a mut #type_ident)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn make_ast_node_ref_unwrapper(build_spec: &BuildSpec) -> Option<TokenStream> {
|
||||
make_type_ident(build_spec).map(|type_ident| {
|
||||
quote! {
|
||||
AstNodeRef::#type_ident(inner) => *inner
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn make_ast_node_ref_mut_unwrapper(build_spec: &BuildSpec) -> Option<TokenStream> {
|
||||
make_type_ident(build_spec).map(|type_ident| {
|
||||
quote! {
|
||||
AstNodeRefMut::#type_ident(inner) => *inner
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
use convert_case::{Case, Casing};
|
||||
use crate::spec::polymorphic_enum_inner_build::{PolymorphicEnumInnerBuild, PolymorphicEnumInnerBuildMemberKind};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_polymorphic_enum_inner_build_ast_node_impl(
|
||||
spec: &PolymorphicEnumInnerBuild,
|
||||
) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", spec.name());
|
||||
|
||||
let match_arms = spec.members()
|
||||
.map(|member| {
|
||||
let member_ident = format_ident!("{}", member.name());
|
||||
match member.kind() {
|
||||
PolymorphicEnumInnerBuildMemberKind::Leaf => {
|
||||
quote! {
|
||||
#type_ident::#member_ident => vec![]
|
||||
}
|
||||
}
|
||||
PolymorphicEnumInnerBuildMemberKind::Struct => {
|
||||
let child_ident = format_ident!("{}", member.name().to_case(Case::Snake));
|
||||
quote! {
|
||||
#type_ident::#member_ident(#child_ident) => vec![
|
||||
#child_ident
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut_match_arms = spec.members()
|
||||
.map(|member| {
|
||||
let member_ident = format_ident!("{}", member.name());
|
||||
match member.kind() {
|
||||
PolymorphicEnumInnerBuildMemberKind::Leaf => {
|
||||
quote! {
|
||||
#type_ident::#member_ident => {}
|
||||
}
|
||||
}
|
||||
PolymorphicEnumInnerBuildMemberKind::Struct => {
|
||||
let child_ident = format_ident!("{}", member.name().to_case(Case::Snake));
|
||||
quote! {
|
||||
#type_ident::#member_ident(#child_ident) => {
|
||||
f(#child_ident as &'a mut dyn AstNode<'a>)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl<'a> AstNode<'a> for #type_ident {
|
||||
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>> {
|
||||
match self {
|
||||
#(#match_arms,)*
|
||||
}
|
||||
}
|
||||
|
||||
fn for_each_child_mut(&'a mut self, mut f: &mut dyn FnMut(&'a mut dyn AstNode<'a>)) {
|
||||
match self {
|
||||
#(#mut_match_arms,)*
|
||||
}
|
||||
}
|
||||
|
||||
fn as_node_ref(&'a self) -> AstNodeRef<'a> {
|
||||
AstNodeRef::#type_ident(&self)
|
||||
}
|
||||
|
||||
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a> {
|
||||
AstNodeRefMut::#type_ident(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
ast-generator/src/ast_node/polymorphic_enum_loop_ast_node.rs
Normal file
62
ast-generator/src/ast_node/polymorphic_enum_loop_ast_node.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use crate::spec::polymorphic_enum_loop_spec::{
|
||||
PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuildChild,
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_polymorphic_enum_loop_ast_node_impl(
|
||||
spec: &PolymorphicEnumLoopBuildSpec,
|
||||
) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", spec.name());
|
||||
let build_rule = spec
|
||||
.rules()
|
||||
.filter_map(|rule| match rule {
|
||||
PolymorphicEnumLoopRule::PassThrough(_) => None,
|
||||
PolymorphicEnumLoopRule::Build(build_rule) => Some(build_rule),
|
||||
})
|
||||
.next()
|
||||
.unwrap();
|
||||
let child_adders = build_rule
|
||||
.children()
|
||||
.map(|child| {
|
||||
let child_ident = match child {
|
||||
PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => {
|
||||
Some(format_ident!("{}", use_current.name()))
|
||||
}
|
||||
PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => {
|
||||
Some(format_ident!("{}", on_each.name()))
|
||||
}
|
||||
PolymorphicEnumLoopRuleBuildChild::Special(_) => None,
|
||||
};
|
||||
child_ident.map(|child_ident| {
|
||||
quote! {
|
||||
children.push(self.#child_ident() as &dyn AstNode);
|
||||
}
|
||||
})
|
||||
})
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl<'a> AstNode<'a> for #type_ident {
|
||||
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>> {
|
||||
let mut children = vec![];
|
||||
#(#child_adders;)*
|
||||
children
|
||||
}
|
||||
|
||||
fn for_each_child_mut(&'a mut self, mut f: &mut dyn FnMut(&'a mut dyn AstNode<'a>)) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn as_node_ref(&'a self) -> AstNodeRef<'a> {
|
||||
AstNodeRef::#type_ident(&self)
|
||||
}
|
||||
|
||||
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a> {
|
||||
AstNodeRefMut::#type_ident(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
55
ast-generator/src/ast_node/polymorphic_type_ast_node.rs
Normal file
55
ast-generator/src/ast_node/polymorphic_type_ast_node.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use crate::spec::polymorphic_type_spec::PolymorphicTypeBuildSpec;
|
||||
use convert_case::{Case, Casing};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_polymorphic_type_ast_node_impl(spec: &PolymorphicTypeBuildSpec) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", spec.name());
|
||||
let match_arms = spec
|
||||
.variants()
|
||||
.map(|variant| {
|
||||
let variant_ident = format_ident!("{}", variant.name());
|
||||
let child_ident = format_ident!("{}", variant.inner_kind().to_case(Case::Snake));
|
||||
quote! {
|
||||
#type_ident::#variant_ident(#child_ident) => vec![#child_ident]
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let match_arms_mut = spec
|
||||
.variants()
|
||||
.map(|variant| {
|
||||
let variant_ident = format_ident!("{}", variant.name());
|
||||
let child_ident = format_ident!("{}", variant.inner_kind().to_case(Case::Snake));
|
||||
quote! {
|
||||
#type_ident::#variant_ident(#child_ident) => {
|
||||
f(#child_ident as &'a mut dyn AstNode)
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl<'a> AstNode<'a> for #type_ident {
|
||||
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>> {
|
||||
match self {
|
||||
#(#match_arms,)*
|
||||
}
|
||||
}
|
||||
|
||||
fn for_each_child_mut(&'a mut self, mut f: &mut dyn FnMut(&'a mut dyn AstNode<'a>)) {
|
||||
match self {
|
||||
#(#match_arms_mut,)*
|
||||
}
|
||||
}
|
||||
|
||||
fn as_node_ref(&'a self) -> AstNodeRef<'a> {
|
||||
AstNodeRef::#type_ident(&self)
|
||||
}
|
||||
|
||||
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a> {
|
||||
AstNodeRefMut::#type_ident(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
86
ast-generator/src/ast_node/struct_ast_node.rs
Normal file
86
ast-generator/src/ast_node/struct_ast_node.rs
Normal file
@ -0,0 +1,86 @@
|
||||
use crate::spec::struct_spec::{MemberChildBuild, StructChild, StructSpec, VecChildBuild};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
fn make_child_adders(spec: &StructSpec, is_mut: bool) -> Vec<TokenStream> {
|
||||
let as_clause = if is_mut {
|
||||
quote! { as &mut dyn AstNode }
|
||||
} else {
|
||||
quote! { as &dyn AstNode }
|
||||
};
|
||||
|
||||
spec.children()
|
||||
.map(|child| match child {
|
||||
StructChild::SkipChild(_) => None,
|
||||
StructChild::VecChild(vec_child) => match vec_child.build() {
|
||||
VecChildBuild::String(_) => None,
|
||||
VecChildBuild::Node(_) => {
|
||||
let child_ident = if is_mut {
|
||||
format_ident!("{}_mut", vec_child.name())
|
||||
} else {
|
||||
format_ident!("{}", vec_child.name())
|
||||
};
|
||||
let children_stream = quote! {
|
||||
for child in self.#child_ident() {
|
||||
children.push(child #as_clause);
|
||||
}
|
||||
};
|
||||
Some(children_stream)
|
||||
}
|
||||
},
|
||||
StructChild::MemberChild(member_child) => match member_child.build() {
|
||||
MemberChildBuild::Node(_) => {
|
||||
let child_ident = if is_mut {
|
||||
format_ident!("{}_mut", member_child.name())
|
||||
} else {
|
||||
format_ident!("{}", member_child.name())
|
||||
};
|
||||
|
||||
if member_child.optional() {
|
||||
Some(quote! {
|
||||
if let Some(#child_ident) = self.#child_ident() {
|
||||
children.push(#child_ident #as_clause);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Some(quote! {
|
||||
children.push(self.#child_ident() #as_clause)
|
||||
})
|
||||
}
|
||||
}
|
||||
MemberChildBuild::Boolean(_) => None,
|
||||
},
|
||||
StructChild::Special(_) => None,
|
||||
})
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub fn make_struct_ast_node_impl(spec: &StructSpec) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", spec.build());
|
||||
let child_adders = make_child_adders(spec, false);
|
||||
let child_adders_mut = make_child_adders(spec, true);
|
||||
|
||||
quote! {
|
||||
impl<'a> AstNode<'a> for #type_ident {
|
||||
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>> {
|
||||
let mut children = vec![];
|
||||
#(#child_adders;)*
|
||||
children
|
||||
}
|
||||
|
||||
fn for_each_child_mut(&'a mut self, mut f: &mut dyn FnMut(&'a mut dyn AstNode<'a>)) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn as_node_ref(&'a self) -> AstNodeRef<'a> {
|
||||
AstNodeRef::#type_ident(&self)
|
||||
}
|
||||
|
||||
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a> {
|
||||
AstNodeRefMut::#type_ident(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
ast-generator/src/build_fn/leaf_enum_build_fn.rs
Normal file
30
ast-generator/src/build_fn/leaf_enum_build_fn.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use crate::deserialize::util::{make_build_fn_name, make_build_pair};
|
||||
use crate::spec::leaf_enum_spec::LeafEnumBuildSpec;
|
||||
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);
|
||||
quote! {
|
||||
Rule::#rule_ident => #return_type_ident::#rule_ident
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||
let inner_pair = #pair_ident.into_inner().next().unwrap();
|
||||
match inner_pair.as_rule() {
|
||||
#(#rule_branches,)*
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
54
ast-generator/src/build_fn/leaf_struct_build_fn.rs
Normal file
54
ast-generator/src/build_fn/leaf_struct_build_fn.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use crate::deserialize::util::{make_build_fn_name, make_build_pair};
|
||||
use crate::spec::leaf_struct_spec::{LeafStructBuildSpec, LeafStructMemberKind};
|
||||
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()
|
||||
}
|
||||
}
|
||||
LeafStructMemberKind::FileId => {
|
||||
quote! {
|
||||
let #child_ident = file_id
|
||||
}
|
||||
}
|
||||
LeafStructMemberKind::Range => {
|
||||
quote! {
|
||||
let #child_ident = {
|
||||
let as_span = #pair_ident.as_span();
|
||||
Range {
|
||||
start: as_span.start(),
|
||||
end: as_span.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let child_args = build_spec
|
||||
.members()
|
||||
.map(|member| format_ident!("{}", member.name()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||
#(#child_builders;)*
|
||||
#return_type_ident::new(
|
||||
#(#child_args,)*
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
58
ast-generator/src/build_fn/mod.rs
Normal file
58
ast-generator/src/build_fn/mod.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use crate::spec::BuildSpec;
|
||||
use leaf_enum_build_fn::make_leaf_enum_build_fn;
|
||||
use leaf_struct_build_fn::make_leaf_struct_build_fn;
|
||||
use node_production_build_fn::make_node_production_build_fn;
|
||||
use polymorphic_enum_inner_build_build_fn::make_polymorphic_enum_inner_build_build_fn;
|
||||
use polymorphic_enum_loop_build_fn::make_polymorphic_enum_loop_build_fn;
|
||||
use polymorphic_leaf_enum_build_fn::make_polymorphic_leaf_enum_build_fn;
|
||||
use polymorphic_pass_through_build_fn::make_polymorphic_pass_through_build_fn;
|
||||
use polymorphic_tree_enum_build_fn::make_polymorphic_tree_enum_build_fn;
|
||||
use polymorphic_type_build_fn::make_polymorphic_type_build_fn;
|
||||
use proc_macro2::TokenStream;
|
||||
use production_build_fn::make_production_build_fn;
|
||||
use struct_build_fn::make_struct_build_fn;
|
||||
use tree_enum_build_fn::make_enum_build_fn;
|
||||
|
||||
mod leaf_enum_build_fn;
|
||||
mod leaf_struct_build_fn;
|
||||
mod node_production_build_fn;
|
||||
mod polymorphic_enum_inner_build_build_fn;
|
||||
mod polymorphic_enum_loop_build_fn;
|
||||
mod polymorphic_leaf_enum_build_fn;
|
||||
mod polymorphic_pass_through_build_fn;
|
||||
mod polymorphic_tree_enum_build_fn;
|
||||
mod polymorphic_type_build_fn;
|
||||
mod production_build_fn;
|
||||
mod struct_build_fn;
|
||||
mod tree_enum_build_fn;
|
||||
|
||||
pub fn make_build_fn(build_spec: &BuildSpec) -> TokenStream {
|
||||
match build_spec {
|
||||
BuildSpec::Struct(struct_spec) => make_struct_build_fn(struct_spec),
|
||||
BuildSpec::LeafStruct(leaf_struct_spec) => make_leaf_struct_build_fn(leaf_struct_spec),
|
||||
BuildSpec::Enum(enum_spec) => make_enum_build_fn(enum_spec),
|
||||
BuildSpec::LeafEnum(leaf_enum_spec) => make_leaf_enum_build_fn(leaf_enum_spec),
|
||||
BuildSpec::Production(production_spec) => make_production_build_fn(production_spec),
|
||||
BuildSpec::NodeProduction(node_production_spec) => {
|
||||
make_node_production_build_fn(node_production_spec)
|
||||
}
|
||||
BuildSpec::PolymorphicType(polymorphic_type_spec) => {
|
||||
make_polymorphic_type_build_fn(polymorphic_type_spec)
|
||||
}
|
||||
BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop_spec) => {
|
||||
make_polymorphic_enum_loop_build_fn(polymorphic_enum_loop_spec)
|
||||
}
|
||||
BuildSpec::PolymorphicPassThrough(polymorphic_pass_through_spec) => {
|
||||
make_polymorphic_pass_through_build_fn(polymorphic_pass_through_spec)
|
||||
}
|
||||
BuildSpec::PolymorphicEnumInnerBuild(polymorphic_enum_inner_build_spec) => {
|
||||
make_polymorphic_enum_inner_build_build_fn(polymorphic_enum_inner_build_spec)
|
||||
}
|
||||
BuildSpec::PolymorphicLeafEnum(polymorphic_leaf_enum_spec) => {
|
||||
make_polymorphic_leaf_enum_build_fn(polymorphic_leaf_enum_spec)
|
||||
}
|
||||
BuildSpec::PolymorphicTreeEnum(polymorphic_tree_enum_spec) => {
|
||||
make_polymorphic_tree_enum_build_fn(polymorphic_tree_enum_spec)
|
||||
}
|
||||
}
|
||||
}
|
||||
18
ast-generator/src/build_fn/node_production_build_fn.rs
Normal file
18
ast-generator/src/build_fn/node_production_build_fn.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use crate::deserialize::util::{make_build_fn_name, make_build_pair};
|
||||
use crate::spec::node_production_spec::NodeProductionBuildSpec;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_node_production_build_fn(spec: &NodeProductionBuildSpec) -> TokenStream {
|
||||
let build_fn_ident = format_ident!("{}", make_build_fn_name(spec.name()));
|
||||
let pair_ident = format_ident!("{}", make_build_pair(spec.name()));
|
||||
let return_type_ident = format_ident!("{}", spec.kind());
|
||||
let inner_build_fn_ident = format_ident!("{}", spec.with());
|
||||
|
||||
quote! {
|
||||
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||
let inner_pair = #pair_ident.into_inner().next().unwrap();
|
||||
#inner_build_fn_ident(file_id, inner_pair)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
use crate::spec::polymorphic_enum_inner_build::PolymorphicEnumInnerBuild;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use crate::deserialize::util::{make_build_fn_name, make_build_pair};
|
||||
|
||||
pub fn make_polymorphic_enum_inner_build_build_fn(spec: &PolymorphicEnumInnerBuild) -> TokenStream {
|
||||
let build_fn_ident = format_ident!("{}", make_build_fn_name(spec.name()));
|
||||
let pair_ident = format_ident!("{}", make_build_pair(spec.name()));
|
||||
let return_type_ident = format_ident!("{}", spec.name());
|
||||
|
||||
let rule_branches = spec.rules()
|
||||
.map(|rule| {
|
||||
let rule_ident = format_ident!("{}", rule.name());
|
||||
let rule_build_fn_ident = format_ident!("{}", rule.with());
|
||||
quote! {
|
||||
Rule::#rule_ident => #rule_build_fn_ident(file_id, inner_pair)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||
let inner_pair = #pair_ident.into_inner().next().unwrap();
|
||||
match inner_pair.as_rule() {
|
||||
#(#rule_branches,)*
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
135
ast-generator/src/build_fn/polymorphic_enum_loop_build_fn.rs
Normal file
135
ast-generator/src/build_fn/polymorphic_enum_loop_build_fn.rs
Normal file
@ -0,0 +1,135 @@
|
||||
use crate::deserialize::util::{make_build_fn_name, make_build_pair};
|
||||
use crate::spec::polymorphic_enum_loop_spec::{
|
||||
PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuild,
|
||||
PolymorphicEnumLoopRuleBuildChild, PolymorphicEnumLoopRuleChildOnEach,
|
||||
PolymorphicEnumLoopRulePassThrough,
|
||||
};
|
||||
use crate::spec::SpecialChildKind;
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
fn make_pass_through(pass_through: &PolymorphicEnumLoopRulePassThrough) -> TokenStream {
|
||||
let rule_ident = format_ident!("{}", pass_through.name());
|
||||
let inner_build_fn_ident = format_ident!("{}", pass_through.with());
|
||||
quote! {
|
||||
Rule::#rule_ident => {
|
||||
result = Some(#inner_build_fn_ident(file_id, inner_pair))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_on_each_child_build(child: &PolymorphicEnumLoopRuleChildOnEach) -> TokenStream {
|
||||
let child_build_fn_ident = format_ident!("{}", make_build_fn_name(child.rule()));
|
||||
quote! {
|
||||
Box::new(#child_build_fn_ident(file_id, inner_pair))
|
||||
}
|
||||
}
|
||||
|
||||
fn make_build(
|
||||
spec: &PolymorphicEnumLoopBuildSpec,
|
||||
build: &PolymorphicEnumLoopRuleBuild,
|
||||
) -> TokenStream {
|
||||
let rule_ident = format_ident!("{}", build.name());
|
||||
let variant_type_ident = format_ident!("{}", spec.name());
|
||||
let return_type_ident = format_ident!("{}", spec.kind());
|
||||
let variant_ident = format_ident!("{}", build.variant());
|
||||
|
||||
let on_each_child = build
|
||||
.children()
|
||||
.find(|child| match child {
|
||||
PolymorphicEnumLoopRuleBuildChild::OnEach(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
.map(|child| match child {
|
||||
PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => on_each,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let build_on_each_child = make_on_each_child_build(on_each_child);
|
||||
|
||||
let child_args = build
|
||||
.children()
|
||||
.map(|child| match child {
|
||||
PolymorphicEnumLoopRuleBuildChild::UseCurrent(_) => {
|
||||
quote! { Box::new(result.unwrap()) }
|
||||
}
|
||||
PolymorphicEnumLoopRuleBuildChild::OnEach(_) => quote! { on_each_child },
|
||||
PolymorphicEnumLoopRuleBuildChild::Special(special_child) => {
|
||||
match special_child.kind() {
|
||||
SpecialChildKind::FileId => {
|
||||
quote! { file_id }
|
||||
}
|
||||
SpecialChildKind::Range => {
|
||||
quote! { Range { start: as_span.start(), end: as_span.end() } }
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
Rule::#rule_ident => {
|
||||
let on_each_child = #build_on_each_child;
|
||||
let built = #variant_type_ident::new(#(#child_args),*);
|
||||
result = Some(#return_type_ident::#variant_ident(built))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_match_arm(
|
||||
spec: &PolymorphicEnumLoopBuildSpec,
|
||||
rule: &PolymorphicEnumLoopRule,
|
||||
) -> TokenStream {
|
||||
match rule {
|
||||
PolymorphicEnumLoopRule::PassThrough(pass_through) => make_pass_through(pass_through),
|
||||
PolymorphicEnumLoopRule::Build(build) => make_build(spec, build),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_preamble(spec: &PolymorphicEnumLoopBuildSpec, pair_ident: &Ident) -> TokenStream {
|
||||
if spec.rules().any(|rule| match rule {
|
||||
PolymorphicEnumLoopRule::Build(build) => build.children().any(|child| match child {
|
||||
PolymorphicEnumLoopRuleBuildChild::Special(_) => true,
|
||||
_ => false,
|
||||
}),
|
||||
_ => false,
|
||||
}) {
|
||||
quote! { let as_span = #pair_ident.as_span(); }
|
||||
} else {
|
||||
quote! {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_polymorphic_enum_loop_build_fn(spec: &PolymorphicEnumLoopBuildSpec) -> TokenStream {
|
||||
let build_fn_ident = format_ident!("{}", make_build_fn_name(spec.name()));
|
||||
let pair_ident = format_ident!("{}", make_build_pair(spec.name()));
|
||||
let return_type_ident = format_ident!("{}", spec.kind());
|
||||
|
||||
let preamble = make_preamble(spec, &pair_ident);
|
||||
|
||||
let iter_expr = if spec.reverse() {
|
||||
quote! { #pair_ident.into_inner().rev() }
|
||||
} else {
|
||||
quote! { #pair_ident.into_inner() }
|
||||
};
|
||||
|
||||
let match_arms = spec
|
||||
.rules()
|
||||
.map(|rule| make_match_arm(spec, rule))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||
#preamble
|
||||
let mut result: Option<#return_type_ident> = None;
|
||||
for inner_pair in #iter_expr {
|
||||
match inner_pair.as_rule() {
|
||||
#(#match_arms,)*
|
||||
_ => unreachable!("Unexpected parse rule: {:?} (inner pair: {:#?}", inner_pair.as_rule(), inner_pair),
|
||||
}
|
||||
}
|
||||
result.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
29
ast-generator/src/build_fn/polymorphic_leaf_enum_build_fn.rs
Normal file
29
ast-generator/src/build_fn/polymorphic_leaf_enum_build_fn.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use crate::spec::polymorphic_leaf_enum::PolymorphicLeafEnum;
|
||||
use convert_case::{Case, Casing};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_polymorphic_leaf_enum_build_fn(spec: &PolymorphicLeafEnum) -> TokenStream {
|
||||
let build_fn_ident = format_ident!("build_{}", spec.name().to_case(Case::Snake));
|
||||
let pair_ident = format_ident!("{}_pair", spec.name().to_case(Case::Snake));
|
||||
let return_type_ident = format_ident!("{}", spec.kind());
|
||||
|
||||
let child_matchers = spec.rules()
|
||||
.map(|rule| {
|
||||
let rule_ident = format_ident!("{}", rule);
|
||||
quote! {
|
||||
Rule::#rule_ident => #return_type_ident::#rule_ident
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||
let inner_pair = #pair_ident.into_inner().next().unwrap();
|
||||
match inner_pair.as_rule() {
|
||||
#(#child_matchers,)*
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
use crate::deserialize::util::{make_build_fn_name, make_build_pair};
|
||||
use crate::spec::polymorphic_pass_through_spec::{
|
||||
PolymorphicPassThroughBuildSpec, PolymorphicPassThroughVariant,
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_polymorphic_pass_through_build_fn(
|
||||
spec: &PolymorphicPassThroughBuildSpec,
|
||||
) -> TokenStream {
|
||||
let build_fn_ident = format_ident!("{}", make_build_fn_name(spec.name()));
|
||||
let pair_ident = format_ident!("{}", make_build_pair(spec.name()));
|
||||
let return_type_ident = format_ident!("{}", spec.build_kind());
|
||||
|
||||
let match_arms = spec
|
||||
.variants()
|
||||
.map(|variant| match variant {
|
||||
PolymorphicPassThroughVariant::Inner { name, kind } => {
|
||||
let rule_ident = format_ident!("{}", kind);
|
||||
let variant_ident = format_ident!("{}", name);
|
||||
let inner_build_fn_ident = format_ident!("{}", make_build_fn_name(kind));
|
||||
|
||||
quote! {
|
||||
Rule::#rule_ident => {
|
||||
#return_type_ident::#variant_ident(
|
||||
#inner_build_fn_ident(file_id, inner_pair)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
PolymorphicPassThroughVariant::PassThrough { name, kind: _kind } => {
|
||||
let rule_ident = format_ident!("{}", name);
|
||||
let inner_build_fn_ident = format_ident!("{}", make_build_fn_name(name));
|
||||
|
||||
quote! {
|
||||
Rule::#rule_ident => #inner_build_fn_ident(file_id, inner_pair)
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||
let inner_pair = #pair_ident.into_inner().next().unwrap();
|
||||
match inner_pair.as_rule() {
|
||||
#(#match_arms,)*
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
33
ast-generator/src/build_fn/polymorphic_tree_enum_build_fn.rs
Normal file
33
ast-generator/src/build_fn/polymorphic_tree_enum_build_fn.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use crate::spec::polymorphic_tree_enum_spec::PolymorphicTreeEnumSpec;
|
||||
use convert_case::{Case, Casing};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_polymorphic_tree_enum_build_fn(spec: &PolymorphicTreeEnumSpec) -> TokenStream {
|
||||
let build_fn_ident = format_ident!("build_{}", spec.name().to_case(Case::Snake));
|
||||
let pair_ident = format_ident!("{}_pair", spec.name().to_case(Case::Snake));
|
||||
let return_type_ident = format_ident!("{}", spec.kind());
|
||||
|
||||
let rule_matchers = spec
|
||||
.rules()
|
||||
.map(|rule| {
|
||||
let rule_ident = format_ident!("{}", rule);
|
||||
let inner_build_fn_ident = format_ident!("build_{}", rule.to_case(Case::Snake));
|
||||
quote! {
|
||||
Rule::#rule_ident => #return_type_ident::#rule_ident(
|
||||
#inner_build_fn_ident(file_id, inner_pair)
|
||||
)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||
let inner_pair = #pair_ident.into_inner().next().unwrap();
|
||||
match inner_pair.as_rule() {
|
||||
#(#rule_matchers,)*
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
ast-generator/src/build_fn/polymorphic_type_build_fn.rs
Normal file
18
ast-generator/src/build_fn/polymorphic_type_build_fn.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use crate::deserialize::util::{make_build_fn_name, make_build_pair};
|
||||
use crate::spec::polymorphic_type_spec::PolymorphicTypeBuildSpec;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_polymorphic_type_build_fn(build_spec: &PolymorphicTypeBuildSpec) -> TokenStream {
|
||||
let build_fn_ident = format_ident!("{}", make_build_fn_name(build_spec.name()));
|
||||
let pair_ident = format_ident!("{}", make_build_pair(&build_spec.name()));
|
||||
let return_type_ident = format_ident!("{}", build_spec.name());
|
||||
let inner_build_fn_ident = format_ident!("{}", make_build_fn_name(build_spec.kind()));
|
||||
|
||||
quote! {
|
||||
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||
let inner_pair = #pair_ident.into_inner().next().unwrap();
|
||||
#inner_build_fn_ident(file_id, inner_pair)
|
||||
}
|
||||
}
|
||||
}
|
||||
87
ast-generator/src/build_fn/production_build_fn.rs
Normal file
87
ast-generator/src/build_fn/production_build_fn.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use crate::deserialize::util::{make_build_fn_name, make_build_pair};
|
||||
use crate::spec::production_spec::{ProductionBuildSpec, ProductionKind, ProductionStringFrom};
|
||||
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.name()));
|
||||
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.name()));
|
||||
|
||||
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(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||
#pair_mapper
|
||||
}
|
||||
}
|
||||
}
|
||||
273
ast-generator/src/build_fn/struct_build_fn.rs
Normal file
273
ast-generator/src/build_fn/struct_build_fn.rs
Normal file
@ -0,0 +1,273 @@
|
||||
use crate::deserialize::util::{make_build_fn_name, make_build_pair};
|
||||
use crate::spec::struct_spec::{
|
||||
MemberChildBuild, NodeMemberBuild, StructChild, StructSpec, VecChild, VecChildBuild,
|
||||
};
|
||||
use crate::spec::{SpecialChild, SpecialChildKind};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
fn make_preamble(spec: &StructSpec, pair_ident: &Ident) -> TokenStream {
|
||||
if spec.children().any(StructChild::is_special) {
|
||||
quote! {
|
||||
let as_span = #pair_ident.as_span();
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_special_children(spec: &StructSpec) -> Vec<TokenStream> {
|
||||
spec.children()
|
||||
.map(StructChild::unwrap_special)
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.map(|special_child| {
|
||||
let child_ident = format_ident!("{}", special_child.name());
|
||||
match special_child.kind() {
|
||||
SpecialChildKind::FileId => {
|
||||
quote! {
|
||||
let #child_ident = file_id
|
||||
}
|
||||
}
|
||||
SpecialChildKind::Range => {
|
||||
quote! {
|
||||
let #child_ident = Range {
|
||||
start: as_span.start(),
|
||||
end: as_span.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn make_vec_child_holder(vec_child: &VecChild) -> TokenStream {
|
||||
let child_ident = format_ident!("{}", vec_child.name());
|
||||
match vec_child.build() {
|
||||
VecChildBuild::String(_) => {
|
||||
quote! {
|
||||
let mut #child_ident: Vec<String> = vec![]
|
||||
}
|
||||
}
|
||||
VecChildBuild::Node(node_build) => {
|
||||
let child_type_ident = format_ident!("{}", node_build.kind());
|
||||
quote! {
|
||||
let mut #child_ident: Vec<Box<#child_type_ident>> = vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_node_child_holder(name: &str, node_child: &NodeMemberBuild) -> TokenStream {
|
||||
let child_ident = format_ident!("{}", name);
|
||||
let child_type_ident = format_ident!("{}", node_child.kind());
|
||||
quote! {
|
||||
let mut #child_ident: Option<Box<#child_type_ident>> = None
|
||||
}
|
||||
}
|
||||
|
||||
fn make_boolean_child_holder(name: &str) -> TokenStream {
|
||||
let child_ident = format_ident!("{}", name);
|
||||
quote! {
|
||||
let mut #child_ident: bool = false
|
||||
}
|
||||
}
|
||||
|
||||
fn make_child_holder(child_spec: &StructChild) -> Option<TokenStream> {
|
||||
match child_spec {
|
||||
StructChild::SkipChild(_) => None,
|
||||
StructChild::VecChild(vec_child) => Some(make_vec_child_holder(vec_child)),
|
||||
StructChild::MemberChild(member_child) => match member_child.build() {
|
||||
MemberChildBuild::Node(node_child) => {
|
||||
Some(make_node_child_holder(member_child.name(), node_child))
|
||||
}
|
||||
MemberChildBuild::Boolean(_) => Some(make_boolean_child_holder(member_child.name())),
|
||||
},
|
||||
StructChild::Special(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_vec_child_match_action(vec_child: &VecChild) -> TokenStream {
|
||||
let child_name_ident = format_ident!("{}", vec_child.name());
|
||||
match vec_child.build() {
|
||||
VecChildBuild::String(string_build) => {
|
||||
let build_fn_ident = format_ident!("{}", string_build.with());
|
||||
quote! {
|
||||
#child_name_ident.push(#build_fn_ident(file_id, inner_pair))
|
||||
}
|
||||
}
|
||||
VecChildBuild::Node(node_build) => {
|
||||
let build_fn_ident = format_ident!("{}", node_build.with());
|
||||
quote! {
|
||||
#child_name_ident.push(Box::new(#build_fn_ident(file_id, inner_pair)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_node_member_child_match_action(name: &str, node_child: &NodeMemberBuild) -> TokenStream {
|
||||
let child_name_ident = format_ident!("{}", name);
|
||||
let build_fn_ident = format_ident!("{}", node_child.with());
|
||||
quote! {
|
||||
#child_name_ident = Some(Box::new(#build_fn_ident(file_id, inner_pair)))
|
||||
}
|
||||
}
|
||||
|
||||
fn make_boolean_member_child_match_action(name: &str) -> TokenStream {
|
||||
let child_name_ident = format_ident!("{}", name);
|
||||
quote! {
|
||||
#child_name_ident = true
|
||||
}
|
||||
}
|
||||
|
||||
fn make_rule_matcher(child_spec: &StructChild) -> Option<TokenStream> {
|
||||
match child_spec {
|
||||
StructChild::SkipChild(skip_child) => {
|
||||
let rule_ident = format_ident!("{}", skip_child.rule());
|
||||
Some(quote! {
|
||||
Rule::#rule_ident => {}
|
||||
})
|
||||
}
|
||||
StructChild::VecChild(vec_child) => {
|
||||
let rule_ident = format_ident!("{}", vec_child.rule());
|
||||
let action = make_vec_child_match_action(vec_child);
|
||||
Some(quote! {
|
||||
Rule::#rule_ident => { #action }
|
||||
})
|
||||
}
|
||||
StructChild::MemberChild(member_child) => match member_child.build() {
|
||||
MemberChildBuild::Node(node_member_build) => {
|
||||
let rule_ident = format_ident!("{}", member_child.rule());
|
||||
let action =
|
||||
make_node_member_child_match_action(member_child.name(), node_member_build);
|
||||
Some(quote! {
|
||||
Rule::#rule_ident => { #action }
|
||||
})
|
||||
}
|
||||
MemberChildBuild::Boolean(_) => {
|
||||
let rule_ident = format_ident!("{}", member_child.rule());
|
||||
let action = make_boolean_member_child_match_action(member_child.name());
|
||||
Some(quote! {
|
||||
Rule::#rule_ident => { #action }
|
||||
})
|
||||
}
|
||||
},
|
||||
StructChild::Special(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
optional: bool,
|
||||
node_child: &NodeMemberBuild,
|
||||
) -> TokenStream {
|
||||
let child_ident = format_ident!("{}", name);
|
||||
if optional {
|
||||
quote! { #child_ident }
|
||||
} else if let Some(or_else) = node_child.or_else() {
|
||||
let child_type_ident = format_ident!("{}", node_child.kind());
|
||||
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(name: &str) -> TokenStream {
|
||||
let child_ident = format_ident!("{}", name);
|
||||
quote! { #child_ident }
|
||||
}
|
||||
|
||||
fn make_special_child_arg(special_child: &SpecialChild) -> TokenStream {
|
||||
let child_ident = format_ident!("{}", special_child.name());
|
||||
quote! { #child_ident }
|
||||
}
|
||||
|
||||
fn make_child_arg(child_spec: &StructChild, pair_ident: &Ident) -> Option<TokenStream> {
|
||||
match child_spec {
|
||||
StructChild::SkipChild(_) => None,
|
||||
StructChild::VecChild(vec_child) => Some(make_vec_child_arg(vec_child)),
|
||||
StructChild::MemberChild(member_child) => match member_child.build() {
|
||||
MemberChildBuild::Node(node_member_build) => Some(make_node_member_child_arg(
|
||||
member_child.name(),
|
||||
member_child.optional(),
|
||||
node_member_build,
|
||||
)),
|
||||
MemberChildBuild::Boolean(_) => {
|
||||
Some(make_boolean_member_child_arg(member_child.name()))
|
||||
}
|
||||
},
|
||||
StructChild::Special(special_child) => Some(make_special_child_arg(special_child)),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_return_value_stream(build_spec: &StructSpec, pair_ident: &Ident) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", build_spec.build());
|
||||
let child_args = build_spec
|
||||
.children()
|
||||
.map(|child| make_child_arg(child, pair_ident))
|
||||
.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: &StructSpec) -> 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 preamble = make_preamble(build_spec, &pair_ident);
|
||||
|
||||
let special_children = make_special_children(build_spec);
|
||||
|
||||
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, &pair_ident);
|
||||
|
||||
quote! {
|
||||
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||
#preamble
|
||||
|
||||
#(#special_children;)*
|
||||
|
||||
#(#child_holders;)*
|
||||
|
||||
#iter_stream
|
||||
|
||||
#new_stream
|
||||
}
|
||||
}
|
||||
}
|
||||
63
ast-generator/src/build_fn/tree_enum_build_fn.rs
Normal file
63
ast-generator/src/build_fn/tree_enum_build_fn.rs
Normal file
@ -0,0 +1,63 @@
|
||||
use crate::deserialize::util::{make_build_fn_name, make_build_pair};
|
||||
use crate::spec::tree_enum_spec::{EnumRuleChildKind, TreeEnumBuildSpec};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_enum_build_fn(enum_build_spec: &TreeEnumBuildSpec) -> 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!("{}", node_child.with());
|
||||
quote! { #inner_build_fn_ident(file_id, inner_pair) }
|
||||
}
|
||||
EnumRuleChildKind::Int(name_and_with) => {
|
||||
let inner_build_fn_ident = format_ident!("{}", name_and_with.with());
|
||||
quote! { #inner_build_fn_ident(file_id, inner_pair) }
|
||||
}
|
||||
EnumRuleChildKind::Long(name_and_with) => {
|
||||
let inner_build_fn_ident = format_ident!("{}", name_and_with.with());
|
||||
quote! { #inner_build_fn_ident(file_id, inner_pair) }
|
||||
}
|
||||
EnumRuleChildKind::Double(name_and_with) => {
|
||||
let inner_build_fn_ident = format_ident!("{}", name_and_with.with());
|
||||
quote! { #inner_build_fn_ident(file_id, inner_pair) }
|
||||
}
|
||||
EnumRuleChildKind::String(name_and_with) => {
|
||||
let inner_build_fn_ident = format_ident!("{}", name_and_with.with());
|
||||
quote! { #inner_build_fn_ident(file_id, inner_pair) }
|
||||
}
|
||||
EnumRuleChildKind::Boolean(name_and_with) => {
|
||||
let inner_build_fn_ident = format_ident!("{}", name_and_with.with());
|
||||
quote! { #inner_build_fn_ident(file_id, 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(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||
let inner_pair = #pair_ident.into_inner().next().unwrap();
|
||||
match inner_pair.as_rule() {
|
||||
#(#rule_branches,)*
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,127 +0,0 @@
|
||||
use crate::spec::{
|
||||
BuildBooleanOn, ChildSpec, SingleChildToBuild, StructBuildSpec, 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_child_holder(child_spec: &ChildSpec) -> Option<TokenStream> {
|
||||
match child_spec {
|
||||
ChildSpec::SkipChild(_) => None,
|
||||
ChildSpec::VecChild(vec_child) => {
|
||||
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()),
|
||||
),
|
||||
};
|
||||
Some(quote! {
|
||||
let mut #child_ident: Vec<#child_type_ident> = vec![]
|
||||
})
|
||||
}
|
||||
ChildSpec::SingleChild(single_child) => match single_child.build() {
|
||||
SingleChildToBuild::Type(single_type_child) => {
|
||||
let child_ident = format_ident!("{}", single_type_child.var_name());
|
||||
let child_type_ident = format_ident!("{}", single_type_child.build());
|
||||
Some(quote! {
|
||||
let mut #child_ident: Option<#child_type_ident> = None;
|
||||
})
|
||||
}
|
||||
SingleChildToBuild::Boolean(boolean_child) => {
|
||||
let child_ident = format_ident!("{}", boolean_child.var_name());
|
||||
Some(quote! {
|
||||
let #child_ident: bool = false
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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(#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(#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)*
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
fn #build_fn_ident(#pair_ident: Pair<Rule>) -> #return_type_ident {
|
||||
#(#child_holders;)*
|
||||
|
||||
#iter_stream
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,168 +0,0 @@
|
||||
use crate::build_fn_gen::make_build_fn_name;
|
||||
use crate::spec::{
|
||||
BuildBooleanOn, BuildSpec, ChildSpec, EnumBuildSpec, EnumRule, SingleBooleanChildToBuild,
|
||||
SingleChild, SingleChildToBuild, SingleTypeChildToBuild, SkipChild, StructBuildSpec, VecChild,
|
||||
VecChildToBuild, VecTypeChildToBuild,
|
||||
};
|
||||
use convert_case::{Case, Casing};
|
||||
use yaml_rust2::{Yaml, YamlLoader};
|
||||
|
||||
fn get_skip(skip: &Yaml) -> bool {
|
||||
skip.as_bool().unwrap_or_else(|| false)
|
||||
}
|
||||
|
||||
fn get_vec(vec: &Yaml) -> bool {
|
||||
vec.as_bool().unwrap_or_else(|| false)
|
||||
}
|
||||
|
||||
fn get_vec_child_to_build(build: &Yaml, name: &str, rule: &str) -> VecChildToBuild {
|
||||
if build.is_hash() {
|
||||
let build_type = build["build"].as_str().unwrap();
|
||||
let var_name = build["var"]
|
||||
.as_str()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| build_type.to_case(Case::Snake));
|
||||
let with = build["with"]
|
||||
.as_str()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| make_build_fn_name(build_type));
|
||||
|
||||
VecChildToBuild::Type(VecTypeChildToBuild::new(build_type, &var_name, &with))
|
||||
} else {
|
||||
let build_as_str = build.as_str().unwrap_or(rule);
|
||||
VecChildToBuild::Type(VecTypeChildToBuild::new(
|
||||
build_as_str,
|
||||
name,
|
||||
make_build_fn_name(build_as_str).as_str(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_vec_child(name: &str, rule: &str, build: &Yaml) -> ChildSpec {
|
||||
ChildSpec::VecChild(VecChild::new(
|
||||
name,
|
||||
rule,
|
||||
get_vec_child_to_build(build, name, rule),
|
||||
))
|
||||
}
|
||||
|
||||
fn get_single_child_to_build(name: &str, rule: &str, build: &Yaml) -> SingleChildToBuild {
|
||||
if build.is_hash() {
|
||||
let r#type = build["type"].as_str().unwrap();
|
||||
let var_name = build["var"]
|
||||
.as_str()
|
||||
.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") {
|
||||
SingleChildToBuild::Boolean(SingleBooleanChildToBuild::new(
|
||||
&var_name,
|
||||
BuildBooleanOn::RulePresent,
|
||||
))
|
||||
} else {
|
||||
todo!("currently on boolean types with on: rule_present are supported")
|
||||
}
|
||||
} else {
|
||||
match build.as_str() {
|
||||
Some(s) => SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(s)),
|
||||
None => SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(rule)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_single_child(name: &str, rule: &str, build: &Yaml) -> ChildSpec {
|
||||
ChildSpec::SingleChild(SingleChild::new(
|
||||
name,
|
||||
rule,
|
||||
get_single_child_to_build(name, rule, build),
|
||||
))
|
||||
}
|
||||
|
||||
fn get_child_specs(children: &Yaml) -> Vec<ChildSpec> {
|
||||
children
|
||||
.as_vec()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|child_spec| {
|
||||
if child_spec.is_hash() {
|
||||
let as_hash = child_spec.as_hash().unwrap();
|
||||
let (name, props) = as_hash
|
||||
.iter()
|
||||
.next()
|
||||
.map(|(name, props)| (name.as_str().unwrap(), props))
|
||||
.unwrap();
|
||||
|
||||
let rule = props["rule"].as_str().unwrap();
|
||||
|
||||
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 {
|
||||
get_single_child(name, rule, build)
|
||||
}
|
||||
} else {
|
||||
ChildSpec::SingleChild(SingleChild::from_name_snake(child_spec.as_str().unwrap()))
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_enum_rule_specs(rule_specs: &Yaml) -> Vec<EnumRule> {
|
||||
rule_specs
|
||||
.as_vec()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|rule_spec| {
|
||||
if rule_spec.is_hash() {
|
||||
let rule = rule_spec["rule"].as_str().unwrap();
|
||||
let build = rule_spec["build"].as_str().unwrap();
|
||||
let with = if !rule_spec["with"].is_badvalue() {
|
||||
rule_spec.as_str().unwrap().to_string()
|
||||
} else {
|
||||
make_build_fn_name(build)
|
||||
};
|
||||
|
||||
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 {
|
||||
let rule_specs = &build_spec["rules"];
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize_yaml_spec(yaml: &str) -> Vec<BuildSpec> {
|
||||
let docs = YamlLoader::load_from_str(yaml).unwrap();
|
||||
let doc = &docs[0];
|
||||
|
||||
doc.as_hash()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|(build_spec_name, build_spec)| deserialize_build_spec(build_spec_name, build_spec))
|
||||
.collect()
|
||||
}
|
||||
13
ast-generator/src/deserialize/leaf_enum_spec.rs
Normal file
13
ast-generator/src/deserialize/leaf_enum_spec.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use yaml_rust2::Yaml;
|
||||
use crate::spec::leaf_enum_spec::LeafEnumBuildSpec;
|
||||
|
||||
pub fn deserialize_leaf_enum(name: &str, props: &Yaml) -> LeafEnumBuildSpec {
|
||||
let rules = props["rules"].as_vec()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|rule_yaml| {
|
||||
rule_yaml.as_str().unwrap().to_string()
|
||||
})
|
||||
.collect();
|
||||
LeafEnumBuildSpec::new(name, rules)
|
||||
}
|
||||
37
ast-generator/src/deserialize/leaf_struct_spec.rs
Normal file
37
ast-generator/src/deserialize/leaf_struct_spec.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use crate::deserialize::util::unwrap_single_member_hash;
|
||||
use yaml_rust2::Yaml;
|
||||
use crate::spec::leaf_struct_spec::{LeafStructBuildSpec, LeafStructMember, LeafStructMemberKind};
|
||||
|
||||
fn deserialize_member(member_name: &str, member_props: &Yaml) -> LeafStructMember {
|
||||
let kind = match member_props["kind"].as_str().unwrap() {
|
||||
"string" => LeafStructMemberKind::String,
|
||||
"file_id" => LeafStructMemberKind::FileId,
|
||||
"range" => LeafStructMemberKind::Range,
|
||||
_ => panic!()
|
||||
};
|
||||
LeafStructMember::new(member_name, kind)
|
||||
}
|
||||
|
||||
pub fn deserialize_leaf_struct(name: &str, props: &Yaml) -> LeafStructBuildSpec {
|
||||
let members = props["members"]
|
||||
.as_vec()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|member_hash| {
|
||||
let (member_name, props) = unwrap_single_member_hash(member_hash);
|
||||
deserialize_member(&member_name, props)
|
||||
})
|
||||
.map(Box::new)
|
||||
.collect();
|
||||
let derive = props["derive"]
|
||||
.as_vec()
|
||||
.map(|derive_yaml| {
|
||||
derive_yaml.iter()
|
||||
.map(|trait_yaml| {
|
||||
trait_yaml.as_str().unwrap().to_string()
|
||||
}).collect::<Vec<_>>()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
LeafStructBuildSpec::new(name, members, derive)
|
||||
}
|
||||
108
ast-generator/src/deserialize/mod.rs
Normal file
108
ast-generator/src/deserialize/mod.rs
Normal file
@ -0,0 +1,108 @@
|
||||
mod leaf_enum_spec;
|
||||
mod leaf_struct_spec;
|
||||
mod node_production_spec;
|
||||
mod polymorphic_enum_build_inner;
|
||||
mod polymorphic_enum_loop_spec;
|
||||
mod polymorphic_leaf_enum;
|
||||
mod polymorphic_pass_through_spec;
|
||||
mod polymorphic_tree_enum;
|
||||
mod polymorphic_type_spec;
|
||||
mod production_spec;
|
||||
mod struct_spec;
|
||||
mod tree_enum_spec;
|
||||
pub(crate) mod util;
|
||||
|
||||
use crate::deserialize::leaf_enum_spec::deserialize_leaf_enum;
|
||||
use crate::deserialize::leaf_struct_spec::deserialize_leaf_struct;
|
||||
use crate::deserialize::node_production_spec::deserialize_node_production;
|
||||
use crate::deserialize::polymorphic_enum_build_inner::deserialize_polymorphic_enum_inner_build;
|
||||
use crate::deserialize::polymorphic_enum_loop_spec::deserialize_polymorphic_enum_loop;
|
||||
use crate::deserialize::polymorphic_leaf_enum::deserialize_polymorphic_leaf_enum;
|
||||
use crate::deserialize::polymorphic_pass_through_spec::deserialize_polymorphic_pass_through;
|
||||
use crate::deserialize::polymorphic_type_spec::deserialize_polymorphic_type;
|
||||
use crate::deserialize::production_spec::deserialize_production;
|
||||
use crate::deserialize::struct_spec::deserialize_struct_spec;
|
||||
use crate::deserialize::tree_enum_spec::deserialize_tree_enum;
|
||||
use crate::spec::BuildSpec;
|
||||
use yaml_rust2::{Yaml, YamlLoader};
|
||||
use crate::deserialize::polymorphic_tree_enum::deserialize_polymorphic_tree_enum;
|
||||
|
||||
fn deserialize_build_spec(build_spec_name: &str, build_spec: &Yaml) -> BuildSpec {
|
||||
if build_spec["struct"].is_hash() {
|
||||
BuildSpec::Struct(deserialize_struct_spec(
|
||||
build_spec_name,
|
||||
&build_spec["struct"],
|
||||
))
|
||||
} else if build_spec["leaf_struct"].is_hash() {
|
||||
BuildSpec::LeafStruct(deserialize_leaf_struct(
|
||||
build_spec_name,
|
||||
&build_spec["leaf_struct"],
|
||||
))
|
||||
} else if build_spec["tree_enum"].is_hash() {
|
||||
BuildSpec::Enum(deserialize_tree_enum(
|
||||
build_spec_name,
|
||||
&build_spec["tree_enum"],
|
||||
))
|
||||
} else if build_spec["leaf_enum"].is_hash() {
|
||||
BuildSpec::LeafEnum(deserialize_leaf_enum(
|
||||
build_spec_name,
|
||||
&build_spec["leaf_enum"],
|
||||
))
|
||||
} else if build_spec["production"].is_hash() {
|
||||
BuildSpec::Production(deserialize_production(
|
||||
build_spec_name,
|
||||
&build_spec["production"],
|
||||
))
|
||||
} else if build_spec["node_production"].is_hash() {
|
||||
BuildSpec::NodeProduction(deserialize_node_production(
|
||||
build_spec_name,
|
||||
&build_spec["node_production"],
|
||||
))
|
||||
} else if build_spec["polymorphic_type"].is_hash() {
|
||||
BuildSpec::PolymorphicType(deserialize_polymorphic_type(
|
||||
build_spec_name,
|
||||
&build_spec["polymorphic_type"],
|
||||
))
|
||||
} else if build_spec["polymorphic_enum_loop_build"].is_hash() {
|
||||
BuildSpec::PolymorphicEnumLoop(deserialize_polymorphic_enum_loop(
|
||||
build_spec_name,
|
||||
&build_spec["polymorphic_enum_loop_build"],
|
||||
))
|
||||
} else if build_spec["polymorphic_pass_through"].is_hash() {
|
||||
BuildSpec::PolymorphicPassThrough(deserialize_polymorphic_pass_through(
|
||||
build_spec_name,
|
||||
&build_spec["polymorphic_pass_through"],
|
||||
))
|
||||
} else if build_spec["polymorphic_enum_inner_build"].is_hash() {
|
||||
BuildSpec::PolymorphicEnumInnerBuild(deserialize_polymorphic_enum_inner_build(
|
||||
build_spec_name,
|
||||
&build_spec["polymorphic_enum_inner_build"],
|
||||
))
|
||||
} else if build_spec["polymorphic_leaf_enum"].is_hash() {
|
||||
BuildSpec::PolymorphicLeafEnum(deserialize_polymorphic_leaf_enum(
|
||||
build_spec_name,
|
||||
&build_spec["polymorphic_leaf_enum"],
|
||||
))
|
||||
} else if build_spec["polymorphic_tree_enum"].is_hash() {
|
||||
BuildSpec::PolymorphicTreeEnum(deserialize_polymorphic_tree_enum(
|
||||
build_spec_name,
|
||||
&build_spec["polymorphic_tree_enum"],
|
||||
))
|
||||
} else {
|
||||
panic!("Missing or incorrect build type for {}", build_spec_name);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize_yaml_spec(yaml: &str) -> Vec<BuildSpec> {
|
||||
let docs = YamlLoader::load_from_str(yaml).unwrap();
|
||||
let doc = &docs[0];
|
||||
|
||||
doc.as_hash()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.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()
|
||||
}
|
||||
9
ast-generator/src/deserialize/node_production_spec.rs
Normal file
9
ast-generator/src/deserialize/node_production_spec.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use crate::deserialize::util::make_build_fn_name;
|
||||
use crate::spec::node_production_spec::NodeProductionBuildSpec;
|
||||
use yaml_rust2::Yaml;
|
||||
|
||||
pub fn deserialize_node_production(name: &str, props: &Yaml) -> NodeProductionBuildSpec {
|
||||
let kind = props["kind"].as_str().unwrap();
|
||||
let with = make_build_fn_name(props["with"].as_str().unwrap());
|
||||
NodeProductionBuildSpec::new(name, kind, &with)
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
use crate::deserialize::util::{make_build_fn_name, unwrap_single_member_hash};
|
||||
use crate::spec::polymorphic_enum_inner_build::{
|
||||
PolymorphicEnumInnerBuild, PolymorphicEnumInnerBuildMember,
|
||||
PolymorphicEnumInnerBuildMemberKind, PolymorphicEnumInnerBuildRule,
|
||||
};
|
||||
use yaml_rust2::Yaml;
|
||||
|
||||
pub fn deserialize_polymorphic_enum_inner_build(
|
||||
name: &str,
|
||||
props: &Yaml,
|
||||
) -> PolymorphicEnumInnerBuild {
|
||||
let members = props["members"]
|
||||
.as_vec()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|member| {
|
||||
if let Some(name) = member.as_str() {
|
||||
PolymorphicEnumInnerBuildMember::new(
|
||||
name,
|
||||
PolymorphicEnumInnerBuildMemberKind::Struct,
|
||||
)
|
||||
} else if member.is_hash() {
|
||||
let (name, props) = unwrap_single_member_hash(member);
|
||||
if let Some(kind) = props["kind"].as_str() {
|
||||
match kind {
|
||||
"leaf" => PolymorphicEnumInnerBuildMember::new(
|
||||
&name,
|
||||
PolymorphicEnumInnerBuildMemberKind::Leaf,
|
||||
),
|
||||
"struct" => PolymorphicEnumInnerBuildMember::new(
|
||||
&name,
|
||||
PolymorphicEnumInnerBuildMemberKind::Struct,
|
||||
),
|
||||
_ => panic!(),
|
||||
}
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let rules = props["rules"]
|
||||
.as_vec()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|rule| {
|
||||
let rule_as_string = rule.as_str().unwrap().to_string();
|
||||
PolymorphicEnumInnerBuildRule::new(
|
||||
&rule_as_string,
|
||||
&make_build_fn_name(&rule_as_string),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
PolymorphicEnumInnerBuild::new(name, members, rules)
|
||||
}
|
||||
102
ast-generator/src/deserialize/polymorphic_enum_loop_spec.rs
Normal file
102
ast-generator/src/deserialize/polymorphic_enum_loop_spec.rs
Normal file
@ -0,0 +1,102 @@
|
||||
use crate::deserialize::util::{get_as_bool, make_build_fn_name, unwrap_single_member_hash};
|
||||
use crate::spec::polymorphic_enum_loop_spec::{
|
||||
PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopChildUseCurrent, PolymorphicEnumLoopRule,
|
||||
PolymorphicEnumLoopRuleBuild, PolymorphicEnumLoopRuleBuildChild,
|
||||
PolymorphicEnumLoopRuleChildOnEach, PolymorphicEnumLoopRulePassThrough,
|
||||
};
|
||||
use crate::spec::{SpecialChild, SpecialChildKind, StructField};
|
||||
use yaml_rust2::Yaml;
|
||||
|
||||
fn deserialize_build_child(child_name: &str, props: &Yaml) -> PolymorphicEnumLoopRuleBuildChild {
|
||||
if props["use_current"].is_hash() {
|
||||
let kind = props["use_current"]["kind"].as_str().unwrap();
|
||||
PolymorphicEnumLoopRuleBuildChild::UseCurrent(PolymorphicEnumLoopChildUseCurrent::new(
|
||||
child_name, kind,
|
||||
))
|
||||
} else if props["on_each"].is_hash() {
|
||||
let rule = props["on_each"]["rule"].as_str().unwrap();
|
||||
PolymorphicEnumLoopRuleBuildChild::OnEach(PolymorphicEnumLoopRuleChildOnEach::new(
|
||||
child_name, rule,
|
||||
))
|
||||
} else if props["special"].is_hash() {
|
||||
let kind = match props["special"]["kind"].as_str().unwrap() {
|
||||
"range" => SpecialChildKind::Range,
|
||||
"file_id" => SpecialChildKind::FileId,
|
||||
_ => panic!("Unknown special child kind"),
|
||||
};
|
||||
PolymorphicEnumLoopRuleBuildChild::Special(SpecialChild::new(child_name, kind))
|
||||
} else {
|
||||
panic!("Expected 'use_current' or 'on_each' hash for polymorphic enum loop build child");
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_build(name: &str, props: &Yaml) -> PolymorphicEnumLoopRuleBuild {
|
||||
let variant = props["variant"].as_str().unwrap();
|
||||
let children = props["children"]
|
||||
.as_vec()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|child_yaml| {
|
||||
let (child_name, child_props) = unwrap_single_member_hash(child_yaml);
|
||||
deserialize_build_child(&child_name, child_props)
|
||||
})
|
||||
.map(Box::new)
|
||||
.collect();
|
||||
|
||||
let fields = props["fields"]
|
||||
.as_vec()
|
||||
.map(|fields| {
|
||||
fields.iter()
|
||||
.map(|field_yaml| {
|
||||
let (field_name, field_props) = unwrap_single_member_hash(field_yaml);
|
||||
let kind = field_props["kind"].as_str().unwrap();
|
||||
StructField::new(
|
||||
&field_name,
|
||||
kind,
|
||||
None,
|
||||
false
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.unwrap_or_else(|| vec![]);
|
||||
|
||||
PolymorphicEnumLoopRuleBuild::new(name, variant, children, fields)
|
||||
}
|
||||
|
||||
fn deserialize_pass_through(name: &str, props: &Yaml) -> PolymorphicEnumLoopRulePassThrough {
|
||||
let kind = props["kind"].as_str().unwrap();
|
||||
let with = make_build_fn_name(props["with"].as_str().unwrap_or(kind));
|
||||
PolymorphicEnumLoopRulePassThrough::new(name, kind, &with)
|
||||
}
|
||||
|
||||
fn deserialize_rule(rule_name: &str, props: &Yaml) -> PolymorphicEnumLoopRule {
|
||||
if props["pass_through"].is_hash() {
|
||||
PolymorphicEnumLoopRule::PassThrough(deserialize_pass_through(
|
||||
rule_name,
|
||||
&props["pass_through"],
|
||||
))
|
||||
} else if props["build"].is_hash() {
|
||||
PolymorphicEnumLoopRule::Build(deserialize_build(rule_name, &props["build"]))
|
||||
} else {
|
||||
panic!("Polymorphic enum loop rule must have 'pass_through' or 'build' hash.");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize_polymorphic_enum_loop(name: &str, props: &Yaml) -> PolymorphicEnumLoopBuildSpec {
|
||||
let kind = props["kind"].as_str().unwrap();
|
||||
let reverse = get_as_bool(&props["reverse"]);
|
||||
|
||||
let rules = props["rules"]
|
||||
.as_vec()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|rule_yaml| {
|
||||
let (rule_name, rule_props) = unwrap_single_member_hash(rule_yaml);
|
||||
deserialize_rule(&rule_name, rule_props)
|
||||
})
|
||||
.map(Box::new)
|
||||
.collect();
|
||||
|
||||
PolymorphicEnumLoopBuildSpec::new(name, kind, reverse, rules)
|
||||
}
|
||||
13
ast-generator/src/deserialize/polymorphic_leaf_enum.rs
Normal file
13
ast-generator/src/deserialize/polymorphic_leaf_enum.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use yaml_rust2::Yaml;
|
||||
use crate::spec::polymorphic_leaf_enum::PolymorphicLeafEnum;
|
||||
|
||||
pub fn deserialize_polymorphic_leaf_enum(name: &str, props: &Yaml) -> PolymorphicLeafEnum {
|
||||
let kind = props["kind"].as_str().unwrap();
|
||||
let rules = props["rules"].as_vec().unwrap()
|
||||
.iter()
|
||||
.map(|rule| {
|
||||
rule.as_str().unwrap().to_string()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
PolymorphicLeafEnum::new(name, kind, rules)
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
use crate::deserialize::util::unwrap_single_member_hash;
|
||||
use crate::spec::polymorphic_pass_through_spec::{PolymorphicPassThroughBuildSpec, PolymorphicPassThroughVariant};
|
||||
use yaml_rust2::Yaml;
|
||||
|
||||
pub fn deserialize_polymorphic_pass_through(name: &str, props: &Yaml) -> PolymorphicPassThroughBuildSpec {
|
||||
let build_kind = props["build"]["kind"].as_str().unwrap();
|
||||
let variants = props["variants"].as_vec().unwrap()
|
||||
.iter()
|
||||
.map(|variant_yaml| {
|
||||
let (variant_name, variant_props) = unwrap_single_member_hash(variant_yaml);
|
||||
if variant_props["inner"].is_hash() {
|
||||
let inner_kind = variant_props["inner"]["kind"].as_str().unwrap();
|
||||
PolymorphicPassThroughVariant::Inner {
|
||||
name: variant_name,
|
||||
kind: inner_kind.to_string(),
|
||||
}
|
||||
} else if variant_props["pass_through"].is_hash() {
|
||||
let pass_through_kind = variant_props["pass_through"]["kind"].is_hash();
|
||||
PolymorphicPassThroughVariant::PassThrough {
|
||||
name: variant_name,
|
||||
kind: pass_through_kind.to_string(),
|
||||
}
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
})
|
||||
.map(Box::new)
|
||||
.collect();
|
||||
PolymorphicPassThroughBuildSpec::new(name, build_kind, variants)
|
||||
}
|
||||
12
ast-generator/src/deserialize/polymorphic_tree_enum.rs
Normal file
12
ast-generator/src/deserialize/polymorphic_tree_enum.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use yaml_rust2::Yaml;
|
||||
use crate::spec::polymorphic_tree_enum_spec::PolymorphicTreeEnumSpec;
|
||||
|
||||
pub fn deserialize_polymorphic_tree_enum(name: &str, props: &Yaml) -> PolymorphicTreeEnumSpec {
|
||||
let kind = props["kind"].as_str().unwrap();
|
||||
let rules = props["rules"].as_vec().unwrap()
|
||||
.iter()
|
||||
.map(|rule| rule.as_str().unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
PolymorphicTreeEnumSpec::new(name, kind, &rules)
|
||||
}
|
||||
23
ast-generator/src/deserialize/polymorphic_type_spec.rs
Normal file
23
ast-generator/src/deserialize/polymorphic_type_spec.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use crate::deserialize::util::unwrap_single_member_hash;
|
||||
use crate::spec::polymorphic_type_spec::{PolymorphicTypeBuildSpec, PolymorphicTypeVariant};
|
||||
use yaml_rust2::Yaml;
|
||||
|
||||
fn deserialize_variant(variant_name: &str, props: &Yaml) -> PolymorphicTypeVariant {
|
||||
let inner_kind = props["inner"]["kind"].as_str().unwrap();
|
||||
PolymorphicTypeVariant::new(variant_name, inner_kind)
|
||||
}
|
||||
|
||||
pub fn deserialize_polymorphic_type(name: &str, props: &Yaml) -> PolymorphicTypeBuildSpec {
|
||||
let variants = props["variants"]
|
||||
.as_vec()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|variant_yaml| {
|
||||
let (variant_name, variant_props) = unwrap_single_member_hash(variant_yaml);
|
||||
deserialize_variant(&variant_name, variant_props)
|
||||
})
|
||||
.map(Box::new)
|
||||
.collect();
|
||||
let kind = props["build"]["kind"].as_str().unwrap();
|
||||
PolymorphicTypeBuildSpec::new(name, variants, kind)
|
||||
}
|
||||
21
ast-generator/src/deserialize/production_spec.rs
Normal file
21
ast-generator/src/deserialize/production_spec.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use crate::spec::production_spec::{ProductionBooleanFrom, ProductionBuildSpec, ProductionKind, ProductionStringFrom};
|
||||
use yaml_rust2::Yaml;
|
||||
|
||||
pub fn deserialize_production(name: &str, props: &Yaml) -> ProductionBuildSpec {
|
||||
let kind = match props["kind"].as_str().unwrap() {
|
||||
"int" => ProductionKind::Int,
|
||||
"long" => ProductionKind::Long,
|
||||
"double" => ProductionKind::Double,
|
||||
"string" => {
|
||||
let from = match props["from"].as_str().unwrap() {
|
||||
"string_inner" => ProductionStringFrom::StringInner,
|
||||
"whole_pair" => ProductionStringFrom::WholePair,
|
||||
_ => panic!("Unknown string production from: {}", props["from"].as_str().unwrap())
|
||||
};
|
||||
ProductionKind::String(from)
|
||||
},
|
||||
"boolean" => ProductionKind::Boolean(ProductionBooleanFrom::ParseWholePair),
|
||||
_ => panic!("Unknown production kind: {}", props["kind"].as_str().unwrap()),
|
||||
};
|
||||
ProductionBuildSpec::new(name, kind)
|
||||
}
|
||||
199
ast-generator/src/deserialize/struct_spec.rs
Normal file
199
ast-generator/src/deserialize/struct_spec.rs
Normal file
@ -0,0 +1,199 @@
|
||||
use crate::deserialize_error;
|
||||
use convert_case::{Case, Casing};
|
||||
|
||||
use crate::deserialize::util::{get_as_bool, make_build_fn_name, unwrap_single_member_hash};
|
||||
use crate::spec::struct_spec::*;
|
||||
use crate::spec::{SpecialChild, SpecialChildKind, StructField, StructFieldWrap};
|
||||
use yaml_rust2::Yaml;
|
||||
|
||||
fn deserialize_field(field_yaml: &Yaml) -> StructField {
|
||||
let (name, props) = unwrap_single_member_hash(field_yaml);
|
||||
let kind = props["kind"].as_str().unwrap();
|
||||
let wrap = if let Some(wrap) = props["wrap"].as_str() {
|
||||
match wrap {
|
||||
"rc_ref_cell" => Some(StructFieldWrap::RcRefCell),
|
||||
_ => panic!(),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let vec = get_as_bool(&props["vec"]);
|
||||
StructField::new(&name, kind, wrap, vec)
|
||||
}
|
||||
|
||||
fn deserialize_skip_child(props: &Yaml) -> StructChild {
|
||||
let rule = props["rule"].as_str().unwrap();
|
||||
StructChild::SkipChild(SkipChild::new(rule))
|
||||
}
|
||||
|
||||
fn deserialize_vec_child(child_name: &str, props: &Yaml) -> StructChild {
|
||||
let rule = props["rule"].as_str().unwrap();
|
||||
let kind = props["kind"].as_str().unwrap_or(rule);
|
||||
if kind == "string" {
|
||||
let build = VecChildBuild::String(VecChildStringBuild::new(&make_build_fn_name(rule)));
|
||||
StructChild::VecChild(VecChild::new(child_name, rule, Box::new(build)))
|
||||
} else {
|
||||
let build = VecChildBuild::Node(VecChildNodeBuild::new(rule, &make_build_fn_name(rule)));
|
||||
StructChild::VecChild(VecChild::new(child_name, rule, Box::new(build)))
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_member_build(child_name: &str, rule: &str, props: &Yaml) -> MemberChildBuild {
|
||||
if props["node"].is_hash() {
|
||||
let node_props = &props["node"];
|
||||
let kind = node_props["kind"].as_str().unwrap_or(rule);
|
||||
let with = node_props["with"]
|
||||
.as_str()
|
||||
.map(|with| make_build_fn_name(with))
|
||||
.unwrap_or_else(|| make_build_fn_name(kind));
|
||||
|
||||
let or_else = if get_as_bool(&node_props["or_else_default"]) {
|
||||
Some(String::from("default"))
|
||||
} else if let Some(or_else) = node_props["or_else"].as_str() {
|
||||
Some(or_else.to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
MemberChildBuild::Node(NodeMemberBuild::new(kind, &with, or_else))
|
||||
} else if props["boolean"].is_hash() {
|
||||
let boolean_props = &props["boolean"];
|
||||
if let Some(on) = boolean_props["on"].as_str() {
|
||||
if on == "rule_present" {
|
||||
MemberChildBuild::Boolean(BooleanMemberBuild::new(
|
||||
BooleanMemberBuildOn::RulePresent,
|
||||
))
|
||||
} else {
|
||||
panic!(
|
||||
"Expected 'on' in 'boolean' in 'build' in {} to be 'rule_present'",
|
||||
child_name
|
||||
);
|
||||
}
|
||||
} else {
|
||||
panic!("Expected 'on' in 'boolean' in 'build' in {}", child_name);
|
||||
}
|
||||
} else {
|
||||
panic!(
|
||||
"Expected one of 'node', 'boolean', or 'special' in 'build' in {}",
|
||||
child_name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_member_child(child_name: &str, props: &Yaml) -> StructChild {
|
||||
let rule = props["rule"]
|
||||
.as_str()
|
||||
.map(ToString::to_string)
|
||||
.unwrap_or_else(|| child_name.to_case(Case::Pascal));
|
||||
let optional = get_as_bool(&props["optional"]);
|
||||
if props["build"].is_hash() {
|
||||
let build = deserialize_member_build(child_name, &rule, &props["build"]);
|
||||
StructChild::MemberChild(MemberChild::new(
|
||||
child_name,
|
||||
&rule,
|
||||
optional,
|
||||
Box::new(build),
|
||||
))
|
||||
} else {
|
||||
StructChild::MemberChild(MemberChild::new(
|
||||
child_name,
|
||||
&rule,
|
||||
optional,
|
||||
Box::new(MemberChildBuild::Node(NodeMemberBuild::new(
|
||||
&rule,
|
||||
&make_build_fn_name(&rule),
|
||||
None,
|
||||
))),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_special_child(name: &str, props: &Yaml) -> StructChild {
|
||||
match props["kind"].as_str().unwrap() {
|
||||
"file_id" => StructChild::Special(SpecialChild::new(name, SpecialChildKind::FileId)),
|
||||
"range" => StructChild::Special(SpecialChild::new(name, SpecialChildKind::Range)),
|
||||
_ => panic!(
|
||||
"Invalid special child kind {} in {}",
|
||||
props["kind"].as_str().unwrap(),
|
||||
name
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_hash_child(name: &str, props: &Yaml) -> StructChild {
|
||||
if props["skip"].is_hash() {
|
||||
deserialize_skip_child(&props["skip"])
|
||||
} else if props["vec"].is_hash() {
|
||||
deserialize_vec_child(name, &props["vec"])
|
||||
} else if props["member"].is_hash() {
|
||||
deserialize_member_child(name, &props["member"])
|
||||
} else if props["special"].is_hash() {
|
||||
deserialize_special_child(name, &props["special"])
|
||||
} else {
|
||||
panic!("Expected 'skip' or 'vec' in 'member' in {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_string_child(name: &str) -> StructChild {
|
||||
let name_pascal = name.to_case(Case::Pascal);
|
||||
StructChild::MemberChild(MemberChild::new(
|
||||
name,
|
||||
&name_pascal,
|
||||
false,
|
||||
Box::new(MemberChildBuild::Node(NodeMemberBuild::new(
|
||||
&name_pascal,
|
||||
&make_build_fn_name(name),
|
||||
None,
|
||||
))),
|
||||
))
|
||||
}
|
||||
|
||||
fn deserialize_children(children_yaml: &Yaml) -> Vec<Box<StructChild>> {
|
||||
children_yaml
|
||||
.as_vec()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|child_yaml| {
|
||||
if let Some(name) = child_yaml.as_str() {
|
||||
deserialize_string_child(name)
|
||||
} else {
|
||||
let (child_name, child_props) = unwrap_single_member_hash(child_yaml);
|
||||
deserialize_hash_child(&child_name, child_props)
|
||||
}
|
||||
})
|
||||
.map(Box::new)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn deserialize_struct_spec(name: &str, struct_yaml: &Yaml) -> StructSpec {
|
||||
let children = if struct_yaml["children"].is_array() {
|
||||
deserialize_children(&struct_yaml["children"])
|
||||
} else {
|
||||
deserialize_error!("array", "children", name);
|
||||
};
|
||||
|
||||
let fields = if struct_yaml["fields"].is_array() {
|
||||
struct_yaml["fields"]
|
||||
.as_vec()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|field| deserialize_field(field))
|
||||
.map(Box::new)
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let derive = if struct_yaml["derive"].is_array() {
|
||||
struct_yaml["derive"]
|
||||
.as_vec()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|derive| derive.as_str().unwrap().to_string())
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
StructSpec::new(name, children, fields, derive)
|
||||
}
|
||||
56
ast-generator/src/deserialize/tree_enum_spec.rs
Normal file
56
ast-generator/src/deserialize/tree_enum_spec.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use convert_case::{Case, Casing};
|
||||
use crate::deserialize::util::{get_as_bool_or, make_build_fn_name, unwrap_single_member_hash};
|
||||
use crate::spec::tree_enum_spec::{EnumRuleChild, EnumRuleChildKind, EnumRuleChildNodeKind, NameAndWith, TreeEnumBuildSpec, TreeEnumRule};
|
||||
use yaml_rust2::Yaml;
|
||||
|
||||
fn deserialize_hash_enum_rule(rule: &str, props: &Yaml) -> TreeEnumRule {
|
||||
if get_as_bool_or(&props["child"], true) {
|
||||
let name_and_with = NameAndWith::new(
|
||||
&rule.to_case(Case::Snake),
|
||||
&make_build_fn_name(rule)
|
||||
);
|
||||
let kind = match props["kind"].as_str().unwrap() {
|
||||
"int" => EnumRuleChildKind::Int(name_and_with),
|
||||
"long" => EnumRuleChildKind::Long(name_and_with),
|
||||
"double" => EnumRuleChildKind::Double(name_and_with),
|
||||
"string" => EnumRuleChildKind::String(name_and_with),
|
||||
"boolean" => EnumRuleChildKind::Boolean(name_and_with),
|
||||
_ => panic!("Invalid kind: {}", props["kind"].as_str().unwrap()),
|
||||
};
|
||||
TreeEnumRule::new(rule, Some(Box::new(EnumRuleChild::new(Box::new(kind)))))
|
||||
} else {
|
||||
// no child
|
||||
TreeEnumRule::new(rule, None)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_string_enum_rule(rule: &str) -> TreeEnumRule {
|
||||
TreeEnumRule::new(
|
||||
rule,
|
||||
Some(Box::new(EnumRuleChild::new(Box::new(
|
||||
EnumRuleChildKind::Node(EnumRuleChildNodeKind::new(
|
||||
rule,
|
||||
&make_build_fn_name(rule)
|
||||
)),
|
||||
)))),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn deserialize_tree_enum(name: &str, props: &Yaml) -> TreeEnumBuildSpec {
|
||||
let rules = props["rules"]
|
||||
.as_vec()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|rule_yaml| {
|
||||
if let Some(rule) = rule_yaml.as_str() {
|
||||
deserialize_string_enum_rule(rule)
|
||||
} else {
|
||||
let (rule, props) = unwrap_single_member_hash(rule_yaml);
|
||||
deserialize_hash_enum_rule(&rule, props)
|
||||
}
|
||||
})
|
||||
.map(Box::new)
|
||||
.collect();
|
||||
|
||||
TreeEnumBuildSpec::new(name, rules)
|
||||
}
|
||||
37
ast-generator/src/deserialize/util.rs
Normal file
37
ast-generator/src/deserialize/util.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use convert_case::{Case, Casing};
|
||||
use yaml_rust2::Yaml;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! deserialize_error {
|
||||
($kind:expr, $key:expr, $name:expr) => {
|
||||
panic!("Expected {} '{}' in {}.", $kind, $key, $name);
|
||||
};
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
pub fn unwrap_single_member_hash(hash: &Yaml) -> (String, &Yaml) {
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn get_as_bool(yaml: &Yaml) -> bool {
|
||||
yaml.as_bool().unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn get_as_bool_or(yaml: &Yaml, or: bool) -> bool {
|
||||
yaml.as_bool().unwrap_or(or)
|
||||
}
|
||||
@ -1,10 +1,20 @@
|
||||
mod build_fn_gen;
|
||||
mod ast_node;
|
||||
mod build_fn;
|
||||
mod deserialize;
|
||||
mod pretty_print;
|
||||
mod spec;
|
||||
mod type_gen;
|
||||
mod walk;
|
||||
|
||||
use crate::build_fn_gen::make_struct_build_fn;
|
||||
use crate::ast_node::{
|
||||
make_ast_enum_member, make_ast_enum_mut_member, make_ast_node_impl,
|
||||
make_ast_node_ref_mut_unwrapper, make_ast_node_ref_unwrapper,
|
||||
};
|
||||
use crate::build_fn::make_build_fn;
|
||||
use crate::deserialize::deserialize_yaml_spec;
|
||||
use crate::pretty_print::make_pretty_print_impl;
|
||||
use crate::type_gen::make_type;
|
||||
use crate::walk::make_walk_fn;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use spec::BuildSpec;
|
||||
@ -14,10 +24,64 @@ fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) {
|
||||
println!("*** BuildSpec ***");
|
||||
match build_spec {
|
||||
BuildSpec::Enum(enum_build_spec) => {
|
||||
println!("Spec name: {}", enum_build_spec.name());
|
||||
println!("Enum Spec - build: {}", enum_build_spec.build());
|
||||
}
|
||||
BuildSpec::LeafEnum(leaf_enum_build_spec) => {
|
||||
println!("Leaf Enum Spec - build: {}", leaf_enum_build_spec.build());
|
||||
}
|
||||
BuildSpec::Struct(struct_build_spec) => {
|
||||
println!("Spec name: {}", struct_build_spec.name());
|
||||
println!("Struct Spec - build: {}", struct_build_spec.build());
|
||||
}
|
||||
BuildSpec::LeafStruct(leaf_struct_build_spec) => {
|
||||
println!(
|
||||
"Leaf Struct Spec - build: {}",
|
||||
leaf_struct_build_spec.build()
|
||||
);
|
||||
}
|
||||
BuildSpec::Production(production_build_spec) => {
|
||||
println!("Production Spec - name: {}", production_build_spec.name());
|
||||
}
|
||||
BuildSpec::NodeProduction(node_production_build_spec) => {
|
||||
println!(
|
||||
"Node Production Spec - name: {}",
|
||||
node_production_build_spec.name()
|
||||
);
|
||||
}
|
||||
BuildSpec::PolymorphicType(polymorphic_build_spec) => {
|
||||
println!(
|
||||
"Polymorphic Type Spec - name: {}",
|
||||
polymorphic_build_spec.name()
|
||||
);
|
||||
}
|
||||
BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop_build_spec) => {
|
||||
println!(
|
||||
"Polymorphic Enum Loop Spec - name: {}",
|
||||
polymorphic_enum_loop_build_spec.name()
|
||||
);
|
||||
}
|
||||
BuildSpec::PolymorphicPassThrough(pass_through_build_spec) => {
|
||||
println!(
|
||||
"Polymorphic Pass Through Spec - name: {}",
|
||||
pass_through_build_spec.name()
|
||||
);
|
||||
}
|
||||
BuildSpec::PolymorphicEnumInnerBuild(polymorphic_enum_inner_build_spec) => {
|
||||
println!(
|
||||
"Polymorphic Enum Inner Build Spec - name: {}",
|
||||
polymorphic_enum_inner_build_spec.name()
|
||||
);
|
||||
}
|
||||
BuildSpec::PolymorphicLeafEnum(polymorphic_leaf_enum) => {
|
||||
println!(
|
||||
"Polymorphic Leaf Enum Build Spec - name: {}",
|
||||
polymorphic_leaf_enum.name()
|
||||
);
|
||||
}
|
||||
BuildSpec::PolymorphicTreeEnum(polymorphic_tree_enum) => {
|
||||
println!(
|
||||
"Polymorphic Tree Enum Build Spec - name: {}",
|
||||
polymorphic_tree_enum.name()
|
||||
);
|
||||
}
|
||||
}
|
||||
println!("{:#?}", token_stream);
|
||||
@ -25,30 +89,191 @@ fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) {
|
||||
println!("{}", prettyplease::unparse(&parsed));
|
||||
}
|
||||
|
||||
pub fn test_dump() -> String {
|
||||
let build_specs = deserialize::deserialize_yaml_spec(include_str!("../../src/parser/ast.yaml"));
|
||||
let mut streams: Vec<TokenStream> = vec![];
|
||||
pub struct AstGeneratedFile {
|
||||
pub name: String,
|
||||
pub contents: String,
|
||||
}
|
||||
|
||||
for build_spec in &build_specs {
|
||||
let type_stream = make_type(build_spec);
|
||||
debug_built_spec(build_spec, &type_stream);
|
||||
streams.push(type_stream);
|
||||
}
|
||||
|
||||
for build_spec in &build_specs {
|
||||
match build_spec {
|
||||
BuildSpec::Enum(_) => {}
|
||||
BuildSpec::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();
|
||||
fn token_stream_to_string(token_stream: TokenStream) -> String {
|
||||
let file: File = syn::parse2(token_stream).unwrap();
|
||||
prettyplease::unparse(&file)
|
||||
}
|
||||
|
||||
fn generate_build_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
|
||||
let build_fns = build_specs
|
||||
.iter()
|
||||
.map(|build_spec| {
|
||||
let build_fn = make_build_fn(build_spec);
|
||||
debug_built_spec(build_spec, &build_fn);
|
||||
build_fn
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let combined = quote! {
|
||||
use crate::parser::Rule;
|
||||
use pest::iterators::Pair;
|
||||
use crate::ast::node::*;
|
||||
use std::range::Range;
|
||||
|
||||
#(#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! {
|
||||
use std::range::Range;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use crate::name_analysis::symbol::*;
|
||||
use crate::type_analysis::kinds::*;
|
||||
|
||||
#(#types)*
|
||||
};
|
||||
AstGeneratedFile {
|
||||
name: String::from("node.rs"),
|
||||
contents: token_stream_to_string(combined),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_pretty_print_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
|
||||
let impls = build_specs
|
||||
.iter()
|
||||
.map(|build_spec| {
|
||||
let maybe_stream = make_pretty_print_impl(build_spec);
|
||||
if let Some(stream) = &maybe_stream {
|
||||
debug_built_spec(build_spec, &stream);
|
||||
}
|
||||
maybe_stream
|
||||
})
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let combined = quote! {
|
||||
use crate::ast::node::*;
|
||||
#(#impls)*
|
||||
};
|
||||
AstGeneratedFile {
|
||||
name: String::from("pretty_print.rs"),
|
||||
contents: token_stream_to_string(combined),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_ast_node_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
|
||||
let impls = build_specs
|
||||
.iter()
|
||||
.map(|build_spec| {
|
||||
let maybe_stream = make_ast_node_impl(build_spec);
|
||||
if let Some(stream) = &maybe_stream {
|
||||
debug_built_spec(build_spec, &stream);
|
||||
}
|
||||
maybe_stream
|
||||
})
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let ast_enum_members = build_specs
|
||||
.iter()
|
||||
.map(|build_spec| make_ast_enum_member(build_spec))
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let ast_enum_mut_members = build_specs
|
||||
.iter()
|
||||
.map(|build_spec| make_ast_enum_mut_member(build_spec))
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let inner_unwrappers = build_specs
|
||||
.iter()
|
||||
.map(|build_spec| make_ast_node_ref_unwrapper(build_spec))
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let inner_mut_unwrappers = build_specs
|
||||
.iter()
|
||||
.map(|build_spec| make_ast_node_ref_mut_unwrapper(build_spec))
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let combined = quote! {
|
||||
use crate::ast::node::*;
|
||||
|
||||
pub enum AstNodeRef<'a> {
|
||||
#(#ast_enum_members,)*
|
||||
}
|
||||
|
||||
impl<'a> AstNodeRef<'a> {
|
||||
pub fn inner(&self) -> &dyn AstNode<'a> {
|
||||
match self {
|
||||
#(#inner_unwrappers,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AstNodeRefMut<'a> {
|
||||
#(#ast_enum_mut_members,)*
|
||||
}
|
||||
|
||||
impl<'a> AstNodeRefMut<'a> {
|
||||
pub fn inner(&mut self) -> &mut dyn AstNode<'a> {
|
||||
match self {
|
||||
#(#inner_mut_unwrappers,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AstNode<'a> {
|
||||
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>>;
|
||||
|
||||
fn for_each_child_mut(&'a mut self, f: &mut dyn FnMut(&'a mut dyn AstNode<'a>));
|
||||
|
||||
fn as_node_ref(&'a self) -> AstNodeRef<'a>;
|
||||
|
||||
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a>;
|
||||
}
|
||||
|
||||
#(#impls)*
|
||||
};
|
||||
AstGeneratedFile {
|
||||
name: String::from("ast_node.rs"),
|
||||
contents: token_stream_to_string(combined),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_walk_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
|
||||
let stream = make_walk_fn(build_specs);
|
||||
AstGeneratedFile {
|
||||
name: String::from("walk.rs"),
|
||||
contents: token_stream_to_string(stream),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_build_specs(yaml: &str) -> Vec<BuildSpec> {
|
||||
deserialize_yaml_spec(yaml)
|
||||
}
|
||||
|
||||
pub fn generate_files(build_specs: &[BuildSpec]) -> Vec<AstGeneratedFile> {
|
||||
vec![
|
||||
generate_build_file(build_specs),
|
||||
generate_node_file(build_specs),
|
||||
generate_pretty_print_file(build_specs),
|
||||
generate_ast_node_file(build_specs),
|
||||
generate_walk_file(build_specs),
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
use ast_generator::test_dump;
|
||||
|
||||
fn main() {
|
||||
let s = test_dump();
|
||||
println!("{}", s);
|
||||
}
|
||||
347
ast-generator/src/pretty_print.rs
Normal file
347
ast-generator/src/pretty_print.rs
Normal file
@ -0,0 +1,347 @@
|
||||
use crate::spec::leaf_enum_spec::LeafEnumBuildSpec;
|
||||
use crate::spec::leaf_struct_spec::{LeafStructBuildSpec, LeafStructMemberKind};
|
||||
use crate::spec::polymorphic_enum_inner_build::{
|
||||
PolymorphicEnumInnerBuild, PolymorphicEnumInnerBuildMemberKind,
|
||||
};
|
||||
use crate::spec::polymorphic_enum_loop_spec::{
|
||||
PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuildChild,
|
||||
};
|
||||
use crate::spec::polymorphic_type_spec::PolymorphicTypeBuildSpec;
|
||||
use crate::spec::struct_spec::{MemberChildBuild, StructChild, StructSpec, VecChildBuild};
|
||||
use crate::spec::tree_enum_spec::{EnumRuleChildKind, TreeEnumBuildSpec};
|
||||
use crate::spec::BuildSpec;
|
||||
use convert_case::{Case, Casing};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
fn make_result() -> TokenStream {
|
||||
quote! { std::fmt::Result }
|
||||
}
|
||||
|
||||
fn make_polymorphic_enum_inner_build_p2_impl(spec: &PolymorphicEnumInnerBuild) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", spec.name());
|
||||
let result = make_result();
|
||||
|
||||
let type_string = spec.name();
|
||||
|
||||
let child_matchers = spec
|
||||
.members()
|
||||
.map(|member| {
|
||||
let variant_ident = format_ident!("{}", member.name());
|
||||
match member.kind() {
|
||||
PolymorphicEnumInnerBuildMemberKind::Leaf => {
|
||||
let variant_string = member.name();
|
||||
quote! {
|
||||
#type_ident::#variant_ident => writer.writeln_indented(#variant_string)?
|
||||
}
|
||||
}
|
||||
PolymorphicEnumInnerBuildMemberKind::Struct => {
|
||||
let child_ident = format_ident!("{}", member.name().to_case(Case::Snake));
|
||||
quote! {
|
||||
#type_ident::#variant_ident(#child_ident) => {
|
||||
#child_ident.pretty_print(writer)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl PrettyPrint for #type_ident {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
|
||||
writer.writeln_indented(#type_string)?;
|
||||
writer.increase_indent();
|
||||
match self {
|
||||
#(#child_matchers,)*
|
||||
_ => {}
|
||||
}
|
||||
writer.decrease_indent();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_polymorphic_enum_loop_p2_impl(spec: &PolymorphicEnumLoopBuildSpec) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", spec.name());
|
||||
let type_string = spec.name();
|
||||
|
||||
let build = spec
|
||||
.rules()
|
||||
.find(|rule| match rule {
|
||||
PolymorphicEnumLoopRule::Build(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
.map(|rule| match rule {
|
||||
PolymorphicEnumLoopRule::Build(build) => build,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let child_print_statements = build
|
||||
.children()
|
||||
.filter(|child| match child {
|
||||
PolymorphicEnumLoopRuleBuildChild::Special(_) => false,
|
||||
_ => true,
|
||||
})
|
||||
.map(|child| {
|
||||
let child_ident = match child {
|
||||
PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => {
|
||||
format_ident!("{}", use_current.name())
|
||||
}
|
||||
PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => {
|
||||
format_ident!("{}", on_each.name())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
quote! {
|
||||
self.#child_ident().pretty_print(writer)?;
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let result = make_result();
|
||||
|
||||
quote! {
|
||||
impl PrettyPrint for #type_ident {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
|
||||
writer.writeln_indented(#type_string)?;
|
||||
writer.increase_indent();
|
||||
#(#child_print_statements)*
|
||||
writer.decrease_indent();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_polymorphic_type_p2_impl(spec: &PolymorphicTypeBuildSpec) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", spec.name());
|
||||
let child_matchers = spec
|
||||
.variants()
|
||||
.map(|member| {
|
||||
let enum_member_ident = format_ident!("{}", member.name());
|
||||
let inner_name = format_ident!("{}", member.inner_kind().to_case(Case::Snake));
|
||||
quote! {
|
||||
#type_ident::#enum_member_ident(#inner_name) => #inner_name.pretty_print(writer)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let result = make_result();
|
||||
|
||||
quote! {
|
||||
impl PrettyPrint for #type_ident {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
|
||||
match self {
|
||||
#(#child_matchers,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_leaf_enum_p2_impl(spec: &LeafEnumBuildSpec) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", spec.build());
|
||||
let child_matchers = spec
|
||||
.rules()
|
||||
.map(|rule| {
|
||||
let enum_variant_ident = format_ident!("{}", rule);
|
||||
let name_str = rule;
|
||||
quote! {
|
||||
#type_ident::#enum_variant_ident => writer.writeln_indented(#name_str)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let result = make_result();
|
||||
|
||||
quote! {
|
||||
impl PrettyPrint for #type_ident {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
|
||||
match self {
|
||||
#(#child_matchers,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_tree_enum_p2_impl(spec: &TreeEnumBuildSpec) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", spec.build());
|
||||
let type_str = spec.build();
|
||||
|
||||
let child_matchers = spec
|
||||
.rules()
|
||||
.map(|rule| {
|
||||
let enum_variant_ident = format_ident!("{}", rule.rule());
|
||||
if let Some(child) = rule.child() {
|
||||
match child.kind() {
|
||||
EnumRuleChildKind::Node(node_child) => {
|
||||
let child_name_ident =
|
||||
format_ident!("{}", node_child.node_kind().to_case(Case::Snake));
|
||||
Some(quote! {
|
||||
#type_ident::#enum_variant_ident(#child_name_ident) => {
|
||||
#child_name_ident.pretty_print(writer)?;
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
let variant_str = rule.rule();
|
||||
Some(quote! {
|
||||
#type_ident::#enum_variant_ident => writer.writeln_indented(#variant_str)?
|
||||
})
|
||||
}
|
||||
})
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let result = make_result();
|
||||
|
||||
quote! {
|
||||
impl PrettyPrint for #type_ident {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
|
||||
writer.writeln_indented(#type_str)?;
|
||||
writer.increase_indent();
|
||||
match self {
|
||||
#(#child_matchers,)*
|
||||
_ => {}
|
||||
}
|
||||
writer.decrease_indent();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_leaf_struct_p2_impl(leaf_struct_build_spec: &LeafStructBuildSpec) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", leaf_struct_build_spec.build());
|
||||
let member_formatters = leaf_struct_build_spec
|
||||
.members()
|
||||
.map(|member| match member.kind() {
|
||||
LeafStructMemberKind::String => Some("{}"),
|
||||
LeafStructMemberKind::FileId => None,
|
||||
LeafStructMemberKind::Range => None,
|
||||
})
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
let format_string = format!("{}({})", leaf_struct_build_spec.build(), member_formatters);
|
||||
|
||||
let members = leaf_struct_build_spec
|
||||
.members()
|
||||
.map(|member| match member.kind() {
|
||||
LeafStructMemberKind::String => {
|
||||
let member_ident = format_ident!("{}", member.name());
|
||||
Some(quote! {
|
||||
self.#member_ident()
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let result = make_result();
|
||||
|
||||
quote! {
|
||||
impl PrettyPrint for #type_ident {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
|
||||
writer.writeln_indented(&format!(#format_string, #(#members),*))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_struct_p2_impl(struct_build_spec: &StructSpec) -> TokenStream {
|
||||
let child_print_statements = struct_build_spec
|
||||
.children()
|
||||
.map(|child| match child {
|
||||
StructChild::SkipChild(_) => None,
|
||||
StructChild::VecChild(vec_child) => match vec_child.build() {
|
||||
VecChildBuild::Node(_) => {
|
||||
let child_ident = format_ident!("{}", vec_child.name());
|
||||
Some(quote! {
|
||||
for child in self.#child_ident() {
|
||||
child.pretty_print(writer)?;
|
||||
}
|
||||
})
|
||||
}
|
||||
VecChildBuild::String(_) => None,
|
||||
},
|
||||
StructChild::MemberChild(member_child) => match member_child.build() {
|
||||
MemberChildBuild::Node(_) => {
|
||||
let child_ident = format_ident!("{}", member_child.name());
|
||||
if member_child.optional() {
|
||||
Some(quote! {
|
||||
if let Some(child) = self.#child_ident() {
|
||||
child.pretty_print(writer)?;
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Some(quote! {
|
||||
self.#child_ident().pretty_print(writer)?;
|
||||
})
|
||||
}
|
||||
}
|
||||
MemberChildBuild::Boolean(_) => {
|
||||
let format_string = format!("{}({})", member_child.name(), "{}");
|
||||
let child_ident = format_ident!("{}", member_child.name());
|
||||
Some(quote! {
|
||||
writer.writeln_indented(&format!(#format_string, self.#child_ident()))?;
|
||||
})
|
||||
}
|
||||
},
|
||||
StructChild::Special(_) => None,
|
||||
})
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let type_ident = format_ident!("{}", struct_build_spec.build());
|
||||
let type_string = struct_build_spec.build();
|
||||
|
||||
let result = make_result();
|
||||
|
||||
quote! {
|
||||
impl PrettyPrint for #type_ident {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
|
||||
writer.writeln_indented(#type_string)?;
|
||||
writer.increase_indent();
|
||||
#(#child_print_statements)*
|
||||
writer.decrease_indent();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_pretty_print_impl(build_spec: &BuildSpec) -> Option<TokenStream> {
|
||||
match build_spec {
|
||||
BuildSpec::Struct(struct_spec) => Some(make_struct_p2_impl(struct_spec)),
|
||||
BuildSpec::LeafStruct(leaf_struct) => Some(make_leaf_struct_p2_impl(leaf_struct)),
|
||||
BuildSpec::Enum(enum_spec) => Some(make_tree_enum_p2_impl(enum_spec)),
|
||||
BuildSpec::LeafEnum(leaf_enum) => Some(make_leaf_enum_p2_impl(leaf_enum)),
|
||||
BuildSpec::Production(_) => None,
|
||||
BuildSpec::NodeProduction(_) => None,
|
||||
BuildSpec::PolymorphicType(polymorphic_type) => {
|
||||
Some(make_polymorphic_type_p2_impl(polymorphic_type))
|
||||
}
|
||||
BuildSpec::PolymorphicPassThrough(_) => None,
|
||||
BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop) => {
|
||||
Some(make_polymorphic_enum_loop_p2_impl(polymorphic_enum_loop))
|
||||
}
|
||||
BuildSpec::PolymorphicEnumInnerBuild(polymorphic_enum_inner_build) => Some(
|
||||
make_polymorphic_enum_inner_build_p2_impl(polymorphic_enum_inner_build),
|
||||
),
|
||||
BuildSpec::PolymorphicLeafEnum(_) => None,
|
||||
BuildSpec::PolymorphicTreeEnum(_) => None,
|
||||
}
|
||||
}
|
||||
@ -1,328 +0,0 @@
|
||||
use crate::build_fn_gen::make_build_fn_name;
|
||||
use convert_case::{Case, Casing};
|
||||
|
||||
pub enum BuildSpec {
|
||||
Enum(EnumBuildSpec),
|
||||
Struct(StructBuildSpec),
|
||||
}
|
||||
|
||||
pub struct EnumBuildSpec {
|
||||
name: String,
|
||||
build: String,
|
||||
rules: Vec<EnumRule>,
|
||||
}
|
||||
|
||||
impl EnumBuildSpec {
|
||||
pub fn from_name(name: &str, rules: Vec<EnumRule>) -> Self {
|
||||
EnumBuildSpec {
|
||||
name: name.to_string(),
|
||||
build: name.to_string(),
|
||||
rules,
|
||||
}
|
||||
}
|
||||
|
||||
/// The top-level key for the build spec in the yaml file.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// The enum type to be built, in Pascal case.
|
||||
pub fn build(&self) -> &str {
|
||||
&self.build
|
||||
}
|
||||
|
||||
/// The individual rule specs.
|
||||
pub fn rules(&self) -> &[EnumRule] {
|
||||
&self.rules
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EnumRule {
|
||||
rule: String,
|
||||
build: String,
|
||||
with: String,
|
||||
}
|
||||
|
||||
impl EnumRule {
|
||||
pub fn from_rule(rule: &str) -> Self {
|
||||
Self {
|
||||
rule: rule.to_string(),
|
||||
build: rule.to_string(),
|
||||
with: make_build_fn_name(rule),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(rule: &str, build: &str, with: &str) -> Self {
|
||||
Self {
|
||||
rule: rule.to_string(),
|
||||
build: build.to_string(),
|
||||
with: with.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The enum rule to match, in Pascal case.
|
||||
pub fn rule(&self) -> &str {
|
||||
&self.rule
|
||||
}
|
||||
|
||||
/// The type to build, in Pascal case.
|
||||
pub fn build(&self) -> &str {
|
||||
&self.build
|
||||
}
|
||||
|
||||
/// The build-fn name, in snake case.
|
||||
pub fn with(&self) -> &str {
|
||||
&self.with
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StructBuildSpec {
|
||||
name: String,
|
||||
build: String,
|
||||
var_name: String,
|
||||
with: String,
|
||||
children: Vec<ChildSpec>,
|
||||
}
|
||||
|
||||
impl StructBuildSpec {
|
||||
pub fn from_name(name: &str, child_specs: Vec<ChildSpec>) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
build: name.to_string(),
|
||||
var_name: name.to_case(Case::Snake),
|
||||
with: make_build_fn_name(name),
|
||||
children: child_specs,
|
||||
}
|
||||
}
|
||||
|
||||
/// The top-level name of this build spec.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// The type to be built, in Pascal case.
|
||||
pub fn build(&self) -> &str {
|
||||
&self.build
|
||||
}
|
||||
|
||||
/// The name of the variable to be built, in snake case.
|
||||
pub fn var_name(&self) -> &str {
|
||||
&self.var_name
|
||||
}
|
||||
|
||||
/// The build-fn name, in snake case.
|
||||
pub fn with(&self) -> &str {
|
||||
&self.with
|
||||
}
|
||||
|
||||
/// The children for this build spec.
|
||||
pub fn children(&self) -> &[ChildSpec] {
|
||||
&self.children
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ChildSpec {
|
||||
SkipChild(SkipChild),
|
||||
VecChild(VecChild),
|
||||
SingleChild(SingleChild),
|
||||
}
|
||||
|
||||
pub struct SkipChild {
|
||||
name: String,
|
||||
rule: String,
|
||||
}
|
||||
|
||||
impl SkipChild {
|
||||
pub fn new(name: &str, rule: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
rule: rule.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The name of this child spec.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// The grammar rule to match.
|
||||
pub fn rule(&self) -> &str {
|
||||
&self.rule
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VecChild {
|
||||
name: String,
|
||||
rule: String,
|
||||
build: VecChildToBuild,
|
||||
}
|
||||
|
||||
impl VecChild {
|
||||
pub fn new(name: &str, rule: &str, build: VecChildToBuild) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
rule: rule.to_string(),
|
||||
build,
|
||||
}
|
||||
}
|
||||
|
||||
/// The name of this child.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// The rule to match to build this child.
|
||||
pub fn rule(&self) -> &str {
|
||||
&self.rule
|
||||
}
|
||||
|
||||
/// The build info for this child.
|
||||
pub fn build(&self) -> &VecChildToBuild {
|
||||
&self.build
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VecChildToBuild {
|
||||
Type(VecTypeChildToBuild),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VecTypeChildToBuild {
|
||||
build: String,
|
||||
var_name: String,
|
||||
with: String,
|
||||
}
|
||||
|
||||
impl VecTypeChildToBuild {
|
||||
pub fn new(build: &str, var_name: &str, with: &str) -> Self {
|
||||
Self {
|
||||
build: build.to_string(),
|
||||
var_name: var_name.to_string(),
|
||||
with: with.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The type to build, in Pascal case.
|
||||
pub fn build(&self) -> &str {
|
||||
&self.build
|
||||
}
|
||||
|
||||
/// The name of the variable to build, in snake case.
|
||||
pub fn var_name(&self) -> &str {
|
||||
&self.var_name
|
||||
}
|
||||
|
||||
/// The build-fn name.
|
||||
pub fn with(&self) -> &str {
|
||||
&self.with
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SingleChild {
|
||||
name: String,
|
||||
rule: String,
|
||||
build: SingleChildToBuild,
|
||||
}
|
||||
|
||||
impl SingleChild {
|
||||
pub fn from_name_snake(name: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
rule: name.to_case(Case::Pascal),
|
||||
build: SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(
|
||||
&name.to_case(Case::Pascal),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(name: &str, rule: &str, build: SingleChildToBuild) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
rule: rule.to_string(),
|
||||
build,
|
||||
}
|
||||
}
|
||||
|
||||
/// The name of this child in the yaml file, in snake case.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// The grammar rule to match to build this child.
|
||||
pub fn rule(&self) -> &str {
|
||||
&self.rule
|
||||
}
|
||||
|
||||
/// The specification for what to actually build.
|
||||
pub fn build(&self) -> &SingleChildToBuild {
|
||||
&self.build
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SingleChildToBuild {
|
||||
Type(SingleTypeChildToBuild),
|
||||
Boolean(SingleBooleanChildToBuild),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SingleTypeChildToBuild {
|
||||
build: String,
|
||||
var_name: String,
|
||||
with: String,
|
||||
}
|
||||
|
||||
impl SingleTypeChildToBuild {
|
||||
pub fn from_build_or_rule(build_or_rule: &str) -> Self {
|
||||
Self {
|
||||
build: build_or_rule.to_string(),
|
||||
var_name: build_or_rule.to_case(Case::Snake),
|
||||
with: make_build_fn_name(build_or_rule),
|
||||
}
|
||||
}
|
||||
|
||||
/// The type to build, in Pascal case.
|
||||
pub fn build(&self) -> &str {
|
||||
&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
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SingleBooleanChildToBuild {
|
||||
var_name: String,
|
||||
on: BuildBooleanOn,
|
||||
}
|
||||
|
||||
impl SingleBooleanChildToBuild {
|
||||
pub fn new(var_name: &str, on: BuildBooleanOn) -> Self {
|
||||
Self {
|
||||
var_name: var_name.to_string(),
|
||||
on,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn var_name(&self) -> &str {
|
||||
&self.var_name
|
||||
}
|
||||
|
||||
pub fn on(&self) -> &BuildBooleanOn {
|
||||
&self.on
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BuildBooleanOn {
|
||||
RulePresent,
|
||||
}
|
||||
21
ast-generator/src/spec/leaf_enum_spec.rs
Normal file
21
ast-generator/src/spec/leaf_enum_spec.rs
Normal file
@ -0,0 +1,21 @@
|
||||
pub struct LeafEnumBuildSpec {
|
||||
build: String,
|
||||
rules: Vec<String>,
|
||||
}
|
||||
|
||||
impl LeafEnumBuildSpec {
|
||||
pub fn new(build: &str, rules: Vec<String>) -> Self {
|
||||
Self {
|
||||
build: build.to_string(),
|
||||
rules,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(&self) -> &str {
|
||||
&self.build
|
||||
}
|
||||
|
||||
pub fn rules(&self) -> impl Iterator<Item = &str> {
|
||||
self.rules.iter().map(AsRef::as_ref)
|
||||
}
|
||||
}
|
||||
55
ast-generator/src/spec/leaf_struct_spec.rs
Normal file
55
ast-generator/src/spec/leaf_struct_spec.rs
Normal file
@ -0,0 +1,55 @@
|
||||
pub struct LeafStructBuildSpec {
|
||||
build: String,
|
||||
members: Vec<Box<LeafStructMember>>,
|
||||
derive: Vec<String>,
|
||||
}
|
||||
|
||||
impl LeafStructBuildSpec {
|
||||
pub fn new(build: &str, members: Vec<Box<LeafStructMember>>, derive: Vec<String>) -> Self {
|
||||
Self {
|
||||
build: build.to_string(),
|
||||
members,
|
||||
derive,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(&self) -> &str {
|
||||
&self.build
|
||||
}
|
||||
|
||||
pub fn members(&self) -> impl Iterator<Item = &LeafStructMember> {
|
||||
self.members.iter().map(Box::as_ref)
|
||||
}
|
||||
|
||||
pub fn derive(&self) -> impl Iterator<Item = &str> {
|
||||
self.derive.iter().map(String::as_str)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
FileId,
|
||||
Range
|
||||
}
|
||||
109
ast-generator/src/spec/mod.rs
Normal file
109
ast-generator/src/spec/mod.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use leaf_enum_spec::LeafEnumBuildSpec;
|
||||
use leaf_struct_spec::LeafStructBuildSpec;
|
||||
use node_production_spec::NodeProductionBuildSpec;
|
||||
use polymorphic_enum_inner_build::PolymorphicEnumInnerBuild;
|
||||
use polymorphic_enum_loop_spec::PolymorphicEnumLoopBuildSpec;
|
||||
use polymorphic_leaf_enum::PolymorphicLeafEnum;
|
||||
use polymorphic_pass_through_spec::PolymorphicPassThroughBuildSpec;
|
||||
use polymorphic_tree_enum_spec::PolymorphicTreeEnumSpec;
|
||||
use polymorphic_type_spec::PolymorphicTypeBuildSpec;
|
||||
use production_spec::ProductionBuildSpec;
|
||||
use struct_spec::StructSpec;
|
||||
use tree_enum_spec::TreeEnumBuildSpec;
|
||||
|
||||
pub(crate) mod leaf_enum_spec;
|
||||
pub(crate) mod leaf_struct_spec;
|
||||
pub(crate) mod node_production_spec;
|
||||
pub(crate) mod polymorphic_enum_inner_build;
|
||||
pub(crate) mod polymorphic_enum_loop_spec;
|
||||
pub(crate) mod polymorphic_leaf_enum;
|
||||
pub(crate) mod polymorphic_pass_through_spec;
|
||||
pub(crate) mod polymorphic_tree_enum_spec;
|
||||
pub(crate) mod polymorphic_type_spec;
|
||||
pub(crate) mod production_spec;
|
||||
pub(crate) mod struct_spec;
|
||||
pub(crate) mod tree_enum_spec;
|
||||
|
||||
pub enum BuildSpec {
|
||||
Enum(TreeEnumBuildSpec),
|
||||
LeafEnum(LeafEnumBuildSpec),
|
||||
Struct(StructSpec),
|
||||
LeafStruct(LeafStructBuildSpec),
|
||||
Production(ProductionBuildSpec),
|
||||
NodeProduction(NodeProductionBuildSpec),
|
||||
PolymorphicType(PolymorphicTypeBuildSpec),
|
||||
PolymorphicEnumLoop(PolymorphicEnumLoopBuildSpec),
|
||||
PolymorphicPassThrough(PolymorphicPassThroughBuildSpec),
|
||||
PolymorphicEnumInnerBuild(PolymorphicEnumInnerBuild),
|
||||
PolymorphicLeafEnum(PolymorphicLeafEnum),
|
||||
PolymorphicTreeEnum(PolymorphicTreeEnumSpec),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SpecialChild {
|
||||
name: String,
|
||||
kind: SpecialChildKind,
|
||||
}
|
||||
|
||||
impl SpecialChild {
|
||||
pub fn new(name: &str, kind: SpecialChildKind) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
kind,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &SpecialChildKind {
|
||||
&self.kind
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SpecialChildKind {
|
||||
FileId,
|
||||
Range,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StructField {
|
||||
name: String,
|
||||
kind: String,
|
||||
wrap: Option<StructFieldWrap>,
|
||||
vec: bool,
|
||||
}
|
||||
|
||||
impl StructField {
|
||||
pub fn new(name: &str, kind: &str, wrap: Option<StructFieldWrap>, vec: bool) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
kind: kind.to_string(),
|
||||
wrap,
|
||||
vec,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &str {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
pub fn wrap(&self) -> Option<&StructFieldWrap> {
|
||||
self.wrap.as_ref()
|
||||
}
|
||||
|
||||
pub fn vec(&self) -> bool {
|
||||
self.vec
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum StructFieldWrap {
|
||||
RcRefCell,
|
||||
}
|
||||
27
ast-generator/src/spec/node_production_spec.rs
Normal file
27
ast-generator/src/spec/node_production_spec.rs
Normal file
@ -0,0 +1,27 @@
|
||||
pub struct NodeProductionBuildSpec {
|
||||
name: String,
|
||||
kind: String,
|
||||
with: String
|
||||
}
|
||||
|
||||
impl NodeProductionBuildSpec {
|
||||
pub fn new(name: &str, kind: &str, with: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
kind: kind.to_string(),
|
||||
with: with.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &str {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
pub fn with(&self) -> &str {
|
||||
&self.with
|
||||
}
|
||||
}
|
||||
76
ast-generator/src/spec/polymorphic_enum_inner_build.rs
Normal file
76
ast-generator/src/spec/polymorphic_enum_inner_build.rs
Normal file
@ -0,0 +1,76 @@
|
||||
pub struct PolymorphicEnumInnerBuild {
|
||||
name: String,
|
||||
members: Vec<PolymorphicEnumInnerBuildMember>,
|
||||
rules: Vec<PolymorphicEnumInnerBuildRule>,
|
||||
}
|
||||
|
||||
impl PolymorphicEnumInnerBuild {
|
||||
pub fn new(name: &str, members: Vec<PolymorphicEnumInnerBuildMember>, rules: Vec<PolymorphicEnumInnerBuildRule>) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
members,
|
||||
rules,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn members(&self) -> impl Iterator<Item = &PolymorphicEnumInnerBuildMember> {
|
||||
self.members.iter()
|
||||
}
|
||||
|
||||
pub fn rules(&self) -> impl Iterator<Item = &PolymorphicEnumInnerBuildRule> {
|
||||
self.rules.iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PolymorphicEnumInnerBuildMember {
|
||||
name: String,
|
||||
kind: PolymorphicEnumInnerBuildMemberKind
|
||||
}
|
||||
|
||||
impl PolymorphicEnumInnerBuildMember {
|
||||
pub fn new(name: &str, kind: PolymorphicEnumInnerBuildMemberKind) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
kind
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &PolymorphicEnumInnerBuildMemberKind {
|
||||
&self.kind
|
||||
}
|
||||
}
|
||||
|
||||
pub enum PolymorphicEnumInnerBuildMemberKind {
|
||||
Leaf,
|
||||
Struct
|
||||
}
|
||||
|
||||
pub struct PolymorphicEnumInnerBuildRule {
|
||||
name: String,
|
||||
with: String,
|
||||
}
|
||||
|
||||
impl PolymorphicEnumInnerBuildRule {
|
||||
pub fn new(name: &str, with: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
with: with.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn with(&self) -> &str {
|
||||
&self.with
|
||||
}
|
||||
}
|
||||
162
ast-generator/src/spec/polymorphic_enum_loop_spec.rs
Normal file
162
ast-generator/src/spec/polymorphic_enum_loop_spec.rs
Normal file
@ -0,0 +1,162 @@
|
||||
use crate::spec::{SpecialChild, StructField};
|
||||
|
||||
pub struct PolymorphicEnumLoopBuildSpec {
|
||||
name: String,
|
||||
kind: String,
|
||||
reverse: bool,
|
||||
rules: Vec<Box<PolymorphicEnumLoopRule>>,
|
||||
}
|
||||
|
||||
impl PolymorphicEnumLoopBuildSpec {
|
||||
pub fn new(
|
||||
name: &str,
|
||||
kind: &str,
|
||||
reverse: bool,
|
||||
rules: Vec<Box<PolymorphicEnumLoopRule>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
kind: kind.to_string(),
|
||||
reverse,
|
||||
rules,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &str {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
pub fn reverse(&self) -> bool {
|
||||
self.reverse
|
||||
}
|
||||
|
||||
pub fn rules(&self) -> impl Iterator<Item = &PolymorphicEnumLoopRule> {
|
||||
self.rules.iter().map(Box::as_ref)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum PolymorphicEnumLoopRule {
|
||||
PassThrough(PolymorphicEnumLoopRulePassThrough),
|
||||
Build(PolymorphicEnumLoopRuleBuild),
|
||||
}
|
||||
|
||||
pub struct PolymorphicEnumLoopRulePassThrough {
|
||||
name: String,
|
||||
kind: String,
|
||||
with: String,
|
||||
}
|
||||
|
||||
impl PolymorphicEnumLoopRulePassThrough {
|
||||
pub fn new(name: &str, kind: &str, with: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
kind: kind.to_string(),
|
||||
with: with.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &str {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
pub fn with(&self) -> &str {
|
||||
&self.with
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PolymorphicEnumLoopRuleBuild {
|
||||
name: String,
|
||||
variant: String,
|
||||
children: Vec<Box<PolymorphicEnumLoopRuleBuildChild>>,
|
||||
fields: Vec<StructField>,
|
||||
}
|
||||
|
||||
impl PolymorphicEnumLoopRuleBuild {
|
||||
pub fn new(
|
||||
name: &str,
|
||||
variant: &str,
|
||||
children: Vec<Box<PolymorphicEnumLoopRuleBuildChild>>,
|
||||
fields: Vec<StructField>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
variant: variant.to_string(),
|
||||
children,
|
||||
fields,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn variant(&self) -> &str {
|
||||
&self.variant
|
||||
}
|
||||
|
||||
pub fn children(&self) -> impl Iterator<Item = &PolymorphicEnumLoopRuleBuildChild> {
|
||||
self.children.iter().map(Box::as_ref)
|
||||
}
|
||||
|
||||
pub fn fields(&self) -> &[StructField] {
|
||||
&self.fields
|
||||
}
|
||||
}
|
||||
|
||||
pub enum PolymorphicEnumLoopRuleBuildChild {
|
||||
UseCurrent(PolymorphicEnumLoopChildUseCurrent),
|
||||
OnEach(PolymorphicEnumLoopRuleChildOnEach),
|
||||
Special(SpecialChild),
|
||||
}
|
||||
|
||||
pub struct PolymorphicEnumLoopChildUseCurrent {
|
||||
name: String,
|
||||
kind: String,
|
||||
}
|
||||
|
||||
impl PolymorphicEnumLoopChildUseCurrent {
|
||||
pub fn new(name: &str, kind: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
kind: kind.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &str {
|
||||
&self.kind
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PolymorphicEnumLoopRuleChildOnEach {
|
||||
name: String,
|
||||
rule: String,
|
||||
}
|
||||
|
||||
impl PolymorphicEnumLoopRuleChildOnEach {
|
||||
pub fn new(name: &str, rule: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
rule: rule.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn rule(&self) -> &str {
|
||||
&self.rule
|
||||
}
|
||||
}
|
||||
27
ast-generator/src/spec/polymorphic_leaf_enum.rs
Normal file
27
ast-generator/src/spec/polymorphic_leaf_enum.rs
Normal file
@ -0,0 +1,27 @@
|
||||
pub struct PolymorphicLeafEnum {
|
||||
name: String,
|
||||
kind: String,
|
||||
rules: Vec<String>,
|
||||
}
|
||||
|
||||
impl PolymorphicLeafEnum {
|
||||
pub fn new(name: &str, kind: &str, rules: Vec<String>) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
kind: kind.to_string(),
|
||||
rules,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &str {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
pub fn rules(&self) -> impl Iterator<Item = &str> {
|
||||
self.rules.iter().map(AsRef::as_ref)
|
||||
}
|
||||
}
|
||||
36
ast-generator/src/spec/polymorphic_pass_through_spec.rs
Normal file
36
ast-generator/src/spec/polymorphic_pass_through_spec.rs
Normal file
@ -0,0 +1,36 @@
|
||||
pub struct PolymorphicPassThroughBuildSpec {
|
||||
name: String,
|
||||
build_kind: String,
|
||||
variants: Vec<Box<PolymorphicPassThroughVariant>>,
|
||||
}
|
||||
|
||||
impl PolymorphicPassThroughBuildSpec {
|
||||
pub fn new(
|
||||
name: &str,
|
||||
build_kind: &str,
|
||||
variants: Vec<Box<PolymorphicPassThroughVariant>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
build_kind: build_kind.to_string(),
|
||||
variants,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn build_kind(&self) -> &str {
|
||||
&self.build_kind
|
||||
}
|
||||
|
||||
pub fn variants(&self) -> impl Iterator<Item = &PolymorphicPassThroughVariant> {
|
||||
self.variants.iter().map(Box::as_ref)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum PolymorphicPassThroughVariant {
|
||||
Inner { name: String, kind: String },
|
||||
PassThrough { name: String, kind: String },
|
||||
}
|
||||
27
ast-generator/src/spec/polymorphic_tree_enum_spec.rs
Normal file
27
ast-generator/src/spec/polymorphic_tree_enum_spec.rs
Normal file
@ -0,0 +1,27 @@
|
||||
pub struct PolymorphicTreeEnumSpec {
|
||||
name: String,
|
||||
kind: String,
|
||||
rules: Vec<String>,
|
||||
}
|
||||
|
||||
impl PolymorphicTreeEnumSpec {
|
||||
pub fn new(name: &str, kind: &str, rules: &[&str]) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
kind: kind.to_string(),
|
||||
rules: rules.iter().map(ToString::to_string).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &str {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
pub fn rules(&self) -> impl Iterator<Item = &str> {
|
||||
self.rules.iter().map(AsRef::as_ref)
|
||||
}
|
||||
}
|
||||
49
ast-generator/src/spec/polymorphic_type_spec.rs
Normal file
49
ast-generator/src/spec/polymorphic_type_spec.rs
Normal file
@ -0,0 +1,49 @@
|
||||
pub struct PolymorphicTypeBuildSpec {
|
||||
name: String,
|
||||
variants: Vec<Box<PolymorphicTypeVariant>>,
|
||||
kind: String,
|
||||
}
|
||||
|
||||
impl PolymorphicTypeBuildSpec {
|
||||
pub fn new(name: &str, variants: Vec<Box<PolymorphicTypeVariant>>, kind: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
variants,
|
||||
kind: kind.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn variants(&self) -> impl Iterator<Item = &PolymorphicTypeVariant> {
|
||||
self.variants.iter().map(Box::as_ref)
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &str {
|
||||
self.kind.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PolymorphicTypeVariant {
|
||||
name: String,
|
||||
inner_kind: String,
|
||||
}
|
||||
|
||||
impl PolymorphicTypeVariant {
|
||||
pub fn new(name: &str, inner_kind: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
inner_kind: inner_kind.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn inner_kind(&self) -> &str {
|
||||
&self.inner_kind
|
||||
}
|
||||
}
|
||||
38
ast-generator/src/spec/production_spec.rs
Normal file
38
ast-generator/src/spec/production_spec.rs
Normal file
@ -0,0 +1,38 @@
|
||||
pub struct ProductionBuildSpec {
|
||||
name: String,
|
||||
kind: ProductionKind,
|
||||
}
|
||||
|
||||
impl ProductionBuildSpec {
|
||||
pub fn new(name: &str, kind: ProductionKind) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
kind,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &ProductionKind {
|
||||
&self.kind
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ProductionKind {
|
||||
Int,
|
||||
Long,
|
||||
Double,
|
||||
String(ProductionStringFrom),
|
||||
Boolean(ProductionBooleanFrom),
|
||||
}
|
||||
|
||||
pub enum ProductionStringFrom {
|
||||
StringInner,
|
||||
WholePair,
|
||||
}
|
||||
|
||||
pub enum ProductionBooleanFrom {
|
||||
ParseWholePair,
|
||||
}
|
||||
250
ast-generator/src/spec/struct_spec.rs
Normal file
250
ast-generator/src/spec/struct_spec.rs
Normal file
@ -0,0 +1,250 @@
|
||||
use crate::spec::{SpecialChild, StructField};
|
||||
|
||||
pub struct StructSpec {
|
||||
build: String,
|
||||
children: Vec<Box<StructChild>>,
|
||||
fields: Vec<Box<StructField>>,
|
||||
derive: Vec<String>,
|
||||
}
|
||||
|
||||
impl StructSpec {
|
||||
pub fn new(
|
||||
build: &str,
|
||||
children: Vec<Box<StructChild>>,
|
||||
fields: Vec<Box<StructField>>,
|
||||
derive: Vec<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
build: build.to_string(),
|
||||
children,
|
||||
fields,
|
||||
derive,
|
||||
}
|
||||
}
|
||||
|
||||
/// The type to be built, in Pascal case.
|
||||
pub fn build(&self) -> &str {
|
||||
&self.build
|
||||
}
|
||||
|
||||
/// The children for this build spec.
|
||||
pub fn children(&self) -> impl Iterator<Item = &StructChild> {
|
||||
self.children.iter().map(Box::as_ref)
|
||||
}
|
||||
|
||||
pub fn fields(&self) -> impl Iterator<Item = &StructField> {
|
||||
self.fields.iter().map(Box::as_ref)
|
||||
}
|
||||
|
||||
pub fn derive(&self) -> &[String] {
|
||||
&self.derive
|
||||
}
|
||||
}
|
||||
|
||||
pub enum StructChild {
|
||||
SkipChild(SkipChild),
|
||||
VecChild(VecChild),
|
||||
MemberChild(MemberChild),
|
||||
Special(SpecialChild),
|
||||
}
|
||||
|
||||
impl StructChild {
|
||||
pub fn is_special(&self) -> bool {
|
||||
match self {
|
||||
StructChild::Special(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_special(&self) -> Option<&SpecialChild> {
|
||||
match self {
|
||||
StructChild::Special(special_child) => Some(special_child),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SkipChild {
|
||||
rule: String,
|
||||
}
|
||||
|
||||
impl SkipChild {
|
||||
pub fn new(rule: &str) -> Self {
|
||||
Self {
|
||||
rule: rule.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The grammar rule to match.
|
||||
pub fn rule(&self) -> &str {
|
||||
&self.rule
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VecChild {
|
||||
name: String,
|
||||
rule: String,
|
||||
build: Box<VecChildBuild>,
|
||||
}
|
||||
|
||||
impl VecChild {
|
||||
pub fn new(name: &str, rule: &str, build: Box<VecChildBuild>) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
rule: rule.to_string(),
|
||||
build,
|
||||
}
|
||||
}
|
||||
|
||||
/// The name of this child.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// The rule to match to build this child.
|
||||
pub fn rule(&self) -> &str {
|
||||
&self.rule
|
||||
}
|
||||
|
||||
pub fn build(&self) -> &VecChildBuild {
|
||||
&self.build
|
||||
}
|
||||
}
|
||||
|
||||
pub enum VecChildBuild {
|
||||
String(VecChildStringBuild),
|
||||
Node(VecChildNodeBuild),
|
||||
}
|
||||
|
||||
pub struct VecChildStringBuild {
|
||||
with: String,
|
||||
}
|
||||
|
||||
impl VecChildStringBuild {
|
||||
pub fn new(with: &str) -> Self {
|
||||
Self {
|
||||
with: with.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with(&self) -> &str {
|
||||
&self.with
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VecChildNodeBuild {
|
||||
kind: String,
|
||||
with: String,
|
||||
}
|
||||
|
||||
impl VecChildNodeBuild {
|
||||
pub fn new(kind: &str, with: &str) -> Self {
|
||||
Self {
|
||||
kind: kind.to_string(),
|
||||
with: with.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &str {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
pub fn with(&self) -> &str {
|
||||
&self.with
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MemberChild {
|
||||
name: String,
|
||||
rule: String,
|
||||
optional: bool,
|
||||
build: Box<MemberChildBuild>,
|
||||
}
|
||||
|
||||
impl MemberChild {
|
||||
pub fn new(name: &str, rule: &str, optional: bool, build: Box<MemberChildBuild>) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
rule: rule.to_string(),
|
||||
optional,
|
||||
build,
|
||||
}
|
||||
}
|
||||
|
||||
/// The name of this child in the yaml file, in snake case.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// The grammar rule to match to build this child.
|
||||
pub fn rule(&self) -> &str {
|
||||
&self.rule
|
||||
}
|
||||
|
||||
pub fn optional(&self) -> bool {
|
||||
self.optional
|
||||
}
|
||||
|
||||
/// The specification for what to actually build.
|
||||
pub fn build(&self) -> &MemberChildBuild {
|
||||
&self.build
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MemberChildBuild {
|
||||
Node(NodeMemberBuild),
|
||||
Boolean(BooleanMemberBuild),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NodeMemberBuild {
|
||||
kind: String,
|
||||
with: String,
|
||||
or_else: Option<String>,
|
||||
}
|
||||
|
||||
impl NodeMemberBuild {
|
||||
pub fn new(kind: &str, with: &str, or_else: Option<String>) -> Self {
|
||||
Self {
|
||||
kind: kind.to_string(),
|
||||
with: with.to_string(),
|
||||
or_else,
|
||||
}
|
||||
}
|
||||
|
||||
/// The type to build, in Pascal case.
|
||||
pub fn kind(&self) -> &str {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
pub fn with(&self) -> &str {
|
||||
&self.with
|
||||
}
|
||||
|
||||
/// The default fn to call when unwrapping the child (before passing as arg to new).
|
||||
pub fn or_else(&self) -> Option<&str> {
|
||||
self.or_else.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BooleanMemberBuild {
|
||||
on: BooleanMemberBuildOn,
|
||||
}
|
||||
|
||||
impl BooleanMemberBuild {
|
||||
pub fn new(on: BooleanMemberBuildOn) -> Self {
|
||||
Self { on }
|
||||
}
|
||||
|
||||
pub fn on(&self) -> &BooleanMemberBuildOn {
|
||||
&self.on
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BooleanMemberBuildOn {
|
||||
RulePresent,
|
||||
}
|
||||
117
ast-generator/src/spec/tree_enum_spec.rs
Normal file
117
ast-generator/src/spec/tree_enum_spec.rs
Normal file
@ -0,0 +1,117 @@
|
||||
pub struct TreeEnumBuildSpec {
|
||||
build: String,
|
||||
rules: Vec<Box<TreeEnumRule>>,
|
||||
}
|
||||
|
||||
impl TreeEnumBuildSpec {
|
||||
pub fn new(build: &str, rules: Vec<Box<TreeEnumRule>>) -> Self {
|
||||
TreeEnumBuildSpec {
|
||||
build: build.to_string(),
|
||||
rules,
|
||||
}
|
||||
}
|
||||
|
||||
/// The enum type to be built, in Pascal case.
|
||||
pub fn build(&self) -> &str {
|
||||
&self.build
|
||||
}
|
||||
|
||||
/// The individual rule specs.
|
||||
pub fn rules(&self) -> impl Iterator<Item = &TreeEnumRule> {
|
||||
self.rules.iter().map(Box::as_ref)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TreeEnumRule {
|
||||
rule: String,
|
||||
child: Option<Box<EnumRuleChild>>,
|
||||
}
|
||||
|
||||
impl TreeEnumRule {
|
||||
pub fn new(rule: &str, child: Option<Box<EnumRuleChild>>) -> Self {
|
||||
Self {
|
||||
rule: rule.to_string(),
|
||||
child,
|
||||
}
|
||||
}
|
||||
|
||||
/// The enum rule to match, in Pascal case.
|
||||
pub fn rule(&self) -> &str {
|
||||
&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(EnumRuleChildNodeKind),
|
||||
Int(NameAndWith),
|
||||
Long(NameAndWith),
|
||||
Double(NameAndWith),
|
||||
String(NameAndWith),
|
||||
Boolean(NameAndWith),
|
||||
}
|
||||
|
||||
pub struct EnumRuleChildNodeKind {
|
||||
node_kind: String,
|
||||
with: String,
|
||||
}
|
||||
|
||||
impl EnumRuleChildNodeKind {
|
||||
pub fn new(node_kind: &str, with: &str) -> Self {
|
||||
Self {
|
||||
node_kind: node_kind.to_string(),
|
||||
with: with.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_kind(&self) -> &str {
|
||||
&self.node_kind
|
||||
}
|
||||
|
||||
pub fn with(&self) -> &str {
|
||||
&self.with
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NameAndWith {
|
||||
name: String,
|
||||
with: String,
|
||||
}
|
||||
|
||||
impl NameAndWith {
|
||||
pub fn new(name: &str, with: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
with: with.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn with(&self) -> &str {
|
||||
&self.with
|
||||
}
|
||||
}
|
||||
@ -1,162 +0,0 @@
|
||||
use crate::spec::{
|
||||
BuildSpec, ChildSpec, EnumBuildSpec, SingleBooleanChildToBuild, SingleChildToBuild,
|
||||
SingleTypeChildToBuild, StructBuildSpec, VecChild, VecChildToBuild,
|
||||
};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
fn make_enum_type(build_spec: &EnumBuildSpec) -> TokenStream {
|
||||
let children: Vec<TokenStream> = build_spec
|
||||
.rules()
|
||||
.iter()
|
||||
.map(|rule| {
|
||||
let member_name_ident = format_ident!("{}", rule.rule());
|
||||
let child_name_ident = format_ident!("{}", rule.build());
|
||||
quote! {
|
||||
#member_name_ident(#child_name_ident)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let type_name_ident = format_ident!("{}", build_spec.build());
|
||||
quote! {
|
||||
pub enum #type_name_ident {
|
||||
#(#children),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_vec_child(
|
||||
vec_child: &VecChild,
|
||||
member_names: &mut Vec<Ident>,
|
||||
annotated_members: &mut Vec<TokenStream>,
|
||||
accessors: &mut Vec<TokenStream>,
|
||||
) {
|
||||
let (child_ident, child_ident_mut, child_type_ident) = match vec_child.build() {
|
||||
VecChildToBuild::Type(vec_type_child) => (
|
||||
format_ident!("{}", vec_type_child.var_name()),
|
||||
format_ident!("{}_mut", vec_type_child.var_name()),
|
||||
format_ident!("{}", vec_type_child.build()),
|
||||
),
|
||||
};
|
||||
|
||||
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> {
|
||||
self.#child_ident.iter_mut().map(Box::as_mut)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_single_type_child(
|
||||
single_type_child: &SingleTypeChildToBuild,
|
||||
member_names: &mut Vec<Ident>,
|
||||
annotated_members: &mut Vec<TokenStream>,
|
||||
accessors: &mut Vec<TokenStream>,
|
||||
) {
|
||||
let child_ident = format_ident!("{}", single_type_child.var_name());
|
||||
let child_ident_mut = format_ident!("{}_mut", single_type_child.var_name());
|
||||
let child_type_ident = format_ident!("{}", single_type_child.build());
|
||||
member_names.push(child_ident.clone());
|
||||
annotated_members.push(quote! {
|
||||
#child_ident: Box<#child_type_ident>
|
||||
});
|
||||
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(
|
||||
single_boolean_child: &SingleBooleanChildToBuild,
|
||||
member_names: &mut Vec<Ident>,
|
||||
annotated_members: &mut Vec<TokenStream>,
|
||||
accessors: &mut Vec<TokenStream>,
|
||||
) {
|
||||
let child_ident = format_ident!("{}", single_boolean_child.var_name());
|
||||
member_names.push(child_ident.clone());
|
||||
annotated_members.push(quote! {
|
||||
#child_ident: bool
|
||||
});
|
||||
accessors.push(quote! {
|
||||
pub fn #child_ident(&self) -> bool {
|
||||
self.#child_ident
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn make_struct_type(build_spec: &StructBuildSpec) -> TokenStream {
|
||||
let mut member_names: Vec<Ident> = vec![];
|
||||
let mut annotated_members: Vec<TokenStream> = vec![];
|
||||
let mut accessors: Vec<TokenStream> = vec![];
|
||||
|
||||
for child_spec in build_spec.children().iter() {
|
||||
match child_spec {
|
||||
ChildSpec::SkipChild(_) => {}
|
||||
ChildSpec::VecChild(vec_child) => {
|
||||
handle_vec_child(
|
||||
vec_child,
|
||||
&mut member_names,
|
||||
&mut annotated_members,
|
||||
&mut accessors,
|
||||
);
|
||||
}
|
||||
ChildSpec::SingleChild(single_child) => {
|
||||
match single_child.build() {
|
||||
SingleChildToBuild::Type(single_type_child) => {
|
||||
handle_single_type_child(
|
||||
single_type_child,
|
||||
&mut member_names,
|
||||
&mut annotated_members,
|
||||
&mut accessors,
|
||||
);
|
||||
}
|
||||
SingleChildToBuild::Boolean(single_boolean_child) => {
|
||||
handle_single_boolean_child(
|
||||
single_boolean_child,
|
||||
&mut member_names,
|
||||
&mut annotated_members,
|
||||
&mut accessors,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let type_ident = format_ident!("{}", build_spec.build());
|
||||
|
||||
quote! {
|
||||
pub struct #type_ident {
|
||||
#(#annotated_members),*
|
||||
}
|
||||
|
||||
impl #type_ident {
|
||||
pub fn new(#(#annotated_members),*) -> Self {
|
||||
Self {
|
||||
#(#member_names),*
|
||||
}
|
||||
}
|
||||
|
||||
#(#accessors)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_type(build_spec: &BuildSpec) -> TokenStream {
|
||||
match build_spec {
|
||||
BuildSpec::Enum(enum_build_spec) => make_enum_type(enum_build_spec),
|
||||
BuildSpec::Struct(struct_build_spec) => make_struct_type(struct_build_spec),
|
||||
}
|
||||
}
|
||||
37
ast-generator/src/type_gen/enum_type.rs
Normal file
37
ast-generator/src/type_gen/enum_type.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use crate::spec::tree_enum_spec::{EnumRuleChildKind, TreeEnumBuildSpec};
|
||||
|
||||
pub fn make_enum_type(build_spec: &TreeEnumBuildSpec) -> TokenStream {
|
||||
let children: Vec<TokenStream> = build_spec
|
||||
.rules()
|
||||
.map(|enum_rule| {
|
||||
let member_name_ident = format_ident!("{}", enum_rule.rule());
|
||||
if let Some(enum_rule_child) = enum_rule.child() {
|
||||
let child_type_ident = match enum_rule_child.kind() {
|
||||
EnumRuleChildKind::Node(node_child) => {
|
||||
format_ident!("{}", node_child.node_kind())
|
||||
}
|
||||
EnumRuleChildKind::Int(_) => format_ident!("i32"),
|
||||
EnumRuleChildKind::Long(_) => format_ident!("i64"),
|
||||
EnumRuleChildKind::Double(_) => format_ident!("f64"),
|
||||
EnumRuleChildKind::String(_) => format_ident!("String"),
|
||||
EnumRuleChildKind::Boolean(_) => format_ident!("bool"),
|
||||
};
|
||||
quote! {
|
||||
#member_name_ident(#child_type_ident)
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#member_name_ident
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let type_name_ident = format_ident!("{}", build_spec.build());
|
||||
quote! {
|
||||
pub enum #type_name_ident {
|
||||
#(#children),*
|
||||
}
|
||||
}
|
||||
}
|
||||
22
ast-generator/src/type_gen/leaf_enum_type.rs
Normal file
22
ast-generator/src/type_gen/leaf_enum_type.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use crate::spec::leaf_enum_spec::LeafEnumBuildSpec;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub 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);
|
||||
quote! {
|
||||
#rule_name_ident
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
pub enum #type_name_ident {
|
||||
#(#children),*
|
||||
}
|
||||
}
|
||||
}
|
||||
118
ast-generator/src/type_gen/leaf_struct_type.rs
Normal file
118
ast-generator/src/type_gen/leaf_struct_type.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use crate::spec::leaf_struct_spec::{LeafStructBuildSpec, LeafStructMemberKind};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_leaf_struct_type(build_spec: &LeafStructBuildSpec) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", build_spec.build());
|
||||
|
||||
let annotated_members = build_spec
|
||||
.members()
|
||||
.map(|member| {
|
||||
let name_ident = format_ident!("{}", member.name());
|
||||
let type_ident = match member.kind() {
|
||||
LeafStructMemberKind::String => quote! { String },
|
||||
LeafStructMemberKind::FileId => quote! { usize },
|
||||
LeafStructMemberKind::Range => quote! { Range<usize> },
|
||||
};
|
||||
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 }
|
||||
}
|
||||
LeafStructMemberKind::FileId => {
|
||||
quote! { usize }
|
||||
}
|
||||
LeafStructMemberKind::Range => {
|
||||
quote! { Range<usize> }
|
||||
}
|
||||
};
|
||||
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()
|
||||
}
|
||||
}
|
||||
_ => quote! { #member_ident },
|
||||
}
|
||||
})
|
||||
.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
|
||||
}
|
||||
}
|
||||
}
|
||||
LeafStructMemberKind::FileId => {
|
||||
quote! {
|
||||
pub fn #name_ident(&self) -> usize {
|
||||
self.#name_ident
|
||||
}
|
||||
}
|
||||
}
|
||||
LeafStructMemberKind::Range => {
|
||||
quote! {
|
||||
pub fn #name_ident(&self) -> Range<usize> {
|
||||
self.#name_ident
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let struct_stream = if build_spec.derive().count() > 0 {
|
||||
let derives = build_spec.derive().map(|derive| {
|
||||
format_ident!("{}", derive)
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
#[derive(#(#derives),*)]
|
||||
pub struct #type_ident {
|
||||
#(#annotated_members),*
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
pub struct #type_ident {
|
||||
#(#annotated_members),*
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
#struct_stream
|
||||
|
||||
impl #type_ident {
|
||||
pub fn new(#(#member_args),*) -> Self {
|
||||
Self {
|
||||
#(#initializers),*
|
||||
}
|
||||
}
|
||||
|
||||
#(#accessors)*
|
||||
}
|
||||
}
|
||||
}
|
||||
44
ast-generator/src/type_gen/mod.rs
Normal file
44
ast-generator/src/type_gen/mod.rs
Normal file
@ -0,0 +1,44 @@
|
||||
mod enum_type;
|
||||
mod leaf_enum_type;
|
||||
mod leaf_struct_type;
|
||||
mod polymorphic_enum_inner_build_type;
|
||||
mod polymorphic_enum_loop_type;
|
||||
mod polymorphic_type_type;
|
||||
mod struct_type;
|
||||
|
||||
use crate::spec::BuildSpec;
|
||||
use crate::type_gen::enum_type::make_enum_type;
|
||||
use crate::type_gen::leaf_enum_type::make_leaf_enum_type;
|
||||
use crate::type_gen::leaf_struct_type::make_leaf_struct_type;
|
||||
use crate::type_gen::polymorphic_enum_inner_build_type::make_polymorphic_enum_inner_build_type;
|
||||
use crate::type_gen::polymorphic_enum_loop_type::make_polymorphic_enum_loop_type;
|
||||
use crate::type_gen::polymorphic_type_type::make_polymorphic_type_type;
|
||||
use crate::type_gen::struct_type::make_struct_type;
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
pub fn make_type(build_spec: &BuildSpec) -> Option<TokenStream> {
|
||||
match 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::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::Production(_) => None,
|
||||
BuildSpec::NodeProduction(_) => None,
|
||||
BuildSpec::PolymorphicType(polymorphic_build_spec) => {
|
||||
Some(make_polymorphic_type_type(polymorphic_build_spec))
|
||||
}
|
||||
BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop_spec) => {
|
||||
Some(make_polymorphic_enum_loop_type(polymorphic_enum_loop_spec))
|
||||
}
|
||||
BuildSpec::PolymorphicPassThrough(_) => None,
|
||||
BuildSpec::PolymorphicEnumInnerBuild(polymorphic_enum_inner_build_spec) => Some(
|
||||
make_polymorphic_enum_inner_build_type(polymorphic_enum_inner_build_spec),
|
||||
),
|
||||
BuildSpec::PolymorphicLeafEnum(_) => None,
|
||||
BuildSpec::PolymorphicTreeEnum(_) => None,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use crate::spec::polymorphic_enum_inner_build::{PolymorphicEnumInnerBuild, PolymorphicEnumInnerBuildMemberKind};
|
||||
|
||||
pub fn make_polymorphic_enum_inner_build_type(spec: &PolymorphicEnumInnerBuild) -> TokenStream {
|
||||
let members = spec.members()
|
||||
.map(|member| {
|
||||
let name_ident = format_ident!("{}", member.name());
|
||||
match member.kind() {
|
||||
PolymorphicEnumInnerBuildMemberKind::Leaf => {
|
||||
quote! { #name_ident }
|
||||
}
|
||||
PolymorphicEnumInnerBuildMemberKind::Struct => {
|
||||
quote! { #name_ident(#name_ident) }
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let type_name_ident = format_ident!("{}", spec.name());
|
||||
quote! {
|
||||
pub enum #type_name_ident {
|
||||
#(#members,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
193
ast-generator/src/type_gen/polymorphic_enum_loop_type.rs
Normal file
193
ast-generator/src/type_gen/polymorphic_enum_loop_type.rs
Normal file
@ -0,0 +1,193 @@
|
||||
use crate::spec::polymorphic_enum_loop_spec::{
|
||||
PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuildChild,
|
||||
};
|
||||
use crate::spec::SpecialChildKind;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_polymorphic_enum_loop_type(spec: &PolymorphicEnumLoopBuildSpec) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", spec.name());
|
||||
|
||||
let build = spec
|
||||
.rules()
|
||||
.find(|rule| match rule {
|
||||
PolymorphicEnumLoopRule::Build(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
.map(|rule| match rule {
|
||||
PolymorphicEnumLoopRule::Build(build) => build,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let annotated_members = build
|
||||
.children()
|
||||
.map(|child| match child {
|
||||
PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => {
|
||||
let child_ident = format_ident!("{}", use_current.name());
|
||||
let child_type_ident = format_ident!("{}", use_current.kind());
|
||||
quote! {
|
||||
#child_ident: Box<#child_type_ident>
|
||||
}
|
||||
}
|
||||
PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => {
|
||||
let child_ident = format_ident!("{}", on_each.name());
|
||||
let child_type_ident = format_ident!("{}", on_each.rule());
|
||||
quote! {
|
||||
#child_ident: Box<#child_type_ident>
|
||||
}
|
||||
}
|
||||
PolymorphicEnumLoopRuleBuildChild::Special(special_child) => {
|
||||
let child_ident = format_ident!("{}", special_child.name());
|
||||
let child_type_ident = match special_child.kind() {
|
||||
SpecialChildKind::FileId => {
|
||||
quote! { usize }
|
||||
}
|
||||
SpecialChildKind::Range => {
|
||||
quote! { Range<usize> }
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
#child_ident: #child_type_ident
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let annotated_fields = build
|
||||
.fields()
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let field_ident = format_ident!("{}", field.name());
|
||||
let field_type = format_ident!("{}", field.kind());
|
||||
quote! {
|
||||
#field_ident: Option<#field_type>
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let member_names = build
|
||||
.children()
|
||||
.map(|child| match child {
|
||||
PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => {
|
||||
format_ident!("{}", use_current.name())
|
||||
}
|
||||
PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => {
|
||||
format_ident!("{}", on_each.name())
|
||||
}
|
||||
PolymorphicEnumLoopRuleBuildChild::Special(special_child) => {
|
||||
format_ident!("{}", special_child.name())
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let field_initializers = build
|
||||
.fields()
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let field_ident = format_ident!("{}", field.name());
|
||||
quote! { #field_ident: None }
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let accessors = build
|
||||
.children()
|
||||
.map(|child| {
|
||||
let (child_ident, child_type_ident) = match child {
|
||||
PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => {
|
||||
let child_ident = format_ident!("{}", use_current.name());
|
||||
let child_type_ident = format_ident!("{}", use_current.kind());
|
||||
(child_ident, quote! { #child_type_ident })
|
||||
}
|
||||
PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => {
|
||||
let child_ident = format_ident!("{}", on_each.name());
|
||||
let child_type_ident = format_ident!("{}", on_each.rule());
|
||||
(child_ident, quote! { #child_type_ident })
|
||||
}
|
||||
PolymorphicEnumLoopRuleBuildChild::Special(special_child) => {
|
||||
let child_ident = format_ident!("{}", special_child.name());
|
||||
let child_type_ident = match special_child.kind() {
|
||||
SpecialChildKind::FileId => {
|
||||
quote! { usize }
|
||||
}
|
||||
SpecialChildKind::Range => {
|
||||
quote! { Range<usize> }
|
||||
}
|
||||
};
|
||||
(child_ident, child_type_ident)
|
||||
}
|
||||
};
|
||||
let child_mut_ident = format_ident!("{}_mut", child_ident);
|
||||
|
||||
let as_ref = match child {
|
||||
PolymorphicEnumLoopRuleBuildChild::Special(_) => {
|
||||
quote! { &self.#child_ident }
|
||||
}
|
||||
_ => quote! { self.#child_ident.as_ref() },
|
||||
};
|
||||
|
||||
let as_mut = match child {
|
||||
PolymorphicEnumLoopRuleBuildChild::Special(_) => {
|
||||
quote! { &mut self.#child_ident }
|
||||
}
|
||||
_ => quote! { self.#child_ident.as_mut() },
|
||||
};
|
||||
|
||||
quote! {
|
||||
pub fn #child_ident(&self) -> &#child_type_ident {
|
||||
#as_ref
|
||||
}
|
||||
|
||||
pub fn #child_mut_ident(&mut self) -> &mut #child_type_ident {
|
||||
#as_mut
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let field_accessors = build
|
||||
.fields()
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let field_ident = format_ident!("{}", field.name());
|
||||
let field_ident_mut = format_ident!("{}_mut", field.name());
|
||||
let field_type = format_ident!("{}", field.kind());
|
||||
|
||||
let set_ident = format_ident!("set_{}", field.name());
|
||||
|
||||
quote! {
|
||||
pub fn #field_ident(&self) -> Option<&#field_type> {
|
||||
self.#field_ident.as_ref()
|
||||
}
|
||||
|
||||
pub fn #field_ident_mut(&mut self) -> Option<&mut #field_type> {
|
||||
self.#field_ident.as_mut()
|
||||
}
|
||||
|
||||
pub fn #set_ident(&mut self, #field_ident: #field_type) {
|
||||
self.#field_ident = Some(#field_ident);
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
pub struct #type_ident {
|
||||
#(#annotated_members,)*
|
||||
#(#annotated_fields,)*
|
||||
}
|
||||
|
||||
impl #type_ident {
|
||||
pub fn new(#(#annotated_members),*) -> Self {
|
||||
Self {
|
||||
#(#member_names,)*
|
||||
#(#field_initializers,)*
|
||||
}
|
||||
}
|
||||
|
||||
#(#accessors)*
|
||||
|
||||
#(#field_accessors)*
|
||||
}
|
||||
}
|
||||
}
|
||||
22
ast-generator/src/type_gen/polymorphic_type_type.rs
Normal file
22
ast-generator/src/type_gen/polymorphic_type_type.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use crate::spec::polymorphic_type_spec::PolymorphicTypeBuildSpec;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_polymorphic_type_type(build_spec: &PolymorphicTypeBuildSpec) -> TokenStream {
|
||||
let members = build_spec
|
||||
.variants()
|
||||
.map(|enum_member| {
|
||||
let member_ident = format_ident!("{}", enum_member.name());
|
||||
let inner_type_ident = format_ident!("{}", enum_member.inner_kind());
|
||||
quote! {
|
||||
#member_ident(#inner_type_ident)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let type_name_ident = format_ident!("{}", build_spec.name());
|
||||
quote! {
|
||||
pub enum #type_name_ident {
|
||||
#(#members),*
|
||||
}
|
||||
}
|
||||
}
|
||||
339
ast-generator/src/type_gen/struct_type.rs
Normal file
339
ast-generator/src/type_gen/struct_type.rs
Normal file
@ -0,0 +1,339 @@
|
||||
use crate::spec::struct_spec::{
|
||||
MemberChild, MemberChildBuild, StructChild, StructSpec, VecChild, VecChildBuild,
|
||||
};
|
||||
use crate::spec::{SpecialChild, SpecialChildKind, StructField, StructFieldWrap};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
fn make_field_accessors(field: &StructField) -> TokenStream {
|
||||
let field_ident = format_ident!("{}", field.name());
|
||||
let field_type = {
|
||||
let inner = format_ident!("{}", field.kind());
|
||||
if let Some(wrap) = field.wrap() {
|
||||
match wrap {
|
||||
StructFieldWrap::RcRefCell => {
|
||||
quote! { Rc<RefCell<#inner>> }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! { #inner }
|
||||
}
|
||||
};
|
||||
let field_ident_mut = format_ident!("{}_mut", field.name());
|
||||
|
||||
if field.vec() {
|
||||
quote! {
|
||||
pub fn #field_ident(&self) -> &[#field_type] {
|
||||
self.#field_ident.as_slice()
|
||||
}
|
||||
|
||||
pub fn #field_ident_mut(&mut self) -> &mut Vec<#field_type> {
|
||||
&mut self.#field_ident
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let set_field_ident = format_ident!("set_{}", field.name());
|
||||
quote! {
|
||||
pub fn #field_ident(&self) -> Option<&#field_type> {
|
||||
self.#field_ident.as_ref()
|
||||
}
|
||||
|
||||
pub fn #field_ident_mut(&mut self) -> Option<&mut #field_type> {
|
||||
self.#field_ident.as_mut()
|
||||
}
|
||||
|
||||
pub fn #set_field_ident(&mut self, #field_ident: #field_type) {
|
||||
self.#field_ident = Some(#field_ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_field_initializer(field: &StructField) -> TokenStream {
|
||||
let field_ident = format_ident!("{}", field.name());
|
||||
if field.vec() {
|
||||
quote! { #field_ident: vec![] }
|
||||
} else {
|
||||
quote! { #field_ident: None }
|
||||
}
|
||||
}
|
||||
|
||||
fn make_annotated_field(field: &StructField) -> TokenStream {
|
||||
let field_ident = format_ident!("{}", field.name());
|
||||
let field_type = if let Some(wrap) = field.wrap() {
|
||||
let inner = format_ident!("{}", field.kind());
|
||||
match wrap {
|
||||
StructFieldWrap::RcRefCell => {
|
||||
quote! { Rc<RefCell<#inner>> }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let inner = format_ident!("{}", field.kind());
|
||||
quote! { #inner }
|
||||
};
|
||||
|
||||
if field.vec() {
|
||||
quote! {
|
||||
#field_ident: Vec<#field_type>
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#field_ident: Option<#field_type>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_vec_child_accessors(vec_child: &VecChild) -> TokenStream {
|
||||
let child_ident = format_ident!("{}", vec_child.name());
|
||||
match vec_child.build() {
|
||||
VecChildBuild::String(_) => {
|
||||
quote! {
|
||||
pub fn #child_ident(&self) -> impl Iterator<Item = &str> {
|
||||
self.#child_ident.iter().map(String::as_str)
|
||||
}
|
||||
}
|
||||
}
|
||||
VecChildBuild::Node(vec_child_node_build) => {
|
||||
let child_type_ident = format_ident!("{}", vec_child_node_build.kind());
|
||||
let child_ident_mut = format_ident!("{}_mut", vec_child.name());
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_member_child_accessors(member_child: &MemberChild) -> TokenStream {
|
||||
let child_ident = format_ident!("{}", member_child.name());
|
||||
match member_child.build() {
|
||||
MemberChildBuild::Node(node_member_build) => {
|
||||
let return_type_ident = format_ident!("{}", node_member_build.kind());
|
||||
let child_ident_mut = format_ident!("{}_mut", member_child.name());
|
||||
|
||||
if member_child.optional() {
|
||||
quote! {
|
||||
pub fn #child_ident(&self) -> Option<&#return_type_ident> {
|
||||
if let Some(#child_ident) = &self.#child_ident {
|
||||
Some(#child_ident.as_ref())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn #child_ident_mut(&mut self) -> Option<&mut #return_type_ident> {
|
||||
if let Some(#child_ident) = &mut self.#child_ident {
|
||||
Some(#child_ident.as_mut())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
pub fn #child_ident(&self) -> &#return_type_ident {
|
||||
self.#child_ident.as_ref()
|
||||
}
|
||||
|
||||
pub fn #child_ident_mut(&mut self) -> &mut #return_type_ident {
|
||||
self.#child_ident.as_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MemberChildBuild::Boolean(_) => {
|
||||
quote! {
|
||||
pub fn #child_ident(&self) -> bool {
|
||||
self.#child_ident
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_special_child_accessors(special_child: &SpecialChild) -> TokenStream {
|
||||
let child_ident = format_ident!("{}", special_child.name());
|
||||
match special_child.kind() {
|
||||
SpecialChildKind::FileId => {
|
||||
quote! {
|
||||
pub fn #child_ident(&self) -> usize {
|
||||
self.#child_ident
|
||||
}
|
||||
}
|
||||
}
|
||||
SpecialChildKind::Range => {
|
||||
quote! {
|
||||
pub fn #child_ident(&self) -> Range<usize> {
|
||||
self.#child_ident
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_accessors(child: &StructChild) -> Option<TokenStream> {
|
||||
match child {
|
||||
StructChild::SkipChild(_) => None,
|
||||
StructChild::VecChild(vec_child) => Some(make_vec_child_accessors(vec_child)),
|
||||
StructChild::MemberChild(member_child) => Some(make_member_child_accessors(member_child)),
|
||||
StructChild::Special(special_child) => Some(make_special_child_accessors(special_child)),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_member_ident(child: &StructChild) -> Option<Ident> {
|
||||
match child {
|
||||
StructChild::SkipChild(_) => None,
|
||||
StructChild::VecChild(vec_child) => Some(format_ident!("{}", vec_child.name())),
|
||||
StructChild::MemberChild(member_child) => Some(format_ident!("{}", member_child.name())),
|
||||
StructChild::Special(special_child) => Some(format_ident!("{}", special_child.name())),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_vec_child_annotated_member(vec_child: &VecChild) -> TokenStream {
|
||||
let child_ident = format_ident!("{}", vec_child.name());
|
||||
let type_stream = match vec_child.build() {
|
||||
VecChildBuild::String(_) => quote! { String },
|
||||
VecChildBuild::Node(vec_child_node_build) => {
|
||||
let type_ident = format_ident!("{}", vec_child_node_build.kind());
|
||||
quote! { Box<#type_ident> }
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
#child_ident: Vec<#type_stream>
|
||||
}
|
||||
}
|
||||
|
||||
fn make_member_child_type_ident(member_child: &MemberChild) -> TokenStream {
|
||||
match member_child.build() {
|
||||
MemberChildBuild::Node(node_member_build) => {
|
||||
let type_ident = format_ident!("{}", node_member_build.kind());
|
||||
quote! { Box<#type_ident> }
|
||||
}
|
||||
MemberChildBuild::Boolean(_) => {
|
||||
quote! { bool }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_member_child_annotated_member(member_child: &MemberChild) -> TokenStream {
|
||||
let child_name_ident = format_ident!("{}", member_child.name());
|
||||
let type_ident = make_member_child_type_ident(member_child);
|
||||
let type_stream = if member_child.optional() {
|
||||
quote! { Option<#type_ident> }
|
||||
} else {
|
||||
quote! { #type_ident }
|
||||
};
|
||||
|
||||
quote! {
|
||||
#child_name_ident: #type_stream
|
||||
}
|
||||
}
|
||||
|
||||
fn make_special_child_annotated_member(special_child: &SpecialChild) -> TokenStream {
|
||||
let child_ident = format_ident!("{}", special_child.name());
|
||||
let child_type_ident = match special_child.kind() {
|
||||
SpecialChildKind::FileId => quote! { usize },
|
||||
SpecialChildKind::Range => quote! { Range<usize> },
|
||||
};
|
||||
quote! {
|
||||
#child_ident: #child_type_ident
|
||||
}
|
||||
}
|
||||
|
||||
fn make_annotated_member(child: &StructChild) -> Option<TokenStream> {
|
||||
match child {
|
||||
StructChild::SkipChild(_) => None,
|
||||
StructChild::VecChild(vec_child) => Some(make_vec_child_annotated_member(vec_child)),
|
||||
StructChild::MemberChild(member_child) => {
|
||||
Some(make_member_child_annotated_member(member_child))
|
||||
}
|
||||
StructChild::Special(special_child) => {
|
||||
Some(make_special_child_annotated_member(special_child))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_struct_type(build_spec: &StructSpec) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", build_spec.build());
|
||||
let annotated_children_members = build_spec
|
||||
.children()
|
||||
.map(|child| make_annotated_member(child))
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let annotated_fields = build_spec
|
||||
.fields()
|
||||
.map(|field| make_annotated_field(field))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let member_names = build_spec
|
||||
.children()
|
||||
.map(|child| make_member_ident(child))
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let field_initializers = build_spec
|
||||
.fields()
|
||||
.map(|field| make_field_initializer(field))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let child_accessors = build_spec
|
||||
.children()
|
||||
.map(|child| make_accessors(child))
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let field_accessors = build_spec
|
||||
.fields()
|
||||
.map(|field| make_field_accessors(field))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let struct_stream = {
|
||||
let base = quote! {
|
||||
pub struct #type_ident {
|
||||
#(#annotated_children_members,)*
|
||||
#(#annotated_fields,)*
|
||||
}
|
||||
};
|
||||
if !build_spec.derive().is_empty() {
|
||||
let derives = build_spec
|
||||
.derive()
|
||||
.iter()
|
||||
.map(|derive| format_ident!("{}", derive))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
#[derive(#(#derives,)*)]
|
||||
#base
|
||||
}
|
||||
} else {
|
||||
base
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
#struct_stream
|
||||
|
||||
impl #type_ident {
|
||||
pub fn new(#(#annotated_children_members),*) -> Self {
|
||||
Self {
|
||||
#(#member_names,)*
|
||||
#(#field_initializers,)*
|
||||
}
|
||||
}
|
||||
|
||||
#(#child_accessors)*
|
||||
#(#field_accessors)*
|
||||
}
|
||||
}
|
||||
}
|
||||
18
ast-generator/src/walk.rs
Normal file
18
ast-generator/src/walk.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use crate::spec::BuildSpec;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub fn make_walk_fn(specs: &[BuildSpec]) -> TokenStream {
|
||||
quote! {
|
||||
use crate::ast::node::*;
|
||||
use crate::ast::ast_node::*;
|
||||
|
||||
pub fn walk_depth_first<'a>(node: &'a dyn AstNode<'a>, f: &mut impl FnMut(AstNodeRef<'a>)) {
|
||||
use AstNodeRef::*;
|
||||
for child in node.children() {
|
||||
walk_depth_first(child, f);
|
||||
}
|
||||
f(node.as_node_ref());
|
||||
}
|
||||
}
|
||||
}
|
||||
45
build.rs
45
build.rs
@ -1,9 +1,40 @@
|
||||
fn main() -> std::io::Result<()> {
|
||||
println!("cargo:rerun-if-changed=src/parser/deimos.pest");
|
||||
// let out_dir = env::var("OUT_DIR").unwrap();
|
||||
// let out_dir_path = Path::new(&out_dir);
|
||||
// let testing_txt_path = out_dir_path.join("testing.rs");
|
||||
// let output = test_dump();
|
||||
// write(&testing_txt_path, output)?;
|
||||
use ast_generator::{get_build_specs, generate_files};
|
||||
use cst_test_generator::generate_test_files;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
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 = get_build_specs(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(())
|
||||
}
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
4
examples/array_return.dm
Normal file
4
examples/array_return.dm
Normal file
@ -0,0 +1,4 @@
|
||||
fn test()
|
||||
println(42)
|
||||
[0, 1, 2]
|
||||
end
|
||||
3
examples/forty_two.dm
Normal file
3
examples/forty_two.dm
Normal file
@ -0,0 +1,3 @@
|
||||
fn main()
|
||||
println(42)
|
||||
end
|
||||
8
examples/hello_42.dm
Normal file
8
examples/hello_42.dm
Normal file
@ -0,0 +1,8 @@
|
||||
use std::core::println
|
||||
|
||||
fn main(args: Array<String>)
|
||||
let hello = 42
|
||||
let world = hello + 16
|
||||
println(hello)
|
||||
println(world)
|
||||
end
|
||||
3
examples/incompat.dm
Normal file
3
examples/incompat.dm
Normal file
@ -0,0 +1,3 @@
|
||||
fn main()
|
||||
let x = 1 + 2L
|
||||
end
|
||||
4
examples/op_prec.dm
Normal file
4
examples/op_prec.dm
Normal file
@ -0,0 +1,4 @@
|
||||
fn main()
|
||||
let n = 1 + 2 * 3 + 4
|
||||
println(n)
|
||||
end
|
||||
5
examples/op_prec.dvm_ir
Normal file
5
examples/op_prec.dvm_ir
Normal file
@ -0,0 +1,5 @@
|
||||
fn main() -> Void
|
||||
$0 = 2 * 3
|
||||
$1 = 1 + $0
|
||||
$2 = $1 + 4
|
||||
call std::core::println($2)
|
||||
19
examples/worlds.dm
Normal file
19
examples/worlds.dm
Normal file
@ -0,0 +1,19 @@
|
||||
class World(pub name: String, pub color: String) end
|
||||
|
||||
fn getWorlds() -> List<World> = [
|
||||
World('Mercury', 'Red'),
|
||||
World('Earth', 'Blue'),
|
||||
World('Jupiter', 'Orange')
|
||||
]
|
||||
|
||||
fn findWorldByColor(worlds: List<World>, color: String) -> String
|
||||
worlds.find { it -> it.color == color }
|
||||
.map { it -> it.name }
|
||||
.expect("No world has the given color ${color}")
|
||||
end
|
||||
|
||||
fn main()
|
||||
let worlds = getWorlds()
|
||||
let blueWorld = findWorldByColor(worlds, 'Blue')
|
||||
println("Hello, ${blueWorld}!")
|
||||
end
|
||||
89
examples/worlds.dvm_ir
Normal file
89
examples/worlds.dvm_ir
Normal file
@ -0,0 +1,89 @@
|
||||
struct World
|
||||
pub String* name
|
||||
pub String* color
|
||||
end
|
||||
|
||||
void World::ctor(World *self, String* name, String* color)
|
||||
self.name = move name
|
||||
self.color = move color
|
||||
end
|
||||
|
||||
const String %s0 = 'Mercury'
|
||||
const String %s1 = 'Red'
|
||||
const String %s2 = 'Earth'
|
||||
const String %s3 = 'Blue'
|
||||
const String %s4 = 'Jupiter'
|
||||
const String %s5 = 'Orange'
|
||||
|
||||
List<World> *getWorlds()
|
||||
List<World> *$0 = alloc std::list::ArrayList
|
||||
call std::list::ArrayList::ctor($0)
|
||||
World *$1 = alloc World
|
||||
call World::ctor($1, %s0, %s1)
|
||||
call std::list::ArrayList::add($0, $1)
|
||||
World *$2 = alloc World
|
||||
call World::ctor($2, %s2, %s3)
|
||||
call std::list::ArrayList::add($0, $2)
|
||||
World *$3 = alloc World
|
||||
call World::ctor($3, %s4, %s5)
|
||||
call std::list::ArrayList::add($0, $3)
|
||||
ret $0
|
||||
end
|
||||
|
||||
struct __findWorldByColor__cl0_captures
|
||||
pub String* color
|
||||
end
|
||||
|
||||
Boolean __findWorldByColor_cl0(__findWorldByColor__cl0_captures *__captures, World *it)
|
||||
String *$0 = it.color
|
||||
String *$1 = __captures.color
|
||||
Boolean $2 = *$0 == *$1
|
||||
ret $2
|
||||
end
|
||||
|
||||
String* __findWorldByColor_cl1(World* it)
|
||||
$0 = it.name
|
||||
ret $0
|
||||
end
|
||||
|
||||
|
||||
String *findWorldByColor(List<World> *worlds, String *color)
|
||||
__findWorldByColor__cl0_captures *$0 = alloc __findWorldByColor__cl0_captures
|
||||
$0.color = color
|
||||
Closure(__findWorldByColor__cl0_captures*)(World*)<Boolean> $1
|
||||
= closure(__findWorldByColor_cl0, $0)
|
||||
List<World> $2 = call std::list::ArrayList_impl_Find::find(worlds, $1)
|
||||
Closure()(World*)<String*> $3 = closure(__findWorldByColor_cl1)
|
||||
Option<String*> $4 = call std::list::ArrayList_impl_Monad($2, $3)
|
||||
|
||||
Display*[1] $5 = alloc Display[1]
|
||||
$5[0] = color
|
||||
DString *$6 = alloc DString
|
||||
call DString::ctor($6, { }, $5)
|
||||
String *$7 = call DString::toString($6)
|
||||
|
||||
String* $8 = call option::Option::expect($4, $7)
|
||||
|
||||
drop $0
|
||||
drop $1
|
||||
drop $2
|
||||
drop $3
|
||||
drop $5
|
||||
drop $6
|
||||
|
||||
ret $8
|
||||
end
|
||||
|
||||
const String %s7 = 'Blue'
|
||||
const String[2] %sa0 = { 'Hello, ', '!' }
|
||||
|
||||
void main()
|
||||
List<World> *$0 = call getWorlds()
|
||||
World *$1 = call findWorldByColor($0, %s7)
|
||||
Display[1] $2 = alloc Display[1]
|
||||
$2[0] = move $1
|
||||
std::string::DString *$3 = alloc std::string::DString
|
||||
call std::string::DString::ctor($3, %sa0, $2)
|
||||
String *$4 = call std::DString::toString($3)
|
||||
call std::core::println($4)
|
||||
end
|
||||
211
sketching/november_2025/json.dm
Normal file
211
sketching/november_2025/json.dm
Normal file
@ -0,0 +1,211 @@
|
||||
ns jsonexample
|
||||
|
||||
fn main()
|
||||
let o: obj = match parseJsonObj('{ "a": { "b": { "c": [42] }} }')
|
||||
Success(o) => o,
|
||||
Failure(err) => panic!("There was an error during Json parsing: ${err}")
|
||||
end
|
||||
assertEq!(o.a.b.c[0], 42)
|
||||
end
|
||||
|
||||
fn parseJsonObj(s: String) -> Result<obj, JsonParseError>
|
||||
let lexer = JsonLexer(s)
|
||||
let parser = JsonParser(lexer)
|
||||
try
|
||||
parser.object()
|
||||
catch (parseError: JsonParseError)
|
||||
Failure(parseError)
|
||||
end
|
||||
end
|
||||
|
||||
enum JsonToken
|
||||
LBrace,
|
||||
RBrace,
|
||||
LSquare,
|
||||
RSquare,
|
||||
Comma,
|
||||
String(String),
|
||||
Number(Number),
|
||||
Boolean(Boolean)
|
||||
end
|
||||
|
||||
platform class String
|
||||
pub fn <R> matchAt(startIndex: Int, matchers: Map<Regex, R | fn (match: String) -> R), opts: obj + { ignoreWhitespace: Boolean }) -> Option<R>
|
||||
let slice: String = if opts.ignoreWhitespace then
|
||||
/[\t\n\r ]*(<rest>.*)/.match(self[startIndex..]).rest as String
|
||||
else
|
||||
self[startIndex..]
|
||||
end
|
||||
for [regex, result] in matchers do
|
||||
if regex.test(slice) is Some(match) then
|
||||
if result is Fn then
|
||||
Some(result(match))
|
||||
else
|
||||
Some(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
None
|
||||
end
|
||||
end
|
||||
|
||||
platform class Regex
|
||||
|
||||
int RegexMatchBase
|
||||
val match: String
|
||||
val groups: [String],
|
||||
val namedGroups: Map<String, String>
|
||||
end
|
||||
|
||||
pub int RegexMatch
|
||||
props!::<RegexMatchBase>()
|
||||
end
|
||||
|
||||
platform fn doMatch(s) -> Option<RegexMatchBase>
|
||||
|
||||
pub fn match(s: String) -> Option<obj : RegexMatch>
|
||||
doMatch(s).map { base ->
|
||||
dyn obj {
|
||||
...base,
|
||||
propertyMissing: { name: String ->
|
||||
base.namedGroups.get(name).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class JsonLexer(val source: String)
|
||||
var index: Int
|
||||
|
||||
fn matchCurrent() -> Option<JsonToken>
|
||||
source.matchAt(index, [
|
||||
/\{/ => JsonToken::LBrace,
|
||||
/}/ => JsonToken::RBrace,
|
||||
/\[/ => JsonToken::LSquare,
|
||||
/]/ => JsonToken::RSquare,
|
||||
/[0-9]+/ => { rawNumber: String => JsonToken::Number(number::parse(rawNumber))) }
|
||||
/true/ => JsonToken::Boolean(true),
|
||||
/false/ => JsonToken::False(false),
|
||||
// The following is not a good way for matching strings but whatever
|
||||
/"%w*"/ => { stringWithQuotes => JsonToken::String(stringWithQuotes[1..-1]) }
|
||||
], obj { ignoreWhitespace: true })
|
||||
end
|
||||
|
||||
pub fn nextToken() -> Option<JsonToken>
|
||||
matchCurrent().ifSome { token ->
|
||||
match token
|
||||
JsonToken::LBrace | JsonToken::RBrace | JsonToken::LSquare | JsonToken::RSquare | JsonToken::Comma => index++,
|
||||
JsonToken::String(s) => index += s.len() + 2,
|
||||
JsonToken::Number(n) => index += n.toString().len(),
|
||||
JsonToken::Boolean(b) => match b
|
||||
true => index += 4,
|
||||
false => index += 5,
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
pub fn peek() -> Option<JsonToken> alias matchCurrent
|
||||
end
|
||||
|
||||
class JsonParser(val lexer: JsonLexer)
|
||||
|
||||
macro parseError!($message)
|
||||
throw JsonParserError($message)
|
||||
end
|
||||
|
||||
macro expect!($tokenType)
|
||||
match lexer.nextToken()
|
||||
Some(token) => match token
|
||||
JsonToken::$tokenType => do end,
|
||||
_ => parseError!("Expected ${$tokenType}")
|
||||
end,
|
||||
_ => parseError!("Expected ${$tokenType}")
|
||||
end
|
||||
end
|
||||
|
||||
macro peek!($tokenType)
|
||||
match lexer.peek()
|
||||
Some(token) => match token
|
||||
JsonToken::$tokenType => true,
|
||||
_ => false
|
||||
end,
|
||||
None => false
|
||||
end
|
||||
end
|
||||
|
||||
pub fn object() -> obj
|
||||
expect!(LBrace)
|
||||
let result = obj {}
|
||||
while keyValue() is Some((key, value)) do
|
||||
result[key] = value
|
||||
end
|
||||
expect!(RBrace)
|
||||
result
|
||||
end
|
||||
|
||||
fn keyValue() -> Option<(String, Any)>
|
||||
let key = match lexer.peek()
|
||||
Some(token) => do
|
||||
match token
|
||||
JsonToken::String(s) => s,
|
||||
_ => parseError!("Expected String")
|
||||
end
|
||||
end,
|
||||
_ => do return None end
|
||||
end
|
||||
let value = value()
|
||||
Some((key, value))
|
||||
end
|
||||
|
||||
fn value() -> Any
|
||||
match lexer.peek()
|
||||
Some(token) => match token
|
||||
JsonToken::LBrace => object(),
|
||||
JsonToken::LSquare => array(),
|
||||
_ => do
|
||||
match lexer.nextToken()
|
||||
JsonToken::String(s) => s,
|
||||
JsonToken::Number(n) => n,
|
||||
JsonToken::Boolean(b) => b,
|
||||
_ => parseError!("Expected String, Number, or Boolean")
|
||||
end
|
||||
end
|
||||
end,
|
||||
None => parseError!("Expected LBrace, LSquare, String, Number, or Boolean")
|
||||
end
|
||||
end
|
||||
|
||||
fn array() -> [Any]
|
||||
expect!(LSquare)
|
||||
let results: List<Any> = []
|
||||
while true do
|
||||
match lexer.peek()
|
||||
Some(token) => match token
|
||||
JsonToken::LBrace => results << object(),
|
||||
JsonToken::LSquare => results << array(),
|
||||
_ => do
|
||||
match lexer.nextToken()
|
||||
JsonToken::String(s) => results << s,
|
||||
JsonToken::Number(n) => results << n,
|
||||
JsonToken::Boolean(b) => results << b,
|
||||
_ => parseError!("Expected String, Number, or Boolean")
|
||||
end
|
||||
end
|
||||
end,
|
||||
None => panic! "Expected LBrace, LSquare, String, Number, or Boolean"
|
||||
end
|
||||
match lexer.nextToken()
|
||||
Some(token) => match token
|
||||
JsonToken::Comma => continue,
|
||||
JsonToken::RSquare => break
|
||||
end
|
||||
_ => panic! "Expected Comma or RSquare"
|
||||
end
|
||||
end
|
||||
expect!(RSquare)
|
||||
end
|
||||
|
||||
end
|
||||
128
sketching/november_2025/rail.dm
Normal file
128
sketching/november_2025/rail.dm
Normal file
@ -0,0 +1,128 @@
|
||||
ns nr::train
|
||||
|
||||
use object::{assign, Props, props!}
|
||||
|
||||
pub int Train
|
||||
name: String
|
||||
primaryColor: Color
|
||||
|
||||
fld destination: String
|
||||
fld volume: Int
|
||||
|
||||
fn blowHorn() -> IO
|
||||
end
|
||||
|
||||
pub enum Color
|
||||
White,
|
||||
Blue,
|
||||
Red
|
||||
end
|
||||
|
||||
pub type Printer = fn (message: String) -> IO
|
||||
|
||||
pub fn create(props: obj Props<Train> + { printer: Printer }) -> Train =
|
||||
SimpleTrain(props).with {
|
||||
volume = 80
|
||||
}
|
||||
|
||||
class SimpleTrain : Train
|
||||
props!::<Train>()
|
||||
printer: Printer
|
||||
|
||||
ctor(props: obj Props<Train> + { printer: Printer })
|
||||
assign(self, props)
|
||||
end
|
||||
|
||||
impl fn blowHorn() -> IO
|
||||
printer("Train ${name} is blowing its horn at volume ${volume}!")
|
||||
end
|
||||
end
|
||||
|
||||
pub fn createViaObj(props: obj Props<Train>, printer: Printer) -> obj Train
|
||||
obj {
|
||||
...props,
|
||||
printer,
|
||||
blowHorn: fn () -> IO
|
||||
printer("Dynamic object Train ${train} is blowing its horn at volume ${self.volume}!")
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
// props lib file
|
||||
ns std::core::object
|
||||
|
||||
pub fn assign(target: obj, source: obj)
|
||||
for [key, value] source.props() do
|
||||
target[key] = value
|
||||
end
|
||||
end
|
||||
|
||||
pub type Props<T> = obj {
|
||||
[K in keyof T]: T[K]
|
||||
}
|
||||
|
||||
pub macro fn <T> props!(_: TokenStream) -> TokenStream
|
||||
let result = TokenStream()
|
||||
for [key, type] in std::intrinsics::propsAndTypes::<T>() do
|
||||
result << quote! {
|
||||
#key: #type
|
||||
}
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
// io lib file
|
||||
ns std::core
|
||||
|
||||
pub enum IO<S : Stream = Stream, E = Error> : fn () -> IO<S, E>
|
||||
Success(stream: S)
|
||||
impl ()
|
||||
try
|
||||
doCall(stream)
|
||||
catch (e: E)
|
||||
Failure(e)
|
||||
end
|
||||
end
|
||||
end,
|
||||
Failure(_)
|
||||
impl ()
|
||||
self
|
||||
end
|
||||
end;
|
||||
|
||||
int
|
||||
fn <Z : Stream = S> doCall(stream: S) -> IO<Z, E>
|
||||
end
|
||||
end
|
||||
|
||||
// main example file
|
||||
ns nr::example
|
||||
|
||||
use nr::train::{create, createViaObj, Train, Color}
|
||||
|
||||
fn main()
|
||||
let train = create(obj {
|
||||
name: 'Test Train',
|
||||
primaryColor: Color::Blue,
|
||||
destination: 'Nonavosa Central',
|
||||
printer: println
|
||||
})
|
||||
train.blowHorn()
|
||||
train.volume = 100
|
||||
train.blowHorn()
|
||||
|
||||
let objTrain: obj Train = createViaObj(obj {
|
||||
name: 'Obj Train',
|
||||
primaryColor: Color::Red,
|
||||
destination: 'Durandana',
|
||||
printer: println
|
||||
})
|
||||
// Since it's an obj, we can dynamically add properties and override methods, similar to JS
|
||||
objTrain.secondaryColor = Color::White
|
||||
let originalBlowHorn = objTrain.blowHorn
|
||||
objTrain.blowHorn = fn ()
|
||||
originalBlowHorn()
|
||||
println "Hello again! The train's colors are ${self.primaryColor} and ${self.secondaryColor}!"
|
||||
end
|
||||
objTrain.blowHorn()
|
||||
end
|
||||
5
sketching/september_2025/adder/adder.dm
Normal file
5
sketching/september_2025/adder/adder.dm
Normal file
@ -0,0 +1,5 @@
|
||||
use math::add
|
||||
|
||||
fn main()
|
||||
println add(1, 2) // 3
|
||||
end
|
||||
3
sketching/september_2025/adder/math/add.dm
Normal file
3
sketching/september_2025/adder/math/add.dm
Normal file
@ -0,0 +1,3 @@
|
||||
mod math
|
||||
|
||||
pub fn add(a: Int, b: Int) = a + b
|
||||
81
sketching/september_2025/dll_rc/dll_rc.dm
Normal file
81
sketching/september_2025/dll_rc/dll_rc.dm
Normal file
@ -0,0 +1,81 @@
|
||||
pub class Rc<T> : Drop, Copy
|
||||
|
||||
class Inner<T>(t: T)
|
||||
mut count = 1
|
||||
end
|
||||
|
||||
inner: &weak Inner = Null
|
||||
|
||||
pub ctor(t: T)
|
||||
inner = Inner(t)
|
||||
end
|
||||
|
||||
ctor(parent: &Self)
|
||||
parent.inner++
|
||||
inner = &weak parent.inner
|
||||
end
|
||||
|
||||
pub fn copy() -> Self
|
||||
Self(self)
|
||||
end
|
||||
|
||||
impl fn drop(self)
|
||||
if inner.upgrade() is Some(inner) then
|
||||
inner.count--
|
||||
if inner.count == 0 then
|
||||
drop inner
|
||||
end
|
||||
else
|
||||
throw Exception('rc.inner could not be converted from a weak reference')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Game(player: Player) end
|
||||
|
||||
class Player
|
||||
pub mut game: &weak Game = Null
|
||||
end
|
||||
|
||||
fn circular()
|
||||
let player = Player()
|
||||
let game = Game(player)
|
||||
|
||||
expectThrows(EmptyWeakReferenceException) {
|
||||
player.game.upgrade()
|
||||
}
|
||||
|
||||
player.game = &weak game
|
||||
|
||||
expectDoesNotThrow {
|
||||
player.game.upgrade()
|
||||
}
|
||||
end
|
||||
|
||||
class Board
|
||||
pub fn flip()
|
||||
println 'Flipped!'
|
||||
end
|
||||
end
|
||||
|
||||
class ChessGame(pub board: rc Board) end
|
||||
|
||||
class ChessPlayer(pub board: rc Board) end
|
||||
|
||||
fn getGameAndPlayer() -> (ChessGame, ChessPlayer)
|
||||
let board = rc Board() // Rc<Board> count is 1
|
||||
let game = ChessGame(board) // Rc<Board> copied, count is 2
|
||||
let player = ChessPlayer(board) // Rc<Board> copied, count is 3
|
||||
(game, player) // initial Rc<Board> dropped before fn return, count now 2
|
||||
end
|
||||
|
||||
fn playChess()
|
||||
let (game, player) = getGameAndPlayer()
|
||||
// Rc<Board> count is 2
|
||||
let board = game.board // Copy, so count is now 3
|
||||
board.flip()
|
||||
drop board // count now 2
|
||||
drop game // count now 1
|
||||
drop player // count now 0, board memory finally dropped
|
||||
end
|
||||
236
sketching/september_2025/hkt/functor.dm
Normal file
236
sketching/september_2025/hkt/functor.dm
Normal file
@ -0,0 +1,236 @@
|
||||
pub trait Functor[*T]
|
||||
fn <U> map(ftu: fn (t: &*T) -> U) -> Self<U>
|
||||
end
|
||||
|
||||
pub trait Functor[*L -> *R]
|
||||
fn mapLeft(flr: fn (l: &*L) -> *R) -> Self<*L, *R>
|
||||
end
|
||||
|
||||
pub enum Option<T>
|
||||
Some(T),
|
||||
None
|
||||
end
|
||||
|
||||
pub enum Either<L, R>
|
||||
Left(L),
|
||||
Right(R)
|
||||
end
|
||||
|
||||
pub impl Functor[*T] for Option<T>
|
||||
fn <U> map(ftu: fn (t: &*T) -> U) -> Self<U>
|
||||
if self is Some(t) then
|
||||
Some(ftu(t))
|
||||
else
|
||||
None
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pub impl Functor[*R] for Either<_, *R>
|
||||
fn <U> map(f: fn (t: &*R) -> U) -> Self<_, U>
|
||||
if self is Right(t) then
|
||||
Right(f(t))
|
||||
else
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pub impl Functor[*L -> *R] for Either<L, R>
|
||||
fn mapLeft(f: fn (l: &*L) -> *R) -> Self<*L, *R>
|
||||
if self is Left(l) then
|
||||
Right(f(l))
|
||||
else
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pub trait Monad[*T]
|
||||
static fn lift(t: *T) -> Self<*T>
|
||||
fn <U> flatMap(f: fn (t: &*T) -> Self<U>) -> Self<U>
|
||||
end
|
||||
|
||||
pub trait Default
|
||||
static fn default() -> Self
|
||||
end
|
||||
|
||||
pub trait Empty[*T]
|
||||
static fn empty() -> Self<*T>
|
||||
end
|
||||
|
||||
pub trait Semigroup[*T]
|
||||
type Other<*T>
|
||||
|
||||
fn concat(other: &Other<*T>) -> Self<&*T>
|
||||
cons fn consConcat(other: Other<*T>) -> Self<*T>
|
||||
end
|
||||
|
||||
pub int Iterable<T>
|
||||
fn iter() -> Iterator<T> ref self
|
||||
cons fn consIter() -> ConsIterator<T>
|
||||
end
|
||||
|
||||
pub int Iterator<T>
|
||||
fn next() -> Option<&T ref self>
|
||||
end
|
||||
|
||||
pub int ConsIterator<T>
|
||||
fn next() -> Option<T>
|
||||
end
|
||||
|
||||
pub impl Semigroup[*T] for Iterable<*T>
|
||||
type Other = Iterable<*T>
|
||||
|
||||
fn concat(other: &Other<*T>) -> Self<*T>
|
||||
let result: Self<*T> = ArrayList(count())
|
||||
for t in self do
|
||||
result.add(t)
|
||||
end
|
||||
for t in other do
|
||||
result.add(t)
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
cons fn consConcat(other: Other<*T>) -> Self<*T>
|
||||
let result: Self<*T> = ArrayList(count())
|
||||
for t in self do
|
||||
result.add(t)
|
||||
end
|
||||
for t in other do
|
||||
result.add(t)
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
pub int List<T> : Iterable<T>
|
||||
fn add(t: T) -> Void
|
||||
end
|
||||
|
||||
pub impl Functor[*T] for List<*T>
|
||||
fn <U> map(f: (t: &*T) -> U) -> Self<U>
|
||||
let mut result: Self<U> = ArrayList(count())
|
||||
for t in self do
|
||||
result.add(f(t))
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
pub impl Empty[*T] for List<*T>
|
||||
static fn empty() -> List<*T> = ArrayList()
|
||||
end
|
||||
|
||||
pub inst Monad[*T] for List<*T>
|
||||
static fn lift(t: *T) -> List<*T>
|
||||
let result: List<*T> = ArrayList()
|
||||
result.add(t)
|
||||
result
|
||||
end
|
||||
|
||||
fn <U> flatMap(f: fn (t: &*T) -> List<U>) -> List<U>
|
||||
let result: List<U> = ArrayList(count())
|
||||
for t in self do
|
||||
for u in f(t).consIter() do
|
||||
result.add(u)
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
pub class ArrayList<T> : List<T>
|
||||
|
||||
let capacity: USize
|
||||
let mut ts: Array<Option<T>>
|
||||
let count: USize = 0
|
||||
|
||||
pub ctor(startCapacity: USize = 10)
|
||||
capacity = startCapacity
|
||||
ts = array(startCapacity, None)
|
||||
end
|
||||
|
||||
class ArrayListIterator<T>(owner: &ArrayList<T>) /* ref owner (implied by constructor) */ : Iterator<T>
|
||||
let i: USize = 0
|
||||
|
||||
impl fn next() -> Option<&T ref self>
|
||||
if owner.ts[i] is Some(t) then
|
||||
i++
|
||||
Some(t)
|
||||
else
|
||||
None
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ConsArrayListIterator<T>(ts: Array<Option<T>>) : ConsIterator<T>
|
||||
let i: USize = 0
|
||||
|
||||
impl fn next() -> Option<T>
|
||||
if ts[i] is Some then
|
||||
let wrappedT = mem::take(ts[i], None)
|
||||
i++
|
||||
wrappedT
|
||||
else
|
||||
None
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
impl fn iter() -> Iterator<&T> ref self
|
||||
ArrayListIterator(&self)
|
||||
end
|
||||
|
||||
impl cons fn consIter() -> Iterator<T>
|
||||
ConsArrayListIterator(mem::take(ts, arrays::empty()))
|
||||
end
|
||||
|
||||
impl fn add(t: T) -> Void
|
||||
ts[count] = Some(t)
|
||||
count++
|
||||
if count == capacity then
|
||||
resize()
|
||||
end
|
||||
end
|
||||
|
||||
fn resize() -> Void
|
||||
let oldTs = mem::take(&ts, array(capacity * 2, None))
|
||||
for (i, wrappedT) in oldTs.consIter() do
|
||||
ts[i] = wrappedT
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
pub mod std::core
|
||||
|
||||
pub mod mem
|
||||
pub native fn <T> take(target: &T, replacement: T) -> T
|
||||
end
|
||||
|
||||
pub native fn <T> array(capacity: USize, init: fn () -> T) -> Array<T>
|
||||
|
||||
pub native fn <T> array(capacity: USize) -> Array<T> where T has Default
|
||||
|
||||
pub fn <T> list(...items: Array<T>) -> List<T>
|
||||
let result = ArrayList(items.capacity())
|
||||
for item in items.consIter() do
|
||||
result.add(item)
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
// Other file
|
||||
use std::hkt::{Functor[List], Monad[List]}
|
||||
|
||||
fn main()
|
||||
let someNums: List<Int> = [1, 2, 3, 4, 5]
|
||||
let plusOnes = someNums.map { it + 1 }
|
||||
println plusOnes // 2, 3, 4, 5, 6
|
||||
let twoOfEach = someNums.flatMap { [it, it] }
|
||||
println twoOfEach // 1, 1, 2, 2, 3, 3, 4, 4, 5, 5
|
||||
end
|
||||
198
sketching/september_2025/map/Map.dm
Normal file
198
sketching/september_2025/map/Map.dm
Normal file
@ -0,0 +1,198 @@
|
||||
pub trait Functor<T>[Self<T>]
|
||||
fn map(mapper: fn (t: &T) -> U) -> Self<U>
|
||||
end
|
||||
|
||||
pub trait Sum<T>
|
||||
fn sum() -> T
|
||||
end
|
||||
|
||||
pub trait Default
|
||||
fn default() -> Self
|
||||
end
|
||||
|
||||
pub trait Iter<T>
|
||||
ref fn iter() -> Iterator<&T>
|
||||
end
|
||||
|
||||
pub trait ConsIter<T>
|
||||
cons fn consIter() -> Iterator<T>
|
||||
end
|
||||
|
||||
pub int Iterator<T>
|
||||
fn next() -> Option<T>
|
||||
end
|
||||
|
||||
pub int List<T> impl Functor<T>, Iter<T>, ConsIter<T>
|
||||
fn add(t: T) -> Void
|
||||
end
|
||||
|
||||
pub class ArrayList<T> : List<T>
|
||||
let mut currentIndex = 0
|
||||
let mut inner: Array<T>
|
||||
|
||||
ctor(capacity: Int) where T impl Default
|
||||
inner = Array(capacity, Default[T])
|
||||
end
|
||||
|
||||
ctor(capacity: Int, init: fn () -> T)
|
||||
inner = Array(capacity, init)
|
||||
end
|
||||
|
||||
impl fn add(t: T) -> Void
|
||||
inner[currentIndex] = t
|
||||
let currentCapacity = inner.capacity()
|
||||
currentIndex++
|
||||
if currentIndex == currentCapacity then
|
||||
let oldInner = std::mem::take(inner, Array(currentCapacity * 2))
|
||||
arrays::copy(oldInner, inner)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
impl Functor[ArrayList<*>]
|
||||
fn map(mapper: fn (t: &T) -> U) -> Self<U>
|
||||
let mut result: Array<U> = Array(inner.capacity)
|
||||
for (i, t) in inner.iterWithIndex() do
|
||||
result[i] = mapper(t)
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
impl Sum[Self = List<Int>, T = Int]
|
||||
fn sum() -> Int
|
||||
let mut result = 0
|
||||
for value in self do
|
||||
result += value
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
impl<T impl Default> Default[ArrayList<T>]
|
||||
const DEFAULT_SIZE = 10
|
||||
|
||||
static fn default() -> Self = ArrayList(DEFAULT_SIZE, Default[T]::default)
|
||||
end
|
||||
|
||||
impl<T> Iter[ArrayList<T>]
|
||||
ref fn iter() -> Iterator<&T>
|
||||
let mut currentIndex = 0
|
||||
return {
|
||||
if currentIndex < inner.count() then
|
||||
let item = inner[currentIndex]
|
||||
currentIndex++
|
||||
Some(item)
|
||||
else
|
||||
None
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
impl<T impl Default> ConsIter[ArrayList<T>]
|
||||
cons fn consIter() -> Iterator<T>
|
||||
let mut currentIndex = 0
|
||||
let inner = std::mem::take(inner, std::arrays::empty())
|
||||
return |inner| {
|
||||
if currentIndex < inner.count() then
|
||||
let item = inner.take(currentIndex, Default[T]::default)
|
||||
currentIndex++
|
||||
Some(item)
|
||||
else
|
||||
None
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
pub enum Option<T>
|
||||
Some(t: T),
|
||||
None
|
||||
end
|
||||
|
||||
impl Default[Option<*>]
|
||||
static fn default() -> Self = None
|
||||
end
|
||||
|
||||
pub trait HashCode
|
||||
fn hash() -> USize
|
||||
end
|
||||
|
||||
pub int Map<K, V>
|
||||
fn get(key: K) -> Option<&V>
|
||||
fn put(key: K, value: V) -> Void
|
||||
fn take(key: K) -> Option<V>
|
||||
end
|
||||
|
||||
pub class HashMap<K, V> : Map<K, V>
|
||||
where K impl HashCode
|
||||
|
||||
const LOAD_FACTOR = 0.8
|
||||
|
||||
class Entry<K, V>(pub key: K, pub value: V) end
|
||||
|
||||
let mut buckets: List<List<Option<Entry<K, V>>>> = ArrayList(10)
|
||||
|
||||
fn getBucketIndex(key: K) -> Int
|
||||
HashCode[K].hash(key) % buckets.size()
|
||||
end
|
||||
|
||||
fn getCount() -> Int = buckets.map { it.count() }.sum::<Int>()
|
||||
|
||||
fn getCapacity() -> Int = buckets.map { it.capacity() }.sum::<Int>()
|
||||
|
||||
fn redistribute()
|
||||
use std::mem::take
|
||||
let oldBucketCapacity = buckets.first().unwrap().capacity()
|
||||
let oldBuckets = take(buckets, ArrayList(oldBucketCapacity * 2))
|
||||
for bucket in oldBuckets.consIter() do
|
||||
for entry in bucket.consIter() do
|
||||
let newIndex = getBucketIndex(entry.key)
|
||||
let newBucket = buckets[newIndex]
|
||||
newBucket.add(Entry(entry.key, entry.value))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
impl fn put(key: K, value: V) -> Void
|
||||
let bucket = buckets[getBucketIndex(key)]
|
||||
bucket.add(Entry(key, value))
|
||||
if getCount() / getCapacity() > LOAD_FACTOR then
|
||||
redistribute()
|
||||
end
|
||||
end
|
||||
|
||||
impl fn get(key: K) -> Option<&V>
|
||||
let bucket = buckets[getBucketIndex(key)] // bucket: &List<Entry<K, V>>
|
||||
bucket.find { it.key == key } // it: &Entry<K, V>
|
||||
.map { it.value } // it.value: &V
|
||||
end
|
||||
|
||||
impl fn take(key: K) -> Option<V>
|
||||
let mut bucket = buckets.take(getBucketIndex(key)) // bucket: List<Entry<K, V>>
|
||||
if bucket.findIndex { it.key == key } is Some(index) then
|
||||
let entry = bucket.take(index) // entry: Entry<K, V>
|
||||
Some(entry.value) // moved out of Entry
|
||||
else
|
||||
None
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
impl HashCode for String
|
||||
fn hash() -> Int = todo()
|
||||
end
|
||||
|
||||
class Greeter(pub greeting: String) end
|
||||
|
||||
fn main()
|
||||
let greeterMap: Map<String, Greeter> = HashMap()
|
||||
greeterMap.put("friendly", Greeter("Friendly hello!"))
|
||||
greeterMap.put("nasty", Greeter("Nasty hello!"))
|
||||
let friendlyGreeter: &Greeter = greeterMap.get("friendly").unwrap()
|
||||
println friendlyGreeter.greeting
|
||||
let nastyGreeter: Greeter = greeterMap.take("nasty").unwrap()
|
||||
println nastyGreeter.greeting
|
||||
end
|
||||
244
src/asm/assemble_ir.rs
Normal file
244
src/asm/assemble_ir.rs
Normal file
@ -0,0 +1,244 @@
|
||||
use crate::asm::{
|
||||
AsmBinaryOperator, AsmFunction, AsmInstruction, AsmOperand, ControlUnit, ControlUnitId,
|
||||
VirtualRegisterId,
|
||||
};
|
||||
use crate::ir::{
|
||||
IrAssign, IrBinaryOperation, IrBinaryOperator, IrCall, IrCallType, IrExpression, IrFunction,
|
||||
IrLiteral, IrReturn, IrStatement,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
struct AssemblyContext {
|
||||
control_unit_counter: ControlUnitId,
|
||||
control_units: Vec<ControlUnit>,
|
||||
virtual_register_counter: VirtualRegisterId,
|
||||
virtual_registers: HashMap<Rc<str>, VirtualRegisterId>,
|
||||
}
|
||||
|
||||
impl AssemblyContext {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
control_unit_counter: 0,
|
||||
control_units: Vec::new(),
|
||||
virtual_register_counter: 0,
|
||||
virtual_registers: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn next_control_unit_id(&mut self) -> ControlUnitId {
|
||||
let id = self.control_unit_counter;
|
||||
self.control_unit_counter += 1;
|
||||
id
|
||||
}
|
||||
|
||||
pub fn new_control_unit(&mut self) -> ControlUnitId {
|
||||
let new_id = self.next_control_unit_id();
|
||||
self.control_units.push(ControlUnit::new(new_id));
|
||||
new_id
|
||||
}
|
||||
|
||||
pub fn current_control_unit(&mut self) -> &ControlUnit {
|
||||
if self.control_units.is_empty() {
|
||||
self.new_control_unit();
|
||||
}
|
||||
self.control_units.last().unwrap()
|
||||
}
|
||||
|
||||
pub fn current_control_unit_mut(&mut self) -> &mut ControlUnit {
|
||||
if self.control_units.is_empty() {
|
||||
self.new_control_unit();
|
||||
}
|
||||
self.control_units.last_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn into_control_units(self) -> Vec<ControlUnit> {
|
||||
self.control_units
|
||||
}
|
||||
|
||||
pub fn get_virtual_register(&self, name: &str) -> VirtualRegisterId {
|
||||
*self
|
||||
.virtual_registers
|
||||
.get(name)
|
||||
.expect(&format!("Did not set a virtual register for name {}", name))
|
||||
}
|
||||
|
||||
pub fn next_virtual_register(&mut self, name: &str) -> VirtualRegisterId {
|
||||
if self.virtual_registers.contains_key(name) {
|
||||
panic!(
|
||||
"Should not already have a virtual register with name {}",
|
||||
name
|
||||
);
|
||||
}
|
||||
let virtual_register_id = self.virtual_register_counter;
|
||||
self.virtual_register_counter += 1;
|
||||
self.virtual_registers
|
||||
.insert(name.into(), virtual_register_id);
|
||||
virtual_register_id
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assemble_ir_function(ir_function: &IrFunction) -> AsmFunction {
|
||||
let mut context = AssemblyContext::new();
|
||||
|
||||
// body
|
||||
for statement in ir_function.statements() {
|
||||
assemble_ir_statement(statement, &mut context);
|
||||
}
|
||||
|
||||
AsmFunction::new(ir_function.fqn().into(), context.into_control_units())
|
||||
}
|
||||
|
||||
fn assemble_ir_statement(ir_statement: &IrStatement, context: &mut AssemblyContext) {
|
||||
match ir_statement {
|
||||
IrStatement::Allocate(_) => {
|
||||
todo!()
|
||||
}
|
||||
IrStatement::Call(ir_call) => {
|
||||
assemble_ir_call(ir_call, context);
|
||||
}
|
||||
IrStatement::Return(ir_return) => {
|
||||
assemble_ir_return(ir_return, context);
|
||||
}
|
||||
IrStatement::Assign(ir_assign) => {
|
||||
assemble_ir_assign(ir_assign, context);
|
||||
}
|
||||
IrStatement::MakeClosure(_) => {
|
||||
todo!()
|
||||
}
|
||||
IrStatement::BinaryOperation(ir_binary_operation) => {
|
||||
assemble_ir_binary_operation(ir_binary_operation, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn assemble_ir_assign(ir_assign: &IrAssign, context: &mut AssemblyContext) {
|
||||
let source_operand = match ir_assign.rhs() {
|
||||
IrExpression::Variable(ir_variable) => {
|
||||
let virtual_register_id = context.get_virtual_register(ir_variable.name());
|
||||
AsmOperand::VirtualRegister(virtual_register_id)
|
||||
}
|
||||
IrExpression::Literal(ir_literal) => ir_literal_to_asm_operand(ir_literal),
|
||||
};
|
||||
let destination = context.next_virtual_register(ir_assign.lhs().name());
|
||||
context
|
||||
.current_control_unit_mut()
|
||||
.append_instruction(AsmInstruction::Move {
|
||||
source: source_operand,
|
||||
destination,
|
||||
})
|
||||
}
|
||||
|
||||
fn ir_literal_to_asm_operand(ir_literal: &IrLiteral) -> AsmOperand {
|
||||
match ir_literal {
|
||||
IrLiteral::I8(i) => AsmOperand::I8(*i),
|
||||
IrLiteral::I16(i) => AsmOperand::I16(*i),
|
||||
IrLiteral::I32(i) => AsmOperand::I32(*i),
|
||||
IrLiteral::I64(i) => AsmOperand::I64(*i),
|
||||
IrLiteral::I128(i) => AsmOperand::I128(*i),
|
||||
IrLiteral::ISize(i) => AsmOperand::ISize(*i),
|
||||
IrLiteral::U8(u) => AsmOperand::U8(*u),
|
||||
IrLiteral::U16(u) => AsmOperand::U16(*u),
|
||||
IrLiteral::U32(u) => AsmOperand::U32(*u),
|
||||
IrLiteral::U64(u) => AsmOperand::U64(*u),
|
||||
IrLiteral::U128(u) => AsmOperand::U128(*u),
|
||||
IrLiteral::USize(u) => AsmOperand::USize(*u),
|
||||
IrLiteral::Boolean(b) => AsmOperand::Boolean(*b),
|
||||
IrLiteral::Float(f) => AsmOperand::Float(*f),
|
||||
IrLiteral::Double(d) => AsmOperand::Double(*d),
|
||||
IrLiteral::String(s) => AsmOperand::String(s.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn assemble_ir_call(ir_call: &IrCall, context: &mut AssemblyContext) {
|
||||
// push args
|
||||
for argument in ir_call.arguments() {
|
||||
match argument {
|
||||
IrExpression::Variable(ir_variable) => {
|
||||
let variable_register_id = context.get_virtual_register(ir_variable.name());
|
||||
context
|
||||
.current_control_unit_mut()
|
||||
.append_instruction(AsmInstruction::Push {
|
||||
source: AsmOperand::VirtualRegister(variable_register_id),
|
||||
})
|
||||
}
|
||||
IrExpression::Literal(ir_literal) => context
|
||||
.current_control_unit_mut()
|
||||
.append_instruction(AsmInstruction::Push {
|
||||
source: ir_literal_to_asm_operand(ir_literal),
|
||||
}),
|
||||
}
|
||||
}
|
||||
// issue call
|
||||
match ir_call.call_type() {
|
||||
IrCallType::Static => {
|
||||
context
|
||||
.current_control_unit_mut()
|
||||
.append_instruction(AsmInstruction::InvokeStatic {
|
||||
fqn: ir_call.function_name_owned(),
|
||||
});
|
||||
}
|
||||
IrCallType::Object => {
|
||||
context
|
||||
.current_control_unit_mut()
|
||||
.append_instruction(AsmInstruction::InvokeObject {
|
||||
fqn: ir_call.function_name_owned(),
|
||||
})
|
||||
}
|
||||
IrCallType::PlatformStatic => {}
|
||||
IrCallType::PlatformObject => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn assemble_ir_return(ir_return: &IrReturn, context: &mut AssemblyContext) {
|
||||
let operand = ir_return.expression().map(|expression| {
|
||||
match expression {
|
||||
IrExpression::Variable(ir_variable) => {
|
||||
let variable_virtual_register_id = context.get_virtual_register(ir_variable.name());
|
||||
AsmOperand::VirtualRegister(variable_virtual_register_id)
|
||||
}
|
||||
IrExpression::Literal(ir_literal) => ir_literal_to_asm_operand(ir_literal),
|
||||
}
|
||||
});
|
||||
context
|
||||
.current_control_unit_mut()
|
||||
.append_instruction(AsmInstruction::Return { operand });
|
||||
}
|
||||
|
||||
fn assemble_ir_binary_operation(
|
||||
ir_binary_operation: &IrBinaryOperation,
|
||||
context: &mut AssemblyContext,
|
||||
) {
|
||||
let left = match ir_binary_operation.left() {
|
||||
IrExpression::Variable(ir_variable) => {
|
||||
AsmOperand::VirtualRegister(context.get_virtual_register(ir_variable.name()))
|
||||
}
|
||||
IrExpression::Literal(ir_literal) => ir_literal_to_asm_operand(ir_literal),
|
||||
};
|
||||
let right = match ir_binary_operation.right() {
|
||||
IrExpression::Variable(ir_variable) => {
|
||||
AsmOperand::VirtualRegister(context.get_virtual_register(ir_variable.name()))
|
||||
}
|
||||
IrExpression::Literal(ir_literal) => ir_literal_to_asm_operand(ir_literal),
|
||||
};
|
||||
let destination = context.next_virtual_register(ir_binary_operation.destination().name());
|
||||
let operator = match ir_binary_operation.operator() {
|
||||
IrBinaryOperator::Add => AsmBinaryOperator::Add,
|
||||
IrBinaryOperator::Subtract => AsmBinaryOperator::Subtract,
|
||||
IrBinaryOperator::Multiply => AsmBinaryOperator::Multiply,
|
||||
IrBinaryOperator::Divide => AsmBinaryOperator::Divide,
|
||||
IrBinaryOperator::Exponent => {
|
||||
todo!()
|
||||
}
|
||||
};
|
||||
|
||||
let instruction = AsmInstruction::BinaryOperation {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
operator,
|
||||
};
|
||||
context
|
||||
.current_control_unit_mut()
|
||||
.append_instruction(instruction);
|
||||
}
|
||||
256
src/asm/mod.rs
Normal file
256
src/asm/mod.rs
Normal file
@ -0,0 +1,256 @@
|
||||
pub mod assemble_ir;
|
||||
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub type ControlUnitId = usize;
|
||||
|
||||
pub type VirtualRegisterId = u32;
|
||||
|
||||
pub struct AsmFunction {
|
||||
fqn: Rc<str>,
|
||||
control_units: Vec<ControlUnit>,
|
||||
}
|
||||
|
||||
impl AsmFunction {
|
||||
pub fn new(fqn: Rc<str>, control_units: Vec<ControlUnit>) -> Self {
|
||||
Self { fqn, control_units }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AsmFunction {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "fn {}", self.fqn)?;
|
||||
for control_unit in &self.control_units {
|
||||
writeln!(f, " #{}:", control_unit.id)?;
|
||||
for instruction in &control_unit.instructions {
|
||||
writeln!(f, " {}", instruction)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ControlUnit {
|
||||
id: ControlUnitId,
|
||||
successor_ids: Vec<ControlUnitId>,
|
||||
instructions: Vec<AsmInstruction>,
|
||||
}
|
||||
|
||||
impl ControlUnit {
|
||||
pub fn new(id: ControlUnitId) -> Self {
|
||||
Self {
|
||||
id,
|
||||
successor_ids: vec![],
|
||||
instructions: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append_instruction(&mut self, instruction: AsmInstruction) {
|
||||
self.instructions.push(instruction);
|
||||
}
|
||||
|
||||
pub fn append_instructions(&mut self, instructions: Vec<AsmInstruction>) {
|
||||
self.instructions.extend(instructions);
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AsmInstruction {
|
||||
Goto {
|
||||
control_unit_id: ControlUnitId,
|
||||
},
|
||||
Move {
|
||||
source: AsmOperand,
|
||||
destination: VirtualRegisterId,
|
||||
},
|
||||
Push {
|
||||
source: AsmOperand,
|
||||
},
|
||||
Pop {
|
||||
destination: VirtualRegisterId,
|
||||
},
|
||||
Allocate {
|
||||
class_name: Rc<str>,
|
||||
destination: VirtualRegisterId,
|
||||
},
|
||||
InvokeStatic {
|
||||
fqn: Rc<str>,
|
||||
},
|
||||
InvokeObject {
|
||||
fqn: Rc<str>,
|
||||
},
|
||||
InvokePlatformStatic {
|
||||
fqn: Rc<str>,
|
||||
},
|
||||
InvokePlatformObject {
|
||||
fqn: Rc<str>,
|
||||
},
|
||||
Return {
|
||||
operand: Option<AsmOperand>,
|
||||
},
|
||||
BinaryOperation {
|
||||
destination: VirtualRegisterId,
|
||||
left: AsmOperand,
|
||||
right: AsmOperand,
|
||||
operator: AsmBinaryOperator,
|
||||
},
|
||||
}
|
||||
|
||||
impl Display for AsmInstruction {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
AsmInstruction::Goto { control_unit_id } => {
|
||||
write!(f, "goto #{}", control_unit_id)
|
||||
}
|
||||
AsmInstruction::Move {
|
||||
source,
|
||||
destination,
|
||||
} => {
|
||||
write!(f, "move vr{}, {}", destination, source)
|
||||
}
|
||||
AsmInstruction::Push { source } => {
|
||||
write!(f, "push {}", source)
|
||||
}
|
||||
AsmInstruction::Pop { destination } => {
|
||||
write!(f, "pop vr{}", destination)
|
||||
}
|
||||
AsmInstruction::Allocate {
|
||||
class_name,
|
||||
destination,
|
||||
} => {
|
||||
write!(f, "allocate r{}, {}", destination, class_name)
|
||||
}
|
||||
AsmInstruction::InvokeStatic { fqn } => {
|
||||
write!(f, "invoke_static {}", fqn)
|
||||
}
|
||||
AsmInstruction::InvokeObject { fqn } => {
|
||||
write!(f, "invoke_object {}", fqn)
|
||||
}
|
||||
AsmInstruction::InvokePlatformStatic { fqn } => {
|
||||
write!(f, "invoke_platform_static {}", fqn)
|
||||
}
|
||||
AsmInstruction::InvokePlatformObject { fqn } => {
|
||||
write!(f, "invoke_platform_object {}", fqn)
|
||||
}
|
||||
AsmInstruction::Return { operand } => {
|
||||
write!(f, "return")?;
|
||||
if let Some(operand) = operand {
|
||||
write!(f, " {}", operand)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
AsmInstruction::BinaryOperation {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
operator,
|
||||
} => {
|
||||
write!(f, "op{} vr{}, {}, {}", operator, destination, left, right)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AsmOperand {
|
||||
I8(i8),
|
||||
I16(i16),
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
I128(i128),
|
||||
ISize(isize),
|
||||
U8(u8),
|
||||
U16(u16),
|
||||
U32(u32),
|
||||
U64(u64),
|
||||
U128(u128),
|
||||
USize(usize),
|
||||
Float(f32),
|
||||
Double(f64),
|
||||
Boolean(bool),
|
||||
String(Rc<str>),
|
||||
VirtualRegister(VirtualRegisterId),
|
||||
}
|
||||
|
||||
impl Display for AsmOperand {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
AsmOperand::I8(i) => {
|
||||
write!(f, "{}i8", i)
|
||||
}
|
||||
AsmOperand::I16(i) => {
|
||||
write!(f, "{}i16", i)
|
||||
}
|
||||
AsmOperand::I32(i) => {
|
||||
write!(f, "{}i32", i)
|
||||
}
|
||||
AsmOperand::I64(i) => {
|
||||
write!(f, "{}i64", i)
|
||||
}
|
||||
AsmOperand::I128(i) => {
|
||||
write!(f, "{}i128", i)
|
||||
}
|
||||
AsmOperand::ISize(i) => {
|
||||
write!(f, "{}isize", i)
|
||||
}
|
||||
AsmOperand::U8(u) => {
|
||||
write!(f, "{}u8", u)
|
||||
}
|
||||
AsmOperand::U16(u) => {
|
||||
write!(f, "{}u16", u)
|
||||
}
|
||||
AsmOperand::U32(u) => {
|
||||
write!(f, "{}u32", u)
|
||||
}
|
||||
AsmOperand::U64(u) => {
|
||||
write!(f, "{}u64", u)
|
||||
}
|
||||
AsmOperand::U128(u) => {
|
||||
write!(f, "{}u128", u)
|
||||
}
|
||||
AsmOperand::USize(u) => {
|
||||
write!(f, "{}usize", u)
|
||||
}
|
||||
AsmOperand::Float(float) => {
|
||||
write!(f, "{}f32", float)
|
||||
}
|
||||
AsmOperand::Double(d) => {
|
||||
write!(f, "{}f64", d)
|
||||
}
|
||||
AsmOperand::Boolean(b) => {
|
||||
write!(f, "{}", b)
|
||||
}
|
||||
AsmOperand::String(s) => {
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
AsmOperand::VirtualRegister(id) => {
|
||||
write!(f, "vr{}", id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AsmBinaryOperator {
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
}
|
||||
|
||||
impl Display for AsmBinaryOperator {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
AsmBinaryOperator::Add => {
|
||||
write!(f, "+")
|
||||
}
|
||||
AsmBinaryOperator::Subtract => {
|
||||
write!(f, "-")
|
||||
}
|
||||
AsmBinaryOperator::Multiply => {
|
||||
write!(f, "*")
|
||||
}
|
||||
AsmBinaryOperator::Divide => {
|
||||
write!(f, "/")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
339
src/ast/mod.rs
339
src/ast/mod.rs
@ -1,6 +1,333 @@
|
||||
pub mod build;
|
||||
pub mod children;
|
||||
pub mod node;
|
||||
pub mod pretty_print;
|
||||
pub mod unparse;
|
||||
pub mod walk;
|
||||
pub mod node {
|
||||
include!(concat!(env!("OUT_DIR"), "/src/ast/node.rs"));
|
||||
|
||||
impl OperatorInner {
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
OperatorInner::Or => "op_or",
|
||||
OperatorInner::And => "op_and",
|
||||
OperatorInner::EqualTo => "op_eq",
|
||||
OperatorInner::NotEqualTo => "op_neq",
|
||||
OperatorInner::Greater => "op_gt",
|
||||
OperatorInner::Less => "op_lt",
|
||||
OperatorInner::GreaterEqual => "op_ge",
|
||||
OperatorInner::LessEqual => "op_le",
|
||||
OperatorInner::Add => "op_add",
|
||||
OperatorInner::Subtract => "op_sub",
|
||||
OperatorInner::Multiply => "op_mul",
|
||||
OperatorInner::Divide => "op_div",
|
||||
OperatorInner::Modulo => "op_mod",
|
||||
OperatorInner::LeftShift => "op_ls",
|
||||
OperatorInner::RightShift => "op_rs",
|
||||
OperatorInner::Spread => "op_spread",
|
||||
OperatorInner::Star => "op_star",
|
||||
OperatorInner::Not => "op_not",
|
||||
OperatorInner::Negative => "op_neg",
|
||||
OperatorInner::PlusPlus => "op_pp",
|
||||
OperatorInner::MinusMinus => "op_mm",
|
||||
OperatorInner::CallOp => "op_call",
|
||||
OperatorInner::Index => "op_index",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
pub fn analyzed_kind(&self) -> Kind {
|
||||
match self {
|
||||
Expression::Ternary(ternary_expression) => {
|
||||
todo!()
|
||||
}
|
||||
Expression::Or(or_expression) => {
|
||||
todo!()
|
||||
}
|
||||
Expression::And(and_expression) => {
|
||||
todo!()
|
||||
}
|
||||
Expression::Comparison(comparison_expression) => {
|
||||
todo!()
|
||||
}
|
||||
Expression::Shift(shift_expression) => {
|
||||
todo!()
|
||||
}
|
||||
Expression::Additive(additive_expression) => additive_expression
|
||||
.analyzed_kind()
|
||||
.expect("AdditiveExpression's analyzed_kind not set.")
|
||||
.clone(),
|
||||
Expression::Multiplicative(multiplicative_expression) => {
|
||||
todo!()
|
||||
}
|
||||
Expression::Prefix(prefix_expression) => {
|
||||
todo!()
|
||||
}
|
||||
Expression::Suffix(suffix_expression) => {
|
||||
todo!()
|
||||
}
|
||||
Expression::Literal(literal) => literal.analyzed_kind(),
|
||||
Expression::Identifier(identifier) => identifier
|
||||
.analyzed_kind()
|
||||
.expect("IdentifierExpression's analyzed_kind not set.")
|
||||
.clone(),
|
||||
Expression::Fqn(fqn) => {
|
||||
todo!()
|
||||
}
|
||||
Expression::Closure(closure) => {
|
||||
todo!()
|
||||
}
|
||||
Expression::List(list_expression) => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Literal {
|
||||
pub fn analyzed_kind(&self) -> Kind {
|
||||
match self {
|
||||
Literal::IntLiteral(_) => Kind::Primitive(PrimitiveKind::Int.into()),
|
||||
Literal::LongLiteral(_) => Kind::Primitive(PrimitiveKind::Long.into()),
|
||||
Literal::DoubleLiteral(_) => Kind::Primitive(PrimitiveKind::Double.into()),
|
||||
Literal::SingleQuoteString(_) => Kind::Primitive(PrimitiveKind::String.into()),
|
||||
Literal::DString(_) => {
|
||||
todo!()
|
||||
}
|
||||
Literal::BacktickString(_) => {
|
||||
todo!()
|
||||
}
|
||||
Literal::BooleanLiteral(_) => Kind::Primitive(PrimitiveKind::Boolean.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Parameters {
|
||||
fn default() -> Self {
|
||||
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 {
|
||||
use pest::iterators::Pairs;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/src/ast/build.rs"));
|
||||
|
||||
pub fn build_ast(file_id: usize, parsed_pairs: &mut Pairs<Rule>) -> CompilationUnit {
|
||||
let compilation_unit_pair = parsed_pairs.next().unwrap();
|
||||
build_compilation_unit(file_id, compilation_unit_pair)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod build_tests {
|
||||
use super::*;
|
||||
use crate::ast::ast_node::AstNodeRef;
|
||||
use crate::ast::pretty_print::PrettyPrint;
|
||||
use crate::ast::walk::walk_depth_first;
|
||||
use crate::parser::DeimosParser;
|
||||
use crate::util::indent_writer::IndentWriter;
|
||||
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()
|
||||
}
|
||||
|
||||
fn fmt_ast(node: &dyn PrettyPrint) -> String {
|
||||
let mut acc = String::new();
|
||||
let mut indent_writer = IndentWriter::new(0, " ", &mut acc);
|
||||
node.pretty_print(&mut indent_writer)
|
||||
.expect("Pretty print failed");
|
||||
acc
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean_literal_true() {
|
||||
let pair = parse(
|
||||
Rule::BooleanLiteral,
|
||||
include_str!("build_tests/boolean_literal/true"),
|
||||
);
|
||||
assert_eq!(true, build_boolean_literal(0, pair));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean_literal_false() {
|
||||
let pair = parse(
|
||||
Rule::BooleanLiteral,
|
||||
include_str!("build_tests/boolean_literal/false"),
|
||||
);
|
||||
assert_eq!(false, build_boolean_literal(0, 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(0, pair));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backtick_string_mixed() {
|
||||
let pair = parse(
|
||||
Rule::BacktickString,
|
||||
include_str!("build_tests/backtick_string/mixed"),
|
||||
);
|
||||
let backtick_string = build_backtick_string(0, pair);
|
||||
assert_eq!(backtick_string.inners().count(), 2);
|
||||
assert_eq!(backtick_string.expressions().count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn d_string_expression_simple() {
|
||||
let pair = parse(Rule::DStringExpression, "${thing}");
|
||||
let d_string_expression = build_d_string_expression(0, pair);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn d_string_inner() {
|
||||
let pair = parse(Rule::DStringInner, "Hello!");
|
||||
let d_string_inner = build_d_string_inner(0, pair);
|
||||
assert_eq!("Hello!", d_string_inner);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn d_string_mixed() {
|
||||
let pair = parse(Rule::DString, "\"Hello, ${world}!\"");
|
||||
let d_string = build_d_string(0, pair);
|
||||
assert_eq!(d_string.inners().count(), 2);
|
||||
assert_eq!(d_string.expressions().count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expression_simple_call() {
|
||||
let pair = parse(Rule::Expression, "hello(42)");
|
||||
let expression = build_expression(0, pair);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_expression_statements() {
|
||||
let pair = parse(
|
||||
Rule::Function,
|
||||
"
|
||||
fn test()
|
||||
println(42)
|
||||
println(43)
|
||||
end
|
||||
",
|
||||
);
|
||||
let function = build_function(0, pair);
|
||||
|
||||
let mut number_of_statements = 0;
|
||||
|
||||
walk_depth_first(&function, &mut |node| match node {
|
||||
AstNodeRef::Statement(_) => {
|
||||
number_of_statements += 1;
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
|
||||
assert_eq!(2, number_of_statements, "Ast: {}", fmt_ast(&function));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_return_after_statement() {
|
||||
let pair = parse(
|
||||
Rule::Function,
|
||||
"
|
||||
fn test()
|
||||
println(42)
|
||||
[0, 1, 2]
|
||||
end
|
||||
",
|
||||
);
|
||||
let function = build_function(0, pair);
|
||||
|
||||
let mut number_of_statements = 0;
|
||||
|
||||
walk_depth_first(&function, &mut |node| {
|
||||
if let AstNodeRef::Statement(stmt) = node {
|
||||
number_of_statements += 1;
|
||||
}
|
||||
});
|
||||
|
||||
assert_eq!(2, number_of_statements);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_return_after_bare_identifier() {
|
||||
// The following should parse as two expression statements
|
||||
let pair = parse(
|
||||
Rule::Function,
|
||||
"
|
||||
fn test()
|
||||
x
|
||||
[0, 1, 2]
|
||||
end
|
||||
",
|
||||
);
|
||||
let function = build_function(0, pair);
|
||||
|
||||
let mut number_of_statements = 0;
|
||||
walk_depth_first(&function, &mut |node| {
|
||||
if let AstNodeRef::Statement(stmt) = node {
|
||||
number_of_statements += 1;
|
||||
}
|
||||
});
|
||||
|
||||
assert_eq!(2, number_of_statements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod pretty_print {
|
||||
use crate::util::indent_writer::IndentWriter;
|
||||
|
||||
pub trait PrettyPrint {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> std::fmt::Result;
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/src/ast/pretty_print.rs"));
|
||||
}
|
||||
|
||||
pub mod ast_node {
|
||||
include!(concat!(env!("OUT_DIR"), "/src/ast/ast_node.rs"));
|
||||
}
|
||||
|
||||
pub mod walk {
|
||||
include!(concat!(env!("OUT_DIR"), "/src/ast/walk.rs"));
|
||||
}
|
||||
|
||||
@ -101,45 +101,45 @@ where
|
||||
f(node.as_node_ref());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::ast::build::build_ast;
|
||||
use crate::ast::children::NodeRef;
|
||||
use crate::ast::walk::walk_depth_first;
|
||||
use crate::parser::{DeimosParser, Rule};
|
||||
use indoc::indoc;
|
||||
use pest::Parser;
|
||||
|
||||
#[test]
|
||||
fn collect_identifiers() {
|
||||
let parse_result = DeimosParser::parse(
|
||||
Rule::CompilationUnit,
|
||||
indoc! {"
|
||||
ns greeter;
|
||||
|
||||
class Greeter {}
|
||||
|
||||
fn main() {
|
||||
let greeter = Greeter();
|
||||
}
|
||||
"},
|
||||
);
|
||||
match parse_result {
|
||||
Ok(cu_pairs) => {
|
||||
let cu = build_ast("greeter.dm", 0, cu_pairs.into_iter().next().unwrap());
|
||||
let mut identifier_count = 0;
|
||||
walk_depth_first(&cu, &mut |node_ref| match node_ref {
|
||||
NodeRef::Identifier(identifier) => {
|
||||
dbg!(identifier);
|
||||
identifier_count += 1;
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
assert_eq!(identifier_count, 5);
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("{}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use crate::ast::build::build_ast;
|
||||
// use crate::ast::children::NodeRef;
|
||||
// use crate::ast::walk::walk_depth_first;
|
||||
// use crate::parser::{DeimosParser, Rule};
|
||||
// use indoc::indoc;
|
||||
// use pest::Parser;
|
||||
//
|
||||
// #[test]
|
||||
// fn collect_identifiers() {
|
||||
// let parse_result = DeimosParser::parse(
|
||||
// Rule::CompilationUnit,
|
||||
// indoc! {"
|
||||
// ns greeter;
|
||||
//
|
||||
// class Greeter {}
|
||||
//
|
||||
// fn main() {
|
||||
// let greeter = Greeter();
|
||||
// }
|
||||
// "},
|
||||
// );
|
||||
// match parse_result {
|
||||
// Ok(cu_pairs) => {
|
||||
// let cu = build_ast("greeter.dm", 0, cu_pairs.into_iter().next().unwrap());
|
||||
// let mut identifier_count = 0;
|
||||
// walk_depth_first(&cu, &mut |node_ref| match node_ref {
|
||||
// NodeRef::Identifier(identifier) => {
|
||||
// dbg!(identifier);
|
||||
// identifier_count += 1;
|
||||
// }
|
||||
// _ => {}
|
||||
// });
|
||||
// assert_eq!(identifier_count, 5);
|
||||
// }
|
||||
// Err(err) => {
|
||||
// panic!("{}", err);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
30
src/bin/dmc/cst.rs
Normal file
30
src/bin/dmc/cst.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use deimos::parser::{DeimosParser, Rule};
|
||||
use std::path::PathBuf;
|
||||
use pest::iterators::{Pair, Pairs};
|
||||
use pest::Parser;
|
||||
|
||||
fn print_pair(pair: Pair<Rule>) {
|
||||
println!("{:?}", pair.as_rule());
|
||||
println!("{:?}", pair.as_span());
|
||||
println!("----");
|
||||
print_pairs(pair.into_inner());
|
||||
}
|
||||
|
||||
fn print_pairs(pairs: Pairs<Rule>) {
|
||||
for pair in pairs {
|
||||
print_pair(pair);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show_cst(path: &PathBuf) {
|
||||
let src = std::fs::read_to_string(path).unwrap();
|
||||
let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src);
|
||||
match parse_result {
|
||||
Ok(pairs) => {
|
||||
print_pairs(pairs);
|
||||
}
|
||||
Err(error) => {
|
||||
eprintln!("{:?}", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
146
src/bin/dmc/ir.rs
Normal file
146
src/bin/dmc/ir.rs
Normal file
@ -0,0 +1,146 @@
|
||||
use codespan_reporting::files::SimpleFiles;
|
||||
use codespan_reporting::term;
|
||||
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
||||
use deimos::asm::assemble_ir::assemble_ir_function;
|
||||
use deimos::ast::build::build_ast;
|
||||
use deimos::ast::node::CompilationUnit;
|
||||
use deimos::ir::lower_ast::lower_compilation_unit;
|
||||
use deimos::ir::Ir;
|
||||
use deimos::name_analysis::analyze_names;
|
||||
use deimos::name_analysis::symbol_table::SymbolTable;
|
||||
use deimos::parser::{DeimosParser, Rule};
|
||||
use deimos::std_core::add_std_core_symbols;
|
||||
use deimos::type_analysis::analyze_types;
|
||||
use pest::Parser;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::path::PathBuf;
|
||||
|
||||
struct ParseErrors {
|
||||
errors: Vec<pest::error::Error<Rule>>,
|
||||
}
|
||||
|
||||
impl Debug for ParseErrors {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ParseErrors {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "There were errors during parsing.")?;
|
||||
for parse_error in &self.errors {
|
||||
writeln!(f, "{}", parse_error)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParseErrors {}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CompilationErrors {
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl Display for CompilationErrors {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
if self.count == 1 {
|
||||
writeln!(f, "There was 1 error during compilation. See above.")
|
||||
} else {
|
||||
writeln!(
|
||||
f,
|
||||
"There were {} errors during compilation. See above.",
|
||||
self.count
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CompilationErrors {}
|
||||
|
||||
pub fn compile_to_ir(
|
||||
paths: &[PathBuf],
|
||||
) -> Result<Vec<CompilationUnit>, Box<dyn std::error::Error>> {
|
||||
let mut paths_and_sources: HashMap<String, String> = HashMap::new();
|
||||
for path in paths {
|
||||
let src = std::fs::read_to_string(path).unwrap();
|
||||
paths_and_sources.insert(path.display().to_string(), src);
|
||||
}
|
||||
|
||||
let mut compilation_units = vec![];
|
||||
let mut files: SimpleFiles<&str, &str> = SimpleFiles::new();
|
||||
let mut parse_errors = vec![];
|
||||
|
||||
for (path, source) in &paths_and_sources {
|
||||
let parse_result = DeimosParser::parse(Rule::CompilationUnit, source);
|
||||
match parse_result {
|
||||
Ok(mut pairs) => {
|
||||
let file_id = files.add(path, source);
|
||||
let compilation_unit = build_ast(file_id, &mut pairs);
|
||||
compilation_units.push(compilation_unit);
|
||||
}
|
||||
Err(error) => {
|
||||
parse_errors.push(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !parse_errors.is_empty() {
|
||||
return Err(Box::new(ParseErrors {
|
||||
errors: parse_errors,
|
||||
}));
|
||||
}
|
||||
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols.");
|
||||
|
||||
let name_diagnostics = analyze_names(&mut compilation_units, &files, &mut symbol_table);
|
||||
|
||||
if name_diagnostics.is_empty() {
|
||||
println!("Name analysis complete.");
|
||||
} else {
|
||||
let writer = StandardStream::stderr(ColorChoice::Always);
|
||||
let config = term::Config::default();
|
||||
for diagnostic in &name_diagnostics {
|
||||
term::emit(&mut writer.lock(), &config, &files, diagnostic)?;
|
||||
}
|
||||
return Err(Box::new(CompilationErrors {
|
||||
count: name_diagnostics.len(),
|
||||
}));
|
||||
}
|
||||
|
||||
let type_diagnostics = analyze_types(&mut compilation_units);
|
||||
|
||||
if type_diagnostics.is_empty() {
|
||||
println!("Type analysis complete.");
|
||||
} else {
|
||||
let writer = StandardStream::stderr(ColorChoice::Always);
|
||||
let config = term::Config::default();
|
||||
for diagnostic in &type_diagnostics {
|
||||
term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
|
||||
}
|
||||
return Err(Box::new(CompilationErrors {
|
||||
count: type_diagnostics.len(),
|
||||
}));
|
||||
}
|
||||
|
||||
for compilation_unit in &compilation_units {
|
||||
let cu_irs = lower_compilation_unit(compilation_unit);
|
||||
for ir in &cu_irs {
|
||||
println!("{}", ir)
|
||||
}
|
||||
|
||||
for ir in &cu_irs {
|
||||
match ir {
|
||||
Ir::Function(ir_function) => {
|
||||
let asm_function = assemble_ir_function(ir_function);
|
||||
println!("{}", asm_function);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(compilation_units)
|
||||
}
|
||||
@ -1,12 +1,16 @@
|
||||
mod cst;
|
||||
mod ir;
|
||||
mod name_analysis;
|
||||
mod p3;
|
||||
mod unparse;
|
||||
// mod unparse;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
// use crate::unparse::unparse;
|
||||
use crate::cst::show_cst;
|
||||
use crate::ir::compile_to_ir;
|
||||
use crate::name_analysis::name_analysis;
|
||||
use crate::p3::pretty_print_parse;
|
||||
use crate::unparse::unparse;
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@ -19,35 +23,35 @@ struct Cli {
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum Commands {
|
||||
#[command(arg_required_else_help = true)]
|
||||
Unparse {
|
||||
paths: Vec<PathBuf>,
|
||||
},
|
||||
P3 {
|
||||
paths: Vec<PathBuf>,
|
||||
},
|
||||
NameAnalysis {
|
||||
paths: Vec<PathBuf>,
|
||||
},
|
||||
Cst { paths: Vec<PathBuf> },
|
||||
P3 { paths: Vec<PathBuf> },
|
||||
NameAnalysis { paths: Vec<PathBuf> },
|
||||
Ir { paths: Vec<PathBuf> },
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Cli::parse();
|
||||
match args.command {
|
||||
Commands::Unparse { paths } => {
|
||||
for path in paths {
|
||||
unparse(&path);
|
||||
Commands::Cst { paths } => {
|
||||
for path in &paths {
|
||||
show_cst(path);
|
||||
}
|
||||
}
|
||||
Commands::P3 { paths } => {
|
||||
for path in paths {
|
||||
pretty_print_parse(&path)
|
||||
pretty_print_parse(&path);
|
||||
}
|
||||
}
|
||||
Commands::NameAnalysis { paths } => {
|
||||
let result = name_analysis(&paths);
|
||||
if let Err(e) = result {
|
||||
eprintln!("{}", e)
|
||||
eprintln!("{}", e);
|
||||
}
|
||||
}
|
||||
Commands::Ir { paths } => {
|
||||
let result = compile_to_ir(&paths);
|
||||
if let Err(e) = result {
|
||||
eprintln!("{}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,37 +5,73 @@ use deimos::ast::build::build_ast;
|
||||
use deimos::name_analysis::analyze_names;
|
||||
use deimos::name_analysis::symbol_table::SymbolTable;
|
||||
use deimos::parser::{DeimosParser, Rule};
|
||||
use pest::Parser;
|
||||
use std::path::PathBuf;
|
||||
use deimos::std_core::add_std_core_symbols;
|
||||
use pest::Parser;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::path::PathBuf;
|
||||
use deimos::ast::node::CompilationUnit;
|
||||
|
||||
pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut compilation_units = vec![];
|
||||
let mut files = SimpleFiles::new();
|
||||
struct ParseErrors {
|
||||
errors: Vec<pest::error::Error<Rule>>,
|
||||
}
|
||||
|
||||
impl Debug for ParseErrors {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ParseErrors {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "There were errors during parsing.")?;
|
||||
for parse_error in &self.errors {
|
||||
writeln!(f, "{}", parse_error)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParseErrors {}
|
||||
|
||||
pub fn name_analysis(paths: &[PathBuf]) -> Result<Vec<CompilationUnit>, Box<dyn std::error::Error>> {
|
||||
let mut paths_and_sources: HashMap<String, String> = HashMap::new();
|
||||
for path in paths {
|
||||
let src = std::fs::read_to_string(path).unwrap();
|
||||
let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src);
|
||||
let file_id = files.add(path.display().to_string(), src.clone()); // I don't love this clone
|
||||
paths_and_sources.insert(path.display().to_string(), src);
|
||||
}
|
||||
|
||||
let mut compilation_units = vec![];
|
||||
let mut files: SimpleFiles<&str, &str> = SimpleFiles::new();
|
||||
let mut parse_errors = vec![];
|
||||
|
||||
for (path, source) in &paths_and_sources {
|
||||
let parse_result = DeimosParser::parse(Rule::CompilationUnit, source);
|
||||
match parse_result {
|
||||
Ok(mut pairs) => {
|
||||
let compilation_unit_pair = pairs.next().unwrap();
|
||||
let compilation_unit = build_ast(&path.display().to_string(), file_id, compilation_unit_pair);
|
||||
let file_id = files.add(path, source);
|
||||
let compilation_unit = build_ast(file_id, &mut pairs);
|
||||
compilation_units.push(compilation_unit);
|
||||
Ok::<(), Box<dyn std::error::Error>>(())
|
||||
}
|
||||
Err(e) => Err(e.into()),
|
||||
}?;
|
||||
Err(error) => {
|
||||
parse_errors.push(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !parse_errors.is_empty() {
|
||||
return Err(Box::new(ParseErrors {
|
||||
errors: parse_errors,
|
||||
}));
|
||||
}
|
||||
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols.");
|
||||
|
||||
let diagnostics = analyze_names(&mut compilation_units, &mut symbol_table);
|
||||
let diagnostics = analyze_names(&mut compilation_units, &files, &mut symbol_table);
|
||||
if diagnostics.is_empty() {
|
||||
println!("Name analysis complete.");
|
||||
println!("Symbol table\n-------\n{}", symbol_table);
|
||||
println!("{}", symbol_table);
|
||||
} else {
|
||||
let writer = StandardStream::stderr(ColorChoice::Always);
|
||||
let config = term::Config::default();
|
||||
@ -44,5 +80,5 @@ pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Err
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(compilation_units)
|
||||
}
|
||||
|
||||
@ -10,12 +10,13 @@ pub fn pretty_print_parse(path: &PathBuf) {
|
||||
let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src);
|
||||
match parse_result {
|
||||
Ok(mut pairs) => {
|
||||
let compilation_unit_pair = pairs.next().unwrap();
|
||||
let compilation_unit = build_ast(&path.display().to_string(), 0, compilation_unit_pair);
|
||||
let mut indent_writer = IndentWriter::new(0, " ", Box::new(std::io::stdout()));
|
||||
let compilation_unit = build_ast(0, &mut pairs);
|
||||
let mut acc = String::new();
|
||||
let mut indent_writer = IndentWriter::new(0, " ", &mut acc);
|
||||
compilation_unit
|
||||
.pretty_print(&mut indent_writer)
|
||||
.expect("Unable to pretty-print.");
|
||||
println!("{}", acc);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user