Merge remote-tracking branch 'origin/main' into roc-dev-inline-expects

This commit is contained in:
Folkert 2022-10-09 18:46:57 +02:00
commit e62ab00c65
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
275 changed files with 4038 additions and 2432 deletions

View file

@ -6,7 +6,7 @@ name: Nightly Release macOS x86_64
env: env:
ZIG_VERSION: 0.9.1 ZIG_VERSION: 0.9.1
LLVM_SYS_130_PREFIX: /usr/local/opt/llvm LLVM_SYS_130_PREFIX: /usr/local/opt/llvm@13
jobs: jobs:
test-build-upload: test-build-upload:

View file

@ -28,7 +28,7 @@ jobs:
run: ls | grep "roc_nightly.*tar\.gz" | xargs tar -xzvf run: ls | grep "roc_nightly.*tar\.gz" | xargs tar -xzvf
- name: test roc hello world - name: test roc hello world
run: ./roc examples/hello-world/main.roc run: ./roc examples/helloWorld.roc

View file

@ -41,7 +41,7 @@ jobs:
run: ls | grep "roc_nightly.*tar\.gz" | xargs tar -xzvf run: ls | grep "roc_nightly.*tar\.gz" | xargs tar -xzvf
- name: test roc hello world - name: test roc hello world
run: ./roc examples/hello-world/main.roc run: ./roc examples/helloWorld.roc

View file

