Merge branch 'main' into simplify-refcount

This commit is contained in:
Luke Boswell 2025-01-09 10:18:00 +11:00 committed by GitHub
commit 37cd04c002
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
730 changed files with 22850 additions and 23870 deletions

View file

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -122,24 +122,24 @@ sudo apt-get install libz-dev libzstd-dev
### Zig ### Zig
**version: 0.11.0** **version: 0.13.0**
For any OS, you can use [`zigup`](https://github.com/marler8997/zigup) to manage zig installations. For any OS, you can use [`zigup`](https://github.com/marler8997/zigup) to manage zig installations.
If you prefer a package manager, you can try the following: If you prefer a package manager, you can try the following:
- MacOS: `brew install zig@0.11.0` - MacOS: `brew install zig`
- Systems with snap (such as Ubuntu): `snap install zig --classic --beta` - Systems with snap (such as Ubuntu): `snap install zig --classic --beta`
- Other systems: refer to the [zig documentation](https://github.com/ziglang/zig/wiki/Install-Zig-from-a-Package-Manager) - Other systems: refer to the [zig documentation](https://github.com/ziglang/zig/wiki/Install-Zig-from-a-Package-Manager)
If you want to install it manually, you can [download the binary](https://ziglang.org/download/#release-0.11.0) and place it on your PATH. If you want to install it manually, you can [download the binary](https://ziglang.org/download/#release-0.13.0) and place it on your PATH.
Apart from the binary, the archive contains a `lib` folder, which needs to be copied next to the binary. Apart from the binary, the archive contains a `lib` folder, which needs to be copied next to the binary.
> WINDOWS NOTE: when you unpack the Zig archive on windows, the result is nested in an extra directory. The instructions on the zig website will seem to not work. So, double-check that the path to zig executable does not include the same directory name twice. > WINDOWS NOTE: when you unpack the Zig archive on windows, the result is nested in an extra directory. The instructions on the zig website will seem to not work. So, double-check that the path to zig executable does not include the same directory name twice.
### LLVM ### LLVM
**version: 16.0.x** **version: 18.0.x**
See below for operating system specific installation instructions. See below for operating system specific installation instructions.
@ -167,10 +167,10 @@ chmod +x llvm.sh
``` ```
If you use this script, you'll need to add `clang` to your `PATH`. If you use this script, you'll need to add `clang` to your `PATH`.
By default, the script installs it as `clang-16`. You can address this with symlinks like so: By default, the script installs it as `clang-18`. You can address this with symlinks like so:
```sh ```sh
sudo ln -s /usr/bin/clang-16 /usr/bin/clang sudo ln -s /usr/bin/clang-18 /usr/bin/clang
``` ```
There are also alternative installation options at <http://releases.llvm.org/download.html> There are also alternative installation options at <http://releases.llvm.org/download.html>
@ -203,7 +203,7 @@ Add `export LLVM_SYS_180_PREFIX=/usr/lib/llvm-18` to your `~/.bashrc` or equival
For macOS, you can install LLVM 18 using `brew install llvm@18` and then adding For macOS, you can install LLVM 18 using `brew install llvm@18` and then adding
`$(brew --prefix llvm@18)/bin` to your `PATH`. You can confirm this worked by `$(brew --prefix llvm@18)/bin` to your `PATH`. You can confirm this worked by
running `llc --version` - it should mention "LLVM version 16.0.x" at the top. running `llc --version` - it should mention "LLVM version 18.x.x" at the top.
You may also need to manually specify a prefix env var like so: You may also need to manually specify a prefix env var like so:
```sh ```sh
@ -261,8 +261,8 @@ Create `~/.cargo/config.toml` if it does not exist and add this to it:
rustflags = ["-C", "link-arg=-fuse-ld=lld", "-C", "target-cpu=native"] rustflags = ["-C", "link-arg=-fuse-ld=lld", "-C", "target-cpu=native"]
``` ```
Then install `lld` version 16 (e.g. with `$ sudo apt-get install lld-16`) Then install `lld` version 18 (e.g. with `$ sudo apt-get install lld-18`)
and add make sure there's a `ld.lld` executable on your `PATH` which and add make sure there's a `ld.lld` executable on your `PATH` which
is symlinked to `lld-16`. is symlinked to `lld-18`.
That's it! Enjoy the faster builds. That's it! Enjoy the faster builds.

View file

@ -12,7 +12,8 @@ mkdir -p $1 $1/examples
mv target/release-with-lto/{roc,roc_language_server,lib} $1 mv target/release-with-lto/{roc,roc_language_server,lib} $1
mv LICENSE LEGAL_DETAILS $1 mv LICENSE LEGAL_DETAILS $1
mv examples/platform-switching $1/examples mv crates/cli/tests/platform-switching $1/examples
mv examples/README.md $1/examples
# temporary github.com/roc-lang/roc/pull/7231 # temporary github.com/roc-lang/roc/pull/7231
rm $1/examples/platform-switching/rocLovesRust.roc rm $1/examples/platform-switching/rocLovesRust.roc

View file

@ -190,7 +190,7 @@ pub enum FormatProblem {
pub fn format_src(arena: &Bump, src: &str, flags: MigrationFlags) -> Result<String, FormatProblem> { pub fn format_src(arena: &Bump, src: &str, flags: MigrationFlags) -> Result<String, FormatProblem> {
let ast = arena.alloc(parse_all(arena, src).unwrap_or_else(|e| { let ast = arena.alloc(parse_all(arena, src).unwrap_or_else(|e| {
user_error!("Unexpected parse failure when parsing this formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", src, e) user_error!("Unexpected parse failure when parsing this formatting:\n\n{src}\n\nParse error was:\n\n{:#?}\n\n", e)
})); }));
let mut buf = Buf::new_in(arena, flags); let mut buf = Buf::new_in(arena, flags);
fmt_all(&mut buf, ast); fmt_all(&mut buf, ast);
@ -305,7 +305,10 @@ main =
fn test_single_file_needs_reformatting() { fn test_single_file_needs_reformatting() {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let file_path = setup_test_file(dir.path(), "test1.roc", UNFORMATTED_ROC); let file_path = setup_test_file(dir.path(), "test1.roc", UNFORMATTED_ROC);
let flags = MigrationFlags::new(false); let flags = MigrationFlags {
snakify: false,
parens_and_commas: false,
};
let result = format_files(vec![file_path.clone()], FormatMode::CheckOnly, flags); let result = format_files(vec![file_path.clone()], FormatMode::CheckOnly, flags);
assert!(result.is_err()); assert!(result.is_err());
@ -325,7 +328,10 @@ main =
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let file1 = setup_test_file(dir.path(), "test1.roc", UNFORMATTED_ROC); let file1 = setup_test_file(dir.path(), "test1.roc", UNFORMATTED_ROC);
let file2 = setup_test_file(dir.path(), "test2.roc", UNFORMATTED_ROC); let file2 = setup_test_file(dir.path(), "test2.roc", UNFORMATTED_ROC);
let flags = MigrationFlags::new(false); let flags = MigrationFlags {
snakify: false,
parens_and_commas: false,
};
let result = format_files(vec![file1, file2], FormatMode::CheckOnly, flags); let result = format_files(vec![file1, file2], FormatMode::CheckOnly, flags);
assert!(result.is_err()); assert!(result.is_err());
@ -339,7 +345,10 @@ main =
fn test_no_files_need_reformatting() { fn test_no_files_need_reformatting() {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let file_path = setup_test_file(dir.path(), "formatted.roc", FORMATTED_ROC); let file_path = setup_test_file(dir.path(), "formatted.roc", FORMATTED_ROC);
let flags = MigrationFlags::new(false); let flags = MigrationFlags {
snakify: false,
parens_and_commas: false,
};
let result = format_files(vec![file_path], FormatMode::CheckOnly, flags); let result = format_files(vec![file_path], FormatMode::CheckOnly, flags);
assert!(result.is_ok()); assert!(result.is_ok());
@ -353,7 +362,10 @@ main =
let file_formatted = setup_test_file(dir.path(), "formatted.roc", FORMATTED_ROC); let file_formatted = setup_test_file(dir.path(), "formatted.roc", FORMATTED_ROC);
let file1_unformated = setup_test_file(dir.path(), "test1.roc", UNFORMATTED_ROC); let file1_unformated = setup_test_file(dir.path(), "test1.roc", UNFORMATTED_ROC);
let file2_unformated = setup_test_file(dir.path(), "test2.roc", UNFORMATTED_ROC); let file2_unformated = setup_test_file(dir.path(), "test2.roc", UNFORMATTED_ROC);
let flags = MigrationFlags::new(false); let flags = MigrationFlags {
snakify: false,
parens_and_commas: false,
};
let result = format_files( let result = format_files(
vec![file_formatted, file1_unformated, file2_unformated], vec![file_formatted, file1_unformated, file2_unformated],

View file

@ -89,6 +89,7 @@ pub const FLAG_PP_HOST: &str = "host";
pub const FLAG_PP_PLATFORM: &str = "platform"; pub const FLAG_PP_PLATFORM: &str = "platform";
pub const FLAG_PP_DYLIB: &str = "lib"; pub const FLAG_PP_DYLIB: &str = "lib";
pub const FLAG_MIGRATE: &str = "migrate"; pub const FLAG_MIGRATE: &str = "migrate";
pub const FLAG_DOCS_ROOT: &str = "root-dir";
pub const VERSION: &str = env!("ROC_VERSION"); pub const VERSION: &str = env!("ROC_VERSION");
const DEFAULT_GENERATED_DOCS_DIR: &str = "generated-docs"; const DEFAULT_GENERATED_DOCS_DIR: &str = "generated-docs";
@ -184,6 +185,12 @@ pub fn build_app() -> Command {
.num_args(0..) .num_args(0..)
.allow_hyphen_values(true); .allow_hyphen_values(true);
let flag_docs_root_dir = Arg::new(FLAG_DOCS_ROOT)
.long(FLAG_DOCS_ROOT)
.help("Set a root directory path to be used as a prefix for URL links in the generated documentation files.")
.value_parser(value_parser!(Option<String>))
.required(false);
let build_target_values_parser = let build_target_values_parser =
PossibleValuesParser::new(Target::iter().map(Into::<&'static str>::into)); PossibleValuesParser::new(Target::iter().map(Into::<&'static str>::into));
@ -405,6 +412,7 @@ pub fn build_app() -> Command {
.required(false) .required(false)
.default_value(DEFAULT_ROC_FILENAME), .default_value(DEFAULT_ROC_FILENAME),
) )
.arg(flag_docs_root_dir)
) )
.subcommand(Command::new(CMD_GLUE) .subcommand(Command::new(CMD_GLUE)
.about("Generate glue code between a platform's Roc API and its host language") .about("Generate glue code between a platform's Roc API and its host language")

View file

@ -5,10 +5,10 @@ use roc_build::program::{check_file, CodeGenBackend};
use roc_cli::{ use roc_cli::{
build_app, default_linking_strategy, format_files, format_src, test, BuildConfig, FormatMode, build_app, default_linking_strategy, format_files, format_src, test, BuildConfig, FormatMode,
CMD_BUILD, CMD_CHECK, CMD_DEV, CMD_DOCS, CMD_FORMAT, CMD_GLUE, CMD_PREPROCESS_HOST, CMD_REPL, CMD_BUILD, CMD_CHECK, CMD_DEV, CMD_DOCS, CMD_FORMAT, CMD_GLUE, CMD_PREPROCESS_HOST, CMD_REPL,
CMD_RUN, CMD_TEST, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_DEV, FLAG_LIB, FLAG_MAIN, CMD_RUN, CMD_TEST, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_DEV, FLAG_DOCS_ROOT,
FLAG_MIGRATE, FLAG_NO_COLOR, FLAG_NO_HEADER, FLAG_NO_LINK, FLAG_OUTPUT, FLAG_PP_DYLIB, FLAG_LIB, FLAG_MAIN, FLAG_MIGRATE, FLAG_NO_COLOR, FLAG_NO_HEADER, FLAG_NO_LINK, FLAG_OUTPUT,
FLAG_PP_HOST, FLAG_PP_PLATFORM, FLAG_STDIN, FLAG_STDOUT, FLAG_TARGET, FLAG_TIME, FLAG_VERBOSE, FLAG_PP_DYLIB, FLAG_PP_HOST, FLAG_PP_PLATFORM, FLAG_STDIN, FLAG_STDOUT, FLAG_TARGET, FLAG_TIME,
GLUE_DIR, GLUE_SPEC, ROC_FILE, VERSION, FLAG_VERBOSE, GLUE_DIR, GLUE_SPEC, ROC_FILE, VERSION,
}; };
use roc_docs::generate_docs_html; use roc_docs::generate_docs_html;
use roc_error_macros::user_error; use roc_error_macros::user_error;
@ -324,7 +324,25 @@ fn main() -> io::Result<()> {
let root_path = matches.get_one::<PathBuf>(ROC_FILE).unwrap(); let root_path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
let out_dir = matches.get_one::<OsString>(FLAG_OUTPUT).unwrap(); let out_dir = matches.get_one::<OsString>(FLAG_OUTPUT).unwrap();
generate_docs_html(root_path.to_owned(), out_dir.as_ref()); let maybe_root_dir: Option<String> = {
if let Ok(root_dir) = std::env::var("ROC_DOCS_URL_ROOT") {
// if the env var is set, it should override the flag for now
// TODO -- confirm we no longer need this and remove
// once docs are migrated to individual repositories and not roc website
Some(root_dir)
} else {
matches
.get_one::<Option<String>>(FLAG_DOCS_ROOT)
.unwrap_or(&None)
.clone()
}
};
generate_docs_html(
root_path.to_owned(),
out_dir.as_ref(),
maybe_root_dir.clone(),
);
Ok(0) Ok(0)
} }
@ -340,7 +358,10 @@ fn main() -> io::Result<()> {
false => FormatMode::WriteToFile, false => FormatMode::WriteToFile,
} }
}; };
let flags = MigrationFlags::new(migrate); let flags = MigrationFlags {
snakify: migrate,
parens_and_commas: migrate,
};
if from_stdin && matches!(format_mode, FormatMode::WriteToFile) { if from_stdin && matches!(format_mode, FormatMode::WriteToFile) {
eprintln!("When using the --stdin flag, either the --check or the --stdout flag must also be specified. (Otherwise, it's unclear what filename to write to!)"); eprintln!("When using the --stdin flag, either the --check or the --stdout flag must also be specified. (Otherwise, it's unclear what filename to write to!)");

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because one or more lines are too long

View file

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

View file

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

View file

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

View file

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

View file

@ -55,7 +55,7 @@ mod cli_tests {
// pre-build the platform // pre-build the platform
std::process::Command::new("bash") std::process::Command::new("bash")
.arg(file_from_root( .arg(file_from_root(
"examples/platform-switching/rust-platform", "crates/cli/tests/platform-switching/rust-platform",
"build.sh", "build.sh",
)) ))
.status() .status()
@ -63,7 +63,7 @@ mod cli_tests {
let cli_build = ExecCli::new( let cli_build = ExecCli::new(
roc_cli::CMD_DEV, roc_cli::CMD_DEV,
file_from_root("examples/platform-switching", "rocLovesRust.roc"), file_from_root("crates/cli/tests/platform-switching", "rocLovesRust.roc"),
); );
let expected_output = "Roc <3 Rust!\n"; let expected_output = "Roc <3 Rust!\n";
@ -80,7 +80,7 @@ mod cli_tests {
let cli_build = ExecCli::new( let cli_build = ExecCli::new(
CMD_BUILD, CMD_BUILD,
file_from_root("examples/platform-switching", "rocLovesZig.roc"), file_from_root("crates/cli/tests/platform-switching", "rocLovesZig.roc"),
) )
.arg(BUILD_HOST_FLAG) .arg(BUILD_HOST_FLAG)
.arg(SUPPRESS_BUILD_HOST_WARNING_FLAG); .arg(SUPPRESS_BUILD_HOST_WARNING_FLAG);
@ -104,7 +104,10 @@ mod cli_tests {
// so let's just check it for now // so let's just check it for now
let cli_check = ExecCli::new( let cli_check = ExecCli::new(
CMD_CHECK, CMD_CHECK,
file_from_root("examples/platform-switching", "rocLovesWebAssembly.roc"), file_from_root(
"crates/cli/tests/platform-switching",
"rocLovesWebAssembly.roc",
),
); );
let cli_check_out = cli_check.run(); let cli_check_out = cli_check.run();
@ -160,34 +163,6 @@ mod cli_tests {
); );
} }
// TODO: write a new test once mono bugs are resolved in investigation
// Encountering this TODO years later, I presume the new test should test the execution, not just roc check.
#[test]
#[cfg(not(debug_assertions))] // https://github.com/roc-lang/roc/issues/4806 - later observation: this issue is closed but the tests still hangs in debug mode
fn check_virtual_dom_server() {
let cli_check = ExecCli::new(
CMD_CHECK,
file_from_root("examples/virtual-dom-wip", "example-server.roc"),
);
let cli_check_out = cli_check.run();
cli_check_out.assert_clean_success();
}
// TODO: write a new test once mono bugs are resolved in investigation
// Encountering this TODO years later, I presume the new test should test the execution, not just roc check.
#[test]
#[cfg(not(debug_assertions))] // https://github.com/roc-lang/roc/issues/4806 - later observation: this issue is closed but the tests still hangs in debug mode
fn check_virtual_dom_client() {
let cli_check = ExecCli::new(
CMD_CHECK,
file_from_root("examples/virtual-dom-wip", "example-client.roc"),
);
let cli_check_out = cli_check.run();
cli_check_out.assert_clean_success();
}
#[test] #[test]
#[cfg_attr(windows, ignore)] #[cfg_attr(windows, ignore)]
// tea = The Elm Architecture // tea = The Elm Architecture
@ -213,7 +188,8 @@ mod cli_tests {
} }
#[test] #[test]
#[cfg_attr(windows, ignore)] // #[cfg_attr(windows, ignore)]
#[ignore]
fn false_interpreter() { fn false_interpreter() {
let cli_build = ExecCli::new( let cli_build = ExecCli::new(
CMD_BUILD, CMD_BUILD,
@ -877,7 +853,7 @@ mod cli_tests {
), ),
); );
let expected_output = "(@Community {friends: [{2}, {2}, {0, 1}], people: [(@Person {age: 27, favoriteColor: Blue, firstName: \"John\", hasBeard: Bool.true, lastName: \"Smith\"}), (@Person {age: 47, favoriteColor: Green, firstName: \"Debby\", hasBeard: Bool.false, lastName: \"Johnson\"}), (@Person {age: 33, favoriteColor: (RGB (255, 255, 0)), firstName: \"Jane\", hasBeard: Bool.false, lastName: \"Doe\"})]})\n"; let expected_output = "(@Community {friends: [{2}, {2}, {0, 1}], people: [(@Person {age: 27, favorite_color: Blue, first_name: \"John\", has_beard: Bool.true, last_name: \"Smith\"}), (@Person {age: 47, favorite_color: Green, first_name: \"Debby\", has_beard: Bool.false, last_name: \"Johnson\"}), (@Person {age: 33, favorite_color: (RGB (255, 255, 0)), first_name: \"Jane\", has_beard: Bool.false, last_name: \"Doe\"})]})\n";
cli_build.full_check_build_and_run( cli_build.full_check_build_and_run(
expected_output, expected_output,
@ -894,7 +870,7 @@ mod cli_tests {
build_platform_host(); build_platform_host();
let cli_build = ExecCli::new( let cli_build = ExecCli::new(
roc_cli::CMD_DEV, roc_cli::CMD_BUILD,
file_from_root("crates/cli/tests/test-projects/effectful", "form.roc"), file_from_root("crates/cli/tests/test-projects/effectful", "form.roc"),
); );
@ -914,14 +890,14 @@ mod cli_tests {
fn effectful_hello() { fn effectful_hello() {
build_platform_host(); build_platform_host();
let cli_build = ExecCli::new( let cli_dev = ExecCli::new(
roc_cli::CMD_DEV, roc_cli::CMD_DEV,
file_from_root("crates/cli/tests/test-projects/effectful/", "hello.roc"), file_from_root("crates/cli/tests/test-projects/effectful/", "hello.roc"),
); );
let expected_out = "I'm an effect 👻\n"; let expected_out = "I'm an effect 👻\n";
cli_build.run().assert_clean_stdout(expected_out); cli_dev.run().assert_clean_stdout(expected_out);
} }
#[test] #[test]
@ -929,14 +905,14 @@ mod cli_tests {
fn effectful_loops() { fn effectful_loops() {
build_platform_host(); build_platform_host();
let cli_build = ExecCli::new( let cli_dev = ExecCli::new(
roc_cli::CMD_DEV, roc_cli::CMD_DEV,
file_from_root("crates/cli/tests/test-projects/effectful/", "loops.roc"), file_from_root("crates/cli/tests/test-projects/effectful/", "loops.roc"),
); );
let expected_out = "Lu\nMarce\nJoaquin\nChloé\nMati\nPedro\n"; let expected_out = "Lu\nMarce\nJoaquin\nChloé\nMati\nPedro\n";
cli_build.run().assert_clean_stdout(expected_out); cli_dev.run().assert_clean_stdout(expected_out);
} }
#[test] #[test]
@ -944,7 +920,7 @@ mod cli_tests {
fn effectful_untyped_passed_fx() { fn effectful_untyped_passed_fx() {
build_platform_host(); build_platform_host();
let cli_build = ExecCli::new( let cli_dev = ExecCli::new(
roc_cli::CMD_DEV, roc_cli::CMD_DEV,
file_from_root( file_from_root(
"crates/cli/tests/test-projects/effectful/", "crates/cli/tests/test-projects/effectful/",
@ -954,7 +930,7 @@ mod cli_tests {
let expected_out = "Before hello\nHello, World!\nAfter hello\n"; let expected_out = "Before hello\nHello, World!\nAfter hello\n";
cli_build.run().assert_clean_stdout(expected_out); cli_dev.run().assert_clean_stdout(expected_out);
} }
#[test] #[test]
@ -962,7 +938,7 @@ mod cli_tests {
fn effectful_ignore_result() { fn effectful_ignore_result() {
build_platform_host(); build_platform_host();
let cli_build = ExecCli::new( let cli_dev = ExecCli::new(
roc_cli::CMD_DEV, roc_cli::CMD_DEV,
file_from_root( file_from_root(
"crates/cli/tests/test-projects/effectful/", "crates/cli/tests/test-projects/effectful/",
@ -972,7 +948,7 @@ mod cli_tests {
let expected_out = "I asked for input and I ignored it. Deal with it! 😎\n"; let expected_out = "I asked for input and I ignored it. Deal with it! 😎\n";
cli_build.run().assert_clean_stdout(expected_out); cli_dev.run().assert_clean_stdout(expected_out);
} }
#[test] #[test]
@ -981,14 +957,14 @@ mod cli_tests {
build_platform_host(); build_platform_host();
let cli_build = ExecCli::new( let cli_build = ExecCli::new(
roc_cli::CMD_DEV, roc_cli::CMD_BUILD,
file_from_root( file_from_root(
"crates/cli/tests/test-projects/effectful", "crates/cli/tests/test-projects/effectful",
"suffixed_record_field.roc", "suffixed_record_field.roc",
), ),
); );
let expected_output = "notEffectful: hardcoded\neffectful: from stdin\n"; let expected_output = "not_effectful: hardcoded\neffectful: from stdin\n";
cli_build.check_build_and_run( cli_build.check_build_and_run(
expected_output, expected_output,
@ -1004,7 +980,7 @@ mod cli_tests {
build_platform_host(); build_platform_host();
let cli_build = ExecCli::new( let cli_build = ExecCli::new(
roc_cli::CMD_DEV, roc_cli::CMD_BUILD,
file_from_root("crates/cli/tests/test-projects/effectful", "on_err.roc"), file_from_root("crates/cli/tests/test-projects/effectful", "on_err.roc"),
); );
@ -1019,7 +995,7 @@ mod cli_tests {
build_platform_host(); build_platform_host();
let cli_build = ExecCli::new( let cli_build = ExecCli::new(
roc_cli::CMD_DEV, roc_cli::CMD_BUILD,
file_from_root( file_from_root(
"crates/cli/tests/test-projects/effectful", "crates/cli/tests/test-projects/effectful",
"for_each_try.roc", "for_each_try.roc",

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,54 +1,54 @@
app [quicksort] { pf: platform "quicksort-platform/main.roc" } app [quicksort] { pf: platform "quicksort-platform/main.roc" }
quicksort = \originalList -> quicksort = \original_list ->
n = List.len originalList n = List.len(original_list)
quicksortHelp originalList 0 (n - 1) quicksort_help(original_list, 0, (n - 1))
quicksortHelp : List (Num a), U64, U64 -> List (Num a) quicksort_help : List (Num a), U64, U64 -> List (Num a)
quicksortHelp = \list, low, high -> quicksort_help = \list, low, high ->
if low < high then if low < high then
when partition low high list is when partition(low, high, list) is
Pair partitionIndex partitioned -> Pair(partition_index, partitioned) ->
partitioned partitioned
|> quicksortHelp low (partitionIndex - 1) |> quicksort_help(low, (partition_index - 1))
|> quicksortHelp (partitionIndex + 1) high |> quicksort_help((partition_index + 1), high)
else else
list list
partition : U64, U64, List (Num a) -> [Pair U64 (List (Num a))] partition : U64, U64, List (Num a) -> [Pair U64 (List (Num a))]
partition = \low, high, initialList -> partition = \low, high, initial_list ->
when List.get initialList high is when List.get(initial_list, high) is
Ok pivot -> Ok(pivot) ->
when partitionHelp low low initialList high pivot is when partition_help(low, low, initial_list, high, pivot) is
Pair newI newList -> Pair(new_i, new_list) ->
Pair newI (swap newI high newList) Pair(new_i, swap(new_i, high, new_list))
Err _ -> Err(_) ->
Pair low initialList Pair(low, initial_list)
partitionHelp : U64, U64, List (Num c), U64, Num c -> [Pair U64 (List (Num c))] partition_help : U64, U64, List (Num c), U64, Num c -> [Pair U64 (List (Num c))]
partitionHelp = \i, j, list, high, pivot -> partition_help = \i, j, list, high, pivot ->
if j < high then if j < high then
when List.get list j is when List.get(list, j) is
Ok value -> Ok(value) ->
if value <= pivot then if value <= pivot then
partitionHelp (i + 1) (j + 1) (swap i j list) high pivot partition_help((i + 1), (j + 1), swap(i, j, list), high, pivot)
else else
partitionHelp i (j + 1) list high pivot partition_help(i, (j + 1), list, high, pivot)
Err _ -> Err(_) ->
Pair i list Pair(i, list)
else else
Pair i list Pair(i, list)
swap : U64, U64, List a -> List a swap : U64, U64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair(List.get(list, i), List.get(list, j)) is
Pair (Ok atI) (Ok atJ) -> Pair(Ok(at_i), Ok(at_j)) ->
list list
|> List.set i atJ |> List.set(i, at_j)
|> List.set j atI |> List.set(j, at_i)
_ -> _ ->
# to prevent a decrement on list # to prevent a decrement on list

View file

@ -1,10 +1,10 @@
module [ module [
Community, Community,
empty, empty,
addPerson, add_person,
addFriend, add_friend,
Person, Person,
walkFriendNames, walk_friend_names,
] ]
## Datatype representing a community for demonstration purposes in inspect-gui.roc and inspect-logging.roc ## Datatype representing a community for demonstration purposes in inspect-gui.roc and inspect-logging.roc
@ -16,11 +16,11 @@ Community := {
implements [Inspect] implements [Inspect]
Person := { Person := {
firstName : Str, first_name : Str,
lastName : Str, last_name : Str,
age : U8, age : U8,
hasBeard : Bool, has_beard : Bool,
favoriteColor : Color, favorite_color : Color,
} }
implements [Inspect] implements [Inspect]
@ -31,52 +31,52 @@ Color : [
RGB (U8, U8, U8), RGB (U8, U8, U8),
] ]
empty = @Community { people: [], friends: [] } empty = @Community({ people: [], friends: [] })
addPerson = \@Community { people, friends }, person -> add_person = \@Community({ people, friends }), person ->
@Community { @Community({
people: List.append people (@Person person), people: List.append(people, @Person(person)),
friends: List.append friends (Set.empty {}), friends: List.append(friends, Set.empty({})),
} })
addFriend = \@Community { people, friends }, from, to -> add_friend = \@Community({ people, friends }), from, to ->
when (List.get friends from, List.get friends to) is when (List.get(friends, from), List.get(friends, to)) is
(Ok fromSet, Ok toSet) -> (Ok(from_set), Ok(to_set)) ->
@Community { @Community({
people, people,
friends: friends friends: friends
|> List.set from (Set.insert fromSet to) |> List.set(from, Set.insert(from_set, to))
|> List.set to (Set.insert toSet from), |> List.set(to, Set.insert(to_set, from)),
} })
_ -> _ ->
@Community { people, friends } @Community({ people, friends })
walkFriendNames : Community, state, (state, Str, Set Str -> state) -> state walk_friend_names : Community, state, (state, Str, Set Str -> state) -> state
walkFriendNames = \@Community { people, friends }, s0, nextFn -> walk_friend_names = \@Community({ people, friends }), s0, next_fn ->
(out, _) = (out, _) =
List.walk friends (s0, 0) \(s1, id), friendSet -> List.walk(friends, (s0, 0), \(s1, id), friend_set ->
(@Person person) = @Person(person) =
when List.get people id is when List.get(people, id) is
Ok v -> v Ok(v) -> v
Err _ -> crash "Unknown Person" Err(_) -> crash("Unknown Person")
personName = person_name =
person.firstName person.first_name
|> Str.concat " " |> Str.concat(" ")
|> Str.concat person.lastName |> Str.concat(person.last_name)
friendNames = friend_names =
Set.walk friendSet (Set.empty {}) \friendsSet, friendId -> Set.walk(friend_set, Set.empty({}), \friends_set, friend_id ->
(@Person friend) = @Person(friend) =
when List.get people friendId is when List.get(people, friend_id) is
Ok v -> v Ok(v) -> v
Err _ -> crash "Unknown Person" Err(_) -> crash("Unknown Person")
friendName = friend_name =
friend.firstName friend.first_name
|> Str.concat " " |> Str.concat(" ")
|> Str.concat friend.lastName |> Str.concat(friend.last_name)
Set.insert friendsSet friendName Set.insert(friends_set, friend_name))
(nextFn s1 personName friendNames, id + 1) (next_fn(s1, person_name, friend_names), id + 1))
out out

View file

@ -3,19 +3,19 @@ app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/downlo
import pf.Stdout import pf.Stdout
main = main =
multipleIn = multiple_in =
{ sequential <- { sequential <-
a: Task.ok 123, a: Task.ok(123),
b: Task.ok "abc", b: Task.ok("abc"),
c: Task.ok [123], c: Task.ok([123]),
_d: Task.ok ["abc"], _d: Task.ok(["abc"]),
_: Task.ok (Dict.single "a" "b"), _: Task.ok(Dict.single("a", "b")),
}! }!
Stdout.line! "For multiple tasks: $(Inspect.toStr multipleIn)" Stdout.line!("For multiple tasks: $(Inspect.to_str(multiple_in))")
sequential : Task a err, Task b err, (a, b -> c) -> Task c err sequential : Task a err, Task b err, (a, b -> c) -> Task c err
sequential = \firstTask, secondTask, mapper -> sequential = \first_task, second_task, mapper ->
first = firstTask! first = first_task!
second = secondTask! second = second_task!
Task.ok (mapper first second) Task.ok(mapper(first, second))

View file

@ -3,33 +3,33 @@ app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
import pf.Effect import pf.Effect
main! : {} => {} main! : {} => {}
main! = \{} -> tick! {} main! = \{} -> tick!({})
tick! = \{} -> tick! = \{} ->
line = Effect.getLine! {} line = Effect.get_line!({})
if !(Str.isEmpty line) then if !(Str.is_empty(line)) then
Effect.putLine! (echo line) Effect.put_line!(echo(line))
else else
Effect.putLine! "Received no input." Effect.put_line!("Received no input.")
echo : Str -> Str echo : Str -> Str
echo = \shout -> echo = \shout ->
silence = \length -> List.repeat ' ' length silence = \length -> List.repeat(' ', length)
shout shout
|> Str.toUtf8 |> Str.to_utf8
|> List.mapWithIndex \_, i -> |> List.map_with_index(\_, i ->
length = (List.len (Str.toUtf8 shout) - i) length = (List.len(Str.to_utf8(shout)) - i)
phrase = (List.splitAt (Str.toUtf8 shout) length).before phrase = (List.split_at(Str.to_utf8(shout), length)).before
List.concat (silence (if i == 0 then 2 * length else length)) phrase List.concat(silence((if i == 0 then 2 * length else length)), phrase))
|> List.join |> List.join
|> Str.fromUtf8 |> Str.from_utf8
|> Result.withDefault "" |> Result.with_default("")
expect expect
message = "hello!" message = "hello!"
echoedMessage = echo message echoed_message = echo(message)
echoedMessage == " hello! hello hell hel he h" echoed_message == " hello! hello hell hel he h"

View file

@ -4,20 +4,19 @@ import pf.Effect
main! : {} => {} main! : {} => {}
main! = \{} -> main! = \{} ->
good = [0, 2, 4] |> List.forEachTry! validate! good = [0, 2, 4] |> List.for_each_try!(validate!)
expect good == Ok {} expect good == Ok({})
bad = [6, 8, 9, 10] |> List.forEachTry! validate! bad = [6, 8, 9, 10] |> List.for_each_try!(validate!)
expect bad == Err 9 expect bad == Err(9)
{} {}
validate! : U32 => Result {} U32 validate! : U32 => Result {} U32
validate! = \x -> validate! = \x ->
if Num.isEven x then if Num.is_even(x) then
Effect.putLine! "✅ $(Num.toStr x)" Effect.put_line!("✅ $(Num.to_str(x))")
Ok {} Ok({})
else else
Effect.putLine! "$(Num.toStr x) is not even! ABORT!" Effect.put_line!("$(Num.to_str(x)) is not even! ABORT!")
Err x Err(x)

View file

@ -4,24 +4,24 @@ import pf.Effect
main! : {} => {} main! : {} => {}
main! = \{} -> main! = \{} ->
first = ask! "What's your first name?" first = ask!("What's your first name?")
last = ask! "What's your last name?" last = ask!("What's your last name?")
Effect.putLine! "\nHi, $(first) $(last)!\n" Effect.put_line!("\nHi, $(first) $(last)!\n")
when Str.toU8 (ask! "How old are you?") is when Str.to_u8(ask!("How old are you?")) is
Err InvalidNumStr -> Err(InvalidNumStr) ->
Effect.putLine! "Enter a valid number" Effect.put_line!("Enter a valid number")
Ok age if age >= 18 -> Ok(age) if age >= 18 ->
Effect.putLine! "\nNice! You can vote!" Effect.put_line!("\nNice! You can vote!")
Ok age -> Ok(age) ->
Effect.putLine! "\nYou'll be able to vote in $(Num.toStr (18 - age)) years" Effect.put_line!("\nYou'll be able to vote in $(Num.to_str((18 - age))) years")
Effect.putLine! "\nBye! 👋" Effect.put_line!("\nBye! 👋")
ask! : Str => Str ask! : Str => Str
ask! = \question -> ask! = \question ->
Effect.putLine! question Effect.put_line!(question)
Effect.getLine! {} Effect.get_line!({})

View file

@ -4,4 +4,4 @@ import pf.Effect
main! : {} => {} main! : {} => {}
main! = \{} -> main! = \{} ->
Effect.putLine! "I'm an effect 👻" Effect.put_line!("I'm an effect 👻")

View file

@ -4,5 +4,5 @@ import pf.Effect
main! : {} => {} main! : {} => {}
main! = \{} -> main! = \{} ->
_ = Effect.getLine! {} _ = Effect.get_line!({})
Effect.putLine! "I asked for input and I ignored it. Deal with it! 😎" Effect.put_line!("I asked for input and I ignored it. Deal with it! 😎")

View file

@ -8,28 +8,28 @@ import Community
main! = \{} -> main! = \{} ->
Community.empty Community.empty
|> Community.addPerson { |> Community.add_person({
firstName: "John", first_name: "John",
lastName: "Smith", last_name: "Smith",
age: 27, age: 27,
hasBeard: Bool.true, has_beard: Bool.true,
favoriteColor: Blue, favorite_color: Blue,
} })
|> Community.addPerson { |> Community.add_person({
firstName: "Debby", first_name: "Debby",
lastName: "Johnson", last_name: "Johnson",
age: 47, age: 47,
hasBeard: Bool.false, has_beard: Bool.false,
favoriteColor: Green, favorite_color: Green,
} })
|> Community.addPerson { |> Community.add_person({
firstName: "Jane", first_name: "Jane",
lastName: "Doe", last_name: "Doe",
age: 33, age: 33,
hasBeard: Bool.false, has_beard: Bool.false,
favoriteColor: RGB (255, 255, 0), favorite_color: RGB((255, 255, 0)),
} })
|> Community.addFriend 0 2 |> Community.add_friend(0, 2)
|> Community.addFriend 1 2 |> Community.add_friend(1, 2)
|> Inspect.toStr |> Inspect.to_str
|> Effect.putLine! |> Effect.put_line!

View file

@ -5,12 +5,12 @@ import pf.Effect
main! : {} => {} main! : {} => {}
main! = \{} -> main! = \{} ->
friends = ["Lu", "Marce", "Joaquin", "Chloé", "Mati", "Pedro"] friends = ["Lu", "Marce", "Joaquin", "Chloé", "Mati", "Pedro"]
printAll! friends print_all!(friends)
printAll! : List Str => {} print_all! : List Str => {}
printAll! = \friends -> print_all! = \friends ->
when friends is when friends is
[] -> {} [] -> {}
[first, .. as remaining] -> [first, .. as remaining] ->
Effect.putLine! first Effect.put_line!(first)
printAll! remaining print_all!(remaining)

View file

@ -5,20 +5,20 @@ import pf.Effect
main! : {} => {} main! : {} => {}
main! = \{} -> main! = \{} ->
_ = _ =
authenticate! {} authenticate!({})
|> Result.onErr! \BadPass -> |> Result.on_err!(\BadPass ->
Effect.putLine! "LOG: Failed login attempt" Effect.put_line!("LOG: Failed login attempt")
Ok "Bad password" Ok("Bad password"))
{} {}
authenticate! : {} => Result Str [BadPass] authenticate! : {} => Result Str [BadPass]
authenticate! = \{} -> authenticate! = \{} ->
Effect.putLine! "Enter your password:" Effect.put_line!("Enter your password:")
password = Effect.getLine! {} password = Effect.get_line!({})
if password == "password" then if password == "password" then
Ok "You are in" Ok("You are in")
else else
Err BadPass Err(BadPass)

View file

@ -5,15 +5,15 @@ import pf.Effect
main! : {} => {} main! : {} => {}
main! = \{} -> main! = \{} ->
["Welcome!", "What's your name?"] ["Welcome!", "What's your name?"]
|> List.forEach! Effect.putLine! |> List.for_each!(Effect.put_line!)
line = Effect.getLine! {} line = Effect.get_line!({})
if line == "secret" then if line == "secret" then
Effect.putLine! "You found the secret" Effect.put_line!("You found the secret")
Effect.putLine! "Congratulations!" Effect.put_line!("Congratulations!")
else else
{} {}
Effect.putLine! "You entered: $(line)" Effect.put_line!("You entered: $(line)")
Effect.putLine! "It is known" Effect.put_line!("It is known")

View file

@ -3,20 +3,20 @@ app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
import pf.Effect import pf.Effect
Fx : { Fx : {
getLine!: {} => Str, get_line! : {} => Str,
} }
main! : {} => {} main! : {} => {}
main! = \{} -> main! = \{} ->
notEffectful : Fx not_effectful : Fx
notEffectful = { not_effectful = {
getLine!: \{} -> "hardcoded" get_line!: \{} -> "hardcoded",
} }
effectful : Fx effectful : Fx
effectful = { effectful = {
getLine!: Effect.getLine! get_line!: Effect.get_line!,
} }
Effect.putLine! "notEffectful: $(notEffectful.getLine! {})" Effect.put_line!("not_effectful: $(not_effectful.get_line!({}))")
Effect.putLine! "effectful: $(effectful.getLine! {})" Effect.put_line!("effectful: $(effectful.get_line!({}))")

View file

@ -4,9 +4,9 @@ import pf.Effect
main! : {} => {} main! : {} => {}
main! = \{} -> main! = \{} ->
logged! "hello" (\{} -> Effect.putLine! "Hello, World!") logged!("hello", \{} -> Effect.put_line!("Hello, World!"))
logged! = \name, fx! -> logged! = \name, fx! ->
Effect.putLine! "Before $(name)" Effect.put_line!("Before $(name)")
fx! {} fx!({})
Effect.putLine! "After $(name)" Effect.put_line!("After $(name)")

View file

@ -1,6 +1,6 @@
app [main] { pf: platform "../test-platform-simple-zig/main.roc" } app [main] { pf: platform "../test-platform-simple-zig/main.roc" }
makeA = make_a =
a = 1 a = 1
expect a == 2 expect a == 2
@ -9,29 +9,29 @@ makeA =
a a
expect expect
a = makeA a = make_a
b = 2i64 b = 2i64
a == b a == b
polyDbg = \x -> poly_dbg = \x ->
dbg x dbg(x)
x x
main = main =
str = "this will for sure be a large string so when we split it it will use seamless slices which affect printing" str = "this will for sure be a large string so when we split it it will use seamless slices which affect printing"
words = Str.splitOn str " " words = Str.split_on(str, " ")
expect words == [] expect words == []
x = 42 x = 42
dbg x dbg(x)
dbg "Fjoer en ferdjer frieten oan dyn geve lea" dbg("Fjoer en ferdjer frieten oan dyn geve lea")
dbg "this is line 24" dbg("this is line 24")
r = { x: polyDbg "abc", y: polyDbg 10u8, z: polyDbg (A (B C)) } r = { x: poly_dbg("abc"), y: poly_dbg(10u8), z: poly_dbg(A(B(C))) }
when r is when r is
_ -> "Program finished!\n" _ -> "Program finished!\n"

View file

@ -1,12 +1,12 @@
module [ module [
addAndStringify, add_and_stringify,
] ]
import Transitive import Transitive
addAndStringify = \num1, num2 -> add_and_stringify = \num1, num2 ->
Num.toStr (Transitive.add num1 num2) Num.to_str(Transitive.add(num1, num2))
expect addAndStringify 1 2 == "3" expect add_and_stringify(1, 2) == "3"
expect addAndStringify 3 4 == "7" expect add_and_stringify(3, 4) == "7"

View file

@ -4,4 +4,4 @@ module [
add = \num1, num2 -> (num1 + num2) add = \num1, num2 -> (num1 + num2)
expect add 1 2 == 3 expect add(1, 2) == 3

View file

@ -1,4 +1,4 @@
module [Context, Data, with!, getChar!, Option, pushStack, popStack, toStr, inWhileScope] module [Context, Data, with!, get_char!, Option, push_stack, pop_stack, to_str, in_while_scope]
import pf.File import pf.File
import Variable exposing [Variable] import Variable exposing [Variable]
@ -9,100 +9,100 @@ Option a : [Some a, None]
Data : [Lambda (List U8), Number I32, Var Variable] Data : [Lambda (List U8), Number I32, Var Variable]
# While loops are special and have their own Scope specific state. # While loops are special and have their own Scope specific state.
WhileState : { cond : List U8, body : List U8, state : [InCond, InBody] } WhileState : { cond : List U8, body : List U8, state : [InCond, InBody] }
Scope : { data : Option File.Handle, index : U64, buf : List U8, whileInfo : Option WhileState } Scope : { data : Option File.Handle, index : U64, buf : List U8, while_info : Option WhileState }
State : [Executing, InComment, InLambda U64 (List U8), InString (List U8), InNumber I32, InSpecialChar, LoadChar] State : [Executing, InComment, InLambda U64 (List U8), InString (List U8), InNumber I32, InSpecialChar, LoadChar]
Context : { scopes : List Scope, stack : List Data, vars : List Data, state : State } Context : { scopes : List Scope, stack : List Data, vars : List Data, state : State }
pushStack : Context, Data -> Context push_stack : Context, Data -> Context
pushStack = \ctx, data -> push_stack = \ctx, data ->
{ ctx & stack: List.append ctx.stack data } { ctx & stack: List.append(ctx.stack, data) }
# I think an open tag union should just work here. # I think an open tag union should just work here.
# Instead at a call sites, I need to match on the error and then return the same error. # Instead at a call sites, I need to match on the error and then return the same error.
# Otherwise it hits unreachable code in ir.rs # Otherwise it hits unreachable code in ir.rs
popStack : Context -> Result (Context, Data) [EmptyStack] pop_stack : Context -> Result (Context, Data) [EmptyStack]
popStack = \ctx -> pop_stack = \ctx ->
when List.last ctx.stack is when List.last(ctx.stack) is
Ok val -> Ok(val) ->
poppedCtx = { ctx & stack: List.dropAt ctx.stack (List.len ctx.stack - 1) } popped_ctx = { ctx & stack: List.drop_at(ctx.stack, (List.len(ctx.stack) - 1)) }
Ok (poppedCtx, val) Ok((popped_ctx, val))
Err ListWasEmpty -> Err(ListWasEmpty) ->
Err EmptyStack Err(EmptyStack)
toStrData : Data -> Str to_str_data : Data -> Str
toStrData = \data -> to_str_data = \data ->
when data is when data is
Lambda _ -> "[]" Lambda(_) -> "[]"
Number n -> Num.toStr (Num.intCast n) Number(n) -> Num.to_str(Num.int_cast(n))
Var v -> Variable.toStr v Var(v) -> Variable.to_str(v)
toStrState : State -> Str to_str_state : State -> Str
toStrState = \state -> to_str_state = \state ->
when state is when state is
Executing -> "Executing" Executing -> "Executing"
InComment -> "InComment" InComment -> "InComment"
InString _ -> "InString" InString(_) -> "InString"
InNumber _ -> "InNumber" InNumber(_) -> "InNumber"
InLambda _ _ -> "InLambda" InLambda(_, _) -> "InLambda"
InSpecialChar -> "InSpecialChar" InSpecialChar -> "InSpecialChar"
LoadChar -> "LoadChar" LoadChar -> "LoadChar"
toStr : Context -> Str to_str : Context -> Str
toStr = \{ scopes, stack, state, vars } -> to_str = \{ scopes, stack, state, vars } ->
depth = Num.toStr (List.len scopes) depth = Num.to_str(List.len(scopes))
stateStr = toStrState state state_str = to_str_state(state)
stackStr = Str.joinWith (List.map stack toStrData) " " stack_str = Str.join_with(List.map(stack, to_str_data), " ")
varsStr = Str.joinWith (List.map vars toStrData) " " vars_str = Str.join_with(List.map(vars, to_str_data), " ")
"\n============\nDepth: $(depth)\nState: $(stateStr)\nStack: [$(stackStr)]\nVars: [$(varsStr)]\n============\n" "\n============\nDepth: $(depth)\nState: $(state_str)\nStack: [$(stack_str)]\nVars: [$(vars_str)]\n============\n"
with! : Str, (Context => a) => a with! : Str, (Context => a) => a
with! = \path, callback! -> with! = \path, callback! ->
File.withOpen! path \handle -> File.with_open!(path, \handle ->
# I cant define scope here and put it in the list in callback. It breaks alias anaysis. # I cant define scope here and put it in the list in callback. It breaks alias anaysis.
# Instead I have to inline this. # Instead I have to inline this.
# root_scope = { data: Some handle, index: 0, buf: [], whileInfo: None } # root_scope = { data: Some handle, index: 0, buf: [], whileInfo: None }
callback! { scopes: [{ data: Some handle, index: 0, buf: [], whileInfo: None }], state: Executing, stack: [], vars: List.repeat (Number 0) Variable.totalCount } callback!({ scopes: [{ data: Some(handle), index: 0, buf: [], while_info: None }], state: Executing, stack: [], vars: List.repeat(Number(0), Variable.total_count) }))
# I am pretty sure there is a syntax to destructure and keep a reference to the whole, but Im not sure what it is. # I am pretty sure there is a syntax to destructure and keep a reference to the whole, but Im not sure what it is.
getChar! : Context => Result (U8, Context) [EndOfData, NoScope] get_char! : Context => Result (U8, Context) [EndOfData, NoScope]
getChar! = \ctx -> get_char! = \ctx ->
when List.last ctx.scopes is when List.last(ctx.scopes) is
Ok scope -> Ok(scope) ->
(val, newScope) = getCharScope!? scope (val, new_scope) = get_char_scope!?(scope)
Ok (val, { ctx & scopes: List.set ctx.scopes (List.len ctx.scopes - 1) newScope }) Ok((val, { ctx & scopes: List.set(ctx.scopes, (List.len(ctx.scopes) - 1), new_scope) }))
Err ListWasEmpty -> Err(ListWasEmpty) ->
Err NoScope Err(NoScope)
getCharScope! : Scope => Result (U8, Scope) [EndOfData, NoScope] get_char_scope! : Scope => Result (U8, Scope) [EndOfData, NoScope]
getCharScope! = \scope -> get_char_scope! = \scope ->
when List.get scope.buf scope.index is when List.get(scope.buf, scope.index) is
Ok val -> Ok(val) ->
Ok (val, { scope & index: scope.index + 1 }) Ok((val, { scope & index: scope.index + 1 }))
Err OutOfBounds -> Err(OutOfBounds) ->
when scope.data is when scope.data is
Some h -> Some(h) ->
bytes = File.chunk! h bytes = File.chunk!(h)
when List.first bytes is when List.first(bytes) is
Ok val -> Ok(val) ->
# This starts at 1 because the first character is already being returned. # This starts at 1 because the first character is already being returned.
Ok (val, { scope & buf: bytes, index: 1 }) Ok((val, { scope & buf: bytes, index: 1 }))
Err ListWasEmpty -> Err(ListWasEmpty) ->
Err EndOfData Err(EndOfData)
None -> None ->
Err EndOfData Err(EndOfData)
inWhileScope : Context -> Bool in_while_scope : Context -> Bool
inWhileScope = \ctx -> in_while_scope = \ctx ->
when List.last ctx.scopes is when List.last(ctx.scopes) is
Ok scope -> Ok(scope) ->
scope.whileInfo != None scope.while_info != None
Err ListWasEmpty -> Err(ListWasEmpty) ->
Bool.false Bool.false

View file

@ -1,33 +1,33 @@
module [Variable, fromUtf8, toIndex, totalCount, toStr] module [Variable, from_utf8, to_index, total_count, to_str]
# Variables in False can only be single letters. Thus, the valid variables are "a" to "z". # Variables in False can only be single letters. Thus, the valid variables are "a" to "z".
# This opaque type deals with ensure we always have valid variables. # This opaque type deals with ensure we always have valid variables.
Variable := U8 Variable := U8
totalCount : U64 total_count : U64
totalCount = total_count =
0x7A # "z" 0x7A # "z"
- 0x61 # "a" - 0x61 # "a"
+ 1 + 1
toStr : Variable -> Str to_str : Variable -> Str
toStr = \@Variable char -> to_str = \@Variable(char) ->
when Str.fromUtf8 [char] is when Str.from_utf8([char]) is
Ok str -> str Ok(str) -> str
_ -> "_" _ -> "_"
fromUtf8 : U8 -> Result Variable [InvalidVariableUtf8] from_utf8 : U8 -> Result Variable [InvalidVariableUtf8]
fromUtf8 = \char -> from_utf8 = \char ->
if if
char char
>= 0x61 # "a" >= 0x61 # "a"
&& char && char
<= 0x7A # "z" <= 0x7A # "z"
then then
Ok (@Variable char) Ok(@Variable(char))
else else
Err InvalidVariableUtf8 Err(InvalidVariableUtf8)
toIndex : Variable -> U64 to_index : Variable -> U64
toIndex = \@Variable char -> to_index = \@Variable(char) ->
Num.intCast (char - 0x61) # "a" Num.int_cast((char - 0x61)) # "a"

View file

@ -16,454 +16,454 @@ InterpreterErrors : [BadUtf8, DivByZero, EmptyStack, InvalidBooleanValue, Invali
main! : Str => {} main! : Str => {}
main! = \filename -> main! = \filename ->
when interpretFile! filename is when interpret_file!(filename) is
Ok {} -> Ok({}) ->
{} {}
Err (StringErr e) -> Err(StringErr(e)) ->
Stdout.line! "Ran into problem:\n$(e)\n" Stdout.line!("Ran into problem:\n$(e)\n")
interpretFile! : Str => Result {} [StringErr Str] interpret_file! : Str => Result {} [StringErr Str]
interpretFile! = \filename -> interpret_file! = \filename ->
Context.with! filename \ctx -> Context.with!(filename, \ctx ->
result = interpretCtx! ctx result = interpret_ctx!(ctx)
when result is when result is
Ok _ -> Ok(_) ->
Ok {} Ok({})
Err BadUtf8 -> Err(BadUtf8) ->
Err (StringErr "Failed to convert string from Utf8 bytes") Err(StringErr("Failed to convert string from Utf8 bytes"))
Err DivByZero -> Err(DivByZero) ->
Err (StringErr "Division by zero") Err(StringErr("Division by zero"))
Err EmptyStack -> Err(EmptyStack) ->
Err (StringErr "Tried to pop a value off of the stack when it was empty") Err(StringErr("Tried to pop a value off of the stack when it was empty"))
Err InvalidBooleanValue -> Err(InvalidBooleanValue) ->
Err (StringErr "Ran into an invalid boolean that was neither false (0) or true (-1)") Err(StringErr("Ran into an invalid boolean that was neither false (0) or true (-1)"))
Err (InvalidChar char) -> Err(InvalidChar(char)) ->
Err (StringErr "Ran into an invalid character with ascii code: $(char)") Err(StringErr("Ran into an invalid character with ascii code: $(char)"))
Err MaxInputNumber -> Err(MaxInputNumber) ->
Err (StringErr "Like the original false compiler, the max input number is 320,000") Err(StringErr("Like the original false compiler, the max input number is 320,000"))
Err NoLambdaOnStack -> Err(NoLambdaOnStack) ->
Err (StringErr "Tried to run a lambda when no lambda was on the stack") Err(StringErr("Tried to run a lambda when no lambda was on the stack"))
Err NoNumberOnStack -> Err(NoNumberOnStack) ->
Err (StringErr "Tried to run a number when no number was on the stack") Err(StringErr("Tried to run a number when no number was on the stack"))
Err NoVariableOnStack -> Err(NoVariableOnStack) ->
Err (StringErr "Tried to load a variable when no variable was on the stack") Err(StringErr("Tried to load a variable when no variable was on the stack"))
Err NoScope -> Err(NoScope) ->
Err (StringErr "Tried to run code when not in any scope") Err(StringErr("Tried to run code when not in any scope"))
Err OutOfBounds -> Err(OutOfBounds) ->
Err (StringErr "Tried to load from an offset that was outside of the stack") Err(StringErr("Tried to load from an offset that was outside of the stack"))
Err UnexpectedEndOfData -> Err(UnexpectedEndOfData) ->
Err (StringErr "Hit end of data while still parsing something") Err(StringErr("Hit end of data while still parsing something")))
interpretCtx! : Context => Result Context InterpreterErrors interpret_ctx! : Context => Result Context InterpreterErrors
interpretCtx! = \ctx -> interpret_ctx! = \ctx ->
when interpretCtxLoop! ctx is when interpret_ctx_loop!(ctx) is
Ok (Step next) -> Ok(Step(next)) ->
interpretCtx! next interpret_ctx!(next)
Ok (Done next) -> Ok(Done(next)) ->
Ok next Ok(next)
Err e -> Err(e) ->
Err e Err(e)
interpretCtxLoop! : Context => Result [Step Context, Done Context] InterpreterErrors interpret_ctx_loop! : Context => Result [Step Context, Done Context] InterpreterErrors
interpretCtxLoop! = \ctx -> interpret_ctx_loop! = \ctx ->
when ctx.state is when ctx.state is
Executing if Context.inWhileScope ctx -> Executing if Context.in_while_scope(ctx) ->
# Deal with the current while loop potentially looping. # Deal with the current while loop potentially looping.
last = (List.len ctx.scopes - 1) last = (List.len(ctx.scopes) - 1)
scope = List.get ctx.scopes last |> Result.mapErr? \_ -> NoScope scope = List.get(ctx.scopes, last) |> Result.map_err?(\_ -> NoScope)
when scope.whileInfo is when scope.while_info is
Some { state: InCond, body, cond } -> Some({ state: InCond, body, cond }) ->
# Just ran condition. Check the top of stack to see if body should run. # Just ran condition. Check the top of stack to see if body should run.
(popCtx, n) = popNumber? ctx (pop_ctx, n) = pop_number?(ctx)
if n == 0 then if n == 0 then
newScope = { scope & whileInfo: None } new_scope = { scope & while_info: None }
Ok (Step { popCtx & scopes: List.set ctx.scopes last newScope }) Ok(Step({ pop_ctx & scopes: List.set(ctx.scopes, last, new_scope) }))
else else
newScope = { scope & whileInfo: Some { state: InBody, body, cond } } new_scope = { scope & while_info: Some({ state: InBody, body, cond }) }
Ok (Step { popCtx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: body, index: 0, whileInfo: None } }) Ok(Step({ pop_ctx & scopes: List.append(List.set(ctx.scopes, last, new_scope), { data: None, buf: body, index: 0, while_info: None }) }))
Some { state: InBody, body, cond } -> Some({ state: InBody, body, cond }) ->
# Just rand the body. Run the condition again. # Just rand the body. Run the condition again.
newScope = { scope & whileInfo: Some { state: InCond, body, cond } } new_scope = { scope & while_info: Some({ state: InCond, body, cond }) }
Ok (Step { ctx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: cond, index: 0, whileInfo: None } }) Ok(Step({ ctx & scopes: List.append(List.set(ctx.scopes, last, new_scope), { data: None, buf: cond, index: 0, while_info: None }) }))
None -> None ->
Err NoScope Err(NoScope)
Executing -> Executing ->
# Stdout.line! (Context.toStr ctx) # Stdout.line! (Context.to_str ctx)
result = Context.getChar! ctx result = Context.get_char!(ctx)
when result is when result is
Ok (val, newCtx) -> Ok((val, new_ctx)) ->
execCtx = stepExecCtx!? newCtx val exec_ctx = step_exec_ctx!?(new_ctx, val)
Ok (Step execCtx) Ok(Step(exec_ctx))
Err NoScope -> Err(NoScope) ->
Err NoScope Err(NoScope)
Err EndOfData -> Err(EndOfData) ->
# Computation complete for this scope. # Computation complete for this scope.
# Drop a scope. # Drop a scope.
dropCtx = { ctx & scopes: List.dropAt ctx.scopes (List.len ctx.scopes - 1) } drop_ctx = { ctx & scopes: List.drop_at(ctx.scopes, (List.len(ctx.scopes) - 1)) }
# If no scopes left, all execution complete. # If no scopes left, all execution complete.
if List.isEmpty dropCtx.scopes then if List.is_empty(drop_ctx.scopes) then
Ok (Done dropCtx) Ok(Done(drop_ctx))
else else
Ok (Step dropCtx) Ok(Step(drop_ctx))
InComment -> InComment ->
(val, newCtx) = Context.getChar! ctx |> Result.mapErr? endUnexpected (val, new_ctx) = Context.get_char!(ctx) |> Result.map_err?(end_unexpected)
if val == 0x7D then if val == 0x7D then
# `}` end of comment # `}` end of comment
Ok (Step { newCtx & state: Executing }) Ok(Step({ new_ctx & state: Executing }))
else else
Ok (Step { newCtx & state: InComment }) Ok(Step({ new_ctx & state: InComment }))
InNumber accum -> InNumber(accum) ->
(val, newCtx) = Context.getChar! ctx |> Result.mapErr? endUnexpected (val, new_ctx) = Context.get_char!(ctx) |> Result.map_err?(end_unexpected)
if isDigit val then if is_digit(val) then
# still in the number # still in the number
# i32 multiplication is kinda broken because it implicitly seems to want to upcast to i64. # i32 multiplication is kinda broken because it implicitly seems to want to upcast to i64.
# so like should be (i32, i32) -> i32, but seems to be (i32, i32) -> i64 # so like should be (i32, i32) -> i32, but seems to be (i32, i32) -> i64
# so this is make i64 mul by 10 then convert back to i32. # so this is make i64 mul by 10 then convert back to i32.
nextAccum = (10 * Num.intCast accum) + Num.intCast (val - 0x30) next_accum = (10 * Num.int_cast(accum)) + Num.int_cast((val - 0x30))
Ok (Step { newCtx & state: InNumber (Num.intCast nextAccum) }) Ok(Step({ new_ctx & state: InNumber(Num.int_cast(next_accum)) }))
else else
# outside of number now, this needs to be executed. # outside of number now, this needs to be executed.
pushCtx = Context.pushStack newCtx (Number accum) push_ctx = Context.push_stack(new_ctx, Number(accum))
execCtx = stepExecCtx!? { pushCtx & state: Executing } val exec_ctx = step_exec_ctx!?({ push_ctx & state: Executing }, val)
Ok (Step execCtx) Ok(Step(exec_ctx))
InString bytes -> InString(bytes) ->
(val, newCtx) = Context.getChar! ctx |> Result.mapErr? endUnexpected (val, new_ctx) = Context.get_char!(ctx) |> Result.map_err?(end_unexpected)
if val == 0x22 then if val == 0x22 then
# `"` end of string # `"` end of string
when Str.fromUtf8 bytes is when Str.from_utf8(bytes) is
Ok str -> Ok(str) ->
Stdout.raw! str Stdout.raw!(str)
Ok (Step { newCtx & state: Executing }) Ok(Step({ new_ctx & state: Executing }))
Err _ -> Err(_) ->
Err BadUtf8 Err(BadUtf8)
else else
Ok (Step { newCtx & state: InString (List.append bytes val) }) Ok(Step({ new_ctx & state: InString(List.append(bytes, val)) }))
InLambda depth bytes -> InLambda(depth, bytes) ->
(val, newCtx) = Context.getChar! ctx |> Result.mapErr? endUnexpected (val, new_ctx) = Context.get_char!(ctx) |> Result.map_err?(end_unexpected)
if val == 0x5B then if val == 0x5B then
# start of a nested lambda `[` # start of a nested lambda `[`
Ok (Step { newCtx & state: InLambda (depth + 1) (List.append bytes val) }) Ok(Step({ new_ctx & state: InLambda((depth + 1), List.append(bytes, val)) }))
else if val == 0x5D then else if val == 0x5D then
# `]` end of current lambda # `]` end of current lambda
if depth == 0 then if depth == 0 then
# end of all lambdas # end of all lambdas
Ok (Step (Context.pushStack { newCtx & state: Executing } (Lambda bytes))) Ok(Step(Context.push_stack({ new_ctx & state: Executing }, Lambda(bytes))))
else else
# end of nested lambda # end of nested lambda
Ok (Step { newCtx & state: InLambda (depth - 1) (List.append bytes val) }) Ok(Step({ new_ctx & state: InLambda((depth - 1), List.append(bytes, val)) }))
else else
Ok (Step { newCtx & state: InLambda depth (List.append bytes val) }) Ok(Step({ new_ctx & state: InLambda(depth, List.append(bytes, val)) }))
InSpecialChar -> InSpecialChar ->
val = Context.getChar! { ctx & state: Executing } |> Result.mapErr? endUnexpected val = Context.get_char!({ ctx & state: Executing }) |> Result.map_err?(end_unexpected)
when val is when val is
(0xB8, newCtx) -> (0xB8, new_ctx) ->
(popCtx, index) = popNumber? newCtx (pop_ctx, index) = pop_number?(new_ctx)
# I think Num.abs is too restrictive, it should be able to produce a natural number, but it seem to be restricted to signed numbers. # I think Num.abs is too restrictive, it should be able to produce a natural number, but it seem to be restricted to signed numbers.
size = List.len popCtx.stack - 1 size = List.len(pop_ctx.stack) - 1
offset = Num.intCast size - index offset = Num.int_cast(size) - index
if offset >= 0 then if offset >= 0 then
stackVal = List.get? popCtx.stack (Num.intCast offset) stack_val = List.get?(pop_ctx.stack, Num.int_cast(offset))
Ok (Step (Context.pushStack popCtx stackVal)) Ok(Step(Context.push_stack(pop_ctx, stack_val)))
else else
Err OutOfBounds Err(OutOfBounds)
(0x9F, newCtx) -> (0x9F, new_ctx) ->
# This is supposed to flush io buffers. We don't buffer, so it does nothing # This is supposed to flush io buffers. We don't buffer, so it does nothing
Ok (Step newCtx) Ok(Step(new_ctx))
(x, _) -> (x, _) ->
data = Num.toStr (Num.intCast x) data = Num.to_str(Num.int_cast(x))
Err (InvalidChar data) Err(InvalidChar(data))
LoadChar -> LoadChar ->
(x, newCtx) = Context.getChar! { ctx & state: Executing } |> Result.mapErr? endUnexpected (x, new_ctx) = Context.get_char!({ ctx & state: Executing }) |> Result.map_err?(end_unexpected)
Ok (Step (Context.pushStack newCtx (Number (Num.intCast x)))) Ok(Step(Context.push_stack(new_ctx, Number(Num.int_cast(x)))))
# If it weren't for reading stdin or writing to stdout, this could return a result. # If it weren't for reading stdin or writing to stdout, this could return a result.
stepExecCtx! : Context, U8 => Result Context InterpreterErrors step_exec_ctx! : Context, U8 => Result Context InterpreterErrors
stepExecCtx! = \ctx, char -> step_exec_ctx! = \ctx, char ->
when char is when char is
0x21 -> 0x21 ->
# `!` execute lambda # `!` execute lambda
(popCtx, bytes) = popLambda? ctx (pop_ctx, bytes) = pop_lambda?(ctx)
Ok { popCtx & scopes: List.append popCtx.scopes { data: None, buf: bytes, index: 0, whileInfo: None } } Ok({ pop_ctx & scopes: List.append(pop_ctx.scopes, { data: None, buf: bytes, index: 0, while_info: None }) })
0x3F -> 0x3F ->
# `?` if # `?` if
(popCtx1, bytes) = popLambda? ctx (pop_ctx1, bytes) = pop_lambda?(ctx)
(popCtx2, n1) = popNumber? popCtx1 (pop_ctx2, n1) = pop_number?(pop_ctx1)
if n1 == 0 then if n1 == 0 then
Ok popCtx2 Ok(pop_ctx2)
else else
Ok { popCtx2 & scopes: List.append popCtx2.scopes { data: None, buf: bytes, index: 0, whileInfo: None } } Ok({ pop_ctx2 & scopes: List.append(pop_ctx2.scopes, { data: None, buf: bytes, index: 0, while_info: None }) })
0x23 -> 0x23 ->
# `#` while # `#` while
(popCtx1, body) = popLambda? ctx (pop_ctx1, body) = pop_lambda?(ctx)
(popCtx2, cond) = popLambda? popCtx1 (pop_ctx2, cond) = pop_lambda?(pop_ctx1)
last = (List.len popCtx2.scopes - 1) last = (List.len(pop_ctx2.scopes) - 1)
scope = List.get popCtx2.scopes last |> Result.mapErr? \_ -> NoScope scope = List.get(pop_ctx2.scopes, last) |> Result.map_err?(\_ -> NoScope)
# set the current scope to be in a while loop. # set the current scope to be in a while loop.
scopes = List.set popCtx2.scopes last { scope & whileInfo: Some { cond: cond, body: body, state: InCond } } scopes = List.set(pop_ctx2.scopes, last, { scope & while_info: Some({ cond: cond, body: body, state: InCond }) })
# push a scope to execute the condition. # push a scope to execute the condition.
Ok { popCtx2 & scopes: List.append scopes { data: None, buf: cond, index: 0, whileInfo: None } } Ok({ pop_ctx2 & scopes: List.append(scopes, { data: None, buf: cond, index: 0, while_info: None }) })
0x24 -> 0x24 ->
# `$` dup # `$` dup
# Switching this to List.last and changing the error to ListWasEmpty leads to a compiler bug. # Switching this to List.last and changing the error to ListWasEmpty leads to a compiler bug.
# Complains about the types eq not matching. # Complains about the types eq not matching.
when List.get ctx.stack (List.len ctx.stack - 1) is when List.get(ctx.stack, (List.len(ctx.stack) - 1)) is
Ok dupItem -> Ok (Context.pushStack ctx dupItem) Ok(dup_item) -> Ok(Context.push_stack(ctx, dup_item))
Err OutOfBounds -> Err EmptyStack Err(OutOfBounds) -> Err(EmptyStack)
0x25 -> 0x25 ->
# `%` drop # `%` drop
when Context.popStack ctx is when Context.pop_stack(ctx) is
# Dropping with an empty stack, all results here are fine # Dropping with an empty stack, all results here are fine
Ok (popCtx, _) -> Ok popCtx Ok((pop_ctx, _)) -> Ok(pop_ctx)
Err _ -> Ok ctx Err(_) -> Ok(ctx)
0x5C -> 0x5C ->
# `\` swap # `\` swap
(popCtx1, n1) = Context.popStack? ctx (pop_ctx1, n1) = Context.pop_stack?(ctx)
(popCtx2, n2) = Context.popStack? popCtx1 (pop_ctx2, n2) = Context.pop_stack?(pop_ctx1)
Ok (Context.pushStack (Context.pushStack popCtx2 n1) n2) Ok(Context.push_stack(Context.push_stack(pop_ctx2, n1), n2))
0x40 -> 0x40 ->
# `@` rot # `@` rot
result2 = result2 =
(popCtx1, n1) = Context.popStack? ctx (pop_ctx1, n1) = Context.pop_stack?(ctx)
(popCtx2, n2) = Context.popStack? popCtx1 (pop_ctx2, n2) = Context.pop_stack?(pop_ctx1)
(popCtx3, n3) = Context.popStack? popCtx2 (pop_ctx3, n3) = Context.pop_stack?(pop_ctx2)
Ok (Context.pushStack (Context.pushStack (Context.pushStack popCtx3 n2) n1) n3) Ok(Context.push_stack(Context.push_stack(Context.push_stack(pop_ctx3, n2), n1), n3))
when result2 is when result2 is
Ok a -> Ok(a) ->
Ok a Ok(a)
# Being explicit with error type is required to stop the need to propogate the error parameters to Context.popStack # Being explicit with error type is required to stop the need to propogate the error parameters to Context.popStack
Err EmptyStack -> Err(EmptyStack) ->
Err EmptyStack Err(EmptyStack)
0xC3 -> 0xC3 ->
# `ø` pick or `ß` flush # `ø` pick or `ß` flush
# these are actually 2 bytes, 0xC3 0xB8 or 0xC3 0x9F # these are actually 2 bytes, 0xC3 0xB8 or 0xC3 0x9F
# requires special parsing # requires special parsing
Ok { ctx & state: InSpecialChar } Ok({ ctx & state: InSpecialChar })
0x4F -> 0x4F ->
# `O` also treat this as pick for easier script writing # `O` also treat this as pick for easier script writing
(popCtx, index) = popNumber? ctx (pop_ctx, index) = pop_number?(ctx)
# I think Num.abs is too restrictive, it should be able to produce a natural number, but it seem to be restricted to signed numbers. # I think Num.abs is too restrictive, it should be able to produce a natural number, but it seem to be restricted to signed numbers.
size = List.len popCtx.stack - 1 size = List.len(pop_ctx.stack) - 1
offset = Num.intCast size - index offset = Num.int_cast(size) - index
if offset >= 0 then if offset >= 0 then
stackVal = List.get? popCtx.stack (Num.intCast offset) stack_val = List.get?(pop_ctx.stack, Num.int_cast(offset))
Ok (Context.pushStack popCtx stackVal) Ok(Context.push_stack(pop_ctx, stack_val))
else else
Err OutOfBounds Err(OutOfBounds)
0x42 -> 0x42 ->
# `B` also treat this as flush for easier script writing # `B` also treat this as flush for easier script writing
# This is supposed to flush io buffers. We don't buffer, so it does nothing # This is supposed to flush io buffers. We don't buffer, so it does nothing
Ok ctx Ok(ctx)
0x27 -> 0x27 ->
# `'` load next char # `'` load next char
Ok { ctx & state: LoadChar } Ok({ ctx & state: LoadChar })
0x2B -> 0x2B ->
# `+` add # `+` add
binaryOp ctx Num.addWrap binary_op(ctx, Num.add_wrap)
0x2D -> 0x2D ->
# `-` sub # `-` sub
binaryOp ctx Num.subWrap binary_op(ctx, Num.sub_wrap)
0x2A -> 0x2A ->
# `*` mul # `*` mul
binaryOp ctx Num.mulWrap binary_op(ctx, Num.mul_wrap)
0x2F -> 0x2F ->
# `/` div # `/` div
# Due to possible division by zero error, this must be handled specially. # Due to possible division by zero error, this must be handled specially.
(popCtx1, numR) = popNumber? ctx (pop_ctx1, num_r) = pop_number?(ctx)
(popCtx2, numL) = popNumber? popCtx1 (pop_ctx2, num_l) = pop_number?(pop_ctx1)
res = Num.divTruncChecked? numL numR res = Num.div_trunc_checked?(num_l, num_r)
Ok (Context.pushStack popCtx2 (Number res)) Ok(Context.push_stack(pop_ctx2, Number(res)))
0x26 -> 0x26 ->
# `&` bitwise and # `&` bitwise and
binaryOp ctx Num.bitwiseAnd binary_op(ctx, Num.bitwise_and)
0x7C -> 0x7C ->
# `|` bitwise or # `|` bitwise or
binaryOp ctx Num.bitwiseOr binary_op(ctx, Num.bitwise_or)
0x3D -> 0x3D ->
# `=` equals # `=` equals
binaryOp ctx \a, b -> binary_op(ctx, \a, b ->
if a == b then if a == b then
-1 -1
else else
0 0)
0x3E -> 0x3E ->
# `>` greater than # `>` greater than
binaryOp ctx \a, b -> binary_op(ctx, \a, b ->
if a > b then if a > b then
-1 -1
else else
0 0)
0x5F -> 0x5F ->
# `_` negate # `_` negate
unaryOp ctx Num.neg unary_op(ctx, Num.neg)
0x7E -> 0x7E ->
# `~` bitwise not # `~` bitwise not
unaryOp ctx (\x -> Num.bitwiseXor x -1) # xor with -1 should be bitwise not unary_op(ctx, \x -> Num.bitwise_xor(x, -1)) # xor with -1 should be bitwise not
0x2C -> 0x2C ->
# `,` write char # `,` write char
(popCtx, num) = popNumber? ctx (pop_ctx, num) = pop_number?(ctx)
str = Str.fromUtf8 [Num.intCast num] |> Result.mapErr? \_ -> BadUtf8 str = Str.from_utf8([Num.int_cast(num)]) |> Result.map_err?(\_ -> BadUtf8)
Stdout.raw! str Stdout.raw!(str)
Ok popCtx Ok(pop_ctx)
0x2E -> 0x2E ->
# `.` write int # `.` write int
(popCtx, num) = popNumber? ctx (pop_ctx, num) = pop_number?(ctx)
Stdout.raw! (Num.toStr (Num.intCast num)) Stdout.raw!(Num.to_str(Num.int_cast(num)))
Ok popCtx Ok(pop_ctx)
0x5E -> 0x5E ->
# `^` read char as int # `^` read char as int
in = Stdin.char! {} in = Stdin.char!({})
if in == 255 then if in == 255 then
# max char sent on EOF. Change to -1 # max char sent on EOF. Change to -1
Ok (Context.pushStack ctx (Number -1)) Ok(Context.push_stack(ctx, Number(-1)))
else else
Ok (Context.pushStack ctx (Number (Num.intCast in))) Ok(Context.push_stack(ctx, Number(Num.int_cast(in))))
0x3A -> 0x3A ->
# `:` store to variable # `:` store to variable
(popCtx1, var) = popVariable? ctx (pop_ctx1, var) = pop_variable?(ctx)
(popCtx2, n1) = Context.popStack? popCtx1 (pop_ctx2, n1) = Context.pop_stack?(pop_ctx1)
Ok { popCtx2 & vars: List.set popCtx2.vars (Variable.toIndex var) n1 } Ok({ pop_ctx2 & vars: List.set(pop_ctx2.vars, Variable.to_index(var), n1) })
0x3B -> 0x3B ->
# `;` load from variable # `;` load from variable
(popCtx, var) = popVariable? ctx (pop_ctx, var) = pop_variable?(ctx)
elem = List.get? popCtx.vars (Variable.toIndex var) elem = List.get?(pop_ctx.vars, Variable.to_index(var))
Ok (Context.pushStack popCtx elem) Ok(Context.push_stack(pop_ctx, elem))
0x22 -> 0x22 ->
# `"` string start # `"` string start
Ok { ctx & state: InString [] } Ok({ ctx & state: InString([]) })
0x5B -> 0x5B ->
# `"` string start # `"` string start
Ok { ctx & state: InLambda 0 [] } Ok({ ctx & state: InLambda(0, []) })
0x7B -> 0x7B ->
# `{` comment start # `{` comment start
Ok { ctx & state: InComment } Ok({ ctx & state: InComment })
x if isDigit x -> x if is_digit(x) ->
# number start # number start
Ok { ctx & state: InNumber (Num.intCast (x - 0x30)) } Ok({ ctx & state: InNumber(Num.int_cast((x - 0x30))) })
x if isWhitespace x -> x if is_whitespace(x) ->
Ok ctx Ok(ctx)
x -> x ->
when Variable.fromUtf8 x is when Variable.from_utf8(x) is
# letters are variable names # letters are variable names
Ok var -> Ok(var) ->
Ok (Context.pushStack ctx (Var var)) Ok(Context.push_stack(ctx, Var(var)))
Err _ -> Err(_) ->
data = Num.toStr (Num.intCast x) data = Num.to_str(Num.int_cast(x))
Err (InvalidChar data) Err(InvalidChar(data))
unaryOp : Context, (I32 -> I32) -> Result Context InterpreterErrors unary_op : Context, (I32 -> I32) -> Result Context InterpreterErrors
unaryOp = \ctx, op -> unary_op = \ctx, op ->
(popCtx, num) = popNumber? ctx (pop_ctx, num) = pop_number?(ctx)
Ok (Context.pushStack popCtx (Number (op num))) Ok(Context.push_stack(pop_ctx, Number(op(num))))
binaryOp : Context, (I32, I32 -> I32) -> Result Context InterpreterErrors binary_op : Context, (I32, I32 -> I32) -> Result Context InterpreterErrors
binaryOp = \ctx, op -> binary_op = \ctx, op ->
(popCtx1, numR) = popNumber? ctx (pop_ctx1, num_r) = pop_number?(ctx)
(popCtx2, numL) = popNumber? popCtx1 (pop_ctx2, num_l) = pop_number?(pop_ctx1)
Ok (Context.pushStack popCtx2 (Number (op numL numR))) Ok(Context.push_stack(pop_ctx2, Number(op(num_l, num_r))))
popNumber : Context -> Result (Context, I32) InterpreterErrors pop_number : Context -> Result (Context, I32) InterpreterErrors
popNumber = \ctx -> pop_number = \ctx ->
when Context.popStack? ctx is when Context.pop_stack?(ctx) is
(popCtx, Number num) -> Ok (popCtx, num) (pop_ctx, Number(num)) -> Ok((pop_ctx, num))
_ -> Err NoNumberOnStack _ -> Err(NoNumberOnStack)
popLambda : Context -> Result (Context, List U8) InterpreterErrors pop_lambda : Context -> Result (Context, List U8) InterpreterErrors
popLambda = \ctx -> pop_lambda = \ctx ->
when Context.popStack? ctx is when Context.pop_stack?(ctx) is
(popCtx, Lambda bytes) -> Ok (popCtx, bytes) (pop_ctx, Lambda(bytes)) -> Ok((pop_ctx, bytes))
_ -> Err NoLambdaOnStack _ -> Err(NoLambdaOnStack)
popVariable : Context -> Result (Context, Variable) InterpreterErrors pop_variable : Context -> Result (Context, Variable) InterpreterErrors
popVariable = \ctx -> pop_variable = \ctx ->
when Context.popStack? ctx is when Context.pop_stack?(ctx) is
(popCtx, Var var) -> Ok (popCtx, var) (pop_ctx, Var(var)) -> Ok((pop_ctx, var))
_ -> Err NoVariableOnStack _ -> Err(NoVariableOnStack)
isDigit : U8 -> Bool is_digit : U8 -> Bool
isDigit = \char -> is_digit = \char ->
char char
>= 0x30 # `0` >= 0x30 # `0`
&& char && char
<= 0x39 # `0` <= 0x39 # `0`
isWhitespace : U8 -> Bool is_whitespace : U8 -> Bool
isWhitespace = \char -> is_whitespace = \char ->
char char
== 0xA # new line == 0xA # new line
|| char || char
@ -473,7 +473,7 @@ isWhitespace = \char ->
|| char || char
== 0x9 # tab == 0x9 # tab
endUnexpected = \err -> end_unexpected = \err ->
when err is when err is
NoScope -> NoScope ->
NoScope NoScope

View file

@ -1,30 +1,30 @@
module [line!, withOpen!, chunk!, Handle] module [line!, with_open!, chunk!, Handle]
import pf.Host import pf.Host
Handle := U64 Handle := U64
line! : Handle => Str line! : Handle => Str
line! = \@Handle handle -> line! = \@Handle(handle) ->
Host.getFileLine! handle Host.get_file_line!(handle)
chunk! : Handle => List U8 chunk! : Handle => List U8
chunk! = \@Handle handle -> chunk! = \@Handle(handle) ->
Host.getFileBytes! handle Host.get_file_bytes!(handle)
open! : Str => Handle open! : Str => Handle
open! = \path -> open! = \path ->
Host.openFile! path Host.open_file!(path)
|> @Handle |> @Handle
close! : Handle => {} close! : Handle => {}
close! = \@Handle handle -> close! = \@Handle(handle) ->
Host.closeFile! handle Host.close_file!(handle)
withOpen! : Str, (Handle => a) => a with_open! : Str, (Handle => a) => a
withOpen! = \path, callback! -> with_open! = \path, callback! ->
handle = open! path handle = open!(path)
result = callback! handle result = callback!(handle)
close! handle close!(handle)
result result

View file

@ -1,19 +1,19 @@
hosted Host hosted Host
exposes [openFile!, closeFile!, getFileLine!, getFileBytes!, putLine!, putRaw!, getLine!, getChar!] exposes [open_file!, close_file!, get_file_line!, get_file_bytes!, put_line!, put_raw!, get_line!, get_char!]
imports [] imports []
openFile! : Str => U64 open_file! : Str => U64
closeFile! : U64 => {} close_file! : U64 => {}
getFileLine! : U64 => Str get_file_line! : U64 => Str
getFileBytes! : U64 => List U8 get_file_bytes! : U64 => List U8
putLine! : Str => {} put_line! : Str => {}
putRaw! : Str => {} put_raw! : Str => {}
getLine! : {} => Str get_line! : {} => Str
getChar! : {} => U8 get_char! : {} => U8

View file

@ -4,8 +4,8 @@ import pf.Host
line! : {} => Str line! : {} => Str
line! = \{} -> line! = \{} ->
Host.getLine! {} Host.get_line!({})
char! : {} => U8 char! : {} => U8
char! = \{} -> char! = \{} ->
Host.getChar! {} Host.get_char!({})

View file

@ -4,8 +4,8 @@ import pf.Host
line! : Str => {} line! : Str => {}
line! = \text -> line! = \text ->
Host.putLine! text Host.put_line!(text)
raw! : Str => {} raw! : Str => {}
raw! = \text -> raw! = \text ->
Host.putRaw! text Host.put_raw!(text)

View file

@ -3,7 +3,7 @@ platform "false-interpreter"
exposes [] exposes []
packages {} packages {}
imports [] imports []
provides [mainForHost!] provides [main_for_host!]
mainForHost! : Str => {} main_for_host! : Str => {}
mainForHost! = \file -> main! file main_for_host! = \file -> main!(file)

View file

@ -19,7 +19,7 @@ fn file_handles() -> &'static Mutex<HashMap<u64, BufReader<File>>> {
} }
extern "C" { extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"] #[link_name = "roc__main_for_host_1_exposed_generic"]
fn roc_main(void: *const c_void, args: *mut RocStr); fn roc_main(void: *const c_void, args: *mut RocStr);
} }

View file

@ -1,4 +1,4 @@
app [main] { pf: "platform/main.roc" } app [main] { pf: "platform/main.roc" }
main : Str main : Str
main = Dep1.value1 {} main = Dep1.value1({})

View file

@ -1,4 +1,4 @@
app [main] { pf: "platform/main.roc" } app [main] { pf: "platform/main.roc" }
main : Str main : Str
main = Dep1.value1 {} main = Dep1.value1({})

View file

@ -1,4 +1,4 @@
app [main] { pf: "platform/main.roc" } app [main] { pf: "platform/main.roc" }
main : Str main : Str
main = Dep1.value1 {} main = Dep1.value1({})

View file

@ -3,4 +3,4 @@ module [value1]
import Dep2 import Dep2
value1 : {} -> Str value1 : {} -> Str
value1 = \_ -> Dep2.value2 {} value1 = \_ -> Dep2.value2({})

View file

@ -3,4 +3,4 @@ app [main] { pf: platform "../../test-platform-simple-zig/main.roc" }
import Dep1 import Dep1
main : Str main : Str
main = Dep1.value1 {} main = Dep1.value1({})

View file

@ -1,7 +1,7 @@
module [plainText, emText] module [plain_text, em_text]
import Symbol exposing [Ident] import Symbol exposing [Ident]
plainText = \str -> PlainText str plain_text = \str -> PlainText(str)
emText = \str -> EmText str em_text = \str -> EmText(str)

View file

@ -1,7 +1,7 @@
module [plainText, emText] module [plain_text, em_text]
import Symbol exposing [Ident] import Symbol exposing [Ident]
plainText = \str -> PlainText str plain_text = \str -> PlainText(str)
emText = \str -> EmText str em_text = \str -> EmText(str)

View file

@ -1,7 +1,7 @@
module [valueFromPkg] module [value_from_pkg]
import cli.Foo import cli.Foo
valueFromPkg = Foo.foo value_from_pkg = Foo.foo
expect valueFromPkg == "Foo" expect value_from_pkg == "Foo"

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