Compare commits

..

157 Commits

Author SHA1 Message Date
Jesse Brault
8ba79be920 Add cst command. 2025-12-03 10:38:22 -06:00
Jesse Brault
8eb13791ee Update pest. 2025-11-23 20:15:28 -06:00
Jesse Brault
7ffa516c03 Add fields and special children for Polymorphic enum loop specs. 2025-11-23 14:22:33 -06:00
Jesse Brault
734a00ea92 Beginnings of type analysis. 2025-11-22 17:59:08 -06:00
Jesse Brault
8d73a8ea73 Update ast schema. 2025-11-22 16:50:32 -06:00
Jesse Brault
70aea0ba6f Rewrite pretty_print as Display impl; tweaking ir. 2025-11-22 16:31:08 -06:00
Jesse Brault
f656d0d4d4 Merge remote-tracking branch 'origin/grammar-overhaul' into grammar-overhaul 2025-11-21 21:38:35 -06:00
Jesse Brault
026bc68fa7 Introduce asm representation (between ir and vm instructions). 2025-11-21 21:38:12 -06:00
Jesse Brault
9dfdcd716b Modest implementation of lowering to IR and Instruction code. 2025-11-19 23:23:43 -06:00
Jesse Brault
f21128fd68 Add support for polymorphic enum builds (used for suffix operators). 2025-11-15 18:13:25 -06:00
Jesse Brault
803b95c5dc More json sketching. 2025-11-10 22:08:09 -06:00
Jesse Brault
053d08849e Sketching out rail and json. 2025-11-10 16:32:19 -06:00
Jesse Brault
7439ca554c Fixed spacing for suffix operators. Now calls require at least one of parentheses and/or closure. 2025-11-03 14:42:41 -06:00
Jesse Brault
51c39f5f34 WIP fixing newlines/call grammar. 2025-11-03 13:41:05 -06:00
Jesse Brault
1d11a45c68 Seem to have fixed newlines and no-parentheses calls. 2025-11-03 12:16:28 -06:00
Jesse Brault
da05bb101b Better handling of global imports. 2025-11-02 13:41:00 -06:00
Jesse Brault
6bcef184eb Successfully fixed implicit global println test. 2025-11-01 21:38:52 -05:00
Jesse Brault
0550df534e Move parameter gathering to second pass in order to match analysis of all locals there. 2025-11-01 12:23:41 -05:00
Jesse Brault
13330300c1 WIP fleshing out of block and expression name analysis. 2025-10-31 13:04:22 -05:00
Jesse Brault
12a3a61156 Work on functions and parameter name resolution. 2025-10-31 12:14:33 -05:00
Jesse Brault
9b83a531ca Remove un-needed test. 2025-10-29 12:14:52 -05:00
Jesse Brault
e5c5be6d95 Re-enable tests for name analysis. Currently failing. 2025-10-29 12:13:39 -05:00
Jesse Brault
d653d26e14 Work on star-use symbols. 2025-10-28 10:43:42 -05:00
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
165 changed files with 15724 additions and 2699 deletions

56
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",
@ -271,28 +283,21 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "once_cell"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "pest"
version = "2.8.0"
version = "2.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6"
checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22"
dependencies = [
"memchr",
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.8.0"
version = "2.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5"
checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f"
dependencies = [
"pest",
"pest_generator",
@ -300,9 +305,9 @@ dependencies = [
[[package]]
name = "pest_generator"
version = "2.8.0"
version = "2.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841"
checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625"
dependencies = [
"pest",
"pest_meta",
@ -313,11 +318,10 @@ dependencies = [
[[package]]
name = "pest_meta"
version = "2.8.0"
version = "2.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82"
dependencies = [
"once_cell",
"pest",
"sha2",
]
@ -407,26 +411,6 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "typenum"
version = "1.17.0"

View File

@ -12,16 +12,17 @@ name = "dmc"
path = "src/bin/dmc/main.rs"
[dependencies]
pest = { version = "2.8.0" }
pest = { version = "2.8.4" }
clap = { version = "4.5.23", features = ["derive"] }
pest_derive = { version = "2.8.0", features = ["grammar-extras"] }
pest_derive = { version = "2.8.4", features = ["grammar-extras"] }
codespan-reporting = "0.12.0"
log = "0.4.27"
indoc = "2.0.6"
[build-dependencies]
ast-generator = { path = "ast-generator" }
cst-test-generator = { path = "cst-test-generator" }
[workspace]
resolver = "3"
members = ["ast-generator"]
members = ["ast-generator", "cst-test-generator"]

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,96 @@
mod enum_ast_node;
mod leaf_enum_ast_node;
mod leaf_struct_ast_node;
mod polymorphic_enum_inner_build_ast_node;
mod polymorphic_enum_loop_ast_node;
mod polymorphic_type_ast_node;
mod struct_ast_node;
use crate::ast_node::enum_ast_node::make_enum_ast_node_impl;
use crate::ast_node::leaf_enum_ast_node::make_leaf_enum_ast_node_impl;
use crate::ast_node::leaf_struct_ast_node::make_leaf_struct_ast_node_impl;
use crate::ast_node::polymorphic_enum_inner_build_ast_node::make_polymorphic_enum_inner_build_ast_node_impl;
use crate::ast_node::polymorphic_enum_loop_ast_node::make_polymorphic_enum_loop_ast_node_impl;
use crate::ast_node::polymorphic_type_ast_node::make_polymorphic_type_ast_node_impl;
use crate::ast_node::struct_ast_node::make_struct_ast_node_impl;
use crate::spec::BuildSpec;
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
pub fn make_ast_node_impl(build_spec: &BuildSpec) -> Option<TokenStream> {
match build_spec {
BuildSpec::Enum(enum_spec) => Some(make_enum_ast_node_impl(enum_spec)),
BuildSpec::LeafEnum(leaf_enum) => Some(make_leaf_enum_ast_node_impl(leaf_enum)),
BuildSpec::Struct(struct_spec) => Some(make_struct_ast_node_impl(struct_spec)),
BuildSpec::LeafStruct(leaf_struct) => Some(make_leaf_struct_ast_node_impl(leaf_struct)),
BuildSpec::Production(_) => None,
BuildSpec::NodeProduction(_) => None,
BuildSpec::PolymorphicType(polymorphic_type) => {
Some(make_polymorphic_type_ast_node_impl(polymorphic_type))
}
BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop) => Some(
make_polymorphic_enum_loop_ast_node_impl(polymorphic_enum_loop),
),
BuildSpec::PolymorphicPassThrough(_) => None,
BuildSpec::PolymorphicEnumInnerBuild(polymorphic_enum_inner_build) => Some(
make_polymorphic_enum_inner_build_ast_node_impl(polymorphic_enum_inner_build),
),
BuildSpec::PolymorphicLeafEnum(_) => None,
BuildSpec::PolymorphicTreeEnum(_) => None,
}
}
fn make_type_ident(build_spec: &BuildSpec) -> Option<Ident> {
match build_spec {
BuildSpec::Struct(struct_spec) => Some(format_ident!("{}", struct_spec.build())),
BuildSpec::LeafStruct(leaf_struct) => Some(format_ident!("{}", leaf_struct.build())),
BuildSpec::Enum(enum_spec) => Some(format_ident!("{}", enum_spec.build())),
BuildSpec::LeafEnum(leaf_enum) => Some(format_ident!("{}", leaf_enum.build())),
BuildSpec::PolymorphicType(polymorphic_type) => {
Some(format_ident!("{}", polymorphic_type.name()))
}
BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop) => {
Some(format_ident!("{}", polymorphic_enum_loop.name()))
}
BuildSpec::PolymorphicPassThrough(_) => None,
BuildSpec::Production(_) => None,
BuildSpec::NodeProduction(_) => None,
BuildSpec::PolymorphicEnumInnerBuild(polymorphic_enum_inner_build) => {
Some(format_ident!("{}", polymorphic_enum_inner_build.name()))
}
BuildSpec::PolymorphicLeafEnum(_) => None,
BuildSpec::PolymorphicTreeEnum(_) => None,
}
}
pub fn make_ast_enum_member(build_spec: &BuildSpec) -> Option<TokenStream> {
make_type_ident(build_spec).map(|type_ident| {
quote! {
#type_ident(&'a #type_ident)
}
})
}
pub fn make_ast_enum_mut_member(build_spec: &BuildSpec) -> Option<TokenStream> {
make_type_ident(build_spec).map(|type_ident| {
quote! {
#type_ident(&'a mut #type_ident)
}
})
}
pub fn make_ast_node_ref_unwrapper(build_spec: &BuildSpec) -> Option<TokenStream> {
make_type_ident(build_spec).map(|type_ident| {
quote! {
AstNodeRef::#type_ident(inner) => *inner
}
})
}
pub fn make_ast_node_ref_mut_unwrapper(build_spec: &BuildSpec) -> Option<TokenStream> {
make_type_ident(build_spec).map(|type_ident| {
quote! {
AstNodeRefMut::#type_ident(inner) => *inner
}
})
}

View File

@ -0,0 +1,76 @@
use convert_case::{Case, Casing};
use crate::spec::polymorphic_enum_inner_build::{PolymorphicEnumInnerBuild, PolymorphicEnumInnerBuildMemberKind};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
pub fn make_polymorphic_enum_inner_build_ast_node_impl(
spec: &PolymorphicEnumInnerBuild,
) -> TokenStream {
let type_ident = format_ident!("{}", spec.name());
let match_arms = spec.members()
.map(|member| {
let member_ident = format_ident!("{}", member.name());
match member.kind() {
PolymorphicEnumInnerBuildMemberKind::Leaf => {
quote! {
#type_ident::#member_ident => vec![]
}
}
PolymorphicEnumInnerBuildMemberKind::Struct => {
let child_ident = format_ident!("{}", member.name().to_case(Case::Snake));
quote! {
#type_ident::#member_ident(#child_ident) => vec![
#child_ident
]
}
}
}
})
.collect::<Vec<_>>();
let mut_match_arms = spec.members()
.map(|member| {
let member_ident = format_ident!("{}", member.name());
match member.kind() {
PolymorphicEnumInnerBuildMemberKind::Leaf => {
quote! {
#type_ident::#member_ident => {}
}
}
PolymorphicEnumInnerBuildMemberKind::Struct => {
let child_ident = format_ident!("{}", member.name().to_case(Case::Snake));
quote! {
#type_ident::#member_ident(#child_ident) => {
f(#child_ident as &'a mut dyn AstNode<'a>)
}
}
}
}
})
.collect::<Vec<_>>();
quote! {
impl<'a> AstNode<'a> for #type_ident {
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>> {
match self {
#(#match_arms,)*
}
}
fn for_each_child_mut(&'a mut self, mut f: &mut dyn FnMut(&'a mut dyn AstNode<'a>)) {
match self {
#(#mut_match_arms,)*
}
}
fn as_node_ref(&'a self) -> AstNodeRef<'a> {
AstNodeRef::#type_ident(&self)
}
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a> {
AstNodeRefMut::#type_ident(self)
}
}
}
}

View File

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

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,58 @@
use crate::spec::BuildSpec;
use leaf_enum_build_fn::make_leaf_enum_build_fn;
use leaf_struct_build_fn::make_leaf_struct_build_fn;
use node_production_build_fn::make_node_production_build_fn;
use polymorphic_enum_inner_build_build_fn::make_polymorphic_enum_inner_build_build_fn;
use polymorphic_enum_loop_build_fn::make_polymorphic_enum_loop_build_fn;
use polymorphic_leaf_enum_build_fn::make_polymorphic_leaf_enum_build_fn;
use polymorphic_pass_through_build_fn::make_polymorphic_pass_through_build_fn;
use polymorphic_tree_enum_build_fn::make_polymorphic_tree_enum_build_fn;
use polymorphic_type_build_fn::make_polymorphic_type_build_fn;
use proc_macro2::TokenStream;
use production_build_fn::make_production_build_fn;
use struct_build_fn::make_struct_build_fn;
use tree_enum_build_fn::make_enum_build_fn;
mod leaf_enum_build_fn;
mod leaf_struct_build_fn;
mod node_production_build_fn;
mod polymorphic_enum_inner_build_build_fn;
mod polymorphic_enum_loop_build_fn;
mod polymorphic_leaf_enum_build_fn;
mod polymorphic_pass_through_build_fn;
mod polymorphic_tree_enum_build_fn;
mod polymorphic_type_build_fn;
mod production_build_fn;
mod struct_build_fn;
mod tree_enum_build_fn;
pub fn make_build_fn(build_spec: &BuildSpec) -> TokenStream {
match build_spec {
BuildSpec::Struct(struct_spec) => make_struct_build_fn(struct_spec),
BuildSpec::LeafStruct(leaf_struct_spec) => make_leaf_struct_build_fn(leaf_struct_spec),
BuildSpec::Enum(enum_spec) => make_enum_build_fn(enum_spec),
BuildSpec::LeafEnum(leaf_enum_spec) => make_leaf_enum_build_fn(leaf_enum_spec),
BuildSpec::Production(production_spec) => make_production_build_fn(production_spec),
BuildSpec::NodeProduction(node_production_spec) => {
make_node_production_build_fn(node_production_spec)
}
BuildSpec::PolymorphicType(polymorphic_type_spec) => {
make_polymorphic_type_build_fn(polymorphic_type_spec)
}
BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop_spec) => {
make_polymorphic_enum_loop_build_fn(polymorphic_enum_loop_spec)
}
BuildSpec::PolymorphicPassThrough(polymorphic_pass_through_spec) => {
make_polymorphic_pass_through_build_fn(polymorphic_pass_through_spec)
}
BuildSpec::PolymorphicEnumInnerBuild(polymorphic_enum_inner_build_spec) => {
make_polymorphic_enum_inner_build_build_fn(polymorphic_enum_inner_build_spec)
}
BuildSpec::PolymorphicLeafEnum(polymorphic_leaf_enum_spec) => {
make_polymorphic_leaf_enum_build_fn(polymorphic_leaf_enum_spec)
}
BuildSpec::PolymorphicTreeEnum(polymorphic_tree_enum_spec) => {
make_polymorphic_tree_enum_build_fn(polymorphic_tree_enum_spec)
}
}
}

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,30 @@
use crate::spec::polymorphic_enum_inner_build::PolymorphicEnumInnerBuild;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use crate::deserialize::util::{make_build_fn_name, make_build_pair};
pub fn make_polymorphic_enum_inner_build_build_fn(spec: &PolymorphicEnumInnerBuild) -> TokenStream {
let build_fn_ident = format_ident!("{}", make_build_fn_name(spec.name()));
let pair_ident = format_ident!("{}", make_build_pair(spec.name()));
let return_type_ident = format_ident!("{}", spec.name());
let rule_branches = spec.rules()
.map(|rule| {
let rule_ident = format_ident!("{}", rule.name());
let rule_build_fn_ident = format_ident!("{}", rule.with());
quote! {
Rule::#rule_ident => #rule_build_fn_ident(file_id, inner_pair)
}
})
.collect::<Vec<_>>();
quote! {
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
let inner_pair = #pair_ident.into_inner().next().unwrap();
match inner_pair.as_rule() {
#(#rule_branches,)*
_ => unreachable!()
}
}
}
}

View File

@ -0,0 +1,135 @@
use crate::deserialize::util::{make_build_fn_name, make_build_pair};
use crate::spec::polymorphic_enum_loop_spec::{
PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuild,
PolymorphicEnumLoopRuleBuildChild, PolymorphicEnumLoopRuleChildOnEach,
PolymorphicEnumLoopRulePassThrough,
};
use crate::spec::SpecialChildKind;
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
fn make_pass_through(pass_through: &PolymorphicEnumLoopRulePassThrough) -> TokenStream {
let rule_ident = format_ident!("{}", pass_through.name());
let inner_build_fn_ident = format_ident!("{}", pass_through.with());
quote! {
Rule::#rule_ident => {
result = Some(#inner_build_fn_ident(file_id, inner_pair))
}
}
}
fn make_on_each_child_build(child: &PolymorphicEnumLoopRuleChildOnEach) -> TokenStream {
let child_build_fn_ident = format_ident!("{}", make_build_fn_name(child.rule()));
quote! {
Box::new(#child_build_fn_ident(file_id, inner_pair))
}
}
fn make_build(
spec: &PolymorphicEnumLoopBuildSpec,
build: &PolymorphicEnumLoopRuleBuild,
) -> TokenStream {
let rule_ident = format_ident!("{}", build.name());
let variant_type_ident = format_ident!("{}", spec.name());
let return_type_ident = format_ident!("{}", spec.kind());
let variant_ident = format_ident!("{}", build.variant());
let on_each_child = build
.children()
.find(|child| match child {
PolymorphicEnumLoopRuleBuildChild::OnEach(_) => true,
_ => false,
})
.map(|child| match child {
PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => on_each,
_ => unreachable!(),
})
.unwrap();
let build_on_each_child = make_on_each_child_build(on_each_child);
let child_args = build
.children()
.map(|child| match child {
PolymorphicEnumLoopRuleBuildChild::UseCurrent(_) => {
quote! { Box::new(result.unwrap()) }
}
PolymorphicEnumLoopRuleBuildChild::OnEach(_) => quote! { on_each_child },
PolymorphicEnumLoopRuleBuildChild::Special(special_child) => {
match special_child.kind() {
SpecialChildKind::FileId => {
quote! { file_id }
}
SpecialChildKind::Range => {
quote! { Range { start: as_span.start(), end: as_span.end() } }
}
}
}
})
.collect::<Vec<_>>();
quote! {
Rule::#rule_ident => {
let on_each_child = #build_on_each_child;
let built = #variant_type_ident::new(#(#child_args),*);
result = Some(#return_type_ident::#variant_ident(built))
}
}
}
fn make_match_arm(
spec: &PolymorphicEnumLoopBuildSpec,
rule: &PolymorphicEnumLoopRule,
) -> TokenStream {
match rule {
PolymorphicEnumLoopRule::PassThrough(pass_through) => make_pass_through(pass_through),
PolymorphicEnumLoopRule::Build(build) => make_build(spec, build),
}
}
fn make_preamble(spec: &PolymorphicEnumLoopBuildSpec, pair_ident: &Ident) -> TokenStream {
if spec.rules().any(|rule| match rule {
PolymorphicEnumLoopRule::Build(build) => build.children().any(|child| match child {
PolymorphicEnumLoopRuleBuildChild::Special(_) => true,
_ => false,
}),
_ => false,
}) {
quote! { let as_span = #pair_ident.as_span(); }
} else {
quote! {}
}
}
pub fn make_polymorphic_enum_loop_build_fn(spec: &PolymorphicEnumLoopBuildSpec) -> TokenStream {
let build_fn_ident = format_ident!("{}", make_build_fn_name(spec.name()));
let pair_ident = format_ident!("{}", make_build_pair(spec.name()));
let return_type_ident = format_ident!("{}", spec.kind());
let preamble = make_preamble(spec, &pair_ident);
let iter_expr = if spec.reverse() {
quote! { #pair_ident.into_inner().rev() }
} else {
quote! { #pair_ident.into_inner() }
};
let match_arms = spec
.rules()
.map(|rule| make_match_arm(spec, rule))
.collect::<Vec<_>>();
quote! {
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
#preamble
let mut result: Option<#return_type_ident> = None;
for inner_pair in #iter_expr {
match inner_pair.as_rule() {
#(#match_arms,)*
_ => unreachable!("Unexpected parse rule: {:?} (inner pair: {:#?}", inner_pair.as_rule(), inner_pair),
}
}
result.unwrap()
}
}
}

View File

@ -0,0 +1,29 @@
use crate::spec::polymorphic_leaf_enum::PolymorphicLeafEnum;
use convert_case::{Case, Casing};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
pub fn make_polymorphic_leaf_enum_build_fn(spec: &PolymorphicLeafEnum) -> TokenStream {
let build_fn_ident = format_ident!("build_{}", spec.name().to_case(Case::Snake));
let pair_ident = format_ident!("{}_pair", spec.name().to_case(Case::Snake));
let return_type_ident = format_ident!("{}", spec.kind());
let child_matchers = spec.rules()
.map(|rule| {
let rule_ident = format_ident!("{}", rule);
quote! {
Rule::#rule_ident => #return_type_ident::#rule_ident
}
})
.collect::<Vec<_>>();
quote! {
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
let inner_pair = #pair_ident.into_inner().next().unwrap();
match inner_pair.as_rule() {
#(#child_matchers,)*
_ => unreachable!()
}
}
}
}

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,33 @@
use crate::spec::polymorphic_tree_enum_spec::PolymorphicTreeEnumSpec;
use convert_case::{Case, Casing};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
pub fn make_polymorphic_tree_enum_build_fn(spec: &PolymorphicTreeEnumSpec) -> TokenStream {
let build_fn_ident = format_ident!("build_{}", spec.name().to_case(Case::Snake));
let pair_ident = format_ident!("{}_pair", spec.name().to_case(Case::Snake));
let return_type_ident = format_ident!("{}", spec.kind());
let rule_matchers = spec
.rules()
.map(|rule| {
let rule_ident = format_ident!("{}", rule);
let inner_build_fn_ident = format_ident!("build_{}", rule.to_case(Case::Snake));
quote! {
Rule::#rule_ident => #return_type_ident::#rule_ident(
#inner_build_fn_ident(file_id, inner_pair)
)
}
})
.collect::<Vec<_>>();
quote! {
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
let inner_pair = #pair_ident.into_inner().next().unwrap();
match inner_pair.as_rule() {
#(#rule_matchers,)*
_ => unreachable!(),
}
}
}
}

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,273 @@
use crate::deserialize::util::{make_build_fn_name, make_build_pair};
use crate::spec::struct_spec::{
MemberChildBuild, NodeMemberBuild, StructChild, StructSpec, VecChild, VecChildBuild,
};
use crate::spec::{SpecialChild, SpecialChildKind};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
fn make_preamble(spec: &StructSpec, pair_ident: &Ident) -> TokenStream {
if spec.children().any(StructChild::is_special) {
quote! {
let as_span = #pair_ident.as_span();
}
} else {
quote! {}
}
}
fn make_special_children(spec: &StructSpec) -> Vec<TokenStream> {
spec.children()
.map(StructChild::unwrap_special)
.filter(Option::is_some)
.map(Option::unwrap)
.map(|special_child| {
let child_ident = format_ident!("{}", special_child.name());
match special_child.kind() {
SpecialChildKind::FileId => {
quote! {
let #child_ident = file_id
}
}
SpecialChildKind::Range => {
quote! {
let #child_ident = Range {
start: as_span.start(),
end: as_span.end()
}
}
}
}
})
.collect::<Vec<_>>()
}
fn make_vec_child_holder(vec_child: &VecChild) -> TokenStream {
let child_ident = format_ident!("{}", vec_child.name());
match vec_child.build() {
VecChildBuild::String(_) => {
quote! {
let mut #child_ident: Vec<String> = vec![]
}
}
VecChildBuild::Node(node_build) => {
let child_type_ident = format_ident!("{}", node_build.kind());
quote! {
let mut #child_ident: Vec<Box<#child_type_ident>> = vec![]
}
}
}
}
fn make_node_child_holder(name: &str, node_child: &NodeMemberBuild) -> TokenStream {
let child_ident = format_ident!("{}", name);
let child_type_ident = format_ident!("{}", node_child.kind());
quote! {
let mut #child_ident: Option<Box<#child_type_ident>> = None
}
}
fn make_boolean_child_holder(name: &str) -> TokenStream {
let child_ident = format_ident!("{}", name);
quote! {
let mut #child_ident: bool = false
}
}
fn make_child_holder(child_spec: &StructChild) -> Option<TokenStream> {
match child_spec {
StructChild::SkipChild(_) => None,
StructChild::VecChild(vec_child) => Some(make_vec_child_holder(vec_child)),
StructChild::MemberChild(member_child) => match member_child.build() {
MemberChildBuild::Node(node_child) => {
Some(make_node_child_holder(member_child.name(), node_child))
}
MemberChildBuild::Boolean(_) => Some(make_boolean_child_holder(member_child.name())),
},
StructChild::Special(_) => None,
}
}
fn make_vec_child_match_action(vec_child: &VecChild) -> TokenStream {
let child_name_ident = format_ident!("{}", vec_child.name());
match vec_child.build() {
VecChildBuild::String(string_build) => {
let build_fn_ident = format_ident!("{}", string_build.with());
quote! {
#child_name_ident.push(#build_fn_ident(file_id, inner_pair))
}
}
VecChildBuild::Node(node_build) => {
let build_fn_ident = format_ident!("{}", node_build.with());
quote! {
#child_name_ident.push(Box::new(#build_fn_ident(file_id, inner_pair)))
}
}
}
}
fn make_node_member_child_match_action(name: &str, node_child: &NodeMemberBuild) -> TokenStream {
let child_name_ident = format_ident!("{}", name);
let build_fn_ident = format_ident!("{}", node_child.with());
quote! {
#child_name_ident = Some(Box::new(#build_fn_ident(file_id, inner_pair)))
}
}
fn make_boolean_member_child_match_action(name: &str) -> TokenStream {
let child_name_ident = format_ident!("{}", name);
quote! {
#child_name_ident = true
}
}
fn make_rule_matcher(child_spec: &StructChild) -> Option<TokenStream> {
match child_spec {
StructChild::SkipChild(skip_child) => {
let rule_ident = format_ident!("{}", skip_child.rule());
Some(quote! {
Rule::#rule_ident => {}
})
}
StructChild::VecChild(vec_child) => {
let rule_ident = format_ident!("{}", vec_child.rule());
let action = make_vec_child_match_action(vec_child);
Some(quote! {
Rule::#rule_ident => { #action }
})
}
StructChild::MemberChild(member_child) => match member_child.build() {
MemberChildBuild::Node(node_member_build) => {
let rule_ident = format_ident!("{}", member_child.rule());
let action =
make_node_member_child_match_action(member_child.name(), node_member_build);
Some(quote! {
Rule::#rule_ident => { #action }
})
}
MemberChildBuild::Boolean(_) => {
let rule_ident = format_ident!("{}", member_child.rule());
let action = make_boolean_member_child_match_action(member_child.name());
Some(quote! {
Rule::#rule_ident => { #action }
})
}
},
StructChild::Special(_) => None,
}
}
fn make_vec_child_arg(vec_child: &VecChild) -> TokenStream {
let child_ident = format_ident!("{}", vec_child.name());
quote! { #child_ident }
}
fn make_node_member_child_arg(
name: &str,
optional: bool,
node_child: &NodeMemberBuild,
) -> TokenStream {
let child_ident = format_ident!("{}", name);
if optional {
quote! { #child_ident }
} else if let Some(or_else) = node_child.or_else() {
let child_type_ident = format_ident!("{}", node_child.kind());
let or_else_ident = format_ident!("{}", or_else);
quote! {
#child_ident.unwrap_or_else(|| Box::new(#child_type_ident::#or_else_ident()))
}
} else {
quote! { #child_ident.unwrap() }
}
}
fn make_boolean_member_child_arg(name: &str) -> TokenStream {
let child_ident = format_ident!("{}", name);
quote! { #child_ident }
}
fn make_special_child_arg(special_child: &SpecialChild) -> TokenStream {
let child_ident = format_ident!("{}", special_child.name());
quote! { #child_ident }
}
fn make_child_arg(child_spec: &StructChild, pair_ident: &Ident) -> Option<TokenStream> {
match child_spec {
StructChild::SkipChild(_) => None,
StructChild::VecChild(vec_child) => Some(make_vec_child_arg(vec_child)),
StructChild::MemberChild(member_child) => match member_child.build() {
MemberChildBuild::Node(node_member_build) => Some(make_node_member_child_arg(
member_child.name(),
member_child.optional(),
node_member_build,
)),
MemberChildBuild::Boolean(_) => {
Some(make_boolean_member_child_arg(member_child.name()))
}
},
StructChild::Special(special_child) => Some(make_special_child_arg(special_child)),
}
}
fn make_return_value_stream(build_spec: &StructSpec, pair_ident: &Ident) -> TokenStream {
let type_ident = format_ident!("{}", build_spec.build());
let child_args = build_spec
.children()
.map(|child| make_child_arg(child, pair_ident))
.filter(|child_arg| child_arg.is_some())
.map(|child_arg| child_arg.unwrap())
.collect::<Vec<_>>();
quote! {
#type_ident::new(
#(#child_args,)*
)
}
}
pub fn make_struct_build_fn(build_spec: &StructSpec) -> TokenStream {
let build_fn_ident = format_ident!("{}", make_build_fn_name(build_spec.build()));
let pair_ident = format_ident!("{}", make_build_pair(build_spec.build()));
let return_type_ident = format_ident!("{}", build_spec.build());
let preamble = make_preamble(build_spec, &pair_ident);
let special_children = make_special_children(build_spec);
let child_holders = build_spec
.children()
.map(|child_spec| make_child_holder(child_spec))
.filter(|child_holder| child_holder.is_some())
.map(|child_holder| child_holder.unwrap())
.collect::<Vec<_>>();
let rule_matchers = build_spec
.children()
.map(|child_spec| make_rule_matcher(child_spec))
.collect::<Vec<_>>();
let iter_stream = quote! {
for inner_pair in #pair_ident.into_inner() {
match inner_pair.as_rule() {
#(#rule_matchers)*
_ => panic!("Unexpected rule: {:?}", inner_pair.as_rule())
}
}
};
let new_stream = make_return_value_stream(build_spec, &pair_ident);
quote! {
fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident {
#preamble
#(#special_children;)*
#(#child_holders;)*
#iter_stream
#new_stream
}
}
}

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,108 @@
mod leaf_enum_spec;
mod leaf_struct_spec;
mod node_production_spec;
mod polymorphic_enum_build_inner;
mod polymorphic_enum_loop_spec;
mod polymorphic_leaf_enum;
mod polymorphic_pass_through_spec;
mod polymorphic_tree_enum;
mod polymorphic_type_spec;
mod production_spec;
mod struct_spec;
mod tree_enum_spec;
pub(crate) mod util;
use crate::deserialize::leaf_enum_spec::deserialize_leaf_enum;
use crate::deserialize::leaf_struct_spec::deserialize_leaf_struct;
use crate::deserialize::node_production_spec::deserialize_node_production;
use crate::deserialize::polymorphic_enum_build_inner::deserialize_polymorphic_enum_inner_build;
use crate::deserialize::polymorphic_enum_loop_spec::deserialize_polymorphic_enum_loop;
use crate::deserialize::polymorphic_leaf_enum::deserialize_polymorphic_leaf_enum;
use crate::deserialize::polymorphic_pass_through_spec::deserialize_polymorphic_pass_through;
use crate::deserialize::polymorphic_type_spec::deserialize_polymorphic_type;
use crate::deserialize::production_spec::deserialize_production;
use crate::deserialize::struct_spec::deserialize_struct_spec;
use crate::deserialize::tree_enum_spec::deserialize_tree_enum;
use crate::spec::BuildSpec;
use yaml_rust2::{Yaml, YamlLoader};
use crate::deserialize::polymorphic_tree_enum::deserialize_polymorphic_tree_enum;
fn deserialize_build_spec(build_spec_name: &str, build_spec: &Yaml) -> BuildSpec {
if build_spec["struct"].is_hash() {
BuildSpec::Struct(deserialize_struct_spec(
build_spec_name,
&build_spec["struct"],
))
} else if build_spec["leaf_struct"].is_hash() {
BuildSpec::LeafStruct(deserialize_leaf_struct(
build_spec_name,
&build_spec["leaf_struct"],
))
} else if build_spec["tree_enum"].is_hash() {
BuildSpec::Enum(deserialize_tree_enum(
build_spec_name,
&build_spec["tree_enum"],
))
} else if build_spec["leaf_enum"].is_hash() {
BuildSpec::LeafEnum(deserialize_leaf_enum(
build_spec_name,
&build_spec["leaf_enum"],
))
} else if build_spec["production"].is_hash() {
BuildSpec::Production(deserialize_production(
build_spec_name,
&build_spec["production"],
))
} else if build_spec["node_production"].is_hash() {
BuildSpec::NodeProduction(deserialize_node_production(
build_spec_name,
&build_spec["node_production"],
))
} else if build_spec["polymorphic_type"].is_hash() {
BuildSpec::PolymorphicType(deserialize_polymorphic_type(
build_spec_name,
&build_spec["polymorphic_type"],
))
} else if build_spec["polymorphic_enum_loop_build"].is_hash() {
BuildSpec::PolymorphicEnumLoop(deserialize_polymorphic_enum_loop(
build_spec_name,
&build_spec["polymorphic_enum_loop_build"],
))
} else if build_spec["polymorphic_pass_through"].is_hash() {
BuildSpec::PolymorphicPassThrough(deserialize_polymorphic_pass_through(
build_spec_name,
&build_spec["polymorphic_pass_through"],
))
} else if build_spec["polymorphic_enum_inner_build"].is_hash() {
BuildSpec::PolymorphicEnumInnerBuild(deserialize_polymorphic_enum_inner_build(
build_spec_name,
&build_spec["polymorphic_enum_inner_build"],
))
} else if build_spec["polymorphic_leaf_enum"].is_hash() {
BuildSpec::PolymorphicLeafEnum(deserialize_polymorphic_leaf_enum(
build_spec_name,
&build_spec["polymorphic_leaf_enum"],
))
} else if build_spec["polymorphic_tree_enum"].is_hash() {
BuildSpec::PolymorphicTreeEnum(deserialize_polymorphic_tree_enum(
build_spec_name,
&build_spec["polymorphic_tree_enum"],
))
} else {
panic!("Missing or incorrect build type for {}", build_spec_name);
}
}
pub fn deserialize_yaml_spec(yaml: &str) -> Vec<BuildSpec> {
let docs = YamlLoader::load_from_str(yaml).unwrap();
let doc = &docs[0];
doc.as_hash()
.unwrap()
.iter()
.map(|(build_spec_name, build_spec)| {
let name_as_str = build_spec_name.as_str().unwrap();
deserialize_build_spec(name_as_str, build_spec)
})
.collect()
}

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,59 @@
use crate::deserialize::util::{make_build_fn_name, unwrap_single_member_hash};
use crate::spec::polymorphic_enum_inner_build::{
PolymorphicEnumInnerBuild, PolymorphicEnumInnerBuildMember,
PolymorphicEnumInnerBuildMemberKind, PolymorphicEnumInnerBuildRule,
};
use yaml_rust2::Yaml;
pub fn deserialize_polymorphic_enum_inner_build(
name: &str,
props: &Yaml,
) -> PolymorphicEnumInnerBuild {
let members = props["members"]
.as_vec()
.unwrap()
.iter()
.map(|member| {
if let Some(name) = member.as_str() {
PolymorphicEnumInnerBuildMember::new(
name,
PolymorphicEnumInnerBuildMemberKind::Struct,
)
} else if member.is_hash() {
let (name, props) = unwrap_single_member_hash(member);
if let Some(kind) = props["kind"].as_str() {
match kind {
"leaf" => PolymorphicEnumInnerBuildMember::new(
&name,
PolymorphicEnumInnerBuildMemberKind::Leaf,
),
"struct" => PolymorphicEnumInnerBuildMember::new(
&name,
PolymorphicEnumInnerBuildMemberKind::Struct,
),
_ => panic!(),
}
} else {
panic!()
}
} else {
panic!()
}
})
.collect::<Vec<_>>();
let rules = props["rules"]
.as_vec()
.unwrap()
.iter()
.map(|rule| {
let rule_as_string = rule.as_str().unwrap().to_string();
PolymorphicEnumInnerBuildRule::new(
&rule_as_string,
&make_build_fn_name(&rule_as_string),
)
})
.collect::<Vec<_>>();
PolymorphicEnumInnerBuild::new(name, members, rules)
}

View File

@ -0,0 +1,102 @@
use crate::deserialize::util::{get_as_bool, make_build_fn_name, unwrap_single_member_hash};
use crate::spec::polymorphic_enum_loop_spec::{
PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopChildUseCurrent, PolymorphicEnumLoopRule,
PolymorphicEnumLoopRuleBuild, PolymorphicEnumLoopRuleBuildChild,
PolymorphicEnumLoopRuleChildOnEach, PolymorphicEnumLoopRulePassThrough,
};
use crate::spec::{SpecialChild, SpecialChildKind, StructField};
use yaml_rust2::Yaml;
fn deserialize_build_child(child_name: &str, props: &Yaml) -> PolymorphicEnumLoopRuleBuildChild {
if props["use_current"].is_hash() {
let kind = props["use_current"]["kind"].as_str().unwrap();
PolymorphicEnumLoopRuleBuildChild::UseCurrent(PolymorphicEnumLoopChildUseCurrent::new(
child_name, kind,
))
} else if props["on_each"].is_hash() {
let rule = props["on_each"]["rule"].as_str().unwrap();
PolymorphicEnumLoopRuleBuildChild::OnEach(PolymorphicEnumLoopRuleChildOnEach::new(
child_name, rule,
))
} else if props["special"].is_hash() {
let kind = match props["special"]["kind"].as_str().unwrap() {
"range" => SpecialChildKind::Range,
"file_id" => SpecialChildKind::FileId,
_ => panic!("Unknown special child kind"),
};
PolymorphicEnumLoopRuleBuildChild::Special(SpecialChild::new(child_name, kind))
} else {
panic!("Expected 'use_current' or 'on_each' hash for polymorphic enum loop build child");
}
}
fn deserialize_build(name: &str, props: &Yaml) -> PolymorphicEnumLoopRuleBuild {
let variant = props["variant"].as_str().unwrap();
let children = props["children"]
.as_vec()
.unwrap()
.iter()
.map(|child_yaml| {
let (child_name, child_props) = unwrap_single_member_hash(child_yaml);
deserialize_build_child(&child_name, child_props)
})
.map(Box::new)
.collect();
let fields = props["fields"]
.as_vec()
.map(|fields| {
fields.iter()
.map(|field_yaml| {
let (field_name, field_props) = unwrap_single_member_hash(field_yaml);
let kind = field_props["kind"].as_str().unwrap();
StructField::new(
&field_name,
kind,
None,
false
)
})
.collect::<Vec<_>>()
})
.unwrap_or_else(|| vec![]);
PolymorphicEnumLoopRuleBuild::new(name, variant, children, fields)
}
fn deserialize_pass_through(name: &str, props: &Yaml) -> PolymorphicEnumLoopRulePassThrough {
let kind = props["kind"].as_str().unwrap();
let with = make_build_fn_name(props["with"].as_str().unwrap_or(kind));
PolymorphicEnumLoopRulePassThrough::new(name, kind, &with)
}
fn deserialize_rule(rule_name: &str, props: &Yaml) -> PolymorphicEnumLoopRule {
if props["pass_through"].is_hash() {
PolymorphicEnumLoopRule::PassThrough(deserialize_pass_through(
rule_name,
&props["pass_through"],
))
} else if props["build"].is_hash() {
PolymorphicEnumLoopRule::Build(deserialize_build(rule_name, &props["build"]))
} else {
panic!("Polymorphic enum loop rule must have 'pass_through' or 'build' hash.");
}
}
pub fn deserialize_polymorphic_enum_loop(name: &str, props: &Yaml) -> PolymorphicEnumLoopBuildSpec {
let kind = props["kind"].as_str().unwrap();
let reverse = get_as_bool(&props["reverse"]);
let rules = props["rules"]
.as_vec()
.unwrap()
.iter()
.map(|rule_yaml| {
let (rule_name, rule_props) = unwrap_single_member_hash(rule_yaml);
deserialize_rule(&rule_name, rule_props)
})
.map(Box::new)
.collect();
PolymorphicEnumLoopBuildSpec::new(name, kind, reverse, rules)
}

View File

@ -0,0 +1,13 @@
use yaml_rust2::Yaml;
use crate::spec::polymorphic_leaf_enum::PolymorphicLeafEnum;
pub fn deserialize_polymorphic_leaf_enum(name: &str, props: &Yaml) -> PolymorphicLeafEnum {
let kind = props["kind"].as_str().unwrap();
let rules = props["rules"].as_vec().unwrap()
.iter()
.map(|rule| {
rule.as_str().unwrap().to_string()
})
.collect::<Vec<_>>();
PolymorphicLeafEnum::new(name, kind, rules)
}

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,12 @@
use yaml_rust2::Yaml;
use crate::spec::polymorphic_tree_enum_spec::PolymorphicTreeEnumSpec;
pub fn deserialize_polymorphic_tree_enum(name: &str, props: &Yaml) -> PolymorphicTreeEnumSpec {
let kind = props["kind"].as_str().unwrap();
let rules = props["rules"].as_vec().unwrap()
.iter()
.map(|rule| rule.as_str().unwrap())
.collect::<Vec<_>>();
PolymorphicTreeEnumSpec::new(name, kind, &rules)
}

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

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,64 @@ fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) {
println!("*** BuildSpec ***");
match build_spec {
BuildSpec::Enum(enum_build_spec) => {
println!("Spec name: {}", enum_build_spec.name());
println!("Enum Spec - build: {}", enum_build_spec.build());
}
BuildSpec::LeafEnum(leaf_enum_build_spec) => {
println!("Leaf Enum Spec - build: {}", leaf_enum_build_spec.build());
}
BuildSpec::Struct(struct_build_spec) => {
println!("Spec name: {}", struct_build_spec.name());
println!("Struct Spec - build: {}", struct_build_spec.build());
}
BuildSpec::LeafStruct(leaf_struct_build_spec) => {
println!(
"Leaf Struct Spec - build: {}",
leaf_struct_build_spec.build()
);
}
BuildSpec::Production(production_build_spec) => {
println!("Production Spec - name: {}", production_build_spec.name());
}
BuildSpec::NodeProduction(node_production_build_spec) => {
println!(
"Node Production Spec - name: {}",
node_production_build_spec.name()
);
}
BuildSpec::PolymorphicType(polymorphic_build_spec) => {
println!(
"Polymorphic Type Spec - name: {}",
polymorphic_build_spec.name()
);
}
BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop_build_spec) => {
println!(
"Polymorphic Enum Loop Spec - name: {}",
polymorphic_enum_loop_build_spec.name()
);
}
BuildSpec::PolymorphicPassThrough(pass_through_build_spec) => {
println!(
"Polymorphic Pass Through Spec - name: {}",
pass_through_build_spec.name()
);
}
BuildSpec::PolymorphicEnumInnerBuild(polymorphic_enum_inner_build_spec) => {
println!(
"Polymorphic Enum Inner Build Spec - name: {}",
polymorphic_enum_inner_build_spec.name()
);
}
BuildSpec::PolymorphicLeafEnum(polymorphic_leaf_enum) => {
println!(
"Polymorphic Leaf Enum Build Spec - name: {}",
polymorphic_leaf_enum.name()
);
}
BuildSpec::PolymorphicTreeEnum(polymorphic_tree_enum) => {
println!(
"Polymorphic Tree Enum Build Spec - name: {}",
polymorphic_tree_enum.name()
);
}
}
println!("{:#?}", token_stream);
@ -25,30 +89,191 @@ fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) {
println!("{}", prettyplease::unparse(&parsed));
}
pub fn test_dump() -> String {
let build_specs = deserialize::deserialize_yaml_spec(include_str!("../../src/parser/ast.yaml"));
let mut streams: Vec<TokenStream> = vec![];
pub struct AstGeneratedFile {
pub name: String,
pub contents: String,
}
for build_spec in &build_specs {
let type_stream = make_type(build_spec);
debug_built_spec(build_spec, &type_stream);
streams.push(type_stream);
}
for build_spec in &build_specs {
match build_spec {
BuildSpec::Enum(_) => {}
BuildSpec::Struct(struct_spec) => {
let struct_build_fn_stream = make_struct_build_fn(struct_spec);
debug_built_spec(build_spec, &struct_build_fn_stream);
streams.push(struct_build_fn_stream);
}
}
}
let combined = quote! {
#(#streams)*
};
let file: File = syn::parse2(combined).unwrap();
fn token_stream_to_string(token_stream: TokenStream) -> String {
let file: File = syn::parse2(token_stream).unwrap();
prettyplease::unparse(&file)
}
fn generate_build_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
let build_fns = build_specs
.iter()
.map(|build_spec| {
let build_fn = make_build_fn(build_spec);
debug_built_spec(build_spec, &build_fn);
build_fn
})
.collect::<Vec<_>>();
let combined = quote! {
use crate::parser::Rule;
use pest::iterators::Pair;
use crate::ast::node::*;
use std::range::Range;
#(#build_fns)*
};
AstGeneratedFile {
name: String::from("build.rs"),
contents: token_stream_to_string(combined),
}
}
fn generate_node_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
let types = build_specs
.iter()
.map(|build_spec| make_type(build_spec))
.filter(Option::is_some)
.collect::<Vec<_>>();
let combined = quote! {
use std::range::Range;
use std::rc::Rc;
use std::cell::RefCell;
use crate::name_analysis::symbol::*;
use crate::type_analysis::kinds::*;
#(#types)*
};
AstGeneratedFile {
name: String::from("node.rs"),
contents: token_stream_to_string(combined),
}
}
fn generate_pretty_print_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
let impls = build_specs
.iter()
.map(|build_spec| {
let maybe_stream = make_pretty_print_impl(build_spec);
if let Some(stream) = &maybe_stream {
debug_built_spec(build_spec, &stream);
}
maybe_stream
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let combined = quote! {
use crate::ast::node::*;
#(#impls)*
};
AstGeneratedFile {
name: String::from("pretty_print.rs"),
contents: token_stream_to_string(combined),
}
}
fn generate_ast_node_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
let impls = build_specs
.iter()
.map(|build_spec| {
let maybe_stream = make_ast_node_impl(build_spec);
if let Some(stream) = &maybe_stream {
debug_built_spec(build_spec, &stream);
}
maybe_stream
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let ast_enum_members = build_specs
.iter()
.map(|build_spec| make_ast_enum_member(build_spec))
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let ast_enum_mut_members = build_specs
.iter()
.map(|build_spec| make_ast_enum_mut_member(build_spec))
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let inner_unwrappers = build_specs
.iter()
.map(|build_spec| make_ast_node_ref_unwrapper(build_spec))
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let inner_mut_unwrappers = build_specs
.iter()
.map(|build_spec| make_ast_node_ref_mut_unwrapper(build_spec))
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let combined = quote! {
use crate::ast::node::*;
pub enum AstNodeRef<'a> {
#(#ast_enum_members,)*
}
impl<'a> AstNodeRef<'a> {
pub fn inner(&self) -> &dyn AstNode<'a> {
match self {
#(#inner_unwrappers,)*
}
}
}
pub enum AstNodeRefMut<'a> {
#(#ast_enum_mut_members,)*
}
impl<'a> AstNodeRefMut<'a> {
pub fn inner(&mut self) -> &mut dyn AstNode<'a> {
match self {
#(#inner_mut_unwrappers,)*
}
}
}
pub trait AstNode<'a> {
fn children(&'a self) -> Vec<&'a dyn AstNode<'a>>;
fn for_each_child_mut(&'a mut self, f: &mut dyn FnMut(&'a mut dyn AstNode<'a>));
fn as_node_ref(&'a self) -> AstNodeRef<'a>;
fn as_node_ref_mut(&'a mut self) -> AstNodeRefMut<'a>;
}
#(#impls)*
};
AstGeneratedFile {
name: String::from("ast_node.rs"),
contents: token_stream_to_string(combined),
}
}
fn generate_walk_file(build_specs: &[BuildSpec]) -> AstGeneratedFile {
let stream = make_walk_fn(build_specs);
AstGeneratedFile {
name: String::from("walk.rs"),
contents: token_stream_to_string(stream),
}
}
pub fn get_build_specs(yaml: &str) -> Vec<BuildSpec> {
deserialize_yaml_spec(yaml)
}
pub fn generate_files(build_specs: &[BuildSpec]) -> Vec<AstGeneratedFile> {
vec![
generate_build_file(build_specs),
generate_node_file(build_specs),
generate_pretty_print_file(build_specs),
generate_ast_node_file(build_specs),
generate_walk_file(build_specs),
]
}

View File

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

View File

@ -0,0 +1,347 @@
use crate::spec::leaf_enum_spec::LeafEnumBuildSpec;
use crate::spec::leaf_struct_spec::{LeafStructBuildSpec, LeafStructMemberKind};
use crate::spec::polymorphic_enum_inner_build::{
PolymorphicEnumInnerBuild, PolymorphicEnumInnerBuildMemberKind,
};
use crate::spec::polymorphic_enum_loop_spec::{
PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuildChild,
};
use crate::spec::polymorphic_type_spec::PolymorphicTypeBuildSpec;
use crate::spec::struct_spec::{MemberChildBuild, StructChild, StructSpec, VecChildBuild};
use crate::spec::tree_enum_spec::{EnumRuleChildKind, TreeEnumBuildSpec};
use crate::spec::BuildSpec;
use convert_case::{Case, Casing};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
fn make_result() -> TokenStream {
quote! { std::fmt::Result }
}
fn make_polymorphic_enum_inner_build_p2_impl(spec: &PolymorphicEnumInnerBuild) -> TokenStream {
let type_ident = format_ident!("{}", spec.name());
let result = make_result();
let type_string = spec.name();
let child_matchers = spec
.members()
.map(|member| {
let variant_ident = format_ident!("{}", member.name());
match member.kind() {
PolymorphicEnumInnerBuildMemberKind::Leaf => {
let variant_string = member.name();
quote! {
#type_ident::#variant_ident => writer.writeln_indented(#variant_string)?
}
}
PolymorphicEnumInnerBuildMemberKind::Struct => {
let child_ident = format_ident!("{}", member.name().to_case(Case::Snake));
quote! {
#type_ident::#variant_ident(#child_ident) => {
#child_ident.pretty_print(writer)?;
}
}
}
}
})
.collect::<Vec<_>>();
quote! {
impl PrettyPrint for #type_ident {
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
writer.writeln_indented(#type_string)?;
writer.increase_indent();
match self {
#(#child_matchers,)*
_ => {}
}
writer.decrease_indent();
Ok(())
}
}
}
}
fn make_polymorphic_enum_loop_p2_impl(spec: &PolymorphicEnumLoopBuildSpec) -> TokenStream {
let type_ident = format_ident!("{}", spec.name());
let type_string = spec.name();
let build = spec
.rules()
.find(|rule| match rule {
PolymorphicEnumLoopRule::Build(_) => true,
_ => false,
})
.map(|rule| match rule {
PolymorphicEnumLoopRule::Build(build) => build,
_ => unreachable!(),
})
.unwrap();
let child_print_statements = build
.children()
.filter(|child| match child {
PolymorphicEnumLoopRuleBuildChild::Special(_) => false,
_ => true,
})
.map(|child| {
let child_ident = match child {
PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => {
format_ident!("{}", use_current.name())
}
PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => {
format_ident!("{}", on_each.name())
}
_ => unreachable!(),
};
quote! {
self.#child_ident().pretty_print(writer)?;
}
})
.collect::<Vec<_>>();
let result = make_result();
quote! {
impl PrettyPrint for #type_ident {
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
writer.writeln_indented(#type_string)?;
writer.increase_indent();
#(#child_print_statements)*
writer.decrease_indent();
Ok(())
}
}
}
}
fn make_polymorphic_type_p2_impl(spec: &PolymorphicTypeBuildSpec) -> TokenStream {
let type_ident = format_ident!("{}", spec.name());
let child_matchers = spec
.variants()
.map(|member| {
let enum_member_ident = format_ident!("{}", member.name());
let inner_name = format_ident!("{}", member.inner_kind().to_case(Case::Snake));
quote! {
#type_ident::#enum_member_ident(#inner_name) => #inner_name.pretty_print(writer)
}
})
.collect::<Vec<_>>();
let result = make_result();
quote! {
impl PrettyPrint for #type_ident {
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
match self {
#(#child_matchers,)*
}
}
}
}
}
fn make_leaf_enum_p2_impl(spec: &LeafEnumBuildSpec) -> TokenStream {
let type_ident = format_ident!("{}", spec.build());
let child_matchers = spec
.rules()
.map(|rule| {
let enum_variant_ident = format_ident!("{}", rule);
let name_str = rule;
quote! {
#type_ident::#enum_variant_ident => writer.writeln_indented(#name_str)
}
})
.collect::<Vec<_>>();
let result = make_result();
quote! {
impl PrettyPrint for #type_ident {
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
match self {
#(#child_matchers,)*
}
}
}
}
}
fn make_tree_enum_p2_impl(spec: &TreeEnumBuildSpec) -> TokenStream {
let type_ident = format_ident!("{}", spec.build());
let type_str = spec.build();
let child_matchers = spec
.rules()
.map(|rule| {
let enum_variant_ident = format_ident!("{}", rule.rule());
if let Some(child) = rule.child() {
match child.kind() {
EnumRuleChildKind::Node(node_child) => {
let child_name_ident =
format_ident!("{}", node_child.node_kind().to_case(Case::Snake));
Some(quote! {
#type_ident::#enum_variant_ident(#child_name_ident) => {
#child_name_ident.pretty_print(writer)?;
}
})
}
_ => None,
}
} else {
let variant_str = rule.rule();
Some(quote! {
#type_ident::#enum_variant_ident => writer.writeln_indented(#variant_str)?
})
}
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let result = make_result();
quote! {
impl PrettyPrint for #type_ident {
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
writer.writeln_indented(#type_str)?;
writer.increase_indent();
match self {
#(#child_matchers,)*
_ => {}
}
writer.decrease_indent();
Ok(())
}
}
}
}
fn make_leaf_struct_p2_impl(leaf_struct_build_spec: &LeafStructBuildSpec) -> TokenStream {
let type_ident = format_ident!("{}", leaf_struct_build_spec.build());
let member_formatters = leaf_struct_build_spec
.members()
.map(|member| match member.kind() {
LeafStructMemberKind::String => Some("{}"),
LeafStructMemberKind::FileId => None,
LeafStructMemberKind::Range => None,
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>()
.join(", ");
let format_string = format!("{}({})", leaf_struct_build_spec.build(), member_formatters);
let members = leaf_struct_build_spec
.members()
.map(|member| match member.kind() {
LeafStructMemberKind::String => {
let member_ident = format_ident!("{}", member.name());
Some(quote! {
self.#member_ident()
})
}
_ => None,
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let result = make_result();
quote! {
impl PrettyPrint for #type_ident {
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
writer.writeln_indented(&format!(#format_string, #(#members),*))
}
}
}
}
fn make_struct_p2_impl(struct_build_spec: &StructSpec) -> TokenStream {
let child_print_statements = struct_build_spec
.children()
.map(|child| match child {
StructChild::SkipChild(_) => None,
StructChild::VecChild(vec_child) => match vec_child.build() {
VecChildBuild::Node(_) => {
let child_ident = format_ident!("{}", vec_child.name());
Some(quote! {
for child in self.#child_ident() {
child.pretty_print(writer)?;
}
})
}
VecChildBuild::String(_) => None,
},
StructChild::MemberChild(member_child) => match member_child.build() {
MemberChildBuild::Node(_) => {
let child_ident = format_ident!("{}", member_child.name());
if member_child.optional() {
Some(quote! {
if let Some(child) = self.#child_ident() {
child.pretty_print(writer)?;
}
})
} else {
Some(quote! {
self.#child_ident().pretty_print(writer)?;
})
}
}
MemberChildBuild::Boolean(_) => {
let format_string = format!("{}({})", member_child.name(), "{}");
let child_ident = format_ident!("{}", member_child.name());
Some(quote! {
writer.writeln_indented(&format!(#format_string, self.#child_ident()))?;
})
}
},
StructChild::Special(_) => None,
})
.filter(Option::is_some)
.map(Option::unwrap)
.collect::<Vec<_>>();
let type_ident = format_ident!("{}", struct_build_spec.build());
let type_string = struct_build_spec.build();
let result = make_result();
quote! {
impl PrettyPrint for #type_ident {
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
writer.writeln_indented(#type_string)?;
writer.increase_indent();
#(#child_print_statements)*
writer.decrease_indent();
Ok(())
}
}
}
}
pub fn make_pretty_print_impl(build_spec: &BuildSpec) -> Option<TokenStream> {
match build_spec {
BuildSpec::Struct(struct_spec) => Some(make_struct_p2_impl(struct_spec)),
BuildSpec::LeafStruct(leaf_struct) => Some(make_leaf_struct_p2_impl(leaf_struct)),
BuildSpec::Enum(enum_spec) => Some(make_tree_enum_p2_impl(enum_spec)),
BuildSpec::LeafEnum(leaf_enum) => Some(make_leaf_enum_p2_impl(leaf_enum)),
BuildSpec::Production(_) => None,
BuildSpec::NodeProduction(_) => None,
BuildSpec::PolymorphicType(polymorphic_type) => {
Some(make_polymorphic_type_p2_impl(polymorphic_type))
}
BuildSpec::PolymorphicPassThrough(_) => None,
BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop) => {
Some(make_polymorphic_enum_loop_p2_impl(polymorphic_enum_loop))
}
BuildSpec::PolymorphicEnumInnerBuild(polymorphic_enum_inner_build) => Some(
make_polymorphic_enum_inner_build_p2_impl(polymorphic_enum_inner_build),
),
BuildSpec::PolymorphicLeafEnum(_) => None,
BuildSpec::PolymorphicTreeEnum(_) => None,
}
}

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,109 @@
use leaf_enum_spec::LeafEnumBuildSpec;
use leaf_struct_spec::LeafStructBuildSpec;
use node_production_spec::NodeProductionBuildSpec;
use polymorphic_enum_inner_build::PolymorphicEnumInnerBuild;
use polymorphic_enum_loop_spec::PolymorphicEnumLoopBuildSpec;
use polymorphic_leaf_enum::PolymorphicLeafEnum;
use polymorphic_pass_through_spec::PolymorphicPassThroughBuildSpec;
use polymorphic_tree_enum_spec::PolymorphicTreeEnumSpec;
use polymorphic_type_spec::PolymorphicTypeBuildSpec;
use production_spec::ProductionBuildSpec;
use struct_spec::StructSpec;
use tree_enum_spec::TreeEnumBuildSpec;
pub(crate) mod leaf_enum_spec;
pub(crate) mod leaf_struct_spec;
pub(crate) mod node_production_spec;
pub(crate) mod polymorphic_enum_inner_build;
pub(crate) mod polymorphic_enum_loop_spec;
pub(crate) mod polymorphic_leaf_enum;
pub(crate) mod polymorphic_pass_through_spec;
pub(crate) mod polymorphic_tree_enum_spec;
pub(crate) mod polymorphic_type_spec;
pub(crate) mod production_spec;
pub(crate) mod struct_spec;
pub(crate) mod tree_enum_spec;
pub enum BuildSpec {
Enum(TreeEnumBuildSpec),
LeafEnum(LeafEnumBuildSpec),
Struct(StructSpec),
LeafStruct(LeafStructBuildSpec),
Production(ProductionBuildSpec),
NodeProduction(NodeProductionBuildSpec),
PolymorphicType(PolymorphicTypeBuildSpec),
PolymorphicEnumLoop(PolymorphicEnumLoopBuildSpec),
PolymorphicPassThrough(PolymorphicPassThroughBuildSpec),
PolymorphicEnumInnerBuild(PolymorphicEnumInnerBuild),
PolymorphicLeafEnum(PolymorphicLeafEnum),
PolymorphicTreeEnum(PolymorphicTreeEnumSpec),
}
#[derive(Debug)]
pub struct SpecialChild {
name: String,
kind: SpecialChildKind,
}
impl SpecialChild {
pub fn new(name: &str, kind: SpecialChildKind) -> Self {
Self {
name: name.to_string(),
kind,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &SpecialChildKind {
&self.kind
}
}
#[derive(Debug)]
pub enum SpecialChildKind {
FileId,
Range,
}
#[derive(Debug)]
pub struct StructField {
name: String,
kind: String,
wrap: Option<StructFieldWrap>,
vec: bool,
}
impl StructField {
pub fn new(name: &str, kind: &str, wrap: Option<StructFieldWrap>, vec: bool) -> Self {
Self {
name: name.to_string(),
kind: kind.to_string(),
wrap,
vec,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &str {
&self.kind
}
pub fn wrap(&self) -> Option<&StructFieldWrap> {
self.wrap.as_ref()
}
pub fn vec(&self) -> bool {
self.vec
}
}
#[derive(Debug)]
pub enum StructFieldWrap {
RcRefCell,
}

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,76 @@
pub struct PolymorphicEnumInnerBuild {
name: String,
members: Vec<PolymorphicEnumInnerBuildMember>,
rules: Vec<PolymorphicEnumInnerBuildRule>,
}
impl PolymorphicEnumInnerBuild {
pub fn new(name: &str, members: Vec<PolymorphicEnumInnerBuildMember>, rules: Vec<PolymorphicEnumInnerBuildRule>) -> Self {
Self {
name: name.to_string(),
members,
rules,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn members(&self) -> impl Iterator<Item = &PolymorphicEnumInnerBuildMember> {
self.members.iter()
}
pub fn rules(&self) -> impl Iterator<Item = &PolymorphicEnumInnerBuildRule> {
self.rules.iter()
}
}
pub struct PolymorphicEnumInnerBuildMember {
name: String,
kind: PolymorphicEnumInnerBuildMemberKind
}
impl PolymorphicEnumInnerBuildMember {
pub fn new(name: &str, kind: PolymorphicEnumInnerBuildMemberKind) -> Self {
Self {
name: name.to_string(),
kind
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &PolymorphicEnumInnerBuildMemberKind {
&self.kind
}
}
pub enum PolymorphicEnumInnerBuildMemberKind {
Leaf,
Struct
}
pub struct PolymorphicEnumInnerBuildRule {
name: String,
with: String,
}
impl PolymorphicEnumInnerBuildRule {
pub fn new(name: &str, with: &str) -> Self {
Self {
name: name.to_string(),
with: with.to_string(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn with(&self) -> &str {
&self.with
}
}

View File

@ -0,0 +1,162 @@
use crate::spec::{SpecialChild, StructField};
pub struct PolymorphicEnumLoopBuildSpec {
name: String,
kind: String,
reverse: bool,
rules: Vec<Box<PolymorphicEnumLoopRule>>,
}
impl PolymorphicEnumLoopBuildSpec {
pub fn new(
name: &str,
kind: &str,
reverse: bool,
rules: Vec<Box<PolymorphicEnumLoopRule>>,
) -> Self {
Self {
name: name.to_string(),
kind: kind.to_string(),
reverse,
rules,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &str {
&self.kind
}
pub fn reverse(&self) -> bool {
self.reverse
}
pub fn rules(&self) -> impl Iterator<Item = &PolymorphicEnumLoopRule> {
self.rules.iter().map(Box::as_ref)
}
}
pub enum PolymorphicEnumLoopRule {
PassThrough(PolymorphicEnumLoopRulePassThrough),
Build(PolymorphicEnumLoopRuleBuild),
}
pub struct PolymorphicEnumLoopRulePassThrough {
name: String,
kind: String,
with: String,
}
impl PolymorphicEnumLoopRulePassThrough {
pub fn new(name: &str, kind: &str, with: &str) -> Self {
Self {
name: name.to_string(),
kind: kind.to_string(),
with: with.to_string(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &str {
&self.kind
}
pub fn with(&self) -> &str {
&self.with
}
}
pub struct PolymorphicEnumLoopRuleBuild {
name: String,
variant: String,
children: Vec<Box<PolymorphicEnumLoopRuleBuildChild>>,
fields: Vec<StructField>,
}
impl PolymorphicEnumLoopRuleBuild {
pub fn new(
name: &str,
variant: &str,
children: Vec<Box<PolymorphicEnumLoopRuleBuildChild>>,
fields: Vec<StructField>,
) -> Self {
Self {
name: name.to_string(),
variant: variant.to_string(),
children,
fields,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn variant(&self) -> &str {
&self.variant
}
pub fn children(&self) -> impl Iterator<Item = &PolymorphicEnumLoopRuleBuildChild> {
self.children.iter().map(Box::as_ref)
}
pub fn fields(&self) -> &[StructField] {
&self.fields
}
}
pub enum PolymorphicEnumLoopRuleBuildChild {
UseCurrent(PolymorphicEnumLoopChildUseCurrent),
OnEach(PolymorphicEnumLoopRuleChildOnEach),
Special(SpecialChild),
}
pub struct PolymorphicEnumLoopChildUseCurrent {
name: String,
kind: String,
}
impl PolymorphicEnumLoopChildUseCurrent {
pub fn new(name: &str, kind: &str) -> Self {
Self {
name: name.to_string(),
kind: kind.to_string(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &str {
&self.kind
}
}
pub struct PolymorphicEnumLoopRuleChildOnEach {
name: String,
rule: String,
}
impl PolymorphicEnumLoopRuleChildOnEach {
pub fn new(name: &str, rule: &str) -> Self {
Self {
name: name.to_string(),
rule: rule.to_string(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn rule(&self) -> &str {
&self.rule
}
}

View File

@ -0,0 +1,27 @@
pub struct PolymorphicLeafEnum {
name: String,
kind: String,
rules: Vec<String>,
}
impl PolymorphicLeafEnum {
pub fn new(name: &str, kind: &str, rules: Vec<String>) -> Self {
Self {
name: name.to_string(),
kind: kind.to_string(),
rules,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &str {
&self.kind
}
pub fn rules(&self) -> impl Iterator<Item = &str> {
self.rules.iter().map(AsRef::as_ref)
}
}

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,27 @@
pub struct PolymorphicTreeEnumSpec {
name: String,
kind: String,
rules: Vec<String>,
}
impl PolymorphicTreeEnumSpec {
pub fn new(name: &str, kind: &str, rules: &[&str]) -> Self {
Self {
name: name.to_string(),
kind: kind.to_string(),
rules: rules.iter().map(ToString::to_string).collect(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &str {
&self.kind
}
pub fn rules(&self) -> impl Iterator<Item = &str> {
self.rules.iter().map(AsRef::as_ref)
}
}

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

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,44 @@
mod enum_type;
mod leaf_enum_type;
mod leaf_struct_type;
mod polymorphic_enum_inner_build_type;
mod polymorphic_enum_loop_type;
mod polymorphic_type_type;
mod struct_type;
use crate::spec::BuildSpec;
use crate::type_gen::enum_type::make_enum_type;
use crate::type_gen::leaf_enum_type::make_leaf_enum_type;
use crate::type_gen::leaf_struct_type::make_leaf_struct_type;
use crate::type_gen::polymorphic_enum_inner_build_type::make_polymorphic_enum_inner_build_type;
use crate::type_gen::polymorphic_enum_loop_type::make_polymorphic_enum_loop_type;
use crate::type_gen::polymorphic_type_type::make_polymorphic_type_type;
use crate::type_gen::struct_type::make_struct_type;
use proc_macro2::TokenStream;
pub fn make_type(build_spec: &BuildSpec) -> Option<TokenStream> {
match build_spec {
BuildSpec::Struct(struct_build_spec) => Some(make_struct_type(struct_build_spec)),
BuildSpec::LeafStruct(leaf_struct_build_spec) => {
Some(make_leaf_struct_type(leaf_struct_build_spec))
}
BuildSpec::Enum(enum_build_spec) => Some(make_enum_type(enum_build_spec)),
BuildSpec::LeafEnum(leaf_enum_build_spec) => {
Some(make_leaf_enum_type(leaf_enum_build_spec))
}
BuildSpec::Production(_) => None,
BuildSpec::NodeProduction(_) => None,
BuildSpec::PolymorphicType(polymorphic_build_spec) => {
Some(make_polymorphic_type_type(polymorphic_build_spec))
}
BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop_spec) => {
Some(make_polymorphic_enum_loop_type(polymorphic_enum_loop_spec))
}
BuildSpec::PolymorphicPassThrough(_) => None,
BuildSpec::PolymorphicEnumInnerBuild(polymorphic_enum_inner_build_spec) => Some(
make_polymorphic_enum_inner_build_type(polymorphic_enum_inner_build_spec),
),
BuildSpec::PolymorphicLeafEnum(_) => None,
BuildSpec::PolymorphicTreeEnum(_) => None,
}
}

View File

@ -0,0 +1,25 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use crate::spec::polymorphic_enum_inner_build::{PolymorphicEnumInnerBuild, PolymorphicEnumInnerBuildMemberKind};
pub fn make_polymorphic_enum_inner_build_type(spec: &PolymorphicEnumInnerBuild) -> TokenStream {
let members = spec.members()
.map(|member| {
let name_ident = format_ident!("{}", member.name());
match member.kind() {
PolymorphicEnumInnerBuildMemberKind::Leaf => {
quote! { #name_ident }
}
PolymorphicEnumInnerBuildMemberKind::Struct => {
quote! { #name_ident(#name_ident) }
}
}
})
.collect::<Vec<_>>();
let type_name_ident = format_ident!("{}", spec.name());
quote! {
pub enum #type_name_ident {
#(#members,)*
}
}
}

View File

@ -0,0 +1,193 @@
use crate::spec::polymorphic_enum_loop_spec::{
PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuildChild,
};
use crate::spec::SpecialChildKind;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
pub fn make_polymorphic_enum_loop_type(spec: &PolymorphicEnumLoopBuildSpec) -> TokenStream {
let type_ident = format_ident!("{}", spec.name());
let build = spec
.rules()
.find(|rule| match rule {
PolymorphicEnumLoopRule::Build(_) => true,
_ => false,
})
.map(|rule| match rule {
PolymorphicEnumLoopRule::Build(build) => build,
_ => unreachable!(),
})
.unwrap();
let annotated_members = build
.children()
.map(|child| match child {
PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => {
let child_ident = format_ident!("{}", use_current.name());
let child_type_ident = format_ident!("{}", use_current.kind());
quote! {
#child_ident: Box<#child_type_ident>
}
}
PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => {
let child_ident = format_ident!("{}", on_each.name());
let child_type_ident = format_ident!("{}", on_each.rule());
quote! {
#child_ident: Box<#child_type_ident>
}
}
PolymorphicEnumLoopRuleBuildChild::Special(special_child) => {
let child_ident = format_ident!("{}", special_child.name());
let child_type_ident = match special_child.kind() {
SpecialChildKind::FileId => {
quote! { usize }
}
SpecialChildKind::Range => {
quote! { Range<usize> }
}
};
quote! {
#child_ident: #child_type_ident
}
}
})
.collect::<Vec<_>>();
let annotated_fields = build
.fields()
.iter()
.map(|field| {
let field_ident = format_ident!("{}", field.name());
let field_type = format_ident!("{}", field.kind());
quote! {
#field_ident: Option<#field_type>
}
})
.collect::<Vec<_>>();
let member_names = build
.children()
.map(|child| match child {
PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => {
format_ident!("{}", use_current.name())
}
PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => {
format_ident!("{}", on_each.name())
}
PolymorphicEnumLoopRuleBuildChild::Special(special_child) => {
format_ident!("{}", special_child.name())
}
})
.collect::<Vec<_>>();
let field_initializers = build
.fields()
.iter()
.map(|field| {
let field_ident = format_ident!("{}", field.name());
quote! { #field_ident: None }
})
.collect::<Vec<_>>();
let accessors = build
.children()
.map(|child| {
let (child_ident, child_type_ident) = match child {
PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => {
let child_ident = format_ident!("{}", use_current.name());
let child_type_ident = format_ident!("{}", use_current.kind());
(child_ident, quote! { #child_type_ident })
}
PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => {
let child_ident = format_ident!("{}", on_each.name());
let child_type_ident = format_ident!("{}", on_each.rule());
(child_ident, quote! { #child_type_ident })
}
PolymorphicEnumLoopRuleBuildChild::Special(special_child) => {
let child_ident = format_ident!("{}", special_child.name());
let child_type_ident = match special_child.kind() {
SpecialChildKind::FileId => {
quote! { usize }
}
SpecialChildKind::Range => {
quote! { Range<usize> }
}
};
(child_ident, child_type_ident)
}
};
let child_mut_ident = format_ident!("{}_mut", child_ident);
let as_ref = match child {
PolymorphicEnumLoopRuleBuildChild::Special(_) => {
quote! { &self.#child_ident }
}
_ => quote! { self.#child_ident.as_ref() },
};
let as_mut = match child {
PolymorphicEnumLoopRuleBuildChild::Special(_) => {
quote! { &mut self.#child_ident }
}
_ => quote! { self.#child_ident.as_mut() },
};
quote! {
pub fn #child_ident(&self) -> &#child_type_ident {
#as_ref
}
pub fn #child_mut_ident(&mut self) -> &mut #child_type_ident {
#as_mut
}
}
})
.collect::<Vec<_>>();
let field_accessors = build
.fields()
.iter()
.map(|field| {
let field_ident = format_ident!("{}", field.name());
let field_ident_mut = format_ident!("{}_mut", field.name());
let field_type = format_ident!("{}", field.kind());
let set_ident = format_ident!("set_{}", field.name());
quote! {
pub fn #field_ident(&self) -> Option<&#field_type> {
self.#field_ident.as_ref()
}
pub fn #field_ident_mut(&mut self) -> Option<&mut #field_type> {
self.#field_ident.as_mut()
}
pub fn #set_ident(&mut self, #field_ident: #field_type) {
self.#field_ident = Some(#field_ident);
}
}
})
.collect::<Vec<_>>();
quote! {
pub struct #type_ident {
#(#annotated_members,)*
#(#annotated_fields,)*
}
impl #type_ident {
pub fn new(#(#annotated_members),*) -> Self {
Self {
#(#member_names,)*
#(#field_initializers,)*
}
}
#(#accessors)*
#(#field_accessors)*
}
}
}

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

18
ast-generator/src/walk.rs Normal file
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,
})
}

4
examples/array_return.dm Normal file
View File

@ -0,0 +1,4 @@
fn test()
println(42)
[0, 1, 2]
end

3
examples/forty_two.dm Normal file
View File

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

8
examples/hello_42.dm Normal file
View File

@ -0,0 +1,8 @@
use std::core::println
fn main(args: Array<String>)
let hello = 42
let world = hello + 16
println(hello)
println(world)
end

3
examples/incompat.dm Normal file
View File

@ -0,0 +1,3 @@
fn main()
let x = 1 + 2L
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,211 @@
ns jsonexample
fn main()
let o: obj = match parseJsonObj('{ "a": { "b": { "c": [42] }} }')
Success(o) => o,
Failure(err) => panic!("There was an error during Json parsing: ${err}")
end
assertEq!(o.a.b.c[0], 42)
end
fn parseJsonObj(s: String) -> Result<obj, JsonParseError>
let lexer = JsonLexer(s)
let parser = JsonParser(lexer)
try
parser.object()
catch (parseError: JsonParseError)
Failure(parseError)
end
end
enum JsonToken
LBrace,
RBrace,
LSquare,
RSquare,
Comma,
String(String),
Number(Number),
Boolean(Boolean)
end
platform class String
pub fn <R> matchAt(startIndex: Int, matchers: Map<Regex, R | fn (match: String) -> R), opts: obj + { ignoreWhitespace: Boolean }) -> Option<R>
let slice: String = if opts.ignoreWhitespace then
/[\t\n\r ]*(<rest>.*)/.match(self[startIndex..]).rest as String
else
self[startIndex..]
end
for [regex, result] in matchers do
if regex.test(slice) is Some(match) then
if result is Fn then
Some(result(match))
else
Some(result)
end
end
end
None
end
end
platform class Regex
int RegexMatchBase
val match: String
val groups: [String],
val namedGroups: Map<String, String>
end
pub int RegexMatch
props!::<RegexMatchBase>()
end
platform fn doMatch(s) -> Option<RegexMatchBase>
pub fn match(s: String) -> Option<obj : RegexMatch>
doMatch(s).map { base ->
dyn obj {
...base,
propertyMissing: { name: String ->
base.namedGroups.get(name).unwrap()
}
}
}
end
end
class JsonLexer(val source: String)
var index: Int
fn matchCurrent() -> Option<JsonToken>
source.matchAt(index, [
/\{/ => JsonToken::LBrace,
/}/ => JsonToken::RBrace,
/\[/ => JsonToken::LSquare,
/]/ => JsonToken::RSquare,
/[0-9]+/ => { rawNumber: String => JsonToken::Number(number::parse(rawNumber))) }
/true/ => JsonToken::Boolean(true),
/false/ => JsonToken::False(false),
// The following is not a good way for matching strings but whatever
/"%w*"/ => { stringWithQuotes => JsonToken::String(stringWithQuotes[1..-1]) }
], obj { ignoreWhitespace: true })
end
pub fn nextToken() -> Option<JsonToken>
matchCurrent().ifSome { token ->
match token
JsonToken::LBrace | JsonToken::RBrace | JsonToken::LSquare | JsonToken::RSquare | JsonToken::Comma => index++,
JsonToken::String(s) => index += s.len() + 2,
JsonToken::Number(n) => index += n.toString().len(),
JsonToken::Boolean(b) => match b
true => index += 4,
false => index += 5,
end
end
}
end
pub fn peek() -> Option<JsonToken> alias matchCurrent
end
class JsonParser(val lexer: JsonLexer)
macro parseError!($message)
throw JsonParserError($message)
end
macro expect!($tokenType)
match lexer.nextToken()
Some(token) => match token
JsonToken::$tokenType => do end,
_ => parseError!("Expected ${$tokenType}")
end,
_ => parseError!("Expected ${$tokenType}")
end
end
macro peek!($tokenType)
match lexer.peek()
Some(token) => match token
JsonToken::$tokenType => true,
_ => false
end,
None => false
end
end
pub fn object() -> obj
expect!(LBrace)
let result = obj {}
while keyValue() is Some((key, value)) do
result[key] = value
end
expect!(RBrace)
result
end
fn keyValue() -> Option<(String, Any)>
let key = match lexer.peek()
Some(token) => do
match token
JsonToken::String(s) => s,
_ => parseError!("Expected String")
end
end,
_ => do return None end
end
let value = value()
Some((key, value))
end
fn value() -> Any
match lexer.peek()
Some(token) => match token
JsonToken::LBrace => object(),
JsonToken::LSquare => array(),
_ => do
match lexer.nextToken()
JsonToken::String(s) => s,
JsonToken::Number(n) => n,
JsonToken::Boolean(b) => b,
_ => parseError!("Expected String, Number, or Boolean")
end
end
end,
None => parseError!("Expected LBrace, LSquare, String, Number, or Boolean")
end
end
fn array() -> [Any]
expect!(LSquare)
let results: List<Any> = []
while true do
match lexer.peek()
Some(token) => match token
JsonToken::LBrace => results << object(),
JsonToken::LSquare => results << array(),
_ => do
match lexer.nextToken()
JsonToken::String(s) => results << s,
JsonToken::Number(n) => results << n,
JsonToken::Boolean(b) => results << b,
_ => parseError!("Expected String, Number, or Boolean")
end
end
end,
None => panic! "Expected LBrace, LSquare, String, Number, or Boolean"
end
match lexer.nextToken()
Some(token) => match token
JsonToken::Comma => continue,
JsonToken::RSquare => break
end
_ => panic! "Expected Comma or RSquare"
end
end
expect!(RSquare)
end
end

View File

@ -0,0 +1,128 @@
ns nr::train
use object::{assign, Props, props!}
pub int Train
name: String
primaryColor: Color
fld destination: String
fld volume: Int
fn blowHorn() -> IO
end
pub enum Color
White,
Blue,
Red
end
pub type Printer = fn (message: String) -> IO
pub fn create(props: obj Props<Train> + { printer: Printer }) -> Train =
SimpleTrain(props).with {
volume = 80
}
class SimpleTrain : Train
props!::<Train>()
printer: Printer
ctor(props: obj Props<Train> + { printer: Printer })
assign(self, props)
end
impl fn blowHorn() -> IO
printer("Train ${name} is blowing its horn at volume ${volume}!")
end
end
pub fn createViaObj(props: obj Props<Train>, printer: Printer) -> obj Train
obj {
...props,
printer,
blowHorn: fn () -> IO
printer("Dynamic object Train ${train} is blowing its horn at volume ${self.volume}!")
end
}
end
// props lib file
ns std::core::object
pub fn assign(target: obj, source: obj)
for [key, value] source.props() do
target[key] = value
end
end
pub type Props<T> = obj {
[K in keyof T]: T[K]
}
pub macro fn <T> props!(_: TokenStream) -> TokenStream
let result = TokenStream()
for [key, type] in std::intrinsics::propsAndTypes::<T>() do
result << quote! {
#key: #type
}
end
result
end
// io lib file
ns std::core
pub enum IO<S : Stream = Stream, E = Error> : fn () -> IO<S, E>
Success(stream: S)
impl ()
try
doCall(stream)
catch (e: E)
Failure(e)
end
end
end,
Failure(_)
impl ()
self
end
end;
int
fn <Z : Stream = S> doCall(stream: S) -> IO<Z, E>
end
end
// main example file
ns nr::example
use nr::train::{create, createViaObj, Train, Color}
fn main()
let train = create(obj {
name: 'Test Train',
primaryColor: Color::Blue,
destination: 'Nonavosa Central',
printer: println
})
train.blowHorn()
train.volume = 100
train.blowHorn()
let objTrain: obj Train = createViaObj(obj {
name: 'Obj Train',
primaryColor: Color::Red,
destination: 'Durandana',
printer: println
})
// Since it's an obj, we can dynamically add properties and override methods, similar to JS
objTrain.secondaryColor = Color::White
let originalBlowHorn = objTrain.blowHorn
objTrain.blowHorn = fn ()
originalBlowHorn()
println "Hello again! The train's colors are ${self.primaryColor} and ${self.secondaryColor}!"
end
objTrain.blowHorn()
end

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

244
src/asm/assemble_ir.rs Normal file
View File

@ -0,0 +1,244 @@
use crate::asm::{
AsmBinaryOperator, AsmFunction, AsmInstruction, AsmOperand, ControlUnit, ControlUnitId,
VirtualRegisterId,
};
use crate::ir::{
IrAssign, IrBinaryOperation, IrBinaryOperator, IrCall, IrCallType, IrExpression, IrFunction,
IrLiteral, IrReturn, IrStatement,
};
use std::collections::HashMap;
use std::rc::Rc;
struct AssemblyContext {
control_unit_counter: ControlUnitId,
control_units: Vec<ControlUnit>,
virtual_register_counter: VirtualRegisterId,
virtual_registers: HashMap<Rc<str>, VirtualRegisterId>,
}
impl AssemblyContext {
pub fn new() -> Self {
Self {
control_unit_counter: 0,
control_units: Vec::new(),
virtual_register_counter: 0,
virtual_registers: HashMap::new(),
}
}
fn next_control_unit_id(&mut self) -> ControlUnitId {
let id = self.control_unit_counter;
self.control_unit_counter += 1;
id
}
pub fn new_control_unit(&mut self) -> ControlUnitId {
let new_id = self.next_control_unit_id();
self.control_units.push(ControlUnit::new(new_id));
new_id
}
pub fn current_control_unit(&mut self) -> &ControlUnit {
if self.control_units.is_empty() {
self.new_control_unit();
}
self.control_units.last().unwrap()
}
pub fn current_control_unit_mut(&mut self) -> &mut ControlUnit {
if self.control_units.is_empty() {
self.new_control_unit();
}
self.control_units.last_mut().unwrap()
}
pub fn into_control_units(self) -> Vec<ControlUnit> {
self.control_units
}
pub fn get_virtual_register(&self, name: &str) -> VirtualRegisterId {
*self
.virtual_registers
.get(name)
.expect(&format!("Did not set a virtual register for name {}", name))
}
pub fn next_virtual_register(&mut self, name: &str) -> VirtualRegisterId {
if self.virtual_registers.contains_key(name) {
panic!(
"Should not already have a virtual register with name {}",
name
);
}
let virtual_register_id = self.virtual_register_counter;
self.virtual_register_counter += 1;
self.virtual_registers
.insert(name.into(), virtual_register_id);
virtual_register_id
}
}
pub fn assemble_ir_function(ir_function: &IrFunction) -> AsmFunction {
let mut context = AssemblyContext::new();
// body
for statement in ir_function.statements() {
assemble_ir_statement(statement, &mut context);
}
AsmFunction::new(ir_function.fqn().into(), context.into_control_units())
}
fn assemble_ir_statement(ir_statement: &IrStatement, context: &mut AssemblyContext) {
match ir_statement {
IrStatement::Allocate(_) => {
todo!()
}
IrStatement::Call(ir_call) => {
assemble_ir_call(ir_call, context);
}
IrStatement::Return(ir_return) => {
assemble_ir_return(ir_return, context);
}
IrStatement::Assign(ir_assign) => {
assemble_ir_assign(ir_assign, context);
}
IrStatement::MakeClosure(_) => {
todo!()
}
IrStatement::BinaryOperation(ir_binary_operation) => {
assemble_ir_binary_operation(ir_binary_operation, context)
}
}
}
fn assemble_ir_assign(ir_assign: &IrAssign, context: &mut AssemblyContext) {
let source_operand = match ir_assign.rhs() {
IrExpression::Variable(ir_variable) => {
let virtual_register_id = context.get_virtual_register(ir_variable.name());
AsmOperand::VirtualRegister(virtual_register_id)
}
IrExpression::Literal(ir_literal) => ir_literal_to_asm_operand(ir_literal),
};
let destination = context.next_virtual_register(ir_assign.lhs().name());
context
.current_control_unit_mut()
.append_instruction(AsmInstruction::Move {
source: source_operand,
destination,
})
}
fn ir_literal_to_asm_operand(ir_literal: &IrLiteral) -> AsmOperand {
match ir_literal {
IrLiteral::I8(i) => AsmOperand::I8(*i),
IrLiteral::I16(i) => AsmOperand::I16(*i),
IrLiteral::I32(i) => AsmOperand::I32(*i),
IrLiteral::I64(i) => AsmOperand::I64(*i),
IrLiteral::I128(i) => AsmOperand::I128(*i),
IrLiteral::ISize(i) => AsmOperand::ISize(*i),
IrLiteral::U8(u) => AsmOperand::U8(*u),
IrLiteral::U16(u) => AsmOperand::U16(*u),
IrLiteral::U32(u) => AsmOperand::U32(*u),
IrLiteral::U64(u) => AsmOperand::U64(*u),
IrLiteral::U128(u) => AsmOperand::U128(*u),
IrLiteral::USize(u) => AsmOperand::USize(*u),
IrLiteral::Boolean(b) => AsmOperand::Boolean(*b),
IrLiteral::Float(f) => AsmOperand::Float(*f),
IrLiteral::Double(d) => AsmOperand::Double(*d),
IrLiteral::String(s) => AsmOperand::String(s.clone()),
}
}
fn assemble_ir_call(ir_call: &IrCall, context: &mut AssemblyContext) {
// push args
for argument in ir_call.arguments() {
match argument {
IrExpression::Variable(ir_variable) => {
let variable_register_id = context.get_virtual_register(ir_variable.name());
context
.current_control_unit_mut()
.append_instruction(AsmInstruction::Push {
source: AsmOperand::VirtualRegister(variable_register_id),
})
}
IrExpression::Literal(ir_literal) => context
.current_control_unit_mut()
.append_instruction(AsmInstruction::Push {
source: ir_literal_to_asm_operand(ir_literal),
}),
}
}
// issue call
match ir_call.call_type() {
IrCallType::Static => {
context
.current_control_unit_mut()
.append_instruction(AsmInstruction::InvokeStatic {
fqn: ir_call.function_name_owned(),
});
}
IrCallType::Object => {
context
.current_control_unit_mut()
.append_instruction(AsmInstruction::InvokeObject {
fqn: ir_call.function_name_owned(),
})
}
IrCallType::PlatformStatic => {}
IrCallType::PlatformObject => {}
}
}
fn assemble_ir_return(ir_return: &IrReturn, context: &mut AssemblyContext) {
let operand = ir_return.expression().map(|expression| {
match expression {
IrExpression::Variable(ir_variable) => {
let variable_virtual_register_id = context.get_virtual_register(ir_variable.name());
AsmOperand::VirtualRegister(variable_virtual_register_id)
}
IrExpression::Literal(ir_literal) => ir_literal_to_asm_operand(ir_literal),
}
});
context
.current_control_unit_mut()
.append_instruction(AsmInstruction::Return { operand });
}
fn assemble_ir_binary_operation(
ir_binary_operation: &IrBinaryOperation,
context: &mut AssemblyContext,
) {
let left = match ir_binary_operation.left() {
IrExpression::Variable(ir_variable) => {
AsmOperand::VirtualRegister(context.get_virtual_register(ir_variable.name()))
}
IrExpression::Literal(ir_literal) => ir_literal_to_asm_operand(ir_literal),
};
let right = match ir_binary_operation.right() {
IrExpression::Variable(ir_variable) => {
AsmOperand::VirtualRegister(context.get_virtual_register(ir_variable.name()))
}
IrExpression::Literal(ir_literal) => ir_literal_to_asm_operand(ir_literal),
};
let destination = context.next_virtual_register(ir_binary_operation.destination().name());
let operator = match ir_binary_operation.operator() {
IrBinaryOperator::Add => AsmBinaryOperator::Add,
IrBinaryOperator::Subtract => AsmBinaryOperator::Subtract,
IrBinaryOperator::Multiply => AsmBinaryOperator::Multiply,
IrBinaryOperator::Divide => AsmBinaryOperator::Divide,
IrBinaryOperator::Exponent => {
todo!()
}
};
let instruction = AsmInstruction::BinaryOperation {
destination,
left,
right,
operator,
};
context
.current_control_unit_mut()
.append_instruction(instruction);
}

256
src/asm/mod.rs Normal file
View File

@ -0,0 +1,256 @@
pub mod assemble_ir;
use std::fmt::{Display, Formatter};
use std::rc::Rc;
pub type ControlUnitId = usize;
pub type VirtualRegisterId = u32;
pub struct AsmFunction {
fqn: Rc<str>,
control_units: Vec<ControlUnit>,
}
impl AsmFunction {
pub fn new(fqn: Rc<str>, control_units: Vec<ControlUnit>) -> Self {
Self { fqn, control_units }
}
}
impl Display for AsmFunction {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "fn {}", self.fqn)?;
for control_unit in &self.control_units {
writeln!(f, " #{}:", control_unit.id)?;
for instruction in &control_unit.instructions {
writeln!(f, " {}", instruction)?;
}
}
Ok(())
}
}
pub struct ControlUnit {
id: ControlUnitId,
successor_ids: Vec<ControlUnitId>,
instructions: Vec<AsmInstruction>,
}
impl ControlUnit {
pub fn new(id: ControlUnitId) -> Self {
Self {
id,
successor_ids: vec![],
instructions: vec![],
}
}
pub fn append_instruction(&mut self, instruction: AsmInstruction) {
self.instructions.push(instruction);
}
pub fn append_instructions(&mut self, instructions: Vec<AsmInstruction>) {
self.instructions.extend(instructions);
}
}
pub enum AsmInstruction {
Goto {
control_unit_id: ControlUnitId,
},
Move {
source: AsmOperand,
destination: VirtualRegisterId,
},
Push {
source: AsmOperand,
},
Pop {
destination: VirtualRegisterId,
},
Allocate {
class_name: Rc<str>,
destination: VirtualRegisterId,
},
InvokeStatic {
fqn: Rc<str>,
},
InvokeObject {
fqn: Rc<str>,
},
InvokePlatformStatic {
fqn: Rc<str>,
},
InvokePlatformObject {
fqn: Rc<str>,
},
Return {
operand: Option<AsmOperand>,
},
BinaryOperation {
destination: VirtualRegisterId,
left: AsmOperand,
right: AsmOperand,
operator: AsmBinaryOperator,
},
}
impl Display for AsmInstruction {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
AsmInstruction::Goto { control_unit_id } => {
write!(f, "goto #{}", control_unit_id)
}
AsmInstruction::Move {
source,
destination,
} => {
write!(f, "move vr{}, {}", destination, source)
}
AsmInstruction::Push { source } => {
write!(f, "push {}", source)
}
AsmInstruction::Pop { destination } => {
write!(f, "pop vr{}", destination)
}
AsmInstruction::Allocate {
class_name,
destination,
} => {
write!(f, "allocate r{}, {}", destination, class_name)
}
AsmInstruction::InvokeStatic { fqn } => {
write!(f, "invoke_static {}", fqn)
}
AsmInstruction::InvokeObject { fqn } => {
write!(f, "invoke_object {}", fqn)
}
AsmInstruction::InvokePlatformStatic { fqn } => {
write!(f, "invoke_platform_static {}", fqn)
}
AsmInstruction::InvokePlatformObject { fqn } => {
write!(f, "invoke_platform_object {}", fqn)
}
AsmInstruction::Return { operand } => {
write!(f, "return")?;
if let Some(operand) = operand {
write!(f, " {}", operand)?;
}
Ok(())
}
AsmInstruction::BinaryOperation {
destination,
left,
right,
operator,
} => {
write!(f, "op{} vr{}, {}, {}", operator, destination, left, right)
}
}
}
}
pub enum AsmOperand {
I8(i8),
I16(i16),
I32(i32),
I64(i64),
I128(i128),
ISize(isize),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
U128(u128),
USize(usize),
Float(f32),
Double(f64),
Boolean(bool),
String(Rc<str>),
VirtualRegister(VirtualRegisterId),
}
impl Display for AsmOperand {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
AsmOperand::I8(i) => {
write!(f, "{}i8", i)
}
AsmOperand::I16(i) => {
write!(f, "{}i16", i)
}
AsmOperand::I32(i) => {
write!(f, "{}i32", i)
}
AsmOperand::I64(i) => {
write!(f, "{}i64", i)
}
AsmOperand::I128(i) => {
write!(f, "{}i128", i)
}
AsmOperand::ISize(i) => {
write!(f, "{}isize", i)
}
AsmOperand::U8(u) => {
write!(f, "{}u8", u)
}
AsmOperand::U16(u) => {
write!(f, "{}u16", u)
}
AsmOperand::U32(u) => {
write!(f, "{}u32", u)
}
AsmOperand::U64(u) => {
write!(f, "{}u64", u)
}
AsmOperand::U128(u) => {
write!(f, "{}u128", u)
}
AsmOperand::USize(u) => {
write!(f, "{}usize", u)
}
AsmOperand::Float(float) => {
write!(f, "{}f32", float)
}
AsmOperand::Double(d) => {
write!(f, "{}f64", d)
}
AsmOperand::Boolean(b) => {
write!(f, "{}", b)
}
AsmOperand::String(s) => {
write!(f, "{}", s)
}
AsmOperand::VirtualRegister(id) => {
write!(f, "vr{}", id)
}
}
}
}
pub enum AsmBinaryOperator {
Add,
Subtract,
Multiply,
Divide,
}
impl Display for AsmBinaryOperator {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
AsmBinaryOperator::Add => {
write!(f, "+")
}
AsmBinaryOperator::Subtract => {
write!(f, "-")
}
AsmBinaryOperator::Multiply => {
write!(f, "*")
}
AsmBinaryOperator::Divide => {
write!(f, "/")
}
}
}
}

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,333 @@
pub mod build;
pub mod children;
pub mod node;
pub mod pretty_print;
pub mod unparse;
pub mod walk;
pub mod node {
include!(concat!(env!("OUT_DIR"), "/src/ast/node.rs"));
impl OperatorInner {
pub fn name(&self) -> &'static str {
match self {
OperatorInner::Or => "op_or",
OperatorInner::And => "op_and",
OperatorInner::EqualTo => "op_eq",
OperatorInner::NotEqualTo => "op_neq",
OperatorInner::Greater => "op_gt",
OperatorInner::Less => "op_lt",
OperatorInner::GreaterEqual => "op_ge",
OperatorInner::LessEqual => "op_le",
OperatorInner::Add => "op_add",
OperatorInner::Subtract => "op_sub",
OperatorInner::Multiply => "op_mul",
OperatorInner::Divide => "op_div",
OperatorInner::Modulo => "op_mod",
OperatorInner::LeftShift => "op_ls",
OperatorInner::RightShift => "op_rs",
OperatorInner::Spread => "op_spread",
OperatorInner::Star => "op_star",
OperatorInner::Not => "op_not",
OperatorInner::Negative => "op_neg",
OperatorInner::PlusPlus => "op_pp",
OperatorInner::MinusMinus => "op_mm",
OperatorInner::CallOp => "op_call",
OperatorInner::Index => "op_index",
}
}
}
impl Expression {
pub fn analyzed_kind(&self) -> Kind {
match self {
Expression::Ternary(ternary_expression) => {
todo!()
}
Expression::Or(or_expression) => {
todo!()
}
Expression::And(and_expression) => {
todo!()
}
Expression::Comparison(comparison_expression) => {
todo!()
}
Expression::Shift(shift_expression) => {
todo!()
}
Expression::Additive(additive_expression) => additive_expression
.analyzed_kind()
.expect("AdditiveExpression's analyzed_kind not set.")
.clone(),
Expression::Multiplicative(multiplicative_expression) => {
todo!()
}
Expression::Prefix(prefix_expression) => {
todo!()
}
Expression::Suffix(suffix_expression) => {
todo!()
}
Expression::Literal(literal) => literal.analyzed_kind(),
Expression::Identifier(identifier) => identifier
.analyzed_kind()
.expect("IdentifierExpression's analyzed_kind not set.")
.clone(),
Expression::Fqn(fqn) => {
todo!()
}
Expression::Closure(closure) => {
todo!()
}
Expression::List(list_expression) => {
todo!()
}
}
}
}
impl Literal {
pub fn analyzed_kind(&self) -> Kind {
match self {
Literal::IntLiteral(_) => Kind::Primitive(PrimitiveKind::Int.into()),
Literal::LongLiteral(_) => Kind::Primitive(PrimitiveKind::Long.into()),
Literal::DoubleLiteral(_) => Kind::Primitive(PrimitiveKind::Double.into()),
Literal::SingleQuoteString(_) => Kind::Primitive(PrimitiveKind::String.into()),
Literal::DString(_) => {
todo!()
}
Literal::BacktickString(_) => {
todo!()
}
Literal::BooleanLiteral(_) => Kind::Primitive(PrimitiveKind::Boolean.into()),
}
}
}
impl Default for Parameters {
fn default() -> Self {
Self::new(vec![])
}
}
impl Default for GenericParameters {
fn default() -> Self {
Self::new(Box::new(IdentifierList::new(vec![])))
}
}
impl Default for GenericArguments {
fn default() -> Self {
Self::new(Box::new(TypeUseList::new(vec![])))
}
}
impl Default for ImplementsList {
fn default() -> Self {
Self::new(vec![])
}
}
impl ReturnType {
pub fn void() -> Self {
Self::new(Box::new(TypeUse::PrimitiveType(PrimitiveType::Void)))
}
}
impl Default for ClassConstructor {
fn default() -> Self {
Self::new(vec![])
}
}
impl Default for ClosureParameters {
fn default() -> Self {
Self::new(vec![])
}
}
}
pub mod build {
use pest::iterators::Pairs;
include!(concat!(env!("OUT_DIR"), "/src/ast/build.rs"));
pub fn build_ast(file_id: usize, parsed_pairs: &mut Pairs<Rule>) -> CompilationUnit {
let compilation_unit_pair = parsed_pairs.next().unwrap();
build_compilation_unit(file_id, compilation_unit_pair)
}
#[cfg(test)]
mod build_tests {
use super::*;
use crate::ast::ast_node::AstNodeRef;
use crate::ast::pretty_print::PrettyPrint;
use crate::ast::walk::walk_depth_first;
use crate::parser::DeimosParser;
use crate::util::indent_writer::IndentWriter;
use pest::Parser;
fn parse(rule: Rule, input: &str) -> Pair<Rule> {
let parse_result = DeimosParser::parse(rule, input);
parse_result.expect("parsing failed").next().unwrap()
}
fn fmt_ast(node: &dyn PrettyPrint) -> String {
let mut acc = String::new();
let mut indent_writer = IndentWriter::new(0, " ", &mut acc);
node.pretty_print(&mut indent_writer)
.expect("Pretty print failed");
acc
}
#[test]
fn boolean_literal_true() {
let pair = parse(
Rule::BooleanLiteral,
include_str!("build_tests/boolean_literal/true"),
);
assert_eq!(true, build_boolean_literal(0, pair));
}
#[test]
fn boolean_literal_false() {
let pair = parse(
Rule::BooleanLiteral,
include_str!("build_tests/boolean_literal/false"),
);
assert_eq!(false, build_boolean_literal(0, pair));
}
#[test]
fn backtick_inner_greeting() {
let pair = parse(
Rule::BacktickInner,
include_str!("build_tests/backtick_inner/greeting"),
);
assert_eq!("Hello, World!", build_backtick_inner(0, pair));
}
#[test]
fn backtick_string_mixed() {
let pair = parse(
Rule::BacktickString,
include_str!("build_tests/backtick_string/mixed"),
);
let backtick_string = build_backtick_string(0, pair);
assert_eq!(backtick_string.inners().count(), 2);
assert_eq!(backtick_string.expressions().count(), 1);
}
#[test]
fn d_string_expression_simple() {
let pair = parse(Rule::DStringExpression, "${thing}");
let d_string_expression = build_d_string_expression(0, pair);
}
#[test]
fn d_string_inner() {
let pair = parse(Rule::DStringInner, "Hello!");
let d_string_inner = build_d_string_inner(0, pair);
assert_eq!("Hello!", d_string_inner);
}
#[test]
fn d_string_mixed() {
let pair = parse(Rule::DString, "\"Hello, ${world}!\"");
let d_string = build_d_string(0, pair);
assert_eq!(d_string.inners().count(), 2);
assert_eq!(d_string.expressions().count(), 1);
}
#[test]
fn expression_simple_call() {
let pair = parse(Rule::Expression, "hello(42)");
let expression = build_expression(0, pair);
}
#[test]
fn multiple_expression_statements() {
let pair = parse(
Rule::Function,
"
fn test()
println(42)
println(43)
end
",
);
let function = build_function(0, pair);
let mut number_of_statements = 0;
walk_depth_first(&function, &mut |node| match node {
AstNodeRef::Statement(_) => {
number_of_statements += 1;
}
_ => {}
});
assert_eq!(2, number_of_statements, "Ast: {}", fmt_ast(&function));
}
#[test]
fn array_return_after_statement() {
let pair = parse(
Rule::Function,
"
fn test()
println(42)
[0, 1, 2]
end
",
);
let function = build_function(0, pair);
let mut number_of_statements = 0;
walk_depth_first(&function, &mut |node| {
if let AstNodeRef::Statement(stmt) = node {
number_of_statements += 1;
}
});
assert_eq!(2, number_of_statements);
}
#[test]
fn array_return_after_bare_identifier() {
// The following should parse as two expression statements
let pair = parse(
Rule::Function,
"
fn test()
x
[0, 1, 2]
end
",
);
let function = build_function(0, pair);
let mut number_of_statements = 0;
walk_depth_first(&function, &mut |node| {
if let AstNodeRef::Statement(stmt) = node {
number_of_statements += 1;
}
});
assert_eq!(2, number_of_statements);
}
}
}
pub mod pretty_print {
use crate::util::indent_writer::IndentWriter;
pub trait PrettyPrint {
fn pretty_print(&self, writer: &mut IndentWriter) -> std::fmt::Result;
}
include!(concat!(env!("OUT_DIR"), "/src/ast/pretty_print.rs"));
}
pub mod ast_node {
include!(concat!(env!("OUT_DIR"), "/src/ast/ast_node.rs"));
}
pub mod walk {
include!(concat!(env!("OUT_DIR"), "/src/ast/walk.rs"));
}

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

30
src/bin/dmc/cst.rs Normal file
View File

@ -0,0 +1,30 @@
use deimos::parser::{DeimosParser, Rule};
use std::path::PathBuf;
use pest::iterators::{Pair, Pairs};
use pest::Parser;
fn print_pair(pair: Pair<Rule>) {
println!("{:?}", pair.as_rule());
println!("{:?}", pair.as_span());
println!("----");
print_pairs(pair.into_inner());
}
fn print_pairs(pairs: Pairs<Rule>) {
for pair in pairs {
print_pair(pair);
}
}
pub fn show_cst(path: &PathBuf) {
let src = std::fs::read_to_string(path).unwrap();
let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src);
match parse_result {
Ok(pairs) => {
print_pairs(pairs);
}
Err(error) => {
eprintln!("{:?}", error);
}
}
}

146
src/bin/dmc/ir.rs Normal file
View File

@ -0,0 +1,146 @@
use codespan_reporting::files::SimpleFiles;
use codespan_reporting::term;
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
use deimos::asm::assemble_ir::assemble_ir_function;
use deimos::ast::build::build_ast;
use deimos::ast::node::CompilationUnit;
use deimos::ir::lower_ast::lower_compilation_unit;
use deimos::ir::Ir;
use deimos::name_analysis::analyze_names;
use deimos::name_analysis::symbol_table::SymbolTable;
use deimos::parser::{DeimosParser, Rule};
use deimos::std_core::add_std_core_symbols;
use deimos::type_analysis::analyze_types;
use pest::Parser;
use std::collections::HashMap;
use std::fmt::{Debug, Display, Formatter};
use std::path::PathBuf;
struct ParseErrors {
errors: Vec<pest::error::Error<Rule>>,
}
impl Debug for ParseErrors {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
impl Display for ParseErrors {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "There were errors during parsing.")?;
for parse_error in &self.errors {
writeln!(f, "{}", parse_error)?;
}
Ok(())
}
}
impl std::error::Error for ParseErrors {}
#[derive(Debug)]
struct CompilationErrors {
count: usize,
}
impl Display for CompilationErrors {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if self.count == 1 {
writeln!(f, "There was 1 error during compilation. See above.")
} else {
writeln!(
f,
"There were {} errors during compilation. See above.",
self.count
)
}
}
}
impl std::error::Error for CompilationErrors {}
pub fn compile_to_ir(
paths: &[PathBuf],
) -> Result<Vec<CompilationUnit>, Box<dyn std::error::Error>> {
let mut paths_and_sources: HashMap<String, String> = HashMap::new();
for path in paths {
let src = std::fs::read_to_string(path).unwrap();
paths_and_sources.insert(path.display().to_string(), src);
}
let mut compilation_units = vec![];
let mut files: SimpleFiles<&str, &str> = SimpleFiles::new();
let mut parse_errors = vec![];
for (path, source) in &paths_and_sources {
let parse_result = DeimosParser::parse(Rule::CompilationUnit, source);
match parse_result {
Ok(mut pairs) => {
let file_id = files.add(path, source);
let compilation_unit = build_ast(file_id, &mut pairs);
compilation_units.push(compilation_unit);
}
Err(error) => {
parse_errors.push(error);
}
}
}
if !parse_errors.is_empty() {
return Err(Box::new(ParseErrors {
errors: parse_errors,
}));
}
let mut symbol_table = SymbolTable::new();
add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols.");
let name_diagnostics = analyze_names(&mut compilation_units, &files, &mut symbol_table);
if name_diagnostics.is_empty() {
println!("Name analysis complete.");
} else {
let writer = StandardStream::stderr(ColorChoice::Always);
let config = term::Config::default();
for diagnostic in &name_diagnostics {
term::emit(&mut writer.lock(), &config, &files, diagnostic)?;
}
return Err(Box::new(CompilationErrors {
count: name_diagnostics.len(),
}));
}
let type_diagnostics = analyze_types(&mut compilation_units);
if type_diagnostics.is_empty() {
println!("Type analysis complete.");
} else {
let writer = StandardStream::stderr(ColorChoice::Always);
let config = term::Config::default();
for diagnostic in &type_diagnostics {
term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
}
return Err(Box::new(CompilationErrors {
count: type_diagnostics.len(),
}));
}
for compilation_unit in &compilation_units {
let cu_irs = lower_compilation_unit(compilation_unit);
for ir in &cu_irs {
println!("{}", ir)
}
for ir in &cu_irs {
match ir {
Ir::Function(ir_function) => {
let asm_function = assemble_ir_function(ir_function);
println!("{}", asm_function);
}
_ => {}
}
}
}
Ok(compilation_units)
}

View File

@ -1,12 +1,16 @@
mod cst;
mod ir;
mod name_analysis;
mod p3;
mod unparse;
// mod unparse;
use std::path::PathBuf;
// use crate::unparse::unparse;
use crate::cst::show_cst;
use crate::ir::compile_to_ir;
use crate::name_analysis::name_analysis;
use crate::p3::pretty_print_parse;
use crate::unparse::unparse;
use clap::{Parser, Subcommand};
#[derive(Debug, Parser)]
@ -19,35 +23,35 @@ struct Cli {
#[derive(Debug, Subcommand)]
enum Commands {
#[command(arg_required_else_help = true)]
Unparse {
paths: Vec<PathBuf>,
},
P3 {
paths: Vec<PathBuf>,
},
NameAnalysis {
paths: Vec<PathBuf>,
},
Cst { paths: Vec<PathBuf> },
P3 { paths: Vec<PathBuf> },
NameAnalysis { paths: Vec<PathBuf> },
Ir { paths: Vec<PathBuf> },
}
fn main() {
let args = Cli::parse();
match args.command {
Commands::Unparse { paths } => {
for path in paths {
unparse(&path);
Commands::Cst { paths } => {
for path in &paths {
show_cst(path);
}
}
Commands::P3 { paths } => {
for path in paths {
pretty_print_parse(&path)
pretty_print_parse(&path);
}
}
Commands::NameAnalysis { paths } => {
let result = name_analysis(&paths);
if let Err(e) = result {
eprintln!("{}", e)
eprintln!("{}", e);
}
}
Commands::Ir { paths } => {
let result = compile_to_ir(&paths);
if let Err(e) = result {
eprintln!("{}", e);
}
}
}

View File

@ -5,37 +5,73 @@ use deimos::ast::build::build_ast;
use deimos::name_analysis::analyze_names;
use deimos::name_analysis::symbol_table::SymbolTable;
use deimos::parser::{DeimosParser, Rule};
use pest::Parser;
use std::path::PathBuf;
use deimos::std_core::add_std_core_symbols;
use pest::Parser;
use std::collections::HashMap;
use std::fmt::{Debug, Display, Formatter};
use std::path::PathBuf;
use deimos::ast::node::CompilationUnit;
pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Error>> {
let mut compilation_units = vec![];
let mut files = SimpleFiles::new();
struct ParseErrors {
errors: Vec<pest::error::Error<Rule>>,
}
impl Debug for ParseErrors {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
impl Display for ParseErrors {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "There were errors during parsing.")?;
for parse_error in &self.errors {
writeln!(f, "{}", parse_error)?;
}
Ok(())
}
}
impl std::error::Error for ParseErrors {}
pub fn name_analysis(paths: &[PathBuf]) -> Result<Vec<CompilationUnit>, Box<dyn std::error::Error>> {
let mut paths_and_sources: HashMap<String, String> = HashMap::new();
for path in paths {
let src = std::fs::read_to_string(path).unwrap();
let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src);
let file_id = files.add(path.display().to_string(), src.clone()); // I don't love this clone
paths_and_sources.insert(path.display().to_string(), src);
}
let mut compilation_units = vec![];
let mut files: SimpleFiles<&str, &str> = SimpleFiles::new();
let mut parse_errors = vec![];
for (path, source) in &paths_and_sources {
let parse_result = DeimosParser::parse(Rule::CompilationUnit, source);
match parse_result {
Ok(mut pairs) => {
let compilation_unit_pair = pairs.next().unwrap();
let compilation_unit = build_ast(&path.display().to_string(), file_id, compilation_unit_pair);
let file_id = files.add(path, source);
let compilation_unit = build_ast(file_id, &mut pairs);
compilation_units.push(compilation_unit);
Ok::<(), Box<dyn std::error::Error>>(())
}
Err(e) => Err(e.into()),
}?;
Err(error) => {
parse_errors.push(error);
}
}
}
if !parse_errors.is_empty() {
return Err(Box::new(ParseErrors {
errors: parse_errors,
}));
}
let mut symbol_table = SymbolTable::new();
add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols.");
let diagnostics = analyze_names(&mut compilation_units, &mut symbol_table);
let diagnostics = analyze_names(&mut compilation_units, &files, &mut symbol_table);
if diagnostics.is_empty() {
println!("Name analysis complete.");
println!("Symbol table\n-------\n{}", symbol_table);
println!("{}", symbol_table);
} else {
let writer = StandardStream::stderr(ColorChoice::Always);
let config = term::Config::default();
@ -43,6 +79,6 @@ pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Err
term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
}
}
Ok(())
Ok(compilation_units)
}

View File

@ -10,12 +10,13 @@ pub fn pretty_print_parse(path: &PathBuf) {
let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src);
match parse_result {
Ok(mut pairs) => {
let compilation_unit_pair = pairs.next().unwrap();
let compilation_unit = build_ast(&path.display().to_string(), 0, compilation_unit_pair);
let mut indent_writer = IndentWriter::new(0, " ", Box::new(std::io::stdout()));
let compilation_unit = build_ast(0, &mut pairs);
let mut acc = String::new();
let mut indent_writer = IndentWriter::new(0, " ", &mut acc);
compilation_unit
.pretty_print(&mut indent_writer)
.expect("Unable to pretty-print.");
println!("{}", acc);
}
Err(e) => {
eprintln!("{}", e);

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