diff --git a/Cargo.lock b/Cargo.lock index b5b8d8ae..e4734a6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,26 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" +dependencies = [ + "const-random", +] + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "aiken" version = "0.0.15" @@ -19,11 +39,22 @@ dependencies = [ "uplc", ] +[[package]] +name = "aiken-lang" +version = "0.0.0" +dependencies = [ + "chumsky", + "internment", + "miette", + "pretty_assertions", + "vec1", +] + [[package]] name = "anyhow" -version = "1.0.57" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "arrayvec" @@ -62,9 +93,9 @@ checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" [[package]] name = "bit-set" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ "bit-vec", ] @@ -94,17 +125,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "clap" -version = "3.1.18" +name = "chumsky" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" +checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4" +dependencies = [ + "ahash 0.3.8", +] + +[[package]] +name = "clap" +version = "3.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ "atty", "bitflags", "clap_derive", "clap_lex", "indexmap", - "lazy_static", + "once_cell", "strsim", "termcolor", "textwrap", @@ -112,9 +152,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.1.18" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck", "proc-macro-error", @@ -125,13 +165,41 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] +[[package]] +name = "const-random" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f590d95d011aa80b063ffe3253422ed5aa462af4e9867d43ce8337562bac77c4" +dependencies = [ + "const-random-macro", + "proc-macro-hack", +] + +[[package]] +name = "const-random-macro" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "615f6e27d000a2bffbc7f2f6a8669179378fa27ee4d0a509e985dfc0a7defb40" +dependencies = [ + "getrandom", + "lazy_static", + "proc-macro-hack", + "tiny-keccak", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "cryptoxide" version = "0.4.2" @@ -139,10 +207,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "129eabb7b0b78644a3a7e7cf220714aba47463bb281f69fa7a71ca5d12564cca" [[package]] -name = "fastrand" -version = "1.7.0" +name = "ctor" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] @@ -181,9 +265,12 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] [[package]] name = "heck" @@ -208,9 +295,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "indexmap" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", "hashbrown", @@ -225,6 +312,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "internment" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a798d7677f07d6f1e77be484ea8626ddb1566194de399f1206306820c406371" +dependencies = [ + "hashbrown", + "parking_lot", +] + [[package]] name = "itoa" version = "1.0.3" @@ -239,9 +336,19 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] [[package]] name = "log" @@ -252,6 +359,29 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "miette" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28d6092d7e94a90bb9ea8e6c26c99d5d112d49dda2afdb4f7ea8cf09e1a5a6d" +dependencies = [ + "miette-derive", + "once_cell", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2485ed7d1fe80704928e3eb86387439609bd0c6bb96db8208daa364cfd1e09" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "minicbor" version = "0.18.0" @@ -283,16 +413,31 @@ dependencies = [ ] [[package]] -name = "os_str_bytes" -version = "6.0.1" +name = "once_cell" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" + +[[package]] +name = "os_str_bytes" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" + +[[package]] +name = "output_vt100" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" +dependencies = [ + "winapi", +] [[package]] name = "pallas-addresses" -version = "0.14.0-alpha.3" +version = "0.14.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92140c1ffe3d3b71ad41c3879a506e34feb6ea42e5d75e02285c1483dd4a28b6" +checksum = "1188a2434037b74129f8d209a37a1d09721b900e6e378255db1a91abc37199bc" dependencies = [ "base58", "bech32", @@ -304,9 +449,9 @@ dependencies = [ [[package]] name = "pallas-codec" -version = "0.14.0-alpha.3" +version = "0.14.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749de2b1953b806a9f3e2b7d6cfee48a474c709b160eb143e34ab62a9a8eac97" +checksum = "65c035a772aa84e858e53b7c98e6036eaa216d8a699bb9c826787722bde13d05" dependencies = [ "hex", "minicbor", @@ -315,9 +460,9 @@ dependencies = [ [[package]] name = "pallas-crypto" -version = "0.14.0-alpha.3" +version = "0.14.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb57716e86f5c131ee8da655361dd8b9ec83a23c92c2a2ad3fb75352936fd5d" +checksum = "2841f9225dcd6a78c6f386d4d5e76bcdbecd7b4489455b2d2485b105bf4c0499" dependencies = [ "cryptoxide", "hex", @@ -329,9 +474,9 @@ dependencies = [ [[package]] name = "pallas-primitives" -version = "0.14.0-alpha.3" +version = "0.14.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d0711ac752a723c39c02204bf70de9173a83f2de29a2356256dd3b85ce7fd8a" +checksum = "3e51824547f7a1e1a6574ecec4bf8557f3819f435132873c0bae97acba81cbb1" dependencies = [ "base58", "bech32", @@ -345,9 +490,9 @@ dependencies = [ [[package]] name = "pallas-traverse" -version = "0.14.0-alpha.3" +version = "0.14.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aa8f851594c1e23f95ff533824fa8cff2c012d99fbcdcf82d1006237411d582" +checksum = "3291d1ae31cd803b9142fb32e1fcb0a58fc1d65b3bbe1d527d14b322db6eb019" dependencies = [ "hex", "pallas-addresses", @@ -357,6 +502,29 @@ dependencies = [ "thiserror", ] +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + [[package]] name = "peg" version = "0.8.0" @@ -402,6 +570,18 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "pretty_assertions" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +dependencies = [ + "ctor", + "diff", + "output_vt100", + "yansi", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -427,10 +607,16 @@ dependencies = [ ] [[package]] -name = "proc-macro2" -version = "1.0.39" +name = "proc-macro-hack" +version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58" dependencies = [ "unicode-ident", ] @@ -469,9 +655,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] @@ -499,9 +685,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] @@ -517,18 +703,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "regex-syntax" -version = "0.6.26" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "remove_dir_all" @@ -558,19 +744,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] -name = "serde" -version = "1.0.144" +name = "scopeguard" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.144" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ "proc-macro2", "quote", @@ -588,6 +780,12 @@ dependencies = [ "serde", ] +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + [[package]] name = "strsim" version = "0.10.0" @@ -596,9 +794,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.95" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" +checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" dependencies = [ "proc-macro2", "quote", @@ -630,30 +828,39 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "typed-arena" version = "2.0.1" @@ -662,15 +869,21 @@ checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" [[package]] name = "unicode-ident" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" [[package]] name = "unicode-segmentation" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "uplc" @@ -693,6 +906,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "vec1" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc1631c774f0f9570797191e01247cbefde789eebfbf128074cb934115a6133" + [[package]] name = "version_check" version = "0.9.4" @@ -744,3 +963,52 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/Cargo.toml b/Cargo.toml index f409c5ce..c66a4d73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["crates/cli", "crates/flat", "crates/uplc"] +members = ["crates/*"] diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index 5fbbbdcf..c8a6b5f3 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -7,6 +7,15 @@ use clap::{Parser, Subcommand}; #[clap(version, about, long_about = None)] #[clap(propagate_version = true)] pub enum Args { + /// Build an aiken project + Build, + /// Start a development server + Dev, + /// Create a new aiken project + New { + /// Project name + name: PathBuf, + }, /// A subcommand for working with transactions #[clap(subcommand)] Tx(TxCommand), @@ -50,39 +59,56 @@ pub enum TxCommand { /// Commands for working with Untyped Plutus Core #[derive(Subcommand)] pub enum UplcCommand { + /// Evaluate an Untyped Plutus Core program + Eval { + script: PathBuf, + + #[clap(short, long)] + flat: bool, + + /// Arguments to pass to the uplc program + args: Vec, + }, /// Encode textual Untyped Plutus Core to flat bytes Flat { + /// Textual Untyped Plutus Core file input: PathBuf, - #[clap(short, long)] - print: bool, + + /// Output file name #[clap(short, long)] out: Option, - #[clap(short, long)] - cbor_hex: bool, - }, - /// Decode flat bytes to textual Untyped Plutus Core - Unflat { - input: PathBuf, + + /// Print output instead of saving to file #[clap(short, long)] print: bool, - #[clap(short, long)] - out: Option, + #[clap(short, long)] cbor_hex: bool, }, /// Format an Untyped Plutus Core program Fmt { + /// Textual Untyped Plutus Core file input: PathBuf, + + /// Print output instead of saving to file #[clap(short, long)] print: bool, }, - /// Evaluate an Untyped Plutus Core program - Eval { - script: PathBuf, + /// Decode flat bytes to textual Untyped Plutus Core + Unflat { + /// Flat encoded Untyped Plutus Core file + input: PathBuf, + + /// Output file name #[clap(short, long)] - flat: bool, - /// Arguments to pass to the uplc program - args: Vec, + out: Option, + + /// Print output instead of saving to file + #[clap(short, long)] + print: bool, + + #[clap(short, long)] + cbor_hex: bool, }, } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index cdbcacb7..78a7b92b 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -23,6 +23,31 @@ fn main() -> anyhow::Result<()> { let args = Args::default(); match args { + Args::Build => { + // 1. load and parse modules + // * lib - contains modules, types, and functions + // * contracts - contains validators + // * scripts - contains native scripts dsl + // 2. type check everything + // 3. generate uplc and policy/address if relevant + todo!() + } + + Args::Dev => { + // launch a development server + // this should allow people to test + // their contracts over http + todo!() + } + + Args::New { name } => { + if !name.exists() { + fs::create_dir_all(name.join("lib"))?; + fs::create_dir_all(name.join("policies"))?; + fs::create_dir_all(name.join("scripts"))?; + } + } + Args::Tx(tx_cmd) => match tx_cmd { TxCommand::Simulate { input, @@ -161,6 +186,7 @@ fn main() -> anyhow::Result<()> { } } } + UplcCommand::Fmt { input, print } => { let code = std::fs::read_to_string(&input)?; diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml new file mode 100644 index 00000000..3168e866 --- /dev/null +++ b/crates/lang/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "aiken-lang" +version = "0.0.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +chumsky = "0.8.0" +internment = "0.7.0" +miette = "5.2.0" +vec1 = "1.8.0" + +[dev-dependencies] +pretty_assertions = "1.3.0" diff --git a/crates/lang/src/ast.rs b/crates/lang/src/ast.rs new file mode 100644 index 00000000..4264994c --- /dev/null +++ b/crates/lang/src/ast.rs @@ -0,0 +1,547 @@ +use std::{collections::HashMap, fmt, ops::Range, sync::Arc}; + +use internment::Intern; + +use crate::{ + expr::{TypedExpr, UntypedExpr}, + tipo::{self, PatternConstructor, Type, ValueConstructor}, +}; + +pub type TypedModule = Module; +pub type UntypedModule = Module<(), UntypedDefinition>; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ModuleKind { + Contract, + Lib, + Script, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Module { + pub name: Vec, + pub docs: Vec, + pub type_info: Info, + pub definitions: Vec, + pub kind: ModuleKind, +} + +pub type TypedDefinition = Definition, TypedExpr, String, String>; +pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>; + +#[derive(Debug, Clone, PartialEq)] +pub enum Definition { + Fn { + arguments: Vec>, + body: Expr, + doc: Option, + location: Span, + name: String, + public: bool, + return_annotation: Option, + return_type: T, + }, + + TypeAlias { + alias: String, + annotation: Annotation, + doc: Option, + location: Span, + parameters: Vec, + public: bool, + tipo: T, + }, + + DataType { + constructors: Vec>, + doc: Option, + location: Span, + name: String, + opaque: bool, + parameters: Vec, + public: bool, + typed_parameters: Vec, + }, + + Use { + as_name: Option, + location: Span, + module: Vec, + package: PackageName, + unqualified: Vec, + }, + + ModuleConstant { + doc: Option, + location: Span, + public: bool, + name: String, + annotation: Option, + value: Box>, + tipo: T, + }, +} + +pub type TypedConstant = Constant, String>; +pub type UntypedConstant = Constant<(), ()>; + +#[derive(Debug, Clone, PartialEq)] +pub enum Constant { + Int { + location: Span, + value: String, + }, + + String { + location: Span, + value: String, + }, + + Pair { + location: Span, + elements: Vec, + }, + + List { + location: Span, + elements: Vec, + tipo: T, + }, + + Record { + location: Span, + module: Option, + name: String, + args: Vec>, + tag: RecordTag, + tipo: T, + field_map: Option, + }, + + ByteString { + location: Span, + // segments: Vec>, + }, + + Var { + location: Span, + module: Option, + name: String, + constructor: Option>, + tipo: T, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CallArg { + pub label: Option, + pub location: Span, + pub value: A, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FieldMap { + pub arity: usize, + pub fields: HashMap, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct RecordConstructor { + pub location: Span, + pub name: String, + pub arguments: Vec>, + pub documentation: Option, + pub sugar: bool, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct RecordConstructorArg { + pub label: Option, + // ast + pub annotation: Annotation, + pub location: Span, + pub tipo: T, + pub doc: Option, +} + +pub type UntypedArg = Arg<()>; + +#[derive(Debug, Clone, PartialEq)] +pub struct Arg { + pub arg_name: ArgName, + pub location: Span, + pub annotation: Option, + pub tipo: T, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ArgName { + Discard { + name: String, + location: Span, + }, + LabeledDiscard { + label: String, + name: String, + location: Span, + }, + Named { + name: String, + location: Span, + }, + NamedLabeled { + name: String, + label: String, + location: Span, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UnqualifiedImport { + pub location: Span, + pub name: String, + pub as_name: Option, + pub layer: Layer, +} + +// TypeAst +#[derive(Debug, Clone, PartialEq)] +pub enum Annotation { + Constructor { + location: Span, + module: Option, + name: String, + arguments: Vec, + }, + + Fn { + location: Span, + arguments: Vec, + ret: Box, + }, + + Var { + location: Span, + name: String, + }, + + Tuple { + location: Span, + elems: Vec, + }, + + Hole { + location: Span, + name: String, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Layer { + Value, + Type, +} + +impl Default for Layer { + fn default() -> Self { + Layer::Value + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BinOp { + // Boolean logic + And, + Or, + + // Equality + Eq, + NotEq, + + // Order comparison + LtInt, + LtEqInt, + GtEqInt, + GtInt, + + // Maths + AddInt, + SubInt, + MultInt, + DivInt, + ModInt, +} + +pub type UntypedPattern = Pattern<(), ()>; +pub type TypedPattern = Pattern>; + +#[derive(Debug, Clone, PartialEq)] +pub enum Pattern { + Int { + location: Span, + value: String, + }, + + Float { + location: Span, + value: String, + }, + + String { + location: Span, + value: String, + }, + + /// The creation of a variable. + /// e.g. `assert [this_is_a_var, .._] = x` + Var { + location: Span, + name: String, + }, + + /// A reference to a variable in a bit string. This is always a variable + /// being used rather than a new variable being assigned. + VarUsage { + location: Span, + name: String, + tipo: Type, + }, + + /// A name given to a sub-pattern using the `as` keyword. + /// e.g. `assert #(1, [_, _] as the_list) = x` + Assign { + name: String, + location: Span, + pattern: Box, + }, + + /// A pattern that binds to any value but does not assign a variable. + /// Always starts with an underscore. + Discard { + name: String, + location: Span, + }, + + List { + location: Span, + elements: Vec, + tail: Option>, + }, + + /// The constructor for a custom type. Starts with an uppercase letter. + Constructor { + location: Span, + name: String, + arguments: Vec>, + module: Option, + constructor: Constructor, + with_spread: bool, + tipo: Type, + }, + + Tuple { + location: Span, + elems: Vec, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AssignmentKind { + Let, + Assert, +} + +pub type MultiPattern = Vec>; + +pub type UntypedMultiPattern = MultiPattern<(), ()>; +pub type TypedMultiPattern = MultiPattern>; + +pub type TypedClause = Clause, String>; + +pub type UntypedClause = Clause; + +#[derive(Debug, Clone, PartialEq)] +pub struct Clause { + pub location: Span, + pub pattern: MultiPattern, + pub alternative_patterns: Vec>, + pub guard: Option>, + pub then: Expr, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum ClauseGuard { + Equals { + location: Span, + left: Box, + right: Box, + }, + + NotEquals { + location: Span, + left: Box, + right: Box, + }, + + GtInt { + location: Span, + left: Box, + right: Box, + }, + + GtEqInt { + location: Span, + left: Box, + right: Box, + }, + + LtInt { + location: Span, + left: Box, + right: Box, + }, + + LtEqInt { + location: Span, + left: Box, + right: Box, + }, + + Or { + location: Span, + left: Box, + right: Box, + }, + + And { + location: Span, + left: Box, + right: Box, + }, + + Var { + location: Span, + tipo: Type, + name: String, + }, + + TupleIndex { + location: Span, + index: u64, + tipo: Type, + tuple: Box, + }, + + Constant(Constant), +} + +pub struct TypedRecordUpdateArg { + pub label: String, + pub location: Span, + pub value: TypedExpr, + pub index: usize, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct UntypedRecordUpdateArg { + pub label: String, + // pub location: SrcSpan, + pub value: UntypedExpr, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct RecordUpdateSpread { + pub base: Box, + pub location: Span, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TodoKind { + Keyword, + EmptyFunction, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct SrcId(Intern>); + +impl SrcId { + #[cfg(test)] + pub fn empty() -> Self { + SrcId(Intern::new(Vec::new())) + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Span { + pub src: SrcId, + pub start: usize, + pub end: usize, +} + +impl Span { + #[cfg(test)] + pub fn empty() -> Self { + use chumsky::Span; + + Self::new(SrcId::empty(), 0..0) + } + + pub fn src(&self) -> SrcId { + self.src + } + + pub fn range(&self) -> Range { + use chumsky::Span; + + self.start()..self.end() + } + + pub fn union(self, other: Self) -> Self { + use chumsky::Span; + + assert_eq!( + self.src, other.src, + "attempted to union spans with different sources" + ); + + Self { + start: self.start().min(other.start()), + end: self.end().max(other.end()), + ..self + } + } +} + +impl fmt::Debug for Span { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}:{:?}", self.src, self.range()) + } +} + +impl chumsky::Span for Span { + type Context = SrcId; + + type Offset = usize; + + fn new(context: Self::Context, range: Range) -> Self { + assert!(range.start <= range.end); + + Self { + src: context, + start: range.start, + end: range.end, + } + } + + fn context(&self) -> Self::Context { + self.src + } + + fn start(&self) -> Self::Offset { + self.start + } + + fn end(&self) -> Self::Offset { + self.end + } +} diff --git a/crates/lang/src/build.rs b/crates/lang/src/build.rs new file mode 100644 index 00000000..9070f46a --- /dev/null +++ b/crates/lang/src/build.rs @@ -0,0 +1,4 @@ +pub enum Origin { + Src, + Test, +} diff --git a/crates/lang/src/error.rs b/crates/lang/src/error.rs new file mode 100644 index 00000000..ecfce194 --- /dev/null +++ b/crates/lang/src/error.rs @@ -0,0 +1,110 @@ +use std::{collections::HashSet, fmt}; + +use crate::{ast::Span, token::Token}; + +#[derive(Debug)] +pub struct ParseError { + kind: ErrorKind, + span: Span, + #[allow(dead_code)] + while_parsing: Option<(Span, &'static str)>, + expected: HashSet, + label: Option<&'static str>, +} + +impl ParseError { + pub fn merge(mut self, other: Self) -> Self { + // TODO: Use HashSet + for expected in other.expected.into_iter() { + self.expected.insert(expected); + } + self + } +} + +impl PartialEq for ParseError { + fn eq(&self, other: &Self) -> bool { + self.kind == other.kind && self.span == other.span && self.label == other.label + } +} + +impl> chumsky::Error for ParseError { + type Span = Span; + + type Label = &'static str; + + fn expected_input_found>>( + span: Self::Span, + expected: Iter, + found: Option, + ) -> Self { + Self { + kind: found + .map(Into::into) + .map(ErrorKind::Unexpected) + .unwrap_or(ErrorKind::UnexpectedEnd), + span, + while_parsing: None, + expected: expected + .into_iter() + .map(|x| x.map(Into::into).unwrap_or(Pattern::End)) + .collect(), + label: None, + } + } + + fn with_label(mut self, label: Self::Label) -> Self { + self.label.get_or_insert(label); + self + } + + fn merge(self, other: Self) -> Self { + ParseError::merge(self, other) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum ErrorKind { + UnexpectedEnd, + Unexpected(Pattern), + Unclosed { + start: Pattern, + before_span: Span, + before: Option, + }, + NoEndBranch, +} + +#[derive(Debug, PartialEq, Eq, Hash)] +pub enum Pattern { + Char(char), + Token(Token), + Literal, + TypeIdent, + TermIdent, + End, +} + +impl From for Pattern { + fn from(c: char) -> Self { + Self::Char(c) + } +} +impl From for Pattern { + fn from(tok: Token) -> Self { + Self::Token(tok) + } +} + +impl fmt::Display for Pattern { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Pattern::Token(token) => write!(f, "{}", token), + Pattern::Char(c) => write!(f, "{:?}", c), + Pattern::Literal => write!(f, "literal"), + Pattern::TypeIdent => write!(f, "type name"), + Pattern::TermIdent => write!(f, "identifier"), + Pattern::End => write!(f, "end of input"), + } + } +} diff --git a/crates/lang/src/expr.rs b/crates/lang/src/expr.rs new file mode 100644 index 00000000..2cd2247b --- /dev/null +++ b/crates/lang/src/expr.rs @@ -0,0 +1,324 @@ +use std::sync::Arc; + +use vec1::Vec1; + +use crate::{ + ast::{ + Annotation, Arg, AssignmentKind, BinOp, CallArg, Clause, Pattern, RecordUpdateSpread, Span, + TodoKind, TypedRecordUpdateArg, UntypedRecordUpdateArg, + }, + tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor}, +}; + +pub enum TypedExpr { + Int { + location: Span, + tipo: Arc, + value: String, + }, + + Float { + location: Span, + tipo: Arc, + value: String, + }, + + String { + location: Span, + tipo: Arc, + value: String, + }, + + Sequence { + location: Span, + expressions: Vec, + }, + + /// A chain of pipe expressions. + /// By this point the type checker has expanded it into a series of + /// assignments and function calls, but we still have a Pipeline AST node as + /// even though it is identical to `Sequence` we want to use different + /// locations when showing it in error messages, etc. + Pipeline { + location: Span, + expressions: Vec, + }, + + Var { + location: Span, + constructor: ValueConstructor, + name: String, + }, + + Fn { + location: Span, + tipo: Arc, + is_capture: bool, + args: Vec>>, + body: Box, + return_annotation: Option, + }, + + List { + location: Span, + tipo: Arc, + elements: Vec, + tail: Option>, + }, + + Call { + location: Span, + tipo: Arc, + fun: Box, + args: Vec>, + }, + + BinOp { + location: Span, + tipo: Arc, + name: BinOp, + left: Box, + right: Box, + }, + + Assignment { + location: Span, + tipo: Arc, + value: Box, + pattern: Pattern>, + kind: AssignmentKind, + }, + + Try { + location: Span, + tipo: Arc, + value: Box, + then: Box, + pattern: Pattern>, + }, + + When { + location: Span, + tipo: Arc, + subjects: Vec, + clauses: Vec, String>>, + }, + + RecordAccess { + location: Span, + tipo: Arc, + label: String, + index: u64, + record: Box, + }, + + ModuleSelect { + location: Span, + tipo: Arc, + label: String, + module_name: String, + module_alias: String, + constructor: ModuleValueConstructor, + }, + + Tuple { + location: Span, + tipo: Arc, + elems: Vec, + }, + + TupleIndex { + location: Span, + tipo: Arc, + index: u64, + tuple: Box, + }, + + Todo { + location: Span, + label: Option, + tipo: Arc, + }, + + RecordUpdate { + location: Span, + tipo: Arc, + spread: Box, + args: Vec, + }, + + Negate { + location: Span, + value: Box, + }, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum UntypedExpr { + Int { + location: Span, + value: String, + }, + + Float { + location: Span, + value: String, + }, + + String { + location: Span, + value: String, + }, + + Sequence { + location: Span, + expressions: Vec, + }, + + Var { + location: Span, + name: String, + }, + + Fn { + location: Span, + is_capture: bool, + arguments: Vec>, + body: Box, + return_annotation: Option, + }, + + List { + location: Span, + elements: Vec, + tail: Option>, + }, + + Call { + location: Span, + fun: Box, + arguments: Vec>, + }, + + BinOp { + location: Span, + name: BinOp, + left: Box, + right: Box, + }, + + PipeLine { + expressions: Vec1, + }, + + Assignment { + location: Span, + value: Box, + pattern: Pattern<(), ()>, + kind: AssignmentKind, + annotation: Option, + }, + + Try { + location: Span, + value: Box, + pattern: Pattern<(), ()>, + then: Box, + annotation: Option, + }, + + Case { + location: Span, + subjects: Vec, + clauses: Vec>, + }, + + FieldAccess { + location: Span, + label: String, + container: Box, + }, + + Tuple { + location: Span, + elems: Vec, + }, + + TupleIndex { + location: Span, + index: u64, + tuple: Box, + }, + + Todo { + kind: TodoKind, + location: Span, + label: Option, + }, + + RecordUpdate { + location: Span, + constructor: Box, + spread: RecordUpdateSpread, + arguments: Vec, + }, + + Negate { + location: Span, + value: Box, + }, +} + +impl UntypedExpr { + pub fn append_in_sequence(self, next: Self) -> Self { + let location = Span { + start: self.location().start, + end: next.location().end, + ..self.location() + }; + + match self { + Self::Sequence { + mut expressions, .. + } => { + expressions.push(next); + Self::Sequence { + location, + expressions, + } + } + _ => Self::Sequence { + location, + expressions: vec![self, next], + }, + } + } + + pub fn location(&self) -> Span { + match self { + Self::Try { then, .. } => then.location(), + Self::PipeLine { expressions, .. } => expressions.last().location(), + Self::Fn { location, .. } + | Self::Var { location, .. } + | Self::Int { location, .. } + | Self::Todo { location, .. } + | Self::Case { location, .. } + | Self::Call { location, .. } + | Self::List { location, .. } + | Self::Float { location, .. } + | Self::BinOp { location, .. } + | Self::Tuple { location, .. } + | Self::String { location, .. } + | Self::Assignment { location, .. } + | Self::TupleIndex { location, .. } + | Self::FieldAccess { location, .. } + | Self::RecordUpdate { location, .. } + | Self::Negate { location, .. } => *location, + Self::Sequence { + location, + expressions, + .. + } => expressions.last().map(Self::location).unwrap_or(*location), + } + } +} diff --git a/crates/lang/src/lexer.rs b/crates/lang/src/lexer.rs new file mode 100644 index 00000000..504d66af --- /dev/null +++ b/crates/lang/src/lexer.rs @@ -0,0 +1,164 @@ +use chumsky::prelude::*; + +use crate::{ast::Span, error::ParseError, token::Token}; + +pub fn lexer() -> impl Parser, Error = ParseError> { + let int = text::int(10).map(|value| Token::Int { value }); + + let op = choice(( + just("==").to(Token::EqualEqual), + just('=').to(Token::Equal), + just("..").to(Token::Dot), + just('.').to(Token::Dot), + just("!=").to(Token::NotEqual), + just('!').to(Token::Bang), + just("<=").to(Token::LessEqual), + just('<').to(Token::Less), + just(">=").to(Token::GreaterEqual), + just('>').to(Token::Greater), + just('+').to(Token::Plus), + just("->").to(Token::RArrow), + just('-').to(Token::Minus), + just('*').to(Token::Star), + just('/').to(Token::Slash), + just('%').to(Token::Percent), + just("|>").to(Token::Pipe), + just(',').to(Token::Comma), + just(':').to(Token::Colon), + )); + + let grouping = choice(( + just('(').to(Token::LeftParen), + just(')').to(Token::RightParen), + just('[').to(Token::LeftSquare), + just(']').to(Token::RightSquare), + just('{').to(Token::LeftBrace), + just('}').to(Token::RightBrace), + )); + + let escape = just('\\').ignore_then( + just('\\') + .or(just('/')) + .or(just('"')) + .or(just('b').to('\x08')) + .or(just('f').to('\x0C')) + .or(just('n').to('\n')) + .or(just('r').to('\r')) + .or(just('t').to('\t')), + ); + + let string = just('"') + .ignore_then(filter(|c| *c != '\\' && *c != '"').or(escape).repeated()) + .then_ignore(just('"')) + .collect::() + .map(|value| Token::String { value }) + .labelled("string"); + + let keyword = text::ident().map(|s: String| match s.as_str() { + "as" => Token::As, + "assert" => Token::Assert, + "const" => Token::Const, + "fn" => Token::Fn, + "if" => Token::If, + "is" => Token::Is, + "let" => Token::Let, + "opaque" => Token::Opaque, + "pub" => Token::Pub, + "use" => Token::Use, + "todo" => Token::Todo, + "try" => Token::Try, + "type" => Token::Type, + "when" => Token::When, + _ => { + if s.chars().next().map_or(false, |c| c.is_uppercase()) { + Token::UpName { + // TODO: do not allow _ in upname + name: s, + } + } else if s.starts_with('_') { + Token::DiscardName { + // TODO: do not allow uppercase letters in discard name + name: s, + } + } else { + Token::Name { + // TODO: do not allow uppercase letters in name + name: s, + } + } + } + }); + + let token = choice((keyword, int, op, grouping, string)) + .or(any().map(Token::Error).validate(|t, span, emit| { + emit(ParseError::expected_input_found( + span, + None, + Some(t.clone()), + )); + t + })) + .map_with_span(move |token, span| (token, span)) + .padded() + .recover_with(skip_then_retry_until([])); + + let comments = just("//") + .then_ignore( + just('(') + .ignore_then(take_until(just(")#")).ignored()) + .or(none_of('\n').ignored().repeated().ignored()), + ) + .padded() + .ignored() + .repeated(); + + token + .padded_by(comments) + .repeated() + .padded() + .then_ignore(end()) +} + +#[cfg(test)] +mod tests { + use chumsky::prelude::*; + + use crate::{ + ast::{Span, SrcId}, + lexer, + token::Token, + }; + + #[test] + fn simple() { + let code = "pub type |> >=\n{ Thing _na_thing name"; + let len = code.chars().count(); + + let span = |i| Span::new(SrcId::empty(), i..i + 1); + + assert_eq!( + lexer::lexer() + .parse(chumsky::Stream::from_iter( + span(len), + code.chars().enumerate().map(|(i, c)| (c, span(i))), + )) + .map(|tokens| tokens.into_iter().map(|(tok, _)| tok).collect::>()), + Ok(vec![ + Token::Pub, + Token::Type, + Token::Pipe, + Token::GreaterEqual, + Token::LeftBrace, + Token::UpName { + name: "Thing".to_string() + }, + Token::DiscardName { + name: "_na_thing".to_string() + }, + Token::Name { + name: "name".to_string() + } + ]), + ); + } +} diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs new file mode 100644 index 00000000..49da21d2 --- /dev/null +++ b/crates/lang/src/lib.rs @@ -0,0 +1,8 @@ +pub mod ast; +pub mod build; +pub mod error; +pub mod expr; +pub mod lexer; +pub mod parser; +pub mod tipo; +pub mod token; diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs new file mode 100644 index 00000000..c2c21f2d --- /dev/null +++ b/crates/lang/src/parser.rs @@ -0,0 +1,811 @@ +use chumsky::prelude::*; + +use crate::{ + ast::{self, BinOp, TodoKind}, + error::ParseError, + expr, + token::Token, +}; + +pub fn module_parser( + kind: ast::ModuleKind, +) -> impl Parser { + choice(( + import_parser(), + data_parser(), + type_alias_parser(), + fn_parser(), + )) + .repeated() + .then_ignore(end()) + .map(move |definitions| ast::UntypedModule { + kind, + definitions, + docs: vec![], + name: vec![], + type_info: (), + }) +} + +pub fn import_parser() -> impl Parser { + let unqualified_import = choice(( + select! {Token::Name { name } => name}.then( + just(Token::As) + .ignore_then(select! {Token::Name { name } => name}) + .or_not(), + ), + select! {Token::UpName { name } => name}.then( + just(Token::As) + .ignore_then(select! {Token::UpName { name } => name}) + .or_not(), + ), + )) + .map_with_span(|(name, as_name), span| ast::UnqualifiedImport { + name, + location: span, + as_name, + layer: Default::default(), + }); + + let unqualified_imports = just(Token::Dot) + .ignore_then( + unqualified_import + .separated_by(just(Token::Comma)) + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), + ) + .or_not(); + + let as_name = just(Token::As) + .ignore_then(select! {Token::Name { name } => name}) + .or_not(); + + let module_path = select! {Token::Name { name } => name} + .separated_by(just(Token::Slash)) + .then(unqualified_imports) + .then(as_name); + + just(Token::Use).ignore_then(module_path).map_with_span( + |((module, unqualified), as_name), span| ast::UntypedDefinition::Use { + module, + as_name, + unqualified: unqualified.unwrap_or_default(), + package: (), + location: span, + }, + ) +} + +pub fn data_parser() -> impl Parser { + let unlabeled_constructor_type_args = type_parser() + .map_with_span(|annotation, span| ast::RecordConstructorArg { + label: None, + annotation, + tipo: (), + doc: None, + location: span, + }) + .separated_by(just(Token::Comma)) + .delimited_by(just(Token::LeftParen), just(Token::RightParen)); + + let constructors = select! {Token::UpName { name } => name} + .then( + choice(( + labeled_constructor_type_args(), + unlabeled_constructor_type_args, + )) + .or_not(), + ) + .map_with_span(|(name, arguments), span| ast::RecordConstructor { + location: span, + arguments: arguments.unwrap_or_default(), + name, + documentation: None, + sugar: false, + }) + .repeated() + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)); + + let record_sugar = labeled_constructor_type_args().map_with_span(|arguments, span| { + vec![ast::RecordConstructor { + location: span, + arguments, + documentation: None, + name: String::from("_replace"), + sugar: true, + }] + }); + + pub_parser() + .then(just(Token::Opaque).ignored().or_not()) + .or_not() + .then(type_name_with_args()) + .then(choice((constructors, record_sugar))) + .map_with_span(|((pub_opaque, (name, parameters)), constructors), span| { + ast::UntypedDefinition::DataType { + location: span, + constructors: constructors + .into_iter() + .map(|mut constructor| { + if constructor.sugar { + constructor.name = name.clone(); + } + + constructor + }) + .collect(), + doc: None, + name, + opaque: pub_opaque + .map(|(_, opt_opaque)| opt_opaque.is_some()) + .unwrap_or(false), + parameters: parameters.unwrap_or_default(), + public: pub_opaque.is_some(), + typed_parameters: vec![], + } + }) +} + +pub fn type_alias_parser() -> impl Parser { + pub_parser() + .or_not() + .then(type_name_with_args()) + .then_ignore(just(Token::Equal)) + .then(type_parser()) + .map_with_span(|((opt_pub, (alias, parameters)), annotation), span| { + ast::UntypedDefinition::TypeAlias { + alias, + annotation, + doc: None, + location: span, + parameters: parameters.unwrap_or_default(), + public: opt_pub.is_some(), + tipo: (), + } + }) +} + +pub fn fn_parser() -> impl Parser { + pub_parser() + .or_not() + .then_ignore(just(Token::Fn)) + .then(select! {Token::Name {name} => name}) + .then( + fn_param_parser() + .separated_by(just(Token::Comma)) + .delimited_by(just(Token::LeftParen), just(Token::RightParen)), + ) + .then(just(Token::RArrow).ignore_then(type_parser()).or_not()) + .then_ignore(just(Token::LeftBrace)) + .then(expr_seq_parser()) + .then_ignore(just(Token::RightBrace)) + .map_with_span( + |((((opt_pub, name), arguments), return_annotation), body), span| { + ast::UntypedDefinition::Fn { + arguments, + body, + doc: None, + location: span, + name, + public: opt_pub.is_some(), + return_annotation, + return_type: (), + } + }, + ) +} + +pub fn fn_param_parser() -> impl Parser { + choice(( + select! {Token::Name {name} => name} + .then(select! {Token::DiscardName {name} => name}) + .map_with_span(|(label, name), span| ast::ArgName::LabeledDiscard { + label, + name, + location: span, + }), + select! {Token::DiscardName {name} => name}.map_with_span(|name, span| { + ast::ArgName::Discard { + name, + location: span, + } + }), + select! {Token::Name {name} => name} + .then(select! {Token::Name {name} => name}) + .map_with_span(|(label, name), span| ast::ArgName::NamedLabeled { + label, + name, + location: span, + }), + select! {Token::Name {name} => name}.map_with_span(|name, span| ast::ArgName::Named { + name, + location: span, + }), + )) + .then(just(Token::Colon).ignore_then(type_parser()).or_not()) + .map_with_span(|(arg_name, annotation), span| ast::Arg { + location: span, + annotation, + tipo: (), + arg_name, + }) +} + +pub fn expr_seq_parser() -> impl Parser { + recursive(|r| { + choice(( + just(Token::Try) + .ignore_then(pattern_parser()) + .then(just(Token::Colon).ignore_then(type_parser()).or_not()) + .then_ignore(just(Token::Equal)) + .then(expr_parser()) + .then(r.clone()) + .map_with_span(|(((pattern, annotation), value), then_), span| { + expr::UntypedExpr::Try { + location: span, + value: Box::new(value), + pattern, + then: Box::new(then_), + annotation, + } + }), + expr_parser() + .then(r.repeated()) + .map_with_span(|(expr, exprs), _span| { + exprs + .into_iter() + .fold(expr, |acc, elem| acc.append_in_sequence(elem)) + }), + )) + }) +} + +pub fn expr_parser() -> impl Parser { + recursive(|_r| { + let op = choice(( + just(Token::Star).to(BinOp::MultInt), + just(Token::Slash).to(BinOp::DivInt), + just(Token::Percent).to(BinOp::ModInt), + )); + + let product = expr_unit_parser() + .then(op.then(expr_unit_parser()).repeated()) + .foldl(|a, (op, b)| expr::UntypedExpr::BinOp { + location: a.location().union(b.location()), + name: op, + left: Box::new(a), + right: Box::new(b), + }) + .boxed(); + + let op = choice(( + just(Token::Plus).to(BinOp::AddInt), + just(Token::Minus).to(BinOp::SubInt), + )); + + product + .clone() + .then(op.then(product).repeated()) + .foldl(|a, (op, b)| expr::UntypedExpr::BinOp { + location: a.location().union(b.location()), + name: op, + left: Box::new(a), + right: Box::new(b), + }) + }) +} + +pub fn expr_unit_parser() -> impl Parser { + choice(( + select! {Token::String {value} => value}.map_with_span(|value, span| { + expr::UntypedExpr::String { + location: span, + value, + } + }), + select! { Token::Int {value} => value}.map_with_span(|value, span| { + expr::UntypedExpr::Int { + location: span, + value, + } + }), + select! { + Token::Name { name } => name, + Token::UpName { name } => name, + } + .map_with_span(|name, span| expr::UntypedExpr::Var { + location: span, + name, + }), + just(Token::Todo) + .ignore_then( + select! {Token::String {value} => value} + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .or_not(), + ) + .map_with_span(|label, span| expr::UntypedExpr::Todo { + kind: TodoKind::Keyword, + location: span, + label, + }), + )) +} + +pub fn type_parser() -> impl Parser { + recursive(|r| { + choice(( + select! {Token::DiscardName { name } => name}.map_with_span(|name, span| { + ast::Annotation::Hole { + location: span, + name, + } + }), + just(Token::Fn) + .ignore_then( + r.clone() + .separated_by(just(Token::Comma)) + .delimited_by(just(Token::LeftParen), just(Token::RightParen)), + ) + .then_ignore(just(Token::RArrow)) + .then(r.clone()) + .map_with_span(|(arguments, ret), span| ast::Annotation::Fn { + location: span, + arguments, + ret: Box::new(ret), + }), + select! {Token::UpName { name } => name} + .then( + r.clone() + .separated_by(just(Token::Comma)) + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .or_not(), + ) + .map_with_span(|(name, arguments), span| ast::Annotation::Constructor { + location: span, + module: None, + name, + arguments: arguments.unwrap_or_default(), + }), + select! {Token::Name { name } => name} + .then( + just(Token::Dot) + .ignore_then(select! {Token::UpName {name} => name}) + .then( + r.separated_by(just(Token::Comma)) + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .or_not(), + ) + .or_not(), + ) + .map_with_span(|(mod_name, opt_dot), span| { + if let Some((name, arguments)) = opt_dot { + ast::Annotation::Constructor { + location: span, + module: Some(mod_name), + name, + arguments: arguments.unwrap_or_default(), + } + } else { + ast::Annotation::Var { + location: span, + name: mod_name, + } + } + }), + )) + }) +} + +pub fn labeled_constructor_type_args( +) -> impl Parser>, Error = ParseError> { + select! {Token::Name {name} => name} + .then_ignore(just(Token::Colon)) + .then(type_parser()) + .map_with_span(|(name, annotation), span| ast::RecordConstructorArg { + label: Some(name), + annotation, + tipo: (), + doc: None, + location: span, + }) + .separated_by(just(Token::Comma)) + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)) +} + +pub fn type_name_with_args() -> impl Parser>), Error = ParseError> +{ + just(Token::Type).ignore_then( + select! {Token::UpName { name } => name}.then( + select! {Token::Name { name } => name} + .separated_by(just(Token::Comma)) + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .or_not(), + ), + ) +} + +pub fn pub_parser() -> impl Parser { + just(Token::Pub).ignored() +} + +pub fn pattern_parser() -> impl Parser { + recursive(|r| { + let constructor_pattern_arg_parser = choice(( + select! {Token::Name {name} => name} + .then_ignore(just(Token::Colon)) + .then(r.clone()) + .map_with_span(|(name, pattern), span| ast::CallArg { + location: span, + label: Some(name), + value: pattern, + }), + r.map_with_span(|pattern, span| ast::CallArg { + location: span, + value: pattern, + label: None, + }), + )); + + let constructor_pattern_args_parser = constructor_pattern_arg_parser + .separated_by(just(Token::Comma)) + .allow_trailing() + .then( + just(Token::DotDot) + .then_ignore(just(Token::Comma).or_not()) + .ignored() + .or_not(), + ) + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .or_not() + .map(|opt_args| { + opt_args + .map(|(a, b)| (a, b.is_some())) + .unwrap_or_else(|| (vec![], false)) + }); + + let constructor_pattern_parser = + select! {Token::UpName { name } => name}.then(constructor_pattern_args_parser); + + choice(( + select! { Token::Name {name} => name } + .then( + just(Token::Dot) + .ignore_then(constructor_pattern_parser.clone()) + .or_not(), + ) + .map_with_span(|(name, opt_pattern), span| { + if let Some((c_name, (arguments, with_spread))) = opt_pattern { + ast::UntypedPattern::Constructor { + location: span, + name: c_name, + arguments, + module: Some(name), + constructor: (), + with_spread, + tipo: (), + } + } else { + ast::UntypedPattern::Var { + location: span, + name, + } + } + }), + constructor_pattern_parser.map_with_span(|(name, (arguments, with_spread)), span| { + ast::UntypedPattern::Constructor { + location: span, + name, + arguments, + module: None, + constructor: (), + with_spread, + tipo: (), + } + }), + select! {Token::DiscardName {name} => name}.map_with_span(|name, span| { + ast::UntypedPattern::Discard { + name, + location: span, + } + }), + select! {Token::String {value} => value}.map_with_span(|value, span| { + ast::UntypedPattern::String { + location: span, + value, + } + }), + select! {Token::Int {value} => value}.map_with_span(|value, span| { + ast::UntypedPattern::Int { + location: span, + value, + } + }), + )) + .then( + just(Token::As) + .ignore_then(select! { Token::Name {name} => name}) + .or_not(), + ) + .map_with_span(|(pattern, opt_as), span| { + if let Some(name) = opt_as { + ast::UntypedPattern::Assign { + name, + location: span, + pattern: Box::new(pattern), + } + } else { + pattern + } + }) + }) +} + +#[cfg(test)] +mod tests { + use chumsky::prelude::*; + use pretty_assertions::assert_eq; + + use crate::{ + ast::{self, Span, SrcId}, + expr, lexer, + parser::module_parser, + }; + + #[test] + fn simple() { + let code = r#" + use std/list + use std/address.{Address as A, thing as w} + use std/tx as t + + type Option(a) { + Some(a, Int) + None + Wow { name: Int, age: Int } + } + + pub opaque type User { + name: _w + } + + type Thing = Option(Int) + + pub type Me = Option(String) + + pub fn add_one(a) { + a + 1 + } + "#; + let len = code.chars().count(); + + let span = |i| Span::new(SrcId::empty(), i..i + 1); + + let tokens = lexer::lexer() + .parse(chumsky::Stream::from_iter( + span(len), + code.chars().enumerate().map(|(i, c)| (c, span(i))), + )) + .unwrap(); + + let res = module_parser(ast::ModuleKind::Script) + .parse(chumsky::Stream::from_iter(span(len), tokens.into_iter())) + .unwrap(); + + assert_eq!( + res, + ast::UntypedModule { + docs: vec![], + kind: ast::ModuleKind::Script, + name: vec![], + type_info: (), + definitions: vec![ + ast::UntypedDefinition::Use { + location: Span::new(SrcId::empty(), 13..25), + module: vec!["std".to_string(), "list".to_string()], + as_name: None, + unqualified: vec![], + package: (), + }, + ast::UntypedDefinition::Use { + location: Span::new(SrcId::empty(), 38..80), + module: vec!["std".to_string(), "address".to_string()], + as_name: None, + unqualified: vec![ + ast::UnqualifiedImport { + as_name: Some("A".to_string()), + location: Span::new(SrcId::empty(), 55..67), + layer: Default::default(), + name: "Address".to_string() + }, + ast::UnqualifiedImport { + as_name: Some("w".to_string()), + location: Span::new(SrcId::empty(), 69..79), + layer: Default::default(), + name: "thing".to_string() + } + ], + package: (), + }, + ast::UntypedDefinition::Use { + location: Span::new(SrcId::empty(), 93..108), + module: vec!["std".to_string(), "tx".to_string()], + as_name: Some("t".to_string()), + unqualified: vec![], + package: (), + }, + ast::UntypedDefinition::DataType { + location: Span::new(SrcId::empty(), 122..240), + constructors: vec![ + ast::RecordConstructor { + location: Span::new(SrcId::empty(), 153..165), + name: "Some".to_string(), + arguments: vec![ + ast::RecordConstructorArg { + label: None, + annotation: ast::Annotation::Var { + location: Span::new(SrcId::empty(), 158..159), + name: "a".to_string(), + }, + location: Span::new(SrcId::empty(), 158..159), + tipo: (), + doc: None, + }, + ast::RecordConstructorArg { + label: None, + annotation: ast::Annotation::Constructor { + location: Span::new(SrcId::empty(), 161..164), + module: None, + name: "Int".to_string(), + arguments: vec![], + }, + location: Span::new(SrcId::empty(), 161..164), + tipo: (), + doc: None, + }, + ], + documentation: None, + sugar: false, + }, + ast::RecordConstructor { + location: Span::new(SrcId::empty(), 180..184), + name: "None".to_string(), + arguments: vec![], + documentation: None, + sugar: false, + }, + ast::RecordConstructor { + location: Span::new(SrcId::empty(), 199..226), + name: "Wow".to_string(), + arguments: vec![ + ast::RecordConstructorArg { + label: Some("name".to_string(),), + annotation: ast::Annotation::Constructor { + location: Span::new(SrcId::empty(), 211..214), + module: None, + name: "Int".to_string(), + arguments: vec![], + }, + location: Span::new(SrcId::empty(), 205..214), + tipo: (), + doc: None, + }, + ast::RecordConstructorArg { + label: Some("age".to_string(),), + annotation: ast::Annotation::Constructor { + location: Span::new(SrcId::empty(), 221..224), + module: None, + name: "Int".to_string(), + arguments: vec![], + }, + location: Span::new(SrcId::empty(), 216..224), + tipo: (), + doc: None, + }, + ], + documentation: None, + sugar: false, + }, + ], + doc: None, + name: "Option".to_string(), + opaque: false, + parameters: vec!["a".to_string(),], + public: false, + typed_parameters: vec![], + }, + ast::UntypedDefinition::DataType { + location: Span::new(SrcId::empty(), 254..313), + constructors: vec![ast::RecordConstructor { + location: Span::new(SrcId::empty(), 275..313), + name: "User".to_string(), + arguments: vec![ast::RecordConstructorArg { + label: Some("name".to_string()), + annotation: ast::Annotation::Hole { + location: Span::new(SrcId::empty(), 297..299), + name: "_w".to_string(), + }, + location: Span::new(SrcId::empty(), 291..299), + tipo: (), + doc: None, + },], + documentation: None, + sugar: true, + },], + doc: None, + name: "User".to_string(), + opaque: true, + parameters: vec![], + public: true, + typed_parameters: vec![], + }, + ast::UntypedDefinition::TypeAlias { + alias: "Thing".to_string(), + annotation: ast::Annotation::Constructor { + location: Span::new(SrcId::empty(), 340..351), + module: None, + name: "Option".to_string(), + arguments: vec![ast::Annotation::Constructor { + location: Span::new(SrcId::empty(), 347..350), + module: None, + name: "Int".to_string(), + arguments: vec![], + },], + }, + doc: None, + location: Span::new(SrcId::empty(), 327..351), + parameters: vec![], + public: false, + tipo: (), + }, + ast::UntypedDefinition::TypeAlias { + alias: "Me".to_string(), + annotation: ast::Annotation::Constructor { + location: Span::new(SrcId::empty(), 379..393), + module: None, + name: "Option".to_string(), + arguments: vec![ast::Annotation::Constructor { + location: Span::new(SrcId::empty(), 386..392), + module: None, + name: "String".to_string(), + arguments: vec![], + },], + }, + doc: None, + location: Span::new(SrcId::empty(), 365..393), + parameters: vec![], + public: true, + tipo: (), + }, + ast::UntypedDefinition::Fn { + arguments: vec![ast::Arg { + arg_name: ast::ArgName::Named { + name: "a".to_string(), + location: Span::new(SrcId::empty(), 422..423), + }, + location: Span::new(SrcId::empty(), 422..423), + annotation: None, + tipo: (), + },], + body: expr::UntypedExpr::BinOp { + location: Span::new(SrcId::empty(), 441..446), + name: ast::BinOp::AddInt, + left: Box::new(expr::UntypedExpr::Var { + location: Span::new(SrcId::empty(), 441..442), + name: "a".to_string(), + }), + right: Box::new(expr::UntypedExpr::Int { + location: Span::new(SrcId::empty(), 445..446), + value: "1".to_string(), + }), + }, + doc: None, + location: Span::new(SrcId::empty(), 407..460), + name: "add_one".to_string(), + public: true, + return_annotation: None, + return_type: (), + }, + ] + }, + ); + } +} diff --git a/crates/lang/src/tipo.rs b/crates/lang/src/tipo.rs new file mode 100644 index 00000000..265c415c --- /dev/null +++ b/crates/lang/src/tipo.rs @@ -0,0 +1,163 @@ +use std::{cell::RefCell, collections::HashMap, sync::Arc}; + +use crate::{ + ast::{Constant, FieldMap, Span, TypedConstant}, + build::Origin, +}; + +#[derive(Debug, Clone, PartialEq)] +pub enum Type { + /// A nominal (named) type such as `Int`, `Float`, or a programmer defined + /// custom type such as `Person`. The type can take other types as + /// arguments (aka "generics" or "parametric polymorphism"). + /// + /// If the type is defined in the Gleam prelude the `module` field will be + /// empty, otherwise it will contain the name of the module that + /// defines the type. + /// + App { + public: bool, + module: Vec, + name: String, + args: Vec>, + }, + + /// The type of a function. It takes arguments and returns a value. + /// + Fn { + args: Vec>, + retrn: Arc, + }, + + /// A type variable. See the contained `TypeVar` enum for more information. + /// + Var { tipo: Arc> }, + + /// A tuple is an ordered collection of 0 or more values, each of which + /// can have a different type, so the `tuple` type is the sum of all the + /// contained types. + /// + Tuple { elems: Vec> }, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum TypeVar { + /// Unbound is an unbound variable. It is one specific type but we don't + /// know what yet in the inference process. It has a unique id which can be used to + /// identify if two unbound variable Rust values are the same Gleam type variable + /// instance or not. + /// + Unbound { id: u64 }, + /// Link is type variable where it was an unbound variable but we worked out + /// that it is some other type and now we point to that one. + /// + Link { tipo: Arc }, + /// A Generic variable stands in for any possible type and cannot be + /// specialised to any one type + /// + /// # Example + /// + /// ```gleam + /// type Cat(a) { + /// Cat(name: a) + /// } + /// // a is TypeVar::Generic + /// ``` + /// + Generic { id: u64 }, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ValueConstructor { + pub public: bool, + pub variant: ValueConstructorVariant, + pub tipo: Arc, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum ValueConstructorVariant { + /// A locally defined variable or function parameter + LocalVariable { location: Span }, + + /// A module constant + ModuleConstant { + location: Span, + module: String, + literal: Constant, String>, + }, + + /// A function belonging to the module + ModuleFn { + name: String, + field_map: Option, + module: Vec, + arity: usize, + location: Span, + }, + + /// A constructor for a custom type + Record { + name: String, + arity: usize, + field_map: Option, + location: Span, + module: String, + }, +} + +pub struct Module { + pub name: Vec, + pub origin: Origin, + pub package: String, + pub types: HashMap, + pub types_constructors: HashMap>, + pub values: HashMap, + pub accessors: HashMap, +} + +pub struct TypeConstructor { + pub public: bool, + pub origin: Span, + pub module: Vec, + pub parameters: Vec>, + pub typ: Arc, +} + +pub struct AccessorsMap { + pub public: bool, + pub tipo: Arc, + pub accessors: HashMap, +} + +pub struct RecordAccessor { + // TODO: smaller int. Doesn't need to be this big + pub index: u64, + pub label: String, + pub tipo: Arc, +} + +pub enum PatternConstructor { + Record { + name: String, + field_map: Option, + }, +} + +pub enum ModuleValueConstructor { + Record { + name: String, + arity: usize, + type_: Arc, + field_map: Option, + location: Span, + }, + + Fn { + location: Span, + }, + + Constant { + literal: TypedConstant, + location: Span, + }, +} diff --git a/crates/lang/src/token.rs b/crates/lang/src/token.rs new file mode 100644 index 00000000..cf6856e0 --- /dev/null +++ b/crates/lang/src/token.rs @@ -0,0 +1,147 @@ +use std::fmt; + +#[derive(Clone, Debug, PartialEq, Hash, Eq)] +pub enum Token { + Error(char), + Name { name: String }, + UpName { name: String }, + DiscardName { name: String }, + Int { value: String }, + String { value: String }, + // Groupings + LeftParen, // ( + RightParen, // ) + LeftSquare, // [ + RightSquare, // } + LeftBrace, // { + RightBrace, // } + // Int Operators + Plus, + Minus, + Star, + Slash, + Less, + Greater, + LessEqual, + GreaterEqual, + Percent, + // ByteString Operators + PlusDot, // '+.' + MinusDot, // '-.' + StarDot, // '*.' + SlashDot, // '/.' + LessDot, // '<.' + GreaterDot, // '>.' + LessEqualDot, // '<=.' + GreaterEqualDot, // '>=.' + // Other Punctuation + Colon, + Comma, + Hash, // '#' + Bang, // '!' + Equal, + EqualEqual, // '==' + NotEqual, // '!=' + Vbar, // '|' + VbarVbar, // '||' + AmperAmper, // '&&' + Pipe, // '|>' + Dot, // '.' + RArrow, // '->' + DotDot, // '..' + EndOfFile, + // Extra + CommentNormal, + CommentDoc, + CommentModule, + EmptyLine, + // Keywords (alphabetically): + As, + Assert, + Const, + Fn, + If, + Is, + Let, + Opaque, + Pub, + Use, + Todo, + Try, + Type, + When, +} + +impl fmt::Display for Token { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + Token::Error(c) => { + write!(f, "\"{}\"", c)?; + + return Ok(()); + } + Token::Name { name } => name, + Token::UpName { name } => name, + Token::DiscardName { name } => name, + Token::Int { value } => value, + Token::String { value } => value, + Token::LeftParen => "(", + Token::RightParen => ")", + Token::LeftSquare => "[", + Token::RightSquare => "]", + Token::LeftBrace => "{", + Token::RightBrace => "}", + Token::Plus => "+", + Token::Minus => "-", + Token::Star => "*", + Token::Slash => "/", + Token::Less => "<", + Token::Greater => ">", + Token::LessEqual => "<=", + Token::GreaterEqual => ">=", + Token::Percent => "%", + Token::PlusDot => "+.", + Token::MinusDot => "-.", + Token::StarDot => "*.", + Token::SlashDot => "/.", + Token::LessDot => "<.", + Token::GreaterDot => ">.", + Token::LessEqualDot => "<=.", + Token::GreaterEqualDot => ">=.", + Token::Colon => ":", + Token::Comma => ",", + Token::Hash => "#", + Token::Bang => "!", + Token::Equal => "=", + Token::EqualEqual => "==", + Token::NotEqual => "!=", + Token::Vbar => "|", + Token::VbarVbar => "||", + Token::AmperAmper => "&&", + Token::Pipe => "|>", + Token::Dot => ".", + Token::RArrow => "->", + Token::DotDot => "..", + Token::EndOfFile => "EOF", + Token::CommentNormal => "//", + Token::CommentDoc => "///", + Token::CommentModule => "////", + Token::EmptyLine => "EMPTYLINE", + Token::As => "as", + Token::Assert => "assert", + Token::When => "when", + Token::Is => "is", + Token::Const => "const", + Token::Fn => "fn", + Token::If => "if", + Token::Use => "import", + Token::Let => "let", + Token::Opaque => "opaque", + Token::Pub => "pub", + Token::Todo => "todo", + Token::Try => "try", + Token::Type => "type", + }; + write!(f, "\"{}\"", s) + } +} diff --git a/syntax.txt b/syntax.txt new file mode 100644 index 00000000..b71a8d3e --- /dev/null +++ b/syntax.txt @@ -0,0 +1,66 @@ +// imports +use std/address.Address as A // alias + +use std/list.{map, foldl} // access module members in one import + +// `///` used for document generation + +// Records (single constructor `data` type) +pub type Datum { + signer: Address, +} + +// above is suger for +pub type Datum { + Datum { signer: Address }, +} + +// type aliases +type A = Address + +// multiple constructors and a `generic` +pub type Redeemer(a) { + // records wrapped in parens + Buy(Address, a), + // records wrapped in curlies + Sell { address: Address, some_thing: a }, +} + +pub fn main(datum: Datum, redeemer: Redeemer, ctx: ScriptContext) { + [1, 2, 3] + |> list.map(fn(x) -> x + 1) +} + +// named and anonymous functions +fn(x) -> x + 1 + +fn add_one(x) -> x + 1 + +fn(x: Int) -> x + 1 + +fn add_one(label x: Int) -> x + 1 + +fn(x: Int) { + x + 1 +} + +fn(x: Int) -> Int { + x + 1 +} + +fn add_one(x: Int) -> Int { + x + 1 +} + +// can be curried +fn add(a, b) { + a + 1 +} + +let add_one = add(1) + +// matching +when redeemer is { + Buyer(address, thing) -> do_stuff(), + Seller { address, some_thing } -> do_seller_stuff(), +}