mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 22:34:45 +00:00
Merge remote-tracking branch 'origin/trunk' into small-str
This commit is contained in:
commit
5d3645350d
33 changed files with 2597 additions and 902 deletions
2
.envrc
2
.envrc
|
@ -151,4 +151,4 @@ validate_version() {
|
|||
return 1
|
||||
}
|
||||
|
||||
use_nix -s shell.nix -w ./nix/nixpkgs-version.json
|
||||
use_nix -s shell.nix
|
||||
|
|
|
@ -42,12 +42,13 @@ Installing LLVM's prebuilt binaries doesn't seem to be enough for the `llvm-sys`
|
|||
on Windows. After lots of help from [**@IanMacKenzie**](https://github.com/IanMacKenzie) (thank you, Ian!), here's what worked for me:
|
||||
|
||||
1. I downloaded and installed [Build Tools for Visual Studio 2019](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16) (a full Visual Studio install should work tool; the Build Tools are just the CLI tools, which is all I wanted)
|
||||
2. In the installation configuration, under "additional components" I had to check both "C++ ATL for latest v142 build tools (x86 & x64)" and also "C++/CLI support for v142 build tools"
|
||||
3. I launched the "x64 Native Tools Command Prompt for Visual Studio 2019" application (note: not the similarly-named "x86" one!)
|
||||
4. I followed most of the steps under LLVM's [building from source instructions](https://github.com/llvm/llvm-project#getting-the-source-code-and-building-llvm) up to the `cmake -G ...` command, which didn't work for me. Instead, at that point I did the following step.
|
||||
5. I ran `cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ../llvm` to generate a NMake makefile.
|
||||
6. Once that completed, I ran `nmake` to build LLVM. (This took about 2 hours on my laptop.)
|
||||
7. Finally, I set an environment variable `LLVM_SYS_100_PREFIX` to point to the `build` directory where I ran the `cmake` command.
|
||||
1. In the installation configuration, under "additional components" I had to check both "C++ ATL for latest v142 build tools (x86 & x64)" and also "C++/CLI support for v142 build tools"
|
||||
1. I launched the "x64 Native Tools Command Prompt for Visual Studio 2019" application (note: not the similarly-named "x86" one!)
|
||||
1. Make sure [Python 2.7](https://www.python.org/) and [CMake 3.17](http://cmake.org/) are installed on your system.
|
||||
1. I followed most of the steps under LLVM's [building from source instructions](https://github.com/llvm/llvm-project#getting-the-source-code-and-building-llvm) up to the `cmake -G ...` command, which didn't work for me. Instead, at that point I did the following step.
|
||||
1. I ran `cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ../llvm` to generate a NMake makefile.
|
||||
1. Once that completed, I ran `nmake` to build LLVM. (This took about 2 hours on my laptop.)
|
||||
1. Finally, I set an environment variable `LLVM_SYS_100_PREFIX` to point to the `build` directory where I ran the `cmake` command.
|
||||
|
||||
|
||||
Once all that was done, `cargo` ran successfully for Roc!
|
||||
|
|
454
Cargo.lock
generated
454
Cargo.lock
generated
|
@ -2,9 +2,9 @@
|
|||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "ab_glyph"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "600c69210313b93607f32ac23a62bc9c20522f8d6bba88aef6db27657162fe78"
|
||||
checksum = "5b50c188ff14b5a6efeb38eee8ccbc505cdf61e347a3d5eb04dc55d74ae4f20e"
|
||||
dependencies = [
|
||||
"ab_glyph_rasterizer",
|
||||
"owned_ttf_parser",
|
||||
|
@ -12,9 +12,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ab_glyph_rasterizer"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b7e4e8cf778db814365e46839949ca74df4efb10e87ba4913e6ec5967ef0285"
|
||||
checksum = "2692800d602527d2b8fea50036119c37df74ab565b10e285706a3dcec0ec3e16"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
|
@ -88,7 +88,7 @@ version = "0.31.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c69a8137596e84c22d57f3da1b5de1d4230b1742a710091c85f4d7ce50f00f38"
|
||||
dependencies = [
|
||||
"libloading 0.6.2",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -116,9 +116,9 @@ checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
|||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
|
@ -141,6 +141,27 @@ version = "0.1.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
|
||||
dependencies = [
|
||||
"block-padding",
|
||||
"byte-tools",
|
||||
"byteorder",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
|
||||
dependencies = [
|
||||
"byte-tools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.13"
|
||||
|
@ -159,6 +180,12 @@ version = "3.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
|
||||
|
||||
[[package]]
|
||||
name = "byte-tools"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
|
@ -193,9 +220,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.58"
|
||||
version = "1.0.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518"
|
||||
checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -205,9 +232,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.1"
|
||||
version = "2.33.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"textwrap",
|
||||
|
@ -238,9 +265,9 @@ source = "git+https://github.com/rtfeldman/clap#e1d83a78804a271b053d4d21f69b67f7
|
|||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.35",
|
||||
"syn 1.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -365,7 +392,7 @@ checksum = "70daa7ceec6cf143990669a04c7df13391d55fb27bd4079d252fca774ba244d8"
|
|||
dependencies = [
|
||||
"atty",
|
||||
"cast",
|
||||
"clap 2.33.1",
|
||||
"clap 2.33.3",
|
||||
"criterion-plot",
|
||||
"csv",
|
||||
"itertools",
|
||||
|
@ -409,12 +436,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.4.3"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6"
|
||||
checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"maybe-uninit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -434,7 +461,7 @@ version = "0.8.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0",
|
||||
"autocfg 1.0.1",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
|
@ -460,7 +487,7 @@ version = "0.7.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0",
|
||||
"autocfg 1.0.1",
|
||||
"cfg-if",
|
||||
"lazy_static",
|
||||
]
|
||||
|
@ -489,12 +516,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "d3d12"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1324bc4eae34f03b0ced586da5ae2b1ab46acfdae68b5b26d2e23dadae376a2"
|
||||
checksum = "d0a60cceb22c7c53035f8980524fdc7f17cf49681a3c154e6757d30afbec6ec4"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libloading 0.6.2",
|
||||
"libloading",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
|
@ -504,9 +531,9 @@ version = "2.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.35",
|
||||
"syn 1.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -515,6 +542,15 @@ version = "2.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dispatch"
|
||||
version = "0.2.0"
|
||||
|
@ -533,7 +569,19 @@ version = "0.4.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b11f15d1e3268f140f68d390637d5e76d849782d971ae7063e0da69fe9709a76"
|
||||
dependencies = [
|
||||
"libloading 0.6.2",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fs_extra",
|
||||
"handlebars",
|
||||
"pulldown-cmark",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -544,9 +592,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
|||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.5.3"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||
checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
|
@ -577,6 +625,12 @@ dependencies = [
|
|||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fake-simd"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -598,6 +652,12 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "fs_extra"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.1"
|
||||
|
@ -675,9 +735,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.35",
|
||||
"syn 1.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -725,10 +785,19 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.14"
|
||||
name = "generic-array"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
||||
checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
|
@ -748,14 +817,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-backend-dx11"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92de0ddc0fde1a89b2a0e92dcc6bbb554bd34af0135e53a28d5ef064611094a4"
|
||||
checksum = "32d95d5fddfa596c0628be117a16979b273f676b4e5a037a417365f274349123"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"gfx-auxil",
|
||||
"gfx-hal",
|
||||
"libloading 0.5.2",
|
||||
"libloading",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"range-alloc",
|
||||
|
@ -768,9 +837,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-backend-dx12"
|
||||
version = "0.5.8"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05218b5c94539f22ac7d6feb4b2482431b89f6cc897132494701ac48619218d7"
|
||||
checksum = "5959ce9dd48e7e04c71128024c3e76e86aa6fb6d83e48aad6819a1f7afae52e4"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"d3d12",
|
||||
|
@ -921,14 +990,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.8.1"
|
||||
name = "handlebars"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34f595585f103464d8d2f6e9864682d74c1601fed5e07d62b1c9058dba8246fb"
|
||||
checksum = "5deefd4816fb852b1ff3cb48f6c41da67be2d0e1d20b26a7a3b076da11f064b1"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0",
|
||||
"log",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"quick-error 2.0.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.1"
|
||||
|
@ -962,7 +1042,7 @@ version = "1.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||
dependencies = [
|
||||
"quick-error",
|
||||
"quick-error 1.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -995,11 +1075,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b88cd59ee5f71fea89a62248fc8f387d44400cefe05ef548466d61ced9029a7"
|
||||
checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0",
|
||||
"autocfg 1.0.1",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
|
@ -1020,9 +1100,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.35",
|
||||
"syn 1.0.40",
|
||||
"unindent",
|
||||
]
|
||||
|
||||
|
@ -1094,9 +1174,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
|||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.42"
|
||||
version = "0.3.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52732a3d3ad72c58ad2dc70624f9c17b46ecd0943b9a4f1ee37c4c18c5d983e2"
|
||||
checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
@ -1119,32 +1199,23 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.2.1"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.73"
|
||||
version = "0.2.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9"
|
||||
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.5.2"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cadb8e769f070c45df05c78c7520eb4cd17061d4ab262e43cfc68b4d00ac71c"
|
||||
checksum = "2443d8f0478b16759158b2f66d525991a05491138bc05814ef52a250148ef4f9"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
|
@ -1165,9 +1236,9 @@ checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
|
|||
|
||||
[[package]]
|
||||
name = "llvm-sys"
|
||||
version = "100.1.0"
|
||||
version = "100.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5545bf9a09267c644e4d0ac68f37ac200af6579ae2e82aebce382654eb4abab1"
|
||||
checksum = "9109e19fbfac3458f2970189719fa19f1007c6fd4e08c44fdebf4be0ddbe261d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"lazy_static",
|
||||
|
@ -1237,7 +1308,7 @@ version = "0.5.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0",
|
||||
"autocfg 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1363,9 +1434,9 @@ checksum = "2b2820aca934aba5ed91c79acc72b6a44048ceacc5d36c035ed4e051f12d887d"
|
|||
|
||||
[[package]]
|
||||
name = "net2"
|
||||
version = "0.2.34"
|
||||
version = "0.2.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7"
|
||||
checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
|
@ -1391,7 +1462,7 @@ version = "0.2.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0",
|
||||
"autocfg 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1421,9 +1492,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.35",
|
||||
"syn 1.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1447,9 +1518,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
|
||||
checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
|
@ -1457,6 +1528,12 @@ version = "11.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a170cebd8021a008ea92e4db85a72f80b35df514ec664b296fdcbb654eac0b2c"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "1.1.0"
|
||||
|
@ -1468,9 +1545,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "owned_ttf_parser"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92cfbcc7f0436a5b1d93087200c04372acc8073ec59f973aa30a98ee8262fcce"
|
||||
checksum = "fb477c7fd2a3a6e04e1dc6ca2e4e9b04f2df702021dc5a5d1cf078c587dc59f7"
|
||||
dependencies = [
|
||||
"ttf-parser",
|
||||
]
|
||||
|
@ -1514,9 +1591,9 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fb44a25c5bba983be0fc8592dfaf3e6d0935ce8be0c6b15b2a39507af34a926"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.35",
|
||||
"syn 1.0.40",
|
||||
"synstructure",
|
||||
"unicode-xid 0.2.1",
|
||||
]
|
||||
|
@ -1528,23 +1605,66 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "0.4.22"
|
||||
name = "pest"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17"
|
||||
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||
dependencies = [
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
|
||||
dependencies = [
|
||||
"maplit",
|
||||
"pest",
|
||||
"sha-1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "0.4.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "0.4.22"
|
||||
version = "0.4.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7"
|
||||
checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.35",
|
||||
"syn 1.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1579,9 +1699,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.8"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
|
||||
checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
|
@ -1609,9 +1729,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.35",
|
||||
"syn 1.0.40",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
|
@ -1621,18 +1741,18 @@ version = "0.4.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.35",
|
||||
"syn 1.0.40",
|
||||
"syn-mid",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.16"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
|
||||
checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-nested"
|
||||
|
@ -1651,19 +1771,36 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.19"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
|
||||
checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c"
|
||||
dependencies = [
|
||||
"unicode-xid 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"memchr",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda"
|
||||
|
||||
[[package]]
|
||||
name = "quickcheck"
|
||||
version = "0.8.5"
|
||||
|
@ -1702,7 +1839,7 @@ version = "1.0.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1878,11 +2015,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080"
|
||||
checksum = "cfd016f0c045ad38b5251be2c9c0ab806917f82da4d36b2a327e5166adad9270"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0",
|
||||
"autocfg 1.0.1",
|
||||
"crossbeam-deque",
|
||||
"either",
|
||||
"rayon-core",
|
||||
|
@ -1890,12 +2027,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.7.1"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280"
|
||||
checksum = "91739a34c4355b5434ce54c9086c5895604a9c278586d1f1aa95e04f66b525a0"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"num_cpus",
|
||||
|
@ -2473,9 +2610,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.114"
|
||||
version = "1.0.116"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
|
||||
checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5"
|
||||
|
||||
[[package]]
|
||||
name = "serde_cbor"
|
||||
|
@ -2489,20 +2626,20 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.114"
|
||||
version = "1.0.116"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
|
||||
checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.35",
|
||||
"syn 1.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.56"
|
||||
version = "1.0.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3"
|
||||
checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
@ -2510,10 +2647,22 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.2.0"
|
||||
name = "sha-1"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41"
|
||||
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"digest",
|
||||
"fake-simd",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
|
@ -2537,9 +2686,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
|||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.4.1"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f"
|
||||
checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
|
||||
|
||||
[[package]]
|
||||
name = "smithay-client-toolkit"
|
||||
|
@ -2559,9 +2708,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.3.12"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918"
|
||||
checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
|
@ -2626,11 +2775,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.35"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0"
|
||||
checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"unicode-xid 0.2.1",
|
||||
]
|
||||
|
@ -2641,9 +2790,9 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.35",
|
||||
"syn 1.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2652,9 +2801,9 @@ version = "0.12.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.35",
|
||||
"syn 1.0.40",
|
||||
"unicode-xid 0.2.1",
|
||||
]
|
||||
|
||||
|
@ -2746,9 +2895,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ttf-parser"
|
||||
version = "0.7.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "660a1bf32af7e3380bad2b59aff906c96444bb3264c5c272e1054cc7864041f5"
|
||||
checksum = "d973cfa0e6124166b50a1105a67c85de40bbc625082f35c0f56f84cb1fb0a827"
|
||||
|
||||
[[package]]
|
||||
name = "twox-hash"
|
||||
|
@ -2771,6 +2920,21 @@ version = "1.12.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.6.0"
|
||||
|
@ -2879,9 +3043,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.65"
|
||||
version = "0.2.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3edbcc9536ab7eababcc6d2374a0b7bfe13a2b6d562c5e07f370456b1a8f33d"
|
||||
checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
|
@ -2889,24 +3053,24 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.65"
|
||||
version = "0.2.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ed2fb8c84bfad20ea66b26a3743f3e7ba8735a69fe7d95118c33ec8fc1244d"
|
||||
checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.35",
|
||||
"syn 1.0.40",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.65"
|
||||
version = "0.2.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb071268b031a64d92fc6cf691715ca5a40950694d8f683c5bb43db7c730929e"
|
||||
checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038"
|
||||
dependencies = [
|
||||
"quote 1.0.7",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
@ -2914,22 +3078,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.65"
|
||||
version = "0.2.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf592c807080719d1ff2f245a687cbadb3ed28b2077ed7084b47aba8b691f2c6"
|
||||
checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.19",
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.35",
|
||||
"syn 1.0.40",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.65"
|
||||
version = "0.2.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b6c0220ded549d63860c78c38f3bcc558d1ca3f4efa74942c536ddbbb55e87"
|
||||
checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307"
|
||||
|
||||
[[package]]
|
||||
name = "wayland-client"
|
||||
|
@ -2993,9 +3157,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.42"
|
||||
version = "0.3.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8be2398f326b7ba09815d0b403095f34dd708579220d099caae89be0b32137b2"
|
||||
checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
@ -3238,7 +3402,7 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d498dbd1fd7beb83c86709ae1c33ca50942889473473d287d56ce4770a18edfb"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.19",
|
||||
"syn 1.0.35",
|
||||
"proc-macro2 1.0.21",
|
||||
"syn 1.0.40",
|
||||
"synstructure",
|
||||
]
|
||||
|
|
|
@ -25,7 +25,8 @@ members = [
|
|||
"vendor/pathfinding",
|
||||
"vendor/pretty",
|
||||
"editor",
|
||||
"cli"
|
||||
"cli",
|
||||
"docs"
|
||||
]
|
||||
|
||||
# Optimizations based on https://deterministic.space/high-performance-rust.html
|
||||
|
|
225
cli/src/lib.rs
Normal file
225
cli/src/lib.rs
Normal file
|
@ -0,0 +1,225 @@
|
|||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use clap::ArgMatches;
|
||||
use clap::{App, Arg};
|
||||
use roc_build::program::gen;
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_gen::llvm::build::OptLevel;
|
||||
use roc_load::file::LoadingProblem;
|
||||
use std::io::{self, ErrorKind};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
use std::process::Command;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use target_lexicon::Triple;
|
||||
|
||||
pub mod repl;
|
||||
|
||||
pub static FLAG_OPTIMIZE: &str = "optimize";
|
||||
pub static FLAG_ROC_FILE: &str = "ROC_FILE";
|
||||
pub static DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES";
|
||||
|
||||
pub fn build_app<'a>() -> App<'a> {
|
||||
App::new("roc")
|
||||
.version(crate_version!())
|
||||
.subcommand(App::new("build")
|
||||
.about("Build a program")
|
||||
.arg(
|
||||
Arg::with_name(FLAG_ROC_FILE)
|
||||
.help("The .roc file to build")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(FLAG_OPTIMIZE)
|
||||
.long(FLAG_OPTIMIZE)
|
||||
.help("Optimize the compiled program to run faster. (Optimization takes time to complete.)")
|
||||
.required(false),
|
||||
)
|
||||
)
|
||||
.subcommand(App::new("run")
|
||||
.about("Build and run a program")
|
||||
.arg(
|
||||
Arg::with_name(FLAG_ROC_FILE)
|
||||
.help("The .roc file to build and run")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(FLAG_OPTIMIZE)
|
||||
.long(FLAG_OPTIMIZE)
|
||||
.help("Optimize the compiled program to run faster. (Optimization takes time to complete.)")
|
||||
.required(false),
|
||||
)
|
||||
)
|
||||
.subcommand(App::new("repl")
|
||||
.about("Launch the interactive Read Eval Print Loop (REPL)")
|
||||
)
|
||||
.subcommand(App::new("edit")
|
||||
.about("Launch the Roc editor")
|
||||
.arg(Arg::with_name(DIRECTORY_OR_FILES)
|
||||
.index(1)
|
||||
.multiple(true)
|
||||
.required(false)
|
||||
.help("(optional) The directory or files to open on launch.")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn build(matches: &ArgMatches, run_after_build: bool) -> io::Result<()> {
|
||||
let filename = matches.value_of(FLAG_ROC_FILE).unwrap();
|
||||
let opt_level = if matches.is_present(FLAG_OPTIMIZE) {
|
||||
OptLevel::Optimize
|
||||
} else {
|
||||
OptLevel::Normal
|
||||
};
|
||||
let path = Path::new(filename).canonicalize().unwrap();
|
||||
let src_dir = path.parent().unwrap().canonicalize().unwrap();
|
||||
|
||||
// Spawn the root task
|
||||
let path = path.canonicalize().unwrap_or_else(|err| {
|
||||
use ErrorKind::*;
|
||||
|
||||
match err.kind() {
|
||||
NotFound => {
|
||||
match path.to_str() {
|
||||
Some(path_str) => println!("File not found: {}", path_str),
|
||||
None => println!("Malformed file path : {:?}", path),
|
||||
}
|
||||
|
||||
process::exit(1);
|
||||
}
|
||||
_ => {
|
||||
todo!("TODO Gracefully handle opening {:?} - {:?}", path, err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let binary_path =
|
||||
build_file(src_dir, path, opt_level).expect("TODO gracefully handle build_file failing");
|
||||
|
||||
if run_after_build {
|
||||
// Run the compiled app
|
||||
Command::new(binary_path)
|
||||
.spawn()
|
||||
.unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err))
|
||||
.wait()
|
||||
.expect("TODO gracefully handle block_on failing");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn report_timing(buf: &mut String, label: &str, duration: Duration) {
|
||||
buf.push_str(&format!(
|
||||
" {:.3} ms {}\n",
|
||||
duration.as_secs_f64() * 1000.0,
|
||||
label,
|
||||
));
|
||||
}
|
||||
|
||||
fn build_file(
|
||||
src_dir: PathBuf,
|
||||
filename: PathBuf,
|
||||
opt_level: OptLevel,
|
||||
) -> Result<PathBuf, LoadingProblem> {
|
||||
let compilation_start = SystemTime::now();
|
||||
let arena = Bump::new();
|
||||
|
||||
// Step 1: compile the app and generate the .o file
|
||||
let subs_by_module = MutMap::default();
|
||||
|
||||
// Release builds use uniqueness optimizations
|
||||
let stdlib = match opt_level {
|
||||
OptLevel::Normal => roc_builtins::std::standard_stdlib(),
|
||||
OptLevel::Optimize => roc_builtins::unique::uniq_stdlib(),
|
||||
};
|
||||
let loaded =
|
||||
roc_load::file::load(filename.clone(), &stdlib, src_dir.as_path(), subs_by_module)?;
|
||||
let dest_filename = filename.with_extension("o");
|
||||
|
||||
let buf = &mut String::with_capacity(1024);
|
||||
|
||||
for (module_id, module_timing) in loaded.timings.iter() {
|
||||
let module_name = loaded.interns.module_name(*module_id);
|
||||
|
||||
buf.push_str(" ");
|
||||
buf.push_str(module_name);
|
||||
buf.push_str("\n");
|
||||
|
||||
report_timing(buf, "Read .roc file from disk", module_timing.read_roc_file);
|
||||
report_timing(buf, "Parse header", module_timing.parse_header);
|
||||
report_timing(buf, "Parse body", module_timing.parse_body);
|
||||
report_timing(buf, "Canonicalize", module_timing.canonicalize);
|
||||
report_timing(buf, "Constrain", module_timing.constrain);
|
||||
report_timing(buf, "Solve", module_timing.solve);
|
||||
report_timing(buf, "Other", module_timing.other());
|
||||
buf.push('\n');
|
||||
report_timing(buf, "Total", module_timing.total());
|
||||
}
|
||||
|
||||
println!(
|
||||
"\n\nCompilation finished! Here's how long each module took to compile:\n\n{}",
|
||||
buf
|
||||
);
|
||||
|
||||
gen(
|
||||
&arena,
|
||||
loaded,
|
||||
filename,
|
||||
Triple::host(),
|
||||
&dest_filename,
|
||||
opt_level,
|
||||
);
|
||||
|
||||
let compilation_end = compilation_start.elapsed().unwrap();
|
||||
|
||||
println!(
|
||||
"Finished compilation and code gen in {} ms\n",
|
||||
compilation_end.as_millis()
|
||||
);
|
||||
|
||||
let cwd = dest_filename.parent().unwrap();
|
||||
let lib_path = dest_filename.with_file_name("libroc_app.a");
|
||||
|
||||
// Step 2: turn the .o file into a .a static library
|
||||
Command::new("ar") // TODO on Windows, use `link`
|
||||
.args(&[
|
||||
"rcs",
|
||||
lib_path.to_str().unwrap(),
|
||||
dest_filename.to_str().unwrap(),
|
||||
])
|
||||
.spawn()
|
||||
.map_err(|_| {
|
||||
todo!("gracefully handle `ar` failing to spawn.");
|
||||
})?
|
||||
.wait()
|
||||
.map_err(|_| {
|
||||
todo!("gracefully handle error after `ar` spawned");
|
||||
})?;
|
||||
|
||||
// Step 3: have rustc compile the host and link in the .a file
|
||||
let binary_path = cwd.join("app");
|
||||
|
||||
Command::new("rustc")
|
||||
.args(&[
|
||||
"-L",
|
||||
".",
|
||||
"--crate-type",
|
||||
"bin",
|
||||
"host.rs",
|
||||
"-o",
|
||||
binary_path.as_path().to_str().unwrap(),
|
||||
])
|
||||
.current_dir(cwd)
|
||||
.spawn()
|
||||
.map_err(|_| {
|
||||
todo!("gracefully handle `rustc` failing to spawn.");
|
||||
})?
|
||||
.wait()
|
||||
.map_err(|_| {
|
||||
todo!("gracefully handle error after `rustc` spawned");
|
||||
})?;
|
||||
|
||||
Ok(binary_path)
|
||||
}
|
227
cli/src/main.rs
227
cli/src/main.rs
|
@ -1,69 +1,6 @@
|
|||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use roc_build::program::gen;
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_gen::llvm::build::OptLevel;
|
||||
use roc_load::file::LoadingProblem;
|
||||
use std::io::{self, ErrorKind};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
use std::process::Command;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use target_lexicon::Triple;
|
||||
|
||||
pub mod repl;
|
||||
|
||||
pub static FLAG_OPTIMIZE: &str = "optimize";
|
||||
pub static FLAG_ROC_FILE: &str = "ROC_FILE";
|
||||
pub static DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES";
|
||||
|
||||
pub fn build_app<'a>() -> App<'a> {
|
||||
App::new("roc")
|
||||
.version(crate_version!())
|
||||
.subcommand(App::new("build")
|
||||
.about("Build a program")
|
||||
.arg(
|
||||
Arg::with_name(FLAG_ROC_FILE)
|
||||
.help("The .roc file to build")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(FLAG_OPTIMIZE)
|
||||
.long(FLAG_OPTIMIZE)
|
||||
.help("Optimize the compiled program to run faster. (Optimization takes time to complete.)")
|
||||
.required(false),
|
||||
)
|
||||
)
|
||||
.subcommand(App::new("run")
|
||||
.about("Build and run a program")
|
||||
.arg(
|
||||
Arg::with_name(FLAG_ROC_FILE)
|
||||
.help("The .roc file to build and run")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(FLAG_OPTIMIZE)
|
||||
.long(FLAG_OPTIMIZE)
|
||||
.help("Optimize the compiled program to run faster. (Optimization takes time to complete.)")
|
||||
.required(false),
|
||||
)
|
||||
)
|
||||
.subcommand(App::new("repl")
|
||||
.about("Launch the interactive Read Eval Print Loop (REPL)")
|
||||
)
|
||||
.subcommand(App::new("edit")
|
||||
.about("Launch the Roc editor")
|
||||
.arg(Arg::with_name(DIRECTORY_OR_FILES)
|
||||
.index(1)
|
||||
.multiple(true)
|
||||
.required(false)
|
||||
.help("(optional) The directory or files to open on launch.")
|
||||
)
|
||||
)
|
||||
}
|
||||
use roc_cli::{build, build_app, repl, DIRECTORY_OR_FILES};
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
let matches = build_app().get_matches();
|
||||
|
@ -92,161 +29,3 @@ fn main() -> io::Result<()> {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(matches: &ArgMatches, run_after_build: bool) -> io::Result<()> {
|
||||
let filename = matches.value_of(FLAG_ROC_FILE).unwrap();
|
||||
let opt_level = if matches.is_present(FLAG_OPTIMIZE) {
|
||||
OptLevel::Optimize
|
||||
} else {
|
||||
OptLevel::Normal
|
||||
};
|
||||
let path = Path::new(filename).canonicalize().unwrap();
|
||||
let src_dir = path.parent().unwrap().canonicalize().unwrap();
|
||||
|
||||
// Spawn the root task
|
||||
let path = path.canonicalize().unwrap_or_else(|err| {
|
||||
use ErrorKind::*;
|
||||
|
||||
match err.kind() {
|
||||
NotFound => {
|
||||
match path.to_str() {
|
||||
Some(path_str) => println!("File not found: {}", path_str),
|
||||
None => println!("Malformed file path : {:?}", path),
|
||||
}
|
||||
|
||||
process::exit(1);
|
||||
}
|
||||
_ => {
|
||||
todo!("TODO Gracefully handle opening {:?} - {:?}", path, err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let binary_path =
|
||||
build_file(src_dir, path, opt_level).expect("TODO gracefully handle build_file failing");
|
||||
|
||||
if run_after_build {
|
||||
// Run the compiled app
|
||||
Command::new(binary_path)
|
||||
.spawn()
|
||||
.unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err))
|
||||
.wait()
|
||||
.expect("TODO gracefully handle block_on failing");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn report_timing(buf: &mut String, label: &str, duration: Duration) {
|
||||
buf.push_str(&format!(
|
||||
" {:.3} ms {}\n",
|
||||
duration.as_secs_f64() * 1000.0,
|
||||
label,
|
||||
));
|
||||
}
|
||||
|
||||
fn build_file(
|
||||
src_dir: PathBuf,
|
||||
filename: PathBuf,
|
||||
opt_level: OptLevel,
|
||||
) -> Result<PathBuf, LoadingProblem> {
|
||||
let compilation_start = SystemTime::now();
|
||||
let arena = Bump::new();
|
||||
|
||||
// Step 1: compile the app and generate the .o file
|
||||
let subs_by_module = MutMap::default();
|
||||
|
||||
// Release builds use uniqueness optimizations
|
||||
let stdlib = match opt_level {
|
||||
OptLevel::Normal => roc_builtins::std::standard_stdlib(),
|
||||
OptLevel::Optimize => roc_builtins::unique::uniq_stdlib(),
|
||||
};
|
||||
let loaded =
|
||||
roc_load::file::load(filename.clone(), &stdlib, src_dir.as_path(), subs_by_module)?;
|
||||
let dest_filename = filename.with_extension("o");
|
||||
|
||||
let buf = &mut String::with_capacity(1024);
|
||||
|
||||
for (module_id, module_timing) in loaded.timings.iter() {
|
||||
let module_name = loaded.interns.module_name(*module_id);
|
||||
|
||||
buf.push_str(" ");
|
||||
buf.push_str(module_name);
|
||||
buf.push_str("\n");
|
||||
|
||||
report_timing(buf, "Read .roc file from disk", module_timing.read_roc_file);
|
||||
report_timing(buf, "Parse header", module_timing.parse_header);
|
||||
report_timing(buf, "Parse body", module_timing.parse_body);
|
||||
report_timing(buf, "Canonicalize", module_timing.canonicalize);
|
||||
report_timing(buf, "Constrain", module_timing.constrain);
|
||||
report_timing(buf, "Solve", module_timing.solve);
|
||||
report_timing(buf, "Other", module_timing.other());
|
||||
buf.push('\n');
|
||||
report_timing(buf, "Total", module_timing.total());
|
||||
}
|
||||
|
||||
println!(
|
||||
"\n\nCompilation finished! Here's how long each module took to compile:\n\n{}",
|
||||
buf
|
||||
);
|
||||
|
||||
gen(
|
||||
&arena,
|
||||
loaded,
|
||||
filename,
|
||||
Triple::host(),
|
||||
&dest_filename,
|
||||
opt_level,
|
||||
);
|
||||
|
||||
let compilation_end = compilation_start.elapsed().unwrap();
|
||||
|
||||
println!(
|
||||
"Finished compilation and code gen in {} ms\n",
|
||||
compilation_end.as_millis()
|
||||
);
|
||||
|
||||
let cwd = dest_filename.parent().unwrap();
|
||||
let lib_path = dest_filename.with_file_name("libroc_app.a");
|
||||
|
||||
// Step 2: turn the .o file into a .a static library
|
||||
Command::new("ar") // TODO on Windows, use `link`
|
||||
.args(&[
|
||||
"rcs",
|
||||
lib_path.to_str().unwrap(),
|
||||
dest_filename.to_str().unwrap(),
|
||||
])
|
||||
.spawn()
|
||||
.map_err(|_| {
|
||||
todo!("gracefully handle `ar` failing to spawn.");
|
||||
})?
|
||||
.wait()
|
||||
.map_err(|_| {
|
||||
todo!("gracefully handle error after `ar` spawned");
|
||||
})?;
|
||||
|
||||
// Step 3: have rustc compile the host and link in the .a file
|
||||
let binary_path = cwd.join("app");
|
||||
|
||||
Command::new("rustc")
|
||||
.args(&[
|
||||
"-L",
|
||||
".",
|
||||
"--crate-type",
|
||||
"bin",
|
||||
"host.rs",
|
||||
"-o",
|
||||
binary_path.as_path().to_str().unwrap(),
|
||||
])
|
||||
.current_dir(cwd)
|
||||
.spawn()
|
||||
.map_err(|_| {
|
||||
todo!("gracefully handle `rustc` failing to spawn.");
|
||||
})?
|
||||
.wait()
|
||||
.map_err(|_| {
|
||||
todo!("gracefully handle error after `rustc` spawned");
|
||||
})?;
|
||||
|
||||
Ok(binary_path)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use bumpalo::Bump;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::execution_engine::ExecutionEngine;
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::OptimizationLevel;
|
||||
use roc_builtins::unique::uniq_stdlib;
|
||||
use roc_can::constraint::Constraint;
|
||||
|
@ -15,7 +14,6 @@ use roc_constrain::module::{constrain_imported_values, load_builtin_aliases, Imp
|
|||
use roc_fmt::annotation::{Formattable, Newlines, Parens};
|
||||
use roc_gen::layout_id::LayoutIds;
|
||||
use roc_gen::llvm::build::{build_proc, build_proc_header, OptLevel};
|
||||
use roc_gen::llvm::convert::basic_type_from_layout;
|
||||
use roc_module::ident::Ident;
|
||||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol};
|
||||
use roc_mono::ir::Procs;
|
||||
|
@ -223,10 +221,6 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
|
|||
// in --release mode and then trying to eval anything in the repl.
|
||||
ExecutionEngine::link_in_mc_jit();
|
||||
|
||||
let main_fn_type = basic_type_from_layout(&arena, &context, &main_ret_layout, ptr_bytes)
|
||||
.fn_type(&[], false);
|
||||
let main_fn_name = "$Test.main";
|
||||
|
||||
// Compile and add all the Procs before adding main
|
||||
let mut env = roc_gen::llvm::build::Env {
|
||||
arena: &arena,
|
||||
|
@ -322,23 +316,10 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
|
|||
}
|
||||
}
|
||||
|
||||
// Add main to the module.
|
||||
let main_fn = env.module.add_function(main_fn_name, main_fn_type, None);
|
||||
let cc = roc_gen::llvm::build::FAST_CALL_CONV;
|
||||
|
||||
main_fn.set_call_conventions(cc);
|
||||
|
||||
// Add main's body
|
||||
let basic_block = context.append_basic_block(main_fn, "entry");
|
||||
|
||||
builder.position_at_end(basic_block);
|
||||
|
||||
// builds the function body (return statement included)
|
||||
roc_gen::llvm::build::build_exp_stmt(
|
||||
let (main_fn_name, main_fn) = roc_gen::llvm::build::make_main_function(
|
||||
&env,
|
||||
&mut layout_ids,
|
||||
&mut roc_gen::llvm::build::Scope::default(),
|
||||
main_fn,
|
||||
&main_ret_layout,
|
||||
&main_body,
|
||||
);
|
||||
|
||||
|
|
|
@ -118,20 +118,67 @@ fn jit_to_ast_help<'a>(
|
|||
}
|
||||
};
|
||||
|
||||
// Functions can return structs of either 8 or 16 bytes, depending
|
||||
// on whether we're compiling for a 64-bit or 32-bit target.
|
||||
match env.ptr_bytes {
|
||||
// 64-bit target (8-byte pointers, 16-byte structs)
|
||||
8 => jit_map!(
|
||||
8 => match layout.stack_size(env.ptr_bytes) {
|
||||
8 => {
|
||||
// just one eightbyte, returned as-is
|
||||
jit_map!(execution_engine, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
})
|
||||
}
|
||||
16 => {
|
||||
// two eightbytes, returned as-is
|
||||
jit_map!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
[u8; 16],
|
||||
|bytes: [u8; 16]| { ptr_to_ast((&bytes).as_ptr() as *const libc::c_void) }
|
||||
),
|
||||
// 32-bit target (4-byte pointers, 8-byte structs)
|
||||
4 => jit_map!(execution_engine, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
|
||||
|bytes: [u8; 16]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
}),
|
||||
}
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
// anything more than 2 eightbytes
|
||||
// the return "value" is a pointer to the result
|
||||
jit_map!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
*const u8,
|
||||
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
|
||||
)
|
||||
}
|
||||
},
|
||||
// 32-bit target (4-byte pointers, 8-byte structs)
|
||||
4 => {
|
||||
// TODO what are valid return sizes here?
|
||||
// this is just extrapolated from the 64-bit case above
|
||||
// and not (yet) actually tested on a 32-bit system
|
||||
match layout.stack_size(env.ptr_bytes) {
|
||||
4 => {
|
||||
// just one fourbyte, returned as-is
|
||||
jit_map!(execution_engine, main_fn_name, [u8; 4], |bytes: [u8; 4]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
})
|
||||
}
|
||||
8 => {
|
||||
// just one fourbyte, returned as-is
|
||||
jit_map!(execution_engine, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
// anything more than 2 fourbytes
|
||||
// the return "value" is a pointer to the result
|
||||
jit_map!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
*const u8,
|
||||
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
other => {
|
||||
panic!("Unsupported target: Roc cannot currently compile to systems where pointers are {} bytes in length.", other);
|
||||
}
|
||||
|
@ -280,7 +327,7 @@ fn struct_to_ast<'a>(
|
|||
output.push(loc_field);
|
||||
|
||||
// Advance the field pointer to the next field.
|
||||
field_ptr = unsafe { ptr.offset(field_layout.stack_size(env.ptr_bytes) as isize) };
|
||||
field_ptr = unsafe { field_ptr.offset(field_layout.stack_size(env.ptr_bytes) as isize) };
|
||||
}
|
||||
|
||||
Expr::Record {
|
||||
|
|
|
@ -3,8 +3,8 @@ extern crate inlinable_string;
|
|||
extern crate roc_collections;
|
||||
extern crate roc_load;
|
||||
extern crate roc_module;
|
||||
// extern crate roc_cli; // TODO FIXME why doesn't this resolve?
|
||||
|
||||
use roc_cli::repl::{INSTRUCTIONS, PROMPT, WELCOME_MESSAGE};
|
||||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
@ -16,12 +16,6 @@ pub struct Out {
|
|||
pub status: ExitStatus,
|
||||
}
|
||||
|
||||
// TODO get these from roc_cli::repl instead, after figuring out why
|
||||
// `extern crate roc_cli;` doesn't work.
|
||||
const WELCOME_MESSAGE: &str = "\n The rockin’ \u{001b}[36mroc repl\u{001b}[0m\n\u{001b}[35m────────────────────────\u{001b}[0m\n\n";
|
||||
const INSTRUCTIONS: &str = "Enter an expression, or :help, or :exit.\n";
|
||||
const PROMPT: &str = "\n\u{001b}[36m»\u{001b}[0m ";
|
||||
|
||||
pub fn path_to_roc_binary() -> PathBuf {
|
||||
// Adapted from https://github.com/volta-cli/volta/blob/cefdf7436a15af3ce3a38b8fe53bb0cfdb37d3dd/tests/acceptance/support/sandbox.rs#L680 - BSD-2-Clause licensed
|
||||
let mut path = env::var_os("CARGO_BIN_PATH")
|
||||
|
@ -129,8 +123,6 @@ pub fn repl_eval(input: &str) -> Out {
|
|||
|
||||
// Remove the initial instructions from the output.
|
||||
|
||||
// TODO get these from roc_cli::repl instead, after figuring out why
|
||||
// `extern crate roc_cli;` doesn't work.
|
||||
let expected_instructions = format!("{}{}{}", WELCOME_MESSAGE, INSTRUCTIONS, PROMPT);
|
||||
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||
|
||||
|
|
|
@ -232,6 +232,15 @@ mod repl_eval {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn three_element_record() {
|
||||
// if this tests turns out to fail on 32-bit platforms, look at jit_to_ast_help
|
||||
expect_success(
|
||||
"{ a: 1, b: 2, c: 3 }",
|
||||
"{ a: 1, b: 2, c: 3 } : { a : Num *, b : Num *, c : Num * }",
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn multiline_string() {
|
||||
// // If a string contains newlines, format it as a multiline string in the output
|
||||
|
|
|
@ -6,7 +6,7 @@ Builtins are the functions and modules that are implicitly imported into every m
|
|||
|
||||
Towards the bottom of `symbol.rs` there is a `define_builtins!` macro being used that takes many modules and function names. The first level (`List`, `Int` ..) is the module name, and the second level is the function or value name (`reverse`, `mod` ..). If you wanted to add a `Int` function called `addTwo` go to `2 Int: "Int" => {` and inside that case add to the bottom `38 INT_ADD_TWO: "addTwo"` (assuming there are 37 existing ones).
|
||||
|
||||
Some of these have `#` inside their name (`first#list`, #lt` ..). This is a trick we are doing to hide implementation details from Roc programmers. To a Roc programmer, a name with `#` in it is invalid, because `#` means everything after it is parsed to a comment. We are constructing these functions manually, so we are circumventing the parsing step and dont have such restrictions. We get to make functions and values with `#` which as a consequence are not accessible to Roc programmers. Roc programmers simply cannot reference them.
|
||||
Some of these have `#` inside their name (`first#list`, `#lt` ..). This is a trick we are doing to hide implementation details from Roc programmers. To a Roc programmer, a name with `#` in it is invalid, because `#` means everything after it is parsed to a comment. We are constructing these functions manually, so we are circumventing the parsing step and dont have such restrictions. We get to make functions and values with `#` which as a consequence are not accessible to Roc programmers. Roc programmers simply cannot reference them.
|
||||
|
||||
But we can use these values and some of these are necessary for implementing builtins. For example, `List.get` returns tags, and it is not easy for us to create tags when composing LLVM. What is easier however, is:
|
||||
- ..writing `List.#getUnsafe` that has the dangerous signature of `List elem, Int -> elem` in LLVM
|
||||
|
@ -15,9 +15,9 @@ But we can use these values and some of these are necessary for implementing bui
|
|||
|
||||
### can/src/builtins.rs
|
||||
|
||||
Right at the top of this module is a function called `builtin_defs`. All this is doing is mapping the `Symbol` defined in `module/src/symbol.rs` to its implementation. Some of the builtins are quite complex, such as `list_get`, which is complex the reasons mentioned in the previous section; `list_get` returns tags, and it defers to lower-level functions via an if statement.
|
||||
Right at the top of this module is a function called `builtin_defs`. All this is doing is mapping the `Symbol` defined in `module/src/symbol.rs` to its implementation. Some of the builtins are quite complex, such as `list_get`. What makes `list_get` is that it returns tags, and in order to return tags it first has to defer to lower-level functions via an if statement.
|
||||
|
||||
Lets look at `List.repeat : elem, Int -> List elem`, which is more straight forward, and points directly to its lower level implementation:
|
||||
Lets look at `List.repeat : elem, Int -> List elem`, which is more straight-forward, and points directly to its lower level implementation:
|
||||
```
|
||||
fn list_repeat(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let elem_var = var_store.fresh();
|
||||
|
@ -55,10 +55,14 @@ This `LowLevel` thing connects the builtin defined in this module to its impleme
|
|||
This is where bottom-level functions that need to be written as LLVM are created. If the function leads to a tag thats a good sign it should not be written here in `build.rs`. If its simple fundamental stuff like `INT_ADD` then it certainly should be written here.
|
||||
|
||||
## Letting the compiler know these functions exist
|
||||
Its one thing to actually write these functions, its _another_ thing to let the Roc compiler know they exist as part of the standard library. You have to tell the compiler "Hey, this function exists, and it has this type signature". That happens in these modules:
|
||||
### builtins/src/std.rs
|
||||
### builtins/src/unique.rs
|
||||
Its one thing to actually write these functions, its _another_ thing to let the Roc compiler know they exist as part of the standard library. You have to tell the compiler "Hey, this function exists, and it has this type signature". That happens in `std.rs`.
|
||||
|
||||
## Specifying the uniqueness of a function
|
||||
### builtins/src/unique.rs
|
||||
One of the cool things about Roc is that it evaluates if a value in memory is shared between scopes or if it is used in just one place. If the value is used in one place then it is 'unique', and it therefore can be mutated in place. For a value created by a function, the uniqueness of the output is determined in part by the uniqueness of the input arguments. For example `List.single : elem -> List elem` can return a unique list if the `elem` is also unique.
|
||||
|
||||
We have to define the uniqueness constraints of a function just like we have to define a type signature. That is what happens in `unique.rs`. This can be tricky so it would be a good step to ask for help on if it is confusing.
|
||||
|
||||
# Mistakes that are easy to make!!
|
||||
|
||||
|
|
|
@ -282,7 +282,11 @@ joinMap : List before, (before -> List after) -> List after
|
|||
## >>> [ [ 1, 2, 3 ], [], [], [ 4, 5 ] ]
|
||||
## >>> |> List.map List.first
|
||||
## >>> |> List.joinOks
|
||||
joinOks : List (Result elem *) -> List elem
|
||||
##
|
||||
## Eventually, `oks` type signature will be `List [Ok elem]* -> List elem`.
|
||||
## The implementation for that is a lot tricker then `List (Result elem *)`
|
||||
## so we're sticking with `Result` for now.
|
||||
oks : List (Result elem *) -> List elem
|
||||
|
||||
## Iterates over the shortest of the given lists and returns a list of `Pair`
|
||||
## tags, each wrapping one of the elements in that list, along with the elements
|
||||
|
|
|
@ -412,6 +412,18 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// minFloat : Float
|
||||
add_type(Symbol::NUM_MIN_FLOAT, float_type());
|
||||
|
||||
// pow : Float, Float -> Float
|
||||
add_type(
|
||||
Symbol::NUM_POW,
|
||||
SolvedType::Func(vec![float_type(), float_type()], Box::new(float_type())),
|
||||
);
|
||||
|
||||
// ceiling : Float -> Int
|
||||
add_type(
|
||||
Symbol::NUM_CEILING,
|
||||
SolvedType::Func(vec![float_type()], Box::new(int_type())),
|
||||
);
|
||||
|
||||
// Bool module
|
||||
|
||||
// and : Bool, Bool -> Bool
|
||||
|
|
|
@ -448,6 +448,21 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
unique_function(vec![num_type(star1, a)], bool_type(star2))
|
||||
});
|
||||
|
||||
// pow : Float, Float -> Float
|
||||
add_type(Symbol::NUM_POW, {
|
||||
let_tvars! { star1, star2, star3 };
|
||||
unique_function(
|
||||
vec![float_type(star1), float_type(star2)],
|
||||
float_type(star3),
|
||||
)
|
||||
});
|
||||
|
||||
// ceiling : Float -> Int
|
||||
add_type(Symbol::NUM_CEILING, {
|
||||
let_tvars! { star1, star2 };
|
||||
unique_function(vec![float_type(star1)], int_type(star2))
|
||||
});
|
||||
|
||||
// Bool module
|
||||
|
||||
// isEq or (==) : Attr * a, Attr * a -> Attr * Bool
|
||||
|
|
|
@ -91,6 +91,8 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
|
|||
Symbol::NUM_IS_POSITIVE => num_is_positive,
|
||||
Symbol::NUM_IS_NEGATIVE => num_is_negative,
|
||||
Symbol::NUM_TO_FLOAT => num_to_float,
|
||||
Symbol::NUM_POW => num_pow,
|
||||
Symbol::NUM_CEILING => num_ceiling,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -579,6 +581,47 @@ fn num_round(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
)
|
||||
}
|
||||
|
||||
/// Num.pow : Float, Float -> Float
|
||||
fn num_pow(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let float_var = var_store.fresh();
|
||||
|
||||
let body = RunLowLevel {
|
||||
op: LowLevel::NumPow,
|
||||
args: vec![
|
||||
(float_var, Var(Symbol::ARG_1)),
|
||||
(float_var, Var(Symbol::ARG_2)),
|
||||
],
|
||||
ret_var: float_var,
|
||||
};
|
||||
|
||||
defn(
|
||||
symbol,
|
||||
vec![(float_var, Symbol::ARG_1), (float_var, Symbol::ARG_2)],
|
||||
var_store,
|
||||
body,
|
||||
float_var,
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.ceiling : Float -> Int
|
||||
fn num_ceiling(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let float_var = var_store.fresh();
|
||||
let int_var = var_store.fresh();
|
||||
|
||||
let body = RunLowLevel {
|
||||
op: LowLevel::NumCeiling,
|
||||
args: vec![(float_var, Var(Symbol::ARG_1))],
|
||||
ret_var: int_var,
|
||||
};
|
||||
|
||||
defn(
|
||||
symbol,
|
||||
vec![(float_var, Symbol::ARG_1)],
|
||||
var_store,
|
||||
body,
|
||||
int_var,
|
||||
)
|
||||
}
|
||||
/// List.isEmpty : List * -> Bool
|
||||
fn list_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let list_var = var_store.fresh();
|
||||
|
|
|
@ -24,7 +24,8 @@ use inkwell::passes::{PassManager, PassManagerBuilder};
|
|||
use inkwell::types::{BasicTypeEnum, FunctionType, IntType, StructType};
|
||||
use inkwell::values::BasicValueEnum::{self, *};
|
||||
use inkwell::values::{
|
||||
BasicValue, CallSiteValue, FloatValue, FunctionValue, IntValue, PointerValue, StructValue,
|
||||
BasicValue, CallSiteValue, FloatValue, FunctionValue, InstructionOpcode, IntValue,
|
||||
PointerValue, StructValue,
|
||||
};
|
||||
use inkwell::OptimizationLevel;
|
||||
use inkwell::{AddressSpace, IntPredicate};
|
||||
|
@ -194,6 +195,34 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
|
|||
let i8_type = ctx.i8_type();
|
||||
let i8_ptr_type = i8_type.ptr_type(AddressSpace::Generic);
|
||||
|
||||
add_intrinsic(
|
||||
module,
|
||||
LLVM_MEMSET_I64,
|
||||
void_type.fn_type(
|
||||
&[
|
||||
i8_ptr_type.into(),
|
||||
i8_type.into(),
|
||||
i64_type.into(),
|
||||
i1_type.into(),
|
||||
],
|
||||
false,
|
||||
),
|
||||
);
|
||||
|
||||
add_intrinsic(
|
||||
module,
|
||||
LLVM_MEMSET_I32,
|
||||
void_type.fn_type(
|
||||
&[
|
||||
i8_ptr_type.into(),
|
||||
i8_type.into(),
|
||||
i32_type.into(),
|
||||
i1_type.into(),
|
||||
],
|
||||
false,
|
||||
),
|
||||
);
|
||||
|
||||
add_intrinsic(
|
||||
module,
|
||||
LLVM_SQRT_F64,
|
||||
|
@ -226,30 +255,14 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
|
|||
|
||||
add_intrinsic(
|
||||
module,
|
||||
LLVM_MEMSET_I64,
|
||||
void_type.fn_type(
|
||||
&[
|
||||
i8_ptr_type.into(),
|
||||
i8_type.into(),
|
||||
i64_type.into(),
|
||||
i1_type.into(),
|
||||
],
|
||||
false,
|
||||
),
|
||||
LLVM_POW_F64,
|
||||
f64_type.fn_type(&[f64_type.into(), f64_type.into()], false),
|
||||
);
|
||||
|
||||
add_intrinsic(
|
||||
module,
|
||||
LLVM_MEMSET_I32,
|
||||
void_type.fn_type(
|
||||
&[
|
||||
i8_ptr_type.into(),
|
||||
i8_type.into(),
|
||||
i32_type.into(),
|
||||
i1_type.into(),
|
||||
],
|
||||
false,
|
||||
),
|
||||
LLVM_CEILING_F64,
|
||||
f64_type.fn_type(&[f64_type.into()], false),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -260,6 +273,8 @@ static LLVM_LROUND_I64_F64: &str = "llvm.lround.i64.f64";
|
|||
static LLVM_FABS_F64: &str = "llvm.fabs.f64";
|
||||
static LLVM_SIN_F64: &str = "llvm.sin.f64";
|
||||
static LLVM_COS_F64: &str = "llvm.cos.f64";
|
||||
static LLVM_POW_F64: &str = "llvm.pow.f64";
|
||||
static LLVM_CEILING_F64: &str = "llvm.ceil.f64";
|
||||
|
||||
fn add_intrinsic<'ctx>(
|
||||
module: &Module<'ctx>,
|
||||
|
@ -320,6 +335,133 @@ pub fn construct_optimization_passes<'a>(
|
|||
(mpm, fpm)
|
||||
}
|
||||
|
||||
/// For communication with C (tests and platforms) we need to abide by the C calling convention
|
||||
///
|
||||
/// While small values are just returned like with the fast CC, larger structures need to
|
||||
/// be written into a pointer (into the callers stack)
|
||||
enum PassVia {
|
||||
Register,
|
||||
Memory,
|
||||
}
|
||||
|
||||
impl PassVia {
|
||||
fn from_layout(ptr_bytes: u32, layout: &Layout<'_>) -> Self {
|
||||
if layout.stack_size(ptr_bytes) > 16 {
|
||||
PassVia::Memory
|
||||
} else {
|
||||
PassVia::Register
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_main_function<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
layout: &Layout<'a>,
|
||||
main_body: &roc_mono::ir::Stmt<'a>,
|
||||
) -> (&'static str, &'a FunctionValue<'ctx>) {
|
||||
use inkwell::types::BasicType;
|
||||
use PassVia::*;
|
||||
|
||||
let context = env.context;
|
||||
let builder = env.builder;
|
||||
let arena = env.arena;
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
let return_type = basic_type_from_layout(&arena, context, &layout, ptr_bytes);
|
||||
let roc_main_fn_name = "$Test.roc_main";
|
||||
|
||||
// make the roc main function
|
||||
let roc_main_fn_type = return_type.fn_type(&[], false);
|
||||
|
||||
// Add main to the module.
|
||||
let roc_main_fn = env
|
||||
.module
|
||||
.add_function(roc_main_fn_name, roc_main_fn_type, None);
|
||||
|
||||
// our exposed main function adheres to the C calling convention
|
||||
roc_main_fn.set_call_conventions(FAST_CALL_CONV);
|
||||
|
||||
// Add main's body
|
||||
let basic_block = context.append_basic_block(roc_main_fn, "entry");
|
||||
|
||||
builder.position_at_end(basic_block);
|
||||
|
||||
// builds the function body (return statement included)
|
||||
build_exp_stmt(
|
||||
env,
|
||||
layout_ids,
|
||||
&mut Scope::default(),
|
||||
roc_main_fn,
|
||||
main_body,
|
||||
);
|
||||
|
||||
// build the C calling convention wrapper
|
||||
|
||||
let main_fn_name = "$Test.main";
|
||||
let register_or_memory = PassVia::from_layout(env.ptr_bytes, layout);
|
||||
|
||||
let main_fn_type = match register_or_memory {
|
||||
Memory => {
|
||||
let return_value_ptr = context.i64_type().ptr_type(AddressSpace::Generic).into();
|
||||
context.void_type().fn_type(&[return_value_ptr], false)
|
||||
}
|
||||
Register => return_type.fn_type(&[], false),
|
||||
};
|
||||
|
||||
// Add main to the module.
|
||||
let main_fn = env.module.add_function(main_fn_name, main_fn_type, None);
|
||||
|
||||
// our exposed main function adheres to the C calling convention
|
||||
main_fn.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
// Add main's body
|
||||
let basic_block = context.append_basic_block(main_fn, "entry");
|
||||
|
||||
builder.position_at_end(basic_block);
|
||||
|
||||
let call = builder.build_call(roc_main_fn, &[], "call_roc_main");
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
let call_result = call.try_as_basic_value().left().unwrap();
|
||||
|
||||
match register_or_memory {
|
||||
Memory => {
|
||||
// write the result into the supplied pointer
|
||||
// this is a void function, therefore return None
|
||||
let ptr_return_type = return_type.ptr_type(AddressSpace::Generic);
|
||||
|
||||
let ptr_as_int = main_fn.get_first_param().unwrap();
|
||||
|
||||
let ptr = builder.build_bitcast(ptr_as_int, ptr_return_type, "caller_ptr");
|
||||
|
||||
builder.build_store(ptr.into_pointer_value(), call_result);
|
||||
|
||||
builder.build_return(None);
|
||||
}
|
||||
Register => {
|
||||
// construct a normal return
|
||||
// values are passed to the caller via registers
|
||||
builder.build_return(Some(&call_result));
|
||||
}
|
||||
}
|
||||
|
||||
(main_fn_name, env.arena.alloc(main_fn))
|
||||
}
|
||||
|
||||
fn get_inplace_from_layout(layout: &Layout<'_>) -> InPlace {
|
||||
match layout {
|
||||
Layout::Builtin(Builtin::EmptyList) => InPlace::InPlace,
|
||||
Layout::Builtin(Builtin::List(memory_mode, _)) => match memory_mode {
|
||||
MemoryMode::Unique => InPlace::InPlace,
|
||||
MemoryMode::Refcounted => InPlace::Clone,
|
||||
},
|
||||
Layout::Builtin(Builtin::EmptyStr) => InPlace::InPlace,
|
||||
Layout::Builtin(Builtin::Str) => InPlace::Clone,
|
||||
_ => unreachable!("Layout {:?} does not have an inplace", layout),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_exp_literal<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
literal: &roc_mono::ir::Literal<'a>,
|
||||
|
@ -418,8 +560,7 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
|
|||
let len_type = env.ptr_int();
|
||||
let len = len_type.const_int(bytes_len, false);
|
||||
|
||||
let ptr = allocate_list(env, &CHAR_LAYOUT, len);
|
||||
|
||||
let ptr = allocate_list(env, InPlace::Clone, &CHAR_LAYOUT, len);
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int = builder.build_ptr_to_int(ptr, int_type, "list_cast_ptr");
|
||||
let struct_type = collection(ctx, ptr_bytes);
|
||||
|
@ -461,6 +602,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
layout_ids: &mut LayoutIds<'a>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
layout: &Layout<'a>,
|
||||
expr: &roc_mono::ir::Expr<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
use roc_mono::ir::CallType::*;
|
||||
|
@ -468,7 +610,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
|
||||
match expr {
|
||||
Literal(literal) => build_exp_literal(env, literal),
|
||||
RunLowLevel(op, symbols) => run_low_level(env, scope, parent, *op, symbols),
|
||||
RunLowLevel(op, symbols) => run_low_level(env, scope, parent, layout, *op, symbols),
|
||||
|
||||
FunctionCall {
|
||||
call_type: ByName(name),
|
||||
|
@ -833,7 +975,11 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
}
|
||||
}
|
||||
EmptyArray => empty_polymorphic_list(env),
|
||||
Array { elem_layout, elems } => list_literal(env, scope, elem_layout, elems),
|
||||
Array { elem_layout, elems } => {
|
||||
let inplace = get_inplace_from_layout(layout);
|
||||
|
||||
list_literal(env, inplace, scope, elem_layout, elems)
|
||||
}
|
||||
FunctionPointer(symbol, layout) => {
|
||||
let fn_name = layout_ids
|
||||
.get(*symbol, layout)
|
||||
|
@ -922,6 +1068,7 @@ pub fn allocate_with_refcount<'a, 'ctx, 'env>(
|
|||
|
||||
fn list_literal<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
inplace: InPlace,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
elem_layout: &Layout<'a>,
|
||||
elems: &&[Symbol],
|
||||
|
@ -937,7 +1084,7 @@ fn list_literal<'a, 'ctx, 'env>(
|
|||
let len_type = env.ptr_int();
|
||||
let len = len_type.const_int(bytes_len, false);
|
||||
|
||||
allocate_list(env, elem_layout, len)
|
||||
allocate_list(env, inplace, elem_layout, len)
|
||||
|
||||
// TODO check if malloc returned null; if so, runtime error for OOM!
|
||||
};
|
||||
|
@ -995,7 +1142,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
|||
Let(symbol, expr, layout, cont) => {
|
||||
let context = &env.context;
|
||||
|
||||
let val = build_exp_expr(env, layout_ids, &scope, parent, &expr);
|
||||
let val = build_exp_expr(env, layout_ids, &scope, parent, layout, &expr);
|
||||
let expr_bt = if let Layout::RecursivePointer = layout {
|
||||
match expr {
|
||||
Expr::AccessAtIndex { field_layouts, .. } => {
|
||||
|
@ -1611,6 +1758,7 @@ fn call_with_args<'a, 'ctx, 'env>(
|
|||
.unwrap_or_else(|| panic!("LLVM error: Invalid call by name for name {:?}", symbol))
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum InPlace {
|
||||
InPlace,
|
||||
Clone,
|
||||
|
@ -1639,6 +1787,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
layout: &Layout<'a>,
|
||||
op: LowLevel,
|
||||
args: &[Symbol],
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
|
@ -1649,7 +1798,9 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
// Str.concat : Str, Str -> Str
|
||||
debug_assert_eq!(args.len(), 2);
|
||||
|
||||
str_concat(env, scope, parent, args[0], args[1])
|
||||
let inplace = get_inplace_from_layout(layout);
|
||||
|
||||
str_concat(env, inplace, scope, parent, args[0], args[1])
|
||||
}
|
||||
StrIsEmpty => {
|
||||
// Str.isEmpty : Str -> Str
|
||||
|
@ -1679,7 +1830,9 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
let (arg, arg_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||
|
||||
list_single(env, arg, arg_layout)
|
||||
let inplace = get_inplace_from_layout(layout);
|
||||
|
||||
list_single(env, inplace, arg, arg_layout)
|
||||
}
|
||||
ListRepeat => {
|
||||
// List.repeat : Int, elem -> List elem
|
||||
|
@ -1688,7 +1841,9 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
let list_len = load_symbol(env, scope, &args[0]).into_int_value();
|
||||
let (elem, elem_layout) = load_symbol_and_layout(env, scope, &args[1]);
|
||||
|
||||
list_repeat(env, parent, list_len, elem, elem_layout)
|
||||
let inplace = get_inplace_from_layout(layout);
|
||||
|
||||
list_repeat(env, inplace, parent, list_len, elem, elem_layout)
|
||||
}
|
||||
ListReverse => {
|
||||
// List.reverse : List elem -> List elem
|
||||
|
@ -1696,7 +1851,9 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
let (list, list_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||
|
||||
list_reverse(env, parent, list, list_layout)
|
||||
let inplace = get_inplace_from_layout(layout);
|
||||
|
||||
list_reverse(env, parent, inplace, list, list_layout)
|
||||
}
|
||||
ListConcat => {
|
||||
debug_assert_eq!(args.len(), 2);
|
||||
|
@ -1705,7 +1862,9 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
let second_list = load_symbol(env, scope, &args[1]);
|
||||
|
||||
list_concat(env, parent, first_list, second_list, list_layout)
|
||||
let inplace = get_inplace_from_layout(layout);
|
||||
|
||||
list_concat(env, inplace, parent, first_list, second_list, list_layout)
|
||||
}
|
||||
ListMap => {
|
||||
// List.map : List before, (before -> after) -> List after
|
||||
|
@ -1715,7 +1874,9 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
let (func, func_layout) = load_symbol_and_layout(env, scope, &args[1]);
|
||||
|
||||
list_map(env, parent, func, func_layout, list, list_layout)
|
||||
let inplace = get_inplace_from_layout(layout);
|
||||
|
||||
list_map(env, inplace, parent, func, func_layout, list, list_layout)
|
||||
}
|
||||
ListKeepIf => {
|
||||
// List.keepIf : List elem, (elem -> Bool) -> List elem
|
||||
|
@ -1725,7 +1886,9 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
let (func, func_layout) = load_symbol_and_layout(env, scope, &args[1]);
|
||||
|
||||
list_keep_if(env, parent, func, func_layout, list, list_layout)
|
||||
let inplace = get_inplace_from_layout(layout);
|
||||
|
||||
list_keep_if(env, inplace, parent, func, func_layout, list, list_layout)
|
||||
}
|
||||
ListWalkRight => {
|
||||
// List.walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
||||
|
@ -1755,7 +1918,9 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
let original_wrapper = load_symbol(env, scope, &args[0]).into_struct_value();
|
||||
let (elem, elem_layout) = load_symbol_and_layout(env, scope, &args[1]);
|
||||
|
||||
list_append(env, original_wrapper, elem, elem_layout)
|
||||
let inplace = get_inplace_from_layout(layout);
|
||||
|
||||
list_append(env, inplace, original_wrapper, elem, elem_layout)
|
||||
}
|
||||
ListPrepend => {
|
||||
// List.prepend : List elem, elem -> List elem
|
||||
|
@ -1764,7 +1929,9 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
let original_wrapper = load_symbol(env, scope, &args[0]).into_struct_value();
|
||||
let (elem, elem_layout) = load_symbol_and_layout(env, scope, &args[1]);
|
||||
|
||||
list_prepend(env, original_wrapper, elem, elem_layout)
|
||||
let inplace = get_inplace_from_layout(layout);
|
||||
|
||||
list_prepend(env, inplace, original_wrapper, elem, elem_layout)
|
||||
}
|
||||
ListJoin => {
|
||||
// List.join : List (List elem) -> List elem
|
||||
|
@ -1772,9 +1939,12 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
let (list, outer_list_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||
|
||||
list_join(env, parent, list, outer_list_layout)
|
||||
let inplace = get_inplace_from_layout(layout);
|
||||
|
||||
list_join(env, inplace, parent, list, outer_list_layout)
|
||||
}
|
||||
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumToFloat => {
|
||||
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumCeiling
|
||||
| NumToFloat => {
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
||||
let (arg, arg_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||
|
@ -1885,7 +2055,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
}
|
||||
|
||||
NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumRemUnchecked
|
||||
| NumDivUnchecked => {
|
||||
| NumDivUnchecked | NumPow => {
|
||||
debug_assert_eq!(args.len(), 2);
|
||||
|
||||
let (lhs_arg, lhs_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||
|
@ -1990,6 +2160,8 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
ListSetInPlace => {
|
||||
let (list_symbol, list_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||
|
||||
let output_inplace = get_inplace_from_layout(layout);
|
||||
|
||||
list_set(
|
||||
parent,
|
||||
&[
|
||||
|
@ -1999,6 +2171,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
],
|
||||
env,
|
||||
InPlace::InPlace,
|
||||
output_inplace,
|
||||
)
|
||||
}
|
||||
ListSet => {
|
||||
|
@ -2010,10 +2183,43 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
(load_symbol_and_layout(env, scope, &args[2])),
|
||||
];
|
||||
|
||||
let output_inplace = get_inplace_from_layout(layout);
|
||||
|
||||
let in_place = || list_set(parent, arguments, env, InPlace::InPlace, output_inplace);
|
||||
let clone = || list_set(parent, arguments, env, InPlace::Clone, output_inplace);
|
||||
let empty = || list_symbol;
|
||||
|
||||
maybe_inplace_list(
|
||||
env,
|
||||
parent,
|
||||
list_layout,
|
||||
list_symbol.into_struct_value(),
|
||||
in_place,
|
||||
clone,
|
||||
empty,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_inplace_list<'a, 'ctx, 'env, InPlace, CloneFirst, Empty>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
list_layout: &Layout<'a>,
|
||||
original_wrapper: StructValue<'ctx>,
|
||||
mut in_place: InPlace,
|
||||
clone: CloneFirst,
|
||||
mut empty: Empty,
|
||||
) -> BasicValueEnum<'ctx>
|
||||
where
|
||||
InPlace: FnMut() -> BasicValueEnum<'ctx>,
|
||||
CloneFirst: FnMut() -> BasicValueEnum<'ctx>,
|
||||
Empty: FnMut() -> BasicValueEnum<'ctx>,
|
||||
{
|
||||
match list_layout {
|
||||
Layout::Builtin(Builtin::List(MemoryMode::Unique, _)) => {
|
||||
// the layout tells us this List.set can be done in-place
|
||||
list_set(parent, arguments, env, InPlace::InPlace)
|
||||
in_place()
|
||||
}
|
||||
Layout::Builtin(Builtin::List(MemoryMode::Refcounted, _)) => {
|
||||
// no static guarantees, but all is not lost: we can check the refcount
|
||||
|
@ -2021,11 +2227,9 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
let builder = env.builder;
|
||||
let ctx = env.context;
|
||||
|
||||
let ret_type =
|
||||
basic_type_from_layout(env.arena, ctx, list_layout, env.ptr_bytes);
|
||||
let ret_type = basic_type_from_layout(env.arena, ctx, list_layout, env.ptr_bytes);
|
||||
|
||||
let refcount_ptr =
|
||||
list_get_refcount_ptr(env, list_layout, list_symbol.into_struct_value());
|
||||
let refcount_ptr = list_get_refcount_ptr(env, list_layout, original_wrapper);
|
||||
|
||||
let refcount = env
|
||||
.builder
|
||||
|
@ -2034,22 +2238,12 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
let comparison = refcount_is_one_comparison(builder, env.context, refcount);
|
||||
|
||||
// build then block
|
||||
// refcount is 1, so work in-place
|
||||
let build_pass = || list_set(parent, arguments, env, InPlace::InPlace);
|
||||
|
||||
// build else block
|
||||
// refcount != 1, so clone first
|
||||
let build_fail = || list_set(parent, arguments, env, InPlace::Clone);
|
||||
|
||||
crate::llvm::build_list::build_basic_phi2(
|
||||
env, parent, comparison, build_pass, build_fail, ret_type,
|
||||
env, parent, comparison, in_place, clone, ret_type,
|
||||
)
|
||||
}
|
||||
Layout::Builtin(Builtin::EmptyList) => list_symbol,
|
||||
other => unreachable!("List.set: weird layout {:?}", other),
|
||||
}
|
||||
}
|
||||
Layout::Builtin(Builtin::EmptyList) => empty(),
|
||||
other => unreachable!("Attempting list operation on invalid layout {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2105,6 +2299,7 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
|||
NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(),
|
||||
NumRemUnchecked => bd.build_float_rem(lhs, rhs, "rem_float").into(),
|
||||
NumDivUnchecked => bd.build_float_div(lhs, rhs, "div_float").into(),
|
||||
NumPow => env.call_intrinsic(LLVM_POW_F64, &[lhs.into(), rhs.into()]),
|
||||
_ => {
|
||||
unreachable!("Unrecognized int binary operation: {:?}", op);
|
||||
}
|
||||
|
@ -2202,6 +2397,12 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
|||
NumSin => env.call_intrinsic(LLVM_SIN_F64, &[arg.into()]),
|
||||
NumCos => env.call_intrinsic(LLVM_COS_F64, &[arg.into()]),
|
||||
NumToFloat => arg.into(), /* Converting from Float to Float is a no-op */
|
||||
NumCeiling => env.builder.build_cast(
|
||||
InstructionOpcode::FPToSI,
|
||||
env.call_intrinsic(LLVM_CEILING_F64, &[arg.into()]),
|
||||
env.context.i64_type(),
|
||||
"num_ceiling",
|
||||
),
|
||||
_ => {
|
||||
unreachable!("Unrecognized int unary operation: {:?}", op);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,6 @@
|
|||
use crate::llvm::build::{ptr_from_symbol, Env, Scope};
|
||||
use crate::llvm::build::{ptr_from_symbol, Env, InPlace, Scope};
|
||||
use crate::llvm::build_list::{
|
||||
allocate_list, build_basic_phi2, empty_list, incrementing_elem_loop, load_list_ptr, store_list,
|
||||
LoopListArg,
|
||||
};
|
||||
use crate::llvm::convert::{collection, ptr_int};
|
||||
use inkwell::builder::Builder;
|
||||
|
@ -16,6 +15,7 @@ pub static CHAR_LAYOUT: Layout = Layout::Builtin(Builtin::Int8);
|
|||
/// Str.concat : Str, Str -> Str
|
||||
pub fn str_concat<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
inplace: InPlace,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
first_str_symbol: Symbol,
|
||||
|
@ -55,6 +55,7 @@ pub fn str_concat<'a, 'ctx, 'env>(
|
|||
let if_second_str_is_nonempty = || {
|
||||
let (new_wrapper, _) = clone_nonempty_str(
|
||||
env,
|
||||
inplace,
|
||||
second_str_smallness,
|
||||
second_str_len,
|
||||
second_str_ptr,
|
||||
|
@ -79,6 +80,7 @@ pub fn str_concat<'a, 'ctx, 'env>(
|
|||
let if_second_str_is_empty = || {
|
||||
let (new_wrapper, _) = clone_nonempty_str(
|
||||
env,
|
||||
inplace,
|
||||
first_str_smallness,
|
||||
first_str_len,
|
||||
first_str_ptr,
|
||||
|
@ -111,7 +113,7 @@ pub fn str_concat<'a, 'ctx, 'env>(
|
|||
|
||||
let if_big = || {
|
||||
let combined_str_ptr =
|
||||
allocate_list(env, &CHAR_LAYOUT, combined_str_len);
|
||||
allocate_list(env, inplace, &CHAR_LAYOUT, combined_str_len);
|
||||
|
||||
// TODO replace FIRST_LOOP with a memcpy!
|
||||
// FIRST LOOP
|
||||
|
@ -133,14 +135,11 @@ pub fn str_concat<'a, 'ctx, 'env>(
|
|||
|
||||
let index_alloca = incrementing_elem_loop(
|
||||
builder,
|
||||
parent,
|
||||
ctx,
|
||||
LoopListArg {
|
||||
ptr: first_str_ptr,
|
||||
len: first_str_len,
|
||||
},
|
||||
parent,
|
||||
first_str_ptr,
|
||||
first_str_len,
|
||||
index_name,
|
||||
None,
|
||||
first_loop,
|
||||
);
|
||||
|
||||
|
@ -179,14 +178,11 @@ pub fn str_concat<'a, 'ctx, 'env>(
|
|||
|
||||
incrementing_elem_loop(
|
||||
builder,
|
||||
parent,
|
||||
ctx,
|
||||
LoopListArg {
|
||||
ptr: second_str_ptr,
|
||||
len: second_str_len,
|
||||
},
|
||||
parent,
|
||||
second_str_ptr,
|
||||
second_str_len,
|
||||
index_name,
|
||||
Some(index_alloca),
|
||||
second_loop,
|
||||
);
|
||||
|
||||
|
@ -220,14 +216,11 @@ pub fn str_concat<'a, 'ctx, 'env>(
|
|||
|
||||
let index_alloca = incrementing_elem_loop(
|
||||
builder,
|
||||
parent,
|
||||
ctx,
|
||||
LoopListArg {
|
||||
ptr: first_str_ptr,
|
||||
len: first_str_len,
|
||||
},
|
||||
parent,
|
||||
first_str_ptr,
|
||||
first_str_len,
|
||||
index_name,
|
||||
None,
|
||||
first_loop,
|
||||
);
|
||||
|
||||
|
@ -266,14 +259,11 @@ pub fn str_concat<'a, 'ctx, 'env>(
|
|||
|
||||
incrementing_elem_loop(
|
||||
builder,
|
||||
parent,
|
||||
ctx,
|
||||
LoopListArg {
|
||||
ptr: second_str_ptr,
|
||||
len: second_str_len,
|
||||
},
|
||||
parent,
|
||||
second_str_ptr,
|
||||
second_str_len,
|
||||
index_name,
|
||||
Some(index_alloca),
|
||||
second_loop,
|
||||
);
|
||||
|
||||
|
@ -445,6 +435,7 @@ enum Smallness {
|
|||
|
||||
fn clone_nonempty_str<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
inplace: InPlace,
|
||||
smallness: Smallness,
|
||||
len: IntValue<'ctx>,
|
||||
bytes_ptr: PointerValue<'ctx>,
|
||||
|
@ -465,7 +456,7 @@ fn clone_nonempty_str<'a, 'ctx, 'env>(
|
|||
(wrapper_struct.into_struct_value(), alloca)
|
||||
}
|
||||
Smallness::Big => {
|
||||
let clone_ptr = allocate_list(env, &CHAR_LAYOUT, len);
|
||||
let clone_ptr = allocate_list(env, inplace, &CHAR_LAYOUT, len);
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int = builder.build_ptr_to_int(clone_ptr, int_type, "list_cast_ptr");
|
||||
|
||||
|
|
|
@ -665,4 +665,14 @@ mod gen_num {
|
|||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pow() {
|
||||
assert_evals_to!("Num.pow 2.0 2.0", 4.0, f64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ceiling() {
|
||||
assert_evals_to!("Num.ceiling 1.5", 2, i64);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -533,4 +533,147 @@ mod gen_records {
|
|||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_record_2() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x: 3, y: 5 }
|
||||
"#
|
||||
),
|
||||
[3, 5],
|
||||
[i64; 2]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_record_3() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x: 3, y: 5, z: 4 }
|
||||
"#
|
||||
),
|
||||
(3, 5, 4),
|
||||
(i64, i64, i64)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_record_4() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 3, b: 5, c: 4, d: 2 }
|
||||
"#
|
||||
),
|
||||
[3, 5, 4, 2],
|
||||
[i64; 4]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_record_5() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 3, b: 5, c: 4, d: 2, e: 1 }
|
||||
"#
|
||||
),
|
||||
[3, 5, 4, 2, 1],
|
||||
[i64; 5]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_record_6() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7 }
|
||||
"#
|
||||
),
|
||||
[3, 5, 4, 2, 1, 7],
|
||||
[i64; 6]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_record_7() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7, g: 8 }
|
||||
"#
|
||||
),
|
||||
[3, 5, 4, 2, 1, 7, 8],
|
||||
[i64; 7]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_record_float_int() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 3.14, b: 0x1 }
|
||||
"#
|
||||
),
|
||||
(3.14, 0x1),
|
||||
(f64, i64)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_record_int_float() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 0x1, b: 3.14 }
|
||||
"#
|
||||
),
|
||||
(0x1, 3.14),
|
||||
(i64, f64)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_record_float_float() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 6.28, b: 3.14 }
|
||||
"#
|
||||
),
|
||||
(6.28, 3.14),
|
||||
(f64, f64)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_record_float_float_float() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 6.28, b: 3.14, c: 0.1 }
|
||||
"#
|
||||
),
|
||||
(6.28, 3.14, 0.1),
|
||||
(f64, f64, f64)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn just_to_be_sure() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 1, b : 2, c : 3 }
|
||||
"#
|
||||
),
|
||||
[1, 2, 3],
|
||||
[i64; 3]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,8 @@ pub fn helper_without_uniqueness<'a>(
|
|||
context: &'a inkwell::context::Context,
|
||||
) -> (&'static str, inkwell::execution_engine::ExecutionEngine<'a>) {
|
||||
use crate::helpers::{can_expr, infer_expr, CanExprOut};
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::OptimizationLevel;
|
||||
use roc_gen::llvm::build::Scope;
|
||||
use roc_gen::llvm::build::{build_proc, build_proc_header};
|
||||
use roc_gen::llvm::convert::basic_type_from_layout;
|
||||
use roc_mono::layout::Layout;
|
||||
|
||||
let target = target_lexicon::Triple::host();
|
||||
|
@ -66,7 +63,7 @@ pub fn helper_without_uniqueness<'a>(
|
|||
roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
|
||||
|
||||
// Compute main_fn_type before moving subs to Env
|
||||
let layout = Layout::new(&arena, content, &subs).unwrap_or_else(|err| {
|
||||
let return_layout = Layout::new(&arena, content, &subs).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"Code gen error in NON-OPTIMIZED test: could not convert to layout. Err was {:?}",
|
||||
err
|
||||
|
@ -76,10 +73,6 @@ pub fn helper_without_uniqueness<'a>(
|
|||
.create_jit_execution_engine(OptimizationLevel::None)
|
||||
.expect("Error creating JIT execution engine for test");
|
||||
|
||||
let main_fn_type =
|
||||
basic_type_from_layout(&arena, context, &layout, ptr_bytes).fn_type(&[], false);
|
||||
let main_fn_name = "$Test.main";
|
||||
|
||||
// Compile and add all the Procs before adding main
|
||||
let mut env = roc_gen::llvm::build::Env {
|
||||
arena: &arena,
|
||||
|
@ -163,25 +156,8 @@ pub fn helper_without_uniqueness<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
// Add main to the module.
|
||||
let main_fn = env.module.add_function(main_fn_name, main_fn_type, None);
|
||||
let cc = roc_gen::llvm::build::FAST_CALL_CONV;
|
||||
|
||||
main_fn.set_call_conventions(cc);
|
||||
|
||||
// Add main's body
|
||||
let basic_block = context.append_basic_block(main_fn, "entry");
|
||||
|
||||
builder.position_at_end(basic_block);
|
||||
|
||||
// builds the function body (return statement included)
|
||||
roc_gen::llvm::build::build_exp_stmt(
|
||||
&env,
|
||||
&mut layout_ids,
|
||||
&mut Scope::default(),
|
||||
main_fn,
|
||||
&main_body,
|
||||
);
|
||||
let (main_fn_name, main_fn) =
|
||||
roc_gen::llvm::build::make_main_function(&env, &mut layout_ids, &return_layout, &main_body);
|
||||
|
||||
// Uncomment this to see the module's un-optimized LLVM instruction output:
|
||||
// env.module.print_to_stderr();
|
||||
|
@ -212,11 +188,8 @@ pub fn helper_with_uniqueness<'a>(
|
|||
context: &'a inkwell::context::Context,
|
||||
) -> (&'static str, inkwell::execution_engine::ExecutionEngine<'a>) {
|
||||
use crate::helpers::{infer_expr, uniq_expr};
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::OptimizationLevel;
|
||||
use roc_gen::llvm::build::Scope;
|
||||
use roc_gen::llvm::build::{build_proc, build_proc_header};
|
||||
use roc_gen::llvm::convert::basic_type_from_layout;
|
||||
use roc_mono::layout::Layout;
|
||||
|
||||
let target = target_lexicon::Triple::host();
|
||||
|
@ -258,7 +231,7 @@ pub fn helper_with_uniqueness<'a>(
|
|||
let (mpm, fpm) = roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
|
||||
|
||||
// Compute main_fn_type before moving subs to Env
|
||||
let layout = Layout::new(&arena, content, &subs).unwrap_or_else(|err| {
|
||||
let return_layout = Layout::new(&arena, content, &subs).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"Code gen error in OPTIMIZED test: could not convert to layout. Err was {:?}",
|
||||
err
|
||||
|
@ -269,11 +242,6 @@ pub fn helper_with_uniqueness<'a>(
|
|||
.create_jit_execution_engine(OptimizationLevel::None)
|
||||
.expect("Error creating JIT execution engine for test");
|
||||
|
||||
let main_fn_type = basic_type_from_layout(&arena, context, &layout, ptr_bytes)
|
||||
.fn_type(&[], false)
|
||||
.clone();
|
||||
let main_fn_name = "$Test.main";
|
||||
|
||||
// Compile and add all the Procs before adding main
|
||||
let mut env = roc_gen::llvm::build::Env {
|
||||
arena: &arena,
|
||||
|
@ -357,25 +325,8 @@ pub fn helper_with_uniqueness<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
// Add main to the module.
|
||||
let main_fn = env.module.add_function(main_fn_name, main_fn_type, None);
|
||||
let cc = roc_gen::llvm::build::FAST_CALL_CONV;
|
||||
|
||||
main_fn.set_call_conventions(cc);
|
||||
|
||||
// Add main's body
|
||||
let basic_block = context.append_basic_block(main_fn, "entry");
|
||||
|
||||
builder.position_at_end(basic_block);
|
||||
|
||||
// builds the function body (return statement included)
|
||||
roc_gen::llvm::build::build_exp_stmt(
|
||||
&env,
|
||||
&mut layout_ids,
|
||||
&mut Scope::default(),
|
||||
main_fn,
|
||||
&main_body,
|
||||
);
|
||||
let (main_fn_name, main_fn) =
|
||||
roc_gen::llvm::build::make_main_function(&env, &mut layout_ids, &return_layout, &main_body);
|
||||
|
||||
// you're in the version with uniqueness!
|
||||
|
||||
|
|
|
@ -36,6 +36,8 @@ pub enum LowLevel {
|
|||
NumSqrtUnchecked,
|
||||
NumRound,
|
||||
NumToFloat,
|
||||
NumPow,
|
||||
NumCeiling,
|
||||
Eq,
|
||||
NotEq,
|
||||
And,
|
||||
|
|
|
@ -640,6 +640,8 @@ define_builtins! {
|
|||
35 NUM_SQRT: "sqrt"
|
||||
36 NUM_ROUND: "round"
|
||||
37 NUM_COMPARE: "compare"
|
||||
38 NUM_POW: "pow"
|
||||
39 NUM_CEILING: "ceiling"
|
||||
}
|
||||
2 BOOL: "Bool" => {
|
||||
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
||||
|
|
|
@ -522,12 +522,11 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
ListWalkRight => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]),
|
||||
|
||||
Eq | NotEq | And | Or | NumAdd | NumSub | NumMul | NumGt | NumGte | NumLt | NumLte
|
||||
| NumCompare | NumDivUnchecked | NumRemUnchecked => {
|
||||
| NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow => {
|
||||
arena.alloc_slice_copy(&[irrelevant, irrelevant])
|
||||
}
|
||||
|
||||
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumToFloat | Not => {
|
||||
arena.alloc_slice_copy(&[irrelevant])
|
||||
}
|
||||
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumToFloat
|
||||
| NumCeiling | Not => arena.alloc_slice_copy(&[irrelevant]),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,26 +86,33 @@ where
|
|||
return err_unexpected();
|
||||
}
|
||||
}
|
||||
next_ch if next_ch.is_ascii_digit() => {
|
||||
has_parsed_digits = true;
|
||||
'_' => {
|
||||
// Underscores are ignored.
|
||||
}
|
||||
next_ch
|
||||
if next_ch != '_' &&
|
||||
// ASCII alphabetic chars (like 'a' and 'f') are allowed in Hex int literals.
|
||||
// We parse them in any int literal, so we can give a more helpful error
|
||||
// in canonicalization (e.g. "the character 'f' is not allowed in Octal literals"
|
||||
// or "the character 'g' is outside the range of valid Hex literals")
|
||||
!next_ch.is_ascii_alphabetic() =>
|
||||
{
|
||||
if has_parsed_digits {
|
||||
// We hit an invalid number literal character; we're done!
|
||||
break;
|
||||
next_ch => {
|
||||
if next_ch.is_ascii_digit() {
|
||||
has_parsed_digits = true;
|
||||
} else {
|
||||
// No digits! We likely parsed a minus sign that's actually an operator.
|
||||
if !has_parsed_digits {
|
||||
// No digits! We likely parsed a minus sign
|
||||
// that's actually a unary negation operator.
|
||||
return err_unexpected();
|
||||
}
|
||||
|
||||
// ASCII alphabetic chars (like 'a' and 'f') are
|
||||
// allowed in Hex int literals. We verify them in
|
||||
// canonicalization, so if there's a problem, we can
|
||||
// give a more helpful error (e.g. "the character 'f'
|
||||
// is not allowed in Octal literals" or
|
||||
// "the character 'g' is outside the range of valid
|
||||
// Hex literals") while still allowing the formatter
|
||||
// to format them normally.
|
||||
if !next_ch.is_ascii_alphabetic() {
|
||||
// We hit an invalid number literal character; we're done!
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Since we only consume characters in the ASCII range for number literals,
|
||||
|
|
|
@ -1405,6 +1405,23 @@ mod test_parse {
|
|||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unary_negation_access() {
|
||||
// Regression test for https://github.com/rtfeldman/roc/issues/509
|
||||
let arena = Bump::new();
|
||||
let var = Var {
|
||||
module_name: "",
|
||||
ident: "rec1",
|
||||
};
|
||||
let loc_op = Located::new(0, 0, 0, 1, UnaryOp::Negate);
|
||||
let access = Access(arena.alloc(var), "field");
|
||||
let loc_access = Located::new(0, 0, 1, 11, access);
|
||||
let expected = UnaryOp(arena.alloc(loc_access), loc_op);
|
||||
let actual = parse_with(&arena, "-rec1.field");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
// CLOSURE
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -2382,6 +2382,30 @@ mod solve_expr {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ceiling() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
Num.ceiling
|
||||
"#
|
||||
),
|
||||
"Float -> Int",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pow() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
Num.pow
|
||||
"#
|
||||
),
|
||||
"Float, Float -> Float",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reconstruct_path() {
|
||||
infer_eq_without_problem(
|
||||
|
|
15
docs/Cargo.toml
Normal file
15
docs/Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "docs"
|
||||
version = "0.1.0"
|
||||
authors = ["Pablo Hirafuji <pablohirafuji@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
handlebars = "3.4.0"
|
||||
serde = "1.0.0"
|
||||
serde_json = "1.0.39"
|
||||
serde_derive = "1.0.75"
|
||||
fs_extra = "1.2.0"
|
||||
pulldown-cmark = { version = "0.8", default-features = false }
|
250
docs/src/main.rs
Normal file
250
docs/src/main.rs
Normal file
|
@ -0,0 +1,250 @@
|
|||
extern crate fs_extra;
|
||||
extern crate handlebars;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate pulldown_cmark;
|
||||
extern crate serde_json;
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Package {
|
||||
name: String,
|
||||
version: String,
|
||||
docs: String,
|
||||
modules: Vec<Module>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct Module {
|
||||
name: String,
|
||||
docs: String,
|
||||
entries: Vec<ModuleEntry>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct ModuleEntry {
|
||||
name: String,
|
||||
docs: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Template {
|
||||
package_name: String,
|
||||
package_version: String,
|
||||
module_name: String,
|
||||
module_docs: String,
|
||||
module_entries: Vec<ModuleEntry>,
|
||||
module_links: Vec<TemplateLink>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct TemplateLink {
|
||||
name: String,
|
||||
href: String,
|
||||
classes: String,
|
||||
entries: Vec<TemplateLinkEntry>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct TemplateLinkEntry {
|
||||
name: String,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let package = Package {
|
||||
name: "roc/builtins".to_string(),
|
||||
version: "1.0.0".to_string(),
|
||||
docs: "Package introduction or README.".to_string(),
|
||||
modules: vec![
|
||||
Module {
|
||||
name: "Str".to_string(),
|
||||
docs: "Module introduction".to_string(),
|
||||
entries: vec![
|
||||
ModuleEntry {
|
||||
name: "Str".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
||||
.to_string(),
|
||||
},
|
||||
ModuleEntry {
|
||||
name: "isEmpty".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
||||
.to_string(),
|
||||
},
|
||||
ModuleEntry {
|
||||
name: "append".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
||||
.to_string(),
|
||||
},
|
||||
ModuleEntry {
|
||||
name: "prepend".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
||||
.to_string(),
|
||||
},
|
||||
ModuleEntry {
|
||||
name: "concat".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
||||
.to_string(),
|
||||
},
|
||||
ModuleEntry {
|
||||
name: "join".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
||||
.to_string(),
|
||||
},
|
||||
ModuleEntry {
|
||||
name: "split".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
||||
.to_string(),
|
||||
},
|
||||
ModuleEntry {
|
||||
name: "countGraphemes".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
||||
.to_string(),
|
||||
},
|
||||
ModuleEntry {
|
||||
name: "foldGraphemes".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
||||
.to_string(),
|
||||
},
|
||||
],
|
||||
},
|
||||
Module {
|
||||
name: "Bool".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
|
||||
entries: vec![
|
||||
ModuleEntry {
|
||||
name: "isEq".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
||||
.to_string(),
|
||||
},
|
||||
ModuleEntry {
|
||||
name: "isNeq".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
||||
.to_string(),
|
||||
},
|
||||
],
|
||||
},
|
||||
Module {
|
||||
name: "Num".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
|
||||
entries: vec![
|
||||
ModuleEntry {
|
||||
name: "add".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
||||
.to_string(),
|
||||
},
|
||||
ModuleEntry {
|
||||
name: "sub".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
||||
.to_string(),
|
||||
},
|
||||
ModuleEntry {
|
||||
name: "mul".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
||||
.to_string(),
|
||||
},
|
||||
],
|
||||
},
|
||||
Module {
|
||||
name: "List".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
|
||||
entries: vec![],
|
||||
},
|
||||
Module {
|
||||
name: "Set".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
|
||||
entries: vec![],
|
||||
},
|
||||
Module {
|
||||
name: "Map".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
|
||||
entries: vec![],
|
||||
},
|
||||
Module {
|
||||
name: "Result".to_string(),
|
||||
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
|
||||
entries: vec![],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Make sure the directories exists
|
||||
fs::create_dir_all(format!("./build/{}/{}", package.name, package.version))?;
|
||||
|
||||
// Register handlebar template
|
||||
let mut handlebars = handlebars::Handlebars::new();
|
||||
assert!(handlebars
|
||||
.register_template_file("page", "./src/templates/page.hbs")
|
||||
.is_ok());
|
||||
|
||||
let markdown_options = pulldown_cmark::Options::empty();
|
||||
|
||||
// Write each package's module docs
|
||||
for module in &package.modules {
|
||||
// Convert module docs from markdown to html
|
||||
let docs_parser = pulldown_cmark::Parser::new_ext(&module.docs, markdown_options);
|
||||
let mut docs_html: String = String::with_capacity(module.docs.len() * 3 / 2);
|
||||
pulldown_cmark::html::push_html(&mut docs_html, docs_parser);
|
||||
|
||||
let template = Template {
|
||||
package_name: package.name.clone(),
|
||||
package_version: package.version.clone(),
|
||||
module_name: module.name.clone(),
|
||||
module_docs: docs_html,
|
||||
module_entries: module
|
||||
.entries
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|entry| {
|
||||
// Convert entry docs from markdown to html
|
||||
let entry_docs_parser =
|
||||
pulldown_cmark::Parser::new_ext(&entry.docs, markdown_options);
|
||||
let mut entry_docs_html: String =
|
||||
String::with_capacity(entry.docs.len() * 3 / 2);
|
||||
pulldown_cmark::html::push_html(&mut entry_docs_html, entry_docs_parser);
|
||||
|
||||
ModuleEntry {
|
||||
name: entry.name.clone(),
|
||||
docs: entry_docs_html,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
module_links: package
|
||||
.modules
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|module_link| TemplateLink {
|
||||
name: module_link.name.clone(),
|
||||
href: format!("./{}.html", module_link.name),
|
||||
classes: "".to_string(),
|
||||
entries: module_link
|
||||
.entries
|
||||
.into_iter()
|
||||
.map(|entry| TemplateLinkEntry { name: entry.name })
|
||||
.collect(),
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let handlebars_data = handlebars::to_json(&template);
|
||||
let mut output_file = fs::File::create(format!(
|
||||
"./build/{}/{}/{}.html",
|
||||
package.name, package.version, module.name
|
||||
))?;
|
||||
handlebars.render_to_write("page", &handlebars_data, &mut output_file)?;
|
||||
}
|
||||
|
||||
// Copy /static folder content to /build
|
||||
let copy_options = fs_extra::dir::CopyOptions {
|
||||
overwrite: true,
|
||||
skip_exist: false,
|
||||
buffer_size: 64000, //64kb
|
||||
copy_inside: false,
|
||||
content_only: true,
|
||||
depth: 0,
|
||||
};
|
||||
fs_extra::dir::copy("./src/static/", "./build", ©_options)?;
|
||||
println!("Docs generated at /build");
|
||||
Ok(())
|
||||
}
|
4
docs/src/static/favicon.svg
Normal file
4
docs/src/static/favicon.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<svg viewBox="0 0 52 53" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>polygon {fill: #5c0bff;}@media (prefers-color-scheme: dark) {polygon {fill: #7733ff;}} </style>
|
||||
<polygon points="0,0 23.8834,3.21052 37.2438,19.0101 45.9665,16.6324 50.5,22 45,22 44.0315,26.3689 26.4673,39.3424 27.4527,45.2132 17.655,53 23.6751,22.7086"/>
|
||||
</svg>
|
After Width: | Height: | Size: 335 B |
44
docs/src/static/search.js
Normal file
44
docs/src/static/search.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
(() => {
|
||||
let sidebar = document.getElementById("sidebar-nav");
|
||||
let searchBox = document.getElementById("module-search");
|
||||
|
||||
function search() {
|
||||
let text = searchBox.value.toLowerCase(); // Search is case-insensitive.
|
||||
|
||||
if (text === "") {
|
||||
// Un-hide everything
|
||||
sidebar.querySelectorAll(".sidebar-entry a").forEach((entry) => entry.classList.remove("hidden"));
|
||||
|
||||
// Re-hide all the sub-entries except for those of the current module
|
||||
let currentModuleName = document.querySelector('.module-name').textContent;
|
||||
|
||||
sidebar.querySelectorAll(".sidebar-entry").forEach((entry) => {
|
||||
let entryName = entry.querySelector('.sidebar-module-link').textContent;
|
||||
if (currentModuleName === entryName) return;
|
||||
entry.querySelectorAll(".sidebar-sub-entries a").forEach((subEntry) => subEntry.classList.add("hidden"));
|
||||
})
|
||||
} else {
|
||||
// First, show/hide all the sub-entries within each module (top-level functions etc.)
|
||||
sidebar.querySelectorAll(".sidebar-sub-entries a").forEach((entry) => {
|
||||
if (entry.textContent.toLowerCase().includes(text)) {
|
||||
entry.classList.remove("hidden");
|
||||
} else {
|
||||
entry.classList.add("hidden");
|
||||
}
|
||||
});
|
||||
|
||||
// Then, show/hide modules based on whether they match, or any of their sub-entries matched
|
||||
sidebar.querySelectorAll(".sidebar-module-link").forEach((entry) => {
|
||||
if (entry.textContent.toLowerCase().includes(text) || entry.parentNode.querySelectorAll(".sidebar-sub-entries a:not(.hidden)").length > 0) {
|
||||
entry.classList.remove("hidden");
|
||||
} else {
|
||||
entry.classList.add("hidden");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
searchBox.addEventListener("input", search);
|
||||
|
||||
search();
|
||||
})();
|
489
docs/src/static/styles.css
Normal file
489
docs/src/static/styles.css
Normal file
|
@ -0,0 +1,489 @@
|
|||
:root {
|
||||
--link-color: #612bde;
|
||||
--code-link-color: #5721d4;
|
||||
--text-color: #333333;
|
||||
--code-color: #222222;
|
||||
--code-bg-color: #eeeeee;
|
||||
--body-bg-color: #fdfdfd;
|
||||
--border-color: #e9e9e9;
|
||||
--faded-color: #4c4c4c;
|
||||
--monospace-font;
|
||||
--font-sans: -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif;
|
||||
--font-mono: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
--top-header-height: 67px;
|
||||
--sidebar-width: 280px;
|
||||
--top-bar-bg: #8257e5;
|
||||
--top-bar-fg: #ffffff;
|
||||
--nav-link-hover-color: #000000;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #972395;
|
||||
}
|
||||
|
||||
.logo {
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
.logo svg {
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
fill: var(--top-bar-fg);
|
||||
}
|
||||
|
||||
.logo:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.logo svg:hover {
|
||||
fill: var(--nav-link-hover-color);
|
||||
}
|
||||
|
||||
.pkg-full-name {
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 32px;
|
||||
margin: 0 8px;
|
||||
font-weight: normal;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.pkg-full-name a {
|
||||
padding-top: 12px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.pkg-and-logo {
|
||||
min-width: 0;
|
||||
/* necessary for text-overflow: ellipsis to work in descendants */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
background-color: var(--top-bar-bg);
|
||||
}
|
||||
|
||||
.pkg-and-logo a,
|
||||
.pkg-and-logo a:visited {
|
||||
color: var(--top-bar-fg);
|
||||
}
|
||||
|
||||
.pkg-and-logo a:hover {
|
||||
color: var(--nav-link-hover-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
min-width: 0;
|
||||
/* necessary for text-overflow: ellipsis to work in descendants */
|
||||
}
|
||||
|
||||
.search-button {
|
||||
flex-shrink: 0;
|
||||
/* always shrink the package name before these; they have a relatively constrained length */
|
||||
padding: 12px 18px;
|
||||
margin-right: 42px;
|
||||
display: none;
|
||||
/* only show this in the mobile view */
|
||||
}
|
||||
|
||||
.version {
|
||||
padding: 18px 10px;
|
||||
min-width: 48px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
body {
|
||||
display: grid;
|
||||
grid-template-columns: [before-sidebar] 1fr [sidebar] var(--sidebar-width) [main-content] fit-content(calc(1280px - var(--sidebar-width))) [end] 1fr;
|
||||
grid-template-rows: [top-header] var(--top-header-height) [above-footer] auto [footer] auto;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: var(--font-sans);
|
||||
color: var(--text-color);
|
||||
background-color: var(--body-bg-color);
|
||||
}
|
||||
|
||||
main {
|
||||
grid-column-start: main-content;
|
||||
grid-column-end: main-content;
|
||||
grid-row-start: above-footer;
|
||||
grid-row-end: above-footer;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
font-size: 18px;
|
||||
line-height: 1.85em;
|
||||
margin-top: 2px;
|
||||
padding: 48px;
|
||||
}
|
||||
|
||||
#sidebar-nav {
|
||||
grid-column-start: sidebar;
|
||||
grid-column-end: sidebar;
|
||||
grid-row-start: above-footer;
|
||||
grid-row-end: above-footer;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
padding-left: 56px;
|
||||
padding-top: 6px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.top-header-extension {
|
||||
grid-column-start: before-sidebar;
|
||||
grid-column-end: sidebar;
|
||||
grid-row-start: top-header;
|
||||
grid-row-end: top-header;
|
||||
background-color: var(--top-bar-bg);
|
||||
}
|
||||
|
||||
.top-header {
|
||||
grid-column-start: sidebar;
|
||||
grid-column-end: end;
|
||||
grid-row-start: top-header;
|
||||
grid-row-end: top-header;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
flex-grow: 1;
|
||||
box-sizing: border-box;
|
||||
font-family: var(--font-sans);
|
||||
font-size: 24px;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
/* necessary for text-overflow: ellipsis to work in descendants */
|
||||
}
|
||||
|
||||
.top-header-triangle {
|
||||
/* This used to be a clip-path, but Firefox on Android (at least as of early 2020)
|
||||
* rendered the page extremely slowly in that version. With this approach it's super fast.
|
||||
*/
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: var(--top-header-height) 0 0 48px;
|
||||
border-color: transparent transparent transparent var(--top-bar-bg);
|
||||
}
|
||||
|
||||
p {
|
||||
overflow-wrap: break-word;
|
||||
margin: 24px 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
grid-column-start: main-content;
|
||||
grid-column-end: main-content;
|
||||
grid-row-start: footer;
|
||||
grid-row-end: footer;
|
||||
max-width: var(--main-content-max-width);
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
footer p {
|
||||
display: inline-block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.content {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.sidebar-entry ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sidebar-entry a {
|
||||
box-sizing: border-box;
|
||||
min-height: 48px;
|
||||
min-width: 48px;
|
||||
padding: 12px 16px;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
.sidebar-sub-entries a {
|
||||
display: block;
|
||||
line-height: 24px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-left: 36px;
|
||||
}
|
||||
|
||||
.module-name {
|
||||
font-size: 56px;
|
||||
line-height: 1em;
|
||||
font-family: var(--font-mono);
|
||||
font-weight: bold;
|
||||
margin-top: 18px;
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.module-name a,
|
||||
.module-name a:visited {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.sidebar-module-link {
|
||||
box-sizing: border-box;
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
font-family: var(--font-mono);
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 8px 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
a,
|
||||
a:visited {
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 32px;
|
||||
margin: 48px 0 24px 0;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.type-def {
|
||||
font-size: 24px;
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
.code-snippet {
|
||||
padding: 12px 16px;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
font-family: var(--font-mono);
|
||||
background-color: var(--code-bg-color);
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: var(--font-mono);
|
||||
color: var(--code-color);
|
||||
background-color: var(--code-bg-color);
|
||||
padding: 2px 8px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
code a {
|
||||
color: var(--code-link-color);
|
||||
}
|
||||
|
||||
code a:visited {
|
||||
color: var(--code-link-color);
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 36px 0;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--code-bg-color);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
pre code {
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
/* Use !important to win all specificity fights. */
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#module-search:placeholder-shown {
|
||||
padding: 0;
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
#module-search,
|
||||
#module-search:focus {
|
||||
opacity: 1;
|
||||
padding: 12px 16px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
/* Show the "Search" label link when the text input has a placeholder */
|
||||
#module-search:placeholder-shown+#search-link {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Hide the "Search" label link when the text input has focus */
|
||||
#module-search:focus+#search-link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#module-search {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--code-bg-color);
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
font-size: 18px;
|
||||
line-height: 18px;
|
||||
margin-top: 6px;
|
||||
border: none;
|
||||
color: var(--faded-color);
|
||||
background-color: var(--code-bg-color);
|
||||
font-family: var(--font-serif);
|
||||
}
|
||||
|
||||
#module-search::placeholder {
|
||||
color: var(--faded-color);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#search-link {
|
||||
box-sizing: border-box;
|
||||
display: none;
|
||||
align-items: center;
|
||||
font-size: 18px;
|
||||
line-height: 18px;
|
||||
padding: 12px 16px;
|
||||
height: 48px;
|
||||
cursor: pointer;
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
#search-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--body-bg-color: #303030;
|
||||
--code-bg-color: #393939;
|
||||
--border-color: #555555;
|
||||
--code-color: #eeeeee;
|
||||
--text-color: #cccccc;
|
||||
--logo-solid: #777777;
|
||||
--faded-color: #bbbbbb;
|
||||
--link-color: #c5a8ff;
|
||||
--code-link-color: #b894ff;
|
||||
--top-bar-bg: #6845b9;
|
||||
--top-bar-fg: #eeeeee;
|
||||
}
|
||||
|
||||
html {
|
||||
scrollbar-color: #444444 #2f2f2f;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-device-width: 480px) {
|
||||
.search-button {
|
||||
display: block;
|
||||
/* This is only visible in mobile. */
|
||||
}
|
||||
|
||||
.top-header {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.pkg-full-name {
|
||||
margin-left: 8px;
|
||||
margin-right: 12px;
|
||||
font-size: 24px;
|
||||
padding-bottom: 14px;
|
||||
}
|
||||
|
||||
.pkg-full-name a {
|
||||
vertical-align: middle;
|
||||
padding: 18px 0;
|
||||
}
|
||||
|
||||
.logo {
|
||||
padding-left: 2px;
|
||||
width: 50px;
|
||||
height: 54px;
|
||||
}
|
||||
|
||||
.version {
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
font-size: 18px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.module-name {
|
||||
font-size: 36px;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
max-width: calc(100% - 18px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 18px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin: 0;
|
||||
min-width: 320px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
margin-top: 0;
|
||||
padding-left: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#sidebar-heading {
|
||||
font-size: 24px;
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.top-header {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.content {
|
||||
/* Display the sidebar below <main> without affecting tab index */
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
}
|
65
docs/src/templates/page.hbs
Normal file
65
docs/src/templates/page.hbs
Normal file
|
@ -0,0 +1,65 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>The Roc Programming Language</title>
|
||||
<meta name="description" content="A language for building fast, reliable software.">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link rel="icon" href="/favicon.svg">
|
||||
<script type="text/javascript" src="/search.js" defer></script>
|
||||
<link rel="stylesheet" href="/styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav id="sidebar-nav">
|
||||
<input id="module-search" aria-labelledby="search-link" type="text" placeholder="Search" />
|
||||
<label for="module-search" id="search-link">Search</label>
|
||||
<div class="module-links">
|
||||
{{#each module_links as |link| ~}}
|
||||
<div class="sidebar-entry">
|
||||
<a class="sidebar-module-link" href="{{link.href}}">{{link.name}}</a>
|
||||
<div class="sidebar-sub-entries">
|
||||
{{#each link.entries as |entry| ~}}
|
||||
<a class="{{link.classes}}" href="{{link.href}}#{{entry.name}}">{{entry.name}}</a></li>
|
||||
{{/each~}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each~}}
|
||||
</div>
|
||||
</nav>
|
||||
<div class="top-header-extension">
|
||||
<!-- if the window gets big, this extends the purple bar on the top header to the left edge of the window -->
|
||||
</div>
|
||||
<header class="top-header">
|
||||
<nav class="pkg-and-logo">
|
||||
<a class="logo" href="/" aria-labelledby="logo-link">
|
||||
<svg viewBox="0 -6 51 58" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="logo-link" role="img">
|
||||
<title id="logo-link">Return to Roc packages</title>
|
||||
<polygon role="presentation" points="0,0 23.8834,3.21052 37.2438,19.0101 45.9665,16.6324 50.5,22 45,22 44.0315,26.3689 26.4673,39.3424 27.4527,45.2132 17.655,53 23.6751,22.7086" />
|
||||
</svg>
|
||||
</a>
|
||||
<h1 class="pkg-full-name">
|
||||
<a href="/{{package_name}}">{{package_name}}</a>
|
||||
</h1>
|
||||
<a class="version" href="/{{package_name}}/{{package_version}}">{{package_version}}</a>
|
||||
</nav>
|
||||
<div class="top-header-triangle">
|
||||
<!-- if the window gets big, this extends the purple bar on the top header to the left edge of the window -->
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<h2 class="module-name"><a href="#">{{module_name}}</a></h2>
|
||||
{{{module_docs}}}
|
||||
{{#each module_entries as |entry| ~}}
|
||||
<h3 id="{{entry.name}}"><a href="#{{entry.name}}">{{entry.name}}</a></h3>
|
||||
{{{entry.docs}}}
|
||||
{{/each~}}
|
||||
</main>
|
||||
<footer>
|
||||
<p>Made by people who like to make nice things.</p>
|
||||
<p>© 2020-present</p>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue