Compare commits

..

134 Commits

Author SHA1 Message Date
Jesse Brault
5721bd1e83 WIP variety of name-analysis things. 2025-10-27 19:47:57 -05:00
Jesse Brault
8b374e1066 WIP seemingly endless name analysis refactoring. 2025-10-27 08:48:08 -05:00
Jesse Brault
93c6a71185 WIP never ending name analysis. 2025-10-24 14:33:55 -05:00
Jesse Brault
664aebfd61 WIP more name analysis. 2025-10-21 18:36:03 -05:00
Jesse Brault
bdbc2543b8 WIP name analysis, more symbol types, add symbol tree abstraction. 2025-10-21 12:41:14 -05:00
Jesse Brault
b5c0e44eeb Bunch of work on name analysis. Reintroduce Rc<RefCell<_>> for symbols. 2025-10-20 20:55:29 -05:00
Jesse Brault
273d197841 New phases for name analysis. 2025-10-20 19:20:43 -05:00
Jesse Brault
af8f0b5dac Various new ideas for name analysis. 2025-10-20 18:31:54 -05:00
Jesse Brault
d32580a1d4 Remove scope table usage. 2025-10-20 13:24:37 -05:00
Jesse Brault
65136c3a1c Fix memory bugs. 2025-10-18 22:35:14 -05:00
Jesse Brault
b52df2b452 Rework gc api. 2025-10-17 18:11:02 -05:00
Jesse Brault
434a113d97 Fix check for color of object. 2025-10-14 21:21:26 -05:00
Jesse Brault
d09d945323 Experimenting with garbage collection algorithm. 2025-10-14 21:18:06 -05:00
Jesse Brault
34fae6ccca Resolve local variables. 2025-10-14 11:14:07 -05:00
Jesse Brault
dd249dd5bd Properly using dyn for better dispatch of gather methods. 2025-10-13 11:58:30 -05:00
Jesse Brault
6b206605c1 Various gathering and scope table. 2025-10-13 10:51:22 -05:00
Jesse Brault
b47dea9136 Remove mut on compilation unit params in name analysis. 2025-10-09 17:16:18 -05:00
Jesse Brault
2b5be6ca49 Change scope_ids table to use refs. 2025-10-09 17:14:22 -05:00
Jesse Brault
f0772fbf11 Introduce scope_ids hash table. 2025-10-08 20:20:09 -05:00
Jesse Brault
5d41a22899 WIP name gather, add fields for struct nodes.
# Conflicts:
#	src/name_analysis/gather.rs
2025-10-08 20:01:09 -05:00
Jesse Brault
6d37545b35 Small clean up. 2025-10-08 19:58:20 -05:00
Jesse Brault
3ab59961dd WIP on name-analysis gather, up to statements. 2025-10-06 13:43:38 -05:00
Jesse Brault
d5ac6dfc2d WIP on name-analysis gather. 2025-10-05 12:19:59 -05:00
Jesse Brault
36e28ae4a9 Sketching out IR structs/enums. 2025-10-04 21:32:33 -05:00
Jesse Brault
9731bb38fe Sketching DVM IR. 2025-10-04 09:07:49 -05:00
Jesse Brault
8969186467 Adding more name analysis gather. 2025-10-03 10:43:20 -05:00
Jesse Brault
e879ad2d90 Fix skip rules. 2025-10-03 09:54:21 -05:00
Jesse Brault
54882b187c Refactor name-analysis SymbolTable, etc. 2025-10-03 09:52:04 -05:00
Jesse Brault
1f5d17ef79 Update ast gen yaml to better special child build; fix as_span moving issue. 2025-10-02 10:39:28 -05:00
Jesse Brault
e578250ee6 WIP name analysis. 2025-10-02 10:08:03 -05:00
Jesse Brault
583136711a Add (not working) support for Range and FileId in struct nodes. 2025-10-01 13:45:49 -05:00
Jesse Brault
eaebf8c926 Add derive for leaf enum spec, fix compilation errors. 2025-09-29 12:22:28 -05:00
Jesse Brault
058b33ece5 Fix ast_node gen errors. 2025-09-29 09:49:02 -05:00
Jesse Brault
c32ae72beb WIP redoing name analysis. 2025-09-29 09:39:13 -05:00
Jesse Brault
d6faa37515 Add missing call of f on self. 2025-09-29 09:14:45 -05:00
Jesse Brault
e8a4268949 Fix missing _. 2025-09-29 09:12:28 -05:00
Jesse Brault
5b772443f8 Add walk impl. 2025-09-29 09:10:57 -05:00
Jesse Brault
dd0bee1c91 Finish AstNode impls. 2025-09-29 08:53:19 -05:00
Jesse Brault
df8e2279dc WIP ast node ref enum and AstNode impls. 2025-09-28 15:02:20 -05:00
Jesse Brault
6e37e3a5dd WIP bringing back name analysis. 2025-09-28 12:34:37 -05:00
Jesse Brault
cfe24aa107 More sketching of functor, list, and related. 2025-09-28 12:34:29 -05:00
Jesse Brault
309149c7dd Sketching out hkts and such. 2025-09-26 17:15:50 -05:00
Jesse Brault
5b5386c7e3 Add file_id param/prop and Range props. 2025-09-25 18:43:05 -05:00
Jesse Brault
41673a68f8 Make all tests passing. 2025-09-25 12:16:55 -05:00
Jesse Brault
86331ee9b0 Resolve all compile errors. 2025-09-25 11:57:55 -05:00
Jesse Brault
4eb48cc1a2 Fixing polymorphic enum loop build fn bug. 2025-09-24 20:40:17 -05:00
Jesse Brault
3159f119bc Add pretty print for polymorphic enum loop. 2025-09-24 20:35:19 -05:00
Jesse Brault
5a3403cc28 Add polymorphic enum loop type gen. 2025-09-24 20:29:17 -05:00
Jesse Brault
39e9c2ddd5 Squishing bugs. 2025-09-24 14:23:14 -05:00
Jesse Brault
12c565d0e1 Fix missing box. 2025-09-24 14:18:49 -05:00
Jesse Brault
8a6e4277a7 Refactor PrettyPrint. 2025-09-24 14:17:17 -05:00
Jesse Brault
0d2db659ca Work on Map sketching and related. 2025-09-24 12:33:48 -05:00
Jesse Brault
5f1233a393 Sketch HashMap impl. 2025-09-23 20:04:26 -05:00
Jesse Brault
2a2936ef02 Ast gen successful, but compile errors. 2025-09-23 18:15:09 -05:00
Jesse Brault
c73bb50d6f WIP cargo build no more compile errors. 2025-09-23 18:11:53 -05:00
Jesse Brault
7a7eda97e3 Clean up other type gen. 2025-09-23 18:08:36 -05:00
Jesse Brault
2986bbe37e Refactor struct type. 2025-09-23 18:07:04 -05:00
Jesse Brault
1565abace5 Add debug for polymorphic pass through. 2025-09-23 17:35:43 -05:00
Jesse Brault
a347e69f9d Remove old enum stuff. 2025-09-23 17:34:51 -05:00
Jesse Brault
13e2ae6b0c Add polymorphic pass through. 2025-09-23 17:34:41 -05:00
Jesse Brault
4b59abc989 Update schema for ast gen. 2025-09-23 14:14:42 -05:00
Jesse Brault
e21b428e26 Various refactoring. 2025-09-23 11:03:25 -05:00
Jesse Brault
eb83d1202a Make polymorphic enum loop build fn. 2025-09-23 10:56:11 -05:00
Jesse Brault
d7b01377d7 Refactor polymorphic type build fn. 2025-09-23 10:25:52 -05:00
Jesse Brault
8aa4248e07 Make node production build fn. 2025-09-23 10:23:10 -05:00
Jesse Brault
de021789c1 Refactor production build fn. 2025-09-23 10:19:28 -05:00
Jesse Brault
f3ebcd77bd Refactor leaf enum build fn. 2025-09-23 10:17:21 -05:00
Jesse Brault
5842304f0b Refactor tree_enum deserialize and build fn. 2025-09-22 21:08:28 -05:00
Jesse Brault
2d8843b80d Move tree enum build fn. 2025-09-22 20:58:59 -05:00
Jesse Brault
1b23fbf683 Fix struct spec. 2025-09-22 20:57:45 -05:00
Jesse Brault
8143894257 Refactor build fn for leaf-struct nodes. 2025-09-22 20:57:22 -05:00
Jesse Brault
4d2e76338a Refactor build fn for struct nodes. 2025-09-22 20:56:24 -05:00
Jesse Brault
63643d86ba New deserialization code. 2025-09-22 20:19:30 -05:00
Jesse Brault
0f64fee5ef Move deserialize. 2025-09-22 08:46:52 -05:00
Jesse Brault
b5e6f1c502 Sketching rc. 2025-09-22 08:45:31 -05:00
Jesse Brault
aff2fe2a2b Moving things around and cargo fmt. 2025-09-21 11:19:27 -05:00
Jesse Brault
2176d0eb8d Add op_prec.dm. 2025-09-20 17:51:06 -05:00
Jesse Brault
dad25dcbf2 Updates to ast and schema. 2025-09-20 17:50:51 -05:00
Jesse Brault
fe2fff5882 Update grammar to reflect correct operator precedence. 2025-09-19 21:30:43 -05:00
Jesse Brault
e795664a09 Add list expressions to grammar/ast. 2025-09-19 12:57:53 -05:00
Jesse Brault
49a96eba85 Update worlds example. 2025-09-19 12:44:57 -05:00
Jesse Brault
9e3d71d73b Update d_string example. 2025-09-18 17:14:49 -05:00
Jesse Brault
5ff14f9dea Add d_string example. 2025-09-18 17:00:20 -05:00
Jesse Brault
522869371e Or else default generics. 2025-09-18 17:00:12 -05:00
Jesse Brault
11f97a2174 Auto gen pretty_print impls. 2025-09-18 08:38:30 -05:00
Jesse Brault
26cb28307c Move imports to generated build code. 2025-09-17 19:47:53 -05:00
Jesse Brault
cce927d964 Add a couple build tests. 2025-09-17 17:28:57 -05:00
Jesse Brault
7399a8748c Add polymorphic build types gen. 2025-09-17 17:21:37 -05:00
Jesse Brault
a7eabae3e3 WIP polymorphic building. 2025-09-16 10:59:38 -05:00
Jesse Brault
c94a698a52 D string expression test. 2025-09-16 10:31:38 -05:00
Jesse Brault
2dd3bf5a06 Merge remote-tracking branch 'origin/grammar-overhaul' into grammar-overhaul
# Conflicts:
#	src/parser/ast.yaml
2025-09-16 00:01:16 -05:00
Jesse Brault
fc2912edd2 Start adding ast build tests. 2025-09-16 00:00:26 -05:00
Jesse Brault
de8e2ba397 Small bugs. 2025-09-15 21:47:10 -05:00
Jesse Brault
ac9ff6ecec Squash bugs with ast gen. 2025-09-15 21:41:36 -05:00
Jesse Brault
608d89645e Add some Default impl. 2025-09-15 21:17:58 -05:00
Jesse Brault
f3c3e40eb2 Big refactor of ast gen. 2025-09-15 21:12:38 -05:00
Jesse Brault
5d640ca585 Refactor struct build-fn gen. 2025-09-15 20:10:59 -05:00
Jesse Brault
b5cdb8dd29 WIP refactor of deserialization and ast code generation. 2025-09-15 18:20:09 -05:00
Jesse Brault
2aee2cdd4e Refactor of ast yaml and schema. 2025-09-15 11:48:30 -05:00
Jesse Brault
4c2ee8f929 WIP on ast gen schema. 2025-09-14 21:19:33 -05:00
Jesse Brault
e9ccb0a5bd Add default rule match panic branch. 2025-09-14 21:15:48 -05:00
Jesse Brault
799d8762cd Fix no Pair import. 2025-09-14 21:10:55 -05:00
Jesse Brault
44f6ab10af WIP on enum generation and solving generated errors. 2025-09-14 21:06:58 -05:00
Jesse Brault
434df5642a Much work on ast gen, leaf enums and leaf structs. 2025-09-14 19:34:38 -05:00
Jesse Brault
42cc6720d1 Move around util fn and set up enum build fn mod. 2025-09-14 18:24:56 -05:00
Jesse Brault
0842690e6f More generation of node types. 2025-09-14 16:18:39 -05:00
Jesse Brault
c2c885d85b Get less errors in name_analysis module. 2025-09-14 15:59:18 -05:00
Jesse Brault
300e65a8d3 Add generation for node ast file. 2025-09-14 15:57:06 -05:00
Jesse Brault
b75e51ee41 Fill out build-fn generation for ast nodes. 2025-09-14 15:40:39 -05:00
Jesse Brault
968b950436 Handle deserialization of leaf_enum nodes. 2025-09-14 09:36:38 -05:00
Jesse Brault
0704e7d504 Skeleton code for leaf_enum specs. 2025-09-14 09:12:00 -05:00
Jesse Brault
152f5a6150 Start outputting build.rs file from ast gen. 2025-09-14 08:54:06 -05:00
Jesse Brault
e802fc70d8 Move all parser tests to generated tests. 2025-09-14 08:28:06 -05:00
Jesse Brault
c453557deb Add rerun if changed for tests parser tests directory. 2025-09-14 08:14:49 -05:00
Jesse Brault
9c43e28f32 Finish adding generated parser test generation. 2025-09-14 08:11:33 -05:00
Jesse Brault
024baf2064 Work on auto CST parser tests. 2025-09-13 18:36:18 -05:00
Jesse Brault
cde6d18e5c Add names to ast gen. 2025-09-08 14:18:20 -05:00
Jesse Brault
41693788fc Work on ast.schema and related. 2025-09-08 11:57:20 -05:00
Jesse Brault
29a2d77b6f Fix backtick strings. 2025-09-08 11:06:10 -05:00
Jesse Brault
5c01589ca3 Work on literals and numbers. 2025-09-07 19:06:13 -05:00
Jesse Brault
6652b9fc63 Work on closures. 2025-09-07 18:42:25 -05:00
Jesse Brault
bae90b8b80 Fix calls. 2025-09-07 18:37:49 -05:00
Jesse Brault
4bc89d5ca3 Fix if statements. 2025-09-07 18:25:30 -05:00
Jesse Brault
3f3df59761 Work on expressions. 2025-09-07 16:37:52 -05:00
Jesse Brault
3f534bf7fd Refine and add statements. 2025-09-07 15:56:34 -05:00
Jesse Brault
17f5d2d62d Add function and class constructs to ast.yaml. 2025-09-07 15:09:00 -05:00
Jesse Brault
59165f6235 Build fn child unwrap; some tests. 2025-09-04 19:27:26 -05:00
Jesse Brault
4d70765d17 Wrap in Boxes. 2025-09-04 11:10:59 -05:00
Jesse Brault
0a97cc01b9 Add return value to build fn. 2025-09-04 11:08:23 -05:00
Jesse Brault
0adb4bbe0e Add or else (default) to yaml spec. 2025-09-04 10:21:30 -05:00
Jesse Brault
9f3f3e0f0d Implement optionality for types. 2025-09-03 17:12:19 -05:00
Jesse Brault
3b07cef209 Basic function types. 2025-09-03 17:05:15 -05:00
Jesse Brault
a53388155a Add optional to ast schema and ast-gen. 2025-09-03 16:45:09 -05:00
Jesse Brault
4dcb5ee783 Pest and ast syntax changes. 2025-09-03 16:39:31 -05:00
136 changed files with 10936 additions and 2708 deletions

