From 3a837e30be93d138ba5bbe9a868bd84b6d687978 Mon Sep 17 00:00:00 2001 From: azur Date: Thu, 29 Jun 2023 20:07:52 +0700 Subject: [PATCH] Copy over i dont know how to merge --- .gitignore | 9 - Cargo.lock | 729 +++++++++++++++++++++++++++++++++++------- Cargo.toml | 19 +- a.hlm | 1 - bin/Cargo.toml | 17 + bin/src/args.rs | 15 + bin/src/main.rs | 87 +++++ com/Cargo.toml | 13 + com/src/lib.rs | 0 example/fact.hlm | 8 - example/simple.hlm | 7 + ir/Cargo.toml | 12 + ir/src/lib.rs | 163 ++++++++++ src/args.rs | 11 - src/asts/ast.rs | 129 -------- src/asts/js.rs | 140 -------- src/asts/mod.rs | 3 - src/asts/past.rs | 59 ---- src/main.rs | 55 ---- src/read/mod.rs | 1 - src/read/parse.rs | 521 ------------------------------ src/trans/low.rs | 198 ------------ src/trans/mod.rs | 2 - src/trans/ty.rs | 31 -- syntax/Cargo.toml | 7 + syntax/src/expr.rs | 167 ++++++++++ syntax/src/lib.rs | 3 + syntax/src/parser.rs | 459 ++++++++++++++++++++++++++ syntax/src/ty.rs | 51 +++ typing/Cargo.toml | 8 + typing/src/infer.rs | 744 +++++++++++++++++++++++++++++++++++++++++++ typing/src/lib.rs | 3 + typing/src/rename.rs | 185 +++++++++++ typing/src/typed.rs | 60 ++++ 34 files changed, 2618 insertions(+), 1299 deletions(-) delete mode 100644 a.hlm create mode 100644 bin/Cargo.toml create mode 100644 bin/src/args.rs create mode 100644 bin/src/main.rs create mode 100644 com/Cargo.toml create mode 100644 com/src/lib.rs delete mode 100644 example/fact.hlm create mode 100644 example/simple.hlm create mode 100644 ir/Cargo.toml create mode 100644 ir/src/lib.rs delete mode 100644 src/args.rs delete mode 100644 src/asts/ast.rs delete mode 100644 src/asts/js.rs delete mode 100644 src/asts/mod.rs delete mode 100644 src/asts/past.rs delete mode 100644 src/main.rs delete mode 100644 src/read/mod.rs delete mode 100644 src/read/parse.rs delete mode 100644 src/trans/low.rs delete mode 100644 src/trans/mod.rs delete mode 100644 src/trans/ty.rs create mode 100644 syntax/Cargo.toml create mode 100644 syntax/src/expr.rs create mode 100644 syntax/src/lib.rs create mode 100644 syntax/src/parser.rs create mode 100644 syntax/src/ty.rs create mode 100644 typing/Cargo.toml create mode 100644 typing/src/infer.rs create mode 100644 typing/src/lib.rs create mode 100644 typing/src/rename.rs create mode 100644 typing/src/typed.rs diff --git a/.gitignore b/.gitignore index 88345f8..ea8c4bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1 @@ /target - -# These are backup files generated by rustfmt -**/*.rs.bk - -# MSVC Windows builds of rustc generate these, which store debugging information -*.pdb - -# Generated by the compiler -/*.js \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 7f1c4dc..1b35d7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,33 +4,103 @@ version = 3 [[package]] name = "ahash" -version = "0.7.6" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ - "getrandom", + "cfg-if", "once_cell", "version_check", ] [[package]] -name = "ansi_term" -version = "0.12.1" +name = "anstream" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371" dependencies = [ - "winapi", + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", ] [[package]] -name = "atty" -version = "0.2.14" +name = "anstyle" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" dependencies = [ - "hermit-abi", - "libc", - "winapi", + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "arbitrary" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" + +[[package]] +name = "ariadne" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "367fd0ad87307588d087544707bc5fbf4805ded96c7db922b70d368fa1cb5702" +dependencies = [ + "unicode-width", + "yansi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bin" +version = "0.1.0" +dependencies = [ + "ariadne", + "chumsky", + "clap", + "com", + "ir", + "syntax", + "typing", ] [[package]] @@ -39,6 +109,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + [[package]] name = "cc" version = "1.0.79" @@ -53,38 +129,241 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chumsky" -version = "0.9.2" +version = "1.0.0-alpha.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23170228b96236b5a7299057ac284a321457700bc8c41a4476052f0f4ba5349d" +checksum = "379cdc19530b72a1e76d94a350676eaea1455375533eb38f18dfa712f9996902" dependencies = [ - "hashbrown", + "hashbrown 0.13.2", "stacker", ] [[package]] name = "clap" -version = "2.34.0" +version = "4.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "956ac1f6381d8d82ab4684768f89c0ea3afe66925ceadb4eeb3fc452ffc55d62" dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", + "clap_builder", + "clap_derive", + "once_cell", ] [[package]] -name = "getrandom" -version = "0.2.8" +name = "clap_builder" +version = "4.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749" dependencies = [ - "cfg-if", + "anstream", + "anstyle", + "bitflags", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "com" +version = "0.1.0" +dependencies = [ + "chumsky", + "cranelift", + "cranelift-jit", + "cranelift-module", + "cranelift-native", + "syntax", + "typing", +] + +[[package]] +name = "cranelift" +version = "0.96.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128089b15e74e782e4e1a51c4b5b3bd4fbc35755777988aff24300dfa120f1c9" +dependencies = [ + "cranelift-codegen", + "cranelift-frontend", +] + +[[package]] +name = "cranelift-bforest" +version = "0.96.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c064a534a914eb6709d198525321a386dad50627aecfaf64053f369993a3e5a" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.96.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619ed4d24acef0bd58b16a1be39077c0b36c65782e6c933892439af5e799110e" +dependencies = [ + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli", + "hashbrown 0.13.2", + "log", + "regalloc2", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.96.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c777ce22678ae1869f990b2f31e0cd7ca109049213bfc0baf3e2205a18b21ebb" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.96.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb65884d17a1fa55990dd851c43c140afb4c06c3312cf42cfa1222c3b23f9561" + +[[package]] +name = "cranelift-control" +version = "0.96.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a0cea8abc90934d0a7ee189a29fd35fecd5c40f59ae7e6aab1805e8ab1a535e" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.96.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e50bebc05f2401a1320169314b62f91ad811ef20163cac00151d78e0684d4c" + +[[package]] +name = "cranelift-frontend" +version = "0.96.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b82ccfe704d53f669791399d417928410785132d809ec46f5e2ce069e9d17c8" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.96.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2515d8e7836f9198b160b2c80aaa1f586d7749d57d6065af86223fb65b7e2c3" + +[[package]] +name = "cranelift-jit" +version = "0.96.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3830a0c5c18c5ddf1c6d4a3dc049a3723d4606e24699796116963ac5bb0404" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity", + "cranelift-module", + "cranelift-native", "libc", - "wasi", + "log", + "region", + "target-lexicon", + "wasmtime-jit-icache-coherence", + "windows-sys", +] + +[[package]] +name = "cranelift-module" +version = "0.96.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2709ba70d7340ef9ffc408df1905bfd02f33eddeb6f8a7198d93c1d0c1154c3" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", +] + +[[package]] +name = "cranelift-native" +version = "0.96.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcb47ffdcdac7e9fed6e4a618939773a4dc4a412fa7da9e701ae667431a10af3" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", ] [[package]] @@ -92,47 +371,126 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ "ahash", ] [[package]] name = "heck" -version = "0.3.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "libc", + "autocfg", + "hashbrown 0.12.3", ] [[package]] -name = "holymer" +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "ir" version = "0.1.0" dependencies = [ "chumsky", - "structopt", + "serde", + "serde-lexpr", + "serde_derive", + "syntax", + "typing", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "is-terminal" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi", + "io-lifetimes", + "rustix", + "windows-sys", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "lexpr" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a84de6a9df442363b08f5dbf0cd5b92edc70097b89c4ce4bfea4679fe48bc67" +dependencies = [ + "itoa", + "lexpr-macros", + "ryu", +] + +[[package]] +name = "lexpr-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36b5cb8bb985c81a8ac1a0f8b5c4865214f574ddd64397ef7a99c236e21f35bb" +dependencies = [ + "proc-macro2", + "quote", +] [[package]] name = "libc" -version = "0.2.139" +version = "0.2.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" + +[[package]] +name = "linux-raw-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] [[package]] name = "once_cell" @@ -140,35 +498,11 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -184,13 +518,109 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] +[[package]] +name = "regalloc2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a52e724646c6c0800fc456ec43b4165d2f91fba88ceaca06d9e0b400023478" +dependencies = [ + "hashbrown 0.13.2", + "log", + "rustc-hash", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "region" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.37.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "serde" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" + +[[package]] +name = "serde-lexpr" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4cda13396159f59e7946118cdac0beadeecfb7cf76b197f4147e546f4ead6f" +dependencies = [ + "lexpr", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "slice-group-by" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "stacker" version = "0.1.15" @@ -206,39 +636,15 @@ dependencies = [ [[package]] name = "strsim" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.109" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -246,25 +652,31 @@ dependencies = [ ] [[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +name = "syntax" +version = "0.1.0" dependencies = [ - "unicode-width", + "chumsky", +] + +[[package]] +name = "target-lexicon" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" + +[[package]] +name = "typing" +version = "0.1.0" +dependencies = [ + "chumsky", + "syntax", ] [[package]] name = "unicode-ident" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775c11906edafc97bc378816b94585fbd9a054eabaf86fdd0ced94af449efab7" - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-width" @@ -273,10 +685,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] -name = "vec_map" -version = "0.8.2" +name = "utf8parse" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "version_check" @@ -285,10 +697,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "wasmtime-jit-icache-coherence" +version = "9.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "374ff63b3eb41db57c56682a9ef7737d2c9efa801f5dbf9da93941c9dd436a06" +dependencies = [ + "cfg-if", + "libc", + "windows-sys", +] [[package]] name = "winapi" @@ -311,3 +728,75 @@ 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.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[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 0b8b22d..bcc25ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,9 @@ -[package] -name = "holymer" -version = "0.1.0" -edition = "2021" +[workspace] -[dependencies] -chumsky = "0.9.0" -structopt = "0.3.26" - -[[bin]] -name = "hlmc" -path = "src/main.rs" \ No newline at end of file +members = [ + "bin", + "syntax", + "typing", + "ir", + "com", +] \ No newline at end of file diff --git a/a.hlm b/a.hlm deleted file mode 100644 index b1189ce..0000000 --- a/a.hlm +++ /dev/null @@ -1 +0,0 @@ -print("Hello\n"); \ No newline at end of file diff --git a/bin/Cargo.toml b/bin/Cargo.toml new file mode 100644 index 0000000..d003340 --- /dev/null +++ b/bin/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "bin" +version = "0.1.0" +edition = "2021" + +[dependencies] +ariadne = "0.2.0" +chumsky = "1.0.0-alpha.3" +clap = { version = "4.2.4", features = ["derive"] } +syntax = { path = "../syntax" } +typing = { path = "../typing" } +ir = { path = "../ir" } +com = { path = "../com" } + +[[bin]] +name = "hc" +path = "src/main.rs" \ No newline at end of file diff --git a/bin/src/args.rs b/bin/src/args.rs new file mode 100644 index 0000000..c6856b0 --- /dev/null +++ b/bin/src/args.rs @@ -0,0 +1,15 @@ +use clap::Parser; + +#[derive(Debug, Parser)] +pub struct Args { + /// The path to the file to be compiled. + #[arg(required = true)] + pub file: String, + /// Only run the type checker. + #[arg(short = 'c', long = "check")] + pub typecheck: bool, +} + +pub fn get_args() -> Args { + Args::parse() +} \ No newline at end of file diff --git a/bin/src/main.rs b/bin/src/main.rs new file mode 100644 index 0000000..5277c39 --- /dev/null +++ b/bin/src/main.rs @@ -0,0 +1,87 @@ +use ariadne::{sources, Color, Label, Report, ReportKind}; +use chumsky::{Parser, prelude::Input}; + +use ir::lower_expr; +use syntax::parser::{lexer, exprs_parser}; +use typing::infer::{infer_exprs, InferErrorKind}; + +pub mod args; + +fn main() { + let args = args::get_args(); + let filename = args.file.clone(); + let src = std::fs::read_to_string(&args.file).expect("file not found"); + + // Lexing & parsing + let (ts, errs) = lexer().parse(&src).into_output_errors(); + + let (ast, parse_errs) = if let Some(tokens) = &ts { + let (ast, parse_errs) = exprs_parser() + .map_with_span(|ast, span| (ast, span)) + .parse(tokens.as_slice().spanned((src.len()..src.len()).into())) + .into_output_errors(); + + (ast, parse_errs) + } else { + (None, vec![]) + }; + + // Typecheck if there are no lexing or parsing errors + if let Some(ast) = ast.filter(|_| errs.len() + parse_errs.len() == 0) { + let (ast, e) = infer_exprs(ast.0); + // If there is an error, print it + if !e.is_empty() { + e.into_iter() + .for_each(|e| { + let mut r = Report::build(ReportKind::Error, filename.clone(), e.span.start) + .with_message(e.title.to_string()); + + for (msg, kind, span) in e.labels { + r = r.with_label( + Label::new((filename.clone(), span.into_range())) + .with_message(msg.to_string()) + .with_color(match kind { + InferErrorKind::Error => Color::Red, + InferErrorKind::Hint => Color::Blue, + }), + ); + } + + r + .finish() + .print(sources([(filename.clone(), src.clone())])) + .unwrap() + }); + // Else go to the next stage + } else { + if args.typecheck { + ast.iter().for_each(|node| println!("{:?}", node.0)); + return; + } + // ast.iter().for_each(|node| println!("{:?}", node.0)); + let irs = ast.into_iter().map(|node| lower_expr(node.0)).collect::>(); + irs.iter().for_each(|ir| println!("{}", ir)); + } + }; + + // Report lex & parse errors + errs.into_iter() + .map(|e| e.map_token(|c| c.to_string())) + .chain( + parse_errs + .into_iter() + .map(|e| e.map_token(|tok| tok.to_string())), + ) + .for_each(|e| { + Report::build(ReportKind::Error, filename.clone(), e.span().start) + .with_message(e.to_string()) + .with_label( + Label::new((filename.clone(), e.span().into_range())) + .with_message(e.reason().to_string()) + .with_color(Color::Red), + ) + .finish() + .print(sources([(filename.clone(), src.clone())])) + .unwrap() + }); +} \ No newline at end of file diff --git a/com/Cargo.toml b/com/Cargo.toml new file mode 100644 index 0000000..e6f2479 --- /dev/null +++ b/com/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "com" +version = "0.1.0" +edition = "2021" + +[dependencies] +chumsky = "1.0.0-alpha.3" +cranelift = "0.96.3" +cranelift-jit = "0.96.3" +cranelift-module = "0.96.3" +cranelift-native = "0.96.3" +syntax = { path = "../syntax" } +typing = { path = "../typing" } \ No newline at end of file diff --git a/com/src/lib.rs b/com/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/example/fact.hlm b/example/fact.hlm deleted file mode 100644 index ea5a15b..0000000 --- a/example/fact.hlm +++ /dev/null @@ -1,8 +0,0 @@ -func fact (n : num) num = - if n == 0 then - 1 - else - n * fact(n - 1) -; - -println(fact(5)); \ No newline at end of file diff --git a/example/simple.hlm b/example/simple.hlm new file mode 100644 index 0000000..4bb2408 --- /dev/null +++ b/example/simple.hlm @@ -0,0 +1,7 @@ +let add = fun (x Int, y Int) Int -> x + y; +let succ = fun (x) -> x + 1; +let mul = fun (x, y) -> x * y; + +add(33, 34) +|> fun (x) -> succ(x) +|> fun (x) -> mul(x, 10) \ No newline at end of file diff --git a/ir/Cargo.toml b/ir/Cargo.toml new file mode 100644 index 0000000..5b0be5f --- /dev/null +++ b/ir/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ir" +version = "0.1.0" +edition = "2021" + +[dependencies] +chumsky = "1.0.0-alpha.3" +serde = "1.0.164" +serde-lexpr = "0.1.3" +serde_derive = "1.0.164" +syntax = { path = "../syntax" } +typing = { path = "../typing" } diff --git a/ir/src/lib.rs b/ir/src/lib.rs new file mode 100644 index 0000000..760bb01 --- /dev/null +++ b/ir/src/lib.rs @@ -0,0 +1,163 @@ +use typing::typed::TExpr; +use syntax::expr::{Lit as ExprLit, UnaryOp, BinaryOp}; + +use std::fmt::{self, Display, Formatter, Result as FmtResult}; + +#[derive(Clone, Debug)] +pub enum Lit<'src> { + Unit, + Bool(bool), + Int(i64), + Str(&'src str), +} + +impl Display for Lit<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + Lit::Unit => write!(f, "()"), + Lit::Bool(b) => write!(f, "{}", b), + Lit::Int(i) => write!(f, "{}", i), + Lit::Str(s) => write!(f, "\"{}\"", s), + } + } +} + +#[derive(Clone, Debug)] +pub enum Expr<'src> { + Lit(Lit<'src>), + // v0 + Var(&'src str), + // f(v0, v1, ...) + Call(Vec), +} + +impl Display for Expr<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + Expr::Lit(l) => write!(f, "{}", l), + Expr::Var(s) => write!(f, "{}", s), + Expr::Call(v) => { + write!(f, "(")?; + for (i, e) in v.iter().enumerate() { + if i != 0 { + write!(f, " ")?; + } + write!(f, "{}", e)?; + } + write!(f, ")") + } + } + } +} + +macro_rules! unbox { + ($e:expr) => { + *(($e).0) + }; +} + +macro_rules! str { + ($e:expr) => { + Expr::Lit(Lit::Str($e)) + }; +} + +macro_rules! var { + ($e:expr) => { + Expr::Var($e) + }; +} + +macro_rules! call { + ($e:expr) => { + Expr::Call($e) + }; +} + +pub fn lower_lit(lit: ExprLit) -> Lit { + match lit { + ExprLit::Unit => Lit::Unit, + ExprLit::Bool(b) => Lit::Bool(b), + ExprLit::Int(i) => Lit::Int(i), + ExprLit::Str(s) => Lit::Str(s), + } +} + +pub fn lower_expr(e: TExpr) -> Expr { + match e { + TExpr::Lit(l) => Expr::Lit(lower_lit(l)), + TExpr::Ident(s) => var!(s), + TExpr::Unary { op, expr, .. } => { + let expr = lower_expr(unbox!(expr)); + match op { + UnaryOp::Neg => call!(vec![var!("neg"), expr]), + UnaryOp::Not => call!(vec![var!("not"), expr]), + } + } + TExpr::Binary { op: BinaryOp::Pipe, lhs, rhs, .. } => { + let lhs = lower_expr(unbox!(lhs)); // arguments + let rhs = lower_expr(unbox!(rhs)); // function + call!(vec![rhs, lhs]) + } + TExpr::Binary { op, lhs, rhs, .. } => { + let lhs = lower_expr(unbox!(lhs)); + let rhs = lower_expr(unbox!(rhs)); + match op { + BinaryOp::Add => call!(vec![var!("+"), lhs, rhs]), + BinaryOp::Sub => call!(vec![var!("-"), lhs, rhs]), + BinaryOp::Mul => call!(vec![var!("*"), lhs, rhs]), + BinaryOp::Div => call!(vec![var!("/"), lhs, rhs]), + BinaryOp::Rem => call!(vec![var!("%"), lhs, rhs]), + BinaryOp::Eq => call!(vec![var!("=="), lhs, rhs]), + BinaryOp::Ne => call!(vec![var!("!="), lhs, rhs]), + BinaryOp::Lt => call!(vec![var!("<"), lhs, rhs]), + BinaryOp::Le => call!(vec![var!("<="), lhs, rhs]), + BinaryOp::Gt => call!(vec![var!(">"), lhs, rhs]), + BinaryOp::Ge => call!(vec![var!(">="), lhs, rhs]), + BinaryOp::And => call!(vec![var!("&&"), lhs, rhs]), + BinaryOp::Or => call!(vec![var!("||"), lhs, rhs]), + BinaryOp::Pipe => unreachable!("pipe operator is handled separately"), + } + } + TExpr::Lambda { params, body, .. } => { + let body = lower_expr(unbox!(body)); + call!(vec![ + var!("lambda"), + call!(params.into_iter().map(|(p, _)| var!(p)).collect()), + body, + ]) + } + TExpr::Call { func, args } => { + let func = lower_expr(unbox!(func)); + let args = args.into_iter() + .map(|(a, _)| lower_expr(a)) + .collect::>(); + call!(vec![func].into_iter().chain(args).collect()) + } + TExpr::If { cond, t, f, .. } => { + let cond = lower_expr(unbox!(cond)); + let t = lower_expr(unbox!(t)); + let f = lower_expr(unbox!(f)); + call!(vec![var!("if"), cond, t, f]) + } + TExpr::Let { name, value, body, .. } => { + let value = lower_expr(unbox!(value)); + let body = lower_expr(unbox!(body)); + call!(vec![var!("let"), str!(name), value, body]) + } + TExpr::Define { name, value, .. } => { + let value = lower_expr(unbox!(value)); + call!(vec![var!("define"), var!(name), value]) + } + TExpr::Block { exprs, void, .. } => { + let exprs = exprs.into_iter() + .map(|(e, _)| lower_expr(e)) + .collect::>(); + if void { + call!(vec![var!("block"), call!(exprs)]) + } else { + call!(vec![var!("block"), call!(exprs), var!("()")]) + } + } + } +} \ No newline at end of file diff --git a/src/args.rs b/src/args.rs deleted file mode 100644 index f176741..0000000 --- a/src/args.rs +++ /dev/null @@ -1,11 +0,0 @@ -use std::path::PathBuf; -use structopt::StructOpt; - -#[derive(Debug, StructOpt)] -pub struct Options { - #[structopt(name = "FILE", parse(from_os_str))] - pub file: PathBuf, - - #[structopt(short, long = "out", parse(from_os_str))] - pub output: Option, -} diff --git a/src/asts/ast.rs b/src/asts/ast.rs deleted file mode 100644 index aed7bbb..0000000 --- a/src/asts/ast.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::fmt::{Display, Formatter, Result as FmtResult}; -use crate::trans::ty::Type; - -#[derive(Clone, Debug)] -pub enum UnaryOp { - Neg, - Not, -} - -#[derive(Clone, Debug)] -pub enum BinaryOp { - Add, Sub, Mul, Div, Mod, - Eq, Neq, Lt, Gt, Lte, Gte, - And, Or, -} - -#[derive(Clone, Debug)] -pub enum Literal { - Num(i64), Str(String), Bool(bool), Unit, -} - -/// Enum to represent internal expression -#[derive(Clone, Debug)] -pub enum Expr { - Lit(Literal), - Sym(String), - Vec(Vec), - - UnaryOp(UnaryOp, Box), - BinaryOp(BinaryOp, Box, Box), - - Call(Box, Vec), - Lambda { - args: Vec, - body: Vec, - }, - If { - cond: Box, - t: Box, - f: Box, - }, - Defines(Vec<(String, Self)>), - Return(Box), -} - -#[derive(Clone, Debug)] -pub enum Stmt { - Expr(Expr), - Func { - name: String, - args: Vec, - ret: Type, - body: Expr, - }, -} - -impl Display for Expr { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - match self { - Expr::Lit(l) => match l { - Literal::Num(n) => write!(f, "{}", n), - Literal::Str(s) => write!(f, "\"{}\"", s), - Literal::Bool(b) => write!(f, "{}", b), - Literal::Unit => write!(f, "()"), - }, - Expr::Sym(s) => write!(f, "{}", s), - Expr::Vec(v) => { - write!(f, "[")?; - for (i, e) in v.iter().enumerate() { - if i > 0 { write!(f, " ")?; } - write!(f, "{}", e)?; - } - write!(f, "]") - }, - - Expr::UnaryOp(op, e) => write!(f, "({} {})", format!("{:?}", op).to_lowercase(), e), - Expr::BinaryOp(op, e1, e2) => write!(f, "({} {} {})", format!("{:?}", op).to_lowercase(), e1, e2), - - Expr::Call(c, args) => { - write!(f, "({}", c)?; - for arg in args { - write!(f, " {}", arg)?; - } - write!(f, ")") - }, - Expr::Lambda { args, body } => { - write!(f, "(lambda ")?; - for arg in args { - write!(f, " {}", arg)?; - } - if body.len() == 1 { - write!(f, " {})", body[0]) - } else { - write!(f, " (do")?; - for e in body { - write!(f, " {}", e)?; - } - write!(f, "))") - } - }, - Expr::If { cond, t, f: fe } => { - write!(f, "(if {} {} {})", cond, t, fe) - }, - Expr::Defines(defs) => { - write!(f, "(defs ")?; - for (name, expr) in defs { - write!(f, "({} {})", name, expr)?; - } - write!(f, ")") - }, - Expr::Return(e) => write!(f, "(return {})", e), - } - } -} - -impl Display for Stmt { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - match self { - Stmt::Expr(e) => write!(f, "{}", e), - Stmt::Func { name, args, ret, body } => { - write!(f, "(defn {} [", name)?; - for name in args { - write!(f, " {}", name)?; - } - write!(f, "] {} {})", ret, body) - }, - } - } -} diff --git a/src/asts/js.rs b/src/asts/js.rs deleted file mode 100644 index 396521a..0000000 --- a/src/asts/js.rs +++ /dev/null @@ -1,140 +0,0 @@ -use std::fmt::{Display, Formatter, Result as FmtResult}; -use crate::trans::ty::Type; - -#[derive(Clone, Debug)] -pub enum JSLiteral { Num(i64), Str(String), Bool(bool), Undefined } - -/// Enum to represent javascript expression -#[derive(Clone, Debug)] -pub enum JSExpr { - Lit(JSLiteral), - Sym(String), - Array(Vec), - - Op(&'static str, Box, Option>), - - Call(Box, Vec), - Method(Box, String), - Lambda { - args: Vec, - body: Vec, - }, - If { - cond: Box, - t: Box, - f: Box, - }, - Defines(Vec<(String, Self)>), - Return(Box), -} - -#[derive(Clone, Debug)] -pub enum JSStmt { - Expr(JSExpr), - Func { - name: String, - args: Vec, - ret: Type, - body: JSExpr, - }, -} - -impl Display for JSExpr { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - match self { - JSExpr::Lit(l) => match l { - JSLiteral::Num(n) => write!(f, "{}", n), - JSLiteral::Str(s) => write!(f, "'{}'", s), - JSLiteral::Bool(b) => write!(f, "{}", b), - JSLiteral::Undefined => write!(f, "undefined"), - }, - JSExpr::Sym(s) => write!(f, "{}", s), - JSExpr::Array(v) => { - write!(f, "[")?; - for (i, e) in v.iter().enumerate() { - if i > 0 { write!(f, ", ")?; } - write!(f, "{}", e)?; - } - write!(f, "]") - }, - - JSExpr::Op(op, lhs, rhs) => { - match rhs { - Some(rhs) => write!(f, "({} {} {})", lhs, op, rhs), - None => write!(f, "({} {})", op, lhs), - } - } - - JSExpr::Call(c, args) => { - write!(f, "{}(", c)?; - for (i, arg) in args.iter().enumerate() { - if i > 0 { - write!(f, ", ")?; - } - write!(f, "{}", arg)?; - } - write!(f, ")") - }, - JSExpr::Method(c, m) => write!(f, "{}.{}", c, m), - JSExpr::Lambda { args, body } => { - write!(f, "((")?; - for (i, name) in args.iter().enumerate() { - if i > 0 { - write!(f, ", ")?; - } - write!(f, "{}", name)?; - } - // write!(f, ") => {})", body) - if body.len() == 1 { - write!(f, ") => {})", body[0]) - } else { - write!(f, ") => {{")?; - for e in body { - write!(f, "{};", e)?; - } - write!(f, "}})") - } - }, - JSExpr::If { cond, t, f: fe } => { - write!(f, "({} ? {} : {})", cond, t, fe) - }, - JSExpr::Defines(vs) => { - write!(f, "let [")?; - for (i, (name, _)) in vs.iter().enumerate() { - if i > 0 { - write!(f, ", ")?; - } - write!(f, "{}", name)?; - } - write!(f, "] = [")?; - for (i, (_, expr)) in vs.iter().enumerate() { - if i > 0 { - write!(f, ", ")?; - } - write!(f, "{}", expr)?; - } - write!(f, "]") - } - JSExpr::Return(e) => write!(f, "return {}", e), - } - } -} - -impl Display for JSStmt { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - match self { - JSStmt::Expr(e) => write!(f, "{}", e), - JSStmt::Func { name, args, ret: _, body } => { - // const name = (args) => body; - write!(f, "const {} = (", name)?; - for (i, name) in args.iter().enumerate() { - if i > 0 { - write!(f, ", ")?; - } - write!(f, "{}", name)?; - } - write!(f, ") => {};", body) - }, - } - } -} \ No newline at end of file diff --git a/src/asts/mod.rs b/src/asts/mod.rs deleted file mode 100644 index 80a8284..0000000 --- a/src/asts/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod past; -pub mod ast; -pub mod js; \ No newline at end of file diff --git a/src/asts/past.rs b/src/asts/past.rs deleted file mode 100644 index 93cbd54..0000000 --- a/src/asts/past.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::trans::ty::*; - -use crate::read::parse::Spanned; - -#[derive(Clone, Debug)] -pub enum PUnaryOp { - Neg, - Not, -} - -#[derive(Clone, Debug)] -pub enum PBinaryOp { - Add, Sub, Mul, Div, Mod, - Eq, Neq, Lt, Gt, Lte, Gte, - And, Or, -} - -#[derive(Clone, Debug)] -pub enum PLiteral { Num(i64), Str(String), Bool(bool), Unit } - -#[derive(Clone, Debug)] -pub enum PExpr { - Error, - - Lit(PLiteral), - Sym(String), - Vec(Vec>), - - Unary(Spanned, Box>), - Binary(Spanned, Box>, Box>), - - Call(Box>, Vec>), - Lambda { - args: Vec<(String, Type)>, - body: Box>, - }, - Let { - vars: Vec<(String, Type, Spanned)>, - body: Option>>, - }, - If { - cond: Box>, - t: Box>, - f: Box>, - }, - Block(Vec>), - Return(Box>), -} - -#[derive(Clone, Debug)] -pub enum PStmt { - Expr(Spanned), - Func { - name: String, - args: Vec<(String, Type)>, - ret: Type, - body: Box>, - }, -} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index baa5696..0000000 --- a/src/main.rs +++ /dev/null @@ -1,55 +0,0 @@ -#![feature(trait_alias)] -pub mod asts; -pub mod read; -pub mod trans; -pub mod args; - -use std::io::Write; -use args::Options; -use read::parse::{lex, parse}; -use structopt::StructOpt; -use trans::low::{translate_stmt, translate_js_stmt}; - -fn main() { - let opt = Options::from_args(); - let src = std::fs::read_to_string(opt.file).expect("Failed to read file"); - - let (tokens, lex_errs) = lex(src.to_owned()); - - let parse_errs = if let Some(tokens) = tokens { - let (past, parse_errs) = parse(tokens, src.len()); - - if let Some(past) = past { - let ast = past.into_iter().map(|(e, _)| translate_stmt(e)).collect::>(); - let js = ast.into_iter().map(translate_js_stmt).collect::>(); - - let mut file = std::fs::File::create(opt.output.unwrap_or_else(|| "out.js".to_owned())) - .expect("Failed to create file"); - let s = js - .into_iter() - .map(|e| { - let s = format!("{}", e); - if s.ends_with(';') { - s - } else { - format!("{};", s) - } - }) - .collect::>() - .join("\n"); - file.write_all(s.as_bytes()).expect("Failed to write to file"); - } - - parse_errs - } else { - Vec::new() - }; - - if !lex_errs.is_empty() || !parse_errs.is_empty() { - lex_errs - .into_iter() - .map(|e| e.map(|c| c.to_string())) - .chain(parse_errs.into_iter().map(|e| e.map(|t| t.to_string()))) - .for_each(|e| println!("[{:?} {:?}] {}", e.span(), e.label(), e)); - } -} diff --git a/src/read/mod.rs b/src/read/mod.rs deleted file mode 100644 index 329584d..0000000 --- a/src/read/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod parse; \ No newline at end of file diff --git a/src/read/parse.rs b/src/read/parse.rs deleted file mode 100644 index b93fade..0000000 --- a/src/read/parse.rs +++ /dev/null @@ -1,521 +0,0 @@ -#![allow(clippy::type_complexity)] -use chumsky::{prelude::*, Stream}; -use std::fmt::{Display, Formatter, Result as FmtResult}; -use crate::trans::ty::Type; - -use crate::asts::past::*; - -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum Delim { Paren, Brack, Brace } - -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub enum Token { - Num(i64), Str(String), Bool(bool), Sym(String), - - Add, Sub, Mul, Div, Mod, - Eq, Neq, Lt, Gt, Lte, Gte, - And, Or, Not, - - Assign, Comma, Colon, Semicolon, - Open(Delim), Close(Delim), - Lambda, Arrow, - - Let, In, Func, Return, If, Then, Else, -} - -impl Display for Token { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - match self { - Token::Num(n) => write!(f, "{}", n), - Token::Str(s) => write!(f, "\"{}\"", s), - Token::Bool(b) => write!(f, "{}", b), - Token::Sym(s) => write!(f, "{}", s), - - Token::Add => write!(f, "+"), - Token::Sub => write!(f, "-"), - Token::Mul => write!(f, "*"), - Token::Div => write!(f, "/"), - Token::Mod => write!(f, "%"), - Token::Eq => write!(f, "=="), - Token::Neq => write!(f, "!="), - Token::Lt => write!(f, "<"), - Token::Gt => write!(f, ">"), - Token::Lte => write!(f, "<="), - Token::Gte => write!(f, ">="), - Token::And => write!(f, "&&"), - Token::Or => write!(f, "||"), - Token::Not => write!(f, "!"), - - Token::Assign => write!(f, "="), - Token::Comma => write!(f, ","), - Token::Colon => write!(f, ":"), - Token::Semicolon => write!(f, ";"), - Token::Open(d) => write!(f, "{}", match d { - Delim::Paren => "(", - Delim::Brack => "[", - Delim::Brace => "{", - }), - Token::Close(d) => write!(f, "{}", match d { - Delim::Paren => ")", - Delim::Brack => "]", - Delim::Brace => "}", - }), - Token::Lambda => write!(f, "\\"), - Token::Arrow => write!(f, "->"), - - Token::Let => write!(f, "let"), - Token::In => write!(f, "in"), - Token::Func => write!(f, "func"), - Token::Return => write!(f, "return"), - Token::If => write!(f, "if"), - Token::Then => write!(f, "then"), - Token::Else => write!(f, "else"), - } - } -} - -pub type Span = std::ops::Range; -pub type Spanned = (T, Span); - -pub fn lexer() -> impl Parser, Error = Simple> { - let num = text::int(10) - .map(|s: String| Token::Num(s.parse().unwrap())); - - let string = just('"') - .ignore_then(filter(|c| *c != '"').repeated()) - .then_ignore(just('"')) - .collect::() - .map(Token::Str); - - let symbol = choice(( - just("->").to(Token::Arrow), - - just('+').to(Token::Add), - just('-').to(Token::Sub), - just('*').to(Token::Mul), - just('/').to(Token::Div), - just('%').to(Token::Mod), - just("==").to(Token::Eq), - just("!=").to(Token::Neq), - just("<=").to(Token::Lte), - just(">=").to(Token::Gte), - just('<').to(Token::Lt), - just('>').to(Token::Gt), - just("&&").to(Token::And), - just("||").to(Token::Or), - just('!').to(Token::Not), - - just('=').to(Token::Assign), - just(',').to(Token::Comma), - just(':').to(Token::Colon), - just(';').to(Token::Semicolon), - just('\\').to(Token::Lambda), - )); - - let delim = choice(( - just('(').to(Token::Open(Delim::Paren)), - just(')').to(Token::Close(Delim::Paren)), - just('[').to(Token::Open(Delim::Brack)), - just(']').to(Token::Close(Delim::Brack)), - just('{').to(Token::Open(Delim::Brace)), - just('}').to(Token::Close(Delim::Brace)), - )); - - let kw = text::ident() - .map(|s: String| match s.as_str() { - "true" => Token::Bool(true), - "false" => Token::Bool(false), - "let" => Token::Let, - "in" => Token::In, - "func" => Token::Func, - "return" => Token::Return, - "if" => Token::If, - "then" => Token::Then, - "else" => Token::Else, - _ => Token::Sym(s), - }); - - let token = num - .or(string) - .or(symbol) - .or(delim) - .or(kw) - .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(just('/').ignore_then(none_of('\n').ignored().repeated().ignored())), - ) - .padded() - .ignored() - .repeated(); - - token - .padded_by(comments) - .repeated() - .padded() - .then_ignore(end()) -} - -pub fn lex(src: String) -> (Option>, Vec>) { - let (tokens, lex_error) = lexer().parse_recovery(src.as_str()); - (tokens, lex_error) -} - -pub trait P = chumsky::Parser> + Clone; - -pub fn literal_parser() -> impl P { - filter_map(|span, token| match token { - Token::Num(i) => Ok(PLiteral::Num(i)), - Token::Bool(b) => Ok(PLiteral::Bool(b)), - Token::Str(s) => Ok(PLiteral::Str(s)), - _ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))), - }) - .labelled("literal") -} - -pub fn symbol_parser() -> impl P { - filter_map(|span, token| match token { - Token::Sym(s) => Ok(s), - _ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))), - }) - .labelled("symbol") -} - -pub fn type_parser() -> impl P { - recursive(|ty| { - let litty = symbol_parser().map(|s| match s.as_str() { - "num" => Type::Num, - "str" => Type::Str, - "bool" => Type::Bool, - "?" => Type::Unknown, - _ => Type::Sym(s), - }); - - let fun = just(Token::Open(Delim::Paren)) - .ignore_then( - ty.clone() - .separated_by(just(Token::Comma)) - ) - .then_ignore(just(Token::Close(Delim::Paren))) - .then_ignore(just(Token::Arrow)) - .then(ty) - .map(|(args, ret)| Type::Fun(args, Box::new(ret))); - - litty - .or(fun) - .labelled("type") - }) -} - -pub fn nested_parser<'a, T: 'a>( - parser: impl P + 'a, - delim: Delim, - f: impl Fn(Span) -> T + Clone + 'a, -) -> impl P + 'a { - parser - .delimited_by(just(Token::Open(delim)), just(Token::Close(delim))) - .recover_with(nested_delimiters( - Token::Open(delim), - Token::Close(delim), - [ - ( - Token::Open(Delim::Paren), - Token::Close(Delim::Paren), - ), - ( - Token::Open(Delim::Brack), - Token::Close(Delim::Brack), - ), - ( - Token::Open(Delim::Brace), - Token::Close(Delim::Brace), - ), - ], - f, - )) - .boxed() -} - -pub fn expr_parser() -> impl P> { - recursive(|expr: Recursive, Simple>| { - let lit = literal_parser().map(PExpr::Lit); - let sym = symbol_parser().map(PExpr::Sym); - - let vec = nested_parser( - expr.clone() - .separated_by(just(Token::Comma)) - .allow_trailing() - .map(Some), - Delim::Brack, - |_| None, - ) - .map(|xs| match xs { - Some(xs) => PExpr::Vec(xs), - None => PExpr::Vec(Vec::new()), - }) - .labelled("vector"); - - // (e) - let paren_expr = just(Token::Open(Delim::Paren)) - .ignore_then(expr.clone()) - .then_ignore(just(Token::Close(Delim::Paren))) - .map(|e| e.0) - .labelled("parenthesized expression"); - - // \[sym : type]* -> expr - let lam = just(Token::Lambda) - .ignore_then( - ( - symbol_parser() - .then_ignore(just(Token::Colon)) - .then(type_parser()) - ) - .repeated() - ) - .then_ignore(just(Token::Arrow)) - .then(expr.clone()) - .map(|(args, body)| PExpr::Lambda { - args, - body: Box::new(body), - }) - .labelled("lambda"); - - let let_binds = symbol_parser() - .then_ignore(just(Token::Colon)) - .then(type_parser()) - .then_ignore(just(Token::Assign)) - .then(expr.clone()) - .map(|((sym, ty), body)| (sym, ty, body)) - .boxed() - .labelled("let binding") - .separated_by(just(Token::Comma)) - .allow_trailing() - .labelled("let bindings"); - - let let_in = just(Token::Let) - .ignore_then(let_binds.clone()) - .then_ignore(just(Token::In)) - .then(expr.clone()) - .map(|(vars, body)| PExpr::Let { - vars, - body: Some(Box::new(body)), - }) - .boxed() - .labelled("let with expression"); - - let let_def = just(Token::Let) - .ignore_then(let_binds) - .map(|vars| PExpr::Let { vars, body: None }) - .boxed() - .labelled("let definition"); - - let block = nested_parser( - expr.clone() - .separated_by(just(Token::Semicolon)) - .allow_trailing(), - Delim::Brace, - |_| Vec::new(), - ) - .map(PExpr::Block) - .labelled("block"); - - let ret = just(Token::Return) - .ignore_then(expr.clone()) - .map(Box::new) - .map(PExpr::Return) - .labelled("return"); - - let ifelse = just(Token::If) - .ignore_then(expr.clone()) - .then_ignore(just(Token::Then)) - .then(expr.clone()) - .then_ignore(just(Token::Else)) - .then(expr.clone()) - .map(|((cond, then), f)| PExpr::If { - cond: Box::new(cond), - t: Box::new(then), - f: Box::new(f), - }) - .boxed() - .labelled("if else"); - - let atom = lit - .or(sym) - .or(vec) - .or(paren_expr) - .or(lam) - .or(let_in) - .or(let_def) - .or(block) - .or(ret) - .or(ifelse) - .map_with_span(|e, s| (e, s)) - .boxed() - .labelled("atom"); - - // e(e*) - let call = atom - .then( - nested_parser( - expr.clone() - .separated_by(just(Token::Comma)) - .allow_trailing() - .map(Some), - Delim::Paren, - |_| None, - ) - .or_not(), - ) - .map_with_span(|(f, args), s| match args { - Some(Some(args)) => (PExpr::Call(Box::new(f), args), s), - Some(None) => (PExpr::Error, s), - None => f, - }); - - // op e - let unary = choice(( - just(Token::Sub).to(PUnaryOp::Neg), - just(Token::Not).to(PUnaryOp::Not), - )) - .map_with_span(|op, s| (op, s)) - .repeated() - .then(call) - .foldr(|op, expr| { - let s = op.1.start()..expr.1.end(); - (PExpr::Unary(op, Box::new(expr)), s) - }) - .boxed(); - - let product = unary - .clone() - .then( - choice(( - just(Token::Mul).to(PBinaryOp::Mul), - just(Token::Div).to(PBinaryOp::Div), - just(Token::Mod).to(PBinaryOp::Mod), - )) - .map_with_span(|op, s| (op, s)) - .then(unary) - .repeated(), - ) - .foldl(|lhs, (op, rhs)| { - let s = lhs.1.start()..rhs.1.end(); - (PExpr::Binary(op, Box::new(lhs), Box::new(rhs)), s) - }) - .boxed(); - - let sum = product - .clone() - .then( - choice(( - just(Token::Add).to(PBinaryOp::Add), - just(Token::Sub).to(PBinaryOp::Sub), - )) - .map_with_span(|op, s| (op, s)) - .then(product) - .repeated(), - ) - .foldl(|lhs, (op, rhs)| { - let s = lhs.1.start()..rhs.1.end(); - (PExpr::Binary(op, Box::new(lhs), Box::new(rhs)), s) - }) - .boxed(); - - let comparison = sum - .clone() - .then( - choice(( - just(Token::Eq).to(PBinaryOp::Eq), - just(Token::Neq).to(PBinaryOp::Neq), - just(Token::Lt).to(PBinaryOp::Lt), - just(Token::Lte).to(PBinaryOp::Lte), - just(Token::Gt).to(PBinaryOp::Gt), - just(Token::Gte).to(PBinaryOp::Gte), - )) - .map_with_span(|op, s| (op, s)) - .then(sum) - .repeated(), - ) - .foldl(|lhs, (op, rhs)| { - let s = lhs.1.start()..rhs.1.end(); - (PExpr::Binary(op, Box::new(lhs), Box::new(rhs)), s) - }) - .boxed(); - - comparison - .clone() - .then( - choice(( - just(Token::And).to(PBinaryOp::And), - just(Token::Or).to(PBinaryOp::Or), - )) - .map_with_span(|op, s| (op, s)) - .then(comparison) - .repeated(), - ) - .foldl(|lhs, (op, rhs)| { - let s = lhs.1.start()..rhs.1.end(); - (PExpr::Binary(op, Box::new(lhs), Box::new(rhs)), s) - }) - .boxed() - }) -} - -pub fn exprs_parser() -> impl P>> { - expr_parser() - .then_ignore(just(Token::Semicolon)) - .repeated() -} - -pub fn stmt_parser() -> impl P> { - let func = just(Token::Func) - .ignore_then(symbol_parser()) - .then( - nested_parser( - symbol_parser() - .then_ignore(just(Token::Colon)) - .then(type_parser()) - .separated_by(just(Token::Comma)) - .allow_trailing(), - Delim::Paren, - |_| Vec::new(), - ) - ) - .then(type_parser()) - .then_ignore(just(Token::Assign)) - .then(expr_parser().map(Box::new)) - .map(|(((name, args), ret), body)| PStmt::Func { - name, - args, - ret, - body, - }); - - let expr = expr_parser().map(PStmt::Expr); - - func - .or(expr) - .map_with_span(|s, span| (s, span)) -} - -pub fn stmts_parser() -> impl P>> { - stmt_parser() - .then_ignore(just(Token::Semicolon)) - .repeated() -} - -pub fn parse( - tokens: Vec>, - len: usize, -) -> (Option>>, Vec>) { - let (ast, parse_error) = stmts_parser() - .then_ignore(end()) - .parse_recovery(Stream::from_iter(len..=len, tokens.into_iter())); - - (ast, parse_error) -} \ No newline at end of file diff --git a/src/trans/low.rs b/src/trans/low.rs deleted file mode 100644 index 81ce92e..0000000 --- a/src/trans/low.rs +++ /dev/null @@ -1,198 +0,0 @@ -use crate::asts::{ - past::*, - ast::*, - js::*, -}; - -pub fn translate_stmt(stmt: PStmt) -> Stmt { - match stmt { - PStmt::Expr(e) => Stmt::Expr(translate_expr(e.0)), - PStmt::Func { name, args, ret, body } => Stmt::Func { - name, - args: args.into_iter().map(|(name, _ty)| name).collect(), - ret, - body: translate_expr(body.0), - }, - } -} - -pub fn exprs_to_lam(es: Vec) -> Expr { - let lam = Expr::Lambda { - args: vec![], - body: es.into_iter().map(translate_expr).collect(), - }; - Expr::Call(Box::new(lam), vec![]) -} - -pub fn translate_expr(expr: PExpr) -> Expr { - match expr { - PExpr::Error => panic!("Error in expression!"), - - PExpr::Lit(l) => Expr::Lit(match l { - PLiteral::Num(n) => Literal::Num(n), - PLiteral::Str(s) => Literal::Str(s), - PLiteral::Bool(b) => Literal::Bool(b), - PLiteral::Unit => Literal::Unit, - }), - PExpr::Sym(s) => Expr::Sym(s), - PExpr::Vec(v) => Expr::Vec(v.into_iter().map(|e| translate_expr(e.0)).collect()), - - PExpr::Unary(op, e) => Expr::UnaryOp(match op.0 { - PUnaryOp::Neg => UnaryOp::Neg, - PUnaryOp::Not => UnaryOp::Not, - }, Box::new(translate_expr((*e).0))), - PExpr::Binary((op, _), e1, e2) => Expr::BinaryOp( - match op { - PBinaryOp::Add => BinaryOp::Add, - PBinaryOp::Sub => BinaryOp::Sub, - PBinaryOp::Mul => BinaryOp::Mul, - PBinaryOp::Div => BinaryOp::Div, - PBinaryOp::Mod => BinaryOp::Mod, - - PBinaryOp::Eq => BinaryOp::Eq, - PBinaryOp::Neq => BinaryOp::Neq, - - PBinaryOp::Lt => BinaryOp::Lt, - PBinaryOp::Gt => BinaryOp::Gt, - PBinaryOp::Lte => BinaryOp::Lte, - PBinaryOp::Gte => BinaryOp::Gte, - - PBinaryOp::And => BinaryOp::And, - PBinaryOp::Or => BinaryOp::Or, - }, - Box::new(translate_expr((*e1).0)), - Box::new(translate_expr((*e2).0)), - ), - - PExpr::Call(f, args) => Expr::Call( - Box::new(translate_expr((*f).0)), - args.into_iter().map(|a| translate_expr(a.0)).collect(), - ), - PExpr::Lambda { args, body } => Expr::Lambda { - args: args.into_iter().map(|(name, _ty)| name).collect(), - body: vec![translate_expr((*body).0)], - }, - PExpr::Let { vars, body } => { - if let Some(body) = body { - let mut expr: Expr = translate_expr(body.0); // The expression we're building up - for (name, _ty, val) in vars.into_iter().rev() { // Reverse so we can build up the lambda - // e.g.: let x : t = e1 in e2; => (lambda (x : t) = e2)(e1) - // Build up the lambda - expr = Expr::Lambda { - args: vec![name], - body: vec![expr], - }; - // Call the lambda with the value - let val = translate_expr(val.0); - expr = Expr::Call(Box::new(expr), vec![val]); - } - expr - } else { - Expr::Defines(vars.into_iter().map(|(name, _ty, val)| - (name, translate_expr(val.0)) - ).collect()) - } - }, - PExpr::If { cond, t, f } => Expr::If { - cond: Box::new(translate_expr((*cond).0)), - t: Box::new(translate_expr((*t).0)), - f: Box::new(translate_expr((*f).0)), - }, - PExpr::Block(es) => { - exprs_to_lam(es.into_iter().map(|e| e.0).collect()) - }, - PExpr::Return(e) => Expr::Return(Box::new(translate_expr((*e).0))), - } -} - -pub fn translate_js_stmt(stmt: Stmt) -> JSStmt { - match stmt { - Stmt::Expr(e) => JSStmt::Expr(translate_js_expr(e)), - Stmt::Func { name, args, ret, body } => JSStmt::Func { - name, - args, - ret, - body: translate_js_expr(body), - }, - } -} - -pub fn translate_js_expr(expr: Expr) -> JSExpr { - match expr { - Expr::Lit(l) => match l { - Literal::Num(n) => JSExpr::Lit(JSLiteral::Num(n)), - Literal::Str(s) => JSExpr::Lit(JSLiteral::Str(s)), - Literal::Bool(b) => JSExpr::Lit(JSLiteral::Bool(b)), - Literal::Unit => JSExpr::Lit(JSLiteral::Undefined), - }, - Expr::Sym(s) => JSExpr::Sym(s), - Expr::Vec(v) => JSExpr::Array(v.into_iter().map(translate_js_expr).collect()), - - Expr::UnaryOp(op, e) => JSExpr::Op(match op { - UnaryOp::Neg => "-", - UnaryOp::Not => "!", - }, Box::new(translate_js_expr(*e)), None), - Expr::BinaryOp(op, e1, e2) => JSExpr::Op(match op { - BinaryOp::Add => "+", - BinaryOp::Sub => "-", - BinaryOp::Mul => "*", - BinaryOp::Div => "/", - BinaryOp::Mod => "%", - - BinaryOp::Eq => "==", - BinaryOp::Neq => "!=", - BinaryOp::Lt => "<", - BinaryOp::Gt => ">", - BinaryOp::Lte => "<=", - BinaryOp::Gte => ">=", - - BinaryOp::And => "&&", - BinaryOp::Or => "||", - }, Box::new(translate_js_expr(*e1)), Some(Box::new(translate_js_expr(*e2)))), - - Expr::Call(f, args) => { - match *f { - Expr::Sym(ref s) => { - match s.as_str() { - "println" => JSExpr::Call( - Box::new(JSExpr::Method( - Box::new(JSExpr::Sym("console".to_string())), - "log".to_string(), - )), - args.into_iter().map(translate_js_expr).collect()), - "print" => JSExpr::Call( - Box::new(JSExpr::Method( - Box::new(JSExpr::Method( - Box::new(JSExpr::Sym("process".to_string())), - "stdout".to_string(), - )), - "write".to_string(), - )), - args.into_iter().map(translate_js_expr).collect()), - _ => JSExpr::Call( - Box::new(translate_js_expr(*f)), - args.into_iter().map(translate_js_expr).collect(), - ), - } - }, - _ => JSExpr::Call( - Box::new(translate_js_expr(*f)), - args.into_iter().map(translate_js_expr).collect(), - ), - } - } - Expr::Lambda { args, body } => JSExpr::Lambda { - args, - body: body.into_iter().map(translate_js_expr).collect(), - }, - Expr::If { cond, t, f } => JSExpr::If { - cond: Box::new(translate_js_expr(*cond)), - t: Box::new(translate_js_expr(*t)), - f: Box::new(translate_js_expr(*f)), - }, - Expr::Defines(defs) => JSExpr::Defines(defs.into_iter().map(|(name, val)| - (name, translate_js_expr(val)) - ).collect()), - Expr::Return(e) => JSExpr::Return(Box::new(translate_js_expr(*e))), - } -} \ No newline at end of file diff --git a/src/trans/mod.rs b/src/trans/mod.rs deleted file mode 100644 index a5a7890..0000000 --- a/src/trans/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod ty; -pub mod low; \ No newline at end of file diff --git a/src/trans/ty.rs b/src/trans/ty.rs deleted file mode 100644 index 841de62..0000000 --- a/src/trans/ty.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::fmt::{Display, Formatter, Result as FmtResult}; - -#[derive(Clone, Debug)] -pub enum Type { - Num, Str, Bool, - Sym(String), - Fun(Vec, Box), - Unknown, -} - -impl Display for Type { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - match self { - Type::Num => write!(f, "num"), - Type::Str => write!(f, "str"), - Type::Bool => write!(f, "bool"), - Type::Sym(s) => write!(f, "{}", s), - Type::Fun(args, ret) => { - write!(f, "(")?; - for (i, arg) in args.iter().enumerate() { - if i > 0 { - write!(f, ", ")?; - } - write!(f, "{}", arg)?; - } - write!(f, ") -> {}", ret) - }, - Type::Unknown => write!(f, "unknown"), - } - } -} \ No newline at end of file diff --git a/syntax/Cargo.toml b/syntax/Cargo.toml new file mode 100644 index 0000000..b332876 --- /dev/null +++ b/syntax/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "syntax" +version = "0.1.0" +edition = "2021" + +[dependencies] +chumsky = { version = "1.0.0-alpha.3", features = ["label"] } diff --git a/syntax/src/expr.rs b/syntax/src/expr.rs new file mode 100644 index 0000000..71fca1b --- /dev/null +++ b/syntax/src/expr.rs @@ -0,0 +1,167 @@ +use std::fmt::{ Display, Formatter, self }; +use chumsky::span::SimpleSpan; + +use super::ty::Type; + +#[derive(Clone, Debug, PartialEq)] +pub enum Delim { Paren, Brack, Brace } + +// The tokens of the language. +// 'src is the lifetime of the source code string. +#[derive(Clone, Debug, PartialEq)] +pub enum Token<'src> { + Unit, Bool(bool), Int(i64), Str(&'src str), + Ident(&'src str), + + Add, Sub, Mul, Div, Rem, + Eq, Ne, Lt, Gt, Le, Ge, + And, Or, Not, + Pipe, + + Assign, Comma, Colon, Semicolon, + Open(Delim), Close(Delim), + Lambda, Arrow, + + Let, In, Func, Return, If, Then, Else, +} + +impl<'src> Display for Token<'src> { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Token::Unit => write!(f, "()"), + Token::Bool(b) => write!(f, "{}", b), + Token::Int(n) => write!(f, "{}", n), + Token::Str(s) => write!(f, "\"{}\"", s), + Token::Ident(s) => write!(f, "{}", s), + + Token::Add => write!(f, "+"), + Token::Sub => write!(f, "-"), + Token::Mul => write!(f, "*"), + Token::Div => write!(f, "/"), + Token::Rem => write!(f, "%"), + Token::Eq => write!(f, "=="), + Token::Ne => write!(f, "!="), + Token::Lt => write!(f, "<"), + Token::Gt => write!(f, ">"), + Token::Le => write!(f, "<="), + Token::Ge => write!(f, ">="), + Token::And => write!(f, "&&"), + Token::Or => write!(f, "||"), + Token::Not => write!(f, "!"), + Token::Pipe => write!(f, "|>"), + + Token::Assign => write!(f, "="), + Token::Comma => write!(f, ","), + Token::Colon => write!(f, ":"), + Token::Semicolon => write!(f, ";"), + Token::Open(d) => write!(f, "{}", match d { + Delim::Paren => "(", + Delim::Brack => "[", + Delim::Brace => "{", + }), + Token::Close(d) => write!(f, "{}", match d { + Delim::Paren => ")", + Delim::Brack => "]", + Delim::Brace => "}", + }), + Token::Lambda => write!(f, "\\"), + Token::Arrow => write!(f, "->"), + + Token::Let => write!(f, "let"), + Token::In => write!(f, "in"), + Token::Func => write!(f, "func"), + Token::Return => write!(f, "return"), + Token::If => write!(f, "if"), + Token::Then => write!(f, "then"), + Token::Else => write!(f, "else"), + } + } +} + +pub type Span = SimpleSpan; + +#[derive(Clone, Debug, PartialEq)] +pub enum Lit<'src> { + Unit, + Bool(bool), + Int(i64), + Str(&'src str), +} + +#[derive(Clone, Debug)] +pub enum UnaryOp { Neg, Not } + +impl Display for UnaryOp { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + UnaryOp::Neg => write!(f, "-"), + UnaryOp::Not => write!(f, "!"), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum BinaryOp { + Add, Sub, Mul, Div, Rem, + And, Or, + Eq, Ne, Lt, Le, Gt, Ge, + Pipe, +} + +impl Display for BinaryOp { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + BinaryOp::Add => write!(f, "+"), + BinaryOp::Sub => write!(f, "-"), + BinaryOp::Mul => write!(f, "*"), + BinaryOp::Div => write!(f, "/"), + BinaryOp::Rem => write!(f, "%"), + BinaryOp::And => write!(f, "&&"), + BinaryOp::Or => write!(f, "||"), + BinaryOp::Eq => write!(f, "=="), + BinaryOp::Ne => write!(f, "!="), + BinaryOp::Lt => write!(f, "<"), + BinaryOp::Le => write!(f, "<="), + BinaryOp::Gt => write!(f, ">"), + BinaryOp::Ge => write!(f, ">="), + BinaryOp::Pipe => write!(f, "|>"), + } + } +} + +pub type Spanned = (T, Span); + +// Clone is needed for type checking since the type checking +// algorithm is recursive and sometimes consume the AST. +#[derive(Clone, Debug)] +pub enum Expr<'src> { + Lit(Lit<'src>), + Ident(&'src str), + + Unary(UnaryOp, Spanned>), + Binary(BinaryOp, Spanned>, Spanned>), + + Lambda(Vec<(&'src str, Option)>, Option, Spanned>), + Call(Spanned>, Vec>), + + If { + cond: Spanned>, + t: Spanned>, + f: Spanned>, + }, + Let { + name: &'src str, + ty: Option, + value: Spanned>, + body: Spanned>, + }, + Define { + name: &'src str, + ty: Option, + value: Spanned>, + }, + Block { + exprs: Vec>>, + void: bool, // True if last expression is discarded (ends with semicolon). + }, +} \ No newline at end of file diff --git a/syntax/src/lib.rs b/syntax/src/lib.rs new file mode 100644 index 0000000..c9a032c --- /dev/null +++ b/syntax/src/lib.rs @@ -0,0 +1,3 @@ +pub mod expr; +pub mod parser; +pub mod ty; diff --git a/syntax/src/parser.rs b/syntax/src/parser.rs new file mode 100644 index 0000000..0b84d36 --- /dev/null +++ b/syntax/src/parser.rs @@ -0,0 +1,459 @@ +use chumsky::prelude::*; + +use super::{ expr::*, ty::Type }; + +pub fn lexer<'src>() -> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, extra::Err>> { + // let num = text::int(10) + // .then(just('.').then(text::digits(10)).or_not()) + // .slice() + // .from_str() + // .unwrapped() + // .map(Token::Int); + let int = text::int(10) + .slice() + .from_str() + .unwrapped() + .map(Token::Int); + + let strn = just('"') + .ignore_then(none_of('"').repeated()) + .then_ignore(just('"')) + .map_slice(Token::Str); + + fn id_filter(c: &C) -> bool where C: text::Char { + c.to_char().is_ascii_alphabetic() + || "_'".contains(c.to_char()) + } + let id = any() + .filter(id_filter) + .then(any() + .filter(id_filter) + .repeated()) + .slice(); + + let word = id.map(|s: &str| match s { + "true" => Token::Bool(true), + "false" => Token::Bool(false), + "let" => Token::Let, + "in" => Token::In, + "fun" => Token::Func, + "return" => Token::Return, + "if" => Token::If, + "then" => Token::Then, + "else" => Token::Else, + _ => Token::Ident(s), + }); + + let sym = choice(( + just("()").to(Token::Unit), + just("\\").to(Token::Lambda), + just("->").to(Token::Arrow), + just("|>").to(Token::Pipe), + + just('+').to(Token::Add), + just('-').to(Token::Sub), + just('*').to(Token::Mul), + just('/').to(Token::Div), + just('%').to(Token::Rem), + just("==").to(Token::Eq), + just("!=").to(Token::Ne), + just("<=").to(Token::Le), + just(">=").to(Token::Ge), + just('<').to(Token::Lt), + just('>').to(Token::Gt), + just("&&").to(Token::And), + just("||").to(Token::Or), + just('!').to(Token::Not), + + just('=').to(Token::Assign), + just(',').to(Token::Comma), + just(':').to(Token::Colon), + just(';').to(Token::Semicolon), + )); + + let delim = choice(( + just('(').to(Token::Open(Delim::Paren)), + just(')').to(Token::Close(Delim::Paren)), + just('[').to(Token::Open(Delim::Brack)), + just(']').to(Token::Close(Delim::Brack)), + just('{').to(Token::Open(Delim::Brace)), + just('}').to(Token::Close(Delim::Brace)), + )); + + let token = choice(( + int, + strn, + word, + sym, + delim, + )); + + let comment = just("//") + .then(any().and_is(just('\n').not()).repeated()) + .padded(); + + token + .map_with_span(move |tok, span| (tok, span)) + .padded_by(comment.repeated()) + .padded() + // If we get an error, skip to the next character and try again. + .recover_with(skip_then_retry_until(any().ignored(), end())) + .repeated() + .collect() +} + +// (a, s) -> (Box::new(a), s) +fn boxspan(a: Spanned) -> Spanned> { + (Box::new(a.0), a.1) +} + +// Lifetime 'tokens is the lifetime of the token buffer from the lexer. +type ParserInput<'tokens, 'src> = + chumsky::input::SpannedInput< + Token<'src>, + Span, + &'tokens [(Token<'src>, Span)] + >; + +pub fn expr_parser<'tokens, 'src: 'tokens>() -> impl Parser< + 'tokens, + ParserInput<'tokens, 'src>, + Spanned>, + extra::Err, Span>>, +> + Clone { + recursive(|expr| { + let lit = select! { + Token::Unit => Expr::Lit(Lit::Unit), + Token::Bool(b) => Expr::Lit(Lit::Bool(b)), + Token::Int(n) => Expr::Lit(Lit::Int(n)), + Token::Str(s) => Expr::Lit(Lit::Str(s)), + }; + + let symbol = select! { + Token::Ident(s) => s, + }; + + let ident = symbol + .map(Expr::Ident); + + let paren_expr = expr.clone() + .delimited_by( + just(Token::Open(Delim::Paren)), + just(Token::Close(Delim::Paren)), + ) + .map(|e: Spanned| e.0); + + let lambda = just(Token::Func) + .ignore_then( + (symbol + .then(type_parser().or_not()) + .separated_by(just(Token::Comma)) + .collect::>() + .delimited_by( + just(Token::Open(Delim::Paren)), + just(Token::Close(Delim::Paren)), + )) + .or(just(Token::Unit).to(Vec::new())) + ) + .then(type_parser().or_not()) + .then_ignore(just(Token::Arrow)) + .then(expr.clone()) + .map(|((args, ret), body)| Expr::Lambda(args, ret, boxspan(body))); + + // ident (: type)? + let bind = symbol + .then( + just(Token::Colon) + .ignore_then(type_parser()) + .or_not() + ) + .then_ignore(just(Token::Assign)) + .then(expr.clone()) + .map(|((name, ty), expr)| (name, ty, boxspan(expr))); + + let let_or_define = just(Token::Let) + .ignore_then(bind) + .then( + just(Token::In) + .ignore_then(expr.clone()) + .or_not() + ) + .map(|((name, ty, expr), body)| match body { + Some(body) => Expr::Let { name, ty, value: expr, body: boxspan(body) }, + None => Expr::Define { name, ty, value: expr }, + }); + + let if_ = just(Token::If) + .ignore_then(expr.clone()) + .then_ignore(just(Token::Then)) + .then(expr.clone()) + .then_ignore(just(Token::Else)) + .then(expr.clone()) + .map(|((cond, t), f)| Expr::If { + cond: boxspan(cond), + t: boxspan(t), + f: boxspan(f) + }); + + let block = expr.clone() + .map(boxspan) + .then_ignore(just(Token::Semicolon)) + .repeated() + .collect::>() + .then(expr.clone() + .map(boxspan) + .or_not()) + .delimited_by( + just(Token::Open(Delim::Brace)), + just(Token::Close(Delim::Brace)), + ) + .map(|(mut exprs, end)| { + let void = end.is_none(); + if let Some(end) = end { + exprs.push(end); + } + Expr::Block { + exprs, + void, + } + }); + + let atom = lit + .or(ident) + .or(paren_expr) + .or(lambda) + .or(let_or_define) + .or(if_) + .or(block) + .map_with_span(|e, s| (e, s)) + .boxed() + .labelled("(atomic) expression"); + + let call = atom + .then( + expr.clone() + .separated_by(just(Token::Comma)) + .allow_trailing() + .collect::>() + .delimited_by( + just(Token::Open(Delim::Paren)), + just(Token::Close(Delim::Paren)), + ) + .or_not() + ) + .map_with_span(|(f, args), s| match args { + Some(args) => (Expr::Call(boxspan(f), args), s), + None => (f.0, f.1), + }); + + let op = choice(( + just(Token::Sub).to(UnaryOp::Neg), + just(Token::Not).to(UnaryOp::Not), + )); + let unary = op + .map_with_span(|op, s| (op, s)) + .repeated() + .foldr( + call, + |op, expr| { + let span = op.1.start..expr.1.end; + (Expr::Unary(op.0, boxspan(expr)), span.into()) + }); + + let op = choice(( + just(Token::Mul).to(BinaryOp::Mul), + just(Token::Div).to(BinaryOp::Div), + just(Token::Rem).to(BinaryOp::Rem), + )); + let product = unary.clone() + .foldl( + op.then(unary).repeated(), + |a, (op, b)| { + let span = a.1.start..b.1.end; + (Expr::Binary(op, boxspan(a), boxspan(b)), span.into()) + } + ); + + let op = choice(( + just(Token::Add).to(BinaryOp::Add), + just(Token::Sub).to(BinaryOp::Sub), + )); + let sum = product.clone() + .foldl( + op.then(product).repeated(), + |a, (op, b)| { + let span = a.1.start..b.1.end; + (Expr::Binary(op, boxspan(a), boxspan(b)), span.into()) + } + ); + + let op = choice(( + just(Token::Eq).to(BinaryOp::Eq), + just(Token::Ne).to(BinaryOp::Ne), + just(Token::Lt).to(BinaryOp::Lt), + just(Token::Le).to(BinaryOp::Le), + just(Token::Gt).to(BinaryOp::Gt), + just(Token::Ge).to(BinaryOp::Ge), + )); + let comparison = sum.clone() + .foldl( + op.then(sum).repeated(), + |a, (op, b)| { + let span = a.1.start..b.1.end; + (Expr::Binary(op, boxspan(a), boxspan(b)), span.into()) + } + ); + + let op = choice(( + just(Token::And).to(BinaryOp::And), + just(Token::Or).to(BinaryOp::Or), + )); + let logical = comparison.clone() + .foldl( + op.then(comparison).repeated(), + |a, (op, b)| { + let span = a.1.start..b.1.end; + (Expr::Binary(op, boxspan(a), boxspan(b)), span.into()) + } + ); + + let pipe = logical.clone() + .foldl( + just(Token::Pipe).to(BinaryOp::Pipe) + .then(logical).repeated(), + |a, (op, b)| { + let span = a.1.start..b.1.end; + (Expr::Binary(op, boxspan(a), boxspan(b)), span.into()) + } + ); + + pipe + .labelled("expression") + }) +} + +pub fn type_parser<'tokens, 'src: 'tokens>() -> impl Parser< + 'tokens, + ParserInput<'tokens, 'src>, + Type, + extra::Err, Span>>, +> + Clone { + recursive(|ty| { + let lit_ty = select! { + Token::Ident("Bool") => Type::Bool, + Token::Ident("Int") => Type::Int, + Token::Ident("Str") => Type::Str, + // TODO: Support type variables in both the parser and the type checker. + Token::Ident(_) => Type::Var(69), + Token::Unit => Type::Unit, + }.validate(|tys, span, emitter| { + if let Type::Var(_) = tys { + emitter.emit(Rich::custom(span, + "Type variables are not yet supported.".to_string() + )); + } + tys + }); + + let tys_paren = ty.clone() + .separated_by(just(Token::Comma)) + .allow_trailing() + .collect::>() + .delimited_by( + just(Token::Open(Delim::Paren)), + just(Token::Close(Delim::Paren)), + ); + + let func = tys_paren.clone() + .then_ignore(just(Token::Arrow)) + .then(ty.clone()) + .map(|(ta, tr)| Type::Func(ta, Box::new(tr))); + + let tuple = tys_paren + .validate(|tys, span, emitter| { + if tys.is_empty() { + emitter.emit(Rich::custom(span, + "Tuple must have at least one element. Use `()` for the unit type." + .to_string() + )); + } + tys + }) + .map(Type::Tuple); + + let array = ty.clone() + .delimited_by( + just(Token::Open(Delim::Brack)), + just(Token::Close(Delim::Brack)), + ) + .map(|t| Type::Array(Box::new(t))); + + lit_ty + .or(array) + .or(func) + .or(tuple) + .boxed() + .labelled("type") + }) +} + +pub fn exprs_parser<'tokens, 'src: 'tokens>() -> impl Parser< + 'tokens, + ParserInput<'tokens, 'src>, + Vec>>, + extra::Err, Span>>, +> + Clone { + expr_parser() + .separated_by(just(Token::Semicolon)) + .allow_trailing() + .collect::>() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_type_parser() { + let input = "(() -> () -> () -> (num)) -> bool"; + let (ts, errs) = lexer().parse(input).into_output_errors(); + + assert!(ts.is_some()); + assert!(errs.is_empty()); + + if let Some(ts) = ts { + let (ast, parse_errs) = type_parser() + .map_with_span(|ty, span| (ty, span)) + .parse(ts.as_slice().spanned((input.len()..input.len()).into())) + .into_output_errors(); + + println!("{:?}", ast); + println!("{:?}", parse_errs); + } + } + + #[test] + fn test_expr_parser_atom() { + let input = " + let id : (A) -> A = (\\x -> x) in { + if false + then id(3.14) + else id(true); + } + "; + let (ast, errs) = lexer().parse(input).into_output_errors(); + + assert!(ast.is_some()); + assert!(errs.is_empty()); + + if let Some(ast) = ast { + let (ast, parse_errs) = expr_parser() + .map_with_span(|ty, span| (ty, span)) + .parse(ast.as_slice().spanned((input.len()..input.len()).into())) + .into_output_errors(); + + println!("{:?}", ast); + println!("{:?}", parse_errs); + } + } +} \ No newline at end of file diff --git a/syntax/src/ty.rs b/syntax/src/ty.rs new file mode 100644 index 0000000..5257615 --- /dev/null +++ b/syntax/src/ty.rs @@ -0,0 +1,51 @@ +use std::fmt::{self, Display, Formatter}; + +// TODO: Introduce lifetime here to reduce cloning. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Type { + Unit, Bool, Int, Str, + Var(usize), // This type is only used during type inference. + Func(Vec, Box), + Tuple(Vec), + Array(Box), +} + +impl Display for Type { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match *self { + Type::Unit => write!(f, "Unit"), + Type::Bool => write!(f, "Bool"), + Type::Int => write!(f, "Int"), + Type::Str => write!(f, "Str"), + Type::Var(id) => write!(f, "{}", itoa(id)), + Type::Func(ref args, ref ret) => { + write!(f, "({}", args[0])?; + for arg in &args[1..] { + write!(f, " {}", arg)?; + } + write!(f, ") -> {}", ret) + } + Type::Tuple(ref tys) => { + write!(f, "({}", tys[0])?; + for ty in &tys[1..] { + write!(f, " {}", ty)?; + } + write!(f, ")") + } + Type::Array(ref ty) => write!(f, "[{}]", ty), + } + } +} + +/// Convert a number to a string of lowercase letters +pub fn itoa(i: usize) -> String { + let mut s = String::new(); + let mut i = i; + + while i >= 26 { + s.push((b'A' + (i % 26) as u8) as char); + i /= 26; + } + s.push((b'A' + i as u8) as char); + s +} \ No newline at end of file diff --git a/typing/Cargo.toml b/typing/Cargo.toml new file mode 100644 index 0000000..c8c29cd --- /dev/null +++ b/typing/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "typing" +version = "0.1.0" +edition = "2021" + +[dependencies] +chumsky = "1.0.0-alpha.3" +syntax = { path = "../syntax" } diff --git a/typing/src/infer.rs b/typing/src/infer.rs new file mode 100644 index 0000000..d07ff95 --- /dev/null +++ b/typing/src/infer.rs @@ -0,0 +1,744 @@ +use std::collections::HashMap; +use chumsky::span::SimpleSpan; +use syntax::{ + expr::{ + Lit, UnaryOp, BinaryOp, + Expr, + }, + ty::*, +}; + +use crate::rename::{rename_exprs, rename_type}; + +use super::typed::TExpr; + +macro_rules! ok { + ($e:expr) => { + ($e, vec![]) + }; +} + +macro_rules! unbox { + ($e:expr) => { + (*$e.0, $e.1) + }; +} + +#[derive(Clone, Debug)] +pub enum InferErrorKind { + Error, + Hint, +} + +#[derive(Clone, Debug)] +pub struct InferError { + pub title: String, + pub labels: Vec<(String, InferErrorKind, SimpleSpan)>, + pub span: SimpleSpan, +} + +impl InferError { + pub fn new>(title: S, span: SimpleSpan) -> Self { + Self { + title: title.into(), + labels: Vec::new(), + span, + } + } + + pub fn add_error>(mut self, reason: S, span: SimpleSpan) -> Self { + self.labels.push((reason.into(), InferErrorKind::Error, span)); + self + } + + pub fn add_hint>(mut self, reason: S, span: SimpleSpan) -> Self { + self.labels.push((reason.into(), InferErrorKind::Hint, span)); + self + } +} + +#[derive(Clone, Debug, PartialEq)] +struct Constraint { + t1: Type, + t2: Type, + // Where the constraint was generated, for error reporting + span: SimpleSpan, +} + +impl Constraint { + fn new(t1: Type, t2: Type, span: SimpleSpan) -> Self { + Self { + t1, + t2, + span, + } + } +} + +#[derive(Clone, Debug)] +struct Infer<'src> { + env: HashMap<&'src str, Type>, + subst: Vec, + constraints: Vec, +} + +impl<'src> Infer<'src> { + fn new() -> Self { + Infer { + env: HashMap::new(), + subst: Vec::new(), + constraints: Vec::new(), + } + } + + /// Generate a fresh type variable + fn fresh(&mut self) -> Type { + let i = self.subst.len(); + self.subst.push(Type::Var(i)); + Type::Var(i) + } + + /// Get a substitution for a type variable + fn subst(&self, i: usize) -> Option { + self.subst.get(i).cloned() + } + + /// Add new constraint + fn add_constraint(&mut self, c: Constraint) { + self.constraints.push(c); + } + + /// Check if a type variable occurs in a type + fn occurs(&self, i: usize, t: Type) -> bool { + use Type::*; + match t { + Unit | Bool | Int | Str => false, + Var(j) => { + if let Some(t) = self.subst(j) { + if t != Var(j) { + return self.occurs(i, t); + } + } + i == j + }, + Func(args, ret) => { + args.into_iter().any(|t| self.occurs(i, t)) || self.occurs(i, *ret) + }, + Tuple(tys) => tys.into_iter().any(|t| self.occurs(i, t)), + Array(ty) => self.occurs(i, *ty), + } + } + + /// Unify two types + fn unify(&mut self, c: Constraint) -> Result<(), InferError> { + macro_rules! constraint { + ($t1:expr, $t2:expr) => { + Constraint::new($t1, $t2, c.span) + }; + } + + use Type::*; + match (c.t1.clone(), c.t2.clone()) { + // Literal types + (Unit, Unit) + | (Bool, Bool) + | (Int, Int) + | (Str, Str) => Ok(()), + + // Variable + (Var(i), Var(j)) if i == j => Ok(()), // Same variables can be unified + (Var(i), t2) => { + // If the substitution is not the variable itself, + // unify the substitution with t2 + if let Some(t) = self.subst(i) { + if t != Var(i) { + return self.unify(constraint!(t, t2)); + } + } + // If the variable occurs in t2 + if self.occurs(i, t2.clone()) { + return Err(InferError::new("Infinite type", c.span) + .add_error(format!( + "This type contains itself: {}", rename_type(Var(i)) + ), c.span)); + } + // Set the substitution + self.subst[i] = t2; + Ok(()) + }, + (t1, Var(i)) => { + if let Some(t) = self.subst(i) { + if t != Var(i) { + return self.unify(constraint!(t1, t)); + } + } + if self.occurs(i, t1.clone()) { + return Err(InferError::new("Infinite type", c.span) + .add_error(format!( + "This type contains itself: {}", + rename_type(Var(i)) + ), c.span)); + } + self.subst[i] = t1; + Ok(()) + }, + + // Function + (Func(a1, r1), Func(a2, r2)) => { + // Check the number of arguments + if a1.len() != a2.len() { + let mut e = InferError::new("Argument length mismatch", c.span) + .add_error(format!( + "This function is expected to take {} arguments, found {}", + a2.len(), a1.len() + ), c.span); + if a2.len() > a1.len() { + // Get the types of the needed arguments + let mut args = Vec::new(); + for i in a1.len()..a2.len() { + args.push(self.substitute(a2[i].clone()).to_string()); + } + e = e.add_hint(format!( + "Need arguments of type `{}` to call this function", + args.join(", ") + ), c.span); + } + return Err(e); + } + // Unify the arguments + for (a1, a2) in a1.into_iter().zip(a2.into_iter()) { + self.unify(constraint!(a1, a2))?; + } + // Unify the return types + self.unify(constraint!(*r1, *r2)) + }, + + // Tuple + (Tuple(t1), Tuple(t2)) => { + // Check the number of elements + if t1.len() != t2.len() { + return Err(InferError::new("Tuple length mismatch", c.span) + .add_error(format!( + "Expected {} elements, found {}", + t1.len(), t2.len() + ), c.span)); + } + // Unify the elements + for (t1, t2) in t1.into_iter().zip(t2.into_iter()) { + self.unify(constraint!(t1, t2))?; + } + Ok(()) + }, + + // Array + (Array(t1), Array(t2)) => self.unify(constraint!(*t1, *t2)), + + // The rest will be type mismatch + (t1, t2) => Err(InferError::new("Type mismatch", c.span) + .add_error(format!( + "Expected {}, found {}", + rename_type(t1), rename_type(t2) + ), c.span)), + } + } + + /// Solve the constraints by unifying them + + fn solve(&mut self) -> Vec { + let mut errors = Vec::new(); + for c in self.constraints.clone().into_iter() { + if let Err(e) = self.unify(c) { + errors.push(e); + } + } + errors + } + + /// Substitute the type variables with the substitutions + fn substitute(&mut self, t: Type) -> Type { + use Type::*; + match t { + // Only match any type that can contain type variables + Var(i) => { + if let Some(t) = self.subst(i) { + if t != Var(i) { + return self.substitute(t); + } + } + Var(i) + }, + Func(args, ret) => { + Func( + args.into_iter().map(|t| self.substitute(t)).collect(), + Box::new(self.substitute(*ret)), + ) + }, + Tuple(tys) => Tuple(tys.into_iter().map(|t| self.substitute(t)).collect()), + Array(ty) => Array(Box::new(self.substitute(*ty))), + // The rest will be returned as is + _ => t, + } + } + + /// Find a type variable in (typed) expression and substitute them + fn substitute_texp(&mut self, e: TExpr<'src>) -> TExpr<'src> { + use TExpr::*; + match e { + Lit(_) | Ident(_) => e, + Unary { op, expr: (e, lspan), ret_ty } => { + Unary { + op, + expr: (Box::new(self.substitute_texp(*e)), lspan), + ret_ty, + } + }, + Binary { op, lhs: (lhs, lspan), rhs: (rhs, rspan), ret_ty } => { + let lhst = self.substitute_texp(*lhs); + let rhst = self.substitute_texp(*rhs); + Binary { + op, + lhs: (Box::new(lhst), lspan), + rhs: (Box::new(rhst), rspan), + ret_ty: self.substitute(ret_ty), + } + }, + Lambda { params, body: (body, bspan), ret_ty } => { + let bodyt = self.substitute_texp(*body); + let paramst = params.into_iter() + .map(|(name, ty)| (name, self.substitute(ty))) + .collect::>(); + Lambda { + params: paramst, + body: (Box::new(bodyt), bspan), + ret_ty: self.substitute(ret_ty), + } + }, + Call { func: (func, fspan), args } => { + let funct = self.substitute_texp(*func); + let argst = args.into_iter() + .map(|(arg, span)| (self.substitute_texp(arg), span)) + .collect::>(); + Call { + func: (Box::new(funct), fspan), + args: argst, + } + }, + If { cond: (cond, cspan), t: (t, tspan), f: (f, fspan), br_ty } => { + let condt = self.substitute_texp(*cond); + let tt = self.substitute_texp(*t); + let ft = self.substitute_texp(*f); + If { + cond: (Box::new(condt), cspan), + t: (Box::new(tt), tspan), + f: (Box::new(ft), fspan), + br_ty, + } + }, + Let { name, ty, value: (v, vspan), body: (b, bspan) } => { + let vt = self.substitute_texp(*v); + let bt = self.substitute_texp(*b); + Let { + name, + ty: self.substitute(ty), + value: (Box::new(vt), vspan), + body: (Box::new(bt), bspan), + } + }, + Define { name, ty, value: (v, vspan) } => { + let vt = self.substitute_texp(*v); + Define { + name, + ty: self.substitute(ty), + value: (Box::new(vt), vspan), + } + }, + Block { exprs, void, ret_ty } => { + let exprst = exprs.into_iter() + .map(|(e, span)| (self.substitute_texp(e), span)) + .collect::>(); + Block { + exprs: exprst, + void, + ret_ty: self.substitute(ret_ty), + } + }, + } + } + + /// Infer the type of an expression + fn infer( + &mut self, e: (Expr<'src>, SimpleSpan), expected: Type + ) -> (TExpr<'src>, Vec) { + let span = e.1; + macro_rules! constraint { + ($ty:expr) => { + self.add_constraint(Constraint::new(expected, $ty, span)) + }; + } + + match e.0 { + // Literal values + // Push the constraint (expected type to be the literal type) and + // return the typed expression + Expr::Lit(l) => match l { + Lit::Unit => { + constraint!(Type::Unit); + ok!(TExpr::Lit(Lit::Unit)) + } + Lit::Bool(b) => { + constraint!(Type::Bool); + ok!(TExpr::Lit(Lit::Bool(b))) + } + Lit::Int(i) => { + constraint!(Type::Int); + ok!(TExpr::Lit(Lit::Int(i))) + } + Lit::Str(s) => { + constraint!(Type::Str); + ok!(TExpr::Lit(Lit::Str(s))) + } + } + + // Identifiers + // The same as literals but the type is looked up in the environment + Expr::Ident(ref x) => { + if let Some(t) = self.env.get(x) { + constraint!(t.clone()); + ok!(TExpr::Ident(x)) + } else { + let kind = match &expected { + Type::Func(_, _) => "function", + _ => "value", + }; + (TExpr::Ident(x), vec![ + InferError::new(format!("Undefined {}", kind), span) + .add_error(format!("`{}` is not defined", x), span) + ]) + } + } + + // Unary & binary operators + // The type of the left and right hand side are inferred and + // the expected type is determined by the operator + Expr::Unary(op, e) => match op { + // Numeric operators (Int -> Int) + UnaryOp::Neg => { + let (te, err) = self.infer(unbox!(e), Type::Int); + constraint!(Type::Int); + (TExpr::Unary { + op, + expr: (Box::new(te), span), + ret_ty: Type::Int, + }, err) + }, + // Boolean operators (Bool -> Bool) + UnaryOp::Not => { + let (te, err) = self.infer(unbox!(e), Type::Bool); + constraint!(Type::Bool); + (TExpr::Unary { + op, + expr: (Box::new(te), span), + ret_ty: Type::Bool, + }, err) + }, + } + Expr::Binary(op, lhs, rhs) => match op { + // Numeric operators (Int -> Int -> Int) + BinaryOp::Add + | BinaryOp::Sub + | BinaryOp::Mul + | BinaryOp::Div + | BinaryOp::Rem + => { + let (lt, mut errs0) = self.infer(unbox!(lhs), Type::Int); + let (rt, errs1) = self.infer(unbox!(rhs), Type::Int); + errs0.extend(errs1); + constraint!(Type::Int); + (TExpr::Binary { + op, + lhs: (Box::new(lt), lhs.1), + rhs: (Box::new(rt), rhs.1), + ret_ty: Type::Int, + }, errs0) + }, + // Boolean operators (Bool -> Bool -> Bool) + BinaryOp::And + | BinaryOp::Or + => { + let (lt, mut errs0) = self.infer(unbox!(lhs), Type::Bool); + let (rt, errs1) = self.infer(unbox!(rhs), Type::Bool); + errs0.extend(errs1); + constraint!(Type::Bool); + (TExpr::Binary { + op, + lhs: (Box::new(lt), lhs.1), + rhs: (Box::new(rt), rhs.1), + ret_ty: Type::Bool, + }, errs0) + }, + // Comparison operators ('a -> 'a -> Bool) + BinaryOp::Eq + | BinaryOp::Ne + | BinaryOp::Lt + | BinaryOp::Le + | BinaryOp::Gt + | BinaryOp::Ge + => { + // Create a fresh type variable and then use it as the + // expected type for both the left and right hand side + // so the type on both side have to be the same + let t = self.fresh(); + let (lt, mut errs0) = self.infer(unbox!(lhs), t.clone()); + let (rt, errs1) = self.infer(unbox!(rhs), t); + errs0.extend(errs1); + constraint!(Type::Bool); + (TExpr::Binary { + op, + lhs: (Box::new(lt), lhs.1), + rhs: (Box::new(rt), rhs.1), + ret_ty: Type::Bool, + }, errs0) + }, + + BinaryOp::Pipe => { + // Since this is parsed with a fold left, the right hand + // side should always be a function + let t = self.fresh(); + let (lt, mut errs0) = self.infer(unbox!(lhs), t.clone()); + // The right hand side should be a function that takes + // 1 argument with the type of t + let (rt, errs1) = self.infer( + unbox!(rhs), + Type::Func(vec![t.clone()], Box::new(t.clone())), + ); + errs0.extend(errs1); + constraint!(t.clone()); + (TExpr::Binary { + op, + lhs: (Box::new(lt), lhs.1), + rhs: (Box::new(rt), rhs.1), + ret_ty: t, + }, errs0) + }, + } + + // Lambda + Expr::Lambda(args, ret, b) => { + // Get the return type or create a fresh type variable + let rt = ret.unwrap_or(self.fresh()); + // Fill in the type of the arguments with a fresh type + let xs = args.into_iter() + .map(|(x, t)| (x, t.unwrap_or(self.fresh()))) + .collect::>(); + + // Create a new environment, and add the arguments to it + // and use the new environment to infer the body + let mut env = self.env.clone(); + xs.clone().into_iter().for_each(|(x, t)| { env.insert(x, t); }); + let mut inf = self.clone(); + inf.env = env; + let (bt, errs) = inf.infer(unbox!(b), rt.clone()); + + // Add the substitutions & constraints from the body + // if it doesn't already exist + for s in inf.subst { + if !self.subst.contains(&s) { + self.subst.push(s); + } + } + for c in inf.constraints { + if !self.constraints.contains(&c) { + self.constraints.push(c); + } + } + + // Push the constraints + constraint!(Type::Func( + xs.clone().into_iter() + .map(|x| x.1) + .collect(), + Box::new(rt.clone()), + )); + + (TExpr::Lambda { + params: xs, + body: (Box::new(bt), b.1), + ret_ty: rt, + }, errs) + }, + + // Call + Expr::Call(f, args) => { + // Generate fresh types for the arguments + let freshes = args.clone().into_iter() + .map(|_| self.fresh()) + .collect::>(); + // Create a function type + let fsig = Type::Func( + freshes.clone(), + Box::new(expected), + ); + // Expect the function to have the function type + let (ft, mut errs) = self.infer(unbox!(f), fsig); + // Infer the arguments + let (xs, xerrs) = args.into_iter() + .zip(freshes.into_iter()) + .map(|(x, t)| { + let span = x.1; + let (xt, err) = self.infer(x, t); + ((xt, span), err) + }) + // Flatten errors + .fold((vec![], vec![]), |(mut xs, mut errs), ((x, span), err)| { + xs.push((x, span)); + errs.extend(err); + (xs, errs) + }); + errs.extend(xerrs); + + (TExpr::Call { + func: (Box::new(ft), f.1), + args: xs, + }, errs) + }, + + // If + Expr::If { cond, t, f } => { + // Condition has to be a boolean + let (ct, mut errs) = self.infer(unbox!(cond), Type::Bool); + // The type of the if expression is the same as the + // expected type + let (tt, terrs) = self.infer(unbox!(t), expected.clone()); + let (ft, ferrs) = self.infer(unbox!(f), expected.clone()); + errs.extend(terrs); + errs.extend(ferrs); + + (TExpr::If { + cond: (Box::new(ct), cond.1), + t: (Box::new(tt), t.1), + f: (Box::new(ft), f.1), + br_ty: expected, + }, errs) + }, + + // Let & define + Expr::Let { name, ty, value, body } => { + // Infer the type of the value + let ty = ty.unwrap_or(self.fresh()); + let (vt, mut errs) = self.infer(unbox!(value), ty.clone()); + + // Create a new environment and add the binding to it + // and then use the new environment to infer the body + let mut env = self.env.clone(); + env.insert(name.clone(), ty.clone()); + let mut inf = Infer::new(); + inf.env = env; + let (bt, berrs) = inf.infer(unbox!(body), expected.clone()); + errs.extend(berrs); + + for s in inf.subst { + if !self.subst.contains(&s) { + self.subst.push(s); + } + } + for c in inf.constraints { + if !self.constraints.contains(&c) { + self.constraints.push(c); + } + } + + (TExpr::Let { + name, ty, + value: (Box::new(vt), value.1), + body: (Box::new(bt), body.1), + }, errs) + }, + Expr::Define { name, ty, value } => { + let ty = ty.unwrap_or(self.fresh()); + self.env.insert(name.clone(), ty.clone()); + let (val_ty, errs) = self.infer(unbox!(value), ty.clone()); + + constraint!(Type::Unit); + + (TExpr::Define { + name, + ty, + value: (Box::new(val_ty), value.1), + }, errs) + }, + + // Block + Expr::Block { exprs, void } => { + // Infer the type of each expression + let mut last = None; + let len = exprs.len(); + let (texprs, errs) = exprs.into_iter() + .enumerate() + .map(|(i, x)| { + let span = x.1; + let t = self.fresh(); + let (xt, err) = self.infer(unbox!(x), t.clone()); + // Save the type of the last expression + if i == len - 1 { + last = Some(t); + } + ((xt, span), err) + }) + .fold((vec![], vec![]), |(mut xs, mut errs), ((x, span), err)| { + xs.push((x, span)); + errs.extend(err); + (xs, errs) + }); + + let rt = if void || last.is_none() { + // If the block is void or there is no expression, + // the return type is unit + constraint!(Type::Unit); + Type::Unit + } else { + // Otherwise, the return type is the same as the expected type + // constraint!(last.unwrap()); + self.add_constraint(Constraint::new(expected.clone(), last.unwrap(), span)); + expected + }; + + (TExpr::Block { + exprs: texprs, + void, + ret_ty: rt, + }, errs) + }, + } + } +} + +/// Infer a list of expressions +pub fn infer_exprs(es: Vec<(Expr, SimpleSpan)>) -> (Vec<(TExpr, SimpleSpan)>, Vec) { + let mut inf = Infer::new(); + // Type expressions + let mut tes = vec![]; + // Unsubstituted typed expressions + let mut errors = vec![]; + + for e in es { + let span = e.1; + let fresh = inf.fresh(); + // Infer the types + let (te, err) = inf.infer(e, fresh); + + // Push the expression to the list + tes.push((te.clone(), span)); + + if !err.is_empty() { + errors.extend(err); + } + } + + let solve_errors = inf.solve(); + if !solve_errors.is_empty() { + errors.extend(solve_errors); + } + + + (rename_exprs(tes), errors) +} \ No newline at end of file diff --git a/typing/src/lib.rs b/typing/src/lib.rs new file mode 100644 index 0000000..4c01380 --- /dev/null +++ b/typing/src/lib.rs @@ -0,0 +1,3 @@ +pub mod infer; +pub mod rename; +pub mod typed; \ No newline at end of file diff --git a/typing/src/rename.rs b/typing/src/rename.rs new file mode 100644 index 0000000..b77aafa --- /dev/null +++ b/typing/src/rename.rs @@ -0,0 +1,185 @@ +use chumsky::span::SimpleSpan; +use syntax::ty::Type; + +use crate::typed::TExpr; + +/// A renamer to rename type variables to a "minimized" form for more readable output +pub struct Renamer { + // Type variables encountered so far + vars: Vec, +} + +impl<'src> Renamer { + pub fn new() -> Self { + Self { + vars: vec![], + } + } + + fn rename_var(&self, i: usize) -> Type { + let n = self.vars.iter().position(|x| x == &i).unwrap(); + Type::Var(n) + } + + fn add_var(&mut self, i: usize) { + if !self.vars.contains(&i) { + self.vars.push(i); + } + } + + fn find_var(&mut self, t: Type) { + match t { + Type::Var(i) => { + self.add_var(i); + }, + Type::Func(args, ret) => { + args.into_iter().for_each(|t| self.find_var(t)); + self.find_var(*ret); + }, + Type::Tuple(tys) => { + tys.into_iter().for_each(|t| self.find_var(t)); + }, + Type::Array(ty) => { + self.find_var(*ty); + }, + _ => {}, + } + } + + fn traverse(&mut self, e: TExpr) { + match e { + TExpr::Unary { expr, ret_ty, ..} => { + self.traverse(*expr.0); + self.find_var(ret_ty); + }, + TExpr::Binary { lhs, rhs, ret_ty, ..} => { + self.traverse(*lhs.0); + self.traverse(*rhs.0); + self.find_var(ret_ty); + }, + TExpr::Lambda { params, body, ret_ty } => { + for (_, t) in params { self.find_var(t); } + self.find_var(ret_ty); + self.traverse(*body.0); + }, + TExpr::Call { func, args } => { + self.traverse(*func.0); + for arg in args { + self.traverse(arg.0); + } + }, + TExpr::Let { ty, value, body, .. } => { + self.find_var(ty); + self.traverse(*value.0); + self.traverse(*body.0); + }, + TExpr::Define { ty, value, .. } => { + self.find_var(ty); + self.traverse(*value.0); + }, + TExpr::Block { exprs, ret_ty, .. } => { + for expr in exprs { + self.traverse(expr.0); + } + self.find_var(ret_ty); + }, + _ => {}, + } + } + + fn rename_type(&self, t: Type) -> Type { + match t { + Type::Var(i) => self.rename_var(i), + Type::Func(args, ret) => { + Type::Func( + args.into_iter().map(|x| self.rename_type(x)).collect(), + Box::new(self.rename_type(*ret)), + ) + }, + Type::Tuple(tys) => { + Type::Tuple(tys.into_iter().map(|x| self.rename_type(x)).collect()) + }, + Type::Array(ty) => { + Type::Array(Box::new(self.rename_type(*ty))) + }, + _ => t, + } + } + + fn rename_texp(&self, e: TExpr<'src>) -> TExpr<'src> { + match e { + TExpr::Unary { op, expr, ret_ty } => { + TExpr::Unary { + op, + expr: (Box::new(self.rename_texp(*expr.0)), expr.1), + ret_ty: self.rename_type(ret_ty) + } + }, + TExpr::Binary { op, lhs, rhs, ret_ty } => { + TExpr::Binary { + op, + lhs: (Box::new(self.rename_texp(*lhs.0)), lhs.1), + rhs: (Box::new(self.rename_texp(*rhs.0)), rhs.1), + ret_ty: self.rename_type(ret_ty) + } + }, + TExpr::Lambda { params, body, ret_ty } => { + TExpr::Lambda { + params: params.into_iter() + .map(|(x, t)| (x, self.rename_type(t))) + .collect(), + body: (Box::new(self.rename_texp(*body.0)), body.1), + ret_ty: self.rename_type(ret_ty) + } + }, + TExpr::Call { func, args } => { + TExpr::Call { + func: (Box::new(self.rename_texp(*func.0)), func.1), + args: args.into_iter() + .map(|x| (self.rename_texp(x.0), x.1)) + .collect() + } + }, + TExpr::Let { name, ty, value, body } => { + TExpr::Let { + name, + ty: self.rename_type(ty), + value: (Box::new(self.rename_texp(*value.0)), value.1), + body: (Box::new(self.rename_texp(*body.0)), body.1) + } + }, + TExpr::Define { name, ty, value } => { + TExpr::Define { + name, + ty: self.rename_type(ty), + value: (Box::new(self.rename_texp(*value.0)), value.1) + } + }, + TExpr::Block { exprs, void, ret_ty } => { + TExpr::Block { + exprs: exprs.into_iter() + .map(|x| (self.rename_texp(x.0), x.1)) + .collect(), + void, + ret_ty: self.rename_type(ret_ty) + } + }, + _ => e, + } + } +} + +pub fn rename_type(t: Type) -> Type { + let mut renamer = Renamer::new(); + renamer.find_var(t.clone()); + renamer.rename_type(t) +} + +pub fn rename_exprs(es: Vec<(TExpr, SimpleSpan)>) -> Vec<(TExpr, SimpleSpan)> { + let mut renamer = Renamer::new(); + es.clone().into_iter() + .for_each(|e| renamer.traverse(e.0)); + es.into_iter() + .map(|(e, s)| (renamer.rename_texp(e), s)) + .collect() +} \ No newline at end of file diff --git a/typing/src/typed.rs b/typing/src/typed.rs new file mode 100644 index 0000000..90b30c0 --- /dev/null +++ b/typing/src/typed.rs @@ -0,0 +1,60 @@ +use syntax::{ + expr::{ + BinaryOp, + UnaryOp, + Lit, + Spanned, + }, + ty::Type, +}; + +// Typed version of the expression. +#[derive(Clone, Debug)] +pub enum TExpr<'src> { + Lit(Lit<'src>), + Ident(&'src str), + + Unary { + op: UnaryOp, + expr: Spanned>, + ret_ty: Type, + }, + Binary { + op: BinaryOp, + lhs: Spanned>, + rhs: Spanned>, + ret_ty: Type, + }, + + Lambda { + params: Vec<(&'src str, Type)>, + body: Spanned>, + ret_ty: Type, + }, + Call { + func: Spanned>, + args: Vec>, + }, + If { + cond: Spanned>, + t: Spanned>, + f: Spanned>, + br_ty: Type, + }, + Let { + name: &'src str, + ty: Type, + value: Spanned>, + body: Spanned>, + }, + Define { + name: &'src str, + ty: Type, + value: Spanned>, + }, + Block { + exprs: Vec>, + void: bool, + ret_ty: Type, + }, +} \ No newline at end of file