mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
Merge branch 'main' of github.com:roc-lang/roc into wasm_interp_test_gen
This commit is contained in:
commit
01d0c5fabc
83 changed files with 1567 additions and 1382 deletions
|
@ -29,7 +29,7 @@ jobs:
|
||||||
env:
|
env:
|
||||||
DATE: ${{ env.DATE }}
|
DATE: ${{ env.DATE }}
|
||||||
SHA: ${{ env.SHA }}
|
SHA: ${{ env.SHA }}
|
||||||
run: echo "RELEASE_TAR_FILENAME=roc_nightly-macos_12_apple_silicon-$DATE-$SHA.tar.gz" >> $GITHUB_ENV
|
run: echo "RELEASE_TAR_FILENAME=roc_nightly-macos_apple_silicon-$DATE-$SHA.tar.gz" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: write version to file
|
- name: write version to file
|
||||||
run: ./ci/write_version.sh
|
run: ./ci/write_version.sh
|
||||||
|
|
35
.github/workflows/nightly_macos_x86_64.yml
vendored
35
.github/workflows/nightly_macos_x86_64.yml
vendored
|
@ -4,17 +4,10 @@ on:
|
||||||
|
|
||||||
name: Nightly Release macOS x86_64
|
name: Nightly Release macOS x86_64
|
||||||
|
|
||||||
env:
|
|
||||||
ZIG_VERSION: 0.9.1
|
|
||||||
LLVM_SYS_130_PREFIX: /usr/local/opt/llvm@13
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-build-upload:
|
test-build-upload:
|
||||||
name: build, test, package and upload nightly release
|
name: build, test, package and upload nightly release
|
||||||
strategy:
|
runs-on: [self-hosted, macOS, X64]
|
||||||
matrix:
|
|
||||||
os: [ macos-11, macos-12 ]
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
timeout-minutes: 120
|
timeout-minutes: 120
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
@ -22,26 +15,12 @@ jobs:
|
||||||
- name: write version to file
|
- name: write version to file
|
||||||
run: ./ci/write_version.sh
|
run: ./ci/write_version.sh
|
||||||
|
|
||||||
- name: Install zig
|
|
||||||
run: |
|
|
||||||
curl -L -o zig.tar.xz https://ziglang.org/download/${ZIG_VERSION}/zig-macos-x86_64-${ZIG_VERSION}.tar.xz && tar -xf zig.tar.xz
|
|
||||||
echo "${GITHUB_WORKSPACE}/zig-macos-x86_64-${ZIG_VERSION}" >> $GITHUB_PATH
|
|
||||||
- name: zig version
|
|
||||||
run: zig version
|
|
||||||
- name: Install LLVM
|
|
||||||
run: brew install llvm@13
|
|
||||||
|
|
||||||
# build has to be done before tests #2572
|
# build has to be done before tests #2572
|
||||||
- name: build release
|
- name: build release
|
||||||
run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --features with_sound --release --locked
|
run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --features with_sound --release --locked
|
||||||
# target-cpu=x86-64 -> For maximal compatibility for all CPU's. Note that this setting will likely make the compiler slower.
|
# target-cpu=x86-64 -> For maximal compatibility for all CPU's. Note that this setting will likely make the compiler slower.
|
||||||
|
|
||||||
- name: execute rust tests if macos 12
|
- name: execute rust tests
|
||||||
if: endsWith(matrix.os, '12')
|
|
||||||
run: cargo test --release --locked -- --skip opaque_wrap_function --skip bool_list_literal
|
|
||||||
|
|
||||||
- name: execute rust tests if macos 11
|
|
||||||
if: endsWith(matrix.os, '11')
|
|
||||||
run: cargo test --release --locked -- --skip opaque_wrap_function --skip bool_list_literal --skip platform_switching_swift --skip swift_ui
|
run: cargo test --release --locked -- --skip opaque_wrap_function --skip bool_list_literal --skip platform_switching_swift --skip swift_ui
|
||||||
# swift tests are skipped because of "Could not find or use auto-linked library 'swiftCompatibilityConcurrency'" on macos-11 x86_64 CI machine
|
# swift tests are skipped because of "Could not find or use auto-linked library 'swiftCompatibilityConcurrency'" on macos-11 x86_64 CI machine
|
||||||
# this issue may be caused by using older versions of XCode
|
# this issue may be caused by using older versions of XCode
|
||||||
|
@ -52,19 +31,11 @@ jobs:
|
||||||
- name: get date
|
- name: get date
|
||||||
run: echo "DATE=$(date "+%Y-%m-%d")" >> $GITHUB_ENV
|
run: echo "DATE=$(date "+%Y-%m-%d")" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: get macos version if 11
|
|
||||||
if: endsWith(matrix.os, '11')
|
|
||||||
run: echo "MACOSVERSION=11" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: get macos version if 12
|
|
||||||
if: endsWith(matrix.os, '12')
|
|
||||||
run: echo "MACOSVERSION=12" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: build file name
|
- name: build file name
|
||||||
env:
|
env:
|
||||||
DATE: ${{ env.DATE }}
|
DATE: ${{ env.DATE }}
|
||||||
SHA: ${{ env.SHA }}
|
SHA: ${{ env.SHA }}
|
||||||
run: echo "RELEASE_TAR_FILENAME=roc_nightly-macos_${MACOSVERSION}_x86_64-$DATE-$SHA.tar.gz" >> $GITHUB_ENV
|
run: echo "RELEASE_TAR_FILENAME=roc_nightly-macos_x86_64-$DATE-$SHA.tar.gz" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: package release
|
- name: package release
|
||||||
run: ./ci/package_release.sh ${{ env.RELEASE_TAR_FILENAME }}
|
run: ./ci/package_release.sh ${{ env.RELEASE_TAR_FILENAME }}
|
||||||
|
|
|
@ -18,7 +18,7 @@ jobs:
|
||||||
- name: get the url of today`s release for macos apple silicon
|
- name: get the url of today`s release for macos apple silicon
|
||||||
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh silicon)" >> $GITHUB_ENV
|
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh silicon)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: get the archive from the url
|
- name: Get the archive from the url.
|
||||||
run: curl -OL ${{ env.RELEASE_URL }}
|
run: curl -OL ${{ env.RELEASE_URL }}
|
||||||
|
|
||||||
- name: remove everything in this dir except the tar # we want to test like a user who would have downloaded the release, so we clean up all files from the repo checkout
|
- name: remove everything in this dir except the tar # we want to test like a user who would have downloaded the release, so we clean up all files from the repo checkout
|
||||||
|
@ -30,5 +30,14 @@ jobs:
|
||||||
- name: test roc hello world
|
- name: test roc hello world
|
||||||
run: ./roc examples/helloWorld.roc
|
run: ./roc examples/helloWorld.roc
|
||||||
|
|
||||||
|
- name: test platform switching rust
|
||||||
|
run: ./roc examples/platform-switching/rocLovesRust.roc
|
||||||
|
|
||||||
|
- name: test platform switching zig
|
||||||
|
run: ./roc examples/platform-switching/rocLovesZig.roc
|
||||||
|
|
||||||
|
- name: test platform switching c
|
||||||
|
run: ./roc examples/platform-switching/rocLovesC.roc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
37
.github/workflows/test_nightly_many_os.yml
vendored
37
.github/workflows/test_nightly_many_os.yml
vendored
|
@ -4,6 +4,9 @@ on:
|
||||||
|
|
||||||
name: Test latest nightly release for macOS, ubu 20.04, ubu 22.04 x86_64
|
name: Test latest nightly release for macOS, ubu 20.04, ubu 22.04 x86_64
|
||||||
|
|
||||||
|
env:
|
||||||
|
ZIG_VERSION: 0.9.1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-nightly:
|
test-nightly:
|
||||||
name: test nightly macos 11, macos 12, ubu 20.04, ubu 22.04
|
name: test nightly macos 11, macos 12, ubu 20.04, ubu 22.04
|
||||||
|
@ -28,13 +31,9 @@ jobs:
|
||||||
if: startsWith(matrix.os, 'ubuntu')
|
if: startsWith(matrix.os, 'ubuntu')
|
||||||
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh linux_x86_64)" >> $GITHUB_ENV
|
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh linux_x86_64)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: get the url of today`s release for macos 11 (x86_64)
|
- name: get the url of today`s release for macos (x86_64)
|
||||||
if: startsWith(matrix.os, 'macos-11')
|
if: startsWith(matrix.os, 'macos')
|
||||||
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh macos_11_x86_64)" >> $GITHUB_ENV
|
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh macos_x86_64)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: get the url of today`s release for macos 12 (x86_64)
|
|
||||||
if: startsWith(matrix.os, 'macos-12')
|
|
||||||
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh macos_12_x86_64)" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: get the archive from the url
|
- name: get the archive from the url
|
||||||
run: curl -OL ${{ env.RELEASE_URL }}
|
run: curl -OL ${{ env.RELEASE_URL }}
|
||||||
|
@ -48,5 +47,29 @@ jobs:
|
||||||
- name: test roc hello world
|
- name: test roc hello world
|
||||||
run: ./roc examples/helloWorld.roc
|
run: ./roc examples/helloWorld.roc
|
||||||
|
|
||||||
|
- name: test platform switching rust
|
||||||
|
run: ./roc examples/platform-switching/rocLovesRust.roc
|
||||||
|
|
||||||
|
- name: get OS to use for zig download
|
||||||
|
if: startsWith(matrix.os, 'ubuntu')
|
||||||
|
run: echo "OS_TYPE=linux" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: get OS to use for zig download
|
||||||
|
if: startsWith(matrix.os, 'macos')
|
||||||
|
run: echo "OS_TYPE=macos" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Install zig
|
||||||
|
run: |
|
||||||
|
curl -L -o zig.tar.xz https://ziglang.org/download/${ZIG_VERSION}/zig-${{env.OS_TYPE}}-x86_64-${ZIG_VERSION}.tar.xz && tar -xf zig.tar.xz
|
||||||
|
echo "${GITHUB_WORKSPACE}/zig-${{env.OS_TYPE}}-x86_64-${ZIG_VERSION}" >> $GITHUB_PATH
|
||||||
|
- name: zig version
|
||||||
|
run: zig version
|
||||||
|
|
||||||
|
- name: test platform switching zig
|
||||||
|
run: ./roc examples/platform-switching/rocLovesZig.roc
|
||||||
|
|
||||||
|
- name: test platform switching c
|
||||||
|
run: ./roc examples/platform-switching/rocLovesC.roc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -21,6 +21,13 @@ zig-cache
|
||||||
# valgrind
|
# valgrind
|
||||||
vgcore.*
|
vgcore.*
|
||||||
|
|
||||||
|
# roc cache files
|
||||||
|
*.rh1
|
||||||
|
*.rm1
|
||||||
|
preprocessedhost
|
||||||
|
metadata
|
||||||
|
roc-cheaty-lib.so
|
||||||
|
|
||||||
#editors
|
#editors
|
||||||
.idea/
|
.idea/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
63
Cargo.lock
generated
63
Cargo.lock
generated
|
@ -3172,6 +3172,26 @@ version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2f61dcf0b917cd75d4521d7343d1ffff3d1583054133c9b5cbea3375c703c40d"
|
checksum = "2f61dcf0b917cd75d4521d7343d1ffff3d1583054133c9b5cbea3375c703c40d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proptest"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5"
|
||||||
|
dependencies = [
|
||||||
|
"bit-set",
|
||||||
|
"bitflags",
|
||||||
|
"byteorder",
|
||||||
|
"lazy_static",
|
||||||
|
"num-traits",
|
||||||
|
"quick-error 2.0.1",
|
||||||
|
"rand",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_xorshift",
|
||||||
|
"regex-syntax",
|
||||||
|
"rusty-fork",
|
||||||
|
"tempfile",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ptr_meta"
|
name = "ptr_meta"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
@ -3203,6 +3223,18 @@ dependencies = [
|
||||||
"unicase",
|
"unicase",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-error"
|
||||||
|
version = "1.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-error"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
version = "0.22.0"
|
version = "0.22.0"
|
||||||
|
@ -3295,6 +3327,15 @@ dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_xorshift"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_xoshiro"
|
name = "rand_xoshiro"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
@ -4178,6 +4219,7 @@ dependencies = [
|
||||||
"encode_unicode 1.0.0",
|
"encode_unicode 1.0.0",
|
||||||
"indoc",
|
"indoc",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
|
"proptest",
|
||||||
"quickcheck",
|
"quickcheck",
|
||||||
"quickcheck_macros",
|
"quickcheck_macros",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
|
@ -4575,6 +4617,18 @@ version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf"
|
checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rusty-fork"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"quick-error 1.2.3",
|
||||||
|
"tempfile",
|
||||||
|
"wait-timeout",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustyline"
|
name = "rustyline"
|
||||||
version = "9.1.1"
|
version = "9.1.1"
|
||||||
|
@ -5745,6 +5799,15 @@ dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wait-timeout"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "walkdir"
|
name = "walkdir"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
|
|
|
@ -1042,12 +1042,9 @@ fn roc_dev_native(
|
||||||
envp: bumpalo::collections::Vec<*const c_char>,
|
envp: bumpalo::collections::Vec<*const c_char>,
|
||||||
expect_metadata: ExpectMetadata,
|
expect_metadata: ExpectMetadata,
|
||||||
) -> ! {
|
) -> ! {
|
||||||
use roc_repl_expect::run::ExpectMemory;
|
use std::sync::{atomic::AtomicBool, Arc};
|
||||||
use signal_hook::{
|
|
||||||
consts::signal::SIGCHLD,
|
use roc_repl_expect::run::{ChildProcessMsg, ExpectMemory};
|
||||||
consts::signal::{SIGUSR1, SIGUSR2},
|
|
||||||
iterator::Signals,
|
|
||||||
};
|
|
||||||
|
|
||||||
let ExpectMetadata {
|
let ExpectMetadata {
|
||||||
mut expectations,
|
mut expectations,
|
||||||
|
@ -1055,11 +1052,9 @@ fn roc_dev_native(
|
||||||
layout_interner,
|
layout_interner,
|
||||||
} = expect_metadata;
|
} = expect_metadata;
|
||||||
|
|
||||||
let mut signals = Signals::new(&[SIGCHLD, SIGUSR1, SIGUSR2]).unwrap();
|
|
||||||
|
|
||||||
// let shm_name =
|
// let shm_name =
|
||||||
let shm_name = format!("/roc_expect_buffer_{}", std::process::id());
|
let shm_name = format!("/roc_expect_buffer_{}", std::process::id());
|
||||||
let memory = ExpectMemory::create_or_reuse_mmap(&shm_name);
|
let mut memory = ExpectMemory::create_or_reuse_mmap(&shm_name);
|
||||||
|
|
||||||
let layout_interner = layout_interner.into_global();
|
let layout_interner = layout_interner.into_global();
|
||||||
|
|
||||||
|
@ -1085,12 +1080,14 @@ fn roc_dev_native(
|
||||||
std::process::exit(1)
|
std::process::exit(1)
|
||||||
}
|
}
|
||||||
1.. => {
|
1.. => {
|
||||||
for sig in &mut signals {
|
let sigchld = Arc::new(AtomicBool::new(false));
|
||||||
match sig {
|
signal_hook::flag::register(signal_hook::consts::SIGCHLD, Arc::clone(&sigchld))
|
||||||
SIGCHLD => break,
|
.unwrap();
|
||||||
SIGUSR1 => {
|
|
||||||
// this is the signal we use for an expect failure. Let's see what the child told us
|
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match memory.wait_for_child(sigchld.clone()) {
|
||||||
|
ChildProcessMsg::Terminate => break,
|
||||||
|
ChildProcessMsg::Expect => {
|
||||||
roc_repl_expect::run::render_expects_in_memory(
|
roc_repl_expect::run::render_expects_in_memory(
|
||||||
&mut writer,
|
&mut writer,
|
||||||
arena,
|
arena,
|
||||||
|
@ -1100,10 +1097,10 @@ fn roc_dev_native(
|
||||||
&memory,
|
&memory,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
|
||||||
SIGUSR2 => {
|
|
||||||
// this is the signal we use for a dbg
|
|
||||||
|
|
||||||
|
memory.reset();
|
||||||
|
}
|
||||||
|
ChildProcessMsg::Dbg => {
|
||||||
roc_repl_expect::run::render_dbgs_in_memory(
|
roc_repl_expect::run::render_dbgs_in_memory(
|
||||||
&mut writer,
|
&mut writer,
|
||||||
arena,
|
arena,
|
||||||
|
@ -1113,8 +1110,9 @@ fn roc_dev_native(
|
||||||
&memory,
|
&memory,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
memory.reset();
|
||||||
}
|
}
|
||||||
_ => println!("received signal {}", sig),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ mod cli_run {
|
||||||
};
|
};
|
||||||
use const_format::concatcp;
|
use const_format::concatcp;
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use roc_cli::{CMD_BUILD, CMD_CHECK, CMD_FORMAT, CMD_RUN};
|
use roc_cli::{CMD_BUILD, CMD_CHECK, CMD_DEV, CMD_FORMAT, CMD_RUN, CMD_TEST};
|
||||||
use roc_test_utils::assert_multiline_str_eq;
|
use roc_test_utils::assert_multiline_str_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
@ -44,6 +44,8 @@ mod cli_run {
|
||||||
enum TestCliCommands {
|
enum TestCliCommands {
|
||||||
Many,
|
Many,
|
||||||
Run,
|
Run,
|
||||||
|
Test,
|
||||||
|
Dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
const OPTIMIZE_FLAG: &str = concatcp!("--", roc_cli::FLAG_OPTIMIZE);
|
const OPTIMIZE_FLAG: &str = concatcp!("--", roc_cli::FLAG_OPTIMIZE);
|
||||||
|
@ -58,6 +60,8 @@ mod cli_run {
|
||||||
Roc, // buildAndRunIfNoErrors
|
Roc, // buildAndRunIfNoErrors
|
||||||
RocBuild, // buildOnly
|
RocBuild, // buildOnly
|
||||||
RocRun, // buildAndRun
|
RocRun, // buildAndRun
|
||||||
|
RocTest,
|
||||||
|
RocDev,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||||
|
@ -177,11 +181,15 @@ mod cli_run {
|
||||||
match test_cli_commands {
|
match test_cli_commands {
|
||||||
TestCliCommands::Many => vec![CliMode::RocBuild, CliMode::RocRun],
|
TestCliCommands::Many => vec![CliMode::RocBuild, CliMode::RocRun],
|
||||||
TestCliCommands::Run => vec![CliMode::RocRun],
|
TestCliCommands::Run => vec![CliMode::RocRun],
|
||||||
|
TestCliCommands::Test => vec![],
|
||||||
|
TestCliCommands::Dev => vec![],
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match test_cli_commands {
|
match test_cli_commands {
|
||||||
TestCliCommands::Many => vec![CliMode::RocBuild, CliMode::RocRun, CliMode::Roc],
|
TestCliCommands::Many => vec![CliMode::RocBuild, CliMode::RocRun, CliMode::Roc],
|
||||||
TestCliCommands::Run => vec![CliMode::Roc],
|
TestCliCommands::Run => vec![CliMode::Roc],
|
||||||
|
TestCliCommands::Test => vec![CliMode::RocTest],
|
||||||
|
TestCliCommands::Dev => vec![CliMode::RocDev],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -270,9 +278,41 @@ mod cli_run {
|
||||||
roc_app_args,
|
roc_app_args,
|
||||||
extra_env,
|
extra_env,
|
||||||
),
|
),
|
||||||
|
CliMode::RocTest => {
|
||||||
|
// here failure is what we expect
|
||||||
|
|
||||||
|
run_roc_on(
|
||||||
|
file,
|
||||||
|
iter::once(CMD_TEST).chain(flags.clone()),
|
||||||
|
stdin,
|
||||||
|
roc_app_args,
|
||||||
|
extra_env,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CliMode::RocDev => {
|
||||||
|
// here failure is what we expect
|
||||||
|
|
||||||
|
run_roc_on(
|
||||||
|
file,
|
||||||
|
iter::once(CMD_DEV).chain(flags.clone()),
|
||||||
|
stdin,
|
||||||
|
roc_app_args,
|
||||||
|
extra_env,
|
||||||
|
)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let actual = strip_colors(&out.stdout);
|
let mut actual = strip_colors(&out.stdout);
|
||||||
|
|
||||||
|
// e.g. "1 failed and 0 passed in 123 ms."
|
||||||
|
if let Some(split) = actual.rfind("passed in ") {
|
||||||
|
let (before_first_digit, _) = actual.split_at(split);
|
||||||
|
actual = format!("{}passed in <ignored for test> ms.", before_first_digit);
|
||||||
|
}
|
||||||
|
|
||||||
|
let self_path = file.display().to_string();
|
||||||
|
actual = actual.replace(&self_path, "<ignored for tests>");
|
||||||
|
|
||||||
if !actual.ends_with(expected_ending) {
|
if !actual.ends_with(expected_ending) {
|
||||||
panic!(
|
panic!(
|
||||||
"expected output to end with:\n{}\nbut instead got:\n{}\n stderr was:\n{}",
|
"expected output to end with:\n{}\nbut instead got:\n{}\n stderr was:\n{}",
|
||||||
|
@ -280,7 +320,7 @@ mod cli_run {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !out.status.success() {
|
if !out.status.success() && !matches!(cli_mode, CliMode::RocTest) {
|
||||||
// We don't need stdout, Cargo prints it for us.
|
// We don't need stdout, Cargo prints it for us.
|
||||||
panic!(
|
panic!(
|
||||||
"Example program exited with status {:?}\nstderr was:\n{:#?}",
|
"Example program exited with status {:?}\nstderr was:\n{:#?}",
|
||||||
|
@ -511,6 +551,74 @@ mod cli_run {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(windows, ignore)]
|
||||||
|
fn expects_dev_and_test() {
|
||||||
|
// these are in the same test function so we don't have to worry about race conditions
|
||||||
|
// on the building of the platform
|
||||||
|
|
||||||
|
test_roc_app(
|
||||||
|
"crates/cli_testing_examples/expects",
|
||||||
|
"expects.roc",
|
||||||
|
"expects",
|
||||||
|
&[],
|
||||||
|
&[],
|
||||||
|
&[],
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
This expectation failed:
|
||||||
|
|
||||||
|
14│ expect x != x
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
When it failed, these variables had these values:
|
||||||
|
|
||||||
|
x : Num *
|
||||||
|
x = 42
|
||||||
|
|
||||||
|
[<ignored for tests> 15:9] 42
|
||||||
|
[<ignored for tests> 16:9] "Fjoer en ferdjer frieten oan dyn geve lea"
|
||||||
|
Program finished!
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
UseValgrind::Yes,
|
||||||
|
TestCliCommands::Dev,
|
||||||
|
);
|
||||||
|
|
||||||
|
test_roc_app(
|
||||||
|
"crates/cli_testing_examples/expects",
|
||||||
|
"expects.roc",
|
||||||
|
"expects",
|
||||||
|
&[],
|
||||||
|
&[],
|
||||||
|
&[],
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
This expectation failed:
|
||||||
|
|
||||||
|
6│> expect
|
||||||
|
7│> a = 1
|
||||||
|
8│> b = 2
|
||||||
|
9│>
|
||||||
|
10│> a == b
|
||||||
|
|
||||||
|
When it failed, these variables had these values:
|
||||||
|
|
||||||
|
a : Num *
|
||||||
|
a = 1
|
||||||
|
|
||||||
|
b : Num *
|
||||||
|
b = 2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1 failed and 0 passed in <ignored for test> ms."#
|
||||||
|
),
|
||||||
|
UseValgrind::Yes,
|
||||||
|
TestCliCommands::Test,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
windows,
|
windows,
|
||||||
|
|
|
@ -78,9 +78,6 @@ fn roc_getppid_windows_stub() callconv(.C) c_int {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
|
|
||||||
return kill(pid, sig);
|
|
||||||
}
|
|
||||||
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
||||||
return shm_open(name, oflag, mode);
|
return shm_open(name, oflag, mode);
|
||||||
}
|
}
|
||||||
|
@ -92,7 +89,6 @@ comptime {
|
||||||
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||||
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||||
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
||||||
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
|
|
||||||
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,9 +77,6 @@ fn roc_getppid_windows_stub() callconv(.C) c_int {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
|
|
||||||
return kill(pid, sig);
|
|
||||||
}
|
|
||||||
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
||||||
return shm_open(name, oflag, mode);
|
return shm_open(name, oflag, mode);
|
||||||
}
|
}
|
||||||
|
@ -91,7 +88,6 @@ comptime {
|
||||||
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||||
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||||
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
||||||
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
|
|
||||||
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,9 +94,6 @@ fn roc_getppid_windows_stub() callconv(.C) c_int {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
|
|
||||||
return kill(pid, sig);
|
|
||||||
}
|
|
||||||
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
||||||
return shm_open(name, oflag, mode);
|
return shm_open(name, oflag, mode);
|
||||||
}
|
}
|
||||||
|
@ -108,7 +105,6 @@ comptime {
|
||||||
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||||
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||||
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
||||||
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
|
|
||||||
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,9 +93,6 @@ fn roc_getppid_windows_stub() callconv(.C) c_int {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
|
|
||||||
return kill(pid, sig);
|
|
||||||
}
|
|
||||||
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
||||||
return shm_open(name, oflag, mode);
|
return shm_open(name, oflag, mode);
|
||||||
}
|
}
|
||||||
|
@ -107,7 +104,6 @@ comptime {
|
||||||
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||||
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||||
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
||||||
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
|
|
||||||
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,9 +98,6 @@ fn roc_getppid_windows_stub() callconv(.C) c_int {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
|
|
||||||
return kill(pid, sig);
|
|
||||||
}
|
|
||||||
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
||||||
return shm_open(name, oflag, mode);
|
return shm_open(name, oflag, mode);
|
||||||
}
|
}
|
||||||
|
@ -112,7 +109,6 @@ comptime {
|
||||||
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||||
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||||
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
||||||
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
|
|
||||||
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
crates/cli_testing_examples/expects/expects.roc
Normal file
17
crates/cli_testing_examples/expects/expects.roc
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
app "expects"
|
||||||
|
packages { pf: "zig-platform/main.roc" }
|
||||||
|
imports []
|
||||||
|
provides [main] to pf
|
||||||
|
|
||||||
|
expect
|
||||||
|
a = 1
|
||||||
|
b = 2
|
||||||
|
|
||||||
|
a == b
|
||||||
|
|
||||||
|
main =
|
||||||
|
x = 42
|
||||||
|
expect x != x
|
||||||
|
dbg x
|
||||||
|
dbg "Fjoer en ferdjer frieten oan dyn geve lea"
|
||||||
|
"Program finished!\n"
|
142
crates/cli_testing_examples/expects/zig-platform/host.zig
Normal file
142
crates/cli_testing_examples/expects/zig-platform/host.zig
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const str = @import("str");
|
||||||
|
const RocStr = str.RocStr;
|
||||||
|
const testing = std.testing;
|
||||||
|
const expectEqual = testing.expectEqual;
|
||||||
|
const expect = testing.expect;
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
// This is a workaround for https://github.com/ziglang/zig/issues/8218
|
||||||
|
// which is only necessary on macOS.
|
||||||
|
//
|
||||||
|
// Once that issue is fixed, we can undo the changes in
|
||||||
|
// 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing
|
||||||
|
// -fcompiler-rt in link.rs instead of doing this. Note that this
|
||||||
|
// workaround is present in many host.zig files, so make sure to undo
|
||||||
|
// it everywhere!
|
||||||
|
if (builtin.os.tag == .macos) {
|
||||||
|
_ = @import("compiler_rt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Align = 2 * @alignOf(usize);
|
||||||
|
extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque;
|
||||||
|
extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque;
|
||||||
|
extern fn free(c_ptr: [*]align(Align) u8) callconv(.C) void;
|
||||||
|
extern fn memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void;
|
||||||
|
extern fn memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void;
|
||||||
|
|
||||||
|
const DEBUG: bool = false;
|
||||||
|
|
||||||
|
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*anyopaque {
|
||||||
|
if (DEBUG) {
|
||||||
|
var ptr = malloc(size);
|
||||||
|
const stdout = std.io.getStdOut().writer();
|
||||||
|
stdout.print("alloc: {d} (alignment {d}, size {d})\n", .{ ptr, alignment, size }) catch unreachable;
|
||||||
|
return ptr;
|
||||||
|
} else {
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn roc_realloc(c_ptr: *anyopaque, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*anyopaque {
|
||||||
|
if (DEBUG) {
|
||||||
|
const stdout = std.io.getStdOut().writer();
|
||||||
|
stdout.print("realloc: {d} (alignment {d}, old_size {d})\n", .{ c_ptr, alignment, old_size }) catch unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return realloc(@alignCast(Align, @ptrCast([*]u8, c_ptr)), new_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
|
||||||
|
if (DEBUG) {
|
||||||
|
const stdout = std.io.getStdOut().writer();
|
||||||
|
stdout.print("dealloc: {d} (alignment {d})\n", .{ c_ptr, alignment }) catch unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(@alignCast(Align, @ptrCast([*]u8, c_ptr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
|
||||||
|
_ = tag_id;
|
||||||
|
|
||||||
|
const stderr = std.io.getStdErr().writer();
|
||||||
|
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||||
|
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||||
|
std.process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
|
||||||
|
return memcpy(dst, src, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
|
||||||
|
return memset(dst, value, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn kill(pid: c_int, sig: c_int) c_int;
|
||||||
|
extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
|
||||||
|
extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
|
||||||
|
extern fn getppid() c_int;
|
||||||
|
|
||||||
|
fn roc_getppid() callconv(.C) c_int {
|
||||||
|
return getppid();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn roc_getppid_windows_stub() callconv(.C) c_int {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
|
||||||
|
return kill(pid, sig);
|
||||||
|
}
|
||||||
|
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
||||||
|
return shm_open(name, oflag, mode);
|
||||||
|
}
|
||||||
|
fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque {
|
||||||
|
return mmap(addr, length, prot, flags, fd, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||||
|
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||||
|
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
||||||
|
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
|
||||||
|
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (builtin.os.tag == .windows) {
|
||||||
|
@export(roc_getppid_windows_stub, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mem = std.mem;
|
||||||
|
const Allocator = mem.Allocator;
|
||||||
|
|
||||||
|
extern fn roc__mainForHost_1_exposed_generic(*RocStr) void;
|
||||||
|
|
||||||
|
const Unit = extern struct {};
|
||||||
|
|
||||||
|
pub fn main() u8 {
|
||||||
|
const stdout = std.io.getStdOut().writer();
|
||||||
|
const stderr = std.io.getStdErr().writer();
|
||||||
|
|
||||||
|
var timer = std.time.Timer.start() catch unreachable;
|
||||||
|
|
||||||
|
// actually call roc to populate the callresult
|
||||||
|
var callresult = RocStr.empty();
|
||||||
|
roc__mainForHost_1_exposed_generic(&callresult);
|
||||||
|
|
||||||
|
const nanos = timer.read();
|
||||||
|
const seconds = (@intToFloat(f64, nanos) / 1_000_000_000.0);
|
||||||
|
|
||||||
|
// stdout the result
|
||||||
|
stdout.print("{s}", .{callresult.asSlice()}) catch unreachable;
|
||||||
|
|
||||||
|
callresult.deinit();
|
||||||
|
|
||||||
|
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
platform "echo-in-zig"
|
||||||
|
requires {} { main : Str }
|
||||||
|
exposes []
|
||||||
|
packages {}
|
||||||
|
imports []
|
||||||
|
provides [mainForHost]
|
||||||
|
|
||||||
|
mainForHost : Str
|
||||||
|
mainForHost = main
|
|
@ -10,11 +10,12 @@ use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
|
||||||
use roc_mono::ir::{
|
use roc_mono::ir::{
|
||||||
Call, CallType, Expr, HigherOrderLowLevel, HostExposedLayouts, ListLiteralElement, Literal,
|
Call, CallType, EntryPoint, Expr, HigherOrderLowLevel, HostExposedLayouts, ListLiteralElement,
|
||||||
ModifyRc, OptLevel, Proc, Stmt,
|
Literal, ModifyRc, OptLevel, Proc, ProcLayout, SingleEntryPoint, Stmt,
|
||||||
};
|
};
|
||||||
use roc_mono::layout::{
|
use roc_mono::layout::{
|
||||||
Builtin, CapturesNiche, Layout, RawFunctionLayout, STLayoutInterner, UnionLayout,
|
Builtin, CapturesNiche, FieldOrderHash, Layout, RawFunctionLayout, STLayoutInterner,
|
||||||
|
UnionLayout,
|
||||||
};
|
};
|
||||||
|
|
||||||
// just using one module for now
|
// just using one module for now
|
||||||
|
@ -136,7 +137,7 @@ pub fn spec_program<'a, I>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
interner: &STLayoutInterner<'a>,
|
interner: &STLayoutInterner<'a>,
|
||||||
opt_level: OptLevel,
|
opt_level: OptLevel,
|
||||||
opt_entry_point: Option<roc_mono::ir::EntryPoint<'a>>,
|
entry_point: roc_mono::ir::EntryPoint<'a>,
|
||||||
procs: I,
|
procs: I,
|
||||||
) -> Result<morphic_lib::Solutions>
|
) -> Result<morphic_lib::Solutions>
|
||||||
where
|
where
|
||||||
|
@ -226,13 +227,17 @@ where
|
||||||
m.add_func(func_name, spec)?;
|
m.add_func(func_name, spec)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(entry_point) = opt_entry_point {
|
match entry_point {
|
||||||
|
EntryPoint::Single(SingleEntryPoint {
|
||||||
|
symbol: entry_point_symbol,
|
||||||
|
layout: entry_point_layout,
|
||||||
|
}) => {
|
||||||
// the entry point wrapper
|
// the entry point wrapper
|
||||||
let roc_main_bytes = func_name_bytes_help(
|
let roc_main_bytes = func_name_bytes_help(
|
||||||
entry_point.symbol,
|
entry_point_symbol,
|
||||||
entry_point.layout.arguments.iter().copied(),
|
entry_point_layout.arguments.iter().copied(),
|
||||||
CapturesNiche::no_niche(),
|
CapturesNiche::no_niche(),
|
||||||
&entry_point.layout.result,
|
&entry_point_layout.result,
|
||||||
);
|
);
|
||||||
let roc_main = FuncName(&roc_main_bytes);
|
let roc_main = FuncName(&roc_main_bytes);
|
||||||
|
|
||||||
|
@ -241,8 +246,8 @@ where
|
||||||
let entry_point_function = build_entry_point(
|
let entry_point_function = build_entry_point(
|
||||||
&mut env,
|
&mut env,
|
||||||
interner,
|
interner,
|
||||||
entry_point.layout,
|
entry_point_layout,
|
||||||
roc_main,
|
Some(roc_main),
|
||||||
&host_exposed_functions,
|
&host_exposed_functions,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -251,6 +256,42 @@ where
|
||||||
let entry_point_name = FuncName(ENTRY_POINT_NAME);
|
let entry_point_name = FuncName(ENTRY_POINT_NAME);
|
||||||
m.add_func(entry_point_name, entry_point_function)?;
|
m.add_func(entry_point_name, entry_point_function)?;
|
||||||
}
|
}
|
||||||
|
EntryPoint::Expects { symbols } => {
|
||||||
|
// construct a big pattern match picking one of the expects at random
|
||||||
|
let layout: ProcLayout<'a> = ProcLayout {
|
||||||
|
arguments: &[],
|
||||||
|
result: Layout::Struct {
|
||||||
|
field_order_hash: FieldOrderHash::from_ordered_fields(&[]),
|
||||||
|
field_layouts: &[],
|
||||||
|
},
|
||||||
|
captures_niche: CapturesNiche::no_niche(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let host_exposed: Vec<_> = symbols
|
||||||
|
.iter()
|
||||||
|
.map(|symbol| {
|
||||||
|
(
|
||||||
|
func_name_bytes_help(
|
||||||
|
*symbol,
|
||||||
|
[],
|
||||||
|
CapturesNiche::no_niche(),
|
||||||
|
&layout.result,
|
||||||
|
),
|
||||||
|
[].as_slice(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut env = Env::new(arena);
|
||||||
|
let entry_point_function =
|
||||||
|
build_entry_point(&mut env, interner, layout, None, &host_exposed)?;
|
||||||
|
|
||||||
|
type_definitions.extend(env.type_names);
|
||||||
|
|
||||||
|
let entry_point_name = FuncName(ENTRY_POINT_NAME);
|
||||||
|
m.add_func(entry_point_name, entry_point_function)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for union_layout in type_definitions {
|
for union_layout in type_definitions {
|
||||||
let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes();
|
let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes();
|
||||||
|
@ -286,10 +327,11 @@ where
|
||||||
let mut p = ProgramBuilder::new();
|
let mut p = ProgramBuilder::new();
|
||||||
p.add_mod(MOD_APP, main_module)?;
|
p.add_mod(MOD_APP, main_module)?;
|
||||||
|
|
||||||
if opt_entry_point.is_some() {
|
p.add_entry_point(
|
||||||
let entry_point_name = FuncName(ENTRY_POINT_NAME);
|
EntryPointName(ENTRY_POINT_NAME),
|
||||||
p.add_entry_point(EntryPointName(ENTRY_POINT_NAME), MOD_APP, entry_point_name)?;
|
MOD_APP,
|
||||||
}
|
FuncName(ENTRY_POINT_NAME),
|
||||||
|
)?;
|
||||||
|
|
||||||
p.build()?
|
p.build()?
|
||||||
};
|
};
|
||||||
|
@ -324,7 +366,7 @@ fn build_entry_point<'a>(
|
||||||
env: &mut Env<'a>,
|
env: &mut Env<'a>,
|
||||||
interner: &STLayoutInterner<'a>,
|
interner: &STLayoutInterner<'a>,
|
||||||
layout: roc_mono::ir::ProcLayout<'a>,
|
layout: roc_mono::ir::ProcLayout<'a>,
|
||||||
func_name: FuncName,
|
entry_point_function: Option<FuncName>,
|
||||||
host_exposed_functions: &[([u8; SIZE], &'a [Layout<'a>])],
|
host_exposed_functions: &[([u8; SIZE], &'a [Layout<'a>])],
|
||||||
) -> Result<FuncDef> {
|
) -> Result<FuncDef> {
|
||||||
let mut builder = FuncDefBuilder::new();
|
let mut builder = FuncDefBuilder::new();
|
||||||
|
@ -332,7 +374,7 @@ fn build_entry_point<'a>(
|
||||||
|
|
||||||
let mut cases = Vec::new();
|
let mut cases = Vec::new();
|
||||||
|
|
||||||
{
|
if let Some(entry_point_function) = entry_point_function {
|
||||||
let block = builder.add_block();
|
let block = builder.add_block();
|
||||||
|
|
||||||
// to the modelling language, the arguments appear out of thin air
|
// to the modelling language, the arguments appear out of thin air
|
||||||
|
@ -352,7 +394,7 @@ fn build_entry_point<'a>(
|
||||||
|
|
||||||
let name_bytes = [0; 16];
|
let name_bytes = [0; 16];
|
||||||
let spec_var = CalleeSpecVar(&name_bytes);
|
let spec_var = CalleeSpecVar(&name_bytes);
|
||||||
let result = builder.add_call(block, spec_var, MOD_APP, func_name, argument)?;
|
let result = builder.add_call(block, spec_var, MOD_APP, entry_point_function, argument)?;
|
||||||
|
|
||||||
// to the modelling language, the result disappears into the void
|
// to the modelling language, the result disappears into the void
|
||||||
let unit_type = builder.add_tuple_type(&[])?;
|
let unit_type = builder.add_tuple_type(&[])?;
|
||||||
|
@ -365,7 +407,7 @@ fn build_entry_point<'a>(
|
||||||
for (name_bytes, layouts) in host_exposed_functions {
|
for (name_bytes, layouts) in host_exposed_functions {
|
||||||
let host_exposed_func_name = FuncName(name_bytes);
|
let host_exposed_func_name = FuncName(name_bytes);
|
||||||
|
|
||||||
if host_exposed_func_name == func_name {
|
if Some(host_exposed_func_name) == entry_point_function {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,7 +434,11 @@ fn build_entry_point<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let unit_type = builder.add_tuple_type(&[])?;
|
let unit_type = builder.add_tuple_type(&[])?;
|
||||||
let unit_value = builder.add_choice(outer_block, &cases)?;
|
let unit_value = if cases.is_empty() {
|
||||||
|
builder.add_make_tuple(outer_block, &[])?
|
||||||
|
} else {
|
||||||
|
builder.add_choice(outer_block, &cases)?
|
||||||
|
};
|
||||||
|
|
||||||
let root = BlockExpr(outer_block, unit_value);
|
let root = BlockExpr(outer_block, unit_value);
|
||||||
let spec = builder.build(unit_type, unit_type, root)?;
|
let spec = builder.build(unit_type, unit_type, root)?;
|
||||||
|
|
|
@ -3,7 +3,7 @@ use roc_error_macros::internal_error;
|
||||||
use roc_gen_llvm::llvm::build::{module_from_builtins, LlvmBackendMode};
|
use roc_gen_llvm::llvm::build::{module_from_builtins, LlvmBackendMode};
|
||||||
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
||||||
use roc_load::{EntryPoint, ExpectMetadata, LoadedModule, MonomorphizedModule};
|
use roc_load::{EntryPoint, ExpectMetadata, LoadedModule, MonomorphizedModule};
|
||||||
use roc_mono::ir::OptLevel;
|
use roc_mono::ir::{OptLevel, SingleEntryPoint};
|
||||||
use roc_reporting::cli::{report_problems, Problems};
|
use roc_reporting::cli::{report_problems, Problems};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
@ -188,18 +188,25 @@ fn gen_from_mono_module_llvm<'a>(
|
||||||
// expects that would confuse the surgical linker
|
// expects that would confuse the surgical linker
|
||||||
add_default_roc_externs(&env);
|
add_default_roc_externs(&env);
|
||||||
|
|
||||||
let opt_entry_point = match loaded.entry_point {
|
let entry_point = match loaded.entry_point {
|
||||||
EntryPoint::Executable { symbol, layout, .. } => {
|
EntryPoint::Executable {
|
||||||
Some(roc_mono::ir::EntryPoint { symbol, layout })
|
exposed_to_host,
|
||||||
|
platform_path: _,
|
||||||
|
} => {
|
||||||
|
// TODO support multiple of these!
|
||||||
|
debug_assert_eq!(exposed_to_host.len(), 1);
|
||||||
|
let (symbol, layout) = exposed_to_host[0];
|
||||||
|
|
||||||
|
roc_mono::ir::EntryPoint::Single(SingleEntryPoint { symbol, layout })
|
||||||
}
|
}
|
||||||
EntryPoint::Test => None,
|
EntryPoint::Test => roc_mono::ir::EntryPoint::Expects { symbols: &[] },
|
||||||
};
|
};
|
||||||
|
|
||||||
roc_gen_llvm::llvm::build::build_procedures(
|
roc_gen_llvm::llvm::build::build_procedures(
|
||||||
&env,
|
&env,
|
||||||
opt_level,
|
opt_level,
|
||||||
loaded.procedures,
|
loaded.procedures,
|
||||||
opt_entry_point,
|
entry_point,
|
||||||
Some(&app_ll_file),
|
Some(&app_ll_file),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
const SIGUSR1: c_int = if (builtin.os.tag.isDarwin()) 30 else 10;
|
const Atomic = std.atomic.Atomic;
|
||||||
const SIGUSR2: c_int = if (builtin.os.tag.isDarwin()) 31 else 12;
|
|
||||||
|
|
||||||
const O_RDWR: c_int = 2;
|
const O_RDWR: c_int = 2;
|
||||||
const O_CREAT: c_int = 64;
|
const O_CREAT: c_int = 64;
|
||||||
|
@ -51,7 +50,6 @@ pub fn expectFailedStartSharedFile() callconv(.C) [*]u8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn roc_send_signal(pid: c_int, sig: c_int) c_int;
|
|
||||||
extern fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
|
extern fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
|
||||||
extern fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
|
extern fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
|
||||||
extern fn roc_getppid() c_int;
|
extern fn roc_getppid() c_int;
|
||||||
|
@ -81,18 +79,24 @@ pub fn readSharedBufferEnv() callconv(.C) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expectFailedFinalize() callconv(.C) void {
|
pub fn notifyParent(shared_buffer: [*]u8, tag: u32) callconv(.C) void {
|
||||||
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||||
const parent_pid = roc_getppid();
|
const usize_ptr = @ptrCast([*]u32, @alignCast(@alignOf(usize), shared_buffer));
|
||||||
|
const atomic_ptr = @ptrCast(*Atomic(u32), &usize_ptr[5]);
|
||||||
|
atomic_ptr.storeUnchecked(tag);
|
||||||
|
|
||||||
_ = roc_send_signal(parent_pid, SIGUSR1);
|
// wait till the parent is done before proceeding
|
||||||
|
const Ordering = std.atomic.Ordering;
|
||||||
|
while (atomic_ptr.load(Ordering.Acquire) != 0) {
|
||||||
|
std.atomic.spinLoopHint();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sendDbg() callconv(.C) void {
|
pub fn notifyParentExpect(shared_buffer: [*]u8) callconv(.C) void {
|
||||||
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
notifyParent(shared_buffer, 1);
|
||||||
const parent_pid = roc_getppid();
|
}
|
||||||
|
|
||||||
_ = roc_send_signal(parent_pid, SIGUSR2);
|
pub fn notifyParentDbg(shared_buffer: [*]u8) callconv(.C) void {
|
||||||
}
|
notifyParent(shared_buffer, 2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,8 +172,8 @@ comptime {
|
||||||
if (builtin.target.cpu.arch != .wasm32) {
|
if (builtin.target.cpu.arch != .wasm32) {
|
||||||
exportUtilsFn(expect.expectFailedStartSharedBuffer, "expect_failed_start_shared_buffer");
|
exportUtilsFn(expect.expectFailedStartSharedBuffer, "expect_failed_start_shared_buffer");
|
||||||
exportUtilsFn(expect.expectFailedStartSharedFile, "expect_failed_start_shared_file");
|
exportUtilsFn(expect.expectFailedStartSharedFile, "expect_failed_start_shared_file");
|
||||||
exportUtilsFn(expect.expectFailedFinalize, "expect_failed_finalize");
|
exportUtilsFn(expect.notifyParentExpect, "notify_parent_expect");
|
||||||
exportUtilsFn(expect.sendDbg, "send_dbg");
|
exportUtilsFn(expect.notifyParentDbg, "notify_parent_dbg");
|
||||||
|
|
||||||
// sets the buffer used for expect failures
|
// sets the buffer used for expect failures
|
||||||
@export(expect.setSharedBuffer, .{ .name = "set_shared_buffer", .linkage = .Weak });
|
@export(expect.setSharedBuffer, .{ .name = "set_shared_buffer", .linkage = .Weak });
|
||||||
|
|
|
@ -32,9 +32,6 @@ fn roc_getppid_windows_stub() callconv(.C) c_int {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testing_roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
|
|
||||||
return kill(pid, sig);
|
|
||||||
}
|
|
||||||
fn testing_roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
fn testing_roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
||||||
return shm_open(name, oflag, mode);
|
return shm_open(name, oflag, mode);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +52,6 @@ comptime {
|
||||||
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||||
@export(testing_roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
@export(testing_roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||||
@export(testing_roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
@export(testing_roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
||||||
@export(testing_roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
|
|
||||||
@export(testing_roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
@export(testing_roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -653,19 +653,11 @@ mapWithIndexHelp = \src, dest, func, index, length ->
|
||||||
##
|
##
|
||||||
## If `step` is specified, each integer increases by that much. (`step: 1` is the default.)
|
## If `step` is specified, each integer increases by that much. (`step: 1` is the default.)
|
||||||
##
|
##
|
||||||
## List.range { start: After 1, end: Before 10, step: 3 } # returns [2, 5, 8]
|
## List.range { start: After 0, end: Before 9, step: 3 } # returns [3, 6]
|
||||||
##
|
##
|
||||||
## All of these options are compatible with the others. For example, you can use `At` or `After`
|
## All of these options are compatible with the others. For example, you can use `At` or `After`
|
||||||
## with `start` regardless of what `end` and `step` are set to.
|
## with `start` regardless of what `end` and `step` are set to.
|
||||||
# TODO: Make the type annotation work
|
range : _
|
||||||
# range :
|
|
||||||
# {
|
|
||||||
# start : [At (Int a), After (Int a)],
|
|
||||||
# end : [At (Int a), Before (Int a), Length Nat],
|
|
||||||
# # TODO: We want this to be Int *, but that requires the ability to convert or add from Int * to Int a
|
|
||||||
# step ? Int a
|
|
||||||
# }
|
|
||||||
# -> List (Int a) | a has Bool.Eq
|
|
||||||
range = \{ start, end, step ? 0 } ->
|
range = \{ start, end, step ? 0 } ->
|
||||||
{ incByStep, stepIsPositive } =
|
{ incByStep, stepIsPositive } =
|
||||||
if step == 0 then
|
if step == 0 then
|
||||||
|
|
|
@ -424,9 +424,9 @@ pub const UTILS_EXPECT_FAILED_START_SHARED_BUFFER: &str =
|
||||||
"roc_builtins.utils.expect_failed_start_shared_buffer";
|
"roc_builtins.utils.expect_failed_start_shared_buffer";
|
||||||
pub const UTILS_EXPECT_FAILED_START_SHARED_FILE: &str =
|
pub const UTILS_EXPECT_FAILED_START_SHARED_FILE: &str =
|
||||||
"roc_builtins.utils.expect_failed_start_shared_file";
|
"roc_builtins.utils.expect_failed_start_shared_file";
|
||||||
pub const UTILS_EXPECT_FAILED_FINALIZE: &str = "roc_builtins.utils.expect_failed_finalize";
|
|
||||||
pub const UTILS_EXPECT_READ_ENV_SHARED_BUFFER: &str = "roc_builtins.utils.read_env_shared_buffer";
|
pub const UTILS_EXPECT_READ_ENV_SHARED_BUFFER: &str = "roc_builtins.utils.read_env_shared_buffer";
|
||||||
pub const UTILS_SEND_DBG: &str = "roc_builtins.utils.send_dbg";
|
pub const NOTIFY_PARENT_EXPECT: &str = "roc_builtins.utils.notify_parent_expect";
|
||||||
|
pub const NOTIFY_PARENT_DBG: &str = "roc_builtins.utils.notify_parent_dbg";
|
||||||
|
|
||||||
pub const UTILS_LONGJMP: &str = "longjmp";
|
pub const UTILS_LONGJMP: &str = "longjmp";
|
||||||
pub const UTILS_SETJMP: &str = "setjmp";
|
pub const UTILS_SETJMP: &str = "setjmp";
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::annotation::IntroducedVariables;
|
||||||
use crate::annotation::OwnedNamedOrAble;
|
use crate::annotation::OwnedNamedOrAble;
|
||||||
use crate::derive;
|
use crate::derive;
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
|
use crate::expr::get_lookup_symbols;
|
||||||
use crate::expr::AnnotatedMark;
|
use crate::expr::AnnotatedMark;
|
||||||
use crate::expr::ClosureData;
|
use crate::expr::ClosureData;
|
||||||
use crate::expr::Declarations;
|
use crate::expr::Declarations;
|
||||||
|
@ -2414,10 +2415,12 @@ fn decl_to_let(decl: Declaration, loc_ret: Loc<Expr>) -> Loc<Expr> {
|
||||||
|
|
||||||
for ((expect_region, condition_region), condition) in it {
|
for ((expect_region, condition_region), condition) in it {
|
||||||
let region = Region::span_across(&expect_region, &loc_ret.region);
|
let region = Region::span_across(&expect_region, &loc_ret.region);
|
||||||
|
let lookups_in_cond = get_lookup_symbols(&condition);
|
||||||
|
|
||||||
let expr = Expr::Expect {
|
let expr = Expr::Expect {
|
||||||
loc_condition: Box::new(Loc::at(condition_region, condition)),
|
loc_condition: Box::new(Loc::at(condition_region, condition)),
|
||||||
loc_continuation: Box::new(loc_ret),
|
loc_continuation: Box::new(loc_ret),
|
||||||
lookups_in_cond: vec![],
|
lookups_in_cond,
|
||||||
};
|
};
|
||||||
|
|
||||||
loc_ret = Loc::at(region, expr);
|
loc_ret = Loc::at(region, expr);
|
||||||
|
|
|
@ -2778,7 +2778,7 @@ pub struct DestructureDef {
|
||||||
pub pattern_vars: VecMap<Symbol, Variable>,
|
pub pattern_vars: VecMap<Symbol, Variable>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
|
pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
|
||||||
let mut stack: Vec<&Expr> = vec![expr];
|
let mut stack: Vec<&Expr> = vec![expr];
|
||||||
let mut lookups: Vec<ExpectLookup> = Vec::new();
|
let mut lookups: Vec<ExpectLookup> = Vec::new();
|
||||||
|
|
||||||
|
@ -2840,8 +2840,16 @@ fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
|
||||||
|
|
||||||
stack.push(&final_else.value);
|
stack.push(&final_else.value);
|
||||||
}
|
}
|
||||||
Expr::LetRec(_, _, _) => todo!(),
|
Expr::LetRec(defs, expr, _illegal_cycle_mark) => {
|
||||||
Expr::LetNonRec { .. } => todo!(),
|
for def in defs {
|
||||||
|
stack.push(&def.loc_expr.value);
|
||||||
|
}
|
||||||
|
stack.push(&expr.value);
|
||||||
|
}
|
||||||
|
Expr::LetNonRec(def, expr) => {
|
||||||
|
stack.push(&def.loc_expr.value);
|
||||||
|
stack.push(&expr.value);
|
||||||
|
}
|
||||||
Expr::Call(boxed_expr, args, _called_via) => {
|
Expr::Call(boxed_expr, args, _called_via) => {
|
||||||
stack.reserve(1 + args.len());
|
stack.reserve(1 + args.len());
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ use roc_module::ident::Ident;
|
||||||
use roc_module::ident::Lowercase;
|
use roc_module::ident::Lowercase;
|
||||||
use roc_module::symbol::{IdentIds, IdentIdsByModule, ModuleId, ModuleIds, Symbol};
|
use roc_module::symbol::{IdentIds, IdentIdsByModule, ModuleId, ModuleIds, Symbol};
|
||||||
use roc_parse::ast::{Defs, TypeAnnotation};
|
use roc_parse::ast::{Defs, TypeAnnotation};
|
||||||
use roc_parse::header::HeaderFor;
|
use roc_parse::header::HeaderType;
|
||||||
use roc_parse::pattern::PatternType;
|
use roc_parse::pattern::PatternType;
|
||||||
use roc_problem::can::{Problem, RuntimeError};
|
use roc_problem::can::{Problem, RuntimeError};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
|
@ -194,16 +194,17 @@ enum GeneratedInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GeneratedInfo {
|
impl GeneratedInfo {
|
||||||
fn from_header_for<'a>(
|
fn from_header_type<'a>(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
header_for: &HeaderFor<'a>,
|
header_type: &HeaderType<'a>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match header_for {
|
match header_type {
|
||||||
HeaderFor::Hosted {
|
HeaderType::Hosted {
|
||||||
generates,
|
generates,
|
||||||
generates_with,
|
generates_with,
|
||||||
|
name: _,
|
||||||
} => {
|
} => {
|
||||||
let name: &str = generates.into();
|
let name: &str = generates.into();
|
||||||
let (generated_functions, unknown_generated) =
|
let (generated_functions, unknown_generated) =
|
||||||
|
@ -236,7 +237,10 @@ impl GeneratedInfo {
|
||||||
generated_functions,
|
generated_functions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HeaderFor::Builtin { generates_with } => {
|
HeaderType::Builtin {
|
||||||
|
generates_with,
|
||||||
|
name: _,
|
||||||
|
} => {
|
||||||
debug_assert!(generates_with.is_empty());
|
debug_assert!(generates_with.is_empty());
|
||||||
GeneratedInfo::Builtin
|
GeneratedInfo::Builtin
|
||||||
}
|
}
|
||||||
|
@ -266,7 +270,7 @@ fn has_no_implementation(expr: &Expr) -> bool {
|
||||||
pub fn canonicalize_module_defs<'a>(
|
pub fn canonicalize_module_defs<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
loc_defs: &'a mut Defs<'a>,
|
loc_defs: &'a mut Defs<'a>,
|
||||||
header_for: &roc_parse::header::HeaderFor,
|
header_type: &roc_parse::header::HeaderType,
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
module_ids: &'a ModuleIds,
|
module_ids: &'a ModuleIds,
|
||||||
exposed_ident_ids: IdentIds,
|
exposed_ident_ids: IdentIds,
|
||||||
|
@ -294,7 +298,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let generated_info =
|
let generated_info =
|
||||||
GeneratedInfo::from_header_for(&mut env, &mut scope, var_store, header_for);
|
GeneratedInfo::from_header_type(&mut env, &mut scope, var_store, header_type);
|
||||||
|
|
||||||
// Desugar operators (convert them to Apply calls, taking into account
|
// Desugar operators (convert them to Apply calls, taking into account
|
||||||
// operator precedence and associativity rules), before doing other canonicalization.
|
// operator precedence and associativity rules), before doing other canonicalization.
|
||||||
|
|
|
@ -556,8 +556,13 @@ pub fn fmt_str_literal<'buf>(buf: &mut Buf<'buf>, literal: StrLiteral, indent: u
|
||||||
|
|
||||||
for segments in lines.iter() {
|
for segments in lines.iter() {
|
||||||
for seg in segments.iter() {
|
for seg in segments.iter() {
|
||||||
|
// only add indent if the line isn't empty
|
||||||
|
if *seg != StrSegment::Plaintext("\n") {
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
format_str_segment(seg, buf, indent);
|
format_str_segment(seg, buf, indent);
|
||||||
|
} else {
|
||||||
|
buf.newline();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.newline();
|
buf.newline();
|
||||||
|
|
|
@ -9,7 +9,7 @@ use roc_parse::ast::{Collection, Header, Module, Spaced, Spaces};
|
||||||
use roc_parse::header::{
|
use roc_parse::header::{
|
||||||
AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry,
|
AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry,
|
||||||
ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName, PackageEntry,
|
ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName, PackageEntry,
|
||||||
PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, PlatformRequires,
|
PackageKeyword, PackagePath, PackagesKeyword, PlatformHeader, PlatformRequires,
|
||||||
ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
||||||
};
|
};
|
||||||
use roc_parse::ident::UppercaseIdent;
|
use roc_parse::ident::UppercaseIdent;
|
||||||
|
@ -276,7 +276,7 @@ impl<'a> Formattable for TypedIdent<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_package_name<'buf>(buf: &mut Buf<'buf>, name: PackageName, _indent: u16) {
|
fn fmt_package_name<'buf>(buf: &mut Buf<'buf>, name: PackagePath, _indent: u16) {
|
||||||
buf.push('"');
|
buf.push('"');
|
||||||
buf.push_str_allow_spaces(name.to_str());
|
buf.push_str_allow_spaces(name.to_str());
|
||||||
buf.push('"');
|
buf.push('"');
|
||||||
|
@ -453,7 +453,7 @@ fn fmt_packages_entry<'a, 'buf>(buf: &mut Buf<'buf>, entry: &PackageEntry<'a>, i
|
||||||
buf.push_str(entry.shorthand);
|
buf.push_str(entry.shorthand);
|
||||||
buf.push(':');
|
buf.push(':');
|
||||||
fmt_default_spaces(buf, entry.spaces_after_shorthand, indent);
|
fmt_default_spaces(buf, entry.spaces_after_shorthand, indent);
|
||||||
fmt_package_name(buf, entry.package_name.value, indent);
|
fmt_package_name(buf, entry.package_path.value, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_imports_entry<'a, 'buf>(buf: &mut Buf<'buf>, entry: &ImportsEntry<'a>, indent: u16) {
|
fn fmt_imports_entry<'a, 'buf>(buf: &mut Buf<'buf>, entry: &ImportsEntry<'a>, indent: u16) {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use roc_parse::{
|
||||||
},
|
},
|
||||||
header::{
|
header::{
|
||||||
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, KeywordItem,
|
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, KeywordItem,
|
||||||
ModuleName, PackageEntry, PackageName, PlatformHeader, PlatformRequires, ProvidesTo, To,
|
ModuleName, PackageEntry, PackagePath, PlatformHeader, PlatformRequires, ProvidesTo, To,
|
||||||
TypedIdent,
|
TypedIdent,
|
||||||
},
|
},
|
||||||
ident::UppercaseIdent,
|
ident::UppercaseIdent,
|
||||||
|
@ -349,7 +349,7 @@ impl<'a> RemoveSpaces<'a> for ModuleName<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for PackageName<'a> {
|
impl<'a> RemoveSpaces<'a> for PackagePath<'a> {
|
||||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
|
@ -394,7 +394,7 @@ impl<'a> RemoveSpaces<'a> for PackageEntry<'a> {
|
||||||
PackageEntry {
|
PackageEntry {
|
||||||
shorthand: self.shorthand,
|
shorthand: self.shorthand,
|
||||||
spaces_after_shorthand: &[],
|
spaces_after_shorthand: &[],
|
||||||
package_name: self.package_name.remove_spaces(arena),
|
package_path: self.package_path.remove_spaces(arena),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::llvm::build_list::{self, allocate_list, empty_polymorphic_list};
|
||||||
use crate::llvm::convert::{
|
use crate::llvm::convert::{
|
||||||
argument_type_from_layout, basic_type_from_builtin, basic_type_from_layout, zig_str_type,
|
argument_type_from_layout, basic_type_from_builtin, basic_type_from_layout, zig_str_type,
|
||||||
};
|
};
|
||||||
use crate::llvm::expect::clone_to_shared_memory;
|
use crate::llvm::expect::{clone_to_shared_memory, SharedMemoryPointer};
|
||||||
use crate::llvm::refcounting::{
|
use crate::llvm::refcounting::{
|
||||||
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
|
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
|
||||||
};
|
};
|
||||||
|
@ -41,7 +41,7 @@ use roc_error_macros::internal_error;
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
use roc_mono::ir::{
|
use roc_mono::ir::{
|
||||||
BranchInfo, CallType, CrashTag, EntryPoint, JoinPointId, ListLiteralElement, ModifyRc,
|
BranchInfo, CallType, CrashTag, EntryPoint, JoinPointId, ListLiteralElement, ModifyRc,
|
||||||
OptLevel, ProcLayout,
|
OptLevel, ProcLayout, SingleEntryPoint,
|
||||||
};
|
};
|
||||||
use roc_mono::layout::{
|
use roc_mono::layout::{
|
||||||
Builtin, CapturesNiche, LambdaName, LambdaSet, Layout, LayoutIds, RawFunctionLayout,
|
Builtin, CapturesNiche, LambdaName, LambdaSet, Layout, LayoutIds, RawFunctionLayout,
|
||||||
|
@ -2611,17 +2611,20 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
match env.target_info.ptr_width() {
|
match env.target_info.ptr_width() {
|
||||||
roc_target::PtrWidth::Bytes8 => {
|
roc_target::PtrWidth::Bytes8 => {
|
||||||
|
let shared_memory = SharedMemoryPointer::get(env);
|
||||||
|
|
||||||
clone_to_shared_memory(
|
clone_to_shared_memory(
|
||||||
env,
|
env,
|
||||||
scope,
|
scope,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
|
&shared_memory,
|
||||||
*cond_symbol,
|
*cond_symbol,
|
||||||
*region,
|
*region,
|
||||||
lookups,
|
lookups,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let LlvmBackendMode::BinaryDev = env.mode {
|
if let LlvmBackendMode::BinaryDev = env.mode {
|
||||||
crate::llvm::expect::finalize(env);
|
crate::llvm::expect::notify_parent_expect(env, &shared_memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
bd.build_unconditional_branch(then_block);
|
bd.build_unconditional_branch(then_block);
|
||||||
|
@ -2677,10 +2680,13 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
match env.target_info.ptr_width() {
|
match env.target_info.ptr_width() {
|
||||||
roc_target::PtrWidth::Bytes8 => {
|
roc_target::PtrWidth::Bytes8 => {
|
||||||
|
let shared_memory = SharedMemoryPointer::get(env);
|
||||||
|
|
||||||
clone_to_shared_memory(
|
clone_to_shared_memory(
|
||||||
env,
|
env,
|
||||||
scope,
|
scope,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
|
&shared_memory,
|
||||||
*cond_symbol,
|
*cond_symbol,
|
||||||
*region,
|
*region,
|
||||||
lookups,
|
lookups,
|
||||||
|
@ -4164,29 +4170,23 @@ pub fn build_procedures<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
opt_level: OptLevel,
|
opt_level: OptLevel,
|
||||||
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||||
opt_entry_point: Option<EntryPoint<'a>>,
|
entry_point: EntryPoint<'a>,
|
||||||
debug_output_file: Option<&Path>,
|
debug_output_file: Option<&Path>,
|
||||||
) {
|
) {
|
||||||
build_procedures_help(
|
build_procedures_help(env, opt_level, procedures, entry_point, debug_output_file);
|
||||||
env,
|
|
||||||
opt_level,
|
|
||||||
procedures,
|
|
||||||
opt_entry_point,
|
|
||||||
debug_output_file,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_wasm_test_wrapper<'a, 'ctx, 'env>(
|
pub fn build_wasm_test_wrapper<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
opt_level: OptLevel,
|
opt_level: OptLevel,
|
||||||
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||||
entry_point: EntryPoint<'a>,
|
entry_point: SingleEntryPoint<'a>,
|
||||||
) -> (&'static str, FunctionValue<'ctx>) {
|
) -> (&'static str, FunctionValue<'ctx>) {
|
||||||
let mod_solutions = build_procedures_help(
|
let mod_solutions = build_procedures_help(
|
||||||
env,
|
env,
|
||||||
opt_level,
|
opt_level,
|
||||||
procedures,
|
procedures,
|
||||||
Some(entry_point),
|
EntryPoint::Single(entry_point),
|
||||||
Some(&std::env::temp_dir().join("test.ll")),
|
Some(&std::env::temp_dir().join("test.ll")),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -4197,13 +4197,13 @@ pub fn build_procedures_return_main<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
opt_level: OptLevel,
|
opt_level: OptLevel,
|
||||||
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||||
entry_point: EntryPoint<'a>,
|
entry_point: SingleEntryPoint<'a>,
|
||||||
) -> (&'static str, FunctionValue<'ctx>) {
|
) -> (&'static str, FunctionValue<'ctx>) {
|
||||||
let mod_solutions = build_procedures_help(
|
let mod_solutions = build_procedures_help(
|
||||||
env,
|
env,
|
||||||
opt_level,
|
opt_level,
|
||||||
procedures,
|
procedures,
|
||||||
Some(entry_point),
|
EntryPoint::Single(entry_point),
|
||||||
Some(&std::env::temp_dir().join("test.ll")),
|
Some(&std::env::temp_dir().join("test.ll")),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -4215,13 +4215,14 @@ pub fn build_procedures_expose_expects<'a, 'ctx, 'env>(
|
||||||
opt_level: OptLevel,
|
opt_level: OptLevel,
|
||||||
expects: &[Symbol],
|
expects: &[Symbol],
|
||||||
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||||
opt_entry_point: Option<EntryPoint<'a>>,
|
|
||||||
) -> Vec<'a, &'a str> {
|
) -> Vec<'a, &'a str> {
|
||||||
|
let entry_point = EntryPoint::Expects { symbols: expects };
|
||||||
|
|
||||||
let mod_solutions = build_procedures_help(
|
let mod_solutions = build_procedures_help(
|
||||||
env,
|
env,
|
||||||
opt_level,
|
opt_level,
|
||||||
procedures,
|
procedures,
|
||||||
opt_entry_point,
|
entry_point,
|
||||||
Some(&std::env::temp_dir().join("test.ll")),
|
Some(&std::env::temp_dir().join("test.ll")),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -4243,7 +4244,11 @@ pub fn build_procedures_expose_expects<'a, 'ctx, 'env>(
|
||||||
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
|
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
|
||||||
|
|
||||||
let mut it = func_solutions.specs();
|
let mut it = func_solutions.specs();
|
||||||
let func_spec = it.next().unwrap();
|
let func_spec = match it.next() {
|
||||||
|
Some(spec) => spec,
|
||||||
|
None => panic!("no specialization for expect {}", symbol),
|
||||||
|
};
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
it.next().is_none(),
|
it.next().is_none(),
|
||||||
"we expect only one specialization of this symbol"
|
"we expect only one specialization of this symbol"
|
||||||
|
@ -4283,7 +4288,7 @@ fn build_procedures_help<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
opt_level: OptLevel,
|
opt_level: OptLevel,
|
||||||
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||||
opt_entry_point: Option<EntryPoint<'a>>,
|
entry_point: EntryPoint<'a>,
|
||||||
debug_output_file: Option<&Path>,
|
debug_output_file: Option<&Path>,
|
||||||
) -> &'a ModSolutions {
|
) -> &'a ModSolutions {
|
||||||
let mut layout_ids = roc_mono::layout::LayoutIds::default();
|
let mut layout_ids = roc_mono::layout::LayoutIds::default();
|
||||||
|
@ -4295,7 +4300,7 @@ fn build_procedures_help<'a, 'ctx, 'env>(
|
||||||
env.arena,
|
env.arena,
|
||||||
env.layout_interner,
|
env.layout_interner,
|
||||||
opt_level,
|
opt_level,
|
||||||
opt_entry_point,
|
entry_point,
|
||||||
it,
|
it,
|
||||||
) {
|
) {
|
||||||
Err(e) => panic!("Error in alias analysis: {}", e),
|
Err(e) => panic!("Error in alias analysis: {}", e),
|
||||||
|
|
|
@ -18,6 +18,32 @@ use super::build::{
|
||||||
Scope,
|
Scope,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub(crate) struct SharedMemoryPointer<'ctx>(PointerValue<'ctx>);
|
||||||
|
|
||||||
|
impl<'ctx> SharedMemoryPointer<'ctx> {
|
||||||
|
pub(crate) fn get<'a, 'env>(env: &Env<'a, 'ctx, 'env>) -> Self {
|
||||||
|
let start_function = if let LlvmBackendMode::BinaryDev = env.mode {
|
||||||
|
bitcode::UTILS_EXPECT_FAILED_START_SHARED_FILE
|
||||||
|
} else {
|
||||||
|
bitcode::UTILS_EXPECT_FAILED_START_SHARED_BUFFER
|
||||||
|
};
|
||||||
|
|
||||||
|
let func = env.module.get_function(start_function).unwrap();
|
||||||
|
|
||||||
|
let call_result = env
|
||||||
|
.builder
|
||||||
|
.build_call(func, &[], "call_expect_start_failed");
|
||||||
|
|
||||||
|
let ptr = call_result
|
||||||
|
.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.unwrap()
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
|
Self(ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct Cursors<'ctx> {
|
struct Cursors<'ctx> {
|
||||||
offset: IntValue<'ctx>,
|
offset: IntValue<'ctx>,
|
||||||
|
@ -94,48 +120,39 @@ fn write_state<'a, 'ctx, 'env>(
|
||||||
env.builder.build_store(offset_ptr, offset);
|
env.builder.build_store(offset_ptr, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn finalize(env: &Env) {
|
pub(crate) fn notify_parent_expect(env: &Env, shared_memory: &SharedMemoryPointer) {
|
||||||
let func = env
|
let func = env
|
||||||
.module
|
.module
|
||||||
.get_function(bitcode::UTILS_EXPECT_FAILED_FINALIZE)
|
.get_function(bitcode::NOTIFY_PARENT_EXPECT)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
env.builder
|
env.builder.build_call(
|
||||||
.build_call(func, &[], "call_expect_failed_finalize");
|
func,
|
||||||
|
&[shared_memory.0.into()],
|
||||||
|
"call_expect_failed_finalize",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn send_dbg(env: &Env) {
|
pub(crate) fn notify_parent_dbg(env: &Env, shared_memory: &SharedMemoryPointer) {
|
||||||
let func = env.module.get_function(bitcode::UTILS_SEND_DBG).unwrap();
|
let func = env.module.get_function(bitcode::NOTIFY_PARENT_DBG).unwrap();
|
||||||
|
|
||||||
env.builder
|
env.builder.build_call(
|
||||||
.build_call(func, &[], "call_expect_failed_finalize");
|
func,
|
||||||
|
&[shared_memory.0.into()],
|
||||||
|
"call_expect_failed_finalize",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>(
|
pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
scope: &Scope<'a, 'ctx>,
|
scope: &Scope<'a, 'ctx>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
shared_memory: &SharedMemoryPointer<'ctx>,
|
||||||
condition: Symbol,
|
condition: Symbol,
|
||||||
region: Region,
|
region: Region,
|
||||||
lookups: &[Symbol],
|
lookups: &[Symbol],
|
||||||
) {
|
) {
|
||||||
let start_function = if let LlvmBackendMode::BinaryDev = env.mode {
|
let original_ptr = shared_memory.0;
|
||||||
bitcode::UTILS_EXPECT_FAILED_START_SHARED_FILE
|
|
||||||
} else {
|
|
||||||
bitcode::UTILS_EXPECT_FAILED_START_SHARED_BUFFER
|
|
||||||
};
|
|
||||||
|
|
||||||
let func = env.module.get_function(start_function).unwrap();
|
|
||||||
|
|
||||||
let call_result = env
|
|
||||||
.builder
|
|
||||||
.build_call(func, &[], "call_expect_start_failed");
|
|
||||||
|
|
||||||
let original_ptr = call_result
|
|
||||||
.try_as_basic_value()
|
|
||||||
.left()
|
|
||||||
.unwrap()
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let (count, mut offset) = read_state(env, original_ptr);
|
let (count, mut offset) = read_state(env, original_ptr);
|
||||||
|
|
||||||
|
|
|
@ -158,7 +158,6 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
|
||||||
|
|
||||||
unreachable_function(env, "roc_getppid");
|
unreachable_function(env, "roc_getppid");
|
||||||
unreachable_function(env, "roc_mmap");
|
unreachable_function(env, "roc_mmap");
|
||||||
unreachable_function(env, "roc_send_signal");
|
|
||||||
unreachable_function(env, "roc_shm_open");
|
unreachable_function(env, "roc_shm_open");
|
||||||
|
|
||||||
add_sjlj_roc_panic(env)
|
add_sjlj_roc_panic(env)
|
||||||
|
@ -168,7 +167,10 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
|
||||||
fn unreachable_function(env: &Env, name: &str) {
|
fn unreachable_function(env: &Env, name: &str) {
|
||||||
// The type of this function (but not the implementation) should have
|
// The type of this function (but not the implementation) should have
|
||||||
// already been defined by the builtins, which rely on it.
|
// already been defined by the builtins, which rely on it.
|
||||||
let fn_val = env.module.get_function(name).unwrap();
|
let fn_val = match env.module.get_function(name) {
|
||||||
|
Some(f) => f,
|
||||||
|
None => panic!("extern function {name} is not defined by the builtins"),
|
||||||
|
};
|
||||||
|
|
||||||
// Add a basic block for the entry point
|
// Add a basic block for the entry point
|
||||||
let entry = env.context.append_basic_block(fn_val, "entry");
|
let entry = env.context.append_basic_block(fn_val, "entry");
|
||||||
|
|
|
@ -1126,16 +1126,19 @@ pub(crate) fn run_low_level<'a, 'ctx, 'env>(
|
||||||
if env.mode.runs_expects() {
|
if env.mode.runs_expects() {
|
||||||
let region = unsafe { std::mem::transmute::<_, roc_region::all::Region>(args[0]) };
|
let region = unsafe { std::mem::transmute::<_, roc_region::all::Region>(args[0]) };
|
||||||
|
|
||||||
|
let shared_memory = crate::llvm::expect::SharedMemoryPointer::get(env);
|
||||||
|
|
||||||
crate::llvm::expect::clone_to_shared_memory(
|
crate::llvm::expect::clone_to_shared_memory(
|
||||||
env,
|
env,
|
||||||
scope,
|
scope,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
|
&shared_memory,
|
||||||
args[0],
|
args[0],
|
||||||
region,
|
region,
|
||||||
&[args[0]],
|
&[args[0]],
|
||||||
);
|
);
|
||||||
|
|
||||||
crate::llvm::expect::send_dbg(env);
|
crate::llvm::expect::notify_parent_dbg(env, &shared_memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
condition
|
condition
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -119,11 +119,17 @@ pub enum OptLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct EntryPoint<'a> {
|
pub struct SingleEntryPoint<'a> {
|
||||||
pub symbol: Symbol,
|
pub symbol: Symbol,
|
||||||
pub layout: ProcLayout<'a>,
|
pub layout: ProcLayout<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum EntryPoint<'a> {
|
||||||
|
Single(SingleEntryPoint<'a>),
|
||||||
|
Expects { symbols: &'a [Symbol] },
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct PartialProcId(usize);
|
pub struct PartialProcId(usize);
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ encode_unicode.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
roc_test_utils = { path = "../../test_utils" }
|
roc_test_utils = { path = "../../test_utils" }
|
||||||
|
proptest = "1.0.0"
|
||||||
|
|
||||||
criterion.workspace = true
|
criterion.workspace = true
|
||||||
pretty_assertions.workspace = true
|
pretty_assertions.workspace = true
|
||||||
|
|
|
@ -1,22 +1,42 @@
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
use roc_parse::{module, module::module_defs, parser::Parser, state::State};
|
use roc_parse::{module, module::module_defs, parser::Parser, state::State};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
pub fn parse_benchmark(c: &mut Criterion) {
|
||||||
let mut path = std::env::current_dir()
|
c.bench_function("parse false-interpreter", |b| {
|
||||||
.unwrap()
|
let mut path = PathBuf::from(std::env!("ROC_WORKSPACE_DIR"));
|
||||||
.parent()
|
|
||||||
.unwrap()
|
|
||||||
.parent()
|
|
||||||
.unwrap()
|
|
||||||
.to_owned();
|
|
||||||
path.push("examples");
|
path.push("examples");
|
||||||
|
path.push("cli");
|
||||||
path.push("false-interpreter");
|
path.push("false-interpreter");
|
||||||
path.push("False.roc");
|
path.push("False.roc");
|
||||||
|
|
||||||
let src = std::fs::read_to_string(&path).unwrap();
|
let src = std::fs::read_to_string(&path).unwrap();
|
||||||
|
|
||||||
c.bench_function("parse false-interpreter", |b| {
|
b.iter(|| {
|
||||||
|
let arena = Bump::new();
|
||||||
|
|
||||||
|
let (_actual, state) =
|
||||||
|
module::parse_header(&arena, State::new(src.as_bytes())).unwrap();
|
||||||
|
|
||||||
|
let min_indent = 0;
|
||||||
|
let res = module_defs()
|
||||||
|
.parse(&arena, state, min_indent)
|
||||||
|
.map(|tuple| tuple.1)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
black_box(res.len());
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
c.bench_function("parse Num builtin", |b| {
|
||||||
|
let mut path = PathBuf::from(std::env!("ROC_WORKSPACE_DIR"));
|
||||||
|
path.push("crates");
|
||||||
|
path.push("compiler");
|
||||||
|
path.push("builtins");
|
||||||
|
path.push("roc");
|
||||||
|
path.push("Num.roc");
|
||||||
|
let src = std::fs::read_to_string(&path).unwrap();
|
||||||
|
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
|
|
||||||
|
@ -34,5 +54,5 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group!(benches, criterion_benchmark);
|
criterion_group!(benches, parse_benchmark);
|
||||||
criterion_main!(benches);
|
criterion_main!(benches);
|
||||||
|
|
22
crates/compiler/parse/fuzz/Cargo.lock
generated
22
crates/compiler/parse/fuzz/Cargo.lock
generated
|
@ -72,9 +72,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.10.0"
|
version = "3.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
|
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
|
@ -96,9 +96,15 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encode_unicode"
|
name = "encode_unicode"
|
||||||
version = "0.3.6"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "funty"
|
name = "funty"
|
||||||
|
@ -167,12 +173,6 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lazy_static"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.131"
|
version = "0.2.131"
|
||||||
|
@ -264,6 +264,7 @@ version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitvec",
|
"bitvec",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
|
"fnv",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"im",
|
"im",
|
||||||
"im-rc",
|
"im-rc",
|
||||||
|
@ -283,7 +284,6 @@ name = "roc_module"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"lazy_static",
|
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_error_macros",
|
"roc_error_macros",
|
||||||
"roc_ident",
|
"roc_ident",
|
||||||
|
|
|
@ -37,10 +37,7 @@ where
|
||||||
E: 'a + SpaceProblem,
|
E: 'a + SpaceProblem,
|
||||||
{
|
{
|
||||||
parser::map_with_arena(
|
parser::map_with_arena(
|
||||||
and(
|
and(space0_e(indent_before_problem), and(parser, spaces())),
|
||||||
space0_e(indent_before_problem),
|
|
||||||
and(parser, space0_no_after_indent_check()),
|
|
||||||
),
|
|
||||||
spaces_around_help,
|
spaces_around_help,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -164,474 +161,268 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn simple_eat_whitespace(bytes: &[u8]) -> usize {
|
||||||
|
let mut i = 0;
|
||||||
|
while i < bytes.len() {
|
||||||
|
match bytes[i] {
|
||||||
|
b' ' => i += 1,
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fast_eat_whitespace(bytes: &[u8]) -> usize {
|
||||||
|
// Load 8 bytes at a time, keeping in mind that the initial offset may not be aligned
|
||||||
|
let mut i = 0;
|
||||||
|
while i + 8 <= bytes.len() {
|
||||||
|
let chunk = unsafe {
|
||||||
|
// Safe because we know the pointer is in bounds
|
||||||
|
(bytes.as_ptr().add(i) as *const u64)
|
||||||
|
.read_unaligned()
|
||||||
|
.to_le()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Space character is 0x20, which has a single bit set
|
||||||
|
// We can check for any space character by checking if any other bit is set
|
||||||
|
let spaces = 0x2020_2020_2020_2020;
|
||||||
|
|
||||||
|
// First, generate a mask where each byte is 0xff if the byte is a space,
|
||||||
|
// and some other bit sequence otherwise
|
||||||
|
let mask = !(chunk ^ spaces);
|
||||||
|
|
||||||
|
// Now mask off the high bit, so there's some place to carry into without
|
||||||
|
// overflowing into the next byte.
|
||||||
|
let mask = mask & !0x8080_8080_8080_8080;
|
||||||
|
|
||||||
|
// Now add 0x0101_0101_0101_0101 to each byte, which will carry into the high bit
|
||||||
|
// if and only if the byte is a space.
|
||||||
|
let mask = mask + 0x0101_0101_0101_0101;
|
||||||
|
|
||||||
|
// Now mask off areas where the original bytes had the high bit set, so that
|
||||||
|
// 0x80|0x20 = 0xa0 will not be considered a space.
|
||||||
|
let mask = mask & !(chunk & 0x8080_8080_8080_8080);
|
||||||
|
|
||||||
|
// Make sure all the _other_ bits aside from the high bit are set,
|
||||||
|
// and count the number of trailing one bits, dividing by 8 to get the number of
|
||||||
|
// bytes that are spaces.
|
||||||
|
let count = ((mask | !0x8080_8080_8080_8080).trailing_ones() as usize) / 8;
|
||||||
|
|
||||||
|
if count == 8 {
|
||||||
|
i += 8;
|
||||||
|
} else {
|
||||||
|
return i + count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the remaining bytes
|
||||||
|
simple_eat_whitespace(&bytes[i..]) + i
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn simple_eat_until_control_character(bytes: &[u8]) -> usize {
|
||||||
|
let mut i = 0;
|
||||||
|
while i < bytes.len() {
|
||||||
|
if bytes[i] < b' ' {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fast_eat_until_control_character(bytes: &[u8]) -> usize {
|
||||||
|
// Load 8 bytes at a time, keeping in mind that the initial offset may not be aligned
|
||||||
|
let mut i = 0;
|
||||||
|
while i + 8 <= bytes.len() {
|
||||||
|
let chunk = unsafe {
|
||||||
|
// Safe because we know the pointer is in bounds
|
||||||
|
(bytes.as_ptr().add(i) as *const u64)
|
||||||
|
.read_unaligned()
|
||||||
|
.to_le()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Control characters are 0x00-0x1F, and don't have any high bits set.
|
||||||
|
// They only have bits set that fall under the 0x1F mask.
|
||||||
|
let control = 0x1F1F_1F1F_1F1F_1F1F;
|
||||||
|
|
||||||
|
// First we set up a value where, if a given byte is a control character,
|
||||||
|
// it'll have a all the non-control bits set to 1. All control bits are set to zero.
|
||||||
|
let mask = !(chunk & !control) & !control;
|
||||||
|
|
||||||
|
// Now, down shift by one bit. This will leave room for the following add to
|
||||||
|
// carry, without impacting the next byte.
|
||||||
|
let mask = mask >> 1;
|
||||||
|
|
||||||
|
// Add one (shifted by the right amount), causing all the one bits in the control
|
||||||
|
// characters to cascade, and put a one in the high bit.
|
||||||
|
let mask = mask.wrapping_add(0x1010_1010_1010_1010);
|
||||||
|
|
||||||
|
// Now, we can count the number of trailing zero bits, dividing by 8 to get the
|
||||||
|
// number of bytes before the first control character.
|
||||||
|
let count = (mask & 0x8080_8080_8080_8080).trailing_zeros() as usize / 8;
|
||||||
|
|
||||||
|
if count == 8 {
|
||||||
|
i += 8;
|
||||||
|
} else {
|
||||||
|
return i + count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the remaining bytes
|
||||||
|
simple_eat_until_control_character(&bytes[i..]) + i
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use proptest::prelude::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eat_whitespace_simple() {
|
||||||
|
let bytes = &[0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
assert_eq!(simple_eat_whitespace(bytes), fast_eat_whitespace(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
#[test]
|
||||||
|
fn test_eat_whitespace(bytes in proptest::collection::vec(any::<u8>(), 0..100)) {
|
||||||
|
prop_assert_eq!(simple_eat_whitespace(&bytes), fast_eat_whitespace(&bytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eat_until_control_character_simple() {
|
||||||
|
let bytes = &[32, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
assert_eq!(
|
||||||
|
simple_eat_until_control_character(bytes),
|
||||||
|
fast_eat_until_control_character(bytes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
#[test]
|
||||||
|
fn test_eat_until_control_character(bytes in proptest::collection::vec(any::<u8>(), 0..100)) {
|
||||||
|
prop_assert_eq!(
|
||||||
|
simple_eat_until_control_character(&bytes),
|
||||||
|
fast_eat_until_control_character(&bytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn space0_e<'a, E>(
|
pub fn space0_e<'a, E>(
|
||||||
indent_problem: fn(Position) -> E,
|
indent_problem: fn(Position) -> E,
|
||||||
) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E>
|
) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E>
|
||||||
where
|
where
|
||||||
E: 'a + SpaceProblem,
|
E: 'a + SpaceProblem,
|
||||||
{
|
{
|
||||||
spaces_help_help(indent_problem)
|
move |arena, state: State<'a>, min_indent: u32| {
|
||||||
|
let start = state.pos();
|
||||||
|
match spaces().parse(arena, state, min_indent) {
|
||||||
|
Ok((progress, spaces, state)) => {
|
||||||
|
if progress == NoProgress || state.column() >= min_indent {
|
||||||
|
Ok((progress, spaces, state))
|
||||||
|
} else {
|
||||||
|
Err((progress, indent_problem(start)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err((progress, err)) => Err((progress, err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
fn spaces<'a, E>() -> impl Parser<'a, &'a [CommentOrNewline<'a>], E>
|
||||||
fn spaces_help_help<'a, E>(
|
|
||||||
indent_problem: fn(Position) -> E,
|
|
||||||
) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E>
|
|
||||||
where
|
where
|
||||||
E: 'a + SpaceProblem,
|
E: 'a + SpaceProblem,
|
||||||
{
|
{
|
||||||
move |arena, state: State<'a>, min_indent: u32| match fast_eat_spaces(&state) {
|
move |arena, mut state: State<'a>, _min_indent: u32| {
|
||||||
FastSpaceState::HasTab(position) => Err((
|
let mut newlines = Vec::new_in(arena);
|
||||||
MadeProgress,
|
let mut progress = NoProgress;
|
||||||
E::space_problem(BadInputError::HasTab, position),
|
loop {
|
||||||
)),
|
let whitespace = fast_eat_whitespace(state.bytes());
|
||||||
FastSpaceState::Good {
|
if whitespace > 0 {
|
||||||
newlines,
|
state.advance_mut(whitespace);
|
||||||
consumed,
|
progress = MadeProgress;
|
||||||
column,
|
}
|
||||||
} => {
|
|
||||||
if consumed == 0 {
|
match state.bytes().first() {
|
||||||
Ok((NoProgress, &[] as &[_], state))
|
Some(b'#') => {
|
||||||
} else if column < min_indent {
|
state.advance_mut(1);
|
||||||
Err((MadeProgress, indent_problem(state.pos())))
|
|
||||||
|
let is_doc_comment = state.bytes().first() == Some(&b'#')
|
||||||
|
&& (state.bytes().get(1) == Some(&b' ')
|
||||||
|
|| state.bytes().get(1) == Some(&b'\n')
|
||||||
|
|| state.bytes().get(1) == None);
|
||||||
|
|
||||||
|
if is_doc_comment {
|
||||||
|
state.advance_mut(1);
|
||||||
|
if state.bytes().first() == Some(&b' ') {
|
||||||
|
state.advance_mut(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let len = fast_eat_until_control_character(state.bytes());
|
||||||
|
|
||||||
|
// We already checked that the string is valid UTF-8
|
||||||
|
debug_assert!(std::str::from_utf8(&state.bytes()[..len]).is_ok());
|
||||||
|
let text = unsafe { std::str::from_utf8_unchecked(&state.bytes()[..len]) };
|
||||||
|
|
||||||
|
let comment = if is_doc_comment {
|
||||||
|
CommentOrNewline::DocComment(text)
|
||||||
} else {
|
} else {
|
||||||
let comments_and_newlines = Vec::with_capacity_in(newlines, arena);
|
CommentOrNewline::LineComment(text)
|
||||||
let spaces = eat_spaces(state, comments_and_newlines);
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
MadeProgress,
|
|
||||||
spaces.comments_and_newlines.into_bump_slice(),
|
|
||||||
spaces.state,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn space0_no_after_indent_check<'a, E>() -> impl Parser<'a, &'a [CommentOrNewline<'a>], E>
|
|
||||||
where
|
|
||||||
E: 'a + SpaceProblem,
|
|
||||||
{
|
|
||||||
move |arena, state: State<'a>, _min_indent: u32| match fast_eat_spaces(&state) {
|
|
||||||
FastSpaceState::HasTab(position) => Err((
|
|
||||||
MadeProgress,
|
|
||||||
E::space_problem(BadInputError::HasTab, position),
|
|
||||||
)),
|
|
||||||
FastSpaceState::Good {
|
|
||||||
newlines,
|
|
||||||
consumed,
|
|
||||||
column: _,
|
|
||||||
} => {
|
|
||||||
if consumed == 0 {
|
|
||||||
Ok((NoProgress, &[] as &[_], state))
|
|
||||||
} else {
|
|
||||||
let comments_and_newlines = Vec::with_capacity_in(newlines, arena);
|
|
||||||
let spaces = eat_spaces(state, comments_and_newlines);
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
MadeProgress,
|
|
||||||
spaces.comments_and_newlines.into_bump_slice(),
|
|
||||||
spaces.state,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum FastSpaceState {
|
|
||||||
Good {
|
|
||||||
newlines: usize,
|
|
||||||
consumed: usize,
|
|
||||||
column: u32,
|
|
||||||
},
|
|
||||||
HasTab(Position),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fast_eat_spaces(state: &State) -> FastSpaceState {
|
|
||||||
use FastSpaceState::*;
|
|
||||||
|
|
||||||
let mut newlines = 0;
|
|
||||||
let mut line_start = state.line_start.offset as usize;
|
|
||||||
let base_offset = state.pos().offset as usize;
|
|
||||||
|
|
||||||
let mut index = base_offset;
|
|
||||||
let bytes = state.original_bytes();
|
|
||||||
let length = bytes.len();
|
|
||||||
|
|
||||||
'outer: while index < length {
|
|
||||||
match bytes[index] {
|
|
||||||
b' ' => {
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
b'\n' => {
|
|
||||||
newlines += 1;
|
|
||||||
index += 1;
|
|
||||||
line_start = index;
|
|
||||||
}
|
|
||||||
b'\r' => {
|
|
||||||
index += 1;
|
|
||||||
line_start = index;
|
|
||||||
}
|
|
||||||
b'\t' => {
|
|
||||||
return HasTab(Position::new(index as u32));
|
|
||||||
}
|
|
||||||
b'#' => {
|
|
||||||
index += 1;
|
|
||||||
|
|
||||||
// try to use SIMD instructions explicitly
|
|
||||||
// run with RUSTFLAGS="-C target-cpu=native" to enable
|
|
||||||
#[cfg(all(
|
|
||||||
target_arch = "x86_64",
|
|
||||||
target_feature = "sse2",
|
|
||||||
target_feature = "sse4.2"
|
|
||||||
))]
|
|
||||||
{
|
|
||||||
use std::arch::x86_64::*;
|
|
||||||
|
|
||||||
// a bytestring with the three characters we're looking for (the rest is ignored)
|
|
||||||
let needle = b"\r\n\t=============";
|
|
||||||
let needle = unsafe { _mm_loadu_si128(needle.as_ptr() as *const _) };
|
|
||||||
|
|
||||||
while index < length {
|
|
||||||
let remaining = length - index;
|
|
||||||
let length = if remaining < 16 { remaining as i32 } else { 16 };
|
|
||||||
|
|
||||||
// the source bytes we'll be looking at
|
|
||||||
let haystack =
|
|
||||||
unsafe { _mm_loadu_si128(bytes.as_ptr().add(index) as *const _) };
|
|
||||||
|
|
||||||
// use first 3 characters of needle, first `length` characters of haystack
|
|
||||||
// finds the first index where one of the `needle` characters occurs
|
|
||||||
// or 16 when none of the needle characters occur
|
|
||||||
let first_special_char = unsafe {
|
|
||||||
_mm_cmpestri(needle, 3, haystack, length, _SIDD_CMP_EQUAL_ANY)
|
|
||||||
};
|
};
|
||||||
|
newlines.push(comment);
|
||||||
|
state.advance_mut(len);
|
||||||
|
|
||||||
// we've made `first_special_char` characters of progress
|
if state.bytes().first() == Some(&b'\n') {
|
||||||
index += usize::min(first_special_char as usize, remaining);
|
|
||||||
|
|
||||||
// if we found a special char, let the outer loop handle it
|
|
||||||
if first_special_char != 16 {
|
|
||||||
continue 'outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(all(
|
|
||||||
target_arch = "x86_64",
|
|
||||||
target_feature = "sse2",
|
|
||||||
target_feature = "sse4.2"
|
|
||||||
)))]
|
|
||||||
{
|
|
||||||
while index < length {
|
|
||||||
match bytes[index] {
|
|
||||||
b'\n' | b'\t' | b'\r' => {
|
|
||||||
continue 'outer;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Good {
|
|
||||||
newlines,
|
|
||||||
consumed: index - base_offset,
|
|
||||||
column: (index - line_start) as u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SpaceState<'a> {
|
|
||||||
state: State<'a>,
|
|
||||||
comments_and_newlines: Vec<'a, CommentOrNewline<'a>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eat_spaces<'a>(
|
|
||||||
mut state: State<'a>,
|
|
||||||
mut comments_and_newlines: Vec<'a, CommentOrNewline<'a>>,
|
|
||||||
) -> SpaceState<'a> {
|
|
||||||
for c in state.bytes() {
|
|
||||||
match c {
|
|
||||||
b' ' => {
|
|
||||||
state = state.advance(1);
|
|
||||||
}
|
|
||||||
b'\n' => {
|
|
||||||
state = state.advance_newline();
|
|
||||||
comments_and_newlines.push(CommentOrNewline::Newline);
|
|
||||||
}
|
|
||||||
b'\r' => {
|
|
||||||
state = state.advance_newline();
|
state = state.advance_newline();
|
||||||
}
|
}
|
||||||
b'\t' => unreachable!(),
|
|
||||||
|
|
||||||
b'#' => {
|
progress = MadeProgress;
|
||||||
state = state.advance(1);
|
|
||||||
return eat_line_comment(state, comments_and_newlines);
|
|
||||||
}
|
}
|
||||||
_ => {
|
Some(b'\r') => {
|
||||||
if !comments_and_newlines.is_empty() {
|
if state.bytes().get(1) == Some(&b'\n') {
|
||||||
state = state.mark_current_indent();
|
newlines.push(CommentOrNewline::Newline);
|
||||||
|
state.advance_mut(1);
|
||||||
|
state = state.advance_newline();
|
||||||
|
progress = MadeProgress;
|
||||||
|
} else {
|
||||||
|
return Err((
|
||||||
|
progress,
|
||||||
|
E::space_problem(
|
||||||
|
BadInputError::HasMisplacedCarriageReturn,
|
||||||
|
state.pos(),
|
||||||
|
),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SpaceState {
|
|
||||||
state,
|
|
||||||
comments_and_newlines,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eat_line_comment<'a>(
|
|
||||||
mut state: State<'a>,
|
|
||||||
mut comments_and_newlines: Vec<'a, CommentOrNewline<'a>>,
|
|
||||||
) -> SpaceState<'a> {
|
|
||||||
let mut index = state.pos().offset as usize;
|
|
||||||
let bytes = state.original_bytes();
|
|
||||||
let length = bytes.len();
|
|
||||||
|
|
||||||
'outer: loop {
|
|
||||||
let is_doc_comment = if let Some(b'#') = bytes.get(index) {
|
|
||||||
match bytes.get(index + 1) {
|
|
||||||
Some(b' ') => {
|
|
||||||
state = state.advance(2);
|
|
||||||
index += 2;
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
Some(b'\n') => {
|
Some(b'\n') => {
|
||||||
// consume the second # and the \n
|
newlines.push(CommentOrNewline::Newline);
|
||||||
state = state.advance(1);
|
|
||||||
state = state.advance_newline();
|
state = state.advance_newline();
|
||||||
index += 2;
|
progress = MadeProgress;
|
||||||
|
|
||||||
comments_and_newlines.push(CommentOrNewline::DocComment(""));
|
|
||||||
|
|
||||||
for c in state.bytes() {
|
|
||||||
match c {
|
|
||||||
b' ' => {
|
|
||||||
state = state.advance(1);
|
|
||||||
}
|
}
|
||||||
b'\n' => {
|
Some(b'\t') => {
|
||||||
state = state.advance_newline();
|
return Err((
|
||||||
comments_and_newlines.push(CommentOrNewline::Newline);
|
progress,
|
||||||
|
E::space_problem(BadInputError::HasTab, state.pos()),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
b'\r' => {
|
Some(x) if *x < b' ' => {
|
||||||
state = state.advance_newline();
|
return Err((
|
||||||
}
|
progress,
|
||||||
b'\t' => unreachable!(),
|
E::space_problem(BadInputError::HasAsciiControl, state.pos()),
|
||||||
b'#' => {
|
));
|
||||||
state = state.advance(1);
|
|
||||||
index += 1;
|
|
||||||
continue 'outer;
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
if !newlines.is_empty() {
|
||||||
state = state.mark_current_indent();
|
state = state.mark_current_indent();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
index += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return SpaceState {
|
Ok((progress, newlines.into_bump_slice(), state))
|
||||||
state,
|
|
||||||
comments_and_newlines,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// consume the second #
|
|
||||||
state = state.advance(1);
|
|
||||||
|
|
||||||
return SpaceState {
|
|
||||||
state,
|
|
||||||
comments_and_newlines,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(_) => false,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
let loop_start = index;
|
|
||||||
|
|
||||||
#[cfg(all(
|
|
||||||
target_arch = "x86_64",
|
|
||||||
target_feature = "sse2",
|
|
||||||
target_feature = "sse4.2"
|
|
||||||
))]
|
|
||||||
{
|
|
||||||
use std::arch::x86_64::*;
|
|
||||||
|
|
||||||
// a bytestring with the three characters we're looking for (the rest is ignored)
|
|
||||||
let needle = b"\r\n\t=============";
|
|
||||||
let needle = unsafe { _mm_loadu_si128(needle.as_ptr() as *const _) };
|
|
||||||
|
|
||||||
while index < length {
|
|
||||||
let remaining = length - index;
|
|
||||||
let chunk = if remaining < 16 { remaining as i32 } else { 16 };
|
|
||||||
|
|
||||||
// the source bytes we'll be looking at
|
|
||||||
let haystack = unsafe { _mm_loadu_si128(bytes.as_ptr().add(index) as *const _) };
|
|
||||||
|
|
||||||
// use first 3 characters of needle, first chunk` characters of haystack
|
|
||||||
// finds the first index where one of the `needle` characters occurs
|
|
||||||
// or 16 when none of the needle characters occur
|
|
||||||
let first_special_char =
|
|
||||||
unsafe { _mm_cmpestri(needle, 3, haystack, chunk, _SIDD_CMP_EQUAL_ANY) };
|
|
||||||
|
|
||||||
// we've made `first_special_char` characters of progress
|
|
||||||
let progress = usize::min(first_special_char as usize, remaining);
|
|
||||||
index += progress;
|
|
||||||
state = state.advance(progress);
|
|
||||||
|
|
||||||
if first_special_char != 16 {
|
|
||||||
match bytes[index] {
|
|
||||||
b'\t' => unreachable!(),
|
|
||||||
b'\n' => {
|
|
||||||
let comment =
|
|
||||||
unsafe { std::str::from_utf8_unchecked(&bytes[loop_start..index]) };
|
|
||||||
|
|
||||||
if is_doc_comment {
|
|
||||||
comments_and_newlines.push(CommentOrNewline::DocComment(comment));
|
|
||||||
} else {
|
|
||||||
comments_and_newlines.push(CommentOrNewline::LineComment(comment));
|
|
||||||
}
|
|
||||||
state = state.advance_newline();
|
|
||||||
|
|
||||||
index += 1;
|
|
||||||
while index < length {
|
|
||||||
match bytes[index] {
|
|
||||||
b' ' => {
|
|
||||||
state = state.advance(1);
|
|
||||||
}
|
|
||||||
b'\n' => {
|
|
||||||
state = state.advance_newline();
|
|
||||||
comments_and_newlines.push(CommentOrNewline::Newline);
|
|
||||||
}
|
|
||||||
b'\r' => {
|
|
||||||
state = state.advance_newline();
|
|
||||||
}
|
|
||||||
b'\t' => unreachable!(),
|
|
||||||
b'#' => {
|
|
||||||
state = state.advance(1);
|
|
||||||
index += 1;
|
|
||||||
continue 'outer;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
state = state.mark_current_indent();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SpaceState {
|
|
||||||
state,
|
|
||||||
comments_and_newlines,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
b'\r' => {
|
|
||||||
state = state.advance_newline();
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
odd_character => {
|
|
||||||
unreachable!(
|
|
||||||
"unexpected_character {} {}",
|
|
||||||
odd_character, odd_character as char
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(all(
|
|
||||||
target_arch = "x86_64",
|
|
||||||
target_feature = "sse2",
|
|
||||||
target_feature = "sse4.2"
|
|
||||||
)))]
|
|
||||||
while index < length {
|
|
||||||
match bytes[index] {
|
|
||||||
b'\t' => unreachable!(),
|
|
||||||
b'\n' => {
|
|
||||||
let comment =
|
|
||||||
unsafe { std::str::from_utf8_unchecked(&bytes[loop_start..index]) };
|
|
||||||
|
|
||||||
if is_doc_comment {
|
|
||||||
comments_and_newlines.push(CommentOrNewline::DocComment(comment));
|
|
||||||
} else {
|
|
||||||
comments_and_newlines.push(CommentOrNewline::LineComment(comment));
|
|
||||||
}
|
|
||||||
state = state.advance_newline();
|
|
||||||
|
|
||||||
index += 1;
|
|
||||||
while index < length {
|
|
||||||
match bytes[index] {
|
|
||||||
b' ' => {
|
|
||||||
state = state.advance(1);
|
|
||||||
}
|
|
||||||
b'\n' => {
|
|
||||||
state = state.advance_newline();
|
|
||||||
comments_and_newlines.push(CommentOrNewline::Newline);
|
|
||||||
}
|
|
||||||
b'\r' => {
|
|
||||||
state = state.advance_newline();
|
|
||||||
}
|
|
||||||
b'\t' => unreachable!(),
|
|
||||||
b'#' => {
|
|
||||||
state = state.advance(1);
|
|
||||||
index += 1;
|
|
||||||
continue 'outer;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
state = state.mark_current_indent();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SpaceState {
|
|
||||||
state,
|
|
||||||
comments_and_newlines,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
b'\r' => {
|
|
||||||
state = state.advance_newline();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
state = state.advance(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We made it to the end of the bytes. This means there's a comment without a trailing newline.
|
|
||||||
let comment = unsafe { std::str::from_utf8_unchecked(&bytes[loop_start..index]) };
|
|
||||||
|
|
||||||
if is_doc_comment {
|
|
||||||
comments_and_newlines.push(CommentOrNewline::DocComment(comment));
|
|
||||||
} else {
|
|
||||||
comments_and_newlines.push(CommentOrNewline::LineComment(comment));
|
|
||||||
}
|
|
||||||
|
|
||||||
return SpaceState {
|
|
||||||
state,
|
|
||||||
comments_and_newlines,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1114,7 +1114,15 @@ fn finish_parsing_alias_or_opaque<'a>(
|
||||||
Ok(good) => {
|
Ok(good) => {
|
||||||
type_arguments.push(Loc::at(argument.region, good));
|
type_arguments.push(Loc::at(argument.region, good));
|
||||||
}
|
}
|
||||||
Err(_) => panic!(),
|
Err(()) => {
|
||||||
|
return Err((
|
||||||
|
MadeProgress,
|
||||||
|
EExpr::Pattern(
|
||||||
|
arena.alloc(EPattern::NotAPattern(state.pos())),
|
||||||
|
state.pos(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1577,8 +1585,8 @@ fn parse_expr_operator<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err((NoProgress, expr)) => {
|
Err((NoProgress, _e)) => {
|
||||||
todo!("{:?} {:?}", expr, state)
|
return Err((MadeProgress, EExpr::TrailingOperator(state.pos())));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1722,10 +1730,17 @@ fn parse_expr_end<'a>(
|
||||||
expr_state.consume_spaces(arena);
|
expr_state.consume_spaces(arena);
|
||||||
let call = to_call(arena, expr_state.arguments, expr_state.expr);
|
let call = to_call(arena, expr_state.arguments, expr_state.expr);
|
||||||
|
|
||||||
let loc_pattern = Loc::at(
|
let pattern = expr_to_pattern_help(arena, &call.value).map_err(|()| {
|
||||||
call.region,
|
(
|
||||||
expr_to_pattern_help(arena, &call.value).unwrap(),
|
MadeProgress,
|
||||||
);
|
EExpr::Pattern(
|
||||||
|
arena.alloc(EPattern::NotAPattern(state.pos())),
|
||||||
|
state.pos(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let loc_pattern = Loc::at(call.region, pattern);
|
||||||
|
|
||||||
patterns.insert(0, loc_pattern);
|
patterns.insert(0, loc_pattern);
|
||||||
|
|
||||||
|
|
|
@ -2,37 +2,43 @@ use crate::ast::{Collection, CommentOrNewline, Spaced, Spaces, StrLiteral, TypeA
|
||||||
use crate::blankspace::space0_e;
|
use crate::blankspace::space0_e;
|
||||||
use crate::ident::{lowercase_ident, UppercaseIdent};
|
use crate::ident::{lowercase_ident, UppercaseIdent};
|
||||||
use crate::parser::{optional, then};
|
use crate::parser::{optional, then};
|
||||||
use crate::parser::{specialize, word1, EPackageEntry, EPackageName, Parser};
|
use crate::parser::{specialize, word1, EPackageEntry, EPackagePath, Parser};
|
||||||
use crate::string_literal;
|
use crate::string_literal;
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_region::all::Loc;
|
use roc_region::all::Loc;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum HeaderFor<'a> {
|
pub enum HeaderType<'a> {
|
||||||
App {
|
App {
|
||||||
|
output_name: StrLiteral<'a>,
|
||||||
to_platform: To<'a>,
|
to_platform: To<'a>,
|
||||||
},
|
},
|
||||||
Hosted {
|
Hosted {
|
||||||
|
name: ModuleName<'a>,
|
||||||
generates: UppercaseIdent<'a>,
|
generates: UppercaseIdent<'a>,
|
||||||
generates_with: &'a [Loc<ExposedName<'a>>],
|
generates_with: &'a [Loc<ExposedName<'a>>],
|
||||||
},
|
},
|
||||||
/// Only created during canonicalization, never actually parsed from source
|
/// Only created during canonicalization, never actually parsed from source
|
||||||
Builtin {
|
Builtin {
|
||||||
|
name: ModuleName<'a>,
|
||||||
generates_with: &'a [Symbol],
|
generates_with: &'a [Symbol],
|
||||||
},
|
},
|
||||||
Platform {
|
Platform {
|
||||||
|
opt_app_module_id: Option<ModuleId>,
|
||||||
|
/// the name and type scheme of the main function (required by the platform)
|
||||||
|
/// (type scheme is currently unused)
|
||||||
|
provides: &'a [(Loc<ExposedName<'a>>, Loc<TypedIdent<'a>>)],
|
||||||
|
requires: &'a [Loc<TypedIdent<'a>>],
|
||||||
|
requires_types: &'a [Loc<UppercaseIdent<'a>>],
|
||||||
|
|
||||||
/// usually `pf`
|
/// usually `pf`
|
||||||
config_shorthand: &'a str,
|
config_shorthand: &'a str,
|
||||||
/// the type scheme of the main function (required by the platform)
|
|
||||||
/// (currently unused)
|
|
||||||
#[allow(dead_code)]
|
|
||||||
platform_main_type: TypedIdent<'a>,
|
|
||||||
/// provided symbol to host (commonly `mainForHost`)
|
|
||||||
main_for_host: roc_module::symbol::Symbol,
|
|
||||||
},
|
},
|
||||||
Interface,
|
Interface {
|
||||||
|
name: ModuleName<'a>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||||
|
@ -53,9 +59,9 @@ pub enum VersionComparison {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct PackageName<'a>(&'a str);
|
pub struct PackagePath<'a>(&'a str);
|
||||||
|
|
||||||
impl<'a> PackageName<'a> {
|
impl<'a> PackagePath<'a> {
|
||||||
pub fn to_str(self) -> &'a str {
|
pub fn to_str(self) -> &'a str {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
@ -65,13 +71,13 @@ impl<'a> PackageName<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<PackageName<'a>> for &'a str {
|
impl<'a> From<PackagePath<'a>> for &'a str {
|
||||||
fn from(name: PackageName<'a>) -> &'a str {
|
fn from(name: PackagePath<'a>) -> &'a str {
|
||||||
name.0
|
name.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a str> for PackageName<'a> {
|
impl<'a> From<&'a str> for PackagePath<'a> {
|
||||||
fn from(string: &'a str) -> Self {
|
fn from(string: &'a str) -> Self {
|
||||||
Self(string)
|
Self(string)
|
||||||
}
|
}
|
||||||
|
@ -96,15 +102,6 @@ impl<'a> ModuleName<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ModuleNameEnum<'a> {
|
|
||||||
/// A filename
|
|
||||||
App(StrLiteral<'a>),
|
|
||||||
Interface(ModuleName<'a>),
|
|
||||||
Hosted(ModuleName<'a>),
|
|
||||||
Platform,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||||
pub struct ExposedName<'a>(&'a str);
|
pub struct ExposedName<'a>(&'a str);
|
||||||
|
|
||||||
|
@ -184,7 +181,7 @@ pub struct HostedHeader<'a> {
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum To<'a> {
|
pub enum To<'a> {
|
||||||
ExistingPackage(&'a str),
|
ExistingPackage(&'a str),
|
||||||
NewPackage(PackageName<'a>),
|
NewPackage(PackagePath<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -212,13 +209,13 @@ pub struct ProvidesTo<'a> {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct PackageHeader<'a> {
|
pub struct PackageHeader<'a> {
|
||||||
pub before_name: &'a [CommentOrNewline<'a>],
|
pub before_name: &'a [CommentOrNewline<'a>],
|
||||||
pub name: Loc<PackageName<'a>>,
|
pub name: Loc<PackagePath<'a>>,
|
||||||
|
|
||||||
pub exposes_keyword: Spaces<'a, ExposesKeyword>,
|
pub exposes_keyword: Spaces<'a, ExposesKeyword>,
|
||||||
pub exposes: Vec<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
pub exposes: Vec<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||||
|
|
||||||
pub packages_keyword: Spaces<'a, PackagesKeyword>,
|
pub packages_keyword: Spaces<'a, PackagesKeyword>,
|
||||||
pub packages: Vec<'a, (Loc<&'a str>, Loc<PackageName<'a>>)>,
|
pub packages: Vec<'a, (Loc<&'a str>, Loc<PackagePath<'a>>)>,
|
||||||
|
|
||||||
pub imports_keyword: Spaces<'a, ImportsKeyword>,
|
pub imports_keyword: Spaces<'a, ImportsKeyword>,
|
||||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||||
|
@ -233,7 +230,7 @@ pub struct PlatformRequires<'a> {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct PlatformHeader<'a> {
|
pub struct PlatformHeader<'a> {
|
||||||
pub before_name: &'a [CommentOrNewline<'a>],
|
pub before_name: &'a [CommentOrNewline<'a>],
|
||||||
pub name: Loc<PackageName<'a>>,
|
pub name: Loc<PackagePath<'a>>,
|
||||||
|
|
||||||
pub requires: KeywordItem<'a, RequiresKeyword, PlatformRequires<'a>>,
|
pub requires: KeywordItem<'a, RequiresKeyword, PlatformRequires<'a>>,
|
||||||
pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>,
|
pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>,
|
||||||
|
@ -274,7 +271,7 @@ pub struct TypedIdent<'a> {
|
||||||
pub struct PackageEntry<'a> {
|
pub struct PackageEntry<'a> {
|
||||||
pub shorthand: &'a str,
|
pub shorthand: &'a str,
|
||||||
pub spaces_after_shorthand: &'a [CommentOrNewline<'a>],
|
pub spaces_after_shorthand: &'a [CommentOrNewline<'a>],
|
||||||
pub package_name: Loc<PackageName<'a>>,
|
pub package_path: Loc<PackagePath<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPackageEntry<'a>> {
|
pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPackageEntry<'a>> {
|
||||||
|
@ -291,19 +288,19 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac
|
||||||
),
|
),
|
||||||
space0_e(EPackageEntry::IndentPackage)
|
space0_e(EPackageEntry::IndentPackage)
|
||||||
)),
|
)),
|
||||||
loc!(specialize(EPackageEntry::BadPackage, package_name()))
|
loc!(specialize(EPackageEntry::BadPackage, package_path()))
|
||||||
),
|
),
|
||||||
move |(opt_shorthand, package_or_path)| {
|
move |(opt_shorthand, package_or_path)| {
|
||||||
let entry = match opt_shorthand {
|
let entry = match opt_shorthand {
|
||||||
Some((shorthand, spaces_after_shorthand)) => PackageEntry {
|
Some((shorthand, spaces_after_shorthand)) => PackageEntry {
|
||||||
shorthand,
|
shorthand,
|
||||||
spaces_after_shorthand,
|
spaces_after_shorthand,
|
||||||
package_name: package_or_path,
|
package_path: package_or_path,
|
||||||
},
|
},
|
||||||
None => PackageEntry {
|
None => PackageEntry {
|
||||||
shorthand: "",
|
shorthand: "",
|
||||||
spaces_after_shorthand: &[],
|
spaces_after_shorthand: &[],
|
||||||
package_name: package_or_path,
|
package_path: package_or_path,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -312,13 +309,13 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, EPackageName<'a>> {
|
pub fn package_path<'a>() -> impl Parser<'a, PackagePath<'a>, EPackagePath<'a>> {
|
||||||
then(
|
then(
|
||||||
loc!(specialize(EPackageName::BadPath, string_literal::parse())),
|
loc!(specialize(EPackagePath::BadPath, string_literal::parse())),
|
||||||
move |_arena, state, progress, text| match text.value {
|
move |_arena, state, progress, text| match text.value {
|
||||||
StrLiteral::PlainLine(text) => Ok((progress, PackageName(text), state)),
|
StrLiteral::PlainLine(text) => Ok((progress, PackagePath(text), state)),
|
||||||
StrLiteral::Line(_) => Err((progress, EPackageName::Escapes(text.region.start()))),
|
StrLiteral::Line(_) => Err((progress, EPackagePath::Escapes(text.region.start()))),
|
||||||
StrLiteral::Block(_) => Err((progress, EPackageName::Multiline(text.region.start()))),
|
StrLiteral::Block(_) => Err((progress, EPackagePath::Multiline(text.region.start()))),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::ast::{Collection, Defs, Header, Module, Spaced, Spaces};
|
use crate::ast::{Collection, Defs, Header, Module, Spaced, Spaces};
|
||||||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||||
use crate::header::{
|
use crate::header::{
|
||||||
package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword,
|
package_entry, package_path, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword,
|
||||||
HostedHeader, ImportsEntry, ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName,
|
HostedHeader, ImportsEntry, ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName,
|
||||||
PackageEntry, PackagesKeyword, PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo,
|
PackageEntry, PackagesKeyword, PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo,
|
||||||
RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
||||||
|
@ -187,7 +187,7 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
|
||||||
fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|
fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|
||||||
record!(PlatformHeader {
|
record!(PlatformHeader {
|
||||||
before_name: space0_e(EHeader::IndentStart),
|
before_name: space0_e(EHeader::IndentStart),
|
||||||
name: loc!(specialize(EHeader::PlatformName, package_name())),
|
name: loc!(specialize(EHeader::PlatformName, package_path())),
|
||||||
requires: specialize(EHeader::Requires, requires()),
|
requires: specialize(EHeader::Requires, requires()),
|
||||||
exposes: specialize(EHeader::Exposes, exposes_modules()),
|
exposes: specialize(EHeader::Exposes, exposes_modules()),
|
||||||
packages: specialize(EHeader::Packages, packages()),
|
packages: specialize(EHeader::Packages, packages()),
|
||||||
|
@ -203,7 +203,7 @@ fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, EProvides<'a>> {
|
||||||
|_, pos| EProvides::Identifier(pos),
|
|_, pos| EProvides::Identifier(pos),
|
||||||
map!(lowercase_ident(), To::ExistingPackage)
|
map!(lowercase_ident(), To::ExistingPackage)
|
||||||
),
|
),
|
||||||
specialize(EProvides::Package, map!(package_name(), To::NewPackage))
|
specialize(EProvides::Package, map!(package_path(), To::NewPackage))
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ pub enum SyntaxError<'a> {
|
||||||
Space(BadInputError),
|
Space(BadInputError),
|
||||||
NotEndOfFile(Position),
|
NotEndOfFile(Position),
|
||||||
}
|
}
|
||||||
pub trait SpaceProblem {
|
pub trait SpaceProblem: std::fmt::Debug {
|
||||||
fn space_problem(e: BadInputError, pos: Position) -> Self;
|
fn space_problem(e: BadInputError, pos: Position) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ pub enum EHeader<'a> {
|
||||||
Start(Position),
|
Start(Position),
|
||||||
ModuleName(Position),
|
ModuleName(Position),
|
||||||
AppName(EString<'a>, Position),
|
AppName(EString<'a>, Position),
|
||||||
PlatformName(EPackageName<'a>, Position),
|
PlatformName(EPackagePath<'a>, Position),
|
||||||
IndentStart(Position),
|
IndentStart(Position),
|
||||||
|
|
||||||
InconsistentModuleName(Region),
|
InconsistentModuleName(Region),
|
||||||
|
@ -146,7 +146,7 @@ pub enum EProvides<'a> {
|
||||||
ListStart(Position),
|
ListStart(Position),
|
||||||
ListEnd(Position),
|
ListEnd(Position),
|
||||||
Identifier(Position),
|
Identifier(Position),
|
||||||
Package(EPackageName<'a>, Position),
|
Package(EPackagePath<'a>, Position),
|
||||||
Space(BadInputError, Position),
|
Space(BadInputError, Position),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ pub enum EPackages<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum EPackageName<'a> {
|
pub enum EPackagePath<'a> {
|
||||||
BadPath(EString<'a>, Position),
|
BadPath(EString<'a>, Position),
|
||||||
Escapes(Position),
|
Escapes(Position),
|
||||||
Multiline(Position),
|
Multiline(Position),
|
||||||
|
@ -210,7 +210,7 @@ pub enum EPackageName<'a> {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum EPackageEntry<'a> {
|
pub enum EPackageEntry<'a> {
|
||||||
BadPackage(EPackageName<'a>, Position),
|
BadPackage(EPackagePath<'a>, Position),
|
||||||
Shorthand(Position),
|
Shorthand(Position),
|
||||||
Colon(Position),
|
Colon(Position),
|
||||||
IndentPackage(Position),
|
IndentPackage(Position),
|
||||||
|
@ -265,6 +265,8 @@ pub enum EGeneratesWith {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum BadInputError {
|
pub enum BadInputError {
|
||||||
HasTab,
|
HasTab,
|
||||||
|
HasMisplacedCarriageReturn,
|
||||||
|
HasAsciiControl,
|
||||||
///
|
///
|
||||||
TooManyLines,
|
TooManyLines,
|
||||||
///
|
///
|
||||||
|
@ -272,15 +274,6 @@ pub enum BadInputError {
|
||||||
BadUtf8,
|
BadUtf8,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bad_input_to_syntax_error<'a>(bad_input: BadInputError) -> SyntaxError<'a> {
|
|
||||||
use crate::parser::BadInputError::*;
|
|
||||||
match bad_input {
|
|
||||||
HasTab => SyntaxError::NotYetImplemented("call error on tabs".to_string()),
|
|
||||||
TooManyLines => SyntaxError::TooManyLines,
|
|
||||||
BadUtf8 => SyntaxError::BadUtf8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> SourceError<'a, T> {
|
impl<'a, T> SourceError<'a, T> {
|
||||||
pub fn new(problem: T, state: &State<'a>) -> Self {
|
pub fn new(problem: T, state: &State<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -323,6 +316,8 @@ impl<'a> SyntaxError<'a> {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum EExpr<'a> {
|
pub enum EExpr<'a> {
|
||||||
|
TrailingOperator(Position),
|
||||||
|
|
||||||
Start(Position),
|
Start(Position),
|
||||||
End(Position),
|
End(Position),
|
||||||
BadExprEnd(Position),
|
BadExprEnd(Position),
|
||||||
|
@ -560,6 +555,7 @@ pub enum EPattern<'a> {
|
||||||
Record(PRecord<'a>, Position),
|
Record(PRecord<'a>, Position),
|
||||||
List(PList<'a>, Position),
|
List(PList<'a>, Position),
|
||||||
Underscore(Position),
|
Underscore(Position),
|
||||||
|
NotAPattern(Position),
|
||||||
|
|
||||||
Start(Position),
|
Start(Position),
|
||||||
End(Position),
|
End(Position),
|
||||||
|
@ -773,7 +769,7 @@ pub struct FileError<'a, T> {
|
||||||
pub trait Parser<'a, Output, Error> {
|
pub trait Parser<'a, Output, Error> {
|
||||||
fn parse(
|
fn parse(
|
||||||
&self,
|
&self,
|
||||||
alloc: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
min_indent: u32,
|
min_indent: u32,
|
||||||
) -> ParseResult<'a, Output, Error>;
|
) -> ParseResult<'a, Output, Error>;
|
||||||
|
|
|
@ -98,7 +98,7 @@ impl<'a> State<'a> {
|
||||||
self.offset += 1;
|
self.offset += 1;
|
||||||
self.line_start = self.pos();
|
self.line_start = self.pos();
|
||||||
|
|
||||||
// WARNING! COULD CAUSE BUGS IF WE FORGET TO CALL mark_current_ident LATER!
|
// WARNING! COULD CAUSE BUGS IF WE FORGET TO CALL mark_current_indent LATER!
|
||||||
// We really need to be stricter about this.
|
// We really need to be stricter about this.
|
||||||
self.line_start_after_whitespace = self.line_start;
|
self.line_start_after_whitespace = self.line_start;
|
||||||
|
|
||||||
|
|
|
@ -41,3 +41,15 @@ pub fn parse_defs_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Defs<'a>,
|
||||||
Err(tuple) => Err(tuple.1),
|
Err(tuple) => Err(tuple.1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_header_with<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
input: &'a str,
|
||||||
|
) -> Result<ast::Module<'a>, SyntaxError<'a>> {
|
||||||
|
let state = State::new(input.trim().as_bytes());
|
||||||
|
|
||||||
|
match crate::module::parse_header(arena, state.clone()) {
|
||||||
|
Ok((header, _)) => Ok(header),
|
||||||
|
Err(fail) => Err(SyntaxError::Header(fail.problem)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Expr(InParens(End(@3), @0), @0)
|
|
@ -0,0 +1,2 @@
|
||||||
|
(@,B
|
||||||
|
.e:
|
|
@ -0,0 +1 @@
|
||||||
|
Expr(Pattern(NotAPattern(@3), @3), @0)
|
|
@ -0,0 +1 @@
|
||||||
|
.e,
|
|
@ -0,0 +1 @@
|
||||||
|
SourceError { problem: Space(HasMisplacedCarriageReturn, @1), bytes: [35, 13, 12, 9, 65] }
|
|
@ -0,0 +1 @@
|
||||||
|
#
A
|
|
@ -0,0 +1 @@
|
||||||
|
Expr(TrailingOperator(@2), @0)
|
|
@ -0,0 +1 @@
|
||||||
|
J-
|
|
@ -3,7 +3,7 @@ Module {
|
||||||
header: Platform(
|
header: Platform(
|
||||||
PlatformHeader {
|
PlatformHeader {
|
||||||
before_name: [],
|
before_name: [],
|
||||||
name: @9-25 PackageName(
|
name: @9-25 PackagePath(
|
||||||
"rtfeldman/blah",
|
"rtfeldman/blah",
|
||||||
),
|
),
|
||||||
requires: KeywordItem {
|
requires: KeywordItem {
|
||||||
|
|
|
@ -19,7 +19,7 @@ Module {
|
||||||
@31-47 PackageEntry {
|
@31-47 PackageEntry {
|
||||||
shorthand: "pf",
|
shorthand: "pf",
|
||||||
spaces_after_shorthand: [],
|
spaces_after_shorthand: [],
|
||||||
package_name: @35-47 PackageName(
|
package_path: @35-47 PackagePath(
|
||||||
"./platform",
|
"./platform",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,7 +19,7 @@ Module {
|
||||||
@31-47 PackageEntry {
|
@31-47 PackageEntry {
|
||||||
shorthand: "pf",
|
shorthand: "pf",
|
||||||
spaces_after_shorthand: [],
|
spaces_after_shorthand: [],
|
||||||
package_name: @35-47 PackageName(
|
package_path: @35-47 PackagePath(
|
||||||
"./platform",
|
"./platform",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,7 +3,7 @@ Module {
|
||||||
header: Platform(
|
header: Platform(
|
||||||
PlatformHeader {
|
PlatformHeader {
|
||||||
before_name: [],
|
before_name: [],
|
||||||
name: @9-14 PackageName(
|
name: @9-14 PackagePath(
|
||||||
"cli",
|
"cli",
|
||||||
),
|
),
|
||||||
requires: KeywordItem {
|
requires: KeywordItem {
|
||||||
|
|
|
@ -22,7 +22,7 @@ Module {
|
||||||
after: [],
|
after: [],
|
||||||
},
|
},
|
||||||
to: @30-38 NewPackage(
|
to: @30-38 NewPackage(
|
||||||
PackageName(
|
PackagePath(
|
||||||
"./blah",
|
"./blah",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -3,7 +3,7 @@ Module {
|
||||||
header: Platform(
|
header: Platform(
|
||||||
PlatformHeader {
|
PlatformHeader {
|
||||||
before_name: [],
|
before_name: [],
|
||||||
name: @9-21 PackageName(
|
name: @9-21 PackagePath(
|
||||||
"foo/barbaz",
|
"foo/barbaz",
|
||||||
),
|
),
|
||||||
requires: KeywordItem {
|
requires: KeywordItem {
|
||||||
|
@ -52,7 +52,7 @@ Module {
|
||||||
@87-99 PackageEntry {
|
@87-99 PackageEntry {
|
||||||
shorthand: "foo",
|
shorthand: "foo",
|
||||||
spaces_after_shorthand: [],
|
spaces_after_shorthand: [],
|
||||||
package_name: @92-99 PackageName(
|
package_path: @92-99 PackagePath(
|
||||||
"./foo",
|
"./foo",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,7 +19,7 @@ Module {
|
||||||
@26-42 PackageEntry {
|
@26-42 PackageEntry {
|
||||||
shorthand: "pf",
|
shorthand: "pf",
|
||||||
spaces_after_shorthand: [],
|
spaces_after_shorthand: [],
|
||||||
package_name: @30-42 PackageName(
|
package_path: @30-42 PackagePath(
|
||||||
"./platform",
|
"./platform",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,7 +3,7 @@ Module {
|
||||||
header: Platform(
|
header: Platform(
|
||||||
PlatformHeader {
|
PlatformHeader {
|
||||||
before_name: [],
|
before_name: [],
|
||||||
name: @9-21 PackageName(
|
name: @9-21 PackagePath(
|
||||||
"test/types",
|
"test/types",
|
||||||
),
|
),
|
||||||
requires: KeywordItem {
|
requires: KeywordItem {
|
||||||
|
|
|
@ -166,8 +166,12 @@ mod test_parse {
|
||||||
fail/record_type_open.expr,
|
fail/record_type_open.expr,
|
||||||
fail/record_type_tab.expr,
|
fail/record_type_tab.expr,
|
||||||
fail/single_no_end.expr,
|
fail/single_no_end.expr,
|
||||||
|
fail/tab_crash.header,
|
||||||
fail/tag_union_end.expr,
|
fail/tag_union_end.expr,
|
||||||
fail/tag_union_lowercase_tag_name.expr,
|
fail/tag_union_lowercase_tag_name.expr,
|
||||||
|
fail/trailing_operator.expr,
|
||||||
|
fail/expr_to_pattern_fail.expr,
|
||||||
|
fail/alias_or_opaque_fail.expr,
|
||||||
fail/tag_union_open.expr,
|
fail/tag_union_open.expr,
|
||||||
fail/tag_union_second_lowercase_tag_name.expr,
|
fail/tag_union_second_lowercase_tag_name.expr,
|
||||||
fail/type_annotation_double_colon.expr,
|
fail/type_annotation_double_colon.expr,
|
||||||
|
|
|
@ -2,6 +2,7 @@ use libloading::Library;
|
||||||
use roc_build::link::{link, LinkType};
|
use roc_build::link::{link, LinkType};
|
||||||
use roc_builtins::bitcode;
|
use roc_builtins::bitcode;
|
||||||
use roc_load::{EntryPoint, ExecutionMode, LoadConfig, Threading};
|
use roc_load::{EntryPoint, ExecutionMode, LoadConfig, Threading};
|
||||||
|
use roc_mono::ir::SingleEntryPoint;
|
||||||
use roc_packaging::cache::RocCacheDir;
|
use roc_packaging::cache::RocCacheDir;
|
||||||
use roc_region::all::LineInfo;
|
use roc_region::all::LineInfo;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
@ -105,8 +106,15 @@ pub fn helper(
|
||||||
|
|
||||||
debug_assert_eq!(exposed_to_host.values.len(), 1);
|
debug_assert_eq!(exposed_to_host.values.len(), 1);
|
||||||
let entry_point = match loaded.entry_point {
|
let entry_point = match loaded.entry_point {
|
||||||
EntryPoint::Executable { symbol, layout, .. } => {
|
EntryPoint::Executable {
|
||||||
roc_mono::ir::EntryPoint { symbol, layout }
|
exposed_to_host,
|
||||||
|
platform_path: _,
|
||||||
|
} => {
|
||||||
|
// TODO support multiple of these!
|
||||||
|
debug_assert_eq!(exposed_to_host.len(), 1);
|
||||||
|
let (symbol, layout) = exposed_to_host[0];
|
||||||
|
|
||||||
|
SingleEntryPoint { symbol, layout }
|
||||||
}
|
}
|
||||||
EntryPoint::Test => {
|
EntryPoint::Test => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|
|
@ -8,7 +8,7 @@ use roc_collections::all::MutSet;
|
||||||
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
||||||
use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult};
|
use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult};
|
||||||
use roc_load::{EntryPoint, ExecutionMode, LoadConfig, LoadMonomorphizedError, Threading};
|
use roc_load::{EntryPoint, ExecutionMode, LoadConfig, LoadMonomorphizedError, Threading};
|
||||||
use roc_mono::ir::{CrashTag, OptLevel};
|
use roc_mono::ir::{CrashTag, OptLevel, SingleEntryPoint};
|
||||||
use roc_packaging::cache::RocCacheDir;
|
use roc_packaging::cache::RocCacheDir;
|
||||||
use roc_region::all::LineInfo;
|
use roc_region::all::LineInfo;
|
||||||
use roc_reporting::report::{RenderTarget, DEFAULT_PALETTE};
|
use roc_reporting::report::{RenderTarget, DEFAULT_PALETTE};
|
||||||
|
@ -99,7 +99,6 @@ fn create_llvm_module<'a>(
|
||||||
use roc_load::MonomorphizedModule;
|
use roc_load::MonomorphizedModule;
|
||||||
let MonomorphizedModule {
|
let MonomorphizedModule {
|
||||||
procedures,
|
procedures,
|
||||||
entry_point,
|
|
||||||
interns,
|
interns,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
..
|
..
|
||||||
|
@ -238,9 +237,16 @@ fn create_llvm_module<'a>(
|
||||||
// platform to provide them.
|
// platform to provide them.
|
||||||
add_default_roc_externs(&env);
|
add_default_roc_externs(&env);
|
||||||
|
|
||||||
let entry_point = match entry_point {
|
let entry_point = match loaded.entry_point {
|
||||||
EntryPoint::Executable { symbol, layout, .. } => {
|
EntryPoint::Executable {
|
||||||
roc_mono::ir::EntryPoint { symbol, layout }
|
exposed_to_host,
|
||||||
|
platform_path: _,
|
||||||
|
} => {
|
||||||
|
// TODO support multiple of these!
|
||||||
|
debug_assert_eq!(exposed_to_host.len(), 1);
|
||||||
|
let (symbol, layout) = exposed_to_host[0];
|
||||||
|
|
||||||
|
SingleEntryPoint { symbol, layout }
|
||||||
}
|
}
|
||||||
EntryPoint::Test => {
|
EntryPoint::Test => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
@ -607,9 +613,6 @@ macro_rules! assert_llvm_evals_to {
|
||||||
CrashTag::User => panic!(r#"User crash with message: "{}""#, msg),
|
CrashTag::User => panic!(r#"User crash with message: "{}""#, msg),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// artificially extend the lifetime of `lib`
|
|
||||||
lib.close().unwrap();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
($src:expr, $expected:expr, $ty:ty) => {
|
($src:expr, $expected:expr, $ty:ty) => {
|
||||||
|
|
|
@ -85,7 +85,6 @@ fn collect_roc_definitions<'a>(object: &object::File<'a, &'a [u8]>) -> MutMap<St
|
||||||
// for expects
|
// for expects
|
||||||
"roc_mmap" => Some("mmap"),
|
"roc_mmap" => Some("mmap"),
|
||||||
"roc_getppid" => Some("getppid"),
|
"roc_getppid" => Some("getppid"),
|
||||||
"roc_send_signal" => Some("kill"),
|
|
||||||
"roc_shm_open" => Some("shm_open"),
|
"roc_shm_open" => Some("shm_open"),
|
||||||
|
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -234,8 +234,15 @@ fn mono_module_to_dylib<'a>(
|
||||||
add_default_roc_externs(&env);
|
add_default_roc_externs(&env);
|
||||||
|
|
||||||
let entry_point = match entry_point {
|
let entry_point = match entry_point {
|
||||||
EntryPoint::Executable { symbol, layout, .. } => {
|
EntryPoint::Executable {
|
||||||
roc_mono::ir::EntryPoint { symbol, layout }
|
exposed_to_host,
|
||||||
|
platform_path: _,
|
||||||
|
} => {
|
||||||
|
// TODO support multiple of these!
|
||||||
|
debug_assert_eq!(exposed_to_host.len(), 1);
|
||||||
|
let (symbol, layout) = exposed_to_host[0];
|
||||||
|
|
||||||
|
roc_mono::ir::SingleEntryPoint { symbol, layout }
|
||||||
}
|
}
|
||||||
EntryPoint::Test => {
|
EntryPoint::Test => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
use std::{os::unix::process::parent_id, sync::Arc};
|
use std::{
|
||||||
|
os::unix::process::parent_id,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, AtomicU32},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
use bumpalo::collections::Vec as BumpVec;
|
use bumpalo::collections::Vec as BumpVec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
@ -13,7 +19,7 @@ use roc_gen_llvm::{
|
||||||
run_roc_dylib,
|
run_roc_dylib,
|
||||||
};
|
};
|
||||||
use roc_intern::{GlobalInterner, SingleThreadedInterner};
|
use roc_intern::{GlobalInterner, SingleThreadedInterner};
|
||||||
use roc_load::{EntryPoint, Expectations, MonomorphizedModule};
|
use roc_load::{Expectations, MonomorphizedModule};
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
use roc_mono::{ir::OptLevel, layout::Layout};
|
use roc_mono::{ir::OptLevel, layout::Layout};
|
||||||
use roc_region::all::Region;
|
use roc_region::all::Region;
|
||||||
|
@ -105,6 +111,16 @@ impl<'a> ExpectMemory<'a> {
|
||||||
let mut result = RocCallResult::default();
|
let mut result = RocCallResult::default();
|
||||||
unsafe { set_shared_buffer((self.ptr, self.length), &mut result) };
|
unsafe { set_shared_buffer((self.ptr, self.length), &mut result) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn wait_for_child(&self, sigchld: Arc<AtomicBool>) -> ChildProcessMsg {
|
||||||
|
let sequence = ExpectSequence { ptr: self.ptr };
|
||||||
|
sequence.wait_for_child(sigchld)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
let mut sequence = ExpectSequence { ptr: self.ptr };
|
||||||
|
sequence.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
@ -591,16 +607,18 @@ struct ExpectSequence {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExpectSequence {
|
impl ExpectSequence {
|
||||||
const START_OFFSET: usize = 16;
|
const START_OFFSET: usize = 8 + 8 + 8;
|
||||||
|
|
||||||
const COUNT_INDEX: usize = 0;
|
const COUNT_INDEX: usize = 0;
|
||||||
const OFFSET_INDEX: usize = 1;
|
const OFFSET_INDEX: usize = 1;
|
||||||
|
const LOCK_INDEX: usize = 2;
|
||||||
|
|
||||||
fn new(ptr: *mut u8) -> Self {
|
fn new(ptr: *mut u8) -> Self {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = ptr as *mut usize;
|
let ptr = ptr as *mut usize;
|
||||||
std::ptr::write_unaligned(ptr.add(Self::COUNT_INDEX), 0);
|
std::ptr::write_unaligned(ptr.add(Self::COUNT_INDEX), 0);
|
||||||
std::ptr::write_unaligned(ptr.add(Self::OFFSET_INDEX), Self::START_OFFSET);
|
std::ptr::write_unaligned(ptr.add(Self::OFFSET_INDEX), Self::START_OFFSET);
|
||||||
|
std::ptr::write_unaligned(ptr.add(Self::LOCK_INDEX), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -611,11 +629,47 @@ impl ExpectSequence {
|
||||||
fn count_failures(&self) -> usize {
|
fn count_failures(&self) -> usize {
|
||||||
unsafe { *(self.ptr as *const usize).add(Self::COUNT_INDEX) }
|
unsafe { *(self.ptr as *const usize).add(Self::COUNT_INDEX) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn wait_for_child(&self, sigchld: Arc<AtomicBool>) -> ChildProcessMsg {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
let ptr = self.ptr as *const u32;
|
||||||
|
let atomic_ptr: *const AtomicU32 = unsafe { ptr.add(5).cast() };
|
||||||
|
let atomic = unsafe { &*atomic_ptr };
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if sigchld.load(Ordering::Relaxed) {
|
||||||
|
break ChildProcessMsg::Terminate;
|
||||||
|
}
|
||||||
|
|
||||||
|
match atomic.load(Ordering::Acquire) {
|
||||||
|
0 => std::hint::spin_loop(),
|
||||||
|
1 => break ChildProcessMsg::Expect,
|
||||||
|
2 => break ChildProcessMsg::Dbg,
|
||||||
|
n => panic!("invalid atomic value set by the child: {:#x}", n),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let ptr = self.ptr as *mut usize;
|
||||||
|
std::ptr::write_unaligned(ptr.add(Self::COUNT_INDEX), 0);
|
||||||
|
std::ptr::write_unaligned(ptr.add(Self::OFFSET_INDEX), Self::START_OFFSET);
|
||||||
|
std::ptr::write_unaligned(ptr.add(Self::LOCK_INDEX), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ChildProcessMsg {
|
||||||
|
Expect = 1,
|
||||||
|
Dbg = 2,
|
||||||
|
Terminate = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExpectFrame {
|
struct ExpectFrame {
|
||||||
region: Region,
|
region: Region,
|
||||||
module_id: ModuleId,
|
module_id: ModuleId,
|
||||||
|
|
||||||
start_offset: usize,
|
start_offset: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,8 +681,8 @@ impl ExpectFrame {
|
||||||
let module_id_bytes: [u8; 4] = unsafe { *(start.add(offset + 8).cast()) };
|
let module_id_bytes: [u8; 4] = unsafe { *(start.add(offset + 8).cast()) };
|
||||||
let module_id: ModuleId = unsafe { std::mem::transmute(module_id_bytes) };
|
let module_id: ModuleId = unsafe { std::mem::transmute(module_id_bytes) };
|
||||||
|
|
||||||
// skip to frame, 8 bytes for region, 4 for module id
|
// skip to frame
|
||||||
let start_offset = offset + 12;
|
let start_offset = offset + 8 + 4;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
region,
|
region,
|
||||||
|
@ -670,7 +724,6 @@ pub fn expect_mono_module_to_dylib<'a>(
|
||||||
let MonomorphizedModule {
|
let MonomorphizedModule {
|
||||||
toplevel_expects,
|
toplevel_expects,
|
||||||
procedures,
|
procedures,
|
||||||
entry_point,
|
|
||||||
interns,
|
interns,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
..
|
..
|
||||||
|
@ -708,13 +761,6 @@ pub fn expect_mono_module_to_dylib<'a>(
|
||||||
// platform to provide them.
|
// platform to provide them.
|
||||||
add_default_roc_externs(&env);
|
add_default_roc_externs(&env);
|
||||||
|
|
||||||
let opt_entry_point = match entry_point {
|
|
||||||
EntryPoint::Executable { symbol, layout, .. } => {
|
|
||||||
Some(roc_mono::ir::EntryPoint { symbol, layout })
|
|
||||||
}
|
|
||||||
EntryPoint::Test => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let capacity = toplevel_expects.pure.len() + toplevel_expects.fx.len();
|
let capacity = toplevel_expects.pure.len() + toplevel_expects.fx.len();
|
||||||
let mut expect_symbols = BumpVec::with_capacity_in(capacity, env.arena);
|
let mut expect_symbols = BumpVec::with_capacity_in(capacity, env.arena);
|
||||||
|
|
||||||
|
@ -726,7 +772,6 @@ pub fn expect_mono_module_to_dylib<'a>(
|
||||||
opt_level,
|
opt_level,
|
||||||
&expect_symbols,
|
&expect_symbols,
|
||||||
procedures,
|
procedures,
|
||||||
opt_entry_point,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let expects_fx = bumpalo::collections::Vec::from_iter_in(
|
let expects_fx = bumpalo::collections::Vec::from_iter_in(
|
||||||
|
|
|
@ -105,12 +105,6 @@ pub unsafe extern "C" fn roc_shm_open(
|
||||||
libc::shm_open(name, oflag, mode as libc::c_uint)
|
libc::shm_open(name, oflag, mode as libc::c_uint)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn roc_send_signal(pid: libc::pid_t, sig: libc::c_int) -> libc::c_int {
|
|
||||||
libc::kill(pid, sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_backtrace() {
|
fn print_backtrace() {
|
||||||
eprintln!("Here is the call stack that led to the crash:\n");
|
eprintln!("Here is the call stack that led to the crash:\n");
|
||||||
|
|
||||||
|
|
|
@ -97,9 +97,6 @@ fn roc_getppid_windows_stub() callconv(.C) c_int {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
|
|
||||||
return kill(pid, sig);
|
|
||||||
}
|
|
||||||
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
||||||
return shm_open(name, oflag, mode);
|
return shm_open(name, oflag, mode);
|
||||||
}
|
}
|
||||||
|
@ -111,7 +108,6 @@ comptime {
|
||||||
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||||
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||||
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
||||||
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
|
|
||||||
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,12 +101,6 @@ pub unsafe extern "C" fn roc_shm_open(
|
||||||
libc::shm_open(name, oflag, mode as libc::c_uint)
|
libc::shm_open(name, oflag, mode as libc::c_uint)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn roc_send_signal(pid: libc::pid_t, sig: libc::c_int) -> libc::c_int {
|
|
||||||
libc::kill(pid, sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn rust_main() -> i32 {
|
pub extern "C" fn rust_main() -> i32 {
|
||||||
let arg = env::args()
|
let arg = env::args()
|
||||||
|
|
|
@ -157,9 +157,6 @@ fn roc_getppid_windows_stub() callconv(.C) c_int {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
|
|
||||||
return kill(pid, sig);
|
|
||||||
}
|
|
||||||
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
||||||
return shm_open(name, oflag, mode);
|
return shm_open(name, oflag, mode);
|
||||||
}
|
}
|
||||||
|
@ -171,7 +168,6 @@ comptime {
|
||||||
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||||
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||||
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
||||||
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
|
|
||||||
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,9 +105,6 @@ fn roc_getppid_windows_stub() callconv(.C) c_int {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
|
|
||||||
return kill(pid, sig);
|
|
||||||
}
|
|
||||||
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
||||||
return shm_open(name, oflag, mode);
|
return shm_open(name, oflag, mode);
|
||||||
}
|
}
|
||||||
|
@ -119,7 +116,6 @@ comptime {
|
||||||
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||||
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||||
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
||||||
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
|
|
||||||
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,15 +36,6 @@ void* roc_memcpy(void* dest, const void* src, size_t n) {
|
||||||
|
|
||||||
void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); }
|
void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); }
|
||||||
|
|
||||||
|
|
||||||
int roc_send_signal(int pid, int sig) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
return kill(pid, sig);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int roc_shm_open(char* name, int oflag, int mode) {
|
int roc_shm_open(char* name, int oflag, int mode) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "1.64.0"
|
||||||
|
|
||||||
|
profile = "default"
|
||||||
|
|
||||||
|
components = [
|
||||||
|
# for usages of rust-analyzer or similar tools inside `nix develop`
|
||||||
|
"rust-src"
|
||||||
|
]
|
|
@ -83,12 +83,6 @@ pub unsafe extern "C" fn roc_shm_open(
|
||||||
libc::shm_open(name, oflag, mode as libc::c_uint)
|
libc::shm_open(name, oflag, mode as libc::c_uint)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn roc_send_signal(pid: libc::pid_t, sig: libc::c_int) -> libc::c_int {
|
|
||||||
libc::kill(pid, sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn rust_main() -> i32 {
|
pub extern "C" fn rust_main() -> i32 {
|
||||||
let mut roc_str = RocStr::default();
|
let mut roc_str = RocStr::default();
|
||||||
|
|
|
@ -88,9 +88,6 @@ fn roc_getppid_windows_stub() callconv(.C) c_int {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
|
|
||||||
return kill(pid, sig);
|
|
||||||
}
|
|
||||||
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
||||||
return shm_open(name, oflag, mode);
|
return shm_open(name, oflag, mode);
|
||||||
}
|
}
|
||||||
|
@ -102,7 +99,6 @@ comptime {
|
||||||
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||||
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||||
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
||||||
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
|
|
||||||
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,14 +30,6 @@ void* roc_memcpy(void* dest, const void* src, size_t n) {
|
||||||
|
|
||||||
void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); }
|
void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); }
|
||||||
|
|
||||||
int roc_send_signal(int pid, int sig) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
return kill(pid, sig);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int roc_shm_open(char* name, int oflag, int mode) {
|
int roc_shm_open(char* name, int oflag, int mode) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -62,12 +62,6 @@ pub unsafe extern "C" fn roc_shm_open(
|
||||||
libc::shm_open(name, oflag, mode as libc::c_uint)
|
libc::shm_open(name, oflag, mode as libc::c_uint)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn roc_send_signal(pid: libc::pid_t, sig: libc::c_int) -> libc::c_int {
|
|
||||||
libc::kill(pid, sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn rust_main() -> i32 {
|
pub extern "C" fn rust_main() -> i32 {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
|
@ -113,13 +107,10 @@ pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(input_dirname: &str, output_dirname: &str) -> Result<(), String> {
|
fn run(input_dirname: &str, output_dirname: &str) -> Result<(), String> {
|
||||||
|
let input_dir = strip_windows_prefix(
|
||||||
|
|
||||||
let input_dir =
|
|
||||||
strip_windows_prefix(
|
|
||||||
PathBuf::from(input_dirname)
|
PathBuf::from(input_dirname)
|
||||||
.canonicalize()
|
.canonicalize()
|
||||||
.map_err(|e| format!("{}: {}", input_dirname, e))?
|
.map_err(|e| format!("{}: {}", input_dirname, e))?,
|
||||||
);
|
);
|
||||||
|
|
||||||
let output_dir = {
|
let output_dir = {
|
||||||
|
@ -129,7 +120,7 @@ fn run(input_dirname: &str, output_dirname: &str) -> Result<(), String> {
|
||||||
}
|
}
|
||||||
strip_windows_prefix(
|
strip_windows_prefix(
|
||||||
dir.canonicalize()
|
dir.canonicalize()
|
||||||
.map_err(|e| format!("{}: {}", output_dirname, e))?
|
.map_err(|e| format!("{}: {}", output_dirname, e))?,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -157,7 +148,10 @@ fn run(input_dirname: &str, output_dirname: &str) -> Result<(), String> {
|
||||||
num_successes += 1;
|
num_successes += 1;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Failed to process file:\n\n ({:?})with error:\n\n {}", &input_file, e);
|
eprintln!(
|
||||||
|
"Failed to process file:\n\n ({:?})with error:\n\n {}",
|
||||||
|
&input_file, e
|
||||||
|
);
|
||||||
num_errors += 1;
|
num_errors += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +170,6 @@ fn run(input_dirname: &str, output_dirname: &str) -> Result<(), String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_file(input_dir: &Path, output_dir: &Path, input_file: &Path) -> Result<(), String> {
|
fn process_file(input_dir: &Path, output_dir: &Path, input_file: &Path) -> Result<(), String> {
|
||||||
|
|
||||||
match input_file.extension() {
|
match input_file.extension() {
|
||||||
Some(s) if s.eq("md".into()) => {}
|
Some(s) if s.eq("md".into()) => {}
|
||||||
_ => return Err("Only .md files are supported".into()),
|
_ => return Err("Only .md files are supported".into()),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
[toolchain]
|
[toolchain]
|
||||||
# How to update version:
|
# How to update version:
|
||||||
# - update `channel = "RUST_VERSION"`
|
# - update `channel = "RUST_VERSION"`
|
||||||
|
# - update `channel = "RUST_VERSION"` in examples/platform-switching/rust-platform
|
||||||
# - to update the nightly version:
|
# - to update the nightly version:
|
||||||
# - Find the latest nightly release that matches RUST_VERSION here: https://github.com/oxalica/rust-overlay/tree/master/manifests/nightly/2022
|
# - Find the latest nightly release that matches RUST_VERSION here: https://github.com/oxalica/rust-overlay/tree/master/manifests/nightly/2022
|
||||||
# - update `channel = "nightly-OLD_DATE"` below
|
# - update `channel = "nightly-OLD_DATE"` below
|
||||||
|
|
15
www/public/site.js
Normal file
15
www/public/site.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
const tutorialTocToggle = document.querySelector("#tutorial-toc-toggle");
|
||||||
|
|
||||||
|
document.querySelectorAll("#tutorial-toc li a").forEach((elem) => {
|
||||||
|
// Clicking any of the ToC links closes the ToC
|
||||||
|
elem.addEventListener("click", (event) => {
|
||||||
|
tutorialTocToggle.checked = false;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("keydown", (event) => {
|
||||||
|
// Escape closes the ToC
|
||||||
|
if (event.key == "Escape") {
|
||||||
|
tutorialTocToggle.checked = false;
|
||||||
|
}
|
||||||
|
});
|
|
@ -176,11 +176,11 @@ to use them for more than that.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section><h2 id="building-an-application"><a href="#building-an-application">Building an Application</a></h2>
|
<section><h2 id="building-an-application"><a href="#building-an-application">Building an Application</a></h2>
|
||||||
<p><code>URL imports don't work yet on MacOS x86_64 and NixOS. See <a href=github.com/roc-lang/roc/issues/4655>github issue 4655</a> for a workaround.</code></p>
|
<p><code>URL imports don't work yet on NixOS. Instead you'll have to clone roc-lang/basic-cli locally and use it like <a href="https://github.com/roc-lang/roc/issues/4655#issuecomment-1336215883">this</a>.</code></p>
|
||||||
<p>Let's move out of the REPL and create our first Roc application!</p>
|
<p>Let's move out of the REPL and create our first Roc application!</p>
|
||||||
<p>Make a file named <code>main.roc</code> and put this in it:</p>
|
<p>Make a file named <code>main.roc</code> and put this in it:</p>
|
||||||
<samp><span class="kw">app</span> <span class="str">"hello"</span>
|
<samp><span class="kw">app</span> <span class="str">"hello"</span>
|
||||||
<span class="kw">packages</span> <span class="brace">{</span> pf: <span class="str">"https://github.com/roc-lang/basic-cli/releases/download/0.1.1/zAoiC9xtQPHywYk350_b7ust04BmWLW00sjb9ZPtSQk.tar.br"</span> <span class="brace">}</span>
|
<span class="kw">packages</span> <span class="brace">{</span> pf: <span class="str">"https://github.com/roc-lang/basic-cli/releases/download/0.1.3/5SXwdW7rH8QAOnD71IkHcFxCmBEPtFSLAIkclPEgjHQ.tar.br"</span> <span class="brace">}</span>
|
||||||
<span class="kw">imports</span> <span class="brace">[</span>pf.Stdout<span class="brace">]</span>
|
<span class="kw">imports</span> <span class="brace">[</span>pf.Stdout<span class="brace">]</span>
|
||||||
<span class="kw">provides</span> <span class="brace">[</span>main<span class="brace">]</span> <span class="kw">to</span> pf
|
<span class="kw">provides</span> <span class="brace">[</span>main<span class="brace">]</span> <span class="kw">to</span> pf
|
||||||
|
|
||||||
|
@ -301,7 +301,6 @@ on the same line, although the convention is to use the original version's style
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<section><h2 id="debugging"><a href="#debugging">Debugging</a></h2>
|
<section><h2 id="debugging"><a href="#debugging">Debugging</a></h2>
|
||||||
<p><code>dbg doesn't work yet on MacOS x86_64</code></p>
|
|
||||||
<p><a href="https://en.wikipedia.org/wiki/Debugging#Techniques">Print debugging</a> is the most
|
<p><a href="https://en.wikipedia.org/wiki/Debugging#Techniques">Print debugging</a> is the most
|
||||||
common debugging technique in the history of programming, and Roc has a <code>dbg</code>
|
common debugging technique in the history of programming, and Roc has a <code>dbg</code>
|
||||||
keyword to facilitate it. Here's an example of how to use <code>dbg</code>:</p>
|
keyword to facilitate it. Here's an example of how to use <code>dbg</code>:</p>
|
||||||
|
@ -1243,7 +1242,7 @@ Roc compiler. That's why they're called "builtins!"</p>
|
||||||
<h2 id="the-app-module-header"><a href="#the-app-module-header">The app module header</a></h2>
|
<h2 id="the-app-module-header"><a href="#the-app-module-header">The app module header</a></h2>
|
||||||
<p>Let's take a closer look at the part of <code>main.roc</code> above the <code>main</code> def:</p>
|
<p>Let's take a closer look at the part of <code>main.roc</code> above the <code>main</code> def:</p>
|
||||||
<samp><span class="hljs-selector-tag">app</span> "<span class="hljs-selector-tag">hello</span>"
|
<samp><span class="hljs-selector-tag">app</span> "<span class="hljs-selector-tag">hello</span>"
|
||||||
<span class="kw">packages</span> <span class="brace">{</span> pf: <span class="str">"https://github.com/roc-lang/basic-cli/releases/download/0.1.1/zAoiC9xtQPHywYk350_b7ust04BmWLW00sjb9ZPtSQk.tar.br"</span> <span class="brace">}</span>
|
<span class="kw">packages</span> <span class="brace">{</span> pf: <span class="str">"https://github.com/roc-lang/basic-cli/releases/download/0.1.3/5SXwdW7rH8QAOnD71IkHcFxCmBEPtFSLAIkclPEgjHQ.tar.br"</span> <span class="brace">}</span>
|
||||||
<span class="hljs-selector-tag">imports</span> <span class="hljs-selector-attr">[pf.Stdout]</span>
|
<span class="hljs-selector-tag">imports</span> <span class="hljs-selector-attr">[pf.Stdout]</span>
|
||||||
<span class="hljs-selector-tag">provides</span> <span class="hljs-selector-tag">main</span> <span class="hljs-selector-tag">to</span> <span class="hljs-selector-tag">pf</span>
|
<span class="hljs-selector-tag">provides</span> <span class="hljs-selector-tag">main</span> <span class="hljs-selector-tag">to</span> <span class="hljs-selector-tag">pf</span>
|
||||||
</samp>
|
</samp>
|
||||||
|
@ -1256,7 +1255,7 @@ means when you run <code>roc dev</code>, the Roc compiler will build an executab
|
||||||
named <code>hello</code> (or <code>hello.exe</code> on Windows) and run it. You can also build the executable
|
named <code>hello</code> (or <code>hello.exe</code> on Windows) and run it. You can also build the executable
|
||||||
without running it by running <code>roc build</code>.</p>
|
without running it by running <code>roc build</code>.</p>
|
||||||
<p>The remaining lines all involve the <em>platform</em> this application is built on:</p>
|
<p>The remaining lines all involve the <em>platform</em> this application is built on:</p>
|
||||||
<samp><span class="kw">packages</span> <span class="brace">{</span> pf: <span class="str">"https://github.com/roc-lang/basic-cli/releases/download/0.1.1/zAoiC9xtQPHywYk350_b7ust04BmWLW00sjb9ZPtSQk.tar.br"</span> <span class="brace">}</span>
|
<samp><span class="kw">packages</span> <span class="brace">{</span> pf: <span class="str">"https://github.com/roc-lang/basic-cli/releases/download/0.1.3/5SXwdW7rH8QAOnD71IkHcFxCmBEPtFSLAIkclPEgjHQ.tar.br"</span> <span class="brace">}</span>
|
||||||
<span class="kw">imports</span> <span class="brace">[</span>pf.Stdout<span class="brace">]</span>
|
<span class="kw">imports</span> <span class="brace">[</span>pf.Stdout<span class="brace">]</span>
|
||||||
<span class="kw">provides</span> <span class="brace">[</span>main<span class="brace">]</span> <span class="kw">to</span> pf
|
<span class="kw">provides</span> <span class="brace">[</span>main<span class="brace">]</span> <span class="kw">to</span> pf
|
||||||
</samp>
|
</samp>
|
||||||
|
@ -1301,7 +1300,7 @@ we've been using up to this point as an example!</p>
|
||||||
<p>We'll use these four operations to learn about tasks.</p>
|
<p>We'll use these four operations to learn about tasks.</p>
|
||||||
<p>Let's start with a basic "Hello World" program.</p>
|
<p>Let's start with a basic "Hello World" program.</p>
|
||||||
<samp><span class="kw">app</span> <span class="str">"cli-tutorial"</span>
|
<samp><span class="kw">app</span> <span class="str">"cli-tutorial"</span>
|
||||||
<span class="kw">packages</span> <span class="brace">{</span> pf: <span class="str">"https://github.com/roc-lang/basic-cli/releases/download/0.1.1/zAoiC9xtQPHywYk350_b7ust04BmWLW00sjb9ZPtSQk.tar.br"</span> <span class="brace">}</span>
|
<span class="kw">packages</span> <span class="brace">{</span> pf: <span class="str">"https://github.com/roc-lang/basic-cli/releases/download/0.1.3/5SXwdW7rH8QAOnD71IkHcFxCmBEPtFSLAIkclPEgjHQ.tar.br"</span> <span class="brace">}</span>
|
||||||
imports [pf.Stdout]
|
imports [pf.Stdout]
|
||||||
provides [main] to pf
|
provides [main] to pf
|
||||||
|
|
||||||
|
@ -1326,7 +1325,7 @@ when it runs (hence the <code>*</code>).</p>
|
||||||
</samp>
|
</samp>
|
||||||
<p>Let's change <code>main</code> to read a line from <code>stdin</code>, and then print it back out again:</p>
|
<p>Let's change <code>main</code> to read a line from <code>stdin</code>, and then print it back out again:</p>
|
||||||
<samp>app <span class="str">"cli-tutorial"</span>
|
<samp>app <span class="str">"cli-tutorial"</span>
|
||||||
<span class="kw">packages</span> <span class="brace">{</span> pf: <span class="str">"https://github.com/roc-lang/basic-cli/releases/download/0.1.1/zAoiC9xtQPHywYk350_b7ust04BmWLW00sjb9ZPtSQk.tar.br"</span> <span class="brace">}</span>
|
<span class="kw">packages</span> <span class="brace">{</span> pf: <span class="str">"https://github.com/roc-lang/basic-cli/releases/download/0.1.3/5SXwdW7rH8QAOnD71IkHcFxCmBEPtFSLAIkclPEgjHQ.tar.br"</span> <span class="brace">}</span>
|
||||||
imports [pf.Stdout, pf.Stdin, pf.Task]
|
imports [pf.Stdout, pf.Stdin, pf.Task]
|
||||||
provides [main] to pf
|
provides [main] to pf
|
||||||
|
|
||||||
|
@ -1361,7 +1360,7 @@ the program isn't doing anything when we start it up:</p>
|
||||||
</samp>
|
</samp>
|
||||||
<p>This works, but we can make it a little nicer to read. Let's change it to the following:</p>
|
<p>This works, but we can make it a little nicer to read. Let's change it to the following:</p>
|
||||||
<samp>app <span class="str">"cli-tutorial"</span>
|
<samp>app <span class="str">"cli-tutorial"</span>
|
||||||
<span class="kw">packages</span> <span class="brace">{</span> pf: <span class="str">"https://github.com/roc-lang/basic-cli/releases/download/0.1.1/zAoiC9xtQPHywYk350_b7ust04BmWLW00sjb9ZPtSQk.tar.br"</span> <span class="brace">}</span>
|
<span class="kw">packages</span> <span class="brace">{</span> pf: <span class="str">"https://github.com/roc-lang/basic-cli/releases/download/0.1.3/5SXwdW7rH8QAOnD71IkHcFxCmBEPtFSLAIkclPEgjHQ.tar.br"</span> <span class="brace">}</span>
|
||||||
imports [pf.Stdout, pf.Stdin, pf.Task.{ await }]
|
imports [pf.Stdout, pf.Stdin, pf.Task.{ await }]
|
||||||
provides [main] to pf
|
provides [main] to pf
|
||||||
|
|
||||||
|
@ -1859,5 +1858,6 @@ For example, <code>[Foo Str][Bar Bool][Baz (List Str)]</code> is equivalent to <
|
||||||
</table>
|
</table>
|
||||||
</main>
|
</main>
|
||||||
<footer>Made by people who like to make nice things. © 2022</footer>
|
<footer>Made by people who like to make nice things. © 2022</footer>
|
||||||
|
<script src="/site.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue