diff --git a/Cargo.lock b/Cargo.lock index e02ca72..986ed6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys", +] + [[package]] name = "cpufeatures" version = "0.2.12" @@ -56,6 +66,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.155" @@ -85,6 +101,19 @@ dependencies = [ "ucd-trie", ] +[[package]] +name = "pest-test" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a426ffa96d4df49eb2a43b010059dc085c4a0b655fd229de11b49340d1deaffe" +dependencies = [ + "colored", + "pest", + "pest_derive", + "snailquote", + "thiserror", +] + [[package]] name = "pest_derive" version = "2.7.11" @@ -148,11 +177,23 @@ dependencies = [ "digest", ] +[[package]] +name = "snailquote" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec62a949bda7f15800481a711909f946e1204f2460f89210eaf7f57730f88f86" +dependencies = [ + "thiserror", + "unicode_categories", +] + [[package]] name = "snek-parser" version = "0.1.0" dependencies = [ + "lazy_static", "pest", + "pest-test", "pest_derive", ] @@ -205,8 +246,80 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml index c1ca16e..6c8d768 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +lazy_static = "1.5.0" pest = "2.7.11" +pest-test = "0.1.6" pest_derive = "2.7.11" diff --git a/README.md b/README.md index 38c1f57..cf296db 100644 --- a/README.md +++ b/README.md @@ -68,39 +68,38 @@ let complex_addition a b c = { ### Complex types -All type declarations follow a similar patter. The value for a type is one of the following: -- `#[...]` enum -- `#{...}` record +- `enum {...}` enum +- `rec {...}` record - `#(...)` tuple These expressions can be nested. ```sk -type MyEnum = #[ +type MyEnum = enum { Var1 Int Var2 Bool -] +} -type MyRec = #{ +type MyRec = rec { field1: Float field2: Str } // records declared in one line need to put a semicolon after each field declaration -type MyRecOneline = #{field1: Float; field2: Str} +type MyRecOneline = rec {field1: Float; field2: Str} // tuples can be declared in one line without a semicolon because there's no ambiguity type MyTuple = #(Float Str) -type Nested = #{ - field1: #[ +type Nested = rec { + field1: enum { Boolean Bool Number Float - ] + } field2: #(Bool Bool) - field3: #{ + field3: rec { first: Int second: Int } diff --git a/corpus/types.sk b/corpus/types.sk index e0552ff..b9b36bf 100644 --- a/corpus/types.sk +++ b/corpus/types.sk @@ -1,40 +1,40 @@ -type Food = #[ +type Food = enum { Apple Num Banana Num Cereal Num Milk #(String Num) -] +} -type Utils = #[ +type Utils = enum { Paper Num Detergent Num Pencils Num -] +} -type Packaging = #[ +type Packaging = enum { Bag PlasticBag -] +} -type ShoppingList = #{ +type ShoppingList = rec { food: Food utils: Utils packaging: Packaging } -type NestedHell = #[ +type NestedHell = enum { NoNesting Num - Nesting #[ - FirstLevel #[ - SecondLevel #[ + Nesting enum { + FirstLevel enum { + SecondLevel enum { Third Lvl - ] - Second #{ + } + Second rec { first: Num second: Num } - ] + } StillFirst Num - ] -] + } +} diff --git a/src/grammar.pest b/src/grammar.pest index f0d9c13..e99471b 100644 --- a/src/grammar.pest +++ b/src/grammar.pest @@ -7,18 +7,21 @@ decl = { "let" ~ #name = ident ~ #args = (ident)* ~ "=" ~ expr } block = { "{" ~ MLF ~ block_line ~ MLF ~ "}" } block_line = _{ expr | (statement ~ MLF)* } -type_decl = { "type" ~ #name = type_ident ~ "=" ~ type_expr } -type_expr = { tuple | rec | enum | type_ident } +type_decl = { "type" ~ #name = type_ident ~ "=" ~ type_expr } +type_expr = { type_term | func | type_ident } +type_term = _{ tuple | rec | enum | type_ident } tuple = { "#(" ~ MLF ~ tuple_entry* ~ MLF ~ ")" } tuple_entry = _{ type_expr ~ MLF } -rec = { "#{" ~ MLF ~ (rec_field ~ TERMINATE ~ MLF)* ~ rec_field? ~ MLF ~ "}" } +rec = { "rec" ~ "{" ~ MLF ~ (rec_field ~ TERMINATE ~ MLF)* ~ rec_field? ~ MLF ~ "}" } rec_field = { #field_name = ident ~ ":" ~ #field_type = type_expr* } -enum = { "#[" ~ MLF ~ (enum_vrt ~ TERMINATE ~ MLF)* ~ enum_vrt? ~ "]" } +enum = { "enum" ~ "{" ~ MLF ~ (enum_vrt ~ TERMINATE ~ MLF)* ~ enum_vrt? ~ "}" } enum_vrt = { #vrt_name = ident ~ type_expr? } +func = { type_term ~ "->" ~ type_expr } + type_ident = ${ #name = ident ~ #args = type_args? } type_args = { "<" ~ MLF ~ type_ident* ~ MLF ~ ">" } @@ -48,7 +51,7 @@ float = @{ byte = @{ "0x" ~ ASCII_HEX_DIGIT{1, 2} | "0b" ~ ASCII_BIN_DIGIT{0, 8} } boolean = @{ "true" | "false" } -KEYWORD = _{ "let" } +KEYWORD = _{ "let" | "rec" | "enum" } TERMINATE = _{ LF | EOI | ";" } MLF = _{ (LF)* } LF = _{ "\n" | "\r" } diff --git a/src/test/mod.rs b/src/test/mod.rs index b4dfdfa..3feddd1 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -4,6 +4,21 @@ mod statements; mod types; use crate::parse; +use std::{collections::HashSet, path::PathBuf}; + +use lazy_static::lazy_static; +use pest_test::PestTester; + +use crate::{Rule, SnekParser}; + +fn s_tester>(dir: D) -> PestTester { + PestTester::new( + PathBuf::from("tests/corpus").join(dir.into()), + "txt", + Rule::file, + HashSet::new(), + ) +} #[test] fn it_parse_types() { @@ -22,3 +37,12 @@ fn it_parse_declarations() { panic!("{e}") } } + +lazy_static! { + static ref TESTER: PestTester = PestTester::new( + PathBuf::from("test/corpus"), + "txt", + Rule::file, + HashSet::new() + ); +} diff --git a/src/test/types.rs b/src/test/types.rs index 54c91f6..86a4fef 100644 --- a/src/test/types.rs +++ b/src/test/types.rs @@ -1,6 +1,12 @@ use pest::{consumes_to, parses_to}; +use pest_test::PestTester; use crate::{Rule, SnekParser}; +use lazy_static::lazy_static; + +lazy_static! { + static ref TESTER: PestTester = super::s_tester("types"); +} #[test] fn it_parses_aliases() { @@ -51,97 +57,21 @@ fn it_parses_tuples() { #[test] fn it_parses_recs() { - parses_to!(parser: SnekParser, input: "type MyRec = #{\nfield1: Num\n field2: Num2 }", rule: Rule::statement, tokens: [ - statement(0, 43, [ - type_decl(0, 43, [ - type_ident(5, 10, [ - ident(5, 10) - ]), - type_expr(13, 43, [ - rec(13, 43, [ - rec_field(16, 27, [ - ident(16, 22), - type_expr(24, 27, [ - type_ident(24, 27, [ - ident(24, 27) - ]) - ]), - ]), - rec_field(29, 41, [ - ident(29, 35), - type_expr(37, 41, [ - type_ident(37, 41, [ - ident(37, 41) - ]) - ]), - ]) - ]) - ]) - ]), - EOI(43, 43) - ]) - ]); + if let Err(e) = (*TESTER).evaluate_strict("recs") { + panic!("{e}") + } } #[test] fn it_parses_enums() { - parses_to!(parser: SnekParser, input: "type MyEnum = #[Var1 Num\nVar2 Type\n]", rule: Rule::statement, tokens: [ - statement(0, 36, [ - type_decl(0, 36, [ - type_ident(5, 11, [ - ident(5, 11) - ]), - type_expr(14, 36, [ - r#enum(14, 36, [ - enum_vrt(16, 24, [ - ident(16, 20), - type_expr(21, 24, [ - type_ident(21, 24, [ - ident(21, 24) - ]) - ]), - ]), - enum_vrt(25, 34, [ - ident(25, 29), - type_expr(30, 34, [ - type_ident(30, 34, [ - ident(30, 34) - ]) - ]), - ]), - ]) - ]) - ]), - EOI(36, 36) - ]) - ]); + if let Err(e) = (*TESTER).evaluate_strict("enums") { + panic!("{e}") + } } #[test] fn it_parses_generics() { - parses_to!(parser: SnekParser, input: "type MyNum = Num", rule: Rule::statement, tokens: [ - statement(0, 22, [ - type_decl(0, 22, [ - type_ident(5, 13, [ - ident(5, 10), - type_args(10, 13, [ - type_ident(11, 12, [ - ident(11, 12) - ]) - ]) - ]), - type_expr(16, 22, [ - type_ident(16, 22, [ - ident(16, 19), - type_args(19, 22, [ - type_ident(20, 21, [ - ident(20, 21) - ]) - ]) - ]) - ]) - ]), - EOI(22, 22) - ]) - ]); + if let Err(e) = (*TESTER).evaluate_strict("generics") { + panic!("{e}") + } } diff --git a/tests/corpus/types/enums.txt b/tests/corpus/types/enums.txt new file mode 100644 index 0000000..1bad6b0 --- /dev/null +++ b/tests/corpus/types/enums.txt @@ -0,0 +1,41 @@ +Enums Test + +====== + +type MyEnum = enum { + First Num + Second Str +} + +====== + +(file + (statement + (type_decl + (type_ident + (ident: "MyEnum") + ) + (type_expr + (enum + (enum_vrt + (ident: "First") + (type_expr + (type_ident + (ident: "Num") + ) + ) + ) + (enum_vrt + (ident: "Second") + (type_expr + (type_ident + (ident: "Str") + ) + ) + ) + ) + ) + ) + ) + (EOI: "") +) diff --git a/tests/corpus/types/generics.txt b/tests/corpus/types/generics.txt new file mode 100644 index 0000000..222e654 --- /dev/null +++ b/tests/corpus/types/generics.txt @@ -0,0 +1,33 @@ +Generics Test + +====== + +type MyNum = Num + +====== + +(file + (statement + (type_decl + (type_ident + (ident: "MyNum") + (type_args + (type_ident + (ident: "a") + ) + ) + ) + (type_expr + (type_ident + (ident: "Num") + (type_args + (type_ident + (ident: "a") + ) + ) + ) + ) + ) + ) + (EOI: "") +) diff --git a/tests/corpus/types/recs.txt b/tests/corpus/types/recs.txt new file mode 100644 index 0000000..12ad6fd --- /dev/null +++ b/tests/corpus/types/recs.txt @@ -0,0 +1,41 @@ +Recs Test + +====== + +type MyRec = rec { + first: Num + second: Str +} + +====== + +(file + (statement + (type_decl + (type_ident + (ident: "MyRec") + ) + (type_expr + (rec + (rec_field + (ident: "first") + (type_expr + (type_ident + (ident: "Num") + ) + ) + ) + (rec_field + (ident: "second") + (type_expr + (type_ident + (ident: "Str") + ) + ) + ) + ) + ) + ) + ) + (EOI: "") +)