12
Cargo.lock generated
View File

@ -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",

View File

@ -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"]

View File

@ -0,0 +1,87 @@
use crate::spec::tree_enum_spec::{EnumRuleChildKind, TreeEnumBuildSpec};
use convert_case::{Case, Casing};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
pub fn make_enum_ast_node_impl(enum_spec: &TreeEnumBuildSpec) -> TokenStream {
let type_ident = format_ident!("{}", enum_spec.build());
let match_arms = enum_spec
.rules()
.map(|rule| {
let rule_ident = format_ident!("{}", rule.rule());
match rule.child() {
Some(child) => match child.kind() {
EnumRuleChildKind::Node(node_child) => {
let child_ident =
format_ident!("{}", node_child.node_kind().to_case(Case::Snake));
quote! {
#type_ident::#rule_ident(#child_ident) => vec![
#child_ident
]
}
}
_ => quote! {
#type_ident::#rule_ident(_) => vec![]
},
},
None => {
quote! {
#type_ident::#rule_ident => vec![]
}
}
}
})
.collect::<Vec<_>>();
let mut_match_arms = enum_spec
.rules()
.map(|rule| {
let rule_ident = format_ident!("{}", rule.rule());
match rule.child() {
Some(child) => match child.kind() {
EnumRuleChildKind::Node(node_child) => {
let child_ident =
format_ident!("{}", node_child.node_kind().to_case(Case::Snake));
quote! {
#type_ident::#rule_ident(#child_ident) => {
f(#child_ident as &'a mut dyn AstNode<'a>)
}
}
}
_ => quote! {
#type_ident::#rule_ident(_) => {}
},
},
None => {
quote! {
#type_ident::#rule_ident => {}
}
}
}
})
.collect::<Vec<_>>();
quote! {
impl<'a> AstNode<'a> for #type_ident {
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>> {
match self {
#(#match_arms,)*
}
}
fn for_each_child_mut(&'a mut self, mut f: &mut dyn FnMut(&'a mut dyn AstNode<'a>)) {
match self {
#(#mut_match_arms,)*
}
}
fn as_node_ref(&'a self) -> AstNodeRef<'a> {
AstNodeRef::#type_ident(&self)
}
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a> {
AstNodeRefMut::#type_ident(self)
}
}
}
}

View File

