mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-19 06:40:20 +03:00
wip
This commit is contained in:
parent
59bdb21ea2
commit
422f8cc37a
8
BUILDING_FROM_SOURCE.md
Normal file
8
BUILDING_FROM_SOURCE.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Building the Roc compiler from source
|
||||
|
||||
|
||||
## Installing LLVM
|
||||
|
||||
To build the compiler, you need LLVM installed somwhere on your `PATH`.
|
||||
|
||||
For Ubuntu, I used the `Automatic installation script` at [apt.llvm.org](https://apt.llvm.org)
|
167
Cargo.lock
generated
167
Cargo.lock
generated
@ -36,6 +36,21 @@ name = "byteorder"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cargo_toml"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.6"
|
||||
@ -59,6 +74,20 @@ name = "dogged"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "enum-methods"
|
||||
version = "0.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.6.2"
|
||||
@ -126,6 +155,31 @@ dependencies = [
|
||||
"unindent 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inkwell"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/TheDan64/inkwell?branch=llvm8-0#d0f5c1e198853bc06d8427fbafb7b068032d1d1a"
|
||||
dependencies = [
|
||||
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"inkwell_internal_macros 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm8-0)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"llvm-sys 80.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inkwell_internal_macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/TheDan64/inkwell?branch=llvm8-0#d0f5c1e198853bc06d8427fbafb7b068032d1d1a"
|
||||
dependencies = [
|
||||
"cargo_toml 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.3.0"
|
||||
@ -136,6 +190,18 @@ name = "libc"
|
||||
version = "0.2.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "llvm-sys"
|
||||
version = "80.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.8"
|
||||
@ -264,6 +330,14 @@ dependencies = [
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quickcheck"
|
||||
version = "0.8.5"
|
||||
@ -285,6 +359,11 @@ dependencies = [
|
||||
"syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.6.13"
|
||||
@ -293,6 +372,14 @@ dependencies = [
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.6.5"
|
||||
@ -423,6 +510,7 @@ dependencies = [
|
||||
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"im-rc 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indoc 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"inkwell 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm8-0)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -453,6 +541,21 @@ name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sized-chunks"
|
||||
version = "0.3.0"
|
||||
@ -461,6 +564,16 @@ dependencies = [
|
||||
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.11.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.40"
|
||||
@ -471,6 +584,24 @@ dependencies = [
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synom"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.3.6"
|
||||
@ -479,16 +610,34 @@ dependencies = [
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
version = "0.1.3"
|
||||
@ -520,10 +669,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
|
||||
"checksum bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad807f2fc2bf185eeb98ff3a901bd46dc5ad58163d0fa4577ba0d25674d71708"
|
||||
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||
"checksum cargo_toml 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "097f5ce64ba566a83d9d914fd005de1e5937fdd57d8c5d99a7593040955d75a9"
|
||||
"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be"
|
||||
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
|
||||
"checksum dogged 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2638df109789fe360f0d9998c5438dd19a36678aaf845e46f285b688b1a1657a"
|
||||
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||
"checksum enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7798e7da2d4cb0d6d6fc467e8d6b5bf247e9e989f786dde1732d79899c32bb10"
|
||||
"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
|
||||
"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33"
|
||||
"checksum fraction 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1055159ac82fb210c813303f716b6c8db57ace9d5ec2dbbc2e1d7a864c1dd74e"
|
||||
@ -532,8 +685,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum im-rc 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a0197597d095c0d11107975d3175173f810ee572c2501ff4de64f4f3f119806"
|
||||
"checksum indoc 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1f59f228c76fda6ecd8dab79683039a7054c748587f682a911094f473647bd6"
|
||||
"checksum indoc-impl 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "63f070ef080db3601c1a0ecc75c7bb35104cc0ce2d7c4e049952a96a61d8933b"
|
||||
"checksum inkwell 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm8-0)" = "<none>"
|
||||
"checksum inkwell_internal_macros 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm8-0)" = "<none>"
|
||||
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
|
||||
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
|
||||
"checksum llvm-sys 80.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2110cd4daf9cd8e39dd3b933b1a2a2ac7315e91f7c92b3a20beab526c63b5978"
|
||||
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
"checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43"
|
||||
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
|
||||
@ -549,9 +705,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6"
|
||||
"checksum proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "982a35d1194084ba319d65c4a68d24ca28f5fdb5b8bc20899e4eef8641ea5178"
|
||||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||
"checksum proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90cf5f418035b98e655e9cdb225047638296b862b42411c4e45bb88d700f7fc0"
|
||||
"checksum quickcheck 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9c35d9c36a562f37eca96e79f66d5fd56eefbc22560dacc4a864cabd2d277456"
|
||||
"checksum quickcheck_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d7dfc1c4a1e048f5cc7d36a4c4118dfcf31d217c79f4b9a61bad65d68185752c"
|
||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
||||
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
||||
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
@ -568,11 +727,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd"
|
||||
"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e"
|
||||
"checksum sized-chunks 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2a2eb3fe454976eefb479f78f9b394d34d661b647c6326a3a6e66f68bb12c26"
|
||||
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
|
||||
"checksum syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)" = "bc945221ccf4a7e8c31222b9d1fc77aefdd6638eb901a6ce457a3dc29d4c31e8"
|
||||
"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
|
||||
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
|
||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||
"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
|
||||
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
|
||||
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum unindent 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "834b4441326c660336850c5c0926cc20548e848967a5f57bc20c2b741c8d41f4"
|
||||
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
@ -12,6 +12,7 @@ fraction = "0.6.2"
|
||||
num = "0.2.0"
|
||||
fxhash = "0.2.1"
|
||||
bumpalo = "2.6.0"
|
||||
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm8-0" }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "0.5.1"
|
||||
|
@ -1,3 +1,5 @@
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use can::expr::Expr;
|
||||
use can::pattern::Pattern;
|
||||
use can::problem::Problem;
|
||||
@ -5,44 +7,55 @@ use can::procedure::{Procedure, References};
|
||||
use can::symbol::Symbol;
|
||||
use collections::{ImMap, MutMap};
|
||||
use region::{Located, Region};
|
||||
use subs::Subs;
|
||||
|
||||
/// The canonicalization environment for a particular module.
|
||||
pub struct Env {
|
||||
pub struct Env<'a> {
|
||||
/// The module's path. Unqualified references to identifiers and variant names are assumed
|
||||
/// to be relative to this path.
|
||||
pub home: String,
|
||||
pub home: &'a str,
|
||||
|
||||
/// Problems we've encountered along the way, which will be reported to the user at the end.
|
||||
pub problems: Vec<Problem>,
|
||||
pub problems: Vec<'a, Problem<'a>>,
|
||||
|
||||
/// Variants either declared in this module, or imported.
|
||||
pub variants: ImMap<Symbol, Located<Box<str>>>,
|
||||
pub variants: ImMap<Symbol<'a>, Located<&'a str>>,
|
||||
|
||||
/// Former closures converted to top-level procedures.
|
||||
pub procedures: MutMap<Symbol, Procedure>,
|
||||
pub procedures: MutMap<Symbol<'a>, Procedure<'a>>,
|
||||
|
||||
pub arena: &'a Bump,
|
||||
|
||||
pub subs: Subs<'a>,
|
||||
}
|
||||
|
||||
impl Env {
|
||||
pub fn new(home: String, declared_variants: ImMap<Symbol, Located<Box<str>>>) -> Env {
|
||||
impl<'a> Env<'a> {
|
||||
pub fn new(
|
||||
arena: &'a Bump,
|
||||
home: &'a str,
|
||||
declared_variants: ImMap<Symbol<'a>, Located<&'a str>>,
|
||||
) -> Env<'a> {
|
||||
Env {
|
||||
home,
|
||||
variants: declared_variants,
|
||||
problems: Vec::new(),
|
||||
problems: Vec::new_in(arena),
|
||||
procedures: MutMap::default(),
|
||||
arena,
|
||||
subs: Subs::new(arena),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn problem(&mut self, problem: Problem) -> () {
|
||||
pub fn problem(&mut self, problem: Problem<'a>) -> () {
|
||||
self.problems.push(problem)
|
||||
}
|
||||
|
||||
pub fn register_closure(
|
||||
&mut self,
|
||||
symbol: Symbol,
|
||||
args: Vec<Located<Pattern>>,
|
||||
body: Located<Expr>,
|
||||
symbol: Symbol<'a>,
|
||||
args: &'a [Located<Pattern<'a>>],
|
||||
body: Located<Expr<'a>>,
|
||||
definition: Region,
|
||||
references: References,
|
||||
references: References<'a>,
|
||||
) -> () {
|
||||
// We can't if the closure is self tail recursive yet, because it doesn't know its final name yet.
|
||||
// (Assign sets that.) Assume this is false, and let Assign change it to true after it sets final name.
|
||||
|
@ -4,42 +4,46 @@ use can::symbol::Symbol;
|
||||
use operator::Operator;
|
||||
use region::Located;
|
||||
use std::i64;
|
||||
use subs::Variable;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Expr {
|
||||
pub enum Expr<'a> {
|
||||
// Literals
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Str(Box<str>),
|
||||
Char(char), // OBSOLETE
|
||||
List(Vec<Located<Expr>>),
|
||||
EmptyList,
|
||||
Int(Variable, i64),
|
||||
Float(Variable, f64),
|
||||
Str(Variable, &'a str),
|
||||
List(Variable, &'a [Located<Expr<'a>>]),
|
||||
|
||||
// Lookups
|
||||
Var(Symbol),
|
||||
Var(Variable, Symbol<'a>),
|
||||
/// Works the same as Var, but has an important marking purpose.
|
||||
/// See 13623e3f5f65ea2d703cf155f16650c1e8246502 for the bug this fixed.
|
||||
FunctionPointer(Symbol),
|
||||
/// We have a separate variant for this so that we can report errors
|
||||
/// (including type errors later) in the context of the sugar rather than
|
||||
/// confusingly talking about the desugared version the user can't see.
|
||||
InterpolatedStr(Vec<(Box<str>, Located<Expr>)>, Box<str>),
|
||||
FunctionPointer(Variable, Symbol<'a>),
|
||||
|
||||
// Pattern Matching
|
||||
Case(Box<Located<Expr>>, Vec<(Located<Pattern>, Located<Expr>)>),
|
||||
Assign(Vec<(Located<Pattern>, Located<Expr>)>, Box<Located<Expr>>),
|
||||
Case(
|
||||
Variable,
|
||||
&'a Located<Expr<'a>>,
|
||||
&'a [(Located<Pattern<'a>>, Located<Expr<'a>>)],
|
||||
),
|
||||
Define(
|
||||
Variable,
|
||||
&'a [(Located<Pattern<'a>>, Located<Expr<'a>>)],
|
||||
&'a Located<Expr<'a>>,
|
||||
),
|
||||
|
||||
// Application
|
||||
Call(Box<Located<Expr>>, Vec<Located<Expr>>),
|
||||
ApplyVariant(Symbol, Option<Vec<Located<Expr>>>),
|
||||
Call(Variable, &'a Located<Expr<'a>>, &'a [Located<Expr<'a>>]),
|
||||
|
||||
// This has to be separate from Call so we can do precedence reordering
|
||||
Operator(
|
||||
Variable,
|
||||
&'a (Located<Expr<'a>>, Located<Operator>, Located<Expr<'a>>),
|
||||
),
|
||||
|
||||
// Product Types
|
||||
EmptyRecord,
|
||||
|
||||
// Sugar
|
||||
If(Box<Located<Expr>>, Box<Located<Expr>>, Box<Located<Expr>>),
|
||||
Operator(Box<Located<Expr>>, Located<Operator>, Box<Located<Expr>>),
|
||||
Record(Variable, &'a [Located<(&'a str, Located<Expr<'a>>)>]),
|
||||
|
||||
// Compiles, but will crash if reached
|
||||
RuntimeError(RuntimeError),
|
||||
RuntimeError(Variable, RuntimeError<'a>),
|
||||
}
|
||||
|
390
src/can/mod.rs
390
src/can/mod.rs
@ -7,6 +7,8 @@ use self::problem::RuntimeError::*;
|
||||
use self::procedure::{Procedure, References};
|
||||
use self::scope::Scope;
|
||||
use self::symbol::Symbol;
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use collections::{ImMap, ImSet, MutMap, MutSet};
|
||||
use graph::{strongly_connected_component, topological_sort};
|
||||
use ident::Ident;
|
||||
@ -26,23 +28,24 @@ pub mod string;
|
||||
pub mod symbol;
|
||||
|
||||
pub fn canonicalize_declaration<'a>(
|
||||
home: String,
|
||||
name: &str,
|
||||
arena: &'a Bump,
|
||||
home: &'a str,
|
||||
name: &'a str,
|
||||
region: Region,
|
||||
expr: &'a ast::Expr<'a>,
|
||||
declared_idents: &ImMap<Ident, (Symbol, Region)>,
|
||||
declared_variants: &ImMap<Symbol, Located<Box<str>>>,
|
||||
declared_idents: &ImMap<Ident, (Symbol<'a>, Region)>,
|
||||
declared_variants: &ImMap<Symbol<'a>, Located<&'a str>>,
|
||||
) -> (
|
||||
Located<Expr>,
|
||||
Output,
|
||||
Vec<Problem>,
|
||||
MutMap<Symbol, Procedure>,
|
||||
Located<Expr<'a>>,
|
||||
Output<'a>,
|
||||
Vec<'a, Problem<'a>>,
|
||||
MutMap<Symbol<'a>, Procedure<'a>>,
|
||||
) {
|
||||
// If we're canonicalizing the declaration `foo = ...` inside the `Main` module,
|
||||
// scope_prefix will be "Main$foo$" and its first closure will be named "Main$foo$0"
|
||||
let scope_prefix = format!("{}${}$", home, name);
|
||||
let mut scope = Scope::new(scope_prefix, declared_idents.clone());
|
||||
let mut env = Env::new(home, declared_variants.clone());
|
||||
let mut scope = Scope::new(&scope_prefix, declared_idents.clone());
|
||||
let mut env = Env::new(&arena, home, declared_variants.clone());
|
||||
let (mut new_loc_expr, output) = canonicalize(&mut env, &mut scope, region, expr);
|
||||
|
||||
// Apply operator precedence and associativity rules once, after canonicalization is
|
||||
@ -56,13 +59,13 @@ pub fn canonicalize_declaration<'a>(
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Output {
|
||||
pub references: References,
|
||||
pub tail_call: Option<Symbol>,
|
||||
pub struct Output<'a> {
|
||||
pub references: References<'a>,
|
||||
pub tail_call: Option<Symbol<'a>>,
|
||||
}
|
||||
|
||||
impl Output {
|
||||
pub fn new() -> Output {
|
||||
impl<'a> Output<'a> {
|
||||
pub fn new() -> Output<'a> {
|
||||
Output {
|
||||
references: References::new(),
|
||||
tail_call: None,
|
||||
@ -71,31 +74,31 @@ impl Output {
|
||||
}
|
||||
|
||||
fn canonicalize<'a>(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
env: &'a mut Env<'a>,
|
||||
scope: &'a mut Scope<'a>,
|
||||
region: Region,
|
||||
expr: &'a ast::Expr<'a>,
|
||||
) -> (Located<Expr>, Output) {
|
||||
) -> (Located<Expr<'a>>, Output<'a>) {
|
||||
use self::Expr::*;
|
||||
|
||||
let (expr, output) = match expr {
|
||||
ast::Expr::Int(string) => (int_from_parsed(string, &mut env.problems), Output::new()),
|
||||
ast::Expr::Float(string) => (float_from_parsed(string, &mut env.problems), Output::new()),
|
||||
ast::Expr::Int(string) => (int_from_parsed(string, env), Output::new()),
|
||||
ast::Expr::Float(string) => (float_from_parsed(string, env), Output::new()),
|
||||
ast::Expr::Record(fields) => {
|
||||
if fields.is_empty() {
|
||||
(EmptyRecord, Output::new())
|
||||
(Record(env.subs.mk_flex_var(), &[]), Output::new())
|
||||
} else {
|
||||
panic!("TODO canonicalize nonempty record");
|
||||
}
|
||||
}
|
||||
ast::Expr::Str(string) => (Str(string.clone().into()), Output::new()),
|
||||
ast::Expr::Str(string) => (Str(env.subs.mk_flex_var(), string), Output::new()),
|
||||
ast::Expr::List(elems) => {
|
||||
let mut output = Output::new();
|
||||
|
||||
if elems.is_empty() {
|
||||
(EmptyList, output)
|
||||
(List(env.subs.mk_flex_var(), &[]), output)
|
||||
} else {
|
||||
let mut can_elems = Vec::with_capacity(elems.len());
|
||||
let mut can_elems = Vec::with_capacity_in(elems.len(), &env.arena);
|
||||
|
||||
for loc_elem in elems.iter() {
|
||||
let (can_expr, elem_out) =
|
||||
@ -109,7 +112,10 @@ fn canonicalize<'a>(
|
||||
// A list literal is never a tail call!
|
||||
output.tail_call = None;
|
||||
|
||||
(List(can_elems), output)
|
||||
(
|
||||
List(env.subs.mk_flex_var(), can_elems.into_bump_slice()),
|
||||
output,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,8 +150,8 @@ fn canonicalize<'a>(
|
||||
// Canonicalize the function expression and its arguments
|
||||
let (fn_expr, mut output) =
|
||||
canonicalize(env, scope, loc_fn.region.clone(), &loc_fn.value);
|
||||
let mut args = Vec::new();
|
||||
let mut outputs = Vec::new();
|
||||
let mut args = Vec::new_in(&env.arena);
|
||||
let mut outputs = Vec::new_in(&env.arena);
|
||||
|
||||
for loc_arg in loc_args.iter() {
|
||||
let (arg_expr, arg_out) =
|
||||
@ -156,13 +162,17 @@ fn canonicalize<'a>(
|
||||
}
|
||||
|
||||
match &fn_expr.value {
|
||||
&Var(ref sym) => {
|
||||
&Var(_, ref sym) => {
|
||||
output.references.calls.insert(sym.clone());
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
|
||||
let expr = Call(Box::new(fn_expr), args);
|
||||
let expr = Call(
|
||||
env.subs.mk_flex_var(),
|
||||
env.arena.alloc(fn_expr),
|
||||
args.into_bump_slice(),
|
||||
);
|
||||
|
||||
for arg_out in outputs {
|
||||
output.references = output.references.union(arg_out.references);
|
||||
@ -187,9 +197,9 @@ fn canonicalize<'a>(
|
||||
// because it's the only one that can call a function by name.
|
||||
output.tail_call = match loc_op.value {
|
||||
Pizza => match &right_expr.value {
|
||||
&Var(ref sym) => Some(sym.clone()),
|
||||
&Call(ref loc_boxed_expr, _) => match &loc_boxed_expr.value {
|
||||
Var(sym) => Some(sym.clone()),
|
||||
&Var(_, sym) => Some(sym.clone()),
|
||||
&Call(_, loc_boxed_expr, _) => match &loc_boxed_expr.value {
|
||||
Var(_, sym) => Some(sym.clone()),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
@ -197,7 +207,10 @@ fn canonicalize<'a>(
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let expr = Operator(Box::new(left_expr), loc_op.clone(), Box::new(right_expr));
|
||||
let expr = Operator(
|
||||
env.subs.mk_flex_var(),
|
||||
env.arena.alloc((left_expr, loc_op.clone(), right_expr)),
|
||||
);
|
||||
|
||||
(expr, output)
|
||||
}
|
||||
@ -205,7 +218,7 @@ fn canonicalize<'a>(
|
||||
let mut output = Output::new();
|
||||
let ident = Ident::new(module_parts, name);
|
||||
let can_expr = match resolve_ident(&env, &scope, ident, &mut output.references) {
|
||||
Ok(symbol) => Var(symbol),
|
||||
Ok(symbol) => Var(env.subs.mk_flex_var(), symbol),
|
||||
Err(ident) => {
|
||||
let loc_ident = Located {
|
||||
region: region.clone(),
|
||||
@ -214,7 +227,7 @@ fn canonicalize<'a>(
|
||||
|
||||
env.problem(Problem::UnrecognizedConstant(loc_ident.clone()));
|
||||
|
||||
RuntimeError(UnrecognizedConstant(loc_ident))
|
||||
RuntimeError(env.subs.mk_flex_var(), UnrecognizedConstant(loc_ident))
|
||||
}
|
||||
};
|
||||
|
||||
@ -311,6 +324,7 @@ fn canonicalize<'a>(
|
||||
// Add the assigned identifiers to scope. If there's a collision, it means there
|
||||
// was shadowing, which will be handled later.
|
||||
let assigned_idents: Vec<(Ident, (Symbol, Region))> = idents_from_patterns(
|
||||
&env.arena,
|
||||
defs.clone().iter().flat_map(|(_, def)| match def {
|
||||
Def::AnnotationOnly(_region) => None,
|
||||
Def::BodyOnly(loc_pattern, _expr) => Some(loc_pattern),
|
||||
@ -331,7 +345,7 @@ fn canonicalize<'a>(
|
||||
// block. Order of assignments doesn't matter, thanks to referential transparency!
|
||||
let (opt_loc_pattern, (loc_can_expr, can_output)) = match def {
|
||||
Def::AnnotationOnly(loc_annotation) => {
|
||||
let value = Expr::RuntimeError(NoImplementation);
|
||||
let value = Expr::RuntimeError(env.subs.mk_flex_var(), NoImplementation);
|
||||
let loc_expr = Located {
|
||||
value,
|
||||
region: loc_annotation.region.clone(),
|
||||
@ -382,15 +396,15 @@ fn canonicalize<'a>(
|
||||
// Only assignments of the form (foo = ...) can be closure declarations or self tail calls.
|
||||
(
|
||||
&ast::Pattern::Identifier(ref name),
|
||||
&Pattern::Identifier(ref assigned_symbol),
|
||||
&FunctionPointer(ref symbol),
|
||||
&Pattern::Identifier(_, ref assigned_symbol),
|
||||
&FunctionPointer(_, ref symbol),
|
||||
) => {
|
||||
// Since everywhere in the code it'll be referred to by its assigned name,
|
||||
// remove its generated name from the procedure map. (We'll re-insert it later.)
|
||||
let mut procedure = env.procedures.remove(&symbol).unwrap();
|
||||
|
||||
// The original ident name will be used for debugging and stack traces.
|
||||
procedure.name = Some(name.to_string());
|
||||
procedure.name = Some(name);
|
||||
|
||||
// The closure is self tail recursive iff it tail calls itself (by assigned name).
|
||||
procedure.is_self_tail_recursive = match &can_output.tail_call {
|
||||
@ -414,17 +428,17 @@ fn canonicalize<'a>(
|
||||
|
||||
// Return a reference to the assigned symbol, since the auto-generated one no
|
||||
// longer references any entry in the procedure map!
|
||||
Var(assigned_symbol.clone())
|
||||
Var(env.subs.mk_flex_var(), assigned_symbol.clone())
|
||||
}
|
||||
_ => loc_can_expr.value,
|
||||
};
|
||||
|
||||
let mut assigned_symbols = Vec::new();
|
||||
let mut assigned_symbols = Vec::new_in(env.arena);
|
||||
|
||||
// Store the referenced locals in the refs_by_assignment map, so we can later figure out
|
||||
// which assigned names reference each other.
|
||||
for (ident, (symbol, region)) in
|
||||
idents_from_patterns(std::iter::once(loc_pattern), &scope)
|
||||
idents_from_patterns(&env.arena, std::iter::once(loc_pattern), &scope)
|
||||
{
|
||||
let refs =
|
||||
// Functions' references don't count in assignments.
|
||||
@ -527,56 +541,79 @@ fn canonicalize<'a>(
|
||||
// This way, during code gen, no assignment will refer to a value that hasn't been initialized yet.
|
||||
// As a bonus, the topological sort also reveals any cycles between the assignments, allowing
|
||||
// us to give a CircularAssignment error.
|
||||
let successors = |symbol: &Symbol| -> ImSet<Symbol> {
|
||||
let successors = |symbol: &Symbol<'a>| -> ImSet<Symbol<'a>> {
|
||||
let (_, references) = refs_by_assignment.get(symbol).unwrap();
|
||||
|
||||
local_successors(&references, &env.procedures)
|
||||
};
|
||||
|
||||
let assigned_symbols: Vec<Symbol> = can_assignments_by_symbol
|
||||
.keys()
|
||||
.into_iter()
|
||||
.map(Symbol::clone)
|
||||
.collect();
|
||||
let mut assigned_symbols: Vec<Symbol<'a>> = Vec::new_in(env.arena);
|
||||
|
||||
for symbol in can_assignments_by_symbol.keys().into_iter() {
|
||||
assigned_symbols.push(symbol.clone())
|
||||
}
|
||||
|
||||
match topological_sort(assigned_symbols.as_slice(), successors) {
|
||||
Ok(sorted_symbols) => {
|
||||
let can_assignments = sorted_symbols
|
||||
.into_iter()
|
||||
.rev() // Topological sort gives us the reverse of the sorting we want!
|
||||
.map(|symbol| can_assignments_by_symbol.get(&symbol).unwrap().clone())
|
||||
.collect();
|
||||
let mut can_assignments = Vec::new_in(env.arena);
|
||||
|
||||
(Assign(can_assignments, Box::new(ret_expr)), output)
|
||||
for symbol in sorted_symbols
|
||||
.into_iter()
|
||||
// Topological sort gives us the reverse of the sorting we want!
|
||||
.rev()
|
||||
{
|
||||
can_assignments
|
||||
.push(can_assignments_by_symbol.get(&symbol).unwrap().clone());
|
||||
}
|
||||
|
||||
(
|
||||
Define(
|
||||
env.subs.mk_flex_var(),
|
||||
can_assignments.into_bump_slice(),
|
||||
env.arena.alloc(ret_expr),
|
||||
),
|
||||
output,
|
||||
)
|
||||
}
|
||||
Err(node_in_cycle) => {
|
||||
// We have one node we know is in the cycle.
|
||||
// We want to show the entire cycle in the error message, so expand it out.
|
||||
let mut loc_idents_in_cycle: Vec<Located<Ident>> =
|
||||
strongly_connected_component(&node_in_cycle, successors)
|
||||
.into_iter()
|
||||
.rev() // Strongly connected component gives us the reverse of the sorting we want!
|
||||
.map(|symbol| refs_by_assignment.get(&symbol).unwrap().0.clone())
|
||||
.collect();
|
||||
let mut loc_idents_in_cycle: Vec<'a, Located<Ident>> = Vec::new_in(env.arena);
|
||||
|
||||
for symbol in strongly_connected_component(&node_in_cycle, successors)
|
||||
.into_iter()
|
||||
// Strongly connected component gives us the reverse of the sorting we want!
|
||||
.rev()
|
||||
{
|
||||
loc_idents_in_cycle
|
||||
.push(refs_by_assignment.get(&symbol).unwrap().0.clone());
|
||||
}
|
||||
|
||||
// Sort them to make the report more helpful.
|
||||
loc_idents_in_cycle = sort_cyclic_idents(
|
||||
&env.arena,
|
||||
loc_idents_in_cycle,
|
||||
&mut assigned_idents.iter().map(|(ident, _)| ident),
|
||||
);
|
||||
|
||||
env.problem(Problem::CircularAssignment(loc_idents_in_cycle.clone()));
|
||||
|
||||
let can_assignments = can_assignments_by_symbol
|
||||
.values()
|
||||
.map(|tuple| tuple.clone())
|
||||
.collect();
|
||||
let mut can_assignments =
|
||||
Vec::with_capacity_in(can_assignments_by_symbol.len(), env.arena);
|
||||
|
||||
for tuple in can_assignments_by_symbol.values() {
|
||||
can_assignments.push(tuple.clone());
|
||||
}
|
||||
|
||||
(
|
||||
RuntimeError(CircularAssignment(
|
||||
RuntimeError(
|
||||
env.subs.mk_flex_var(),
|
||||
CircularAssignment(
|
||||
loc_idents_in_cycle,
|
||||
can_assignments,
|
||||
Box::new(ret_expr),
|
||||
)),
|
||||
env.arena.alloc(ret_expr),
|
||||
),
|
||||
),
|
||||
output,
|
||||
)
|
||||
}
|
||||
@ -598,29 +635,30 @@ fn canonicalize<'a>(
|
||||
let mut scope = scope.clone();
|
||||
|
||||
let arg_idents: Vec<(Ident, (Symbol, Region))> =
|
||||
idents_from_patterns(loc_arg_patterns.iter(), &scope);
|
||||
idents_from_patterns(&env.arena, loc_arg_patterns.iter(), &scope);
|
||||
|
||||
// Add the arguments' idents to scope.idents. If there's a collision,
|
||||
// it means there was shadowing, which will be handled later.
|
||||
scope.idents = union_pairs(scope.idents, arg_idents.iter());
|
||||
|
||||
let can_args: Vec<Located<Pattern>> = loc_arg_patterns
|
||||
.into_iter()
|
||||
.map(|loc_pattern| {
|
||||
let can_args: Vec<Located<Pattern>> =
|
||||
Vec::with_capacity_in(loc_arg_patterns.len(), env.arena);
|
||||
|
||||
for loc_pattern in loc_arg_patterns.into_iter() {
|
||||
// Exclude the current ident from shadowable_idents; you can't shadow yourself!
|
||||
// (However, still include it in scope, because you *can* recursively refer to yourself.)
|
||||
let mut shadowable_idents = scope.idents.clone();
|
||||
remove_idents(&loc_pattern.value, &mut shadowable_idents);
|
||||
|
||||
canonicalize_pattern(
|
||||
can_args.push(canonicalize_pattern(
|
||||
env,
|
||||
&mut scope,
|
||||
&FunctionArg,
|
||||
&loc_pattern,
|
||||
&mut shadowable_idents,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
))
|
||||
}
|
||||
|
||||
let (loc_body_expr, mut output) = canonicalize(
|
||||
env,
|
||||
&mut scope,
|
||||
@ -650,14 +688,14 @@ fn canonicalize<'a>(
|
||||
// Register it as a top-level procedure in the Env!
|
||||
env.register_closure(
|
||||
symbol.clone(),
|
||||
can_args,
|
||||
can_args.into_bump_slice(),
|
||||
loc_body_expr,
|
||||
region.clone(),
|
||||
output.references.clone(),
|
||||
);
|
||||
|
||||
// Always return a function pointer, in case that's how the closure is being used (e.g. with Apply).
|
||||
(FunctionPointer(symbol), output)
|
||||
(FunctionPointer(env.subs.mk_flex_var(), symbol), output)
|
||||
}
|
||||
|
||||
//ast::Expr::Case(loc_cond, branches) => {
|
||||
@ -738,9 +776,9 @@ fn canonicalize<'a>(
|
||||
|
||||
// (expr, output)
|
||||
//}
|
||||
ast::Expr::HexInt(string) => (hex_from_parsed(string, &mut env.problems), Output::new()),
|
||||
ast::Expr::BinaryInt(string) => (bin_from_parsed(string, &mut env.problems), Output::new()),
|
||||
ast::Expr::OctalInt(string) => (oct_from_parsed(string, &mut env.problems), Output::new()),
|
||||
ast::Expr::HexInt(string) => (hex_from_parsed(string, env), Output::new()),
|
||||
ast::Expr::BinaryInt(string) => (bin_from_parsed(string, env), Output::new()),
|
||||
ast::Expr::OctalInt(string) => (oct_from_parsed(string, env), Output::new()),
|
||||
ast::Expr::SpaceBefore(sub_expr, _spaces) => {
|
||||
return canonicalize(env, scope, region, sub_expr);
|
||||
}
|
||||
@ -795,10 +833,10 @@ where
|
||||
map
|
||||
}
|
||||
|
||||
fn local_successors(
|
||||
references: &References,
|
||||
procedures: &MutMap<Symbol, Procedure>,
|
||||
) -> ImSet<Symbol> {
|
||||
fn local_successors<'a>(
|
||||
references: &'a References<'a>,
|
||||
procedures: &'a MutMap<Symbol<'a>, Procedure<'a>>,
|
||||
) -> ImSet<Symbol<'a>> {
|
||||
let mut answer = references.locals.clone();
|
||||
|
||||
for call_symbol in references.calls.iter() {
|
||||
@ -808,7 +846,10 @@ fn local_successors(
|
||||
answer
|
||||
}
|
||||
|
||||
fn call_successors(call_symbol: &Symbol, procedures: &MutMap<Symbol, Procedure>) -> ImSet<Symbol> {
|
||||
fn call_successors<'a>(
|
||||
call_symbol: &'a Symbol<'a>,
|
||||
procedures: &'a MutMap<Symbol<'a>, Procedure<'a>>,
|
||||
) -> ImSet<Symbol<'a>> {
|
||||
// TODO (this comment should be moved to a GH issue) this may cause an infinite loop if 2 procedures reference each other; may need to track visited procedures!
|
||||
match procedures.get(call_symbol) {
|
||||
Some(procedure) => {
|
||||
@ -822,21 +863,21 @@ fn call_successors(call_symbol: &Symbol, procedures: &MutMap<Symbol, Procedure>)
|
||||
}
|
||||
}
|
||||
|
||||
fn references_from_local<T>(
|
||||
assigned_symbol: Symbol,
|
||||
visited: &mut MutSet<Symbol>,
|
||||
refs_by_assignment: &MutMap<Symbol, (T, References)>,
|
||||
procedures: &MutMap<Symbol, Procedure>,
|
||||
) -> References {
|
||||
fn references_from_local<'a, T>(
|
||||
assigned_symbol: Symbol<'a>,
|
||||
visited: &'a mut MutSet<Symbol<'a>>,
|
||||
refs_by_assignment: &'a MutMap<Symbol<'a>, (T, References<'a>)>,
|
||||
procedures: &'a MutMap<Symbol<'a>, Procedure<'a>>,
|
||||
) -> References<'a> {
|
||||
match refs_by_assignment.get(&assigned_symbol) {
|
||||
Some((_, refs)) => {
|
||||
let mut answer = References::new();
|
||||
let mut answer: References<'a> = References::new();
|
||||
|
||||
visited.insert(assigned_symbol);
|
||||
|
||||
for local in refs.locals.iter() {
|
||||
if !visited.contains(&local) {
|
||||
let other_refs = references_from_local(
|
||||
let other_refs: References<'a> = references_from_local(
|
||||
local.clone(),
|
||||
visited,
|
||||
refs_by_assignment,
|
||||
@ -876,9 +917,10 @@ fn references_from_local<T>(
|
||||
///
|
||||
/// Example: the cycle (c ---> a ---> b) becomes (a ---> b ---> c)
|
||||
pub fn sort_cyclic_idents<'a, I>(
|
||||
arena: &'a Bump,
|
||||
loc_idents: Vec<Located<Ident>>,
|
||||
ordered_idents: &mut I,
|
||||
) -> Vec<Located<Ident>>
|
||||
) -> Vec<'a, Located<Ident>>
|
||||
where
|
||||
I: Iterator<Item = &'a Ident>,
|
||||
{
|
||||
@ -891,8 +933,8 @@ where
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut answer = Vec::with_capacity(loc_idents.len());
|
||||
let mut end = Vec::with_capacity(loc_idents.len());
|
||||
let mut answer = Vec::with_capacity_in(loc_idents.len(), arena);
|
||||
let mut end = Vec::with_capacity_in(loc_idents.len(), arena);
|
||||
let mut encountered_first_ident = false;
|
||||
|
||||
for loc_ident in loc_idents {
|
||||
@ -913,12 +955,12 @@ where
|
||||
answer
|
||||
}
|
||||
|
||||
fn references_from_call<T>(
|
||||
call_symbol: Symbol,
|
||||
visited: &mut MutSet<Symbol>,
|
||||
refs_by_assignment: &MutMap<Symbol, (T, References)>,
|
||||
procedures: &MutMap<Symbol, Procedure>,
|
||||
) -> References {
|
||||
fn references_from_call<'a, T>(
|
||||
call_symbol: Symbol<'a>,
|
||||
visited: &'a mut MutSet<Symbol<'a>>,
|
||||
refs_by_assignment: &'a MutMap<Symbol, (T, References<'a>)>,
|
||||
procedures: &'a MutMap<Symbol, Procedure<'a>>,
|
||||
) -> References<'a> {
|
||||
match procedures.get(&call_symbol) {
|
||||
Some(procedure) => {
|
||||
let mut answer = procedure.references.clone();
|
||||
@ -961,11 +1003,15 @@ fn references_from_call<T>(
|
||||
}
|
||||
}
|
||||
|
||||
fn idents_from_patterns<'a, I>(loc_patterns: I, scope: &Scope) -> Vec<(Ident, (Symbol, Region))>
|
||||
fn idents_from_patterns<'a, I>(
|
||||
arena: &'a Bump,
|
||||
loc_patterns: I,
|
||||
scope: &'a Scope<'a>,
|
||||
) -> Vec<'a, (Ident, (Symbol<'a>, Region))>
|
||||
where
|
||||
I: Iterator<Item = &'a Located<ast::Pattern<'a>>>,
|
||||
{
|
||||
let mut answer = Vec::new();
|
||||
let mut answer = Vec::new_in(arena);
|
||||
|
||||
for loc_pattern in loc_patterns {
|
||||
add_idents_from_pattern(&loc_pattern.region, &loc_pattern.value, scope, &mut answer);
|
||||
@ -976,10 +1022,10 @@ where
|
||||
|
||||
/// helper function for idents_from_patterns
|
||||
fn add_idents_from_pattern<'a>(
|
||||
region: &Region,
|
||||
pattern: &ast::Pattern<'a>,
|
||||
scope: &Scope,
|
||||
answer: &mut Vec<(Ident, (Symbol, Region))>,
|
||||
region: &'a Region,
|
||||
pattern: &'a ast::Pattern<'a>,
|
||||
scope: &'a Scope<'a>,
|
||||
answer: &'a mut Vec<(Ident, (Symbol<'a>, Region))>,
|
||||
) {
|
||||
use parse::ast::Pattern::*;
|
||||
|
||||
@ -1067,12 +1113,12 @@ fn remove_idents(pattern: &ast::Pattern, idents: &mut ImMap<Ident, (Symbol, Regi
|
||||
|
||||
/// If it could not be found, return it unchanged as an Err.
|
||||
#[inline(always)] // This is shared code between Var and InterpolatedStr; it was inlined when handwritten
|
||||
fn resolve_ident(
|
||||
env: &Env,
|
||||
scope: &Scope,
|
||||
fn resolve_ident<'a>(
|
||||
env: &'a Env<'a>,
|
||||
scope: &'a Scope<'a>,
|
||||
ident: Ident,
|
||||
references: &mut References,
|
||||
) -> Result<Symbol, Ident> {
|
||||
references: &'a mut References<'a>,
|
||||
) -> Result<Symbol<'a>, Ident> {
|
||||
if scope.idents.contains_key(&ident) {
|
||||
let recognized = match ident {
|
||||
Ident::Unqualified(name) => {
|
||||
@ -1096,7 +1142,7 @@ fn resolve_ident(
|
||||
match ident {
|
||||
Ident::Unqualified(name) => {
|
||||
// Try again, this time using the current module as the path.
|
||||
let qualified = Ident::Qualified(env.home.clone(), name.clone());
|
||||
let qualified = Ident::Qualified(env.home.clone().to_string(), name.clone());
|
||||
|
||||
if scope.idents.contains_key(&qualified) {
|
||||
let symbol = Symbol::new(&env.home, &name);
|
||||
@ -1142,11 +1188,12 @@ fn resolve_ident(
|
||||
// Precedence logic adapted from Gluon by Markus Westerlind, MIT licensed
|
||||
// https://github.com/gluon-lang/gluon
|
||||
// Thank you, Markus!
|
||||
fn new_op_expr(
|
||||
left: Box<Located<Expr>>,
|
||||
fn new_op_expr<'a>(
|
||||
env: &'a mut Env<'a>,
|
||||
left: Located<Expr<'a>>,
|
||||
op: Located<Operator>,
|
||||
right: Box<Located<Expr>>,
|
||||
) -> Located<Expr> {
|
||||
right: Located<Expr<'a>>,
|
||||
) -> Located<Expr<'a>> {
|
||||
let new_region = Region {
|
||||
start_line: left.region.start_line,
|
||||
start_col: left.region.start_col,
|
||||
@ -1154,7 +1201,7 @@ fn new_op_expr(
|
||||
end_line: right.region.end_line,
|
||||
end_col: right.region.end_col,
|
||||
};
|
||||
let new_expr = Expr::Operator(left, op, right);
|
||||
let new_expr = Expr::Operator(env.subs.mk_flex_var(), env.arena.alloc((left, op, right)));
|
||||
|
||||
Located {
|
||||
value: new_expr,
|
||||
@ -1170,7 +1217,10 @@ fn new_op_expr(
|
||||
/// By design, Roc neither allows custom operators nor has any built-in operators with
|
||||
/// the same precedence and different associativity, so this operation always succeeds
|
||||
/// and can never produce any user-facing errors.
|
||||
fn apply_precedence_and_associativity(env: &mut Env, expr: Located<Expr>) -> Located<Expr> {
|
||||
fn apply_precedence_and_associativity<'a>(
|
||||
env: &'a mut Env<'a>,
|
||||
expr: Located<Expr<'a>>,
|
||||
) -> Located<Expr<'a>> {
|
||||
use can::problem::PrecedenceProblem::*;
|
||||
use operator::Associativity::*;
|
||||
use std::cmp::Ordering;
|
||||
@ -1179,9 +1229,10 @@ fn apply_precedence_and_associativity(env: &mut Env, expr: Located<Expr>) -> Loc
|
||||
// arena bump allocation for Infixes, arg_stack, and op_stack. As long as we
|
||||
// allocate each element inside arg_stack outside the arena, this should end
|
||||
// up being a decent bit more efficient.
|
||||
let mut infixes = Infixes::new(expr);
|
||||
let mut arg_stack: Vec<Box<Located<Expr>>> = Vec::new();
|
||||
let mut op_stack: Vec<Located<Operator>> = Vec::new();
|
||||
let arena = env.arena;
|
||||
let mut infixes = Infixes::new(&expr);
|
||||
let mut arg_stack: Vec<&'a Located<Expr>> = Vec::new_in(arena);
|
||||
let mut op_stack: Vec<Located<Operator>> = Vec::new_in(arena);
|
||||
|
||||
while let Some(token) = infixes.next() {
|
||||
match token {
|
||||
@ -1196,7 +1247,8 @@ fn apply_precedence_and_associativity(env: &mut Env, expr: Located<Expr>) -> Loc
|
||||
let left = arg_stack.pop().unwrap();
|
||||
|
||||
infixes.next_op = Some(next_op);
|
||||
arg_stack.push(Box::new(new_op_expr(left, stack_op, right)));
|
||||
arg_stack
|
||||
.push(arena.alloc(new_op_expr(env, *left, stack_op, *right)));
|
||||
}
|
||||
|
||||
Ordering::Greater => {
|
||||
@ -1216,8 +1268,9 @@ fn apply_precedence_and_associativity(env: &mut Env, expr: Located<Expr>) -> Loc
|
||||
let left = arg_stack.pop().unwrap();
|
||||
|
||||
infixes.next_op = Some(next_op);
|
||||
arg_stack
|
||||
.push(Box::new(new_op_expr(left, stack_op, right)));
|
||||
arg_stack.push(
|
||||
arena.alloc(new_op_expr(env, *left, stack_op, *right)),
|
||||
);
|
||||
}
|
||||
|
||||
(RightAssociative, RightAssociative) => {
|
||||
@ -1235,12 +1288,12 @@ fn apply_precedence_and_associativity(env: &mut Env, expr: Located<Expr>) -> Loc
|
||||
|
||||
let right = arg_stack.pop().unwrap();
|
||||
let left = arg_stack.pop().unwrap();
|
||||
let broken_expr = new_op_expr(left, next_op, right);
|
||||
let broken_expr = new_op_expr(env, *left, next_op, *right);
|
||||
let region = broken_expr.region.clone();
|
||||
let value = Expr::RuntimeError(InvalidPrecedence(
|
||||
problem,
|
||||
Box::new(broken_expr),
|
||||
));
|
||||
let value = Expr::RuntimeError(
|
||||
env.subs.mk_flex_var(),
|
||||
InvalidPrecedence(problem, arena.alloc(broken_expr)),
|
||||
);
|
||||
|
||||
return Located { region, value };
|
||||
}
|
||||
@ -1270,7 +1323,7 @@ fn apply_precedence_and_associativity(env: &mut Env, expr: Located<Expr>) -> Loc
|
||||
let right = arg_stack.pop().unwrap();
|
||||
let left = arg_stack.pop().unwrap();
|
||||
|
||||
arg_stack.push(Box::new(new_op_expr(left, op, right)));
|
||||
arg_stack.push(arena.alloc(new_op_expr(env, *left, op, *right)));
|
||||
}
|
||||
|
||||
assert_eq!(arg_stack.len(), 1);
|
||||
@ -1279,8 +1332,8 @@ fn apply_precedence_and_associativity(env: &mut Env, expr: Located<Expr>) -> Loc
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum InfixToken {
|
||||
Arg(Box<Located<Expr>>),
|
||||
enum InfixToken<'a> {
|
||||
Arg(&'a Located<Expr<'a>>),
|
||||
Op(Located<Operator>),
|
||||
}
|
||||
|
||||
@ -1308,116 +1361,113 @@ enum InfixToken {
|
||||
/// Op: -
|
||||
/// Arg: 8
|
||||
/// ```
|
||||
struct Infixes {
|
||||
struct Infixes<'a> {
|
||||
/// The next part of the expression that we need to flatten
|
||||
remaining_expr: Option<Box<Located<Expr>>>,
|
||||
remaining_expr: Option<&'a Located<Expr<'a>>>,
|
||||
/// Cached operator from a previous iteration
|
||||
next_op: Option<Located<Operator>>,
|
||||
}
|
||||
|
||||
impl Infixes {
|
||||
fn new(expr: Located<Expr>) -> Infixes {
|
||||
impl<'a> Infixes<'a> {
|
||||
fn new(expr: &'a Located<Expr<'a>>) -> Infixes<'a> {
|
||||
Infixes {
|
||||
remaining_expr: Some(Box::new(expr)),
|
||||
remaining_expr: Some(expr),
|
||||
next_op: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Infixes {
|
||||
type Item = InfixToken;
|
||||
impl<'a> Iterator for Infixes<'a> {
|
||||
type Item = InfixToken<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<InfixToken> {
|
||||
fn next(&mut self) -> Option<InfixToken<'a>> {
|
||||
match self.next_op.take() {
|
||||
Some(op) => Some(InfixToken::Op(op)),
|
||||
None => self.remaining_expr.take().map(|boxed_expr| {
|
||||
let expr = *boxed_expr;
|
||||
|
||||
match expr.value {
|
||||
Expr::Operator(left, op, right) => {
|
||||
None => self.remaining_expr.take().map(|expr| match expr.value {
|
||||
Expr::Operator(_, (left, op, right)) => {
|
||||
self.remaining_expr = Some(right);
|
||||
self.next_op = Some(op);
|
||||
self.next_op = Some(*op);
|
||||
|
||||
InfixToken::Arg(left)
|
||||
}
|
||||
_ => InfixToken::Arg(Box::new(expr)),
|
||||
}
|
||||
_ => InfixToken::Arg(expr),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn float_from_parsed<'a>(raw: &str, problems: &mut Vec<Problem>) -> Expr {
|
||||
|
||||
fn float_from_parsed<'a>(raw: &'a str, env: &'a mut Env<'a>) -> Expr<'a> {
|
||||
// Ignore underscores.
|
||||
match raw.replace("_", "").parse::<f64>() {
|
||||
Ok(float) if float.is_finite() => Expr::Float(float),
|
||||
Ok(float) if float.is_finite() => Expr::Float(env.subs.mk_flex_var(), float),
|
||||
_ => {
|
||||
let runtime_error = FloatOutsideRange(raw.into());
|
||||
|
||||
problems.push(Problem::RuntimeError(runtime_error.clone()));
|
||||
env.problem(Problem::RuntimeError(runtime_error.clone()));
|
||||
|
||||
Expr::RuntimeError(runtime_error)
|
||||
Expr::RuntimeError(env.subs.mk_flex_var(), runtime_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn int_from_parsed<'a>(raw: &str, problems: &mut Vec<Problem>) -> Expr {
|
||||
fn int_from_parsed<'a>(raw: &'a str, env: &'a mut Env<'a>) -> Expr<'a> {
|
||||
// Ignore underscores.
|
||||
match raw.replace("_", "").parse::<i64>() {
|
||||
Ok(int) => Expr::Int(int),
|
||||
Ok(int) => Expr::Int(env.subs.mk_flex_var(), int),
|
||||
Err(_) => {
|
||||
let runtime_error = IntOutsideRange(raw.into());
|
||||
|
||||
problems.push(Problem::RuntimeError(runtime_error.clone()));
|
||||
env.problem(Problem::RuntimeError(runtime_error.clone()));
|
||||
|
||||
Expr::RuntimeError(runtime_error)
|
||||
Expr::RuntimeError(env.subs.mk_flex_var(), runtime_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn hex_from_parsed<'a>(raw: &str, problems: &mut Vec<Problem>) -> Expr {
|
||||
fn hex_from_parsed<'a>(raw: &'a str, env: &'a mut Env<'a>) -> Expr<'a> {
|
||||
// Ignore underscores.
|
||||
match i64::from_str_radix(raw.replace("_", "").as_str(), 16) {
|
||||
Ok(int) => Expr::Int(int),
|
||||
Ok(int) => Expr::Int(env.subs.mk_flex_var(), int),
|
||||
Err(parse_err) => {
|
||||
let runtime_error = InvalidHex(parse_err, raw.into());
|
||||
|
||||
problems.push(Problem::RuntimeError(runtime_error.clone()));
|
||||
env.problem(Problem::RuntimeError(runtime_error.clone()));
|
||||
|
||||
Expr::RuntimeError(runtime_error)
|
||||
Expr::RuntimeError(env.subs.mk_flex_var(), runtime_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn oct_from_parsed<'a>(raw: &str, problems: &mut Vec<Problem>) -> Expr {
|
||||
fn oct_from_parsed<'a>(raw: &'a str, env: &'a mut Env<'a>) -> Expr<'a> {
|
||||
// Ignore underscores.
|
||||
match i64::from_str_radix(raw.replace("_", "").as_str(), 8) {
|
||||
Ok(int) => Expr::Int(int),
|
||||
Ok(int) => Expr::Int(env.subs.mk_flex_var(), int),
|
||||
Err(parse_err) => {
|
||||
let runtime_error = InvalidOctal(parse_err, raw.into());
|
||||
|
||||
problems.push(Problem::RuntimeError(runtime_error.clone()));
|
||||
env.problem(Problem::RuntimeError(runtime_error.clone()));
|
||||
|
||||
Expr::RuntimeError(runtime_error)
|
||||
Expr::RuntimeError(env.subs.mk_flex_var(), runtime_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn bin_from_parsed<'a>(raw: &str, problems: &mut Vec<Problem>) -> Expr {
|
||||
fn bin_from_parsed<'a>(raw: &'a str, env: &'a mut Env<'a>) -> Expr<'a> {
|
||||
// Ignore underscores.
|
||||
match i64::from_str_radix(raw.replace("_", "").as_str(), 2) {
|
||||
Ok(int) => Expr::Int(int),
|
||||
Ok(int) => Expr::Int(env.subs.mk_flex_var(), int),
|
||||
Err(parse_err) => {
|
||||
let runtime_error = InvalidBinary(parse_err, raw.into());
|
||||
|
||||
problems.push(Problem::RuntimeError(runtime_error.clone()));
|
||||
env.problem(Problem::RuntimeError(runtime_error.clone()));
|
||||
|
||||
Expr::RuntimeError(runtime_error)
|
||||
Expr::RuntimeError(env.subs.mk_flex_var(), runtime_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,19 +6,20 @@ use collections::ImMap;
|
||||
use ident::{Ident, VariantName};
|
||||
use parse::ast;
|
||||
use region::{Located, Region};
|
||||
use subs::Variable;
|
||||
|
||||
/// A pattern, including possible problems (e.g. shadowing) so that
|
||||
/// codegen can generate a runtime error if this pattern is reached.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Pattern {
|
||||
Identifier(Symbol),
|
||||
Variant(Symbol),
|
||||
AppliedVariant(Symbol, Vec<Located<Pattern>>),
|
||||
IntLiteral(i64),
|
||||
FloatLiteral(f64),
|
||||
ExactString(String),
|
||||
EmptyRecordLiteral,
|
||||
Underscore,
|
||||
pub enum Pattern<'a> {
|
||||
Identifier(Variable, Symbol<'a>),
|
||||
Variant(Variable, Symbol<'a>),
|
||||
AppliedVariant(Variable, Symbol<'a>, Vec<Located<Pattern<'a>>>),
|
||||
IntLiteral(Variable, i64),
|
||||
FloatLiteral(Variable, f64),
|
||||
ExactString(Variable, &'a str),
|
||||
EmptyRecordLiteral(Variable),
|
||||
Underscore(Variable),
|
||||
|
||||
// Runtime Exceptions
|
||||
Shadowed(Located<Ident>),
|
||||
@ -38,13 +39,13 @@ pub enum PatternType {
|
||||
CaseBranch,
|
||||
}
|
||||
|
||||
pub fn canonicalize_pattern(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
pattern_type: &PatternType,
|
||||
loc_pattern: &Located<ast::Pattern>,
|
||||
shadowable_idents: &mut ImMap<Ident, (Symbol, Region)>,
|
||||
) -> Located<Pattern> {
|
||||
pub fn canonicalize_pattern<'a>(
|
||||
env: &'a mut Env<'a>,
|
||||
scope: &'a mut Scope<'a>,
|
||||
pattern_type: &'a PatternType,
|
||||
loc_pattern: &'a Located<ast::Pattern<'a>>,
|
||||
shadowable_idents: &'a mut ImMap<Ident, (Symbol<'a>, Region)>,
|
||||
) -> Located<Pattern<'a>> {
|
||||
use self::PatternType::*;
|
||||
use can::ast::Pattern::*;
|
||||
|
||||
@ -78,7 +79,7 @@ pub fn canonicalize_pattern(
|
||||
None => {
|
||||
// Make sure we aren't shadowing something in the home module's scope.
|
||||
let qualified_ident =
|
||||
Ident::Qualified(env.home.clone(), unqualified_ident.name());
|
||||
Ident::Qualified(env.home.to_string(), unqualified_ident.name());
|
||||
|
||||
match scope.idents.get(&qualified_ident) {
|
||||
Some((_, region)) => {
|
||||
@ -113,7 +114,7 @@ pub fn canonicalize_pattern(
|
||||
.insert(new_ident.clone(), symbol_and_region.clone());
|
||||
shadowable_idents.insert(new_ident, symbol_and_region);
|
||||
|
||||
Pattern::Identifier(symbol)
|
||||
Pattern::Identifier(env.subs.mk_flex_var(), symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -156,7 +157,7 @@ pub fn canonicalize_pattern(
|
||||
|
||||
if env.variants.contains_key(&symbol) {
|
||||
// No problems; the qualified variant name was in scope!
|
||||
Pattern::Variant(symbol)
|
||||
Pattern::Variant(env.subs.mk_flex_var(), symbol)
|
||||
} else {
|
||||
let loc_name = Located {
|
||||
region: region.clone(),
|
||||
@ -184,7 +185,7 @@ pub fn canonicalize_pattern(
|
||||
// ptype @ Assignment | ptype @ FunctionArg => unsupported_pattern(env, *ptype, region),
|
||||
// },
|
||||
&Underscore => match pattern_type {
|
||||
CaseBranch | FunctionArg => Pattern::Underscore,
|
||||
CaseBranch | FunctionArg => Pattern::Underscore(env.subs.mk_flex_var()),
|
||||
Assignment => unsupported_pattern(env, Assignment, region.clone()),
|
||||
},
|
||||
|
||||
@ -200,7 +201,11 @@ pub fn canonicalize_pattern(
|
||||
|
||||
/// When we detect an unsupported pattern type (e.g. 5 = 1 + 2 is unsupported because you can't
|
||||
/// assign to Int patterns), report it to Env and return an UnsupportedPattern runtime error pattern.
|
||||
fn unsupported_pattern<'a>(env: &mut Env, pattern_type: PatternType, region: Region) -> Pattern {
|
||||
fn unsupported_pattern<'a>(
|
||||
env: &'a mut Env<'a>,
|
||||
pattern_type: PatternType,
|
||||
region: Region,
|
||||
) -> Pattern<'a> {
|
||||
env.problem(Problem::UnsupportedPattern(pattern_type, region.clone()));
|
||||
|
||||
Pattern::UnsupportedPattern(region)
|
||||
|
@ -1,3 +1,4 @@
|
||||
use bumpalo::collections::Vec;
|
||||
use can::expr::Expr;
|
||||
use can::pattern::{Pattern, PatternType};
|
||||
use ident::{Ident, VariantName};
|
||||
@ -6,7 +7,7 @@ use region::{Located, Region};
|
||||
|
||||
/// Problems that can occur in the course of canonicalization.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Problem {
|
||||
pub enum Problem<'a> {
|
||||
Shadowing(Located<Ident>),
|
||||
UnrecognizedFunctionName(Located<Ident>),
|
||||
UnrecognizedConstant(Located<Ident>),
|
||||
@ -16,8 +17,8 @@ pub enum Problem {
|
||||
PrecedenceProblem(PrecedenceProblem),
|
||||
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
||||
UnsupportedPattern(PatternType, Region),
|
||||
CircularAssignment(Vec<Located<Ident>>),
|
||||
RuntimeError(RuntimeError),
|
||||
CircularAssignment(Vec<'a, Located<Ident>>),
|
||||
RuntimeError(RuntimeError<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
@ -26,20 +27,20 @@ pub enum PrecedenceProblem {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum RuntimeError {
|
||||
InvalidPrecedence(PrecedenceProblem, Box<Located<Expr>>),
|
||||
pub enum RuntimeError<'a> {
|
||||
InvalidPrecedence(PrecedenceProblem, &'a Located<Expr<'a>>),
|
||||
UnrecognizedFunctionName(Located<Ident>),
|
||||
UnrecognizedConstant(Located<Ident>),
|
||||
UnrecognizedVariant(Located<VariantName>),
|
||||
FloatOutsideRange(Box<str>),
|
||||
IntOutsideRange(Box<str>),
|
||||
InvalidHex(std::num::ParseIntError, Box<str>),
|
||||
InvalidOctal(std::num::ParseIntError, Box<str>),
|
||||
InvalidBinary(std::num::ParseIntError, Box<str>),
|
||||
FloatOutsideRange(&'a str),
|
||||
IntOutsideRange(&'a str),
|
||||
InvalidHex(std::num::ParseIntError, &'a str),
|
||||
InvalidOctal(std::num::ParseIntError, &'a str),
|
||||
InvalidBinary(std::num::ParseIntError, &'a str),
|
||||
CircularAssignment(
|
||||
Vec<Located<Ident>>,
|
||||
Vec<(Located<Pattern>, Located<Expr>)>,
|
||||
Box<Located<Expr>>,
|
||||
Vec<'a, Located<Ident>>,
|
||||
Vec<'a, (Located<Pattern<'a>>, Located<Expr<'a>>)>,
|
||||
&'a Located<Expr<'a>>,
|
||||
),
|
||||
|
||||
/// When the author specifies a type annotation but no implementation
|
||||
|
@ -5,22 +5,22 @@ use collections::ImSet;
|
||||
use region::{Located, Region};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Procedure {
|
||||
pub name: Option<String>,
|
||||
pub struct Procedure<'a> {
|
||||
pub name: Option<&'a str>,
|
||||
pub is_self_tail_recursive: bool,
|
||||
pub definition: Region,
|
||||
pub args: Vec<Located<Pattern>>,
|
||||
pub body: Located<Expr>,
|
||||
pub references: References,
|
||||
pub args: &'a [Located<Pattern<'a>>],
|
||||
pub body: Located<Expr<'a>>,
|
||||
pub references: References<'a>,
|
||||
}
|
||||
|
||||
impl Procedure {
|
||||
impl<'a> Procedure<'a> {
|
||||
pub fn new(
|
||||
definition: Region,
|
||||
args: Vec<Located<Pattern>>,
|
||||
body: Located<Expr>,
|
||||
references: References,
|
||||
) -> Procedure {
|
||||
args: &'a [Located<Pattern<'a>>],
|
||||
body: Located<Expr<'a>>,
|
||||
references: References<'a>,
|
||||
) -> Procedure<'a> {
|
||||
Procedure {
|
||||
name: None,
|
||||
is_self_tail_recursive: false,
|
||||
@ -36,15 +36,15 @@ impl Procedure {
|
||||
/// to determine how assignments shuold be ordered. We want builds to be reproducible,
|
||||
/// so it's important that building the same code gives the same order every time!
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct References {
|
||||
pub locals: ImSet<Symbol>,
|
||||
pub globals: ImSet<Symbol>,
|
||||
pub variants: ImSet<Symbol>,
|
||||
pub calls: ImSet<Symbol>,
|
||||
pub struct References<'a> {
|
||||
pub locals: ImSet<Symbol<'a>>,
|
||||
pub globals: ImSet<Symbol<'a>>,
|
||||
pub variants: ImSet<Symbol<'a>>,
|
||||
pub calls: ImSet<Symbol<'a>>,
|
||||
}
|
||||
|
||||
impl References {
|
||||
pub fn new() -> References {
|
||||
impl<'a> References<'a> {
|
||||
pub fn new() -> References<'a> {
|
||||
References {
|
||||
locals: ImSet::default(),
|
||||
globals: ImSet::default(),
|
||||
@ -53,7 +53,7 @@ impl References {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn union(mut self, other: References) -> Self {
|
||||
pub fn union(mut self, other: References<'a>) -> Self {
|
||||
self.locals = self.locals.union(other.locals);
|
||||
self.globals = self.globals.union(other.globals);
|
||||
self.variants = self.variants.union(other.variants);
|
||||
@ -62,11 +62,11 @@ impl References {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn has_local(&self, symbol: &Symbol) -> bool {
|
||||
pub fn has_local(&self, symbol: &Symbol<'a>) -> bool {
|
||||
self.locals.contains(symbol)
|
||||
}
|
||||
|
||||
pub fn has_variant(&self, symbol: &Symbol) -> bool {
|
||||
pub fn has_variant(&self, symbol: &Symbol<'a>) -> bool {
|
||||
self.variants.contains(symbol)
|
||||
}
|
||||
}
|
||||
|
@ -4,14 +4,17 @@ use ident::Ident;
|
||||
use region::Region;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Scope {
|
||||
pub idents: ImMap<Ident, (Symbol, Region)>,
|
||||
symbol_prefix: String,
|
||||
pub struct Scope<'a> {
|
||||
pub idents: ImMap<Ident, (Symbol<'a>, Region)>,
|
||||
symbol_prefix: &'a str,
|
||||
next_unique_id: u64,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
pub fn new(symbol_prefix: String, declared_idents: ImMap<Ident, (Symbol, Region)>) -> Scope {
|
||||
impl<'a> Scope<'a> {
|
||||
pub fn new(
|
||||
symbol_prefix: &'a str,
|
||||
declared_idents: ImMap<Ident, (Symbol<'a>, Region)>,
|
||||
) -> Scope<'a> {
|
||||
Scope {
|
||||
symbol_prefix,
|
||||
|
||||
@ -23,11 +26,11 @@ impl Scope {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbol(&self, name: &str) -> Symbol {
|
||||
pub fn symbol(&'a self, name: &'a str) -> Symbol<'a> {
|
||||
Symbol::new(&self.symbol_prefix, name)
|
||||
}
|
||||
|
||||
pub fn gen_unique_symbol(&mut self) -> Symbol {
|
||||
pub fn gen_unique_symbol(&mut self) -> Symbol<'a> {
|
||||
self.next_unique_id = self.next_unique_id + 1;
|
||||
|
||||
Symbol::new(&self.symbol_prefix, &self.next_unique_id.to_string())
|
||||
|
@ -3,14 +3,14 @@ use ident::VariantName;
|
||||
/// A globally unique identifier, used for both vars and variants.
|
||||
/// It will be used directly in code gen.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct Symbol(String);
|
||||
pub struct Symbol<'a>(&'a str);
|
||||
|
||||
impl Symbol {
|
||||
pub fn new(prefix: &str, name: &str) -> Symbol {
|
||||
Symbol(format!("{}{}", prefix, name))
|
||||
impl<'a> Symbol<'a> {
|
||||
pub fn new(prefix: &'a str, name: &'a str) -> Symbol<'a> {
|
||||
Symbol(&format!("{}{}", prefix, name))
|
||||
}
|
||||
|
||||
pub fn from_variant(variant_name: &VariantName, home: &str) -> Symbol {
|
||||
pub fn from_variant(variant_name: &'a VariantName, home: &'a str) -> Symbol<'a> {
|
||||
match &variant_name {
|
||||
&VariantName::Unqualified(ref name) => Symbol::new(home, name),
|
||||
|
||||
@ -19,8 +19,8 @@ impl Symbol {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<String> for Symbol {
|
||||
fn into(self) -> String {
|
||||
impl<'a> Into<&'a str> for Symbol<'a> {
|
||||
fn into(self) -> &'a str {
|
||||
let Symbol(string) = self;
|
||||
|
||||
string
|
||||
|
1150
src/constrain.rs
1150
src/constrain.rs
File diff suppressed because it is too large
Load Diff
633
src/gen/mod.rs
Normal file
633
src/gen/mod.rs
Normal file
@ -0,0 +1,633 @@
|
||||
//! This is an example of the [Kaleidoscope tutorial](https://llvm.org/docs/tutorial/)
|
||||
//! made in Rust, using Inkwell.
|
||||
//! Currently, all features up to the [7th chapter](https://llvm.org/docs/tutorial/LangImpl07.html)
|
||||
//! are available.
|
||||
//! This example is supposed to be ran as a executable, which launches a REPL.
|
||||
//! The source code is in the following order:
|
||||
//! - Lexer,
|
||||
//! - Parser,
|
||||
//! - Emitter,
|
||||
//! - Program.
|
||||
//!
|
||||
//! Both the `Parser` and the `Emitter` may fail, in which case they would return
|
||||
//! an error represented by `Result<T, &'static str>`, for easier error reporting.
|
||||
|
||||
extern crate inkwell;
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
// use std::iter::Peekable;
|
||||
// use std::ops::DerefMut;
|
||||
// use std::str::Chars;
|
||||
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::module::Module;
|
||||
use inkwell::passes::PassManager;
|
||||
use inkwell::types::BasicTypeEnum;
|
||||
use inkwell::values::{BasicValueEnum, FloatValue, FunctionValue, PointerValue};
|
||||
use inkwell::FloatPredicate;
|
||||
|
||||
// ======================================================================================
|
||||
// LEXER ================================================================================
|
||||
// ======================================================================================
|
||||
|
||||
/// Represents a primitive syntax token.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Token {
|
||||
Binary,
|
||||
Comma,
|
||||
Comment,
|
||||
Def,
|
||||
Else,
|
||||
EOF,
|
||||
Extern,
|
||||
For,
|
||||
Ident(String),
|
||||
If,
|
||||
In,
|
||||
LParen,
|
||||
Number(f64),
|
||||
Op(char),
|
||||
RParen,
|
||||
Then,
|
||||
Unary,
|
||||
Var,
|
||||
}
|
||||
|
||||
/// Defines the prototype (name and parameters) of a function.
|
||||
#[derive(Debug)]
|
||||
pub struct Prototype {
|
||||
pub name: String,
|
||||
pub args: Vec<String>,
|
||||
}
|
||||
|
||||
/// Defines a user-defined or external function.
|
||||
#[derive(Debug)]
|
||||
pub struct Function {
|
||||
pub prototype: Prototype,
|
||||
pub body: Option<Expr>,
|
||||
pub is_anon: bool,
|
||||
}
|
||||
|
||||
pub struct Emitter<'a> {
|
||||
pub context: &'a Context,
|
||||
pub builder: &'a Builder,
|
||||
pub fpm: &'a PassManager<FunctionValue>,
|
||||
pub module: &'a Module,
|
||||
pub function: &'a Function,
|
||||
|
||||
variables: HashMap<String, PointerValue>,
|
||||
fn_value_opt: Option<FunctionValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Expr {
|
||||
Binary {
|
||||
op: char,
|
||||
left: Box<Expr>,
|
||||
right: Box<Expr>,
|
||||
},
|
||||
|
||||
Call {
|
||||
fn_name: String,
|
||||
args: Vec<Expr>,
|
||||
},
|
||||
|
||||
Conditional {
|
||||
cond: Box<Expr>,
|
||||
consequence: Box<Expr>,
|
||||
alternative: Box<Expr>,
|
||||
},
|
||||
|
||||
For {
|
||||
var_name: String,
|
||||
start: Box<Expr>,
|
||||
end: Box<Expr>,
|
||||
step: Option<Box<Expr>>,
|
||||
body: Box<Expr>,
|
||||
},
|
||||
|
||||
Number(f64),
|
||||
|
||||
Variable(String),
|
||||
|
||||
VarIn {
|
||||
variables: Vec<(String, Option<Expr>)>,
|
||||
body: Box<Expr>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> Emitter<'a> {
|
||||
/// Gets a defined function given its name.
|
||||
#[inline]
|
||||
fn get_function(&self, name: &str) -> Option<FunctionValue> {
|
||||
self.module.get_function(name)
|
||||
}
|
||||
|
||||
/// Returns the `FunctionValue` representing the function being compiled.
|
||||
#[inline]
|
||||
fn fn_value(&self) -> FunctionValue {
|
||||
// TODO shouldn't there be like a stack of these? What about functions inside functions?
|
||||
// Also, do we even need this?
|
||||
self.fn_value_opt.unwrap()
|
||||
}
|
||||
|
||||
/// Creates a new stack allocation instruction in the entry block of the function.
|
||||
fn create_entry_block_alloca(&self, name: &str) -> PointerValue {
|
||||
let builder = self.context.create_builder();
|
||||
|
||||
let entry = self.fn_value().get_first_basic_block().unwrap();
|
||||
|
||||
match entry.get_first_instruction() {
|
||||
Some(first_instr) => builder.position_before(&first_instr),
|
||||
None => builder.position_at_end(&entry),
|
||||
}
|
||||
|
||||
builder.build_alloca(self.context.f64_type(), name)
|
||||
}
|
||||
|
||||
/// Compiles the specified `Expr` into an LLVM `FloatValue`.
|
||||
fn compile_expr(&mut self, expr: &Expr) -> Result<FloatValue, &'static str> {
|
||||
match *expr {
|
||||
// Expr::IntLiteral(num) => Ok(self.context.i64_type().const_int(num as u64, false)),
|
||||
Expr::Number(num) => Ok(self.context.f64_type().const_float(num)),
|
||||
|
||||
Expr::Variable(ref name) => match self.variables.get(name.as_str()) {
|
||||
Some(var) => Ok(self
|
||||
.builder
|
||||
.build_load(*var, name.as_str())
|
||||
.into_float_value()),
|
||||
None => Err("Could not find a matching variable."),
|
||||
},
|
||||
|
||||
Expr::VarIn {
|
||||
ref variables,
|
||||
ref body,
|
||||
} => {
|
||||
let mut old_bindings = Vec::new();
|
||||
|
||||
for &(ref var_name, ref initializer) in variables {
|
||||
let var_name = var_name.as_str();
|
||||
|
||||
let initial_val = match *initializer {
|
||||
Some(ref init) => self.compile_expr(init)?,
|
||||
None => self.context.f64_type().const_float(0.),
|
||||
};
|
||||
|
||||
let alloca = self.create_entry_block_alloca(var_name);
|
||||
|
||||
self.builder.build_store(alloca, initial_val);
|
||||
|
||||
if let Some(old_binding) = self.variables.remove(var_name) {
|
||||
old_bindings.push(old_binding);
|
||||
}
|
||||
|
||||
self.variables.insert(var_name.to_string(), alloca);
|
||||
}
|
||||
|
||||
let body = self.compile_expr(body)?;
|
||||
|
||||
for binding in old_bindings {
|
||||
self.variables
|
||||
.insert(binding.get_name().to_str().unwrap().to_string(), binding);
|
||||
}
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
Expr::Binary {
|
||||
op,
|
||||
ref left,
|
||||
ref right,
|
||||
} => {
|
||||
if op == '=' {
|
||||
// handle assignement
|
||||
let var_name = match *left.borrow() {
|
||||
Expr::Variable(ref var_name) => var_name,
|
||||
_ => {
|
||||
return Err("Expected variable as left-hand operator of assignement.");
|
||||
}
|
||||
};
|
||||
|
||||
let var_val = self.compile_expr(right)?;
|
||||
let var = self
|
||||
.variables
|
||||
.get(var_name.as_str())
|
||||
.ok_or("Undefined variable.")?;
|
||||
|
||||
self.builder.build_store(*var, var_val);
|
||||
|
||||
Ok(var_val)
|
||||
} else {
|
||||
let lhs = self.compile_expr(left)?;
|
||||
let rhs = self.compile_expr(right)?;
|
||||
|
||||
match op {
|
||||
'+' => Ok(self.builder.build_float_add(lhs, rhs, "tmpadd")),
|
||||
'-' => Ok(self.builder.build_float_sub(lhs, rhs, "tmpsub")),
|
||||
'*' => Ok(self.builder.build_float_mul(lhs, rhs, "tmpmul")),
|
||||
'/' => Ok(self.builder.build_float_div(lhs, rhs, "tmpdiv")),
|
||||
'<' => Ok({
|
||||
let cmp = self.builder.build_float_compare(
|
||||
FloatPredicate::ULT,
|
||||
lhs,
|
||||
rhs,
|
||||
"tmpcmp",
|
||||
);
|
||||
|
||||
self.builder.build_unsigned_int_to_float(
|
||||
cmp,
|
||||
self.context.f64_type(),
|
||||
"tmpbool",
|
||||
)
|
||||
}),
|
||||
'>' => Ok({
|
||||
let cmp = self.builder.build_float_compare(
|
||||
FloatPredicate::ULT,
|
||||
rhs,
|
||||
lhs,
|
||||
"tmpcmp",
|
||||
);
|
||||
|
||||
self.builder.build_unsigned_int_to_float(
|
||||
cmp,
|
||||
self.context.f64_type(),
|
||||
"tmpbool",
|
||||
)
|
||||
}),
|
||||
|
||||
custom => {
|
||||
let mut name = String::from("binary");
|
||||
|
||||
name.push(custom);
|
||||
|
||||
match self.get_function(name.as_str()) {
|
||||
Some(fun) => {
|
||||
match self
|
||||
.builder
|
||||
.build_call(fun, &[lhs.into(), rhs.into()], "tmpbin")
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
{
|
||||
Some(value) => Ok(value.into_float_value()),
|
||||
None => Err("Invalid call produced."),
|
||||
}
|
||||
}
|
||||
|
||||
None => Err("Undefined binary operator."),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Call {
|
||||
ref fn_name,
|
||||
ref args,
|
||||
} => match self.get_function(fn_name.as_str()) {
|
||||
Some(fun) => {
|
||||
let mut compiled_args = Vec::with_capacity(args.len());
|
||||
|
||||
for arg in args {
|
||||
compiled_args.push(self.compile_expr(arg)?);
|
||||
}
|
||||
|
||||
let argsv: Vec<BasicValueEnum> = compiled_args
|
||||
.iter()
|
||||
.by_ref()
|
||||
.map(|&val| val.into())
|
||||
.collect();
|
||||
|
||||
match self
|
||||
.builder
|
||||
.build_call(fun, argsv.as_slice(), "tmp")
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
{
|
||||
Some(value) => Ok(value.into_float_value()),
|
||||
None => Err("Invalid call produced."),
|
||||
}
|
||||
}
|
||||
None => Err("Unknown function."),
|
||||
},
|
||||
|
||||
Expr::Conditional {
|
||||
ref cond,
|
||||
ref consequence,
|
||||
ref alternative,
|
||||
} => {
|
||||
let parent = self.fn_value();
|
||||
let zero_const = self.context.f64_type().const_float(0.0);
|
||||
|
||||
// create condition by comparing without 0.0 and returning an int
|
||||
let cond = self.compile_expr(cond)?;
|
||||
let cond = self.builder.build_float_compare(
|
||||
FloatPredicate::ONE,
|
||||
cond,
|
||||
zero_const,
|
||||
"ifcond",
|
||||
);
|
||||
|
||||
// build branch
|
||||
let then_bb = self.context.append_basic_block(&parent, "then");
|
||||
let else_bb = self.context.append_basic_block(&parent, "else");
|
||||
let cont_bb = self.context.append_basic_block(&parent, "ifcont");
|
||||
|
||||
self.builder
|
||||
.build_conditional_branch(cond, &then_bb, &else_bb);
|
||||
|
||||
// build then block
|
||||
self.builder.position_at_end(&then_bb);
|
||||
let then_val = self.compile_expr(consequence)?;
|
||||
self.builder.build_unconditional_branch(&cont_bb);
|
||||
|
||||
let then_bb = self.builder.get_insert_block().unwrap();
|
||||
|
||||
// build else block
|
||||
self.builder.position_at_end(&else_bb);
|
||||
let else_val = self.compile_expr(alternative)?;
|
||||
self.builder.build_unconditional_branch(&cont_bb);
|
||||
|
||||
let else_bb = self.builder.get_insert_block().unwrap();
|
||||
|
||||
// emit merge block
|
||||
self.builder.position_at_end(&cont_bb);
|
||||
|
||||
let phi = self.builder.build_phi(self.context.f64_type(), "iftmp");
|
||||
|
||||
phi.add_incoming(&[(&then_val, &then_bb), (&else_val, &else_bb)]);
|
||||
|
||||
Ok(phi.as_basic_value().into_float_value())
|
||||
}
|
||||
|
||||
Expr::For {
|
||||
ref var_name,
|
||||
ref start,
|
||||
ref end,
|
||||
ref step,
|
||||
ref body,
|
||||
} => {
|
||||
let parent = self.fn_value();
|
||||
|
||||
let start_alloca = self.create_entry_block_alloca(var_name);
|
||||
let start = self.compile_expr(start)?;
|
||||
|
||||
self.builder.build_store(start_alloca, start);
|
||||
|
||||
// go from current block to loop block
|
||||
let loop_bb = self.context.append_basic_block(&parent, "loop");
|
||||
|
||||
self.builder.build_unconditional_branch(&loop_bb);
|
||||
self.builder.position_at_end(&loop_bb);
|
||||
|
||||
let old_val = self.variables.remove(var_name.as_str());
|
||||
|
||||
self.variables.insert(var_name.to_owned(), start_alloca);
|
||||
|
||||
// emit body
|
||||
self.compile_expr(body)?;
|
||||
|
||||
// emit step
|
||||
let step = match *step {
|
||||
Some(ref step) => self.compile_expr(step)?,
|
||||
None => self.context.f64_type().const_float(1.0),
|
||||
};
|
||||
|
||||
// compile end condition
|
||||
let end_cond = self.compile_expr(end)?;
|
||||
|
||||
let curr_var = self.builder.build_load(start_alloca, var_name);
|
||||
let next_var =
|
||||
self.builder
|
||||
.build_float_add(curr_var.into_float_value(), step, "nextvar");
|
||||
|
||||
self.builder.build_store(start_alloca, next_var);
|
||||
|
||||
let end_cond = self.builder.build_float_compare(
|
||||
FloatPredicate::ONE,
|
||||
end_cond,
|
||||
self.context.f64_type().const_float(0.0),
|
||||
"loopcond",
|
||||
);
|
||||
let after_bb = self.context.append_basic_block(&parent, "afterloop");
|
||||
|
||||
self.builder
|
||||
.build_conditional_branch(end_cond, &loop_bb, &after_bb);
|
||||
self.builder.position_at_end(&after_bb);
|
||||
|
||||
self.variables.remove(var_name);
|
||||
|
||||
if let Some(val) = old_val {
|
||||
self.variables.insert(var_name.to_owned(), val);
|
||||
}
|
||||
|
||||
Ok(self.context.f64_type().const_float(0.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compiles the specified `Prototype` into an extern LLVM `FunctionValue`.
|
||||
fn compile_prototype(&self, proto: &Prototype) -> Result<FunctionValue, &'static str> {
|
||||
let ret_type = self.context.f64_type();
|
||||
let args_types = std::iter::repeat(ret_type)
|
||||
.take(proto.args.len())
|
||||
.map(|f| f.into())
|
||||
.collect::<Vec<BasicTypeEnum>>();
|
||||
let args_types = args_types.as_slice();
|
||||
|
||||
let fn_type = self.context.f64_type().fn_type(args_types, false);
|
||||
let fn_val = self.module.add_function(proto.name.as_str(), fn_type, None);
|
||||
|
||||
// set arguments names
|
||||
for (i, arg) in fn_val.get_param_iter().enumerate() {
|
||||
arg.into_float_value().set_name(proto.args[i].as_str());
|
||||
}
|
||||
|
||||
// finally return built prototype
|
||||
Ok(fn_val)
|
||||
}
|
||||
|
||||
/// Compiles the specified `Function` into an LLVM `FunctionValue`.
|
||||
fn compile_fn(&mut self) -> Result<FunctionValue, &'static str> {
|
||||
let proto = &self.function.prototype;
|
||||
let function = self.compile_prototype(proto)?;
|
||||
|
||||
// got external function, returning only compiled prototype
|
||||
if self.function.body.is_none() {
|
||||
return Ok(function);
|
||||
}
|
||||
|
||||
let entry = self.context.append_basic_block(&function, "entry");
|
||||
|
||||
self.builder.position_at_end(&entry);
|
||||
|
||||
// update fn field
|
||||
self.fn_value_opt = Some(function);
|
||||
|
||||
// build variables map
|
||||
self.variables.reserve(proto.args.len());
|
||||
|
||||
for (i, arg) in function.get_param_iter().enumerate() {
|
||||
let arg_name = proto.args[i].as_str();
|
||||
let alloca = self.create_entry_block_alloca(arg_name);
|
||||
|
||||
self.builder.build_store(alloca, arg);
|
||||
|
||||
self.variables.insert(proto.args[i].clone(), alloca);
|
||||
}
|
||||
|
||||
// compile body
|
||||
let body = self.compile_expr(self.function.body.as_ref().unwrap())?;
|
||||
|
||||
self.builder.build_return(Some(&body));
|
||||
|
||||
// return the whole thing after verification and optimization
|
||||
if function.verify(true) {
|
||||
self.fpm.run_on(&function);
|
||||
|
||||
Ok(function)
|
||||
} else {
|
||||
unsafe {
|
||||
function.delete();
|
||||
}
|
||||
|
||||
Err("Invalid generated function.")
|
||||
}
|
||||
}
|
||||
|
||||
/// Compiles the specified `Function` in the given `Context` and using the specified `Builder`, `PassManager`, and `Module`.
|
||||
pub fn compile(
|
||||
context: &'a Context,
|
||||
builder: &'a Builder,
|
||||
pass_manager: &'a PassManager<FunctionValue>,
|
||||
module: &'a Module,
|
||||
function: &Function,
|
||||
) -> Result<FunctionValue, &'static str> {
|
||||
let mut compiler = Emitter {
|
||||
context: context,
|
||||
builder: builder,
|
||||
fpm: pass_manager,
|
||||
module: module,
|
||||
function: function,
|
||||
fn_value_opt: None,
|
||||
variables: HashMap::new(),
|
||||
};
|
||||
|
||||
compiler.compile_fn()
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================================
|
||||
// PROGRAM ==============================================================================
|
||||
// ======================================================================================
|
||||
|
||||
// macro used to print & flush without printing a new line
|
||||
macro_rules! print_flush {
|
||||
( $( $x:expr ),* ) => {
|
||||
print!( $($x, )* );
|
||||
|
||||
std::io::stdout().flush().expect("Could not flush to standard output.");
|
||||
};
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn putchard(x: f64) -> f64 {
|
||||
print_flush!("{}", x as u8 as char);
|
||||
x
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn printd(x: f64) -> f64 {
|
||||
println!("{}", x);
|
||||
x
|
||||
}
|
||||
|
||||
// Adding the functions above to a global array,
|
||||
// so Rust compiler won't remove them.
|
||||
#[used]
|
||||
static EXTERNAL_FNS: [extern "C" fn(f64) -> f64; 2] = [putchard, printd];
|
||||
|
||||
#[test]
|
||||
fn gen() {
|
||||
use inkwell::OptimizationLevel;
|
||||
// use self::inkwell::support::add_symbol;
|
||||
|
||||
let context = Context::create();
|
||||
let module = context.create_module("repl");
|
||||
let builder = context.create_builder();
|
||||
|
||||
// Create FPM
|
||||
let fpm = PassManager::create(&module);
|
||||
|
||||
fpm.add_instruction_combining_pass();
|
||||
fpm.add_reassociate_pass();
|
||||
fpm.add_gvn_pass();
|
||||
fpm.add_cfg_simplification_pass();
|
||||
fpm.add_basic_alias_analysis_pass();
|
||||
fpm.add_promote_memory_to_register_pass();
|
||||
fpm.add_instruction_combining_pass();
|
||||
fpm.add_reassociate_pass();
|
||||
|
||||
fpm.initialize();
|
||||
|
||||
// make module
|
||||
let module = context.create_module("tmp");
|
||||
|
||||
let name = "main".to_string();
|
||||
let prototype = Prototype {
|
||||
name,
|
||||
args: Vec::new(),
|
||||
};
|
||||
let function = Function {
|
||||
prototype,
|
||||
body: Some(Expr::Number(12345.0)),
|
||||
is_anon: false,
|
||||
};
|
||||
|
||||
// TODO remove this dead code
|
||||
for prev in &Vec::new() {
|
||||
Emitter::compile(&context, &builder, &fpm, &module, prev)
|
||||
.expect("Cannot re-add previously compiled function.");
|
||||
}
|
||||
|
||||
// TODO write a function which takes a context and expr (etc) and panics
|
||||
// if the given expr is not a function expr, and then takes the contents
|
||||
// of that function and writes them all to the context.
|
||||
//
|
||||
// For now, it needs to support only Float values.
|
||||
//
|
||||
// The function doesn't need to take any arguments yet. In fact, it can
|
||||
// just return 12345.
|
||||
//
|
||||
// The whole key here is that we want a main function which looks like:
|
||||
//
|
||||
// fn main() -> f64 { return 12345; }
|
||||
//
|
||||
// we want fn_val to be equal to that, so we can name it "main" and run it.
|
||||
|
||||
// make main(), a function which returns an f64
|
||||
|
||||
Emitter::compile(&context, &builder, &fpm, &module, &function).expect("Error compiling main");
|
||||
|
||||
// let fn_type = context.f64_type().fn_type(&Vec::new(), false);
|
||||
// let function = module.add_function(proto.name.as_str(), fn_type, None);
|
||||
|
||||
// make execution engine
|
||||
let ee = module
|
||||
.create_jit_execution_engine(OptimizationLevel::None)
|
||||
.unwrap();
|
||||
|
||||
let maybe_fn = unsafe { ee.get_function::<unsafe extern "C" fn() -> f64>("main") };
|
||||
let compiled_fn = match maybe_fn {
|
||||
Ok(f) => f,
|
||||
Err(err) => {
|
||||
panic!("!> Error during execution: {:?}", err);
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
panic!("=> {}", compiled_fn.call());
|
||||
}
|
||||
}
|
45
src/infer.rs
45
src/infer.rs
@ -10,34 +10,35 @@ use types::Constraint;
|
||||
use types::Expected::*;
|
||||
use types::Type::*;
|
||||
|
||||
pub fn infer_expr(
|
||||
subs: &mut Subs,
|
||||
loc_expr: Located<Expr>,
|
||||
procedures: MutMap<Symbol, Procedure>,
|
||||
) -> Content {
|
||||
let bound_vars = ImMap::default();
|
||||
let mut env = ImMap::default();
|
||||
let mut constraints = Vec::with_capacity(1 + procedures.len());
|
||||
pub fn infer_expr<'a>(
|
||||
subs: &'a mut Subs<'a>,
|
||||
loc_expr: Located<Expr<'a>>,
|
||||
procedures: MutMap<Symbol<'a>, Procedure<'a>>,
|
||||
) -> Content<'a> {
|
||||
panic!("TODO re-constrain procedures.");
|
||||
// let bound_vars = ImMap::default();
|
||||
// let mut env = ImMap::default();
|
||||
// let mut constraints = Vec::with_capacity(1 + procedures.len());
|
||||
|
||||
// First add constraints for all the procedures
|
||||
for (symbol, proc) in procedures {
|
||||
let variable = subs.mk_flex_var();
|
||||
let expected = NoExpectation(Variable(variable));
|
||||
// for (symbol, proc) in procedures {
|
||||
// let variable = subs.mk_flex_var();
|
||||
// let expected = NoExpectation(Variable(variable));
|
||||
|
||||
constraints.push(constrain_procedure(&bound_vars, subs, proc, expected));
|
||||
// constraints.push(constrain_procedure(&bound_vars, subs, proc, expected));
|
||||
|
||||
// Record this procedure in the env; variable lookups may reference it!
|
||||
env.insert(symbol, variable);
|
||||
}
|
||||
// // Record this procedure in the env; variable lookups may reference it!
|
||||
// env.insert(symbol, variable);
|
||||
// }
|
||||
|
||||
// Next, constrain the expression.
|
||||
let variable = subs.mk_flex_var();
|
||||
let expected = NoExpectation(Variable(variable));
|
||||
let constraint = constrain(&bound_vars, subs, loc_expr, expected);
|
||||
// let variable = subs.mk_flex_var();
|
||||
// let expected = NoExpectation(Variable(variable));
|
||||
// let constraint = constrain(&bound_vars, subs, loc_expr, expected);
|
||||
|
||||
constraints.push(constraint);
|
||||
// constraints.push(constraint);
|
||||
//
|
||||
// solve(&env, subs, &Constraint::And(constraints.into_bump_slice()));
|
||||
|
||||
solve(&env, subs, Constraint::And(constraints));
|
||||
|
||||
subs.get(variable).content
|
||||
// subs.get(variable).content
|
||||
}
|
||||
|
@ -19,10 +19,13 @@ pub mod subs;
|
||||
pub mod types;
|
||||
pub mod unify;
|
||||
|
||||
pub mod gen;
|
||||
|
||||
extern crate bumpalo;
|
||||
extern crate fraction;
|
||||
extern crate fxhash;
|
||||
extern crate im_rc;
|
||||
extern crate inkwell;
|
||||
extern crate num;
|
||||
|
||||
#[macro_use]
|
||||
|
@ -1,4 +1,5 @@
|
||||
use self::Operator::*;
|
||||
use can::symbol::Symbol;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@ -73,6 +74,27 @@ impl Operator {
|
||||
Pizza => 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn desugar(&self) -> Symbol<'static> {
|
||||
match self {
|
||||
Caret => Symbol::new("Num", "pow"),
|
||||
Star => Symbol::new("Num", "mul"),
|
||||
Slash => Symbol::new("Float", "div"),
|
||||
DoubleSlash => Symbol::new("Int", "div"),
|
||||
Percent => Symbol::new("Num", "rem"),
|
||||
DoublePercent => Symbol::new("Num", "mod"),
|
||||
Plus => Symbol::new("Num", "plus"),
|
||||
Minus => Symbol::new("Num", "sub"),
|
||||
Equals => Symbol::new("Bool", "isEqual"),
|
||||
LessThan => Symbol::new("Num", "isLessThan"),
|
||||
GreaterThan => Symbol::new("Num", "isGreaterThan"),
|
||||
LessThanOrEq => Symbol::new("Num", "isLessThanOrEqualTo"),
|
||||
GreaterThanOrEq => Symbol::new("Num", "isGreaterThanOrEqualTo"),
|
||||
And => Symbol::new("Bool", "and"),
|
||||
Or => Symbol::new("Bool", "or"),
|
||||
Pizza => panic!("Cannot desugar the |> operator"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Operator {
|
||||
|
@ -5,142 +5,143 @@ static EMPTY_RECORD: &'static str = "{}";
|
||||
|
||||
pub fn content_to_string(content: Content, subs: &mut Subs) -> String {
|
||||
let mut buf = String::new();
|
||||
panic!("TODO restore content_to_string()");
|
||||
|
||||
write_content(content, subs, &mut buf, false);
|
||||
// write_content(content, subs, &mut buf, false);
|
||||
|
||||
buf
|
||||
}
|
||||
|
||||
fn write_content(content: Content, subs: &mut Subs, buf: &mut String, use_parens: bool) {
|
||||
use subs::Content::*;
|
||||
// fn write_content(content: Content, subs: &mut Subs, buf: &mut String, use_parens: bool) {
|
||||
// use subs::Content::*;
|
||||
|
||||
match content {
|
||||
FlexVar(Some(name)) => buf.push_str(&name),
|
||||
FlexVar(None) => buf.push_str(WILDCARD),
|
||||
RigidVar(name) => buf.push_str(&name),
|
||||
Structure(flat_type) => write_flat_type(flat_type, subs, buf, use_parens),
|
||||
Error(_) => buf.push_str("<type mismatch>"),
|
||||
}
|
||||
}
|
||||
// match content {
|
||||
// FlexVar(Some(name)) => buf.push_str(&name),
|
||||
// FlexVar(None) => buf.push_str(WILDCARD),
|
||||
// RigidVar(name) => buf.push_str(&name),
|
||||
// Structure(flat_type) => write_flat_type(flat_type, subs, buf, use_parens),
|
||||
// Error(_) => buf.push_str("<type mismatch>"),
|
||||
// }
|
||||
// }
|
||||
|
||||
fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, use_parens: bool) {
|
||||
use subs::FlatType::*;
|
||||
// fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, use_parens: bool) {
|
||||
// use subs::FlatType::*;
|
||||
|
||||
match flat_type {
|
||||
Apply(module_name, type_name, args) => {
|
||||
write_apply(module_name, type_name, args, subs, buf, use_parens)
|
||||
}
|
||||
EmptyRecord => buf.push_str(EMPTY_RECORD),
|
||||
Func(args, ret) => write_fn(args, ret, subs, buf, use_parens),
|
||||
Operator(l_arg, r_arg, ret) => write_fn(vec![l_arg, r_arg], ret, subs, buf, use_parens),
|
||||
Erroneous(problem) => {
|
||||
buf.push_str(&format!("<Type Mismatch: {:?}>", problem));
|
||||
}
|
||||
}
|
||||
}
|
||||
// match flat_type {
|
||||
// Apply(module_name, type_name, args) => {
|
||||
// write_apply(module_name, type_name, args, subs, buf, use_parens)
|
||||
// }
|
||||
// EmptyRecord => buf.push_str(EMPTY_RECORD),
|
||||
// Func(args, ret) => write_fn(args, ret, subs, buf, use_parens),
|
||||
// Operator(l_arg, r_arg, ret) => write_fn(vec![l_arg, r_arg], ret, subs, buf, use_parens),
|
||||
// Erroneous(problem) => {
|
||||
// buf.push_str(&format!("<Type Mismatch: {:?}>", problem));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
fn write_apply(
|
||||
module_name: String,
|
||||
type_name: String,
|
||||
args: Vec<Variable>,
|
||||
subs: &mut Subs,
|
||||
buf: &mut String,
|
||||
use_parens: bool,
|
||||
) {
|
||||
let write_parens = use_parens && !args.is_empty();
|
||||
// fn write_apply(
|
||||
// module_name: String,
|
||||
// type_name: String,
|
||||
// args: Vec<Variable>,
|
||||
// subs: &mut Subs,
|
||||
// buf: &mut String,
|
||||
// use_parens: bool,
|
||||
// ) {
|
||||
// let write_parens = use_parens && !args.is_empty();
|
||||
|
||||
// Hardcoded type aliases
|
||||
if module_name == "Str" && type_name == "Str" {
|
||||
buf.push_str("Str");
|
||||
} else if module_name == "Num" && type_name == "Num" {
|
||||
let arg = args
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap_or_else(|| panic!("Num did not have any type parameters somehow."));
|
||||
let arg_content = subs.get(arg).content;
|
||||
let mut arg_param = String::new();
|
||||
// // Hardcoded type aliases
|
||||
// if module_name == "Str" && type_name == "Str" {
|
||||
// buf.push_str("Str");
|
||||
// } else if module_name == "Num" && type_name == "Num" {
|
||||
// let arg = args
|
||||
// .into_iter()
|
||||
// .next()
|
||||
// .unwrap_or_else(|| panic!("Num did not have any type parameters somehow."));
|
||||
// let arg_content = subs.get(arg).content;
|
||||
// let mut arg_param = String::new();
|
||||
|
||||
write_content(arg_content, subs, &mut arg_param, true);
|
||||
// write_content(arg_content, subs, &mut arg_param, true);
|
||||
|
||||
if arg_param == "Int.Integer" {
|
||||
buf.push_str("Int");
|
||||
} else if arg_param == "Float.FloatingPoint" {
|
||||
buf.push_str("Float");
|
||||
} else {
|
||||
if write_parens {
|
||||
buf.push_str("(");
|
||||
}
|
||||
// if arg_param == "Int.Integer" {
|
||||
// buf.push_str("Int");
|
||||
// } else if arg_param == "Float.FloatingPoint" {
|
||||
// buf.push_str("Float");
|
||||
// } else {
|
||||
// if write_parens {
|
||||
// buf.push_str("(");
|
||||
// }
|
||||
|
||||
buf.push_str("Num ");
|
||||
buf.push_str(&arg_param);
|
||||
// buf.push_str("Num ");
|
||||
// buf.push_str(&arg_param);
|
||||
|
||||
if write_parens {
|
||||
buf.push_str(")");
|
||||
}
|
||||
}
|
||||
} else if module_name == "List" && type_name == "List" {
|
||||
if write_parens {
|
||||
buf.push_str("(");
|
||||
}
|
||||
// if write_parens {
|
||||
// buf.push_str(")");
|
||||
// }
|
||||
// }
|
||||
// } else if module_name == "List" && type_name == "List" {
|
||||
// if write_parens {
|
||||
// buf.push_str("(");
|
||||
// }
|
||||
|
||||
buf.push_str("List ");
|
||||
// buf.push_str("List ");
|
||||
|
||||
let arg = args
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap_or_else(|| panic!("List did not have any type parameters somehow."));
|
||||
let arg_content = subs.get(arg).content;
|
||||
// let arg = args
|
||||
// .into_iter()
|
||||
// .next()
|
||||
// .unwrap_or_else(|| panic!("List did not have any type parameters somehow."));
|
||||
// let arg_content = subs.get(arg).content;
|
||||
|
||||
write_content(arg_content, subs, buf, true);
|
||||
// write_content(arg_content, subs, buf, true);
|
||||
|
||||
if write_parens {
|
||||
buf.push_str(")");
|
||||
}
|
||||
} else {
|
||||
if write_parens {
|
||||
buf.push_str("(");
|
||||
}
|
||||
// if write_parens {
|
||||
// buf.push_str(")");
|
||||
// }
|
||||
// } else {
|
||||
// if write_parens {
|
||||
// buf.push_str("(");
|
||||
// }
|
||||
|
||||
buf.push_str(&format!("{}.{}", module_name, type_name));
|
||||
// buf.push_str(&format!("{}.{}", module_name, type_name));
|
||||
|
||||
for arg in args {
|
||||
buf.push_str(" ");
|
||||
write_content(subs.get(arg).content, subs, buf, true);
|
||||
}
|
||||
// for arg in args {
|
||||
// buf.push_str(" ");
|
||||
// write_content(subs.get(arg).content, subs, buf, true);
|
||||
// }
|
||||
|
||||
if write_parens {
|
||||
buf.push_str(")");
|
||||
}
|
||||
}
|
||||
}
|
||||
// if write_parens {
|
||||
// buf.push_str(")");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
fn write_fn(
|
||||
args: Vec<Variable>,
|
||||
ret: Variable,
|
||||
subs: &mut Subs,
|
||||
buf: &mut String,
|
||||
use_parens: bool,
|
||||
) {
|
||||
let mut needs_comma = false;
|
||||
// fn write_fn(
|
||||
// args: Vec<Variable>,
|
||||
// ret: Variable,
|
||||
// subs: &mut Subs,
|
||||
// buf: &mut String,
|
||||
// use_parens: bool,
|
||||
// ) {
|
||||
// let mut needs_comma = false;
|
||||
|
||||
if use_parens {
|
||||
buf.push_str("(");
|
||||
}
|
||||
// if use_parens {
|
||||
// buf.push_str("(");
|
||||
// }
|
||||
|
||||
for arg in args {
|
||||
if needs_comma {
|
||||
buf.push_str(", ");
|
||||
} else {
|
||||
needs_comma = true;
|
||||
}
|
||||
// for arg in args {
|
||||
// if needs_comma {
|
||||
// buf.push_str(", ");
|
||||
// } else {
|
||||
// needs_comma = true;
|
||||
// }
|
||||
|
||||
write_content(subs.get(arg).content, subs, buf, false);
|
||||
}
|
||||
// write_content(subs.get(arg).content, subs, buf, false);
|
||||
// }
|
||||
|
||||
buf.push_str(" -> ");
|
||||
write_content(subs.get(ret).content, subs, buf, false);
|
||||
// buf.push_str(" -> ");
|
||||
// write_content(subs.get(ret).content, subs, buf, false);
|
||||
|
||||
if use_parens {
|
||||
buf.push_str(")");
|
||||
}
|
||||
}
|
||||
// if use_parens {
|
||||
// buf.push_str(")");
|
||||
// }
|
||||
// }
|
||||
|
66
src/solve.rs
66
src/solve.rs
@ -1,31 +1,19 @@
|
||||
// type Env =
|
||||
// Map.Map Name.Name Variable
|
||||
|
||||
// type Pools =
|
||||
// MVector.IOVector [Variable]
|
||||
|
||||
// data State =
|
||||
// State
|
||||
// { _env :: Env
|
||||
// , _mark :: Mark
|
||||
// , _errors :: [Error.Error]
|
||||
// }
|
||||
|
||||
use bumpalo::collections::Vec;
|
||||
use can::symbol::Symbol;
|
||||
use collections::ImMap;
|
||||
use subs::{Content, Descriptor, FlatType, Subs, Variable};
|
||||
use types::Constraint::{self, *};
|
||||
use types::Type::{self, *};
|
||||
|
||||
type Env = ImMap<Symbol, Variable>;
|
||||
type Env<'a> = ImMap<Symbol<'a>, Variable>;
|
||||
|
||||
pub fn solve(env: &Env, subs: &mut Subs, constraint: Constraint) {
|
||||
pub fn solve<'a>(env: &'a Env<'a>, subs: &'a mut Subs<'a>, constraint: &'a Constraint<'a>) {
|
||||
// println!("\nSolving:\n\n\t{:?}\n\n", constraint);
|
||||
match constraint {
|
||||
True => (),
|
||||
Eq(typ, expected_type, _region) => {
|
||||
// TODO use region?
|
||||
let actual = type_to_variable(subs, typ);
|
||||
let actual = type_to_variable(subs, typ.clone());
|
||||
let expected = type_to_variable(subs, expected_type.get_type());
|
||||
|
||||
subs.union(actual, expected);
|
||||
@ -41,7 +29,7 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: Constraint) {
|
||||
subs.union(actual, expected);
|
||||
}
|
||||
And(sub_constraints) => {
|
||||
for sub_constraint in sub_constraints {
|
||||
for sub_constraint in sub_constraints.iter() {
|
||||
solve(env, subs, sub_constraint);
|
||||
}
|
||||
}
|
||||
@ -52,11 +40,11 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: Constraint) {
|
||||
True => {
|
||||
// If the return expression is guaranteed to solve,
|
||||
// solve the assignments themselves and move on.
|
||||
solve(env, subs, let_con.assignments_constraint)
|
||||
solve(env, subs, &let_con.assignments_constraint)
|
||||
}
|
||||
ret_con => {
|
||||
// Solve the assignments' constraints first.
|
||||
solve(env, subs, let_con.assignments_constraint);
|
||||
solve(env, subs, &let_con.assignments_constraint);
|
||||
|
||||
// Add a variable for each assignment to the env.
|
||||
let mut new_env = env.clone();
|
||||
@ -75,7 +63,7 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: Constraint) {
|
||||
|
||||
// Now solve the body, using the new env which includes
|
||||
// the assignments' name-to-variable mappings.
|
||||
solve(&new_env, subs, ret_con);
|
||||
solve(&new_env, subs, &ret_con);
|
||||
|
||||
// TODO do an occurs check for each of the assignments!
|
||||
}
|
||||
@ -84,16 +72,25 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: Constraint) {
|
||||
}
|
||||
}
|
||||
|
||||
fn type_to_variable(subs: &mut Subs, typ: Type) -> Variable {
|
||||
fn type_to_variable<'a>(subs: &'a mut Subs<'a>, typ: Type<'a>) -> Variable {
|
||||
match typ {
|
||||
Variable(var) => var,
|
||||
Apply(module_name, name, arg_types) => {
|
||||
let args: Vec<Variable> = arg_types
|
||||
.into_iter()
|
||||
.map(|arg| type_to_variable(subs, arg))
|
||||
.collect();
|
||||
Apply {
|
||||
module_name,
|
||||
name,
|
||||
args,
|
||||
} => {
|
||||
let mut arg_vars = Vec::with_capacity_in(args.len(), subs.arena);
|
||||
|
||||
let flat_type = FlatType::Apply(module_name, name, args);
|
||||
for arg in args {
|
||||
arg_vars.push(type_to_variable(subs, arg.clone()))
|
||||
}
|
||||
|
||||
let flat_type = FlatType::Apply {
|
||||
module_name,
|
||||
name,
|
||||
args: arg_vars.into_bump_slice(),
|
||||
};
|
||||
let content = Content::Structure(flat_type);
|
||||
|
||||
subs.fresh(Descriptor::from(content))
|
||||
@ -103,13 +100,16 @@ fn type_to_variable(subs: &mut Subs, typ: Type) -> Variable {
|
||||
|
||||
subs.fresh(Descriptor::from(content))
|
||||
}
|
||||
Function(arg_types, ret_type) => {
|
||||
let arg_vars = arg_types
|
||||
.into_iter()
|
||||
.map(|arg_type| type_to_variable(subs, arg_type))
|
||||
.collect();
|
||||
Function(args, ret_type) => {
|
||||
let mut arg_vars: Vec<'a, Variable> = Vec::with_capacity_in(args.len(), subs.arena);
|
||||
|
||||
for arg in args {
|
||||
arg_vars.push(type_to_variable(subs, arg.clone()))
|
||||
}
|
||||
|
||||
let ret_var = type_to_variable(subs, *ret_type);
|
||||
let content = Content::Structure(FlatType::Func(arg_vars, ret_var));
|
||||
let content: Content<'a> =
|
||||
Content::Structure(FlatType::Func(arg_vars.into_bump_slice(), ret_var));
|
||||
|
||||
subs.fresh(Descriptor::from(content))
|
||||
}
|
||||
|
53
src/subs.rs
53
src/subs.rs
@ -1,11 +1,13 @@
|
||||
use bumpalo::Bump;
|
||||
use ena::unify::{InPlace, UnificationTable, UnifyKey};
|
||||
use std::fmt;
|
||||
use types::Problem;
|
||||
use unify;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Subs {
|
||||
pub struct Subs<'a> {
|
||||
utable: UnificationTable<InPlace<Variable>>,
|
||||
pub arena: &'a Bump,
|
||||
}
|
||||
|
||||
#[derive(Copy, PartialEq, Eq, Clone)]
|
||||
@ -25,7 +27,7 @@ impl fmt::Debug for Variable {
|
||||
}
|
||||
|
||||
impl UnifyKey for Variable {
|
||||
type Value = Descriptor;
|
||||
type Value = Descriptor<'static>;
|
||||
|
||||
fn index(&self) -> u32 {
|
||||
self.0
|
||||
@ -40,19 +42,20 @@ impl UnifyKey for Variable {
|
||||
}
|
||||
}
|
||||
|
||||
impl Subs {
|
||||
pub fn new() -> Self {
|
||||
impl<'a> Subs<'a> {
|
||||
pub fn new(arena: &'a Bump) -> Self {
|
||||
Subs {
|
||||
utable: UnificationTable::default(),
|
||||
arena,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fresh(&mut self, value: Descriptor) -> Variable {
|
||||
pub fn fresh(&'a mut self, value: Descriptor<'a>) -> Variable {
|
||||
self.utable.new_key(value)
|
||||
}
|
||||
|
||||
/// Unions two keys without the possibility of failure.
|
||||
pub fn union(&mut self, left: Variable, right: Variable) {
|
||||
pub fn union(&'a mut self, left: Variable, right: Variable) {
|
||||
let l_root = self.utable.get_root_key(left.into());
|
||||
let r_root = self.utable.get_root_key(right.into());
|
||||
|
||||
@ -63,11 +66,11 @@ impl Subs {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&mut self, key: Variable) -> Descriptor {
|
||||
pub fn get(&'a mut self, key: Variable) -> Descriptor<'a> {
|
||||
self.utable.probe_value(key)
|
||||
}
|
||||
|
||||
pub fn set(&mut self, key: Variable, r_value: Descriptor) {
|
||||
pub fn set(&'a mut self, key: Variable, r_value: Descriptor<'a>) {
|
||||
let l_key = self.utable.get_root_key(key.into());
|
||||
let unified = unify::unify_var_val(self, l_key, &r_value);
|
||||
|
||||
@ -101,25 +104,25 @@ impl Subs {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn flex_var_descriptor() -> Descriptor {
|
||||
fn flex_var_descriptor() -> Descriptor<'static> {
|
||||
Descriptor::from(unnamed_flex_var())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn unnamed_flex_var() -> Content {
|
||||
fn unnamed_flex_var() -> Content<'static> {
|
||||
Content::FlexVar(None)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Descriptor {
|
||||
pub content: Content,
|
||||
pub struct Descriptor<'a> {
|
||||
pub content: Content<'a>,
|
||||
pub rank: usize,
|
||||
pub mark: u32,
|
||||
pub copy: Option<Variable>,
|
||||
}
|
||||
|
||||
impl From<Content> for Descriptor {
|
||||
fn from(content: Content) -> Self {
|
||||
impl<'a> From<Content<'a>> for Descriptor<'a> {
|
||||
fn from(content: Content<'a>) -> Descriptor<'a> {
|
||||
Descriptor {
|
||||
content,
|
||||
rank: 0,
|
||||
@ -130,21 +133,21 @@ impl From<Content> for Descriptor {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Content {
|
||||
FlexVar(Option<String> /* name */),
|
||||
RigidVar(String /* name */),
|
||||
Structure(FlatType),
|
||||
pub enum Content<'a> {
|
||||
FlexVar(Option<&'a str> /* name */),
|
||||
RigidVar(&'a str /* name */),
|
||||
Structure(FlatType<'a>),
|
||||
Error(Problem),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum FlatType {
|
||||
Apply(
|
||||
String, /* module name */
|
||||
String, /* type name */
|
||||
Vec<Variable>,
|
||||
),
|
||||
Func(Vec<Variable>, Variable),
|
||||
pub enum FlatType<'a> {
|
||||
Apply {
|
||||
module_name: &'a str,
|
||||
name: &'a str,
|
||||
args: &'a [Variable],
|
||||
},
|
||||
Func(&'a [Variable], Variable),
|
||||
Operator(Variable, Variable, Variable),
|
||||
Erroneous(Problem),
|
||||
EmptyRecord,
|
||||
|
97
src/types.rs
97
src/types.rs
@ -1,3 +1,4 @@
|
||||
use bumpalo::{self, Bump};
|
||||
use can::symbol::Symbol;
|
||||
use collections::ImMap;
|
||||
use operator::{ArgSide, Operator};
|
||||
@ -20,25 +21,29 @@ pub const MOD_DEFAULT: &'static str = "Default";
|
||||
pub const TYPE_NUM: &'static str = "Num";
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub enum Type {
|
||||
pub enum Type<'a> {
|
||||
EmptyRec,
|
||||
/// A function. The types of its arguments, then the type of its return value.
|
||||
Function(Vec<Type>, Box<Type>),
|
||||
Operator(Box<OperatorType>),
|
||||
Function(&'a [Type<'a>], &'a Type<'a>),
|
||||
Operator(&'a OperatorType<'a>),
|
||||
/// Applying a type to some arguments (e.g. Map.Map String Int)
|
||||
Apply(ModuleName, String, Vec<Type>),
|
||||
Apply {
|
||||
module_name: &'a str,
|
||||
name: &'a str,
|
||||
args: &'a [Type<'a>],
|
||||
},
|
||||
Variable(Variable),
|
||||
/// A type error, which will code gen to a runtime error
|
||||
Erroneous(Problem),
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn for_operator(op: Operator) -> OperatorType {
|
||||
impl<'a> Type<'a> {
|
||||
pub fn for_operator(arena: &'a Bump, op: Operator) -> OperatorType<'a> {
|
||||
use self::Operator::*;
|
||||
|
||||
match op {
|
||||
Slash => op_type(Type::float(), Type::float(), Type::float()),
|
||||
DoubleSlash => op_type(Type::int(), Type::int(), Type::int()),
|
||||
Slash => op_type(Type::float(arena), Type::float(arena), Type::float(arena)),
|
||||
DoubleSlash => op_type(Type::int(arena), Type::int(arena), Type::int(arena)),
|
||||
// TODO actually, don't put these in types.rs - instead, replace them
|
||||
// with an equivalence to their corresponding stdlib functions - e.g.
|
||||
// Slash generates a new variable and an Eq constraint with Float.div.
|
||||
@ -46,45 +51,61 @@ impl Type {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num(args: Vec<Type>) -> Self {
|
||||
Type::Apply(MOD_NUM.to_string(), TYPE_NUM.to_string(), args)
|
||||
pub fn num(args: &'a [Type<'a>]) -> Self {
|
||||
Type::Apply {
|
||||
module_name: MOD_NUM,
|
||||
name: TYPE_NUM,
|
||||
args,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn float() -> Self {
|
||||
let floating_point = Type::Apply(
|
||||
MOD_FLOAT.to_string(),
|
||||
"FloatingPoint".to_string(),
|
||||
Vec::new(),
|
||||
);
|
||||
pub fn float(arena: &'a Bump) -> Self {
|
||||
let floating_point = Type::Apply {
|
||||
module_name: MOD_FLOAT,
|
||||
name: "FloatingPoint",
|
||||
args: &[],
|
||||
};
|
||||
|
||||
Type::num(vec![floating_point])
|
||||
Type::num(bumpalo::vec![in &arena; floating_point].into_bump_slice())
|
||||
}
|
||||
|
||||
pub fn int() -> Self {
|
||||
let integer = Type::Apply(MOD_INT.to_string(), "Integer".to_string(), Vec::new());
|
||||
pub fn int(arena: &'a Bump) -> Self {
|
||||
let integer = Type::Apply {
|
||||
module_name: MOD_INT,
|
||||
name: "Integer",
|
||||
args: &[],
|
||||
};
|
||||
|
||||
Type::num(vec![integer])
|
||||
Type::num(bumpalo::vec![in &arena; integer].into_bump_slice())
|
||||
}
|
||||
|
||||
pub fn string() -> Self {
|
||||
Type::Apply(MOD_STR.to_string(), "Str".to_string(), Vec::new())
|
||||
Type::Apply {
|
||||
module_name: MOD_STR,
|
||||
name: "Str",
|
||||
args: &[],
|
||||
}
|
||||
}
|
||||
|
||||
/// This is needed to constrain `if` conditionals
|
||||
pub fn bool() -> Self {
|
||||
Type::Apply(MOD_DEFAULT.to_string(), "Bool".to_string(), Vec::new())
|
||||
Type::Apply {
|
||||
module_name: MOD_DEFAULT,
|
||||
name: "Bool",
|
||||
args: &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn op_type(left: Type, right: Type, ret: Type) -> OperatorType {
|
||||
fn op_type<'a>(left: Type<'a>, right: Type<'a>, ret: Type<'a>) -> OperatorType<'a> {
|
||||
OperatorType { left, right, ret }
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub struct OperatorType {
|
||||
pub left: Type,
|
||||
pub right: Type,
|
||||
pub ret: Type,
|
||||
pub struct OperatorType<'a> {
|
||||
pub left: Type<'a>,
|
||||
pub right: Type<'a>,
|
||||
pub ret: Type<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -117,21 +138,21 @@ pub enum Reason {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Constraint {
|
||||
Eq(Type, Expected<Type>, Region),
|
||||
Lookup(Symbol, Expected<Type>, Region),
|
||||
pub enum Constraint<'a> {
|
||||
Eq(Type<'a>, Expected<Type<'a>>, Region),
|
||||
Lookup(Symbol<'a>, Expected<Type<'a>>, Region),
|
||||
True, // Used for things that always unify, e.g. blanks and runtime errors
|
||||
Let(Box<LetConstraint>),
|
||||
And(Vec<Constraint>),
|
||||
Let(&'a LetConstraint<'a>),
|
||||
And(&'a [Constraint<'a>]),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LetConstraint {
|
||||
pub rigid_vars: Vec<Variable>,
|
||||
pub flex_vars: Vec<Variable>,
|
||||
pub assignment_types: ImMap<Symbol, Located<Type>>,
|
||||
pub assignments_constraint: Constraint,
|
||||
pub ret_constraint: Constraint,
|
||||
pub struct LetConstraint<'a> {
|
||||
pub rigid_vars: &'a [Variable],
|
||||
pub flex_vars: &'a [Variable],
|
||||
pub assignment_types: ImMap<Symbol<'a>, Located<Type<'a>>>,
|
||||
pub assignments_constraint: Constraint<'a>,
|
||||
pub ret_constraint: Constraint<'a>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
|
77
src/unify.rs
77
src/unify.rs
@ -1,22 +1,35 @@
|
||||
use bumpalo::collections::Vec;
|
||||
use subs::Content::{self, *};
|
||||
use subs::{Descriptor, FlatType, Subs, Variable};
|
||||
use types::Problem;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn unify_vars(subs: &mut Subs, left_key: Variable, right_key: Variable) -> Descriptor {
|
||||
pub fn unify_vars<'a>(
|
||||
subs: &'a mut Subs<'a>,
|
||||
left_key: Variable,
|
||||
right_key: Variable,
|
||||
) -> Descriptor<'a> {
|
||||
let right = subs.get(right_key);
|
||||
|
||||
unify_var_val(subs, left_key, &right)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn unify_var_val(subs: &mut Subs, left_key: Variable, right: &Descriptor) -> Descriptor {
|
||||
pub fn unify_var_val<'a>(
|
||||
subs: &'a mut Subs<'a>,
|
||||
left_key: Variable,
|
||||
right: &'a Descriptor<'a>,
|
||||
) -> Descriptor<'a> {
|
||||
let left = subs.get(left_key);
|
||||
|
||||
unify(subs, &left, right)
|
||||
}
|
||||
|
||||
pub fn unify(subs: &mut Subs, left: &Descriptor, right: &Descriptor) -> Descriptor {
|
||||
pub fn unify<'a>(
|
||||
subs: &'a mut Subs<'a>,
|
||||
left: &'a Descriptor<'a>,
|
||||
right: &'a Descriptor<'a>,
|
||||
) -> Descriptor<'a> {
|
||||
let answer = match left.content {
|
||||
FlexVar(ref opt_name) => unify_flex(opt_name, &right.content),
|
||||
RigidVar(ref name) => unify_rigid(name, &right.content),
|
||||
@ -27,13 +40,15 @@ pub fn unify(subs: &mut Subs, left: &Descriptor, right: &Descriptor) -> Descript
|
||||
}
|
||||
};
|
||||
|
||||
// println!("\nUnifying:\n\n\t{:?}\n\n\t{:?}\n\n\t-----\n\n\t{:?}\n\n", left.content, right.content, answer.content);
|
||||
|
||||
answer
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn unify_structure(subs: &mut Subs, flat_type: &FlatType, other: &Content) -> Descriptor {
|
||||
fn unify_structure<'a>(
|
||||
subs: &'a mut Subs<'a>,
|
||||
flat_type: &'a FlatType,
|
||||
other: &'a Content<'a>,
|
||||
) -> Descriptor<'a> {
|
||||
match other {
|
||||
FlexVar(_) => {
|
||||
// If the other is flex, Structure wins!
|
||||
@ -55,16 +70,33 @@ fn unify_structure(subs: &mut Subs, flat_type: &FlatType, other: &Content) -> De
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn unify_flat_type(subs: &mut Subs, left: &FlatType, right: &FlatType) -> Descriptor {
|
||||
fn unify_flat_type<'a>(
|
||||
subs: &'a mut Subs<'a>,
|
||||
left: &'a FlatType<'a>,
|
||||
right: &'a FlatType<'a>,
|
||||
) -> Descriptor<'a> {
|
||||
use subs::FlatType::*;
|
||||
|
||||
match (left, right) {
|
||||
(EmptyRecord, EmptyRecord) => from_content(Structure(left.clone())),
|
||||
(Apply(l_module_name, l_type_name, l_args), Apply(r_module_name, r_type_name, r_args))
|
||||
if l_module_name == r_module_name && l_type_name == r_type_name =>
|
||||
{
|
||||
(
|
||||
Apply {
|
||||
module_name: l_module_name,
|
||||
name: l_type_name,
|
||||
args: l_args,
|
||||
},
|
||||
Apply {
|
||||
module_name: r_module_name,
|
||||
name: r_type_name,
|
||||
args: r_args,
|
||||
},
|
||||
) if l_module_name == r_module_name && l_type_name == r_type_name => {
|
||||
let args = unify_args(subs, l_args.iter(), r_args.iter());
|
||||
let flat_type = Apply(l_module_name.clone(), l_type_name.clone(), args);
|
||||
let flat_type = Apply {
|
||||
module_name: l_module_name,
|
||||
name: l_type_name,
|
||||
args,
|
||||
};
|
||||
|
||||
from_content(Structure(flat_type))
|
||||
}
|
||||
@ -93,13 +125,13 @@ fn unify_flat_type(subs: &mut Subs, left: &FlatType, right: &FlatType) -> Descri
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_args<'a, I>(subs: &mut Subs, left_iter: I, right_iter: I) -> Vec<Variable>
|
||||
fn unify_args<'a, I>(subs: &'a mut Subs<'a>, left_iter: I, right_iter: I) -> &'a [Variable]
|
||||
where
|
||||
I: Iterator<Item = &'a Variable>,
|
||||
{
|
||||
left_iter
|
||||
.zip(right_iter)
|
||||
.map(|(l_var, r_var)| {
|
||||
let mut answer = Vec::new_in(subs.arena);
|
||||
|
||||
for (l_var, r_var) in left_iter.zip(right_iter) {
|
||||
// Look up the descriptors we have for these variables, and unify them.
|
||||
let descriptor = unify_vars(subs, l_var.clone(), r_var.clone());
|
||||
|
||||
@ -107,12 +139,13 @@ where
|
||||
subs.set(r_var.clone(), descriptor);
|
||||
subs.union(l_var.clone(), r_var.clone());
|
||||
|
||||
r_var.clone()
|
||||
})
|
||||
.collect()
|
||||
answer.push(r_var.clone())
|
||||
}
|
||||
|
||||
answer.into_bump_slice()
|
||||
}
|
||||
|
||||
fn union_vars(subs: &mut Subs, l_var: Variable, r_var: Variable) -> Variable {
|
||||
fn union_vars<'a>(subs: &'a mut Subs<'a>, l_var: Variable, r_var: Variable) -> Variable {
|
||||
// Look up the descriptors we have for these variables, and unify them.
|
||||
let descriptor = unify_vars(subs, l_var.clone(), r_var.clone());
|
||||
|
||||
@ -124,7 +157,7 @@ fn union_vars(subs: &mut Subs, l_var: Variable, r_var: Variable) -> Variable {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn unify_rigid(name: &String, other: &Content) -> Descriptor {
|
||||
fn unify_rigid<'a>(name: &'a str, other: &'a Content<'a>) -> Descriptor<'a> {
|
||||
match other {
|
||||
FlexVar(_) => {
|
||||
// If the other is flex, rigid wins!
|
||||
@ -143,7 +176,7 @@ fn unify_rigid(name: &String, other: &Content) -> Descriptor {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn unify_flex(opt_name: &Option<String>, other: &Content) -> Descriptor {
|
||||
fn unify_flex<'a>(opt_name: &'a Option<&'a str>, other: &'a Content<'a>) -> Descriptor<'a> {
|
||||
match other {
|
||||
FlexVar(None) => {
|
||||
// If both are flex, and only left has a name, keep the name around.
|
||||
@ -159,7 +192,7 @@ fn unify_flex(opt_name: &Option<String>, other: &Content) -> Descriptor {
|
||||
|
||||
/// TODO this was f/k/a merge() - got rid of the rank stuff...good idea? Bad?
|
||||
/// TODO it used to be { rank: std::cmp::min(left_rank, right_rank), ... }
|
||||
fn from_content(content: Content) -> Descriptor {
|
||||
fn from_content<'a>(content: Content<'a>) -> Descriptor<'a> {
|
||||
Descriptor {
|
||||
content,
|
||||
rank: 0,
|
||||
|
@ -53,7 +53,9 @@ pub fn can_expr_with(
|
||||
});
|
||||
|
||||
let home = "Test".to_string();
|
||||
let arena = Bump::new();
|
||||
let (loc_expr, output, problems, procedures) = can::canonicalize_declaration(
|
||||
arena,
|
||||
home,
|
||||
name,
|
||||
Region::zero(),
|
||||
|
@ -9,6 +9,7 @@ mod helpers;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_infer {
|
||||
use bumpalo::Bump;
|
||||
use helpers::can_expr;
|
||||
use roc::infer::infer_expr;
|
||||
use roc::pretty_print_types::content_to_string;
|
||||
@ -18,8 +19,9 @@ mod test_infer {
|
||||
// HELPERS
|
||||
|
||||
fn infer_eq(src: &str, expected: &str) {
|
||||
let arena = Bump::new();
|
||||
let (expr, _, _, procedures) = can_expr(src);
|
||||
let mut subs = Subs::new();
|
||||
let mut subs = Subs::new(&arena);
|
||||
|
||||
let content = infer_expr(&mut subs, Located::new(0, 0, 0, 0, expr), procedures);
|
||||
let actual_str = content_to_string(content, &mut subs);
|
||||
|
Loading…
Reference in New Issue
Block a user