about summary refs log tree commit diff
path: root/users/grfn/achilles
diff options
context:
space:
mode:
Diffstat (limited to 'users/grfn/achilles')
-rw-r--r--users/grfn/achilles/.envrc1
-rw-r--r--users/grfn/achilles/.gitignore1
-rw-r--r--users/grfn/achilles/Cargo.lock868
-rw-r--r--users/grfn/achilles/Cargo.toml26
-rw-r--r--users/grfn/achilles/ach/.gitignore7
-rw-r--r--users/grfn/achilles/ach/Makefile15
-rw-r--r--users/grfn/achilles/ach/externs.ach5
-rw-r--r--users/grfn/achilles/ach/functions.ach8
-rw-r--r--users/grfn/achilles/ach/simple.ach1
-rw-r--r--users/grfn/achilles/ach/units.ach7
-rw-r--r--users/grfn/achilles/default.nix24
-rw-r--r--users/grfn/achilles/shell.nix18
-rw-r--r--users/grfn/achilles/src/ast/hir.rs364
-rw-r--r--users/grfn/achilles/src/ast/mod.rs484
-rw-r--r--users/grfn/achilles/src/codegen/llvm.rs486
-rw-r--r--users/grfn/achilles/src/codegen/mod.rs25
-rw-r--r--users/grfn/achilles/src/commands/check.rs39
-rw-r--r--users/grfn/achilles/src/commands/compile.rs31
-rw-r--r--users/grfn/achilles/src/commands/eval.rs32
-rw-r--r--users/grfn/achilles/src/commands/mod.rs7
-rw-r--r--users/grfn/achilles/src/common/env.rs59
-rw-r--r--users/grfn/achilles/src/common/error.rs59
-rw-r--r--users/grfn/achilles/src/common/mod.rs6
-rw-r--r--users/grfn/achilles/src/common/namer.rs122
-rw-r--r--users/grfn/achilles/src/compiler.rs89
-rw-r--r--users/grfn/achilles/src/interpreter/error.rs19
-rw-r--r--users/grfn/achilles/src/interpreter/mod.rs203
-rw-r--r--users/grfn/achilles/src/interpreter/value.rs224
-rw-r--r--users/grfn/achilles/src/main.rs36
-rw-r--r--users/grfn/achilles/src/parser/expr.rs718
-rw-r--r--users/grfn/achilles/src/parser/macros.rs16
-rw-r--r--users/grfn/achilles/src/parser/mod.rs240
-rw-r--r--users/grfn/achilles/src/parser/type_.rs152
-rw-r--r--users/grfn/achilles/src/parser/util.rs8
-rw-r--r--users/grfn/achilles/src/passes/hir/mod.rs211
-rw-r--r--users/grfn/achilles/src/passes/hir/monomorphize.rs139
-rw-r--r--users/grfn/achilles/src/passes/hir/strip_positive_units.rs191
-rw-r--r--users/grfn/achilles/src/passes/mod.rs1
-rw-r--r--users/grfn/achilles/src/tc/mod.rs808
-rw-r--r--users/grfn/achilles/tests/compile.rs79
40 files changed, 0 insertions, 5829 deletions
diff --git a/users/grfn/achilles/.envrc b/users/grfn/achilles/.envrc
deleted file mode 100644
index 051d09d292..0000000000
--- a/users/grfn/achilles/.envrc
+++ /dev/null
@@ -1 +0,0 @@
-eval "$(lorri direnv)"
diff --git a/users/grfn/achilles/.gitignore b/users/grfn/achilles/.gitignore
deleted file mode 100644
index ea8c4bf7f3..0000000000
--- a/users/grfn/achilles/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/target
diff --git a/users/grfn/achilles/Cargo.lock b/users/grfn/achilles/Cargo.lock
deleted file mode 100644
index 30e5e021f1..0000000000
--- a/users/grfn/achilles/Cargo.lock
+++ /dev/null
@@ -1,868 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-[[package]]
-name = "achilles"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "bimap",
- "clap",
- "crate-root",
- "derive_more",
- "inkwell",
- "itertools",
- "lazy_static",
- "llvm-sys",
- "nom",
- "nom-trace",
- "pratt",
- "pretty_assertions",
- "proptest",
- "test-strategy",
- "thiserror",
- "void",
-]
-
-[[package]]
-name = "aho-corasick"
-version = "0.7.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "ansi_term"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "anyhow"
-version = "1.0.38"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
-
-[[package]]
-name = "arrayvec"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
-
-[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi",
- "libc",
- "winapi",
-]
-
-[[package]]
-name = "autocfg"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
-
-[[package]]
-name = "bimap"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f92b72b8f03128773278bf74418b9205f3d2a12c39a61f92395f47af390c32bf"
-
-[[package]]
-name = "bit-set"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
-dependencies = [
- "bit-vec",
-]
-
-[[package]]
-name = "bit-vec"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
-
-[[package]]
-name = "bitflags"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
-
-[[package]]
-name = "bitvec"
-version = "0.19.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321"
-dependencies = [
- "funty",
- "radium",
- "tap",
- "wyz",
-]
-
-[[package]]
-name = "byteorder"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
-
-[[package]]
-name = "cc"
-version = "1.0.67"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "clap"
-version = "3.0.0-beta.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142"
-dependencies = [
- "atty",
- "bitflags",
- "clap_derive",
- "indexmap",
- "lazy_static",
- "os_str_bytes",
- "strsim",
- "termcolor",
- "textwrap",
- "unicode-width",
- "vec_map",
-]
-
-[[package]]
-name = "clap_derive"
-version = "3.0.0-beta.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1"
-dependencies = [
- "heck",
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "crate-root"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59c6fe4622b269032d2c5140a592d67a9c409031d286174fcde172fbed86f0d3"
-
-[[package]]
-name = "ctor"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19"
-dependencies = [
- "quote",
- "syn",
-]
-
-[[package]]
-name = "derive_more"
-version = "0.99.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "diff"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499"
-
-[[package]]
-name = "either"
-version = "1.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
-
-[[package]]
-name = "fnv"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-
-[[package]]
-name = "funty"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
-
-[[package]]
-name = "getrandom"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
-
-[[package]]
-name = "heck"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac"
-dependencies = [
- "unicode-segmentation",
-]
-
-[[package]]
-name = "hermit-abi"
-version = "0.1.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "indexmap"
-version = "1.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
-dependencies = [
- "autocfg",
- "hashbrown",
-]
-
-[[package]]
-name = "inkwell"
-version = "0.1.0"
-source = "git+https://github.com/TheDan64/inkwell?branch=master#a2db15b0bd1c06d71763585ae10d9ea4e775da0c"
-dependencies = [
- "either",
- "inkwell_internals",
- "libc",
- "llvm-sys",
- "once_cell",
- "parking_lot",
- "regex",
-]
-
-[[package]]
-name = "inkwell_internals"
-version = "0.3.0"
-source = "git+https://github.com/TheDan64/inkwell?branch=master#a2db15b0bd1c06d71763585ae10d9ea4e775da0c"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "instant"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "itertools"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319"
-dependencies = [
- "either",
-]
-
-[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-
-[[package]]
-name = "lexical-core"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374"
-dependencies = [
- "arrayvec",
- "bitflags",
- "cfg-if",
- "ryu",
- "static_assertions",
-]
-
-[[package]]
-name = "libc"
-version = "0.2.88"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a"
-
-[[package]]
-name = "llvm-sys"
-version = "110.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21ede189444b8c78907e5d36da5dabcf153170fcff9c1dba48afc4b33c7e19f0"
-dependencies = [
- "cc",
- "lazy_static",
- "libc",
- "regex",
- "semver",
-]
-
-[[package]]
-name = "lock_api"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
-dependencies = [
- "scopeguard",
-]
-
-[[package]]
-name = "memchr"
-version = "2.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
-
-[[package]]
-name = "nom"
-version = "6.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2"
-dependencies = [
- "bitvec",
- "funty",
- "lexical-core",
- "memchr",
- "version_check",
-]
-
-[[package]]
-name = "nom-trace"
-version = "0.2.1"
-source = "git+https://github.com/glittershark/nom-trace?branch=nom-6#6168d2e15cc51efd12d80260159b76a764dba138"
-dependencies = [
- "nom",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "once_cell"
-version = "1.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
-
-[[package]]
-name = "os_str_bytes"
-version = "2.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85"
-
-[[package]]
-name = "output_vt100"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "parking_lot"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
-dependencies = [
- "instant",
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
-dependencies = [
- "cfg-if",
- "instant",
- "libc",
- "redox_syscall",
- "smallvec",
- "winapi",
-]
-
-[[package]]
-name = "pest"
-version = "2.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
-dependencies = [
- "ucd-trie",
-]
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
-
-[[package]]
-name = "pratt"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e31bbc12f7936a7b195790dd6d9b982b66c54f45ff6766decf25c44cac302dce"
-
-[[package]]
-name = "pretty_assertions"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f297542c27a7df8d45de2b0e620308ab883ad232d06c14b76ac3e144bda50184"
-dependencies = [
- "ansi_term",
- "ctor",
- "diff",
- "output_vt100",
-]
-
-[[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.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
-dependencies = [
- "unicode-xid",
-]
-
-[[package]]
-name = "proptest"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5"
-dependencies = [
- "bit-set",
- "bitflags",
- "byteorder",
- "lazy_static",
- "num-traits",
- "quick-error 2.0.0",
- "rand",
- "rand_chacha",
- "rand_xorshift",
- "regex-syntax",
- "rusty-fork",
- "tempfile",
-]
-
-[[package]]
-name = "quick-error"
-version = "1.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
-
-[[package]]
-name = "quick-error"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda"
-
-[[package]]
-name = "quote"
-version = "1.0.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "radium"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
-
-[[package]]
-name = "rand"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
-dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
- "rand_hc",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
-dependencies = [
- "getrandom",
-]
-
-[[package]]
-name = "rand_hc"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
-dependencies = [
- "rand_core",
-]
-
-[[package]]
-name = "rand_xorshift"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
-dependencies = [
- "rand_core",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "regex"
-version = "1.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
- "thread_local",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.6.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
-
-[[package]]
-name = "remove_dir_all"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "rusty-fork"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
-dependencies = [
- "fnv",
- "quick-error 1.2.3",
- "tempfile",
- "wait-timeout",
-]
-
-[[package]]
-name = "ryu"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
-
-[[package]]
-name = "scopeguard"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
-
-[[package]]
-name = "semver"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
-dependencies = [
- "semver-parser",
-]
-
-[[package]]
-name = "semver-parser"
-version = "0.10.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
-dependencies = [
- "pest",
-]
-
-[[package]]
-name = "smallvec"
-version = "1.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
-
-[[package]]
-name = "static_assertions"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-
-[[package]]
-name = "strsim"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
-
-[[package]]
-name = "syn"
-version = "1.0.61"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed22b90a0e734a23a7610f4283ac9e5acfb96cbb30dfefa540d66f866f1c09c5"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-xid",
-]
-
-[[package]]
-name = "tap"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
-
-[[package]]
-name = "tempfile"
-version = "3.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
-dependencies = [
- "cfg-if",
- "libc",
- "rand",
- "redox_syscall",
- "remove_dir_all",
- "winapi",
-]
-
-[[package]]
-name = "termcolor"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
-name = "test-strategy"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2328963c69243416e811c88066d18f670792b2e36e17fa57f4b1a124f85d18a8"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "textwrap"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789"
-dependencies = [
- "unicode-width",
-]
-
-[[package]]
-name = "thiserror"
-version = "1.0.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
-dependencies = [
- "thiserror-impl",
-]
-
-[[package]]
-name = "thiserror-impl"
-version = "1.0.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "thread_local"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
-dependencies = [
- "once_cell",
-]
-
-[[package]]
-name = "ucd-trie"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
-
-[[package]]
-name = "unicode-segmentation"
-version = "1.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
-
-[[package]]
-name = "unicode-width"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
-
-[[package]]
-name = "unicode-xid"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
-
-[[package]]
-name = "vec_map"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
-
-[[package]]
-name = "version_check"
-version = "0.9.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
-
-[[package]]
-name = "void"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
-
-[[package]]
-name = "wait-timeout"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "wasi"
-version = "0.10.2+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-util"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-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 = "wyz"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
diff --git a/users/grfn/achilles/Cargo.toml b/users/grfn/achilles/Cargo.toml
deleted file mode 100644
index f091399a0d..0000000000
--- a/users/grfn/achilles/Cargo.toml
+++ /dev/null
@@ -1,26 +0,0 @@
-[package]
-name = "achilles"
-version = "0.1.0"
-authors = ["Griffin Smith <root@gws.fyi>"]
-edition = "2018"
-
-[dependencies]
-anyhow = "1.0.38"
-bimap = "0.6.0"
-clap = "3.0.0-beta.2"
-derive_more = "0.99.11"
-inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm11-0"] }
-itertools = "0.10.0"
-lazy_static = "1.4.0"
-llvm-sys = "110.0.1"
-nom = "6.1.2"
-nom-trace = { git = "https://github.com/glittershark/nom-trace", branch = "nom-6" }
-pratt = "0.3.0"
-proptest = "1.0.0"
-test-strategy = "0.1.1"
-thiserror = "1.0.24"
-void = "1.0.2"
-
-[dev-dependencies]
-crate-root = "0.1.3"
-pretty_assertions = "0.7.1"
diff --git a/users/grfn/achilles/ach/.gitignore b/users/grfn/achilles/ach/.gitignore
deleted file mode 100644
index ac5296ebbd..0000000000
--- a/users/grfn/achilles/ach/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-*.ll
-*.o
-
-functions
-simple
-externs
-units
diff --git a/users/grfn/achilles/ach/Makefile b/users/grfn/achilles/ach/Makefile
deleted file mode 100644
index 3a8cd2865e..0000000000
--- a/users/grfn/achilles/ach/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-default: simple
-
-%.ll: %.ach
-	cargo run -- compile $< -o $@ -f llvm
-
-%.o: %.ll
-	llc $< -o $@ -filetype=obj
-
-%: %.o
-	clang $< -o $@
-
-.PHONY: clean
-
-clean:
-	@rm -f *.ll *.o simple functions
diff --git a/users/grfn/achilles/ach/externs.ach b/users/grfn/achilles/ach/externs.ach
deleted file mode 100644
index faf8ce90e3..0000000000
--- a/users/grfn/achilles/ach/externs.ach
+++ /dev/null
@@ -1,5 +0,0 @@
-extern puts : fn cstring -> int
-
-fn main =
-    let _ = puts "foobar"
-    in 0
diff --git a/users/grfn/achilles/ach/functions.ach b/users/grfn/achilles/ach/functions.ach
deleted file mode 100644
index dc6e7a1f3e..0000000000
--- a/users/grfn/achilles/ach/functions.ach
+++ /dev/null
@@ -1,8 +0,0 @@
-ty id : fn a -> a
-fn id x = x
-
-ty plus : fn int -> int
-fn plus (x: int) (y: int) = x + y
-
-ty main : fn -> int
-fn main = plus (id 2) 7
diff --git a/users/grfn/achilles/ach/simple.ach b/users/grfn/achilles/ach/simple.ach
deleted file mode 100644
index 20f1677235..0000000000
--- a/users/grfn/achilles/ach/simple.ach
+++ /dev/null
@@ -1 +0,0 @@
-fn main = let x = 2; y = 3 in x + y
diff --git a/users/grfn/achilles/ach/units.ach b/users/grfn/achilles/ach/units.ach
deleted file mode 100644
index 70635d978c..0000000000
--- a/users/grfn/achilles/ach/units.ach
+++ /dev/null
@@ -1,7 +0,0 @@
-extern puts : fn cstring -> int
-
-ty print : fn cstring -> ()
-fn print x = let _ = puts x in ()
-
-ty main : fn -> int
-fn main = let _ = print "hi" in 0
diff --git a/users/grfn/achilles/default.nix b/users/grfn/achilles/default.nix
deleted file mode 100644
index 5245049d4a..0000000000
--- a/users/grfn/achilles/default.nix
+++ /dev/null
@@ -1,24 +0,0 @@
-{ depot, pkgs, ... }:
-
-let
-  llvmPackages = pkgs.llvmPackages_11;
-in
-
-depot.third_party.naersk.buildPackage {
-  src = ./.;
-
-  buildInputs = [
-    llvmPackages.clang
-    llvmPackages.llvm
-    llvmPackages.bintools
-    llvmPackages.libclang.lib
-  ] ++ (with pkgs; [
-    zlib
-    ncurses
-    libxml2
-    libffi
-    pkgconfig
-  ]);
-
-  doCheck = true;
-}
diff --git a/users/grfn/achilles/shell.nix b/users/grfn/achilles/shell.nix
deleted file mode 100644
index f32dce3ba3..0000000000
--- a/users/grfn/achilles/shell.nix
+++ /dev/null
@@ -1,18 +0,0 @@
-with (import ../../.. {}).third_party.nixpkgs;
-
-mkShell {
-  buildInputs = [
-    clang_11
-    llvm_11.lib
-    llvmPackages_11.bintools
-    llvmPackages_11.clang
-    llvmPackages_11.libclang.lib
-    zlib
-    ncurses
-    libxml2
-    libffi
-    pkg-config
-  ];
-
-  LLVM_SYS_110_PREFIX = llvmPackages_11.bintools;
-}
diff --git a/users/grfn/achilles/src/ast/hir.rs b/users/grfn/achilles/src/ast/hir.rs
deleted file mode 100644
index cdfaef567d..0000000000
--- a/users/grfn/achilles/src/ast/hir.rs
+++ /dev/null
@@ -1,364 +0,0 @@
-use std::collections::HashMap;
-
-use itertools::Itertools;
-
-use super::{BinaryOperator, Ident, Literal, UnaryOperator};
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum Pattern<'a, T> {
-    Id(Ident<'a>, T),
-    Tuple(Vec<Pattern<'a, T>>),
-}
-
-impl<'a, T> Pattern<'a, T> {
-    pub fn to_owned(&self) -> Pattern<'static, T>
-    where
-        T: Clone,
-    {
-        match self {
-            Pattern::Id(id, t) => Pattern::Id(id.to_owned(), t.clone()),
-            Pattern::Tuple(pats) => {
-                Pattern::Tuple(pats.into_iter().map(Pattern::to_owned).collect())
-            }
-        }
-    }
-
-    pub fn traverse_type<F, U, E>(self, f: F) -> Result<Pattern<'a, U>, E>
-    where
-        F: Fn(T) -> Result<U, E> + Clone,
-    {
-        match self {
-            Pattern::Id(id, t) => Ok(Pattern::Id(id, f(t)?)),
-            Pattern::Tuple(pats) => Ok(Pattern::Tuple(
-                pats.into_iter()
-                    .map(|pat| pat.traverse_type(f.clone()))
-                    .collect::<Result<Vec<_>, _>>()?,
-            )),
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct Binding<'a, T> {
-    pub pat: Pattern<'a, T>,
-    pub body: Expr<'a, T>,
-}
-
-impl<'a, T> Binding<'a, T> {
-    fn to_owned(&self) -> Binding<'static, T>
-    where
-        T: Clone,
-    {
-        Binding {
-            pat: self.pat.to_owned(),
-            body: self.body.to_owned(),
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum Expr<'a, T> {
-    Ident(Ident<'a>, T),
-
-    Literal(Literal<'a>, T),
-
-    Tuple(Vec<Expr<'a, T>>, T),
-
-    UnaryOp {
-        op: UnaryOperator,
-        rhs: Box<Expr<'a, T>>,
-        type_: T,
-    },
-
-    BinaryOp {
-        lhs: Box<Expr<'a, T>>,
-        op: BinaryOperator,
-        rhs: Box<Expr<'a, T>>,
-        type_: T,
-    },
-
-    Let {
-        bindings: Vec<Binding<'a, T>>,
-        body: Box<Expr<'a, T>>,
-        type_: T,
-    },
-
-    If {
-        condition: Box<Expr<'a, T>>,
-        then: Box<Expr<'a, T>>,
-        else_: Box<Expr<'a, T>>,
-        type_: T,
-    },
-
-    Fun {
-        type_args: Vec<Ident<'a>>,
-        args: Vec<(Ident<'a>, T)>,
-        body: Box<Expr<'a, T>>,
-        type_: T,
-    },
-
-    Call {
-        fun: Box<Expr<'a, T>>,
-        type_args: HashMap<Ident<'a>, T>,
-        args: Vec<Expr<'a, T>>,
-        type_: T,
-    },
-}
-
-impl<'a, T> Expr<'a, T> {
-    pub fn type_(&self) -> &T {
-        match self {
-            Expr::Ident(_, t) => t,
-            Expr::Literal(_, t) => t,
-            Expr::Tuple(_, t) => t,
-            Expr::UnaryOp { type_, .. } => type_,
-            Expr::BinaryOp { type_, .. } => type_,
-            Expr::Let { type_, .. } => type_,
-            Expr::If { type_, .. } => type_,
-            Expr::Fun { type_, .. } => type_,
-            Expr::Call { type_, .. } => type_,
-        }
-    }
-
-    pub fn traverse_type<F, U, E>(self, f: F) -> Result<Expr<'a, U>, E>
-    where
-        F: Fn(T) -> Result<U, E> + Clone,
-    {
-        match self {
-            Expr::Ident(id, t) => Ok(Expr::Ident(id, f(t)?)),
-            Expr::Literal(lit, t) => Ok(Expr::Literal(lit, f(t)?)),
-            Expr::UnaryOp { op, rhs, type_ } => Ok(Expr::UnaryOp {
-                op,
-                rhs: Box::new(rhs.traverse_type(f.clone())?),
-                type_: f(type_)?,
-            }),
-            Expr::BinaryOp {
-                lhs,
-                op,
-                rhs,
-                type_,
-            } => Ok(Expr::BinaryOp {
-                lhs: Box::new(lhs.traverse_type(f.clone())?),
-                op,
-                rhs: Box::new(rhs.traverse_type(f.clone())?),
-                type_: f(type_)?,
-            }),
-            Expr::Let {
-                bindings,
-                body,
-                type_,
-            } => Ok(Expr::Let {
-                bindings: bindings
-                    .into_iter()
-                    .map(|Binding { pat, body }| {
-                        Ok(Binding {
-                            pat: pat.traverse_type(f.clone())?,
-                            body: body.traverse_type(f.clone())?,
-                        })
-                    })
-                    .collect::<Result<Vec<_>, E>>()?,
-                body: Box::new(body.traverse_type(f.clone())?),
-                type_: f(type_)?,
-            }),
-            Expr::If {
-                condition,
-                then,
-                else_,
-                type_,
-            } => Ok(Expr::If {
-                condition: Box::new(condition.traverse_type(f.clone())?),
-                then: Box::new(then.traverse_type(f.clone())?),
-                else_: Box::new(else_.traverse_type(f.clone())?),
-                type_: f(type_)?,
-            }),
-            Expr::Fun {
-                args,
-                type_args,
-                body,
-                type_,
-            } => Ok(Expr::Fun {
-                args: args
-                    .into_iter()
-                    .map(|(id, t)| Ok((id, f.clone()(t)?)))
-                    .collect::<Result<Vec<_>, E>>()?,
-                type_args,
-                body: Box::new(body.traverse_type(f.clone())?),
-                type_: f(type_)?,
-            }),
-            Expr::Call {
-                fun,
-                type_args,
-                args,
-                type_,
-            } => Ok(Expr::Call {
-                fun: Box::new(fun.traverse_type(f.clone())?),
-                type_args: type_args
-                    .into_iter()
-                    .map(|(id, ty)| Ok((id, f.clone()(ty)?)))
-                    .collect::<Result<HashMap<_, _>, E>>()?,
-                args: args
-                    .into_iter()
-                    .map(|e| e.traverse_type(f.clone()))
-                    .collect::<Result<Vec<_>, E>>()?,
-                type_: f(type_)?,
-            }),
-            Expr::Tuple(members, t) => Ok(Expr::Tuple(
-                members
-                    .into_iter()
-                    .map(|t| t.traverse_type(f.clone()))
-                    .try_collect()?,
-                f(t)?,
-            )),
-        }
-    }
-
-    pub fn to_owned(&self) -> Expr<'static, T>
-    where
-        T: Clone,
-    {
-        match self {
-            Expr::Ident(id, t) => Expr::Ident(id.to_owned(), t.clone()),
-            Expr::Literal(lit, t) => Expr::Literal(lit.to_owned(), t.clone()),
-            Expr::UnaryOp { op, rhs, type_ } => Expr::UnaryOp {
-                op: *op,
-                rhs: Box::new((**rhs).to_owned()),
-                type_: type_.clone(),
-            },
-            Expr::BinaryOp {
-                lhs,
-                op,
-                rhs,
-                type_,
-            } => Expr::BinaryOp {
-                lhs: Box::new((**lhs).to_owned()),
-                op: *op,
-                rhs: Box::new((**rhs).to_owned()),
-                type_: type_.clone(),
-            },
-            Expr::Let {
-                bindings,
-                body,
-                type_,
-            } => Expr::Let {
-                bindings: bindings.iter().map(|b| b.to_owned()).collect(),
-                body: Box::new((**body).to_owned()),
-                type_: type_.clone(),
-            },
-            Expr::If {
-                condition,
-                then,
-                else_,
-                type_,
-            } => Expr::If {
-                condition: Box::new((**condition).to_owned()),
-                then: Box::new((**then).to_owned()),
-                else_: Box::new((**else_).to_owned()),
-                type_: type_.clone(),
-            },
-            Expr::Fun {
-                args,
-                type_args,
-                body,
-                type_,
-            } => Expr::Fun {
-                args: args
-                    .iter()
-                    .map(|(id, t)| (id.to_owned(), t.clone()))
-                    .collect(),
-                type_args: type_args.iter().map(|arg| arg.to_owned()).collect(),
-                body: Box::new((**body).to_owned()),
-                type_: type_.clone(),
-            },
-            Expr::Call {
-                fun,
-                type_args,
-                args,
-                type_,
-            } => Expr::Call {
-                fun: Box::new((**fun).to_owned()),
-                type_args: type_args
-                    .iter()
-                    .map(|(id, t)| (id.to_owned(), t.clone()))
-                    .collect(),
-                args: args.iter().map(|e| e.to_owned()).collect(),
-                type_: type_.clone(),
-            },
-            Expr::Tuple(members, t) => {
-                Expr::Tuple(members.into_iter().map(Expr::to_owned).collect(), t.clone())
-            }
-        }
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum Decl<'a, T> {
-    Fun {
-        name: Ident<'a>,
-        type_args: Vec<Ident<'a>>,
-        args: Vec<(Ident<'a>, T)>,
-        body: Box<Expr<'a, T>>,
-        type_: T,
-    },
-
-    Extern {
-        name: Ident<'a>,
-        arg_types: Vec<T>,
-        ret_type: T,
-    },
-}
-
-impl<'a, T> Decl<'a, T> {
-    pub fn name(&self) -> &Ident<'a> {
-        match self {
-            Decl::Fun { name, .. } => name,
-            Decl::Extern { name, .. } => name,
-        }
-    }
-
-    pub fn set_name(&mut self, new_name: Ident<'a>) {
-        match self {
-            Decl::Fun { name, .. } => *name = new_name,
-            Decl::Extern { name, .. } => *name = new_name,
-        }
-    }
-
-    pub fn type_(&self) -> Option<&T> {
-        match self {
-            Decl::Fun { type_, .. } => Some(type_),
-            Decl::Extern { .. } => None,
-        }
-    }
-
-    pub fn traverse_type<F, U, E>(self, f: F) -> Result<Decl<'a, U>, E>
-    where
-        F: Fn(T) -> Result<U, E> + Clone,
-    {
-        match self {
-            Decl::Fun {
-                name,
-                type_args,
-                args,
-                body,
-                type_,
-            } => Ok(Decl::Fun {
-                name,
-                type_args,
-                args: args
-                    .into_iter()
-                    .map(|(id, t)| Ok((id, f(t)?)))
-                    .try_collect()?,
-                body: Box::new(body.traverse_type(f.clone())?),
-                type_: f(type_)?,
-            }),
-            Decl::Extern {
-                name,
-                arg_types,
-                ret_type,
-            } => Ok(Decl::Extern {
-                name,
-                arg_types: arg_types.into_iter().map(f.clone()).try_collect()?,
-                ret_type: f(ret_type)?,
-            }),
-        }
-    }
-}
diff --git a/users/grfn/achilles/src/ast/mod.rs b/users/grfn/achilles/src/ast/mod.rs
deleted file mode 100644
index 5438d29d2c..0000000000
--- a/users/grfn/achilles/src/ast/mod.rs
+++ /dev/null
@@ -1,484 +0,0 @@
-pub(crate) mod hir;
-
-use std::borrow::Cow;
-use std::collections::HashMap;
-use std::convert::TryFrom;
-use std::fmt::{self, Display, Formatter};
-
-use itertools::Itertools;
-
-#[derive(Debug, PartialEq, Eq)]
-pub struct InvalidIdentifier<'a>(Cow<'a, str>);
-
-#[derive(Debug, PartialEq, Eq, Hash, Clone)]
-pub struct Ident<'a>(pub Cow<'a, str>);
-
-impl<'a> From<&'a Ident<'a>> for &'a str {
-    fn from(id: &'a Ident<'a>) -> Self {
-        id.0.as_ref()
-    }
-}
-
-impl<'a> Display for Ident<'a> {
-    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", self.0)
-    }
-}
-
-impl<'a> Ident<'a> {
-    pub fn to_owned(&self) -> Ident<'static> {
-        Ident(Cow::Owned(self.0.clone().into_owned()))
-    }
-
-    /// Construct an identifier from a &str without checking that it's a valid identifier
-    pub fn from_str_unchecked(s: &'a str) -> Self {
-        debug_assert!(is_valid_identifier(s));
-        Self(Cow::Borrowed(s))
-    }
-
-    pub fn from_string_unchecked(s: String) -> Self {
-        debug_assert!(is_valid_identifier(&s));
-        Self(Cow::Owned(s))
-    }
-}
-
-pub fn is_valid_identifier<S>(s: &S) -> bool
-where
-    S: AsRef<str> + ?Sized,
-{
-    s.as_ref()
-        .chars()
-        .any(|c| !c.is_alphanumeric() || !"_".contains(c))
-}
-
-impl<'a> TryFrom<&'a str> for Ident<'a> {
-    type Error = InvalidIdentifier<'a>;
-
-    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
-        if is_valid_identifier(s) {
-            Ok(Ident(Cow::Borrowed(s)))
-        } else {
-            Err(InvalidIdentifier(Cow::Borrowed(s)))
-        }
-    }
-}
-
-impl<'a> TryFrom<String> for Ident<'a> {
-    type Error = InvalidIdentifier<'static>;
-
-    fn try_from(s: String) -> Result<Self, Self::Error> {
-        if is_valid_identifier(&s) {
-            Ok(Ident(Cow::Owned(s)))
-        } else {
-            Err(InvalidIdentifier(Cow::Owned(s)))
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-pub enum BinaryOperator {
-    /// `+`
-    Add,
-
-    /// `-`
-    Sub,
-
-    /// `*`
-    Mul,
-
-    /// `/`
-    Div,
-
-    /// `^`
-    Pow,
-
-    /// `==`
-    Equ,
-
-    /// `!=`
-    Neq,
-}
-
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-pub enum UnaryOperator {
-    /// !
-    Not,
-
-    /// -
-    Neg,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum Literal<'a> {
-    Unit,
-    Int(u64),
-    Bool(bool),
-    String(Cow<'a, str>),
-}
-
-impl<'a> Literal<'a> {
-    pub fn to_owned(&self) -> Literal<'static> {
-        match self {
-            Literal::Int(i) => Literal::Int(*i),
-            Literal::Bool(b) => Literal::Bool(*b),
-            Literal::String(s) => Literal::String(Cow::Owned(s.clone().into_owned())),
-            Literal::Unit => Literal::Unit,
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum Pattern<'a> {
-    Id(Ident<'a>),
-    Tuple(Vec<Pattern<'a>>),
-}
-
-impl<'a> Pattern<'a> {
-    pub fn to_owned(&self) -> Pattern<'static> {
-        match self {
-            Pattern::Id(id) => Pattern::Id(id.to_owned()),
-            Pattern::Tuple(pats) => Pattern::Tuple(pats.iter().map(Pattern::to_owned).collect()),
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct Binding<'a> {
-    pub pat: Pattern<'a>,
-    pub type_: Option<Type<'a>>,
-    pub body: Expr<'a>,
-}
-
-impl<'a> Binding<'a> {
-    fn to_owned(&self) -> Binding<'static> {
-        Binding {
-            pat: self.pat.to_owned(),
-            type_: self.type_.as_ref().map(|t| t.to_owned()),
-            body: self.body.to_owned(),
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum Expr<'a> {
-    Ident(Ident<'a>),
-
-    Literal(Literal<'a>),
-
-    UnaryOp {
-        op: UnaryOperator,
-        rhs: Box<Expr<'a>>,
-    },
-
-    BinaryOp {
-        lhs: Box<Expr<'a>>,
-        op: BinaryOperator,
-        rhs: Box<Expr<'a>>,
-    },
-
-    Let {
-        bindings: Vec<Binding<'a>>,
-        body: Box<Expr<'a>>,
-    },
-
-    If {
-        condition: Box<Expr<'a>>,
-        then: Box<Expr<'a>>,
-        else_: Box<Expr<'a>>,
-    },
-
-    Fun(Box<Fun<'a>>),
-
-    Call {
-        fun: Box<Expr<'a>>,
-        args: Vec<Expr<'a>>,
-    },
-
-    Tuple(Vec<Expr<'a>>),
-
-    Ascription {
-        expr: Box<Expr<'a>>,
-        type_: Type<'a>,
-    },
-}
-
-impl<'a> Expr<'a> {
-    pub fn to_owned(&self) -> Expr<'static> {
-        match self {
-            Expr::Ident(ref id) => Expr::Ident(id.to_owned()),
-            Expr::Literal(ref lit) => Expr::Literal(lit.to_owned()),
-            Expr::Tuple(ref members) => {
-                Expr::Tuple(members.into_iter().map(Expr::to_owned).collect())
-            }
-            Expr::UnaryOp { op, rhs } => Expr::UnaryOp {
-                op: *op,
-                rhs: Box::new((**rhs).to_owned()),
-            },
-            Expr::BinaryOp { lhs, op, rhs } => Expr::BinaryOp {
-                lhs: Box::new((**lhs).to_owned()),
-                op: *op,
-                rhs: Box::new((**rhs).to_owned()),
-            },
-            Expr::Let { bindings, body } => Expr::Let {
-                bindings: bindings.iter().map(|binding| binding.to_owned()).collect(),
-                body: Box::new((**body).to_owned()),
-            },
-            Expr::If {
-                condition,
-                then,
-                else_,
-            } => Expr::If {
-                condition: Box::new((**condition).to_owned()),
-                then: Box::new((**then).to_owned()),
-                else_: Box::new((**else_).to_owned()),
-            },
-            Expr::Fun(fun) => Expr::Fun(Box::new((**fun).to_owned())),
-            Expr::Call { fun, args } => Expr::Call {
-                fun: Box::new((**fun).to_owned()),
-                args: args.iter().map(|arg| arg.to_owned()).collect(),
-            },
-            Expr::Ascription { expr, type_ } => Expr::Ascription {
-                expr: Box::new((**expr).to_owned()),
-                type_: type_.to_owned(),
-            },
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct Arg<'a> {
-    pub ident: Ident<'a>,
-    pub type_: Option<Type<'a>>,
-}
-
-impl<'a> Arg<'a> {
-    pub fn to_owned(&self) -> Arg<'static> {
-        Arg {
-            ident: self.ident.to_owned(),
-            type_: self.type_.as_ref().map(Type::to_owned),
-        }
-    }
-}
-
-impl<'a> TryFrom<&'a str> for Arg<'a> {
-    type Error = <Ident<'a> as TryFrom<&'a str>>::Error;
-
-    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
-        Ok(Arg {
-            ident: Ident::try_from(value)?,
-            type_: None,
-        })
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct Fun<'a> {
-    pub args: Vec<Arg<'a>>,
-    pub body: Expr<'a>,
-}
-
-impl<'a> Fun<'a> {
-    pub fn to_owned(&self) -> Fun<'static> {
-        Fun {
-            args: self.args.iter().map(|arg| arg.to_owned()).collect(),
-            body: self.body.to_owned(),
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum Decl<'a> {
-    Fun {
-        name: Ident<'a>,
-        body: Fun<'a>,
-    },
-    Ascription {
-        name: Ident<'a>,
-        type_: Type<'a>,
-    },
-    Extern {
-        name: Ident<'a>,
-        type_: FunctionType<'a>,
-    },
-}
-
-////
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct FunctionType<'a> {
-    pub args: Vec<Type<'a>>,
-    pub ret: Box<Type<'a>>,
-}
-
-impl<'a> FunctionType<'a> {
-    pub fn to_owned(&self) -> FunctionType<'static> {
-        FunctionType {
-            args: self.args.iter().map(|a| a.to_owned()).collect(),
-            ret: Box::new((*self.ret).to_owned()),
-        }
-    }
-}
-
-impl<'a> Display for FunctionType<'a> {
-    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        write!(f, "fn {} -> {}", self.args.iter().join(", "), self.ret)
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum Type<'a> {
-    Int,
-    Float,
-    Bool,
-    CString,
-    Unit,
-    Tuple(Vec<Type<'a>>),
-    Var(Ident<'a>),
-    Function(FunctionType<'a>),
-}
-
-impl<'a> Type<'a> {
-    pub fn to_owned(&self) -> Type<'static> {
-        match self {
-            Type::Int => Type::Int,
-            Type::Float => Type::Float,
-            Type::Bool => Type::Bool,
-            Type::CString => Type::CString,
-            Type::Unit => Type::Unit,
-            Type::Var(v) => Type::Var(v.to_owned()),
-            Type::Function(f) => Type::Function(f.to_owned()),
-            Type::Tuple(members) => Type::Tuple(members.iter().map(Type::to_owned).collect()),
-        }
-    }
-
-    pub fn alpha_equiv(&self, other: &Self) -> bool {
-        fn do_alpha_equiv<'a>(
-            substs: &mut HashMap<&'a Ident<'a>, &'a Ident<'a>>,
-            lhs: &'a Type,
-            rhs: &'a Type,
-        ) -> bool {
-            match (lhs, rhs) {
-                (Type::Var(v1), Type::Var(v2)) => substs.entry(v1).or_insert(v2) == &v2,
-                (
-                    Type::Function(FunctionType {
-                        args: args1,
-                        ret: ret1,
-                    }),
-                    Type::Function(FunctionType {
-                        args: args2,
-                        ret: ret2,
-                    }),
-                ) => {
-                    args1.len() == args2.len()
-                        && args1
-                            .iter()
-                            .zip(args2)
-                            .all(|(a1, a2)| do_alpha_equiv(substs, a1, a2))
-                        && do_alpha_equiv(substs, ret1, ret2)
-                }
-                _ => lhs == rhs,
-            }
-        }
-
-        let mut substs = HashMap::new();
-        do_alpha_equiv(&mut substs, self, other)
-    }
-
-    pub fn traverse_type_vars<'b, F>(self, mut f: F) -> Type<'b>
-    where
-        F: FnMut(Ident<'a>) -> Type<'b> + Clone,
-    {
-        match self {
-            Type::Var(tv) => f(tv),
-            Type::Function(FunctionType { args, ret }) => Type::Function(FunctionType {
-                args: args
-                    .into_iter()
-                    .map(|t| t.traverse_type_vars(f.clone()))
-                    .collect(),
-                ret: Box::new(ret.traverse_type_vars(f)),
-            }),
-            Type::Int => Type::Int,
-            Type::Float => Type::Float,
-            Type::Bool => Type::Bool,
-            Type::CString => Type::CString,
-            Type::Tuple(members) => Type::Tuple(
-                members
-                    .into_iter()
-                    .map(|t| t.traverse_type_vars(f.clone()))
-                    .collect(),
-            ),
-            Type::Unit => Type::Unit,
-        }
-    }
-
-    pub fn as_tuple(&self) -> Option<&Vec<Type<'a>>> {
-        if let Self::Tuple(v) = self {
-            Some(v)
-        } else {
-            None
-        }
-    }
-}
-
-impl<'a> Display for Type<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Type::Int => f.write_str("int"),
-            Type::Float => f.write_str("float"),
-            Type::Bool => f.write_str("bool"),
-            Type::CString => f.write_str("cstring"),
-            Type::Unit => f.write_str("()"),
-            Type::Var(v) => v.fmt(f),
-            Type::Function(ft) => ft.fmt(f),
-            Type::Tuple(ms) => write!(f, "({})", ms.iter().join(", ")),
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    fn type_var(n: &str) -> Type<'static> {
-        Type::Var(Ident::try_from(n.to_owned()).unwrap())
-    }
-
-    mod alpha_equiv {
-        use super::*;
-
-        #[test]
-        fn trivial() {
-            assert!(Type::Int.alpha_equiv(&Type::Int));
-            assert!(!Type::Int.alpha_equiv(&Type::Bool));
-        }
-
-        #[test]
-        fn simple_type_var() {
-            assert!(type_var("a").alpha_equiv(&type_var("b")));
-        }
-
-        #[test]
-        fn function_with_type_vars_equiv() {
-            assert!(Type::Function(FunctionType {
-                args: vec![type_var("a")],
-                ret: Box::new(type_var("b")),
-            })
-            .alpha_equiv(&Type::Function(FunctionType {
-                args: vec![type_var("b")],
-                ret: Box::new(type_var("a")),
-            })))
-        }
-
-        #[test]
-        fn function_with_type_vars_non_equiv() {
-            assert!(!Type::Function(FunctionType {
-                args: vec![type_var("a")],
-                ret: Box::new(type_var("a")),
-            })
-            .alpha_equiv(&Type::Function(FunctionType {
-                args: vec![type_var("b")],
-                ret: Box::new(type_var("a")),
-            })))
-        }
-    }
-}
diff --git a/users/grfn/achilles/src/codegen/llvm.rs b/users/grfn/achilles/src/codegen/llvm.rs
deleted file mode 100644
index 9a71ac954e..0000000000
--- a/users/grfn/achilles/src/codegen/llvm.rs
+++ /dev/null
@@ -1,486 +0,0 @@
-use std::convert::{TryFrom, TryInto};
-use std::path::Path;
-use std::result;
-
-use inkwell::basic_block::BasicBlock;
-use inkwell::builder::Builder;
-pub use inkwell::context::Context;
-use inkwell::module::Module;
-use inkwell::support::LLVMString;
-use inkwell::types::{BasicType, BasicTypeEnum, FunctionType, IntType, StructType};
-use inkwell::values::{AnyValueEnum, BasicValueEnum, FunctionValue, StructValue};
-use inkwell::{AddressSpace, IntPredicate};
-use itertools::Itertools;
-use thiserror::Error;
-
-use crate::ast::hir::{Binding, Decl, Expr, Pattern};
-use crate::ast::{BinaryOperator, Ident, Literal, Type, UnaryOperator};
-use crate::common::env::Env;
-
-#[derive(Debug, PartialEq, Eq, Error)]
-pub enum Error {
-    #[error("Undefined variable {0}")]
-    UndefinedVariable(Ident<'static>),
-
-    #[error("LLVM Error: {0}")]
-    LLVMError(String),
-}
-
-impl From<LLVMString> for Error {
-    fn from(s: LLVMString) -> Self {
-        Self::LLVMError(s.to_string())
-    }
-}
-
-pub type Result<T> = result::Result<T, Error>;
-
-pub struct Codegen<'ctx, 'ast> {
-    context: &'ctx Context,
-    pub module: Module<'ctx>,
-    builder: Builder<'ctx>,
-    env: Env<&'ast Ident<'ast>, AnyValueEnum<'ctx>>,
-    function_stack: Vec<FunctionValue<'ctx>>,
-    identifier_counter: u32,
-}
-
-impl<'ctx, 'ast> Codegen<'ctx, 'ast> {
-    pub fn new(context: &'ctx Context, module_name: &str) -> Self {
-        let module = context.create_module(module_name);
-        let builder = context.create_builder();
-        Self {
-            context,
-            module,
-            builder,
-            env: Default::default(),
-            function_stack: Default::default(),
-            identifier_counter: 0,
-        }
-    }
-
-    pub fn new_function<'a>(
-        &'a mut self,
-        name: &str,
-        ty: FunctionType<'ctx>,
-    ) -> &'a FunctionValue<'ctx> {
-        self.function_stack
-            .push(self.module.add_function(name, ty, None));
-        let basic_block = self.append_basic_block("entry");
-        self.builder.position_at_end(basic_block);
-        self.function_stack.last().unwrap()
-    }
-
-    pub fn finish_function(&mut self, res: Option<&BasicValueEnum<'ctx>>) -> FunctionValue<'ctx> {
-        self.builder.build_return(match res {
-            // lol
-            Some(val) => Some(val),
-            None => None,
-        });
-        self.function_stack.pop().unwrap()
-    }
-
-    pub fn append_basic_block(&self, name: &str) -> BasicBlock<'ctx> {
-        self.context
-            .append_basic_block(*self.function_stack.last().unwrap(), name)
-    }
-
-    fn bind_pattern(&mut self, pat: &'ast Pattern<'ast, Type>, val: AnyValueEnum<'ctx>) {
-        match pat {
-            Pattern::Id(id, _) => self.env.set(id, val),
-            Pattern::Tuple(pats) => {
-                for (i, pat) in pats.iter().enumerate() {
-                    let member = self
-                        .builder
-                        .build_extract_value(
-                            StructValue::try_from(val).unwrap(),
-                            i as _,
-                            "pat_bind",
-                        )
-                        .unwrap();
-                    self.bind_pattern(pat, member.into());
-                }
-            }
-        }
-    }
-
-    pub fn codegen_expr(
-        &mut self,
-        expr: &'ast Expr<'ast, Type>,
-    ) -> Result<Option<AnyValueEnum<'ctx>>> {
-        match expr {
-            Expr::Ident(id, _) => self
-                .env
-                .resolve(id)
-                .cloned()
-                .ok_or_else(|| Error::UndefinedVariable(id.to_owned()))
-                .map(Some),
-            Expr::Literal(lit, ty) => {
-                let ty = self.codegen_int_type(ty);
-                match lit {
-                    Literal::Int(i) => Ok(Some(AnyValueEnum::IntValue(ty.const_int(*i, false)))),
-                    Literal::Bool(b) => Ok(Some(AnyValueEnum::IntValue(
-                        ty.const_int(if *b { 1 } else { 0 }, false),
-                    ))),
-                    Literal::String(s) => Ok(Some(
-                        self.builder
-                            .build_global_string_ptr(s, "s")
-                            .as_pointer_value()
-                            .into(),
-                    )),
-                    Literal::Unit => Ok(None),
-                }
-            }
-            Expr::UnaryOp { op, rhs, .. } => {
-                let rhs = self.codegen_expr(rhs)?.unwrap();
-                match op {
-                    UnaryOperator::Not => unimplemented!(),
-                    UnaryOperator::Neg => Ok(Some(AnyValueEnum::IntValue(
-                        self.builder.build_int_neg(rhs.into_int_value(), "neg"),
-                    ))),
-                }
-            }
-            Expr::BinaryOp { lhs, op, rhs, .. } => {
-                let lhs = self.codegen_expr(lhs)?.unwrap();
-                let rhs = self.codegen_expr(rhs)?.unwrap();
-                match op {
-                    BinaryOperator::Add => {
-                        Ok(Some(AnyValueEnum::IntValue(self.builder.build_int_add(
-                            lhs.into_int_value(),
-                            rhs.into_int_value(),
-                            "add",
-                        ))))
-                    }
-                    BinaryOperator::Sub => {
-                        Ok(Some(AnyValueEnum::IntValue(self.builder.build_int_sub(
-                            lhs.into_int_value(),
-                            rhs.into_int_value(),
-                            "add",
-                        ))))
-                    }
-                    BinaryOperator::Mul => {
-                        Ok(Some(AnyValueEnum::IntValue(self.builder.build_int_sub(
-                            lhs.into_int_value(),
-                            rhs.into_int_value(),
-                            "add",
-                        ))))
-                    }
-                    BinaryOperator::Div => Ok(Some(AnyValueEnum::IntValue(
-                        self.builder.build_int_signed_div(
-                            lhs.into_int_value(),
-                            rhs.into_int_value(),
-                            "add",
-                        ),
-                    ))),
-                    BinaryOperator::Pow => unimplemented!(),
-                    BinaryOperator::Equ => Ok(Some(AnyValueEnum::IntValue(
-                        self.builder.build_int_compare(
-                            IntPredicate::EQ,
-                            lhs.into_int_value(),
-                            rhs.into_int_value(),
-                            "eq",
-                        ),
-                    ))),
-                    BinaryOperator::Neq => todo!(),
-                }
-            }
-            Expr::Let { bindings, body, .. } => {
-                self.env.push();
-                for Binding { pat, body, .. } in bindings {
-                    if let Some(val) = self.codegen_expr(body)? {
-                        self.bind_pattern(pat, val);
-                    }
-                }
-                let res = self.codegen_expr(body);
-                self.env.pop();
-                res
-            }
-            Expr::If {
-                condition,
-                then,
-                else_,
-                type_,
-            } => {
-                let then_block = self.append_basic_block("then");
-                let else_block = self.append_basic_block("else");
-                let join_block = self.append_basic_block("join");
-                let condition = self.codegen_expr(condition)?.unwrap();
-                self.builder.build_conditional_branch(
-                    condition.into_int_value(),
-                    then_block,
-                    else_block,
-                );
-                self.builder.position_at_end(then_block);
-                let then_res = self.codegen_expr(then)?;
-                self.builder.build_unconditional_branch(join_block);
-
-                self.builder.position_at_end(else_block);
-                let else_res = self.codegen_expr(else_)?;
-                self.builder.build_unconditional_branch(join_block);
-
-                self.builder.position_at_end(join_block);
-                if let Some(phi_type) = self.codegen_type(type_) {
-                    let phi = self.builder.build_phi(phi_type, "join");
-                    phi.add_incoming(&[
-                        (
-                            &BasicValueEnum::try_from(then_res.unwrap()).unwrap(),
-                            then_block,
-                        ),
-                        (
-                            &BasicValueEnum::try_from(else_res.unwrap()).unwrap(),
-                            else_block,
-                        ),
-                    ]);
-                    Ok(Some(phi.as_basic_value().into()))
-                } else {
-                    Ok(None)
-                }
-            }
-            Expr::Call { fun, args, .. } => {
-                if let Expr::Ident(id, _) = &**fun {
-                    let function = self
-                        .module
-                        .get_function(id.into())
-                        .or_else(|| self.env.resolve(id)?.clone().try_into().ok())
-                        .ok_or_else(|| Error::UndefinedVariable(id.to_owned()))?;
-                    let args = args
-                        .iter()
-                        .map(|arg| Ok(self.codegen_expr(arg)?.unwrap().try_into().unwrap()))
-                        .collect::<Result<Vec<_>>>()?;
-                    Ok(self
-                        .builder
-                        .build_call(function, &args, "call")
-                        .try_as_basic_value()
-                        .left()
-                        .map(|val| val.into()))
-                } else {
-                    todo!()
-                }
-            }
-            Expr::Fun { args, body, .. } => {
-                let fname = self.fresh_ident("f");
-                let cur_block = self.builder.get_insert_block().unwrap();
-                let env = self.env.save(); // TODO: closures
-                let function = self.codegen_function(&fname, args, body)?;
-                self.builder.position_at_end(cur_block);
-                self.env.restore(env);
-                Ok(Some(function.into()))
-            }
-            Expr::Tuple(members, ty) => {
-                let values = members
-                    .into_iter()
-                    .map(|expr| self.codegen_expr(expr))
-                    .collect::<Result<Vec<_>>>()?
-                    .into_iter()
-                    .filter_map(|x| x)
-                    .map(|x| x.try_into().unwrap())
-                    .collect_vec();
-                let field_types = ty.as_tuple().unwrap();
-                let tuple_type = self.codegen_tuple_type(field_types);
-                Ok(Some(tuple_type.const_named_struct(&values).into()))
-            }
-        }
-    }
-
-    pub fn codegen_function(
-        &mut self,
-        name: &str,
-        args: &'ast [(Ident<'ast>, Type)],
-        body: &'ast Expr<'ast, Type>,
-    ) -> Result<FunctionValue<'ctx>> {
-        let arg_types = args
-            .iter()
-            .filter_map(|(_, at)| self.codegen_type(at))
-            .collect::<Vec<_>>();
-
-        self.new_function(
-            name,
-            match self.codegen_type(body.type_()) {
-                Some(ret_ty) => ret_ty.fn_type(&arg_types, false),
-                None => self.context.void_type().fn_type(&arg_types, false),
-            },
-        );
-        self.env.push();
-        for (i, (arg, _)) in args.iter().enumerate() {
-            self.env.set(
-                arg,
-                self.cur_function().get_nth_param(i as u32).unwrap().into(),
-            );
-        }
-        let res = self.codegen_expr(body)?;
-        self.env.pop();
-        Ok(self.finish_function(res.map(|av| av.try_into().unwrap()).as_ref()))
-    }
-
-    pub fn codegen_extern(
-        &mut self,
-        name: &str,
-        args: &'ast [Type],
-        ret: &'ast Type,
-    ) -> Result<()> {
-        let arg_types = args
-            .iter()
-            .map(|t| self.codegen_type(t).unwrap())
-            .collect::<Vec<_>>();
-        self.module.add_function(
-            name,
-            match self.codegen_type(ret) {
-                Some(ret_ty) => ret_ty.fn_type(&arg_types, false),
-                None => self.context.void_type().fn_type(&arg_types, false),
-            },
-            None,
-        );
-        Ok(())
-    }
-
-    pub fn codegen_decl(&mut self, decl: &'ast Decl<'ast, Type>) -> Result<()> {
-        match decl {
-            Decl::Fun {
-                name, args, body, ..
-            } => {
-                self.codegen_function(name.into(), args, body)?;
-                Ok(())
-            }
-            Decl::Extern {
-                name,
-                arg_types,
-                ret_type,
-            } => self.codegen_extern(name.into(), arg_types, ret_type),
-        }
-    }
-
-    pub fn codegen_main(&mut self, expr: &'ast Expr<'ast, Type>) -> Result<()> {
-        self.new_function("main", self.context.i64_type().fn_type(&[], false));
-        let res = self.codegen_expr(expr)?;
-        if *expr.type_() != Type::Int {
-            self.builder
-                .build_return(Some(&self.context.i64_type().const_int(0, false)));
-        } else {
-            self.finish_function(res.map(|r| r.try_into().unwrap()).as_ref());
-        }
-        Ok(())
-    }
-
-    fn codegen_type(&self, type_: &'ast Type) -> Option<BasicTypeEnum<'ctx>> {
-        // TODO
-        match type_ {
-            Type::Int => Some(self.context.i64_type().into()),
-            Type::Float => Some(self.context.f64_type().into()),
-            Type::Bool => Some(self.context.bool_type().into()),
-            Type::CString => Some(
-                self.context
-                    .i8_type()
-                    .ptr_type(AddressSpace::Generic)
-                    .into(),
-            ),
-            Type::Function(_) => todo!(),
-            Type::Var(_) => unreachable!(),
-            Type::Unit => None,
-            Type::Tuple(ts) => Some(self.codegen_tuple_type(ts).into()),
-        }
-    }
-
-    fn codegen_tuple_type(&self, ts: &'ast [Type]) -> StructType<'ctx> {
-        self.context.struct_type(
-            ts.iter()
-                .filter_map(|t| self.codegen_type(t))
-                .collect_vec()
-                .as_slice(),
-            false,
-        )
-    }
-
-    fn codegen_int_type(&self, type_: &'ast Type) -> IntType<'ctx> {
-        // TODO
-        self.context.i64_type()
-    }
-
-    pub fn print_to_file<P>(&self, path: P) -> Result<()>
-    where
-        P: AsRef<Path>,
-    {
-        Ok(self.module.print_to_file(path)?)
-    }
-
-    pub fn binary_to_file<P>(&self, path: P) -> Result<()>
-    where
-        P: AsRef<Path>,
-    {
-        if self.module.write_bitcode_to_path(path.as_ref()) {
-            Ok(())
-        } else {
-            Err(Error::LLVMError(
-                "Error writing bitcode to output path".to_owned(),
-            ))
-        }
-    }
-
-    fn fresh_ident(&mut self, prefix: &str) -> String {
-        self.identifier_counter += 1;
-        format!("{}{}", prefix, self.identifier_counter)
-    }
-
-    fn cur_function(&self) -> &FunctionValue<'ctx> {
-        self.function_stack.last().unwrap()
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use inkwell::execution_engine::JitFunction;
-    use inkwell::OptimizationLevel;
-
-    use super::*;
-
-    fn jit_eval<T>(expr: &str) -> anyhow::Result<T> {
-        let expr = crate::parser::expr(expr).unwrap().1;
-
-        let expr = crate::tc::typecheck_expr(expr).unwrap();
-
-        let context = Context::create();
-        let mut codegen = Codegen::new(&context, "test");
-        let execution_engine = codegen
-            .module
-            .create_jit_execution_engine(OptimizationLevel::None)
-            .unwrap();
-
-        codegen.codegen_function("test", &[], &expr)?;
-
-        unsafe {
-            let fun: JitFunction<unsafe extern "C" fn() -> T> =
-                execution_engine.get_function("test")?;
-            Ok(fun.call())
-        }
-    }
-
-    #[test]
-    fn add_literals() {
-        assert_eq!(jit_eval::<i64>("1 + 2").unwrap(), 3);
-    }
-
-    #[test]
-    fn variable_shadowing() {
-        assert_eq!(
-            jit_eval::<i64>("let x = 1 in (let x = 2 in x) + x").unwrap(),
-            3
-        );
-    }
-
-    #[test]
-    fn eq() {
-        assert_eq!(
-            jit_eval::<i64>("let x = 1 in if x == 1 then 2 else 4").unwrap(),
-            2
-        );
-    }
-
-    #[test]
-    fn function_call() {
-        let res = jit_eval::<i64>("let id = fn x = x in id 1").unwrap();
-        assert_eq!(res, 1);
-    }
-
-    #[test]
-    fn bind_tuple_pattern() {
-        let res = jit_eval::<i64>("let (x, y) = (1, 2) in x + y").unwrap();
-        assert_eq!(res, 3);
-    }
-}
diff --git a/users/grfn/achilles/src/codegen/mod.rs b/users/grfn/achilles/src/codegen/mod.rs
deleted file mode 100644
index 8ef057dba0..0000000000
--- a/users/grfn/achilles/src/codegen/mod.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-pub mod llvm;
-
-use inkwell::execution_engine::JitFunction;
-use inkwell::OptimizationLevel;
-pub use llvm::*;
-
-use crate::ast::hir::Expr;
-use crate::ast::Type;
-use crate::common::Result;
-
-pub fn jit_eval<T>(expr: &Expr<Type>) -> Result<T> {
-    let context = Context::create();
-    let mut codegen = Codegen::new(&context, "eval");
-    let execution_engine = codegen
-        .module
-        .create_jit_execution_engine(OptimizationLevel::None)
-        .map_err(Error::from)?;
-    codegen.codegen_function("test", &[], &expr)?;
-
-    unsafe {
-        let fun: JitFunction<unsafe extern "C" fn() -> T> =
-            execution_engine.get_function("eval").unwrap();
-        Ok(fun.call())
-    }
-}
diff --git a/users/grfn/achilles/src/commands/check.rs b/users/grfn/achilles/src/commands/check.rs
deleted file mode 100644
index 0bea482c14..0000000000
--- a/users/grfn/achilles/src/commands/check.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-use clap::Clap;
-use std::path::PathBuf;
-
-use crate::ast::Type;
-use crate::{parser, tc, Result};
-
-/// Typecheck a file or expression
-#[derive(Clap)]
-pub struct Check {
-    /// File to check
-    path: Option<PathBuf>,
-
-    /// Expression to check
-    #[clap(long, short = 'e')]
-    expr: Option<String>,
-}
-
-fn run_expr(expr: String) -> Result<Type<'static>> {
-    let (_, parsed) = parser::expr(&expr)?;
-    let hir_expr = tc::typecheck_expr(parsed)?;
-    Ok(hir_expr.type_().to_owned())
-}
-
-fn run_path(path: PathBuf) -> Result<Type<'static>> {
-    todo!()
-}
-
-impl Check {
-    pub fn run(self) -> Result<()> {
-        let type_ = match (self.path, self.expr) {
-            (None, None) => Err("Must specify either a file or expression to check".into()),
-            (Some(_), Some(_)) => Err("Cannot specify both a file and expression to check".into()),
-            (None, Some(expr)) => run_expr(expr),
-            (Some(path), None) => run_path(path),
-        }?;
-        println!("type: {}", type_);
-        Ok(())
-    }
-}
diff --git a/users/grfn/achilles/src/commands/compile.rs b/users/grfn/achilles/src/commands/compile.rs
deleted file mode 100644
index be8767575a..0000000000
--- a/users/grfn/achilles/src/commands/compile.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-use std::path::PathBuf;
-
-use clap::Clap;
-
-use crate::common::Result;
-use crate::compiler::{self, CompilerOptions};
-
-/// Compile a source file
-#[derive(Clap)]
-pub struct Compile {
-    /// File to compile
-    file: PathBuf,
-
-    /// Output file
-    #[clap(short = 'o')]
-    out_file: PathBuf,
-
-    #[clap(flatten)]
-    options: CompilerOptions,
-}
-
-impl Compile {
-    pub fn run(self) -> Result<()> {
-        eprintln!(
-            ">>> {} -> {}",
-            &self.file.to_string_lossy(),
-            self.out_file.to_string_lossy()
-        );
-        compiler::compile_file(&self.file, &self.out_file, &self.options)
-    }
-}
diff --git a/users/grfn/achilles/src/commands/eval.rs b/users/grfn/achilles/src/commands/eval.rs
deleted file mode 100644
index 61a712c08a..0000000000
--- a/users/grfn/achilles/src/commands/eval.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use clap::Clap;
-
-use crate::codegen;
-use crate::interpreter;
-use crate::parser;
-use crate::tc;
-use crate::Result;
-
-/// Evaluate an expression and print its result
-#[derive(Clap)]
-pub struct Eval {
-    /// JIT-compile with LLVM instead of interpreting
-    #[clap(long)]
-    jit: bool,
-
-    /// Expression to evaluate
-    expr: String,
-}
-
-impl Eval {
-    pub fn run(self) -> Result<()> {
-        let (_, parsed) = parser::expr(&self.expr)?;
-        let hir = tc::typecheck_expr(parsed)?;
-        let result = if self.jit {
-            codegen::jit_eval::<i64>(&hir)?.into()
-        } else {
-            interpreter::eval(&hir)?
-        };
-        println!("{}", result);
-        Ok(())
-    }
-}
diff --git a/users/grfn/achilles/src/commands/mod.rs b/users/grfn/achilles/src/commands/mod.rs
deleted file mode 100644
index fd0a822708..0000000000
--- a/users/grfn/achilles/src/commands/mod.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-pub mod check;
-pub mod compile;
-pub mod eval;
-
-pub use check::Check;
-pub use compile::Compile;
-pub use eval::Eval;
diff --git a/users/grfn/achilles/src/common/env.rs b/users/grfn/achilles/src/common/env.rs
deleted file mode 100644
index 59a5e46c46..0000000000
--- a/users/grfn/achilles/src/common/env.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use std::borrow::Borrow;
-use std::collections::HashMap;
-use std::hash::Hash;
-use std::mem;
-
-/// A lexical environment
-#[derive(Debug, PartialEq, Eq)]
-pub struct Env<K: Eq + Hash, V>(Vec<HashMap<K, V>>);
-
-impl<K, V> Default for Env<K, V>
-where
-    K: Eq + Hash,
-{
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-impl<K, V> Env<K, V>
-where
-    K: Eq + Hash,
-{
-    pub fn new() -> Self {
-        Self(vec![Default::default()])
-    }
-
-    pub fn push(&mut self) {
-        self.0.push(Default::default());
-    }
-
-    pub fn pop(&mut self) {
-        self.0.pop();
-    }
-
-    pub fn save(&mut self) -> Self {
-        mem::take(self)
-    }
-
-    pub fn restore(&mut self, saved: Self) {
-        *self = saved;
-    }
-
-    pub fn set(&mut self, k: K, v: V) {
-        self.0.last_mut().unwrap().insert(k, v);
-    }
-
-    pub fn resolve<'a, Q>(&'a self, k: &Q) -> Option<&'a V>
-    where
-        K: Borrow<Q>,
-        Q: Hash + Eq + ?Sized,
-    {
-        for ctx in self.0.iter().rev() {
-            if let Some(res) = ctx.get(k) {
-                return Some(res);
-            }
-        }
-        None
-    }
-}
diff --git a/users/grfn/achilles/src/common/error.rs b/users/grfn/achilles/src/common/error.rs
deleted file mode 100644
index 51575a895e..0000000000
--- a/users/grfn/achilles/src/common/error.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use std::{io, result};
-
-use thiserror::Error;
-
-use crate::{codegen, interpreter, parser, tc};
-
-#[derive(Error, Debug)]
-pub enum Error {
-    #[error(transparent)]
-    IOError(#[from] io::Error),
-
-    #[error("Error parsing input: {0}")]
-    ParseError(#[from] parser::Error),
-
-    #[error("Error evaluating expression: {0}")]
-    EvalError(#[from] interpreter::Error),
-
-    #[error("Compile error: {0}")]
-    CodegenError(#[from] codegen::Error),
-
-    #[error("Type error: {0}")]
-    TypeError(#[from] tc::Error),
-
-    #[error("{0}")]
-    Message(String),
-}
-
-impl From<String> for Error {
-    fn from(s: String) -> Self {
-        Self::Message(s)
-    }
-}
-
-impl<'a> From<&'a str> for Error {
-    fn from(s: &'a str) -> Self {
-        Self::Message(s.to_owned())
-    }
-}
-
-impl<'a> From<nom::Err<nom::error::Error<&'a str>>> for Error {
-    fn from(e: nom::Err<nom::error::Error<&'a str>>) -> Self {
-        use nom::error::Error as NomError;
-        use nom::Err::*;
-
-        Self::ParseError(match e {
-            Incomplete(i) => Incomplete(i),
-            Error(NomError { input, code }) => Error(NomError {
-                input: input.to_owned(),
-                code,
-            }),
-            Failure(NomError { input, code }) => Failure(NomError {
-                input: input.to_owned(),
-                code,
-            }),
-        })
-    }
-}
-
-pub type Result<T> = result::Result<T, Error>;
diff --git a/users/grfn/achilles/src/common/mod.rs b/users/grfn/achilles/src/common/mod.rs
deleted file mode 100644
index 8368a6dd18..0000000000
--- a/users/grfn/achilles/src/common/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-pub(crate) mod env;
-pub(crate) mod error;
-pub(crate) mod namer;
-
-pub use error::{Error, Result};
-pub use namer::{Namer, NamerOf};
diff --git a/users/grfn/achilles/src/common/namer.rs b/users/grfn/achilles/src/common/namer.rs
deleted file mode 100644
index 016e9f6ed9..0000000000
--- a/users/grfn/achilles/src/common/namer.rs
+++ /dev/null
@@ -1,122 +0,0 @@
-use std::fmt::Display;
-use std::marker::PhantomData;
-
-pub struct Namer<T, F> {
-    make_name: F,
-    counter: u64,
-    _phantom: PhantomData<T>,
-}
-
-impl<T, F> Namer<T, F> {
-    pub fn new(make_name: F) -> Self {
-        Namer {
-            make_name,
-            counter: 0,
-            _phantom: PhantomData,
-        }
-    }
-}
-
-impl Namer<String, Box<dyn Fn(u64) -> String>> {
-    pub fn with_prefix<T>(prefix: T) -> Self
-    where
-        T: Display + 'static,
-    {
-        Namer::new(move |i| format!("{}{}", prefix, i)).boxed()
-    }
-
-    pub fn with_suffix<T>(suffix: T) -> Self
-    where
-        T: Display + 'static,
-    {
-        Namer::new(move |i| format!("{}{}", i, suffix)).boxed()
-    }
-
-    pub fn alphabetic() -> Self {
-        Namer::new(|i| {
-            if i <= 26 {
-                std::char::from_u32((i + 96) as u32).unwrap().to_string()
-            } else {
-                format!(
-                    "{}{}",
-                    std::char::from_u32(((i % 26) + 96) as u32).unwrap(),
-                    i - 26
-                )
-            }
-        })
-        .boxed()
-    }
-}
-
-impl<T, F> Namer<T, F>
-where
-    F: Fn(u64) -> T,
-{
-    pub fn make_name(&mut self) -> T {
-        self.counter += 1;
-        (self.make_name)(self.counter)
-    }
-
-    pub fn boxed(self) -> NamerOf<T>
-    where
-        F: 'static,
-    {
-        Namer {
-            make_name: Box::new(self.make_name),
-            counter: self.counter,
-            _phantom: self._phantom,
-        }
-    }
-
-    pub fn map<G, U>(self, f: G) -> NamerOf<U>
-    where
-        G: Fn(T) -> U + 'static,
-        T: 'static,
-        F: 'static,
-    {
-        Namer {
-            counter: self.counter,
-            make_name: Box::new(move |x| f((self.make_name)(x))),
-            _phantom: PhantomData,
-        }
-    }
-}
-
-pub type NamerOf<T> = Namer<T, Box<dyn Fn(u64) -> T>>;
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn prefix() {
-        let mut namer = Namer::with_prefix("t");
-        assert_eq!(namer.make_name(), "t1");
-        assert_eq!(namer.make_name(), "t2");
-    }
-
-    #[test]
-    fn suffix() {
-        let mut namer = Namer::with_suffix("t");
-        assert_eq!(namer.make_name(), "1t");
-        assert_eq!(namer.make_name(), "2t");
-    }
-
-    #[test]
-    fn alphabetic() {
-        let mut namer = Namer::alphabetic();
-        assert_eq!(namer.make_name(), "a");
-        assert_eq!(namer.make_name(), "b");
-        (0..25).for_each(|_| {
-            namer.make_name();
-        });
-        assert_eq!(namer.make_name(), "b2");
-    }
-
-    #[test]
-    fn custom_callback() {
-        let mut namer = Namer::new(|n| n + 1);
-        assert_eq!(namer.make_name(), 2);
-        assert_eq!(namer.make_name(), 3);
-    }
-}
diff --git a/users/grfn/achilles/src/compiler.rs b/users/grfn/achilles/src/compiler.rs
deleted file mode 100644
index 45b215473d..0000000000
--- a/users/grfn/achilles/src/compiler.rs
+++ /dev/null
@@ -1,89 +0,0 @@
-use std::fmt::{self, Display};
-use std::path::Path;
-use std::str::FromStr;
-use std::{fs, result};
-
-use clap::Clap;
-use test_strategy::Arbitrary;
-
-use crate::codegen::{self, Codegen};
-use crate::common::Result;
-use crate::passes::hir::{monomorphize, strip_positive_units};
-use crate::{parser, tc};
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Arbitrary)]
-pub enum OutputFormat {
-    LLVM,
-    Bitcode,
-}
-
-impl Default for OutputFormat {
-    fn default() -> Self {
-        Self::Bitcode
-    }
-}
-
-impl FromStr for OutputFormat {
-    type Err = String;
-
-    fn from_str(s: &str) -> result::Result<Self, Self::Err> {
-        match s {
-            "llvm" => Ok(Self::LLVM),
-            "binary" => Ok(Self::Bitcode),
-            _ => Err(format!(
-                "Invalid output format {}, expected one of {{llvm, binary}}",
-                s
-            )),
-        }
-    }
-}
-
-impl Display for OutputFormat {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            OutputFormat::LLVM => f.write_str("llvm"),
-            OutputFormat::Bitcode => f.write_str("binary"),
-        }
-    }
-}
-
-#[derive(Clap, Debug, PartialEq, Eq, Default)]
-pub struct CompilerOptions {
-    #[clap(long, short = 'f', default_value)]
-    format: OutputFormat,
-}
-
-pub fn compile_file(input: &Path, output: &Path, options: &CompilerOptions) -> Result<()> {
-    let src = fs::read_to_string(input)?;
-    let (_, decls) = parser::toplevel(&src)?;
-    let mut decls = tc::typecheck_toplevel(decls)?;
-    monomorphize::run_toplevel(&mut decls);
-    strip_positive_units::run_toplevel(&mut decls);
-
-    let context = codegen::Context::create();
-    let mut codegen = Codegen::new(
-        &context,
-        &input
-            .file_stem()
-            .map_or("UNKNOWN".to_owned(), |s| s.to_string_lossy().into_owned()),
-    );
-    for decl in &decls {
-        codegen.codegen_decl(decl)?;
-    }
-    match options.format {
-        OutputFormat::LLVM => codegen.print_to_file(output)?,
-        OutputFormat::Bitcode => codegen.binary_to_file(output)?,
-    }
-    Ok(())
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use test_strategy::proptest;
-
-    #[proptest]
-    fn output_format_display_from_str_round_trip(of: OutputFormat) {
-        assert_eq!(OutputFormat::from_str(&of.to_string()), Ok(of));
-    }
-}
diff --git a/users/grfn/achilles/src/interpreter/error.rs b/users/grfn/achilles/src/interpreter/error.rs
deleted file mode 100644
index 268d6f479a..0000000000
--- a/users/grfn/achilles/src/interpreter/error.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-use std::result;
-
-use thiserror::Error;
-
-use crate::ast::{Ident, Type};
-
-#[derive(Debug, PartialEq, Eq, Error)]
-pub enum Error {
-    #[error("Undefined variable {0}")]
-    UndefinedVariable(Ident<'static>),
-
-    #[error("Unexpected type {actual}, expected type {expected}")]
-    InvalidType {
-        actual: Type<'static>,
-        expected: Type<'static>,
-    },
-}
-
-pub type Result<T> = result::Result<T, Error>;
diff --git a/users/grfn/achilles/src/interpreter/mod.rs b/users/grfn/achilles/src/interpreter/mod.rs
deleted file mode 100644
index 70df7a0724..0000000000
--- a/users/grfn/achilles/src/interpreter/mod.rs
+++ /dev/null
@@ -1,203 +0,0 @@
-mod error;
-mod value;
-
-use itertools::Itertools;
-use value::Val;
-
-pub use self::error::{Error, Result};
-pub use self::value::{Function, Value};
-use crate::ast::hir::{Binding, Expr, Pattern};
-use crate::ast::{BinaryOperator, FunctionType, Ident, Literal, Type, UnaryOperator};
-use crate::common::env::Env;
-
-#[derive(Debug, Default)]
-pub struct Interpreter<'a> {
-    env: Env<&'a Ident<'a>, Value<'a>>,
-}
-
-impl<'a> Interpreter<'a> {
-    pub fn new() -> Self {
-        Self::default()
-    }
-
-    fn resolve(&self, var: &'a Ident<'a>) -> Result<Value<'a>> {
-        self.env
-            .resolve(var)
-            .cloned()
-            .ok_or_else(|| Error::UndefinedVariable(var.to_owned()))
-    }
-
-    fn bind_pattern(&mut self, pattern: &'a Pattern<'a, Type>, value: Value<'a>) {
-        match pattern {
-            Pattern::Id(id, _) => self.env.set(id, value),
-            Pattern::Tuple(pats) => {
-                for (pat, val) in pats.iter().zip(value.as_tuple().unwrap().clone()) {
-                    self.bind_pattern(pat, val);
-                }
-            }
-        }
-    }
-
-    pub fn eval(&mut self, expr: &'a Expr<'a, Type>) -> Result<Value<'a>> {
-        let res = match expr {
-            Expr::Ident(id, _) => self.resolve(id),
-            Expr::Literal(Literal::Int(i), _) => Ok((*i).into()),
-            Expr::Literal(Literal::Bool(b), _) => Ok((*b).into()),
-            Expr::Literal(Literal::String(s), _) => Ok(s.clone().into()),
-            Expr::Literal(Literal::Unit, _) => unreachable!(),
-            Expr::UnaryOp { op, rhs, .. } => {
-                let rhs = self.eval(rhs)?;
-                match op {
-                    UnaryOperator::Neg => -rhs,
-                    _ => unimplemented!(),
-                }
-            }
-            Expr::BinaryOp { lhs, op, rhs, .. } => {
-                let lhs = self.eval(lhs)?;
-                let rhs = self.eval(rhs)?;
-                match op {
-                    BinaryOperator::Add => lhs + rhs,
-                    BinaryOperator::Sub => lhs - rhs,
-                    BinaryOperator::Mul => lhs * rhs,
-                    BinaryOperator::Div => lhs / rhs,
-                    BinaryOperator::Pow => todo!(),
-                    BinaryOperator::Equ => Ok(lhs.eq(&rhs).into()),
-                    BinaryOperator::Neq => todo!(),
-                }
-            }
-            Expr::Let { bindings, body, .. } => {
-                self.env.push();
-                for Binding { pat, body, .. } in bindings {
-                    let val = self.eval(body)?;
-                    self.bind_pattern(pat, val);
-                }
-                let res = self.eval(body)?;
-                self.env.pop();
-                Ok(res)
-            }
-            Expr::If {
-                condition,
-                then,
-                else_,
-                ..
-            } => {
-                let condition = self.eval(condition)?;
-                if *(condition.as_type::<bool>()?) {
-                    self.eval(then)
-                } else {
-                    self.eval(else_)
-                }
-            }
-            Expr::Call { ref fun, args, .. } => {
-                let fun = self.eval(fun)?;
-                let expected_type = FunctionType {
-                    args: args.iter().map(|_| Type::Int).collect(),
-                    ret: Box::new(Type::Int),
-                };
-
-                let Function {
-                    args: function_args,
-                    body,
-                    ..
-                } = fun.as_function(expected_type)?;
-                let arg_values = function_args.iter().zip(
-                    args.iter()
-                        .map(|v| self.eval(v))
-                        .collect::<Result<Vec<_>>>()?,
-                );
-                let mut interpreter = Interpreter::new();
-                for (arg_name, arg_value) in arg_values {
-                    interpreter.env.set(arg_name, arg_value);
-                }
-                Ok(Value::from(*interpreter.eval(body)?.as_type::<i64>()?))
-            }
-            Expr::Fun {
-                type_args: _,
-                args,
-                body,
-                type_,
-            } => {
-                let type_ = match type_ {
-                    Type::Function(ft) => ft.clone(),
-                    _ => unreachable!("Function expression without function type"),
-                };
-
-                Ok(Value::from(value::Function {
-                    // TODO
-                    type_,
-                    args: args.iter().map(|(arg, _)| arg.to_owned()).collect(),
-                    body: (**body).to_owned(),
-                }))
-            }
-            Expr::Tuple(members, _) => Ok(Val::Tuple(
-                members
-                    .into_iter()
-                    .map(|expr| self.eval(expr))
-                    .try_collect()?,
-            )
-            .into()),
-        }?;
-        debug_assert_eq!(&res.type_(), expr.type_());
-        Ok(res)
-    }
-}
-
-pub fn eval<'a>(expr: &'a Expr<'a, Type>) -> Result<Value<'a>> {
-    let mut interpreter = Interpreter::new();
-    interpreter.eval(expr)
-}
-
-#[cfg(test)]
-mod tests {
-    use std::convert::TryFrom;
-
-    use super::value::{TypeOf, Val};
-    use super::*;
-    use BinaryOperator::*;
-
-    fn int_lit(i: u64) -> Box<Expr<'static, Type<'static>>> {
-        Box::new(Expr::Literal(Literal::Int(i), Type::Int))
-    }
-
-    fn do_eval<T>(src: &str) -> T
-    where
-        for<'a> &'a T: TryFrom<&'a Val<'a>>,
-        T: Clone + TypeOf,
-    {
-        let expr = crate::parser::expr(src).unwrap().1;
-        let hir = crate::tc::typecheck_expr(expr).unwrap();
-        let res = eval(&hir).unwrap();
-        res.as_type::<T>().unwrap().clone()
-    }
-
-    #[test]
-    fn simple_addition() {
-        let expr = Expr::BinaryOp {
-            lhs: int_lit(1),
-            op: Mul,
-            rhs: int_lit(2),
-            type_: Type::Int,
-        };
-        let res = eval(&expr).unwrap();
-        assert_eq!(*res.as_type::<i64>().unwrap(), 2);
-    }
-
-    #[test]
-    fn variable_shadowing() {
-        let res = do_eval::<i64>("let x = 1 in (let x = 2 in x) + x");
-        assert_eq!(res, 3);
-    }
-
-    #[test]
-    fn conditional_with_equals() {
-        let res = do_eval::<i64>("let x = 1 in if x == 1 then 2 else 4");
-        assert_eq!(res, 2);
-    }
-
-    #[test]
-    #[ignore]
-    fn function_call() {
-        let res = do_eval::<i64>("let id = fn x = x in id 1");
-        assert_eq!(res, 1);
-    }
-}
diff --git a/users/grfn/achilles/src/interpreter/value.rs b/users/grfn/achilles/src/interpreter/value.rs
deleted file mode 100644
index 272d1167a3..0000000000
--- a/users/grfn/achilles/src/interpreter/value.rs
+++ /dev/null
@@ -1,224 +0,0 @@
-use std::borrow::Cow;
-use std::convert::TryFrom;
-use std::fmt::{self, Display};
-use std::ops::{Add, Div, Mul, Neg, Sub};
-use std::rc::Rc;
-use std::result;
-
-use derive_more::{Deref, From, TryInto};
-use itertools::Itertools;
-
-use super::{Error, Result};
-use crate::ast::hir::Expr;
-use crate::ast::{FunctionType, Ident, Type};
-
-#[derive(Debug, Clone)]
-pub struct Function<'a> {
-    pub type_: FunctionType<'a>,
-    pub args: Vec<Ident<'a>>,
-    pub body: Expr<'a, Type<'a>>,
-}
-
-#[derive(From, TryInto)]
-#[try_into(owned, ref)]
-pub enum Val<'a> {
-    Int(i64),
-    Float(f64),
-    Bool(bool),
-    String(Cow<'a, str>),
-    Tuple(Vec<Value<'a>>),
-    Function(Function<'a>),
-}
-
-impl<'a> TryFrom<Val<'a>> for String {
-    type Error = ();
-
-    fn try_from(value: Val<'a>) -> result::Result<Self, Self::Error> {
-        match value {
-            Val::String(s) => Ok(s.into_owned()),
-            _ => Err(()),
-        }
-    }
-}
-
-impl<'a> fmt::Debug for Val<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Val::Int(x) => f.debug_tuple("Int").field(x).finish(),
-            Val::Float(x) => f.debug_tuple("Float").field(x).finish(),
-            Val::Bool(x) => f.debug_tuple("Bool").field(x).finish(),
-            Val::String(s) => f.debug_tuple("String").field(s).finish(),
-            Val::Function(Function { type_, .. }) => {
-                f.debug_struct("Function").field("type_", type_).finish()
-            }
-            Val::Tuple(members) => f.debug_tuple("Tuple").field(members).finish(),
-        }
-    }
-}
-
-impl<'a> PartialEq for Val<'a> {
-    fn eq(&self, other: &Self) -> bool {
-        match (self, other) {
-            (Val::Int(x), Val::Int(y)) => x == y,
-            (Val::Float(x), Val::Float(y)) => x == y,
-            (Val::Bool(x), Val::Bool(y)) => x == y,
-            (Val::Function(_), Val::Function(_)) => false,
-            (_, _) => false,
-        }
-    }
-}
-
-impl<'a> From<u64> for Val<'a> {
-    fn from(i: u64) -> Self {
-        Self::from(i as i64)
-    }
-}
-
-impl<'a> Display for Val<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Val::Int(x) => x.fmt(f),
-            Val::Float(x) => x.fmt(f),
-            Val::Bool(x) => x.fmt(f),
-            Val::String(s) => write!(f, "{:?}", s),
-            Val::Function(Function { type_, .. }) => write!(f, "<{}>", type_),
-            Val::Tuple(members) => write!(f, "({})", members.iter().join(", ")),
-        }
-    }
-}
-
-impl<'a> Val<'a> {
-    pub fn type_(&self) -> Type {
-        match self {
-            Val::Int(_) => Type::Int,
-            Val::Float(_) => Type::Float,
-            Val::Bool(_) => Type::Bool,
-            Val::String(_) => Type::CString,
-            Val::Function(Function { type_, .. }) => Type::Function(type_.clone()),
-            Val::Tuple(members) => Type::Tuple(members.iter().map(|expr| expr.type_()).collect()),
-        }
-    }
-
-    pub fn as_type<'b, T>(&'b self) -> Result<&'b T>
-    where
-        T: TypeOf + 'b + Clone,
-        &'b T: TryFrom<&'b Self>,
-    {
-        <&T>::try_from(self).map_err(|_| Error::InvalidType {
-            actual: self.type_().to_owned(),
-            expected: <T as TypeOf>::type_of(),
-        })
-    }
-
-    pub fn as_function<'b>(&'b self, function_type: FunctionType) -> Result<&'b Function<'a>> {
-        match self {
-            Val::Function(f) if f.type_ == function_type => Ok(&f),
-            _ => Err(Error::InvalidType {
-                actual: self.type_().to_owned(),
-                expected: Type::Function(function_type.to_owned()),
-            }),
-        }
-    }
-
-    pub fn as_tuple(&self) -> Option<&Vec<Value<'a>>> {
-        if let Self::Tuple(v) = self {
-            Some(v)
-        } else {
-            None
-        }
-    }
-
-    pub fn try_into_tuple(self) -> result::Result<Vec<Value<'a>>, Self> {
-        if let Self::Tuple(v) = self {
-            Ok(v)
-        } else {
-            Err(self)
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Clone, Deref)]
-pub struct Value<'a>(Rc<Val<'a>>);
-
-impl<'a> Display for Value<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.fmt(f)
-    }
-}
-
-impl<'a, T> From<T> for Value<'a>
-where
-    Val<'a>: From<T>,
-{
-    fn from(x: T) -> Self {
-        Self(Rc::new(x.into()))
-    }
-}
-
-impl<'a> Neg for Value<'a> {
-    type Output = Result<Value<'a>>;
-
-    fn neg(self) -> Self::Output {
-        Ok((-self.as_type::<i64>()?).into())
-    }
-}
-
-impl<'a> Add for Value<'a> {
-    type Output = Result<Value<'a>>;
-
-    fn add(self, rhs: Self) -> Self::Output {
-        Ok((self.as_type::<i64>()? + rhs.as_type::<i64>()?).into())
-    }
-}
-
-impl<'a> Sub for Value<'a> {
-    type Output = Result<Value<'a>>;
-
-    fn sub(self, rhs: Self) -> Self::Output {
-        Ok((self.as_type::<i64>()? - rhs.as_type::<i64>()?).into())
-    }
-}
-
-impl<'a> Mul for Value<'a> {
-    type Output = Result<Value<'a>>;
-
-    fn mul(self, rhs: Self) -> Self::Output {
-        Ok((self.as_type::<i64>()? * rhs.as_type::<i64>()?).into())
-    }
-}
-
-impl<'a> Div for Value<'a> {
-    type Output = Result<Value<'a>>;
-
-    fn div(self, rhs: Self) -> Self::Output {
-        Ok((self.as_type::<f64>()? / rhs.as_type::<f64>()?).into())
-    }
-}
-
-pub trait TypeOf {
-    fn type_of() -> Type<'static>;
-}
-
-impl TypeOf for i64 {
-    fn type_of() -> Type<'static> {
-        Type::Int
-    }
-}
-
-impl TypeOf for bool {
-    fn type_of() -> Type<'static> {
-        Type::Bool
-    }
-}
-
-impl TypeOf for f64 {
-    fn type_of() -> Type<'static> {
-        Type::Float
-    }
-}
-
-impl TypeOf for String {
-    fn type_of() -> Type<'static> {
-        Type::CString
-    }
-}
diff --git a/users/grfn/achilles/src/main.rs b/users/grfn/achilles/src/main.rs
deleted file mode 100644
index 5ae1b59b3a..0000000000
--- a/users/grfn/achilles/src/main.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-use clap::Clap;
-
-pub mod ast;
-pub mod codegen;
-pub(crate) mod commands;
-pub(crate) mod common;
-pub mod compiler;
-pub mod interpreter;
-pub(crate) mod passes;
-#[macro_use]
-pub mod parser;
-pub mod tc;
-
-pub use common::{Error, Result};
-
-#[derive(Clap)]
-struct Opts {
-    #[clap(subcommand)]
-    subcommand: Command,
-}
-
-#[derive(Clap)]
-enum Command {
-    Eval(commands::Eval),
-    Compile(commands::Compile),
-    Check(commands::Check),
-}
-
-fn main() -> anyhow::Result<()> {
-    let opts = Opts::parse();
-    match opts.subcommand {
-        Command::Eval(eval) => Ok(eval.run()?),
-        Command::Compile(compile) => Ok(compile.run()?),
-        Command::Check(check) => Ok(check.run()?),
-    }
-}
diff --git a/users/grfn/achilles/src/parser/expr.rs b/users/grfn/achilles/src/parser/expr.rs
deleted file mode 100644
index f596b18970..0000000000
--- a/users/grfn/achilles/src/parser/expr.rs
+++ /dev/null
@@ -1,718 +0,0 @@
-use std::borrow::Cow;
-
-use nom::alt;
-use nom::character::complete::{digit1, multispace0, multispace1};
-use nom::{
-    call, char, complete, delimited, do_parse, flat_map, many0, map, named, opt, parse_to,
-    preceded, separated_list0, separated_list1, tag, tuple,
-};
-use pratt::{Affix, Associativity, PrattParser, Precedence};
-
-use super::util::comma;
-use crate::ast::{BinaryOperator, Binding, Expr, Fun, Literal, Pattern, UnaryOperator};
-use crate::parser::{arg, ident, type_};
-
-#[derive(Debug)]
-enum TokenTree<'a> {
-    Prefix(UnaryOperator),
-    // Postfix(char),
-    Infix(BinaryOperator),
-    Primary(Expr<'a>),
-    Group(Vec<TokenTree<'a>>),
-}
-
-named!(prefix(&str) -> TokenTree, map!(alt!(
-    complete!(char!('-')) => { |_| UnaryOperator::Neg } |
-    complete!(char!('!')) => { |_| UnaryOperator::Not }
-), TokenTree::Prefix));
-
-named!(infix(&str) -> TokenTree, map!(alt!(
-    complete!(tag!("==")) => { |_| BinaryOperator::Equ } |
-    complete!(tag!("!=")) => { |_| BinaryOperator::Neq } |
-    complete!(char!('+')) => { |_| BinaryOperator::Add } |
-    complete!(char!('-')) => { |_| BinaryOperator::Sub } |
-    complete!(char!('*')) => { |_| BinaryOperator::Mul } |
-    complete!(char!('/')) => { |_| BinaryOperator::Div } |
-    complete!(char!('^')) => { |_| BinaryOperator::Pow }
-), TokenTree::Infix));
-
-named!(primary(&str) -> TokenTree, alt!(
-    do_parse!(
-        multispace0 >>
-        char!('(') >>
-        multispace0 >>
-        group: group >>
-        multispace0 >>
-        char!(')') >>
-        multispace0 >>
-            (TokenTree::Group(group))
-    ) |
-    delimited!(multispace0, simple_expr, multispace0) => { |s| TokenTree::Primary(s) }
-));
-
-named!(
-    rest(&str) -> Vec<(TokenTree, Vec<TokenTree>, TokenTree)>,
-    many0!(tuple!(
-        infix,
-        delimited!(multispace0, many0!(prefix), multispace0),
-        primary
-        // many0!(postfix)
-    ))
-);
-
-named!(group(&str) -> Vec<TokenTree>, do_parse!(
-    prefix: many0!(prefix)
-        >> primary: primary
-        // >> postfix: many0!(postfix)
-        >> rest: rest
-        >> ({
-            let mut res = prefix;
-            res.push(primary);
-            // res.append(&mut postfix);
-            for (infix, mut prefix, primary/*, mut postfix*/) in rest {
-                res.push(infix);
-                res.append(&mut prefix);
-                res.push(primary);
-                // res.append(&mut postfix);
-            }
-            res
-        })
-));
-
-fn token_tree(i: &str) -> nom::IResult<&str, Vec<TokenTree>> {
-    group(i)
-}
-
-struct ExprParser;
-
-impl<'a, I> PrattParser<I> for ExprParser
-where
-    I: Iterator<Item = TokenTree<'a>>,
-{
-    type Error = pratt::NoError;
-    type Input = TokenTree<'a>;
-    type Output = Expr<'a>;
-
-    fn query(&mut self, input: &Self::Input) -> Result<Affix, Self::Error> {
-        use BinaryOperator::*;
-        use UnaryOperator::*;
-
-        Ok(match input {
-            TokenTree::Infix(Add) => Affix::Infix(Precedence(6), Associativity::Left),
-            TokenTree::Infix(Sub) => Affix::Infix(Precedence(6), Associativity::Left),
-            TokenTree::Infix(Mul) => Affix::Infix(Precedence(7), Associativity::Left),
-            TokenTree::Infix(Div) => Affix::Infix(Precedence(7), Associativity::Left),
-            TokenTree::Infix(Pow) => Affix::Infix(Precedence(8), Associativity::Right),
-            TokenTree::Infix(Equ) => Affix::Infix(Precedence(4), Associativity::Right),
-            TokenTree::Infix(Neq) => Affix::Infix(Precedence(4), Associativity::Right),
-            TokenTree::Prefix(Neg) => Affix::Prefix(Precedence(6)),
-            TokenTree::Prefix(Not) => Affix::Prefix(Precedence(6)),
-            TokenTree::Primary(_) => Affix::Nilfix,
-            TokenTree::Group(_) => Affix::Nilfix,
-        })
-    }
-
-    fn primary(&mut self, input: Self::Input) -> Result<Self::Output, Self::Error> {
-        Ok(match input {
-            TokenTree::Primary(expr) => expr,
-            TokenTree::Group(group) => self.parse(&mut group.into_iter()).unwrap(),
-            _ => unreachable!(),
-        })
-    }
-
-    fn infix(
-        &mut self,
-        lhs: Self::Output,
-        op: Self::Input,
-        rhs: Self::Output,
-    ) -> Result<Self::Output, Self::Error> {
-        let op = match op {
-            TokenTree::Infix(op) => op,
-            _ => unreachable!(),
-        };
-        Ok(Expr::BinaryOp {
-            lhs: Box::new(lhs),
-            op,
-            rhs: Box::new(rhs),
-        })
-    }
-
-    fn prefix(&mut self, op: Self::Input, rhs: Self::Output) -> Result<Self::Output, Self::Error> {
-        let op = match op {
-            TokenTree::Prefix(op) => op,
-            _ => unreachable!(),
-        };
-
-        Ok(Expr::UnaryOp {
-            op,
-            rhs: Box::new(rhs),
-        })
-    }
-
-    fn postfix(
-        &mut self,
-        _lhs: Self::Output,
-        _op: Self::Input,
-    ) -> Result<Self::Output, Self::Error> {
-        unreachable!()
-    }
-}
-
-named!(int(&str) -> Literal, map!(flat_map!(digit1, parse_to!(u64)), Literal::Int));
-
-named!(bool_(&str) -> Literal, alt!(
-    complete!(tag!("true")) => { |_| Literal::Bool(true) } |
-    complete!(tag!("false")) => { |_| Literal::Bool(false) }
-));
-
-fn string_internal(i: &str) -> nom::IResult<&str, Cow<'_, str>, nom::error::Error<&str>> {
-    // TODO(grfn): use String::split_once when that's stable
-    let (s, rem) = if let Some(pos) = i.find('"') {
-        (&i[..pos], &i[(pos + 1)..])
-    } else {
-        return Err(nom::Err::Error(nom::error::Error::new(
-            i,
-            nom::error::ErrorKind::Tag,
-        )));
-    };
-
-    Ok((rem, Cow::Borrowed(s)))
-}
-
-named!(string(&str) -> Literal, preceded!(
-    complete!(char!('"')),
-    map!(
-        string_internal,
-        |s| Literal::String(s)
-    )
-));
-
-named!(unit(&str) -> Literal, map!(complete!(tag!("()")), |_| Literal::Unit));
-
-named!(literal(&str) -> Literal, alt!(int | bool_ | string | unit));
-
-named!(literal_expr(&str) -> Expr, map!(literal, Expr::Literal));
-
-named!(tuple(&str) -> Expr, do_parse!(
-    complete!(tag!("("))
-        >> multispace0
-        >> fst: expr
-        >> comma
-        >> rest: separated_list0!(
-            comma,
-            expr
-        )
-        >> multispace0
-        >> tag!(")")
-        >> ({
-            let mut members = Vec::with_capacity(rest.len() + 1);
-            members.push(fst);
-            members.append(&mut rest.clone());
-            Expr::Tuple(members)
-        })
-));
-
-named!(tuple_pattern(&str) -> Pattern, do_parse!(
-    complete!(tag!("("))
-        >> multispace0
-        >> pats: separated_list0!(
-            comma,
-            pattern
-        )
-        >> multispace0
-        >> tag!(")")
-        >> (Pattern::Tuple(pats))
-));
-
-named!(pattern(&str) -> Pattern, alt!(
-    ident => { |id| Pattern::Id(id) } |
-    tuple_pattern
-));
-
-named!(binding(&str) -> Binding, do_parse!(
-    multispace0
-        >> pat: pattern
-        >> multispace0
-        >> type_: opt!(preceded!(tuple!(tag!(":"), multispace0), type_))
-        >> multispace0
-        >> char!('=')
-        >> multispace0
-        >> body: expr
-        >> (Binding {
-            pat,
-            type_,
-            body
-        })
-));
-
-named!(let_(&str) -> Expr, do_parse!(
-    tag!("let")
-        >> multispace0
-        >> bindings: separated_list1!(alt!(char!(';') | char!('\n')), binding)
-        >> multispace0
-        >> tag!("in")
-        >> multispace0
-        >> body: expr
-        >> (Expr::Let {
-            bindings,
-            body: Box::new(body)
-        })
-));
-
-named!(if_(&str) -> Expr, do_parse! (
-    tag!("if")
-        >> multispace0
-        >> condition: expr
-        >> multispace0
-        >> tag!("then")
-        >> multispace0
-        >> then: expr
-        >> multispace0
-        >> tag!("else")
-        >> multispace0
-        >> else_: expr
-        >> (Expr::If {
-            condition: Box::new(condition),
-            then: Box::new(then),
-            else_: Box::new(else_)
-        })
-));
-
-named!(ident_expr(&str) -> Expr, map!(ident, Expr::Ident));
-
-fn ascripted<'a>(
-    p: impl Fn(&'a str) -> nom::IResult<&'a str, Expr, nom::error::Error<&'a str>> + 'a,
-) -> impl Fn(&'a str) -> nom::IResult<&str, Expr, nom::error::Error<&'a str>> {
-    move |i| {
-        do_parse!(
-            i,
-            expr: p
-                >> multispace0
-                >> complete!(tag!(":"))
-                >> multispace0
-                >> type_: type_
-                >> (Expr::Ascription {
-                    expr: Box::new(expr),
-                    type_
-                })
-        )
-    }
-}
-
-named!(paren_expr(&str) -> Expr,
-       delimited!(complete!(tag!("(")), expr, complete!(tag!(")"))));
-
-named!(funcref(&str) -> Expr, alt!(
-    ident_expr |
-    tuple |
-    paren_expr
-));
-
-named!(no_arg_call(&str) -> Expr, do_parse!(
-    fun: funcref
-        >> complete!(tag!("()"))
-        >> (Expr::Call {
-            fun: Box::new(fun),
-            args: vec![],
-        })
-));
-
-named!(fun_expr(&str) -> Expr, do_parse!(
-    tag!("fn")
-        >> multispace1
-        >> args: separated_list0!(multispace1, arg)
-        >> multispace0
-        >> char!('=')
-        >> multispace0
-        >> body: expr
-        >> (Expr::Fun(Box::new(Fun {
-            args,
-            body
-        })))
-));
-
-named!(fn_arg(&str) -> Expr, alt!(
-    ident_expr |
-    literal_expr |
-    tuple |
-    paren_expr
-));
-
-named!(call_with_args(&str) -> Expr, do_parse!(
-    fun: funcref
-        >> multispace1
-        >> args: separated_list1!(multispace1, fn_arg)
-        >> (Expr::Call {
-            fun: Box::new(fun),
-            args
-        })
-));
-
-named!(simple_expr_unascripted(&str) -> Expr, alt!(
-    let_ |
-    if_ |
-    fun_expr |
-    literal_expr |
-    ident_expr |
-    tuple
-));
-
-named!(simple_expr(&str) -> Expr, alt!(
-    call!(ascripted(simple_expr_unascripted)) |
-    simple_expr_unascripted
-));
-
-named!(pub expr(&str) -> Expr, alt!(
-    no_arg_call |
-    call_with_args |
-    map!(token_tree, |tt| {
-        ExprParser.parse(&mut tt.into_iter()).unwrap()
-    }) |
-    simple_expr
-));
-
-#[cfg(test)]
-pub(crate) mod tests {
-    use super::*;
-    use crate::ast::{Arg, Ident, Pattern, Type};
-    use std::convert::TryFrom;
-    use BinaryOperator::*;
-    use Expr::{BinaryOp, If, Let, UnaryOp};
-    use UnaryOperator::*;
-
-    pub(crate) fn ident_expr(s: &str) -> Box<Expr> {
-        Box::new(Expr::Ident(Ident::try_from(s).unwrap()))
-    }
-
-    mod operators {
-        use super::*;
-
-        #[test]
-        fn mul_plus() {
-            let (rem, res) = expr("x*y+z").unwrap();
-            assert!(rem.is_empty());
-            assert_eq!(
-                res,
-                BinaryOp {
-                    lhs: Box::new(BinaryOp {
-                        lhs: ident_expr("x"),
-                        op: Mul,
-                        rhs: ident_expr("y")
-                    }),
-                    op: Add,
-                    rhs: ident_expr("z")
-                }
-            )
-        }
-
-        #[test]
-        fn mul_plus_ws() {
-            let (rem, res) = expr("x * y    +    z").unwrap();
-            assert!(rem.is_empty(), "non-empty remainder: \"{}\"", rem);
-            assert_eq!(
-                res,
-                BinaryOp {
-                    lhs: Box::new(BinaryOp {
-                        lhs: ident_expr("x"),
-                        op: Mul,
-                        rhs: ident_expr("y")
-                    }),
-                    op: Add,
-                    rhs: ident_expr("z")
-                }
-            )
-        }
-
-        #[test]
-        fn unary() {
-            let (rem, res) = expr("x * -z").unwrap();
-            assert!(rem.is_empty(), "non-empty remainder: \"{}\"", rem);
-            assert_eq!(
-                res,
-                BinaryOp {
-                    lhs: ident_expr("x"),
-                    op: Mul,
-                    rhs: Box::new(UnaryOp {
-                        op: Neg,
-                        rhs: ident_expr("z"),
-                    })
-                }
-            )
-        }
-
-        #[test]
-        fn mul_literal() {
-            let (rem, res) = expr("x * 3").unwrap();
-            assert!(rem.is_empty());
-            assert_eq!(
-                res,
-                BinaryOp {
-                    lhs: ident_expr("x"),
-                    op: Mul,
-                    rhs: Box::new(Expr::Literal(Literal::Int(3))),
-                }
-            )
-        }
-
-        #[test]
-        fn equ() {
-            let res = test_parse!(expr, "x * 7 == 7");
-            assert_eq!(
-                res,
-                BinaryOp {
-                    lhs: Box::new(BinaryOp {
-                        lhs: ident_expr("x"),
-                        op: Mul,
-                        rhs: Box::new(Expr::Literal(Literal::Int(7)))
-                    }),
-                    op: Equ,
-                    rhs: Box::new(Expr::Literal(Literal::Int(7)))
-                }
-            )
-        }
-    }
-
-    #[test]
-    fn unit() {
-        assert_eq!(test_parse!(expr, "()"), Expr::Literal(Literal::Unit));
-    }
-
-    #[test]
-    fn bools() {
-        assert_eq!(
-            test_parse!(expr, "true"),
-            Expr::Literal(Literal::Bool(true))
-        );
-        assert_eq!(
-            test_parse!(expr, "false"),
-            Expr::Literal(Literal::Bool(false))
-        );
-    }
-
-    #[test]
-    fn tuple() {
-        assert_eq!(
-            test_parse!(expr, "(1, \"seven\")"),
-            Expr::Tuple(vec![
-                Expr::Literal(Literal::Int(1)),
-                Expr::Literal(Literal::String(Cow::Borrowed("seven")))
-            ])
-        )
-    }
-
-    #[test]
-    fn simple_string_lit() {
-        assert_eq!(
-            test_parse!(expr, "\"foobar\""),
-            Expr::Literal(Literal::String(Cow::Borrowed("foobar")))
-        )
-    }
-
-    #[test]
-    fn let_complex() {
-        let res = test_parse!(expr, "let x = 1; y = x * 7 in (x + y) * 4");
-        assert_eq!(
-            res,
-            Let {
-                bindings: vec![
-                    Binding {
-                        pat: Pattern::Id(Ident::try_from("x").unwrap()),
-                        type_: None,
-                        body: Expr::Literal(Literal::Int(1))
-                    },
-                    Binding {
-                        pat: Pattern::Id(Ident::try_from("y").unwrap()),
-                        type_: None,
-                        body: Expr::BinaryOp {
-                            lhs: ident_expr("x"),
-                            op: Mul,
-                            rhs: Box::new(Expr::Literal(Literal::Int(7)))
-                        }
-                    }
-                ],
-                body: Box::new(Expr::BinaryOp {
-                    lhs: Box::new(Expr::BinaryOp {
-                        lhs: ident_expr("x"),
-                        op: Add,
-                        rhs: ident_expr("y"),
-                    }),
-                    op: Mul,
-                    rhs: Box::new(Expr::Literal(Literal::Int(4))),
-                })
-            }
-        )
-    }
-
-    #[test]
-    fn if_simple() {
-        let res = test_parse!(expr, "if x == 8 then 9 else 20");
-        assert_eq!(
-            res,
-            If {
-                condition: Box::new(BinaryOp {
-                    lhs: ident_expr("x"),
-                    op: Equ,
-                    rhs: Box::new(Expr::Literal(Literal::Int(8))),
-                }),
-                then: Box::new(Expr::Literal(Literal::Int(9))),
-                else_: Box::new(Expr::Literal(Literal::Int(20)))
-            }
-        )
-    }
-
-    #[test]
-    fn no_arg_call() {
-        let res = test_parse!(expr, "f()");
-        assert_eq!(
-            res,
-            Expr::Call {
-                fun: ident_expr("f"),
-                args: vec![]
-            }
-        );
-    }
-
-    #[test]
-    fn unit_call() {
-        let res = test_parse!(expr, "f ()");
-        assert_eq!(
-            res,
-            Expr::Call {
-                fun: ident_expr("f"),
-                args: vec![Expr::Literal(Literal::Unit)]
-            }
-        )
-    }
-
-    #[test]
-    fn call_with_args() {
-        let res = test_parse!(expr, "f x 1");
-        assert_eq!(
-            res,
-            Expr::Call {
-                fun: ident_expr("f"),
-                args: vec![*ident_expr("x"), Expr::Literal(Literal::Int(1))]
-            }
-        )
-    }
-
-    #[test]
-    fn call_funcref() {
-        let res = test_parse!(expr, "(let x = 1 in x) 2");
-        assert_eq!(
-            res,
-            Expr::Call {
-                fun: Box::new(Expr::Let {
-                    bindings: vec![Binding {
-                        pat: Pattern::Id(Ident::try_from("x").unwrap()),
-                        type_: None,
-                        body: Expr::Literal(Literal::Int(1))
-                    }],
-                    body: ident_expr("x")
-                }),
-                args: vec![Expr::Literal(Literal::Int(2))]
-            }
-        )
-    }
-
-    #[test]
-    fn anon_function() {
-        let res = test_parse!(expr, "let id = fn x = x in id 1");
-        assert_eq!(
-            res,
-            Expr::Let {
-                bindings: vec![Binding {
-                    pat: Pattern::Id(Ident::try_from("id").unwrap()),
-                    type_: None,
-                    body: Expr::Fun(Box::new(Fun {
-                        args: vec![Arg::try_from("x").unwrap()],
-                        body: *ident_expr("x")
-                    }))
-                }],
-                body: Box::new(Expr::Call {
-                    fun: ident_expr("id"),
-                    args: vec![Expr::Literal(Literal::Int(1))],
-                })
-            }
-        );
-    }
-
-    #[test]
-    fn tuple_binding() {
-        let res = test_parse!(expr, "let (x, y) = (1, 2) in x");
-        assert_eq!(
-            res,
-            Expr::Let {
-                bindings: vec![Binding {
-                    pat: Pattern::Tuple(vec![
-                        Pattern::Id(Ident::from_str_unchecked("x")),
-                        Pattern::Id(Ident::from_str_unchecked("y"))
-                    ]),
-                    body: Expr::Tuple(vec![
-                        Expr::Literal(Literal::Int(1)),
-                        Expr::Literal(Literal::Int(2))
-                    ]),
-                    type_: None
-                }],
-                body: Box::new(Expr::Ident(Ident::from_str_unchecked("x")))
-            }
-        )
-    }
-
-    mod ascriptions {
-        use super::*;
-
-        #[test]
-        fn bare_ascription() {
-            let res = test_parse!(expr, "1: float");
-            assert_eq!(
-                res,
-                Expr::Ascription {
-                    expr: Box::new(Expr::Literal(Literal::Int(1))),
-                    type_: Type::Float
-                }
-            )
-        }
-
-        #[test]
-        fn fn_body_ascription() {
-            let res = test_parse!(expr, "let const_1 = fn x = 1: int in const_1 2");
-            assert_eq!(
-                res,
-                Expr::Let {
-                    bindings: vec![Binding {
-                        pat: Pattern::Id(Ident::try_from("const_1").unwrap()),
-                        type_: None,
-                        body: Expr::Fun(Box::new(Fun {
-                            args: vec![Arg::try_from("x").unwrap()],
-                            body: Expr::Ascription {
-                                expr: Box::new(Expr::Literal(Literal::Int(1))),
-                                type_: Type::Int,
-                            }
-                        }))
-                    }],
-                    body: Box::new(Expr::Call {
-                        fun: ident_expr("const_1"),
-                        args: vec![Expr::Literal(Literal::Int(2))]
-                    })
-                }
-            )
-        }
-
-        #[test]
-        fn let_binding_ascripted() {
-            let res = test_parse!(expr, "let x: int = 1 in x");
-            assert_eq!(
-                res,
-                Expr::Let {
-                    bindings: vec![Binding {
-                        pat: Pattern::Id(Ident::try_from("x").unwrap()),
-                        type_: Some(Type::Int),
-                        body: Expr::Literal(Literal::Int(1))
-                    }],
-                    body: ident_expr("x")
-                }
-            )
-        }
-    }
-}
diff --git a/users/grfn/achilles/src/parser/macros.rs b/users/grfn/achilles/src/parser/macros.rs
deleted file mode 100644
index 406e5c0e69..0000000000
--- a/users/grfn/achilles/src/parser/macros.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-#[cfg(test)]
-#[macro_use]
-macro_rules! test_parse {
-    ($parser: ident, $src: expr) => {{
-        let res = $parser($src);
-        nom_trace::print_trace!();
-        let (rem, res) = res.unwrap();
-        assert!(
-            rem.is_empty(),
-            "non-empty remainder: \"{}\", parsed: {:?}",
-            rem,
-            res
-        );
-        res
-    }};
-}
diff --git a/users/grfn/achilles/src/parser/mod.rs b/users/grfn/achilles/src/parser/mod.rs
deleted file mode 100644
index e088cbca10..0000000000
--- a/users/grfn/achilles/src/parser/mod.rs
+++ /dev/null
@@ -1,240 +0,0 @@
-use nom::character::complete::{multispace0, multispace1};
-use nom::error::{ErrorKind, ParseError};
-use nom::{alt, char, complete, do_parse, eof, many0, named, separated_list0, tag, terminated};
-
-#[macro_use]
-pub(crate) mod macros;
-mod expr;
-mod type_;
-mod util;
-
-use crate::ast::{Arg, Decl, Fun, Ident};
-pub use expr::expr;
-use type_::function_type;
-pub use type_::type_;
-
-pub type Error = nom::Err<nom::error::Error<String>>;
-
-pub(crate) fn is_reserved(s: &str) -> bool {
-    matches!(
-        s,
-        "if" | "then"
-            | "else"
-            | "let"
-            | "in"
-            | "fn"
-            | "ty"
-            | "int"
-            | "float"
-            | "bool"
-            | "true"
-            | "false"
-            | "cstring"
-    )
-}
-
-pub(crate) fn ident<'a, E>(i: &'a str) -> nom::IResult<&'a str, Ident, E>
-where
-    E: ParseError<&'a str>,
-{
-    let mut chars = i.chars();
-    if let Some(f) = chars.next() {
-        if f.is_alphabetic() || f == '_' {
-            let mut idx = 1;
-            for c in chars {
-                if !(c.is_alphanumeric() || c == '_') {
-                    break;
-                }
-                idx += 1;
-            }
-            let id = &i[..idx];
-            if is_reserved(id) {
-                Err(nom::Err::Error(E::from_error_kind(i, ErrorKind::Satisfy)))
-            } else {
-                Ok((&i[idx..], Ident::from_str_unchecked(id)))
-            }
-        } else {
-            Err(nom::Err::Error(E::from_error_kind(i, ErrorKind::Satisfy)))
-        }
-    } else {
-        Err(nom::Err::Error(E::from_error_kind(i, ErrorKind::Eof)))
-    }
-}
-
-named!(ascripted_arg(&str) -> Arg, do_parse!(
-    complete!(char!('(')) >>
-        multispace0 >>
-        ident: ident >>
-        multispace0 >>
-        complete!(char!(':')) >>
-        multispace0 >>
-        type_: type_ >>
-        multispace0 >>
-        complete!(char!(')')) >>
-        (Arg {
-            ident,
-            type_: Some(type_)
-        })
-));
-
-named!(arg(&str) -> Arg, alt!(
-    ident => { |ident| Arg {ident, type_: None}} |
-    ascripted_arg
-));
-
-named!(extern_decl(&str) -> Decl, do_parse!(
-    complete!(tag!("extern"))
-        >> multispace1
-        >> name: ident
-        >> multispace0
-        >> char!(':')
-        >> multispace0
-        >> type_: function_type
-        >> multispace0
-        >> (Decl::Extern {
-            name,
-            type_
-        })
-));
-
-named!(fun_decl(&str) -> Decl, do_parse!(
-    complete!(tag!("fn"))
-        >> multispace1
-        >> name: ident
-        >> multispace1
-        >> args: separated_list0!(multispace1, arg)
-        >> multispace0
-        >> char!('=')
-        >> multispace0
-        >> body: expr
-        >> (Decl::Fun {
-            name,
-            body: Fun {
-                args,
-                body
-            }
-        })
-));
-
-named!(ascription_decl(&str) -> Decl, do_parse!(
-    complete!(tag!("ty"))
-        >> multispace1
-        >> name: ident
-        >> multispace0
-        >> complete!(char!(':'))
-        >> multispace0
-        >> type_: type_
-        >> multispace0
-        >> (Decl::Ascription {
-            name,
-            type_
-        })
-));
-
-named!(pub decl(&str) -> Decl, alt!(
-    ascription_decl |
-    fun_decl |
-    extern_decl
-));
-
-named!(pub toplevel(&str) -> Vec<Decl>, do_parse!(
-    decls: many0!(decl)
-        >> multispace0
-        >> eof!()
-        >> (decls)));
-
-#[cfg(test)]
-mod tests {
-    use std::convert::TryInto;
-
-    use crate::ast::{BinaryOperator, Expr, FunctionType, Literal, Type};
-
-    use super::*;
-    use expr::tests::ident_expr;
-
-    #[test]
-    fn fn_decl() {
-        let res = test_parse!(decl, "fn id x = x");
-        assert_eq!(
-            res,
-            Decl::Fun {
-                name: "id".try_into().unwrap(),
-                body: Fun {
-                    args: vec!["x".try_into().unwrap()],
-                    body: *ident_expr("x"),
-                }
-            }
-        )
-    }
-
-    #[test]
-    fn ascripted_fn_args() {
-        test_parse!(ascripted_arg, "(x : int)");
-        let res = test_parse!(decl, "fn plus1 (x : int) = x + 1");
-        assert_eq!(
-            res,
-            Decl::Fun {
-                name: "plus1".try_into().unwrap(),
-                body: Fun {
-                    args: vec![Arg {
-                        ident: "x".try_into().unwrap(),
-                        type_: Some(Type::Int),
-                    }],
-                    body: Expr::BinaryOp {
-                        lhs: ident_expr("x"),
-                        op: BinaryOperator::Add,
-                        rhs: Box::new(Expr::Literal(Literal::Int(1))),
-                    }
-                }
-            }
-        );
-    }
-
-    #[test]
-    fn multiple_decls() {
-        let res = test_parse!(
-            toplevel,
-            "fn id x = x
-             fn plus x y = x + y
-             fn main = plus (id 2) 7"
-        );
-        assert_eq!(res.len(), 3);
-        let res = test_parse!(
-            toplevel,
-            "fn id x = x\nfn plus x y = x + y\nfn main = plus (id 2) 7\n"
-        );
-        assert_eq!(res.len(), 3);
-    }
-
-    #[test]
-    fn top_level_ascription() {
-        let res = test_parse!(toplevel, "ty id : fn a -> a");
-        assert_eq!(
-            res,
-            vec![Decl::Ascription {
-                name: "id".try_into().unwrap(),
-                type_: Type::Function(FunctionType {
-                    args: vec![Type::Var("a".try_into().unwrap())],
-                    ret: Box::new(Type::Var("a".try_into().unwrap()))
-                })
-            }]
-        )
-    }
-
-    #[test]
-    fn return_unit() {
-        assert_eq!(
-            test_parse!(decl, "fn g _ = ()"),
-            Decl::Fun {
-                name: "g".try_into().unwrap(),
-                body: Fun {
-                    args: vec![Arg {
-                        ident: "_".try_into().unwrap(),
-                        type_: None,
-                    }],
-                    body: Expr::Literal(Literal::Unit),
-                },
-            }
-        )
-    }
-}
diff --git a/users/grfn/achilles/src/parser/type_.rs b/users/grfn/achilles/src/parser/type_.rs
deleted file mode 100644
index b80f0e0860..0000000000
--- a/users/grfn/achilles/src/parser/type_.rs
+++ /dev/null
@@ -1,152 +0,0 @@
-use nom::character::complete::{multispace0, multispace1};
-use nom::{alt, delimited, do_parse, map, named, opt, separated_list0, tag, terminated, tuple};
-
-use super::ident;
-use super::util::comma;
-use crate::ast::{FunctionType, Type};
-
-named!(pub function_type(&str) -> FunctionType, do_parse!(
-    tag!("fn")
-        >> multispace1
-        >> args: map!(opt!(terminated!(separated_list0!(
-            comma,
-            type_
-        ), multispace1)), |args| args.unwrap_or_default())
-        >> tag!("->")
-        >> multispace1
-        >> ret: type_
-        >> (FunctionType {
-            args,
-            ret: Box::new(ret)
-        })
-));
-
-named!(tuple_type(&str) -> Type, do_parse!(
-    tag!("(")
-        >> multispace0
-        >> fst: type_
-        >> comma
-        >> rest: separated_list0!(
-            comma,
-            type_
-        )
-        >> multispace0
-        >> tag!(")")
-        >> ({
-            let mut members = Vec::with_capacity(rest.len() + 1);
-            members.push(fst);
-            members.append(&mut rest.clone());
-            Type::Tuple(members)
-        })
-));
-
-named!(pub type_(&str) -> Type, alt!(
-    tag!("int") => { |_| Type::Int } |
-    tag!("float") => { |_| Type::Float } |
-    tag!("bool") => { |_| Type::Bool } |
-    tag!("cstring") => { |_| Type::CString } |
-    tag!("()") => { |_| Type::Unit } |
-    tuple_type |
-    function_type => { |ft| Type::Function(ft) }|
-    ident => { |id| Type::Var(id) } |
-    delimited!(
-        tuple!(tag!("("), multispace0),
-        type_,
-        tuple!(tag!(")"), multispace0)
-    )
-));
-
-#[cfg(test)]
-mod tests {
-    use std::convert::TryFrom;
-
-    use super::*;
-    use crate::ast::Ident;
-
-    #[test]
-    fn simple_types() {
-        assert_eq!(test_parse!(type_, "int"), Type::Int);
-        assert_eq!(test_parse!(type_, "float"), Type::Float);
-        assert_eq!(test_parse!(type_, "bool"), Type::Bool);
-        assert_eq!(test_parse!(type_, "cstring"), Type::CString);
-        assert_eq!(test_parse!(type_, "()"), Type::Unit);
-    }
-
-    #[test]
-    fn no_arg_fn_type() {
-        assert_eq!(
-            test_parse!(type_, "fn -> int"),
-            Type::Function(FunctionType {
-                args: vec![],
-                ret: Box::new(Type::Int)
-            })
-        );
-    }
-
-    #[test]
-    fn fn_type_with_args() {
-        assert_eq!(
-            test_parse!(type_, "fn int, bool -> int"),
-            Type::Function(FunctionType {
-                args: vec![Type::Int, Type::Bool],
-                ret: Box::new(Type::Int)
-            })
-        );
-    }
-
-    #[test]
-    fn fn_taking_fn() {
-        assert_eq!(
-            test_parse!(type_, "fn fn int, bool -> bool, float -> float"),
-            Type::Function(FunctionType {
-                args: vec![
-                    Type::Function(FunctionType {
-                        args: vec![Type::Int, Type::Bool],
-                        ret: Box::new(Type::Bool)
-                    }),
-                    Type::Float
-                ],
-                ret: Box::new(Type::Float)
-            })
-        )
-    }
-
-    #[test]
-    fn parenthesized() {
-        assert_eq!(
-            test_parse!(type_, "fn (fn int, bool -> bool), float -> float"),
-            Type::Function(FunctionType {
-                args: vec![
-                    Type::Function(FunctionType {
-                        args: vec![Type::Int, Type::Bool],
-                        ret: Box::new(Type::Bool)
-                    }),
-                    Type::Float
-                ],
-                ret: Box::new(Type::Float)
-            })
-        )
-    }
-
-    #[test]
-    fn tuple() {
-        assert_eq!(
-            test_parse!(type_, "(int, int)"),
-            Type::Tuple(vec![Type::Int, Type::Int])
-        )
-    }
-
-    #[test]
-    fn type_vars() {
-        assert_eq!(
-            test_parse!(type_, "fn x, y -> x"),
-            Type::Function(FunctionType {
-                args: vec![
-                    Type::Var(Ident::try_from("x").unwrap()),
-                    Type::Var(Ident::try_from("y").unwrap()),
-                ],
-                ret: Box::new(Type::Var(Ident::try_from("x").unwrap())),
-            })
-        )
-    }
-}
diff --git a/users/grfn/achilles/src/parser/util.rs b/users/grfn/achilles/src/parser/util.rs
deleted file mode 100644
index bb53fb7fff..0000000000
--- a/users/grfn/achilles/src/parser/util.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-use nom::character::complete::multispace0;
-use nom::{complete, map, named, tag, tuple};
-
-named!(pub(crate) comma(&str) -> (), map!(tuple!(
-    multispace0,
-    complete!(tag!(",")),
-    multispace0
-) ,|_| ()));
diff --git a/users/grfn/achilles/src/passes/hir/mod.rs b/users/grfn/achilles/src/passes/hir/mod.rs
deleted file mode 100644
index 872c449eb0..0000000000
--- a/users/grfn/achilles/src/passes/hir/mod.rs
+++ /dev/null
@@ -1,211 +0,0 @@
-use std::collections::HashMap;
-
-use crate::ast::hir::{Binding, Decl, Expr, Pattern};
-use crate::ast::{BinaryOperator, Ident, Literal, UnaryOperator};
-
-pub(crate) mod monomorphize;
-pub(crate) mod strip_positive_units;
-
-pub(crate) trait Visitor<'a, 'ast, T: 'ast>: Sized + 'a {
-    type Error;
-
-    fn visit_type(&mut self, _type: &mut T) -> Result<(), Self::Error> {
-        Ok(())
-    }
-
-    fn visit_ident(&mut self, _ident: &mut Ident<'ast>) -> Result<(), Self::Error> {
-        Ok(())
-    }
-
-    fn visit_literal(&mut self, _literal: &mut Literal<'ast>) -> Result<(), Self::Error> {
-        Ok(())
-    }
-
-    fn visit_unary_operator(&mut self, _op: &mut UnaryOperator) -> Result<(), Self::Error> {
-        Ok(())
-    }
-
-    fn visit_binary_operator(&mut self, _op: &mut BinaryOperator) -> Result<(), Self::Error> {
-        Ok(())
-    }
-
-    fn visit_pattern(&mut self, _pat: &mut Pattern<'ast, T>) -> Result<(), Self::Error> {
-        Ok(())
-    }
-
-    fn visit_binding(&mut self, binding: &mut Binding<'ast, T>) -> Result<(), Self::Error> {
-        self.visit_pattern(&mut binding.pat)?;
-        self.visit_expr(&mut binding.body)?;
-        Ok(())
-    }
-
-    fn post_visit_call(
-        &mut self,
-        _fun: &mut Expr<'ast, T>,
-        _type_args: &mut HashMap<Ident<'ast>, T>,
-        _args: &mut Vec<Expr<'ast, T>>,
-    ) -> Result<(), Self::Error> {
-        Ok(())
-    }
-
-    fn pre_visit_call(
-        &mut self,
-        _fun: &mut Expr<'ast, T>,
-        _type_args: &mut HashMap<Ident<'ast>, T>,
-        _args: &mut Vec<Expr<'ast, T>>,
-    ) -> Result<(), Self::Error> {
-        Ok(())
-    }
-
-    fn visit_tuple(&mut self, members: &mut Vec<Expr<'ast, T>>) -> Result<(), Self::Error> {
-        for expr in members {
-            self.visit_expr(expr)?;
-        }
-        Ok(())
-    }
-
-    fn pre_visit_expr(&mut self, _expr: &mut Expr<'ast, T>) -> Result<(), Self::Error> {
-        Ok(())
-    }
-
-    fn visit_expr(&mut self, expr: &mut Expr<'ast, T>) -> Result<(), Self::Error> {
-        self.pre_visit_expr(expr)?;
-        match expr {
-            Expr::Ident(id, t) => {
-                self.visit_ident(id)?;
-                self.visit_type(t)?;
-            }
-            Expr::Literal(lit, t) => {
-                self.visit_literal(lit)?;
-                self.visit_type(t)?;
-            }
-            Expr::UnaryOp { op, rhs, type_ } => {
-                self.visit_unary_operator(op)?;
-                self.visit_expr(rhs)?;
-                self.visit_type(type_)?;
-            }
-            Expr::BinaryOp {
-                lhs,
-                op,
-                rhs,
-                type_,
-            } => {
-                self.visit_expr(lhs)?;
-                self.visit_binary_operator(op)?;
-                self.visit_expr(rhs)?;
-                self.visit_type(type_)?;
-            }
-            Expr::Let {
-                bindings,
-                body,
-                type_,
-            } => {
-                for binding in bindings.iter_mut() {
-                    self.visit_binding(binding)?;
-                }
-                self.visit_expr(body)?;
-                self.visit_type(type_)?;
-            }
-            Expr::If {
-                condition,
-                then,
-                else_,
-                type_,
-            } => {
-                self.visit_expr(condition)?;
-                self.visit_expr(then)?;
-                self.visit_expr(else_)?;
-                self.visit_type(type_)?;
-            }
-            Expr::Fun {
-                args,
-                body,
-                type_args,
-                type_,
-            } => {
-                for (ident, t) in args {
-                    self.visit_ident(ident)?;
-                    self.visit_type(t)?;
-                }
-                for ta in type_args {
-                    self.visit_ident(ta)?;
-                }
-                self.visit_expr(body)?;
-                self.visit_type(type_)?;
-            }
-            Expr::Call {
-                fun,
-                args,
-                type_args,
-                type_,
-            } => {
-                self.pre_visit_call(fun, type_args, args)?;
-                self.visit_expr(fun)?;
-                for arg in args.iter_mut() {
-                    self.visit_expr(arg)?;
-                }
-                self.visit_type(type_)?;
-                self.post_visit_call(fun, type_args, args)?;
-            }
-            Expr::Tuple(tup, type_) => {
-                self.visit_tuple(tup)?;
-                self.visit_type(type_)?;
-            }
-        }
-
-        Ok(())
-    }
-
-    fn post_visit_decl(&mut self, _decl: &'a Decl<'ast, T>) -> Result<(), Self::Error> {
-        Ok(())
-    }
-
-    fn post_visit_fun_decl(
-        &mut self,
-        _name: &mut Ident<'ast>,
-        _type_args: &mut Vec<Ident>,
-        _args: &mut Vec<(Ident, T)>,
-        _body: &mut Box<Expr<T>>,
-        _type_: &mut T,
-    ) -> Result<(), Self::Error> {
-        Ok(())
-    }
-
-    fn visit_decl(&mut self, decl: &'a mut Decl<'ast, T>) -> Result<(), Self::Error> {
-        match decl {
-            Decl::Fun {
-                name,
-                type_args,
-                args,
-                body,
-                type_,
-            } => {
-                self.visit_ident(name)?;
-                for type_arg in type_args.iter_mut() {
-                    self.visit_ident(type_arg)?;
-                }
-                for (arg, t) in args.iter_mut() {
-                    self.visit_ident(arg)?;
-                    self.visit_type(t)?;
-                }
-                self.visit_expr(body)?;
-                self.visit_type(type_)?;
-                self.post_visit_fun_decl(name, type_args, args, body, type_)?;
-            }
-            Decl::Extern {
-                name,
-                arg_types,
-                ret_type,
-            } => {
-                self.visit_ident(name)?;
-                for arg_t in arg_types {
-                    self.visit_type(arg_t)?;
-                }
-                self.visit_type(ret_type)?;
-            }
-        }
-
-        self.post_visit_decl(decl)?;
-        Ok(())
-    }
-}
diff --git a/users/grfn/achilles/src/passes/hir/monomorphize.rs b/users/grfn/achilles/src/passes/hir/monomorphize.rs
deleted file mode 100644
index 251a988f4f..0000000000
--- a/users/grfn/achilles/src/passes/hir/monomorphize.rs
+++ /dev/null
@@ -1,139 +0,0 @@
-use std::cell::RefCell;
-use std::collections::{HashMap, HashSet};
-use std::convert::TryInto;
-use std::mem;
-
-use void::{ResultVoidExt, Void};
-
-use crate::ast::hir::{Decl, Expr};
-use crate::ast::{self, Ident};
-
-use super::Visitor;
-
-#[derive(Default)]
-pub(crate) struct Monomorphize<'a, 'ast> {
-    decls: HashMap<&'a Ident<'ast>, &'a Decl<'ast, ast::Type<'ast>>>,
-    extra_decls: Vec<Decl<'ast, ast::Type<'ast>>>,
-    remove_decls: HashSet<Ident<'ast>>,
-}
-
-impl<'a, 'ast> Monomorphize<'a, 'ast> {
-    pub(crate) fn new() -> Self {
-        Default::default()
-    }
-}
-
-impl<'a, 'ast> Visitor<'a, 'ast, ast::Type<'ast>> for Monomorphize<'a, 'ast> {
-    type Error = Void;
-
-    fn post_visit_call(
-        &mut self,
-        fun: &mut Expr<'ast, ast::Type<'ast>>,
-        type_args: &mut HashMap<Ident<'ast>, ast::Type<'ast>>,
-        args: &mut Vec<Expr<'ast, ast::Type<'ast>>>,
-    ) -> Result<(), Self::Error> {
-        let new_fun = match fun {
-            Expr::Ident(id, _) => {
-                let decl: Decl<_> = (**self.decls.get(id).unwrap()).clone();
-                let name = RefCell::new(id.to_string());
-                let type_args = mem::take(type_args);
-                let mut monomorphized = decl
-                    .traverse_type(|ty| -> Result<_, Void> {
-                        Ok(ty.clone().traverse_type_vars(|v| {
-                            let concrete = type_args.get(&v).unwrap();
-                            name.borrow_mut().push_str(&concrete.to_string());
-                            concrete.clone()
-                        }))
-                    })
-                    .void_unwrap();
-                let name: Ident = name.into_inner().try_into().unwrap();
-                if name != *id {
-                    self.remove_decls.insert(id.clone());
-                    monomorphized.set_name(name.clone());
-                    let type_ = monomorphized.type_().unwrap().clone();
-                    self.extra_decls.push(monomorphized);
-                    Some(Expr::Ident(name, type_))
-                } else {
-                    None
-                }
-            }
-            _ => todo!(),
-        };
-        if let Some(new_fun) = new_fun {
-            *fun = new_fun;
-        }
-        Ok(())
-    }
-
-    fn post_visit_decl(
-        &mut self,
-        decl: &'a Decl<'ast, ast::Type<'ast>>,
-    ) -> Result<(), Self::Error> {
-        self.decls.insert(decl.name(), decl);
-        Ok(())
-    }
-}
-
-pub(crate) fn run_toplevel<'a>(toplevel: &mut Vec<Decl<'a, ast::Type<'a>>>) {
-    let mut pass = Monomorphize::new();
-    for decl in toplevel.iter_mut() {
-        pass.visit_decl(decl).void_unwrap();
-    }
-    let remove_decls = mem::take(&mut pass.remove_decls);
-    let mut extra_decls = mem::take(&mut pass.extra_decls);
-    toplevel.retain(|decl| !remove_decls.contains(decl.name()));
-    extra_decls.append(toplevel);
-    *toplevel = extra_decls;
-}
-
-#[cfg(test)]
-mod tests {
-    use std::convert::TryFrom;
-
-    use super::*;
-    use crate::parser::toplevel;
-    use crate::tc::typecheck_toplevel;
-
-    #[test]
-    fn call_id_decl() {
-        let (_, program) = toplevel(
-            "ty id : fn a -> a
-             fn id x = x
-
-             ty main : fn -> int
-             fn main = id 0",
-        )
-        .unwrap();
-        let mut program = typecheck_toplevel(program).unwrap();
-        run_toplevel(&mut program);
-
-        let find_decl = |ident: &str| {
-            program.iter().find(|decl| {
-                matches!(decl, Decl::Fun {name, ..} if name == &Ident::try_from(ident).unwrap())
-            }).unwrap()
-        };
-
-        let main = find_decl("main");
-        let body = match main {
-            Decl::Fun { body, .. } => body,
-            _ => unreachable!(),
-        };
-
-        let expected_type = ast::Type::Function(ast::FunctionType {
-            args: vec![ast::Type::Int],
-            ret: Box::new(ast::Type::Int),
-        });
-
-        match &**body {
-            Expr::Call { fun, .. } => {
-                let fun = match &**fun {
-                    Expr::Ident(fun, _) => fun,
-                    _ => unreachable!(),
-                };
-                let called_decl = find_decl(fun.into());
-                assert_eq!(called_decl.type_().unwrap(), &expected_type);
-            }
-            _ => unreachable!(),
-        }
-    }
-}
diff --git a/users/grfn/achilles/src/passes/hir/strip_positive_units.rs b/users/grfn/achilles/src/passes/hir/strip_positive_units.rs
deleted file mode 100644
index 85ee1cce48..0000000000
--- a/users/grfn/achilles/src/passes/hir/strip_positive_units.rs
+++ /dev/null
@@ -1,191 +0,0 @@
-use std::collections::HashMap;
-use std::mem;
-
-use ast::hir::{Binding, Pattern};
-use ast::Literal;
-use void::{ResultVoidExt, Void};
-
-use crate::ast::hir::{Decl, Expr};
-use crate::ast::{self, Ident};
-
-use super::Visitor;
-
-/// Strip all values with a unit type in positive (non-return) position
-pub(crate) struct StripPositiveUnits {}
-
-impl<'a, 'ast> Visitor<'a, 'ast, ast::Type<'ast>> for StripPositiveUnits {
-    type Error = Void;
-
-    fn pre_visit_expr(
-        &mut self,
-        expr: &mut Expr<'ast, ast::Type<'ast>>,
-    ) -> Result<(), Self::Error> {
-        let mut extracted = vec![];
-        if let Expr::Call { args, .. } = expr {
-            // TODO(grfn): replace with drain_filter once it's stabilized
-            let mut i = 0;
-            while i != args.len() {
-                if args[i].type_() == &ast::Type::Unit {
-                    let expr = args.remove(i);
-                    if !matches!(expr, Expr::Literal(Literal::Unit, _)) {
-                        extracted.push(expr)
-                    };
-                } else {
-                    i += 1
-                }
-            }
-        }
-
-        if !extracted.is_empty() {
-            let body = mem::replace(expr, Expr::Literal(Literal::Unit, ast::Type::Unit));
-            *expr = Expr::Let {
-                bindings: extracted
-                    .into_iter()
-                    .map(|expr| Binding {
-                        pat: Pattern::Id(
-                            Ident::from_str_unchecked("___discarded"),
-                            expr.type_().clone(),
-                        ),
-                        body: expr,
-                    })
-                    .collect(),
-                type_: body.type_().clone(),
-                body: Box::new(body),
-            };
-        }
-
-        Ok(())
-    }
-
-    fn post_visit_call(
-        &mut self,
-        _fun: &mut Expr<'ast, ast::Type<'ast>>,
-        _type_args: &mut HashMap<Ident<'ast>, ast::Type<'ast>>,
-        args: &mut Vec<Expr<'ast, ast::Type<'ast>>>,
-    ) -> Result<(), Self::Error> {
-        args.retain(|arg| arg.type_() != &ast::Type::Unit);
-        Ok(())
-    }
-
-    fn visit_type(&mut self, type_: &mut ast::Type<'ast>) -> Result<(), Self::Error> {
-        if let ast::Type::Function(ft) = type_ {
-            ft.args.retain(|a| a != &ast::Type::Unit);
-        }
-        Ok(())
-    }
-
-    fn post_visit_fun_decl(
-        &mut self,
-        _name: &mut Ident<'ast>,
-        _type_args: &mut Vec<Ident>,
-        args: &mut Vec<(Ident, ast::Type<'ast>)>,
-        _body: &mut Box<Expr<ast::Type<'ast>>>,
-        _type_: &mut ast::Type<'ast>,
-    ) -> Result<(), Self::Error> {
-        args.retain(|(_, ty)| ty != &ast::Type::Unit);
-        Ok(())
-    }
-}
-
-pub(crate) fn run_toplevel<'a>(toplevel: &mut Vec<Decl<'a, ast::Type<'a>>>) {
-    let mut pass = StripPositiveUnits {};
-    for decl in toplevel.iter_mut() {
-        pass.visit_decl(decl).void_unwrap();
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::parser::toplevel;
-    use crate::tc::typecheck_toplevel;
-    use pretty_assertions::assert_eq;
-
-    #[test]
-    fn unit_only_arg() {
-        let (_, program) = toplevel(
-            "ty f : fn () -> int
-             fn f _ = 1
-
-             ty main : fn -> int
-             fn main = f ()",
-        )
-        .unwrap();
-
-        let (_, expected) = toplevel(
-            "ty f : fn -> int
-             fn f = 1
-
-             ty main : fn -> int
-             fn main = f()",
-        )
-        .unwrap();
-        let expected = typecheck_toplevel(expected).unwrap();
-
-        let mut program = typecheck_toplevel(program).unwrap();
-        run_toplevel(&mut program);
-
-        assert_eq!(program, expected);
-    }
-
-    #[test]
-    fn unit_and_other_arg() {
-        let (_, program) = toplevel(
-            "ty f : fn (), int -> int
-             fn f _ x = x
-
-             ty main : fn -> int
-             fn main = f () 1",
-        )
-        .unwrap();
-
-        let (_, expected) = toplevel(
-            "ty f : fn int -> int
-             fn f x = x
-
-             ty main : fn -> int
-             fn main = f 1",
-        )
-        .unwrap();
-        let expected = typecheck_toplevel(expected).unwrap();
-
-        let mut program = typecheck_toplevel(program).unwrap();
-        run_toplevel(&mut program);
-
-        assert_eq!(program, expected);
-    }
-
-    #[test]
-    fn unit_expr_and_other_arg() {
-        let (_, program) = toplevel(
-            "ty f : fn (), int -> int
-             fn f _ x = x
-
-             ty g : fn int -> ()
-             fn g _ = ()
-
-             ty main : fn -> int
-             fn main = f (g 2) 1",
-        )
-        .unwrap();
-
-        let (_, expected) = toplevel(
-            "ty f : fn int -> int
-             fn f x = x
-
-             ty g : fn int -> ()
-             fn g _ = ()
-
-             ty main : fn -> int
-             fn main = let ___discarded = g 2 in f 1",
-        )
-        .unwrap();
-        assert_eq!(expected.len(), 6);
-        let expected = typecheck_toplevel(expected).unwrap();
-
-        let mut program = typecheck_toplevel(program).unwrap();
-        run_toplevel(&mut program);
-
-        assert_eq!(program, expected);
-    }
-}
diff --git a/users/grfn/achilles/src/passes/mod.rs b/users/grfn/achilles/src/passes/mod.rs
deleted file mode 100644
index 306869bef1..0000000000
--- a/users/grfn/achilles/src/passes/mod.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub(crate) mod hir;
diff --git a/users/grfn/achilles/src/tc/mod.rs b/users/grfn/achilles/src/tc/mod.rs
deleted file mode 100644
index 5825bab1fb..0000000000
--- a/users/grfn/achilles/src/tc/mod.rs
+++ /dev/null
@@ -1,808 +0,0 @@
-use bimap::BiMap;
-use derive_more::From;
-use itertools::Itertools;
-use std::cell::RefCell;
-use std::collections::HashMap;
-use std::convert::{TryFrom, TryInto};
-use std::fmt::{self, Display};
-use std::{mem, result};
-use thiserror::Error;
-
-use crate::ast::{self, hir, Arg, BinaryOperator, Ident, Literal, Pattern};
-use crate::common::env::Env;
-use crate::common::{Namer, NamerOf};
-
-#[derive(Debug, Error)]
-pub enum Error {
-    #[error("Undefined variable {0}")]
-    UndefinedVariable(Ident<'static>),
-
-    #[error("Mismatched types: expected {expected}, but got {actual}")]
-    TypeMismatch { expected: Type, actual: Type },
-
-    #[error("Mismatched types, expected numeric type, but got {0}")]
-    NonNumeric(Type),
-
-    #[error("Ambiguous type {0}")]
-    AmbiguousType(TyVar),
-}
-
-pub type Result<T> = result::Result<T, Error>;
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
-pub struct TyVar(u64);
-
-impl Display for TyVar {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "t{}", self.0)
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Hash)]
-pub struct NullaryType(String);
-
-impl Display for NullaryType {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str(&self.0)
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum PrimType {
-    Int,
-    Float,
-    Bool,
-    CString,
-}
-
-impl<'a> From<PrimType> for ast::Type<'a> {
-    fn from(pr: PrimType) -> Self {
-        match pr {
-            PrimType::Int => ast::Type::Int,
-            PrimType::Float => ast::Type::Float,
-            PrimType::Bool => ast::Type::Bool,
-            PrimType::CString => ast::Type::CString,
-        }
-    }
-}
-
-impl Display for PrimType {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            PrimType::Int => f.write_str("int"),
-            PrimType::Float => f.write_str("float"),
-            PrimType::Bool => f.write_str("bool"),
-            PrimType::CString => f.write_str("cstring"),
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, From)]
-pub enum Type {
-    #[from(ignore)]
-    Univ(TyVar),
-    #[from(ignore)]
-    Exist(TyVar),
-    Nullary(NullaryType),
-    Prim(PrimType),
-    Tuple(Vec<Type>),
-    Unit,
-    Fun {
-        args: Vec<Type>,
-        ret: Box<Type>,
-    },
-}
-
-impl<'a> TryFrom<Type> for ast::Type<'a> {
-    type Error = Type;
-
-    fn try_from(value: Type) -> result::Result<Self, Self::Error> {
-        match value {
-            Type::Unit => Ok(ast::Type::Unit),
-            Type::Univ(_) => todo!(),
-            Type::Exist(_) => Err(value),
-            Type::Nullary(_) => todo!(),
-            Type::Prim(p) => Ok(p.into()),
-            Type::Tuple(members) => Ok(ast::Type::Tuple(
-                members.into_iter().map(|ty| ty.try_into()).try_collect()?,
-            )),
-            Type::Fun { ref args, ref ret } => Ok(ast::Type::Function(ast::FunctionType {
-                args: args
-                    .clone()
-                    .into_iter()
-                    .map(Self::try_from)
-                    .try_collect()
-                    .map_err(|_| value.clone())?,
-                ret: Box::new((*ret.clone()).try_into().map_err(|_| value.clone())?),
-            })),
-        }
-    }
-}
-
-const INT: Type = Type::Prim(PrimType::Int);
-const FLOAT: Type = Type::Prim(PrimType::Float);
-const BOOL: Type = Type::Prim(PrimType::Bool);
-const CSTRING: Type = Type::Prim(PrimType::CString);
-
-impl Display for Type {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Type::Nullary(nt) => nt.fmt(f),
-            Type::Prim(p) => p.fmt(f),
-            Type::Univ(TyVar(n)) => write!(f, "∀{}", n),
-            Type::Exist(TyVar(n)) => write!(f, "∃{}", n),
-            Type::Fun { args, ret } => write!(f, "fn {} -> {}", args.iter().join(", "), ret),
-            Type::Tuple(members) => write!(f, "({})", members.iter().join(", ")),
-            Type::Unit => write!(f, "()"),
-        }
-    }
-}
-
-struct Typechecker<'ast> {
-    ty_var_namer: NamerOf<TyVar>,
-    ctx: HashMap<TyVar, Type>,
-    env: Env<Ident<'ast>, Type>,
-
-    /// AST type var -> type
-    instantiations: Env<Ident<'ast>, Type>,
-
-    /// AST type-var -> universal TyVar
-    type_vars: RefCell<(BiMap<Ident<'ast>, TyVar>, NamerOf<Ident<'static>>)>,
-}
-
-impl<'ast> Typechecker<'ast> {
-    fn new() -> Self {
-        Self {
-            ty_var_namer: Namer::new(TyVar).boxed(),
-            type_vars: RefCell::new((
-                Default::default(),
-                Namer::alphabetic().map(|n| Ident::try_from(n).unwrap()),
-            )),
-            ctx: Default::default(),
-            env: Default::default(),
-            instantiations: Default::default(),
-        }
-    }
-
-    fn bind_pattern(
-        &mut self,
-        pat: Pattern<'ast>,
-        type_: Type,
-    ) -> Result<hir::Pattern<'ast, Type>> {
-        match pat {
-            Pattern::Id(ident) => {
-                self.env.set(ident.clone(), type_.clone());
-                Ok(hir::Pattern::Id(ident, type_))
-            }
-            Pattern::Tuple(members) => {
-                let mut tys = Vec::with_capacity(members.len());
-                let mut hir_members = Vec::with_capacity(members.len());
-                for pat in members {
-                    let ty = self.fresh_ex();
-                    hir_members.push(self.bind_pattern(pat, ty.clone())?);
-                    tys.push(ty);
-                }
-                let tuple_type = Type::Tuple(tys);
-                self.unify(&tuple_type, &type_)?;
-                Ok(hir::Pattern::Tuple(hir_members))
-            }
-        }
-    }
-
-    pub(crate) fn tc_expr(&mut self, expr: ast::Expr<'ast>) -> Result<hir::Expr<'ast, Type>> {
-        match expr {
-            ast::Expr::Ident(ident) => {
-                let type_ = self
-                    .env
-                    .resolve(&ident)
-                    .ok_or_else(|| Error::UndefinedVariable(ident.to_owned()))?
-                    .clone();
-                Ok(hir::Expr::Ident(ident, type_))
-            }
-            ast::Expr::Literal(lit) => {
-                let type_ = match lit {
-                    Literal::Int(_) => Type::Prim(PrimType::Int),
-                    Literal::Bool(_) => Type::Prim(PrimType::Bool),
-                    Literal::String(_) => Type::Prim(PrimType::CString),
-                    Literal::Unit => Type::Unit,
-                };
-                Ok(hir::Expr::Literal(lit.to_owned(), type_))
-            }
-            ast::Expr::Tuple(members) => {
-                let members = members
-                    .into_iter()
-                    .map(|expr| self.tc_expr(expr))
-                    .collect::<Result<Vec<_>>>()?;
-                let type_ = Type::Tuple(members.iter().map(|expr| expr.type_().clone()).collect());
-                Ok(hir::Expr::Tuple(members, type_))
-            }
-            ast::Expr::UnaryOp { op, rhs } => todo!(),
-            ast::Expr::BinaryOp { lhs, op, rhs } => {
-                let lhs = self.tc_expr(*lhs)?;
-                let rhs = self.tc_expr(*rhs)?;
-                let type_ = match op {
-                    BinaryOperator::Equ | BinaryOperator::Neq => {
-                        self.unify(lhs.type_(), rhs.type_())?;
-                        Type::Prim(PrimType::Bool)
-                    }
-                    BinaryOperator::Add | BinaryOperator::Sub | BinaryOperator::Mul => {
-                        let ty = self.unify(lhs.type_(), rhs.type_())?;
-                        // if !matches!(ty, Type::Int | Type::Float) {
-                        //     return Err(Error::NonNumeric(ty));
-                        // }
-                        ty
-                    }
-                    BinaryOperator::Div => todo!(),
-                    BinaryOperator::Pow => todo!(),
-                };
-                Ok(hir::Expr::BinaryOp {
-                    lhs: Box::new(lhs),
-                    op,
-                    rhs: Box::new(rhs),
-                    type_,
-                })
-            }
-            ast::Expr::Let { bindings, body } => {
-                self.env.push();
-                let bindings = bindings
-                    .into_iter()
-                    .map(
-                        |ast::Binding { pat, type_, body }| -> Result<hir::Binding<Type>> {
-                            let body = self.tc_expr(body)?;
-                            if let Some(type_) = type_ {
-                                let type_ = self.type_from_ast_type(type_);
-                                self.unify(body.type_(), &type_)?;
-                            }
-                            let pat = self.bind_pattern(pat, body.type_().clone())?;
-                            Ok(hir::Binding { pat, body })
-                        },
-                    )
-                    .collect::<Result<Vec<hir::Binding<Type>>>>()?;
-                let body = self.tc_expr(*body)?;
-                self.env.pop();
-                Ok(hir::Expr::Let {
-                    bindings,
-                    type_: body.type_().clone(),
-                    body: Box::new(body),
-                })
-            }
-            ast::Expr::If {
-                condition,
-                then,
-                else_,
-            } => {
-                let condition = self.tc_expr(*condition)?;
-                self.unify(&Type::Prim(PrimType::Bool), condition.type_())?;
-                let then = self.tc_expr(*then)?;
-                let else_ = self.tc_expr(*else_)?;
-                let type_ = self.unify(then.type_(), else_.type_())?;
-                Ok(hir::Expr::If {
-                    condition: Box::new(condition),
-                    then: Box::new(then),
-                    else_: Box::new(else_),
-                    type_,
-                })
-            }
-            ast::Expr::Fun(f) => {
-                let ast::Fun { args, body } = *f;
-                self.env.push();
-                let args: Vec<_> = args
-                    .into_iter()
-                    .map(|Arg { ident, type_ }| {
-                        let ty = match type_ {
-                            Some(t) => self.type_from_ast_type(t),
-                            None => self.fresh_ex(),
-                        };
-                        self.env.set(ident.clone(), ty.clone());
-                        (ident, ty)
-                    })
-                    .collect();
-                let body = self.tc_expr(body)?;
-                self.env.pop();
-                Ok(hir::Expr::Fun {
-                    type_: Type::Fun {
-                        args: args.iter().map(|(_, ty)| ty.clone()).collect(),
-                        ret: Box::new(body.type_().clone()),
-                    },
-                    type_args: vec![], // TODO fill in once we do let generalization
-                    args,
-                    body: Box::new(body),
-                })
-            }
-            ast::Expr::Call { fun, args } => {
-                let ret_ty = self.fresh_ex();
-                let arg_tys = args.iter().map(|_| self.fresh_ex()).collect::<Vec<_>>();
-                let ft = Type::Fun {
-                    args: arg_tys.clone(),
-                    ret: Box::new(ret_ty.clone()),
-                };
-                let fun = self.tc_expr(*fun)?;
-                self.instantiations.push();
-                self.unify(&ft, fun.type_())?;
-                let args = args
-                    .into_iter()
-                    .zip(arg_tys)
-                    .map(|(arg, ty)| {
-                        let arg = self.tc_expr(arg)?;
-                        self.unify(&ty, arg.type_())?;
-                        Ok(arg)
-                    })
-                    .try_collect()?;
-                let type_args = self.commit_instantiations();
-                Ok(hir::Expr::Call {
-                    fun: Box::new(fun),
-                    type_args,
-                    args,
-                    type_: ret_ty,
-                })
-            }
-            ast::Expr::Ascription { expr, type_ } => {
-                let expr = self.tc_expr(*expr)?;
-                let type_ = self.type_from_ast_type(type_);
-                self.unify(expr.type_(), &type_)?;
-                Ok(expr)
-            }
-        }
-    }
-
-    pub(crate) fn tc_decl(
-        &mut self,
-        decl: ast::Decl<'ast>,
-    ) -> Result<Option<hir::Decl<'ast, Type>>> {
-        match decl {
-            ast::Decl::Fun { name, body } => {
-                let mut expr = ast::Expr::Fun(Box::new(body));
-                if let Some(type_) = self.env.resolve(&name) {
-                    expr = ast::Expr::Ascription {
-                        expr: Box::new(expr),
-                        type_: self.finalize_type(type_.clone())?,
-                    };
-                }
-
-                self.env.push();
-                let body = self.tc_expr(expr)?;
-                let type_ = body.type_().clone();
-                self.env.set(name.clone(), type_);
-                self.env.pop();
-                match body {
-                    hir::Expr::Fun {
-                        type_args,
-                        args,
-                        body,
-                        type_,
-                    } => Ok(Some(hir::Decl::Fun {
-                        name,
-                        type_args,
-                        args,
-                        body,
-                        type_,
-                    })),
-                    _ => unreachable!(),
-                }
-            }
-            ast::Decl::Ascription { name, type_ } => {
-                let type_ = self.type_from_ast_type(type_);
-                self.env.set(name.clone(), type_);
-                Ok(None)
-            }
-            ast::Decl::Extern { name, type_ } => {
-                let type_ = self.type_from_ast_type(ast::Type::Function(type_));
-                self.env.set(name.clone(), type_.clone());
-                let (arg_types, ret_type) = match type_ {
-                    Type::Fun { args, ret } => (args, *ret),
-                    _ => unreachable!(),
-                };
-                Ok(Some(hir::Decl::Extern {
-                    name,
-                    arg_types,
-                    ret_type,
-                }))
-            }
-        }
-    }
-
-    fn fresh_tv(&mut self) -> TyVar {
-        self.ty_var_namer.make_name()
-    }
-
-    fn fresh_ex(&mut self) -> Type {
-        Type::Exist(self.fresh_tv())
-    }
-
-    fn fresh_univ(&mut self) -> Type {
-        Type::Univ(self.fresh_tv())
-    }
-
-    fn unify(&mut self, ty1: &Type, ty2: &Type) -> Result<Type> {
-        match (ty1, ty2) {
-            (Type::Unit, Type::Unit) => Ok(Type::Unit),
-            (Type::Exist(tv), ty) | (ty, Type::Exist(tv)) => match self.resolve_tv(*tv)? {
-                Some(existing_ty) if self.types_match(ty, &existing_ty) => Ok(ty.clone()),
-                Some(var @ ast::Type::Var(_)) => {
-                    let var = self.type_from_ast_type(var);
-                    self.unify(&var, ty)
-                }
-                Some(existing_ty) => match ty {
-                    Type::Exist(_) => {
-                        let rhs = self.type_from_ast_type(existing_ty);
-                        self.unify(ty, &rhs)
-                    }
-                    _ => Err(Error::TypeMismatch {
-                        expected: ty.clone(),
-                        actual: self.type_from_ast_type(existing_ty),
-                    }),
-                },
-                None => match self.ctx.insert(*tv, ty.clone()) {
-                    Some(existing) => self.unify(&existing, ty),
-                    None => Ok(ty.clone()),
-                },
-            },
-            (Type::Univ(u1), Type::Univ(u2)) if u1 == u2 => Ok(ty2.clone()),
-            (Type::Univ(u), ty) | (ty, Type::Univ(u)) => {
-                let ident = self.name_univ(*u);
-                match self.instantiations.resolve(&ident) {
-                    Some(existing_ty) if ty == existing_ty => Ok(ty.clone()),
-                    Some(existing_ty) => Err(Error::TypeMismatch {
-                        expected: ty.clone(),
-                        actual: existing_ty.clone(),
-                    }),
-                    None => {
-                        self.instantiations.set(ident, ty.clone());
-                        Ok(ty.clone())
-                    }
-                }
-            }
-            (Type::Prim(p1), Type::Prim(p2)) if p1 == p2 => Ok(ty2.clone()),
-            (Type::Tuple(t1), Type::Tuple(t2)) if t1.len() == t2.len() => {
-                let ts = t1
-                    .iter()
-                    .zip(t2.iter())
-                    .map(|(t1, t2)| self.unify(t1, t2))
-                    .try_collect()?;
-                Ok(Type::Tuple(ts))
-            }
-            (
-                Type::Fun {
-                    args: args1,
-                    ret: ret1,
-                },
-                Type::Fun {
-                    args: args2,
-                    ret: ret2,
-                },
-            ) => {
-                let args = args1
-                    .iter()
-                    .zip(args2)
-                    .map(|(t1, t2)| self.unify(t1, t2))
-                    .try_collect()?;
-                let ret = self.unify(ret1, ret2)?;
-                Ok(Type::Fun {
-                    args,
-                    ret: Box::new(ret),
-                })
-            }
-            (Type::Nullary(_), _) | (_, Type::Nullary(_)) => todo!(),
-            _ => Err(Error::TypeMismatch {
-                expected: ty1.clone(),
-                actual: ty2.clone(),
-            }),
-        }
-    }
-
-    fn finalize_expr(
-        &self,
-        expr: hir::Expr<'ast, Type>,
-    ) -> Result<hir::Expr<'ast, ast::Type<'ast>>> {
-        expr.traverse_type(|ty| self.finalize_type(ty))
-    }
-
-    fn finalize_decl(
-        &mut self,
-        decl: hir::Decl<'ast, Type>,
-    ) -> Result<hir::Decl<'ast, ast::Type<'ast>>> {
-        let res = decl.traverse_type(|ty| self.finalize_type(ty))?;
-        if let Some(type_) = res.type_() {
-            let ty = self.type_from_ast_type(type_.clone());
-            self.env.set(res.name().clone(), ty);
-        }
-        Ok(res)
-    }
-
-    fn finalize_type(&self, ty: Type) -> Result<ast::Type<'static>> {
-        let ret = match ty {
-            Type::Exist(tv) => self.resolve_tv(tv)?.ok_or(Error::AmbiguousType(tv)),
-            Type::Univ(tv) => Ok(ast::Type::Var(self.name_univ(tv))),
-            Type::Unit => Ok(ast::Type::Unit),
-            Type::Nullary(_) => todo!(),
-            Type::Prim(pr) => Ok(pr.into()),
-            Type::Tuple(members) => Ok(ast::Type::Tuple(
-                members
-                    .into_iter()
-                    .map(|ty| self.finalize_type(ty))
-                    .try_collect()?,
-            )),
-            Type::Fun { args, ret } => Ok(ast::Type::Function(ast::FunctionType {
-                args: args
-                    .into_iter()
-                    .map(|ty| self.finalize_type(ty))
-                    .try_collect()?,
-                ret: Box::new(self.finalize_type(*ret)?),
-            })),
-        };
-        ret
-    }
-
-    fn resolve_tv(&self, tv: TyVar) -> Result<Option<ast::Type<'static>>> {
-        let mut res = &Type::Exist(tv);
-        Ok(loop {
-            match res {
-                Type::Exist(tv) => {
-                    res = match self.ctx.get(tv) {
-                        Some(r) => r,
-                        None => return Ok(None),
-                    };
-                }
-                Type::Univ(tv) => {
-                    let ident = self.name_univ(*tv);
-                    if let Some(r) = self.instantiations.resolve(&ident) {
-                        res = r;
-                    } else {
-                        break Some(ast::Type::Var(ident));
-                    }
-                }
-                Type::Nullary(_) => todo!(),
-                Type::Prim(pr) => break Some((*pr).into()),
-                Type::Unit => break Some(ast::Type::Unit),
-                Type::Fun { args, ret } => todo!(),
-                Type::Tuple(_) => break Some(self.finalize_type(res.clone())?),
-            }
-        })
-    }
-
-    fn type_from_ast_type(&mut self, ast_type: ast::Type<'ast>) -> Type {
-        match ast_type {
-            ast::Type::Unit => Type::Unit,
-            ast::Type::Int => INT,
-            ast::Type::Float => FLOAT,
-            ast::Type::Bool => BOOL,
-            ast::Type::CString => CSTRING,
-            ast::Type::Tuple(members) => Type::Tuple(
-                members
-                    .into_iter()
-                    .map(|ty| self.type_from_ast_type(ty))
-                    .collect(),
-            ),
-            ast::Type::Function(ast::FunctionType { args, ret }) => Type::Fun {
-                args: args
-                    .into_iter()
-                    .map(|t| self.type_from_ast_type(t))
-                    .collect(),
-                ret: Box::new(self.type_from_ast_type(*ret)),
-            },
-            ast::Type::Var(id) => Type::Univ({
-                let opt_tv = { self.type_vars.borrow_mut().0.get_by_left(&id).copied() };
-                opt_tv.unwrap_or_else(|| {
-                    let tv = self.fresh_tv();
-                    self.type_vars
-                        .borrow_mut()
-                        .0
-                        .insert_no_overwrite(id, tv)
-                        .unwrap();
-                    tv
-                })
-            }),
-        }
-    }
-
-    fn name_univ(&self, tv: TyVar) -> Ident<'static> {
-        let mut vars = self.type_vars.borrow_mut();
-        vars.0
-            .get_by_right(&tv)
-            .map(Ident::to_owned)
-            .unwrap_or_else(|| {
-                let name = loop {
-                    let name = vars.1.make_name();
-                    if !vars.0.contains_left(&name) {
-                        break name;
-                    }
-                };
-                vars.0.insert_no_overwrite(name.clone(), tv).unwrap();
-                name
-            })
-    }
-
-    fn commit_instantiations(&mut self) -> HashMap<Ident<'ast>, Type> {
-        let mut res = HashMap::new();
-        let mut ctx = mem::take(&mut self.ctx);
-        for (_, v) in ctx.iter_mut() {
-            if let Type::Univ(tv) = v {
-                let tv_name = self.name_univ(*tv);
-                if let Some(concrete) = self.instantiations.resolve(&tv_name) {
-                    res.insert(tv_name, concrete.clone());
-                    *v = concrete.clone();
-                }
-            }
-        }
-        self.ctx = ctx;
-        self.instantiations.pop();
-        res
-    }
-
-    fn types_match(&self, type_: &Type, ast_type: &ast::Type<'ast>) -> bool {
-        match (type_, ast_type) {
-            (Type::Univ(u), ast::Type::Var(v)) => {
-                Some(u) == self.type_vars.borrow().0.get_by_left(v)
-            }
-            (Type::Univ(_), _) => false,
-            (Type::Exist(_), _) => false,
-            (Type::Unit, ast::Type::Unit) => true,
-            (Type::Unit, _) => false,
-            (Type::Nullary(_), _) => todo!(),
-            (Type::Prim(pr), ty) => ast::Type::from(*pr) == *ty,
-            (Type::Tuple(members), ast::Type::Tuple(members2)) => members
-                .iter()
-                .zip(members2.iter())
-                .all(|(t1, t2)| self.types_match(t1, t2)),
-            (Type::Tuple(members), _) => false,
-            (Type::Fun { args, ret }, ast::Type::Function(ft)) => {
-                args.len() == ft.args.len()
-                    && args
-                        .iter()
-                        .zip(&ft.args)
-                        .all(|(a1, a2)| self.types_match(a1, &a2))
-                    && self.types_match(&*ret, &*ft.ret)
-            }
-            (Type::Fun { .. }, _) => false,
-        }
-    }
-}
-
-pub fn typecheck_expr(expr: ast::Expr) -> Result<hir::Expr<ast::Type>> {
-    let mut typechecker = Typechecker::new();
-    let typechecked = typechecker.tc_expr(expr)?;
-    typechecker.finalize_expr(typechecked)
-}
-
-pub fn typecheck_toplevel(decls: Vec<ast::Decl>) -> Result<Vec<hir::Decl<ast::Type>>> {
-    let mut typechecker = Typechecker::new();
-    let mut res = Vec::with_capacity(decls.len());
-    for decl in decls {
-        if let Some(hir_decl) = typechecker.tc_decl(decl)? {
-            let hir_decl = typechecker.finalize_decl(hir_decl)?;
-            res.push(hir_decl);
-        }
-        typechecker.ctx.clear();
-    }
-    Ok(res)
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    macro_rules! assert_type {
-        ($expr: expr, $type: expr) => {
-            use crate::parser::{expr, type_};
-            let parsed_expr = test_parse!(expr, $expr);
-            let parsed_type = test_parse!(type_, $type);
-            let res = typecheck_expr(parsed_expr).unwrap_or_else(|e| panic!("{}", e));
-            assert!(
-                res.type_().alpha_equiv(&parsed_type),
-                "{} inferred type {}, but expected {}",
-                $expr,
-                res.type_(),
-                $type
-            );
-        };
-
-        (toplevel($program: expr), $($decl: ident => $type: expr),+ $(,)?) => {{
-            use crate::parser::{toplevel, type_};
-            let program = test_parse!(toplevel, $program);
-            let res = typecheck_toplevel(program).unwrap_or_else(|e| panic!("{}", e));
-            $(
-            let parsed_type = test_parse!(type_, $type);
-            let ident = Ident::try_from(::std::stringify!($decl)).unwrap();
-            let decl = res.iter().find(|decl| {
-                matches!(decl, crate::ast::hir::Decl::Fun { name, .. } if name == &ident)
-            }).unwrap_or_else(|| panic!("Could not find declaration for {}", ident));
-            assert!(
-                decl.type_().unwrap().alpha_equiv(&parsed_type),
-                "inferred type {} for {}, but expected {}",
-                decl.type_().unwrap(),
-                ident,
-                $type
-            );
-            )+
-        }};
-    }
-
-    macro_rules! assert_type_error {
-        ($expr: expr) => {
-            use crate::parser::expr;
-            let parsed_expr = test_parse!(expr, $expr);
-            let res = typecheck_expr(parsed_expr);
-            assert!(
-                res.is_err(),
-                "Expected type error, but got type: {}",
-                res.unwrap().type_()
-            );
-        };
-    }
-
-    #[test]
-    fn literal_int() {
-        assert_type!("1", "int");
-    }
-
-    #[test]
-    fn conditional() {
-        assert_type!("if 1 == 2 then 3 else 4", "int");
-    }
-
-    #[test]
-    #[ignore]
-    fn add_bools() {
-        assert_type_error!("true + false");
-    }
-
-    #[test]
-    fn call_generic_function() {
-        assert_type!("(fn x = x) 1", "int");
-    }
-
-    #[test]
-    fn call_let_bound_generic() {
-        assert_type!("let id = fn x = x in id 1", "int");
-    }
-
-    #[test]
-    fn universal_ascripted_let() {
-        assert_type!("let id: fn a -> a = fn x = x in id 1", "int");
-    }
-
-    #[test]
-    fn call_generic_function_toplevel() {
-        assert_type!(
-            toplevel(
-                "ty id : fn a -> a
-                 fn id x = x
-
-                 fn main = id 0"
-            ),
-            main => "fn -> int",
-            id => "fn a -> a",
-        );
-    }
-
-    #[test]
-    #[ignore]
-    fn let_generalization() {
-        assert_type!("let id = fn x = x in if id true then id 1 else 2", "int");
-    }
-
-    #[test]
-    fn concrete_function() {
-        assert_type!("fn x = x + 1", "fn int -> int");
-    }
-
-    #[test]
-    fn arg_ascriptions() {
-        assert_type!("fn (x: int) = x", "fn int -> int");
-    }
-
-    #[test]
-    fn call_concrete_function() {
-        assert_type!("(fn x = x + 1) 2", "int");
-    }
-
-    #[test]
-    fn conditional_non_bool() {
-        assert_type_error!("if 3 then true else false");
-    }
-
-    #[test]
-    fn let_int() {
-        assert_type!("let x = 1 in x", "int");
-    }
-}
diff --git a/users/grfn/achilles/tests/compile.rs b/users/grfn/achilles/tests/compile.rs
deleted file mode 100644
index 0f1086bfd8..0000000000
--- a/users/grfn/achilles/tests/compile.rs
+++ /dev/null
@@ -1,79 +0,0 @@
-use std::process::Command;
-
-use crate_root::root;
-
-struct Fixture {
-    name: &'static str,
-    exit_code: i32,
-    expected_output: &'static str,
-}
-
-const FIXTURES: &[Fixture] = &[
-    Fixture {
-        name: "simple",
-        exit_code: 5,
-        expected_output: "",
-    },
-    Fixture {
-        name: "functions",
-        exit_code: 9,
-        expected_output: "",
-    },
-    Fixture {
-        name: "externs",
-        exit_code: 0,
-        expected_output: "foobar\n",
-    },
-    Fixture {
-        name: "units",
-        exit_code: 0,
-        expected_output: "hi\n",
-    },
-];
-
-#[test]
-fn compile_and_run_files() {
-    let ach = root().unwrap().join("ach");
-
-    println!("Running: `make clean`");
-    assert!(
-        Command::new("make")
-            .arg("clean")
-            .current_dir(&ach)
-            .spawn()
-            .unwrap()
-            .wait()
-            .unwrap()
-            .success(),
-        "make clean failed"
-    );
-
-    for Fixture {
-        name,
-        exit_code,
-        expected_output,
-    } in FIXTURES
-    {
-        println!(">>> Testing: {}", name);
-
-        println!("    Running: `make {}`", name);
-        assert!(
-            Command::new("make")
-                .arg(name)
-                .current_dir(&ach)
-                .spawn()
-                .unwrap()
-                .wait()
-                .unwrap()
-                .success(),
-            "make failed"
-        );
-
-        let out_path = ach.join(name);
-        println!("    Running: `{}`", out_path.to_str().unwrap());
-        let output = Command::new(out_path).output().unwrap();
-        assert_eq!(output.status.code().unwrap(), *exit_code,);
-        assert_eq!(output.stdout, expected_output.as_bytes());
-        println!("    OK");
-    }
-}