@ -0,0 +1,24 @@
use crate::spec::leaf_enum_spec::LeafEnumBuildSpec;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
pub fn make_leaf_enum_ast_node_impl(spec: &LeafEnumBuildSpec) -> TokenStream {
let type_ident = format_ident!("{}", spec.build());
quote! {
impl<'a> AstNode<'a> for #type_ident {
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>> {
vec![]
}
fn for_each_child_mut(&'a mut self, mut f: &mut dyn FnMut(&'a mut dyn AstNode<'a>)) {}
fn as_node_ref(&'a self) -> AstNodeRef<'a> {
AstNodeRef::#type_ident(&self)
}
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a> {
AstNodeRefMut::#type_ident(self)
}
}
}
}

View File

@ -0,0 +1,24 @@
use crate::spec::leaf_struct_spec::LeafStructBuildSpec;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
pub fn make_leaf_struct_ast_node_impl(spec: &LeafStructBuildSpec) -> TokenStream {
let type_ident = format_ident!("{}", spec.build());
quote! {
impl<'a> AstNode<'a> for #type_ident {
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>> {
vec![]
}
fn for_each_child_mut(&'a mut self, mut f: &mut dyn FnMut(&'a mut dyn AstNode<'a>)) {}
fn as_node_ref(&'a self) -> AstNodeRef<'a> {
AstNodeRef::#type_ident(&self)
}
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a> {
AstNodeRefMut::#type_ident(self)
}
}
}
}

View File

@ -0,0 +1,84 @@
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_type_ident(build_spec: &BuildSpec) -> Option<Ident> {
match build_spec {
BuildSpec::Struct(struct_spec) => Some(format_ident!("{}", struct_spec.build())),
BuildSpec::LeafStruct(leaf_struct) => Some(format_ident!("{}", leaf_struct.build())),
BuildSpec::Enum(enum_spec) => Some(format_ident!("{}", enum_spec.build())),
BuildSpec::LeafEnum(leaf_enum) => Some(format_ident!("{}", leaf_enum.build())),
BuildSpec::PolymorphicType(polymorphic_type) => {
Some(format_ident!("{}", polymorphic_type.name()))
}
BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop) => {
Some(format_ident!("{}", polymorphic_enum_loop.name()))
}
BuildSpec::PolymorphicPassThrough(_) => None,
BuildSpec::Production(_) => None,
BuildSpec::NodeProduction(_) => None,
}
}
pub fn make_ast_enum_member(build_spec: &BuildSpec) -> Option<TokenStream> {
make_type_ident(build_spec).map(|type_ident| {
quote! {
#type_ident(&'a #type_ident)
}
})
}
pub fn make_ast_enum_mut_member(build_spec: &BuildSpec) -> Option<TokenStream> {
make_type_ident(build_spec).map(|type_ident| {
quote! {
#type_ident(&'a mut #type_ident)
}
})
}
pub fn make_ast_node_ref_unwrapper(build_spec: &BuildSpec) -> Option<TokenStream> {
make_type_ident(build_spec).map(|type_ident| {
quote! {
AstNodeRef::#type_ident(inner) => *inner
}
})
}
pub fn make_ast_node_ref_mut_unwrapper(build_spec: &BuildSpec) -> Option<TokenStream> {
make_type_ident(build_spec).map(|type_ident| {
quote! {
AstNodeRefMut::#type_ident(inner) => *inner
}
})
}

View File

@ -0,0 +1,57 @@
use crate::spec::polymorphic_enum_loop_spec::{
PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuildChild,
};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
pub fn make_polymorphic_enum_loop_ast_node_impl(
spec: &PolymorphicEnumLoopBuildSpec,
) -> TokenStream {
let type_ident = format_ident!("{}", spec.name());
let build_rule = spec
.rules()
.filter_map(|rule| match rule {
PolymorphicEnumLoopRule::PassThrough(_) => None,
PolymorphicEnumLoopRule::Build(build_rule) => Some(build_rule),
})
.next()
.unwrap();
let child_adders = build_rule
.children()
.map(|child| {
let child_ident = match child {
PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => {
format_ident!("{}", use_current.name())
}
PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => {
format_ident!("{}", on_each.name())
}
};
quote! {
children.push(self.#child_ident() as &dyn AstNode);
}
})
.collect::<Vec<_>>();
quote! {
impl<'a> AstNode<'a> for #type_ident {
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>> {
let mut children = vec![];
#(#child_adders;)*
children
}
fn for_each_child_mut(&'a mut self, mut f: &mut dyn FnMut(&'a mut dyn AstNode<'a>)) {
todo!()
}
fn as_node_ref(&'a self) -> AstNodeRef<'a> {
AstNodeRef::#type_ident(&self)
}
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a> {
AstNodeRefMut::#type_ident(self)
}
}
}
}

View File

@ -0,0 +1,55 @@
use crate::spec::polymorphic_type_spec::PolymorphicTypeBuildSpec;
use convert_case::{Case, Casing};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
pub fn make_polymorphic_type_ast_node_impl(spec: &PolymorphicTypeBuildSpec) -> TokenStream {
let type_ident = format_ident!("{}", spec.name());
let match_arms = spec
.variants()
.map(|variant| {
let variant_ident = format_ident!("{}", variant.name());
let child_ident = format_ident!("{}", variant.inner_kind().to_case(Case::Snake));
quote! {
#type_ident::#variant_ident(#child_ident) => vec![#child_ident]
}
})
.collect::<Vec<_>>();
let match_arms_mut = spec
.variants()
.map(|variant| {
let variant_ident = format_ident!("{}", variant.name());
let child_ident = format_ident!("{}", variant.inner_kind().to_case(Case::Snake));
quote! {
#type_ident::#variant_ident(#child_ident) => {
f(#child_ident as &'a mut dyn AstNode)
}
}
})
.collect::<Vec<_>>();
quote! {
impl<'a> AstNode<'a> for #type_ident {
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>> {
match self {
#(#match_arms,)*
}
}
fn for_each_child_mut(&'a mut self, mut f: &mut dyn FnMut(&'a mut dyn AstNode<'a>)) {
match self {
#(#match_arms_mut,)*
}
}
fn as_node_ref(&'a self) -> AstNodeRef<'a> {
AstNodeRef::#type_ident(&self)
}
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a> {
AstNodeRefMut::#type_ident(self)
}
}
}
}

View File

@ -0,0 +1,86 @@
use crate::spec::struct_spec::{MemberChildBuild, StructChild, StructSpec, VecChildBuild};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
fn make_child_adders(spec: &StructSpec, is_mut: bool) -> Vec<TokenStream> {
let as_clause = if is_mut {
quote! { as &mut dyn AstNode }
} else {
quote! { as &dyn AstNode }
};
spec.children()
.map(|child| match child {
StructChild::SkipChild(_) => None,
StructChild::VecChild(vec_child) => match vec_child.build() {
VecChildBuild::String(_) => None,
VecChildBuild::Node(_) => {
let child_ident = if is_mut {
format_ident!("{}_mut", vec_child.name())
} else {
format_ident!("{}", vec_child.name())
};
let children_stream = quote! {
for child in self.#child_ident() {
children.push(child #as_clause);
}
};
Some(children_stream)
}
},
StructChild::MemberChild(member_child) => match member_child.build() {
MemberChildBuild::Node(_) => {
let child_ident = if is_mut {
format_ident!("{}_mut", member_child.name())
} else {
format_ident!("{}", member_child.name())
};
if member_child.optional() {
Some(quote! {
if let Some(#child_ident) = self.#child_ident() {
children.push(#child_ident #as_clause);
}
})
} else {
Some(quote! {
children.push(self.#child_ident() #as_clause)
})
}
}
MemberChildBuild::Boolean(_) => None,
},
StructChild::Special(_) => None,
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>()
}
pub fn make_struct_ast_node_impl(spec: &StructSpec) -> TokenStream {
let type_ident = format_ident!("{}", spec.build());
let child_adders = make_child_adders(spec, false);
let child_adders_mut = make_child_adders(spec, true);
quote! {
impl<'a> AstNode<'a> for #type_ident {
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>> {
let mut children = vec![];
#(#child_adders;)*
children
}
fn for_each_child_mut(&'a mut self, mut f: &mut dyn FnMut(&'a mut dyn AstNode<'a>)) {
todo!()
}
fn as_node_ref(&'a self) -> AstNodeRef<'a> {
AstNodeRef::#type_ident(&self)
}
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a> {
AstNodeRefMut::#type_ident(self)
}
}
}
}

View 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!()
}
}
}
}

View 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,)*
)
}
}
}

View 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)
}
}
}

View 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)
}
}
}

View 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()
}
}
}

View File

@ -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!(),
}
}
}
}

View 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)
}
}
}

View 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
}
}
}

View File

@ -0,0 +1,274 @@
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, pair_ident: &Ident) -> Option<TokenStream> {
match child_spec {
StructChild::SkipChild(_) => None,
StructChild::VecChild(vec_child) => Some(make_vec_child_arg(vec_child)),
StructChild::MemberChild(member_child) => match member_child.build() {
MemberChildBuild::Node(node_member_build) => Some(make_node_member_child_arg(
member_child.name(),
member_child.optional(),
node_member_build,
)),
MemberChildBuild::Boolean(_) => {
Some(make_boolean_member_child_arg(member_child.name()))
},
},
StructChild::Special(special_child) => Some(make_special_child_arg(special_child)),
}
}
fn make_return_value_stream(build_spec: &StructSpec, pair_ident: &Ident) -> TokenStream {
let type_ident = format_ident!("{}", build_spec.build());
let child_args = build_spec
.children()
.map(|child| make_child_arg(child, pair_ident))
.filter(|child_arg| child_arg.is_some())
.map(|child_arg| child_arg.unwrap())
.collect::<Vec<_>>();
quote! {
#type_ident::new(
#(#child_args,)*
)
}
}
pub fn make_struct_build_fn(build_spec: &StructSpec) -> TokenStream {
let build_fn_ident = format_ident!("{}", make_build_fn_name(build_spec.build()));
let pair_ident = format_ident!("{}", make_build_pair(build_spec.build()));
let return_type_ident = format_ident!("{}", build_spec.build());
let preamble = make_preamble(build_spec, &pair_ident);
let special_children = make_special_children(build_spec);
let child_holders = build_spec
.children()
.map(|child_spec| make_child_holder(child_spec))
.filter(|child_holder| child_holder.is_some())
.map(|child_holder| child_holder.unwrap())
.collect::<Vec<_>>();
let rule_matchers = build_spec
.children()
.map(|child_spec| make_rule_matcher(child_spec))
.collect::<Vec<_>>();
let iter_stream = quote! {
for inner_pair in #pair_ident.into_inner() {
match inner_pair.as_rule() {
#(#rule_matchers)*
_ => panic!("Unexpected rule: {:?}", inner_pair.as_rule())
}
}
};
let new_stream = make_return_value_stream(build_spec, &pair_ident);
quote! {
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
#preamble
#(#special_children;)*
#(#child_holders;)*
#iter_stream
#new_stream
}
}
}

View 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!()
}
}
}
}

View File

@ -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
}
}
}

View File

@ -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()
}

View 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)
}

View 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)
}

View 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()
}

View 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)
}

View 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)
}

View File

@ -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)
}

View 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)
}

View 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)
}

View File

@ -0,0 +1,198 @@
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_field(field_yaml: &Yaml) -> StructField {
let (name, props) = unwrap_single_member_hash(field_yaml);
let kind = props["kind"].as_str().unwrap();
let wrap = if let Some(wrap) = props["wrap"].as_str() {
match wrap {
"rc_ref_cell" => Some(StructFieldWrap::RcRefCell),
_ => panic!(),
}
} else {
None
};
let vec = get_as_bool(&props["vec"]);
StructField::new(&name, kind, wrap, vec)
}
fn deserialize_skip_child(props: &Yaml) -> StructChild {
let rule = props["rule"].as_str().unwrap();
StructChild::SkipChild(SkipChild::new(rule))
}
fn deserialize_vec_child(child_name: &str, props: &Yaml) -> StructChild {
let rule = props["rule"].as_str().unwrap();
let kind = props["kind"].as_str().unwrap_or(rule);
if kind == "string" {
let build = VecChildBuild::String(VecChildStringBuild::new(&make_build_fn_name(rule)));
StructChild::VecChild(VecChild::new(child_name, rule, Box::new(build)))
} else {
let build = VecChildBuild::Node(VecChildNodeBuild::new(rule, &make_build_fn_name(rule)));
StructChild::VecChild(VecChild::new(child_name, rule, Box::new(build)))
}
}
fn deserialize_member_build(child_name: &str, rule: &str, props: &Yaml) -> MemberChildBuild {
if props["node"].is_hash() {
let node_props = &props["node"];
let kind = node_props["kind"].as_str().unwrap_or(rule);
let with = node_props["with"]
.as_str()
.map(|with| make_build_fn_name(with))
.unwrap_or_else(|| make_build_fn_name(kind));
let or_else = if get_as_bool(&node_props["or_else_default"]) {
Some(String::from("default"))
} else if let Some(or_else) = node_props["or_else"].as_str() {
Some(or_else.to_string())
} else {
None
};
MemberChildBuild::Node(NodeMemberBuild::new(kind, &with, or_else))
} else if props["boolean"].is_hash() {
let boolean_props = &props["boolean"];
if let Some(on) = boolean_props["on"].as_str() {
if on == "rule_present" {
MemberChildBuild::Boolean(BooleanMemberBuild::new(
BooleanMemberBuildOn::RulePresent,
))
} else {
panic!(
"Expected 'on' in 'boolean' in 'build' in {} to be 'rule_present'",
child_name
);
}
} else {
panic!("Expected 'on' in 'boolean' in 'build' in {}", child_name);
}
} else {
panic!(
"Expected one of 'node', 'boolean', or 'special' in 'build' in {}",
child_name
);
}
}
fn deserialize_member_child(child_name: &str, props: &Yaml) -> StructChild {
let rule = props["rule"]
.as_str()
.map(ToString::to_string)
.unwrap_or_else(|| child_name.to_case(Case::Pascal));
let optional = get_as_bool(&props["optional"]);
if props["build"].is_hash() {
let build = deserialize_member_build(child_name, &rule, &props["build"]);
StructChild::MemberChild(MemberChild::new(
child_name,
&rule,
optional,
Box::new(build),
))
} else {
StructChild::MemberChild(MemberChild::new(
child_name,
&rule,
optional,
Box::new(MemberChildBuild::Node(NodeMemberBuild::new(
&rule,
&make_build_fn_name(&rule),
None,
))),
))
}
}
fn deserialize_special_child(name: &str, props: &Yaml) -> StructChild {
match props["kind"].as_str().unwrap() {
"file_id" => StructChild::Special(SpecialChild::new(name, SpecialChildKind::FileId)),
"range" => StructChild::Special(SpecialChild::new(name, SpecialChildKind::Range)),
_ => panic!(
"Invalid special child kind {} in {}",
props["kind"].as_str().unwrap(),
name
),
}
}
fn deserialize_hash_child(name: &str, props: &Yaml) -> StructChild {
if props["skip"].is_hash() {
deserialize_skip_child(&props["skip"])
} else if props["vec"].is_hash() {
deserialize_vec_child(name, &props["vec"])
} else if props["member"].is_hash() {
deserialize_member_child(name, &props["member"])
} else if props["special"].is_hash() {
deserialize_special_child(name, &props["special"])
} else {
panic!("Expected 'skip' or 'vec' in 'member' in {}", name);
}
}
fn deserialize_string_child(name: &str) -> StructChild {
let name_pascal = name.to_case(Case::Pascal);
StructChild::MemberChild(MemberChild::new(
name,
&name_pascal,
false,
Box::new(MemberChildBuild::Node(NodeMemberBuild::new(
&name_pascal,
&make_build_fn_name(name),
None,
))),
))
}
fn deserialize_children(children_yaml: &Yaml) -> Vec<Box<StructChild>> {
children_yaml
.as_vec()
.unwrap()
.iter()
.map(|child_yaml| {
if let Some(name) = child_yaml.as_str() {
deserialize_string_child(name)
} else {
let (child_name, child_props) = unwrap_single_member_hash(child_yaml);
deserialize_hash_child(&child_name, child_props)
}
})
.map(Box::new)
.collect()
}
pub fn deserialize_struct_spec(name: &str, struct_yaml: &Yaml) -> StructSpec {
let children = if struct_yaml["children"].is_array() {
deserialize_children(&struct_yaml["children"])
} else {
deserialize_error!("array", "children", name);
};
let fields = if struct_yaml["fields"].is_array() {
struct_yaml["fields"]
.as_vec()
.unwrap()
.iter()
.map(|field| deserialize_field(field))
.map(Box::new)
.collect()
} else {
vec![]
};
let derive = if struct_yaml["derive"].is_array() {
struct_yaml["derive"]
.as_vec()
.unwrap()
.iter()
.map(|derive| derive.as_str().unwrap().to_string())
.collect()
} else {
vec![]
};
StructSpec::new(name, children, fields, derive)
}

View 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)
}

View 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)
}

View File

@ -1,10 +1,20 @@
mod build_fn_gen;
mod ast_node;
mod build_fn;
mod deserialize;
mod pretty_print;
mod spec;
mod type_gen;
mod walk;
use crate::build_fn_gen::make_struct_build_fn;
use crate::ast_node::{
make_ast_enum_member, make_ast_enum_mut_member, make_ast_node_impl,
make_ast_node_ref_mut_unwrapper, make_ast_node_ref_unwrapper,
};
use crate::build_fn::make_build_fn;
use crate::deserialize::deserialize_yaml_spec;
use crate::pretty_print::make_pretty_print_impl;
use crate::type_gen::make_type;
use crate::walk::make_walk_fn;
use proc_macro2::TokenStream;
use quote::quote;
use spec::BuildSpec;
@ -14,10 +24,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 +71,191 @@ fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) {
println!("{}", prettyplease::unparse(&parsed));
}
pub fn test_dump() -> String {
let build_specs = deserialize::deserialize_yaml_spec(include_str!("../../src/parser/ast.yaml"));
let mut streams: Vec<TokenStream> = vec![];
pub struct AstGeneratedFile {
pub name: String,
pub contents: String,
}
for build_spec in &build_specs {
let type_stream = make_type(build_spec);
debug_built_spec(build_spec, &type_stream);
streams.push(type_stream);
}
for build_spec in &build_specs {
match build_spec {
BuildSpec::Enum(_) => {}
BuildSpec::Struct(struct_spec) => {
let struct_build_fn_stream = make_struct_build_fn(struct_spec);
debug_built_spec(build_spec, &struct_build_fn_stream);
streams.push(struct_build_fn_stream);
}
}
}
let combined = quote! {
#(#streams)*
};
let file: File = syn::parse2(combined).unwrap();
fn token_stream_to_string(token_stream: TokenStream) -> String {
let file: File = syn::parse2(token_stream).unwrap();
prettyplease::unparse(&file)
}
fn generate_build_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
let build_fns = build_specs
.iter()
.map(|build_spec| {
let build_fn = make_build_fn(build_spec);
debug_built_spec(build_spec, &build_fn);
build_fn
})
.collect::<Vec<_>>();
let combined = quote! {
use crate::parser::Rule;
use pest::iterators::Pair;
use crate::ast::node::*;
use std::range::Range;
#(#build_fns)*
};
AstGeneratedFile {
name: String::from("build.rs"),
contents: token_stream_to_string(combined),
}
}
fn generate_node_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
let types = build_specs
.iter()
.map(|build_spec| make_type(build_spec))
.filter(Option::is_some)
.collect::<Vec<_>>();
let combined = quote! {
use std::range::Range;
use std::rc::Rc;
use std::cell::RefCell;
use crate::name_analysis::symbol::type_symbol::*;
use crate::name_analysis::symbol::use_symbol::*;
#(#types)*
};
AstGeneratedFile {
name: String::from("node.rs"),
contents: token_stream_to_string(combined),
}
}
fn generate_pretty_print_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
let impls = build_specs
.iter()
.map(|build_spec| {
let maybe_stream = make_pretty_print_impl(build_spec);
if let Some(stream) = &maybe_stream {
debug_built_spec(build_spec, &stream);
}
maybe_stream
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let combined = quote! {
use crate::ast::node::*;
#(#impls)*
};
AstGeneratedFile {
name: String::from("pretty_print.rs"),
contents: token_stream_to_string(combined),
}
}
fn generate_ast_node_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
let impls = build_specs
.iter()
.map(|build_spec| {
let maybe_stream = make_ast_node_impl(build_spec);
if let Some(stream) = &maybe_stream {
debug_built_spec(build_spec, &stream);
}
maybe_stream
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let ast_enum_members = build_specs
.iter()
.map(|build_spec| make_ast_enum_member(build_spec))
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let ast_enum_mut_members = build_specs
.iter()
.map(|build_spec| make_ast_enum_mut_member(build_spec))
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let inner_unwrappers = build_specs
.iter()
.map(|build_spec| make_ast_node_ref_unwrapper(build_spec))
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let inner_mut_unwrappers = build_specs
.iter()
.map(|build_spec| make_ast_node_ref_mut_unwrapper(build_spec))
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let combined = quote! {
use crate::ast::node::*;
pub enum AstNodeRef<'a> {
#(#ast_enum_members,)*
}
impl<'a> AstNodeRef<'a> {
pub fn inner(&self) -> &dyn AstNode<'a> {
match self {
#(#inner_unwrappers,)*
}
}
}
pub enum AstNodeRefMut<'a> {
#(#ast_enum_mut_members,)*
}
impl<'a> AstNodeRefMut<'a> {
pub fn inner(&mut self) -> &mut dyn AstNode<'a> {
match self {
#(#inner_mut_unwrappers,)*
}
}
}
pub trait AstNode<'a> {
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>>;
fn for_each_child_mut(&'a mut self, f: &mut dyn FnMut(&'a mut dyn AstNode<'a>));
fn as_node_ref(&'a self) -> AstNodeRef<'a>;
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a>;
}
#(#impls)*
};
AstGeneratedFile {
name: String::from("ast_node.rs"),
contents: token_stream_to_string(combined),
}
}
fn generate_walk_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
let stream = make_walk_fn(build_specs);
AstGeneratedFile {
name: String::from("walk.rs"),
contents: token_stream_to_string(stream),
}
}
pub fn get_build_specs(yaml: &str) -> Vec<BuildSpec> {
deserialize_yaml_spec(yaml)
}
pub fn generate_files(build_specs: &[BuildSpec]) -> Vec<AstGeneratedFile> {
vec![
generate_build_file(build_specs),
generate_node_file(build_specs),
generate_pretty_print_file(build_specs),
generate_ast_node_file(build_specs),
generate_walk_file(build_specs),
]
}

View File

@ -1,6 +0,0 @@
use ast_generator::test_dump;
fn main() {
let s = test_dump();
println!("{}", s);
}

View 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))
}
}
}

View File

@ -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,
}

View 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)
}
}

View 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
}

View 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)
}

View 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
}
}

View 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
}
}

View 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 },
}

View 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
}
}

View 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,
}

View File

