mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +00:00
Merge remote-tracking branch 'upstream/main' into str-withprefix
This commit is contained in:
commit
54fb1bc32f
222 changed files with 1198 additions and 1070 deletions
2
.github/workflows/nightly_macos_x86_64.yml
vendored
2
.github/workflows/nightly_macos_x86_64.yml
vendored
|
@ -6,7 +6,7 @@ name: Nightly Release macOS x86_64
|
|||
|
||||
env:
|
||||
ZIG_VERSION: 0.9.1
|
||||
LLVM_SYS_130_PREFIX: /usr/local/opt/llvm
|
||||
LLVM_SYS_130_PREFIX: /usr/local/opt/llvm@13
|
||||
|
||||
jobs:
|
||||
test-build-upload:
|
||||
|
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
run: ls | grep "roc_nightly.*tar\.gz" | xargs tar -xzvf
|
||||
|
||||
- name: test roc hello world
|
||||
run: ./roc examples/hello-world/main.roc
|
||||
run: ./roc examples/helloWorld.roc
|
||||
|
||||
|
||||
|
||||
|
|
2
.github/workflows/test_nightly_many_os.yml
vendored
2
.github/workflows/test_nightly_many_os.yml
vendored
|
@ -41,7 +41,7 @@ jobs:
|
|||
run: ls | grep "roc_nightly.*tar\.gz" | xargs tar -xzvf
|
||||
|
||||
- name: test roc hello world
|
||||
run: ./roc examples/hello-world/main.roc
|
||||
run: ./roc examples/helloWorld.roc
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -28,13 +28,7 @@ sh <(curl -L https://nixos.org/nix/install) --no-daemon
|
|||
sh <(curl -L https://nixos.org/nix/install) --daemon
|
||||
```
|
||||
|
||||
Open a new terminal and install nixFlakes in your environment:
|
||||
|
||||
```sh
|
||||
nix-env -iA nixpkgs.nixFlakes
|
||||
```
|
||||
|
||||
Edit either `~/.config/nix/nix.conf` or `/etc/nix/nix.conf` and add:
|
||||
Open a new terminal and edit either `~/.config/nix/nix.conf` or `/etc/nix/nix.conf` and add:
|
||||
|
||||
```text
|
||||
experimental-features = nix-command flakes
|
||||
|
|
35
Cargo.lock
generated
35
Cargo.lock
generated
|
@ -620,10 +620,10 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "confy"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/rust-cli/confy#c6b62039281b8643539b436440bcea1b0d634bc7"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/rust-cli/confy#fd069f062aa3373c846f0d8c6e3b5e2a5cd0096b"
|
||||
dependencies = [
|
||||
"directories-next",
|
||||
"directories",
|
||||
"serde",
|
||||
"serde_yaml",
|
||||
"thiserror",
|
||||
|
@ -1160,13 +1160,13 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "directories-next"
|
||||
version = "2.0.0"
|
||||
name = "directories"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc"
|
||||
checksum = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"dirs-sys-next",
|
||||
"cfg-if 0.1.10",
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1179,6 +1179,17 @@ dependencies = [
|
|||
"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]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
|
@ -3466,8 +3477,6 @@ dependencies = [
|
|||
"roc_tracing",
|
||||
"serial_test",
|
||||
"signal-hook",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"target-lexicon",
|
||||
"tempfile",
|
||||
"ven_pretty",
|
||||
|
@ -4766,9 +4775,9 @@ checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
|
|||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.24.2"
|
||||
version = "0.24.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4faebde00e8ff94316c01800f9054fd2ba77d30d9e922541913051d1d978918b"
|
||||
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
|
@ -5154,7 +5163,7 @@ version = "1.6.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if 0.1.10",
|
||||
"rand",
|
||||
"static_assertions",
|
||||
]
|
||||
|
|
|
@ -57,9 +57,10 @@ members = [
|
|||
"crates/wasi-libc-sys",
|
||||
]
|
||||
exclude = [
|
||||
# Examples sometimes have Rust hosts in their platforms. The compiler should ignore those.
|
||||
"examples",
|
||||
"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.
|
||||
# The tests will still correctly build them.
|
||||
"crates/cli_utils",
|
||||
|
|
|
@ -50,7 +50,7 @@ install-zig-llvm-valgrind:
|
|||
|
||||
copy-dirs:
|
||||
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.
|
||||
prep-bench-folder:
|
||||
|
@ -60,11 +60,11 @@ prep-bench-folder:
|
|||
ARG BENCH_SUFFIX=branch
|
||||
RUN cargo criterion -V
|
||||
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/target/release/deps
|
||||
RUN mkdir -p bench-folder/examples/benchmarks
|
||||
RUN cp examples/benchmarks/*.roc bench-folder/examples/benchmarks/
|
||||
RUN cp -r examples/benchmarks/platform bench-folder/examples/benchmarks/
|
||||
RUN cp crates/cli_testing_examples/benchmarks/*.roc bench-folder/crates/cli_testing_examples/benchmarks/
|
||||
RUN cp -r crates/cli_testing_examples/benchmarks/platform bench-folder/crates/cli_testing_examples/benchmarks/
|
||||
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
|
||||
# copy the most recent time bench to bench-folder
|
||||
|
|
28
TUTORIAL.md
28
TUTORIAL.md
|
@ -115,7 +115,7 @@ Create a new file called `Hello.roc` and put this inside it:
|
|||
|
||||
```coffee
|
||||
app "hello"
|
||||
packages { pf: "examples/interactive/cli-platform/main.roc" }
|
||||
packages { pf: "examples/cli/cli-platform/main.roc" }
|
||||
imports [pf.Stdout, pf.Program]
|
||||
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
|
||||
> 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/interactive/cli-platform/main.roc` file in that source code. In the future,
|
||||
> `"examples/cli/cli-platform/main.roc"` with the path to the
|
||||
> `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
|
||||
> necessary!
|
||||
|
||||
|
@ -1273,7 +1273,7 @@ Let's take a closer look at the part of `Hello.roc` above `main`:
|
|||
|
||||
```coffee
|
||||
app "hello"
|
||||
packages { pf: "examples/interactive/cli-platform/main.roc" }
|
||||
packages { pf: "examples/cli/cli-platform/main.roc" }
|
||||
imports [pf.Stdout, pf.Program]
|
||||
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:
|
||||
|
||||
```coffee
|
||||
packages { pf: "examples/interactive/cli-platform/main.roc" }
|
||||
packages { pf: "examples/cli/cli-platform/main.roc" }
|
||||
imports [pf.Stdout, pf.Program]
|
||||
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.
|
||||
|
||||
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`
|
||||
and `Program` modules come from the `pf` package.
|
||||
|
||||
Since `pf` was the name we chose for the `examples/interactive/cli-platform/main.roc`
|
||||
package (when we wrote `packages { pf: "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/cli/cli-platform/main.roc" }`),
|
||||
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
|
||||
`examples/interactive/cli-platform/main.roc` package.
|
||||
`examples/cli/cli-platform/main.roc` package.
|
||||
|
||||
## Tasks
|
||||
|
||||
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:
|
||||
|
||||
|
@ -1344,7 +1344,7 @@ First, let's do a basic "Hello World" using the tutorial app.
|
|||
|
||||
```coffee
|
||||
app "cli-tutorial"
|
||||
packages { pf: "examples/interactive/cli-platform/main.roc" }
|
||||
packages { pf: "examples/cli/cli-platform/main.roc" }
|
||||
imports [pf.Stdout, pf.Program]
|
||||
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
|
||||
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]
|
||||
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
|
||||
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]
|
||||
provides [main] to pf
|
||||
|
||||
|
|
|
@ -227,9 +227,10 @@ fn calc_hashes_for_folder(benches_path_str: &str) -> HashMap<String, String> {
|
|||
}
|
||||
|
||||
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_bench_hashes = calc_hashes_for_folder(&main_benches_path_str);
|
||||
|
||||
let branch_benches_path_str = [BENCH_FOLDER_BRANCH, bench_folder_str].join("");
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
cp target/release/roc ./roc # to be able to exclude "target" later in the tar command
|
||||
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
|
||||
|
|
|
@ -99,8 +99,6 @@ indoc = "1.0.7"
|
|||
serial_test = "0.9.0"
|
||||
criterion = { git = "https://github.com/Anton-4/criterion.rs"}
|
||||
cli_utils = { path = "../cli_utils" }
|
||||
strum = "0.24.0"
|
||||
strum_macros = "0.24"
|
||||
once_cell = "1.14.0"
|
||||
parking_lot = "0.12"
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
|||
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]
|
||||
provides [main] to pf
|
||||
|
||||
|
|
6
crates/cli_testing_examples/.gitignore
vendored
Normal file
6
crates/cli_testing_examples/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
*.dSYM
|
||||
libhost.a
|
||||
libapp.so
|
||||
dynhost
|
||||
preprocessedhost
|
||||
metadata
|
|
@ -16,7 +16,7 @@ name = "host"
|
|||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
roc_std = { path = "../../../crates/roc_std" }
|
||||
roc_std = { path = "../../../../crates/roc_std" }
|
||||
libc = "0.2"
|
||||
|
||||
[workspace]
|
|
@ -3,12 +3,12 @@
|
|||
To run this website, first compile either of these identical apps:
|
||||
|
||||
```bash
|
||||
# Option A: Compile examples/platform-switching/rocLovesWebAssembly.roc
|
||||
cargo run -- build --target=wasm32 examples/platform-switching/rocLovesWebAssembly.roc
|
||||
# Option A: Compile crates/cli_testing_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
|
||||
cargo run -- build --target=wasm32 examples/platform-switching/main.roc
|
||||
(cd examples/platform-switching && mv rocLovesPlatforms.wasm web-assembly-platform/rocLovesWebAssembly.wasm)
|
||||
# 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 crates/cli_testing_examples/platform-switching/main.roc
|
||||
(cd crates/cli_testing_examples/platform-switching && mv rocLovesPlatforms.wasm web-assembly-platform/rocLovesWebAssembly.wasm)
|
||||
```
|
||||
|
||||
Then `cd` into the website directory
|
||||
|
@ -16,7 +16,7 @@ and run any web server that can handle WebAssembly.
|
|||
For example, with `http-server`:
|
||||
|
||||
```bash
|
||||
cd examples/platform-switching/web-assembly-platform
|
||||
cd crates/cli_testing_examples/platform-switching/web-assembly-platform
|
||||
npm install -g http-server
|
||||
http-server
|
||||
```
|
File diff suppressed because one or more lines are too long
|
@ -381,20 +381,31 @@ pub fn root_dir() -> PathBuf {
|
|||
path
|
||||
}
|
||||
|
||||
// start the dir with crates/cli_testing_examples
|
||||
#[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();
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn example_file(dir_name: &str, file_name: &str) -> PathBuf {
|
||||
let mut path = examples_dir(dir_name);
|
||||
pub fn dir_path_from_root(dir_name: &str) -> PathBuf {
|
||||
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);
|
||||
|
||||
|
|
|
@ -374,7 +374,7 @@ pub fn build_zig_host_wasm32(
|
|||
"c",
|
||||
"-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",
|
||||
"--strip",
|
||||
];
|
||||
|
@ -635,6 +635,7 @@ pub fn rebuild_host(
|
|||
} else if cargo_host_src.exists() {
|
||||
// Compile and link Cargo.toml, if it exists
|
||||
let cargo_dir = host_input_path.parent().unwrap();
|
||||
|
||||
let cargo_out_dir = cargo_dir.join("target").join(
|
||||
if matches!(opt_level, OptLevel::Optimize | OptLevel::Size) {
|
||||
"release"
|
||||
|
@ -1215,7 +1216,7 @@ fn link_wasm32(
|
|||
"-O",
|
||||
"ReleaseSmall",
|
||||
// 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()?;
|
||||
|
||||
|
|
|
@ -204,9 +204,8 @@ takeWhile = \list, predicate ->
|
|||
|
||||
helper { taken: [], rest: list }
|
||||
|
||||
asciiByte = \b -> Num.toU8 b
|
||||
|
||||
digits = List.range (asciiByte '0') (asciiByte '9' + 1)
|
||||
digits : List U8
|
||||
digits = List.range '0' ('9' + 1)
|
||||
|
||||
takeDigits = \bytes ->
|
||||
takeWhile bytes \n -> List.contains digits n
|
||||
|
@ -215,10 +214,10 @@ takeFloat = \bytes ->
|
|||
{ taken: intPart, rest } = takeDigits bytes
|
||||
|
||||
when List.get rest 0 is
|
||||
Ok 46 -> # 46 = .
|
||||
Ok '.' ->
|
||||
{ taken: floatPart, rest: afterAll } = takeDigits (List.split rest 1).others
|
||||
builtFloat =
|
||||
List.concat (List.append intPart (asciiByte '.')) floatPart
|
||||
List.concat (List.append intPart '.') floatPart
|
||||
|
||||
{ taken: builtFloat, rest: afterAll }
|
||||
|
||||
|
@ -322,14 +321,14 @@ decodeBool = Decode.custom \bytes, @Json {} ->
|
|||
# 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.
|
||||
if
|
||||
maybeFalse == [asciiByte 'f', asciiByte 'a', asciiByte 'l', asciiByte 's', asciiByte 'e']
|
||||
maybeFalse == ['f', 'a', 'l', 's', 'e']
|
||||
then
|
||||
{ result: Ok Bool.false, rest: afterFalse }
|
||||
else
|
||||
{ before: maybeTrue, others: afterTrue } = List.split bytes 4
|
||||
|
||||
if
|
||||
maybeTrue == [asciiByte 't', asciiByte 'r', asciiByte 'u', asciiByte 'e']
|
||||
maybeTrue == ['t', 'r', 'u', 'e']
|
||||
then
|
||||
{ result: Ok Bool.true, rest: afterTrue }
|
||||
else
|
||||
|
@ -340,10 +339,10 @@ jsonString = \bytes ->
|
|||
{ before, others: afterStartingQuote } = List.split bytes 1
|
||||
|
||||
if
|
||||
before == [asciiByte '"']
|
||||
before == ['"']
|
||||
then
|
||||
# TODO: handle escape sequences
|
||||
{ taken: strSequence, rest } = takeWhile afterStartingQuote \n -> n != asciiByte '"'
|
||||
{ taken: strSequence, rest } = takeWhile afterStartingQuote \n -> n != '"'
|
||||
|
||||
when Str.fromUtf8 strSequence is
|
||||
Ok s ->
|
||||
|
@ -368,7 +367,7 @@ decodeList = \decodeElem -> Decode.custom \bytes, @Json {} ->
|
|||
{ before: afterElem, others } = List.split rest 1
|
||||
|
||||
if
|
||||
afterElem == [asciiByte ',']
|
||||
afterElem == [',']
|
||||
then
|
||||
decodeElems others (List.append accum val)
|
||||
else
|
||||
|
@ -379,7 +378,7 @@ decodeList = \decodeElem -> Decode.custom \bytes, @Json {} ->
|
|||
{ before, others: afterStartingBrace } = List.split bytes 1
|
||||
|
||||
if
|
||||
before == [asciiByte '[']
|
||||
before == ['[']
|
||||
then
|
||||
# TODO: empty lists
|
||||
when decodeElems afterStartingBrace [] is
|
||||
|
@ -388,7 +387,7 @@ decodeList = \decodeElem -> Decode.custom \bytes, @Json {} ->
|
|||
{ before: maybeEndingBrace, others: afterEndingBrace } = List.split rest 1
|
||||
|
||||
if
|
||||
maybeEndingBrace == [asciiByte ']']
|
||||
maybeEndingBrace == [']']
|
||||
then
|
||||
{ result: Ok vals, rest: afterEndingBrace }
|
||||
else
|
||||
|
@ -410,10 +409,10 @@ parseExactChar = \bytes, char ->
|
|||
Err _ -> { result: Err TooShort, rest: bytes }
|
||||
|
||||
openBrace : List U8 -> DecodeResult {}
|
||||
openBrace = \bytes -> parseExactChar bytes (asciiByte '{')
|
||||
openBrace = \bytes -> parseExactChar bytes '{'
|
||||
|
||||
closingBrace : List U8 -> DecodeResult {}
|
||||
closingBrace = \bytes -> parseExactChar bytes (asciiByte '}')
|
||||
closingBrace = \bytes -> parseExactChar bytes '}'
|
||||
|
||||
recordKey : List U8 -> DecodeResult Str
|
||||
recordKey = \bytes -> jsonString bytes
|
||||
|
@ -422,10 +421,10 @@ anything : List U8 -> DecodeResult {}
|
|||
anything = \bytes -> { result: Err TooShort, rest: bytes }
|
||||
|
||||
colon : List U8 -> DecodeResult {}
|
||||
colon = \bytes -> parseExactChar bytes (asciiByte ':')
|
||||
colon = \bytes -> parseExactChar bytes ':'
|
||||
|
||||
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 = \{ result, rest }, mapper ->
|
||||
|
|
|
@ -39,6 +39,7 @@ interface List
|
|||
max,
|
||||
map4,
|
||||
mapTry,
|
||||
walkTry,
|
||||
dropFirst,
|
||||
joinMap,
|
||||
any,
|
||||
|
@ -959,9 +960,8 @@ mapTry = \list, toResult ->
|
|||
Result.map (toResult elem) \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`.
|
||||
## It might be useful to expose this in userspace?
|
||||
walkTry : List elem, state, (state, elem -> Result state err) -> Result state err
|
||||
walkTry = \list, init, func ->
|
||||
walkTryHelp list init func 0 (List.len list)
|
||||
|
|
|
@ -575,7 +575,6 @@ isGte : Num a, Num a -> Bool
|
|||
|
||||
## Returns `Bool.true` if the number is `0`, and `Bool.false` otherwise.
|
||||
isZero : Num a -> Bool
|
||||
isZero = \x -> x == 0
|
||||
|
||||
## A number is even if dividing it by 2 gives a remainder of 0.
|
||||
##
|
||||
|
|
|
@ -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_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,
|
||||
}
|
||||
}
|
||||
|
@ -535,3 +537,33 @@ fn to_num_checked(symbol: Symbol, var_store: &mut VarStore, lowlevel: LowLevel)
|
|||
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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -259,7 +259,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),
|
||||
Float(v1, v2, str, val, bound) => Float(sub!(*v1), sub!(*v2), str.clone(), *val, *bound),
|
||||
Str(str) => Str(str.clone()),
|
||||
SingleQuote(char) => SingleQuote(*char),
|
||||
SingleQuote(v1, v2, char, bound) => SingleQuote(sub!(*v1), sub!(*v2), *char, *bound),
|
||||
List {
|
||||
elem_var,
|
||||
loc_elems,
|
||||
|
@ -725,7 +725,7 @@ fn deep_copy_pattern_help<C: CopyEnv>(
|
|||
FloatLiteral(sub!(*v1), sub!(*v2), s.clone(), *n, *bound)
|
||||
}
|
||||
StrLiteral(s) => StrLiteral(s.clone()),
|
||||
SingleQuote(c) => SingleQuote(*c),
|
||||
SingleQuote(v1, v2, c, bound) => SingleQuote(sub!(*v1), sub!(*v2), *c, *bound),
|
||||
Underscore => Underscore,
|
||||
AbilityMemberSpecialization { ident, specializes } => AbilityMemberSpecialization {
|
||||
ident: *ident,
|
||||
|
|
|
@ -1884,7 +1884,7 @@ fn pattern_to_vars_by_symbol(
|
|||
| IntLiteral(..)
|
||||
| FloatLiteral(..)
|
||||
| StrLiteral(_)
|
||||
| SingleQuote(_)
|
||||
| SingleQuote(..)
|
||||
| Underscore
|
||||
| MalformedPattern(_, _)
|
||||
| UnsupportedPattern(_)
|
||||
|
|
|
@ -253,7 +253,7 @@ fn sketch_pattern(pattern: &crate::pattern::Pattern) -> SketchedPattern {
|
|||
}
|
||||
&FloatLiteral(_, _, _, f, _) => SP::Literal(Literal::Float(f64::to_bits(f))),
|
||||
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, .. } => {
|
||||
let tag_id = TagId(0);
|
||||
let mut patterns = std::vec::Vec::with_capacity(destructs.len());
|
||||
|
|
|
@ -22,6 +22,7 @@ use roc_parse::ast::{self, Defs, EscapedChar, StrLiteral};
|
|||
use roc_parse::pattern::PatternType::*;
|
||||
use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::num::SingleQuoteBound;
|
||||
use roc_types::subs::{ExhaustiveMark, IllegalCycleMark, RedundantMark, VarStore, Variable};
|
||||
use roc_types::types::{Alias, Category, LambdaSet, OptAbleVar, Type};
|
||||
use std::fmt::{Debug, Display};
|
||||
|
@ -91,7 +92,8 @@ pub enum Expr {
|
|||
Int(Variable, Variable, Box<str>, IntValue, IntBound),
|
||||
Float(Variable, Variable, Box<str>, f64, FloatBound),
|
||||
Str(Box<str>),
|
||||
SingleQuote(char),
|
||||
// Number variable, precision variable, value, bound
|
||||
SingleQuote(Variable, Variable, char, SingleQuoteBound),
|
||||
List {
|
||||
elem_var: Variable,
|
||||
loc_elems: Vec<Loc<Expr>>,
|
||||
|
@ -637,7 +639,15 @@ pub fn canonicalize_expr<'a>(
|
|||
let mut it = string.chars().peekable();
|
||||
if let Some(char) = it.next() {
|
||||
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 {
|
||||
// multiple chars is found
|
||||
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 @ Float(..)
|
||||
| other @ Str { .. }
|
||||
| other @ SingleQuote(_)
|
||||
| other @ SingleQuote(..)
|
||||
| other @ RuntimeError(_)
|
||||
| other @ EmptyRecord
|
||||
| other @ Accessor { .. }
|
||||
|
@ -2703,7 +2713,7 @@ fn get_lookup_symbols(expr: &Expr, var_store: &mut VarStore) -> Vec<(Symbol, Var
|
|||
| Expr::Str(_)
|
||||
| Expr::ZeroArgumentTag { .. }
|
||||
| Expr::Accessor(_)
|
||||
| Expr::SingleQuote(_)
|
||||
| Expr::SingleQuote(..)
|
||||
| Expr::EmptyRecord
|
||||
| Expr::TypedHole(_)
|
||||
| Expr::RuntimeError(_)
|
||||
|
|
|
@ -899,7 +899,7 @@ fn fix_values_captured_in_closure_pattern(
|
|||
| IntLiteral(..)
|
||||
| FloatLiteral(..)
|
||||
| StrLiteral(_)
|
||||
| SingleQuote(_)
|
||||
| SingleQuote(..)
|
||||
| Underscore
|
||||
| Shadowed(..)
|
||||
| MalformedPattern(_, _)
|
||||
|
@ -1038,7 +1038,7 @@ fn fix_values_captured_in_closure_expr(
|
|||
| Int(..)
|
||||
| Float(..)
|
||||
| Str(_)
|
||||
| SingleQuote(_)
|
||||
| SingleQuote(..)
|
||||
| Var(_)
|
||||
| AbilityMember(..)
|
||||
| EmptyRecord
|
||||
|
|
|
@ -12,6 +12,7 @@ use roc_parse::ast::{self, StrLiteral, StrSegment};
|
|||
use roc_parse::pattern::PatternType;
|
||||
use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError, ShadowKind};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::num::SingleQuoteBound;
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::{LambdaSet, OptAbleVar, PatternCategory, Type};
|
||||
|
||||
|
@ -59,7 +60,7 @@ pub enum Pattern {
|
|||
IntLiteral(Variable, Variable, Box<str>, IntValue, IntBound),
|
||||
FloatLiteral(Variable, Variable, Box<str>, f64, FloatBound),
|
||||
StrLiteral(Box<str>),
|
||||
SingleQuote(char),
|
||||
SingleQuote(Variable, Variable, char, SingleQuoteBound),
|
||||
Underscore,
|
||||
|
||||
/// An identifier that marks a specialization of an ability member.
|
||||
|
@ -95,7 +96,7 @@ impl Pattern {
|
|||
IntLiteral(var, ..) => Some(*var),
|
||||
FloatLiteral(var, ..) => Some(*var),
|
||||
StrLiteral(_) => None,
|
||||
SingleQuote(_) => None,
|
||||
SingleQuote(..) => None,
|
||||
Underscore => None,
|
||||
|
||||
AbilityMemberSpecialization { .. } => None,
|
||||
|
@ -148,7 +149,7 @@ impl Pattern {
|
|||
IntLiteral(..) => C::Int,
|
||||
FloatLiteral(..) => C::Float,
|
||||
StrLiteral(_) => C::Str,
|
||||
SingleQuote(_) => C::Character,
|
||||
SingleQuote(..) => C::Character,
|
||||
Underscore => C::PatternDefault,
|
||||
|
||||
AbilityMemberSpecialization { .. } => C::PatternDefault,
|
||||
|
@ -456,7 +457,12 @@ pub fn canonicalize_pattern<'a>(
|
|||
let mut it = string.chars().peekable();
|
||||
if let Some(char) = it.next() {
|
||||
if it.peek().is_none() {
|
||||
Pattern::SingleQuote(char)
|
||||
Pattern::SingleQuote(
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
char,
|
||||
SingleQuoteBound::from_char(char),
|
||||
)
|
||||
} else {
|
||||
// multiple chars is found
|
||||
let problem = MalformedPatternProblem::MultipleCharsInSingleQuote;
|
||||
|
@ -724,7 +730,7 @@ impl<'a> BindingsFromPattern<'a> {
|
|||
| IntLiteral(..)
|
||||
| FloatLiteral(..)
|
||||
| StrLiteral(_)
|
||||
| SingleQuote(_)
|
||||
| SingleQuote(..)
|
||||
| Underscore
|
||||
| Shadowed(_, _, _)
|
||||
| MalformedPattern(_, _)
|
||||
|
|
|
@ -4,7 +4,7 @@ use roc_can::expected::Expected::{self, *};
|
|||
use roc_can::num::{FloatBound, FloatWidth, IntBound, IntLitWidth, NumBound, SignDemand};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::Region;
|
||||
use roc_types::num::NumericRange;
|
||||
use roc_types::num::{NumericRange, SingleQuoteBound};
|
||||
use roc_types::subs::Variable;
|
||||
use roc_types::types::Type::{self, *};
|
||||
use roc_types::types::{AliasKind, Category};
|
||||
|
@ -99,6 +99,42 @@ pub fn int_literal(
|
|||
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)]
|
||||
pub fn float_literal(
|
||||
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.
|
||||
/// e.g. `-5` cannot be unsigned, and 300 does not fit in a U8
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::ops::Range;
|
||||
|
||||
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 roc_can::annotation::IntroducedVariables;
|
||||
|
@ -292,7 +293,14 @@ pub fn constrain_expr(
|
|||
constraints.exists(vars, and_constraint)
|
||||
}
|
||||
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 {
|
||||
elem_var,
|
||||
loc_elems,
|
||||
|
|
|
@ -71,7 +71,7 @@ fn headers_from_annotation_help(
|
|||
| NumLiteral(..)
|
||||
| IntLiteral(..)
|
||||
| FloatLiteral(..)
|
||||
| SingleQuote(_)
|
||||
| SingleQuote(..)
|
||||
| StrLiteral(_) => true,
|
||||
|
||||
RecordDestructure { destructs, .. } => match annotation.value.shallow_dealias() {
|
||||
|
@ -320,9 +320,32 @@ pub fn constrain_pattern(
|
|||
));
|
||||
}
|
||||
|
||||
SingleQuote(_) => {
|
||||
&SingleQuote(num_var, precision_var, _, bound) => {
|
||||
// First constraint on the free num var; this improves the resolved type quality in
|
||||
// case the bound is an alias.
|
||||
let num_type = builtins::add_numeric_bound_constr(
|
||||
constraints,
|
||||
&mut state.constraints,
|
||||
num_var,
|
||||
num_var,
|
||||
bound,
|
||||
region,
|
||||
Category::Int,
|
||||
);
|
||||
|
||||
// Link the free num var with the int var and our expectation.
|
||||
let int_type = builtins::num_int(Type::Variable(precision_var));
|
||||
|
||||
state.constraints.push(constraints.equal_types(
|
||||
num_type.clone(), // TODO check me if something breaks!
|
||||
Expected::NoExpectation(int_type),
|
||||
Category::Int,
|
||||
region,
|
||||
));
|
||||
|
||||
// Also constrain the pattern against the num var, again to reuse aliases if they're present.
|
||||
state.constraints.push(constraints.equal_pattern_types(
|
||||
builtins::num_u32(),
|
||||
num_type,
|
||||
expected,
|
||||
PatternCategory::Character,
|
||||
region,
|
||||
|
|
|
@ -1374,6 +1374,7 @@ define_builtins! {
|
|||
72 LIST_SUBLIST_LOWLEVEL: "sublistLowlevel"
|
||||
73 LIST_CAPACITY: "capacity"
|
||||
74 LIST_MAP_TRY: "mapTry"
|
||||
75 LIST_WALK_TRY: "walkTry"
|
||||
}
|
||||
7 RESULT: "Result" => {
|
||||
0 RESULT_RESULT: "Result" exposed_type=true // the Result.Result type alias
|
||||
|
|
|
@ -4027,12 +4027,18 @@ pub fn with_hole<'a>(
|
|||
hole,
|
||||
),
|
||||
|
||||
SingleQuote(character) => Stmt::Let(
|
||||
SingleQuote(_, _, character, _) => {
|
||||
let layout = layout_cache
|
||||
.from_var(env.arena, variable, env.subs)
|
||||
.unwrap();
|
||||
|
||||
Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Int((character as i128).to_ne_bytes())),
|
||||
Layout::int_width(IntWidth::I32),
|
||||
layout,
|
||||
hole,
|
||||
),
|
||||
)
|
||||
}
|
||||
LetNonRec(def, cont) => from_can_let(
|
||||
env,
|
||||
procs,
|
||||
|
@ -8883,10 +8889,12 @@ fn from_can_pattern_help<'a>(
|
|||
IntOrFloatValue::Float(*float),
|
||||
)),
|
||||
StrLiteral(v) => Ok(Pattern::StrLiteral(v.clone())),
|
||||
SingleQuote(c) => Ok(Pattern::IntLiteral(
|
||||
(*c as i128).to_ne_bytes(),
|
||||
IntWidth::I32,
|
||||
)),
|
||||
SingleQuote(var, _, c, _) => match layout_cache.from_var(env.arena, *var, env.subs) {
|
||||
Ok(Layout::Builtin(Builtin::Int(width))) => {
|
||||
Ok(Pattern::IntLiteral((*c as i128).to_ne_bytes(), width))
|
||||
}
|
||||
o => internal_error!("an integer width was expected, but we found {:?}", o),
|
||||
},
|
||||
Shadowed(region, ident, _new_symbol) => Err(RuntimeError::Shadowing {
|
||||
original_region: *region,
|
||||
shadow: ident.clone(),
|
||||
|
|
|
@ -7841,4 +7841,103 @@ mod solve_expr {
|
|||
"hasher -> hasher | hasher has Hasher",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_char_as_u8() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
x : U8
|
||||
x = '.'
|
||||
|
||||
x
|
||||
"#
|
||||
),
|
||||
"U8",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_char_as_u16() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
x : U16
|
||||
x = '.'
|
||||
|
||||
x
|
||||
"#
|
||||
),
|
||||
"U16",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_char_as_u32() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
x : U32
|
||||
x = '.'
|
||||
|
||||
x
|
||||
"#
|
||||
),
|
||||
"U32",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_char_pattern_as_u8() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
f : U8 -> _
|
||||
f = \c ->
|
||||
when c is
|
||||
'.' -> 'A'
|
||||
c1 -> c1
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
"U8 -> U8",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_char_pattern_as_u16() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
f : U16 -> _
|
||||
f = \c ->
|
||||
when c is
|
||||
'.' -> 'A'
|
||||
c1 -> c1
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
"U16 -> U16",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_char_pattern_as_u32() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
f : U32 -> _
|
||||
f = \c ->
|
||||
when c is
|
||||
'.' -> 'A'
|
||||
c1 -> c1
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
"U32 -> U32",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
|||
match e {
|
||||
Num(_, n, _, _) | Int(_, _, n, _, _) | Float(_, _, n, _, _) => f.text(&**n),
|
||||
Str(s) => f.text(format!(r#""{}""#, s)),
|
||||
SingleQuote(c) => f.text(format!("'{}'", c)),
|
||||
SingleQuote(_, _, c, _) => f.text(format!("'{}'", c)),
|
||||
List {
|
||||
elem_var: _,
|
||||
loc_elems,
|
||||
|
@ -366,7 +366,7 @@ fn pattern<'a>(
|
|||
f.text(&**n)
|
||||
}
|
||||
StrLiteral(s) => f.text(format!(r#""{}""#, s)),
|
||||
SingleQuote(c) => f.text(format!("'{}'", c)),
|
||||
SingleQuote(_, _, c, _) => f.text(format!("'{}'", c)),
|
||||
Underscore => f.text("_"),
|
||||
|
||||
Shadowed(_, _, _) => todo!(),
|
||||
|
|
|
@ -4067,3 +4067,21 @@ fn int_let_generalization() {
|
|||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn pattern_match_char() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
c = 'A'
|
||||
|
||||
when c is
|
||||
'A' -> "okay"
|
||||
_ -> "FAIL"
|
||||
"#
|
||||
),
|
||||
RocStr::from("okay"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
|
|
@ -328,6 +328,26 @@ pub enum NumBound {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum SingleQuoteBound {
|
||||
AtLeast { width: IntLitWidth },
|
||||
}
|
||||
|
||||
impl SingleQuoteBound {
|
||||
pub fn from_char(c: char) -> Self {
|
||||
let n = c as u32;
|
||||
let width = if n > u16::MAX as _ {
|
||||
IntLitWidth::U32
|
||||
} else if n > u8::MAX as _ {
|
||||
IntLitWidth::U16
|
||||
} else {
|
||||
IntLitWidth::U8
|
||||
};
|
||||
|
||||
Self::AtLeast { width }
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn int_lit_width_to_variable(w: IntLitWidth) -> Variable {
|
||||
match w {
|
||||
IntLitWidth::U8 => Variable::U8,
|
||||
|
|
|
@ -521,7 +521,7 @@ fn read_main_roc_file(project_dir_path_opt: Option<&Path>) -> (PathBuf, String)
|
|||
|
||||
// returns path and content of app file
|
||||
fn init_new_roc_project(project_dir_path: &Path) -> (PathBuf, String) {
|
||||
let orig_platform_path = Path::new("./examples/hello-world").join(PLATFORM_DIR_NAME);
|
||||
let orig_platform_path = Path::new("./examples/cli").join(PLATFORM_DIR_NAME);
|
||||
|
||||
let roc_file_path = Path::new("./new-roc-project/main.roc");
|
||||
|
||||
|
|
|
@ -730,16 +730,24 @@ test1 =
|
|||
file_to_string(&file_path)
|
||||
}
|
||||
|
||||
fn cli_testing_path(sub_path: &str) -> String {
|
||||
let examples_dir = "../cli_testing_examples/".to_string();
|
||||
|
||||
let file_path = examples_dir + sub_path;
|
||||
|
||||
file_to_string(&file_path)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hello() {
|
||||
let tokens = tokenize(&example_path("hello-world/main.roc"));
|
||||
let tokens = tokenize(&example_path("helloWorld.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fibo() {
|
||||
let tokens = tokenize(&example_path("algorithms/fibonacci.roc"));
|
||||
let tokens = tokenize(&cli_testing_path("algorithms/fibonacci.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
@ -831,14 +839,14 @@ test1 =
|
|||
|
||||
#[test]
|
||||
fn test_base64() {
|
||||
let tokens = tokenize(&example_path("benchmarks/Base64.roc"));
|
||||
let tokens = tokenize(&cli_testing_path("benchmarks/Base64.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_base64_test() {
|
||||
let tokens = tokenize(&example_path("benchmarks/TestBase64.roc"));
|
||||
let tokens = tokenize(&cli_testing_path("benchmarks/TestBase64.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
@ -874,14 +882,14 @@ test1 =
|
|||
|
||||
#[test]
|
||||
fn test_astar_test() {
|
||||
let tokens = tokenize(&example_path("benchmarks/TestAStar.roc"));
|
||||
let tokens = tokenize(&cli_testing_path("benchmarks/TestAStar.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cli_echo() {
|
||||
let tokens = tokenize(&example_path("interactive/echo.roc"));
|
||||
let tokens = tokenize(&example_path("cli/echo.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
@ -1106,7 +1114,7 @@ test1 =
|
|||
|
||||
#[test]
|
||||
fn test_closure_file() {
|
||||
let tokens = tokenize(&example_path("benchmarks/Closure.roc"));
|
||||
let tokens = tokenize(&cli_testing_path("benchmarks/Closure.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
@ -1126,7 +1134,7 @@ test1 =
|
|||
|
||||
#[test]
|
||||
fn test_nqueens() {
|
||||
let tokens = tokenize(&example_path("benchmarks/NQueens.roc"));
|
||||
let tokens = tokenize(&cli_testing_path("benchmarks/NQueens.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
@ -1149,7 +1157,7 @@ test1 =
|
|||
|
||||
#[test]
|
||||
fn test_quicksort() {
|
||||
let tokens = tokenize(&example_path("benchmarks/Quicksort.roc"));
|
||||
let tokens = tokenize(&cli_testing_path("benchmarks/Quicksort.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
@ -1176,7 +1184,7 @@ test1 =
|
|||
|
||||
#[test]
|
||||
fn test_task() {
|
||||
let tokens = tokenize(&example_path("benchmarks/platform/Task.roc"));
|
||||
let tokens = tokenize(&cli_testing_path("benchmarks/platform/Task.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
@ -1221,7 +1229,7 @@ test1 =
|
|||
|
||||
#[test]
|
||||
fn test_cfold() {
|
||||
let tokens = tokenize(&example_path("benchmarks/CFold.roc"));
|
||||
let tokens = tokenize(&cli_testing_path("benchmarks/CFold.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
@ -1291,7 +1299,7 @@ balance = \color ->
|
|||
|
||||
#[test]
|
||||
fn test_rbtree_insert() {
|
||||
let tokens = tokenize(&example_path("benchmarks/RBTreeInsert.roc"));
|
||||
let tokens = tokenize(&cli_testing_path("benchmarks/RBTreeInsert.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
@ -1357,7 +1365,7 @@ balance = \color ->
|
|||
|
||||
#[test]
|
||||
fn test_rbtree_ck() {
|
||||
let tokens = tokenize(&example_path("benchmarks/RBTreeCk.roc"));
|
||||
let tokens = tokenize(&cli_testing_path("benchmarks/RBTreeCk.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
@ -1390,7 +1398,7 @@ balance = \color ->
|
|||
|
||||
#[test]
|
||||
fn test_astar() {
|
||||
let tokens = tokenize(&example_path("benchmarks/AStar.roc"));
|
||||
let tokens = tokenize(&cli_testing_path("benchmarks/AStar.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
@ -1409,7 +1417,7 @@ balance = \color ->
|
|||
|
||||
#[test]
|
||||
fn test_false_interpreter_context() {
|
||||
let tokens = tokenize(&example_path("false-interpreter/Context.roc"));
|
||||
let tokens = tokenize(&example_path("cli/false-interpreter/Context.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
@ -1437,7 +1445,7 @@ balance = \color ->
|
|||
|
||||
#[test]
|
||||
fn test_deriv() {
|
||||
let tokens = tokenize(&example_path("benchmarks/Deriv.roc"));
|
||||
let tokens = tokenize(&cli_testing_path("benchmarks/Deriv.roc"));
|
||||
|
||||
assert_eq!(tokenparser::module(&tokens), Ok(()));
|
||||
}
|
||||
|
|
|
@ -11072,4 +11072,64 @@ All branches in an `if` must have the same type!
|
|||
U8
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
big_char_does_not_fit_in_u8,
|
||||
indoc!(
|
||||
r#"
|
||||
digits : List U8
|
||||
digits = List.range '0' '9'
|
||||
|
||||
List.contains digits '☃'
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||
|
||||
This 2nd argument to `contains` has an unexpected type:
|
||||
|
||||
7│ List.contains digits '☃'
|
||||
^^^^^
|
||||
|
||||
The argument is a Unicode scalar value of type:
|
||||
|
||||
U16, I32, U32, I64, Nat, U64, I128, or U128
|
||||
|
||||
But `contains` needs its 2nd argument to be:
|
||||
|
||||
U8
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
big_char_does_not_fit_in_u8_pattern,
|
||||
indoc!(
|
||||
r#"
|
||||
x : U8
|
||||
|
||||
when x is
|
||||
'☃' -> ""
|
||||
_ -> ""
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||
|
||||
The branches of this `when` expression don't match the condition:
|
||||
|
||||
6│> when x is
|
||||
7│ '☃' -> ""
|
||||
8│ _ -> ""
|
||||
|
||||
This `x` value is a:
|
||||
|
||||
U8
|
||||
|
||||
But the branch patterns have type:
|
||||
|
||||
U16, I32, U32, I64, Nat, U64, I128, or U128
|
||||
|
||||
The branches must be cases of the `when` condition's type!
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
|
2
examples/.gitignore
vendored
2
examples/.gitignore
vendored
|
@ -4,3 +4,5 @@ libapp.so
|
|||
dynhost
|
||||
preprocessedhost
|
||||
metadata
|
||||
|
||||
helloWorld
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue