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