@ -0,0 +1,317 @@
pub struct StructSpec {
build: String,
children: Vec<Box<StructChild>>,
fields: Vec<Box<StructField>>,
derive: Vec<String>,
}
impl StructSpec {
pub fn new(
build: &str,
children: Vec<Box<StructChild>>,
fields: Vec<Box<StructField>>,
derive: Vec<String>,
) -> Self {
Self {
build: build.to_string(),
children,
fields,
derive,
}
}
/// The type to be built, in Pascal case.
pub fn build(&self) -> &str {
&self.build
}
/// The children for this build spec.
pub fn children(&self) -> impl Iterator<Item = &StructChild> {
self.children.iter().map(Box::as_ref)
}
pub fn fields(&self) -> impl Iterator<Item = &StructField> {
self.fields.iter().map(Box::as_ref)
}
pub fn derive(&self) -> &[String] {
&self.derive
}
}
pub enum StructChild {
SkipChild(SkipChild),
VecChild(VecChild),
MemberChild(MemberChild),
Special(SpecialChild),
}
impl StructChild {
pub fn is_special(&self) -> bool {
match self {
StructChild::Special(_) => true,
_ => false,
}
}
pub fn unwrap_special(&self) -> Option<&SpecialChild> {
match self {
StructChild::Special(special_child) => Some(special_child),
_ => None,
}
}
}
pub struct SkipChild {
rule: String,
}
impl SkipChild {
pub fn new(rule: &str) -> Self {
Self {
rule: rule.to_string(),
}
}
/// The grammar rule to match.
pub fn rule(&self) -> &str {
&self.rule
}
}
pub struct VecChild {
name: String,
rule: String,
build: Box<VecChildBuild>,
}
impl VecChild {
pub fn new(name: &str, rule: &str, build: Box<VecChildBuild>) -> Self {
Self {
name: name.to_string(),
rule: rule.to_string(),
build,
}
}
/// The name of this child.
pub fn name(&self) -> &str {
&self.name
}
/// The rule to match to build this child.
pub fn rule(&self) -> &str {
&self.rule
}
pub fn build(&self) -> &VecChildBuild {
&self.build
}
}
pub enum VecChildBuild {
String(VecChildStringBuild),
Node(VecChildNodeBuild),
}
pub struct VecChildStringBuild {
with: String,
}
impl VecChildStringBuild {
pub fn new(with: &str) -> Self {
Self {
with: with.to_string(),
}
}
pub fn with(&self) -> &str {
&self.with
}
}
pub struct VecChildNodeBuild {
kind: String,
with: String,
}
impl VecChildNodeBuild {
pub fn new(kind: &str, with: &str) -> Self {
Self {
kind: kind.to_string(),
with: with.to_string(),
}
}
pub fn kind(&self) -> &str {
&self.kind
}
pub fn with(&self) -> &str {
&self.with
}
}
#[derive(Debug)]
pub struct MemberChild {
name: String,
rule: String,
optional: bool,
build: Box<MemberChildBuild>,
}
impl MemberChild {
pub fn new(name: &str, rule: &str, optional: bool, build: Box<MemberChildBuild>) -> Self {
Self {
name: name.to_string(),
rule: rule.to_string(),
optional,
build,
}
}
/// The name of this child in the yaml file, in snake case.
pub fn name(&self) -> &str {
&self.name
}
/// The grammar rule to match to build this child.
pub fn rule(&self) -> &str {
&self.rule
}
pub fn optional(&self) -> bool {
self.optional
}
/// The specification for what to actually build.
pub fn build(&self) -> &MemberChildBuild {
&self.build
}
}
#[derive(Debug)]
pub enum MemberChildBuild {
Node(NodeMemberBuild),
Boolean(BooleanMemberBuild),
}
#[derive(Debug)]
pub struct NodeMemberBuild {
kind: String,
with: String,
or_else: Option<String>,
}
impl NodeMemberBuild {
pub fn new(kind: &str, with: &str, or_else: Option<String>) -> Self {
Self {
kind: kind.to_string(),
with: with.to_string(),
or_else,
}
}
/// The type to build, in Pascal case.
pub fn kind(&self) -> &str {
&self.kind
}
pub fn with(&self) -> &str {
&self.with
}
/// The default fn to call when unwrapping the child (before passing as arg to new).
pub fn or_else(&self) -> Option<&str> {
self.or_else.as_deref()
}
}
#[derive(Debug)]
pub struct BooleanMemberBuild {
on: BooleanMemberBuildOn,
}
impl BooleanMemberBuild {
pub fn new(on: BooleanMemberBuildOn) -> Self {
Self { on }
}
pub fn on(&self) -> &BooleanMemberBuildOn {
&self.on
}
}
#[derive(Debug)]
pub enum BooleanMemberBuildOn {
RulePresent,
}
#[derive(Debug)]
pub struct SpecialChild {
name: String,
kind: SpecialChildKind,
}
impl SpecialChild {
pub fn new(name: &str, kind: SpecialChildKind) -> Self {
Self {
name: name.to_string(),
kind,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &SpecialChildKind {
&self.kind
}
}
#[derive(Debug)]
pub enum SpecialChildKind {
FileId,
Range,
}
#[derive(Debug)]
pub struct StructField {
name: String,
kind: String,
wrap: Option<StructFieldWrap>,
vec: bool,
}
impl StructField {
pub fn new(name: &str, kind: &str, wrap: Option<StructFieldWrap>, vec: bool) -> Self {
Self {
name: name.to_string(),
kind: kind.to_string(),
wrap,
vec,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &str {
&self.kind
}
pub fn wrap(&self) -> Option<&StructFieldWrap> {
self.wrap.as_ref()
}
pub fn vec(&self) -> bool {
self.vec
}
}
#[derive(Debug)]
pub enum StructFieldWrap {
RcRefCell,
}

View 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
}
}

View File

@ -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),
}
}

View 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),*
}
}
}

View 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),*
}
}
}

View 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)*
}
}
}

View 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,
}
}

View 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)*
}
}
}

View 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),*
}
}
}

View File

@ -0,0 +1,339 @@
use crate::spec::struct_spec::{
MemberChild, MemberChildBuild, SpecialChild, SpecialChildKind, StructChild, StructField,
StructFieldWrap, StructSpec, VecChild, VecChildBuild,
};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
fn make_field_accessors(field: &StructField) -> TokenStream {
let field_ident = format_ident!("{}", field.name());
let field_type = {
let inner = format_ident!("{}", field.kind());
if let Some(wrap) = field.wrap() {
match wrap {
StructFieldWrap::RcRefCell => {
quote! { Rc<RefCell<#inner>> }
}
}
} else {
quote! { #inner }
}
};
let field_ident_mut = format_ident!("{}_mut", field.name());
if field.vec() {
quote! {
pub fn #field_ident(&self) -> &[#field_type] {
self.#field_ident.as_slice()
}
pub fn #field_ident_mut(&mut self) -> &mut Vec<#field_type> {
&mut self.#field_ident
}
}
} else {
let set_field_ident = format_ident!("set_{}", field.name());
quote! {
pub fn #field_ident(&self) -> Option<&#field_type> {
self.#field_ident.as_ref()
}
pub fn #field_ident_mut(&mut self) -> Option<&mut #field_type> {
self.#field_ident.as_mut()
}
pub fn #set_field_ident(&mut self, #field_ident: #field_type) {
self.#field_ident = Some(#field_ident);
}
}
}
}
fn make_field_initializer(field: &StructField) -> TokenStream {
let field_ident = format_ident!("{}", field.name());
if field.vec() {
quote! { #field_ident: vec![] }
} else {
quote! { #field_ident: None }
}
}
fn make_annotated_field(field: &StructField) -> TokenStream {
let field_ident = format_ident!("{}", field.name());
let field_type = if let Some(wrap) = field.wrap() {
let inner = format_ident!("{}", field.kind());
match wrap {
StructFieldWrap::RcRefCell => {
quote! { Rc<RefCell<#inner>> }
}
}
} else {
let inner = format_ident!("{}", field.kind());
quote! { #inner }
};
if field.vec() {
quote! {
#field_ident: Vec<#field_type>
}
} else {
quote! {
#field_ident: Option<#field_type>
}
}
}
fn make_vec_child_accessors(vec_child: &VecChild) -> TokenStream {
let child_ident = format_ident!("{}", vec_child.name());
match vec_child.build() {
VecChildBuild::String(_) => {
quote! {
pub fn #child_ident(&self) -> impl Iterator<Item = &str> {
self.#child_ident.iter().map(String::as_str)
}
}
}
VecChildBuild::Node(vec_child_node_build) => {
let child_type_ident = format_ident!("{}", vec_child_node_build.kind());
let child_ident_mut = format_ident!("{}_mut", vec_child.name());
quote! {
pub fn #child_ident(&self) -> impl Iterator<Item = &#child_type_ident> {
self.#child_ident.iter().map(Box::as_ref)
}
pub fn #child_ident_mut(&mut self) -> impl Iterator<Item = &mut #child_type_ident> {
self.#child_ident.iter_mut().map(Box::as_mut)
}
}
}
}
}
fn make_member_child_accessors(member_child: &MemberChild) -> TokenStream {
let child_ident = format_ident!("{}", member_child.name());
match member_child.build() {
MemberChildBuild::Node(node_member_build) => {
let return_type_ident = format_ident!("{}", node_member_build.kind());
let child_ident_mut = format_ident!("{}_mut", member_child.name());
if member_child.optional() {
quote! {
pub fn #child_ident(&self) -> Option<&#return_type_ident> {
if let Some(#child_ident) = &self.#child_ident {
Some(#child_ident.as_ref())
} else {
None
}
}
pub fn #child_ident_mut(&mut self) -> Option<&mut #return_type_ident> {
if let Some(#child_ident) = &mut self.#child_ident {
Some(#child_ident.as_mut())
} else {
None
}
}
}
} else {
quote! {
pub fn #child_ident(&self) -> &#return_type_ident {
self.#child_ident.as_ref()
}
pub fn #child_ident_mut(&mut self) -> &mut #return_type_ident {
self.#child_ident.as_mut()
}
}
}
}
MemberChildBuild::Boolean(_) => {
quote! {
pub fn #child_ident(&self) -> bool {
self.#child_ident
}
}
}
}
}
fn make_special_child_accessors(special_child: &SpecialChild) -> TokenStream {
let child_ident = format_ident!("{}", special_child.name());
match special_child.kind() {
SpecialChildKind::FileId => {
quote! {
pub fn #child_ident(&self) -> usize {
self.#child_ident
}
}
}
SpecialChildKind::Range => {
quote! {
pub fn #child_ident(&self) -> Range<usize> {
self.#child_ident
}
}
}
}
}
fn make_accessors(child: &StructChild) -> Option<TokenStream> {
match child {
StructChild::SkipChild(_) => None,
StructChild::VecChild(vec_child) => Some(make_vec_child_accessors(vec_child)),
StructChild::MemberChild(member_child) => Some(make_member_child_accessors(member_child)),
StructChild::Special(special_child) => Some(make_special_child_accessors(special_child)),
}
}
fn make_member_ident(child: &StructChild) -> Option<Ident> {
match child {
StructChild::SkipChild(_) => None,
StructChild::VecChild(vec_child) => Some(format_ident!("{}", vec_child.name())),
StructChild::MemberChild(member_child) => Some(format_ident!("{}", member_child.name())),
StructChild::Special(special_child) => Some(format_ident!("{}", special_child.name())),
}
}
fn make_vec_child_annotated_member(vec_child: &VecChild) -> TokenStream {
let child_ident = format_ident!("{}", vec_child.name());
let type_stream = match vec_child.build() {
VecChildBuild::String(_) => quote! { String },
VecChildBuild::Node(vec_child_node_build) => {
let type_ident = format_ident!("{}", vec_child_node_build.kind());
quote! { Box<#type_ident> }
}
};
quote! {
#child_ident: Vec<#type_stream>
}
}
fn make_member_child_type_ident(member_child: &MemberChild) -> TokenStream {
match member_child.build() {
MemberChildBuild::Node(node_member_build) => {
let type_ident = format_ident!("{}", node_member_build.kind());
quote! { Box<#type_ident> }
}
MemberChildBuild::Boolean(_) => {
quote! { bool }
}
}
}
fn make_member_child_annotated_member(member_child: &MemberChild) -> TokenStream {
let child_name_ident = format_ident!("{}", member_child.name());
let type_ident = make_member_child_type_ident(member_child);
let type_stream = if member_child.optional() {
quote! { Option<#type_ident> }
} else {
quote! { #type_ident }
};
quote! {
#child_name_ident: #type_stream
}
}
fn make_special_child_annotated_member(special_child: &SpecialChild) -> TokenStream {
let child_ident = format_ident!("{}", special_child.name());
let child_type_ident = match special_child.kind() {
SpecialChildKind::FileId => quote! { usize },
SpecialChildKind::Range => quote! { Range<usize> },
};
quote! {
#child_ident: #child_type_ident
}
}
fn make_annotated_member(child: &StructChild) -> Option<TokenStream> {
match child {
StructChild::SkipChild(_) => None,
StructChild::VecChild(vec_child) => Some(make_vec_child_annotated_member(vec_child)),
StructChild::MemberChild(member_child) => {
Some(make_member_child_annotated_member(member_child))
}
StructChild::Special(special_child) => {
Some(make_special_child_annotated_member(special_child))
}
}
}
pub fn make_struct_type(build_spec: &StructSpec) -> TokenStream {
let type_ident = format_ident!("{}", build_spec.build());
let annotated_children_members = build_spec
.children()
.map(|child| make_annotated_member(child))
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let annotated_fields = build_spec
.fields()
.map(|field| make_annotated_field(field))
.collect::<Vec<_>>();
let member_names = build_spec
.children()
.map(|child| make_member_ident(child))
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let field_initializers = build_spec
.fields()
.map(|field| make_field_initializer(field))
.collect::<Vec<_>>();
let child_accessors = build_spec
.children()
.map(|child| make_accessors(child))
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let field_accessors = build_spec
.fields()
.map(|field| make_field_accessors(field))
.collect::<Vec<_>>();
let struct_stream = {
let base = quote! {
pub struct #type_ident {
#(#annotated_children_members,)*
#(#annotated_fields,)*
}
};
if !build_spec.derive().is_empty() {
let derives = build_spec
.derive()
.iter()
.map(|derive| format_ident!("{}", derive))
.collect::<Vec<_>>();
quote! {
#[derive(#(#derives,)*)]
#base
}
} else {
base
}
};
quote! {
#struct_stream
impl #type_ident {
pub fn new(#(#annotated_children_members),*) -> Self {
Self {
#(#member_names,)*
#(#field_initializers,)*
}
}
#(#child_accessors)*
#(#field_accessors)*
}
}
}

18
ast-generator/src/walk.rs Normal file
View File

@ -0,0 +1,18 @@
use crate::spec::BuildSpec;
use proc_macro2::TokenStream;
use quote::quote;
pub fn make_walk_fn(specs: &[BuildSpec]) -> TokenStream {
quote! {
use crate::ast::node::*;
use crate::ast::ast_node::*;
pub fn walk_depth_first<'a>(node: &'a dyn AstNode<'a>, f: &mut impl FnMut(AstNodeRef<'a>)) {
use AstNodeRef::*;
for child in node.children() {
walk_depth_first(child, f);
}
f(node.as_node_ref());
}
}
}