@ -28,13 +28,7 @@ sh <(curl -L https://nixos.org/nix/install) --no-daemon
sh <(curl -L https://nixos.org/nix/install) --daemon sh <(curl -L https://nixos.org/nix/install) --daemon
``` ```
Open a new terminal and install nixFlakes in your environment: Open a new terminal and edit either `~/.config/nix/nix.conf` or `/etc/nix/nix.conf` and add:
```sh
nix-env -iA nixpkgs.nixFlakes
```
Edit either `~/.config/nix/nix.conf` or `/etc/nix/nix.conf` and add:
```text ```text
experimental-features = nix-command flakes experimental-features = nix-command flakes

180
Cargo.lock generated
View file

@ -75,15 +75,6 @@ dependencies = [
"pkg-config", "pkg-config",
] ]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "approx" name = "approx"
version = "0.4.0" version = "0.4.0"
@ -253,34 +244,13 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 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 0.12.4",
]
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.10.2" version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
dependencies = [ dependencies = [
"generic-array 0.14.5", "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]] [[package]]
@ -301,12 +271,6 @@ version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]] [[package]]
name = "bytecheck" name = "bytecheck"
version = "0.6.8" version = "0.6.8"
@ -620,10 +584,10 @@ dependencies = [
[[package]] [[package]]
name = "confy" name = "confy"
version = "0.4.0" version = "0.5.0"
source = "git+https://github.com/rust-cli/confy#c6b62039281b8643539b436440bcea1b0d634bc7" source = "git+https://github.com/rust-cli/confy#fd069f062aa3373c846f0d8c6e3b5e2a5cd0096b"
dependencies = [ dependencies = [
"directories-next", "directories",
"serde", "serde",
"serde_yaml", "serde_yaml",
"thiserror", "thiserror",
@ -1023,7 +987,7 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ccfd8c0ee4cce11e45b3fd6f9d5e69e0cc62912aa6a0cb1bf4617b0eba5a12f" checksum = "2ccfd8c0ee4cce11e45b3fd6f9d5e69e0cc62912aa6a0cb1bf4617b0eba5a12f"
dependencies = [ dependencies = [
"generic-array 0.14.5", "generic-array",
"typenum", "typenum",
] ]
@ -1129,22 +1093,13 @@ version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
"generic-array 0.12.4",
]
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.10.3" version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [ dependencies = [
"block-buffer 0.10.2", "block-buffer",
"crypto-common", "crypto-common",
] ]
@ -1160,13 +1115,13 @@ dependencies = [
] ]
[[package]] [[package]]
name = "directories-next" name = "directories"
version = "2.0.0" version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" checksum = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 0.1.10",
"dirs-sys-next", "dirs-sys",
] ]
[[package]] [[package]]
@ -1179,6 +1134,17 @@ dependencies = [
"dirs-sys-next", "dirs-sys-next",
] ]
[[package]]
name = "dirs-sys"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]] [[package]]
name = "dirs-sys-next" name = "dirs-sys-next"
version = "0.1.2" version = "0.1.2"
@ -1231,9 +1197,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]] [[package]]
name = "dunce" name = "dunce"
version = "1.0.2" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541" checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c"
[[package]] [[package]]
name = "dynasm" name = "dynasm"
@ -1367,12 +1333,6 @@ dependencies = [
"str-buf", "str-buf",
] ]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]] [[package]]
name = "fallible-iterator" name = "fallible-iterator"
version = "0.2.0" version = "0.2.0"
@ -1564,15 +1524,6 @@ dependencies = [
"cfg-if 0.1.10", "cfg-if 0.1.10",
] ]
[[package]]
name = "generic-array"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
dependencies = [
"typenum",
]
[[package]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.14.5" version = "0.14.5"
@ -2438,6 +2389,16 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09f1f8e5676e1a1f2ee8b21f38238e1243c827531c9435624c7bfb305102cee4" checksum = "09f1f8e5676e1a1f2ee8b21f38238e1243c827531c9435624c7bfb305102cee4"
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]] [[package]]
name = "num-derive" name = "num-derive"
version = "0.3.3" version = "0.3.3"
@ -2606,12 +2567,6 @@ version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]] [[package]]
name = "ordered-float" name = "ordered-float"
version = "3.0.0" version = "3.0.0"
@ -2636,6 +2591,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]] [[package]]
name = "owned_ttf_parser" name = "owned_ttf_parser"
version = "0.15.0" version = "0.15.0"
@ -2800,9 +2761,9 @@ dependencies = [
[[package]] [[package]]
name = "pest_derive" name = "pest_derive"
version = "2.1.0" version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" checksum = "502b62a6d0245378b04ffe0a7fb4f4419a4815fce813bd8a0ec89a56e07d67b1"
dependencies = [ dependencies = [
"pest", "pest",
"pest_generator", "pest_generator",
@ -2810,9 +2771,9 @@ dependencies = [
[[package]] [[package]]
name = "pest_generator" name = "pest_generator"
version = "2.1.3" version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" checksum = "451e629bf49b750254da26132f1a5a9d11fd8a95a3df51d15c4abd1ba154cb6c"
dependencies = [ dependencies = [
"pest", "pest",
"pest_meta", "pest_meta",
@ -2823,13 +2784,13 @@ dependencies = [
[[package]] [[package]]
name = "pest_meta" name = "pest_meta"
version = "2.1.3" version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" checksum = "bcec162c71c45e269dfc3fc2916eaeb97feab22993a21bcce4721d08cd7801a6"
dependencies = [ dependencies = [
"maplit", "once_cell",
"pest", "pest",
"sha-1", "sha1 0.10.4",
] ]
[[package]] [[package]]
@ -3467,8 +3428,6 @@ dependencies = [
"roc_tracing", "roc_tracing",
"serial_test", "serial_test",
"signal-hook", "signal-hook",
"strum",
"strum_macros",
"target-lexicon", "target-lexicon",
"tempfile", "tempfile",
"ven_pretty", "ven_pretty",
@ -4450,18 +4409,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "sha-1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
dependencies = [
"block-buffer 0.7.3",
"digest 0.8.1",
"fake-simd",
"opaque-debug",
]
[[package]] [[package]]
name = "sha1" name = "sha1"
version = "0.6.1" version = "0.6.1"
@ -4471,6 +4418,17 @@ dependencies = [
"sha1_smol", "sha1_smol",
] ]
[[package]]
name = "sha1"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "006769ba83e921b3085caa8334186b00cf92b4cb1a6cf4632fbccc8eff5c7549"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",
"digest",
]
[[package]] [[package]]
name = "sha1_smol" name = "sha1_smol"
version = "1.0.0" version = "1.0.0"
@ -4485,7 +4443,7 @@ checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"cpufeatures", "cpufeatures",
"digest 0.10.3", "digest",
] ]
[[package]] [[package]]
@ -4728,7 +4686,7 @@ dependencies = [
"serde", "serde",
"serde_derive", "serde_derive",
"serde_json", "serde_json",
"sha1", "sha1 0.6.1",
"syn", "syn",
] ]
@ -4767,9 +4725,9 @@ checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
[[package]] [[package]]
name = "strum_macros" name = "strum_macros"
version = "0.24.2" version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4faebde00e8ff94316c01800f9054fd2ba77d30d9e922541913051d1d978918b" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@ -5106,9 +5064,9 @@ dependencies = [
[[package]] [[package]]
name = "tracing-core" name = "tracing-core"
version = "0.1.29" version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"valuable", "valuable",
@ -5127,12 +5085,12 @@ dependencies = [
[[package]] [[package]]
name = "tracing-subscriber" name = "tracing-subscriber"
version = "0.3.15" version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
dependencies = [ dependencies = [
"ansi_term",
"matchers", "matchers",
"nu-ansi-term",
"once_cell", "once_cell",
"regex", "regex",
"sharded-slab", "sharded-slab",
@ -5155,7 +5113,7 @@ version = "1.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 0.1.10",
"rand", "rand",
"static_assertions", "static_assertions",
] ]

View file

@ -57,9 +57,10 @@ members = [
"crates/wasi-libc-sys", "crates/wasi-libc-sys",
] ]
exclude = [ exclude = [
# Examples sometimes have Rust hosts in their platforms. The compiler should ignore those.
"examples",
"ci/bench-runner", "ci/bench-runner",
# Examples sometimes have Rust hosts in their platforms. The compiler should ignore those.
"crates/cli_testing_examples",
"examples",
# Ignore building these normally. They are only imported by tests. # Ignore building these normally. They are only imported by tests.
# The tests will still correctly build them. # The tests will still correctly build them.
"crates/cli_utils", "crates/cli_utils",

View file

@ -50,7 +50,7 @@ install-zig-llvm-valgrind:
copy-dirs: copy-dirs:
FROM +install-zig-llvm-valgrind FROM +install-zig-llvm-valgrind
COPY --dir crates examples Cargo.toml Cargo.lock version.txt www ./ COPY --dir crates Cargo.toml Cargo.lock version.txt www ./
# compile everything needed for benchmarks and output a self-contained dir from which benchmarks can be run. # compile everything needed for benchmarks and output a self-contained dir from which benchmarks can be run.
prep-bench-folder: prep-bench-folder:
@ -60,11 +60,11 @@ prep-bench-folder:
ARG BENCH_SUFFIX=branch ARG BENCH_SUFFIX=branch
RUN cargo criterion -V RUN cargo criterion -V
RUN --mount=type=cache,target=$SCCACHE_DIR cd crates/cli && cargo criterion --no-run RUN --mount=type=cache,target=$SCCACHE_DIR cd crates/cli && cargo criterion --no-run
RUN mkdir -p bench-folder/crates/cli_testing_examples/benchmarks
RUN mkdir -p bench-folder/crates/compiler/builtins/bitcode/src RUN mkdir -p bench-folder/crates/compiler/builtins/bitcode/src
RUN mkdir -p bench-folder/target/release/deps RUN mkdir -p bench-folder/target/release/deps
RUN mkdir -p bench-folder/examples/benchmarks RUN cp crates/cli_testing_examples/benchmarks/*.roc bench-folder/crates/cli_testing_examples/benchmarks/
RUN cp examples/benchmarks/*.roc bench-folder/examples/benchmarks/ RUN cp -r crates/cli_testing_examples/benchmarks/platform bench-folder/crates/cli_testing_examples/benchmarks/
RUN cp -r examples/benchmarks/platform bench-folder/examples/benchmarks/
RUN cp crates/compiler/builtins/bitcode/src/str.zig bench-folder/crates/compiler/builtins/bitcode/src RUN cp crates/compiler/builtins/bitcode/src/str.zig bench-folder/crates/compiler/builtins/bitcode/src
RUN cp target/release/roc bench-folder/target/release RUN cp target/release/roc bench-folder/target/release
# copy the most recent time bench to bench-folder # copy the most recent time bench to bench-folder

View file

@ -115,7 +115,7 @@ Create a new file called `Hello.roc` and put this inside it:
```coffee ```coffee
app "hello" app "hello"
packages { pf: "examples/interactive/cli-platform/main.roc" } packages { pf: "examples/cli/cli-platform/main.roc" }
imports [pf.Stdout, pf.Program] imports [pf.Stdout, pf.Program]
provides [main] to pf provides [main] to pf
@ -124,8 +124,8 @@ main = Stdout.line "I'm a Roc application!" |> Program.quick
> **NOTE:** This assumes you've put Hello.roc in the root directory of the Roc > **NOTE:** This assumes you've put Hello.roc in the root directory of the Roc
> source code. If you'd like to put it somewhere else, you'll need to replace > source code. If you'd like to put it somewhere else, you'll need to replace
> `"examples/interactive/cli-platform/main.roc"` with the path to the > `"examples/cli/cli-platform/main.roc"` with the path to the
> `examples/interactive/cli-platform/main.roc` file in that source code. In the future, > `examples/cli/cli-platform/main.roc` file in that source code. In the future,
> Roc will have the tutorial built in, and this aside will no longer be > Roc will have the tutorial built in, and this aside will no longer be
> necessary! > necessary!
@ -1273,7 +1273,7 @@ Let's take a closer look at the part of `Hello.roc` above `main`:
```coffee ```coffee
app "hello" app "hello"
packages { pf: "examples/interactive/cli-platform/main.roc" } packages { pf: "examples/cli/cli-platform/main.roc" }
imports [pf.Stdout, pf.Program] imports [pf.Stdout, pf.Program]
provides main to pf provides main to pf
``` ```
@ -1291,14 +1291,14 @@ without running it by running `roc build Hello.roc`.
The remaining lines all involve the *platform* this application is built on: The remaining lines all involve the *platform* this application is built on:
```coffee ```coffee
packages { pf: "examples/interactive/cli-platform/main.roc" } packages { pf: "examples/cli/cli-platform/main.roc" }
imports [pf.Stdout, pf.Program] imports [pf.Stdout, pf.Program]
provides main to pf provides main to pf
``` ```
The `packages { pf: "examples/interactive/cli-platform/main.roc" }` part says two things: The `packages { pf: "examples/cli/cli-platform/main.roc" }` part says two things:
- We're going to be using a *package* (that is, a collection of modules) called `"examples/interactive/cli-platform/main.roc"` - We're going to be using a *package* (that is, a collection of modules) called `"examples/cli/cli-platform/main.roc"`
- We're going to name that package `pf` so we can refer to it more concisely in the future. - We're going to name that package `pf` so we can refer to it more concisely in the future.
The `imports [pf.Stdout, pf.Program]` line says that we want to import the `Stdout` and `Program` modules The `imports [pf.Stdout, pf.Program]` line says that we want to import the `Stdout` and `Program` modules
@ -1320,16 +1320,16 @@ which effectively makes it a simple Roc program.
When we write `imports [pf.Stdout, pf.Program]`, it specifies that the `Stdout` When we write `imports [pf.Stdout, pf.Program]`, it specifies that the `Stdout`
and `Program` modules come from the `pf` package. and `Program` modules come from the `pf` package.
Since `pf` was the name we chose for the `examples/interactive/cli-platform/main.roc` Since `pf` was the name we chose for the `examples/cli/cli-platform/main.roc`
package (when we wrote `packages { pf: "examples/interactive/cli-platform/main.roc" }`), package (when we wrote `packages { pf: "examples/cli/cli-platform/main.roc" }`),
this `imports` line tells the Roc compiler that when we call `Stdout.line`, it this `imports` line tells the Roc compiler that when we call `Stdout.line`, it
should look for that `line` function in the `Stdout` module of the should look for that `line` function in the `Stdout` module of the
`examples/interactive/cli-platform/main.roc` package. `examples/cli/cli-platform/main.roc` package.
## Tasks ## Tasks
Tasks are technically not part of the Roc language, but they're very common in Tasks are technically not part of the Roc language, but they're very common in
platforms. Let's use the CLI platform in `examples/interactive/cli-platform/main.roc` as an example! platforms. Let's use the CLI platform in `examples/cli/cli-platform/main.roc` as an example!
In the CLI platform, we have four operations we can do: In the CLI platform, we have four operations we can do:
@ -1344,7 +1344,7 @@ First, let's do a basic "Hello World" using the tutorial app.
```coffee ```coffee
app "cli-tutorial" app "cli-tutorial"
packages { pf: "examples/interactive/cli-platform/main.roc" } packages { pf: "examples/cli/cli-platform/main.roc" }
imports [pf.Stdout, pf.Program] imports [pf.Stdout, pf.Program]
provides [main] to pf provides [main] to pf
@ -1382,7 +1382,7 @@ Let's change `main` to read a line from `stdin`, and then print it back out agai
```swift ```swift
app "cli-tutorial" app "cli-tutorial"
packages { pf: "examples/interactive/cli-platform/main.roc" } packages { pf: "examples/cli/cli-platform/main.roc" }
imports [pf.Stdout, pf.Stdin, pf.Task, pf.Program] imports [pf.Stdout, pf.Stdin, pf.Task, pf.Program]
provides [main] to pf provides [main] to pf
@ -1434,7 +1434,7 @@ This works, but we can make it a little nicer to read. Let's change it to the fo
```haskell ```haskell
app "cli-tutorial" app "cli-tutorial"
packages { pf: "examples/interactive/cli-platform/main.roc" } packages { pf: "examples/cli/cli-platform/main.roc" }
imports [pf.Stdout, pf.Stdin, pf.Task.{ await }, pf.Program] imports [pf.Stdout, pf.Stdin, pf.Task.{ await }, pf.Program]
provides [main] to pf provides [main] to pf

View file

@ -227,9 +227,10 @@ fn calc_hashes_for_folder(benches_path_str: &str) -> HashMap<String, String> {
} }
fn check_if_bench_executables_changed() -> bool { fn check_if_bench_executables_changed() -> bool {
let bench_folder_str = "/examples/benchmarks/"; let bench_folder_str = "/crates/cli_testing_examples/benchmarks/";
let main_benches_path_str = [BENCH_FOLDER_MAIN, bench_folder_str].join(""); let main_benches_path_str = [BENCH_FOLDER_MAIN, bench_folder_str].join("");
let main_bench_hashes = calc_hashes_for_folder(&main_benches_path_str); let main_bench_hashes = calc_hashes_for_folder(&main_benches_path_str);
let branch_benches_path_str = [BENCH_FOLDER_BRANCH, bench_folder_str].join(""); let branch_benches_path_str = [BENCH_FOLDER_BRANCH, bench_folder_str].join("");

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
cp target/release/roc ./roc # to be able to exclude "target" later in the tar command cp target/release/roc ./roc # to be able to exclude "target" later in the tar command
cp -r target/release/lib ./lib cp -r target/release/lib ./lib
tar -czvf $1 --exclude="target" --exclude="zig-cache" roc lib LICENSE LEGAL_DETAILS examples/hello-world crates/roc_std tar -czvf $1 --exclude="target" --exclude="zig-cache" roc lib LICENSE LEGAL_DETAILS examples/helloWorld.roc examples/cli crates/roc_std

View file

@ -100,8 +100,6 @@ indoc = "1.0.7"
serial_test = "0.9.0" serial_test = "0.9.0"
criterion = { git = "https://github.com/Anton-4/criterion.rs"} criterion = { git = "https://github.com/Anton-4/criterion.rs"}
cli_utils = { path = "../cli_utils" } cli_utils = { path = "../cli_utils" }
strum = "0.24.0"
strum_macros = "0.24"
once_cell = "1.14.0" once_cell = "1.14.0"
parking_lot = "0.12" parking_lot = "0.12"

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
app "type-error" app "type-error"
packages { pf: "../../../../examples/interactive/cli-platform/main.roc" } packages { pf: "../../../../examples/cli/cli-platform/main.roc" }
imports [pf.Stdout.{ line }, pf.Task.{ await }, pf.Program] imports [pf.Stdout.{ line }, pf.Task.{ await }, pf.Program]
provides [main] to pf provides [main] to pf

View file

@ -0,0 +1,6 @@
*.dSYM
libhost.a
libapp.so
dynhost
preprocessedhost
metadata

View file

@ -16,7 +16,7 @@ name = "host"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
roc_std = { path = "../../../crates/roc_std" } roc_std = { path = "../../../../crates/roc_std" }
libc = "0.2" libc = "0.2"
[workspace] [workspace]

View file

@ -3,12 +3,12 @@
To run this website, first compile either of these identical apps: To run this website, first compile either of these identical apps:
```bash ```bash
# Option A: Compile examples/platform-switching/rocLovesWebAssembly.roc # Option A: Compile crates/cli_testing_examples/platform-switching/rocLovesWebAssembly.roc
cargo run -- build --target=wasm32 examples/platform-switching/rocLovesWebAssembly.roc cargo run -- build --target=wasm32 crates/cli_testing_examples/platform-switching/rocLovesWebAssembly.roc
# Option B: Compile examples/platform-switching/main.roc with `pf: "web-assembly-platform/main.roc"` and move the result # Option B: Compile crates/cli_testing_examples/platform-switching/main.roc with `pf: "web-assembly-platform/main.roc"` and move the result
cargo run -- build --target=wasm32 examples/platform-switching/main.roc cargo run -- build --target=wasm32 crates/cli_testing_examples/platform-switching/main.roc
(cd examples/platform-switching && mv rocLovesPlatforms.wasm web-assembly-platform/rocLovesWebAssembly.wasm) (cd crates/cli_testing_examples/platform-switching && mv rocLovesPlatforms.wasm web-assembly-platform/rocLovesWebAssembly.wasm)
``` ```
Then `cd` into the website directory Then `cd` into the website directory
@ -16,7 +16,7 @@ and run any web server that can handle WebAssembly.
For example, with `http-server`: For example, with `http-server`:
```bash ```bash
cd examples/platform-switching/web-assembly-platform cd crates/cli_testing_examples/platform-switching/web-assembly-platform
npm install -g http-server npm install -g http-server
http-server http-server
``` ```

File diff suppressed because one or more lines are too long

View file

@ -22,7 +22,7 @@ pub struct Out {
pub status: ExitStatus, pub status: ExitStatus,
} }
pub fn run_roc<I, S>(args: I, stdin_vals: &[&str]) -> Out pub fn run_roc<I, S>(args: I, stdin_vals: &[&str], extra_env: &[(&str, &str)]) -> Out
where where
I: IntoIterator<Item = S>, I: IntoIterator<Item = S>,
S: AsRef<OsStr>, S: AsRef<OsStr>,
@ -62,7 +62,7 @@ where
} }
} }
run_with_stdin(&roc_binary_path, args, stdin_vals) run_with_stdin_and_env(&roc_binary_path, args, stdin_vals, extra_env)
} }
pub fn run_glue<I, S>(args: I) -> Out pub fn run_glue<I, S>(args: I) -> Out
@ -118,6 +118,19 @@ pub fn strip_colors(str: &str) -> String {
} }
pub fn run_with_stdin<I, S>(path: &Path, args: I, stdin_vals: &[&str]) -> Out pub fn run_with_stdin<I, S>(path: &Path, args: I, stdin_vals: &[&str]) -> Out
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
run_with_stdin_and_env(path, args, stdin_vals, &[])
}
pub fn run_with_stdin_and_env<I, S>(
path: &Path,
args: I,
stdin_vals: &[&str],
extra_env: &[(&str, &str)],
) -> Out
where where
I: IntoIterator<Item = S>, I: IntoIterator<Item = S>,
S: AsRef<OsStr>, S: AsRef<OsStr>,
@ -128,6 +141,10 @@ where
cmd.arg(arg); cmd.arg(arg);
} }
for (k, v) in extra_env {
cmd.env(k, v);
}
let mut child = cmd let mut child = cmd
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
@ -364,20 +381,31 @@ pub fn root_dir() -> PathBuf {
path path
} }
// start the dir with crates/cli_testing_examples
#[allow(dead_code)] #[allow(dead_code)]
pub fn examples_dir(dir_name: &str) -> PathBuf { pub fn cli_testing_dir(dir_name: &str) -> PathBuf {
let mut path = root_dir(); let mut path = root_dir();
// Descend into examples/{dir_name} // Descend into examples/{dir_name}
path.push("examples"); path.push("crates");
path.push("cli_testing_examples");
path.extend(dir_name.split("/")); // Make slashes cross-target path.extend(dir_name.split("/")); // Make slashes cross-target
path path
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn example_file(dir_name: &str, file_name: &str) -> PathBuf { pub fn dir_path_from_root(dir_name: &str) -> PathBuf {
let mut path = examples_dir(dir_name); let mut path = root_dir();
path.extend(dir_name.split("/")); // Make slashes cross-target
path
}
#[allow(dead_code)]
pub fn file_path_from_root(dir_name: &str, file_name: &str) -> PathBuf {
let mut path = dir_path_from_root(dir_name);
path.push(file_name); path.push(file_name);

View file

@ -374,7 +374,7 @@ pub fn build_zig_host_wasm32(
"c", "c",
"-target", "-target",
zig_target, zig_target,
// "-femit-llvm-ir=/home/folkertdev/roc/roc/examples/benchmarks/platform/host.ll", // "-femit-llvm-ir=/home/folkertdev/roc/roc/crates/cli_testing_examples/benchmarks/platform/host.ll",
"-fPIC", "-fPIC",
"--strip", "--strip",
]; ];
@ -635,6 +635,7 @@ pub fn rebuild_host(
} else if cargo_host_src.exists() { } else if cargo_host_src.exists() {
// Compile and link Cargo.toml, if it exists // Compile and link Cargo.toml, if it exists
let cargo_dir = host_input_path.parent().unwrap(); let cargo_dir = host_input_path.parent().unwrap();
let cargo_out_dir = cargo_dir.join("target").join( let cargo_out_dir = cargo_dir.join("target").join(
if matches!(opt_level, OptLevel::Optimize | OptLevel::Size) { if matches!(opt_level, OptLevel::Optimize | OptLevel::Size) {
"release" "release"
@ -1215,7 +1216,7 @@ fn link_wasm32(
"-O", "-O",
"ReleaseSmall", "ReleaseSmall",
// useful for debugging // useful for debugging
// "-femit-llvm-ir=/home/folkertdev/roc/roc/examples/benchmarks/platform/host.ll", // "-femit-llvm-ir=/home/folkertdev/roc/roc/crates/cli_testing_examples/benchmarks/platform/host.ll",
]) ])
.spawn()?; .spawn()?;

View file

@ -15,7 +15,7 @@ lazy_static = "1.4.0"
[build-dependencies] [build-dependencies]
# dunce can be removed once ziglang/zig#5109 is fixed # dunce can be removed once ziglang/zig#5109 is fixed
dunce = "1.0.2" dunce = "1.0.3"
[target.'cfg(target_os = "macos")'.build-dependencies] [target.'cfg(target_os = "macos")'.build-dependencies]
tempfile = "3.2.0" tempfile = "3.2.0"

View file

@ -93,20 +93,6 @@ pub const RocList = extern struct {
return (ptr - 1)[0] == utils.REFCOUNT_ONE; return (ptr - 1)[0] == utils.REFCOUNT_ONE;
} }
pub fn allocate(
alignment: u32,
length: usize,
element_size: usize,
) RocList {
const data_bytes = length * element_size;
return RocList{
.bytes = utils.allocateWithRefcount(data_bytes, alignment),
.length = length,
.capacity = length,
};
}
pub fn makeUniqueExtra(self: RocList, alignment: u32, element_width: usize, update_mode: UpdateMode) RocList { pub fn makeUniqueExtra(self: RocList, alignment: u32, element_width: usize, update_mode: UpdateMode) RocList {
if (update_mode == .InPlace) { if (update_mode == .InPlace) {
return self; return self;
@ -140,11 +126,117 @@ pub const RocList = extern struct {
return new_list; return new_list;
} }
// We follow roughly the [fbvector](https://github.com/facebook/folly/blob/main/folly/docs/FBVector.md) when it comes to growing a RocList.
// Here is [their growth strategy](https://github.com/facebook/folly/blob/3e0525988fd444201b19b76b390a5927c15cb697/folly/FBVector.h#L1128) for push_back:
//
// (1) initial size
// Instead of growing to size 1 from empty, fbvector allocates at least
// 64 bytes. You may still use reserve to reserve a lesser amount of
// memory.
// (2) 1.5x
// For medium-sized vectors, the growth strategy is 1.5x. See the docs
// for details.
// This does not apply to very small or very large fbvectors. This is a
// heuristic.
//
// In our case, we exposed allocate and reallocate, which will use a smart growth stategy.
// We also expose allocateExact and reallocateExact for case where a specific number of elements is requested.
// calculateCapacity should only be called in cases the list will be growing.
// requested_length should always be greater than old_capacity.
inline fn calculateCapacity(
old_capacity: usize,
requested_length: usize,
element_width: usize,
) usize {
// TODO: there are two adjustments that would likely lead to better results for Roc.
// 1. Deal with the fact we allocate an extra u64 for refcount.
// This may lead to allocating page size + 8 bytes.
// That could mean allocating an entire page for 8 bytes of data which isn't great.
// 2. Deal with the fact that we can request more than 1 element at a time.
// fbvector assumes just appending 1 element at a time when using this algorithm.
// As such, they will generally grow in a way that should better match certain memory multiple.
// This is also the normal case for roc, but we could also grow by a much larger amount.
// We may want to round to multiples of 2 or something similar.
var new_capacity: usize = 0;
if (element_width == 0) {
return requested_length;
} else if (old_capacity == 0) {
new_capacity = 64 / element_width;
} else if (old_capacity < 4096 / element_width) {
new_capacity = old_capacity * 2;
} else if (old_capacity > 4096 * 32 / element_width) {
new_capacity = old_capacity * 2;
} else {
new_capacity = (old_capacity * 3 + 1) / 2;
}
return @maximum(new_capacity, requested_length);
}
pub fn allocate(
alignment: u32,
length: usize,
element_width: usize,
) RocList {
if (length == 0) {
return empty();
}
const capacity = calculateCapacity(0, length, element_width);
const data_bytes = capacity * element_width;
return RocList{
.bytes = utils.allocateWithRefcount(data_bytes, alignment),
.length = length,
.capacity = capacity,
};
}
pub fn allocateExact(
alignment: u32,
length: usize,
element_width: usize,
) RocList {
if (length == 0) {
return empty();
}
const data_bytes = length * element_width;
return RocList{
.bytes = utils.allocateWithRefcount(data_bytes, alignment),
.length = length,
.capacity = length,
};
}
pub fn reallocate( pub fn reallocate(
self: RocList, self: RocList,
alignment: u32, alignment: u32,
new_length: usize, new_length: usize,
element_width: usize, element_width: usize,
) RocList {
if (self.bytes) |source_ptr| {
if (self.isUnique()) {
if (self.capacity >= new_length) {
return RocList{ .bytes = self.bytes, .length = new_length, .capacity = self.capacity };
} else {
const new_capacity = calculateCapacity(self.capacity, new_length, element_width);
const new_source = utils.unsafeReallocate(source_ptr, alignment, self.len(), new_capacity, element_width);
return RocList{ .bytes = new_source, .length = new_length, .capacity = new_capacity };
}
}
// TODO: Investigate the performance of this.
// Maybe we should just always reallocate to the new_length instead of expanding capacity?
const new_capacity = if (self.capacity >= new_length) self.capacity else calculateCapacity(self.capacity, new_length, element_width);
return self.reallocateFresh(alignment, new_length, new_capacity, element_width);
}
return RocList.allocate(alignment, new_length, element_width);
}
pub fn reallocateExact(
self: RocList,
alignment: u32,
new_length: usize,
element_width: usize,
) RocList { ) RocList {
if (self.bytes) |source_ptr| { if (self.bytes) |source_ptr| {
if (self.isUnique()) { if (self.isUnique()) {
@ -155,9 +247,9 @@ pub const RocList = extern struct {
return RocList{ .bytes = new_source, .length = new_length, .capacity = new_length }; return RocList{ .bytes = new_source, .length = new_length, .capacity = new_length };
} }
} }
return self.reallocateFresh(alignment, new_length, new_length, element_width);
} }
return RocList.allocateExact(alignment, new_length, element_width);
return self.reallocateFresh(alignment, new_length, element_width);
} }
/// reallocate by explicitly making a new allocation and copying elements over /// reallocate by explicitly making a new allocation and copying elements over
@ -165,16 +257,16 @@ pub const RocList = extern struct {
self: RocList, self: RocList,
alignment: u32, alignment: u32,
new_length: usize, new_length: usize,
new_capacity: usize,
element_width: usize, element_width: usize,
) RocList { ) RocList {
const old_length = self.length; const old_length = self.length;
const delta_length = new_length - old_length; const delta_length = new_length - old_length;
const data_bytes = new_length * element_width; const data_bytes = new_capacity * element_width;
const first_slot = utils.allocateWithRefcount(data_bytes, alignment); const first_slot = utils.allocateWithRefcount(data_bytes, alignment);
// transfer the memory // transfer the memory
if (self.bytes) |source_ptr| { if (self.bytes) |source_ptr| {
const dest_ptr = first_slot; const dest_ptr = first_slot;
@ -185,7 +277,7 @@ pub const RocList = extern struct {
const result = RocList{ const result = RocList{
.bytes = first_slot, .bytes = first_slot,
.length = new_length, .length = new_length,
.capacity = new_length, .capacity = new_capacity,
}; };
utils.decref(self.bytes, old_length * element_width, alignment); utils.decref(self.bytes, old_length * element_width, alignment);
@ -412,7 +504,7 @@ pub fn listWithCapacity(
alignment: u32, alignment: u32,
element_width: usize, element_width: usize,
) callconv(.C) RocList { ) callconv(.C) RocList {
var output = RocList.allocate(alignment, capacity, element_width); var output = RocList.allocateExact(alignment, capacity, element_width);
output.length = 0; output.length = 0;
return output; return output;
} }
@ -517,17 +609,25 @@ pub fn listSublist(
len: usize, len: usize,
dec: Dec, dec: Dec,
) callconv(.C) RocList { ) callconv(.C) RocList {
if (len == 0) { const size = list.len();
if (len == 0 or start >= size) {
if (list.isUnique()) {
// Decrement the reference counts of all elements.
if (list.bytes) |source_ptr| {
var i: usize = 0;
while (i < size) : (i += 1) {
const element = source_ptr + i * element_width;
dec(element);
}
var output = list;
output.length = 0;
return output;
}
}
return RocList.empty(); return RocList.empty();
} }
if (list.bytes) |source_ptr| { if (list.bytes) |source_ptr| {
const size = list.len();
if (start >= size) {
return RocList.empty();
}
const keep_len = std.math.min(len, size - start); const keep_len = std.math.min(len, size - start);
const drop_start_len = start; const drop_start_len = start;
const drop_end_len = size - (start + keep_len); const drop_end_len = size - (start + keep_len);
@ -546,10 +646,17 @@ pub fn listSublist(
dec(element); dec(element);
} }
if (start == 0 and list.isUnique()) { if (list.isUnique()) {
var output = list; var output = list;
output.length = keep_len; output.length = keep_len;
return output; if (start == 0) {
return output;
} else {
// We want memmove due to aliasing. Zig does not expose it directly.
// Instead use copy which can write to aliases as long as the dest is before the source.
mem.copy(u8, source_ptr[0 .. keep_len * element_width], source_ptr[start * element_width .. (start + keep_len) * element_width]);
return output;
}
} else { } else {
const output = RocList.allocate(alignment, keep_len, element_width); const output = RocList.allocate(alignment, keep_len, element_width);
const target_ptr = output.bytes orelse unreachable; const target_ptr = output.bytes orelse unreachable;

View file

@ -144,6 +144,7 @@ comptime {
exportStrFn(str.strTrimLeft, "trim_left"); exportStrFn(str.strTrimLeft, "trim_left");
exportStrFn(str.strTrimRight, "trim_right"); exportStrFn(str.strTrimRight, "trim_right");
exportStrFn(str.strCloneTo, "clone_to"); exportStrFn(str.strCloneTo, "clone_to");
exportStrFn(str.withCapacity, "with_capacity");
inline for (INTEGERS) |T| { inline for (INTEGERS) |T| {
str.exportFromInt(T, ROC_BUILTINS ++ "." ++ STR ++ ".from_int."); str.exportFromInt(T, ROC_BUILTINS ++ "." ++ STR ++ ".from_int.");

View file

@ -2596,6 +2596,10 @@ pub fn reserve(string: RocStr, capacity: usize) callconv(.C) RocStr {
} }
} }
pub fn withCapacity(capacity: usize) callconv(.C) RocStr {
return RocStr.allocate(0, capacity);
}
pub fn getScalarUnsafe(string: RocStr, index: usize) callconv(.C) extern struct { bytesParsed: usize, scalar: u32 } { pub fn getScalarUnsafe(string: RocStr, index: usize) callconv(.C) extern struct { bytesParsed: usize, scalar: u32 } {
const slice = string.asSlice(); const slice = string.asSlice();
const bytesParsed = @intCast(usize, std.unicode.utf8ByteSequenceLength(slice[index]) catch unreachable); const bytesParsed = @intCast(usize, std.unicode.utf8ByteSequenceLength(slice[index]) catch unreachable);

View file

@ -30,6 +30,23 @@ interface Decode
] ]
imports [ imports [
List, List,
Result.{ Result },
Num.{
U8,
U16,
U32,
U64,
U128,
I8,
I16,
I32,
I64,
I128,
F32,
F64,
Dec,
},
Bool.{ Bool },
] ]
DecodeError : [TooShort] DecodeError : [TooShort]

View file

@ -20,6 +20,7 @@ interface Dict
Bool.{ Bool }, Bool.{ Bool },
Result.{ Result }, Result.{ Result },
List, List,
Num.{ Nat },
] ]
## A [dictionary](https://en.wikipedia.org/wiki/Associative_array) that lets you can associate keys with values. ## A [dictionary](https://en.wikipedia.org/wiki/Associative_array) that lets you can associate keys with values.

View file

@ -27,7 +27,24 @@ interface Encode
append, append,
toBytes, toBytes,
] ]
imports [] imports [
Num.{
U8,
U16,
U32,
U64,
U128,
I8,
I16,
I32,
I64,
I128,
F32,
F64,
Dec,
},
Bool.{ Bool },
]
Encoder fmt := List U8, fmt -> List U8 | fmt has EncoderFormatting Encoder fmt := List U8, fmt -> List U8 | fmt has EncoderFormatting

View file

@ -20,6 +20,7 @@ interface Hash
] imports [ ] imports [
List, List,
Str, Str,
Num.{ U8, U16, U32, U64, U128, I8, I16, I32, I64, I128 },
] ]
## A value that can hashed. ## A value that can hashed.

View file

@ -18,6 +18,23 @@ interface Json
DecoderFormatting, DecoderFormatting,
DecodeResult, DecodeResult,
}, },
Num.{
U8,
U16,
U32,
U64,
U128,
I8,
I16,
I32,
I64,
I128,
F32,
F64,
Dec,
},
Bool.{ Bool },
Result,
] ]
Json := {} has [ Json := {} has [
@ -187,9 +204,8 @@ takeWhile = \list, predicate ->
helper { taken: [], rest: list } helper { taken: [], rest: list }
asciiByte = \b -> Num.toU8 b digits : List U8
digits = List.range '0' ('9' + 1)
digits = List.range (asciiByte '0') (asciiByte '9' + 1)
takeDigits = \bytes -> takeDigits = \bytes ->
takeWhile bytes \n -> List.contains digits n takeWhile bytes \n -> List.contains digits n
@ -198,10 +214,10 @@ takeFloat = \bytes ->
{ taken: intPart, rest } = takeDigits bytes { taken: intPart, rest } = takeDigits bytes
when List.get rest 0 is when List.get rest 0 is
Ok 46 -> # 46 = . Ok '.' ->
{ taken: floatPart, rest: afterAll } = takeDigits (List.split rest 1).others { taken: floatPart, rest: afterAll } = takeDigits (List.split rest 1).others
builtFloat = builtFloat =
List.concat (List.append intPart (asciiByte '.')) floatPart List.concat (List.append intPart '.') floatPart
{ taken: builtFloat, rest: afterAll } { taken: builtFloat, rest: afterAll }
@ -305,14 +321,14 @@ decodeBool = Decode.custom \bytes, @Json {} ->
# Note: this could be more performant by traversing both branches char-by-char. # Note: this could be more performant by traversing both branches char-by-char.
# Doing that would also make `rest` more correct in the erroring case. # Doing that would also make `rest` more correct in the erroring case.
if if
maybeFalse == [asciiByte 'f', asciiByte 'a', asciiByte 'l', asciiByte 's', asciiByte 'e'] maybeFalse == ['f', 'a', 'l', 's', 'e']
then then
{ result: Ok Bool.false, rest: afterFalse } { result: Ok Bool.false, rest: afterFalse }
else else
{ before: maybeTrue, others: afterTrue } = List.split bytes 4 { before: maybeTrue, others: afterTrue } = List.split bytes 4
if if
maybeTrue == [asciiByte 't', asciiByte 'r', asciiByte 'u', asciiByte 'e'] maybeTrue == ['t', 'r', 'u', 'e']
then then
{ result: Ok Bool.true, rest: afterTrue } { result: Ok Bool.true, rest: afterTrue }
else else
@ -323,10 +339,10 @@ jsonString = \bytes ->
{ before, others: afterStartingQuote } = List.split bytes 1 { before, others: afterStartingQuote } = List.split bytes 1
if if
before == [asciiByte '"'] before == ['"']
then then
# TODO: handle escape sequences # TODO: handle escape sequences
{ taken: strSequence, rest } = takeWhile afterStartingQuote \n -> n != asciiByte '"' { taken: strSequence, rest } = takeWhile afterStartingQuote \n -> n != '"'
when Str.fromUtf8 strSequence is when Str.fromUtf8 strSequence is
Ok s -> Ok s ->
@ -351,7 +367,7 @@ decodeList = \decodeElem -> Decode.custom \bytes, @Json {} ->
{ before: afterElem, others } = List.split rest 1 { before: afterElem, others } = List.split rest 1
if if
afterElem == [asciiByte ','] afterElem == [',']
then then
decodeElems others (List.append accum val) decodeElems others (List.append accum val)
else else
@ -362,7 +378,7 @@ decodeList = \decodeElem -> Decode.custom \bytes, @Json {} ->
{ before, others: afterStartingBrace } = List.split bytes 1 { before, others: afterStartingBrace } = List.split bytes 1
if if
before == [asciiByte '['] before == ['[']
then then
# TODO: empty lists # TODO: empty lists
when decodeElems afterStartingBrace [] is when decodeElems afterStartingBrace [] is
@ -371,7 +387,7 @@ decodeList = \decodeElem -> Decode.custom \bytes, @Json {} ->
{ before: maybeEndingBrace, others: afterEndingBrace } = List.split rest 1 { before: maybeEndingBrace, others: afterEndingBrace } = List.split rest 1
if if
maybeEndingBrace == [asciiByte ']'] maybeEndingBrace == [']']
then then
{ result: Ok vals, rest: afterEndingBrace } { result: Ok vals, rest: afterEndingBrace }
else else
@ -393,10 +409,10 @@ parseExactChar = \bytes, char ->
Err _ -> { result: Err TooShort, rest: bytes } Err _ -> { result: Err TooShort, rest: bytes }
openBrace : List U8 -> DecodeResult {} openBrace : List U8 -> DecodeResult {}
openBrace = \bytes -> parseExactChar bytes (asciiByte '{') openBrace = \bytes -> parseExactChar bytes '{'
closingBrace : List U8 -> DecodeResult {} closingBrace : List U8 -> DecodeResult {}
closingBrace = \bytes -> parseExactChar bytes (asciiByte '}') closingBrace = \bytes -> parseExactChar bytes '}'
recordKey : List U8 -> DecodeResult Str recordKey : List U8 -> DecodeResult Str
recordKey = \bytes -> jsonString bytes recordKey = \bytes -> jsonString bytes
@ -405,10 +421,10 @@ anything : List U8 -> DecodeResult {}
anything = \bytes -> { result: Err TooShort, rest: bytes } anything = \bytes -> { result: Err TooShort, rest: bytes }
colon : List U8 -> DecodeResult {} colon : List U8 -> DecodeResult {}
colon = \bytes -> parseExactChar bytes (asciiByte ':') colon = \bytes -> parseExactChar bytes ':'
comma : List U8 -> DecodeResult {} comma : List U8 -> DecodeResult {}
comma = \bytes -> parseExactChar bytes (asciiByte ',') comma = \bytes -> parseExactChar bytes ','
tryDecode : DecodeResult a, ({ val : a, rest : List U8 } -> DecodeResult b) -> DecodeResult b tryDecode : DecodeResult a, ({ val : a, rest : List U8 } -> DecodeResult b) -> DecodeResult b
tryDecode = \{ result, rest }, mapper -> tryDecode = \{ result, rest }, mapper ->

View file

@ -39,6 +39,7 @@ interface List
max, max,
map4, map4,
mapTry, mapTry,
walkTry,
dropFirst, dropFirst,
joinMap, joinMap,
any, any,
@ -60,9 +61,12 @@ interface List
sortAsc, sortAsc,
sortDesc, sortDesc,
reserve, reserve,
walkBackwardsUntil,
] ]
imports [ imports [
Bool.{ Bool }, Bool.{ Bool },
Result.{ Result },
Num.{ Nat, Num, Int },
] ]
## Types ## Types
@ -85,9 +89,8 @@ interface List
## ##
## ## Performance Details ## ## Performance Details
## ##
## Under the hood, a list is a record containing a `len : Nat` field as well ## Under the hood, a list is a record containing a `len : Nat` field, a `capacity : Nat`
## as a pointer to a reference count and a flat array of bytes. Unique lists ## field, and a pointer to a reference count and a flat array of bytes.
## store a capacity #Nat instead of a reference count.
## ##
## ## Shared Lists ## ## Shared Lists
## ##
@ -109,9 +112,8 @@ interface List
## begins with a refcount of 1, because so far only `ratings` is referencing it. ## begins with a refcount of 1, because so far only `ratings` is referencing it.
## ##
## The second line alters this refcount. `{ foo: ratings` references ## The second line alters this refcount. `{ foo: ratings` references
## the `ratings` list, which will result in its refcount getting incremented ## the `ratings` list, and so does `bar: ratings }`. This will result in its
## from 0 to 1. Similarly, `bar: ratings }` also references the `ratings` list, ## refcount getting incremented from 1 to 3.
## which will result in its refcount getting incremented from 1 to 2.
## ##
## Let's turn this example into a function. ## Let's turn this example into a function.
## ##
@ -129,11 +131,11 @@ interface List
## ##
## Since `ratings` represented a way to reference the list, and that way is no ## Since `ratings` represented a way to reference the list, and that way is no
## longer accessible, the list's refcount gets decremented when `ratings` goes ## longer accessible, the list's refcount gets decremented when `ratings` goes
## out of scope. It will decrease from 2 back down to 1. ## out of scope. It will decrease from 3 back down to 2.
## ##
## Putting these together, when we call `getRatings 5`, what we get back is ## Putting these together, when we call `getRatings 5`, what we get back is
## a record with two fields, `foo`, and `bar`, each of which refers to the same ## a record with two fields, `foo`, and `bar`, each of which refers to the same
## list, and that list has a refcount of 1. ## list, and that list has a refcount of 2.
## ##
## Let's change the last line to be `(getRatings 5).bar` instead of `getRatings 5`: ## Let's change the last line to be `(getRatings 5).bar` instead of `getRatings 5`:
## ##
@ -433,6 +435,13 @@ walkUntil = \list, initial, step ->
Continue new -> new Continue new -> new
Break new -> new Break new -> new
## Same as [List.walkUntil], but does it from the end of the list instead.
walkBackwardsUntil : List elem, state, (state, elem -> [Continue state, Break state]) -> state
walkBackwardsUntil = \list, initial, func ->
when List.iterateBackwards list initial func is
Continue new -> new
Break new -> new
sum : List (Num a) -> Num a sum : List (Num a) -> Num a
sum = \list -> sum = \list ->
List.walk list 0 Num.add List.walk list 0 Num.add
@ -957,9 +966,8 @@ mapTry = \list, toResult ->
Result.map (toResult elem) \ok -> Result.map (toResult elem) \ok ->
List.append state ok List.append state ok
## This is the same as `iterate` but with Result instead of [Continue, Break]. ## This is the same as `iterate` but with [Result] instead of `[Continue, Break]`.
## Using `Result` saves a conditional in `mapTry`. ## Using `Result` saves a conditional in `mapTry`.
## It might be useful to expose this in userspace?
walkTry : List elem, state, (state, elem -> Result state err) -> Result state err walkTry : List elem, state, (state, elem -> Result state err) -> Result state err
walkTry = \list, init, func -> walkTry = \list, init, func ->
walkTryHelp list init func 0 (List.len list) walkTryHelp list init func 0 (List.len list)

View file

@ -145,6 +145,7 @@ interface Num
] ]
imports [ imports [
Bool.{ Bool }, Bool.{ Bool },
Result.{ Result },
] ]
## Represents a number that could be either an [Int] or a [Frac]. ## Represents a number that could be either an [Int] or a [Frac].
@ -574,7 +575,6 @@ isGte : Num a, Num a -> Bool
## Returns `Bool.true` if the number is `0`, and `Bool.false` otherwise. ## Returns `Bool.true` if the number is `0`, and `Bool.false` otherwise.
isZero : Num a -> Bool isZero : Num a -> Bool
isZero = \x -> x == 0
## A number is even if dividing it by 2 gives a remainder of 0. ## A number is even if dividing it by 2 gives a remainder of 0.
## ##

View file

@ -14,7 +14,7 @@ interface Set
intersection, intersection,
difference, difference,
] ]
imports [List, Bool.{ Bool }, Dict.{ Dict }] imports [List, Bool.{ Bool }, Dict.{ Dict }, Num.{ Nat }]
Set k := Dict.Dict k {} Set k := Dict.Dict k {}

View file

@ -43,8 +43,14 @@ interface Str
appendScalar, appendScalar,
walkScalars, walkScalars,
walkScalarsUntil, walkScalarsUntil,
withCapacity,
]
imports [
Bool.{ Bool },
Result.{ Result },
List,
Num.{ Nat, Num, U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, F32, F64, Dec },
] ]
imports [Bool.{ Bool }, Result.{ Result }, List]
## # Types ## # Types
## ##
@ -139,6 +145,9 @@ Utf8Problem : { byteIndex : Nat, problem : Utf8ByteProblem }
isEmpty : Str -> Bool isEmpty : Str -> Bool
concat : Str, Str -> Str concat : Str, Str -> Str
## Returns a string of the specified capacity without any content
withCapacity : Nat -> Str
## Combine a list of strings into a single string, with a separator ## Combine a list of strings into a single string, with a separator
## string in between each. ## string in between each.
## ##

View file

@ -361,6 +361,7 @@ pub const STR_RESERVE: &str = "roc_builtins.str.reserve";
pub const STR_APPEND_SCALAR: &str = "roc_builtins.str.append_scalar"; pub const STR_APPEND_SCALAR: &str = "roc_builtins.str.append_scalar";
pub const STR_GET_SCALAR_UNSAFE: &str = "roc_builtins.str.get_scalar_unsafe"; pub const STR_GET_SCALAR_UNSAFE: &str = "roc_builtins.str.get_scalar_unsafe";
pub const STR_CLONE_TO: &str = "roc_builtins.str.clone_to"; pub const STR_CLONE_TO: &str = "roc_builtins.str.clone_to";
pub const STR_WITH_CAPACITY: &str = "roc_builtins.str.with_capacity";
pub const LIST_MAP: &str = "roc_builtins.list.map"; pub const LIST_MAP: &str = "roc_builtins.list.map";
pub const LIST_MAP2: &str = "roc_builtins.list.map2"; pub const LIST_MAP2: &str = "roc_builtins.list.map2";

View file

@ -55,6 +55,8 @@ macro_rules! map_symbol_to_lowlevel_and_arity {
Symbol::NUM_TO_F32_CHECKED => Some(to_num_checked(Symbol::NUM_TO_F32_CHECKED, var_store, LowLevel::NumToFloatChecked)), Symbol::NUM_TO_F32_CHECKED => Some(to_num_checked(Symbol::NUM_TO_F32_CHECKED, var_store, LowLevel::NumToFloatChecked)),
Symbol::NUM_TO_F64_CHECKED => Some(to_num_checked(Symbol::NUM_TO_F64_CHECKED, var_store, LowLevel::NumToFloatChecked)), Symbol::NUM_TO_F64_CHECKED => Some(to_num_checked(Symbol::NUM_TO_F64_CHECKED, var_store, LowLevel::NumToFloatChecked)),
Symbol::NUM_IS_ZERO => Some(to_num_is_zero(Symbol::NUM_IS_ZERO, var_store)),
_ => None, _ => None,
} }
} }
@ -121,6 +123,7 @@ map_symbol_to_lowlevel_and_arity! {
StrGetScalarUnsafe; STR_GET_SCALAR_UNSAFE; 2, StrGetScalarUnsafe; STR_GET_SCALAR_UNSAFE; 2,
StrToNum; STR_TO_NUM; 1, StrToNum; STR_TO_NUM; 1,
StrGetCapacity; STR_CAPACITY; 1, StrGetCapacity; STR_CAPACITY; 1,
StrWithCapacity; STR_WITH_CAPACITY; 1,
ListLen; LIST_LEN; 1, ListLen; LIST_LEN; 1,
ListWithCapacity; LIST_WITH_CAPACITY; 1, ListWithCapacity; LIST_WITH_CAPACITY; 1,
@ -535,3 +538,33 @@ fn to_num_checked(symbol: Symbol, var_store: &mut VarStore, lowlevel: LowLevel)
ret_var, ret_var,
) )
} }
fn to_num_is_zero(symbol: Symbol, var_store: &mut VarStore) -> Def {
let bool_var = var_store.fresh();
let num_var = var_store.fresh();
let body = Expr::RunLowLevel {
op: LowLevel::Eq,
args: vec![
(num_var, Var(Symbol::ARG_1)),
(
num_var,
Num(
var_store.fresh(),
"0".to_string().into_boxed_str(),
crate::expr::IntValue::I128(0i128.to_ne_bytes()),
roc_types::num::NumBound::None,
),
),
],
ret_var: bool_var,
};
defn(
symbol,
vec![(num_var, Symbol::ARG_1)],
var_store,
body,
bool_var,
)
}

View file

@ -60,8 +60,6 @@ trait CopyEnv {
fn clone_name(&mut self, name: SubsIndex<Lowercase>) -> SubsIndex<Lowercase>; fn clone_name(&mut self, name: SubsIndex<Lowercase>) -> SubsIndex<Lowercase>;
fn clone_tag_name(&mut self, tag_name: SubsIndex<TagName>) -> SubsIndex<TagName>;
fn clone_field_names(&mut self, field_names: SubsSlice<Lowercase>) -> SubsSlice<Lowercase>; fn clone_field_names(&mut self, field_names: SubsSlice<Lowercase>) -> SubsSlice<Lowercase>;
fn clone_tag_names(&mut self, tag_names: SubsSlice<TagName>) -> SubsSlice<TagName>; fn clone_tag_names(&mut self, tag_names: SubsSlice<TagName>) -> SubsSlice<TagName>;
@ -95,11 +93,6 @@ impl CopyEnv for Subs {
name name
} }
#[inline(always)]
fn clone_tag_name(&mut self, tag_name: SubsIndex<TagName>) -> SubsIndex<TagName> {
tag_name
}
#[inline(always)] #[inline(always)]
fn clone_field_names(&mut self, field_names: SubsSlice<Lowercase>) -> SubsSlice<Lowercase> { fn clone_field_names(&mut self, field_names: SubsSlice<Lowercase>) -> SubsSlice<Lowercase> {
field_names field_names
@ -150,11 +143,6 @@ impl<'a> CopyEnv for AcrossSubs<'a> {
SubsIndex::push_new(&mut self.target.field_names, self.source[name].clone()) SubsIndex::push_new(&mut self.target.field_names, self.source[name].clone())
} }
#[inline(always)]
fn clone_tag_name(&mut self, tag_name: SubsIndex<TagName>) -> SubsIndex<TagName> {
SubsIndex::push_new(&mut self.target.tag_names, self.source[tag_name].clone())
}
#[inline(always)] #[inline(always)]
fn clone_field_names(&mut self, field_names: SubsSlice<Lowercase>) -> SubsSlice<Lowercase> { fn clone_field_names(&mut self, field_names: SubsSlice<Lowercase>) -> SubsSlice<Lowercase> {
SubsSlice::extend_new( SubsSlice::extend_new(
@ -259,7 +247,7 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
Int(v1, v2, str, val, bound) => Int(sub!(*v1), sub!(*v2), str.clone(), *val, *bound), Int(v1, v2, str, val, bound) => Int(sub!(*v1), sub!(*v2), str.clone(), *val, *bound),
Float(v1, v2, str, val, bound) => Float(sub!(*v1), sub!(*v2), str.clone(), *val, *bound), Float(v1, v2, str, val, bound) => Float(sub!(*v1), sub!(*v2), str.clone(), *val, *bound),
Str(str) => Str(str.clone()), Str(str) => Str(str.clone()),
SingleQuote(char) => SingleQuote(*char), SingleQuote(v1, v2, char, bound) => SingleQuote(sub!(*v1), sub!(*v2), *char, *bound),
List { List {
elem_var, elem_var,
loc_elems, loc_elems,
@ -725,7 +713,7 @@ fn deep_copy_pattern_help<C: CopyEnv>(
FloatLiteral(sub!(*v1), sub!(*v2), s.clone(), *n, *bound) FloatLiteral(sub!(*v1), sub!(*v2), s.clone(), *n, *bound)
} }
StrLiteral(s) => StrLiteral(s.clone()), StrLiteral(s) => StrLiteral(s.clone()),
SingleQuote(c) => SingleQuote(*c), SingleQuote(v1, v2, c, bound) => SingleQuote(sub!(*v1), sub!(*v2), *c, *bound),
Underscore => Underscore, Underscore => Underscore,
AbilityMemberSpecialization { ident, specializes } => AbilityMemberSpecialization { AbilityMemberSpecialization { ident, specializes } => AbilityMemberSpecialization {
ident: *ident, ident: *ident,
@ -935,12 +923,13 @@ fn deep_copy_type_vars<C: CopyEnv>(
Structure(RecursiveTagUnion(new_rec_var, new_union_tags, new_ext_var)) Structure(RecursiveTagUnion(new_rec_var, new_union_tags, new_ext_var))
}) })
} }
FunctionOrTagUnion(tag_name, symbol, ext_var) => { FunctionOrTagUnion(tag_names, symbols, ext_var) => {
let new_ext_var = descend_var!(ext_var); let new_ext_var = descend_var!(ext_var);
let new_tag_name = env.clone_tag_name(tag_name); let new_tag_names = env.clone_tag_names(tag_names);
let new_symbols = env.clone_lambda_names(symbols);
perform_clone!(Structure(FunctionOrTagUnion( perform_clone!(Structure(FunctionOrTagUnion(
new_tag_name, new_tag_names,
symbol, new_symbols,
new_ext_var new_ext_var
))) )))
} }

View file

@ -1884,7 +1884,7 @@ fn pattern_to_vars_by_symbol(
| IntLiteral(..) | IntLiteral(..)
| FloatLiteral(..) | FloatLiteral(..)
| StrLiteral(_) | StrLiteral(_)
| SingleQuote(_) | SingleQuote(..)
| Underscore | Underscore
| MalformedPattern(_, _) | MalformedPattern(_, _)
| UnsupportedPattern(_) | UnsupportedPattern(_)

View file

@ -253,7 +253,7 @@ fn sketch_pattern(pattern: &crate::pattern::Pattern) -> SketchedPattern {
} }
&FloatLiteral(_, _, _, f, _) => SP::Literal(Literal::Float(f64::to_bits(f))), &FloatLiteral(_, _, _, f, _) => SP::Literal(Literal::Float(f64::to_bits(f))),
StrLiteral(v) => SP::Literal(Literal::Str(v.clone())), StrLiteral(v) => SP::Literal(Literal::Str(v.clone())),
&SingleQuote(c) => SP::Literal(Literal::Byte(c as u8)), &SingleQuote(_, _, c, _) => SP::Literal(Literal::Byte(c as u8)),
RecordDestructure { destructs, .. } => { RecordDestructure { destructs, .. } => {
let tag_id = TagId(0); let tag_id = TagId(0);
let mut patterns = std::vec::Vec::with_capacity(destructs.len()); let mut patterns = std::vec::Vec::with_capacity(destructs.len());

View file

@ -22,6 +22,7 @@ use roc_parse::ast::{self, Defs, EscapedChar, StrLiteral};
use roc_parse::pattern::PatternType::*; use roc_parse::pattern::PatternType::*;
use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError}; use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
use roc_types::num::SingleQuoteBound;
use roc_types::subs::{ExhaustiveMark, IllegalCycleMark, RedundantMark, VarStore, Variable}; use roc_types::subs::{ExhaustiveMark, IllegalCycleMark, RedundantMark, VarStore, Variable};
use roc_types::types::{Alias, Category, LambdaSet, OptAbleVar, Type}; use roc_types::types::{Alias, Category, LambdaSet, OptAbleVar, Type};
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
@ -91,7 +92,8 @@ pub enum Expr {
Int(Variable, Variable, Box<str>, IntValue, IntBound), Int(Variable, Variable, Box<str>, IntValue, IntBound),
Float(Variable, Variable, Box<str>, f64, FloatBound), Float(Variable, Variable, Box<str>, f64, FloatBound),
Str(Box<str>), Str(Box<str>),
SingleQuote(char), // Number variable, precision variable, value, bound
SingleQuote(Variable, Variable, char, SingleQuoteBound),
List { List {
elem_var: Variable, elem_var: Variable,
loc_elems: Vec<Loc<Expr>>, loc_elems: Vec<Loc<Expr>>,
@ -637,7 +639,15 @@ pub fn canonicalize_expr<'a>(
let mut it = string.chars().peekable(); let mut it = string.chars().peekable();
if let Some(char) = it.next() { if let Some(char) = it.next() {
if it.peek().is_none() { if it.peek().is_none() {
(Expr::SingleQuote(char), Output::default()) (
Expr::SingleQuote(
var_store.fresh(),
var_store.fresh(),
char,
SingleQuoteBound::from_char(char),
),
Output::default(),
)
} else { } else {
// multiple chars is found // multiple chars is found
let error = roc_problem::can::RuntimeError::MultipleCharsInSingleQuote(region); let error = roc_problem::can::RuntimeError::MultipleCharsInSingleQuote(region);
@ -1642,7 +1652,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
| other @ Int(..) | other @ Int(..)
| other @ Float(..) | other @ Float(..)
| other @ Str { .. } | other @ Str { .. }
| other @ SingleQuote(_) | other @ SingleQuote(..)
| other @ RuntimeError(_) | other @ RuntimeError(_)
| other @ EmptyRecord | other @ EmptyRecord
| other @ Accessor { .. } | other @ Accessor { .. }
@ -2703,7 +2713,7 @@ fn get_lookup_symbols(expr: &Expr, var_store: &mut VarStore) -> Vec<(Symbol, Var
| Expr::Str(_) | Expr::Str(_)
| Expr::ZeroArgumentTag { .. } | Expr::ZeroArgumentTag { .. }
| Expr::Accessor(_) | Expr::Accessor(_)
| Expr::SingleQuote(_) | Expr::SingleQuote(..)
| Expr::EmptyRecord | Expr::EmptyRecord
| Expr::TypedHole(_) | Expr::TypedHole(_)
| Expr::RuntimeError(_) | Expr::RuntimeError(_)

View file

@ -899,7 +899,7 @@ fn fix_values_captured_in_closure_pattern(
| IntLiteral(..) | IntLiteral(..)
| FloatLiteral(..) | FloatLiteral(..)
| StrLiteral(_) | StrLiteral(_)
| SingleQuote(_) | SingleQuote(..)
| Underscore | Underscore
| Shadowed(..) | Shadowed(..)
| MalformedPattern(_, _) | MalformedPattern(_, _)
@ -1038,7 +1038,7 @@ fn fix_values_captured_in_closure_expr(
| Int(..) | Int(..)
| Float(..) | Float(..)
| Str(_) | Str(_)
| SingleQuote(_) | SingleQuote(..)
| Var(_) | Var(_)
| AbilityMember(..) | AbilityMember(..)
| EmptyRecord | EmptyRecord

View file

@ -12,6 +12,7 @@ use roc_parse::ast::{self, StrLiteral, StrSegment};
use roc_parse::pattern::PatternType; use roc_parse::pattern::PatternType;
use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError, ShadowKind}; use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError, ShadowKind};
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
use roc_types::num::SingleQuoteBound;
use roc_types::subs::{VarStore, Variable}; use roc_types::subs::{VarStore, Variable};
use roc_types::types::{LambdaSet, OptAbleVar, PatternCategory, Type}; use roc_types::types::{LambdaSet, OptAbleVar, PatternCategory, Type};
@ -59,7 +60,7 @@ pub enum Pattern {
IntLiteral(Variable, Variable, Box<str>, IntValue, IntBound), IntLiteral(Variable, Variable, Box<str>, IntValue, IntBound),
FloatLiteral(Variable, Variable, Box<str>, f64, FloatBound), FloatLiteral(Variable, Variable, Box<str>, f64, FloatBound),
StrLiteral(Box<str>), StrLiteral(Box<str>),
SingleQuote(char), SingleQuote(Variable, Variable, char, SingleQuoteBound),
Underscore, Underscore,
/// An identifier that marks a specialization of an ability member. /// An identifier that marks a specialization of an ability member.
@ -95,7 +96,7 @@ impl Pattern {
IntLiteral(var, ..) => Some(*var), IntLiteral(var, ..) => Some(*var),
FloatLiteral(var, ..) => Some(*var), FloatLiteral(var, ..) => Some(*var),
StrLiteral(_) => None, StrLiteral(_) => None,
SingleQuote(_) => None, SingleQuote(..) => None,
Underscore => None, Underscore => None,
AbilityMemberSpecialization { .. } => None, AbilityMemberSpecialization { .. } => None,
@ -148,7 +149,7 @@ impl Pattern {
IntLiteral(..) => C::Int, IntLiteral(..) => C::Int,
FloatLiteral(..) => C::Float, FloatLiteral(..) => C::Float,
StrLiteral(_) => C::Str, StrLiteral(_) => C::Str,
SingleQuote(_) => C::Character, SingleQuote(..) => C::Character,
Underscore => C::PatternDefault, Underscore => C::PatternDefault,
AbilityMemberSpecialization { .. } => C::PatternDefault, AbilityMemberSpecialization { .. } => C::PatternDefault,
@ -456,7 +457,12 @@ pub fn canonicalize_pattern<'a>(
let mut it = string.chars().peekable(); let mut it = string.chars().peekable();
if let Some(char) = it.next() { if let Some(char) = it.next() {
if it.peek().is_none() { if it.peek().is_none() {
Pattern::SingleQuote(char) Pattern::SingleQuote(
var_store.fresh(),
var_store.fresh(),
char,
SingleQuoteBound::from_char(char),
)
} else { } else {
// multiple chars is found // multiple chars is found
let problem = MalformedPatternProblem::MultipleCharsInSingleQuote; let problem = MalformedPatternProblem::MultipleCharsInSingleQuote;
@ -724,7 +730,7 @@ impl<'a> BindingsFromPattern<'a> {
| IntLiteral(..) | IntLiteral(..)
| FloatLiteral(..) | FloatLiteral(..)
| StrLiteral(_) | StrLiteral(_)
| SingleQuote(_) | SingleQuote(..)
| Underscore | Underscore
| Shadowed(_, _, _) | Shadowed(_, _, _)
| MalformedPattern(_, _) | MalformedPattern(_, _)

View file

@ -4,7 +4,7 @@ use roc_can::expected::Expected::{self, *};
use roc_can::num::{FloatBound, FloatWidth, IntBound, IntLitWidth, NumBound, SignDemand}; use roc_can::num::{FloatBound, FloatWidth, IntBound, IntLitWidth, NumBound, SignDemand};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::Region; use roc_region::all::Region;
use roc_types::num::NumericRange; use roc_types::num::{NumericRange, SingleQuoteBound};
use roc_types::subs::Variable; use roc_types::subs::Variable;
use roc_types::types::Type::{self, *}; use roc_types::types::Type::{self, *};
use roc_types::types::{AliasKind, Category}; use roc_types::types::{AliasKind, Category};
@ -99,6 +99,42 @@ pub fn int_literal(
constraints.exists([num_var], and_constraint) constraints.exists([num_var], and_constraint)
} }
pub fn single_quote_literal(
constraints: &mut Constraints,
num_var: Variable,
precision_var: Variable,
expected: Expected<Type>,
region: Region,
bound: SingleQuoteBound,
) -> Constraint {
let reason = Reason::IntLiteral;
// Always add the bound first; this improves the resolved type quality in case it's an alias like "U8".
let mut constrs = ArrayVec::<_, 3>::new();
let num_type = add_numeric_bound_constr(
constraints,
&mut constrs,
num_var,
precision_var,
bound,
region,
Category::Character,
);
constrs.extend([
constraints.equal_types(
num_type.clone(),
ForReason(reason, num_int(Type::Variable(precision_var)), region),
Category::Character,
region,
),
constraints.equal_types(num_type, expected, Category::Character, region),
]);
let and_constraint = constraints.and_constraint(constrs);
constraints.exists([num_var], and_constraint)
}
#[inline(always)] #[inline(always)]
pub fn float_literal( pub fn float_literal(
constraints: &mut Constraints, constraints: &mut Constraints,
@ -332,6 +368,16 @@ impl TypedNumericBound for NumBound {
} }
} }
impl TypedNumericBound for SingleQuoteBound {
fn numeric_bound(&self) -> NumericBound {
match self {
&SingleQuoteBound::AtLeast { width } => {
NumericBound::Range(NumericRange::IntAtLeastEitherSign(width))
}
}
}
}
/// A bound placed on a number because of its literal value. /// A bound placed on a number because of its literal value.
/// e.g. `-5` cannot be unsigned, and 300 does not fit in a U8 /// e.g. `-5` cannot be unsigned, and 300 does not fit in a U8
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]

View file

@ -1,7 +1,8 @@
use std::ops::Range; use std::ops::Range;
use crate::builtins::{ use crate::builtins::{
empty_list_type, float_literal, int_literal, list_type, num_literal, num_u32, str_type, empty_list_type, float_literal, int_literal, list_type, num_literal, single_quote_literal,
str_type,
}; };
use crate::pattern::{constrain_pattern, PatternState}; use crate::pattern::{constrain_pattern, PatternState};
use roc_can::annotation::IntroducedVariables; use roc_can::annotation::IntroducedVariables;
@ -292,7 +293,14 @@ pub fn constrain_expr(
constraints.exists(vars, and_constraint) constraints.exists(vars, and_constraint)
} }
Str(_) => constraints.equal_types(str_type(), expected, Category::Str, region), Str(_) => constraints.equal_types(str_type(), expected, Category::Str, region),
SingleQuote(_) => constraints.equal_types(num_u32(), expected, Category::Character, region), SingleQuote(num_var, precision_var, _, bound) => single_quote_literal(
constraints,
*num_var,
*precision_var,
expected,
region,
*bound,
),
List { List {
elem_var, elem_var,
loc_elems, loc_elems,

Some files were not shown because too many files have changed in this diff Show more