Compare commits
115 Commits
main
...
ast-node-r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eda25307b0 | ||
|
|
542d5f6c80 | ||
|
|
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 |
12
Cargo.lock
generated
12
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",
|
||||
|
||||
@ -21,7 +21,8 @@ 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"]
|
||||
|
||||
71
ast-generator/src/ast_node/enum_ast_node.rs
Normal file
71
ast-generator/src/ast_node/enum_ast_node.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use crate::spec::tree_enum_spec::{
|
||||
EnumRuleChildKind, TreeEnumBuildSpec, TreeEnumRule,
|
||||
};
|
||||
use convert_case::{Case, Casing};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
fn make_match_arm(rule: &TreeEnumRule, type_ident: &Ident, is_mut: bool) -> TokenStream {
|
||||
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));
|
||||
let as_node_ref_ident = if is_mut {
|
||||
quote! { as_node_ref_mut }
|
||||
} else {
|
||||
quote! { as_node_ref }
|
||||
};
|
||||
quote! {
|
||||
#type_ident::#rule_ident(#child_ident) => vec![
|
||||
#child_ident.#as_node_ref_ident()
|
||||
]
|
||||
}
|
||||
}
|
||||
_ => quote! {
|
||||
#type_ident::#rule_ident(_) => vec![]
|
||||
},
|
||||
},
|
||||
None => {
|
||||
quote! {
|
||||
#type_ident::#rule_ident => vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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| make_match_arm(rule, &type_ident, false))
|
||||
.collect::<Vec<_>>();
|
||||
let match_arms_mut = enum_spec
|
||||
.rules()
|
||||
.map(|rule| make_match_arm(rule, &type_ident, true))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl AstNode for #type_ident {
|
||||
fn children(&self) -> Vec<AstNodeRef> {
|
||||
match self {
|
||||
#(#match_arms,)*
|
||||
}
|
||||
}
|
||||
|
||||
fn children_mut(&mut self) -> Vec<AstNodeRefMut> {
|
||||
match self {
|
||||
#(#match_arms_mut,)*
|
||||
}
|
||||
}
|
||||
|
||||
fn as_node_ref(&self) -> AstNodeRef {
|
||||
AstNodeRef::#type_ident(&self)
|
||||
}
|
||||
|
||||
fn as_node_ref_mut(&mut self) -> AstNodeRefMut {
|
||||
AstNodeRefMut::#type_ident(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
ast-generator/src/ast_node/leaf_enum_ast_node.rs
Normal file
26
ast-generator/src/ast_node/leaf_enum_ast_node.rs
Normal file
@ -0,0 +1,26 @@
|
||||
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 AstNode for #type_ident {
|
||||
fn children(&self) -> Vec<AstNodeRef> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn children_mut(&mut self) -> Vec<AstNodeRefMut> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn as_node_ref(&self) -> AstNodeRef {
|
||||
AstNodeRef::#type_ident(&self)
|
||||
}
|
||||
|
||||
fn as_node_ref_mut(&mut self) -> AstNodeRefMut {
|
||||
AstNodeRefMut::#type_ident(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
ast-generator/src/ast_node/leaf_struct_ast_node.rs
Normal file
26
ast-generator/src/ast_node/leaf_struct_ast_node.rs
Normal file
@ -0,0 +1,26 @@
|
||||
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 AstNode for #type_ident {
|
||||
fn children(&self) -> Vec<AstNodeRef> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn children_mut(&mut self) -> Vec<AstNodeRefMut> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn as_node_ref(&self) -> AstNodeRef {
|
||||
AstNodeRef::#type_ident(&self)
|
||||
}
|
||||
|
||||
fn as_node_ref_mut(&mut self) -> AstNodeRefMut {
|
||||
AstNodeRefMut::#type_ident(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
ast-generator/src/ast_node/mod.rs
Normal file
68
ast-generator/src/ast_node/mod.rs
Normal file
@ -0,0 +1,68 @@
|
||||
mod enum_ast_node;
|
||||
mod leaf_enum_ast_node;
|
||||
mod leaf_struct_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_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,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_ast_enum_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,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_ast_enum_member(build_spec: &BuildSpec) -> Option<TokenStream> {
|
||||
make_ast_enum_ident(build_spec).map(|type_ident| {
|
||||
quote! {
|
||||
#type_ident(&'a #type_ident)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn make_ast_enum_member_mut(build_spec: &BuildSpec) -> Option<TokenStream> {
|
||||
make_ast_enum_ident(build_spec).map(|type_ident| {
|
||||
quote! {
|
||||
#type_ident(&'a mut #type_ident)
|
||||
}
|
||||
})
|
||||
}
|
||||
79
ast-generator/src/ast_node/polymorphic_enum_loop_ast_node.rs
Normal file
79
ast-generator/src/ast_node/polymorphic_enum_loop_ast_node.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use crate::spec::polymorphic_enum_loop_spec::{
|
||||
PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuildChild,
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
fn make_child_adder(child: &PolymorphicEnumLoopRuleBuildChild, is_mut: bool) -> TokenStream {
|
||||
let child_ident = {
|
||||
let base = match child {
|
||||
PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => {
|
||||
format_ident!("{}", use_current.name())
|
||||
}
|
||||
PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => {
|
||||
format_ident!("{}", on_each.name())
|
||||
}
|
||||
};
|
||||
if is_mut {
|
||||
format_ident!("{}_mut", base)
|
||||
} else {
|
||||
base
|
||||
}
|
||||
};
|
||||
|
||||
let as_node_ref_ident = if is_mut {
|
||||
quote! { as_node_ref_mut }
|
||||
} else {
|
||||
quote! { as_node_ref }
|
||||
};
|
||||
quote! {
|
||||
children.push(self.#child_ident().#as_node_ref_ident());
|
||||
}
|
||||
}
|
||||
|
||||
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| make_child_adder(child, false))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let child_adders_mut = build_rule
|
||||
.children()
|
||||
.map(|child| make_child_adder(child, true))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl AstNode for #type_ident {
|
||||
fn children(&self) -> Vec<AstNodeRef> {
|
||||
let mut children: Vec<AstNodeRef> = vec![];
|
||||
#(#child_adders;)*
|
||||
children
|
||||
}
|
||||
|
||||
fn children_mut(&mut self) -> Vec<AstNodeRefMut> {
|
||||
let mut children: Vec<AstNodeRefMut> = vec![];
|
||||
#(#child_adders_mut;)*
|
||||
children
|
||||
}
|
||||
|
||||
fn as_node_ref(&self) -> AstNodeRef {
|
||||
AstNodeRef::#type_ident(&self)
|
||||
}
|
||||
|
||||
fn as_node_ref_mut(&mut self) -> AstNodeRefMut {
|
||||
AstNodeRefMut::#type_ident(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
ast-generator/src/ast_node/polymorphic_type_ast_node.rs
Normal file
59
ast-generator/src/ast_node/polymorphic_type_ast_node.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use crate::spec::polymorphic_type_spec::{PolymorphicTypeBuildSpec, PolymorphicTypeVariant};
|
||||
use convert_case::{Case, Casing};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
fn make_match_arm(
|
||||
variant: &PolymorphicTypeVariant,
|
||||
type_ident: &Ident,
|
||||
is_mut: bool,
|
||||
) -> TokenStream {
|
||||
let variant_ident = format_ident!("{}", variant.name());
|
||||
let child_ident = format_ident!("{}", variant.inner_kind().to_case(Case::Snake));
|
||||
let as_node_ref_ident = if is_mut {
|
||||
quote! { as_node_ref_mut }
|
||||
} else {
|
||||
quote! { as_node_ref }
|
||||
};
|
||||
|
||||
quote! {
|
||||
#type_ident::#variant_ident(#child_ident) => vec![#child_ident.#as_node_ref_ident()]
|
||||
}
|
||||
}
|
||||
|
||||
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| make_match_arm(variant, &type_ident, false))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let match_arms_mut = spec
|
||||
.variants()
|
||||
.map(|variant| make_match_arm(variant, &type_ident, true))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl AstNode for #type_ident {
|
||||
fn children(&self) -> Vec<AstNodeRef> {
|
||||
match self {
|
||||
#(#match_arms,)*
|
||||
}
|
||||
}
|
||||
|
||||
fn children_mut(&mut self) -> Vec<AstNodeRefMut> {
|
||||
match self {
|
||||
#(#match_arms_mut,)*
|
||||
}
|
||||
}
|
||||
|
||||
fn as_node_ref(&self) -> AstNodeRef {
|
||||
AstNodeRef::#type_ident(&self)
|
||||
}
|
||||
|
||||
fn as_node_ref_mut(&mut self) -> AstNodeRefMut {
|
||||
AstNodeRefMut::#type_ident(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
95
ast-generator/src/ast_node/struct_ast_node.rs
Normal file
95
ast-generator/src/ast_node/struct_ast_node.rs
Normal file
@ -0,0 +1,95 @@
|
||||
use crate::spec::struct_spec::{MemberChildBuild, StructChild, StructSpec, VecChildBuild};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
fn make_child_adder(child: &StructChild, is_mut: bool) -> Option<TokenStream> {
|
||||
let as_node_ref_ident = if is_mut {
|
||||
format_ident!("as_node_ref_mut")
|
||||
} else {
|
||||
format_ident!("as_node_ref")
|
||||
};
|
||||
|
||||
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().map(AstNode::#as_node_ref_ident) {
|
||||
children.push(child);
|
||||
}
|
||||
};
|
||||
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_node_ref_ident());
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Some(quote! {
|
||||
children.push(self.#child_ident().#as_node_ref_ident());
|
||||
})
|
||||
}
|
||||
}
|
||||
MemberChildBuild::Boolean(_) => None,
|
||||
},
|
||||
StructChild::Special(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_struct_ast_node_impl(spec: &StructSpec) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", spec.build());
|
||||
let child_adders = spec
|
||||
.children()
|
||||
.map(|child| make_child_adder(child, false))
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let child_adders_mut = spec
|
||||
.children()
|
||||
.map(|child| make_child_adder(child, true))
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl AstNode for #type_ident {
|
||||
fn children(&self) -> Vec<AstNodeRef> {
|
||||
let mut children: Vec<AstNodeRef> = vec![];
|
||||
#(#child_adders;)*
|
||||
children
|
||||
}
|
||||
|
||||
fn children_mut(&mut self) -> Vec<AstNodeRefMut> {
|
||||
let mut children: Vec<AstNodeRefMut> = vec![];
|
||||
#(#child_adders_mut;)*
|
||||
children
|
||||
}
|
||||
|
||||
fn as_node_ref(&self) -> AstNodeRef {
|
||||
AstNodeRef::#type_ident(&self)
|
||||
}
|
||||
|
||||
fn as_node_ref_mut(&mut self) -> AstNodeRefMut {
|
||||
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,)*
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
43
ast-generator/src/build_fn/mod.rs
Normal file
43
ast-generator/src/build_fn/mod.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use crate::build_fn::leaf_enum_build_fn::make_leaf_enum_build_fn;
|
||||
use crate::build_fn::leaf_struct_build_fn::make_leaf_struct_build_fn;
|
||||
use crate::build_fn::node_production_build_fn::make_node_production_build_fn;
|
||||
use crate::build_fn::polymorphic_enum_loop_build_fn::make_polymorphic_enum_loop_build_fn;
|
||||
use crate::build_fn::polymorphic_pass_through_build_fn::make_polymorphic_pass_through_build_fn;
|
||||
use crate::build_fn::polymorphic_type_build_fn::make_polymorphic_type_build_fn;
|
||||
use crate::build_fn::production_build_fn::make_production_build_fn;
|
||||
use crate::build_fn::struct_build_fn::make_struct_build_fn;
|
||||
use crate::build_fn::tree_enum_build_fn::make_enum_build_fn;
|
||||
use crate::spec::BuildSpec;
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
mod leaf_enum_build_fn;
|
||||
mod leaf_struct_build_fn;
|
||||
mod node_production_build_fn;
|
||||
mod polymorphic_enum_loop_build_fn;
|
||||
mod polymorphic_pass_through_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)
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
107
ast-generator/src/build_fn/polymorphic_enum_loop_build_fn.rs
Normal file
107
ast-generator/src/build_fn/polymorphic_enum_loop_build_fn.rs
Normal file
@ -0,0 +1,107 @@
|
||||
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 proc_macro2::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 },
|
||||
})
|
||||
.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),
|
||||
}
|
||||
}
|
||||
|
||||
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 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 {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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, SpecialChild, SpecialChildKind, StructChild, StructSpec,
|
||||
VecChild, VecChildBuild,
|
||||
};
|
||||
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) -> 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))
|
||||
.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)
|
||||
}
|
||||
87
ast-generator/src/deserialize/mod.rs
Normal file
87
ast-generator/src/deserialize/mod.rs
Normal file
@ -0,0 +1,87 @@
|
||||
mod leaf_enum_spec;
|
||||
mod leaf_struct_spec;
|
||||
mod node_production_spec;
|
||||
mod polymorphic_enum_loop_spec;
|
||||
mod polymorphic_pass_through_spec;
|
||||
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_loop_spec::deserialize_polymorphic_enum_loop;
|
||||
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_pass_through_spec::deserialize_polymorphic_pass_through;
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
79
ast-generator/src/deserialize/polymorphic_enum_loop_spec.rs
Normal file
79
ast-generator/src/deserialize/polymorphic_enum_loop_spec.rs
Normal file
@ -0,0 +1,79 @@
|
||||
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 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 {
|
||||
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();
|
||||
|
||||
PolymorphicEnumLoopRuleBuild::new(name, variant, children)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
182
ast-generator/src/deserialize/struct_spec.rs
Normal file
182
ast-generator/src/deserialize/struct_spec.rs
Normal file
@ -0,0 +1,182 @@
|
||||
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 yaml_rust2::Yaml;
|
||||
|
||||
fn deserialize_fields(fields: &Yaml) -> Vec<Box<StructField>> {
|
||||
fields
|
||||
.as_vec()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|field| unwrap_single_member_hash(field))
|
||||
.map(|(name, props)| {
|
||||
let kind = match props["kind"].as_str().unwrap() {
|
||||
"usize" => StructFieldKind::USize,
|
||||
_ => panic!()
|
||||
};
|
||||
StructField::new(&name, kind, get_as_bool(&props["optional"]))
|
||||
})
|
||||
.map(Box::new)
|
||||
.collect()
|
||||
}
|
||||
|
||||
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() {
|
||||
deserialize_fields(&struct_yaml["fields"])
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
StructSpec::new(name, children, fields)
|
||||
}
|
||||
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,17 @@
|
||||
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_member_mut, make_ast_node_impl};
|
||||
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 +21,46 @@ 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
println!("{:#?}", token_stream);
|
||||
@ -25,30 +68,157 @@ 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;
|
||||
|
||||
#(#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_members_mut = build_specs
|
||||
.iter()
|
||||
.map(|build_spec| make_ast_enum_member_mut(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,)*
|
||||
}
|
||||
|
||||
pub enum AstNodeRefMut<'a> {
|
||||
#(#ast_enum_members_mut,)*
|
||||
}
|
||||
|
||||
pub trait AstNode {
|
||||
fn children(&self) -> Vec<AstNodeRef>;
|
||||
|
||||
fn children_mut(&mut self) -> Vec<AstNodeRefMut>;
|
||||
|
||||
fn as_node_ref(&self) -> AstNodeRef;
|
||||
|
||||
fn as_node_ref_mut(&mut self) -> AstNodeRefMut;
|
||||
}
|
||||
|
||||
#(#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);
|
||||
}
|
||||
273
ast-generator/src/pretty_print.rs
Normal file
273
ast-generator/src/pretty_print.rs
Normal file
@ -0,0 +1,273 @@
|
||||
use crate::spec::leaf_enum_spec::LeafEnumBuildSpec;
|
||||
use crate::spec::leaf_struct_spec::{LeafStructBuildSpec, LeafStructMemberKind};
|
||||
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_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()
|
||||
.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())
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
self.#child_ident().pretty_print(writer)?;
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl PrettyPrint for #type_ident {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::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<_>>();
|
||||
|
||||
quote! {
|
||||
impl PrettyPrint for #type_ident {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::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<_>>();
|
||||
|
||||
quote! {
|
||||
impl PrettyPrint for #type_ident {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::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<_>>();
|
||||
|
||||
quote! {
|
||||
impl PrettyPrint for #type_ident {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::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<_>>();
|
||||
|
||||
quote! {
|
||||
impl PrettyPrint for #type_ident {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::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();
|
||||
|
||||
quote! {
|
||||
impl PrettyPrint for #type_ident {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
31
ast-generator/src/spec/mod.rs
Normal file
31
ast-generator/src/spec/mod.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use crate::spec::node_production_spec::NodeProductionBuildSpec;
|
||||
use crate::spec::polymorphic_enum_loop_spec::PolymorphicEnumLoopBuildSpec;
|
||||
use leaf_enum_spec::LeafEnumBuildSpec;
|
||||
use leaf_struct_spec::LeafStructBuildSpec;
|
||||
use polymorphic_type_spec::PolymorphicTypeBuildSpec;
|
||||
use production_spec::ProductionBuildSpec;
|
||||
use struct_spec::StructSpec;
|
||||
use tree_enum_spec::TreeEnumBuildSpec;
|
||||
use crate::spec::polymorphic_pass_through_spec::PolymorphicPassThroughBuildSpec;
|
||||
|
||||
pub(crate) mod leaf_enum_spec;
|
||||
pub(crate) mod leaf_struct_spec;
|
||||
pub(crate) mod node_production_spec;
|
||||
pub(crate) mod polymorphic_enum_loop_spec;
|
||||
pub(crate) mod polymorphic_pass_through_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)
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
143
ast-generator/src/spec/polymorphic_enum_loop_spec.rs
Normal file
143
ast-generator/src/spec/polymorphic_enum_loop_spec.rs
Normal file
@ -0,0 +1,143 @@
|
||||
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>>,
|
||||
}
|
||||
|
||||
impl PolymorphicEnumLoopRuleBuild {
|
||||
pub fn new(name: &str, variant: &str, children: Vec<Box<PolymorphicEnumLoopRuleBuildChild>>) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
variant: variant.to_string(),
|
||||
children,
|
||||
}
|
||||
}
|
||||
|
||||
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 enum PolymorphicEnumLoopRuleBuildChild {
|
||||
UseCurrent(PolymorphicEnumLoopChildUseCurrent),
|
||||
OnEach(PolymorphicEnumLoopRuleChildOnEach),
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
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 },
|
||||
}
|
||||
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,
|
||||
}
|
||||
302
ast-generator/src/spec/struct_spec.rs
Normal file
302
ast-generator/src/spec/struct_spec.rs
Normal file
@ -0,0 +1,302 @@
|
||||
pub struct StructSpec {
|
||||
build: String,
|
||||
children: Vec<Box<StructChild>>,
|
||||
fields: Vec<Box<StructField>>,
|
||||
}
|
||||
|
||||
impl StructSpec {
|
||||
pub fn new(
|
||||
build: &str,
|
||||
children: Vec<Box<StructChild>>,
|
||||
fields: Vec<Box<StructField>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
build: build.to_string(),
|
||||
children,
|
||||
fields,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 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,
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
pub struct StructField {
|
||||
name: String,
|
||||
kind: StructFieldKind,
|
||||
optional: bool,
|
||||
}
|
||||
|
||||
impl StructField {
|
||||
pub fn new(name: &str, kind: StructFieldKind, optional: bool) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
kind,
|
||||
optional,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &StructFieldKind {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
pub fn optional(&self) -> bool {
|
||||
self.optional
|
||||
}
|
||||
}
|
||||
|
||||
pub enum StructFieldKind {
|
||||
USize,
|
||||
}
|
||||
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)*
|
||||
}
|
||||
}
|
||||
}
|
||||
37
ast-generator/src/type_gen/mod.rs
Normal file
37
ast-generator/src/type_gen/mod.rs
Normal file
@ -0,0 +1,37 @@
|
||||
mod enum_type;
|
||||
mod leaf_enum_type;
|
||||
mod leaf_struct_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_type_type::make_polymorphic_type_type;
|
||||
use crate::type_gen::struct_type::make_struct_type;
|
||||
use proc_macro2::TokenStream;
|
||||
use crate::type_gen::polymorphic_enum_loop_type::make_polymorphic_enum_loop_type;
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
96
ast-generator/src/type_gen/polymorphic_enum_loop_type.rs
Normal file
96
ast-generator/src/type_gen/polymorphic_enum_loop_type.rs
Normal file
@ -0,0 +1,96 @@
|
||||
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_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>
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.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())
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let accessors = build.children()
|
||||
.map(|child| {
|
||||
let (child_ident, child_type_ident) = match child {
|
||||
PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => {
|
||||
(format_ident!("{}", use_current.name()), format_ident!("{}", use_current.kind()))
|
||||
}
|
||||
PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => {
|
||||
(format_ident!("{}", on_each.name()), format_ident!("{}", on_each.rule()))
|
||||
}
|
||||
};
|
||||
let child_mut_ident = format_ident!("{}_mut", child_ident);
|
||||
|
||||
quote! {
|
||||
pub fn #child_ident(&self) -> &#child_type_ident {
|
||||
self.#child_ident.as_ref()
|
||||
}
|
||||
|
||||
pub fn #child_mut_ident(&mut self) -> &mut #child_type_ident {
|
||||
self.#child_ident.as_mut()
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
pub struct #type_ident {
|
||||
#(#annotated_members),*
|
||||
}
|
||||
|
||||
impl #type_ident {
|
||||
pub fn new(#(#annotated_members),*) -> Self {
|
||||
Self {
|
||||
#(#member_names),*
|
||||
}
|
||||
}
|
||||
|
||||
#(#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),*
|
||||
}
|
||||
}
|
||||
}
|
||||
289
ast-generator/src/type_gen/struct_type.rs
Normal file
289
ast-generator/src/type_gen/struct_type.rs
Normal file
@ -0,0 +1,289 @@
|
||||
use crate::spec::struct_spec::{
|
||||
MemberChild, MemberChildBuild, SpecialChild, SpecialChildKind, StructChild, StructField,
|
||||
StructFieldKind, StructSpec, VecChild, VecChildBuild,
|
||||
};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
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_child_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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_annotated_field(field: &StructField) -> TokenStream {
|
||||
let field_ident = format_ident!("{}", field.name());
|
||||
let field_type_ident = match field.kind() {
|
||||
StructFieldKind::USize => quote! { usize },
|
||||
};
|
||||
if field.optional() {
|
||||
quote! {
|
||||
#field_ident: Option<#field_type_ident>
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#field_ident: #field_type_ident
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_field_init(field: &StructField) -> TokenStream {
|
||||
let field_ident = format_ident!("{}", field.name());
|
||||
if field.optional() {
|
||||
quote! {
|
||||
#field_ident: None
|
||||
}
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn make_field_accessors(field: &StructField) -> TokenStream {
|
||||
let field_ident = format_ident!("{}", field.name());
|
||||
let field_type_ident = match field.kind() {
|
||||
StructFieldKind::USize => quote! { usize },
|
||||
};
|
||||
if field.optional() {
|
||||
let setter_ident = format_ident!("set_{}", field.name());
|
||||
quote! {
|
||||
pub fn #field_ident(&self) -> Option<#field_type_ident> {
|
||||
self.#field_ident
|
||||
}
|
||||
|
||||
pub fn #setter_ident(&mut self, #field_ident: #field_type_ident) {
|
||||
self.#field_ident = Some(#field_ident);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_struct_type(build_spec: &StructSpec) -> TokenStream {
|
||||
let type_ident = format_ident!("{}", build_spec.build());
|
||||
let annotated_children = 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_inits = build_spec
|
||||
.fields()
|
||||
.map(|field| make_field_init(field))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let child_accessors = build_spec
|
||||
.children()
|
||||
.map(|child| make_child_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<_>>();
|
||||
|
||||
quote! {
|
||||
pub struct #type_ident {
|
||||
#(#annotated_children,)*
|
||||
#(#annotated_fields,)*
|
||||
}
|
||||
|
||||
impl #type_ident {
|
||||
pub fn new(#(#annotated_children),*) -> Self {
|
||||
Self {
|
||||
#(#member_names,)*
|
||||
#(#field_inits,)*
|
||||
}
|
||||
}
|
||||
|
||||
#(#child_accessors)*
|
||||
#(#field_accessors)*
|
||||
}
|
||||
}
|
||||
}
|
||||
61
ast-generator/src/walk.rs
Normal file
61
ast-generator/src/walk.rs
Normal file
@ -0,0 +1,61 @@
|
||||
use crate::spec::BuildSpec;
|
||||
use convert_case::{Case, Casing};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn make_walk_fn(specs: &[BuildSpec]) -> TokenStream {
|
||||
let child_match_arms = specs
|
||||
.iter()
|
||||
.map(|spec| match spec {
|
||||
BuildSpec::Enum(enum_spec) => Some((
|
||||
format_ident!("{}", enum_spec.build()),
|
||||
format_ident!("{}", enum_spec.build().to_case(Case::Snake)),
|
||||
)),
|
||||
BuildSpec::LeafEnum(leaf_enum) => Some((
|
||||
format_ident!("{}", leaf_enum.build()),
|
||||
format_ident!("{}", leaf_enum.build().to_case(Case::Snake)),
|
||||
)),
|
||||
BuildSpec::Struct(struct_spec) => Some((
|
||||
format_ident!("{}", struct_spec.build()),
|
||||
format_ident!("{}", struct_spec.build().to_case(Case::Snake)),
|
||||
)),
|
||||
BuildSpec::LeafStruct(leaf_struct) => Some((
|
||||
format_ident!("{}", leaf_struct.build()),
|
||||
format_ident!("{}", leaf_struct.build().to_case(Case::Snake)),
|
||||
)),
|
||||
BuildSpec::Production(_) => None,
|
||||
BuildSpec::NodeProduction(_) => None,
|
||||
BuildSpec::PolymorphicType(polymorphic_type) => Some((
|
||||
format_ident!("{}", polymorphic_type.name()),
|
||||
format_ident!("{}", polymorphic_type.name().to_case(Case::Snake)),
|
||||
)),
|
||||
BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop) => Some((
|
||||
format_ident!("{}", polymorphic_enum_loop.name()),
|
||||
format_ident!("{}", polymorphic_enum_loop.name().to_case(Case::Snake)),
|
||||
)),
|
||||
BuildSpec::PolymorphicPassThrough(_) => None,
|
||||
})
|
||||
.filter(Option::is_some)
|
||||
.map(Option::unwrap)
|
||||
.map(|(type_ident, inner_ident)| {
|
||||
quote! {
|
||||
#type_ident(#inner_ident) => walk_depth_first(#inner_ident, f)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
use crate::ast::node::*;
|
||||
use crate::ast::ast_node::*;
|
||||
|
||||
pub fn walk_depth_first(node: &impl AstNode, f: &mut impl FnMut(AstNodeRef)) {
|
||||
use AstNodeRef::*;
|
||||
for child in node.children() {
|
||||
match child {
|
||||
#(#child_match_arms,)*
|
||||
}
|
||||
}
|
||||
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,
|
||||
})
|
||||
}
|
||||
3
examples/forty_two.dm
Normal file
3
examples/forty_two.dm
Normal file
@ -0,0 +1,3 @@
|
||||
fn main()
|
||||
println 42
|
||||
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.color == color }
|
||||
.map { 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
|
||||
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
|
||||
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
|
||||
203
src/ast/mod.rs
203
src/ast/mod.rs
@ -1,6 +1,197 @@
|
||||
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 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>) -> Box<CompilationUnit> {
|
||||
let compilation_unit_pair = parsed_pairs.next().unwrap();
|
||||
Box::new(build_compilation_unit(file_id, compilation_unit_pair))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod build_tests {
|
||||
use super::*;
|
||||
use crate::parser::DeimosParser;
|
||||
use pest::Parser;
|
||||
|
||||
fn parse(rule: Rule, input: &str) -> Pair<Rule> {
|
||||
let parse_result = DeimosParser::parse(rule, input);
|
||||
parse_result.expect("parsing failed").next().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean_literal_true() {
|
||||
let pair = parse(
|
||||
Rule::BooleanLiteral,
|
||||
include_str!("build_tests/boolean_literal/true"),
|
||||
);
|
||||
assert_eq!(true, build_boolean_literal(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod pretty_print {
|
||||
use crate::util::indent_writer::IndentWriter;
|
||||
|
||||
pub trait PrettyPrint {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::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"));
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::ast::ast_node::AstNode;
|
||||
use crate::ast::node::CompilationUnit;
|
||||
|
||||
fn get_cu() -> CompilationUnit {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple() {
|
||||
let cu = get_cu();
|
||||
for child in cu.children() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
mod name_analysis;
|
||||
mod p3;
|
||||
mod unparse;
|
||||
// mod unparse;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::name_analysis::name_analysis;
|
||||
use crate::p3::pretty_print_parse;
|
||||
use crate::unparse::unparse;
|
||||
// use crate::unparse::unparse;
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@ -34,11 +34,11 @@ enum Commands {
|
||||
fn main() {
|
||||
let args = Cli::parse();
|
||||
match args.command {
|
||||
Commands::Unparse { paths } => {
|
||||
for path in paths {
|
||||
unparse(&path);
|
||||
}
|
||||
}
|
||||
// Commands::Unparse { paths } => {
|
||||
// for path in paths {
|
||||
// unparse(&path);
|
||||
// }
|
||||
// }
|
||||
Commands::P3 { paths } => {
|
||||
for path in paths {
|
||||
pretty_print_parse(&path)
|
||||
@ -50,5 +50,6 @@ fn main() {
|
||||
eprintln!("{}", e)
|
||||
}
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,14 +5,14 @@ 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 deimos::std_core::add_std_core_symbols;
|
||||
use pest::Parser;
|
||||
use std::path::PathBuf;
|
||||
use deimos::std_core::add_std_core_symbols;
|
||||
|
||||
pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut compilation_units = vec![];
|
||||
let mut files = SimpleFiles::new();
|
||||
|
||||
let mut files: SimpleFiles<String, String> = SimpleFiles::new();
|
||||
|
||||
for path in paths {
|
||||
let src = std::fs::read_to_string(path).unwrap();
|
||||
let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src);
|
||||
@ -20,8 +20,7 @@ pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Err
|
||||
|
||||
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 compilation_unit = build_ast(file_id, &mut pairs);
|
||||
compilation_units.push(compilation_unit);
|
||||
Ok::<(), Box<dyn std::error::Error>>(())
|
||||
}
|
||||
@ -31,11 +30,11 @@ pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Err
|
||||
|
||||
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(compilation_units.as_mut_slice(), &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();
|
||||
@ -43,6 +42,6 @@ pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Err
|
||||
term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -10,8 +10,7 @@ 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 compilation_unit = build_ast(0, &mut pairs);
|
||||
let mut indent_writer = IndentWriter::new(0, " ", Box::new(std::io::stdout()));
|
||||
compilation_unit
|
||||
.pretty_print(&mut indent_writer)
|
||||
|
||||
199
src/ir/mod.rs
Normal file
199
src/ir/mod.rs
Normal file
@ -0,0 +1,199 @@
|
||||
pub enum DvmIr {
|
||||
Struct(Box<DvmIrStruct>),
|
||||
Function(DvmIrFunction),
|
||||
Const(DvmIrConst),
|
||||
}
|
||||
|
||||
pub struct DvmIrStruct {
|
||||
name: String,
|
||||
kind: Box<DvmIrKind>,
|
||||
is_public: bool,
|
||||
members: Vec<Box<DvmIrStructMember>>
|
||||
}
|
||||
|
||||
pub struct DvmIrStructMember {
|
||||
name: String,
|
||||
kind: Box<DvmIrKind>,
|
||||
is_public: bool,
|
||||
is_mut: bool,
|
||||
}
|
||||
|
||||
pub enum DvmIrKind {
|
||||
Primitive(Box<DvmIrPrimitiveKind>),
|
||||
Struct(Box<DvmIrStructKind>),
|
||||
}
|
||||
|
||||
pub enum DvmIrPrimitiveKind {
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
I128,
|
||||
ISize,
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
U128,
|
||||
USize,
|
||||
Boolean,
|
||||
Array(Box<DvmIrKind>),
|
||||
String,
|
||||
Closure(Box<DvmIrClosureKind>),
|
||||
Ref(Box<DvmIrKind>),
|
||||
Void,
|
||||
}
|
||||
|
||||
pub struct DvmIrStructKind {
|
||||
name: String,
|
||||
}
|
||||
|
||||
pub struct DvmIrClosureKind {
|
||||
captures_name: String,
|
||||
parameters: Vec<Box<DvmIrKind>>,
|
||||
return_type: Box<DvmIrKind>,
|
||||
}
|
||||
|
||||
pub struct DvmIrFunction {
|
||||
name: String,
|
||||
parameters: Vec<Box<DvmIrKind>>,
|
||||
return_type: Box<DvmIrKind>,
|
||||
statements: Vec<Box<DvmIrStatement>>,
|
||||
}
|
||||
|
||||
pub enum DvmIrStatement {
|
||||
Alloc(DvmIrAlloc),
|
||||
Call(DvmIrCallStmt),
|
||||
Ret(DvmIrRet),
|
||||
Assign(DvmIrAssign),
|
||||
MakeClosure(DvmIrMakeClosure),
|
||||
Drop(DvmIrDrop),
|
||||
}
|
||||
|
||||
pub enum DvmIrAllocable {
|
||||
Struct(Box<DvmIrStructKind>),
|
||||
Array(Box<DvmIrArrayKind>),
|
||||
}
|
||||
|
||||
pub enum DvmIrArrayKind {
|
||||
StaticSize(Box<DvmIrStaticArrayKind>),
|
||||
DynamicSize(Box<DvmIrDynamicArrayKind>)
|
||||
}
|
||||
|
||||
pub struct DvmIrStaticArrayKind {
|
||||
inner_kind: Box<DvmIrKind>,
|
||||
size: usize,
|
||||
}
|
||||
|
||||
pub struct DvmIrDynamicArrayKind {
|
||||
inner_kind: Box<DvmIrKind>,
|
||||
size_expr: Box<DvmIrExpr>
|
||||
}
|
||||
|
||||
pub struct DvmIrAlloc {
|
||||
declared_kind: Box<DvmIrAllocable>,
|
||||
name: String,
|
||||
kind_to_alloc: Box<DvmIrAllocable>,
|
||||
}
|
||||
|
||||
pub enum DvmIrConst {
|
||||
String(DvmIrStringConst),
|
||||
StringArray(DvmIrStringArrayConst),
|
||||
}
|
||||
|
||||
pub struct DvmIrStringConst {
|
||||
name: String,
|
||||
value: String,
|
||||
}
|
||||
|
||||
pub struct DvmIrStringArrayConst {
|
||||
name: String,
|
||||
value: Vec<String>,
|
||||
}
|
||||
|
||||
pub enum DvmIrExpr {
|
||||
Call(Box<DvmIrCallExpr>),
|
||||
Variable(Box<DvmIrVariable>),
|
||||
ConstRef(Box<DvmIrConstRef>),
|
||||
Literal(Box<DvmIrLiteral>)
|
||||
}
|
||||
|
||||
pub struct DvmIrVariable {
|
||||
name: String,
|
||||
}
|
||||
|
||||
pub struct DvmIrConstRef {
|
||||
name: String,
|
||||
}
|
||||
|
||||
pub enum DvmIrLiteral {
|
||||
I8(i8),
|
||||
I16(i16),
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
I128(i128),
|
||||
ISize(isize),
|
||||
U8(u8),
|
||||
U16(u16),
|
||||
U32(u32),
|
||||
U64(u64),
|
||||
U128(u128),
|
||||
USize(usize),
|
||||
Boolean(bool),
|
||||
}
|
||||
|
||||
pub struct DvmIrCallStmt {
|
||||
declared_type: Box<DvmIrKind>,
|
||||
name: String,
|
||||
call_expr: Box<DvmIrCallExpr>,
|
||||
}
|
||||
|
||||
pub struct DvmIrCallExpr {
|
||||
name: String,
|
||||
arguments: Vec<Box<DvmIrExpr>>,
|
||||
}
|
||||
|
||||
pub struct DvmIrAssign {
|
||||
lhs: Box<DvmIrAssignLhs>,
|
||||
rhs: Box<DvmIrExpr>
|
||||
}
|
||||
|
||||
pub struct DvmIrAssignLhs {
|
||||
base: Box<DvmIrVariable>,
|
||||
suffixes: Vec<Box<DvmIrAssignLhsSuffix>>
|
||||
}
|
||||
|
||||
pub enum DvmIrAssignLhsSuffix {
|
||||
Index(DvmIrAssignLhsIndex),
|
||||
Property(DvmIrAssignLhsProperty)
|
||||
}
|
||||
|
||||
pub struct DvmIrAssignLhsIndex {
|
||||
expr: Box<DvmIrExpr>,
|
||||
}
|
||||
|
||||
pub struct DvmIrAssignLhsProperty {
|
||||
name: String,
|
||||
}
|
||||
|
||||
pub struct DvmIrRet {
|
||||
value: Box<DvmIrReturnable>
|
||||
}
|
||||
|
||||
pub enum DvmIrReturnable {
|
||||
Variable(DvmIrVariable),
|
||||
Literal(DvmIrLiteral)
|
||||
}
|
||||
|
||||
pub struct DvmIrMakeClosure {
|
||||
captures_kind: Option<Box<DvmIrStructKind>>,
|
||||
parameters: Vec<Box<DvmIrKind>>,
|
||||
return_type: Box<DvmIrKind>,
|
||||
name: String,
|
||||
fn_name: String,
|
||||
captures_variable: Box<DvmIrVariable>,
|
||||
}
|
||||
|
||||
pub struct DvmIrDrop {
|
||||
variable: Box<DvmIrVariable>,
|
||||
}
|
||||
@ -11,3 +11,4 @@ pub mod parser;
|
||||
pub mod std_core;
|
||||
pub mod util;
|
||||
pub mod vm;
|
||||
pub mod ir;
|
||||
|
||||
@ -4,14 +4,12 @@ pub struct DmModule {
|
||||
|
||||
pub enum NamespaceVisibility {
|
||||
Public,
|
||||
Partial {
|
||||
visible_to_fqns: Vec<String>
|
||||
},
|
||||
Private
|
||||
Partial { visible_to_fqns: Vec<String> },
|
||||
Private,
|
||||
}
|
||||
|
||||
pub struct DmNamespace {
|
||||
pub name: String,
|
||||
pub dependencies: Vec<String>,
|
||||
pub visibility: NamespaceVisibility
|
||||
pub visibility: NamespaceVisibility,
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
pub(super) struct FqnContext {
|
||||
pub struct FqnContext {
|
||||
stack: Vec<String>,
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1320
src/name_analysis/gather.rs.bak
Normal file
1320
src/name_analysis/gather.rs.bak
Normal file
File diff suppressed because it is too large
Load Diff
@ -19,250 +19,254 @@ The resolve phase has one main responsibility: resolve all references based on t
|
||||
`scope_id` property.
|
||||
*/
|
||||
|
||||
use crate::ast::node::compilation_unit::CompilationUnit;
|
||||
use crate::ast::node::named::Named;
|
||||
use crate::ast::node::{CompilationUnit, Identifier};
|
||||
use crate::diagnostic::DmDiagnostic;
|
||||
use crate::name_analysis::gather::gather_compilation_unit;
|
||||
use crate::name_analysis::resolve::resolve_compilation_unit;
|
||||
// use crate::name_analysis::resolve::resolve_compilation_unit;
|
||||
use crate::name_analysis::symbol_table::SymbolTable;
|
||||
use codespan_reporting::files::Files;
|
||||
use std::collections::HashMap;
|
||||
|
||||
mod fqn_context;
|
||||
pub(self) mod fqn_context;
|
||||
mod gather;
|
||||
mod resolve;
|
||||
// mod resolve;
|
||||
pub mod symbol;
|
||||
pub mod symbol_table;
|
||||
|
||||
pub fn analyze_names(
|
||||
compilation_units: &mut Vec<CompilationUnit>,
|
||||
pub fn analyze_names<'a, F: Files<'a, FileId = usize, Name = String>>(
|
||||
compilation_units: &mut [Box<CompilationUnit>],
|
||||
files: &'a F,
|
||||
symbol_table: &mut SymbolTable,
|
||||
) -> Vec<DmDiagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
let mut identifier_scope_ids: HashMap<Identifier, usize> = HashMap::new();
|
||||
|
||||
// gather symbols
|
||||
for compilation_unit in compilation_units.iter_mut() {
|
||||
gather_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
|
||||
let file_name = files.name(compilation_unit.file_id()).unwrap();
|
||||
gather_compilation_unit(compilation_unit, &file_name, symbol_table, &mut diagnostics);
|
||||
}
|
||||
|
||||
// resolve symbols
|
||||
for compilation_unit in compilation_units.iter_mut() {
|
||||
resolve_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
|
||||
// resolve_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
|
||||
}
|
||||
|
||||
diagnostics.into()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ast::build::build_ast;
|
||||
use crate::ast::children::NodeRef;
|
||||
use crate::ast::walk::walk_depth_first;
|
||||
use crate::parser::{DeimosParser, Rule};
|
||||
use crate::std_core::add_std_core_symbols;
|
||||
use codespan_reporting::files::SimpleFiles;
|
||||
use codespan_reporting::term;
|
||||
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
||||
use indoc::indoc;
|
||||
use pest::Parser;
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn assert_number_of_diagnostics(
|
||||
sources: HashMap<&str, &str>,
|
||||
symbol_table: &mut SymbolTable,
|
||||
n_diagnostics: usize,
|
||||
) -> Vec<CompilationUnit> {
|
||||
let mut files = SimpleFiles::new();
|
||||
let mut compilation_units = vec![];
|
||||
|
||||
for (file_name, source) in sources {
|
||||
let file_id = files.add(file_name, source);
|
||||
let parse_result = DeimosParser::parse(Rule::CompilationUnit, source);
|
||||
if let Err(err) = &parse_result {
|
||||
panic!("{}", err);
|
||||
}
|
||||
let mut pairs = parse_result.unwrap();
|
||||
if pairs.as_str().trim() != source.trim() {
|
||||
panic!("Parsing did not consume entire input.");
|
||||
}
|
||||
|
||||
compilation_units.push(build_ast(file_name, file_id, pairs.next().unwrap()));
|
||||
}
|
||||
|
||||
let diagnostics = analyze_names(&mut compilation_units, symbol_table);
|
||||
|
||||
if diagnostics.len() != n_diagnostics {
|
||||
let writer = StandardStream::stderr(ColorChoice::Always);
|
||||
let config = term::Config::default();
|
||||
|
||||
for diagnostic in &diagnostics {
|
||||
term::emit(&mut writer.lock(), &config, &files, &diagnostic).unwrap();
|
||||
}
|
||||
|
||||
eprintln!("{}", symbol_table);
|
||||
}
|
||||
|
||||
assert_eq!(n_diagnostics, diagnostics.len());
|
||||
|
||||
compilation_units
|
||||
}
|
||||
|
||||
fn assert_no_diagnostics(
|
||||
sources: HashMap<&str, &str>,
|
||||
symbol_table: &mut SymbolTable,
|
||||
) -> Vec<CompilationUnit> {
|
||||
assert_number_of_diagnostics(sources, symbol_table, 0)
|
||||
}
|
||||
|
||||
fn assert_saved_symbols(compilation_unit: &CompilationUnit) {
|
||||
walk_depth_first(compilation_unit, &mut |node_ref| match node_ref {
|
||||
NodeRef::Identifier(identifier) => {
|
||||
if identifier.saved_symbol().is_none() {
|
||||
panic!("{:?} does not have a saved symbol.", identifier)
|
||||
}
|
||||
}
|
||||
NodeRef::FullyQualifiedName(fqn) => {
|
||||
if fqn.saved_symbol().is_none() {
|
||||
panic!("{:?} does not have a saved symbol.", fqn)
|
||||
}
|
||||
}
|
||||
NodeRef::UseStatement(use_statement) => match use_statement {
|
||||
_ => todo!(),
|
||||
},
|
||||
_ => {}
|
||||
})
|
||||
}
|
||||
|
||||
fn assert_resolved_symbols(compilation_unit: &CompilationUnit) {
|
||||
walk_depth_first(compilation_unit, &mut |node_ref| match node_ref {
|
||||
NodeRef::UseStatement(use_statement) => match use_statement {
|
||||
_ => todo!(),
|
||||
},
|
||||
_ => {}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn params_seen() {
|
||||
let sources: HashMap<&str, &str> = HashMap::from([(
|
||||
"main.dm",
|
||||
indoc! {"
|
||||
fn main(args: Array<String>) {
|
||||
let x = args;
|
||||
}"},
|
||||
)]);
|
||||
|
||||
let cus = assert_no_diagnostics(sources, &mut SymbolTable::new());
|
||||
for ref cu in cus {
|
||||
assert_saved_symbols(cu);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_files() {
|
||||
let sources: HashMap<&str, &str> = HashMap::from([
|
||||
(
|
||||
"main.dm",
|
||||
indoc! {"
|
||||
use test::Greeter;
|
||||
"},
|
||||
),
|
||||
(
|
||||
"deps.dm",
|
||||
indoc! {"
|
||||
ns test;
|
||||
|
||||
pub class Greeter {}
|
||||
"},
|
||||
),
|
||||
]);
|
||||
|
||||
let cus = assert_no_diagnostics(sources, &mut SymbolTable::new());
|
||||
for ref cu in cus {
|
||||
assert_saved_symbols(cu);
|
||||
assert_resolved_symbols(cu);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sees_std_core_println() {
|
||||
let sources: HashMap<&str, &str> = HashMap::from([(
|
||||
"main.dm",
|
||||
indoc! {"
|
||||
fn main(args: Array<String>) {
|
||||
println(args)
|
||||
}
|
||||
"},
|
||||
)]);
|
||||
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols.");
|
||||
let cus = assert_no_diagnostics(sources, &mut symbol_table);
|
||||
for ref cu in cus {
|
||||
assert_saved_symbols(cu);
|
||||
assert_resolved_symbols(cu);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sees_duplicate_fn() {
|
||||
let sources: HashMap<&str, &str> = HashMap::from([(
|
||||
"main.dm",
|
||||
indoc! {"
|
||||
fn main(args: Array<String>) {}
|
||||
fn main(args: Array<String>) {}
|
||||
"},
|
||||
)]);
|
||||
assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn use_class_from_other_file() {
|
||||
let sources: HashMap<&str, &str> = HashMap::from([
|
||||
(
|
||||
"main.dm",
|
||||
indoc! {"
|
||||
use greeter::Greeter;
|
||||
|
||||
fn test(greeter: Greeter) {}
|
||||
"},
|
||||
),
|
||||
(
|
||||
"greeter.dm",
|
||||
indoc! {"
|
||||
ns greeter;
|
||||
|
||||
class Greeter {}
|
||||
"},
|
||||
),
|
||||
]);
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
let cus = assert_no_diagnostics(sources, &mut symbol_table);
|
||||
for ref cu in cus {
|
||||
assert_saved_symbols(cu);
|
||||
assert_resolved_symbols(cu);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shadow_import() {
|
||||
let sources: HashMap<&str, &str> = HashMap::from([
|
||||
(
|
||||
"main.dm",
|
||||
indoc! {"
|
||||
use greeter::Greeter;
|
||||
|
||||
class Greeter {}
|
||||
"},
|
||||
),
|
||||
(
|
||||
"greeter.dm",
|
||||
indoc! {"
|
||||
ns greeter;
|
||||
|
||||
class Greeter {}
|
||||
"},
|
||||
),
|
||||
]);
|
||||
assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1);
|
||||
}
|
||||
}
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use super::*;
|
||||
// use crate::ast::build::build_ast;
|
||||
// use crate::ast::children::NodeRef;
|
||||
// use crate::ast::walk::walk_depth_first;
|
||||
// use crate::parser::{DeimosParser, Rule};
|
||||
// use crate::std_core::add_std_core_symbols;
|
||||
// use codespan_reporting::files::SimpleFiles;
|
||||
// use codespan_reporting::term;
|
||||
// use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
||||
// use indoc::indoc;
|
||||
// use pest::Parser;
|
||||
// use std::collections::HashMap;
|
||||
//
|
||||
// fn assert_number_of_diagnostics(
|
||||
// sources: HashMap<&str, &str>,
|
||||
// symbol_table: &mut SymbolTable,
|
||||
// n_diagnostics: usize,
|
||||
// ) -> Vec<CompilationUnit> {
|
||||
// let mut files = SimpleFiles::new();
|
||||
// let mut compilation_units = vec![];
|
||||
//
|
||||
// for (file_name, source) in sources {
|
||||
// let file_id = files.add(file_name, source);
|
||||
// let parse_result = DeimosParser::parse(Rule::CompilationUnit, source);
|
||||
// if let Err(err) = &parse_result {
|
||||
// panic!("{}", err);
|
||||
// }
|
||||
// let mut pairs = parse_result.unwrap();
|
||||
// if pairs.as_str().trim() != source.trim() {
|
||||
// panic!("Parsing did not consume entire input.");
|
||||
// }
|
||||
//
|
||||
// compilation_units.push(build_ast(file_name, file_id, pairs.next().unwrap()));
|
||||
// }
|
||||
//
|
||||
// let diagnostics = analyze_names(&mut compilation_units, symbol_table);
|
||||
//
|
||||
// if diagnostics.len() != n_diagnostics {
|
||||
// let writer = StandardStream::stderr(ColorChoice::Always);
|
||||
// let config = term::Config::default();
|
||||
//
|
||||
// for diagnostic in &diagnostics {
|
||||
// term::emit(&mut writer.lock(), &config, &files, &diagnostic).unwrap();
|
||||
// }
|
||||
//
|
||||
// eprintln!("{}", symbol_table);
|
||||
// }
|
||||
//
|
||||
// assert_eq!(n_diagnostics, diagnostics.len());
|
||||
//
|
||||
// compilation_units
|
||||
// }
|
||||
//
|
||||
// fn assert_no_diagnostics(
|
||||
// sources: HashMap<&str, &str>,
|
||||
// symbol_table: &mut SymbolTable,
|
||||
// ) -> Vec<CompilationUnit> {
|
||||
// assert_number_of_diagnostics(sources, symbol_table, 0)
|
||||
// }
|
||||
//
|
||||
// fn assert_saved_symbols(compilation_unit: &CompilationUnit) {
|
||||
// walk_depth_first(compilation_unit, &mut |node_ref| match node_ref {
|
||||
// NodeRef::Identifier(identifier) => {
|
||||
// if identifier.saved_symbol().is_none() {
|
||||
// panic!("{:?} does not have a saved symbol.", identifier)
|
||||
// }
|
||||
// }
|
||||
// NodeRef::FullyQualifiedName(fqn) => {
|
||||
// if fqn.saved_symbol().is_none() {
|
||||
// panic!("{:?} does not have a saved symbol.", fqn)
|
||||
// }
|
||||
// }
|
||||
// NodeRef::UseStatement(use_statement) => match use_statement {
|
||||
// _ => todo!(),
|
||||
// },
|
||||
// _ => {}
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// fn assert_resolved_symbols(compilation_unit: &CompilationUnit) {
|
||||
// walk_depth_first(compilation_unit, &mut |node_ref| match node_ref {
|
||||
// NodeRef::UseStatement(use_statement) => match use_statement {
|
||||
// _ => todo!(),
|
||||
// },
|
||||
// _ => {}
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn params_seen() {
|
||||
// let sources: HashMap<&str, &str> = HashMap::from([(
|
||||
// "main.dm",
|
||||
// indoc! {"
|
||||
// fn main(args: Array<String>) {
|
||||
// let x = args;
|
||||
// }"},
|
||||
// )]);
|
||||
//
|
||||
// let cus = assert_no_diagnostics(sources, &mut SymbolTable::new());
|
||||
// for ref cu in cus {
|
||||
// assert_saved_symbols(cu);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn two_files() {
|
||||
// let sources: HashMap<&str, &str> = HashMap::from([
|
||||
// (
|
||||
// "main.dm",
|
||||
// indoc! {"
|
||||
// use test::Greeter;
|
||||
// "},
|
||||
// ),
|
||||
// (
|
||||
// "deps.dm",
|
||||
// indoc! {"
|
||||
// ns test;
|
||||
//
|
||||
// pub class Greeter {}
|
||||
// "},
|
||||
// ),
|
||||
// ]);
|
||||
//
|
||||
// let cus = assert_no_diagnostics(sources, &mut SymbolTable::new());
|
||||
// for ref cu in cus {
|
||||
// assert_saved_symbols(cu);
|
||||
// assert_resolved_symbols(cu);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn sees_std_core_println() {
|
||||
// let sources: HashMap<&str, &str> = HashMap::from([(
|
||||
// "main.dm",
|
||||
// indoc! {"
|
||||
// fn main(args: Array<String>) {
|
||||
// println(args)
|
||||
// }
|
||||
// "},
|
||||
// )]);
|
||||
//
|
||||
// let mut symbol_table = SymbolTable::new();
|
||||
// add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols.");
|
||||
// let cus = assert_no_diagnostics(sources, &mut symbol_table);
|
||||
// for ref cu in cus {
|
||||
// assert_saved_symbols(cu);
|
||||
// assert_resolved_symbols(cu);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn sees_duplicate_fn() {
|
||||
// let sources: HashMap<&str, &str> = HashMap::from([(
|
||||
// "main.dm",
|
||||
// indoc! {"
|
||||
// fn main(args: Array<String>) {}
|
||||
// fn main(args: Array<String>) {}
|
||||
// "},
|
||||
// )]);
|
||||
// assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1);
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn use_class_from_other_file() {
|
||||
// let sources: HashMap<&str, &str> = HashMap::from([
|
||||
// (
|
||||
// "main.dm",
|
||||
// indoc! {"
|
||||
// use greeter::Greeter;
|
||||
//
|
||||
// fn test(greeter: Greeter) {}
|
||||
// "},
|
||||
// ),
|
||||
// (
|
||||
// "greeter.dm",
|
||||
// indoc! {"
|
||||
// ns greeter;
|
||||
//
|
||||
// class Greeter {}
|
||||
// "},
|
||||
// ),
|
||||
// ]);
|
||||
// let mut symbol_table = SymbolTable::new();
|
||||
// let cus = assert_no_diagnostics(sources, &mut symbol_table);
|
||||
// for ref cu in cus {
|
||||
// assert_saved_symbols(cu);
|
||||
// assert_resolved_symbols(cu);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn shadow_import() {
|
||||
// let sources: HashMap<&str, &str> = HashMap::from([
|
||||
// (
|
||||
// "main.dm",
|
||||
// indoc! {"
|
||||
// use greeter::Greeter;
|
||||
//
|
||||
// class Greeter {}
|
||||
// "},
|
||||
// ),
|
||||
// (
|
||||
// "greeter.dm",
|
||||
// indoc! {"
|
||||
// ns greeter;
|
||||
//
|
||||
// class Greeter {}
|
||||
// "},
|
||||
// ),
|
||||
// ]);
|
||||
// assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1);
|
||||
// }
|
||||
// }
|
||||
|
||||
@ -1,30 +1,9 @@
|
||||
use crate::ast::node::call_expression::*;
|
||||
use crate::ast::node::class::*;
|
||||
use crate::ast::node::closure::*;
|
||||
use crate::ast::node::compilation_unit::*;
|
||||
use crate::ast::node::d_string::*;
|
||||
use crate::ast::node::expression::*;
|
||||
use crate::ast::node::function::*;
|
||||
use crate::ast::node::generics::*;
|
||||
use crate::ast::node::implements_list::*;
|
||||
use crate::ast::node::interface::*;
|
||||
use crate::ast::node::level::*;
|
||||
use crate::ast::node::literal::*;
|
||||
use crate::ast::node::module::*;
|
||||
use crate::ast::node::names::*;
|
||||
use crate::ast::node::object_access::*;
|
||||
use crate::ast::node::statement::*;
|
||||
use crate::ast::node::tuple_arguments::*;
|
||||
use crate::ast::node::type_use::*;
|
||||
use crate::ast::node::use_statement::*;
|
||||
use crate::ast::node::*;
|
||||
use crate::diagnostic::DmDiagnostic;
|
||||
use crate::name_analysis::symbol::Symbol;
|
||||
use crate::name_analysis::symbol_table::{SymbolLookupError, SymbolTable};
|
||||
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
||||
use std::ops::DerefMut;
|
||||
use std::range::Range;
|
||||
use crate::ast::node::named::Named;
|
||||
/* Type Use */
|
||||
|
||||
fn resolve_type_use(
|
||||
type_use: &mut TypeUse,
|
||||
@ -1,475 +0,0 @@
|
||||
use crate::ast::node::named::Named;
|
||||
use crate::ast::node::names::Identifier;
|
||||
use crate::ast::node::use_statement::UseStatement;
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::range::Range;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SourceDefinition {
|
||||
file_id: usize,
|
||||
range: Range<usize>,
|
||||
}
|
||||
|
||||
impl SourceDefinition {
|
||||
pub fn from_identifier(identifier: &Identifier) -> Self {
|
||||
SourceDefinition {
|
||||
file_id: identifier.file_id(),
|
||||
range: identifier.range(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_identifier_rc(identifier: Rc<RefCell<Identifier>>) -> Self {
|
||||
let borrowed = identifier.borrow();
|
||||
SourceDefinition {
|
||||
file_id: borrowed.file_id(),
|
||||
range: borrowed.range(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_id(&self) -> usize {
|
||||
self.file_id
|
||||
}
|
||||
|
||||
pub fn range(&self) -> Range<usize> {
|
||||
self.range.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SymbolInner {
|
||||
fn declared_name(&self) -> &str;
|
||||
fn definition(&self) -> Option<SourceDefinition>;
|
||||
}
|
||||
|
||||
/* Symbol */
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Symbol {
|
||||
UseStatement(Rc<RefCell<UseStatementSymbol>>),
|
||||
Module(Rc<ModuleSymbol>),
|
||||
Type(Rc<TypeSymbol>),
|
||||
Function(Rc<RefCell<FunctionSymbol>>),
|
||||
Parameter(Rc<ParameterSymbol>),
|
||||
Variable(Rc<VariableSymbol>),
|
||||
ClassMember(Rc<ClassMemberSymbol>),
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
pub fn definition(&self) -> Option<SourceDefinition> {
|
||||
match self {
|
||||
Symbol::UseStatement(s) => s.borrow().definition(),
|
||||
Symbol::Module(s) => s.definition(),
|
||||
Symbol::Type(s) => match s.deref() {
|
||||
TypeSymbol::Concrete(cts) => cts.definition(),
|
||||
TypeSymbol::Generic(gts) => gts.definition(),
|
||||
},
|
||||
Symbol::Function(s) => s.borrow().definition(),
|
||||
Symbol::Parameter(s) => s.definition(),
|
||||
Symbol::Variable(s) => s.definition(),
|
||||
Symbol::ClassMember(s) => s.definition(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_use_statement_symbol(&self) -> Rc<RefCell<UseStatementSymbol>> {
|
||||
match self {
|
||||
Symbol::UseStatement(s) => s.clone(),
|
||||
_ => panic!("unwrap_use_statement_symbol called on non-use statement symbol"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Use-statement */
|
||||
|
||||
pub struct UseStatementSymbol {
|
||||
pub fqn: String,
|
||||
pub declared_name: String,
|
||||
definition: Option<SourceDefinition>,
|
||||
referenced_symbol: Option<Box<Symbol>>,
|
||||
}
|
||||
|
||||
impl UseStatementSymbol {
|
||||
pub fn new(fqn: &str, declared_name: &str, identifier: Option<&Identifier>) -> Self {
|
||||
UseStatementSymbol {
|
||||
fqn: fqn.to_string(),
|
||||
declared_name: declared_name.to_string(),
|
||||
definition: identifier.map(SourceDefinition::from_identifier),
|
||||
referenced_symbol: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_referenced_symbol(&mut self, referenced_symbol: Symbol) {
|
||||
self.referenced_symbol = Some(Box::new(referenced_symbol));
|
||||
}
|
||||
|
||||
pub fn referenced_symbol(&self) -> Option<Box<Symbol>> {
|
||||
self.referenced_symbol.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl SymbolInner for UseStatementSymbol {
|
||||
fn declared_name(&self) -> &str {
|
||||
&self.declared_name
|
||||
}
|
||||
|
||||
fn definition(&self) -> Option<SourceDefinition> {
|
||||
self.definition.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for UseStatementSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("UseStatementSymbol")
|
||||
.field("fqn", &self.fqn)
|
||||
.field("declared_name", &self.declared_name)
|
||||
.field("referenced_symbol", &self.referenced_symbol)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/* Module */
|
||||
|
||||
pub struct ModuleSymbol {
|
||||
fqn: String,
|
||||
declared_name: String,
|
||||
is_public: bool,
|
||||
definition: Option<SourceDefinition>,
|
||||
}
|
||||
|
||||
impl ModuleSymbol {
|
||||
pub fn new(
|
||||
fqn: &str,
|
||||
declared_name: &str,
|
||||
is_public: bool,
|
||||
identifier: Option<&Identifier>,
|
||||
) -> ModuleSymbol {
|
||||
ModuleSymbol {
|
||||
fqn: fqn.to_string(),
|
||||
declared_name: declared_name.to_string(),
|
||||
is_public,
|
||||
definition: identifier.map(SourceDefinition::from_identifier),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SymbolInner for ModuleSymbol {
|
||||
fn declared_name(&self) -> &str {
|
||||
self.declared_name.as_str()
|
||||
}
|
||||
|
||||
fn definition(&self) -> Option<SourceDefinition> {
|
||||
self.definition.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ModuleSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ModuleSymbol")
|
||||
.field("fqn", &self.fqn)
|
||||
.field("declared_name", &self.declared_name)
|
||||
.field("is_public", &self.is_public)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/* TypeSymbol */
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TypeSymbol {
|
||||
Concrete(ConcreteTypeSymbol),
|
||||
Generic(GenericTypeSymbol),
|
||||
}
|
||||
|
||||
impl TypeSymbol {
|
||||
pub fn declared_name(&self) -> &str {
|
||||
match self {
|
||||
TypeSymbol::Concrete(t) => t.declared_name(),
|
||||
TypeSymbol::Generic(t) => t.declared_name(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConcreteTypeSymbol {
|
||||
fqn: String,
|
||||
declared_name: String,
|
||||
is_public: bool,
|
||||
definition: Option<SourceDefinition>,
|
||||
}
|
||||
|
||||
impl ConcreteTypeSymbol {
|
||||
pub fn new(
|
||||
fqn: &str,
|
||||
declared_name: &str,
|
||||
is_public: bool,
|
||||
identifier: Option<&Identifier>,
|
||||
) -> Self {
|
||||
ConcreteTypeSymbol {
|
||||
fqn: fqn.to_string(),
|
||||
declared_name: declared_name.to_string(),
|
||||
is_public,
|
||||
definition: identifier.map(SourceDefinition::from_identifier),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fqn(&self) -> &str {
|
||||
&self.fqn
|
||||
}
|
||||
|
||||
pub fn is_public(&self) -> bool {
|
||||
self.is_public
|
||||
}
|
||||
}
|
||||
|
||||
impl SymbolInner for ConcreteTypeSymbol {
|
||||
fn declared_name(&self) -> &str {
|
||||
&self.declared_name
|
||||
}
|
||||
|
||||
fn definition(&self) -> Option<SourceDefinition> {
|
||||
self.definition.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ConcreteTypeSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("TypeSymbol")
|
||||
.field("fqn", &self.fqn)
|
||||
.field("declared_name", &self.declared_name)
|
||||
.field("is_public", &self.is_public)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GenericTypeSymbol {
|
||||
declared_name: String,
|
||||
source_definition: SourceDefinition,
|
||||
}
|
||||
|
||||
impl GenericTypeSymbol {
|
||||
pub fn new(declared_name: &str, source_definition: SourceDefinition) -> Self {
|
||||
GenericTypeSymbol {
|
||||
declared_name: declared_name.to_string(),
|
||||
source_definition,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SymbolInner for GenericTypeSymbol {
|
||||
fn declared_name(&self) -> &str {
|
||||
self.declared_name.as_str()
|
||||
}
|
||||
|
||||
fn definition(&self) -> Option<SourceDefinition> {
|
||||
Some(self.source_definition.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for GenericTypeSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("GenericTypeSymbol")
|
||||
.field("declared_name", &self.declared_name)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/* Function */
|
||||
|
||||
pub struct FunctionSymbol {
|
||||
fqn: String,
|
||||
declared_name: String,
|
||||
is_public: bool,
|
||||
is_platform: bool,
|
||||
definition: Option<SourceDefinition>,
|
||||
parameters: Vec<Rc<ParameterSymbol>>,
|
||||
return_type: Option<Rc<ConcreteTypeSymbol>>,
|
||||
}
|
||||
|
||||
impl FunctionSymbol {
|
||||
pub fn new(
|
||||
fqn: &str,
|
||||
declared_name: &str,
|
||||
is_public: bool,
|
||||
is_platform: bool,
|
||||
identifier: Option<&Identifier>,
|
||||
) -> FunctionSymbol {
|
||||
FunctionSymbol {
|
||||
fqn: fqn.to_string(),
|
||||
declared_name: declared_name.to_string(),
|
||||
is_public,
|
||||
is_platform,
|
||||
definition: identifier.map(SourceDefinition::from_identifier),
|
||||
parameters: Vec::new(),
|
||||
return_type: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_parameters(self, parameters: Vec<ParameterSymbol>) -> Self {
|
||||
Self {
|
||||
fqn: self.fqn,
|
||||
declared_name: self.declared_name,
|
||||
is_public: self.is_public,
|
||||
is_platform: self.is_platform,
|
||||
definition: self.definition,
|
||||
parameters: parameters
|
||||
.into_iter()
|
||||
.map(|parameter| Rc::new(parameter))
|
||||
.collect(),
|
||||
return_type: self.return_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_return_type(self, return_type: ConcreteTypeSymbol) -> Self {
|
||||
Self {
|
||||
fqn: self.fqn,
|
||||
declared_name: self.declared_name,
|
||||
is_public: self.is_public,
|
||||
is_platform: self.is_platform,
|
||||
definition: self.definition,
|
||||
parameters: self.parameters,
|
||||
return_type: Some(Rc::new(return_type)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fqn(&self) -> &str {
|
||||
&self.fqn
|
||||
}
|
||||
|
||||
pub fn set_parameters(&mut self, parameters: Vec<Rc<ParameterSymbol>>) {
|
||||
self.parameters = parameters;
|
||||
}
|
||||
|
||||
pub fn set_return_type(&mut self, return_type: Rc<ConcreteTypeSymbol>) {
|
||||
self.return_type = Some(return_type);
|
||||
}
|
||||
}
|
||||
|
||||
impl SymbolInner for FunctionSymbol {
|
||||
fn declared_name(&self) -> &str {
|
||||
self.declared_name.as_str()
|
||||
}
|
||||
|
||||
fn definition(&self) -> Option<SourceDefinition> {
|
||||
self.definition.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for FunctionSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("FunctionSymbol")
|
||||
.field("fqn", &self.fqn)
|
||||
.field("declared_name", &self.declared_name)
|
||||
.field("is_public", &self.is_public)
|
||||
.field("is_platform", &self.is_platform)
|
||||
.field("parameters", &self.parameters)
|
||||
.field("return_type", &self.return_type)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/* Parameter */
|
||||
|
||||
pub struct ParameterSymbol {
|
||||
declared_name: String,
|
||||
definition: Option<SourceDefinition>,
|
||||
}
|
||||
|
||||
impl ParameterSymbol {
|
||||
pub fn new(declared_name: &str, identifier: Option<&Identifier>) -> Self {
|
||||
ParameterSymbol {
|
||||
declared_name: declared_name.to_string(),
|
||||
definition: identifier.map(SourceDefinition::from_identifier),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SymbolInner for ParameterSymbol {
|
||||
fn declared_name(&self) -> &str {
|
||||
&self.declared_name
|
||||
}
|
||||
|
||||
fn definition(&self) -> Option<SourceDefinition> {
|
||||
self.definition.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ParameterSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ParameterSymbol")
|
||||
.field("declared_name", &self.declared_name)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/* Variable */
|
||||
|
||||
pub struct VariableSymbol {
|
||||
declared_name: String,
|
||||
is_mutable: bool,
|
||||
definition: Option<SourceDefinition>,
|
||||
}
|
||||
|
||||
impl VariableSymbol {
|
||||
pub fn new(declared_name: &str, is_mutable: bool, identifier: Option<&Identifier>) -> Self {
|
||||
VariableSymbol {
|
||||
declared_name: declared_name.to_string(),
|
||||
is_mutable,
|
||||
definition: identifier.map(SourceDefinition::from_identifier),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SymbolInner for VariableSymbol {
|
||||
fn declared_name(&self) -> &str {
|
||||
self.declared_name.as_str()
|
||||
}
|
||||
|
||||
fn definition(&self) -> Option<SourceDefinition> {
|
||||
self.definition.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for VariableSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("VariableSymbol")
|
||||
.field("declared_name", &self.declared_name)
|
||||
.field("is_mutable", &self.is_mutable)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/* Class Member */
|
||||
|
||||
pub struct ClassMemberSymbol {
|
||||
declared_name: String,
|
||||
is_field: bool,
|
||||
definition: Option<SourceDefinition>,
|
||||
}
|
||||
|
||||
impl ClassMemberSymbol {
|
||||
pub fn new(declared_name: &str, is_field: bool, definition: Option<SourceDefinition>) -> Self {
|
||||
ClassMemberSymbol {
|
||||
declared_name: declared_name.to_string(),
|
||||
is_field,
|
||||
definition,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SymbolInner for ClassMemberSymbol {
|
||||
fn declared_name(&self) -> &str {
|
||||
self.declared_name.as_str()
|
||||
}
|
||||
|
||||
fn definition(&self) -> Option<SourceDefinition> {
|
||||
self.definition.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ClassMemberSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ClassMemberSymbol")
|
||||
.field("declared_name", &self.declared_name)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
53
src/name_analysis/symbol/class_member_symbol.rs
Normal file
53
src/name_analysis/symbol/class_member_symbol.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use crate::name_analysis::symbol::source_definition::SourceDefinition;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ClassMemberSymbol {
|
||||
is_public: bool,
|
||||
is_mut: bool,
|
||||
declared_name: String,
|
||||
source_definition: Option<SourceDefinition>,
|
||||
}
|
||||
|
||||
impl ClassMemberSymbol {
|
||||
pub fn new(
|
||||
is_public: bool,
|
||||
is_mut: bool,
|
||||
declared_name: &str,
|
||||
source_definition: Option<SourceDefinition>,
|
||||
) -> Self {
|
||||
Self {
|
||||
is_public,
|
||||
is_mut,
|
||||
declared_name: declared_name.to_string(),
|
||||
source_definition,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_public(&self) -> bool {
|
||||
self.is_public
|
||||
}
|
||||
|
||||
pub fn is_mut(&self) -> bool {
|
||||
self.is_mut
|
||||
}
|
||||
|
||||
pub fn declared_name(&self) -> &str {
|
||||
&self.declared_name
|
||||
}
|
||||
|
||||
pub fn source_definition(&self) -> Option<&SourceDefinition> {
|
||||
self.source_definition.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ClassMemberSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ClassMemberSymbol")
|
||||
.field("is_public", &self.is_public)
|
||||
.field("is_mut", &self.is_mut)
|
||||
.field("declared_name", &self.declared_name)
|
||||
.field("source_definition", &self.source_definition)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
101
src/name_analysis/symbol/function_symbol.rs
Normal file
101
src/name_analysis/symbol/function_symbol.rs
Normal file
@ -0,0 +1,101 @@
|
||||
use crate::name_analysis::symbol::parameter_symbol::ParameterSymbol;
|
||||
use crate::name_analysis::symbol::source_definition::SourceDefinition;
|
||||
use crate::name_analysis::symbol::type_symbol::ConcreteTypeSymbol;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FunctionSymbol {
|
||||
fqn: String,
|
||||
declared_name: String,
|
||||
is_public: bool,
|
||||
is_platform: bool,
|
||||
source_definition: Option<SourceDefinition>,
|
||||
parameters: Vec<ParameterSymbol>,
|
||||
return_type: Option<ConcreteTypeSymbol>, // todo: can we use TypeSymbol?
|
||||
}
|
||||
|
||||
impl FunctionSymbol {
|
||||
pub fn without_parameters_or_return_type(
|
||||
fqn: &str,
|
||||
declared_name: &str,
|
||||
is_public: bool,
|
||||
is_platform: bool,
|
||||
source_definition: Option<SourceDefinition>,
|
||||
) -> FunctionSymbol {
|
||||
FunctionSymbol {
|
||||
fqn: fqn.to_string(),
|
||||
declared_name: declared_name.to_string(),
|
||||
is_public,
|
||||
is_platform,
|
||||
source_definition,
|
||||
parameters: Vec::new(),
|
||||
return_type: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_parameters(self, parameters: Vec<ParameterSymbol>) -> Self {
|
||||
Self {
|
||||
fqn: self.fqn,
|
||||
declared_name: self.declared_name,
|
||||
is_public: self.is_public,
|
||||
is_platform: self.is_platform,
|
||||
source_definition: self.source_definition,
|
||||
parameters,
|
||||
return_type: self.return_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_return_type(self, return_type: ConcreteTypeSymbol) -> Self {
|
||||
Self {
|
||||
fqn: self.fqn,
|
||||
declared_name: self.declared_name,
|
||||
is_public: self.is_public,
|
||||
is_platform: self.is_platform,
|
||||
source_definition: self.source_definition,
|
||||
parameters: self.parameters,
|
||||
return_type: Some(return_type),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fqn(&self) -> &str {
|
||||
&self.fqn
|
||||
}
|
||||
|
||||
pub fn declared_name(&self) -> &str {
|
||||
&self.declared_name
|
||||
}
|
||||
|
||||
pub fn is_public(&self) -> bool {
|
||||
self.is_public
|
||||
}
|
||||
|
||||
pub fn is_platform(&self) -> bool {
|
||||
self.is_platform
|
||||
}
|
||||
|
||||
pub fn source_definition(&self) -> Option<&SourceDefinition> {
|
||||
self.source_definition.as_ref()
|
||||
}
|
||||
|
||||
pub fn parameters(&self) -> &[ParameterSymbol] {
|
||||
&self.parameters
|
||||
}
|
||||
|
||||
pub fn return_type(&self) -> Option<&ConcreteTypeSymbol> {
|
||||
self.return_type.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for FunctionSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("FunctionSymbol")
|
||||
.field("fqn", &self.fqn)
|
||||
.field("declared_name", &self.declared_name)
|
||||
.field("is_public", &self.is_public)
|
||||
.field("is_platform", &self.is_platform)
|
||||
.field("parameters", &self.parameters)
|
||||
.field("return_type", &self.return_type)
|
||||
.field("source_definition", &self.source_definition)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
46
src/name_analysis/symbol/mod.rs
Normal file
46
src/name_analysis/symbol/mod.rs
Normal file
@ -0,0 +1,46 @@
|
||||
pub(crate) mod class_member_symbol;
|
||||
pub(crate) mod function_symbol;
|
||||
pub(crate) mod module_symbol;
|
||||
pub(crate) mod parameter_symbol;
|
||||
pub(crate) mod source_definition;
|
||||
pub(crate) mod type_symbol;
|
||||
pub(crate) mod use_symbol;
|
||||
pub(crate) mod variable_symbol;
|
||||
|
||||
use crate::name_analysis::symbol::use_symbol::{ConcreteUseSymbol, StarUseSymbol};
|
||||
use class_member_symbol::ClassMemberSymbol;
|
||||
use function_symbol::FunctionSymbol;
|
||||
use module_symbol::ModuleSymbol;
|
||||
use parameter_symbol::ParameterSymbol;
|
||||
use source_definition::SourceDefinition;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::ops::Deref;
|
||||
use type_symbol::TypeSymbol;
|
||||
use variable_symbol::VariableSymbol;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Symbol {
|
||||
ConcreteUse(ConcreteUseSymbol),
|
||||
StarUse(StarUseSymbol),
|
||||
Module(ModuleSymbol),
|
||||
Type(TypeSymbol),
|
||||
Function(FunctionSymbol),
|
||||
Parameter(ParameterSymbol),
|
||||
Variable(VariableSymbol),
|
||||
ClassMember(ClassMemberSymbol),
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
pub fn definition(&self) -> Option<&SourceDefinition> {
|
||||
match self {
|
||||
Symbol::ConcreteUse(concrete) => concrete.source_definition(),
|
||||
Symbol::StarUse(star) => star.source_definition(),
|
||||
Symbol::Module(module) => module.source_definition(),
|
||||
Symbol::Type(type_symbol) => type_symbol.source_definition(),
|
||||
Symbol::Function(function_symbol) => function_symbol.source_definition(),
|
||||
Symbol::Parameter(parameter_symbol) => parameter_symbol.source_definition(),
|
||||
Symbol::Variable(variable_symbol) => variable_symbol.source_definition(),
|
||||
Symbol::ClassMember(class_member_symbol) => class_member_symbol.source_definition(),
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/name_analysis/symbol/module_symbol.rs
Normal file
53
src/name_analysis/symbol/module_symbol.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use crate::name_analysis::symbol::source_definition::SourceDefinition;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ModuleSymbol {
|
||||
fqn: String,
|
||||
declared_name: String,
|
||||
is_public: bool,
|
||||
source_definition: Option<SourceDefinition>,
|
||||
}
|
||||
|
||||
impl ModuleSymbol {
|
||||
pub fn new(
|
||||
fqn: &str,
|
||||
declared_name: &str,
|
||||
is_public: bool,
|
||||
source_definition: Option<SourceDefinition>,
|
||||
) -> Self {
|
||||
Self {
|
||||
fqn: fqn.to_string(),
|
||||
declared_name: declared_name.to_string(),
|
||||
is_public,
|
||||
source_definition,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fqn(&self) -> &str {
|
||||
&self.fqn
|
||||
}
|
||||
|
||||
pub fn declared_name(&self) -> &str {
|
||||
&self.declared_name
|
||||
}
|
||||
|
||||
pub fn is_public(&self) -> bool {
|
||||
self.is_public
|
||||
}
|
||||
|
||||
pub fn source_definition(&self) -> Option<&SourceDefinition> {
|
||||
self.source_definition.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ModuleSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ModuleSymbol")
|
||||
.field("fqn", &self.fqn)
|
||||
.field("declared_name", &self.declared_name)
|
||||
.field("is_public", &self.is_public)
|
||||
.field("source_definition", &self.source_definition)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
34
src/name_analysis/symbol/parameter_symbol.rs
Normal file
34
src/name_analysis/symbol/parameter_symbol.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use crate::name_analysis::symbol::source_definition::SourceDefinition;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ParameterSymbol {
|
||||
declared_name: String,
|
||||
source_definition: Option<SourceDefinition>,
|
||||
}
|
||||
|
||||
impl ParameterSymbol {
|
||||
pub fn new(declared_name: &str, source_definition: Option<SourceDefinition>) -> Self {
|
||||
ParameterSymbol {
|
||||
declared_name: declared_name.to_string(),
|
||||
source_definition,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn declared_name(&self) -> &str {
|
||||
&self.declared_name
|
||||
}
|
||||
|
||||
pub fn source_definition(&self) -> Option<&SourceDefinition> {
|
||||
self.source_definition.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ParameterSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ParameterSymbol")
|
||||
.field("declared_name", &self.declared_name)
|
||||
.field("source_definition", &self.source_definition)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
41
src/name_analysis/symbol/source_definition.rs
Normal file
41
src/name_analysis/symbol/source_definition.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use std::cell::RefCell;
|
||||
use std::range::Range;
|
||||
use std::rc::Rc;
|
||||
use crate::ast::node::{Identifier, Operator, UseStatement};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SourceDefinition {
|
||||
file_id: usize,
|
||||
range: Range<usize>,
|
||||
}
|
||||
|
||||
impl SourceDefinition {
|
||||
pub fn from_identifier(identifier: &Identifier) -> Self {
|
||||
SourceDefinition {
|
||||
file_id: identifier.file_id(),
|
||||
range: identifier.range(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_use_statement(use_statement: &UseStatement) -> Self {
|
||||
Self {
|
||||
file_id: use_statement.file_id(),
|
||||
range: use_statement.range(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_operator(operator: &Operator) -> Self {
|
||||
Self {
|
||||
file_id: operator.file_id(),
|
||||
range: operator.range(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_id(&self) -> usize {
|
||||
self.file_id
|
||||
}
|
||||
|
||||
pub fn range(&self) -> Range<usize> {
|
||||
self.range.clone()
|
||||
}
|
||||
}
|
||||
121
src/name_analysis/symbol/type_symbol.rs
Normal file
121
src/name_analysis/symbol/type_symbol.rs
Normal file
@ -0,0 +1,121 @@
|
||||
use crate::name_analysis::symbol::source_definition::SourceDefinition;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum TypeSymbol {
|
||||
Concrete(ConcreteTypeSymbol),
|
||||
Generic(GenericTypeSymbol),
|
||||
}
|
||||
|
||||
impl TypeSymbol {
|
||||
pub fn declared_name(&self) -> &str {
|
||||
match self {
|
||||
TypeSymbol::Concrete(t) => t.declared_name(),
|
||||
TypeSymbol::Generic(t) => t.declared_name(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source_definition(&self) -> Option<&SourceDefinition> {
|
||||
match self {
|
||||
TypeSymbol::Concrete(t) => t.source_definition(),
|
||||
TypeSymbol::Generic(t) => t.source_definition(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConcreteTypeSymbol {
|
||||
fqn: String,
|
||||
declared_name: String,
|
||||
is_public: bool,
|
||||
kind: ConcreteTypeSymbolKind,
|
||||
source_definition: Option<SourceDefinition>,
|
||||
}
|
||||
|
||||
impl ConcreteTypeSymbol {
|
||||
pub fn new(
|
||||
fqn: &str,
|
||||
declared_name: &str,
|
||||
is_public: bool,
|
||||
kind: ConcreteTypeSymbolKind,
|
||||
source_definition: Option<SourceDefinition>,
|
||||
) -> Self {
|
||||
Self {
|
||||
fqn: fqn.to_string(),
|
||||
declared_name: declared_name.to_string(),
|
||||
is_public,
|
||||
kind,
|
||||
source_definition
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fqn(&self) -> &str {
|
||||
&self.fqn
|
||||
}
|
||||
|
||||
pub fn is_public(&self) -> bool {
|
||||
self.is_public
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &ConcreteTypeSymbolKind {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
pub fn declared_name(&self) -> &str {
|
||||
&self.declared_name
|
||||
}
|
||||
|
||||
pub fn source_definition(&self) -> Option<&SourceDefinition> {
|
||||
self.source_definition.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ConcreteTypeSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("TypeSymbol")
|
||||
.field("fqn", &self.fqn)
|
||||
.field("declared_name", &self.declared_name)
|
||||
.field("is_public", &self.is_public)
|
||||
.field("kind", &self.kind)
|
||||
.field("source_definition", &self.source_definition)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ConcreteTypeSymbolKind {
|
||||
Interface,
|
||||
Class
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GenericTypeSymbol {
|
||||
declared_name: String,
|
||||
source_definition: Option<SourceDefinition>,
|
||||
}
|
||||
|
||||
impl GenericTypeSymbol {
|
||||
pub fn new(declared_name: &str, source_definition: Option<SourceDefinition>) -> Self {
|
||||
GenericTypeSymbol {
|
||||
declared_name: declared_name.to_string(),
|
||||
source_definition,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn declared_name(&self) -> &str {
|
||||
&self.declared_name
|
||||
}
|
||||
|
||||
pub fn source_definition(&self) -> Option<&SourceDefinition> {
|
||||
self.source_definition.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for GenericTypeSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("GenericTypeSymbol")
|
||||
.field("declared_name", &self.declared_name)
|
||||
.field("source_definition", &self.source_definition)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
77
src/name_analysis/symbol/use_symbol.rs
Normal file
77
src/name_analysis/symbol/use_symbol.rs
Normal file
@ -0,0 +1,77 @@
|
||||
use crate::name_analysis::symbol::source_definition::SourceDefinition;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConcreteUseSymbol {
|
||||
base_fqn: String,
|
||||
declared_name: String,
|
||||
source_definition: Option<SourceDefinition>,
|
||||
}
|
||||
|
||||
impl ConcreteUseSymbol {
|
||||
pub fn new(
|
||||
base_fqn: &str,
|
||||
declared_name: &str,
|
||||
source_definition: Option<SourceDefinition>,
|
||||
) -> Self {
|
||||
Self {
|
||||
base_fqn: base_fqn.to_string(),
|
||||
declared_name: declared_name.to_string(),
|
||||
source_definition,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn base_fqn(&self) -> &str {
|
||||
&self.base_fqn
|
||||
}
|
||||
|
||||
pub fn declared_name(&self) -> &str {
|
||||
&self.declared_name
|
||||
}
|
||||
|
||||
pub fn source_definition(&self) -> Option<&SourceDefinition> {
|
||||
self.source_definition.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ConcreteUseSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ConcreteUseStatementSymbol")
|
||||
.field("base_fqn", &self.base_fqn)
|
||||
.field("declared_name", &self.declared_name)
|
||||
.field("source_definition", &self.source_definition)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StarUseSymbol {
|
||||
base_fqn: String,
|
||||
source_definition: Option<SourceDefinition>,
|
||||
}
|
||||
|
||||
impl StarUseSymbol {
|
||||
pub fn new(base_fqn: &str, source_definition: Option<SourceDefinition>) -> Self {
|
||||
Self {
|
||||
base_fqn: base_fqn.to_string(),
|
||||
source_definition,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn base_fqn(&self) -> &str {
|
||||
&self.base_fqn
|
||||
}
|
||||
|
||||
pub fn source_definition(&self) -> Option<&SourceDefinition> {
|
||||
self.source_definition.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for StarUseSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("StarUseStatementSymbol")
|
||||
.field("base_fqn", &self.base_fqn)
|
||||
.field("source_definition", &self.source_definition)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
41
src/name_analysis/symbol/variable_symbol.rs
Normal file
41
src/name_analysis/symbol/variable_symbol.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use crate::name_analysis::symbol::source_definition::SourceDefinition;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VariableSymbol {
|
||||
declared_name: String,
|
||||
is_mutable: bool,
|
||||
source_definition: Option<SourceDefinition>,
|
||||
}
|
||||
|
||||
impl VariableSymbol {
|
||||
pub fn new(declared_name: &str, is_mutable: bool, source_definition: Option<SourceDefinition>) -> Self {
|
||||
VariableSymbol {
|
||||
declared_name: declared_name.to_string(),
|
||||
is_mutable,
|
||||
source_definition,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn declared_name(&self) -> &str {
|
||||
&self.declared_name
|
||||
}
|
||||
|
||||
pub fn is_mutable(&self) -> bool {
|
||||
self.is_mutable
|
||||
}
|
||||
|
||||
pub fn source_definition(&self) -> Option<&SourceDefinition> {
|
||||
self.source_definition.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for VariableSymbol {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("VariableSymbol")
|
||||
.field("declared_name", &self.declared_name)
|
||||
.field("is_mutable", &self.is_mutable)
|
||||
.field("source_definition", &self.source_definition)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@ -1,468 +0,0 @@
|
||||
use crate::name_analysis::symbol::*;
|
||||
use crate::name_analysis::symbol_table::SymbolInsertError::SymbolAlreadyDefined;
|
||||
use crate::name_analysis::symbol_table::SymbolLookupError::NoDefinition;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
/* Scope */
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Scope {
|
||||
parent: Option<usize>,
|
||||
use_statement_symbols: HashMap<String, Rc<RefCell<UseStatementSymbol>>>,
|
||||
module_symbols: HashMap<String, Rc<ModuleSymbol>>,
|
||||
type_symbols: HashMap<String, Rc<TypeSymbol>>,
|
||||
function_symbols: HashMap<String, Rc<RefCell<FunctionSymbol>>>,
|
||||
parameter_symbols: HashMap<String, Rc<ParameterSymbol>>,
|
||||
variable_symbols: HashMap<String, Rc<VariableSymbol>>,
|
||||
class_member_symbols: HashMap<String, Rc<ClassMemberSymbol>>,
|
||||
debug_name: String,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
pub fn new(parent: Option<usize>, debug_name: String) -> Scope {
|
||||
Scope {
|
||||
parent,
|
||||
use_statement_symbols: HashMap::new(),
|
||||
module_symbols: HashMap::new(),
|
||||
type_symbols: HashMap::new(),
|
||||
function_symbols: HashMap::new(),
|
||||
parameter_symbols: HashMap::new(),
|
||||
variable_symbols: HashMap::new(),
|
||||
class_member_symbols: HashMap::new(),
|
||||
debug_name,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_module_symbol_by_declared_name(&self, name: &str) -> Option<Rc<ModuleSymbol>> {
|
||||
for module_symbol in self.module_symbols.values() {
|
||||
if module_symbol.declared_name() == name {
|
||||
return Some(module_symbol.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn get_type_symbol_by_declared_name(&self, declared_name: &str) -> Option<Rc<TypeSymbol>> {
|
||||
if let Some(type_symbol) = self.type_symbols.get(declared_name) {
|
||||
Some(type_symbol.clone())
|
||||
} else {
|
||||
for use_statement_symbol in self.use_statement_symbols.values() {
|
||||
let borrowed = use_statement_symbol.borrow();
|
||||
if borrowed.declared_name() == declared_name {
|
||||
if let Some(referenced_symbol) = borrowed.referenced_symbol() {
|
||||
match *referenced_symbol {
|
||||
Symbol::Type(type_symbol) => return Some(type_symbol.clone()),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_type_symbol_by_fqn(&self, fqn: &str) -> Option<Rc<TypeSymbol>> {
|
||||
self.type_symbols
|
||||
.values()
|
||||
.find(|s| match s.deref().deref() {
|
||||
TypeSymbol::Concrete(cts) => cts.fqn() == fqn,
|
||||
_ => false,
|
||||
})
|
||||
.cloned()
|
||||
}
|
||||
|
||||
fn get_usable_symbol_by_fqn(&self, fqn: &str) -> Option<Symbol> {
|
||||
for function_symbol in self.function_symbols.values() {
|
||||
if function_symbol.borrow().fqn() == fqn {
|
||||
return Some(Symbol::Function(function_symbol.clone()));
|
||||
}
|
||||
}
|
||||
for type_symbol in self.type_symbols.values() {
|
||||
match type_symbol.deref() {
|
||||
TypeSymbol::Concrete(concrete_type_symbol) => {
|
||||
if concrete_type_symbol.fqn() == fqn {
|
||||
return Some(Symbol::Type(type_symbol.clone()));
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn get_usable_symbol_by_declared_name(&self, declared_name: &str) -> Option<Symbol> {
|
||||
for function_symbol in self.function_symbols.values() {
|
||||
if function_symbol.borrow().declared_name() == declared_name {
|
||||
return Some(Symbol::Function(function_symbol.clone()));
|
||||
}
|
||||
}
|
||||
for type_symbol in self.type_symbols.values() {
|
||||
if type_symbol.declared_name() == declared_name {
|
||||
return Some(Symbol::Type(type_symbol.clone()));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn get_value_symbol_by_declared_name(&self, declared_name: &str) -> Option<Symbol> {
|
||||
for variable_symbol in self.variable_symbols.values() {
|
||||
if variable_symbol.declared_name() == declared_name {
|
||||
return Some(Symbol::Variable(variable_symbol.clone()));
|
||||
}
|
||||
}
|
||||
for parameter_symbol in self.parameter_symbols.values() {
|
||||
if parameter_symbol.declared_name() == declared_name {
|
||||
return Some(Symbol::Parameter(parameter_symbol.clone()));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn get_expressible_by_declared_name(&self, declared_name: &str) -> Option<Symbol> {
|
||||
self.variable_symbols
|
||||
.get(declared_name)
|
||||
.map(|s| Symbol::Variable(s.clone()))
|
||||
.or_else(|| {
|
||||
self.parameter_symbols
|
||||
.get(declared_name)
|
||||
.map(|p| Symbol::Parameter(p.clone()))
|
||||
})
|
||||
.or_else(|| {
|
||||
self.class_member_symbols
|
||||
.get(declared_name)
|
||||
.map(|cms| Symbol::ClassMember(cms.clone()))
|
||||
})
|
||||
.or_else(|| {
|
||||
self.function_symbols
|
||||
.get(declared_name)
|
||||
.map(|f| Symbol::Function(f.clone()))
|
||||
})
|
||||
.or_else(|| {
|
||||
self.type_symbols
|
||||
.get(declared_name)
|
||||
.map(|t| Symbol::Type(t.clone()))
|
||||
})
|
||||
.or_else(|| {
|
||||
self.use_statement_symbols
|
||||
.get(declared_name)
|
||||
.map(|us| Symbol::UseStatement(us.clone()))
|
||||
})
|
||||
}
|
||||
|
||||
fn get_expressible_by_fqn(&self, fqn: &str) -> Option<Symbol> {
|
||||
self.function_symbols
|
||||
.values()
|
||||
.find(|fs| fs.borrow().fqn() == fqn)
|
||||
.map(|f| Symbol::Function(f.clone()))
|
||||
.or_else(|| {
|
||||
self.get_type_symbol_by_fqn(fqn)
|
||||
.map(|ts| Symbol::Type(ts.clone()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/* Symbol table */
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SymbolInsertError {
|
||||
SymbolAlreadyDefined(Symbol),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SymbolLookupError {
|
||||
NoDefinition,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SymbolTable {
|
||||
scopes: Vec<Scope>,
|
||||
current_scope_id: usize,
|
||||
}
|
||||
|
||||
/// Contains a vec of scopes, like a flattened tree
|
||||
impl SymbolTable {
|
||||
pub fn new() -> Self {
|
||||
let mut t = SymbolTable {
|
||||
scopes: vec![Scope::new(None, String::from("GlobalScope"))],
|
||||
current_scope_id: 0,
|
||||
};
|
||||
t
|
||||
}
|
||||
|
||||
pub fn current_scope_id(&self) -> usize {
|
||||
self.current_scope_id
|
||||
}
|
||||
|
||||
pub fn scopes(&self) -> &Vec<Scope> {
|
||||
&self.scopes
|
||||
}
|
||||
|
||||
pub fn push_scope(&mut self, debug_name: &str) {
|
||||
let id = self.scopes.len();
|
||||
self.scopes.push(Scope::new(
|
||||
Some(self.current_scope_id),
|
||||
debug_name.to_string(),
|
||||
));
|
||||
self.current_scope_id = id;
|
||||
}
|
||||
|
||||
pub fn pop_scope(&mut self) {
|
||||
if let Some(parent_id) = self.scopes[self.current_scope_id].parent {
|
||||
self.current_scope_id = parent_id;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_use_statement_symbol(
|
||||
&mut self,
|
||||
use_statement_symbol: UseStatementSymbol,
|
||||
) -> Result<Rc<RefCell<UseStatementSymbol>>, SymbolInsertError> {
|
||||
let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap();
|
||||
if let Some(defined_symbol) =
|
||||
current_scope.get_usable_symbol_by_declared_name(use_statement_symbol.declared_name())
|
||||
{
|
||||
Err(SymbolAlreadyDefined(defined_symbol))
|
||||
} else {
|
||||
let declared_name = use_statement_symbol.declared_name().to_string();
|
||||
let to_insert = Rc::new(RefCell::new(use_statement_symbol));
|
||||
let to_return = to_insert.clone();
|
||||
current_scope
|
||||
.use_statement_symbols
|
||||
.insert(declared_name, to_insert);
|
||||
Ok(to_return)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_module_symbol(
|
||||
&mut self,
|
||||
module_symbol: ModuleSymbol,
|
||||
) -> Result<(), SymbolInsertError> {
|
||||
let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap();
|
||||
if let Some(defined_symbol) =
|
||||
current_scope.get_module_symbol_by_declared_name(module_symbol.declared_name())
|
||||
{
|
||||
Err(SymbolAlreadyDefined(Symbol::Module(defined_symbol.clone())))
|
||||
} else {
|
||||
current_scope.module_symbols.insert(
|
||||
module_symbol.declared_name().to_string(),
|
||||
Rc::new(module_symbol),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_type_symbol(&mut self, type_symbol: TypeSymbol) -> Result<(), SymbolInsertError> {
|
||||
let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap();
|
||||
if let Some(defined_symbol) =
|
||||
current_scope.get_usable_symbol_by_declared_name(type_symbol.declared_name())
|
||||
{
|
||||
Err(SymbolAlreadyDefined(defined_symbol))
|
||||
} else {
|
||||
current_scope.type_symbols.insert(
|
||||
type_symbol.declared_name().to_string(),
|
||||
Rc::new(type_symbol),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_function_symbol(
|
||||
&mut self,
|
||||
function_symbol: FunctionSymbol,
|
||||
) -> Result<Rc<RefCell<FunctionSymbol>>, SymbolInsertError> {
|
||||
let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap();
|
||||
if let Some(defined_symbol) =
|
||||
current_scope.get_usable_symbol_by_declared_name(function_symbol.declared_name())
|
||||
{
|
||||
Err(SymbolAlreadyDefined(defined_symbol))
|
||||
} else {
|
||||
let declared_name = function_symbol.declared_name().to_string();
|
||||
let to_insert = Rc::new(RefCell::new(function_symbol));
|
||||
let to_return = to_insert.clone();
|
||||
current_scope
|
||||
.function_symbols
|
||||
.insert(declared_name, to_insert);
|
||||
Ok(to_return)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_parameter_symbol(
|
||||
&mut self,
|
||||
parameter_symbol: ParameterSymbol,
|
||||
) -> Result<Rc<ParameterSymbol>, SymbolInsertError> {
|
||||
let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap();
|
||||
if let Some(defined_symbol) =
|
||||
current_scope.get_value_symbol_by_declared_name(parameter_symbol.declared_name())
|
||||
{
|
||||
Err(SymbolAlreadyDefined(defined_symbol))
|
||||
} else {
|
||||
let to_insert = Rc::new(parameter_symbol);
|
||||
let to_return = to_insert.clone();
|
||||
current_scope
|
||||
.parameter_symbols
|
||||
.insert(to_insert.declared_name().to_string(), to_insert);
|
||||
Ok(to_return)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_variable_symbol(
|
||||
&mut self,
|
||||
variable_symbol: VariableSymbol,
|
||||
) -> Result<Rc<VariableSymbol>, SymbolInsertError> {
|
||||
let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap();
|
||||
if let Some(defined_symbol) =
|
||||
current_scope.get_value_symbol_by_declared_name(variable_symbol.declared_name())
|
||||
{
|
||||
Err(SymbolAlreadyDefined(defined_symbol))
|
||||
} else {
|
||||
let declared_name = variable_symbol.declared_name().to_string();
|
||||
let to_insert = Rc::new(variable_symbol);
|
||||
let to_return = to_insert.clone();
|
||||
current_scope
|
||||
.variable_symbols
|
||||
.insert(declared_name, to_insert);
|
||||
Ok(to_return)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_class_member_symbol(
|
||||
&mut self,
|
||||
class_member_symbol: ClassMemberSymbol,
|
||||
) -> Result<(), SymbolInsertError> {
|
||||
let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap();
|
||||
if let Some(defined_symbol) =
|
||||
current_scope.get_expressible_by_declared_name(class_member_symbol.declared_name())
|
||||
{
|
||||
Err(SymbolAlreadyDefined(defined_symbol))
|
||||
} else {
|
||||
current_scope.class_member_symbols.insert(
|
||||
class_member_symbol.declared_name().to_string(),
|
||||
Rc::new(class_member_symbol),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_type_by_declared_name(
|
||||
&self,
|
||||
declared_name: &str,
|
||||
scope_id: usize,
|
||||
) -> Result<Rc<TypeSymbol>, SymbolLookupError> {
|
||||
let mut scope_opt = Some(&self.scopes[scope_id]);
|
||||
while let Some(scope) = scope_opt {
|
||||
if let Some(symbol) = scope.get_type_symbol_by_declared_name(declared_name) {
|
||||
return Ok(symbol);
|
||||
}
|
||||
scope_opt = if let Some(parent_id) = scope.parent {
|
||||
Some(&self.scopes[parent_id])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Err(NoDefinition)
|
||||
}
|
||||
|
||||
pub fn lookup_type_by_fqn(
|
||||
&self,
|
||||
fqn: &str,
|
||||
scope_id: usize,
|
||||
) -> Result<Rc<TypeSymbol>, SymbolLookupError> {
|
||||
let mut scope_opt = Some(&self.scopes[scope_id]);
|
||||
while let Some(scope) = scope_opt {
|
||||
if let Some(symbol) = scope.get_type_symbol_by_fqn(fqn) {
|
||||
return Ok(symbol);
|
||||
}
|
||||
scope_opt = if let Some(parent_id) = scope.parent {
|
||||
Some(&self.scopes[parent_id])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Err(NoDefinition)
|
||||
}
|
||||
|
||||
pub fn lookup_usable_by_fqn(
|
||||
&self,
|
||||
fully_qualified_name: &str,
|
||||
scope_id: usize,
|
||||
) -> Result<Symbol, SymbolLookupError> {
|
||||
for scope in &self.scopes {
|
||||
if let Some(symbol) = scope.get_usable_symbol_by_fqn(fully_qualified_name) {
|
||||
return Ok(symbol);
|
||||
}
|
||||
}
|
||||
Err(NoDefinition)
|
||||
}
|
||||
|
||||
pub fn lookup_expressible_by_declared_name(
|
||||
&self,
|
||||
declared_name: &str,
|
||||
scope_id: usize,
|
||||
) -> Result<Symbol, SymbolLookupError> {
|
||||
let mut scope_opt = Some(&self.scopes[scope_id]);
|
||||
while let Some(scope) = scope_opt {
|
||||
if let Some(symbol) = scope.get_expressible_by_declared_name(declared_name) {
|
||||
return Ok(symbol);
|
||||
}
|
||||
scope_opt = if let Some(parent_id) = scope.parent {
|
||||
Some(&self.scopes[parent_id])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Err(NoDefinition)
|
||||
}
|
||||
|
||||
pub fn lookup_expressible_by_fqn(
|
||||
&self,
|
||||
fqn: &str,
|
||||
scope_id: usize,
|
||||
) -> Result<Symbol, SymbolLookupError> {
|
||||
let mut scope_opt = Some(&self.scopes[scope_id]);
|
||||
while let Some(scope) = scope_opt {
|
||||
if let Some(symbol) = scope.get_expressible_by_fqn(fqn) {
|
||||
return Ok(symbol);
|
||||
}
|
||||
scope_opt = if let Some(parent_id) = scope.parent {
|
||||
Some(&self.scopes[parent_id])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Err(NoDefinition)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SymbolTable {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
writeln!(f, "SymbolTable(current_scope = {})", self.current_scope_id)?;
|
||||
for (i, scope) in self.scopes.iter().enumerate() {
|
||||
writeln!(f, "----Scope {} {}----", i, scope.debug_name)?;
|
||||
for symbol in scope.use_statement_symbols.values() {
|
||||
writeln!(f, "{:#?}", symbol.borrow())?;
|
||||
}
|
||||
for symbol in scope.module_symbols.values() {
|
||||
writeln!(f, "{:#?}", symbol)?;
|
||||
}
|
||||
for symbol in scope.type_symbols.values() {
|
||||
writeln!(f, "{:#?}", symbol)?;
|
||||
}
|
||||
for symbol in scope.function_symbols.values() {
|
||||
writeln!(f, "{:#?}", symbol.borrow())?;
|
||||
}
|
||||
for symbol in scope.parameter_symbols.values() {
|
||||
writeln!(f, "{:#?}", symbol)?;
|
||||
}
|
||||
for symbol in scope.variable_symbols.values() {
|
||||
writeln!(f, "{:#?}", symbol)?;
|
||||
}
|
||||
for symbol in scope.class_member_symbols.values() {
|
||||
writeln!(f, "{:#?}", symbol)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user