Merge remote-tracking branch 'upstream' into annotate-type-signatures

This commit is contained in:
snobee 2025-01-15 21:15:32 -08:00
commit 406cc6c5e9
No known key found for this signature in database
GPG key ID: ABF756C92D69FDF1
1204 changed files with 34365 additions and 39684 deletions

View file

@ -0,0 +1,38 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/rust
{
"name": "Rust",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/rust:1-1-bullseye",
// Use 'mounts' to make the cargo cache persistent in a Docker Volume.
// "mounts": [
// {
// "source": "devcontainer-cargo-cache-${devcontainerId}",
// "target": "/usr/local/cargo",
// "type": "volume"
// }
// ]
// Features to add to the dev container. More info: https://containers.dev/features.
"features": {
"ghcr.io/devcontainers-contrib/features/zig:1": {
"version": "0.13.0"
},
"ghcr.io/devcontainers-community/features/llvm:3": {
"version": 18
}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "rustc --version",
"onCreateCommand": "sudo apt-get update && sudo apt-get install -y pkg-config libz-dev libzstd-dev valgrind"
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

View file

@ -1,5 +1,5 @@
on:
# pull_request:
# pull_request:
workflow_dispatch:
# this cancels workflows currently in progress if you start a new one
@ -11,7 +11,7 @@ env:
# use .tar.gz for quick testing
ARCHIVE_FORMAT: .tar.br
# Make a new basic-cli git tag and set it here before starting this workflow
RELEASE_TAG: 0.16.0
RELEASE_TAG: 0.18.0
jobs:
prepare:
@ -96,7 +96,7 @@ jobs:
basic-cli/platform/linux-arm64.a
build-macos-x86_64-files:
runs-on: [macos-12] # I expect the generated files to work on macOS 12 and up
runs-on: [macos-13] # I expect the generated files to work on macOS 13 and up
needs: [prepare]
steps:
- uses: actions/checkout@v4
@ -104,6 +104,8 @@ jobs:
- name: Download the previously uploaded roc_nightly archives
uses: actions/download-artifact@v4
- run: brew install z3
- run: ./ci/build_basic_cli.sh macos_x86_64
- name: Save .a file
@ -203,15 +205,29 @@ jobs:
path: |
docs.tar.gz
test-release-ubuntu:
test-release:
needs: [create-release-archive]
runs-on: [ubuntu-20.04]
strategy:
matrix:
os: [ubuntu-20.04, macos-13, macos-14]
runs-on: ${{ matrix.os }}
steps:
- name: Download the previously uploaded files
uses: actions/download-artifact@v4
- name: Set OS-specific variables
id: vars
run: |
if [[ "${{ matrix.os }}" =~ ^ubuntu- ]]; then
echo "os_pattern=linux_x86_64" >> $GITHUB_OUTPUT
elif [ "${{ matrix.os }}" = "macos-13" ]; then
echo "os_pattern=macos_x86_64" >> $GITHUB_OUTPUT
else
echo "os_pattern=macos_apple_silicon" >> $GITHUB_OUTPUT
fi
- name: mv roc nightly and simplify name
run: mv $(ls -d artifact/* | grep "roc_nightly.*tar\.gz" | grep "linux_x86_64") ./roc_nightly.tar.gz
run: mv $(ls -d artifact/* | grep "roc_nightly.*tar\.gz" | grep "${{ steps.vars.outputs.os_pattern }}") ./roc_nightly.tar.gz
- name: decompress the tar
run: tar -xzvf roc_nightly.tar.gz
@ -231,11 +247,18 @@ jobs:
cd basic-cli-platform && ls | grep "tar" | xargs brotli -d
ls | grep "tar$" | xargs tar -xf
- name: Install expect for tests if we dont have it yet
run: if ! dpkg -l | grep -qw expect; then sudo apt install -y expect; fi
- name: Install dependencies (Ubuntu)
if: startsWith(matrix.os, 'ubuntu-')
run: |
if ! dpkg -l | grep -qw expect; then sudo apt install -y expect; fi
if ! dpkg -l | grep -qw ncat; then sudo apt install -y ncat; fi
- name: Install ncat for tests if we dont have it yet
run: if ! dpkg -l | grep -qw ncat; then sudo apt install -y ncat; fi
- name: Install dependencies (macOS)
if: startsWith(matrix.os, 'macos-')
run: |
brew install expect
brew install nmap # includes ncat
brew install z3
- name: prepare testing
run: |

View file

@ -124,10 +124,10 @@ jobs:
if: needs.check-changes.outputs.run_tests == 'full'
uses: ./.github/workflows/benchmarks.yml
start-panic-check:
start-fuzzer-tests:
needs: check-changes
if: needs.check-changes.outputs.run_tests == 'full'
uses: ./.github/workflows/improve_panics.yml
uses: ./.github/workflows/fuzzer.yml
ran-full:
runs-on: ubuntu-22.04
@ -141,8 +141,7 @@ jobs:
start-ubuntu-x86-64-nix-tests-debug,
start-windows-release-build-test,
start-windows-tests,
start-roc-benchmarks,
start-panic-check
start-roc-benchmarks
]
steps:
- run: echo "all workflows succeeded!"

20
.github/workflows/fuzzer.yml vendored Normal file
View file

@ -0,0 +1,20 @@
on:
workflow_call:
name: CI
env:
RUST_BACKTRACE: 1
jobs:
fuzz-test-parser:
name: Fuzz test parser (allowed to fail). Do check this if you've made changes to the parser.
runs-on: [self-hosted, i7-6700K]
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: run fuzz tests - ok to merge if this one fails # for now!
run: |
cargo +nightly-2024-02-03 install --locked cargo-fuzz
cd crates/compiler/test_syntax/fuzz && cargo +nightly-2024-02-03 fuzz run -j4 fuzz_expr --sanitizer=none -- -dict=dict.txt -max_total_time=60

View file

@ -1,56 +0,0 @@
on:
workflow_call:
# Check that the number of panicking function/macro calls has not increased
# https://github.com/roc-lang/roc/issues/2046
name: Improve panics/unwrap/expect
jobs:
improve-panics:
name: improve panics
runs-on: [ubuntu-22.04]
timeout-minutes: 15
env:
FORCE_COLOR: 1
RUST_BACKTRACE: 1
steps:
# install nix
- uses: cachix/install-nix-action@v23
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: checkout source branch in separate directory
uses: actions/checkout@v4
with:
clean: true
path: source_branch_dir
ref: ${{ github.ref }}
- name: checkout target branch in separate directory
uses: actions/checkout@v4
with:
clean: false
path: target_branch_dir
ref: ${{ github.base_ref }}
- name: Compare panics, expects, and unwraps between source and target brach
run: |
for branch_dir in source_branch_dir target_branch_dir; do
cd $branch_dir
echo "Calculating violations in &branch_dir..."
VIOLATIONS=$(nix develop -c cargo clippy --no-deps -- -W clippy::unwrap_used -W clippy::expect_used -W clippy::panic 2> >(grep -e "warning: \`panic\`" -e "warning: used" -e "warning: usage of" | wc -l ))
echo $VIOLATIONS > violations
cd ..
done
SOURCE_VIOLATIONS=$(cat source_branch_dir/violations)
TARGET_VIOLATIONS=$(cat target_branch_dir/violations)
if [ "$SOURCE_VIOLATIONS" -gt "$TARGET_VIOLATIONS" ]; then
echo "You added panic/unwrap/expect, in this PR their count increased from $TARGET_VIOLATIONS to $SOURCE_VIOLATIONS."
echo "These calls can kill the REPL, try alternative error handling."
echo ""
echo "TIP: Ask AI \"In rust, how can I rewrite code that contains panic or unwrap so it doesn't crash?\""
echo ""
echo "If you believe your panic/unwrap/expect is justified, ask Anton-4, rtfeldman or lukewilliamboswell to bypass this workflow failure."
exit 1
fi

View file

@ -31,6 +31,4 @@ jobs:
run: cargo nextest-gen-llvm --release --no-fail-fast --locked -E "package(test_gen) - test(gen_str::str_append_scalar)"
- name: regular rust tests
run: cargo test --locked --release -- --skip opaque_wrap_function --skip gen_list::bool_list_literal --skip platform_switching_swift --skip swift_ui --skip gen_tags::phantom_polymorphic_record && sccache --show-stats
# swift tests are skipped because of "Could not find or use auto-linked library 'swiftCompatibilityConcurrency'" on macos x86_64 CI machine
# this issue may be caused by using older versions of XCode
run: cargo test --locked --release -- --skip opaque_wrap_function --skip gen_list::bool_list_literal --skip gen_tags::phantom_polymorphic_record && sccache --show-stats

View file

@ -1,5 +1,5 @@
on:
#pull_request:
# pull_request:
workflow_dispatch:
schedule:
- cron: "0 9 * * *"

View file

@ -27,9 +27,7 @@ jobs:
run: ./ci/write_version.sh
- name: execute rust tests
run: cargo test --release --locked -- --skip opaque_wrap_function --skip gen_list::bool_list_literal --skip platform_switching_swift --skip swift_ui --skip gen_tags::phantom_polymorphic_record
# swift tests are skipped because of "Could not find or use auto-linked library 'swiftCompatibilityConcurrency'" on macos x86_64 CI machine
# this issue may be caused by using older versions of XCode
run: cargo test --release --locked -- --skip opaque_wrap_function --skip gen_list::bool_list_literal --skip gen_tags::phantom_polymorphic_record
- name: build release
run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --profile=release-with-lto --locked --bin roc --bin roc_language_server

View file

@ -18,22 +18,22 @@ jobs:
run: nix-build
- name: execute tests with --release
# skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step
# skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step, see #7476
run: nix develop -c cargo test --locked --release -- --skip glue_cli_tests
- name: glue_cli_tests
# single threaded due to difficult bug when multithreading
# single threaded due to difficult bug when multithreading, see #7476
run: nix develop -c cargo test --locked --release glue_cli_tests -- --test-threads=1
- name: roc test all builtins
run: nix develop -c ./ci/roc_test_builtins.sh
- name: test wasm32 cli_tests
# skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step
# skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step, see #7476
run: nix develop -c cargo test --locked --release --features="wasm32-cli-run" -- --skip glue_cli_tests
- name: wasm32 glue_cli_tests
# single threaded due to difficult bug when multithreading
# single threaded due to difficult bug when multithreading, see #7476
run: nix develop -c cargo test --locked --release --features="wasm32-cli-run" glue_cli_tests -- --test-threads=1
- name: test the dev backend # these tests require an explicit feature flag

View file

@ -11,6 +11,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Update PATH to use zig 13
run: echo "PATH=/Users/m1ci/Downloads/zig-macos-aarch64-0.13.0:$PATH" >> $GITHUB_ENV
- run: zig version
- name: get the latest release archive
run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_apple_silicon-latest.tar.gz

View file

@ -1,4 +1,5 @@
on:
# pull_request:
workflow_dispatch:
name: Test latest nightly releases for macOS and Linux x86_64
@ -18,9 +19,9 @@ jobs:
with:
version: 0.13.0
- name: Install zlib on macOS-13
- name: Install z3 on macOS-13
if: matrix.os == 'macos-13'
run: brew install zlib
run: brew install z3
- name: get the latest release archive for linux (x86_64)
if: startsWith(matrix.os, 'ubuntu')

View file

@ -63,7 +63,7 @@ jobs:
- name: test website build script
run: bash www/build.sh
- name: run fuzz tests - ok to merge if this one fails # for now!
run: |
cargo +nightly-2024-02-03 install --locked cargo-fuzz
cd crates/compiler/test_syntax/fuzz && cargo +nightly-2024-02-03 fuzz run -j4 fuzz_expr --sanitizer=none -- -dict=dict.txt -max_total_time=60
#- name: run fuzz tests - ok to merge if this one fails # for now!
# run: |
# cargo +nightly-2024-02-03 install --locked cargo-fuzz
# cd crates/compiler/test_syntax/fuzz && cargo +nightly-2024-02-03 fuzz run -j4 fuzz_expr --sanitizer=none -- -dict=dict.txt -max_total_time=60

View file

@ -22,6 +22,10 @@ jobs:
- name: Check if debug flag files are in sync
run: ./ci/check_debug_vars.sh
# for skipped tests; see #6946, #6947
- name: cargo test without --release
run: nix develop -c sh -c 'export ROC_CHECK_MONO_IR=1 && cargo test'
# skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step, see #7476
run: nix develop -c sh -c 'export ROC_CHECK_MONO_IR=1 && cargo test --locked -- --skip glue_cli_tests'
- name: glue_cli_tests
# single threaded due to difficult bug when multithreading, see #7476
run: nix develop -c sh -c 'export ROC_CHECK_MONO_IR=1 && cargo test --locked glue_cli_tests -- --test-threads=1'

View file

@ -1,21 +1,24 @@
name: deploy www.roc-lang.org
# Whenever a commit lands on `main`, deploy the site
on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Whenever a commit lands on `main`, deploy the site
push:
branches:
- deploy-www
jobs:
deploy:
name: 'Deploy to Netlify'
name: "Deploy to Netlify"
runs-on: [self-hosted, linux]
steps:
- uses: jsmrcaga/action-netlify-deploy@v1.6.0
with:
install_command: 'pwd; cd ../../www'
build_command: 'bash build.sh'
build_directory: 'build'
install_command: "pwd; cd ../../www"
build_command: "bash build.sh"
build_directory: "build"
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
NETLIFY_DEPLOY_MESSAGE: "Deploy git ref ${{ github.ref }}"

View file

@ -170,3 +170,4 @@ Isak Jones <isak.jones.980@gmail.com>
Ch1n3du <danielonyesoh@gmail.com>
Elias Mulhall <eli.mulhall@gmail.com>
ABuffSeagull <reecevanatta@hey.com>
Timon Krebs <timonkrebs@hotmail.com>

View file

@ -2,6 +2,17 @@
If you run into any problems getting Roc built from source, please ask for help in the `#beginners` channel on [Roc Zulip](https://roc.zulipchat.com) (the fastest way), or create an issue in this repo!
## Using Devcontainer
### Codespaces
To access the browser-based editor, you can change the URL of your repository to https://github.dev/roc-lang/roc, replacing github.com with github.dev or go to the roc repo on https://github.com/roc-lang/roc and press . (period key).
Or use it locally in [VSCode](https://code.visualstudio.com/docs/remote/codespaces)
### Docker
Or even run it in your local Docker environment [Developing inside a Container](https://code.visualstudio.com/docs/devcontainers/containers)
## Using Nix
On MacOS and Linux, we highly recommend Using [nix](https://nixos.org/download.html) to quickly install all dependencies necessary to build roc.
@ -111,24 +122,24 @@ sudo apt-get install libz-dev libzstd-dev
### Zig
**version: 0.11.0**
**version: 0.13.0**
For any OS, you can use [`zigup`](https://github.com/marler8997/zigup) to manage zig installations.
If you prefer a package manager, you can try the following:
- MacOS: `brew install zig@0.11.0`
- MacOS: `brew install zig`
- Systems with snap (such as Ubuntu): `snap install zig --classic --beta`
- Other systems: refer to the [zig documentation](https://github.com/ziglang/zig/wiki/Install-Zig-from-a-Package-Manager)
If you want to install it manually, you can [download the binary](https://ziglang.org/download/#release-0.11.0) and place it on your PATH.
If you want to install it manually, you can [download the binary](https://ziglang.org/download/#release-0.13.0) and place it on your PATH.
Apart from the binary, the archive contains a `lib` folder, which needs to be copied next to the binary.
> WINDOWS NOTE: when you unpack the Zig archive on windows, the result is nested in an extra directory. The instructions on the zig website will seem to not work. So, double-check that the path to zig executable does not include the same directory name twice.
### LLVM
**version: 16.0.x**
**version: 18.0.x**
See below for operating system specific installation instructions.
@ -140,7 +151,7 @@ To use the `repl` subcommand, execute `cargo run repl`.
The default is a developer build. For an optimized build, use:
```
```sh
cargo build --release --bin roc
```
@ -156,10 +167,10 @@ chmod +x llvm.sh
```
If you use this script, you'll need to add `clang` to your `PATH`.
By default, the script installs it as `clang-16`. You can address this with symlinks like so:
By default, the script installs it as `clang-18`. You can address this with symlinks like so:
```sh
sudo ln -s /usr/bin/clang-16 /usr/bin/clang
sudo ln -s /usr/bin/clang-18 /usr/bin/clang
```
There are also alternative installation options at <http://releases.llvm.org/download.html>
@ -192,7 +203,7 @@ Add `export LLVM_SYS_180_PREFIX=/usr/lib/llvm-18` to your `~/.bashrc` or equival
For macOS, you can install LLVM 18 using `brew install llvm@18` and then adding
`$(brew --prefix llvm@18)/bin` to your `PATH`. You can confirm this worked by
running `llc --version` - it should mention "LLVM version 16.0.x" at the top.
running `llc --version` - it should mention "LLVM version 18.x.x" at the top.
You may also need to manually specify a prefix env var like so:
```sh
@ -250,8 +261,8 @@ Create `~/.cargo/config.toml` if it does not exist and add this to it:
rustflags = ["-C", "link-arg=-fuse-ld=lld", "-C", "target-cpu=native"]
```
Then install `lld` version 16 (e.g. with `$ sudo apt-get install lld-16`)
Then install `lld` version 18 (e.g. with `$ sudo apt-get install lld-18`)
and add make sure there's a `ld.lld` executable on your `PATH` which
is symlinked to `lld-16`.
is symlinked to `lld-18`.
That's it! Enjoy the faster builds.

View file

@ -68,6 +68,8 @@ programs.gnupg.agent = {
<details>
<summary>Forgot to sign commits?</summary>
:exclamation: Make sure [to set up signing on your device](devtools/signing.md) first, then continue below.
You can view your commits on github, those without the "Verified" badge still need to be signed.
If any of those is a merge commit, follow [these steps](https://stackoverflow.com/a/9958215/4200103) instead of the ones below.

43
Cargo.lock generated
View file

@ -216,7 +216,7 @@ dependencies = [
"cfg-if",
"libc",
"miniz_oxide",
"object",
"object 0.32.2",
"rustc-demangle",
]
@ -724,7 +724,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [
"cfg-if",
"hashbrown",
"hashbrown 0.14.3",
"lock_api",
"once_cell",
"parking_lot_core",
@ -932,6 +932,12 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
[[package]]
name = "form_urlencoded"
version = "1.2.0"
@ -1117,6 +1123,15 @@ dependencies = [
"allocator-api2",
]
[[package]]
name = "hashbrown"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
dependencies = [
"foldhash",
]
[[package]]
name = "heck"
version = "0.4.1"
@ -1302,7 +1317,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
"hashbrown 0.14.3",
]
[[package]]
@ -1719,9 +1734,18 @@ name = "object"
version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [
"memchr",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"crc32fast",
"hashbrown",
"hashbrown 0.15.2",
"indexmap",
"memchr",
]
@ -2368,7 +2392,6 @@ dependencies = [
"bitvec",
"bumpalo",
"indoc",
"insta",
"pretty_assertions",
"roc_collections",
"roc_error_macros",
@ -2468,7 +2491,7 @@ dependencies = [
"bitvec",
"bumpalo",
"fnv",
"hashbrown",
"hashbrown 0.14.3",
"im",
"im-rc",
"smallvec",
@ -2598,7 +2621,7 @@ version = "0.0.1"
dependencies = [
"bumpalo",
"capstone",
"object",
"object 0.36.7",
"packed_struct",
"roc_builtins",
"roc_can",
@ -2759,7 +2782,7 @@ dependencies = [
"libc",
"mach_object",
"memmap2 0.5.10",
"object",
"object 0.36.7",
"roc_collections",
"roc_error_macros",
"roc_load",
@ -2877,7 +2900,7 @@ dependencies = [
"arrayvec 0.7.4",
"bitvec",
"bumpalo",
"hashbrown",
"hashbrown 0.14.3",
"indoc",
"parking_lot",
"roc_builtins",
@ -3179,7 +3202,7 @@ dependencies = [
"arrayvec 0.7.4",
"bitvec",
"bumpalo",
"hashbrown",
"hashbrown 0.14.3",
"indoc",
"parking_lot",
"pretty_assertions",

View file

@ -1,5 +1,6 @@
[workspace]
members = [
"crates/build/specialize_types",
"crates/compiler/*",
"crates/vendor/*",
"crates/fs",
@ -56,7 +57,7 @@ repository = "https://github.com/roc-lang/roc"
version = "0.0.1"
[workspace.dependencies]
object = { version = "0.32.2", default-features = false, features = [
object = { version = "0.36.7", default-features = false, features = [
"read",
"write",
] }
@ -212,7 +213,7 @@ roc_serialize = { path = "crates/compiler/serialize" }
roc_solve = { path = "crates/compiler/solve" }
roc_solve_schema = { path = "crates/compiler/solve_schema" }
roc_solve_problem = { path = "crates/compiler/solve_problem" }
roc_specialize_types = { path = "crates/compiler/specialize_types" }
roc_specialize_types = { path = "crates/build/specialize_types" }
roc_std = { path = "crates/roc_std" }
roc_target = { path = "crates/compiler/roc_target" }
roc_test_utils = { path = "crates/test_utils" }

View file

@ -16,20 +16,20 @@ install-zig-llvm:
ARG ZIG_ARCH
FROM +install-other-libs
# zig
RUN wget -c https://ziglang.org/download/0.11.0/zig-linux-$ZIG_ARCH-0.11.0.tar.xz --no-check-certificate
RUN tar -xf zig-linux-$ZIG_ARCH-0.11.0.tar.xz
RUN ln -s /earthbuild/zig-linux-$ZIG_ARCH-0.11.0/zig /bin/zig
RUN wget -c https://ziglang.org/download/0.13.0/zig-linux-$ZIG_ARCH-0.13.0.tar.xz --no-check-certificate
RUN tar -xf zig-linux-$ZIG_ARCH-0.13.0.tar.xz
RUN ln -s /earthbuild/zig-linux-$ZIG_ARCH-0.13.0/zig /bin/zig
# zig builtins wasm tests
RUN apt -y install build-essential
# llvm
RUN apt -y install lsb-release software-properties-common gnupg
RUN wget https://apt.llvm.org/llvm.sh
RUN chmod +x llvm.sh
RUN ./llvm.sh 16
RUN ln -s /usr/bin/clang-16 /usr/bin/clang
RUN ./llvm.sh 18
RUN ln -s /usr/bin/clang-18 /usr/bin/clang
# use lld as linker
RUN ln -s /usr/bin/lld-16 /usr/bin/ld.lld
RUN apt -y install libpolly-16-dev # required by llvm-sys crate
RUN ln -s /usr/bin/lld-18 /usr/bin/ld.lld
RUN apt -y install libpolly-18-dev # required by llvm-sys crate
ENV RUSTFLAGS="-C link-arg=-fuse-ld=lld -C target-cpu=native"
RUN apt -y install libssl-dev
RUN wget https://rustwasm.github.io/wasm-pack/installer/init.sh -O init.sh && sh init.sh

View file

@ -54,6 +54,6 @@ fi
./jump-start.sh
# build the basic cli platform
roc build.roc
roc build.roc --linker=legacy
cd ..

View file

@ -7,14 +7,22 @@ set -euxo pipefail
strip ./target/release-with-lto/roc
strip ./target/release-with-lto/roc_language_server
mkdir -p $1 $1/examples $1/crates/compiler/builtins/bitcode
mkdir -p $1 $1/examples
mv target/release-with-lto/{roc,roc_language_server,lib} $1
mv LICENSE LEGAL_DETAILS $1
mv examples/{platform-switching,cli} $1/examples
mv crates/cli/tests/platform-switching $1/examples
mv examples/README.md $1/examples
mv crates/roc_std $1/crates
mv crates/compiler/builtins/bitcode/src $1/crates/compiler/builtins/bitcode
# temporary github.com/roc-lang/roc/pull/7231
rm $1/examples/platform-switching/rocLovesRust.roc
rm -rf $1/examples/platform-switching/rust-platform
# copy zig builtins
if [ ! -d "$1/examples/platform-switching/zig-platform/glue" ]; then
mkdir $1/examples/platform-switching/zig-platform/glue
mv crates/compiler/builtins/bitcode/src/* $1/examples/platform-switching/zig-platform/glue
fi
tar -czvf "$1.tar.gz" $1

View file

@ -8,6 +8,7 @@ mod mono_expr;
mod mono_ir;
mod mono_module;
mod mono_num;
mod mono_pattern;
mod mono_struct;
mod mono_type;
// mod specialize_expr;
@ -16,9 +17,10 @@ mod specialize_type;
pub use debug_info::DebugInfo;
pub use foreign_symbol::{ForeignSymbolId, ForeignSymbols};
pub use mono_expr::Env;
pub use mono_ir::{MonoExpr, MonoExprId, MonoExprs};
pub use mono_ir::{MonoExpr, MonoExprId, MonoExprs, WhenBranches};
pub use mono_module::{InternedStrId, Interns};
pub use mono_num::Number;
pub use mono_pattern::{MonoPattern, MonoPatternId, MonoPatterns};
pub use mono_struct::MonoFieldId;
pub use mono_type::{MonoType, MonoTypeId, MonoTypes, Primitive};
pub use specialize_type::{MonoTypeCache, Problem, RecordFieldIds, TupleElemIds};

View file

@ -1,13 +1,16 @@
use crate::{
mono_ir::{MonoExpr, MonoExprId, MonoExprs},
mono_ir::{MonoExpr, MonoExprId, MonoExprs, WhenBranch, WhenBranches},
mono_module::Interns,
mono_num::Number,
mono_type::{MonoType, MonoTypes, Primitive},
specialize_type::{MonoTypeCache, Problem, RecordFieldIds, TupleElemIds},
DebugInfo, MonoTypeId,
DebugInfo, MonoPattern, MonoPatterns, MonoTypeId,
};
use bumpalo::{collections::Vec, Bump};
use roc_can::expr::{Expr, IntValue};
use roc_can::{
expr::{Expr, IntValue},
pattern::Pattern,
};
use roc_collections::{Push, VecMap};
use roc_module::symbol::ModuleId;
use roc_region::all::Region;
@ -44,12 +47,14 @@ impl MonoFnCache {
}
}
pub struct Env<'a, 'c, 'd, 'i, 's, 't, P> {
pub struct Env<'a, 'c, 'd, 'i, 's, 't, 'p, 'w, P> {
arena: &'a Bump,
subs: &'s mut Subs,
types_cache: &'c mut MonoTypeCache,
mono_types: &'t mut MonoTypes,
mono_exprs: &'t mut MonoExprs,
mono_patterns: &'p mut MonoPatterns,
when_branches: &'w mut WhenBranches,
record_field_ids: RecordFieldIds,
tuple_elem_ids: TupleElemIds,
debug_info: &'d mut Option<DebugInfo>,
@ -57,13 +62,15 @@ pub struct Env<'a, 'c, 'd, 'i, 's, 't, P> {
problems: P,
}
impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
impl<'a, 'c, 'd, 'i, 's, 't, 'p, 'w, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, 'p, 'w, P> {
pub fn new(
arena: &'a Bump,
subs: &'s mut Solved<Subs>,
types_cache: &'c mut MonoTypeCache,
mono_types: &'t mut MonoTypes,
mono_exprs: &'t mut MonoExprs,
mono_patterns: &'p mut MonoPatterns,
when_branches: &'w mut WhenBranches,
record_field_ids: RecordFieldIds,
tuple_elem_ids: TupleElemIds,
string_interns: &'i mut Interns<'a>,
@ -76,6 +83,8 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
types_cache,
mono_types,
mono_exprs,
mono_patterns,
when_branches,
record_field_ids,
tuple_elem_ids,
string_interns,
@ -84,25 +93,23 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
}
}
pub fn to_mono_expr(&mut self, can_expr: &Expr) -> MonoExpr {
let problems = &mut self.problems;
let mono_types = &mut self.mono_types;
let mut mono_from_var = |var| {
self.types_cache.monomorphize_var(
self.arena,
self.subs,
mono_types,
&mut self.record_field_ids,
&mut self.tuple_elem_ids,
problems,
self.debug_info,
var,
)
};
fn mono_from_var(&mut self, var: Variable) -> MonoTypeId {
self.types_cache.monomorphize_var(
self.arena,
self.subs,
self.mono_types,
&mut self.record_field_ids,
&mut self.tuple_elem_ids,
&mut self.problems,
self.debug_info,
var,
)
}
pub fn to_mono_expr(&mut self, can_expr: &Expr) -> MonoExpr {
macro_rules! compiler_bug {
($problem:expr) => {{
problems.push($problem);
self.problems.push($problem);
MonoExpr::CompilerBug($problem)
}};
}
@ -115,9 +122,11 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
MonoExpr::Number(Number::Dec(*val))
}
_ => {
let mono_type = mono_from_var(*var);
match mono_types.get(mono_type) {
MonoType::Primitive(primitive) => to_frac(*primitive, *val, problems),
let mono_type = self.mono_from_var(*var);
match self.mono_types.get(mono_type) {
MonoType::Primitive(primitive) => {
to_frac(*primitive, *val, &mut self.problems)
}
other => {
compiler_bug!(Problem::NumSpecializedToWrongType(Some(*other)))
}
@ -126,23 +135,28 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
}
}
Expr::Num(var, _, int_value, _) | Expr::Int(var, _, _, int_value, _) => {
let mono_type = mono_from_var(*var);
let mono_type = self.mono_from_var(*var);
// Number literals and int literals both specify integer numbers, so to_num() can work on both.
match mono_types.get(mono_type) {
MonoType::Primitive(primitive) => to_num(*primitive, *int_value, problems),
match self.mono_types.get(mono_type) {
MonoType::Primitive(primitive) => match to_num(*primitive, *int_value) {
Ok(num) => MonoExpr::Number(num),
Err(problem) => compiler_bug!(problem),
},
other => compiler_bug!(Problem::NumSpecializedToWrongType(Some(*other))),
}
}
Expr::SingleQuote(var, _, char, _) => {
let mono_type = mono_from_var(*var);
let mono_type = self.mono_from_var(*var);
// Single-quote characters monomorphize to an integer.
// TODO [mono2] if we store these using the same representation as other ints (e.g. Expr::Int,
// or keeping a separate value but storing an IntValue instead of a char), then
// even though we verify them differently, we can combine this branch with Num and Int.
match mono_types.get(mono_type) {
MonoType::Primitive(primitive) => char_to_int(*primitive, *char, problems),
match self.mono_types.get(mono_type) {
MonoType::Primitive(primitive) => {
char_to_int(*primitive, *char, &mut self.problems)
}
other => compiler_bug!(Problem::CharSpecializedToWrongType(Some(*other))),
}
}
@ -200,7 +214,7 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
branches,
final_else,
} => {
let branch_type = mono_from_var(*branch_var);
let branch_type = self.mono_from_var(*branch_var);
let mono_final_else = self.to_mono_expr(&final_else.value);
let final_else = self.mono_exprs.add(mono_final_else, final_else.region);
@ -224,10 +238,68 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
}
}
Expr::Var(symbol, var) | Expr::ParamsVar { symbol, var, .. } => {
MonoExpr::Lookup(*symbol, mono_from_var(*var))
MonoExpr::Lookup(*symbol, self.mono_from_var(*var))
}
Expr::When {
loc_cond,
cond_var,
expr_var,
region: _,
branches,
branches_cond_var: _,
exhaustive: _,
} => {
let value_type = self.mono_from_var(*cond_var);
let branch_type = self.mono_from_var(*expr_var);
let value = self.to_mono_expr(&loc_cond.value);
let value_id = self.mono_exprs.add(value, loc_cond.region);
let branches_slice = self.when_branches.reserve(branches.len());
for (branch_index, branch) in branches_slice.into_iter().zip(branches.iter()) {
let patterns_slice = self.mono_patterns.reserve(branch.patterns.len());
for (pattern_id, pattern) in
patterns_slice.into_iter().zip(branch.patterns.iter())
{
let mono_pattern = self.to_mono_pattern(&pattern.pattern.value);
self.mono_patterns
.insert(pattern_id, mono_pattern, pattern.pattern.region);
}
let value = self.to_mono_expr(&branch.value.value);
let Some(patterns_slice) = NonEmptySlice::from_slice(patterns_slice) else {
return compiler_bug!(Problem::WhenBranchHasNoPatterns);
};
let guard = branch.guard.as_ref().map(|guard| {
let mono_guard = self.to_mono_expr(&guard.value);
self.mono_exprs.add(mono_guard, guard.region)
});
let mono_branch = WhenBranch {
patterns: patterns_slice,
guard,
value: self.mono_exprs.add(value, branch.value.region),
};
self.when_branches.insert(branch_index, mono_branch);
}
let Some(branches_slice) = NonEmptySlice::from_slice(branches_slice) else {
return compiler_bug!(Problem::WhenHasNoBranches);
};
MonoExpr::When {
value: value_id,
value_type,
branch_type,
branches: branches_slice,
}
}
// Expr::Call((fn_var, fn_expr, capture_var, ret_var), args, called_via) => {
// let opt_ret_type = mono_from_var(*var);
// let opt_ret_type = self.mono_from_var(*var);
// if opt_ret_type.is_none() {
// let fn_type = match self.subs.get_content_without_compacting(fn_var) {
@ -262,7 +334,7 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
// let todo = (); // TODO this is where we need to specialize, which means...duplicating the fn expr body maybe? and caching it under the mono type?
// let fn_expr = self.to_mono_expr(can_expr, stmts)?;
// let args = todo!(); // TODO compute the args. This is tricky because of preallocated slices!
// let capture_type = mono_from_var(*capture_var);
// let capture_type = self.mono_from_var(*capture_var);
// let todo = (); // How do we pre-reserve the statements? Is that possible? It does seem necessary...might not be possible though. Maybe we just need to make Vec rather than Slice on these.
@ -276,11 +348,11 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
// None
// } else {
// let fn_type = mono_from_var(*fn_var)?;
// let fn_type = self.mono_from_var(*fn_var)?;
// let todo = (); // TODO this is where we need to specialize, which means...duplicating the fn expr body maybe? and caching it under the mono type?
// let fn_expr = self.to_mono_expr(can_expr, stmts)?;
// let args = todo!(); // TODO compute the args. This is tricky because of preallocated slices!
// let capture_type = mono_from_var(*capture_var);
// let capture_type = self.mono_from_var(*capture_var);
// Some(MonoExpr::Call {
// fn_type,
@ -320,15 +392,6 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
// params_var,
// } => todo!(),
// Expr::AbilityMember(symbol, specialization_id, variable) => todo!(),
// Expr::When {
// loc_cond,
// cond_var,
// expr_var,
// region,
// branches,
// branches_cond_var,
// exhaustive,
// } => todo!(),
// Expr::Call(_, vec, called_via) => todo!(),
// Expr::RunLowLevel { op, args, ret_var } => todo!(),
// Expr::ForeignCall {
@ -405,36 +468,69 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
// }
}
}
pub fn to_mono_pattern(&mut self, can_pattern: &Pattern) -> MonoPattern {
match can_pattern {
Pattern::Identifier(ident) => MonoPattern::Identifier(*ident),
Pattern::As(_, _) => todo!(),
Pattern::AppliedTag { .. } => todo!(),
Pattern::UnwrappedOpaque { .. } => todo!(),
Pattern::RecordDestructure { .. } => todo!(),
Pattern::TupleDestructure { .. } => todo!(),
Pattern::List { .. } => todo!(),
Pattern::NumLiteral(var, _, int_value, _)
| Pattern::IntLiteral(var, _, _, int_value, _) => {
let mono_type = self.mono_from_var(*var);
match self.mono_types.get(mono_type) {
MonoType::Primitive(primitive) => match to_num(*primitive, *int_value) {
Ok(num) => MonoPattern::NumberLiteral(num),
Err(problem) => MonoPattern::CompilerBug(problem),
},
other => {
MonoPattern::CompilerBug(Problem::NumSpecializedToWrongType(Some(*other)))
}
}
}
Pattern::FloatLiteral(_, _, _, _, _) => todo!(),
Pattern::StrLiteral(_) => todo!(),
Pattern::SingleQuote(_, _, _, _) => todo!(),
Pattern::Underscore => MonoPattern::Underscore,
Pattern::AbilityMemberSpecialization { .. } => todo!(),
Pattern::Shadowed(_, _, _) => todo!(),
Pattern::OpaqueNotInScope(_) => todo!(),
Pattern::UnsupportedPattern(_) => todo!(),
Pattern::MalformedPattern(_, _) => todo!(),
}
}
}
/// Convert a number literal (e.g. `42`) or integer literal (e.g. `0x42`) to a monomorphized type.
/// Nums are allowed to convert to either an integer or a fraction. Integer literals should have
/// given a compile-time error if they ended up unifying to a fractional type, but we can
/// gracefully allow them to compile to that type anyway.
fn to_num(primitive: Primitive, val: IntValue, problems: &mut impl Push<Problem>) -> MonoExpr {
fn to_num(primitive: Primitive, val: IntValue) -> Result<Number, Problem> {
match primitive {
// These are ordered roughly by most to least common integer types
Primitive::U8 => MonoExpr::Number(Number::U8(val.as_i128() as u8)),
Primitive::I8 => MonoExpr::Number(Number::I8(val.as_i128() as i8)),
Primitive::U16 => MonoExpr::Number(Number::U16(val.as_i128() as u16)),
Primitive::I16 => MonoExpr::Number(Number::I16(val.as_i128() as i16)),
Primitive::U32 => MonoExpr::Number(Number::U32(val.as_i128() as u32)),
Primitive::I32 => MonoExpr::Number(Number::I32(val.as_i128() as i32)),
Primitive::U64 => MonoExpr::Number(Number::U64(val.as_i128() as u64)),
Primitive::I64 => MonoExpr::Number(Number::I64(val.as_i128() as i64)),
Primitive::F32 => MonoExpr::Number(Number::F32(val.as_i128() as f32)),
Primitive::F64 => MonoExpr::Number(Number::F64(val.as_i128() as f64)),
Primitive::Dec => MonoExpr::Number(Number::Dec(match val {
Primitive::U8 => Ok(Number::U8(val.as_i128() as u8)),
Primitive::I8 => Ok(Number::I8(val.as_i128() as i8)),
Primitive::U16 => Ok(Number::U16(val.as_i128() as u16)),
Primitive::I16 => Ok(Number::I16(val.as_i128() as i16)),
Primitive::U32 => Ok(Number::U32(val.as_i128() as u32)),
Primitive::I32 => Ok(Number::I32(val.as_i128() as i32)),
Primitive::U64 => Ok(Number::U64(val.as_i128() as u64)),
Primitive::I64 => Ok(Number::I64(val.as_i128() as i64)),
Primitive::F32 => Ok(Number::F32(val.as_i128() as f32)),
Primitive::F64 => Ok(Number::F64(val.as_i128() as f64)),
Primitive::Dec => Ok(Number::Dec(match val {
IntValue::I128(bytes) => i128::from_ne_bytes(bytes) as f64,
IntValue::U128(bytes) => u128::from_ne_bytes(bytes) as f64,
})),
Primitive::U128 => MonoExpr::Number(Number::U128(val.as_u128())),
Primitive::I128 => MonoExpr::Number(Number::I128(val.as_i128())),
Primitive::Str | Primitive::Crash | Primitive::Bool => {
let problem = Problem::NumSpecializedToWrongType(Some(MonoType::Primitive(primitive)));
problems.push(problem);
MonoExpr::CompilerBug(problem)
}
Primitive::U128 => Ok(Number::U128(val.as_u128())),
Primitive::I128 => Ok(Number::I128(val.as_i128())),
Primitive::Str | Primitive::Crash | Primitive::Bool => Err(
Problem::NumSpecializedToWrongType(Some(MonoType::Primitive(primitive))),
),
}
}

View file

@ -1,18 +1,14 @@
use crate::{
foreign_symbol::ForeignSymbolId, mono_module::InternedStrId, mono_num::Number,
mono_struct::MonoFieldId, mono_type::MonoTypeId, specialize_type::Problem,
mono_struct::MonoFieldId, mono_type::MonoTypeId, specialize_type::Problem, MonoPattern,
MonoPatternId,
};
use roc_can::expr::Recursive;
use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol;
use roc_region::all::Region;
use soa::{Index, NonEmptySlice, PairSlice, Slice, Slice2, Slice3};
use std::iter;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct MonoPatternId {
inner: u32,
}
use soa::{Index, NonEmptySlice, PairSlice, Slice, Slice2};
use std::{iter, mem::MaybeUninit};
pub type IdentId = Symbol; // TODO make this an Index into an array local to this module
@ -298,43 +294,75 @@ pub enum MonoExpr {
final_else: MonoExprId,
},
When {
/// The value being matched on
value: MonoExprId,
/// The type of the value being matched on
value_type: MonoTypeId,
/// The return type of all branches and thus the whole when expression
branch_type: MonoTypeId,
/// The branches of the when expression
branches: NonEmptySlice<WhenBranch>,
},
CompilerBug(Problem),
}
#[derive(Clone, Copy, Debug)]
pub enum MonoPattern {
Identifier(IdentId),
As(MonoPatternId, IdentId),
StrLiteral(InternedStrId),
NumberLiteral(Number),
AppliedTag {
tag_union_type: MonoTypeId,
tag_name: IdentId,
args: Slice<MonoPatternId>,
},
StructDestructure {
struct_type: MonoTypeId,
destructs: Slice3<IdentId, MonoFieldId, DestructType>,
},
List {
elem_type: MonoTypeId,
patterns: Slice<MonoPatternId>,
/// Where a rest pattern splits patterns before and after it, if it does at all.
/// If present, patterns at index >= the rest index appear after the rest pattern.
/// For example:
/// [ .., A, B ] -> patterns = [A, B], rest = 0
/// [ A, .., B ] -> patterns = [A, B], rest = 1
/// [ A, B, .. ] -> patterns = [A, B], rest = 2
/// Optionally, the rest pattern can be named - e.g. `[ A, B, ..others ]`
opt_rest: Option<(u16, Option<IdentId>)>,
},
Underscore,
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct WhenBranch {
/// The pattern(s) to match the value against
pub patterns: NonEmptySlice<MonoPattern>,
/// A boolean expression that must be true for this branch to be taken
pub guard: Option<MonoExprId>,
/// The expression to produce if the pattern matches
pub value: MonoExprId,
}
#[derive(Clone, Copy, Debug)]
pub enum DestructType {
Required,
Optional(MonoTypeId, MonoExprId),
Guard(MonoTypeId, MonoPatternId),
#[derive(Debug, Default)]
pub struct WhenBranches {
branches: Vec<MaybeUninit<WhenBranch>>,
}
impl WhenBranches {
pub fn new() -> Self {
Self {
branches: Vec::new(),
}
}
pub fn reserve(&mut self, count: usize) -> Slice<WhenBranch> {
let start = self.branches.len();
let new_size = start + count;
self.branches.reserve(count);
unsafe {
self.branches.set_len(new_size);
}
Slice::new(start as u32, count as u16)
}
pub fn insert(&mut self, id: Index<WhenBranch>, branch: WhenBranch) {
debug_assert!(
self.branches.get(id.index()).is_some(),
"A WhenBranch index was not found in WhenBranches. This should never happen!"
);
// Safety: we should only ever hand out WhenBranch indices that are valid indices into here.
unsafe {
self.branches.get_unchecked_mut(id.index()).write(branch);
}
}
pub fn iter_slice(
&self,
branches: Slice<WhenBranch>,
) -> impl Iterator<Item = &MaybeUninit<WhenBranch>> {
branches.indices().map(|index| {
debug_assert!(self.branches.len() > index, "Slice index out of bounds");
unsafe { self.branches.get_unchecked(index) }
})
}
}

View file

@ -0,0 +1,110 @@
use roc_region::all::Region;
use soa::{Index, Slice, Slice3};
use crate::{
mono_ir::IdentId, InternedStrId, MonoExprId, MonoFieldId, MonoTypeId, Number, Problem,
};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct MonoPatternId {
inner: Index<MonoPattern>,
}
impl MonoPatternId {
fn new(inner: Index<MonoPattern>) -> Self {
Self { inner }
}
}
#[derive(Debug, Default)]
pub struct MonoPatterns {
patterns: Vec<MonoPattern>,
regions: Vec<Region>,
}
impl MonoPatterns {
pub fn new() -> Self {
Self {
patterns: Vec::new(),
regions: Vec::new(),
}
}
pub fn reserve(&mut self, count: usize) -> Slice<MonoPattern> {
let start = self.patterns.len();
self.patterns.extend(
std::iter::repeat(MonoPattern::CompilerBug(
Problem::UninitializedReservedPattern,
))
.take(count),
);
self.regions
.extend(std::iter::repeat(Region::zero()).take(count));
Slice::new(start as u32, count as u16)
}
pub fn insert(&mut self, id: Index<MonoPattern>, pattern: MonoPattern, region: Region) {
debug_assert!(
self.patterns.len() > id.index(),
"Pattern index out of bounds"
);
// Safety: we should only ever hand out WhenBranch indices that are valid indices into here.
unsafe {
*self.patterns.get_unchecked_mut(id.index()) = pattern;
*self.regions.get_unchecked_mut(id.index()) = region;
}
}
pub fn iter_slice(&self, patterns: Slice<MonoPattern>) -> impl Iterator<Item = &MonoPattern> {
patterns.indices().map(|index| {
debug_assert!(
self.patterns.len() > index,
"MonoPattern Slice out of bounds"
);
unsafe { self.patterns.get_unchecked(index) }
})
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MonoPattern {
Identifier(IdentId),
As(MonoPatternId, IdentId),
StrLiteral(InternedStrId),
NumberLiteral(Number),
AppliedTag {
tag_union_type: MonoTypeId,
tag_name: IdentId,
args: Slice<MonoPatternId>,
},
StructDestructure {
struct_type: MonoTypeId,
destructs: Slice3<IdentId, MonoFieldId, DestructType>,
},
List {
elem_type: MonoTypeId,
patterns: Slice<MonoPatternId>,
/// Where a rest pattern splits patterns before and after it, if it does at all.
/// If present, patterns at index >= the rest index appear after the rest pattern.
/// For example:
/// [ .., A, B ] -> patterns = [A, B], rest = 0
/// [ A, .., B ] -> patterns = [A, B], rest = 1
/// [ A, B, .. ] -> patterns = [A, B], rest = 2
/// Optionally, the rest pattern can be named - e.g. `[ A, B, ..others ]`
opt_rest: Option<(u16, Option<IdentId>)>,
},
Underscore,
CompilerBug(Problem),
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum DestructType {
Required,
Optional(MonoTypeId, MonoExprId),
Guard(MonoTypeId, MonoPatternId),
}

View file

@ -35,6 +35,9 @@ pub enum Problem {
BadNumTypeParam,
UninitializedReservedExpr,
FnDidNotHaveFnType,
WhenHasNoBranches,
WhenBranchHasNoPatterns,
UninitializedReservedPattern,
}
/// For MonoTypes that are records, store their field indices.

View file

@ -7,8 +7,8 @@ use roc_load::LoadedModule;
use roc_region::all::Region;
use roc_solve::FunctionKind;
use roc_specialize_types::{
DebugInfo, Env, Interns, MonoExpr, MonoExprs, MonoTypeCache, MonoTypes, RecordFieldIds,
TupleElemIds,
DebugInfo, Env, Interns, MonoExpr, MonoExprs, MonoPattern, MonoPatterns, MonoTypeCache,
MonoTypes, RecordFieldIds, TupleElemIds, WhenBranches,
};
use test_compile::{trim_and_deindent, SpecializedExprOut};
use test_solve_helpers::{format_problems, run_load_and_infer};
@ -65,6 +65,8 @@ fn specialize_expr<'a>(
let mut types_cache = MonoTypeCache::from_solved_subs(&solved);
let mut mono_types = MonoTypes::new();
let mut mono_exprs = MonoExprs::new();
let mut mono_patterns = MonoPatterns::new();
let mut when_branches = WhenBranches::new();
let mut env = Env::new(
arena,
@ -72,6 +74,8 @@ fn specialize_expr<'a>(
&mut types_cache,
&mut mono_types,
&mut mono_exprs,
&mut mono_patterns,
&mut when_branches,
RecordFieldIds::default(),
TupleElemIds::default(),
string_interns,
@ -94,6 +98,8 @@ fn specialize_expr<'a>(
problems,
mono_types,
mono_exprs,
mono_patterns,
when_branches,
region,
}
}
@ -117,9 +123,17 @@ pub fn expect_mono_expr(input: impl AsRef<str>, mono_expr: MonoExpr) {
pub fn expect_mono_expr_str(input: impl AsRef<str>, expr_str: impl AsRef<str>) {
expect_mono_expr_custom(
input,
|_, _, _| expr_str.as_ref().to_string(),
|arena, mono_exprs, interns, expr| {
dbg_mono_expr(arena, mono_exprs, interns, expr).to_string()
|_, _, _, _, _| expr_str.as_ref().to_string(),
|arena, mono_exprs, mono_patterns, when_branches, interns, expr| {
dbg_mono_expr(
arena,
mono_exprs,
mono_patterns,
when_branches,
interns,
expr,
)
.to_string()
},
);
}
@ -127,12 +141,23 @@ pub fn expect_mono_expr_str(input: impl AsRef<str>, expr_str: impl AsRef<str>) {
fn dbg_mono_expr<'a>(
arena: &'a Bump,
mono_exprs: &MonoExprs,
mono_patterns: &MonoPatterns,
when_branches: &WhenBranches,
interns: &Interns<'a>,
expr: &MonoExpr,
) -> &'a str {
let mut buf = bumpalo::collections::String::new_in(arena);
dbg_mono_expr_help(arena, mono_exprs, interns, expr, &mut buf).unwrap();
dbg_mono_expr_help(
arena,
mono_exprs,
mono_patterns,
when_branches,
interns,
expr,
&mut buf,
)
.unwrap();
buf.into_bump_str()
}
@ -140,6 +165,8 @@ fn dbg_mono_expr<'a>(
fn dbg_mono_expr_help<'a>(
arena: &'a Bump,
mono_exprs: &MonoExprs,
mono_patterns: &MonoPatterns,
when_branches: &WhenBranches,
interns: &Interns<'a>,
expr: &MonoExpr,
buf: &mut impl Write,
@ -159,7 +186,15 @@ fn dbg_mono_expr_help<'a>(
write!(buf, ", ")?;
}
dbg_mono_expr_help(arena, mono_exprs, interns, expr, buf)?;
dbg_mono_expr_help(
arena,
mono_exprs,
mono_patterns,
when_branches,
interns,
expr,
buf,
)?;
}
write!(buf, "])")
@ -179,9 +214,25 @@ fn dbg_mono_expr_help<'a>(
write!(buf, ", ")?;
}
dbg_mono_expr_help(arena, mono_exprs, interns, cond, buf)?;
dbg_mono_expr_help(
arena,
mono_exprs,
mono_patterns,
when_branches,
interns,
cond,
buf,
)?;
write!(buf, " -> ")?;
dbg_mono_expr_help(arena, mono_exprs, interns, branch, buf)?;
dbg_mono_expr_help(
arena,
mono_exprs,
mono_patterns,
when_branches,
interns,
branch,
buf,
)?;
write!(buf, ")")?;
}
@ -189,6 +240,8 @@ fn dbg_mono_expr_help<'a>(
dbg_mono_expr_help(
arena,
mono_exprs,
mono_patterns,
when_branches,
interns,
mono_exprs.get_expr(*final_else),
buf,
@ -198,6 +251,73 @@ fn dbg_mono_expr_help<'a>(
MonoExpr::Lookup(ident, _mono_type_id) => {
write!(buf, "{:?}", ident)
}
MonoExpr::When {
value,
value_type: _,
branch_type: _,
branches,
} => {
write!(buf, "When(")?;
dbg_mono_expr_help(
arena,
mono_exprs,
mono_patterns,
when_branches,
interns,
mono_exprs.get_expr(*value),
buf,
)?;
write!(buf, ", ")?;
for (index, branch) in when_branches.iter_slice(branches.as_slice()).enumerate() {
if index > 0 {
write!(buf, ", ")?;
}
let branch = unsafe { branch.assume_init() };
for (index, pattern) in mono_patterns
.iter_slice(branch.patterns.as_slice())
.enumerate()
{
if index > 0 {
write!(buf, " | ")?;
}
dbg_mono_pattern_help(arena, mono_patterns, interns, pattern, buf)?;
}
if let Some(guard) = branch.guard {
write!(buf, " if ")?;
dbg_mono_expr_help(
arena,
mono_exprs,
mono_patterns,
when_branches,
interns,
mono_exprs.get_expr(guard),
buf,
)?;
}
write!(buf, " -> ")?;
dbg_mono_expr_help(
arena,
mono_exprs,
mono_patterns,
when_branches,
interns,
mono_exprs.get_expr(branch.value),
buf,
)?;
}
write!(buf, ")")
}
// MonoExpr::List { elem_type, elems } => todo!(),
// MonoExpr::ParameterizedLookup {
// name,
@ -281,6 +401,44 @@ fn dbg_mono_expr_help<'a>(
}
}
fn dbg_mono_pattern_help<'a>(
_arena: &'a Bump,
_mono_patterns: &MonoPatterns,
_interns: &Interns<'a>,
pattern: &MonoPattern,
buf: &mut impl Write,
) -> Result<(), core::fmt::Error> {
match pattern {
MonoPattern::Identifier(ident) => {
write!(buf, "Identifier({:?})", ident)
}
MonoPattern::As(_, _) => {
todo!()
}
MonoPattern::StrLiteral(_) => {
todo!()
}
MonoPattern::NumberLiteral(number) => {
write!(buf, "Number({:?})", number)
}
MonoPattern::AppliedTag { .. } => {
todo!()
}
MonoPattern::StructDestructure { .. } => {
todo!()
}
MonoPattern::List { .. } => {
todo!()
}
MonoPattern::Underscore => {
write!(buf, "Underscore")
}
MonoPattern::CompilerBug(problem) => {
write!(buf, "CompilerBug({:?}", problem)
}
}
}
#[track_caller]
pub fn expect_mono_expr_with_interns(
input: impl AsRef<str>,
@ -288,24 +446,44 @@ pub fn expect_mono_expr_with_interns(
) {
expect_mono_expr_custom(
input,
|arena, _exprs, interns| from_interns(arena, interns),
|_, _, _, expr| *expr,
|arena, _exprs, _, _, interns| from_interns(arena, interns),
|_, _, _, _, _, expr| *expr,
);
}
#[track_caller]
pub fn expect_mono_expr_custom<T: PartialEq + core::fmt::Debug>(
input: impl AsRef<str>,
to_expected: impl for<'a> Fn(&'a Bump, &MonoExprs, &Interns<'a>) -> T,
to_actual: impl for<'a> Fn(&'a Bump, &MonoExprs, &Interns<'a>, &MonoExpr) -> T,
to_expected: impl for<'a> Fn(&'a Bump, &MonoExprs, &MonoPatterns, &WhenBranches, &Interns<'a>) -> T,
to_actual: impl for<'a> Fn(
&'a Bump,
&MonoExprs,
&MonoPatterns,
&WhenBranches,
&Interns<'a>,
&MonoExpr,
) -> T,
) {
let arena = Bump::new();
let mut string_interns = Interns::new();
let out = specialize_expr(&arena, input.as_ref(), &mut string_interns);
let actual_expr = out.mono_exprs.get_expr(out.mono_expr_id); // Must run first, to populate string interns!
let actual = to_actual(&arena, &out.mono_exprs, &string_interns, actual_expr);
let expected = to_expected(&arena, &out.mono_exprs, &string_interns);
let actual = to_actual(
&arena,
&out.mono_exprs,
&out.mono_patterns,
&out.when_branches,
&string_interns,
actual_expr,
);
let expected = to_expected(
&arena,
&out.mono_exprs,
&out.mono_patterns,
&out.when_branches,
&string_interns,
);
assert_eq!(expected, actual);
}

View file

@ -5,7 +5,7 @@ extern crate pretty_assertions;
mod helpers;
#[cfg(test)]
mod specialize_structs {
mod specialize_if {
use crate::helpers::expect_mono_expr_str;
#[test]

View file

@ -0,0 +1,69 @@
#[macro_use]
extern crate pretty_assertions;
#[cfg(test)]
mod helpers;
#[cfg(test)]
mod specialize_when {
use crate::helpers::expect_mono_expr_str;
#[test]
fn single_branch() {
expect_mono_expr_str(
r"
when 123 is
num -> num
",
"When(Number(I8(123)), Identifier(`#UserApp.IdentId(1)`) -> `#UserApp.IdentId(1)`)",
);
}
#[test]
fn number_pattern() {
expect_mono_expr_str(
r"
when 123 is
123 -> 321
num -> num
",
"When(Number(I16(123)), Number(I16(123)) -> Number(I16(321)), Identifier(`#UserApp.IdentId(1)`) -> `#UserApp.IdentId(1)`)",
);
}
#[test]
fn underscore_pattern() {
expect_mono_expr_str(
r"
when 123 is
123 -> 321
_ -> 0
",
"When(Number(I8(123)), Number(I8(123)) -> Number(I16(321)), Underscore -> Number(I16(0)))"
);
}
#[test]
fn multiple_patterns_per_branch() {
expect_mono_expr_str(
r"
when 123 is
123 | 321 -> 321
_ -> 0
",
"When(Number(I16(123)), Number(I16(123)) | Number(I16(321)) -> Number(I16(321)), Underscore -> Number(I16(0)))"
);
}
#[test]
fn guard() {
expect_mono_expr_str(
r"
when 123 is
123 if Bool.true -> 321
_ -> 0
",
"When(Number(I8(123)), Number(I8(123)) if `Bool.true` -> Number(I16(321)), Underscore -> Number(I16(0)))"
);
}
}

View file

@ -199,7 +199,7 @@ pub enum FormatProblem {
pub fn format_src(arena: &Bump, src: &str, flags: MigrationFlags) -> Result<String, FormatProblem> {
let ast = arena.alloc(parse_all(arena, src).unwrap_or_else(|e| {
user_error!("Unexpected parse failure when parsing this formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", src, e)
user_error!("Unexpected parse failure when parsing this formatting:\n\n{src}\n\nParse error was:\n\n{:#?}\n\n", e)
}));
let mut buf = Buf::new_in(arena, flags);
fmt_all(&mut buf, ast);
@ -438,14 +438,14 @@ import pf.Stdin
main =
Stdout.line! "What's your name?"
name = Stdin.line!
Stdout.line! "Hi $(name)!""#;
Stdout.line! "Hi ${name}!""#;
const UNFORMATTED_ROC: &str = r#"app [main] { pf: platform "platform/main.roc" }
main =
Stdout.line! "What's your name?"
name = Stdin.line!
Stdout.line! "Hi $(name)!"
Stdout.line! "Hi ${name}!"
"#;
fn setup_test_file(dir: &Path, file_name: &str, contents: &str) -> PathBuf {
@ -465,7 +465,10 @@ main =
fn test_single_file_needs_reformatting() {
let dir = tempdir().unwrap();
let file_path = setup_test_file(dir.path(), "test1.roc", UNFORMATTED_ROC);
let flags = MigrationFlags::new(false);
let flags = MigrationFlags {
snakify: false,
parens_and_commas: false,
};
let result = format_files(vec![file_path.clone()], FormatMode::CheckOnly, flags);
assert!(result.is_err());
@ -485,7 +488,10 @@ main =
let dir = tempdir().unwrap();
let file1 = setup_test_file(dir.path(), "test1.roc", UNFORMATTED_ROC);
let file2 = setup_test_file(dir.path(), "test2.roc", UNFORMATTED_ROC);
let flags = MigrationFlags::new(false);
let flags = MigrationFlags {
snakify: false,
parens_and_commas: false,
};
let result = format_files(vec![file1, file2], FormatMode::CheckOnly, flags);
assert!(result.is_err());
@ -499,7 +505,10 @@ main =
fn test_no_files_need_reformatting() {
let dir = tempdir().unwrap();
let file_path = setup_test_file(dir.path(), "formatted.roc", FORMATTED_ROC);
let flags = MigrationFlags::new(false);
let flags = MigrationFlags {
snakify: false,
parens_and_commas: false,
};
let result = format_files(vec![file_path], FormatMode::CheckOnly, flags);
assert!(result.is_ok());
@ -513,7 +522,10 @@ main =
let file_formatted = setup_test_file(dir.path(), "formatted.roc", FORMATTED_ROC);
let file1_unformated = setup_test_file(dir.path(), "test1.roc", UNFORMATTED_ROC);
let file2_unformated = setup_test_file(dir.path(), "test2.roc", UNFORMATTED_ROC);
let flags = MigrationFlags::new(false);
let flags = MigrationFlags {
snakify: false,
parens_and_commas: false,
};
let result = format_files(
vec![file_formatted, file1_unformated, file2_unformated],

View file

@ -92,6 +92,7 @@ pub const FLAG_PP_HOST: &str = "host";
pub const FLAG_PP_PLATFORM: &str = "platform";
pub const FLAG_PP_DYLIB: &str = "lib";
pub const FLAG_MIGRATE: &str = "migrate";
pub const FLAG_DOCS_ROOT: &str = "root-dir";
pub const VERSION: &str = env!("ROC_VERSION");
const DEFAULT_GENERATED_DOCS_DIR: &str = "generated-docs";
@ -187,6 +188,12 @@ pub fn build_app() -> Command {
.num_args(0..)
.allow_hyphen_values(true);
let flag_docs_root_dir = Arg::new(FLAG_DOCS_ROOT)
.long(FLAG_DOCS_ROOT)
.help("Set a root directory path to be used as a prefix for URL links in the generated documentation files.")
.value_parser(value_parser!(Option<String>))
.required(false);
let build_target_values_parser =
PossibleValuesParser::new(Target::iter().map(Into::<&'static str>::into));
@ -244,6 +251,13 @@ pub fn build_app() -> Command {
.action(ArgAction::SetTrue)
.required(false),
)
.arg(
Arg::new(FLAG_VERBOSE)
.long(FLAG_VERBOSE)
.help("Print detailed information while building")
.action(ArgAction::SetTrue)
.required(false)
)
.arg(
Arg::new(ROC_FILE)
.help("The .roc file to build")
@ -411,6 +425,7 @@ pub fn build_app() -> Command {
.required(false)
.default_value(DEFAULT_ROC_FILENAME),
)
.arg(flag_docs_root_dir)
)
.subcommand(Command::new(CMD_GLUE)
.about("Generate glue code between a platform's Roc API and its host language")
@ -434,6 +449,7 @@ pub fn build_app() -> Command {
.required(false)
.default_value(DEFAULT_ROC_FILENAME)
)
.arg(flag_linker.clone())
)
.subcommand(Command::new(CMD_PREPROCESS_HOST)
.about("Runs the surgical linker preprocessor to generate `.rh` and `.rm` files.")
@ -779,6 +795,30 @@ fn nearest_match<'a>(reference: &str, options: &'a [String]) -> Option<(&'a Stri
.min_by(|(_, a), (_, b)| a.cmp(b))
}
pub fn default_linking_strategy(
matches: &ArgMatches,
link_type: LinkType,
target: Target,
) -> LinkingStrategy {
let linker_support_level = roc_linker::support_level(link_type, target);
match matches.get_one::<String>(FLAG_LINKER).map(AsRef::as_ref) {
Some("legacy") => LinkingStrategy::Legacy,
Some("surgical") => match linker_support_level {
roc_linker::SupportLevel::Full => LinkingStrategy::Surgical,
roc_linker::SupportLevel::Wip => {
println!("Warning! Using an unfinished surgical linker for target {target}");
LinkingStrategy::Surgical
}
roc_linker::SupportLevel::None => LinkingStrategy::Legacy,
},
_ => match linker_support_level {
roc_linker::SupportLevel::Full => LinkingStrategy::Surgical,
_ => LinkingStrategy::Legacy,
},
}
}
#[allow(clippy::too_many_arguments)]
pub fn build(
matches: &ArgMatches,
subcommands: &[String],
@ -787,6 +827,7 @@ pub fn build(
out_path: Option<&Path>,
roc_cache_dir: RocCacheDir<'_>,
link_type: LinkType,
verbose: bool,
) -> io::Result<i32> {
use BuildConfig::*;
@ -929,12 +970,8 @@ pub fn build(
let linking_strategy = if wasm_dev_backend {
LinkingStrategy::Additive
} else if !roc_linker::supported(link_type, target)
|| matches.get_one::<String>(FLAG_LINKER).map(|s| s.as_str()) == Some("legacy")
{
LinkingStrategy::Legacy
} else {
LinkingStrategy::Surgical
default_linking_strategy(matches, link_type, target)
};
// All hosts should be prebuilt, this flag keeps the rebuilding behvaiour
@ -982,6 +1019,7 @@ pub fn build(
roc_cache_dir,
load_config,
out_path,
verbose,
);
match res_binary_path {

View file

@ -3,12 +3,13 @@ use bumpalo::Bump;
use roc_build::link::LinkType;
use roc_build::program::{check_file, CodeGenBackend};
use roc_cli::{
annotate_file, build_app, format_files, format_src, test, BuildConfig, FormatMode, CMD_BUILD,
CMD_CHECK, CMD_DEV, CMD_DOCS, CMD_FORMAT, CMD_FORMAT_ANNOTATE, CMD_GLUE, CMD_PREPROCESS_HOST,
CMD_REPL, CMD_RUN, CMD_TEST, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_DEV, FLAG_LIB,
FLAG_MAIN, FLAG_MIGRATE, FLAG_NO_COLOR, FLAG_NO_HEADER, FLAG_NO_LINK, FLAG_OUTPUT,
FLAG_PP_DYLIB, FLAG_PP_HOST, FLAG_PP_PLATFORM, FLAG_STDIN, FLAG_STDOUT, FLAG_TARGET, FLAG_TIME,
GLUE_DIR, GLUE_SPEC, ROC_FILE, VERSION,
annotate_file, build_app, default_linking_strategy, format_files, format_src, test,
BuildConfig, FormatMode, CMD_BUILD, CMD_CHECK, CMD_DEV, CMD_DOCS, CMD_FORMAT,
CMD_FORMAT_ANNOTATE, CMD_GLUE, CMD_PREPROCESS_HOST, CMD_REPL, CMD_RUN, CMD_TEST, CMD_VERSION,
DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_DEV, FLAG_DOCS_ROOT, FLAG_LIB, FLAG_MAIN, FLAG_MIGRATE,
FLAG_NO_COLOR, FLAG_NO_HEADER, FLAG_NO_LINK, FLAG_OUTPUT, FLAG_PP_DYLIB, FLAG_PP_HOST,
FLAG_PP_PLATFORM, FLAG_STDIN, FLAG_STDOUT, FLAG_TARGET, FLAG_TIME, FLAG_VERBOSE, GLUE_DIR,
GLUE_SPEC, ROC_FILE, VERSION,
};
use roc_docs::generate_docs_html;
use roc_error_macros::{internal_error, user_error};
@ -54,6 +55,7 @@ fn main() -> io::Result<()> {
None,
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
LinkType::Executable,
false,
)
} else {
Ok(1)
@ -69,6 +71,7 @@ fn main() -> io::Result<()> {
None,
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
LinkType::Executable,
false,
)
} else {
eprintln!("What .roc file do you want to run? Specify it at the end of the `roc run` command.");
@ -95,6 +98,7 @@ fn main() -> io::Result<()> {
None,
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
LinkType::Executable,
false,
)
} else {
eprintln!("What .roc file do you want to build? Specify it at the end of the `roc run` command.");
@ -113,8 +117,19 @@ fn main() -> io::Result<()> {
false => CodeGenBackend::Llvm(LlvmBackendMode::BinaryGlue),
};
let link_type = LinkType::Dylib;
let target = Triple::host().into();
let linking_strategy = default_linking_strategy(matches, link_type, target);
if !output_path.exists() || output_path.is_dir() {
roc_glue::generate(input_path, output_path, spec_path, backend)
roc_glue::generate(
input_path,
output_path,
spec_path,
backend,
link_type,
linking_strategy,
)
} else {
eprintln!("`roc glue` must be given a directory to output into, because the glue might generate multiple files.");
@ -153,7 +168,7 @@ fn main() -> io::Result<()> {
.and_then(|s| Target::from_str(s).ok())
.unwrap_or_default();
let verbose_and_time = matches.get_one::<bool>(roc_cli::FLAG_VERBOSE).unwrap();
let verbose_and_time = matches.get_one::<bool>(FLAG_VERBOSE).unwrap();
let preprocessed_path = platform_path.with_file_name(target.prebuilt_surgical_host());
let metadata_path = platform_path.with_file_name(target.metadata_file_name());
@ -184,6 +199,7 @@ fn main() -> io::Result<()> {
let out_path = matches
.get_one::<OsString>(FLAG_OUTPUT)
.map(OsString::as_ref);
let verbose = matches.get_flag(FLAG_VERBOSE);
Ok(build(
matches,
@ -193,6 +209,7 @@ fn main() -> io::Result<()> {
out_path,
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
link_type,
verbose,
)?)
}
Some((CMD_CHECK, matches)) => {
@ -282,6 +299,7 @@ fn main() -> io::Result<()> {
) {
Ok((problems, total_time)) => {
problems.print_error_warning_count(total_time);
println!(".\n");
Ok(problems.exit_code())
}
@ -307,7 +325,25 @@ fn main() -> io::Result<()> {
let root_path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
let out_dir = matches.get_one::<OsString>(FLAG_OUTPUT).unwrap();
generate_docs_html(root_path.to_owned(), out_dir.as_ref());
let maybe_root_dir: Option<String> = {
if let Ok(root_dir) = std::env::var("ROC_DOCS_URL_ROOT") {
// if the env var is set, it should override the flag for now
// TODO -- confirm we no longer need this and remove
// once docs are migrated to individual repositories and not roc website
Some(root_dir)
} else {
matches
.get_one::<Option<String>>(FLAG_DOCS_ROOT)
.unwrap_or(&None)
.clone()
}
};
generate_docs_html(
root_path.to_owned(),
out_dir.as_ref(),
maybe_root_dir.clone(),
);
Ok(0)
}
@ -346,7 +382,10 @@ fn main() -> io::Result<()> {
false => FormatMode::WriteToFile,
}
};
let flags = MigrationFlags::new(migrate);
let flags = MigrationFlags {
snakify: migrate,
parens_and_commas: migrate,
};
if from_stdin && matches!(format_mode, FormatMode::WriteToFile) {
eprintln!("When using the --stdin flag, either the --check or the --stdout flag must also be specified. (Otherwise, it's unclear what filename to write to!)");

View file

@ -1,107 +1,107 @@
module [findPath, Model, initialModel, cheapestOpen, reconstructPath]
module [find_path, Model, initial_model, cheapest_open, reconstruct_path]
import Quicksort
findPath = \costFn, moveFn, start, end ->
astar costFn moveFn end (initialModel start)
find_path = \cost_fn, move_fn, start, end ->
astar(cost_fn, move_fn, end, initial_model(start))
Model position : {
evaluated : Set position,
openSet : Set position,
open_set : Set position,
costs : Dict position F64,
cameFrom : Dict position position,
came_from : Dict position position,
} where position implements Hash & Eq
initialModel : position -> Model position where position implements Hash & Eq
initialModel = \start -> {
evaluated: Set.empty {},
openSet: Set.single start,
costs: Dict.single start 0,
cameFrom: Dict.empty {},
initial_model : position -> Model position where position implements Hash & Eq
initial_model = \start -> {
evaluated: Set.empty({}),
open_set: Set.single(start),
costs: Dict.single(start, 0),
came_from: Dict.empty({}),
}
cheapestOpen : (position -> F64), Model position -> Result position {} where position implements Hash & Eq
cheapestOpen = \costFn, model ->
model.openSet
|> Set.toList
|> List.keepOks
(\position ->
when Dict.get model.costs position is
Err _ -> Err {}
Ok cost -> Ok { cost: cost + costFn position, position }
)
|> Quicksort.sortBy .cost
cheapest_open : (position -> F64), Model position -> Result position {} where position implements Hash & Eq
cheapest_open = \cost_fn, model ->
model.open_set
|> Set.to_list
|> List.keep_oks(
\position ->
when Dict.get(model.costs, position) is
Err(_) -> Err({})
Ok(cost) -> Ok({ cost: cost + cost_fn(position), position }),
)
|> Quicksort.sort_by(.cost)
|> List.first
|> Result.map .position
|> Result.mapErr (\_ -> {})
|> Result.map_ok(.position)
|> Result.map_err(\_ -> {})
reconstructPath : Dict position position, position -> List position where position implements Hash & Eq
reconstructPath = \cameFrom, goal ->
when Dict.get cameFrom goal is
Err _ -> []
Ok next -> List.append (reconstructPath cameFrom next) goal
reconstruct_path : Dict position position, position -> List position where position implements Hash & Eq
reconstruct_path = \came_from, goal ->
when Dict.get(came_from, goal) is
Err(_) -> []
Ok(next) -> List.append(reconstruct_path(came_from, next), goal)
updateCost : position, position, Model position -> Model position where position implements Hash & Eq
updateCost = \current, neighbor, model ->
newCameFrom =
Dict.insert model.cameFrom neighbor current
update_cost : position, position, Model position -> Model position where position implements Hash & Eq
update_cost = \current, neighbor, model ->
new_came_from =
Dict.insert(model.came_from, neighbor, current)
newCosts =
Dict.insert model.costs neighbor distanceTo
new_costs =
Dict.insert(model.costs, neighbor, distance_to)
distanceTo =
reconstructPath newCameFrom neighbor
distance_to =
reconstruct_path(new_came_from, neighbor)
|> List.len
|> Num.toFrac
|> Num.to_frac
newModel =
new_model =
{ model &
costs: newCosts,
cameFrom: newCameFrom,
costs: new_costs,
came_from: new_came_from,
}
when Dict.get model.costs neighbor is
Err _ ->
newModel
when Dict.get(model.costs, neighbor) is
Err(_) ->
new_model
Ok previousDistance ->
if distanceTo < previousDistance then
newModel
Ok(previous_distance) ->
if distance_to < previous_distance then
new_model
else
model
astar : (position, position -> F64), (position -> Set position), position, Model position -> Result (List position) {} where position implements Hash & Eq
astar = \costFn, moveFn, goal, model ->
when cheapestOpen (\source -> costFn source goal) model is
Err {} -> Err {}
Ok current ->
astar = \cost_fn, move_fn, goal, model ->
when cheapest_open(\source -> cost_fn(source, goal), model) is
Err({}) -> Err({})
Ok(current) ->
if current == goal then
Ok (reconstructPath model.cameFrom goal)
Ok(reconstruct_path(model.came_from, goal))
else
modelPopped =
model_popped =
{ model &
openSet: Set.remove model.openSet current,
evaluated: Set.insert model.evaluated current,
open_set: Set.remove(model.open_set, current),
evaluated: Set.insert(model.evaluated, current),
}
neighbors =
moveFn current
move_fn(current)
newNeighbors =
Set.difference neighbors modelPopped.evaluated
new_neighbors =
Set.difference(neighbors, model_popped.evaluated)
modelWithNeighbors : Model position
modelWithNeighbors =
modelPopped
|> &openSet (Set.union modelPopped.openSet newNeighbors)
model_with_neighbors : Model position
model_with_neighbors =
model_popped
|> &open_set(Set.union(model_popped.open_set, new_neighbors))
walker : Model position, position -> Model position
walker = \amodel, n -> updateCost current n amodel
walker = \amodel, n -> update_cost(current, n, amodel)
modelWithCosts =
Set.walk newNeighbors modelWithNeighbors walker
model_with_costs =
Set.walk(new_neighbors, model_with_neighbors, walker)
astar costFn moveFn goal modelWithCosts
astar(cost_fn, move_fn, goal, model_with_costs)
# takeStep = \moveFn, _goal, model, current ->
# modelPopped =

View file

@ -1,38 +1,38 @@
module [fromBytes, fromStr, toBytes, toStr]
module [from_bytes, from_str, to_bytes, to_str]
import Base64.Decode
import Base64.Encode
# base 64 encoding from a sequence of bytes
fromBytes : List U8 -> Result Str [InvalidInput]
fromBytes = \bytes ->
when Base64.Decode.fromBytes bytes is
Ok v ->
Ok v
from_bytes : List U8 -> Result Str [InvalidInput]
from_bytes = \bytes ->
when Base64.Decode.from_bytes(bytes) is
Ok(v) ->
Ok(v)
Err _ ->
Err InvalidInput
Err(_) ->
Err(InvalidInput)
# base 64 encoding from a string
fromStr : Str -> Result Str [InvalidInput]
fromStr = \str ->
fromBytes (Str.toUtf8 str)
from_str : Str -> Result Str [InvalidInput]
from_str = \str ->
from_bytes(Str.to_utf8(str))
# base64-encode bytes to the original
toBytes : Str -> Result (List U8) [InvalidInput]
toBytes = \str ->
Ok (Base64.Encode.toBytes str)
to_bytes : Str -> Result (List U8) [InvalidInput]
to_bytes = \str ->
Ok(Base64.Encode.to_bytes(str))
toStr : Str -> Result Str [InvalidInput]
toStr = \str ->
when toBytes str is
Ok bytes ->
when Str.fromUtf8 bytes is
Ok v ->
Ok v
to_str : Str -> Result Str [InvalidInput]
to_str = \str ->
when to_bytes(str) is
Ok(bytes) ->
when Str.from_utf8(bytes) is
Ok(v) ->
Ok(v)
Err _ ->
Err InvalidInput
Err(_) ->
Err(InvalidInput)
Err _ ->
Err InvalidInput
Err(_) ->
Err(InvalidInput)

View file

@ -1,86 +1,86 @@
module [fromBytes]
module [from_bytes]
import Bytes.Decode exposing [ByteDecoder, DecodeProblem]
fromBytes : List U8 -> Result Str DecodeProblem
fromBytes = \bytes ->
Bytes.Decode.decode bytes (decodeBase64 (List.len bytes))
from_bytes : List U8 -> Result Str DecodeProblem
from_bytes = \bytes ->
Bytes.Decode.decode(bytes, decode_base64(List.len(bytes)))
decodeBase64 : U64 -> ByteDecoder Str
decodeBase64 = \width -> Bytes.Decode.loop loopHelp { remaining: width, string: "" }
decode_base64 : U64 -> ByteDecoder Str
decode_base64 = \width -> Bytes.Decode.loop(loop_help, { remaining: width, string: "" })
loopHelp : { remaining : U64, string : Str } -> ByteDecoder (Bytes.Decode.Step { remaining : U64, string : Str } Str)
loopHelp = \{ remaining, string } ->
loop_help : { remaining : U64, string : Str } -> ByteDecoder (Bytes.Decode.Step { remaining : U64, string : Str } Str)
loop_help = \{ remaining, string } ->
if remaining >= 3 then
Bytes.Decode.map3 Bytes.Decode.u8 Bytes.Decode.u8 Bytes.Decode.u8 \x, y, z ->
Bytes.Decode.map3(Bytes.Decode.u8, Bytes.Decode.u8, Bytes.Decode.u8, \x, y, z ->
a : U32
a = Num.intCast x
a = Num.int_cast(x)
b : U32
b = Num.intCast y
b = Num.int_cast(y)
c : U32
c = Num.intCast z
combined = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy a 16) (Num.shiftLeftBy b 8)) c
c = Num.int_cast(z)
combined = Num.bitwise_or(Num.bitwise_or(Num.shift_left_by(a, 16), Num.shift_left_by(b, 8)), c)
Loop {
Loop({
remaining: remaining - 3,
string: Str.concat string (bitsToChars combined 0),
}
string: Str.concat(string, bits_to_chars(combined, 0)),
}))
else if remaining == 0 then
Bytes.Decode.succeed (Done string)
Bytes.Decode.succeed(Done(string))
else if remaining == 2 then
Bytes.Decode.map2 Bytes.Decode.u8 Bytes.Decode.u8 \x, y ->
Bytes.Decode.map2(Bytes.Decode.u8, Bytes.Decode.u8, \x, y ->
a : U32
a = Num.intCast x
a = Num.int_cast(x)
b : U32
b = Num.intCast y
combined = Num.bitwiseOr (Num.shiftLeftBy a 16) (Num.shiftLeftBy b 8)
b = Num.int_cast(y)
combined = Num.bitwise_or(Num.shift_left_by(a, 16), Num.shift_left_by(b, 8))
Done (Str.concat string (bitsToChars combined 1))
Done(Str.concat(string, bits_to_chars(combined, 1))))
else
# remaining = 1
Bytes.Decode.map Bytes.Decode.u8 \x ->
Bytes.Decode.map(Bytes.Decode.u8, \x ->
a : U32
a = Num.intCast x
a = Num.int_cast(x)
Done (Str.concat string (bitsToChars (Num.shiftLeftBy a 16) 2))
Done(Str.concat(string, bits_to_chars(Num.shift_left_by(a, 16), 2))))
bitsToChars : U32, Int * -> Str
bitsToChars = \bits, missing ->
when Str.fromUtf8 (bitsToCharsHelp bits missing) is
Ok str -> str
Err _ -> ""
bits_to_chars : U32, Int * -> Str
bits_to_chars = \bits, missing ->
when Str.from_utf8(bits_to_chars_help(bits, missing)) is
Ok(str) -> str
Err(_) -> ""
# Mask that can be used to get the lowest 6 bits of a binary number
lowest6BitsMask : Int *
lowest6BitsMask = 63
lowest6_bits_mask : Int *
lowest6_bits_mask = 63
bitsToCharsHelp : U32, Int * -> List U8
bitsToCharsHelp = \bits, missing ->
bits_to_chars_help : U32, Int * -> List U8
bits_to_chars_help = \bits, missing ->
# The input is 24 bits, which we have to partition into 4 6-bit segments. We achieve this by
# shifting to the right by (a multiple of) 6 to remove unwanted bits on the right, then `Num.bitwiseAnd`
# with `0b111111` (which is 2^6 - 1 or 63) (so, 6 1s) to remove unwanted bits on the left.
# any 6-bit number is a valid base64 digit, so this is actually safe
p =
Num.shiftRightZfBy bits 18
|> Num.intCast
|> unsafeToChar
Num.shift_right_zf_by(bits, 18)
|> Num.int_cast
|> unsafe_to_char
q =
Num.bitwiseAnd (Num.shiftRightZfBy bits 12) lowest6BitsMask
|> Num.intCast
|> unsafeToChar
Num.bitwise_and(Num.shift_right_zf_by(bits, 12), lowest6_bits_mask)
|> Num.int_cast
|> unsafe_to_char
r =
Num.bitwiseAnd (Num.shiftRightZfBy bits 6) lowest6BitsMask
|> Num.intCast
|> unsafeToChar
Num.bitwise_and(Num.shift_right_zf_by(bits, 6), lowest6_bits_mask)
|> Num.int_cast
|> unsafe_to_char
s =
Num.bitwiseAnd bits lowest6BitsMask
|> Num.intCast
|> unsafeToChar
Num.bitwise_and(bits, lowest6_bits_mask)
|> Num.int_cast
|> unsafe_to_char
equals : U8
equals = 61
@ -94,8 +94,8 @@ bitsToCharsHelp = \bits, missing ->
[]
# Base64 index to character/digit
unsafeToChar : U8 -> U8
unsafeToChar = \n ->
unsafe_to_char : U8 -> U8
unsafe_to_char = \n ->
if n <= 25 then
# uppercase characters
65 + n

View file

@ -1,22 +1,22 @@
module [toBytes]
module [to_bytes]
import Bytes.Encode exposing [ByteEncoder]
InvalidChar : U8
# State : [None, One U8, Two U8, Three U8]
toBytes : Str -> List U8
toBytes = \str ->
to_bytes : Str -> List U8
to_bytes = \str ->
str
|> Str.toUtf8
|> encodeChunks
|> Str.to_utf8
|> encode_chunks
|> Bytes.Encode.sequence
|> Bytes.Encode.encode
encodeChunks : List U8 -> List ByteEncoder
encodeChunks = \bytes ->
List.walk bytes { output: [], accum: None } folder
|> encodeResidual
encode_chunks : List U8 -> List ByteEncoder
encode_chunks = \bytes ->
List.walk(bytes, { output: [], accum: None }, folder)
|> encode_residual
coerce : U64, a -> a
coerce = \_, x -> x
@ -24,113 +24,114 @@ coerce = \_, x -> x
# folder : { output : List ByteEncoder, accum : State }, U8 -> { output : List ByteEncoder, accum : State }
folder = \{ output, accum }, char ->
when accum is
Unreachable n -> coerce n { output, accum: Unreachable n }
None -> { output, accum: One char }
One a -> { output, accum: Two a char }
Two a b -> { output, accum: Three a b char }
Three a b c ->
when encodeCharacters a b c char is
Ok encoder ->
Unreachable(n) -> coerce(n, { output, accum: Unreachable(n) })
None -> { output, accum: One(char) }
One(a) -> { output, accum: Two(a, char) }
Two(a, b) -> { output, accum: Three(a, b, char) }
Three(a, b, c) ->
when encode_characters(a, b, c, char) is
Ok(encoder) ->
{
output: List.append output encoder,
output: List.append(output, encoder),
accum: None,
}
Err _ ->
Err(_) ->
{ output, accum: None }
# SGVs bG8g V29y bGQ=
# encodeResidual : { output : List ByteEncoder, accum : State } -> List ByteEncoder
encodeResidual = \{ output, accum } ->
encode_residual = \{ output, accum } ->
when accum is
Unreachable _ -> output
Unreachable(_) -> output
None -> output
One _ -> output
Two a b ->
when encodeCharacters a b equals equals is
Ok encoder -> List.append output encoder
Err _ -> output
One(_) -> output
Two(a, b) ->
when encode_characters(a, b, equals, equals) is
Ok(encoder) -> List.append(output, encoder)
Err(_) -> output
Three a b c ->
when encodeCharacters a b c equals is
Ok encoder -> List.append output encoder
Err _ -> output
Three(a, b, c) ->
when encode_characters(a, b, c, equals) is
Ok(encoder) -> List.append(output, encoder)
Err(_) -> output
equals : U8
equals = 61
# Convert 4 characters to 24 bits (as an ByteEncoder)
encodeCharacters : U8, U8, U8, U8 -> Result ByteEncoder InvalidChar
encodeCharacters = \a, b, c, d ->
if !(isValidChar a) then
Err a
else if !(isValidChar b) then
Err b
encode_characters : U8, U8, U8, U8 -> Result ByteEncoder InvalidChar
encode_characters = \a, b, c, d ->
if !(is_valid_char(a)) then
Err(a)
else if !(is_valid_char(b)) then
Err(b)
else
# `=` is the padding character, and must be special-cased
# only the `c` and `d` char are allowed to be padding
n1 = unsafeConvertChar a
n2 = unsafeConvertChar b
n1 = unsafe_convert_char(a)
n2 = unsafe_convert_char(b)
x : U32
x = Num.intCast n1
x = Num.int_cast(n1)
y : U32
y = Num.intCast n2
y = Num.int_cast(n2)
if d == equals then
if c == equals then
n = Num.bitwiseOr (Num.shiftLeftBy x 18) (Num.shiftLeftBy y 12)
n = Num.bitwise_or(Num.shift_left_by(x, 18), Num.shift_left_by(y, 12))
# masking higher bits is not needed, Encode.unsignedInt8 ignores higher bits
b1 : U8
b1 = Num.intCast (Num.shiftRightBy n 16)
b1 = Num.int_cast(Num.shift_right_by(n, 16))
Ok (Bytes.Encode.u8 b1)
else if !(isValidChar c) then
Err c
Ok(Bytes.Encode.u8(b1))
else if !(is_valid_char(c)) then
Err(c)
else
n3 = unsafeConvertChar c
n3 = unsafe_convert_char(c)
z : U32
z = Num.intCast n3
z = Num.int_cast(n3)
n = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy x 18) (Num.shiftLeftBy y 12)) (Num.shiftLeftBy z 6)
n = Num.bitwise_or(Num.bitwise_or(Num.shift_left_by(x, 18), Num.shift_left_by(y, 12)), Num.shift_left_by(z, 6))
combined : U16
combined = Num.intCast (Num.shiftRightBy n 8)
combined = Num.int_cast(Num.shift_right_by(n, 8))
Ok (Bytes.Encode.u16 BE combined)
else if !(isValidChar d) then
Err d
Ok(Bytes.Encode.u16(BE, combined))
else if !(is_valid_char(d)) then
Err(d)
else
n3 = unsafeConvertChar c
n4 = unsafeConvertChar d
n3 = unsafe_convert_char(c)
n4 = unsafe_convert_char(d)
z : U32
z = Num.intCast n3
z = Num.int_cast(n3)
w : U32
w = Num.intCast n4
w = Num.int_cast(n4)
n =
Num.bitwiseOr
(Num.bitwiseOr (Num.shiftLeftBy x 18) (Num.shiftLeftBy y 12))
(Num.bitwiseOr (Num.shiftLeftBy z 6) w)
Num.bitwise_or(
Num.bitwise_or(Num.shift_left_by(x, 18), Num.shift_left_by(y, 12)),
Num.bitwise_or(Num.shift_left_by(z, 6), w),
)
b3 : U8
b3 = Num.intCast n
b3 = Num.int_cast(n)
combined : U16
combined = Num.intCast (Num.shiftRightBy n 8)
combined = Num.int_cast(Num.shift_right_by(n, 8))
Ok (Bytes.Encode.sequence [Bytes.Encode.u16 BE combined, Bytes.Encode.u8 b3])
Ok(Bytes.Encode.sequence([Bytes.Encode.u16(BE, combined), Bytes.Encode.u8(b3)]))
# is the character a base64 digit?
# The base16 digits are: A-Z, a-z, 0-1, '+' and '/'
isValidChar : U8 -> Bool
isValidChar = \c ->
if isAlphaNum c then
is_valid_char : U8 -> Bool
is_valid_char = \c ->
if is_alpha_num(c) then
Bool.true
else
when c is
@ -145,14 +146,14 @@ isValidChar = \c ->
_ ->
Bool.false
isAlphaNum : U8 -> Bool
isAlphaNum = \key ->
is_alpha_num : U8 -> Bool
is_alpha_num = \key ->
(key >= 48 && key <= 57) || (key >= 64 && key <= 90) || (key >= 97 && key <= 122)
# Convert a base64 character/digit to its index
# See also [Wikipedia](https://en.wikipedia.org/wiki/Base64#Base64_table)
unsafeConvertChar : U8 -> U8
unsafeConvertChar = \key ->
unsafe_convert_char : U8 -> U8
unsafe_convert_char = \key ->
if key >= 65 && key <= 90 then
# A-Z
key - 65

View file

@ -7,105 +7,111 @@ DecodeProblem : [OutOfBytes]
ByteDecoder a := State -> [Good State a, Bad DecodeProblem]
decode : List U8, ByteDecoder a -> Result a DecodeProblem
decode = \bytes, @ByteDecoder decoder ->
when decoder { bytes, cursor: 0 } is
Good _ value ->
Ok value
decode = \bytes, @ByteDecoder(decoder) ->
when decoder({ bytes, cursor: 0 }) is
Good(_, value) ->
Ok(value)
Bad e ->
Err e
Bad(e) ->
Err(e)
succeed : a -> ByteDecoder a
succeed = \value -> @ByteDecoder \state -> Good state value
succeed = \value -> @ByteDecoder(\state -> Good(state, value))
map : ByteDecoder a, (a -> b) -> ByteDecoder b
map = \@ByteDecoder decoder, transform ->
@ByteDecoder
map = \@ByteDecoder(decoder), transform ->
@ByteDecoder(
\state ->
when decoder state is
Good state1 value ->
Good state1 (transform value)
when decoder(state) is
Good(state1, value) ->
Good(state1, transform(value))
Bad e ->
Bad e
Bad(e) ->
Bad(e),
)
map2 : ByteDecoder a, ByteDecoder b, (a, b -> c) -> ByteDecoder c
map2 = \@ByteDecoder decoder1, @ByteDecoder decoder2, transform ->
@ByteDecoder
map2 = \@ByteDecoder(decoder1), @ByteDecoder(decoder2), transform ->
@ByteDecoder(
\state1 ->
when decoder1 state1 is
Good state2 a ->
when decoder2 state2 is
Good state3 b ->
Good state3 (transform a b)
when decoder1(state1) is
Good(state2, a) ->
when decoder2(state2) is
Good(state3, b) ->
Good(state3, transform(a, b))
Bad e ->
Bad e
Bad(e) ->
Bad(e)
Bad e ->
Bad e
Bad(e) ->
Bad(e),
)
map3 : ByteDecoder a, ByteDecoder b, ByteDecoder c, (a, b, c -> d) -> ByteDecoder d
map3 = \@ByteDecoder decoder1, @ByteDecoder decoder2, @ByteDecoder decoder3, transform ->
@ByteDecoder
map3 = \@ByteDecoder(decoder1), @ByteDecoder(decoder2), @ByteDecoder(decoder3), transform ->
@ByteDecoder(
\state1 ->
when decoder1 state1 is
Good state2 a ->
when decoder2 state2 is
Good state3 b ->
when decoder3 state3 is
Good state4 c ->
Good state4 (transform a b c)
when decoder1(state1) is
Good(state2, a) ->
when decoder2(state2) is
Good(state3, b) ->
when decoder3(state3) is
Good(state4, c) ->
Good(state4, transform(a, b, c))
Bad e ->
Bad e
Bad(e) ->
Bad(e)
Bad e ->
Bad e
Bad(e) ->
Bad(e)
Bad e ->
Bad e
Bad(e) ->
Bad(e),
)
after : ByteDecoder a, (a -> ByteDecoder b) -> ByteDecoder b
after = \@ByteDecoder decoder, transform ->
@ByteDecoder
after = \@ByteDecoder(decoder), transform ->
@ByteDecoder(
\state ->
when decoder state is
Good state1 value ->
(@ByteDecoder decoder1) = transform value
when decoder(state) is
Good(state1, value) ->
@ByteDecoder(decoder1) = transform(value)
decoder1 state1
decoder1(state1)
Bad e ->
Bad e
Bad(e) ->
Bad(e),
)
u8 : ByteDecoder U8
u8 = @ByteDecoder
u8 = @ByteDecoder(
\state ->
when List.get state.bytes state.cursor is
Ok b ->
Good { state & cursor: state.cursor + 1 } b
when List.get(state.bytes, state.cursor) is
Ok(b) ->
Good({ state & cursor: state.cursor + 1 }, b)
Err _ ->
Bad OutOfBytes
Err(_) ->
Bad(OutOfBytes),
)
Step state b : [Loop state, Done b]
loop : (state -> ByteDecoder (Step state a)), state -> ByteDecoder a
loop = \stepper, initial ->
@ByteDecoder
@ByteDecoder(
\state ->
loopHelp stepper initial state
loop_help(stepper, initial, state),
)
loopHelp = \stepper, accum, state ->
(@ByteDecoder stepper1) = stepper accum
loop_help = \stepper, accum, state ->
@ByteDecoder(stepper1) = stepper(accum)
when stepper1 state is
Good newState (Done value) ->
Good newState value
when stepper1(state) is
Good(new_state, Done(value)) ->
Good(new_state, value)
Good newState (Loop newAccum) ->
loopHelp stepper newAccum newState
Good(new_state, Loop(new_accum)) ->
loop_help(stepper, new_accum, new_state)
Bad e ->
Bad e
Bad(e) ->
Bad(e)

View file

@ -5,130 +5,132 @@ Endianness : [BE, LE]
ByteEncoder : [Signed8 I8, Unsigned8 U8, Signed16 Endianness I16, Unsigned16 Endianness U16, Sequence U64 (List ByteEncoder), Bytes (List U8)]
u8 : U8 -> ByteEncoder
u8 = \value -> Unsigned8 value
u8 = \value -> Unsigned8(value)
empty : ByteEncoder
empty =
foo : List ByteEncoder
foo = []
Sequence 0 foo
Sequence(0, foo)
u16 : Endianness, U16 -> ByteEncoder
u16 = \endianness, value -> Unsigned16 endianness value
u16 = \endianness, value -> Unsigned16(endianness, value)
bytes : List U8 -> ByteEncoder
bytes = \bs -> Bytes bs
bytes = \bs -> Bytes(bs)
sequence : List ByteEncoder -> ByteEncoder
sequence = \encoders ->
Sequence (getWidths encoders 0) encoders
Sequence(get_widths(encoders, 0), encoders)
getWidth : ByteEncoder -> U64
getWidth = \encoder ->
get_width : ByteEncoder -> U64
get_width = \encoder ->
when encoder is
Signed8 _ -> 1
Unsigned8 _ -> 1
Signed16 _ _ -> 2
Unsigned16 _ _ -> 2
Signed8(_) -> 1
Unsigned8(_) -> 1
Signed16(_, _) -> 2
Unsigned16(_, _) -> 2
# Signed32 _ -> 4
# Unsigned32 _ -> 4
# Signed64 _ -> 8
# Unsigned64 _ -> 8
# Signed128 _ -> 16
# Unsigned128 _ -> 16
Sequence w _ -> w
Bytes bs -> List.len bs
Sequence(w, _) -> w
Bytes(bs) -> List.len(bs)
getWidths : List ByteEncoder, U64 -> U64
getWidths = \encoders, initial ->
List.walk encoders initial \accum, encoder -> accum + getWidth encoder
get_widths : List ByteEncoder, U64 -> U64
get_widths = \encoders, initial ->
List.walk(encoders, initial, \accum, encoder -> accum + get_width(encoder))
encode : ByteEncoder -> List U8
encode = \encoder ->
output = List.repeat 0 (getWidth encoder)
output = List.repeat(0, get_width(encoder))
encodeHelp encoder 0 output
encode_help(encoder, 0, output)
|> .output
encodeHelp : ByteEncoder, U64, List U8 -> { output : List U8, offset : U64 }
encodeHelp = \encoder, offset, output ->
encode_help : ByteEncoder, U64, List U8 -> { output : List U8, offset : U64 }
encode_help = \encoder, offset, output ->
when encoder is
Unsigned8 value ->
Unsigned8(value) ->
{
output: List.set output offset value,
output: List.set(output, offset, value),
offset: offset + 1,
}
Signed8 value ->
Signed8(value) ->
cast : U8
cast = Num.intCast value
cast = Num.int_cast(value)
{
output: List.set output offset cast,
output: List.set(output, offset, cast),
offset: offset + 1,
}
Unsigned16 endianness value ->
Unsigned16(endianness, value) ->
a : U8
a = Num.intCast (Num.shiftRightBy value 8)
a = Num.int_cast(Num.shift_right_by(value, 8))
b : U8
b = Num.intCast value
b = Num.int_cast(value)
newOutput =
new_output =
when endianness is
BE ->
output
|> List.set (offset + 0) a
|> List.set (offset + 1) b
|> List.set((offset + 0), a)
|> List.set((offset + 1), b)
LE ->
output
|> List.set (offset + 0) b
|> List.set (offset + 1) a
|> List.set((offset + 0), b)
|> List.set((offset + 1), a)
{
output: newOutput,
output: new_output,
offset: offset + 2,
}
Signed16 endianness value ->
Signed16(endianness, value) ->
a : U8
a = Num.intCast (Num.shiftRightBy value 8)
a = Num.int_cast(Num.shift_right_by(value, 8))
b : U8
b = Num.intCast value
b = Num.int_cast(value)
newOutput =
new_output =
when endianness is
BE ->
output
|> List.set (offset + 0) a
|> List.set (offset + 1) b
|> List.set((offset + 0), a)
|> List.set((offset + 1), b)
LE ->
output
|> List.set (offset + 0) b
|> List.set (offset + 1) a
|> List.set((offset + 0), b)
|> List.set((offset + 1), a)
{
output: newOutput,
output: new_output,
offset: offset + 1,
}
Bytes bs ->
List.walk
bs
{ output, offset }
Bytes(bs) ->
List.walk(
bs,
{ output, offset },
\accum, byte -> {
offset: accum.offset + 1,
output: List.set accum.output offset byte,
}
output: List.set(accum.output, offset, byte),
},
)
Sequence _ encoders ->
List.walk
encoders
{ output, offset }
Sequence(_, encoders) ->
List.walk(
encoders,
{ output, offset },
\accum, single ->
encodeHelp single accum.offset accum.output
encode_help(single, accum.offset, accum.output),
)

View file

@ -1,5 +1,5 @@
module [text, asText]
module [text, as_text]
text = "Hello, world!"
asText = Num.toStr
as_text = Num.to_str

View file

@ -1,75 +1,75 @@
module [sortBy, sortWith, show]
module [sort_by, sort_with, show]
show : List I64 -> Str
show = \list ->
if List.isEmpty list then
if List.is_empty(list) then
"[]"
else
content =
list
|> List.map Num.toStr
|> Str.joinWith ", "
|> List.map(Num.to_str)
|> Str.join_with(", ")
"[$(content)]"
"[${content}]"
sortBy : List a, (a -> Num *) -> List a
sortBy = \list, toComparable ->
sortWith list (\x, y -> Num.compare (toComparable x) (toComparable y))
sort_by : List a, (a -> Num *) -> List a
sort_by = \list, to_comparable ->
sort_with(list, \x, y -> Num.compare(to_comparable(x), to_comparable(y)))
Order a : a, a -> [LT, GT, EQ]
sortWith : List a, (a, a -> [LT, GT, EQ]) -> List a
sortWith = \list, order ->
n = List.len list
sort_with : List a, (a, a -> [LT, GT, EQ]) -> List a
sort_with = \list, order ->
n = List.len(list)
quicksortHelp list order 0 (n - 1)
quicksort_help(list, order, 0, (n - 1))
quicksortHelp : List a, Order a, U64, U64 -> List a
quicksortHelp = \list, order, low, high ->
quicksort_help : List a, Order a, U64, U64 -> List a
quicksort_help = \list, order, low, high ->
if low < high then
when partition low high list order is
Pair partitionIndex partitioned ->
when partition(low, high, list, order) is
Pair(partition_index, partitioned) ->
partitioned
|> quicksortHelp order low (Num.subSaturated partitionIndex 1)
|> quicksortHelp order (partitionIndex + 1) high
|> quicksort_help(order, low, Num.sub_saturated(partition_index, 1))
|> quicksort_help(order, (partition_index + 1), high)
else
list
partition : U64, U64, List a, Order a -> [Pair U64 (List a)]
partition = \low, high, initialList, order ->
when List.get initialList high is
Ok pivot ->
when partitionHelp low low initialList order high pivot is
Pair newI newList ->
Pair newI (swap newI high newList)
partition = \low, high, initial_list, order ->
when List.get(initial_list, high) is
Ok(pivot) ->
when partition_help(low, low, initial_list, order, high, pivot) is
Pair(new_i, new_list) ->
Pair(new_i, swap(new_i, high, new_list))
Err _ ->
Pair low initialList
Err(_) ->
Pair(low, initial_list)
partitionHelp : U64, U64, List c, Order c, U64, c -> [Pair U64 (List c)]
partitionHelp = \i, j, list, order, high, pivot ->
partition_help : U64, U64, List c, Order c, U64, c -> [Pair U64 (List c)]
partition_help = \i, j, list, order, high, pivot ->
if j < high then
when List.get list j is
Ok value ->
when order value pivot is
when List.get(list, j) is
Ok(value) ->
when order(value, pivot) is
LT | EQ ->
partitionHelp (i + 1) (j + 1) (swap i j list) order high pivot
partition_help((i + 1), (j + 1), swap(i, j, list), order, high, pivot)
GT ->
partitionHelp i (j + 1) list order high pivot
partition_help(i, (j + 1), list, order, high, pivot)
Err _ ->
Pair i list
Err(_) ->
Pair(i, list)
else
Pair i list
Pair(i, list)
swap : U64, U64, List a -> List a
swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) ->
when Pair(List.get(list, i), List.get(list, j)) is
Pair(Ok(at_i), Ok(at_j)) ->
list
|> List.set i atJ
|> List.set j atI
|> List.set(i, at_j)
|> List.set(j, at_i)
_ ->
[]

View file

@ -1,31 +1,31 @@
app [main] { pf: platform "platform/main.roc" }
app [main!] { pf: platform "platform/main.roc" }
import pf.PlatformTasks
import pf.Host
# adapted from https://github.com/koka-lang/koka/blob/master/test/bench/haskell/cfold.hs
main : Task {} []
main =
{ value, isError } = PlatformTasks.getInt!
inputResult =
if isError then
Err GetIntError
main! : {} => {}
main! = \{} ->
{ value, is_error } = Host.get_int!({})
input_result =
if is_error then
Err(GetIntError)
else
Ok value
Ok(value)
when inputResult is
Ok n ->
e = mkExpr n 1 # original koka n = 20 (set `ulimit -s unlimited` to avoid stack overflow for n = 20)
unoptimized = eval e
optimized = eval (constFolding (reassoc e))
when input_result is
Ok(n) ->
e = mk_expr(n, 1) # original koka n = 20 (set `ulimit -s unlimited` to avoid stack overflow for n = 20)
unoptimized = eval(e)
optimized = eval(const_folding(reassoc(e)))
unoptimized
|> Num.toStr
|> Str.concat " & "
|> Str.concat (Num.toStr optimized)
|> PlatformTasks.putLine
|> Num.to_str
|> Str.concat(" & ")
|> Str.concat(Num.to_str(optimized))
|> Host.put_line!
Err GetIntError ->
PlatformTasks.putLine "Error: Failed to get Integer from stdin."
Err(GetIntError) ->
Host.put_line!("Error: Failed to get Integer from stdin.")
Expr : [
Add Expr Expr,
@ -34,97 +34,97 @@ Expr : [
Var I64,
]
mkExpr : I64, I64 -> Expr
mkExpr = \n, v ->
mk_expr : I64, I64 -> Expr
mk_expr = \n, v ->
when n is
0 ->
if v == 0 then Var 1 else Val v
if v == 0 then Var(1) else Val(v)
_ ->
Add (mkExpr (n - 1) (v + 1)) (mkExpr (n - 1) (max (v - 1) 0))
Add(mk_expr((n - 1), (v + 1)), mk_expr((n - 1), max((v - 1), 0)))
max : I64, I64 -> I64
max = \a, b -> if a > b then a else b
appendAdd : Expr, Expr -> Expr
appendAdd = \e1, e2 ->
append_add : Expr, Expr -> Expr
append_add = \e1, e2 ->
when e1 is
Add a1 a2 ->
Add a1 (appendAdd a2 e2)
Add(a1, a2) ->
Add(a1, append_add(a2, e2))
_ ->
Add e1 e2
Add(e1, e2)
appendMul : Expr, Expr -> Expr
appendMul = \e1, e2 ->
append_mul : Expr, Expr -> Expr
append_mul = \e1, e2 ->
when e1 is
Mul a1 a2 ->
Mul a1 (appendMul a2 e2)
Mul(a1, a2) ->
Mul(a1, append_mul(a2, e2))
_ ->
Mul e1 e2
Mul(e1, e2)
eval : Expr -> I64
eval = \e ->
when e is
Var _ ->
Var(_) ->
0
Val v ->
Val(v) ->
v
Add l r ->
eval l + eval r
Add(l, r) ->
eval(l) + eval(r)
Mul l r ->
eval l * eval r
Mul(l, r) ->
eval(l) * eval(r)
reassoc : Expr -> Expr
reassoc = \e ->
when e is
Add e1 e2 ->
x1 = reassoc e1
x2 = reassoc e2
Add(e1, e2) ->
x1 = reassoc(e1)
x2 = reassoc(e2)
appendAdd x1 x2
append_add(x1, x2)
Mul e1 e2 ->
x1 = reassoc e1
x2 = reassoc e2
Mul(e1, e2) ->
x1 = reassoc(e1)
x2 = reassoc(e2)
appendMul x1 x2
append_mul(x1, x2)
_ ->
e
constFolding : Expr -> Expr
constFolding = \e ->
const_folding : Expr -> Expr
const_folding = \e ->
when e is
Add e1 e2 ->
x1 = constFolding e1
x2 = constFolding e2
Add(e1, e2) ->
x1 = const_folding(e1)
x2 = const_folding(e2)
when x1 is
Val a ->
Val(a) ->
when x2 is
Val b -> Val (a + b)
Add (Val b) x | Add x (Val b) -> Add (Val (a + b)) x
_ -> Add x1 x2
Val(b) -> Val((a + b))
Add(Val(b), x) | Add(x, Val(b)) -> Add(Val((a + b)), x)
_ -> Add(x1, x2)
_ -> Add x1 x2
_ -> Add(x1, x2)
Mul e1 e2 ->
x1 = constFolding e1
x2 = constFolding e2
Mul(e1, e2) ->
x1 = const_folding(e1)
x2 = const_folding(e2)
when x1 is
Val a ->
Val(a) ->
when x2 is
Val b -> Val (a * b)
Mul (Val b) x | Mul x (Val b) -> Mul (Val (a * b)) x
_ -> Mul x1 x2
Val(b) -> Val((a * b))
Mul(Val(b), x) | Mul(x, Val(b)) -> Mul(Val((a * b)), x)
_ -> Mul(x1, x2)
_ -> Mul x1 x2
_ -> Mul(x1, x2)
_ ->
e

View file

@ -1,48 +1,50 @@
app [main] { pf: platform "platform/main.roc" }
app [main!] { pf: platform "platform/main.roc" }
main! : {} => {}
main! = \{} ->
closure1({})
|> Result.try(closure2)
|> Result.try(closure3)
|> Result.try(closure4)
|> Result.with_default({})
main : Task {} []
main =
closure1 {}
|> Task.await (\_ -> closure2 {})
|> Task.await (\_ -> closure3 {})
|> Task.await (\_ -> closure4 {})
# ---
closure1 : {} -> Task {} []
closure1 : {} -> Result {} []
closure1 = \_ ->
Task.ok (foo toUnitBorrowed "a long string such that it's malloced")
|> Task.map \_ -> {}
Ok(foo(to_unit_borrowed, "a long string such that it's malloced"))
|> Result.map_ok(\_ -> {})
toUnitBorrowed = \x -> Str.countUtf8Bytes x
to_unit_borrowed = \x -> Str.count_utf8_bytes(x)
foo = \f, x -> f x
foo = \f, x -> f(x)
# ---
closure2 : {} -> Task {} []
closure2 : {} -> Result {} []
closure2 = \_ ->
x : Str
x = "a long string such that it's malloced"
Task.ok {}
|> Task.map (\_ -> x)
|> Task.map toUnit
Ok({})
|> Result.map_ok(\_ -> x)
|> Result.map_ok(to_unit)
toUnit = \_ -> {}
to_unit = \_ -> {}
# # ---
closure3 : {} -> Task {} []
closure3 : {} -> Result {} []
closure3 = \_ ->
x : Str
x = "a long string such that it's malloced"
Task.ok {}
|> Task.await (\_ -> Task.ok x |> Task.map (\_ -> {}))
Ok({})
|> Result.try(\_ -> Ok(x) |> Result.map_ok(\_ -> {}))
# # ---
closure4 : {} -> Task {} []
closure4 : {} -> Result {} []
closure4 = \_ ->
x : Str
x = "a long string such that it's malloced"
Task.ok {}
|> Task.await (\_ -> Task.ok x)
|> Task.map (\_ -> {})
Ok({})
|> Result.try(\_ -> Ok(x))
|> Result.map_ok(\_ -> {})

View file

@ -1,51 +1,49 @@
app [main] { pf: platform "platform/main.roc" }
app [main!] { pf: platform "platform/main.roc" }
import pf.PlatformTasks
import pf.Host
# based on: https://github.com/koka-lang/koka/blob/master/test/bench/haskell/deriv.hs
IO a : Task a []
main : Task {} []
main =
{ value, isError } = PlatformTasks.getInt!
inputResult =
if isError then
Err GetIntError
main! : {} => {}
main! = \{} ->
{ value, is_error } = Host.get_int!({})
input_result =
if is_error then
Err(GetIntError)
else
Ok value
Ok(value)
when inputResult is
Ok n ->
when input_result is
Ok(n) ->
x : Expr
x = Var "x"
x = Var("x")
f : Expr
f = pow x x
f = pow(x, x)
nest deriv n f # original koka n = 10
|> Task.map \_ -> {}
_ = nest!(deriv!, n, f) # original koka n = 10
{}
Err GetIntError ->
PlatformTasks.putLine "Error: Failed to get Integer from stdin."
Err(GetIntError) ->
Host.put_line!("Error: Failed to get Integer from stdin.")
nestHelp : I64, (I64, Expr -> IO Expr), I64, Expr -> IO Expr
nestHelp = \s, f, m, x ->
nest_help! : I64, (I64, Expr => Expr), I64, Expr => Expr
nest_help! = \s, f!, m, x ->
when m is
0 -> Task.ok x
0 -> x
_ ->
w = f! (s - m) x
nestHelp s f (m - 1) w
w = f!((s - m), x)
nest_help!(s, f!, (m - 1), w)
nest : (I64, Expr -> IO Expr), I64, Expr -> IO Expr
nest = \f, n, e -> nestHelp n f n e
nest! : (I64, Expr => Expr), I64, Expr => Expr
nest! = \f!, n, e -> nest_help!(n, f!, n, e)
Expr : [Val I64, Var Str, Add Expr Expr, Mul Expr Expr, Pow Expr Expr, Ln Expr]
divmod : I64, I64 -> Result { div : I64, mod : I64 } [DivByZero]
divmod = \l, r ->
when Pair (Num.divTruncChecked l r) (Num.remChecked l r) is
Pair (Ok div) (Ok mod) -> Ok { div, mod }
_ -> Err DivByZero
when Pair(Num.div_trunc_checked(l, r), Num.rem_checked(l, r)) is
Pair(Ok(div), Ok(mod)) -> Ok({ div, mod })
_ -> Err(DivByZero)
pown : I64, I64 -> I64
pown = \a, n ->
@ -53,119 +51,119 @@ pown = \a, n ->
0 -> 1
1 -> a
_ ->
when divmod n 2 is
Ok { div, mod } ->
b = pown a div
when divmod(n, 2) is
Ok({ div, mod }) ->
b = pown(a, div)
b * b * (if mod == 0 then 1 else a)
Err DivByZero ->
Err(DivByZero) ->
-1
add : Expr, Expr -> Expr
add = \a, b ->
when Pair a b is
Pair (Val n) (Val m) ->
Val (n + m)
when Pair(a, b) is
Pair(Val(n), Val(m)) ->
Val((n + m))
Pair (Val 0) f ->
Pair(Val(0), f) ->
f
Pair f (Val 0) ->
Pair(f, Val(0)) ->
f
Pair f (Val n) ->
add (Val n) f
Pair(f, Val(n)) ->
add(Val(n), f)
Pair (Val n) (Add (Val m) f) ->
add (Val (n + m)) f
Pair(Val(n), Add(Val(m), f)) ->
add(Val((n + m)), f)
Pair f (Add (Val n) g) ->
add (Val n) (add f g)
Pair(f, Add(Val(n), g)) ->
add(Val(n), add(f, g))
Pair (Add f g) h ->
add f (add g h)
Pair(Add(f, g), h) ->
add(f, add(g, h))
Pair f g ->
Add f g
Pair(f, g) ->
Add(f, g)
mul : Expr, Expr -> Expr
mul = \a, b ->
when Pair a b is
Pair (Val n) (Val m) ->
Val (n * m)
when Pair(a, b) is
Pair(Val(n), Val(m)) ->
Val((n * m))
Pair (Val 0) _ ->
Val 0
Pair(Val(0), _) ->
Val(0)
Pair _ (Val 0) ->
Val 0
Pair(_, Val(0)) ->
Val(0)
Pair (Val 1) f ->
Pair(Val(1), f) ->
f
Pair f (Val 1) ->
Pair(f, Val(1)) ->
f
Pair f (Val n) ->
mul (Val n) f
Pair(f, Val(n)) ->
mul(Val(n), f)
Pair (Val n) (Mul (Val m) f) ->
mul (Val (n * m)) f
Pair(Val(n), Mul(Val(m), f)) ->
mul(Val((n * m)), f)
Pair f (Mul (Val n) g) ->
mul (Val n) (mul f g)
Pair(f, Mul(Val(n), g)) ->
mul(Val(n), mul(f, g))
Pair (Mul f g) h ->
mul f (mul g h)
Pair(Mul(f, g), h) ->
mul(f, mul(g, h))
Pair f g ->
Mul f g
Pair(f, g) ->
Mul(f, g)
pow : Expr, Expr -> Expr
pow = \a, b ->
when Pair a b is
Pair (Val m) (Val n) -> Val (pown m n)
Pair _ (Val 0) -> Val 1
Pair f (Val 1) -> f
Pair (Val 0) _ -> Val 0
Pair f g -> Pow f g
when Pair(a, b) is
Pair(Val(m), Val(n)) -> Val(pown(m, n))
Pair(_, Val(0)) -> Val(1)
Pair(f, Val(1)) -> f
Pair(Val(0), _) -> Val(0)
Pair(f, g) -> Pow(f, g)
ln : Expr -> Expr
ln = \f ->
when f is
Val 1 -> Val 0
_ -> Ln f
Val(1) -> Val(0)
_ -> Ln(f)
d : Str, Expr -> Expr
d = \x, expr ->
when expr is
Val _ -> Val 0
Var y -> if x == y then Val 1 else Val 0
Add f g -> add (d x f) (d x g)
Mul f g -> add (mul f (d x g)) (mul g (d x f))
Pow f g ->
mul (pow f g) (add (mul (mul g (d x f)) (pow f (Val (-1)))) (mul (ln f) (d x g)))
Val(_) -> Val(0)
Var(y) -> if x == y then Val(1) else Val(0)
Add(f, g) -> add(d(x, f), d(x, g))
Mul(f, g) -> add(mul(f, d(x, g)), mul(g, d(x, f)))
Pow(f, g) ->
mul(pow(f, g), add(mul(mul(g, d(x, f)), pow(f, Val(-1))), mul(ln(f), d(x, g))))
Ln f ->
mul (d x f) (pow f (Val (-1)))
Ln(f) ->
mul(d(x, f), pow(f, Val(-1)))
count : Expr -> I64
count = \expr ->
when expr is
Val _ -> 1
Var _ -> 1
Add f g -> count f + count g
Mul f g -> count f + count g
Pow f g -> count f + count g
Ln f -> count f
Val(_) -> 1
Var(_) -> 1
Add(f, g) -> count(f) + count(g)
Mul(f, g) -> count(f) + count(g)
Pow(f, g) -> count(f) + count(g)
Ln(f) -> count(f)
deriv : I64, Expr -> IO Expr
deriv = \i, f ->
fprime = d "x" f
deriv! : I64, Expr => Expr
deriv! = \i, f ->
fprime = d("x", f)
line =
Num.toStr (i + 1)
|> Str.concat " count: "
|> Str.concat (Num.toStr (count fprime))
PlatformTasks.putLine! line
Task.ok fprime
Num.to_str((i + 1))
|> Str.concat(" count: ")
|> Str.concat(Num.to_str(count(fprime)))
Host.put_line!(line)
fprime

View file

@ -1,13 +1,13 @@
app [main] { pf: platform "platform/main.roc" }
app [main!] { pf: platform "platform/main.roc" }
import Issue2279Help
import pf.PlatformTasks
import pf.Host
main =
main! = \{} ->
text =
if Bool.true then
Issue2279Help.text
else
Issue2279Help.asText 42
Issue2279Help.as_text(42)
PlatformTasks.putLine text
Host.put_line!(text)

View file

@ -1,66 +1,66 @@
app [main] { pf: platform "platform/main.roc" }
app [main!] { pf: platform "platform/main.roc" }
import pf.PlatformTasks
import pf.Host
main : Task {} []
main =
{ value, isError } = PlatformTasks.getInt!
inputResult =
if isError then
Err GetIntError
main! : {} => {}
main! = \{} ->
{ value, is_error } = Host.get_int!({})
input_result =
if is_error then
Err(GetIntError)
else
Ok value
Ok(value)
when inputResult is
Ok n ->
queens n # original koka 13
|> Num.toStr
|> PlatformTasks.putLine
when input_result is
Ok(n) ->
queens(n) # original koka 13
|> Num.to_str
|> Host.put_line!
Err GetIntError ->
PlatformTasks.putLine "Error: Failed to get Integer from stdin."
Err(GetIntError) ->
Host.put_line!("Error: Failed to get Integer from stdin.")
ConsList a : [Nil, Cons a (ConsList a)]
queens = \n -> length (findSolutions n n)
queens = \n -> length(find_solutions(n, n))
findSolutions = \n, k ->
find_solutions = \n, k ->
if k <= 0 then
# should we use U64 as input type here instead?
Cons Nil Nil
Cons(Nil, Nil)
else
extend n Nil (findSolutions n (k - 1))
extend(n, Nil, find_solutions(n, (k - 1)))
extend = \n, acc, solutions ->
when solutions is
Nil -> acc
Cons soln rest -> extend n (appendSafe n soln acc) rest
Cons(soln, rest) -> extend(n, append_safe(n, soln, acc), rest)
appendSafe : I64, ConsList I64, ConsList (ConsList I64) -> ConsList (ConsList I64)
appendSafe = \k, soln, solns ->
append_safe : I64, ConsList I64, ConsList (ConsList I64) -> ConsList (ConsList I64)
append_safe = \k, soln, solns ->
if k <= 0 then
solns
else if safe k 1 soln then
appendSafe (k - 1) soln (Cons (Cons k soln) solns)
else if safe(k, 1, soln) then
append_safe((k - 1), soln, Cons(Cons(k, soln), solns))
else
appendSafe (k - 1) soln solns
append_safe((k - 1), soln, solns)
safe : I64, I64, ConsList I64 -> Bool
safe = \queen, diagonal, xs ->
when xs is
Nil -> Bool.true
Cons q t ->
Cons(q, t) ->
if queen != q && queen != q + diagonal && queen != q - diagonal then
safe queen (diagonal + 1) t
safe(queen, (diagonal + 1), t)
else
Bool.false
length : ConsList a -> I64
length = \xs ->
lengthHelp xs 0
length_help(xs, 0)
lengthHelp : ConsList a, I64 -> I64
lengthHelp = \foobar, acc ->
length_help : ConsList a, I64 -> I64
length_help = \foobar, acc ->
when foobar is
Cons _ lrest -> lengthHelp lrest (1 + acc)
Cons(_, lrest) -> length_help(lrest, (1 + acc))
Nil -> acc

View file

@ -0,0 +1,9 @@
hosted Host
exposes [put_line!, put_int!, get_int!]
imports []
put_line! : Str => {}
put_int! : I64 => {}
get_int! : {} => { value : I64, is_error : Bool }

View file

@ -1,9 +0,0 @@
hosted PlatformTasks
exposes [putLine, putInt, getInt]
imports []
putLine : Str -> Task {} *
putInt : I64 -> Task {} *
getInt : Task { value : I64, isError : Bool } *

View file

@ -1,3 +1,4 @@
app [main] { pf: platform "main.roc" }
app [main!] { pf: platform "main.roc" }
main = Task.ok {}
main! : {} => {}
main! = \{} -> {}

View file

@ -10,11 +10,7 @@ const maxInt = std.math.maxInt;
const mem = std.mem;
const Allocator = mem.Allocator;
extern fn roc__mainForHost_1_exposed_generic([*]u8) void;
extern fn roc__mainForHost_1_exposed_size() i64;
extern fn roc__mainForHost_0_caller(*const u8, [*]u8, [*]u8) void;
extern fn roc__mainForHost_0_size() i64;
extern fn roc__mainForHost_0_result_size() i64;
extern fn roc__main_for_host_1_exposed() void;
const Align = 2 * @alignOf(usize);
extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque;
@ -112,48 +108,12 @@ comptime {
const Unit = extern struct {};
pub export fn main() u8 {
// The size might be zero; if so, make it at least 8 so that we don't have a nullptr
const size = @max(@as(usize, @intCast(roc__mainForHost_1_exposed_size())), 8);
const raw_output = roc_alloc(@as(usize, @intCast(size)), @alignOf(u64)) orelse {
std.log.err("Memory allocation failed", .{});
return 1;
};
const output = @as([*]u8, @ptrCast(raw_output));
defer {
roc_dealloc(raw_output, @alignOf(u64));
}
roc__mainForHost_1_exposed_generic(output);
const closure_data_pointer = @as([*]u8, @ptrCast(output));
call_the_closure(closure_data_pointer);
roc__main_for_host_1_exposed();
return 0;
}
fn call_the_closure(closure_data_pointer: [*]u8) void {
const allocator = std.heap.page_allocator;
// The size might be zero; if so, make it at least 8 so that we don't have a nullptr
const size = @max(roc__mainForHost_0_result_size(), 8);
const raw_output = allocator.alignedAlloc(u8, @alignOf(u64), @as(usize, @intCast(size))) catch unreachable;
const output = @as([*]u8, @ptrCast(raw_output));
defer {
allocator.free(raw_output);
}
const flags: u8 = 0;
roc__mainForHost_0_caller(&flags, closure_data_pointer, output);
// The closure returns result, nothing interesting to do with it
return;
}
pub export fn roc_fx_putInt(int: i64) i64 {
pub export fn roc_fx_put_int(int: i64) i64 {
const stdout = std.io.getStdOut().writer();
stdout.print("{d}", .{int}) catch unreachable;
@ -163,7 +123,7 @@ pub export fn roc_fx_putInt(int: i64) i64 {
return 0;
}
export fn roc_fx_putLine(rocPath: *str.RocStr) callconv(.C) void {
export fn roc_fx_put_line(rocPath: *str.RocStr) callconv(.C) void {
const stdout = std.io.getStdOut().writer();
for (rocPath.asSlice()) |char| {
@ -180,14 +140,14 @@ const GetInt = extern struct {
comptime {
if (@sizeOf(usize) == 8) {
@export(roc_fx_getInt_64bit, .{ .name = "roc_fx_getInt" });
@export(roc_fx_get_int_64bit, .{ .name = "roc_fx_get_int" });
} else {
@export(roc_fx_getInt_32bit, .{ .name = "roc_fx_getInt" });
@export(roc_fx_get_int_32bit, .{ .name = "roc_fx_get_int" });
}
}
fn roc_fx_getInt_64bit() callconv(.C) GetInt {
if (roc_fx_getInt_help()) |value| {
fn roc_fx_get_int_64bit() callconv(.C) GetInt {
if (roc_fx_get_int_help()) |value| {
const get_int = GetInt{ .is_error = false, .value = value };
return get_int;
} else |err| switch (err) {
@ -202,8 +162,8 @@ fn roc_fx_getInt_64bit() callconv(.C) GetInt {
return 0;
}
fn roc_fx_getInt_32bit(output: *GetInt) callconv(.C) void {
if (roc_fx_getInt_help()) |value| {
fn roc_fx_get_int_32bit(output: *GetInt) callconv(.C) void {
if (roc_fx_get_int_help()) |value| {
const get_int = GetInt{ .is_error = false, .value = value };
output.* = get_int;
} else |err| switch (err) {
@ -218,7 +178,7 @@ fn roc_fx_getInt_32bit(output: *GetInt) callconv(.C) void {
return;
}
fn roc_fx_getInt_help() !i64 {
fn roc_fx_get_int_help() !i64 {
const stdout = std.io.getStdOut().writer();
stdout.print("Please enter an integer\n", .{}) catch unreachable;

View file

@ -1,9 +1,9 @@
platform "benchmarks"
requires {} { main : Task {} [] }
requires {} { main! : {} => {} }
exposes []
packages {}
imports []
provides [mainForHost]
provides [main_for_host!]
mainForHost : Task {} []
mainForHost = main
main_for_host! : {} => {}
main_for_host! = \{} -> main!({})

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
app [main] { pf: platform "platform/main.roc" }
app [main!] { pf: platform "platform/main.roc" }
import pf.PlatformTasks
import pf.Host
Color : [Red, Black]
@ -10,75 +10,75 @@ Map : Tree I64 Bool
ConsList a : [Nil, Cons a (ConsList a)]
makeMap : I64, I64 -> ConsList Map
makeMap = \freq, n ->
makeMapHelp freq n Leaf Nil
make_map : I64, I64 -> ConsList Map
make_map = \freq, n ->
make_map_help(freq, n, Leaf, Nil)
makeMapHelp : I64, I64, Map, ConsList Map -> ConsList Map
makeMapHelp = \freq, n, m, acc ->
make_map_help : I64, I64, Map, ConsList Map -> ConsList Map
make_map_help = \freq, n, m, acc ->
when n is
0 -> Cons m acc
0 -> Cons(m, acc)
_ ->
powerOf10 =
power_of10 =
n % 10 == 0
m1 = insert m n powerOf10
m1 = insert(m, n, power_of10)
isFrequency =
is_frequency =
n % freq == 0
x = (if isFrequency then Cons m1 acc else acc)
x = (if is_frequency then Cons(m1, acc) else acc)
makeMapHelp freq (n - 1) m1 x
make_map_help(freq, (n - 1), m1, x)
fold : (a, b, omega -> omega), Tree a b, omega -> omega
fold = \f, tree, b ->
when tree is
Leaf -> b
Node _ l k v r -> fold f r (f k v (fold f l b))
Node(_, l, k, v, r) -> fold(f, r, f(k, v, fold(f, l, b)))
main : Task {} []
main =
{ value, isError } = PlatformTasks.getInt!
inputResult =
if isError then
Err GetIntError
main! : {} => {}
main! = \{} ->
{ value, is_error } = Host.get_int!({})
input_result =
if is_error then
Err(GetIntError)
else
Ok value
Ok(value)
when inputResult is
Ok n ->
when input_result is
Ok(n) ->
# original koka n = 4_200_000
ms : ConsList Map
ms = makeMap 5 n
ms = make_map(5, n)
when ms is
Cons head _ ->
val = fold (\_, v, r -> if v then r + 1 else r) head 0
Cons(head, _) ->
val = fold(\_, v, r -> if v then r + 1 else r, head, 0)
val
|> Num.toStr
|> PlatformTasks.putLine
|> Num.to_str
|> Host.put_line!
Nil ->
PlatformTasks.putLine "fail"
Host.put_line!("fail")
Err GetIntError ->
PlatformTasks.putLine "Error: Failed to get Integer from stdin."
Err(GetIntError) ->
Host.put_line!("Error: Failed to get Integer from stdin.")
insert : Tree (Num k) v, Num k, v -> Tree (Num k) v
insert = \t, k, v -> if isRed t then setBlack (ins t k v) else ins t k v
insert = \t, k, v -> if is_red(t) then set_black(ins(t, k, v)) else ins(t, k, v)
setBlack : Tree a b -> Tree a b
setBlack = \tree ->
set_black : Tree a b -> Tree a b
set_black = \tree ->
when tree is
Node _ l k v r -> Node Black l k v r
Node(_, l, k, v, r) -> Node(Black, l, k, v, r)
_ -> tree
isRed : Tree a b -> Bool
isRed = \tree ->
is_red : Tree a b -> Bool
is_red = \tree ->
when tree is
Node Red _ _ _ _ -> Bool.true
Node(Red, _, _, _, _) -> Bool.true
_ -> Bool.false
lt = \x, y -> x < y
@ -86,43 +86,43 @@ lt = \x, y -> x < y
ins : Tree (Num k) v, Num k, v -> Tree (Num k) v
ins = \tree, kx, vx ->
when tree is
Leaf -> Node Red Leaf kx vx Leaf
Node Red a ky vy b ->
if lt kx ky then
Node Red (ins a kx vx) ky vy b
else if lt ky kx then
Node Red a ky vy (ins b kx vx)
Leaf -> Node(Red, Leaf, kx, vx, Leaf)
Node(Red, a, ky, vy, b) ->
if lt(kx, ky) then
Node(Red, ins(a, kx, vx), ky, vy, b)
else if lt(ky, kx) then
Node(Red, a, ky, vy, ins(b, kx, vx))
else
Node Red a ky vy (ins b kx vx)
Node(Red, a, ky, vy, ins(b, kx, vx))
Node Black a ky vy b ->
if lt kx ky then
if isRed a then
balance1 (Node Black Leaf ky vy b) (ins a kx vx)
Node(Black, a, ky, vy, b) ->
if lt(kx, ky) then
if is_red(a) then
balance1(Node(Black, Leaf, ky, vy, b), ins(a, kx, vx))
else
Node Black (ins a kx vx) ky vy b
else if lt ky kx then
if isRed b then
balance2 (Node Black a ky vy Leaf) (ins b kx vx)
Node(Black, ins(a, kx, vx), ky, vy, b)
else if lt(ky, kx) then
if is_red(b) then
balance2(Node(Black, a, ky, vy, Leaf), ins(b, kx, vx))
else
Node Black a ky vy (ins b kx vx)
Node(Black, a, ky, vy, ins(b, kx, vx))
else
Node Black a kx vx b
Node(Black, a, kx, vx, b)
balance1 : Tree a b, Tree a b -> Tree a b
balance1 = \tree1, tree2 ->
when tree1 is
Leaf -> Leaf
Node _ _ kv vv t ->
Node(_, _, kv, vv, t) ->
when tree2 is
Node _ (Node Red l kx vx r1) ky vy r2 ->
Node Red (Node Black l kx vx r1) ky vy (Node Black r2 kv vv t)
Node(_, Node(Red, l, kx, vx, r1), ky, vy, r2) ->
Node(Red, Node(Black, l, kx, vx, r1), ky, vy, Node(Black, r2, kv, vv, t))
Node _ l1 ky vy (Node Red l2 kx vx r) ->
Node Red (Node Black l1 ky vy l2) kx vx (Node Black r kv vv t)
Node(_, l1, ky, vy, Node(Red, l2, kx, vx, r)) ->
Node(Red, Node(Black, l1, ky, vy, l2), kx, vx, Node(Black, r, kv, vv, t))
Node _ l ky vy r ->
Node Black (Node Red l ky vy r) kv vv t
Node(_, l, ky, vy, r) ->
Node(Black, Node(Red, l, ky, vy, r), kv, vv, t)
Leaf -> Leaf
@ -130,16 +130,16 @@ balance2 : Tree a b, Tree a b -> Tree a b
balance2 = \tree1, tree2 ->
when tree1 is
Leaf -> Leaf
Node _ t kv vv _ ->
Node(_, t, kv, vv, _) ->
when tree2 is
Node _ (Node Red l kx1 vx1 r1) ky vy r2 ->
Node Red (Node Black t kv vv l) kx1 vx1 (Node Black r1 ky vy r2)
Node(_, Node(Red, l, kx1, vx1, r1), ky, vy, r2) ->
Node(Red, Node(Black, t, kv, vv, l), kx1, vx1, Node(Black, r1, ky, vy, r2))
Node _ l1 ky vy (Node Red l2 kx2 vx2 r2) ->
Node Red (Node Black t kv vv l1) ky vy (Node Black l2 kx2 vx2 r2)
Node(_, l1, ky, vy, Node(Red, l2, kx2, vx2, r2)) ->
Node(Red, Node(Black, t, kv, vv, l1), ky, vy, Node(Black, l2, kx2, vx2, r2))
Node _ l ky vy r ->
Node Black t kv vv (Node Red l ky vy r)
Node(_, l, ky, vy, r) ->
Node(Black, t, kv, vv, Node(Red, l, ky, vy, r))
Leaf ->
Leaf

View file

@ -1,45 +1,45 @@
app [main] { pf: platform "platform/main.roc" }
app [main!] { pf: platform "platform/main.roc" }
import pf.PlatformTasks
import pf.Host
main : Task {} []
main =
main! : {} => {}
main! = \{} ->
tree : RedBlackTree I64 {}
tree = insert 0 {} Empty
tree = insert(0, {}, Empty)
tree
|> show
|> PlatformTasks.putLine
|> Host.put_line!
show : RedBlackTree I64 {} -> Str
show = \tree -> showRBTree tree Num.toStr (\{} -> "{}")
show = \tree -> show_rb_tree(tree, Num.to_str, \{} -> "{}")
showRBTree : RedBlackTree k v, (k -> Str), (v -> Str) -> Str
showRBTree = \tree, showKey, showValue ->
show_rb_tree : RedBlackTree k v, (k -> Str), (v -> Str) -> Str
show_rb_tree = \tree, show_key, show_value ->
when tree is
Empty -> "Empty"
Node color key value left right ->
sColor = showColor color
sKey = showKey key
sValue = showValue value
sL = nodeInParens left showKey showValue
sR = nodeInParens right showKey showValue
Node(color, key, value, left, right) ->
s_color = show_color(color)
s_key = show_key(key)
s_value = show_value(value)
s_l = node_in_parens(left, show_key, show_value)
s_r = node_in_parens(right, show_key, show_value)
"Node $(sColor) $(sKey) $(sValue) $(sL) $(sR)"
"Node ${s_color} ${s_key} ${s_value} ${s_l} ${s_r}"
nodeInParens : RedBlackTree k v, (k -> Str), (v -> Str) -> Str
nodeInParens = \tree, showKey, showValue ->
node_in_parens : RedBlackTree k v, (k -> Str), (v -> Str) -> Str
node_in_parens = \tree, show_key, show_value ->
when tree is
Empty ->
showRBTree tree showKey showValue
show_rb_tree(tree, show_key, show_value)
Node _ _ _ _ _ ->
inner = showRBTree tree showKey showValue
Node(_, _, _, _, _) ->
inner = show_rb_tree(tree, show_key, show_value)
"($(inner))"
"(${inner})"
showColor : NodeColor -> Str
showColor = \color ->
show_color : NodeColor -> Str
show_color = \color ->
when color is
Red -> "Red"
Black -> "Black"
@ -52,49 +52,51 @@ Key k : Num k
insert : Key k, v, RedBlackTree (Key k) v -> RedBlackTree (Key k) v
insert = \key, value, dict ->
when insertHelp key value dict is
Node Red k v l r -> Node Black k v l r
when insert_help(key, value, dict) is
Node(Red, k, v, l, r) -> Node(Black, k, v, l, r)
x -> x
insertHelp : Key k, v, RedBlackTree (Key k) v -> RedBlackTree (Key k) v
insertHelp = \key, value, dict ->
insert_help : Key k, v, RedBlackTree (Key k) v -> RedBlackTree (Key k) v
insert_help = \key, value, dict ->
when dict is
Empty ->
# New nodes are always red. If it violates the rules, it will be fixed
# when balancing.
Node Red key value Empty Empty
Node(Red, key, value, Empty, Empty)
Node nColor nKey nValue nLeft nRight ->
when Num.compare key nKey is
LT -> balance nColor nKey nValue (insertHelp key value nLeft) nRight
EQ -> Node nColor nKey value nLeft nRight
GT -> balance nColor nKey nValue nLeft (insertHelp key value nRight)
Node(n_color, n_key, n_value, n_left, n_right) ->
when Num.compare(key, n_key) is
LT -> balance(n_color, n_key, n_value, insert_help(key, value, n_left), n_right)
EQ -> Node(n_color, n_key, value, n_left, n_right)
GT -> balance(n_color, n_key, n_value, n_left, insert_help(key, value, n_right))
balance : NodeColor, k, v, RedBlackTree k v, RedBlackTree k v -> RedBlackTree k v
balance = \color, key, value, left, right ->
when right is
Node Red rK rV rLeft rRight ->
Node(Red, r_k, r_v, r_left, r_right) ->
when left is
Node Red lK lV lLeft lRight ->
Node
Red
key
value
(Node Black lK lV lLeft lRight)
(Node Black rK rV rLeft rRight)
Node(Red, l_k, l_v, l_left, l_right) ->
Node(
Red,
key,
value,
Node(Black, l_k, l_v, l_left, l_right),
Node(Black, r_k, r_v, r_left, r_right),
)
_ ->
Node color rK rV (Node Red key value left rLeft) rRight
Node(color, r_k, r_v, Node(Red, key, value, left, r_left), r_right)
_ ->
when left is
Node Red lK lV (Node Red llK llV llLeft llRight) lRight ->
Node
Red
lK
lV
(Node Black llK llV llLeft llRight)
(Node Black key value lRight right)
Node(Red, l_k, l_v, Node(Red, ll_k, ll_v, ll_left, ll_right), l_right) ->
Node(
Red,
l_k,
l_v,
Node(Black, ll_k, ll_v, ll_left, ll_right),
Node(Black, key, value, l_right, right),
)
_ ->
Node color key value left right
Node(color, key, value, left, right)

View file

@ -1,13 +1,13 @@
app [main] { pf: platform "platform/main.roc" }
app [main!] { pf: platform "platform/main.roc" }
import pf.PlatformTasks
import pf.Host
import AStar
main =
PlatformTasks.putLine! (showBool test1)
main! = \{} ->
Host.put_line!(show_bool(test1))
showBool : Bool -> Str
showBool = \b ->
show_bool : Bool -> Str
show_bool = \b ->
if
b
then
@ -24,14 +24,14 @@ example1 =
step : I64 -> Set I64
step = \n ->
when n is
1 -> Set.fromList [2, 3]
2 -> Set.fromList [4]
3 -> Set.fromList [4]
_ -> Set.fromList []
1 -> Set.from_list([2, 3])
2 -> Set.from_list([4])
3 -> Set.from_list([4])
_ -> Set.from_list([])
cost : I64, I64 -> F64
cost = \_, _ -> 1
when AStar.findPath cost step 1 4 is
Ok path -> path
Err _ -> []
when AStar.find_path(cost, step, 1, 4) is
Ok(path) -> path
Err(_) -> []

View file

@ -1,17 +1,15 @@
app [main] { pf: platform "platform/main.roc" }
app [main!] { pf: platform "platform/main.roc" }
import Base64
import pf.PlatformTasks
import pf.Host
IO a : Task a []
main! : {} => {}
main! = \{} ->
when Base64.from_bytes(Str.to_utf8("Hello World")) is
Err(_) -> Host.put_line!("sadness")
Ok(encoded) ->
Host.put_line!(Str.concat("encoded: ", encoded))
main : IO {}
main =
when Base64.fromBytes (Str.toUtf8 "Hello World") is
Err _ -> PlatformTasks.putLine "sadness"
Ok encoded ->
PlatformTasks.putLine! (Str.concat "encoded: " encoded)
when Base64.toStr encoded is
Ok decoded -> PlatformTasks.putLine (Str.concat "decoded: " decoded)
Err _ -> PlatformTasks.putLine "sadness"
when Base64.to_str(encoded) is
Ok(decoded) -> Host.put_line!(Str.concat("decoded: ", decoded))
Err(_) -> Host.put_line!("sadness")

View file

@ -50,12 +50,12 @@ mod cli_tests {
const TEST_LEGACY_LINKER: bool = false;
#[test]
#[ignore = "Works when run manually, but not in CI"]
#[ignore = "Needs investigation, see also github.com/roc-lang/roc/pull/7231"]
fn platform_switching_rust() {
// pre-build the platform
std::process::Command::new("bash")
.arg(file_from_root(
"examples/platform-switching/rust-platform",
"crates/cli/tests/platform-switching/rust-platform",
"build.sh",
))
.status()
@ -63,7 +63,7 @@ mod cli_tests {
let cli_build = ExecCli::new(
roc_cli::CMD_DEV,
file_from_root("examples/platform-switching", "rocLovesRust.roc"),
file_from_root("crates/cli/tests/platform-switching", "rocLovesRust.roc"),
);
let expected_output = "Roc <3 Rust!\n";
@ -80,7 +80,7 @@ mod cli_tests {
let cli_build = ExecCli::new(
CMD_BUILD,
file_from_root("examples/platform-switching", "rocLovesZig.roc"),
file_from_root("crates/cli/tests/platform-switching", "rocLovesZig.roc"),
)
.arg(BUILD_HOST_FLAG)
.arg(SUPPRESS_BUILD_HOST_WARNING_FLAG);
@ -104,7 +104,10 @@ mod cli_tests {
// so let's just check it for now
let cli_check = ExecCli::new(
CMD_CHECK,
file_from_root("examples/platform-switching", "rocLovesWebAssembly.roc"),
file_from_root(
"crates/cli/tests/platform-switching",
"rocLovesWebAssembly.roc",
),
);
let cli_check_out = cli_check.run();
@ -160,34 +163,6 @@ mod cli_tests {
);
}
// TODO: write a new test once mono bugs are resolved in investigation
// Encountering this TODO years later, I presume the new test should test the execution, not just roc check.
#[test]
#[cfg(not(debug_assertions))] // https://github.com/roc-lang/roc/issues/4806 - later observation: this issue is closed but the tests still hangs in debug mode
fn check_virtual_dom_server() {
let cli_check = ExecCli::new(
CMD_CHECK,
file_from_root("examples/virtual-dom-wip", "example-server.roc"),
);
let cli_check_out = cli_check.run();
cli_check_out.assert_clean_success();
}
// TODO: write a new test once mono bugs are resolved in investigation
// Encountering this TODO years later, I presume the new test should test the execution, not just roc check.
#[test]
#[cfg(not(debug_assertions))] // https://github.com/roc-lang/roc/issues/4806 - later observation: this issue is closed but the tests still hangs in debug mode
fn check_virtual_dom_client() {
let cli_check = ExecCli::new(
CMD_CHECK,
file_from_root("examples/virtual-dom-wip", "example-client.roc"),
);
let cli_check_out = cli_check.run();
cli_check_out.assert_clean_success();
}
#[test]
#[cfg_attr(windows, ignore)]
// tea = The Elm Architecture
@ -212,31 +187,35 @@ mod cli_tests {
);
}
// TODO check this out, there's more that's going wrong than a segfault
//#[test]
/*#[cfg_attr(
any(target_os = "windows", target_os = "linux", target_os = "macos"),
ignore = "Segfault, likely broken because of alias analysis: https://github.com/roc-lang/roc/issues/6544"
)]*/
/*
#[test]
// #[cfg_attr(windows, ignore)]
#[ignore]
fn false_interpreter() {
let cli_build = ExecCli::new(
CMD_BUILD,
file_from_root("crates/cli/tests/test-projects/false-interpreter", "main.roc")
)
.arg(BUILD_HOST_FLAG)
.arg(SUPPRESS_BUILD_HOST_WARNING_FLAG);
CMD_BUILD,
file_from_root(
"crates/cli/tests/test-projects/false-interpreter",
"main.roc",
),
)
.arg(BUILD_HOST_FLAG)
.arg(SUPPRESS_BUILD_HOST_WARNING_FLAG);
let sqrt_false_path_buf = file_from_root("crates/cli/tests/test-projects/false-interpreter/examples", "sqrt.false");
let sqrt_false_path_buf = file_from_root(
"crates/cli/tests/test-projects/false-interpreter/examples",
"sqrt.false",
);
let app_args = ["--",
sqrt_false_path_buf
.as_path()
.to_str()
.unwrap()];
let app_args = [sqrt_false_path_buf.as_path().to_str().unwrap()];
cli_build.full_check_build_and_run("1414", TEST_LEGACY_LINKER, ALLOW_VALGRIND, None, Some(&app_args));
}*/
cli_build.full_check_build_and_run(
"1414",
TEST_LEGACY_LINKER,
ALLOW_VALGRIND,
None,
Some(&app_args),
);
}
#[test]
#[cfg_attr(windows, ignore)]
@ -874,7 +853,7 @@ mod cli_tests {
),
);
let expected_output = "(@Community {friends: [{2}, {2}, {0, 1}], people: [(@Person {age: 27, favoriteColor: Blue, firstName: \"John\", hasBeard: Bool.true, lastName: \"Smith\"}), (@Person {age: 47, favoriteColor: Green, firstName: \"Debby\", hasBeard: Bool.false, lastName: \"Johnson\"}), (@Person {age: 33, favoriteColor: (RGB (255, 255, 0)), firstName: \"Jane\", hasBeard: Bool.false, lastName: \"Doe\"})]})\n";
let expected_output = "(@Community {friends: [{2}, {2}, {0, 1}], people: [(@Person {age: 27, favorite_color: Blue, first_name: \"John\", has_beard: Bool.true, last_name: \"Smith\"}), (@Person {age: 47, favorite_color: Green, first_name: \"Debby\", has_beard: Bool.false, last_name: \"Johnson\"}), (@Person {age: 33, favorite_color: (RGB (255, 255, 0)), first_name: \"Jane\", has_beard: Bool.false, last_name: \"Doe\"})]})\n";
cli_build.full_check_build_and_run(
expected_output,
@ -891,7 +870,7 @@ mod cli_tests {
build_platform_host();
let cli_build = ExecCli::new(
roc_cli::CMD_DEV,
roc_cli::CMD_BUILD,
file_from_root("crates/cli/tests/test-projects/effectful", "form.roc"),
);
@ -911,14 +890,14 @@ mod cli_tests {
fn effectful_hello() {
build_platform_host();
let cli_build = ExecCli::new(
let cli_dev = ExecCli::new(
roc_cli::CMD_DEV,
file_from_root("crates/cli/tests/test-projects/effectful/", "hello.roc"),
);
let expected_out = "I'm an effect 👻\n";
cli_build.run().assert_clean_stdout(expected_out);
cli_dev.run().assert_clean_stdout(expected_out);
}
#[test]
@ -926,14 +905,14 @@ mod cli_tests {
fn effectful_loops() {
build_platform_host();
let cli_build = ExecCli::new(
let cli_dev = ExecCli::new(
roc_cli::CMD_DEV,
file_from_root("crates/cli/tests/test-projects/effectful/", "loops.roc"),
);
let expected_out = "Lu\nMarce\nJoaquin\nChloé\nMati\nPedro\n";
cli_build.run().assert_clean_stdout(expected_out);
cli_dev.run().assert_clean_stdout(expected_out);
}
#[test]
@ -941,7 +920,7 @@ mod cli_tests {
fn effectful_untyped_passed_fx() {
build_platform_host();
let cli_build = ExecCli::new(
let cli_dev = ExecCli::new(
roc_cli::CMD_DEV,
file_from_root(
"crates/cli/tests/test-projects/effectful/",
@ -951,7 +930,7 @@ mod cli_tests {
let expected_out = "Before hello\nHello, World!\nAfter hello\n";
cli_build.run().assert_clean_stdout(expected_out);
cli_dev.run().assert_clean_stdout(expected_out);
}
#[test]
@ -959,7 +938,7 @@ mod cli_tests {
fn effectful_ignore_result() {
build_platform_host();
let cli_build = ExecCli::new(
let cli_dev = ExecCli::new(
roc_cli::CMD_DEV,
file_from_root(
"crates/cli/tests/test-projects/effectful/",
@ -969,7 +948,7 @@ mod cli_tests {
let expected_out = "I asked for input and I ignored it. Deal with it! 😎\n";
cli_build.run().assert_clean_stdout(expected_out);
cli_dev.run().assert_clean_stdout(expected_out);
}
#[test]
@ -978,14 +957,14 @@ mod cli_tests {
build_platform_host();
let cli_build = ExecCli::new(
roc_cli::CMD_DEV,
roc_cli::CMD_BUILD,
file_from_root(
"crates/cli/tests/test-projects/effectful",
"suffixed_record_field.roc",
),
);
let expected_output = "notEffectful: hardcoded\neffectful: from stdin\n";
let expected_output = "not_effectful: hardcoded\neffectful: from stdin\n";
cli_build.check_build_and_run(
expected_output,
@ -1001,7 +980,7 @@ mod cli_tests {
build_platform_host();
let cli_build = ExecCli::new(
roc_cli::CMD_DEV,
roc_cli::CMD_BUILD,
file_from_root("crates/cli/tests/test-projects/effectful", "on_err.roc"),
);
@ -1016,7 +995,7 @@ mod cli_tests {
build_platform_host();
let cli_build = ExecCli::new(
roc_cli::CMD_DEV,
roc_cli::CMD_BUILD,
file_from_root(
"crates/cli/tests/test-projects/effectful",
"for_each_try.roc",
@ -1302,6 +1281,7 @@ mod cli_tests {
}
#[test]
#[ignore = "flaky currently due to 7022"]
fn known_type_error() {
let cli_check = ExecCli::new(
CMD_CHECK,

View file

@ -0,0 +1,14 @@
# Platform switching
To run, `cd` into this directory and run this in your terminal:
```bash
roc --build-host --suppress-build-host-warning rocLovesC.roc
```
## About these examples
They use a very simple [platform](https://www.roc-lang.org/platforms) which does nothing more than printing the string you give it.
If you want to start building your own platforms, these are some very simple example platforms to use as starting points.

View file

@ -85,12 +85,12 @@ size_t roc_str_len(struct RocStr str) {
}
}
extern void roc__mainForHost_1_exposed_generic(struct RocStr *string);
extern void roc__main_for_host_1_exposed_generic(struct RocStr *string);
int main() {
struct RocStr str;
roc__mainForHost_1_exposed_generic(&str);
roc__main_for_host_1_exposed_generic(&str);
// Determine str_len and the str_bytes pointer,
// taking into account the small string optimization.

View file

@ -3,7 +3,7 @@ platform "echo-in-c"
exposes []
packages {}
imports []
provides [mainForHost]
provides [main_for_host]
mainForHost : Str
mainForHost = main
main_for_host : Str
main_for_host = main

View file

@ -1,5 +1,9 @@
app [main] { pf: platform "c-platform/main.roc" }
# run with `roc --build-host --suppress-build-host-warning rocLovesZig.roc`
# may require:
# ubuntu: sudo apt install build-essential clang
# fedora: sudo dnf install clang
# run with `roc --build-host --suppress-build-host-warning rocLovesC.roc`
main = "Roc <3 C!\n"

View file

@ -0,0 +1,10 @@
app [main] { pf: platform "zig-platform/main.roc" }
# To run:
# cd examples/platform-switching/zig-platform
# mkdir glue
# cp crates/compiler/builtins/bitcode/src/* ./glue
# cd -
# roc --build-host --suppress-build-host-warning rocLovesZig.roc
main = "Roc <3 Zig!\n"

View file

@ -2,7 +2,7 @@
//
// ```
// = note: Undefined symbols for architecture arm64:
// "_roc__mainForHost_1_exposed_generic", referenced from:
// "_roc__main_for_host_1_exposed_generic", referenced from:
// _main in rustplatform-df9e357e0cc989a6.rustplatform.863be87f3956573-cgu.0.rcgu.o
// ld: symbol(s) not found for architecture arm64
// clang-16: error: linker command failed with exit code 1 (use -v to see invocation)

View file

@ -3,7 +3,7 @@ platform "echo-in-rust"
exposes []
packages {}
imports []
provides [mainForHost]
provides [main_for_host]
mainForHost : Str
mainForHost = main
main_for_host : Str
main_for_host = main

View file

@ -1,9 +1,9 @@
[toolchain]
channel = "1.77.2"
profile = "default"
components = [
# for usages of rust-analyzer or similar tools inside `nix develop`
"rust-src"
[toolchain]
channel = "1.77.2"
profile = "default"
components = [
# for usages of rust-analyzer or similar tools inside `nix develop`
"rust-src"
]

View file

@ -6,7 +6,7 @@ use roc_std::RocStr;
use std::io::Write;
extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"]
#[link_name = "roc__main_for_host_1_exposed_generic"]
fn roc_main(_: &mut RocStr);
}

View file

@ -35,14 +35,14 @@ export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
// NOTE roc_panic and roc_dbg is provided in the JS file, so it can throw an exception
extern fn roc__mainForHost_1_exposed(*RocStr) void;
extern fn roc__main_for_host_1_exposed(*RocStr) void;
extern fn js_display_roc_string(str_bytes: ?[*]u8, str_len: usize) void;
pub export fn main() u8 {
// actually call roc to populate the callresult
var callresult = RocStr.empty();
roc__mainForHost_1_exposed(&callresult);
roc__main_for_host_1_exposed(&callresult);
// display the result using JavaScript
js_display_roc_string(callresult.asU8ptrMut(), callresult.len());

View file

@ -3,7 +3,7 @@ platform "echo-in-web-assembly"
exposes []
packages {}
imports []
provides [mainForHost]
provides [main_for_host]
mainForHost : Str
mainForHost = main
main_for_host : Str
main_for_host = main

View file

@ -102,7 +102,7 @@ comptime {
const mem = std.mem;
const Allocator = mem.Allocator;
extern fn roc__mainForHost_1_exposed_generic(*RocStr) void;
extern fn roc__main_for_host_1_exposed_generic(*RocStr) void;
const Unit = extern struct {};
@ -111,7 +111,7 @@ pub export fn main() u8 {
// actually call roc to populate the callresult
var callresult = RocStr.empty();
roc__mainForHost_1_exposed_generic(&callresult);
roc__main_for_host_1_exposed_generic(&callresult);
// stdout the result
stdout.print("{s}", .{callresult.asSlice()}) catch unreachable;

View file

@ -3,7 +3,7 @@ platform "echo-in-zig"
exposes []
packages {}
imports []
provides [mainForHost]
provides [main_for_host]
mainForHost : Str
mainForHost = main
main_for_host : Str
main_for_host = main

View file

@ -1,6 +1,7 @@
---
source: crates/cli/tests/cli_tests.rs
expression: cli_check_out.normalize_stdout_and_stderr()
snapshot_kind: text
---
── MISSING DEFINITION in tests/test-projects/known_bad/ExposedNotDefined.roc ───
@ -12,4 +13,5 @@ from exposes.
────────────────────────────────────────────────────────────────────────────────
1 error and 0 warning found in <ignored for test> ms
1 error and 0 warnings found in <ignored for test> ms.

View file

@ -1,6 +1,7 @@
---
source: crates/cli/tests/cli_tests.rs
expression: cli_check_out.normalize_stdout_and_stderr()
snapshot_kind: text
---
── TYPE MISMATCH in tests/test-projects/known_bad/TypeError.roc ────────────────
@ -25,4 +26,5 @@ this out.
────────────────────────────────────────────────────────────────────────────────
1 error and 0 warning found in <ignored for test> ms
1 error and 0 warnings found in <ignored for test> ms.

View file

@ -1,6 +1,7 @@
---
source: crates/cli/tests/cli_tests.rs
expression: cli_check_out.normalize_stdout_and_stderr()
snapshot_kind: text
---
── UNUSED IMPORT in ...nown_bad/UnusedImportButWithALongFileNameForTesting.roc ─
@ -14,4 +15,5 @@ Since Symbol isn't used, you don't need to import it.
────────────────────────────────────────────────────────────────────────────────
0 error and 1 warning found in <ignored for test> ms
0 errors and 1 warning found in <ignored for test> ms.

View file

@ -1,6 +1,7 @@
---
source: crates/cli/tests/cli_tests.rs
expression: cli_test_out.normalize_stdout_and_stderr()
snapshot_kind: text
---
── EXPECT FAILED in tests/test-projects/expects/expects.roc ────────────────────
@ -31,7 +32,7 @@ a = 1
This expectation failed:
11│> expect
12│> a = makeA
12│> a = make_a
13│> b = 2i64
14│>
15│> a == b

View file

@ -6,10 +6,10 @@ snapshot_kind: text
── TOO MANY ARGS in tests/test-projects/module_params/arity_mismatch.roc ───────
The getUser function expects 1 argument, but it got 2 instead:
The get_user function expects 1 argument, but it got 2 instead:
12│ $(Api.getUser 1 2)
^^^^^^^^^^^
12│ ${Api.get_user(1, 2)}
^^^^^^^^^^^^
Are there any missing commas? Or missing parentheses?
@ -18,18 +18,18 @@ Are there any missing commas? Or missing parentheses?
This value is not a function, but it was given 1 argument:
13│ $(Api.baseUrl 1)
^^^^^^^^^^^
13│ ${Api.base_url(1)}
^^^^^^^^^^^^
Are there any missing commas? Or missing parentheses?
── TOO FEW ARGS in tests/test-projects/module_params/arity_mismatch.roc ────────
The getPostComment function expects 2 arguments, but it got only 1:
The get_post_comment function expects 2 arguments, but it got only 1:
16│ $(Api.getPostComment 1)
^^^^^^^^^^^^^^^^^^
16│ ${Api.get_post_comment(1)}
^^^^^^^^^^^^^^^^^^^^
Roc does not allow functions to be partially applied. Use a closure to
make partial application explicit.

View file

@ -6,34 +6,35 @@ snapshot_kind: text
── TYPE MISMATCH in tests/test-projects/module_params/BadAnn.roc ───────────────
Something is off with the body of the fnAnnotatedAsValue definition:
Something is off with the body of the
fn_annotated_as_value definition:
3│ fnAnnotatedAsValue : Str
4│> fnAnnotatedAsValue = \postId, commentId ->
5│> "/posts/$(postId)/comments/$(Num.toStr commentId)"
3│ fn_annotated_as_value : Str
4│> fn_annotated_as_value = \post_id, comment_id ->
5│> "/posts/${post_id}/comments/${Num.to_str(comment_id)}"
The body is an anonymous function of type:
Str, Num * -> Str
But the type annotation on fnAnnotatedAsValue says it should be:
But the type annotation on fn_annotated_as_value says it should be:
Str
── TYPE MISMATCH in tests/test-projects/module_params/BadAnn.roc ───────────────
Something is off with the body of the missingArg definition:
Something is off with the body of the missing_arg definition:
7│ missingArg : Str -> Str
8│> missingArg = \postId, _ ->
9│> "/posts/$(postId)/comments"
7│ missing_arg : Str -> Str
8│> missing_arg = \post_id, _ ->
9│> "/posts/${post_id}/comments"
The body is an anonymous function of type:
(Str, ? -> Str)
But the type annotation on missingArg says it should be:
But the type annotation on missing_arg says it should be:
(Str -> Str)

View file

@ -8,9 +8,8 @@ snapshot_kind: text
This argument to this string interpolation has an unexpected type:
10│ """
11│> $(Api.getPost)
12│ """
10│ "${Api.get_post}"
^^^^^^^^^^^^
The argument is an anonymous function of type:

View file

@ -1,6 +1,7 @@
---
source: crates/cli/tests/cli_tests.rs
expression: cli_check_out.normalize_stdout_and_stderr()
snapshot_kind: text
---
── UNUSED IMPORT in tests/test-projects/known_bad/UnusedImport.roc ─────────────
@ -14,4 +15,4 @@ Since Symbol isn't used, you don't need to import it.
────────────────────────────────────────────────────────────────────────────────
0 error and 1 warning found in <ignored for test> ms
0 errors and 1 warning found in <ignored for test> ms.

View file

@ -11,8 +11,8 @@ const mem = std.mem;
const Allocator = mem.Allocator;
// NOTE the LLVM backend expects this signature
// extern fn roc__mainForHost_1_exposed(i64, *i64) void;
extern fn roc__mainForHost_1_exposed(i64) i64;
// extern fn roc__main_for_host_1_exposed(i64, *i64) void;
extern fn roc__main_for_host_1_exposed(i64) i64;
const Align = 2 * @alignOf(usize);
extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque;
@ -110,7 +110,7 @@ comptime {
pub export fn main() u8 {
const stdout = std.io.getStdOut().writer();
const result = roc__mainForHost_1_exposed(10);
const result = roc__main_for_host_1_exposed(10);
stdout.print("{d}\n", .{result}) catch unreachable;

View file

@ -3,7 +3,7 @@ platform "fibonacci"
exposes []
packages {}
imports []
provides [mainForHost]
provides [main_for_host]
mainForHost : I64 -> I64
mainForHost = \a -> main a
main_for_host : I64 -> I64
main_for_host = \a -> main(a)

View file

@ -1,10 +1,10 @@
app [main] { pf: platform "fibonacci-platform/main.roc" }
main = \n -> fib n 0 1
main = \n -> fib(n, 0, 1)
# the clever implementation requires join points
fib = \n, a, b ->
if n == 0 then
a
else
fib (n - 1) b (a + b)
fib((n - 1), b, (a + b))

View file

@ -9,7 +9,7 @@ const expect = testing.expect;
const mem = std.mem;
const Allocator = mem.Allocator;
extern fn roc__mainForHost_1_exposed(input: RocList) callconv(.C) RocList;
extern fn roc__main_for_host_1_exposed(input: RocList) callconv(.C) RocList;
const Align = 2 * @alignOf(usize);
extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque;
@ -117,7 +117,7 @@ pub export fn main() u8 {
var raw_numbers: [NUM_NUMS + 1]i64 = undefined;
// set refcount to one
raw_numbers[0] = -9223372036854775808;
raw_numbers[0] = 1;
var numbers = raw_numbers[1..];
@ -128,7 +128,7 @@ pub export fn main() u8 {
const roc_list = RocList{ .elements = numbers, .length = NUM_NUMS, .capacity = NUM_NUMS };
// actually call roc to populate the callresult
const callresult: RocList = roc__mainForHost_1_exposed(roc_list);
const callresult: RocList = roc__main_for_host_1_exposed(roc_list);
// stdout the result
const length = @min(20, callresult.length);

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