View File

@ -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(())
}

View 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"

View 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
View File

@ -0,0 +1,3 @@
fn main()
println 42
end

4
examples/op_prec.dm Normal file
View File

@ -0,0 +1,4 @@
fn main()
let n = 1 + 2 * 3 + 4
println n
end

5
examples/op_prec.dvm_ir Normal file
View 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
View File

@ -0,0 +1,19 @@
class World(pub name: String, pub color: String) end
fn getWorlds() -> List<World> = [
World('Mercury', 'Red'),
World('Earth', 'Blue'),
World('Jupiter', 'Orange')
]
fn findWorldByColor(worlds: List<World>, color: String) -> String
worlds.find { it -> it.color == color }
.map { it -> it.name }
.expect "No world has the given color ${color}"
end
fn main()
let worlds = getWorlds()
let blueWorld = findWorldByColor(worlds, 'Blue')
println "Hello, ${blueWorld}!"
end

89
examples/worlds.dvm_ir Normal file
View 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

View File

@ -0,0 +1,5 @@
use math::add
fn main()
println add(1, 2) // 3
end

View File

@ -0,0 +1,3 @@
mod math
pub fn add(a: Int, b: Int) = a + b

View 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

View 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

View 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

View File

@ -0,0 +1 @@
Hello, World!

View File

@ -0,0 +1 @@
`Hello, ${world}!`

View File

@ -0,0 +1 @@
false

View File

@ -0,0 +1 @@
true

View File

@ -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"));
}

View File

@ -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);
// }
// }
// }
// }

View File

@ -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!(),
}
}

View File

@ -3,16 +3,17 @@ use codespan_reporting::term;
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
use deimos::ast::build::build_ast;
use deimos::name_analysis::analyze_names;
use deimos::name_analysis::symbol_table::symbol_tree::SymbolTree;
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 +21,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 +31,15 @@ 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(
&mut compilation_units,
&files,
&mut symbol_table,
);
if diagnostics.is_empty() {
println!("Name analysis complete.");
println!("Symbol table\n-------\n{}", symbol_table);
println!("{}", symbol_table);
} else {
let writer = StandardStream::stderr(ColorChoice::Always);
let config = term::Config::default();
@ -43,6 +47,6 @@ pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Err
term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
}
}
Ok(())
}

View File

@ -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
View 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>,
}

View File

@ -1,9 +1,12 @@
#![feature(new_range_api)]
#![feature(coerce_unsized)]
#![feature(unsize)]
#![allow(warnings)]
extern crate core;
pub mod ast;
pub mod diagnostic;
pub mod ir;
pub mod module;
pub mod name_analysis;
pub mod object_file;

View File

@ -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,
}

View File

@ -0,0 +1,398 @@
use crate::ast::node::{
CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, Function, FunctionBody,
GenericParameters, Identifier, IdentifierOrFqn, Module, ModuleLevelDeclaration, Parameters,
PrimitiveType, ReturnType, TypeUse, TypedArray, UseStatement, UseStatementIdentifier,
UseStatementPrefix,
};
use crate::diagnostic::DmDiagnostic;
use crate::name_analysis::symbol::function_symbol::FunctionSymbol;
use crate::name_analysis::symbol::generic_type_symbol::GenericTypeSymbol;
use crate::name_analysis::symbol::module_level_symbol::ModuleLevelSymbol;
use crate::name_analysis::symbol::module_symbol::ModuleSymbol;
use crate::name_analysis::symbol::parameter_symbol::ParameterSymbol;
use crate::name_analysis::symbol::primitive_type_symbol::PrimitiveTypeSymbol;
use crate::name_analysis::symbol::source_definition::SourceDefinition;
use crate::name_analysis::symbol::type_symbol::TypeSymbol;
use crate::name_analysis::symbol::use_symbol::ConcreteUseSymbol;
use crate::name_analysis::symbol_table::SymbolTable;
use crate::name_analysis::util::{format_fqn, handle_insert_error, handle_lookup_error};
use std::cell::RefCell;
use std::rc::Rc;
pub fn na_p1_compilation_unit(
file_name: &str,
compilation_unit: &mut CompilationUnit,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
if let Some(namespace) = compilation_unit.namespace() {
match namespace.identifier_or_fqn() {
IdentifierOrFqn::Identifier(identifier) => {
symbol_table.set_current_fqn(&[identifier.name()])
}
IdentifierOrFqn::FullyQualifiedName(fqn) => {
symbol_table
.set_current_fqn(&fqn.identifiers().map(Identifier::name).collect::<Vec<_>>());
}
}
}
symbol_table.push_scope(&format!("FileScope {}", file_name));
for use_statement in compilation_unit.use_statements_mut() {
na_p1_use_statement(use_statement, symbol_table, diagnostics);
}
for module_level_declaration in compilation_unit.module_level_declarations_mut() {
na_p1_module_level_declaration(module_level_declaration, symbol_table, diagnostics);
}
symbol_table.pop_scope();
}
fn na_p1_use_statement(
use_statement: &mut UseStatement,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match use_statement {
UseStatement::ConcreteUseStatement(concrete_use_statement) => {
na_p1_concrete_use_statement(concrete_use_statement, symbol_table, diagnostics);
}
UseStatement::StarUseStatement(star_use_statement) => {
todo!()
}
}
}
fn na_p1_concrete_use_statement(
concrete_use_statement: &mut ConcreteUseStatement,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
let prefixes: Vec<Rc<str>> = concrete_use_statement
.prefixes()
.map(UseStatementPrefix::identifier)
.map(Identifier::name)
.map(Rc::from)
.collect();
match concrete_use_statement.suffix_mut() {
ConcreteUseStatementSuffix::UseStatementIdentifier(use_statement_identifier) => {
handle_concrete_use_statement_identifier(
&prefixes,
use_statement_identifier,
symbol_table,
diagnostics,
);
}
ConcreteUseStatementSuffix::UseList(use_list) => {
for use_statement_identifier in use_list.identifiers_mut() {
handle_concrete_use_statement_identifier(
&prefixes,
use_statement_identifier,
symbol_table,
diagnostics,
);
}
}
}
}
fn handle_concrete_use_statement_identifier(
prefixes: &Vec<Rc<str>>,
use_statement_identifier: &mut UseStatementIdentifier,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
let mut fqn_parts: Vec<Rc<str>> = vec![];
for prefix in prefixes {
fqn_parts.push(prefix.clone());
}
fqn_parts.push(Rc::from(use_statement_identifier.identifier().name()));
let to_insert = ConcreteUseSymbol::new(
&fqn_parts,
Some(SourceDefinition::from_identifier(
use_statement_identifier.identifier(),
)),
);
match symbol_table.insert_concrete_use_symbol(to_insert) {
Ok(inserted) => {
use_statement_identifier.set_symbol(inserted);
}
Err(insert_error) => {
handle_insert_error(
insert_error,
use_statement_identifier.identifier().name(),
use_statement_identifier.identifier().file_id(),
use_statement_identifier.identifier().range(),
diagnostics,
);
}
}
}
fn na_p1_module_level_declaration(
module_level_declaration: &mut ModuleLevelDeclaration,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) -> Option<ModuleLevelSymbol> {
match module_level_declaration {
ModuleLevelDeclaration::Module(module) => na_p1_module(module, symbol_table, diagnostics)
.map(|module_symbol| ModuleLevelSymbol::Module(module_symbol)),
ModuleLevelDeclaration::Interface(interface) => {
todo!()
}
ModuleLevelDeclaration::Class(class) => {
todo!()
}
ModuleLevelDeclaration::Function(function) => {
na_p1_function(function, symbol_table, diagnostics)
.map(|function_symbol| ModuleLevelSymbol::Function(function_symbol))
}
ModuleLevelDeclaration::PlatformFunction(platform_function) => {
todo!()
}
}
}
fn na_p1_module(
module: &mut Module,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) -> Option<Rc<RefCell<ModuleSymbol>>> {
symbol_table.push_fqn_part(module.identifier().name());
symbol_table.push_scope(&format!("ModuleScope {}", module.identifier().name()));
let to_insert = ModuleSymbol::new(
symbol_table.current_fqn_owned(),
module.is_public(),
Some(SourceDefinition::from_identifier(module.identifier())),
);
let module_symbol = match symbol_table.insert_module_symbol(to_insert) {
Ok(module_symbol) => {
for declaration in module.declarations_mut() {
let declaration_result =
na_p1_module_level_declaration(declaration, symbol_table, diagnostics);
if let Some(module_level_symbol) = declaration_result {
module_symbol
.borrow_mut()
.add_inner_symbol(module_level_symbol);
}
}
Some(module_symbol)
}
Err(symbol_insert_error) => {
handle_insert_error(
symbol_insert_error,
module.identifier().name(),
module.identifier().file_id(),
module.identifier().range(),
diagnostics,
);
None
}
};
symbol_table.pop_scope();
symbol_table.pop_fqn_part();
module_symbol
}
fn na_p1_function(
function: &mut Function,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) -> Option<Rc<RefCell<FunctionSymbol>>> {
symbol_table.push_scope(&format!("FunctionScope {}", function.identifier().name()));
let to_insert = FunctionSymbol::new(
&symbol_table.resolve_fqn(Rc::from(function.identifier().name())),
function.is_public(),
false,
Some(SourceDefinition::from_identifier(function.identifier())),
);
let function_symbol = match symbol_table.insert_function_symbol(to_insert) {
Ok(function_symbol) => {
{
let mut as_ref_mut = function_symbol.borrow_mut();
// generics
na_p1_generic_parameters(function.generics_mut(), symbol_table, diagnostics);
// parameters
as_ref_mut.set_parameter_symbols(na_p1_parameters(
function.parameters_mut(),
symbol_table,
diagnostics,
));
// return type
let return_type =
na_p1_return_type(function.return_type_mut(), symbol_table, diagnostics);
if let Some(type_symbol) = return_type {
as_ref_mut.set_return_type(type_symbol);
}
}
Some(function_symbol)
}
Err(symbol_insert_error) => {
handle_insert_error(
symbol_insert_error,
function.identifier().name(),
function.identifier().file_id(),
function.identifier().range(),
diagnostics,
);
None
}
};
symbol_table.pop_scope();
function_symbol
}
fn na_p1_parameters(
parameters: &mut Parameters,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) -> Vec<Rc<RefCell<ParameterSymbol>>> {
todo!()
}
fn na_p1_return_type(
return_type: &mut ReturnType,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) -> Option<TypeSymbol> {
na_p1_type_use(return_type.type_use_mut(), symbol_table, diagnostics)
}
fn na_p1_type_use(
type_use: &mut TypeUse,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) -> Option<TypeSymbol> {
match type_use {
TypeUse::PrimitiveType(primitive_type) => {
Some(TypeSymbol::Primitive(match primitive_type {
PrimitiveType::Byte => PrimitiveTypeSymbol::Byte,
PrimitiveType::Short => PrimitiveTypeSymbol::Short,
PrimitiveType::Char => PrimitiveTypeSymbol::Char,
PrimitiveType::Int => PrimitiveTypeSymbol::Int,
PrimitiveType::Long => PrimitiveTypeSymbol::Long,
PrimitiveType::Double => PrimitiveTypeSymbol::Double,
PrimitiveType::Bool => PrimitiveTypeSymbol::Boolean,
PrimitiveType::String => PrimitiveTypeSymbol::String,
PrimitiveType::TypedArray(typed_array) => {
na_p1_typed_array(typed_array, symbol_table, diagnostics)
}
PrimitiveType::Any => PrimitiveTypeSymbol::Any,
PrimitiveType::Void => PrimitiveTypeSymbol::Void,
}))
}
TypeUse::InterfaceOrClassTypeUse(interface_or_class_type) => {
match interface_or_class_type.identifier_or_fqn() {
IdentifierOrFqn::Identifier(identifier) => {
match symbol_table.lookup_type(identifier.name()) {
Ok(type_symbol) => {
interface_or_class_type.set_type_symbol(type_symbol.clone());
Some(type_symbol)
}
Err(symbol_lookup_error) => {
handle_lookup_error(
symbol_lookup_error,
identifier.name(),
identifier.file_id(),
identifier.range(),
diagnostics,
);
None
}
}
}
IdentifierOrFqn::FullyQualifiedName(fqn) => {
let fqn_parts = fqn
.identifiers()
.map(Identifier::name)
.collect::<Vec<&str>>();
match symbol_table.lookup_type_by_fqn(&fqn_parts) {
Ok(type_symbol) => {
interface_or_class_type.set_type_symbol(type_symbol.clone());
Some(type_symbol)
}
Err(symbol_lookup_error) => {
handle_lookup_error(
symbol_lookup_error,
&format_fqn(&fqn_parts),
fqn.file_id(),
fqn.range(),
diagnostics,
);
None
}
}
}
}
}
TypeUse::TupleTypeUse(tuple_type) => {
todo!()
}
TypeUse::FunctionTypeUse(function_type) => {
todo!()
}
}
}
fn na_p1_typed_array(
typed_array: &mut TypedArray,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) -> PrimitiveTypeSymbol {
let inner_type_use = typed_array
.generic_arguments_mut()
.type_use_list_mut()
.type_uses_mut()
.next()
.unwrap();
let inner_type_symbol = na_p1_type_use(inner_type_use, symbol_table, diagnostics);
PrimitiveTypeSymbol::TypedArray {
inner_type: inner_type_symbol.map(Box::from),
}
}
fn na_p1_generic_parameters(
generic_parameters: &mut GenericParameters,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
for identifier in generic_parameters.identifier_list().identifiers() {
let generic_type_symbol = GenericTypeSymbol::new(
identifier.name(),
Some(SourceDefinition::from_identifier(identifier)),
);
match symbol_table.insert_generic_type_symbol(generic_type_symbol) {
Ok(_) => {}
Err(symbol_insert_error) => {
handle_insert_error(
symbol_insert_error,
identifier.name(),
identifier.file_id(),
identifier.range(),
diagnostics,
);
}
}
}
}
fn na_p1_function_body(
function_body: &mut FunctionBody,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
todo!()
}

View File

@ -1,38 +0,0 @@
pub(super) struct FqnContext {
stack: Vec<String>,
}
impl FqnContext {
pub fn new() -> Self {
FqnContext { stack: Vec::new() }
}
pub fn push(&mut self, name: &str) {
self.stack.push(name.to_string());
}
pub fn pop(&mut self) {
self.stack.pop();
}
pub fn current(&self) -> String {
let mut acc = String::new();
for (i, name) in self.stack.iter().enumerate() {
acc.push_str(name);
if i != self.stack.len() - 1 {
acc.push_str("::")
}
}
acc
}
pub fn resolve(&self, name: &str) -> String {
let mut acc = String::new();
if !self.stack.is_empty() {
acc.push_str(&self.current());
acc.push_str("::");
}
acc.push_str(name);
acc
}
}

View File

@ -1,22 +1,4 @@
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::named::Named;
use crate::ast::node::names::*;
use crate::ast::node::object_access::*;
use crate::ast::node::statement::*;
use crate::ast::node::type_use::*;
use crate::ast::node::use_statement::*;
use crate::ast::node::*;
use crate::diagnostic::DmDiagnostic;
use crate::name_analysis::fqn_context::FqnContext;
use crate::name_analysis::symbol::*;

File diff suppressed because it is too large Load Diff

View File

@ -19,250 +19,256 @@ 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::name_analysis::resolve::resolve_compilation_unit;
use crate::ast::ast_node::AstNode;
use crate::ast::node::CompilationUnit;
use crate::diagnostic::DmDiagnostic;
use crate::name_analysis::gather::gather_compilation_unit;
use crate::name_analysis::resolve::resolve_compilation_unit;
use crate::name_analysis::first_pass::na_p1_compilation_unit;
use crate::name_analysis::second_pass::na_p2_compilation_unit;
use crate::name_analysis::symbol_table::SymbolTable;
use codespan_reporting::files::Files;
use std::hash::Hash;
mod fqn_context;
mod gather;
mod resolve;
// mod resolve;
mod first_pass;
mod second_pass;
pub mod symbol;
pub mod symbol_table;
mod util;
pub fn analyze_names(
compilation_units: &mut Vec<CompilationUnit>,
pub fn analyze_names<'a, F: Files<'a, FileId = usize, Name = String>>(
compilation_units: &mut Vec<Box<CompilationUnit>>,
files: &'a F,
symbol_table: &mut SymbolTable,
) -> Vec<DmDiagnostic> {
let mut diagnostics = vec![];
// gather symbols
for compilation_unit in compilation_units.iter_mut() {
gather_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
let file_name = files.name(compilation_unit.file_id()).unwrap();
na_p1_compilation_unit(&file_name, compilation_unit, symbol_table, &mut diagnostics);
}
// resolve symbols
for compilation_unit in compilation_units.iter_mut() {
resolve_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
for compilation_unit in compilation_units {
na_p2_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);
// }
// }

View File

@ -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,

View File

@ -0,0 +1,102 @@
use crate::ast::node::{
CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, Identifier, UseStatement,
UseStatementIdentifier, UseStatementPrefix,
};
use crate::diagnostic::DmDiagnostic;
use crate::name_analysis::symbol_table::{SymbolLookupError, SymbolTable};
use crate::name_analysis::util::handle_lookup_error;
pub fn na_p2_compilation_unit(
compilation_unit: &mut CompilationUnit,
symbol_table: &SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
// TODO: check namespace for proper file name
for use_statement in compilation_unit.use_statements_mut() {
na_p2_use_statement(use_statement, symbol_table, diagnostics);
}
// TODO: declarations
}
fn na_p2_use_statement(
use_statement: &mut UseStatement,
symbol_table: &SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match use_statement {
UseStatement::ConcreteUseStatement(concrete_use_statement) => {
na_p2_concrete_use_statement(concrete_use_statement, symbol_table, diagnostics);
}
UseStatement::StarUseStatement(star_use_statement) => {
todo!()
}
}
}
fn na_p2_concrete_use_statement(
concrete_use_statement: &mut ConcreteUseStatement,
symbol_table: &SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
let base_fqn_parts: Vec<String> = concrete_use_statement
.prefixes()
.map(UseStatementPrefix::identifier)
.map(Identifier::name)
.map(ToString::to_string)
.collect();
match concrete_use_statement.suffix_mut() {
ConcreteUseStatementSuffix::UseStatementIdentifier(use_statement_identifier) => {
handle_concrete_use_statement_identifier(
&base_fqn_parts,
use_statement_identifier,
symbol_table,
diagnostics,
);
}
ConcreteUseStatementSuffix::UseList(use_list) => {
for use_statement_identifier in use_list.identifiers_mut() {
handle_concrete_use_statement_identifier(
&base_fqn_parts,
use_statement_identifier,
symbol_table,
diagnostics,
);
}
}
}
}
fn handle_concrete_use_statement_identifier(
base_fqn_parts: &[String],
use_statement_identifier: &mut UseStatementIdentifier,
symbol_table: &SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
let fqn_parts = {
let mut all_parts: Vec<&str> = vec![];
for part in base_fqn_parts {
all_parts.push(part);
}
all_parts.push(use_statement_identifier.identifier().name());
all_parts
};
let maybe_usable_symbol = symbol_table.find_usable_symbol(&fqn_parts);
if let Some(usable_symbol) = maybe_usable_symbol {
use_statement_identifier
.symbol_mut()
.expect("Should have set the symbol on the use statement already.")
.borrow_mut()
.set_resolved_symbol(usable_symbol);
} else {
handle_lookup_error(
SymbolLookupError::NoDefinition,
&fqn_parts.join("::"),
use_statement_identifier.identifier().file_id(),
use_statement_identifier.identifier().range(),
diagnostics,
);
}
}

View File

@ -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()
}
}

View 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()
}
}

View File

@ -0,0 +1,40 @@
use crate::name_analysis::symbol::class_member_symbol::ClassMemberSymbol;
use crate::name_analysis::symbol::function_symbol::FunctionSymbol;
use crate::name_analysis::symbol::source_definition::SourceDefinition;
use crate::name_analysis::symbol::Symbol;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
#[derive(Debug)]
pub struct ClassSymbol {
fqn_parts: Vec<Rc<str>>,
members: HashMap<Rc<str>, Rc<RefCell<ClassMemberSymbol>>>,
functions: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>,
source_definition: Option<SourceDefinition>,
}
impl ClassSymbol {
pub fn new(fqn_parts: &[Rc<str>], source_definition: Option<SourceDefinition>) -> Self {
Self {
fqn_parts: fqn_parts.to_vec(),
members: HashMap::new(),
functions: HashMap::new(),
source_definition,
}
}
pub fn declared_name(&self) -> &str {
self.fqn_parts.last().unwrap()
}
pub fn declared_name_owned(&self) -> Rc<str> {
self.fqn_parts.last().unwrap().clone()
}
}
impl Symbol for ClassSymbol {
fn source_definition(&self) -> Option<&SourceDefinition> {
self.source_definition.as_ref()
}
}

View File

@ -0,0 +1,105 @@
use crate::name_analysis::symbol::parameter_symbol::ParameterSymbol;
use crate::name_analysis::symbol::source_definition::SourceDefinition;
use crate::name_analysis::symbol::type_symbol::TypeSymbol;
use crate::name_analysis::symbol::Symbol;
use crate::name_analysis::util::join_fqn_parts;
use std::cell::RefCell;
use std::fmt::{Debug, Formatter};
use std::rc::Rc;
pub struct FunctionSymbol {
fqn_parts: Vec<Rc<str>>,
is_public: bool,
is_platform: bool,
source_definition: Option<SourceDefinition>,
parameters: Vec<Rc<RefCell<ParameterSymbol>>>,
return_type: Option<TypeSymbol>,
}
impl FunctionSymbol {
pub fn new(
fqn_parts: &[Rc<str>],
is_public: bool,
is_platform: bool,
source_definition: Option<SourceDefinition>,
) -> FunctionSymbol {
FunctionSymbol {
fqn_parts: fqn_parts.to_vec(),
is_public,
is_platform,
source_definition,
parameters: Vec::new(),
return_type: None,
}
}
pub fn with_parameters_and_return_type(
fqn_parts: &[Rc<str>],
is_public: bool,
is_platform: bool,
source_definition: Option<SourceDefinition>,
parameters: &[Rc<RefCell<ParameterSymbol>>],
return_type: Option<TypeSymbol>,
) -> Self {
Self {
fqn_parts: fqn_parts.to_vec(),
is_public,
is_platform,
source_definition,
parameters: parameters.to_vec(),
return_type,
}
}
pub fn fqn_parts_owned(&self) -> Vec<Rc<str>> {
self.fqn_parts.to_vec()
}
pub fn declared_name(&self) -> &str {
self.fqn_parts.last().unwrap().as_ref()
}
pub fn declared_name_owned(&self) -> Rc<str> {
self.fqn_parts.last().unwrap().clone()
}
pub fn is_public(&self) -> bool {
self.is_public
}
pub fn is_platform(&self) -> bool {
self.is_platform
}
pub fn parameter_symbols(&self) -> &[Rc<RefCell<ParameterSymbol>>] {
&self.parameters
}
pub fn set_parameter_symbols(&mut self, parameter_symbols: Vec<Rc<RefCell<ParameterSymbol>>>) {
self.parameters = parameter_symbols;
}
pub fn set_return_type(&mut self, type_symbol: TypeSymbol) {
self.return_type = Some(type_symbol);
}
}
impl Symbol for FunctionSymbol {
fn source_definition(&self) -> Option<&SourceDefinition> {
self.source_definition.as_ref()
}
}
impl Debug for FunctionSymbol {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FunctionSymbol")
.field("fqn", &join_fqn_parts(&self.fqn_parts))
.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()
}
}

View File

@ -0,0 +1,46 @@
use crate::name_analysis::symbol::source_definition::SourceDefinition;
use crate::name_analysis::symbol::Symbol;
use std::fmt::{Debug, Formatter};
use std::rc::Rc;
#[derive(Clone)]
pub struct GenericTypeSymbol {
declared_name: Rc<str>,
source_definition: Option<SourceDefinition>,
}
impl GenericTypeSymbol {
pub fn new(declared_name: &str, source_definition: Option<SourceDefinition>) -> Self {
GenericTypeSymbol {
declared_name: Rc::from(declared_name),
source_definition,
}
}
pub fn declared_name(&self) -> &str {
&self.declared_name
}
pub fn declared_name_owned(&self) -> Rc<str> {
self.declared_name.clone()
}
pub fn source_definition(&self) -> Option<&SourceDefinition> {
self.source_definition.as_ref()
}
}
impl Symbol for GenericTypeSymbol {
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()
}
}

View File

@ -0,0 +1,37 @@
use crate::name_analysis::symbol::function_symbol::FunctionSymbol;
use crate::name_analysis::symbol::source_definition::SourceDefinition;
use crate::name_analysis::symbol::Symbol;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
#[derive(Debug)]
pub struct InterfaceSymbol {
fqn_parts: Vec<Rc<str>>,
functions: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>,
source_definition: Option<SourceDefinition>,
}
impl InterfaceSymbol {
pub fn new(fqn_parts: &[Rc<str>], source_definition: Option<SourceDefinition>) -> Self {
Self {
fqn_parts: fqn_parts.to_vec(),
functions: HashMap::new(),
source_definition,
}
}
pub fn declared_name(&self) -> &str {
self.fqn_parts.last().unwrap().as_ref()
}
pub fn declared_name_owned(&self) -> Rc<str> {
self.fqn_parts.last().unwrap().clone()
}
}
impl Symbol for InterfaceSymbol {
fn source_definition(&self) -> Option<&SourceDefinition> {
self.source_definition.as_ref()
}
}

View File

@ -0,0 +1,21 @@
pub mod class_member_symbol;
pub mod class_symbol;
pub mod function_symbol;
pub mod generic_type_symbol;
pub mod interface_symbol;
pub mod module_level_symbol;
pub mod module_symbol;
pub mod parameter_symbol;
pub mod primitive_type_symbol;
pub mod source_definition;
pub mod type_symbol;
pub mod usable_symbol;
pub mod use_symbol;
pub mod variable_symbol;
use crate::name_analysis::symbol::source_definition::SourceDefinition;
use std::fmt::Debug;
pub trait Symbol: Debug {
fn source_definition(&self) -> Option<&SourceDefinition>;
}

View File

@ -0,0 +1,30 @@
use crate::name_analysis::symbol::class_symbol::ClassSymbol;
use crate::name_analysis::symbol::function_symbol::FunctionSymbol;
use crate::name_analysis::symbol::interface_symbol::InterfaceSymbol;
use crate::name_analysis::symbol::module_symbol::ModuleSymbol;
use crate::name_analysis::symbol::Symbol;
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug)]
pub enum ModuleLevelSymbol {
Module(Rc<RefCell<ModuleSymbol>>),
Interface(Rc<RefCell<InterfaceSymbol>>),
Class(Rc<RefCell<ClassSymbol>>),
Function(Rc<RefCell<FunctionSymbol>>),
}
impl ModuleLevelSymbol {
pub fn to_symbol(self) -> Rc<RefCell<dyn Symbol>> {
match self {
ModuleLevelSymbol::Module(module_symbol) => module_symbol as Rc<RefCell<dyn Symbol>>,
ModuleLevelSymbol::Interface(interface_symbol) => {
interface_symbol as Rc<RefCell<dyn Symbol>>
}
ModuleLevelSymbol::Class(class_symbol) => class_symbol as Rc<RefCell<dyn Symbol>>,
ModuleLevelSymbol::Function(function_symbol) => {
function_symbol as Rc<RefCell<dyn Symbol>>
}
}
}
}

View File

@ -0,0 +1,65 @@
use crate::name_analysis::symbol::module_level_symbol::ModuleLevelSymbol;
use crate::name_analysis::symbol::source_definition::SourceDefinition;
use crate::name_analysis::symbol::Symbol;
use crate::name_analysis::util::join_fqn_parts;
use std::fmt::{Debug, Formatter};
use std::rc::Rc;
pub struct ModuleSymbol {
fqn_parts: Vec<Rc<str>>,
is_public: bool,
source_definition: Option<SourceDefinition>,
inner_symbols: Vec<ModuleLevelSymbol>,
}
impl ModuleSymbol {
pub fn new(
fqn_parts: Vec<Rc<str>>,
is_public: bool,
source_definition: Option<SourceDefinition>,
) -> Self {
Self {
fqn_parts,
is_public,
source_definition,
inner_symbols: Vec::new(),
}
}
pub fn fqn_parts(&self) -> &[Rc<str>] {
&self.fqn_parts
}
pub fn declared_name(&self) -> &str {
self.fqn_parts.last().unwrap()
}
pub fn declared_name_owned(&self) -> Rc<str> {
self.fqn_parts.last().unwrap().clone()
}
pub fn is_public(&self) -> bool {
self.is_public
}
pub fn add_inner_symbol(&mut self, module_level_symbol: ModuleLevelSymbol) {
self.inner_symbols.push(module_level_symbol);
}
}
impl Symbol for ModuleSymbol {
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", &join_fqn_parts(&self.fqn_parts))
.field("is_public", &self.is_public)
.field("source_definition", &self.source_definition)
.field("inner_symbols", &self.inner_symbols)
.finish()
}
}

Some files were not shown because too many files have changed in this diff Show More